nook.quantities

Parsing of ENSDF-flavoured physical quantities.

This is the part of the package that actually earns its keep: ENSDF encodes uncertainties in a significant-digit convention, spin-parity as a small grammar of alternatives and limits, and half-lives either as times or as level widths. Everything here is pure, stdlib-only and unit-testable.

nook.quantities.HBAR_LN2_MEV_S = 4.5623797e-22

hbar * ln(2) in MeV.s – converts a level width Gamma to a half-life.

class nook.quantities.Uncertain(value, plus=None, minus=None, operator=None, raw='')[source]

Bases: object

A value with an (optionally asymmetric) uncertainty.

operator carries ENSDF limit flags such as AP (approximately), GT/LT (greater/less than). When an operator is present the uncertainty is usually absent and value should be read as a bound.

value: float | None
plus: float | None = None
minus: float | None = None
operator: str | None = None
raw: str = ''
property symmetric: float | None

A single symmetrised uncertainty, or None if unknown.

property uncertainty_known: bool

False when the evaluator quoted no uncertainty at all.

property is_limit: bool
to_ufloat()[source]

Convert to an uncertainties.ufloat (optional dependency).

Use this when you want that package’s correlation tracking. It is lossy in both directions ENSDF cares about: ufloat carries a single scalar standard deviation, so asymmetric errors are symmetrised and limit operators are dropped. Both are reported.

nook.quantities.parse_value_with_uncertainty(value_text, unc_text='')[source]

Parse an ENSDF value/uncertainty pair.

The uncertainty is expressed in units of the last significant digit of the value, so ("12.1", "11") means 12.1 +/- 1.1. Asymmetric uncertainties are written "+5-3". Leading operators may sit in either field.

nook.quantities.multiply(a, b, strict=False)[source]

Product of two values, each error branch propagated independently.

Relative errors add in quadrature per side, which assumes independence – see DecayScheme.total_feeding() for the correlated case.

nook.quantities.divide(a, b, strict=False)[source]

Quotient of two values, with the denominator’s error branches swapped.

nook.quantities.add(*terms, strict=False)[source]

Sum of independent values, each error branch in quadrature.

Independence is assumed. Summing quantities that share a common scale factor – every intensity in one decay dataset shares NR and BR – violates that; sum the unscaled values and apply the factor once.

class nook.quantities.JPi(two_j, parity=None)[source]

Bases: object

A single spin-parity assignment.

Spin is stored as two_j so that half-integer values stay exact. parity is +1, -1 or None when unassigned.

two_j: int | None
parity: int | None = None
property j: Fraction | None
property j_float: float | None
class nook.quantities.SpinParity(candidates=(), excluded=(), tentative=False, assumed=False, constraint=None, natural_parity=None, raw='')[source]

Bases: object

An ENSDF J field (manual V.20) – a small language, not a number.

Two traps, both silent when got wrong:

  • a parity attaches only where written, except after a closing parenthesis, which covers the group: (3,4)- is 3- or 4-, but 1,2+ is “J=1 with unknown parity, or 2+”;

  • NOT inverts the statement, so those values go to excluded and must never be reported as the assignment.

candidates: tuple[JPi, ...] = ()
excluded: tuple[JPi, ...] = ()

Values the evaluator ruled out with NOT.

tentative: bool = False
assumed: bool = False
constraint: str | None = None
natural_parity: bool | None = None

True for NATURAL parity (pi = (-1)**J), False for UNNATURAL.

raw: str = ''
property unique: JPi | None

The assignment when it is unambiguous, else None.

property is_known: bool
allows(two_j, parity=None)[source]

Whether the field permits this spin-parity.

Honours NOT exclusions and NATURAL/UNNATURAL parity, neither of which appear in candidates.

matches(two_j=None, parity=None)[source]

True if any candidate is compatible with the given quantum numbers.

nook.quantities.parse_spin_parity(text)[source]

Parse an ENSDF J field into a SpinParity.

A parity is attached to a value only where the file attaches it – there is no inheritance across a comma. 1,2+ is “J=1 with unknown parity, or 2+”, not “1+ or 2+”. The one exception is a parity written after a closing parenthesis, which the manual defines as applying to the group.

class nook.quantities.HalfLife(seconds=<factory>, stable=False, from_width=False, width_mev=None, raw='')[source]

Bases: object

A level half-life, possibly given as a width or as STABLE.

seconds: Uncertain
stable: bool = False
from_width: bool = False
width_mev: Uncertain | None = None
raw: str = ''
property is_known: bool
nook.quantities.parse_half_life(text, unc_text='')[source]

Parse an ENSDF T1/2 field, e.g. "12.1 H", "3.2 KEV".

Widths are converted to half-lives through T = hbar ln2 / Gamma.