This file provides guidance to coding agents when working with code in this repository.
AxsDB is the reader and query interface for the absorption coefficient
databases used by the Eradiate radiative transfer model. It supports two
spectral representations: monochromatic and CKD (correlated-k distribution).
The package uses a src/ layout and is managed with uv.
Dependencies are managed with uv, so there is no separate virtual
environment to activate — prefix commands with uv run and uv will resolve
the environment automatically.
uv sync --group dev # install all dev dependencies (test+docs+benchmark)
uv run pytest # run the full test suite (also runs doctests)
uv run pytest tests/test_core.py # run a single test file
uv run pytest tests/test_core.py::test_name # run a single test
uv run pytest --cov=src # run tests with coverage
uv run task test-cov-report # coverage with HTML report (taskipy)
uv run task docs # build the Sphinx docs (docs/_build/html)
uv run task docs-serve # live-reload docs server
uv run task benchmark # run the pytest-benchmark suite (benchmarks/)
uv run ruff check . # lint
uv run ruff format . # format
pre-commit run --all-files # ruff, taplo (TOML), nbstripout, uv-exportThe pytest configuration lives in pyproject.toml under
[tool.pytest.ini_options]. Note that testpaths covers tests/, src/ and
docs/, and doctest execution (--doctest-plus, --doctest-glob='*.rst') is
enabled globally — this means the docstring examples embedded in
src/axsdb/*.py are collected and run as part of the test suite, so keep
them accurate and runnable when editing docstrings.
CI, defined in .github/workflows/ci.yml, runs the full test matrix
(Python 3.9–3.14 across Linux, macOS and Windows) via
uv run coverage run -m pytest, then combines the per-OS coverage data in a
follow-up job. The path mapping used to combine coverage collected on
different OSes is configured in [tool.coverage.paths] in pyproject.toml —
keep it in sync if the CI runner's checkout paths ever change.
Everything lives in src/axsdb/, a small, flat module layout:
-
core.pyholds the core abstraction.AbsorptionDatabaseis anattrsclass that holds a file index (_index, sorted by ascending wavelength) and a spectral coverage table (_spectral_coverage). Both are built lazily from the NetCDF files found in a directory and then cached toindex.csv/spectral.csvalongside the data, so subsequent loads skip the expensive rebuild (seefrom_directory;fix=Trueregenerates missing index files on the fly).MonoAbsorptionDatabaseandCKDAbsorptionDatabasesubclass it and each implement_make_indexpluseval_sigma_a_mono/eval_sigma_a_ckd— the CKD variant additionally carries agquadrature-point dimension. Spectral lookup (lookup_filenames/lookup_datasets) maps a wavelength or wavenumber query onto the file(s) that cover it, using a precomputed digitize-able mesh stored in_chunks. Loaded dataset objects are LRU-cached (cachetools, default size 8), and can be opened either lazily or eagerly (xr.open_datasetvs.xr.load_dataset) via thelazyflag. -
error.pyimplements configurable error handling for interpolation out-of-bounds and missing-coordinate conditions.ErrorHandlingConfigurationholds oneErrorHandlingPolicyper coordinate kind (xfor species concentration,pfor pressure,tfor temperature); each policy hasmissing/scalaractions (ignore/warn/raise) plus an asymmetricboundspair ofBoundsPolicyobjects, each with its own action, mode (fill/clamp) and fill value. All of this funnels through.convert()classmethods that accept partial dicts, bare strings, numbers, or already-built instances interchangeably — this is what lets a caller pass something as terse asbounds="clamp"or as detailed as{"lower": {...}, "upper": {...}}. A process-global default configuration is read and written viaget_error_handling_config()andset_error_handling_config(); the built-in default, defined at the bottom oferror.py, ignores pressure/temperature bound errors (physically, out-of-bounds usually just means high altitude, where absorption is negligible anyway) while raising on missing or out-of-bounds species concentrations. -
interpolation.pyprovides Numba-accelerated multi-dimensional interpolation over the thermophysical profile dimensions (pressure, temperature, mole fractions). It's used byAbsorptionDatabase._interp_thermophysicalto interpolate a data array read from a database file onto the thermophysical profile supplied by the caller. -
factory.pyprovidesAbsorptionDatabaseFactory, a simple name →RegistryEntryregistry (name, database class, a path or a path-resolving callable, and default constructor kwargs). This lets a caller such as Eradiate register known databases once at startup and then instantiate them by name via.create(), without hardcoding filesystem paths throughout the codebase. -
units.pyprovides Pint unit-registry access and quantity-conversion helpers used throughout the package for wavelength/wavenumber and dimensionless conversions. Databases may be indexed in either length or inverse-length units; the unit is auto-detected from each NetCDF file'swcoordinate. -
cli.pywraps atyperCLI, exposed as theaxsdbentry point. It currently has a single command,axsdb check <path> -m {mono,ckd} [--fix], which simply runsfrom_directoryfor its validation side effects (building or fixing missing index files).
Data model. A database directory holds one or more NetCDF data files,
plus an index.csv (per-file spectral bounds), a spectral.csv (unrolled
per-point spectral coverage) and an optional metadata.json. Monochromatic
files have a single w (spectral coordinate) dimension; CKD files
additionally carry a g (quadrature point) dimension and a wbounds
variable giving spectral bin edges. Both _make_index implementations
detect whether w is stored in wavelength or wavenumber units and normalize
the index to hold both.
Known open items are tracked in TODO.md: a review of the exception
hierarchy, missing integration tests for per-bound error handling, and a
handful of interpolation performance ideas (parallel dimension groups, Numba
parallel gufuncs, more aggressive compiled-function caching, additional
interpolation methods).