Missing, unknown and not-applicable values¶
The model treats missing, unknown and not-applicable as conceptually different, and never replaces an unknown scientific value with a default.
Two levels of absence¶
Field level. Optional metadata fields default to None, meaning the datum
is structurally absent from the model (for example ExperimentMetadata.operator
when no operator was recorded).
Value level. Where the distinction matters scientifically, use a Quantity
whose status records it explicitly:
| Meaning | ValueStatus |
Quantity constructor |
|---|---|---|
| A real value is present | PRESENT |
Quantity.of(value, unit) |
| The source did not provide it | MISSING |
Quantity.missing() |
| The source explicitly says unknown | UNKNOWN |
Quantity.unknown() |
| It does not apply to this experiment | NOT_APPLICABLE |
Quantity.not_applicable() |
A PRESENT quantity must carry a finite value; every other status must carry
None. These invariants are enforced at construction.
from openauc.models import Quantity, ValueStatus
Quantity.unknown().status # ValueStatus.UNKNOWN — not the same as MISSING
Quantity.not_applicable().value # None
Quantity.of(20.0, Unit.DEGREE_CELSIUS).is_present # True
Value provenance¶
Each Quantity also records where it came from via ValueProvenance
(SUPPLIED, CONVERTED, INFERRED, USER_CONFIRMED, UNKNOWN). This
per-value tag complements the experiment-level ImportProvenance record, which
lists values by category (supplied/converted/inferred/user-confirmed/unknown)
alongside the source, checksum, parser and any transformations, warnings and
assumptions.
openauc.load populates this record on import; it may also be constructed by
hand for synthetic experiments. No checksum is computed — sha256 is left
None, because checksum computation is deferred to the AUCX phase
(ADR-0003). See
ADR-0002.
How absence is reported¶
Absent values are never replaced by defaults and never make an experiment
invalid. experiment.validate() reports each absence explicitly as a warning or
informational finding, and experiment.summary_data().metadata_presence counts
present / missing / unknown / not_applicable / absent separately, so
the distinctions above survive all the way into a summary. See
validation tiers.
Observations: the authoritative mask¶
In per-scan mode, shorter scans are padded with NaN. Padding is not
identified by testing for NaN — a measured signal could itself legitimately be
recorded as NaN. Instead a boolean validity mask is authoritative: a value
is a real observation if and only if its mask entry is True. This keeps
missing positions distinguishable from measured values, and no interpolation is
ever performed to fill them.