fix(nuclear_data): use measured transition endpoints per branch

paceENSDF stores the measured beta endpoint energy of each transition
(ENSDF column 7). The parser instead derived branch endpoints as
base-endpoint minus level energy with the base taken from an arbitrary
first-listed branch, so Co60 produced negative branch endpoints and a
hard ValueError (bs_pnpi Co60 exited 1 regardless of cutoff).

- BranchInfo carries the measured endpoint_MeV
- overall endpoint = max measured endpoint over all branches
- decay_info_to_config prefers measured endpoints; the subtraction is
  retained only as a legacy fallback for hand-built DecayInfo objects
- Co60/Tc99/Cs137 endpoint tests + CLI dry-run/export tests (D3)
This commit is contained in:
2026-10-02 08:42:51 +03:00
parent 9c3dd80b5c
commit 3590ca91a1
3 changed files with 90 additions and 18 deletions
+17
View File
@@ -163,6 +163,23 @@ class TestCLIPaceENSDF:
assert exc.value.code == 1
assert not (tmp_path / "tc99deg.csv").exists()
def test_paceensdf_co60_dry_run_multi_branch(self, capsys: Any) -> None:
"""Co60 parses into a healthy multi-branch config (D3): no crash,
all branch endpoints positive."""
main(["Co60", "--dry-run"])
out = capsys.readouterr().out
assert "Multi-branch mode: 3 branches" in out
def test_paceensdf_co60_export(self, tmp_path: Any) -> None:
out = str(tmp_path / "co60.csv")
main(["Co60", "--output", out])
df = read_export(out)
area = np.trapezoid(df["total_spectrum"], df["energy_MeV"])
assert np.isclose(area, 1.0, rtol=1e-3)
eps = header_lines_of(out)
assert any("E₀=1492.0" in h for h in eps)
assert not any("excluded" in h for h in eps)
def test_cli_builds_paceensdf_config_via_library(
self, monkeypatch: Any, capsys: Any
) -> None: