Units
Canonical internal units
All pure kernels (decay.py, graph.py, _solver.py) operate on plain
Python/NumPy floats in:
| Quantity | Unit |
|---|---|
| time | seconds |
| atom count | atoms (dimensionless count) |
| activity | becquerel (Bq) |
Plain-float convention at the API
When you pass plain numbers (no pint Quantity, no unit string on unit-typed args):
tis secondsAis BqNis atoms
pint parsing rules
- Strings are accepted only for unit-typed convenience arguments:
t/timeandhalf_life(e.g."8.02 days","24 hours"). N0,A0, andn0values must be numbers or pint Quantities. A string there raisesUnitError(deliberate: these are primary numeric inputs, not unit annotations).- Dimensionally wrong inputs (e.g.
"5 meters"as time) raiseUnitError. - Negative time raises
InvalidTimeError; non-positive / non-finite half-life raisesInvalidHalfLifeError.
Return-kind mirroring rule
Exact wording from the design spec §5: each result mirrors the kind of the primary numeric input it derives from —
decayed_atomsmirrorsN0decayed_activitymirrorsA0Nuclide.activityandDecayChain.at/.activitymirrorn0(default: plain floats)- Quantity in → Quantity out (same kind of unit); float in → float out
- String inputs on
t/half_lifenever change output kind remaining_fractionis dimensionless and always returns float
Conversions
| Conversion | Rule |
|---|---|
| Bq ↔ Ci | 1 Ci = 3.7e10 Bq exact (NIST SP 811) |
| atoms ↔ grams | N = (mass_g / M) * N_A, N_A = 6.02214076e23 exact (2019 SI, BIPM) |
custom atom unit |
dimensionless counting unit on the shared pint registry |
Helpers live in pydecay.units: to_seconds, to_half_life_seconds,
to_float, mirror_quantity, atoms_to_grams, grams_to_atoms,
bq_to_ci, ci_to_bq.
Why not a full units library in the core
Pure kernels stay testable and dependency-light; pint is confined to the API edge. Every call down into the pure modules passes canonical floats, so unit handling bugs cannot corrupt the math layer.