Uncertainties

Uncertain carries an asymmetric error and an ENSDF operator, because the files are full of both: 5.62 +13-9, LT 3.2, GE 5.7, SY.

Propagation

The two error branches stay separate rather than being symmetrised:

multiply(Uncertain(5.62, 0.13, 0.09), x)   # stays +0.26/-0.18
divide(a, b)                               # denominator branches swap over
add(*terms)                                # assumes independence

Limits combine by direction. An upper bound times an upper bound is an upper bound; strictness is contagious (LT × LELT); an upper times a lower bounds nothing and returns an explicit unknown rather than a plausible-looking number. Quality flags degrade to the weakest claim (AP × SYSY).

Blank uncertainties

A blank ENSDF uncertainty propagates as exact, which is the convention for fields like a normalisation of exactly 1.0. It can also mean “not quoted”, in which case the result looks better determined than it is:

multiply(Uncertain(10.0), Uncertain(2.0, 0.2, 0.2))              # 20 +/- 2
multiply(Uncertain(10.0), Uncertain(2.0, 0.2, 0.2), strict=True) # 20, unknown
Uncertain(10.0).uncertainty_known                                # False

Correlations

Every intensity in a decay dataset is scaled by the same NR * BR, so those errors are fully correlated and per-term quadrature understates the total — by 39% on the 180Lu β⁻ dataset’s 33 gammas.

Rather than track correlations, the totals sum first and scale once, which is exact:

scheme.total_photon_intensity()   # (sum RI) * NR * BR
scheme.total_feeding(kind="B")    # (sum IB) * NB * BR

Why not the uncertainties package

It isn’t a drop-in: ufloat carries a single scalar std_dev, so it cannot represent asymmetric errors or limit operators, and both are ubiquitous in ENSDF. ufloat(5.62, (0.13, 0.09)) is a TypeError.

Its correlation tracking is better than anything here, so there is an opt-in bridge (pip install nook[uncertainties]):

level.energy.to_ufloat()   # symmetrises; raises on a limit rather than lying