axsdb 0.0.2__py3-none-any.whl → 0.0.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
axsdb/math.py ADDED
@@ -0,0 +1,503 @@
1
+ """
2
+ Fast interpolation with Numba.
3
+
4
+ This module provides high-performance interpolation functions implemented
5
+ using Numba's ``guvectorize`` decorator. These functions are designed to replace
6
+ xarray's interpolation for specific use cases where performance is critical.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from typing import Literal
12
+
13
+ import numpy as np
14
+ from numba import guvectorize
15
+
16
+
17
+ # Bounds mode constants (used internally by gufunc)
18
+ _BOUNDS_FILL = 0
19
+ _BOUNDS_CLAMP = 1
20
+ _BOUNDS_RAISE = 2
21
+
22
+
23
+ def _make_interp1d_gufunc():
24
+ """
25
+ Create the Numba gufunc for 1D linear interpolation.
26
+
27
+ Returns a gufunc with signature ``(n),(n),(m),(),(),()->(m)`` that performs
28
+ linear interpolation.
29
+
30
+ The function is created at module load time to ensure JIT compilation
31
+ happens only once.
32
+ """
33
+
34
+ @guvectorize(
35
+ [
36
+ "void(float32[:], float32[:], float32[:], int64, float32, float32, float32[:])",
37
+ "void(float64[:], float64[:], float64[:], int64, float64, float64, float64[:])",
38
+ ],
39
+ "(n),(n),(m),(),(),()->(m)",
40
+ nopython=True,
41
+ cache=True,
42
+ )
43
+ def _interp1d_gufunc_impl(x, y, xnew, bounds_mode, fill_lower, fill_upper, out):
44
+ """
45
+ Low-level gufunc for 1D linear interpolation.
46
+
47
+ Parameters
48
+ ----------
49
+ x : ndarray
50
+ X coordinates of the data points (must be sorted in ascending order).
51
+ Shape (n,).
52
+
53
+ y : ndarray
54
+ Y coordinates of the data points.
55
+ Shape (n,).
56
+
57
+ xnew : ndarray
58
+ X coordinates at which to evaluate the interpolation.
59
+ Shape (m,).
60
+
61
+
62
+ bounds_mode : int
63
+ Bounds handling mode:
64
+
65
+ * 0 (fill): use ``fill_lower``/``fill_upper`` for out-of-bounds
66
+ points;
67
+ * 1 (clamp): use nearest boundary value;
68
+ * 2 (raise): mark out-of-bounds with NaN for later validation.
69
+
70
+ fill_lower : float
71
+ Fill value for points below ``x[0]`` (only used when bounds_mode=0).
72
+
73
+ fill_upper : float
74
+ Fill value for points above ``x[-1]`` (only used when bounds_mode=0).
75
+
76
+ out : ndarray
77
+ Output array for interpolated values.
78
+ Shape (m,).
79
+ """
80
+ n = len(x)
81
+ m = len(xnew)
82
+
83
+ x_min = x[0]
84
+ x_max = x[n - 1]
85
+
86
+ for i in range(m):
87
+ xi = xnew[i]
88
+
89
+ # Handle NaN in query point
90
+ if np.isnan(xi):
91
+ out[i] = np.nan
92
+ continue
93
+
94
+ # Handle out-of-bounds: below minimum
95
+ if xi < x_min:
96
+ if bounds_mode == _BOUNDS_FILL:
97
+ out[i] = fill_lower
98
+ elif bounds_mode == _BOUNDS_CLAMP:
99
+ out[i] = y[0]
100
+ else: # bounds_mode == _BOUNDS_RAISE
101
+ out[i] = np.nan # Mark for validation in wrapper
102
+ continue
103
+
104
+ # Handle out-of-bounds: above maximum
105
+ if xi > x_max:
106
+ if bounds_mode == _BOUNDS_FILL:
107
+ out[i] = fill_upper
108
+ elif bounds_mode == _BOUNDS_CLAMP:
109
+ out[i] = y[n - 1]
110
+ else: # bounds_mode == _BOUNDS_RAISE
111
+ out[i] = np.nan # Mark for validation in wrapper
112
+ continue
113
+
114
+ # Binary search to find the interval [x[left], x[right]]
115
+ left = 0
116
+ right = n - 1
117
+
118
+ while right - left > 1:
119
+ mid = (left + right) // 2
120
+ if x[mid] <= xi:
121
+ left = mid
122
+ else:
123
+ right = mid
124
+
125
+ # Handle exact match at boundary to avoid numerical issues
126
+ if xi == x[left]:
127
+ out[i] = y[left]
128
+ continue
129
+ if xi == x[right]:
130
+ out[i] = y[right]
131
+ continue
132
+
133
+ # Linear interpolation: y0 + t * (y1 - y0) where t = (xi - x0) / (x1 - x0)
134
+ x0 = x[left]
135
+ x1 = x[right]
136
+ y0 = y[left]
137
+ y1 = y[right]
138
+
139
+ t = (xi - x0) / (x1 - x0)
140
+ out[i] = y0 + t * (y1 - y0)
141
+
142
+ return _interp1d_gufunc_impl
143
+
144
+
145
+ def _make_lerp_gufunc():
146
+ """
147
+ Create the numba gufunc for lerp with precomputed indices and weights.
148
+
149
+ Returns a gufunc with signature ``(n),(m),(m)->(m)``. The search step
150
+ is skipped entirely; the caller must supply the left-index and weight
151
+ arrays produced by :func:`lerp_indices`.
152
+ """
153
+
154
+ @guvectorize(
155
+ [
156
+ "void(float32[:], float32[:], float32[:], float32[:])",
157
+ "void(float64[:], float64[:], float64[:], float64[:])",
158
+ ],
159
+ "(n),(m),(m)->(m)",
160
+ nopython=True,
161
+ cache=True,
162
+ )
163
+ def _lerp_gufunc_impl(y, indices, weights, out):
164
+ m = len(indices)
165
+ for i in range(m):
166
+ left = int(indices[i])
167
+ t = weights[i]
168
+ out[i] = y[left] + t * (y[left + 1] - y[left])
169
+
170
+ return _lerp_gufunc_impl
171
+
172
+
173
+ # Create gufuncs at module load time
174
+ _interp1d_gufunc = _make_interp1d_gufunc()
175
+ _lerp_gufunc = _make_lerp_gufunc()
176
+
177
+
178
+ def interp1d(
179
+ x: np.ndarray,
180
+ y: np.ndarray,
181
+ xnew: np.ndarray,
182
+ bounds: Literal["fill", "clamp", "raise"] = "fill",
183
+ fill_value: float | tuple[float, float] = np.nan,
184
+ ) -> np.ndarray:
185
+ """
186
+ Fast 1D linear interpolation.
187
+
188
+ This function provides high-performance linear interpolation that
189
+ broadcasts over leading dimensions. It powers a drop-in replacement for
190
+ cases where xarray's interpolation is too slow.
191
+
192
+ Parameters
193
+ ----------
194
+ x : array-like
195
+ X coordinates of the data points. Must be sorted in ascending order
196
+ along the last axis. Results are undefined for unsorted x.
197
+ Shape (..., n).
198
+
199
+ y : array-like
200
+ Y coordinates of the data points.
201
+ Shape (..., n).
202
+
203
+ xnew : array-like
204
+ X coordinates at which to evaluate the interpolation.
205
+ Shape (..., m).
206
+
207
+ bounds : {"fill", "clamp", "raise"}, default: "fill"
208
+ How to handle out-of-bounds query points:
209
+
210
+ * ``"fill"``: use ``fill_value`` for points outside the data range.
211
+ * ``"clamp"``: use the nearest boundary value (``y[0]`` or ``y[-1]``).
212
+ * ``"raise"``: raise a ValueError if any query point is out of bounds.
213
+
214
+ fill_value : float or tuple of (float, float), default: np.nan
215
+ Value(s) to use for out-of-bounds points when ``bounds="fill"``:
216
+
217
+ * if a single float, use for both lower and upper bounds;
218
+ * if a 2-tuple, use (``fill_lower``, ``fill_upper``).
219
+
220
+ Returns
221
+ -------
222
+ ndarray
223
+ Interpolated values at the query points. The output shape is
224
+ determined by numpy broadcasting rules applied to x, y, and xnew.
225
+ Shape (..., m).
226
+
227
+ Raises
228
+ ------
229
+ ValueError
230
+ * If ``bounds="raise"`` and any query point is outside the data range.
231
+ * If ``bounds`` is not one of "fill", "clamp", or "raise".
232
+ * If ``fill_value`` is a tuple with length != 2.
233
+
234
+ Notes
235
+ -----
236
+ * The implementation uses a Numba gufunc with signature ``(n),(n),(m)->(m)``
237
+ for the core interpolation, enabling efficient broadcasting over arbitrary
238
+ leading dimensions.
239
+ * The function assumes ``x`` is sorted in ascending order along the last
240
+ axis. Results are undefined if this assumption is violated.
241
+ * NaN values in ``xnew`` are passed through (output will be NaN).
242
+
243
+ Examples
244
+ --------
245
+ Basic interpolation:
246
+
247
+ >>> x = np.array([0.0, 1.0, 2.0, 3.0])
248
+ >>> y = np.array([0.0, 1.0, 4.0, 9.0])
249
+ >>> xnew = np.array([0.5, 1.5, 2.5])
250
+ >>> interp1d(x, y, xnew)
251
+ array([0.5, 2.5, 6.5])
252
+
253
+ With fill values for out-of-bounds:
254
+
255
+ >>> xnew = np.array([-1.0, 1.5, 5.0])
256
+ >>> interp1d(x, y, xnew, bounds="fill", fill_value=(-999.0, 999.0))
257
+ array([-999. , 2.5, 999. ])
258
+
259
+ Clamping to boundary values:
260
+
261
+ >>> interp1d(x, y, xnew, bounds="clamp")
262
+ array([0. , 2.5, 9. ])
263
+
264
+ Broadcasting over multiple curves:
265
+
266
+ >>> x = np.array([0.0, 1.0, 2.0])
267
+ >>> y = np.array([[0.0, 1.0, 2.0], # Linear
268
+ ... [0.0, 1.0, 4.0]]) # Quadratic
269
+ >>> xnew = np.array([0.5, 1.5])
270
+ >>> interp1d(x, y, xnew)
271
+ array([[0.5, 1.5],
272
+ [0.5, 2.5]])
273
+ """
274
+ # Convert inputs to numpy arrays
275
+ x = np.asarray(x)
276
+ y = np.asarray(y)
277
+ xnew = np.asarray(xnew)
278
+
279
+ # Validate bounds mode
280
+ if bounds == "fill":
281
+ bounds_mode = _BOUNDS_FILL
282
+ elif bounds == "clamp":
283
+ bounds_mode = _BOUNDS_CLAMP
284
+ elif bounds == "raise":
285
+ bounds_mode = _BOUNDS_RAISE
286
+ else:
287
+ raise ValueError(
288
+ f"Invalid bounds mode: {bounds!r}. Must be one of 'fill', 'clamp', 'raise'."
289
+ )
290
+
291
+ # Parse fill_value
292
+ if isinstance(fill_value, tuple):
293
+ if len(fill_value) != 2:
294
+ raise ValueError(
295
+ f"fill_value tuple must have exactly 2 elements, got {len(fill_value)}"
296
+ )
297
+ fill_lower, fill_upper = fill_value
298
+ else:
299
+ fill_lower = fill_upper = fill_value
300
+
301
+ # Ensure float dtype (convert integers to float64)
302
+ if not np.issubdtype(x.dtype, np.floating):
303
+ x = x.astype(np.float64)
304
+ if not np.issubdtype(y.dtype, np.floating):
305
+ y = y.astype(np.float64)
306
+ if not np.issubdtype(xnew.dtype, np.floating):
307
+ xnew = xnew.astype(np.float64)
308
+
309
+ # Promote to common dtype
310
+ common_dtype = np.result_type(x, y, xnew)
311
+ if x.dtype != common_dtype:
312
+ x = x.astype(common_dtype)
313
+ if y.dtype != common_dtype:
314
+ y = y.astype(common_dtype)
315
+ if xnew.dtype != common_dtype:
316
+ xnew = xnew.astype(common_dtype)
317
+
318
+ # Convert fill values to the common dtype
319
+ fill_lower = common_dtype.type(fill_lower)
320
+ fill_upper = common_dtype.type(fill_upper)
321
+
322
+ # Pre-validate bounds="raise" mode
323
+ if bounds == "raise":
324
+ # Get valid (non-NaN) query points
325
+ xnew_flat = xnew.ravel()
326
+ xnew_valid = xnew_flat[~np.isnan(xnew_flat)]
327
+
328
+ if xnew_valid.size > 0:
329
+ # Get overall bounds of x (simplest and most robust approach)
330
+ x_min_val = np.min(x)
331
+ x_max_val = np.max(x)
332
+
333
+ # Check for violations
334
+ min_query = np.min(xnew_valid)
335
+ max_query = np.max(xnew_valid)
336
+
337
+ below = min_query < x_min_val
338
+ above = max_query > x_max_val
339
+
340
+ if below or above:
341
+ # Build error message
342
+ msg_parts = ["Query points out of bounds."]
343
+ if below:
344
+ delta_low = x_min_val - min_query
345
+ msg_parts.append(f"Below lower bound by up to {delta_low:.6g}")
346
+ if above:
347
+ delta_high = max_query - x_max_val
348
+ msg_parts.append(f"Above upper bound by up to {delta_high:.6g}")
349
+ raise ValueError(" ".join(msg_parts))
350
+
351
+ # Call the gufunc
352
+ result = _interp1d_gufunc(x, y, xnew, np.int64(bounds_mode), fill_lower, fill_upper)
353
+
354
+ return result
355
+
356
+
357
+ def lerp_indices(
358
+ x: np.ndarray,
359
+ xnew: np.ndarray,
360
+ bounds: Literal["fill", "clamp", "raise"] = "fill",
361
+ ) -> tuple[np.ndarray, np.ndarray]:
362
+ """
363
+ Precompute left-indices and interpolation weights for linear interpolation.
364
+
365
+ When the same query points ``xnew`` will be applied to many different
366
+ ``y`` arrays sharing the same ``x`` grid, it is far cheaper to run the
367
+ binary search once here and then call :func:`lerp` for each ``y``.
368
+ That function skips the search entirely and executes only the
369
+ ``y[left] + t*(y[left+1] - y[left])`` step.
370
+
371
+ Parameters
372
+ ----------
373
+ x : ndarray
374
+ Sorted coordinate grid (1-D).
375
+ Shape (n,).
376
+
377
+ xnew : ndarray
378
+ Query points (1-D).
379
+ Shape (m,).
380
+
381
+ bounds : {"fill", "clamp", "raise"}, default: "fill"
382
+ Out-of-bounds handling, same semantics as :func:`interp1d`.
383
+
384
+ * ``"fill"``: out-of-bounds indices are set to 0 with weight NaN so that
385
+ :func:`lerp` will produce NaN there. The caller can replace those
386
+ NaNs after the fact if a different fill value is needed.
387
+ * ``"clamp"``: out-of-bounds queries are clamped to the nearest boundary
388
+ index with weight 0 (reproducing ``y[0]`` or ``y[-1]``).
389
+ * ``"raise"``: raises immediately if any query is out of bounds.
390
+
391
+ Returns
392
+ -------
393
+ indices : ndarray
394
+ Left-bin indices as floats (required by the gufunc signature).
395
+ Shape (m,), dtype float64.
396
+
397
+ weights : ndarray
398
+ Fractional position within each bin: ``t = (xnew - x[i]) / (x[i+1] - x[i])``.
399
+ Shape (m,), dtype float64.
400
+
401
+ Raises
402
+ ------
403
+ ValueError
404
+ If ``bounds="raise"`` and any query point is outside ``[x[0], x[-1]]``.
405
+ """
406
+ x = np.asarray(x, dtype=np.float64)
407
+ xnew = np.asarray(xnew, dtype=np.float64)
408
+ n = len(x)
409
+
410
+ # searchsorted gives insertion point; left-bin index = insertion - 1
411
+ raw = np.searchsorted(x, xnew, side="right") - 1 # shape (m,)
412
+
413
+ if bounds == "raise":
414
+ # Check for out-of-bounds points
415
+ x_min_val = x[0]
416
+ x_max_val = x[-1]
417
+ min_query = xnew.min()
418
+ max_query = xnew.max()
419
+
420
+ below = min_query < x_min_val
421
+ above = max_query > x_max_val
422
+
423
+ if below or above:
424
+ # Build informative error message
425
+ msg_parts = ["Query points out of bounds."]
426
+ if below:
427
+ delta_low = x_min_val - min_query
428
+ msg_parts.append(f"Below lower bound by up to {delta_low:.6g}")
429
+ if above:
430
+ delta_high = max_query - x_max_val
431
+ msg_parts.append(f"Above upper bound by up to {delta_high:.6g}")
432
+ raise ValueError(" ".join(msg_parts))
433
+
434
+ # Clamp indices to valid bin range [0, n-2]
435
+ indices = np.clip(raw, 0, n - 2)
436
+
437
+ # Compute weights
438
+ x0 = x[indices]
439
+ x1 = x[indices + 1]
440
+ weights = (xnew - x0) / (x1 - x0)
441
+
442
+ if bounds == "clamp":
443
+ # For clamping, we need special handling for boundary points:
444
+ # - Points below x[0]: index=0, weight=0 -> y[0] + 0*(y[1]-y[0]) = y[0]
445
+ # - Points above x[-1]: index=n-2, weight=1 -> y[n-2] + 1*(y[n-1]-y[n-2]) = y[n-1]
446
+ # We use <= and >= (not < and >) to avoid numerical issues with exact
447
+ # boundary matches where floating-point arithmetic might produce tiny
448
+ # non-zero weights.
449
+ weights = np.where(xnew <= x[0], 0.0, weights)
450
+ weights = np.where(xnew >= x[-1], 1.0, weights)
451
+ elif bounds == "fill":
452
+ # Mark out-of-bounds with NaN weight so lerp produces NaN
453
+ oob = (xnew < x[0]) | (xnew > x[-1])
454
+ weights = np.where(oob, np.nan, weights)
455
+
456
+ return indices.astype(np.float64), weights
457
+
458
+
459
+ def lerp(y: np.ndarray, indices: np.ndarray, weights: np.ndarray) -> np.ndarray:
460
+ """
461
+ Linear interpolation using precomputed indices and weights.
462
+
463
+ This is the fast inner loop for the case where many ``y`` arrays share
464
+ the same ``x`` grid and query points. The binary search is done once
465
+ via :func:`lerp_indices`; this function executes only the linear
466
+ combination ``y[i] + t * (y[i+1] - y[i])``.
467
+
468
+ Parameters
469
+ ----------
470
+ y : ndarray
471
+ Data values. The last axis must correspond to the ``x`` grid used
472
+ in :func:`lerp_indices`. Broadcasting over leading dimensions is
473
+ handled by the underlying gufunc.
474
+ Shape (..., n).
475
+
476
+ indices : ndarray
477
+ Left-bin indices from :func:`lerp_indices`.
478
+ **IMPORTANT**: Indices must be in the range ``[0, n-2]`` where
479
+ ``n = y.shape[-1]``. This invariant is enforced by :func:`lerp_indices`
480
+ but is not validated here for performance reasons.
481
+ Shape (m,).
482
+
483
+ weights : ndarray
484
+ Interpolation weights from :func:`lerp_indices`.
485
+ Shape (m,).
486
+
487
+ Returns
488
+ -------
489
+ ndarray
490
+ Interpolated values. NaN weights (from ``bounds="fill"``) propagate
491
+ as NaN in the output.
492
+ Shape (..., m).
493
+
494
+ Notes
495
+ -----
496
+ This function does not perform bounds checking on ``indices`` for
497
+ performance. The caller must ensure indices are valid (in ``[0, n-2]``).
498
+ Using :func:`lerp_indices` guarantees this invariant.
499
+ """
500
+ y = np.asarray(y, dtype=np.float64)
501
+ indices = np.asarray(indices, dtype=np.float64)
502
+ weights = np.asarray(weights, dtype=np.float64)
503
+ return _lerp_gufunc(y, indices, weights)
File without changes
@@ -0,0 +1,77 @@
1
+ import pytest
2
+ import xarray as xr
3
+
4
+ from ..core import CKDAbsorptionDatabase, MonoAbsorptionDatabase
5
+
6
+
7
+ @pytest.fixture
8
+ def absorption_database_error_handler_config():
9
+ """
10
+ Error handler configuration for absorption coefficient interpolation.
11
+
12
+ Notes
13
+ -----
14
+ This configuration is chosen to ignore all interpolation issues (except
15
+ bounds error along the mole fraction dimension) because warnings are
16
+ captured by pytest which will raise.
17
+ Ignoring the bounds on pressure and temperature is safe because
18
+ out-of-bounds values usually correspond to locations in the atmosphere
19
+ that are so high that the contribution to the absorption coefficient
20
+ are negligible at these heights.
21
+ The bounds error for the 'x' (mole fraction) coordinate is considered
22
+ fatal.
23
+ """
24
+ return {
25
+ "p": {"missing": "raise", "scalar": "raise", "bounds": "ignore"},
26
+ "t": {"missing": "raise", "scalar": "raise", "bounds": "ignore"},
27
+ "x": {"missing": "ignore", "scalar": "ignore", "bounds": "raise"},
28
+ }
29
+
30
+
31
+ @pytest.fixture
32
+ def thermoprops_us_standard(shared_datadir):
33
+ """
34
+ This dataset is created with the following command::
35
+
36
+ joseki.make(
37
+ identifier="afgl_1986-us_standard",
38
+ z=np.linspace(0.0, 120.0, 121) * ureg.km,
39
+ additional_molecules=False,
40
+ )
41
+ """
42
+ yield xr.load_dataset(shared_datadir / "afgl_1986-us_standard.nc")
43
+
44
+
45
+ def _absdb(mode, path):
46
+ if mode == "mono":
47
+ return MonoAbsorptionDatabase.from_directory(
48
+ path / "nanomono_v1", lazy=True, fix=False
49
+ )
50
+ elif mode == "ckd":
51
+ return CKDAbsorptionDatabase.from_directory(
52
+ path / "nanockd_v1", lazy=False, fix=False
53
+ )
54
+ else:
55
+ raise RuntimeError
56
+
57
+
58
+ @pytest.fixture
59
+ def absdb(shared_datadir, request):
60
+ mode = request.param
61
+ _db = _absdb(mode, shared_datadir)
62
+ yield _db
63
+ _db.cache_clear()
64
+
65
+
66
+ @pytest.fixture
67
+ def absdb_mono(shared_datadir):
68
+ _db = _absdb("mono", shared_datadir)
69
+ yield _db
70
+ _db.cache_clear()
71
+
72
+
73
+ @pytest.fixture
74
+ def absdb_ckd(shared_datadir):
75
+ _db = _absdb("ckd", shared_datadir)
76
+ yield _db
77
+ _db.cache_clear()
@@ -1,16 +1,18 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: axsdb
3
- Version: 0.0.2
3
+ Version: 0.0.3
4
4
  Summary: An absorption database reader for the Eradiate radiative transfer model.
5
5
  Author-email: Vincent Leroy <vincent.leroy@rayference.eu>
6
6
  Requires-Python: >=3.9
7
7
  Requires-Dist: attrs
8
8
  Requires-Dist: cachetools
9
9
  Requires-Dist: netcdf4
10
+ Requires-Dist: numba<0.63,>=0.58; sys_platform == 'darwin' and platform_machine == 'x86_64'
11
+ Requires-Dist: numba>=0.58; sys_platform != 'darwin' or platform_machine != 'x86_64'
10
12
  Requires-Dist: pint
11
13
  Requires-Dist: scipy
12
14
  Requires-Dist: typer
13
- Requires-Dist: xarray
15
+ Requires-Dist: xarray>=2024.7.0
14
16
  Description-Content-Type: text/markdown
15
17
 
16
18
  # AxsDB — The Eradiate Absorption Cross-section Database Interface
@@ -21,10 +23,10 @@ Description-Content-Type: text/markdown
21
23
  [![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv)
22
24
  [![ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
23
25
 
24
- This library provides an interface to read and
25
- query [Eradiate](https://eradiate.eu)'s absorption databases.
26
+ This library provides an interface to read and query the absorption databases
27
+ of the [Eradiate radiative transfer model](https://eradiate.eu).
26
28
 
27
29
  ## License
28
30
 
29
- The Eradiate Absorption Database Reader is distributed under the terms of the
31
+ AxsDB is distributed under the terms of the
30
32
  [GNU Lesser General Public License v3.0](https://choosealicense.com/licenses/lgpl-3.0/).
@@ -0,0 +1,17 @@
1
+ axsdb/__init__.py,sha256=9-EMYK0zJ6dTDZxRvqxpoAwZYER6xHjhNRROhCDPqEs,666
2
+ axsdb/_version.py,sha256=zpfzURzIlh9ajjpvYv4uvkfd404RwJGzy_drTalmMm0,163
3
+ axsdb/cli.py,sha256=UUtmlrS1Y0y6TrXDZtjDY2xBeqPlH_RRSDdRQjv9_b4,1689
4
+ axsdb/core.py,sha256=qN8sEeg2gY7CRkc-hO01zmr3drkhQIb2prWxiEHhM9U,31371
5
+ axsdb/error.py,sha256=dmdurgFPkZxyutyWnYb85ddt7TV3jFw1etLln14boPQ,5621
6
+ axsdb/factory.py,sha256=NzJ0iC6DERD2SESDg4yBxqbjnwNCa99SxDdHwiiwp78,3731
7
+ axsdb/interpolation.py,sha256=PwvjiW6hId6A_iMm0tr0XXJwB-XZnLhL_lJoKz07hfM,30681
8
+ axsdb/math.py,sha256=dtuUqCyHMUAEnypJO1gEJUc63zTPhW9gXvUTIJydL6s,16448
9
+ axsdb/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ axsdb/typing.py,sha256=Aol4ouIrfrUs-HBBgblW6Iv-xJDSpFe1RxWCQj8QDFs,94
11
+ axsdb/units.py,sha256=XxbcC-dXiXd8ZkB3cBtd3NTT8zSEd1g8FGQUr5-nrhQ,1762
12
+ axsdb/testing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ axsdb/testing/fixtures.py,sha256=WPByh5M_tAtFOA8rdTLSDulaK6lWTPRRVpbgsB1dxoU,2181
14
+ axsdb-0.0.3.dist-info/METADATA,sha256=-DQ8TDRgTvu3m0uqstNL-QFsEc9RbEV-sdxLflUfF70,1644
15
+ axsdb-0.0.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
16
+ axsdb-0.0.3.dist-info/entry_points.txt,sha256=4OnXeexRQs2Bl3T4F_jwijW6hPb6vOsIcQAECTsnHUE,41
17
+ axsdb-0.0.3.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
2
+ Generator: hatchling 1.28.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,13 +0,0 @@
1
- axsdb/__init__.py,sha256=9-EMYK0zJ6dTDZxRvqxpoAwZYER6xHjhNRROhCDPqEs,666
2
- axsdb/_version.py,sha256=zpfzURzIlh9ajjpvYv4uvkfd404RwJGzy_drTalmMm0,163
3
- axsdb/cli.py,sha256=UUtmlrS1Y0y6TrXDZtjDY2xBeqPlH_RRSDdRQjv9_b4,1689
4
- axsdb/core.py,sha256=_FXP-gio8yTGvYEuiOjWCizi6OV8r9WdI0UNo9Kqk3U,31343
5
- axsdb/error.py,sha256=dmdurgFPkZxyutyWnYb85ddt7TV3jFw1etLln14boPQ,5621
6
- axsdb/factory.py,sha256=Pr8kxQ51hgQY8wBCuMy7sarHT5ORmaRYhFWiu5styhk,3710
7
- axsdb/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- axsdb/typing.py,sha256=Aol4ouIrfrUs-HBBgblW6Iv-xJDSpFe1RxWCQj8QDFs,94
9
- axsdb/units.py,sha256=XxbcC-dXiXd8ZkB3cBtd3NTT8zSEd1g8FGQUr5-nrhQ,1762
10
- axsdb-0.0.2.dist-info/METADATA,sha256=CN4Ek0lbULDF88DFdZnXDni-oquWTsGdAkU6L_TQ-JQ,1457
11
- axsdb-0.0.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
12
- axsdb-0.0.2.dist-info/entry_points.txt,sha256=4OnXeexRQs2Bl3T4F_jwijW6hPb6vOsIcQAECTsnHUE,41
13
- axsdb-0.0.2.dist-info/RECORD,,