imap-processing is the science data processing pipeline for NASA's IMAP mission,
run by the Science Operations Center (SOC) at LASP. It turns raw CCSDS telemetry
packets into ISTP-compliant CDF science products, one instrument and one data level
at a time.
- A high level overview of the mission lives in docs/source/mission-overview.rst. This document is not needed for software development, but provides a helpful reference for connecting together instrument-spanning questions or ideas.
- Every product is produced by a single invocation of
imap_clifor one(instrument, data-level, descriptor, start-date)combination. See imap_processing/cli.py. - imap_processing/cli.py is the only entry point. It defines an abstract
ProcessInstrumentbase class and one subclass per instrument (Codice,Glows,Hi,Hit,Idex,Lo,Mag,Spacecraft,Swapi,Swe,Ultra). Each subclass implementsdo_processing(dependencies) -> list[xr.Dataset]; the base class handles downloading dependencies and writing the returned datasets to CDF. Adding a new data level means updating bothPROCESSING_LEVELSin imap_processing/init.py and the relevantdo_processingbranch. - Instrument subpackages live at
imap_processing/<instrument>/. Two layouts are in use — flat modules (hi/hi_l1a.py,codice/codice_l1a.py) and level subpackages (mag/l1a/mag_l1a.py,swe/l1b/swe_l1b.py). Follow whichever the instrument already uses; do not restructure. - Data flows as
xarray.Datasetobjects throughout. Processing functions take dependencies (animap_data_access.ProcessingInputCollectionor file paths) and returnxr.Dataset/list[xr.Dataset]. They should not write files themselves — the CLI does that.
-
Algorithm behavior is documented per-instrument in docs/source/algorithm-code-documentation/. Check there before changing science logic.
Some instruments have a full working reference distilled from their algorithm document — a product inventory, the algorithms with equations, and an implementation-status page listing deviations and gaps. Read the instrument's page set before proposing or estimating work on it.
- CoDICE: docs/source/algorithm-code-documentation/codice/index.rst
- GLOWS: docs/source/algorithm-code-documentation/glows/index.rst
- HIT: docs/source/algorithm-code-documentation/hit/index.rst
- IDEX: docs/source/algorithm-code-documentation/idex/index.rst
- MAG: docs/source/algorithm-code-documentation/mag/index.rst
- SWAPI: docs/source/algorithm-code-documentation/swapi/index.rst
- SWE: docs/source/algorithm-code-documentation/swe/index.rst
Many of these pages are unreviewed AI-generated drafts. They contain the line
.. include:: /algorithm-code-documentation/_ai_generated_notice.incjust below the page title (this renders as a warning banner). Treat a page with that line as a strong starting point, not as an authority:- When a marked page disagrees with the code, do not assume the code is wrong. The algorithm document or CMAD may be out of date, or the deviation may be intentional. Point out the discrepancy and let a human decide; don't "fix" code to match the page.
- When you cite a marked page in a PR, review, or answer, say that it is unverified.
- Only a person who has verified the page against the code or with the instrument team removes the include line. Never remove it yourself unless that person asks you to. When you edit a marked page, keep the marker.
- As the pages are reviewed by humans to correct discrepancies, they will be added into sphinx docs. Currently, the sphinx documentation only points to autogenerated docs from the code.
- The ENAs (Lo, Hi, and Ultra) will be included in more detail at a later date to this documentation, when their algorithm documents are finalized.
| Need | Use |
|---|---|
| Decommutate CCSDS packets | packet_file_to_datasets() in imap_processing/utils.py |
| Raw DN → engineering units | convert_raw_to_eu() in imap_processing/utils.py |
| CDF read/write | load_cdf() / write_cdf() in imap_processing/cdf/utils.py |
| CDF global/variable attributes | ImapCdfAttributes in imap_processing/cdf/imap_cdf_manager.py |
| Time conversion (MET / ET / TT-J2000 ns) | imap_processing/spice/time.py — never hand-roll epoch math |
| Spin, repointing, pointing frames, geometry | imap_processing/spice/ |
| Data quality bitflags | imap_processing/quality_flags.py |
| ENA sky maps / pointing sets | imap_processing/ena_maps/ena_maps.py |
| Combining time-varying ancillary files | imap_processing/ancillary/ancillary_dataset_combiner.py |
Packet structures are XTCE XML files in imap_processing/<instrument>/packet_definitions/.
They are generated as a first pass from the instrument team's telemetry spreadsheet via
imap_xtce <spreadsheet.xlsx> --output <file.xml> (imap_processing/ccsds/excel_to_xtce.py)
and then hand-refined. Decommutation is always done through packet_file_to_datasets(),
which returns dict[apid, xr.Dataset]; instrument code dispatches on APID enums
defined in the instrument's constants.py.
- Add/extend the YAML attribute configs in imap_processing/cdf/config/:
imap_<instrument>_global_cdf_attrs.yamlandimap_<instrument>_<level>_variable_attrs.yaml. - Load them with
ImapCdfAttributes.add_instrument_global_attrs()and.add_instrument_variable_attrs()and apply them todataset.attrsand each variable's.attrs. - Set
Logical_source = "imap_<instrument>_<level>_<descriptor>"andData_version.write_cdf()derives the output filename from these, so a typo here produces a wrongly-named file rather than an error. - Missing ISTP attributes surface as
cdflibISTPErrorat write time — test by actually callingwrite_cdf()in a test, as imap_processing/tests/hi/test_hi_l1a.py does.
Poetry 2.x with the dynamic-versioning plugin. Full setup instructions, including the
required system libraries (libnetcdf, libhdf5, and openblas/gfortran on 3.14+), are
in docs/source/development/getting-started.rst.
poetry install --extras "test,dev,doc" # or --all-extras
pre-commit install # do this once
poetry run pytest -vvv -n auto -m "not external_kernel and not external_test_data"
poetry run pytest imap_processing/tests/hi/test_hi_l1a.py::test_sci_de_decom -vvv
pre-commit run --all-files # ruff check+format, mypy, numpydoc, codespell
make -C docs html SPHINXOPTS="-W --keep-going"- Default to the
-m "not external_kernel and not external_test_data"selection. Those markers trigger multi-hundred-MB downloads of SPICE kernels from NAIF and test data from the SDC; assume the sandbox has no network access unless told otherwise. - Tests live in imap_processing/tests/, mirroring the instrument packages.
Global fixtures (SPICE kernels, metakernels, fake spin/repoint data, temp
DATA_DIR) are in imap_processing/tests/conftest.py; instrument-specific fixtures in each subdirectory'sconftest.py. Prefer existing fixtures over new ad-hoc setup. - Codecov requires ~90% patch coverage, so new code needs tests.
- Ruff (
ruff check/ruff format) and mypy (strict = true, tests excluded) are enforced in pre-commit and CI. Configuration lives in pyproject.toml. - numpydoc docstrings are validated (
numpydoc-validationhook) on all non-test code: summary,Parameters,Returnswith types are effectively mandatory. Tests are exempt from docstring rules (Dis ignored under*/tests/*). - Modern type hints everywhere:
str | Path,list[xr.Dataset],npt.NDArray. Target version ispy310. logger = logging.getLogger(__name__)— neverprint().- Style details (naming, imports, PR checklist, review standards) are documented in docs/source/development/git-workflow-and-style-guide/index.rst — follow it rather than inventing conventions.
- Pre-commit blocks direct commits to
mainanddev, and files over 1000 KB. Work on a feature branch; test data is downloaded at runtime, never committed (no git-lfs). - CI runs the suite on Linux/macOS/Windows × Python 3.10–3.14, so avoid platform-specific paths and version-specific syntax.