ENSDF format notes

What the parsers decode, and the quirks that cost time to discover. Field and section numbers refer to the ENSDF manual (BNL-NCS-51655).

Values and uncertainties

Uncertainties follow the significant-digit convention (V.11), including exponents and asymmetric errors:

parse_value_with_uncertainty("1368.672", "5").symmetric   # 0.005
parse_value_with_uncertainty("1.2E+3", "3").symmetric     # 300.0
parse_value_with_uncertainty("5.62", "+13-9")             # +0.13 / -0.09
parse_value_with_uncertainty("LT 3.0", "").is_limit       # True

Spin and parity (V.20)

The J field is a small language, not a number. Every alternative is kept, and why an assignment is soft is recorded:

parse_spin_parity("3/2-").unique          # JPi(two_j=3, parity=-1)
parse_spin_parity("(2+)").tentative       # weak arguments
parse_spin_parity("[0+]").assumed         # from theory
parse_spin_parity("1,2+").candidates      # 1 (no parity), 2+
parse_spin_parity("(3,4)-").candidates    # 3-, 4-
parse_spin_parity("NOT 3-").excluded      # 3- is ruled out
parse_spin_parity("NATURAL").natural_parity
parse_spin_parity("GE 4").constraint      # 'GE'

Two rules are easy to get wrong, and getting them wrong is silent:

  • A parity attaches only where it is written. 1,2+ is “J=1 with unknown parity, or 2+” — not “1+ or 2+”. The exception is a parity after a closing parenthesis, which covers the group: (3,4)- is 3− or 4−.

  • NOT inverts the statement. Treating NOT 3- as an assignment of 3− reports the opposite of the data. Those values go to excluded, and allows() honours them.

Ranges follow the manual’s three cases: 3 TO 6- gives every value π=−, while 3+ TO 6- keeps the endpoints and leaves the interior undetermined.

Spin is stored as two_j so half-integers stay exact; .j gives a Fraction, .j_float a float.

Half-lives (V.14)

Time units convert to seconds. Unbound levels quoted as widths convert through T = ħ ln2 / Γ, with the error branches correctly swapped by the reciprocal. half_life.from_width says which case you got; half_life.width_mev keeps Γ.

Energies and floating levels (V.18)

A level whose position relative to the ground state is unknown carries an offset symbol, and it can appear on either side of the number: both X+1440 and 4172.3+X are valid. level.energy_offset and level.is_floating record it, and energy filters drop such levels rather than pretending the number is an excitation energy.

Reading only the prefix form silently discarded 498 energies in the A=180 chain alone — see testing.

Continuation records (IV)

Anything that doesn’t fit the 80-column primary record lands on a continuation as KEY=value$KEY=value, decoded into level.properties / gamma.properties: moments (MOMM1, MOME2), branchings (%B-, %EC, %IT), shell conversion coefficients (KC, LC, …), reduced transition probabilities (BE2W), and XREF.

Column 6 marks a continuation; column 7 splits data (blank) from comment (c/C). Identification records are not data records — anything scanning raw lines rather than dataset blocks will misread 180    REFERENCES as an R record, because R lands in column 8.

Provenance: XREF and X records (V.23, III.B.3)

XREF records which datasets reported a level. The symbols are defined by explicit X records, not by dataset ordering — the ordering convention usually holds but is not guaranteed:

scheme.metadata["xref_key"]   # {'I': '181TA(P,D)', ...}
level.observed_in             # resolved dataset names
scheme.seen_in("(P,D)")

XREF=ALL, ALL EXCEPT xy and parenthesised uncertain symbols are handled.

Bands

Each level carries a one-character band flag in column 77, with the meaning given by a BAND(x) documentation record. The MS field spans columns 78–79, not 78 alone — the 180Ta isomer reads M1.

ASCII markup is rendered on the way out: |p7/2[404] becomes π7/2[404].

Gamma placement

G records name the level a transition depopulates but not the one it feeds. Where a continuation states FL=, that is used directly; otherwise placement falls back to matching E_start E_gamma within a tolerance. Ambiguous cases keep end_index=None rather than a guess.

Q record

Separation energies and decay Q-values in keV, on scheme.metadata["Q"]: s_n, s_p, q_beta_minus, q_alpha.