# Crate Map
The workspace is organised into seven crates with clear dependency
boundaries. The key design principle is that **cal-core has no I/O
dependencies** — it operates purely on in-memory arrays.
## Dependency Graph
```{eval-rst}
.. mermaid::
graph BT
schema[cal-schema
L0/L1 constants]
core[cal-core
pure math]
io[cal-io
Zarr I/O + pipeline]
cli[cal-cli
calibrate binary]
py[cal-py
Python bindings]
zfc[zarr-fits-core
FITS→Zarr library]
zfcli[zarr-fits-cli
zarr-fits binary]
core --> schema
io --> core
io --> schema
io -.->|fits-ingest| zfc
cli --> io
cli --> core
cli -.->|fits-ingest| zfc
py --> io
py --> core
zfc --> schema
zfcli --> zfc
style schema fill:#e8eaf6
style core fill:#e3f2fd
style io fill:#e8f5e9
style zfc fill:#fff3e0
```
Dashed arrows indicate optional dependencies gated by the
`fits-ingest` feature flag.
## Crate Details
### cal-schema
Zero-dependency crate containing string constants for all L0 and L1
array names, group names, and schema versions. Both the writer
(zarr-fits-core) and reader (cal-io) depend on this single source of
truth, preventing schema drift.
**Key file:** `crates/cal-schema/src/lib.rs`
### cal-core
Pure calibration math with no filesystem access. Dependencies are
limited to `ndarray`, `rayon`, `serde`, `nalgebra`, and
`levenberg-marquardt`.
**Modules:**
- `math/` — Rayleigh-Jeans, gamma, temperature, transmission,
dump averaging; includes `airmass_from_elevation()` in `mod.rs`
and `zenith_tau()` in `transmission.rs`
- `atm/` — `AtmosphericModel` trait + `TableAtm` interpolation
- `pwv/` — PWV fitting (grid, LM, external); frequency-adaptive
sigma weighting
- `modes/` — observation mode preparation via a single `prepare.rs`
(contains `prepare()` dispatcher for TP, OTF, OTF-chopped)
- `calibrate.rs` — vectorized `calibrate_full()` — the **only**
calibration function
- `atmosphere.rs` — atmosphere determination orchestration
(full, per-pixel, per-pair)
- `scan/` — core data types, split into submodules:
- `scan_data.rs` — `ScanData`
- `cal_snapshot.rs` — `CalibrationSnapshot` (with
`find_hot_cold()` method)
- `cal_load.rs` — `CalibrationLoad`, `CalibrationLoadFull`
(constructors take `&CalibrationParams`)
- `cal_result.rs` — `CalibrationResult`, `effective_instmode()`
- `config.rs` — `CalibrationConfig`, `CalibrationParams`,
`ObsMode`, `PhysicsConstants` (default: `KalibrateCompat`)
- `gain.rs` — gain interpolation (levels 0, 2, 3)
- `flags.rs` — u16 bitmask constants and utilities
- `qa.rs` — quality metrics
### cal-io
Zarr I/O via the `zarrs` crate (sync API only, no async runtime)
and pipeline orchestration.
**Modules:**
- `raw.rs` — L0 Zarr reader (5D data, metadata, coordinates)
- `calibrated.rs` — L1 Zarr writer (with provenance metadata)
- `resolve.rs` — bridges raw L0 into `CalibrationLoad(Full)`
- `pipeline/` — store-level orchestration, split into submodules:
- `mod.rs` — `calibrate_store()` entry point, prefetch thread
- `scan.rs` — `process_prefetched_scan()`, `ProvenanceContext`
- `linked_pwv.rs` — multi-frontend linked PWV pooling
- `dbs.rs` — DBS (dual-beam-switch) pair merging
- `wrappers.rs` — convenience wrappers: `calibrate_single_scan()`,
`find_next_cal_scan()` (public), `common_pwv_source()`
- `provenance.rs` — L1 provenance metadata (L2 to L0 traceability)
- `skydiff.rs` — SKYDIFF diagnostic history
- `atm_table.rs` — text ATM table parser
- `atm_table_bin.rs` — binary ATM table I/O
- `schema.rs` — L1 schema helpers
- `ingest.rs` — FITS ingest (behind `fits-ingest` feature)
### cal-cli
Thin binary wrapping cal-io's pipeline with clap argument parsing,
indicatif progress bar, and manifest mode. Contains zero calibration
logic.
**Binary name:** `calibrate`
### cal-py
Thin PyO3 surface exposing `calibrate_store()`,
`calibrate_scan_py()`, `list_scans()`, `version()`, and
optionally `convert_fits_folder()` to Python. Contains zero logic
— all work is dispatched to cal-core and cal-io. Uses the
**vectorized path** (`calibrate_full`) for all calibration, same
as the CLI.
### zarr-fits-core
FITS→Zarr v3 conversion library. Reads individual FITS files via
cfitsio, groups by scan, writes Zarr v3 stores with streaming
per-subscan memory.
### zarr-fits-cli
Standalone `zarr-fits` binary for FITS conversion without
calibration.
## Feature Flags
```{eval-rst}
.. list-table::
:header-rows: 1
:widths: 20 20 60
* - Feature
- Crate
- Effect
* - ``fits-ingest``
- cal-io
- Adds ``pub mod ingest`` wrapping zarr-fits-core
* - ``fits-ingest``
- cal-cli
- Enables ``-f``/``--fits-folder`` and ``--zarr-cache`` flags
* - ``fits-ingest``
- cal-py
- Exposes ``convert_fits_folder()`` to Python
```
Without `fits-ingest`, the workspace has **zero C library
dependencies** — everything compiles with a standard Rust toolchain.