Compare commits

...
40 Commits
Author SHA1 Message Date
unzhakov 410e8b6524 style: apply black formatting after cleanup 2026-10-05 23:27:24 +03:00
unzhakov dc8e26403b docs: prune research vault to theory-only scope
Remove gA-extraction notes, nuclear-structure notes, paper-summary
refs, detector-response correction note and the finished test
migration checklist. Rewrite the vault index for the single-package
theory calculator; repoint dangling wiki-links in surviving notes to
inline citations; refresh docs_check.py (drop bs_exp CLI check,
register removed files).

Literature and nuclear-structure research resumes in dedicated
projects; the removed notes are recoverable from history.
2026-10-05 22:53:47 +03:00
unzhakov 6aced1d8ac docs: rewrite README, TODO, AGENTS and CONVENTIONS to theory-only scope
Compactify the descriptive markdown: scope statement replaces the
g_V-extraction goal, package architecture describes the single
theory-only distributable, roadmap keeps calculator capabilities
(C1-C8) and drops the analysis tracks, structure trees match the
actual file layout, the bs_exp debug step and detector-parameter
convention wording are dropped.
2026-10-05 17:20:27 +03:00
unzhakov e8b73eb20f test: check CSV header version against package version, not a literal 2026-10-05 15:14:57 +03:00
unzhakov 62042ae92a build: drop bs_exp entry point and root extra, bump version to 0.5.0
The experimental CLI and the PyROOT extra existed only for the
removed analysis packages. Version bump reflects the scope reduction
to theory-only spectrum calculation; calculation logic is unchanged.
2026-10-05 14:52:30 +03:00
unzhakov bc287e42a6 chore: remove local copies of raw detector data
Raw Tc99/A-major runs, Am-241 calibration files and the ROOT
histogram macro were measurement inputs for the analysis track that
left the project scope; the canonical copies live on the experiment
storage. Also removes scripts/analyze_raw.py, a rate-stability
analysis utility built on the exp_data reader.
2026-10-05 14:43:11 +03:00
unzhakov 57d5c7334a refactor: remove fitter analysis framework
Spectrum model, convolution, chi2 fit engine, C(W) and g_V/g_A
extractors and result containers are analysis code outside the
theory-only scope. Removes the package and its test suites.
2026-10-05 14:35:44 +03:00
unzhakov abde581146 refactor: remove exp_data experimental data package
Detector data handling, energy calibration, peak fitting and
instrumental corrections leave the project scope (theory-only
spectrum shape calculation). Removes the package, its tests, the
exp pipeline integration test, the calibration notebook, and the
ExpSpectrum fixtures from the shared conftest.
2026-10-05 14:31:56 +03:00
unzhakov 67de4e3baa style: canonicalize black formatting on test files 2026-10-03 01:31:35 +03:00
unzhakov 2788ea0683 feat(cli): separate-branches rename, short aliases, clean dry-run
- --normalize-to-one renamed to --separate-branches (no alias; the
  name now describes the output choice: total spectrum with
  intensity-normalized branches vs separate branches, each
  normalized to unit area with intensities left to the data user).
  SpectrumConfig.normalize_to_one -> separate_branches, CSV header
  word to_one -> separate.
- every long option gains a single-letter alias: -N -i -s -m -o -p
  -L -d -c -V -S -b (only -v/-q existed before)
- dry-run drops the top-level Transition: line — the per-branch
  list below already reports each branch's transition type
- version bumped to 0.4.1 (pyproject, __version__, CSV semantics
  docstring, TODO roadmap entry)
2026-10-03 01:08:51 +03:00
unzhakov 7f47a876ae style: apply black formatting to test files 2026-10-02 14:32:04 +03:00
unzhakov 7e31ac4b4e docs: reflect normalization fixes, detector cleanup and CLI overrides
- README: remove stale detector_response.py tree entry and the
  detector-models highlight (module removed); document per-branch
  measured transition endpoints from paceENSDF and JSON-flow CLI
  override semantics
- TODO: drop the DEFAULT_JSON_SCHEMA detector-keys clause (removed)
- data/custom_input_example.json: strip dead detector_* keys
2026-10-02 12:01:45 +03:00
unzhakov 36174b5d25 refactor: remove stale detector schema keys and single-mode duplicates
- DEFAULT_JSON_SCHEMA: drop the 8 detector_* keys left over from the
  removed detector_response module; CLI dry-run drops the dead
  'Detector: not configured (Phase 4)' line
- CSV export: single-mode no longer writes branch_1_PhaseSpace /
  branch_1_Radiative duplicates next to the bare PhaseSpace/Radiative
  columns; per-branch core factors only in multi-branch mode
- Visualizer: debug panels fall back to the unprefixed core-factor
  columns for single-mode CSVs instead of skipping them
- Spectrum ylabel now states the actual units: normalized spectrum
  [MeV^-1], not 'Normalized Counts / keV'
- tests: defaults assert detector keys gone; column layout pinned for
  single vs multi; panel and ylabel assertions
2026-10-02 10:21:07 +03:00
unzhakov 3590ca91a1 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)
2026-10-02 08:42:51 +03:00
unzhakov 9c3dd80b5c refactor(cli): delegate paceENSDF config building to library
Remove the duplicated inline paceENSDF config builder in the CLI.
It renormalized branch intensities before filtering, diverging from
the library decay_info_to_config path that library callers use
(audit finding D5). CLI paceENSDF configs now come from
create_config_from_source exactly once per run.

Regression test spies on create_config_from_source during a CLI run.
2026-10-02 07:13:14 +03:00
unzhakov d166532779 test: cover to_one/concise/single/plot normalization combinations 2026-10-02 06:31:38 +03:00
unzhakov 18a46ad02f feat(cli): propagate export options through JSON/paceENSDF flows
- main(argv) signature enables in-process CLI invocation and testing
- parser defaults for --e-step/--neutrino-mass/--intensity-cutoff/
  --csv-separator/--normalize-to-one now None = 'not provided'
- JSON flow applies file values by default and CLI flags as overrides
  (previously these five options were silently discarded)
- paceENSDF flow resolves None flags to literal defaults
- new tests/integration/test_cli_flows.py: JSON propagation,
  degenerate-mode non-zero exit with no file (JSON + paceENSDF)
- adapted two neutrino-mass default tests to None-parsing semantics
2026-10-02 05:46:08 +03:00
unzhakov 62bf7fdac2 fix(output): apply positive-integral export guard to to_one mode
CSVEmitter.export_to_csv raised ValueError only in intensity mode;
degenerate exports with --normalize-to-one silently wrote all-zero
CSV files. The guard now runs before the mode split and no file is
written in either mode.
2026-10-01 23:02:33 +03:00
unzhakov ee7aedc80a fix(spectrum): lone branch defines endpoint in single-branch mode
When config.branches carries exactly one branch (e.g. after
--intensity-cutoff filtering), the branch endpoint/transition define
the spectrum, energy grid, analyzer unified branch view and CSV
header. The stale top-level endpoint inherited from the parent decay
no longer leaks into physics (proven wrong on Cs137: spectrum
extended to 1176 keV while the lone survivor ends at 514 keV).
2026-10-01 22:04:17 +03:00
unzhakov 6bb7b950c7 style: apply black formatting to pending files 2026-09-30 10:41:28 +03:00
unzhakov ea4b652ccb chore: ignore ROOT data files produced by notebooks 2026-09-30 03:16:45 +03:00
unzhakov 40d0836056 docs: document core spectrum, physical branch columns, CSV-driven plots 2026-09-30 03:11:05 +03:00
unzhakov f5b79000a4 fix(scripts): list min/max calls, typed mains, drop unused header reads 2026-09-30 03:11:05 +03:00
unzhakov dc350aacfc fix(fitter): stop referencing removed detector_response module 2026-09-30 03:08:13 +03:00
unzhakov 8172ba2541 test: adapt existing suites to unconditional core, drop dead detector tests 2026-09-30 03:08:13 +03:00
unzhakov 7dc2ec79d1 feat(cli): report core in dry-run, build plots from exported CSV 2026-09-30 03:04:18 +03:00
unzhakov afb7afc5b7 feat(output): physical branch spectra, universal factor, CSV-driven plotting 2026-09-30 03:04:18 +03:00
unzhakov bab19de458 feat: make phase space and Fermi function unconditional core components 2026-09-30 03:04:18 +03:00
unzhakov 8bc9b0fd87 test: pin core-spectrum invariant (phase_space x fermi always included) 2026-09-30 03:04:18 +03:00
unzhakov 687e05cbe2 style: reformat assertion blocks in test files 2026-09-29 13:06:22 +03:00
unzhakov be3e288e0d docs: update documentation files and fix linting issues 2026-07-07 13:32:48 +03:00
unzhakov 9bf1e1333e refactor: rename visualize module to output module and separate concerns into core, csv_export, and visualization modules 2026-07-07 12:59:11 +03:00
unzhakov 6dc1e3924d feat: add CLI options for custom CSV separator and normalize-to-one branch normalization 2026-07-06 17:32:56 +03:00
unzhakov 1b4d418b5f feat: change CSV output format to use tab delimiters instead of comma delimiters 2026-07-03 18:01:39 +03:00
unzhakov 643e035c54 feat: update CSV headers and plot titles to show active/excluded branches and intensity cutoff exclusions 2026-07-03 17:45:35 +03:00
unzhakov c6aaf3b76a fix: use underscores in branch names for CSV headers in concise mode 2026-07-03 15:21:44 +03:00
unzhakov c2795bf86e fix: use underscores in branch names for CSV headers 2026-07-03 15:10:10 +03:00
unzhakov a7eef7bc20 feat: polish CSV header format with improved metadata and column naming 2026-07-03 14:58:19 +03:00
unzhakov 876e1e3ab8 Add massive neutrino support and improve CSV/CLI output
- Add neutrino mass parameter to SpectrumConfig and CLI interface
- Update PhaseSpace component to handle neutrino mass effects properly
- Update CSV headers to show basic endpoint and all transition types
- Add --csv-verbosity option to CLI with 'full' and 'concise' options
- Fix comparison plots to properly show linear scale variations
- Add comprehensive tests for neutrino mass functionality
2026-07-02 10:33:28 +03:00
unzhakov 406fd57bcd chore: add .pi-lens/ to .gitignore 2026-05-26 15:01:17 +03:00
374 changed files with 7204 additions and 13036 deletions
+4
View File
@@ -178,4 +178,8 @@ htmlcov/
.cache/
.obsidian/
.rpiv/
.pi-lens/
data/raw/
# ROOT data files produced by notebooks
*.root
+34
View File
@@ -0,0 +1,34 @@
# LLM Wiki Schema
## Ownership Rules
| Path | Owner | Rule |
|------|-------|------|
| raw/** | extension | immutable after capture |
| wiki/** | model + user | editable knowledge pages |
| meta/* | extension | auto-generated |
| . | human + explicit request | operating rules |
## Source Packet Format
```
raw/sources/SRC-YYYY-MM-DD-NNN/
manifest.json
original/
extracted.md
attachments/
```
## Page Types
- **source** — what this specific source says
- **entity** — people, orgs, tools, products
- **concept** — ideas, patterns, frameworks
- **synthesis** — cross-source theses and tensions
- **analysis** — durable filed answers from queries
- **requirement** — atomic requirements with status, priority, and traceability
## Linking Style
- Internal: [[folder/page-name]]
- Citation: [[sources/SRC-YYYY-MM-DD-NNN]]
+7
View File
@@ -0,0 +1,7 @@
{
"name": "Beta Spectrum Calculator — High-precision beta decay spectrum simulation for ⁹⁹Tc and other forbidden decays",
"mode": "personal",
"topic": "Beta Spectrum Calculator — High-precision beta decay spectrum simulation for ⁹⁹Tc and other forbidden decays",
"created": "2026-05-27",
"version": "1.0"
}
+20
View File
@@ -0,0 +1,20 @@
{
"sources/SRC-2026-05-27-001": [
"sources/SRC-2026-05-27-001"
],
"sources/SRC-2026-05-27-002": [
"sources/SRC-2026-05-27-002"
],
"sources/SRC-2026-05-27-003": [
"sources/SRC-2026-05-27-003"
],
"sources/SRC-2026-05-27-004": [
"sources/SRC-2026-05-27-004"
],
"sources/SRC-2026-05-27-005": [
"sources/SRC-2026-05-27-005"
],
"sources/SRC-2026-05-27-006": [
"sources/SRC-2026-05-27-006"
]
}
+13
View File
@@ -0,0 +1,13 @@
{"timestamp":"2026-05-27T11:55:40.409Z","kind":"bootstrap","topic":"Beta Spectrum Calculator — High-precision beta decay spectrum simulation for ⁹⁹Tc and other forbidden decays","mode":"personal"}
{"timestamp":"2026-05-27T11:55:55.301Z","kind":"capture","source_id":"SRC-2026-05-27-001","file_path":"/home/unzhakov/workspace/beta_shape_pnpi/docs/gA-extraction/01-ssm-algorithm.md","format":"markdown"}
{"timestamp":"2026-05-27T11:56:08.796Z","kind":"capture","source_id":"SRC-2026-05-27-002","file_path":"/home/unzhakov/workspace/beta_shape_pnpi/docs/nuclear-structure/01-shape-factors.md","format":"markdown"}
{"timestamp":"2026-05-27T11:59:21.939Z","kind":"capture","source_id":"SRC-2026-05-27-003","url":"https://jyx.jyu.fi/bitstreams/eb3a10c8-b94c-4009-8a8e-839bfddc7c58/download","format":"web"}
{"timestamp":"2026-05-27T12:00:22.177Z","kind":"capture","source_id":"SRC-2026-05-27-004","url":"https://cea.hal.science/cea-04796921/file/PhysRevC.110.055503.pdf","format":"web"}
{"timestamp":"2026-05-27T12:01:29.331Z","kind":"capture","source_id":"SRC-2026-05-27-005","url":"https://arxiv.org/pdf/2506.17544","format":"web"}
{"timestamp":"2026-05-27T12:01:51.914Z","kind":"capture","source_id":"SRC-2026-05-27-006","url":"https://arxiv.org/pdf/2312.07448","format":"web"}
{"timestamp":"2026-05-27T12:03:31.427Z","kind":"ensure_page","page_type":"concept","title":"Shape Factor C(W, g_A)","path":"concepts/shape-factor-cw-ga"}
{"timestamp":"2026-05-27T12:04:45.675Z","kind":"ensure_page","page_type":"concept","title":"SSM — Spectrum Shape Method","path":"concepts/ssm-spectrum-shape-method"}
{"timestamp":"2026-05-27T12:05:55.743Z","kind":"ensure_page","page_type":"entity","title":"Paulsen et al. (2024) — High precision measurement of 99Tc β spectrum","path":"entities/paulsen-et-al-2024-high-precision-measurement-of-99tc-spectrum"}
{"timestamp":"2026-05-27T12:07:21.046Z","kind":"ensure_page","page_type":"entity","title":"Song et al. (2025) — 99Tc β-decay spectrum and implications for g_A","path":"entities/song-et-al-2025-99tc--decay-spectrum-and-implications-for-ga"}
{"timestamp":"2026-05-27T12:08:23.407Z","kind":"ensure_page","page_type":"concept","title":"ESSM — Enhanced Spectrum Shape Method","path":"concepts/essm-enhanced-spectrum-shape-method"}
{"timestamp":"2026-05-27T12:09:43.380Z","kind":"ensure_page","page_type":"concept","title":"Nuclear Shell Model for β Decay","path":"concepts/nuclear-shell-model-for-decay"}
+15
View File
@@ -0,0 +1,15 @@
# Wiki Index
> Auto-generated from meta/registry.json. Do not edit manually.
## Sources
- [[sources/SRC-2026-05-27-001]] — SRC-2026-05-27-001 *(created: 2026-05-27)*
- [[sources/SRC-2026-05-27-002]] — SRC-2026-05-27-002 *(created: 2026-05-27)*
- [[sources/SRC-2026-05-27-003]] — SRC-2026-05-27-003 *(created: 2026-05-27)*
- [[sources/SRC-2026-05-27-004]] — SRC-2026-05-27-004 *(created: 2026-05-27)*
- [[sources/SRC-2026-05-27-005]] — SRC-2026-05-27-005 *(created: 2026-05-27)*
- [[sources/SRC-2026-05-27-006]] — SRC-2026-05-27-006 *(created: 2026-05-27)*
---
*Last updated: 2026-05-27T12:01:51.914Z* | *Total pages: 6*
+25
View File
@@ -0,0 +1,25 @@
# Activity Log
> Auto-generated from meta/events.jsonl. Do not edit manually.
## [2026-05-27T11:55:40.409Z] bootstrap
- topic: "Beta Spectrum Calculator — High-precision beta decay spectrum simulation for ⁹⁹Tc and other forbidden decays", mode: "personal"
## [2026-05-27T11:55:55.301Z] capture
- source_id: "SRC-2026-05-27-001", file_path: "/home/unzhakov/workspace/beta_shape_pnpi/docs/gA-extraction/01-ssm-algorithm.md", format: "markdown"
## [2026-05-27T11:56:08.796Z] capture
- source_id: "SRC-2026-05-27-002", file_path: "/home/unzhakov/workspace/beta_shape_pnpi/docs/nuclear-structure/01-shape-factors.md", format: "markdown"
## [2026-05-27T11:59:21.939Z] capture
- source_id: "SRC-2026-05-27-003", url: "https://jyx.jyu.fi/bitstreams/eb3a10c8-b94c-4009-8a8e-839bfddc7c58/download", format: "web"
## [2026-05-27T12:00:22.177Z] capture
- source_id: "SRC-2026-05-27-004", url: "https://cea.hal.science/cea-04796921/file/PhysRevC.110.055503.pdf", format: "web"
## [2026-05-27T12:01:29.331Z] capture
- source_id: "SRC-2026-05-27-005", url: "https://arxiv.org/pdf/2506.17544", format: "web"
## [2026-05-27T12:01:51.914Z] capture
- source_id: "SRC-2026-05-27-006", url: "https://arxiv.org/pdf/2312.07448", format: "web"
+72
View File
@@ -0,0 +1,72 @@
{
"version": "1.0",
"last_updated": "2026-05-27T12:01:51.914Z",
"pages": {
"sources/SRC-2026-05-27-001": {
"type": "source",
"title": "SRC-2026-05-27-001",
"created": "2026-05-27",
"updated": "2026-05-27",
"format": "markdown",
"source_id": "SRC-2026-05-27-001",
"raw_path": "raw/sources/SRC-2026-05-27-001/extracted.md",
"captured": "2026-05-27",
"status": "skeleton"
},
"sources/SRC-2026-05-27-002": {
"type": "source",
"title": "SRC-2026-05-27-002",
"created": "2026-05-27",
"updated": "2026-05-27",
"format": "markdown",
"source_id": "SRC-2026-05-27-002",
"raw_path": "raw/sources/SRC-2026-05-27-002/extracted.md",
"captured": "2026-05-27",
"status": "skeleton"
},
"sources/SRC-2026-05-27-003": {
"type": "source",
"title": "SRC-2026-05-27-003",
"created": "2026-05-27",
"updated": "2026-05-27",
"format": "web",
"source_id": "SRC-2026-05-27-003",
"raw_path": "raw/sources/SRC-2026-05-27-003/extracted.md",
"captured": "2026-05-27",
"status": "skeleton"
},
"sources/SRC-2026-05-27-004": {
"type": "source",
"title": "SRC-2026-05-27-004",
"created": "2026-05-27",
"updated": "2026-05-27",
"format": "web",
"source_id": "SRC-2026-05-27-004",
"raw_path": "raw/sources/SRC-2026-05-27-004/extracted.md",
"captured": "2026-05-27",
"status": "skeleton"
},
"sources/SRC-2026-05-27-005": {
"type": "source",
"title": "SRC-2026-05-27-005",
"created": "2026-05-27",
"updated": "2026-05-27",
"format": "web",
"source_id": "SRC-2026-05-27-005",
"raw_path": "raw/sources/SRC-2026-05-27-005/extracted.md",
"captured": "2026-05-27",
"status": "skeleton"
},
"sources/SRC-2026-05-27-006": {
"type": "source",
"title": "SRC-2026-05-27-006",
"created": "2026-05-27",
"updated": "2026-05-27",
"format": "web",
"source_id": "SRC-2026-05-27-006",
"raw_path": "raw/sources/SRC-2026-05-27-006/extracted.md",
"captured": "2026-05-27",
"status": "skeleton"
}
}
}
@@ -0,0 +1,8 @@
{
"id": "SRC-2026-05-27-001",
"captured": "2026-05-27",
"packet_version": "1.0",
"title": "01-ssm-algorithm.md",
"file_path": "/home/unzhakov/workspace/beta_shape_pnpi/docs/gA-extraction/01-ssm-algorithm.md",
"format": "markdown"
}
@@ -0,0 +1,172 @@
---
title: "SSM — Spectrum Shape Method for gA Extraction"
date: 2026-05-14
tags:
- gA/extraction/algorithm
- gA/ssm
- beta-spectrum/forbidden
status: active
aliases: [Spectrum Shape Method, gA fitting, shape factor fit]
related_notes:
- gA-extraction/02-ssnme
- gA-extraction/03-tc99-case-study
- gA-extraction/04-implementation-todo
- nuclear-structure/01-shape-factors
- corrections/05-radiative
---
# Spectrum Shape Method (SSM) — Step-by-Step Algorithm for gA Extraction
## 1. Physical Basis
For **forbidden nonunique β decays**, the shape factor $C(w_e)$ decomposes into three energy-dependent components with distinct coupling-constant dependence:
$$C(w_e) = g_V^2\, C_V(w_e) + g_A^2\, C_A(w_e) + g_V g_A\, C_{VA}(w_e) \tag{1}$$
where $w_e$ is the total electron energy in units of $m_e c^2$. The components $C_V$, $C_A$, $C_{VA}$ are computed from nuclear many-body theory and are **independent of the coupling constants**. The spectrum shape is sensitive to $g_A$ through the interference between vector, axial-vector, and mixed terms — a purely nuclear-structure effect unique to nonunique forbidden decays.
> [!note] Why nonunique decays?
> Unique forbidden decays have a single dominant NME → universal spectrum shape independent of nuclear structure. Nonunique decays involve multiple NMEs → the relative weight of vector vs. axial contributions depends on $g_A$, creating observable spectral shape differences.
## 2. The Full Spectrum Model
The differential spectrum (Paulsen et al., 2024, Eq. 5):
$$N(W) = \mathcal{N} \cdot F(Z,W) \cdot p W (W_0 - W)^2 \cdot X(W) \cdot S(Z,W) \cdot R(W) \cdot C(W; g_A, g_V) \cdot \rho(Z,W)$$
where:
- $\mathcal{N}$ — overall normalization (absorbs $G_F^2 \cos^2\theta_C / 2\pi^3$)
- $F(Z,W)$ — Fermi function (Coulomb correction) — [[corrections/01-fermi-function]]
- $p W (W_0 - W)^2$ — phase space — [[02-phase-space]]
- $X(W)$ — atomic exchange correction — [[corrections/04-exchange]]
- $S(Z,W)$ — atomic screening — [[corrections/03-screening]]
- $R(W)$ — outer radiative correction — [[corrections/05-radiative]]
- $C(W; g_A, g_V)$ — **shape factor** — [[nuclear-structure/01-shape-factors]]
- $\rho(Z,W)$ — atomic overlap correction — [[corrections/08-atomic-overlap]]
## 3. The Algorithm
### Step 1: Compute shape factor components from nuclear model
**Input:** Nuclear many-body model (NSM, MQPM, etc.) with appropriate valence space and Hamiltonian for the transition of interest.
**Output:** Three energy-dependent lookup tables: $C_V(W)$, $C_A(W)$, $C_{VA}(W)$ for $W \in [1, W_0]$.
**Method:**
1. Run shell model (or MQPM) to obtain one-body transition densities (OBTDs)
2. For each OBTD, compute the contribution to each shape factor component using the Behrens-Bühring formalism (truncated lepton current approximation)
3. Sum over all contributing multipole orders $K$ and lepton quantum numbers $k_e, k_\nu$
**CVC constraint for vector current:**
The relativistic vector current includes the small relativistic NME (sNME), which is related to the large vector NME (l-NME) via the CVC hypothesis:
$$\text{sNME} = \frac{1}{2} \left[ W_0 - (m_n - m_p) + E_C \right] \cdot \text{l-NME}$$
where $E_C$ is the Coulomb displacement energy. The sNME is critical for small-$Q$ decays like $^{99}\text{Tc}$ ($[W_0 - (m_n - m_p)] = -0.487$ MeV).
**Practical note:** For $^{99}\text{Tc}$, the dominant transition is $2d_{5/2} \to 1g_{9/2}$ with OBTD $\approx 0.478$. The sub-dominant $1g_{7/2} \to 1g_{9/2}$ transition contributes OBTD $\approx 0.010$. See Kostensalo & Suhonen (2017), Table VII.
### Step 2: Build theoretical spectrum for trial $g_A$
For each trial value of $g_A$ (with $g_V = 1.0$ fixed by CVC):
1. **Compute shape factor:**
$$C(W; g_A) = C_V(W) + g_A^2\, C_A(W) + g_A\, C_{VA}(W)$$
(using $g_V = 1.0$)
2. **Compute full spectrum:**
$$N_{\text{theo}}(W; g_A) = \mathcal{N} \cdot F(Z,W) \cdot p W (W_0 - W)^2 \cdot X(W) \cdot S(Z,W) \cdot R(W) \cdot C(W; g_A) \cdot \rho(Z,W)$$
3. **Normalize:** Choose $\mathcal{N}$ so that $\int_1^{W_0} N_{\text{theo}}(W; g_A)\, dW = 1$ (or match the total counts in the experimental spectrum)
### Step 3: Compare with experimental spectrum
**Input:** Experimental spectrum data $(W_i, N_i, \sigma_i)$ for $i = 1, \dots, N_{\text{bins}}$.
**χ² function:**
$$\chi^2(g_A) = \sum_{i \in \text{fit range}} \frac{\left[N_i - N_{\text{theo}}(W_i; g_A)\right]^2}{\sigma_i^2}$$
**Fit range selection (Paulsen et al., 2024):**
- **Lower bound:** ~20 keV — below this, detector threshold effects and atomic exchange modeling dominate
- **Upper bound:** ~275 keV for $^{99}\text{Tc}$ — above this, endpoint region with poor statistics and resolution distortion
- Rationale: the $g_A$-driven shape differences are most pronounced in the mid-energy range
### Step 4: Minimize χ² and extract $g_A$
**Grid search:** Evaluate $\chi^2(g_A)$ on a grid (e.g., $g_A \in [0.4, 1.8]$ with step 0.01).
**Refinement:** Use `scipy.optimize.minimize_scalar` (Brent's method) around the grid minimum.
**Uncertainty:** Find $g_A$ values where $\chi^2 = \chi^2_{\text{min}} + 1$. These define the 68% confidence interval.
**Output:** $(g_A^{\text{best}}, g_A^{\text{low}}, g_A^{\text{high}})$
### Step 5 (optional): Simultaneous $g_A$ and $g_V$ fit
If $g_V$ is not fixed to 1.0 (e.g., testing CVC violation):
$$\chi^2(g_A, g_V) = \sum_i \frac{\left[N_i - N_{\text{theo}}(W_i; g_A, g_V)\right]^2}{\sigma_i^2}$$
Fit both parameters simultaneously. The correlation between $g_A$ and $g_V$ is typically strong.
### Step 6 (optional): Half-life normalization — quenched $g_A$
The $g_A$ extracted from **shape alone** can be enhanced ($>1.27$) because the normalization constant $\mathcal{N}$ absorbs the overall rate. To obtain the **quenched** values consistent with half-life:
1. Compute the theoretical partial half-life for the best-fit $(g_A, g_V)$:
$$t_{1/2}^{\text{theo}} = \frac{\kappa}{\tilde{C}}, \quad \tilde{C} = \int_1^{W_0} C(W; g_A, g_V) \cdot p W (W_0 - W)^2 \cdot F(Z,W) \cdot X(W) \cdot S(Z,W) \cdot R(W) \, dW$$
2. Compare with experimental partial half-life:
$$t_{1/2}^{\text{exp}} = \frac{t_{1/2}^{\text{total}}}{\text{branching ratio}}$$
3. Extract quenched values:
$$g_i^{\text{quenched}} = g_i^{\text{shape}} \times \sqrt{\frac{t_{1/2}^{\text{theo}}}{t_{1/2}^{\text{exp}}}}$$
For $^{99}\text{Tc}$: $t_{1/2}^{\text{theo}} \approx 30 \times 10^3$ a vs. $t_{1/2}^{\text{exp}} \approx 212 \times 10^3$ a → quenching factor $\approx 0.38$.
## 4. The sNME Problem and ESSM
### The Problem
The sNME (small relativistic vector NME) is extremely difficult to compute in nuclear shell model because it involves contributions outside the valence shell. The CVC prediction gives a "canonical" value, but this assumes a perfect many-body theory — which finite valence spaces are not.
### The Solution: Enhanced SSM (ESSM)
Kumar et al. (2021) proposed treating the sNME as a **fitting parameter** alongside $g_A$. This is the **Enhanced Spectrum Shape Method (ESSM)**:
1. Fix $g_A$ at a trial value
2. Vary the sNME to simultaneously fit the **experimental half-life** (not just the shape)
3. Because the half-life depends quadratically on sNME, there are typically **two solutions** for each $g_A$ value:
- **sNME(c):** closer to the CVC value
- **sNME(f):** further from the CVC value
4. Compare both solutions' spectral shapes with the data
The "optimal" choice (closest to CVC) is theoretically motivated, but experimental validation is needed. See [[gA-extraction/02-ssnme]] for details.
## 5. Uncertainty Budget
| Component | Uncertainty on $g_A$ | Relative | Dominant for |
|---|---|---|---|
| Fit method (statistics) | 0.0745 | 4.9% | All SSM analyses |
| Coulomb displacement energy | 0.0425 | 2.8% | Small-$Q$ decays |
| Nuclear model | 0.0294 | 1.9% | Model comparison |
| Lepton current treatment | 0.0127 | 0.8% | High-$Z$ nuclei |
| Fit range | 0.0061 | 0.4% | All |
| Endpoint energy | 0.0038 | 0.2% | Precision $Q$-value needed |
| Radiative corrections | 0.0033 | 0.2% | All |
| Atomic exchange | 0.0029 | 0.2% | Low-energy region |
| **Combined** | **0.0919** | **6.0%** | — |
Source: Paulsen et al. (2024), Table VIII.
## 6. Key References
| Ref | Citation | Use |
|---|---|---|
| [SSM-orig] | Kostensalo, Haaranen, Suhonen, *Phys. Rev. C* **95**, 044313 (2017) | SSM introduction; 26 decays |
| [SSM-Tc94-98] | Kostensalo, Suhonen, *Phys. Rev. C* **96**, 024317 (2017) | gA-driven evolution; $^{94,98}$Tc, $^{99}$Tc |
| [Paulsen2024] | Paulsen et al., *Phys. Rev. C* **110**, 055503 (2024) | $^{99}$Tc MMC measurement; $g_A$ extraction (enhanced value) |
| [Song2025] | Song et al., arXiv:2506.17544 (2025) | $^{99}$Tc MMC measurement; quenched $g_A$ via ESSM |
| [ESSM] | Kumar, Srivastava, Suhonen, *Eur. Phys. J. A* **57**, 225 (2021) | Enhanced SSM formalism |
| [Ramalho2024] | Ramalho, Suhonen, *Front. Phys.* **12**, 1455778 (2024) | sNME analysis; five second-forbidden decays |
| [Hayen2017] | Hayen et al., *Rev. Mod. Phys.* **90**, 015008 (2017) | BB formalism; shape factor theory |
@@ -0,0 +1,8 @@
{
"id": "SRC-2026-05-27-002",
"captured": "2026-05-27",
"packet_version": "1.0",
"title": "01-shape-factors.md",
"file_path": "/home/unzhakov/workspace/beta_shape_pnpi/docs/nuclear-structure/01-shape-factors.md",
"format": "markdown"
}
@@ -0,0 +1,93 @@
---
title: "10 – Nuclear Structure Effects"
date: 2026-04-26
tags:
- beta-spectrum/nuclear-structure
status: active
aliases: [Shape Factor, C(Z,W), Form Factors]
cssclasses: []
related_components: [[corrections/06-recoil]],
---
# Nuclear Structure Effects — The Shape Factor $C(Z,W)$
## Overview
Nuclear structure effects are encapsulated in the shape factor $C(Z,W)$ which multiplies the phase space × Fermi function. This is distinct from electrostatic finite size corrections ([[corrections/02-finite-size]]) — it describes how nuclear form factors and matrix elements modify the spectrum beyond simple point-nucleus Coulomb interaction.
$$N(W)\,dW \propto p W (W_0 - W)^2 F(Z,W) C(Z,W) \tag{79}$$
## Two Formalisms Compared
| Approach | Style | Strengths |
|---|---|---|
| **Behrens-Bühring** (BB) | Couples leptonic + nuclear parts; rigorous expansion in $r^2$, $(m_eR)^a$, $(WR)^b$, $(\alpha Z)^c$ with tabulated functions $I(k_e,m,n,\rho)$ | Most precise, established computational machinery |
| **Holstein** (HS) | Decouples leptonic + nuclear parts; manifestly covariant form factors | Clean symmetry properties, intuitive classification |
Hayen et al. adopt BB's computational approach but present results in Holstein notation for clarity. See Appendix E of Hayen paper for detailed comparison.
## Multipole Expansion (Eq. 78)
Form factors $F_{KLs}(q^2)$ describe nuclear structure model-independently:
$$\langle f | V_\mu - A_\mu | i \rangle \propto \sum_{KMs} \sum_{L=K-1}^{K+1} (-1)^{J_f-M_f+M} (-i)^L F_{KLs}(q^2) $$
$$\times \sqrt{4\pi(2J_i+1)} \begin{pmatrix} J_f & K & J_i \\ -M_f & M & M_i \end{pmatrix} \frac{(qR)^L}{(2L+1)!!}$$
Since $(qR)^2$ is very small for beta decay, expansion typically stops after first order.
## Impulse Approximation (Eq. 80–81)
Nucleons treated as non-interacting particles coupling to weak vertex as free particles:
$$O_{KLs} = \sum_{\alpha,\beta} \langle \alpha | O_{KLs} | \beta \rangle a^\dagger_\alpha a_\beta \tag{80}$$
$$\langle f | O_{KLs} | i \rangle = \sum_{\alpha,\beta} \langle \alpha | O_{KLs} | \beta \rangle \langle f | a^\dagger_\alpha a_\beta | i \rangle \tag{81}$$
The one-body density matrix elements $\langle f | a^\dagger_\alpha a_\beta | i \rangle$ are calculable via shell model methods.
> [!warning] Limitations
> The impulse approximation neglects many-body effects:
> - **Meson exchange**: long history (Blin-Stoyle, Chemtob & Rho)
> - **Core polarization**: nuclear medium modifications
>
> For allowed decays, results largely hold due to cancellations between core-polarization and meson exchange (Morita 1985), but individual effects can reach ~40%.
## Induced Currents — Form Factor Classification
See [[corrections/06-recoil]] for detailed equations. Key points:
### First-Class Currents (CVC/PCAC)
- **Vector** $g_V$: conserved, from EM data via CVC
- **Weak magnetism** $(g_M - g_V)/2M$: determined by CVC from M1 transitions
- **Axial-vector** $g_A$: measured in neutron decay
- **Induced pseudoscalar** $g_P/2M$: PCAC predicts ~−229, quenched ~80% in nuclei
### Second-Class Currents (G-parity forbidden)
- **Induced scalar** $g_S/2M$: excluded by CVC for Fermi transitions
- **Induced tensor** $g_{II}/2M$: experimental null results; reduces to first-class component only
## Nuclear Matrix Element Operators (Table V of Hayen)
| Operator | Symbol | Type | $\Delta J$ |
|---|---|---|---|
| $M_F$ | Fermi matrix element | Vector | 0 |
| $M_{GT}$ | Gamow-Teller matrix element | Axial-vector | 0,1 (not 0→0) |
| $M_L$ | Longitudinal vector | — | depends on K |
| $M_{T}, M_{TL}$ | Transverse multipoles | — | depends on K |
## Validity of Impulse Approximation
- **Vector current**: conserved via CVC → impulse approximation reliable
- **Axial current**: broken by PCAC → quenching effects significant (up to 80% for $g_P$)
- Mesonic degrees of freedom absorbed into nuclear potential (Wilkinson 1974)
## Nuclear Deformation Correction $D_C(Z,W,\beta^2)$
The nuclear structure analogue of the electrostatic deformation correction ([[corrections/02-finite-size]]):
- Accounts for non-spherical nuclear shape in convolution terms
- Partially cancels with $DFS$ correction (electrostatic)
- Relevant for deformed nuclei ($\beta_2 \neq 0$)
## Calculator Status
> [!info] Not yet implemented
> The shape factor $C(Z,W)$ is NOT part of the current calculator. This note documents the theory for future implementation. For superallowed Fermi transitions, $C(Z,W) = |M_F|^2 \approx 1$ (isospin symmetry), so it can be approximated as unity in many cases.
@@ -0,0 +1,561 @@
This is a self-archived version of an original article. This version
may differ from the original in pagination and typographic details.
Kostensalo, Joel; Suhonen, Jouni
Author(s):
gA-driven shapes of electron spectra of forbidden β decays in the nuclear shell model
Title:
Year: 2017
Version:
Published version
Copyright: © 2017 American Physical Society. Published in this repository with the kind permission of the publisher.
Rights:
Rights url:
Please cite the original version:
Kostensalo, J., & Suhonen, J. (2017). gA-driven shapes of electron spectra of forbidden β decays
in the nuclear shell model. Physical Review C, 96(2), Article 024317.
https://doi.org/10.1103/PhysRevC.96.024317
PHYSICALREVIEWC96,024317(2017)
g -drivenshapesofelectronspectraofforbiddenβ decaysinthenuclearshellmodel
A
JoelKostensalo* andJouniSuhonen†
DepartmentofPhysics,UniversityofJyvaskyla,P.O.Box35,FI-40014,Finland
(Received10May2017;published21August2017)
The evolution of the shape of the electron spectra of 16 forbidden β− decays as a function of g was
A
studiedusingthenuclearshellmodelinappropriatesingle-particlemodelspaceswithestablished,well-tested
nuclearHamiltonians.Theβspectraof94Nb(6+)→ 94Mo(4+)and98Tc(6+)→ 98Ru(4+)werefoundtodepend
stronglyong ,whichmakesthemexcellentcandidatesforthedeterminationoftheeffectivevalueofg withthe
A A
spectrum-shapemethod(SSM).Astrongg dependenceisalsoseeninthespectrumof96Zr(0+)→ 96Nb(6+).
A
Thisdecaycouldbeusedfordeterminingthequenchingofg insixth-forbiddendecaysinthefuture,whenthe
A
measurementofthespectrumbecomesexperimentallyfeasible.Thecalculatedshell-modelelectronspectraofthe
ground-state-to-ground-statedecaysof87Rb,99Tc,and137Csandthedecayof137Cstotheisomeric11/2−state
in137Bawerefoundtobeinexcellentagreementwiththespectrapreviouslycalculatedusingthemicroscopic
quasiparticle-phononmodel.ThisisfurtherevidenceoftherobustnatureoftheSSMobservedintheprevious
studies.
DOI:10.1103/PhysRevC.96.024317
I. INTRODUCTION valueoftheweakcouplingconstants.IntheSSMtheshapes
of computed and experimental (normalized) electron spectra
At the nuclear level β decay can be considered as a
are compared in order to find the ratio g /g for nonunique
mutual interaction of the hadronic and leptonic currents forbiddenbetaβ−decays,forwhichthes A hap V efactorsdepend
mediated by a massive vector boson W± [1]. The leptonic
ong /g inaverynontrivialway.InRef.[21]theSSMwas
and hadronic currents can be expressed as a mixture of both A V
applied to the fourth-forbidden ground-state-to-ground-state
vector and axial-vector components [2–4]. The weak vector
transitionof113Cdusingthemicroscopicquasiparticle-phonon
andaxial-vectorcouplingconstantsg andg enterthetheory
V A model (MQPM) [22,23], the nuclear shell model (NSM)
when the hadronic current is renormalized at the nucleon
[24–26], the microscopic interacting boson-fermion model
level[5].Theconservedvector-currenthypothesis(CVC)and
(IBFM-2) [27,28], and experimental spectrum of Ref. [29].
partially conserved axial-vector-current hypothesis (PCAC)
The closest match between the theoretical and experimental
yield the free-nucleon values g =1.00 and g =1.27 [6]
V A spectra was found when g /g ≈0.92 for all three nuclear
but inside nuclear matter the value of g is affected by A V
A models.Thehalf-lifecomparisonmethod,ontheotherhand,
many-nucleon correlations and a quenched value might be
gave very different results when different nuclear models
neededtoreproduceexperimentaldata[1].Preciseinformation
were used. In Ref. [30] it was noticed that the shape of the
ontheeffectivevalueofg iscrucialwhenpredictinghalf-lives
A spectrum was largely unaffected by modest changes in the
of neutrinoless double beta decays since the half-lives are
MQPM wave function, even though the predicted half-life
proportionaltothefourthpowerofg [7,8].
A wasaffectedsignificantly.Theobservationsoftheseprevious
The effective value of g has earlier been probed using a
A studiessuggestthattheSSMisveryrobust,quiteinsensitiveto
half-lifecomparisonmethod,inwhichthepredictedandexper-
thedetailsoftheadoptednuclearmeanfieldandHamiltonian.
imentalvaluesarecomparedfordifferentvaluesofg .Thishas
A Itisthusareliabletoolforprobingtheratiog /g .
beendoneforGamow-Tellerandfirst-forbiddendecaysusing A V
The g -driven evolution of the shapes of several exper-
nuclear matrix elements calculated with the proton-neutron A
imentally measurable forbidden nonunique decay spectra in
quasiparticlerandom-phaseapproximation(pnQRPA)[9–13].
medium-heavyodd-AnucleiwasstudiedusingtheMQPMin
Thehalf-lifecomparisonmethodpredictsthatthevalueofg
A Ref.[30].Onlyfourgoodcandidatesfortheapplicationofthe
is quenched significantly in these transitions. Also in the old
spectrum-shape method have been found thus far. These are
studies[14–17]offirst-forbiddenβdecays,strongquenchings
theground-state-to-ground-statedecaysof87Rb,99Tc,113Cd,
werepredicted.Inarecentshell-modelstudy[18]ofr-process
and 115In [20,30]. In the present paper we set out to find
waiting-pointnuclei,astrongquenchingofg wasconfirmed.
A potential new candidates for the application of the SSM in
Thehalf-lifecomparisonmethodcouldbeusedtofindoutif
light to medium-heavy nuclei using the nuclear shell model.
similar quenching of g is seen in highly forbidden unique
A We also compare the NSM and MQPM β spectra of 87Rb,
beta decays by using the half-lives calculated in Ref. [19],
99Tc,and137Cstoseeifthemodelindependenceobservedin
onceexperimentaldatabecomeavailable.
Ref.[21]appliesforthesedecaysaswell.Thedecaysof137Cs
In Ref. [20] the spectrum-shape method (SSM) was tothe3/2+groundstateandthe11/2−isomericstateof137Ba
introduced as a complementary way to study the effective
are particularly interesting for comparing the differences in
the MQPM and NSM spectra, since the MQPM calculations
predictag -independentspectrumshapeforthesedecays.
A
*joel.j.kostensalo@student.jyu.fi This article is organized as follows. In Sec. II we give
†jouni.suhonen@phys.jyu.fi the theoretical background behind the β spectrum shape. In
2469-9985/2017/96(2)/024317(7) 024317-1 ©2017AmericanPhysicalSociety
JOELKOSTENSALOANDJOUNISUHONEN PHYSICALREVIEWC96,024317(2017)
Sec.IIIwedescribetheapplicationofthenuclearshellmodel θ beingtheCabibboangle.Inordertosimplifytheformalism
C
tothespectrumshapeofforbiddenβ− decays.InSec.IVwe it is usual to introduce unitless kinematic quantit(cid:2)ies w
e
=
presentourresultsandinSec.Vwedrawtheconclusions. W /m c2,w =W /m c2,andp =p c/(m c2)= w2−1.
e e 0 0 e e e e
Withtheunitlessquantitiestheintegratedshapefactorcanbe
II. THEORETICALFORMALISM
expressedas
We begin the description of the β− decay by making the (cid:3)
so-calledimpulseapproximation,inwhichattheexactmoment C˜ =
w0
C(w )pw (w −w )2F (Z,w )dw . (6)
of decay the decaying nucleon only interacts via the weak e e 0 e 0 e e
1
interaction and the strong interaction with the other A−1
nucleons can be ignored. Since the vector boson W− has a The shape factor C(w e ) of Eq. (6) contains complicated
largemassandthuspropagatesonlyashortdistance,theflow combinationsofboth(universal)kinematicfactorsandNMEs.
lines of the nucleons, i.e., the hadronic current, and the flow In this paper we use the nuclear shell model to calculate the
lines of the emitted leptons, i.e., the leptonic current, can be one-bodytransitiondensitiesrelatedtotheNMEsoftheshape
considered to interact at a pointlike weak-interaction vertex factor. The details of the shape factor and the constitution
with an effective coupling constant G , the Fermi constant. of its NMEs can be found from Refs. [32,33]. As in the
F
The parity nonconserving nature of the weak interaction is previous SSM studies [20,21,30] we go beyond the earlier
reflected in the fact that the hadronic current can be written studies[32,33]andtakeintoaccountthenext-to-leading-order
at the quark level (up u and down d quarks) as a mixture of
vectorandaxial-vectorcomponentsas[2–4]
Jμ =u¯(x)γμ(1−γ )d(x), (1)
H 5
whereγμ aretheusualDiracmatricesandγ =iγ0γ1γ2γ3.
5
Renormalization effects of strong interactions must be taken
intoaccountwhenmovingfromthequarkleveltothehadron
level.Thehadroniccurrentatthenucleonlevel(neutronnand
protonp)canthenbewrittenas[5,6]
Jμ =p¯(x)γμ(g −g γ )n(x), (2)
H V A 5
where g and g are the weak vector and axial-vector
V A
couplingconstants,respectively.Theconservedvector-current
hypothesisofthestandardmodel(CVC)givesthefree-nucleon
valueg =1.0fortheweakvectorcouplingconstantandthe
V
partially conserved axial-vector-current hypothesis (PCAC)
gives the free-nucleon value g =1.27 for the weak axial-
A
vector coupling constant. Inside the nuclear matter the value
of g is affected by many-nucleon correlations and so the
A
free nucleon value might not be the one to use in practical
calculations [6]. The present paper is a step towards solving
theproblemofwhatvaluetouseforg insidefinitenuclei.
A
Intheimpulseapproximationtheprobabilityoftheelectron
beingemittedwithkineticenergybetweenW andW +dW
e e e
is
G 1
P(W )dW = F C(W )
e e (h¯c)62π3h¯ e
×p cW (W −W )2F (Z,W )dW , (3)
e e 0 e 0 e e
where p is the momentum of the electron, Z is the proton
e
number, F (Z,W ) is the Fermi function, and W is the
0 e 0
end-point energy of the β spectrum. The nuclear structure
informationisburiedintheshapefactorC(w ).
e
Thehalf-lifeofaβ decaycanbewrittenas
κ
t = , (4)
1/2 C˜
whereC˜ istheintegratedshapefactorandtheconstantκ has FIG.1. Normalized electron spectra for the second-forbidden
thevalue[31] nonunique β− decays of 36Cl [panel (a)] and 60Fe [panel (c)], and
thefourth-forbiddennonuniquedecayof50V[panel(b)].Thevalue
κ = 2π3h¯7ln2 =6147s, (5) g V =1.00 was adopted and the color coding represents the value
m5c4(G cosθ )2 ofg .
e F C A
024317-2
g -DRIVENSHAPESOFELECTRONSPECTRAOF ... PHYSICALREVIEWC96,024317(2017)
A
termsoftheshapefunction.Thedetailsofthenext-to-leading tocalculatetheelectronspectraofthesedecays.TheGXPF1A
ordershapefactorsarediscussedin[21]. interactionisverygoodatpredictingthepropertiesofpf-shell
The shape factor C(w ) can be decomposed into vector, nuclei. However, since the computational burden increases
e
axial-vector, and mixed vector-axial-vector parts. In this dramatically when the number of particles approaches half
decompositiontheshapefactoris of the maximum in the valence space, this interaction could
| | | | | | | | | | 60Fe(0+)→ | | 60Co(2+) | | | |
| --- | --- | ------ | --- | --- | --- | --- | ------ | -------- | --------- | --- | -------- | --- | ----- | ----------- |
| | | | | | | | not be | used for | the | | | | decay | transition. |
| | )=g | 2C )+g | 2C | )+g | | | | | | | | | | |
C(w e V (w e A (w e V g A C VA (w e ). (7) Instead, a model space spanning the π0f , ν1p , ν0f ,
| | | V | A | | | | | | | | | 7/2 | 3/2 | 5/2 |
| --- | --- | --- | --- | --- | --- | --- | ------ | ------------------------------------------ | --- | --- | --- | --- | --- | --- |
| | | | | | | | andν1p | orbitalswasadoptedwiththeHorie-Ogawainter- | | | | | | |
1/2
IntegratingEq.(7)overtheelectronkineticenergy,wegetan
| | | | | | | | action [40,41]. | | pfg | shell: | the jj44b | interaction | | developed |
| ---------------------------------------------- | --- | -------- | ----- | ---- | ---- | --- | --------------- | ------- | ------------ | --------- | --------- | ----------- | -------- | --------- |
| analogousexpressionfortheintegratedshapefactor | | | | | | | | | 9 / 2 | | | −1p−0g | | |
| | | | | | | | by Brown | and | Lise t s kiy | for | the 0f | | | model |
| | | | | | | | | | | | | 5/2 | | 9/2 |
| | | | | | | | space (see | endnote | on | Ref. [28] | of | [42]) | was used | for the |
| | | C˜ =g2C˜ | +g2C˜ | +g g | C˜ , | (8) | | | | | | | | |
V V A A V A VA calculation of the 87Rb(3/2−)→ 87Sr(9/2+) transition. For
A=94–99
wherethefactorsC˜ inEq.(8)arejustconstants,independent the decays of the nuclei, the effective interac-
i
| | | | | | | | tion of | Gloeckner | [43] | in the | model | space | spanning | the |
| --- | --- | --- | --- | --- | --- | --- | ------- | --------- | ---- | ------ | ----- | ----- | -------- | --- |
oftheelectronenergy.
| | | | | | | | π1p , | π0g | , ν2s | , and 2d | was | used. | sdgh | shell: |
| --- | --- | --- | --- | --- | --- | --- | -------- | ------- | ----------------- | -------- | ---------- | ----- | --------- | ------ |
| | | | | | | | 1/2 | 9/2 | 1/2 | | 5/2 | | | 11/2 |
| | | | | | | | | | | | 126Sn(0+)→ | | 126Sb(2+) | |
| | | | | | | | finally, | for the | decay transitions | | | | | and |
III. ADOPTEDMODELSPACESANDNUCLEAR 137Cs(7/2+)→ 137Ba(11/2−,3/2+) the Sn100pn interaction
HAMILTONIANS
| | | | | | | | [44]inthe0g | 7/2 | 2s1d0h | 11/2 modelspacewasadopted. | | | | |
| --- | -------- | ------- | ----- | --------- | -------------- | ---- | ----------- | --- | ------ | -------------------------- | --- | --- | --- | --- |
| The | electron | spectra | of 16 | forbidden | β− transitions | were | | | | | | | | |
g
| calculated | for | different | values | of the coupling | constant | A | | | | | | | | |
| ---------- | --- | --------- | ------ | --------------- | -------- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
IV. RESULTSANDDISCUSSION
| using | the nuclear | matrix | elements | produced | by the | nuclear | | | | | | | | |
| ----- | ----------- | ------ | -------- | -------- | ------ | ------- | --- | --- | --- | --- | --- | --- | --- | --- |
shellmodel.Thecalculationsweredoneusingtheshellmodel Below we present our results: the electron spectra of
code NuShellX@MSU [34], with appropriate model spaces 16 forbidden β− decays (Figs. 1–5) and their integrated
and well established Hamiltonians chosen for each studied shape factors (Table I). The electron spectra are discussed
decay individually. No additional truncations, beyond those inSec.IVAandtheintegratedshapefactorsinSec.IVB.
usedintheoriginalworks,wereintroduced.Thecalculations
wererunonadesktopcomputerwitha3.3GHzprocessor,so
| | | | | | | | | A. Electronspectraandtheeffectivevalueofg | | | | | | |
| ---------------------- | --- | ------ | --- | ----------- | ------------ | ---- | ------------------------------------- | ----------------------------------------- | --- | --- | --- | --- | --- | --- |
| the computational | | burden | had | to be taken | into account | when | | | | | | | | A |
| choosingthemodelspace. | | | | | | | Thestudiedspectrafallintothreegroups. | | | | | | | |
TheadoptedmodelspacesandHamiltoniansareasfollows. GROUP 1. The first group, for which the spectra can be
sdshell:forthecalculationofthewavefunctionsandone-body found in Figs. 1 and 2, consists of six transitions for which
| transitiondensitiesrelatedtothedecay36Cl(2+)→ | | | | | 36Ar(0+) | | | | | | | | | |
| --------------------------------------------- | --- | --- | --- | --- | -------- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
theSSManalysishasnotbeendonepriortothisstudy.These
weadoptedthe0d–1smodelspaceandthephenomenological decaysarepotentiallyinterestingforthepracticalapplication
USDBinteraction[35].pfshell:asintheearliershell-model ofthespectrum-shapemethod,sincemostofthemaresecond-
studiesregardingthehalf-livesofthetransitions48Ca(0+)→
| | | | | | | | forbidden | nonunique | transitions | | with | branching | ratios | above |
| --- | --- | --- | --- | --- | --- | --- | --------- | --------- | ----------- | --- | ---- | --------- | ------ | ----- |
48Sc(4+,5+,6+)[36]and50V(6+)→ 50Cr(2+)[37],the0f– 90%. The only exception is the undetected fourth-forbidden
1pmodelspacewiththeinteractionGXPF1A[38,39]wasused β− decay branch of 50V, for which the branching ratio has
TABLEI. UnitlessintegratedshapefunctionsC˜ ofthestudiedtransitionsandtheirvectorC˜ ,axial-vectorC˜ ,andmixedcomponents
| | | | | | | | | | V | | | A | | |
| -------------------------------------- | --- | --- | --- | ----------------------------------------- | --- | --- | --- | --- | --- | ----- | --- | --- | --- | --- |
| C˜ .ForthetotalintegratedshapefactorC˜ | | | | | | | | | =g | =1.0. | | | | |
| VA | | | | thevaluesofthecouplingconstantsweresettog | | | | | V A | | | | | |
| | | | | | | C˜ | | C˜ | | | C˜ | | | C˜ |
| Transition | | | | Type | | | | | | | | | | |
| | | | | | | V | | A | | | VA | | | |
137Cs(7/2+)→137Ba(11/2−) 1stuniq. 9.681×10−11 8.322×10−6 3.761×10−8 8.359×10−6
36Cl(2+)→36Ar(0+) 6.081×10−9 3.152×10−10 −2.746×10−9 3.650×10−9
2ndnonuniq.
60Fe(0+)→60Co(2+) 2.347×10−13 5.232×10−11 −7.000×10−12 4.556×10−11
2ndnonuniq.
94Nb(6+)→94Mo(4+) 2ndnonuniq. 1.598×10−8 1.469×10−8 −3.058×10−8 1.029×10−10
98Tc(6+)→98Ru(4+) 2ndnonuniq. 2.723×10−8 2.544×10−8 −5.254×10−8 1.207×10−10
99Tc(9/2+)→99Ru(5/2+) 2ndnonuniq. 2.240×10−9 2.130×10−9 −4.361×10−9 8.777×10−12
126Sn(0+)→126Sb(2+) 1.422×10−8 7.125×10−9 2.011×10−8 4.145×10−8
2ndnonuniq.
137Cs(7/2+)→137Ba(3/2+) 4.211×10−6 2.836×10−6 6.879×10−6 1.392×10−5
2ndnonuniq.
87Rb(3/2−)→87Sr(9/2+) 3rdnonuniq. 1.185×10−13 2.082×10−14 −9.734×10−14 4.202×10−14
48Ca(0+)→48Sc(4+) 4thnonuniq. 8.946×10−28 5.934×10−29 −4.606×10−28 4.934×10−28
50V(6+)→50Cr(2+) 4thnonuniq. 1.024×10−23 9.137×10−25 2.131×10−24 1.329×10−23
96Zr(0+)→96Nb(4+) 3.176×10−27 3.170×10−28 −2.006×10−27 1.486×10−27
4thnonuniq.
48Ca(0+)→48Sc(5+) 2.577×10−30 3.931×10−25 1.334×10−27 3.945×10−25
4thuniq.
96Zr(0+)→96Nb(5+) 4thuniq. 1.765×10−29 1.551×10−24 6.844×10−27 1.558×10−24
48Ca(0+)→48Sc(6+) 6thnonuniq. 1.124×10−32 3.498×10−34 3.947×10−33 1.554×10−32
96Zr(0+)→96Nb(6+) 5.811×10−33 7.494×10−33 −1.318×10−32 1.234×10−34
6thnonuniq.
024317-3
| JOELKOSTENSALOANDJOUNISUHONEN | | | | PHYSICALREVIEWC96,024317(2017) | | |
| ----------------------------- | --- | --- | --- | ------------------------------ | --- | --- |
FIG.2. SameasFig.1butforthesecond-forbiddennonunique FIG.3. Same as Fig. 1 but for the decays 48Ca(0+)→
decaysof94Nb[panel(a)],98Tc[panel(b)],and126Sn[panel(c)]. 48Sc(4+,5+,6+)[panels(a),(b),and(c)].
beenpredictedtobe≈2%byarecentshell-modelcalculation
| | | | beindependentofthevalueofg | A ,whilethedependencewas | | |
| --- | --- | --- | -------------------------- | ------------------------ | --- | --- |
[36]. An earlier study [30] of the g -driven evolution of the significant for 87Rb and 99Tc. The shell-model and MQPM
A
electronspectraofodd-Anucleidemonstratedthatthesecond spectra of these decays are compared in order to see if the
andespeciallythefourth-forbiddennonuniqueβ-decayspectra 113Cd 115In
| | | | previously observed | model independence | of and | |
| --- | --- | --- | ------------------- | ------------------ | ------ | --- |
dependstronglyontheeffectivevalueoftheweakaxial-vector spectra[21]holdsalsoforthesedecays.
couplingconstant. The spectra presented in Figs. 1 and 2 (GROUP 1) fall
GROUP 2. The second group consists of the fourth- into two subgroups. The spectra of 36Cl, 50V, and 60Fe
48Ca(0+)→ 48Sc(4+,5+,6+) [Fig.1,panels(a),(b),and(c)],and126Sn[Fig.2,panel(c)]
| to-sixth-forbidden | decays | | and | | | |
| ------------------------- | ------ | --- | --- | --- | --- | --- |
| 96Zr(0+)→ 96Nb(4+,5+,6+), | | | | | | |
for which the beta spectra are do not exhibit noticeable dependence on the value of the
presented in Figs. 3 and 4. The decays of 48Ca and 96Zr axial-vector coupling constant. This is very surprising since
thedecayof50Visfourthforbidden,andallthesevenstudied
appeartobedominatedbythetwo-neutrinodoublebetadecay
mode [36,45,46], so the study of these decays is somewhat fourth-forbidden decays in Ref. [30] depend heavily on g .
A
challenging experimentally. However, they can shed light on The spectra of the second subgroup, 94Nb and 98Tc [Fig. 2,
whether the spectra of highly forbidden beta decays are g panels (a) and (b)], are significantly g dependent. The g
| | | | A | | A | A |
| --- | --- | --- | --- | --- | --- | --- |
dependentornot. driven evolution of the spectra is not only very similar for
GROUP 3. The third group consists of four de- these two decays, but also nearly identical to the one of
cays: 137Cs(7/2+)→ 137Ba(11/2−,3/2+), 99Tc(9/2+)→ 99Tc, shown in Fig. 5, panel (c), even though the Q value
| 99Ru(5/2+), | 87Rb(3/2−)→ | 87Sr(9/2+). | | | | |
| ----------- | ----------- | ----------- | --- | --- | --- | --- |
and The electron of these decays varies between 300 keV and 450 keV. A
spectra of these decays was studied previously in [30] using similargroupingphenomenonwasseenforseveraldecaysin
the MQPM. The spectra of the 137Cs decays were found to [30]:forexampletheshapesofthespectraoffourth-forbidden
024317-4
g -DRIVENSHAPESOFELECTRONSPECTRAOF ... PHYSICALREVIEWC96,024317(2017)
A
| | | | in | the present | paper | and | the | fourth-forbidden | | ones | studied |
| --- | --- | --- | ----------- | ------------ | ------------- | ------------- | -------- | ---------------- | --------- | -------- | -------- |
| | | | in | [30]. | The branching | | ratio | is, however, | | so small | that the |
| | | | measurement | | of | this spectrum | | is not | currently | within | reach |
| | | | of | experiments. | | It is | somewhat | striking | that, | from | the 38 |
differentforbiddendecaysstudiedinthepresentpaperandin
| | | | Ref.[30],strongg | | | dependenceisseenonlyineven-forbidden | | | | | |
| --- | --- | --- | ---------------- | --- | --- | ------------------------------------ | --- | --- | --- | --- | --- |
A
decays.
| | | | | In Fig. | 5 the | shell-model | | and | MQPM | spectra | of the |
| --- | --- | --- | --- | ------- | ----- | ----------- | --- | --- | ---- | ------- | ------ |
experimentallyinterestingdecaysfoundinRef.[30]areshown
| | | | for | comparison. | | For 87Rb | [panels | (a) | and (b)], | which | is the |
| --- | --- | --- | ------------------------------------------------- | ----------- | --- | -------- | ------- | --- | --------- | ----- | ------ |
| | | | bestcandidateforthedeterminationofthequenchingofg | | | | | | | | in |
A
| | | | third-forbidden | | | decays, | the MQPM | | and shell-model | | spectra |
| --- | --- | --- | --------------- | --- | --- | ------- | -------- | --- | --------------- | --- | ------- |
99Tc
| | | | are | practically | | identical. | For | | [panels | (c) and | (d)] the |
| --- | --- | --- | --------- | ----------- | --------- | ---------- | ------- | ------ | ------- | ------------- | -------- |
| | | | evolution | | predicted | by | the two | models | is | also similar, | but |
| | | | in | the range | g | =0.9–1.1 | there | is | some | difference | in the |
A
shape,leadingtoapotentialsmalluncertaintyinthevalueof
| | | | g | when | comparing | to | the experimental | | spectrum, | | hopefully |
| --- | --- | --- | --- | ---- | --------- | --- | ---------------- | --- | --------- | --- | --------- |
A
availableinthe(near)future.Thetwostudieddecaysof137Cs
| | | | [panels | (e) | and | (f)] were | found | to be | g A independent | | when |
| --- | --- | --- | ------- | --- | --- | --------- | ----- | ----- | --------------- | --- | ---- |
usingtheMQPMmodelinRef.[30].Theshell-modelspectra
| | | | calculatedforthesedecayswerenotonlyalsog | | | | | | | independent, | |
| --- | --- | --- | ---------------------------------------- | --- | --- | --- | --- | --- | --- | ------------ | --- |
A
butoverlapwiththeMQPMspectraperfectly.Measurements
| | | | of | these | spectra | would | by very | desirable, | | since they | would |
| --- | --- | --- | --- | ----- | ------- | ----- | ------- | ---------- | --- | ---------- | ----- |
shedlightonthequalityofthecalculatedspectra,independent
ofthechosennuclearmodel.
| | | | | | | B. IntegratedshapefactorC˜ | | | | | |
| --- | --- | --- | --- | --------------------------- | --- | -------------------------- | --- | ----------------------- | --- | --- | --- |
| | | | | TheintegratedshapefactorsC˜ | | | | andtheirdecompositionto | | | |
thecomponentsofEq.(8)arepresentedinTableI.Forallthe
studieddecaysthesignofthevectorandaxial-vectorcompo-
nentsispositiveandthesignofthemixedvector-axial-vector
componentvaries.Forthesecond-forbiddennonuniquedecays
| | | | of | 94Nb and | 98,99Tc | which | have | very | similar | spectrum-shape | |
| --- | --- | --- | --- | -------- | ------- | ----- | ---- | ---- | ------- | -------------- | --- |
evolution,thevector-andaxial-vectorcomponentsareroughly
equalandthevector-axial-vectorcomponentisroughlytwice
96Zr(0+)→
| FIG.4. | Same as Fig. 1 | but for the decays | | | | | | | | | |
| ------ | -------------- | ------------------ | --- | --- | --- | --- | --- | --- | --- | --- | --- |
96Nb(4+,5+,6+)[panels(a),(b),and(c)]. aslarge,butwithanegativesign.Theresultingtotalintegrated
| | | | shape | factor | is two | magnitudes | | smaller | than | its components. | |
| --- | --- | --- | ----------------------------------- | ------ | ------ | ---------- | --- | ------- | ----------------- | --------------- | --- |
| | | | Forthesixth-forbiddendecay96Zr(0+)→ | | | | | | 96Nb(6+)theaxial- | | |
ground-state-to-ground-stateβ−decaysof97Zr,101Mo,117Cd,
vectorcomponentis30%largerthanthevectorcomponentand
and119Inhaveanalmostidenticalg evolution. thevector-axial-vectorcomponentisagainroughlythesumof
A
Decays of GROUP 2, 48Ca(0+)→ 48Sc(4+,5+,6+) and thetwoothercomponentsandnegativeinsign.
| 96Zr(0+)→ | 96Nb(4+,5+,6+),werestudiedinordertoseeif | | | 87Rb | | | | | | | |
| --------- | ---------------------------------------- | --- | --- | ---- | --- | ------------- | --- | ---------- | --- | -------- | ------ |
| | | | | For | the | decomposition | | is similar | to | the case | of the |
C˜
thereappearssomeinterestingsystematicbehavior.Sincethe MQPM: the vector component is the largest, the axial-
V
shape of the spectra of fourth-forbidden nonunique decays vectorcomponentC˜ isabout20%ofC˜ ,andthevector-axial-
| | | | | | | A | | | V | | |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
of odd-A nuclei depends on g in a significant way, it vectorcomponentC˜ isthesmallestandhasanegativesign.
| | | A | | | | VA | | | | | |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
is interesting to see if this holds for the even-A nuclei. However,thecomponentsandthetotalintegratedshapefactor
Since the spectrum on 50V is largely independent of the are approximately 30% larger for the MQPM. In the case of
valuesoftheweakcouplingconstants,thedependenceisnot 99TcthedifferenceismuchlargerandtheMQPMresultsare
strong for at least some fourth-forbidden decays. The shape roughlytwicetheshellmodelones.Forthetwostudieddecays
48Ca(0+)→ 48Sc(4+,5+,6+)
of the electron spectra of and of137Csthereisnog dependence.Thedecaytotheisomeric
A
96Zr(0+)→ 96Nb(4+,5+)transitionsisindependentofg ,as 11/2−stateisuniqueandthus(practically)g independent.On
| | | A | | | | | | | A | | |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
showninFig.3,panels(a),(b),and(c)andinFig.4,panels(a) theotherhand,thedecaytothe3/2+groundstateisnonunique
and(b),revealingasecondexception,48Ca(0+)→ 48Sc(4+), independentsincethethreecomponentsofC˜
| | | | butg | A | | | | | | | havethe |
| --- | --- | --- | ---- | --- | --- | --- | --- | --- | --- | --- | ------- |
totheg -dependentfourth-forbiddennonuniquedecays.The samesignandnointerferencebetweenthecomponentsoccurs.
A
sixth-forbidden decay 96Zr(0+)→ 96Nb(6+) [Fig. 4, panel ForthesetwodecaysthenormalizedshellmodelandMQPM
C˜
(c)],ontheotherhand,exhibitsastrongg A dependence.The electron spectra overlap perfectly but the difference in is
evolutionisverydifferentfromtheoneofthe94Nband98Tc considerable. Again, the relative fractional decomposition is
[Fig.2,panels(a)and(b)],and99Tc[Fig.5,panel(c)]studied almost identical for the two nuclear models, but the absolute
024317-5
| JOELKOSTENSALOANDJOUNISUHONEN | | | | | | PHYSICALREVIEWC96,024317(2017) | | | |
| ----------------------------- | --- | --- | --- | --- | --- | ------------------------------ | --- | --- | --- |
FIG.5. Presentlycalculatedshell-modelspectraandtheMQPMspectra,firstpublishedinRef.[30],fortheground-state-to-ground-state
decaysof87Rb[panels(a)and(b)],99Tc[panels(c)and(d)],and137Cs[panel(f)],andthedecayof137Cstotheisomeric11/2−statein137Ba
[panel(e)].Thecolorcodinginthespectraof87Rband99Tcreferstothevalueofg .Theshapeoftheelectronspectraofthetwo137Csdecays
A
isg independent,andthecolorcodinginthesedecaysreferstotheadoptednuclearmodel.Thefree-nucleonvalueg =1.00wasusedinall
| A | | | | | | | V | | |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
calculations.
valueoftheMQPMC˜ is40-foldtheshellmodeloneforthe objective of the study was to find experimentally detectable
decaytothe11/2−state,and8-foldforthedecaytothe3/2+ transitionsforwhichtheshapeoftheelectronspectradepends
state.DuetotheC˜−1dependenceofthehalf-life,thehalf-lives sensitively on the value of the weak coupling constants.
predicted by the two models do not agree with each other at Comparingthecalculatedandmeasuredspectratheeffective
all.Thisisastrongindicationthatthespectrum-shapemethod valueofg canbeextracted.
A
| | | | | | The electron | spectra | of 94Nb(6+)→ | 94Mo(4+) | and |
| --------------------------------------------------- | --- | --- | --- | --- | ------------ | -------- | ------------ | -------- | --- |
| isamorerobusttoolfordeterminingtheeffectivevalueofg | | | | A | | | | | |
| | | | | | 98Tc(6+)→ | 98Ru(4+) | | | |
thanjustasimplehalf-lifecomparison. depend significantly on the effective
| | | | | | value of | g and these transitions | are | thus excellent | new |
| --- | --- | --- | --- | --- | -------- | ----------------------- | --- | -------------- | --- |
A
| | | | | | candidates | for the spectrum-shape | method | (SSM). | Both |
| --- | --- | --- | --- | --- | ---------- | ---------------------- | ------ | ------ | ---- |
V. CONCLUSIONS
| | | | | | of these | decays have almost | a 100% | branching | ratio, |
| --- | --- | --- | --- | --- | -------- | ------------------ | ------ | --------- | ------ |
The evolution of the electron-spectrum shapes of 16 and have been experimentally observed. The shell-model
| | β− | | | | spectra of | the previously | found candidates, | the | ground- |
| --------- | -------------- | ------ | ------------------------- | --- | ---------- | -------------- | ----------------- | ----- | ------- |
| forbidden | decays, driven | by the | value of the axial-vector | | | | | | |
| | | | | | | | 87Rb | 99Tc, | |
coupling constant g , were studied using nuclear matrix state-to-ground-state decays of and agree well
A
elements derived from the nuclear shell model. Established with the microscopic quasiparticle-phonon model predicted
| | | | | | spectra of | a previous study, | which makes | the experimen- | |
| --------------- | ------- | ------------ | --------- | -------- | ---------- | ----------------- | ----------- | -------------- | --- |
| and well-tested | nuclear | Hamiltonians | were used | in these | | | | | |
investigations.Intheβ-decayshapefactorstheusuallyomitted tal measurement of these spectra very desirable. The g A -
next-to-leading-ordertermsweretakenintoaccount.Themain independent shell model and MQPM spectrum shapes of the
024317-6
g -DRIVENSHAPESOFELECTRONSPECTRAOF ... PHYSICALREVIEWC96,024317(2017)
A
137Cs(7/2+)→ 137Ba(11/2−,3/2+) A nuclei with A(cid:2)98 have now been studied. The potential
| | | | transitions overlap | per- | | | | |
| --- | --- | --- | ------------------- | ---- | --- | --- | --- | --- |
fectly.Themeasurementofthesespectrawouldshedlighton new candidates for the spectrum-shape method are heavier,
thequalityofthecalculatedspectra. and the study of these decays requires the adoption of some
othernuclearmodelduetothelargecomputationalburdenand
| Thetransitionsforwhichaheavyg | | | A dependencehasbeen | | | | | |
| ----------------------------- | ------------ | ------------------- | ------------------- | ----------- | ------------------ | ---------------- | ------------ | --------- |
| | | | | | the other problems | with shell-model | calculations | for heavy |
| recorded | thus far are | all even forbidden. | Unlike | in the case | | | | |
ofthefourth-forbiddentransitionsinodd-Anucleistudiedin deformednuclei.
| Ref. [30], | which all | showed a strong | g dependence, | such a | | | | |
| ---------- | --------- | --------------- | ------------- | ------ | --- | --- | --- | --- |
A
dependenceisnotseeninthecorrespondingtransitionsinthe
ACKNOWLEDGMENTS
studiedeven-Anuclei.Thusfarallthefoundtransitionswith
averystronglyg -dependentspectrumshapeareinthemass ThisworkhasbeenpartiallysupportedbytheAcademyof
A
A=94–119.
region Most of the experimentally accessible Finland under the Finnish Centre of Excellence Programme
forbiddennonuniquetransitionsinodd-AnucleiwithA(cid:2)169
| | | | | | 2012-2017 (Nuclear | and Accelerator | Based | Programme at |
| ------------------------------------------------------ | --- | --- | --- | --- | ------------------ | --------------- | ----- | ------------ |
| andsecondorhigher-forbiddennonuniquetransitionsineven- | | | | | JYFL). | | | |
[1] J.Suhonen,FromNucleonstoNucleus:ConceptsofMicroscopic [29] P.Belli,R.Bernabei,N.Bukilic,F.Cappella,R.Cerulli,C.J.
NuclearTheory(Springer,Berlin,2007). Dai, F. A. Danevich, J. R. de Laeter, A. Incicchitti, V. V.
[2] R.P.FeynmannandM.Gell-Mann,Phys.Rev.109,193(1958). Kobychev, S. S. Nagorny, S. Nisi, F. Nozzoli, D. V. Poda, D.
[3] R.P.FeynmannandM.Gell-Mann,Phys.Rev.111,362(1958). Prosperi,V.I.Tretyak,andS.S.Yurchenko,Phys.Rev.C76,
| [4] W.Theis,Z.Phys.150,590(1958). | | | | | 064603(2007). | | | |
| --------------------------------- | --- | --- | --- | --- | ------------- | --- | --- | --- |
[5] K.Zuber,NeutrinoPhysics(InstituteofPhysicsPublishingLtd., [30] J.Kostensalo,M.Haaranen,andJ.Suhonen,Phys.Rev.C95,
| London,2004). | | | | | 044313(2017). | | | |
| ------------- | --- | --- | --- | --- | ------------- | --- | --- | --- |
[6] E.D.Commins,WeakInteractions(McGraw-Hill,NewYork, [31] J. C. Hardy, I. S. Towner, V. Koslowsky, E. Hagberg, and H.
| 2007). | | | | | Schmeing,Nucl.Phys.A509,429(1990). | | | |
| ------ | --- | --- | --- | --- | ---------------------------------- | --- | --- | --- |
[7] J.SuhonenandO.Civitarese,Phys.Rep.300,123(1998). [32] H. Behrens and W. Bühring, Electron Radial Wave Functions
[8] J. Maalampi and J. Suhonen, Adv. High Energy Phys. 2013, andNuclearBetaDecay(Clarendon,Oxford,1982).
505874(2013). [33] M.T.Mustonen,M.Aunola,andJ.Suhonen,Phys.Rev.C73,
[9] H.EjiriandJ.Suhonen,J.Phys.G:Nucl.Part.Phys.42,055201 054301(2006).
(2015). [34] B. A. Brown and W. D. M. Rae, Nucl. Data Sheets 120, 115
| [10] H. Ejiri, | N. Soukouti, | and J. Suhonen, | Phys. Lett. | B 729, 27 | (2014). | | | |
| -------------- | ------------ | --------------- | ----------- | --------- | ------- | --- | --- | --- |
(2014). [35] B.A.BrownandW.A.Richter,Phys.Rev.C74,034315(2006).
[11] P.PirinenandJ.Suhonen,Phys.Rev.C91,054309(2015). [36] M. Haaranen, M. Horoi, and J. Suhonen, Phys. Rev. C 89,
[12] D.S.DelionandJ.Suhonen,Europhys.Lett.107,52001(2014). 034315(2014).
[13] F.F.DeppischandJ.Suhonen,Phys.Rev.C94,055501(2016). [37] M.Haaranen,P.C.Srivastava,J.Suhonen,andK.Zuber,Phys.
[14] E.K.Warburton,Phys.Rev.C42,2479(1990). Rev.C90,044314(2014).
[15] E.K.Warburton,Phys.Rev.C44,233(1991). [38] M.Honma,T.Otsuka,B.A.Brown,andT.Mizusaki,Phys.Rev.
[16] E.K.Warburton,I.S.Towner,andB.A.Brown,Phys.Rev.C C69,034335(2004).
49,824(1994). [39] M.Honma,T.Otsuka,B.A.Brown,andT.Mizusaki,Eur.Phys.
[17] E.K.WarburtonandI.S.Towner,Phys.Rep.242,103(1994). J.A25,499(2005).
[18] Q. Zhi, E. Caurier, J. J. Cuenca-García, K. Langanke, G. [40] H.HorieandK.Ogawa,Prog.Theor.Phys.46,439(1971).
Martínez-Pinedo,andK.Sieja,Phys.Rev.C87,025803(2013). [41] H.HorieandK.Ogawa,Nucl.Phys.A216,407(1973).
[19] J.KostensaloandJ.Suhonen,Phys.Rev.C95,014322(2017). [42] B.Cheal,E.Mané,J.Billowes,M.L.Bissell,K.Blaum,B.A.
[20] M.Haaranen,P.C.Srivastava,andJ.Suhonen,Phys.Rev.C93, Brown, F. C. Charlwood, K. T. Flanagan, D. H. Forest, C.
034308(2016). Geppert,M.Honma,A.Jokinen,M.Kowalska,A.Krieger,J.
[21] M.Haaranen,J.Kotila,andJ.Suhonen,Phys.Rev.C95,024327 Krämer,I.D.Moore,R.Neugart,G.Neyens,W.Nörtershäuser,
| (2017). | | | | | M.Schug,H.H.Stroke,P.Vingerhoets,D.T.Yordanov,andM. | | | |
| ------- | --- | --- | --- | --- | --------------------------------------------------- | --- | --- | --- |
[22] J.ToivanenandJ.Suhonen,J.Phys.G.21,1491(1995). Žáková,Phys.Rev.Lett.104,252502(2010).
[23] J.ToivanenandJ.Suhonen,Phys.Rev.C57,1237(1998). [43] D.H.Gloeckner,Nucl.Phys.A253,301(1975).
[24] B.H.Wildenthal,M.S.Curtin,andB.A.Brown,Phys.Rev.C [44] B. A. Brown, N. J. Stone, J. R. Stone, I. S. Towner, and M.
| 28,1343(1983). | | | | | Hjorth-Jensen,Phys.Rev.C71,044317(2005). | | | |
| -------------- | --- | --- | --- | --- | ---------------------------------------- | --- | --- | --- |
[25] G.Martínez-Pinedo,A.Poves,E.Caurier,andA.P.Zuker,Phys. [45] H. Heiskanen, M. T. Mustonen, and J. Suhonen, J. Phys. G:
| Rev.C53,R2602(R)(1996). | | | | | Nucl.Part.Phys.34,837(2007). | | | |
| ----------------------- | --- | --- | --- | --- | ---------------------------- | --- | --- | --- |
[26] E. Caurier, F. Nowacki, and A. Poves, Phys. Lett. B 711, 62 [46] M. Alanssari, D. Frekers, T. Eronen, L. Canete, J. Dilling,
(2012). M. Haaranen, J. Hakala, M. Holl, M. Ješkovský, A. Jokinen,
[27] F. Iachello and A. Arima, The Interacting Boson Model A. Kankainen, J. Koponen, A. J. Mayer, I. D. Moore, D.
(CambridgeUniversityPress,Cambridge,UK,1987). A. Nesterenko, I. Pohjalainen, P. Povinec, J. Reinikainen, S.
[28] F. Iachello and P. V. Isacker, The Interacting Boson-Fermion Rinta-Antila,P.C.Srivastava,J.Suhonen,R.I.Thompson,A.
Model(CambridgeUniversityPress,NewYork,1991). Voss,andM.E.Wieser,Phys.Rev.Lett.116,072501(2016).
024317-7
@@ -0,0 +1,8 @@
{
"id": "SRC-2026-05-27-003",
"captured": "2026-05-27",
"packet_version": "1.0",
"title": "https://jyx.jyu.fi/bitstreams/eb3a10c8-b94c-4009-8a8e-839bfddc7c58/download",
"url": "https://jyx.jyu.fi/bitstreams/eb3a10c8-b94c-4009-8a8e-839bfddc7c58/download",
"format": "web"
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,8 @@
{
"id": "SRC-2026-05-27-004",
"captured": "2026-05-27",
"packet_version": "1.0",
"title": "https://cea.hal.science/cea-04796921/file/PhysRevC.110.055503.pdf",
"url": "https://cea.hal.science/cea-04796921/file/PhysRevC.110.055503.pdf",
"format": "web"
}
@@ -0,0 +1,504 @@
5202
nuJ
12
]xe-lcun[
1v44571.6052:viXra
The measurement of the 99Tc β-decay spectrum and its implications for the effective
value of weak axial coupling
J. W. Song,1,2 M. Ramalho,3,4 M. K. Lee,1 G. B. Kim,5 I. Kim,5 H. L. Kim,6 Y. C.
Lee,6,7 K. R. Woo,6 J. Kotila,8 J. Kostensalo,9 J. Suhonen,4,10,∗ and H. J. Kim2,†
1Korea Research Institute of Standards and Science, Daejeon 34113, South Korea
2Department of Physics, Kyungpook National University, Daegu 41566, South Korea
3School of Physics, Engineering and Technology,
University of York, Heslington, York YO10 5DD, United Kingdom
4Department of Physics, University of Jyva¨skyla¨,
P. O. Box 35 (YFL), FI-40014, Jyva¨skyl¨a, Finland
5Lawrence Livermore National Laboratory, Livermore, CA 94550, USA
6Institute for Basic Science, Daejeon 34126, South Korea
7Department of Physics and Astronomy, Seoul National University, Seoul 08826, South Korea
8Finnish Institute for Educational Research, University of Jyva¨skyl¨a, Jyv¨askyla¨, Finland
9Natural Resources Institute Finland, Yliopistokatu 6B, FI-80100 Joensuu, Finland
10International Centre for Advanced Training and Research in Physics (CIFRA), 077125 Bucharest-Magurele, Romania
(Dated: June 24, 2025)
Measurements of β-spectral shapes is an important way to examine the effective value of the
weakaxialcouplingg . Thesestudiesfocusspecificallyonforbiddennon-uniqueβ− transitions,as
A
only in these cases is the spectral shape directly sensitive to the ratio g /g . Here, the value of
A V
the weak vector coupling constant, g , is fixed at 1.0 according to the Conserved Vector Current
V
(CVC) hypothesis. In previous studies for the fourth-forbidden non-unique β− decays of 113Cd
[J. Kostensalo et al., Phys. Lett. B 822, 136652 (2021)] and 115In [A. F. Leder et al., Phys.
Rev. Lett. 129, 232502 (2022) and L. Pagnanini et al., Phys. Rev. Lett. 133, 122501 (2024)] a
quenchedvaluewasdeterminedfortheratiog /g usingg =1.0. Anotableexceptionistherecent
A V V
measurementandanalysisofthesecond-forbiddennon-uniqueβ-decaytransitionin99Tc,performed
byM.Paulsenet al.,Phys. Rev. C110,05503(2024). Whereanenhancedratiog /g =1.526(92)
A V
was suggested. To resolve this apparently contradictory situation with the effective value of g , we
A
haveperformedcalculationsbasedonthenuclearshellmodel(NSM)Hamiltoniansglekpn,jj45pnb,
andtheMQPMapproachwithacarefulconsiderationofthesmallrelativisticvectornuclearmatrix
element(sNME).Thetheoreticalspectrawerecomparedtothe99Tcβ-decayspectrumbyusingthe
4πgoldabsorberwithaMetallicMagneticCalorimeter(MMC).Inallcases,wefoundthatthedata
matches well with reduced g /g values of 1.0–1.2. Our result contradicts the previously reported
A V
measurementfor99Tcandinsteadsupportsaquenchedaxialcouplingasreportedforotherisotopes.
Introduction.—Electrons and electron antineutrinos turebeyond-the-standard-modelexperiments;seethere-
are emitted in β− decays as a result of a transmuta- views [1–3].
tion of a parent nucleus (Z,N) to a daughter nucleus
In allowed β decays, such as Fermi and Gamow–Teller
(Z +1,N −1), Z being the number of protons and N
transitions, the emitted lepton pair (electron and an-
the number of neutrons. Studies of β-electrons (elec-
tineutrino)carrieszeroorbitalangularmomentum[4]. In
trons emitted in β− decays) have become an important
contrast,forbiddendecaysoccurwhentheemittedlepton
subject of study, e.g., in connection with nuclear reac-
pair carries nonzero orbital angular momentum [5]. The
tors and the anomalies in their fluxes of antineutrinos,
degreeofforbiddennesscanrangefromfirst-forbiddento
and rare-events experiments searching for beyond-the-
n:th-forbidden, with each additional level typically in-
standard-modelphysics,likethosetryingtomeasurerare
creasing the decay half-life by a factor of approximately
β-decays and double beta decays. The β-electrons can 104. Forbidden β-transitions are generally classified into
also be severe backgrounds in these experiments. In all
two types: unique and non-unique. Unique transitions
the aforementioned cases, measurements of the associ-
are characterized by a single dominant nuclear matrix
atedβ-spectralshapeisessential. Distortionsinβ-decay
element (NME) and thus exhibit a universal β-spectral
spectral shapes also provide important input for under-
shapethatisindependentofnuclearstructure[4]. Incon-
standing the weak axial coupling constant g , which
A trast, non-uniquetransitionsinvolveacomplexinterplay
is relevant for sensitivity estimates in current and fu-
of several NMEs, making the β-decay spectrum highly
sensitivetothenuclearwavefunctionsofboththeinitial
and final states [5–7].
∗ jouni.t.suhonen@jyu.fi The (partial) half-life of a forbidden non-unique β-
† hongjoo@knu.ac.kr transitionsalsodependsonthevaluesoftheweakvector
2
and axial-vector couplings, g and g , respectively [5– plings by writing
V A
7]. The conserved vector current (CVC) hypothesis
sets g V = 1.0 whereas the partially conserved axial- C(w e )=g V 2C V (w e )+g A 2C A (w e )+g V g A C VA (w e ) (2)
vector current (PCAC) hypothesis leads to an effective
where w is the total (rest-mass plus kinetic) energy of
(quenched) value of geff in finite nuclei [8], its bare- e
A the emitted electron. The shape of the β-electron spec-
nucleon value g =1.27 resulting from the experimental
A trum is sensitive to the value of g due to subtle in-
studies of the decay of an isolated neutron. The quench- A
terference effects among the vector C (w ), axial-vector
ing of the effective value of g has recently been studied V e
A C (w), and mixed vector–axial-vector C (w) contribu-
extensively in [2, 8, 9], and in particular its effects on A VA
tionstothetransitionmatrixelement[6]. Forsomenuclei
the neutrinoless double beta decay have been addressed
(veryfew,ascharteduptopresentdays[2,18,19,21,22])
in [1, 3]. To quantify the quenched value of g , com-
A this causes measurable changes in the β-spectra. Thus,
binedexperimentalandtheoreticalanalysesofβ-spectral
the effective value geff can be found by computing the-
shapes of individual β− transitions have recently been A
oretical β-spectra for a set of geff values and comparing
performed for the fourth-forbidden non-unique β-decays A
themwiththemeasuredone. Thismethod,coinedasthe
of 113Cd [10–13] and 115In [14–16].
spectrum-shape method (SSM), was originally proposed
A recent measurement of the β-decay spectrum
in [6].
for the second-forbidden non-unique ground-state-to-
Another factor influencing theoretical analyses of β-
ground-state transition 99Tc(9/2+) → 99Ru(5/2+),
spectral shapes and half-lives is the so-called small rela-
which has a 100% branching ratio, was reported by
tivistic vector nuclear matrix element (sNME) [5]. The
Paulsen et al. [17]. The accompanying theoretical analy-
sNME gathers contributions outside the valence major
sisyieldedacontradictoryresult,suggestinganenhance-
shell that contains the proton and neutron Fermi sur-
ment of the axial coupling with a value of g /g =
A V faces, which makes its calculation particularly hard for
1.526(92) using g =1.0.
V nuclear-theoryframeworksused,e.g.,inthepresentwork
ThegA-EXPERimentandTheory(gA-EXPERT)col-
and also in the work of Paulsen et al. [17]. Despite its
laboration is an international effort dedicated to quan-
smallness, the sNME can influence the β-spectral shapes
tifying the effective axial-vector coupling constant, geff,
A and half-lives quite strongly [11, 12, 19, 21].
by precisely measuring β-decays of various nuclei and
ArealisticvalueofthesNMEcanbeestimatedfromits
comparing the results with state-of-the-art nuclear the-
CVC value [5], first used for spectral-shape calculations
ory calculations. To address the tension raised by the
in Ref. [18] and later by Paulsen et al. [17]. It should be
enhanced value of geff = 1.526(92) reported in Ref. [17],
A noted,though,thattheCVCvaluerepresentsan“ideal”,
we performed a high-precision measurement of the 99Tc
pertaining to a perfect many-body theory, which is not
β-decay spectrum using metallic magnetic calorimeter
the case for the nuclear-structure frameworks typically
quantum sensors with a low-energy threshold of 10 keV.
used for β-decay calculations of heavy open-shell nuclei,
The measured spectrum was compared to predictions
like the presently discussed 99Tc. To circumvent the use
from three nuclear models, each based on distinct nu-
of the CVC value, the experimental works [11, 13, 15]
clear Hamiltonians. In this Letter, we present a quan-
and theoretical works [12, 16] used sNME as a fitting
titative comparison between data and modern nuclear
parameter, together with geff. By this, it was possible to
theory,providingnewconstraintsongeff anddemonstrat- A
A reproduce both the measured β-spectral shape and the
ing the sensitivity of precision β-spectroscopy to nuclear
experimental (partial) half-life of the corresponding β-
structure effects.
transitionwithoutlosingtheCVCvalueg =1.0forthe
Theory.—Tofirstorder,thehalf-lifeofaβ-decaytran- A
vector coupling. This approach, termed the enhanced
sition can be written as:
spectrum-shape method (ESSM) in Ref. [19], is adopted
κ in the present work.
t = , (1)
1/2 C˜ In this Letter, we present new theoretical calculations
of the β spectrum of 99Tc (with Z = 43 and N = 56)
where κ is a constant [18, 19] and C˜ is the integrated and compare them to the measured spectrum in order
shapefunctionC,whichincorporatesvariousphase-space to extract the geff, using ESSM. To gain preliminary in-
A
factors and NMEs arising in the next-to-leading-order sightsintotheeffectiveaxialcouplinggeff intheβ-decay
A
expansion, as discussed in detail in Refs. [6, 7]. Our β- of 99Tc, we performed nuclear shell model (NSM) cal-
spectral shape and half-life analyses employ screening, culations and compared the results with the measured
radiative, and atomic-exchange corrections. For electron spectrum reported in Ref. [17]. As the β-decay rate
energies below some tens of keV, the distortion in the β- has a quadratic dependence on the sNME, two solutions
decay spectral shape is mostly dominated by the atomic werefoundforeachvalueofgeff,onecloserto[sNME(c)]
A
exchange correction, as originally studied for allowed β- and one further away from [sNME(f)] CVC value. In
decays [20]. This correction is responsible for the sharp references [21, 23] the related two sets of theoretical β-
upward trend of the spectral shape at low energies. spectra of 99Tc were introduced for two NSM Hamilto-
The complexity of the shape function C can, however, nians, namely jj45pnb [24] and glekpn [25]; In that pre-
be cast in a very simple dependence on the weak cou- liminary study, for the glekpn interaction we’ve used a
3
set of single particle energies fit to the A=94−98 mass below 1 Bq.
region, however here we use the default single particle
energies since it yields better experimental agreement
for this isotope. Comparison with the measured spec-
trum[17]indicatedthatbothsNMEvaluesusedwiththe
jj45pnb Hamiltonian yielded good agreement, while the
fit is somewhat poorer with sNME(c), and significantly
worsewithsNME(f)whenusingtheglekpn Hamiltonian,
further details of this preliminary study and its calcula-
tions can be found in Refs [21, 23].
In addition to the two NSM Hamiltonians described
above, we also employ the nuclear many-body method
known as the Microscopic Quasiparticle-Phonon Model
(MQPM). A concise presentation of the essentials of
NSM, and MQPM can be found in Ref. [2]. It should
be pointed out here that Ref. [17] employed the NSM
in their analysis, adopting the CVC value for the sNME
withoutfittingittotheexperimentalhalf-lifeofβ-decay.
A systematic comparison of various theoretical models
with the measured spectrum offers a more robust frame-
work for extracting the geff from high-precision spectral
A
data.
Experiment.— Metallic magnetic calorimeters
(MMCs) are some of the most advanced cryogenic FIG. 1. The detector setup used for the β-decay measure-
ment. The gold sample containing the 99Tc source is ther-
detectors for precision energy measurements. These
mally coupled to the MMC sensor via gold wire bonds. A
devices utilize paramagnetic ions embedded in metal-
support washer with a Kapton tape on top for thermal insu-
lic hosts—typically Au:Er or Ag:Er—as temperature
lation was placed below the sample to align its height with
sensors thermally coupled to metallic absorbers. The
that of the MMC sensor.
paramagnetic material is placed in a weak magnetic
field (<10 mT) and operated at temperatures below The detector setup consists of an MMC and a Mag-
100 mK. When an incident particle deposits energy nicon SQUID (Model X1) for signal readout (see Fig-
E in the absorber, it induces a temperature rise ∆T ure 1). The detector was installed and operated in a
in the paramagnetic bath. This temperature increase pulsed tube pre-cooled 3He/4He dilution refrigerator at
leads to a decrease in the total magnetization of the Lawrence Livermore National Laboratory (LLNL). The
paramagneticions,whichissensedbyasuperconducting signal was amplified using an additional array SQUID
pickupcoil. Themagneticsignalisthentransmittedtoa (Model X16) installed at the 4 K stage.
superconducting quantum interference device (SQUID) The output was low-pass filtered using a Stanford Re-
for precise readout. In this work, we employed an search Systems SRS 560 amplifier [30] with a 30 kHz
MMC fabricated at the Korea Research Institute of cutoff frequency. Data acquisition was performed over
Standards and Science (KRISS), incorporating 420 ppm 34 hours at 15 mK in un-triggered continuous mode to
of Ag:168Er as the sensor material Refs [26, 27]. capture the full information, with a sampling rate of
Radionuclide sources were prepared in the form of 4π 200 kS/s. A software trigger was applied using a trape-
solid-angle absorbers. A solution of 99Tc dissolved in ni- zoidal filter with a 1-ms shaping time.
tric acid was dropped onto a gold foil (2 mm × 2 mm For energy calibration, an external 133Ba source was
× 33 µm) and subsequently dried. The foil, containing used as a reference, while a 148Gd source was introduced
approximately 10 Bq of 99Tc activity, was then folded inside the setup for internal calibration. Although the α
androlledthreetimesinaprocessknownasthekneading signals from 148Gd were not observed due to slight mis-
method[28,29]. Thistechniqueenhancestheuniformity alignment, characteristic X-rays from 148Gd interactions
of 99Tc distribution within the absorber and improves weresuccessfullydetected. Thesimultaneousobservation
energyresolution. Aportionofthekneadedfoilwascut, ofγraysfrom133BaandX-raysresultingfrom148Gdpro-
encapsulated between two 25 µm thick gold foils, and videdmultiplereferencepointsforaccuratecalibrationof
compressed using a rolling machine. The final encapsu- the energy scale.
lated sample, measuring 1.2 mm × 1.3 mm × 50 µm, Two trapezoidal filters with different shaping parame-
was thermally coupled to the MMC sensor. The total ters were applied to the raw pulses. The long trapezoid
absorber mass was 1.75 mg, and the total heat capacity, was used to obtain information about the entire DC sig-
includingthecontributionfromtheparamagneticionsin nal, while the short trapezoid was designed to capture
the MMC sensor, was approximately 400 pJ in the 15- information corresponding only to the rise time of the
50 mK temperature range. The final 99Tc activity was DC signal [31, 32]. All data were analyzed using custom
4
MATLAB○R
scripts [33]. five most prominent γ peaks from 133Ba: 383.8 keV,
Results.—Inthebackgrounddataset,atotalof130,001 356.0 keV, 302.8 keV, 276.4 keV, and 80.9 keV. The re-
sultingcalibrationfunctionwasthenappliedtotheanal-
| events | were | recorded. | Among | them, | 80,903 | events ex- | | | | | | | | |
| ------ | ---- | --------- | ----- | ----- | ------ | ---------- | --- | --- | --- | --- | --- | --- | --- | --- |
ceededtheenergythresholddefinedfortheanalysisas10 ysis dataset. The positions of the Gd X-ray peaks were
keV. A pulse shape discrimination (PSD) cut was sub- compared between the calibration and analysis datasets,
sequently applied to select events consistent with 99Tc andnosignificantdiscrepancieswereobserved. Thiscon-
signal-like pulses. After that, 77,487 events remained for sistency validated the use of the same calibration func-
further analysis. tion for both datasets. Specifically, the 42.3097 keV,
Similarly, the calibration dataset obtained with an ex- 42.9968 keV, and 48.6957 keV peaks in the two datasets
| | 133Ba | | | | | | differedby0.14(11)keV,0.093(18)keV,and0.38(5)keV, | | | | | | | |
| ------ | ----- | ------ | --------- | ------ | ------- | ------ | ------------------------------------------------- | --- | --- | --- | --- | --- | --- | --- |
| ternal | | source | contained | 47,491 | events. | Out of | | | | | | | | |
these, 35,821 events passed the energy threshold. Af- respectively—well within the corresponding uncertain-
| ter applying | | the same | PSD | criteria, | 23,596 | events were | ties. | | | | | | | |
| ------------ | --- | -------- | --- | --------- | ------ | ----------- | ----- | --- | --- | --- | --- | --- | --- | --- |
retained.
FIG.2. 99Tcspectrumwithexternal133Bacalibrationsource.
| Details | of the | X-ray peaks | are | provided | in Table | I. | | | | | | | | |
| ------- | ---------- | ----------- | -------- | -------- | -------- | ---- | -------- | -------- | ---- | ----------- | ----- | ----------- | ----- | ------- |
| | | | | | | | FIG. 3. | Spectrum | of | calibrated | 99Tc, | plotted | with | a 1 keV |
| Figure | 2 presents | | the 99Tc | spectrum | measured | with | | | | | | | | |
| | | | | | | | binning, | compared | with | theoretical | | predictions | based | on the |
the external 133Ba source in place. Characteristic X-ray farCVC variants of the glekpn, jj45pnb, and MQPM models.
peaksfromGdandPtfluorescence,aswellasγlinesfrom Themodelswerefittedusingaχ2 method. Theresidualgraph
ν
133Ba, are clearly visible [34]. These peaks, along with below was calculated over the energy range from 0–280 keV,
| | | 133Ba | | | | | corresponding | | to the | χ2 fitting | range. | | | |
| -------- | ----------- | ----- | --------------- | ------------ | ----------- | ---------- | ------------- | ---- | ------ | ---------- | ------ | --------- | --- | --------- |
| the γ | lines from | | and | escape peaks | due | to partial | | | | ν | | | | |
| energy | deposition, | were | used | for energy | calibration | [35]. | | | | | | | | |
| The peak | positions | | were determined | | by Gaussian | fitting | | | | | | | | |
| | | | | | | | In our | ESSM | theory | framework, | | templates | | of the β- |
withalinearbackgroundmodel,andtheresultsaresum- spectra were generated for a set of g values (with
A
marized in Table I. g = 1.0), adjusting simultaneously the sNME value(s)
V
Mostpeaksexhibitexcellentagreementwithliterature to correctly reproduce the decay half-life. Then, reduced
values,with∆E typicallybelow0.3keVandenergyreso- chi-square (χ2) fitting was performed using the provided
ν
lutionsbetterthan1keVinthe66–383keVenergyrange. models,applyingsimpleshapecomparisonsandcalculat-
TheentriesinTableIlabeledas“Notdistinguished,”in- ingresidualstoidentifytheenergyregionswiththemost
| cluding | K and | K | transitions | from | Gd X-ray | and Au | | | | | | | | |
| ------- | ----- | --- | ----------- | ---- | -------- | ------ | ----------- | ----------- | --- | --- | --- | -------- | ---- | -------- |
| | β3 | β1 | | | | | significant | deviations. | | The | bin | size for | each | spectrum |
escape lines, correspond to peaks with low relative in- was set to 1 keV.
tensities [36]. Given the limited event statistics, these Areducedchi-squared(χ2)fittingprocedurewasthen
ν
| peaks | could | not be | individually | resolved | in | the spectral | | | | | | | | |
| ----- | ----- | ------ | ------------ | -------- | --- | ------------ | --- | --- | --- | --- | --- | --- | --- | --- |
appliedtocomparethetheoreticalmodelswiththemea-
fit. Theuncertaintiesreportedforeachvaluereflectonly suredspectrum. Thebinsizeforeachspectrumwassetto
thestatisticalerrorsderivedfromtheGaussianfits,with- 1keV.Thefitswereperformedbyminimizingχ2 overthe
ν
out accounting for potential systematic contributions. energy ranges 10–41 keV and 50–280 keV, excluding the
148Gd
The Gd X-ray peaks from the internal source GdX-rayregion. Simpleshapecomparisonsandresidual
and the 133Ba γ lines were used as primary reference analyseswereusedtoidentifyenergyregionswithsignifi-
points for the calibration. Additional fluorescence lines cantdeviations. Figure3visuallydemonstrateshowwell
| fromPtareattributedtomaterialsinthedetectorsetup | | | | | | | | | | | 99Tc | | | |
| ------------------------------------------------ | --- | --- | --- | --- | --- | --- | ---------- | ------ | ---- | --- | ---- | ----- | --- | --- |
| | | | | | | | each model | aligns | with | the | | data. | | |
and collimator. Several low-energy escape peaks (below We performed the fitting using geff with both the
A
20keV)showbroaderFWHManddegradedrelativereso- closeCVC[sNME(c)]andfarCVC[sNME(f)]models,and
lutionduetoincreasednoiseandlimitedeventstatistics. the results are summarized in Table II. For the farCVC
modelatgeff
A linear energy calibration was performed using the cases,theglekpn =1.2yieldsthebestagree-
A
5
TABLE I. Measured X-ray and gamma-ray lines, including their tabulated and measured energies.
Tabulated Measured FWHM
Origin Radiation type ∆E [keV]
energy [keV] [34, 35] energy [keV] resolution [keV]
X-rayK 42.3097(13) 42.30(10) 0.9(4) 0.01(10)
α2
Gdfluorescence X-rayK 42.9968(12) 42.9427(31) 0.686(10) 0.0541(33)
α1
frominternalsource X-rayK 48.5505(21)
β3 48.29(5)a 1.05(22) 0.41(5)
X-rayK 48.6957(21)
β1
Ptfluorescence X-rayK 65.1226(21) 65.005(22) 0.96(6) 0.118(22)
α2
fromcollimator X-rayK 66.8311(20) 66.78(5) 0.65(15) 0.06(5)
α1
γ 80.9979(11) 80.881(24) 0.77(8) 0.117(25)
γ 276.3989(12) 276.12(5) 0.57(13) 0.28(5)
133Bacalibrationlines γ 302.8508(5) 302.764(21) 0.60(5) 0.087(21)
γ 356.0129(7) 355.926(9) 0.653(21) 0.087(9)
γ 383.8485(12) 383.69(6) 0.82(14) 0.16(6)
Absorbed Escaping
133Ba γ line [keV] Au X-ray [keV]
66.993(23)[K ] 316.8555(26) 316.817(18) 0.37(5) 0.038(18)
383.8485 α2
68.8069(22)[K ] 315.0416(25) 315.09(4) 0.72(9) 0.05(4)
α1
66.993(23)[K ] 289.0199(24) 288.943(21) 0.51(5) 0.077(21)
α2
68.8069(22)[K ] 287.206(23) 287.051(26) 0.59(6) 0.155(26)
356.0129 α1
77.5773(35)[K ] 278.4356(36)
β3 277.911(31)a 0.88(8) 0.122(31)
77.983(37)[K ] 278.0329(38)
Escapelines β1
66.993(23)[K ] 235.8578(24) 235.659(23) 0.47(6) 0.199(23)
302.8508 α2
68.8069(22)[K ] 234.0439(23) 233.99(4) 0.63(11) 0.05(4)
α1
66.993(23)[K ] 209.4059(26) 209.137(12) 0.72(4) 0.269(12)
276.3989 α2
68.8069(22)[K ] 207.592(25) 207.36(5) 0.62(13) 0.24(5)
α1
66.993(23)[K ] 14.0049(25) 13.87(5) 1.49(21) 0.13(5)
80.9979 α2
68.8069(22)[K ] 12.191(25) 12.144(17) 0.97(6) 0.046(17)
α1
a Notdistinguished
considered, glekpn slightly outperforms the others, but
TABLE II. Results for the three considered nuclear models
allthreesuggestthataquenchedaxial-vectorcouplingof
ontheparametersofinterestg . Thereducedchi-squareand
A geff =1.0–1.2providesthebestagreementwiththedata.
p-value are also reported as an estimator of the goodness of A
Our result shows a clear deviation from that reported in
fit.
Ref. [17], and instead exhibits better consistency with a
Model geff sNME[fm3] χ2 p-value
A ν quenched g value comparable to those extracted from
CloseCVC A
glekpn 1.1 0.0674 1.1057 0.12 other nuclei.
jj45pnb 1.0 0.0681 1.2436 0.004 Conclusions.—In this work, we performed a high-
MQPM 1.0 0.0651 1.6437 <0.0001 resolutionmeasurementofthe β-decay spectrum of99Tc
usingmetallicmagneticcalorimeterquantumsensorsand
FarCVC(Bestfit)
glekpn 1.2 -0.0698 1.0664 0.22 compared the results with predictions from multiple nu-
jj45pnb 1.0 -0.0669 1.0966 0.14 clear models. The extracted value of the effective axial-
MQPM 1.0 -0.0683 1.0951 0.14 vector coupling, geff, deviates from the enhanced value
A
reported in Ref. [17] and is more consistent with values
observed in other nuclei. We attribute this discrepancy
ment with the experimental spectrum, with a reduced primarilytothedifferenttreatmentofthesNMEadopted
chi-squared value of χ2 = 1.0664 and a corresponding in Ref. [17]. By applying the ESSM, which simultane-
ν
p-value of 0.22. The jj45pnb model at geff = 1.0 also ously fits the measured β-spectrum and reproduces the
A
provides a good match with χ2 = 1.0966 and p-value of partialhalf-life,weplacedmorerobustconstraintsongeff
ν A
0.14. Similarly, the MQPM model at geff = 1.0 gives χ2 based on three nuclear theory approaches.
A ν
= 1.0951 and p-value of 0.14. All fits were performed Our results for the second-forbidden non-unique β−
with ν = 261 degrees of freedom. decay of 99Tc provide valuable insight into the effective
The reduced chi-squared values close to unity and p- axial-vector coupling strength geff in the mass region.
A
values well above 0.05 indicate that all three farCVC Theextractedquenchingpatternisconsistentwiththose
models are statistically compatible with the measured observed in heavier nuclei, such as 113Cd [10–13] and
spectrum. This consistency supports the reliability of 115In [14–16], suggesting that the quenching of g may
A
the farCVC scenario, where the sNME is adjusted to re- follow a systematic trend across mass regions. This im-
produce the experimental half-life. Among the models pliesthatthesamelevelofquenchingcouldbeappliedto
6
bothallowedandforbiddenvirtualtransitionscontribut- istryofResearch,InnovationandDigitization(theNEP-
ing to 0νββ NMEs, thereby simplifying the theoretical TUN project). M. Ramalho acknowledges support by
evaluation of NMEs. theOskarHuttunenFoundation. Weacknowledgegrants
In particular, our findings can inform the modeling of of computer capacity from the Finnish Grid and Cloud
100Mo, Infrastructure (persistent identifier urn:nbn: fi:research-
| 0νββ decay | in | | which | lies | in the | same | mass re- | | | | | | | |
| ---------- | ----- | ------ | -------- | --------- | ------ | -------- | -------- | ------------------ | --- | --- | ----------- | --- | ------ | ---- |
| | | | | | | | | infras-2016072533) | | and | the support | by | CSC–IT | Cen- |
| gion as | 99Tc. | If the | observed | quenching | | behavior | per- | | | | | | | |
sists across neighboring nuclei, it could facilitate a more ter for Science, Finland, for generous computational re-
| | | | | | | | | sources. | G. B. | Kim | and I. Kim | acknowledge | | support |
| ----------- | ------------ | --------- | ------- | ------------ | ---- | ------- | ------ | -------- | --------------- | --- | ---------- | ----------- | --- | ------- |
| unified and | robust | framework | | for | 0νββ | nuclear | matrix | | | | | | | |
| | | | | | | | | from the | U.S. Department | | of Energy, | performed | | under |
| element | calculations | | in this | mass region. | | | | | | | | | | |
theauspicesofLawrenceLivermoreNationalLaboratory
| These | results | demonstrate | | the sensitivity | | of | precision | | | | | | | |
| -------------- | ------- | ----------- | --- | --------------- | ------- | --- | --------- | -------------- | -------------- | ------------------ | -------- | -------- | --------- | ------ |
| | | | | | | | | under Contract | | DE-AC52-07NA27344. | | | This work | was |
| β-spectroscopy | | to nuclear | | structure | effects | and | provide | | | | | | | |
| | | | | | | | | funded by | the Laboratory | | Directed | Research | and | Devel- |
valuable benchmarks for the development and validation opment program of Lawrence Livermore National Labo-
| of nuclear | theory—an | | effort | central | to the | mission | of the | | | | | | | |
| ---------- | --------- | --- | ------ | ------- | ------ | ------- | ------ | --- | --- | --- | --- | --- | --- | --- |
ratory (23-LW-043).
| gA-EXPERT | | collaboration. | | | | | | | | | | | | |
| --------- | --- | -------------- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
ThisworkwascarriedoutwithinthegA-EXPERiment
Acknowledgments.— H. J. Kim acknowledges support and Theory (gA-EXPERT) collaboration—an interna-
by the National Research Foundation of Korea (NRF) tional effort involving Kyungpook National University
grant, funded by the Korean government (MSIT), Con- (KNU), the Institute for Basic Science (IBS), the Korea
tract No. RS-2024-00348317. J. W. Song acknowledges Research Institute of Standards and Science (KRISS),
support by the National Research Foundation of Korea the University of Jyv¨askyl¨a, and the Finnish Institute
(NRF)grant,fundedbytheMinistryofEducation,Con- of Natural Resources (Luke)—dedicated to determining
tract No. RS-2023-00274589. J. Kotila and J. Suhonen theeffectiveaxial-vectorcouplingconstant, geff, through
A
acknowledge support from project PNRR-I8/C9-CF264, precision β-decay measurements and state-of-the-art nu-
Contract No. 760100/23.05.2023 of the Romanian Min- clear theory comparisons.
[1] J. Engel and J. Menendez, Rep. Prog. Phys. 60, 046301 [19] A. Kumar, P. C. Srivastava, and J. Suhonen, Eur. Phys.
| (2017). | | | | | | | | J. A | 57, 225 | (2021). | | | | |
| ------- | --- | --- | --- | --- | --- | --- | --- | ---- | ------- | ------- | --- | --- | --- | --- |
O.Nitescu,S.Stoica,andF.Sˇimkovic,Phys.Rev.C107
| [2] H. Ejiri, | J. | Suhonen, | and | K. Zuber, | Phys. | Rep. | 797, 1 | [20] | | | | | | |
| ------------- | --- | -------- | --- | --------- | ----- | ---- | ------ | ------ | ------- | --- | --- | --- | --- | --- |
| (2019). | | | | | | | | (2023) | 025501. | | | | | |
[3] M. Agostini, G. Benato, J. A. Detwiler, J. Men´endez, [21] M. Ramalho and J. Suhonen, Phys. Rev. C 109, 034321
| and | F. Vissani, | Rev. | Mod. | Phys. | 95, 025002 | | (2023). | (2024). | | | | | | |
| --- | ----------- | ---- | ---- | ----- | ---------- | --- | ------- | ------- | --- | --- | --- | --- | --- | --- |
[4] J.Suhonen,From Nucleons to Nucleus: Concepts of Mi- [22] M. Ramalho, J. Suhonen, A. Neacsu, and S. Stoica,
croscopic Nuclear Theory (Springer, Berlin, 2007). Front. Phys. 12:1455778 (2024).
[5] H. Behrens and W. Bu¨hring, Electron Radial Wave [23] M. Ramalho and J. Suhonen, Il Nuovo Cimento 47 C,
| Functions | | and Nuclear | Beta-decay | | (International | | Series | 377 (2024). | | | | | | |
| --------- | --- | ----------- | ---------- | --- | -------------- | --- | ------ | ----------- | --- | --- | --- | --- | --- | --- |
of Monographs on Physics) (Clarendon Press, Oxford, [24] A. F. Lisetskiy, B. A. Brown, M. Horoi, and H. Grawe,
| 1982). | | | | | | | | Phys. | Rev. C | 70, 0444314 | (2004). | | | |
| ------ | --- | --- | --- | --- | --- | --- | --- | ----- | ------ | ----------- | ------- | --- | --- | --- |
[6] M. Haaranen, P. C. Srivastava, and J. Suhonen, Phys. [25] H. Mach et al., Phys. Rev. C 41, 226 (1990).
Rev. C 93, 034308 (2016). [26] J. W. Song, S. G. Kim, H. S. Kim, H. J. Kim, and
[7] M. Haaranen, J. Kotila, and J. Suhonen, Phys. Rev. C M. K. Lee, J. Low Temp. Phys. 216, 436 (2024).
95, 024327 (2017). [27] J. W. Song, Y. C. Cho, H. J. Kim, and M. K. Lee, J.
[8] J. Suhonen, Front. Phys. 5, 55 (2017). Low Temp. Phys. 218, 110 (2025).
[9] J.SuhonenandJ.Kostensalo,Front.Phys.7,29(2019). [28] M. P. Croce, A. S. Hoover, M. W. Rabin, E. M. Bond,
[10] L. Bodenstein-Dresler et al. (The COBRA Collabora- L.E.Wolfsberg,D.R.Schmidt,andJ.N.Ullom,J.Low
tion), Phys. Lett. B 800, 135092 (2020). Temp. Phys. 184, 938 (2016).
[11] J.Kostensalo, J.Suhonen,J.Volkmer,S.Zatschler,and [29] A. S. Hoover et al., Anal. Chem. 87, 3996 (2015).
K. Zuber, Phys. Lett. B 822, 136652 (2021). [30] Stanford Research Systems, SR560 Low-
[12] J.Kostensalo,E.Lisi,A.Marrone,andJ.Suhonen,Phys. Noise Preamplifier, Rev. 2.8 (Stanford Re-
Rev. C 107, 055502 (2023). search Systems, Sunnyvale, CA, 2006),
[13] I. Bandac et al., Eur. Phys. J. C (2024) 84:1158. https://www.thinksrs.com/products/sr560.html
[14] A. F. Leder et al., Phys. Rev. Lett. 129, 232502 (2022). [31] V. T. Jordanov, G. F. Knoll, A. C. Huber, and J.
[15] L. Pagnanini et al. (The ACCESS Collaboration), Phys. A. Pantazis, Nucl. Instrum. Methods Phys. Res., Sect.
| Rev. | Lett. | 133, 122501 | (2024). | | | | | A 353, | 261 (1994). | | | | | |
| ---- | ----- | ----------- | ------- | --- | --- | --- | --- | ------ | ----------- | --- | --- | --- | --- | --- |
[16] J.Kostensalo,E.Lisi,A.Marrone,andJ.Suhonen,Phys. [32] A. R. L. Kavner,D. Lee,S. T. P. Boyd,S. Friedrich,
Rev. C 110, 045503 (2024). I. Jovanovic, and G. B. Kim, J. Low Temp. Phys. 209,
| [17] M. Paulsen | | et al., | Phys. | Rev. C | 110, 055503(2024). | | | 1070 | (2022). | | | | | |
| --------------- | --- | ------- | ----- | ------ | ------------------ | --- | --- | ---- | ------- | --- | --- | --- | --- | --- |
[18] A. Kumar, P. C. Srivastava, J. Kostensalo, and J. Suho- [33] The MathWorks, Inc., MATLAB, version R2016b
nen, Phys. Rev. C 101, 064304 (2020). (The MathWorks, Inc., Natick, MA, 2016),
7
https://www.mathworks.com/products/matlab.html [35] Y. Khazov, A. Rodionov, and F. G. Kondev, Nucl.
[34] R. D. Deslattes, E. G. Kessler Jr., P. Indelicato, L. Data Sheets 112, 855 (2011).
De Billy, E. Lindroth, and J. Anton, Rev. Mod. Phys. [36] A. C. Thompson et al., X-Ray Data Booklet (Center
75, 35 (2003). forX-RayOpticsandAdvancedLightSource, Lawrence
Berkeley National Laboratory, Berkeley, CA, 2009).
@@ -0,0 +1,8 @@
{
"id": "SRC-2026-05-27-005",
"captured": "2026-05-27",
"packet_version": "1.0",
"title": "https://arxiv.org/pdf/2506.17544",
"url": "https://arxiv.org/pdf/2506.17544",
"format": "web"
}
@@ -0,0 +1,342 @@
IL NUOVO CIMENTO Vol. ?, N. ? ?
Shell-model treatment of the β decay of 99Tc
M. Ramalho(1) and J. Suhonen(1)(2)
(1) DepartmentofPhysics,UniversityofJyva¨skyla¨,P.O.Box35,FI-40014,Jyva¨skyl¨a,Finland
(2) International Centre for Advanced Training and Research in Physics (CIFRA), P.O. Box
MG12, 077125 Bucharest-Magurele, Romania
Summary.— Inthepresentpaperwetreatthesecond-forbiddennon-unique(2nd-
nu)ground-state-to-ground-stateβ−decay99Tc(9/2+)→ 99Ru(5/2+),witha100%
branchingratio,withintheframeworkofthenuclearshellmodel(NSM).Theenergy
spectrum of the electrons emitted in this β-decay transition (β-electron spectrum)
is sensitive to the wave functions of the involved initial (9/2+) and final (5/2+)
nuclear ground states through the many involved nuclear matrix elements (NME).
The β-electron spectrum of this transition is potentially indicative of the effective
value, geff, of the weak axial coupling, g , of crucial importance for extraction of
A A
information on beyond-the-standard-model physics from the results of the present
and future rare-events experiments. We describe the β spectral shape of this de-
cay by using a state-of-the-art β-decay formalism and compute the many involved
NME using the well established NSM Hamiltonians jj45pnb and glekpn. We have
found a strong dependence of the β spectral shape on the value of g making it a
A
good candidate for determination of the value of geff through comparison with the
A
corresponding experimental β spectral shape. We have also found an interesting
dependence of the β spectral shape on the value of the so-called small relativistic
vector NME, sNME, used to match the computed half-life with the measured one.
1. – Introduction
Rare-eventsexperimentstypicallylookforbeyond-the-standard-model(BSM)physics
by,e.g.,measuringrarenuclearβ decays,doubleβ decays,and(anti)neutrinoandWIMP
(weakly interacting massive particle) scatterings off nuclei in searches for astrophysical
neutrinos and cold dark matter of the Universe. In recent years, a booming interest in
these experiments has concentrated on studies of β electrons (electrons emitted in β
−
decays) and their energy distributions, the so-called β-electron spectra. Experimental
and/ortheoreticalinformationonthesespectraiscrucialfor,e.g.,resolvingtheanomalies
related to the antineutrino flux from nuclear reactors and for resolving the common
backgroundsintherare-eventsexperimentsthemselves. Also,pinningdowntheeffective
'Societ`aItalianadiFisica 1
2 M.RAMALHOand J.SUHONEN
values of weak couplings is a considerable incentive for those present and future β-decay
experiments able to tackle the spectral shapes of β electrons, see the review [1].
Nuclearβdecaysvaryincomplexity: fromallowedtohighlyforbiddenones[2,3]. Like
in the case of allowed β decays, also in the case of forbidden unique β decays the lepton
phase space can be separated from the nuclear part, resulting in a universal β spectral
shape,independentofnuclear-structuredetails[2]. Ofspecialinterestfortherare-events
experiments are the forbidden non-unique β decays for which the β spectral shapes can
bestronglynuclear-structuredependentthroughseveralnuclearmatrixelements(NME).
ThevaluesoftheseNMEarenon-triviallydeterminedbythewavefunctionsoftheinitial
and final states of a β-decay transition.
In addition to the many NME, the (partial) half-life of a forbidden non-unique β
transition depends on the values of the weak vector and axial-vector couplings, g and
V
g [3]. In atomic nuclei, the CVC (conserved vector current) hypothesis sets the value
A
g =1.0, whereas the PCAC (partially conserved axial-vector current) hypothesis leads
V
only to the so-called effective value of g , recently discussed in the reviews [1, 4, 5].
A
On the other hand, the so-called bare-nucleon value g = 1.27 stems from data on the
A
decay of an isolated neutron. Typically, the effective value of g is quenched relative
A
to the bare-nucleon value which can have drastic effects on the sensitivity estimates of
rare-events experiments trying to detect the neutrinoless double beta decay [6, 7], of
crucial importance in the search for the BSM physics. Only in rare cases there is an
enhancement of g present [8].
A
As discussed in [9, 10], information on the value of g can be gained by using the
A
so-called spectrum-shape method (SSM). Use of SSM requires a β-electron spectrum
with a notable g dependence in its shape. In this case information on the effective
A
value of g can be gained through the comparison of computed template β spectra,
A
for different g values, with the measured one in the aim to find a match. Such SSM
A
analyses of β-spectral shapes of individual β transitions have been done recently for
−
the fourth-forbidden non-unique β decays of 113Cd and 115In in [11, 12, 13].
Anadditionalingredientinthetheoreticalanalysesofβspectralshapesistheso-called
small relativistic vector NME, the sNME, used to fix the measured (partial) half-life in
the so-called enhanced SSM [14, 15]. In spite of its smallness, sNME can influence the
(partial) half-lives and shapes of β-electron spectra quite strongly, see [12, 16, 17]. The
sNME gathers its major contributions outside the proton (neutron) valence major shell
thatcontainstheproton(neutron)Fermisurfacemakingithardtobecalculatedinmany
nuclear models.
In the present paper we investigate the g and sNME dependence of the second-
A
forbidden non-unique (2nd-nu) β decay of the 9/2+ ground state of 99Tc to the 5/2+
−
ground state of 99Ru, with a branching ratio of 100%.
2. – Theoretical Background
.
21. NSM Hamiltonians. – The NSM calculations were performed using the software
KSHELL [18] with the interactions jj45pnb [19] and glekpn [20] with the set of single-
particle energies fit for the mass region of A = 94 98. Their model spaces consist of
−
the proton 0f (2)-1p -1p -1g orbitals and the neutron 0g -1d -1d -2s -
5/2 3/2 1/2 9/2 7/2 5/2 3/2 1/2
0h orbitals for jj45pnb, as well as 0f (1)-0f (2)-1p -1p -1g proton orbitals
11/2 7/2 5/2 3/2 1/2 9/2
and 0g (1)-0g -1d -1d -2s neutron orbitals for glekpn. The restrictions in the
9/2 7/2 5/2 3/2 1/2
orbitaloccupationsareimposedinordertoreducethecomputationalburden. Webelieve,
SHELL-MODELTREATMENTOFTHEβ DECAYOF99TC 3
99Tc 0 0 .7 . 5 5 ( 3 2 5 2 3 2 7 2 − − − )+ ( ( 3 2 9 2 7 2 5 2 ) ) + + + + 1 2 1+ 1 2 5 2 3 2 1 2 3 − − + + 1 2 1+ 3 2 7 2 − + 1 9 2 2 1 + − 9 2−
0.25
7 2 + 1 2 5 2 − + 9
5
2 +
+
1 2−
7 2−
2
5+ 2 0 9 2 + 7 2 + 5 2−
Exp. jj45pnb glekpn
)VeM(ygrenEnoitaticxE 99Ru 1 2 3 2+ + 5 2 + 9 2 + 5 3 2+ + 9+ (5 2)+ 9 2 + 2 2 7+ (1 2)+ (5 2 7 2 ) + + 2 1 2 1 2 1+ + 5 2 + (3 2)+ 1 2 +
7+
3 2 + 2 7 2 + 3 2 + 3+ 2
3+
2 5+ 7+ 5+ 2 3+ 5+ 2 2 2 2
Exp. jj45pnb glekpn
Fig.1.: Energylevelschemesfor99Tcand99RuwiththeircorrespondingNSM-computed
values using the interactions jj45pnb and glekpn. Experimental data are taken from the
ENDSF [21] evaluation. Levels in blue are the initial and final states relevant for this
work.
however,thattheseposedlimitationsdonotaffectmuchthewavefunctionsoftheinitial
and final ground states, relevant for the present work.
With the considered truncations we have calculated the excitation spectra for both
interactions and compare them with the experimental spectrum in Fig 1. It can be
seen that the jj45pnb Hamiltonian works, overall, much better than the glekpn one, in
particular for the ground state of 99Tc.
To further assess the reliability of the Hamiltonians employed, we have analyzed the
magnetic dipole and electric quadrupole moments of the 99Tc and 99Ru ground states.
The analysis can be seen in Table I, where once again the jj45pnb Hamiltonian does
better overall than the glekpn Hamiltonian.
.
22. β spectral shapes and half-lives. – The half-life of a β transition can be obtained
fromt =κ/C˜,whereκisaconstant[14,15]andC˜ istheintegratedshapefunctionC,
1/2
(1) Orbital fixed with maximum amount of nucleons, e.g, 8 for 0f .
7/2
(2) Orbital with at least 4 particles.
TableI.: Comparisonofexperimentalandtheoretical(jj45pnbandglekpnHamiltonians)
nuclear magnetic dipole (in units of nuclear magneton) and electric quadrupole (in units
of barns) moments for 99Tc and 99Ru, for the experimental ground states and their
correspondents in theory.
99Tc(9/2+) 99Ru(5/2+)
Exp. jj45pnb glekpn Exp. jj45pnb glekpn
µ (µ ) +5.678(2) +5.662 +5.4160 -0.641(5) -0.2875 +1.0826
N
Q (b) -0.129(6) +0.0218 +0.2428 +0.079(4) -0.0018 -0.3222
| 4 | | | | | | | | M.RAMALHOand | | |
| --- | --- | --- | --- | --- | --- | --- | --- | ------------ | --- | --- |
J.SUHONEN
Table
II.: Values of the small relativistic NME emerging from the fitting procedure for
the 99Tc(9/2+) Ru(5/2+) decay. The sNME values (1) and (2) shown reproduce the
→
| experimental | | half-life | for | their | given geff. | | | | | |
| ------------ | --- | --------- | --- | ----- | ----------- | --- | --- | --- | --- | --- |
A
| | jj45pnb | | - CVC =−0.1705 | | | | glekpn | - CVC | =−0.0080 | |
| ---- | ------- | ------- | -------------- | ------- | --- | ---- | ------ | ------- | -------- | ------- |
| geff | | sNME(1) | | sNME(2) | | geff | | sNME(1) | | sNME(2) |
| A | | | | | | A | | | | |
| 0.8 | | 0.0496 | | -0.0856 | | 0.8 | | 0.1042 | | -0.0311 |
| 0.9 | | 0.0582 | | -0.0772 | | 0.9 | | 0.1091 | | -0.0257 |
| 1.0 | | 0.0666 | | -0.0687 | | 1.0 | | 0.1140 | | -0.0202 |
| 1.1 | | 0.0750 | | -0.0601 | | 1.1 | | 0.1189 | | -0.0148 |
| 1.2 | | 0.0833 | | -0.0515 | | 1.2 | | 0.1237 | | -0.0093 |
fullofphase-spacefactorsandNMEsinthenext-to-leading-orderexpansion,asdiscussed
in detail in [9, 10]. Our calculations employ screening, radiative, and atomic exchange
corrections. The most essential to the current study, due to its small β-decay Q value,
is the atomic exchange correction which was originally derived for allowed β decays [22];
the effects of this correction are the most important for electron energies below 50 keV.
The complexity of the shape function C can, however, be cast in a very simple de-
| pendence | | on the | weak couplings | | by writing | | | | | |
| -------- | --- | ------ | -------------- | ----- | ---------- | ---------- | --- | --------- | ---- | --- |
| | | | | )=g2C | )+g2C | | | | | |
| (1) | | | C(w e | | V (w e | A (w e )+g | V g | A C VA (w | e ), | |
| | | | | V | | A | | | | |
where w is the total (mass plus kinetic) energy of the emitted electron. The variation
e
of the shape of the β-electron spectrum with the value of g comes from the subtle
A
interference of the combined vector C (w ) and axial C (w ) parts with the mixed
| | | | | | V | e | | A e | | |
| ------------ | --- | ---- | ---- | ------ | --- | --- | --- | --- | --- | --- |
| vector-axial | | part | C (w | ) [9]. | | | | | | |
| | | | VA e | | | | | | | |
Forthecurrentlystudied2nd-nudecaytransition99Tc(9/2+) 99Ru(5/2+)wetake
→
theCVC-compatiblevalueg =1.0fortheweakvectorcoupling,andexploretheimpact
V
ofthequenchedgeff
| | | | values,rangingfrom0.8 | | | 1.2(seeTableII),ontheβ | | | spectralshape. | |
| --- | --- | --- | --------------------- | --- | --- | ---------------------- | --- | --- | -------------- | --- |
| | | | A | | | − | | | | |
.
23.SmallrelativisticNuclearElements.–Animportantcontributortotheβ spectral
shape and the half-life is the small relativistic NME (sNME) [12, 15, 16, 17]. In these
works, by fitting the value of the sNME, alongside with geff, one is able to reproduce
A
both the β spectral shape and the half-life of a β transition. The computation of the
value of the sNME is a challenge for the ISM. This is why its CVC value (also presented
in Table II) can be used as a reference [3]. It should be noted, though, that the CVC
value represents an ”ideal”, pertaining to a perfect many-body theory, which is not the
case for NSM, mainly due to its restricted proton and neutron valence spaces.
Following the philosophy adopted in [12, 15, 16, 17], we search for the values of the
sNME that reproduce the experimental half-life of the 99Tc(9/2+) Ru(5/2+) transi-
→
tion. Sincetheβ-decayratehasaquadraticdependencyonthesNME,twosolutionsare
geff,
| usually | found | for | each value | of | as presented | in | Table | II. | | |
| ------- | ----- | --- | ---------- | --- | ------------ | --- | ----- | --- | --- | --- |
A
| 3. – | Results | for | the β | spectral | shapes | | | | | |
| ---- | ------- | --- | ----- | -------- | ------ | --- | --- | --- | --- | --- |
Theβ-decaytransitionofinterest,99Tc(9/2+) Ru(5/2+),is2nd-nuandissensitive
→
to the Q value of the decay. Thus, to ensure reliability of our β spectral-shape calcula-
SHELL-MODELTREATMENTOFTHEβ DECAYOF99TC 5
10 3
−
×
8
6
4
2
0
0 50 100 150 200 250 300
Electronenergy(keV)
).mron(Ed/Nd
jj45pnb glekpn
10 3
−
×
12
8
4
0
0 50 100 150 200 250 300
Electronenergy(keV)
Fig. 2.: Computed β spectral shapes for the decay transition 99Tc(9/2+) Ru(5/2+)
→
using the Hamiltonians jj45pnb (left panel) and glekpn (right panel). The crossed-blue
curvescorrespondtothefittedsNMEclosesttotheCVC-valueandthecircled-redtothe
farthest(seeTableII),withgeff =1.0. Thegray-hatched(gray-crossed)areacorresponds
A
to the range geff = 0.8 1.2 with all the fitted sNME values chosen closest to (farthest
A −
from)thecorrespondingCVCvalues. Allcurvesarenormalizedtounitareaunderthem.
tions, we have fixed the energy difference of the initial 9/2+ and final 5/2+ state by the
experimental Q value. We plot our results for the β spectral shapes of this transition in
Fig. 2. There the crossed-blue (circled-red) curves correspond to a typical moderately
quenched value geff =1.0 [4] and the fitted sNME value closer to (farther from) its CVC
A
value, see Table II. The gray-hatched (gray-crossed) area, in turn, corresponds to the
range geff =0.8 1.2 with all the sNME values chosen closest to (farthest from) the cor-
A −
respondingCVCvalues,thesNMEvaluesbeingshowninTableII. Interestinglyenough,
thetwodifferentoptionsforthesNMEvalue(thetwodifferenthatchedregionsinFig.2)
sometimes produce drastically different shapes, as can be seen for glekpn, a pattern also
recorded in [17]. As for the interaction jj45pnb, both hatched regions overlap nicely and
there seems not to be any strong dependency of the β spectral shape on the choice of
the value of sNME.
Since the β spectral shapes produced by the two interactions clearly disagree, it is
hard to judge which one of the shapes is the more realistic one. Based on both the level
schemesinFig. 1andthemagneticandelectricmomentsofTableIitistemptingtosay
that the jj45pnb Hamiltonian is better suited for these particular nuclei. Furthermore,
looking at a recently measured experimental β spectral shape for this transition in [23],
especially in their Fig. 5, one can see a striking resemblance of it to the spectral shapes
forbothfittedsNME(1,2) ofthejj45pnb Hamiltonian(seeTableII)andabitlessstriking
forthesolutionsNME(2) fortheglekpn Hamiltonian. However,onlyapropercomparison
with data will allow us to draw any definite conclusions concerning the effective value of
the axial coupling and the proper value of sNME. This we leave as a future exercise.
| 6 | | | | | M.RAMALHOand | |
| --- | --- | --- | --- | --- | ------------ | --- |
J.SUHONEN
| 4. – Summary | and | conclusions | | | | |
| ------------ | --- | ----------- | --- | --- | --- | --- |
We have performed shell-model calculations of the β spectral shape related to the
second-forbidden non-unique β -decay transition 99Tc(9/2+) 99Ru(5/2+), with a
−
→
100% branching ratio. We use a state-of-the-art β-decay formalism and compute the
many involved NME using the well established NSM Hamiltonians jj45pnb and glekpn.
When compared with future available data, the β-electron spectrum of this transition
is potentially indicative of the effective value of the weak axial coupling, g , of crucial
A
importance for extraction of information on beyond-the-standard-model physics from
the results of the present and future rare-events experiments. We also point out the
important role played by the small relativistic vector matrix element, used to reproduce
the measured half-life of the transition, in shaping the β-electron spectrum.
5. – Acknowledgments
WeacknowledgethesupportbyCSC–ITCenterforScience,Finland,forthegenerous
| computational | resources, | which | were | essential to | this project. | |
| ------------- | ---------- | ----- | ---- | ------------ | ------------- | --- |
REFERENCES
| [1] Ejiri, | H., Suhonen, | J., and | Zuber, | K., Phys. Rep., | 797 (2019) 1. | |
| ---------- | ------------ | ------- | ------ | --------------- | ------------- | --- |
[2] Suhonen,J.,FromNucleonstoNucleus: ConceptsofMicroscopicNuclearTheory,Springer,
| Berlin, | 2007. | | | | | |
| ------- | ----- | --- | --- | --- | --- | --- |
[3] Behrens, H. and Bu¨hring, W., Electron Radial Wave Functions and Nuclear Beta-decay,
| Clarendon | Press, | Oxford, | 1982. | | | |
| --------------- | ------------------ | --------- | ---------- | ----------------- | -------------- | --- |
| [4] Suhonen, | J., Front. | Phys., | 5 (2017) | 55. | | |
| [5] Suhonen, | J. and Kostensalo, | | J., Front. | Phys., 7 (2019) | 29. | |
| [6] Engel, | J. and Men´endez, | J., | Rep. Prog. | Phys., 60 | (2017) 046301. | |
| [7] Agostini, | M. et al., | Rev. Mod. | Phys., | 95 (2023) 025002. | | |
| [8] Kostensalo, | J. and | Suhonen, | J., Phys. | Lett. B, 781 | (2018) 480. | |
[9] Haaranen, M., Srivastava, P. C., and Suhonen, J., Phys. Rev. C, 93 (2016) 034308.
| [10] Haaranen, | M., Kotila, | J., | and Suhonen, | J., Phys. | Rev. C, 95 (2017) | 024327. |
| -------------- | ----------- | --- | ------------ | --------- | ----------------- | ------- |
[11] Bodenstein-Dresler, L. et al. (The COBRA Collaboration), Phys. Lett. B, 800 (2020)
135092.
[12] Kostensalo, J., Suhonen, J., Volkmer, J., Zatschler, S., and Zuber, K., Phys. Lett. B, 822
| (2021) | 136652. | | | | | |
| ----------- | ------------- | ----- | ----------- | ---------- | ------- | --- |
| [13] Leder, | A. F. et al., | Phys. | Rev. Lett., | 129 (2022) | 232502. | |
[14] Kumar, A., Srivastava, P. C., Kostensalo, J., and Suhonen, J., Phys. Rev. C, 101 (2020)
064304.
[15] Kumar, A., Srivastava, P. C., and Suhonen, J., Eur. Phys. J. A, 57 (2021) 225.
[16] Kostensalo, J., Lisi, E., Marrone, A., and Suhonen, J., Phys. Rev. C, 107 (2023) 055502.
| [17] Ramalho, | M. and | Suhonen, | J., Submitted | to Phys. | Rev. C (2023). | |
| ------------- | ---------- | -------- | ------------- | ---------- | -------------- | --- |
| [18] Shimizu, | N. et al., | Comp. | Phys. Comm., | 244 (2019) | 372. | |
[19] Lisetskiy,A.F.,Brown,B.A.,Horoi,M.,andGrawe,H.,Phys. Rev. C,70(2004)044314.
| [20] Mach, | H. and Warburton, | | E. K. et | al., Phys. Rev. | C, 41 (1990) | 226. |
| ---------- | ----------------- | --- | -------- | --------------- | ------------ | ---- |
[21] From ENSDF database as of Nov, 2023. Version available at
http://www.nndc.bnl.gov/ensarchivals/.
| [22] Nitescu, | O., Stoica, | S., and | Simkovic, | F., Phys. Rev. | C, 107 (2023) | 025501. |
| ------------- | ----------- | ---------------- | --------- | -------------- | ------------- | ------- |
| [23] Paulsen, | M. et al., | arXiv:2309.14014 | | [nucl-ex]. | | |
@@ -0,0 +1,8 @@
{
"id": "SRC-2026-05-27-006",
"captured": "2026-05-27",
"packet_version": "1.0",
"title": "https://arxiv.org/pdf/2312.07448",
"url": "https://arxiv.org/pdf/2312.07448",
"format": "web"
}
@@ -0,0 +1,37 @@
# ESSM — Enhanced Spectrum Shape Method
## Overview
The Enhanced Spectrum Shape Method (ESSM) extends the standard SSM by treating the small relativistic vector nuclear matrix element (sNME) as a free parameter alongside g_A. This allows simultaneous fitting of both the spectral shape AND the experimental half-life.
## Motivation
The sNME is difficult to compute accurately in nuclear shell model because it involves contributions outside the valence shell. The CVC prediction gives a "canonical" value, but this assumes a perfect many-body theory — which finite valence spaces are not.
## Algorithm
1. Fix g_A at a trial value
2. Vary sNME to simultaneously fit the experimental half-life
3. Because the half-life depends quadratically on sNME, there are typically **two solutions** for each g_A:
- **sNME(c)**: closer to the CVC value (closeCVC)
- **sNME(f)**: further from the CVC value (farCVC)
4. Compare both solutions' spectral shapes with the data
## sNME Values for ⁹⁹Tc
| Model | g_A^eff | sNME(c) [fm³] | sNME(f) [fm³] | CVC sNME [fm³] |
|---|---|---|---|---|
| jj45pnb | 1.0 | 0.0666 | −0.0687 | −0.1705 |
| glekpn | 1.0 | 0.1140 | −0.0202 | −0.0080 |
## Results from Song et al. (2025)
- **Best fit**: glekpn with g_A^eff = 1.2, sNME(f) = −0.0698 fm³, χ²_ν = 1.066
- **Conclusion**: Quenched g_A = 1.0–1.2 with farCVC sNME provides the best agreement with data
## References
- [[SSM — Spectrum Shape Method]]
- [[Shape Factor C(W, g_A)]]
- [[Song et al. (2025)]]
- [[Kumar et al. (2021)]]
@@ -0,0 +1,48 @@
# Nuclear Shell Model for β Decay
## Overview
The Nuclear Shell Model (NSM) is the primary theoretical framework for computing nuclear matrix elements (NMEs) in β decay. For forbidden nonunique decays like ⁹⁹Tc, the NSM computes one-body transition densities (OBTDs) that weight the single-particle transitions contributing to the decay.
## Key Codes
| Code | Description | Valence Spaces |
|---|---|---|
| NUSHELLX | Standard NSM code | Multiple (pf, sdg, etc.) |
| KSHELL | High-performance NSM | Custom valence spaces |
## Valence Spaces for ⁹⁹Tc
### glekpn (Mach interaction)
- **Protons**: 0f₇/₂, 0f₅/₂, 2p₃/₂, 2p₁/₂, 1g₉/₂
- **Neutrons**: 0g₉/₂, 0g₇/₂, 1d₅/₂, 1d₃/₂, 2s₁/₂
### jj45pnb (Brown-Lisetskiy interaction)
- **Protons**: 0f₅/₂, 2p₃/₂, 2p₁/₂, 1g₉/₂
- **Neutrons**: 0g₇/₂, 1d₅/₂, 1d₃/₂, 2s₁/₂, 0h₁₁/₂
## OBTDs for ⁹⁹Tc
| Transition | OBTD (glekpn) | OBTD (jj45pnb) |
|---|---|---|
| 1g₇/₂ → 1g₉/₂ | 0.00994 | 0.01107 |
| 2d₅/₂ → 1g₉/₂ | 0.47752 | −0.43985 |
**Key insight**: The dominant transition is 2d₅/₂ → 1g₉/₂ with OBTD ≈ 0.478. The jj45pnb model gives opposite sign for this transition.
## sNME and CVC
The small relativistic vector NME (sNME) is related to the large vector NME via the CVC hypothesis:
```
sNME = (R/√10) · [W₀ − (m_n − m_p) + E_C] · l-NME
```
where E_C is the Coulomb displacement energy. For ⁹⁹Tc, [W₀ − (m_n − m_p)] = −0.487 MeV (small Q-value makes this critical).
## References
- [[Shape Factor C(W, g_A)]]
- [[SSM — Spectrum Shape Method]]
- [[Ramalho & Suhonen (2024)]]
- [[Song et al. (2025)]]
@@ -0,0 +1,53 @@
# Shape Factor C(W, g_A)
## Overview
The g_A-dependent nuclear shape factor C(W, g_A) is the key nuclear structure quantity that encodes the energy-dependent modification of the β decay spectrum due to forbidden nonunique transitions. For ⁹⁹Tc (second-forbidden nonunique, 9/2₁⁺ → 5/2₁⁺, Q ≈ 296 keV), the shape factor decomposes as:
```
C(W; g_A) = g_V² · C_V(W) + g_A² · C_A(W) + g_V · g_A · C_VA(W)
```
with g_V = 1.0 fixed by CVC.
## Components
| Component | Description | Sign for ⁹⁹Tc | Magnitude |
|---|---|---|---|
| C_V(W) | Vector contribution | + | 2.240 × 10⁻⁹ (integrated) |
| C_A(W) | Axial-vector contribution | + | 2.130 × 10⁻⁹ (integrated) |
| C_VA(W) | Vector-axial interference | − | −4.361 × 10⁻⁹ (integrated) |
**Key insight**: C_VA is negative and twice as large as C_V or C_A. The total integrated shape factor is two orders of magnitude smaller due to destructive interference.
## g_A Dependence
The spectral shape is most sensitive to g_A in the **0–100 keV range** (low-energy region). Figure 9 from Paulsen et al. (2024) shows:
- **Small g_A (0.5–1.0)**: Bell-shaped spectrum with low-energy enhancement
- **Large g_A (1.4–2.0)**: Flattened spectrum with low-energy suppression
- **Crossover point**: All spectra intersect around 100 keV
## Table Data Sources
| Source | Method | Coverage | Availability |
|---|---|---|---|
| Kostensalo & Suhonen (2017) PRC 96, 024317 | MQPM | 26 forbidden decays | Supplementary material (not public) |
| Ramalho & Suhonen (2024) | NSM (NUSHELLX) | ⁹⁹Tc-specific | Supplementary material (not public) |
| Song et al. (2025) arXiv:2506.17544 | ESSM + NSM/MQPM | ⁹⁹Tc | Integrated values only |
## Implementation
The ShapeFactorGA component should:
1. Load C_V(W), C_A(W), C_VA(W) tables from CSV files
2. Interpolate using cubic spline (scipy.interpolate.CubicSpline)
3. Compute C(W; g_A) = C_V + g_A²·C_A + g_A·C_VA
4. Return the shape factor for the full spectrum calculation
## References
- [[SSM — Spectrum Shape Method]]
- [[Nuclear Shell Model]]
- [[CVC Hypothesis]]
- [[Paulsen et al. (2024)]]
- [[Song et al. (2025)]]
@@ -0,0 +1,48 @@
# SSM — Spectrum Shape Method
## Overview
The Spectrum Shape Method (SSM) is a technique for determining the effective axial-vector coupling constant g_A by comparing theoretically computed β spectral shapes with experimental measurements. It is applicable to forbidden nonunique β decays, where the spectral shape depends nontrivially on g_A through the shape factor C(W; g_A).
## Algorithm
1. **Compute shape factor components** C_V(W), C_A(W), C_VA(W) from nuclear model
2. **Build theoretical spectrum** for trial g_A values: N(W; g_A) = PhaseSpace × Fermi × ... × C(W; g_A)
3. **Compare with experimental spectrum** using χ² minimization
4. **Extract g_A** from the minimum χ² position
5. **Estimate uncertainty** from χ² = χ²_min + 1
## Key Requirements
- **Forbidden nonunique transition**: The spectral shape must depend on g_A
- **High-precision measurement**: Low energy threshold (< 10 keV), good energy resolution (< 1 keV)
- **Accurate nuclear model**: Nuclear shell model (NSM) or MQPM with appropriate valence space
## g_A Values from Literature
| Source | g_A^eff | Method | Notes |
|---|---|---|---|
| Paulsen et al. (2024) | 1.526(92) | CVC-fixed sNME | Enhanced value |
| Song et al. (2025) | 1.0–1.2 | ESSM (farCVC) | Quenched value |
| Suhonen review | ~0.9 | Half-life comparison | Quenched value |
## Uncertainty Budget
| Component | Uncertainty on g_A | Relative |
|---|---|---|
| Fit method (statistics) | 0.0745 | 4.9% |
| Coulomb displacement energy | 0.0425 | 2.8% |
| Nuclear model | 0.0294 | 1.9% |
| Lepton current treatment | 0.0127 | 0.8% |
| Fit range | 0.0061 | 0.4% |
| Endpoint energy | 0.0038 | 0.2% |
| Radiative corrections | 0.0033 | 0.2% |
| Atomic exchange | 0.0029 | 0.2% |
| **Combined** | **0.0919** | **6.0%** |
## References
- [[Shape Factor C(W, g_A)]]
- [[ESSM — Enhanced Spectrum Shape Method]]
- [[Paulsen et al. (2024)]]
- [[Song et al. (2025)]]
@@ -0,0 +1,42 @@
# Paulsen et al. (2024) — High precision measurement of 99Tc β spectrum
## Citation
Paulsen, M., Ranitzsch, P.C.-O., Loidl, M., et al. (2024). High precision measurement of the ⁹⁹Tc β spectrum. *Physical Review C*, 110, 055503. DOI: 10.1103/PhysRevC.110.055503
## Key Results
| Parameter | Value | Method |
|---|---|---|
| Q_β | 295.82(16) keV | MMC measurement |
| g_A^eff | 1.526(92) | Spectrum shape (CVC-fixed sNME) |
| g_V^eff | 0.376(5) | Half-life normalization |
| g_A^eff (quenched) | 0.574(36) | Half-life normalization |
| E_β | 98.51(23) keV | Theoretical spectrum integration |
| log f | −0.47660(22) | Theoretical spectrum |
| log f t | 12.3478(23) | Partial half-life |
## Methodology
- **Detector**: Metallic Magnetic Calorimeters (MMCs) at LNHB and PTB
- **Energy threshold**: 0.65 keV (orders of magnitude lower than previous measurements)
- **Energy resolution**: 0.1 keV at 383 keV
- **Nuclear model**: NUSHELLX with GLEKPN valence space, Mach interaction
- **sNME treatment**: CVC-fixed (not ESSM)
## Key Figures
- **Figure 9**: g_A-dependent theoretical β spectrum — shows dramatic shape changes for g_A ∈ {0.5, 0.8, 1.0, 1.2, 1.4, 1.6, 2.0}
- **Figure 10**: Best-fit theoretical spectrum vs. measurement
## Limitations
- Used CVC-fixed sNME (not ESSM) → enhanced g_A value
- Song et al. (2025) contradicts this result using ESSM
## References
- [[Shape Factor C(W, g_A)]]
- [[SSM — Spectrum Shape Method]]
- [[Song et al. (2025)]]
- [[Ramalho & Suhonen (2024)]]
@@ -0,0 +1,43 @@
# Song et al. (2025) — The measurement of 99Tc β-decay spectrum and implications for g_A
## Citation
Song, J.W., Ramalho, M., Lee, M.K., et al. (2025). The measurement of the ⁹⁹Tc β-decay spectrum and its implications for the effective value of weak axial coupling. *arXiv:2506.17544*.
## Key Results
| Model | g_A^eff | sNME [fm³] | χ²_ν | p-value |
|---|---|---|---|---|
| glekpn (CloseCVC) | 1.1 | 0.0674 | 1.106 | 0.12 |
| jj45pnb (CloseCVC) | 1.0 | 0.0681 | 1.244 | 0.004 |
| MQPM (CloseCVC) | 1.0 | 0.0651 | 1.644 | <0.0001 |
| **glekpn (FarCVC)** | **1.2** | **−0.0698** | **1.066** | **0.22** |
| jj45pnb (FarCVC) | 1.0 | −0.0669 | 1.097 | 0.14 |
| MQPM (FarCVC) | 1.0 | −0.0683 | 1.095 | 0.14 |
## Methodology
- **Detector**: MMC with 4π gold absorber, energy threshold 10 keV
- **Nuclear models**: glekpn, jj45pnb (NSM), MQPM
- **sNME treatment**: ESSM (sNME as fit parameter) — two solutions per g_A (closeCVC and farCVC)
- **Fit range**: 10–41 keV and 50–280 keV (excluding Gd X-ray region)
## Key Findings
1. **Contradicts Paulsen et al. (2024)**: The enhanced g_A value (1.526) is not supported when using proper ESSM treatment
2. **Quenched g_A**: Best fit is g_A^eff = 1.0–1.2 with farCVC sNME
3. **Best model**: glekpn with g_A^eff = 1.2, sNME = −0.0698 fm³, χ²_ν = 1.066
## Implications
- Resolves the inconsistency between first-forbidden and higher-forbidden nonunique transitions
- Supports quenched g_A consistent with Suhonen's review values
- Demonstrates the importance of proper sNME treatment in SSM analysis
## References
- [[Shape Factor C(W, g_A)]]
- [[SSM — Spectrum Shape Method]]
- [[ESSM — Enhanced Spectrum Shape Method]]
- [[Paulsen et al. (2024)]]
- [[Ramalho & Suhonen (2024)]]
@@ -0,0 +1,40 @@
---
type: source
format: markdown
source_id: SRC-2026-05-27-001
raw_path: raw/sources/SRC-2026-05-27-001/extracted.md
captured: 2026-05-27
status: skeleton
---
# 01-ssm-algorithm.md
## Summary
[LLM: Replace with 2-3 paragraph summary of key content]
> _Auto-preview: --- title: "SSM — Spectrum Shape Method for gA Extraction" date: 2026-05-14 tags: - gA/extraction/algorithm - gA/ssm - beta-spectrum/forbidden status: active aliases: [Spectrum Shape Method, gA fitting, shape factor fit] relatednotes: - gA-extraction/02-ssnme - gA-extraction/03-tc99-case-study - gA-extraction/04-implementation-todo - nuclear-structure/01-shape-factors - corrections/05-radiative --- Spectrum Shape Method (SSM) — Step-by-Step Algorithm for gA Extraction 1. Physical Basis For forbi..._
## Key Takeaways
- [LLM: Most important point]
- [LLM: Second important point]
- [LLM: Third important point]
## Entities Mentioned
- [[entity-name]]
## Concepts Mentioned
- [[concept-name]]
## Notable Quotes
> [LLM: Important quote] — attribution
## Source Packet
- **ID:** `[[sources/SRC-2026-05-27-001]]`
- **Extracted:** [raw/sources/SRC-2026-05-27-001/extracted.md](../raw/sources/SRC-2026-05-27-001/extracted.md)
- **Manifest:** [raw/sources/SRC-2026-05-27-001/manifest.json](../raw/sources/SRC-2026-05-27-001/manifest.json)
@@ -0,0 +1,40 @@
---
type: source
format: markdown
source_id: SRC-2026-05-27-002
raw_path: raw/sources/SRC-2026-05-27-002/extracted.md
captured: 2026-05-27
status: skeleton
---
# 01-shape-factors.md
## Summary
[LLM: Replace with 2-3 paragraph summary of key content]
> _Auto-preview: --- title: "10 – Nuclear Structure Effects" date: 2026-04-26 tags: - beta-spectrum/nuclear-structure status: active aliases: [Shape Factor, C(Z,W), Form Factors] cssclasses: [] relatedcomponents: [[corrections/06-recoil]], --- Nuclear Structure Effects — The Shape Factor $C(Z,W)$ Overview Nuclear structure effects are encapsulated in the shape factor $C(Z,W)$ which multiplies the phase space × Fermi function. This is distinct from electrostatic finite size corrections ([[corrections/02-finite-si..._
## Key Takeaways
- [LLM: Most important point]
- [LLM: Second important point]
- [LLM: Third important point]
## Entities Mentioned
- [[entity-name]]
## Concepts Mentioned
- [[concept-name]]
## Notable Quotes
> [LLM: Important quote] — attribution
## Source Packet
- **ID:** `[[sources/SRC-2026-05-27-002]]`
- **Extracted:** [raw/sources/SRC-2026-05-27-002/extracted.md](../raw/sources/SRC-2026-05-27-002/extracted.md)
- **Manifest:** [raw/sources/SRC-2026-05-27-002/manifest.json](../raw/sources/SRC-2026-05-27-002/manifest.json)
@@ -0,0 +1,41 @@
---
type: source
format: web
source_id: SRC-2026-05-27-003
raw_path: raw/sources/SRC-2026-05-27-003/extracted.md
captured: 2026-05-27
status: skeleton
---
# https://jyx.jyu.fi/bitstreams/eb3a10c8-b94c-4009-8a8e-839bfddc7c58/download
> _Original: [https://jyx.jyu.fi/bitstreams/eb3a10c8-b94c-4009-8a8e-839bfddc7c58/download](https://jyx.jyu.fi/bitstreams/eb3a10c8-b94c-4009-8a8e-839bfddc7c58/download)_
## Summary
[LLM: Replace with 2-3 paragraph summary of key content]
> _Auto-preview: This is a self-archived version of an original article. This version may differ from the original in pagination and typographic details. Kostensalo, Joel; Suhonen, Jouni Author(s): gA-driven shapes of electron spectra of forbidden β decays in the nuclear shell model Title: Year: 2017 Version: Published version Copyright: © 2017 American Physical Society. Published in this repository with the kind permission of the publisher. Rights: Rights url: Please cite the original version: Kostensalo, J., &..._
## Key Takeaways
- [LLM: Most important point]
- [LLM: Second important point]
- [LLM: Third important point]
## Entities Mentioned
- [[entity-name]]
## Concepts Mentioned
- [[concept-name]]
## Notable Quotes
> [LLM: Important quote] — attribution
## Source Packet
- **ID:** `[[sources/SRC-2026-05-27-003]]`
- **Extracted:** [raw/sources/SRC-2026-05-27-003/extracted.md](../raw/sources/SRC-2026-05-27-003/extracted.md)
- **Manifest:** [raw/sources/SRC-2026-05-27-003/manifest.json](../raw/sources/SRC-2026-05-27-003/manifest.json)
@@ -0,0 +1,41 @@
---
type: source
format: web
source_id: SRC-2026-05-27-004
raw_path: raw/sources/SRC-2026-05-27-004/extracted.md
captured: 2026-05-27
status: skeleton
---
# https://cea.hal.science/cea-04796921/file/PhysRevC.110.055503.pdf
> _Original: [https://cea.hal.science/cea-04796921/file/PhysRevC.110.055503.pdf](https://cea.hal.science/cea-04796921/file/PhysRevC.110.055503.pdf)_
## Summary
[LLM: Replace with 2-3 paragraph summary of key content]
> _Auto-preview: High precision measurement of the 99Tc 𝛽 spectrum Michael Paulsen, Philipp Ranitzsch, Martin Loidl, Matias Rodrigues, Karsten Kossert, Xavier Mougeot, Abhilasha Singh, Sylvain Leblond, J. Beyer, L. Bockhorn, et al. To cite this version: Michael Paulsen, Philipp Ranitzsch, Martin Loidl, Matias Rodrigues, Karsten Kossert, et al.. High preci- sion measurement of the 99Tc 𝛽 spectrum. Physical Review C, 2024, 110, 055503 (17 p.). ⟨10.1103/Phys- RevC.110.055503⟩. ⟨cea-04796921⟩ HAL Id: cea-04796921 ..._
## Key Takeaways
- [LLM: Most important point]
- [LLM: Second important point]
- [LLM: Third important point]
## Entities Mentioned
- [[entity-name]]
## Concepts Mentioned
- [[concept-name]]
## Notable Quotes
> [LLM: Important quote] — attribution
## Source Packet
- **ID:** `[[sources/SRC-2026-05-27-004]]`
- **Extracted:** [raw/sources/SRC-2026-05-27-004/extracted.md](../raw/sources/SRC-2026-05-27-004/extracted.md)
- **Manifest:** [raw/sources/SRC-2026-05-27-004/manifest.json](../raw/sources/SRC-2026-05-27-004/manifest.json)
@@ -0,0 +1,41 @@
---
type: source
format: web
source_id: SRC-2026-05-27-005
raw_path: raw/sources/SRC-2026-05-27-005/extracted.md
captured: 2026-05-27
status: skeleton
---
# https://arxiv.org/pdf/2506.17544
> _Original: [https://arxiv.org/pdf/2506.17544](https://arxiv.org/pdf/2506.17544)_
## Summary
[LLM: Replace with 2-3 paragraph summary of key content]
> _Auto-preview: 5202 nuJ 12 ]xe-lcun[ 1v44571.6052:viXra The measurement of the 99Tc β-decay spectrum and its implications for the effective value of weak axial coupling J. W. Song,1,2 M. Ramalho,3,4 M. K. Lee,1 G. B. Kim,5 I. Kim,5 H. L. Kim,6 Y. C. Lee,6,7 K. R. Woo,6 J. Kotila,8 J. Kostensalo,9 J. Suhonen,4,10,∗ and H. J. Kim2,† 1Korea Research Institute of Standards and Science, Daejeon 34113, South Korea 2Department of Physics, Kyungpook National University, Daegu 41566, South Korea 3School of Physics, Eng..._
## Key Takeaways
- [LLM: Most important point]
- [LLM: Second important point]
- [LLM: Third important point]
## Entities Mentioned
- [[entity-name]]
## Concepts Mentioned
- [[concept-name]]
## Notable Quotes
> [LLM: Important quote] — attribution
## Source Packet
- **ID:** `[[sources/SRC-2026-05-27-005]]`
- **Extracted:** [raw/sources/SRC-2026-05-27-005/extracted.md](../raw/sources/SRC-2026-05-27-005/extracted.md)
- **Manifest:** [raw/sources/SRC-2026-05-27-005/manifest.json](../raw/sources/SRC-2026-05-27-005/manifest.json)
@@ -0,0 +1,41 @@
---
type: source
format: web
source_id: SRC-2026-05-27-006
raw_path: raw/sources/SRC-2026-05-27-006/extracted.md
captured: 2026-05-27
status: skeleton
---
# https://arxiv.org/pdf/2312.07448
> _Original: [https://arxiv.org/pdf/2312.07448](https://arxiv.org/pdf/2312.07448)_
## Summary
[LLM: Replace with 2-3 paragraph summary of key content]
> _Auto-preview: IL NUOVO CIMENTO Vol. ?, N. ? ? Shell-model treatment of the β decay of 99Tc M. Ramalho(1) and J. Suhonen(1)(2) (1) DepartmentofPhysics,UniversityofJyva¨skyla¨,P.O.Box35,FI-40014,Jyva¨skyl¨a,Finland (2) International Centre for Advanced Training and Research in Physics (CIFRA), P.O. Box MG12, 077125 Bucharest-Magurele, Romania Summary.— Inthepresentpaperwetreatthesecond-forbiddennon-unique(2nd- nu)ground-state-to-ground-stateβ−decay99Tc(9/2+)→ 99Ru(5/2+),witha100% branchingratio,withintheframewo..._
## Key Takeaways
- [LLM: Most important point]
- [LLM: Second important point]
- [LLM: Third important point]
## Entities Mentioned
- [[entity-name]]
## Concepts Mentioned
- [[concept-name]]
## Notable Quotes
> [LLM: Important quote] — attribution
## Source Packet
- **ID:** `[[sources/SRC-2026-05-27-006]]`
- **Extracted:** [raw/sources/SRC-2026-05-27-006/extracted.md](../raw/sources/SRC-2026-05-27-006/extracted.md)
- **Manifest:** [raw/sources/SRC-2026-05-27-006/manifest.json](../raw/sources/SRC-2026-05-27-006/manifest.json)
+1 -2
View File
@@ -1,6 +1,6 @@
# Agent Instructions — beta-spectrum
High-precision beta decay spectrum calculator. Extracts shape factor C(W) from ⁹⁹Tc beta spectrum to parametrize g_V coupling.
High-precision beta decay spectrum shape calculator. Since v0.5.0 the repository is dedicated solely to spectrum shape calculation: detector response, calibration, fitting and C(W)/g_V/g_A extraction live in separate, dedicated projects.
## Stack & Commands
@@ -68,7 +68,6 @@ After quality gates pass, run:
```bash
bs_pnpi --nuclide Tc99 -vv --output ./output/verification.csv --plot ./output/verification.png --log-file ./output/debug.log 2>&1 | tee ./output/debug_stdout.log
bs_exp --help # verify experimental data CLI
```
Verify parameter consistency across all components. Save artifacts to `./output/`.
+1 -1
View File
@@ -18,7 +18,7 @@ m_e = ℏ = c = 1
**External API:** human-readable MeV/keV. Check the unit suffix in parameter names:
- `_MeV` suffix → value is in MeV
- `_keV` suffix → value is in keV (detector params only)
- `_keV` suffix → value is in keV
- No suffix on energy → value is in m_e units (internal)
### Conversion
+70 -87
View File
@@ -2,6 +2,8 @@
A modular Python package for calculating high-precision **beta decay energy spectra**. It models the spectral shape of nuclear transitions by combining well-established physical corrections into a coherent, configurable pipeline.
Since v0.5.0 the project scope is the spectrum-shape **calculation** alone: detector response modeling, data calibration and fitting/extraction live in separate, dedicated projects.
**Features:**
- Numerical evaluation from near-zero kinetic energies up to the endpoint
@@ -11,6 +13,8 @@ A modular Python package for calculating high-precision **beta decay energy spec
- Normal mode: single spectrum plot with nuclear data header
- Debug mode (`-vv`): 4-panel view with all correction factors
- All plots include commit hash and UTC timestamp for traceability
- Multi-branch decays with intensity weighting and per-branch endpoints
- Nuclear data retrieval from ENSDF via paceENSDF (`bs_pnpi --nuclide Tc99`)
- Comprehensive scientific reference in [`docs/`](docs/)
- Debug verification workflow: `bs_pnpi -vv` validates all parameters end-to-end
@@ -22,7 +26,7 @@ The beta spectrum is constructed as a product of independent correction factors:
$$N(W) \\propto p W (W_0 - W)^2 \\times \\prod_i C_i(W)$$
Each $C_i(W)$ is an independently implemented and toggleable component.
Each $C_i(W)$ is an independently implemented and toggleable component. The phase space and the Fermi function are the unconditional core: every spectrum carries their product, and the higher-order corrections multiply on top of it.
______________________________________________________________________
@@ -31,65 +35,46 @@ ______________________________________________________________________
```
beta_shape_pnpi/
├── pyproject.toml
├── docs/ # Obsidian knowledge base (physics theory & references)
│ ├── README.md # Vault overview + navigation index
│ ├── 00-beta-spectrum-overview.md # Master equation, all corrections table
│ ├── 01-fermi-function.md # Fermi function F₀(Z,W)
│ ├── 02-phase-space.md # Phase space p·W·(W₀−W)²
│ ├── 03-finite-size.md # L₀, U, DFS corrections
│ ├── 04-screening-correction.md # Atomic screening S(Z,W)
│ ├── 05-exchange-correction.md # Electron exchange X(Z,W)
│ ├── 06-radiative-corrections.md # Outer radiative δᵣ(W,W₀)
│ ├── 07-recoil-effects.md # Weak magnetism, recoil kinematics
│ ├── 08-detector-response.md # Detector response models
│ ├── 10-nuclear-structure.md # Shape factor C(Z,W), BB vs HS formalisms
│ ├── 12-atomic-overlap.md # Bahcall correction r(Z,W)
│ ├── 13-chemical-effects.md # Molecular environment effects
├── beta_spectrum/ ← pure theory core
│ ├── __init__.py # Public API: constants, utilities, classes
│ ├── base.py # Abstract SpectrumComponent base class
│ ├── constants.py # Physical constants (natural units, m_e = 1)
│ ├── utils.py # Helpers: T<->W conversion, momentum, nuclear radius
│ ├── spectrum.py # BetaSpectrum + SpectrumConfig + BranchConfig
│ ├── nuclear_data.py # paceENSDF integration + JSON input support
│ ├── cli.py # Command-line interface (bs_pnpi)
│ ├── logging_utils.py # Structured logging helpers
│ └── components/
│ │ ├── phase_space.py ✓ Phase space shape (p·W·(W₀−W)²)
│ │ ├── fermi.py ✓ Coulomb correction (loggamma for stability)
│ │ ├── finite_size.py ✓ L0 expansion + charge distribution U term
│ │ ├── screening.py ✓ Atomic electron screening (ratio method)
│ │ ├── exchange.py ✓ Hayen-2018 empirical fit coefficients
│ │ ├── radiative.py ✓ Outer radiative corrections, soft-photon resummation
│ │ └── detector_response.py ✓ Analytical detector response models
├── beta_spectrum/visualize/ ← plotting / visualization
│ └── __init__.py # BetaSpectrumAnalyzer
├── exp_data/ ← experimental data handling
│ ├── __init__.py # ExpSpectrum, EnergyCalibrator, GaussianFitter, etc.
│ ├── spectrum.py # ExpSpectrum dataclass (energies, counts, errors, metadata)
│ ├── calibration.py # EnergyCalibrator (linear/quadratic channel→energy)
│ ├── fitters.py # GaussianFitter + PeakFitter for cal sources
│ └── corrections.py # DeadTimeCorrection, PileUpCorrection, BackgroundSubtractor
├── fitter/ ← analysis framework
│ ├── __init__.py # SpectrumModel, SpectrumFitter, CWExtractor, etc.
│ ├── model.py # SpectrumModel (BetaSpectrum + DetectorResponse wrapper)
│ ├── fit_engine.py # SpectrumFitter + CurveFitter + FitConfig + FitResult
│ ├── extractor.py # CWExtractor, GVAExtractor, CWExtractionResult, GVAExtractionResult
│ └── result.py # AnalysisResult + FitSummary
├── tests/
│ ├── exp_data/ # exp_data module tests
│ ├── fitter/ # fitter module tests
│ ├── physics/ # Physics component tests
│ ├── quality/ # Code quality tests (logging, API, nuclear data)
│ ├── integration/ # Full pipeline integration tests
│ └── common/ # Shared hypothesis + property tests
├── docs/ ← Obsidian knowledge base (physics theory)
│ ├── README.md ← Vault overview + navigation index
│ ├── 00-beta-spectrum-overview.md
│ ├── 02-phase-space.md
│ └── corrections/ ← One note per correction component
├── beta_spectrum/ ← theory-only core package
│ ├── __init__.py ← Public API
│ ├── base.py ← Abstract SpectrumComponent base class
│ ├── constants.py ← Physical constants (natural units, m_e = 1)
│ ├── utils.py ← T<->W conversion, momentum, nuclear radius
│ ├── spectrum.py ← BetaSpectrum + SpectrumConfig + BranchConfig
│ ├── nuclear_data.py ← paceENSDF integration + JSON input support
│ ├── cli.py ← Command-line interface (bs_pnpi)
│ ├── logging_utils.py ← Structured logging helpers
│ ├── components/
│ │ ├── phase_space.py ← Phase space shape (p·W·(W₀−W)²)
│ │ ├── fermi.py ← Coulomb correction (loggamma for stability)
│ │ ├── finite_size.py ← L0 expansion + charge distribution U term
│ │ ├── screening.py ← Atomic electron screening (ratio method)
│ │ ├── exchange.py ← Hayen-2018 empirical fit coefficients
│ │ └── radiative.py ← Outer radiative corrections, soft-photon resummation
│ ├── data/
│ │ └── exchange_coeff.csv ← Tabulated coefficients for X(Z,W), Z=2..120
│ └── output/ ← visualization, CSV export
│ ├── core.py ← BetaSpectrumAnalyzer
│ ├── csv_export.py ← CSV export
│ └── visualization.py ← Plotting built from exported CSV
├── data/
│ ├── exchange_coeff.csv # Tabulated coefficients for X(Z,W), Z=2..120
│ └── custom_input_example.json # Sample JSON input file
├── output/ # Test run outputs (gitignored except .gitkeep)
├── AGENTS.md # Agent instructions: stack, git workflow, TDD, quality gates
├── CONVENTIONS.md # Physics conventions: natural units, energy representation, component design
├── TODO.md # Development roadmap
│ └── custom_input_example.json ← Sample JSON input file
├── notebooks/ ← Calculator notebook + nbmake conftest
├── output/ ← Test run outputs (gitignored except .gitkeep)
├── tests/
│ ├── physics/ ← Physics component tests
│ ├── quality/ ← Code quality tests (logging, API, nuclear data)
│ ├── integration/ ← CLI and pipeline integration tests
│ └── common/ ← Shared hypothesis + property tests
├── scripts/ ← docs_check.py, notebook runners
├── AGENTS.md ← Agent instructions: stack, git workflow, TDD, quality gates
├── CONVENTIONS.md ← Physics conventions: natural units, energy representation, component design
├── TODO.md ← Development roadmap
```
______________________________________________________________________
@@ -117,15 +102,22 @@ Defined in `beta_spectrum/spectrum.py`. Orchestrates the calculation:
### `BetaSpectrumAnalyzer`
In `beta_spectrum.visualize`. Provides debugging and visualization tools:
In `beta_spectrum.output`. Provides debugging and visualization tools:
- `total_spectrum(normalize=True/False)` — computed spectrum array
- `total_spectrum(normalize=True/False)` — computed spectrum array; raises `ValueError` when every branch is degenerate (non-positive integral)
- `physical_branch_spectrum(i)` — physical spectrum of branch *i*: the core (phase space × Fermi function) times every enabled higher-order correction, clamped beyond the branch endpoint (unnormalized)
- `universal_factor()` — product of the components shared across all branches (Fermi function and the universal higher-order corrections)
- `plot_analysis(save_path=None, show_components=True)` — plot with two modes:
- `show_components=True` (debug mode): 4-panel figure (spectrum, components, cumulative effect, deviations)
- `show_components=False` (normal mode): single spectrum plot with nuclear data header
- All plots include commit hash and UTC timestamp for traceability
- `export_to_csv(filename)` — export via pandas DataFrame with YAML-style metadata header
- `get_data()` — return all numerical data for custom analysis
- Plots are built *from the exported CSV file* (temporary full-verbosity export), so a plot always shows exactly the bytes the CSV export produces
- `export_to_csv(filename)` — export via pandas DataFrame with YAML-style metadata header:
- the basic spectrum is always the core `phase_space × fermi` (the header carries a `# core:` line); finite size, charge distribution, screening, exchange and radiative are higher-order corrections
- `branch_{i}_spectrum` columns contain the *physical* branch spectra weighted by intensity and normalized so that the sum of branch columns equals `total_spectrum`
- with `separate_branches=True` (CLI `--separate-branches`) each branch column integrates to 1 (intensity ignored, no total_spectrum column)
- `parse_spectrum_csv(path)` — read an exported CSV and its metadata header (module function in `beta_spectrum.output.visualization`)
- `get_data()` — return all numerical data for downstream analysis
______________________________________________________________________
@@ -133,7 +125,7 @@ ______________________________________________________________________
```python
from beta_spectrum import SpectrumConfig, BetaSpectrum
from beta_spectrum.visualize import BetaSpectrumAnalyzer
from beta_spectrum.output import BetaSpectrumAnalyzer
config = SpectrumConfig(
Z_parent=90, # Thorium-232 (example)
@@ -153,27 +145,21 @@ analyzer.plot_analysis("debug.png", show_components=True) # Debug mode (4-panel
analyzer.export_to_csv("spectrum.csv") # Export data to CSV
```
### Fitting and C(W) extraction
### From the command line
```python
from beta_spectrum import SpectrumConfig, BetaSpectrum
from fitter import SpectrumModel, SpectrumFitter, CWExtractor
from exp_data.spectrum import ExpSpectrum
# Build theoretical model
spectrum = BetaSpectrum.from_config(config)
model = SpectrumModel(spectrum, detector_response=detector)
# Fit to experimental data
exp = ExpSpectrum(energies=energies_keV, counts=counts, errors=errors)
fitter = SpectrumFitter(model, exp.energies, exp.counts, exp.errors)
result = fitter.fit(x0=[1.0, 0.0])
# Extract C(W) shape factor
extractor = CWExtractor(config)
cw_result = extractor.extract(measured, errors, energies_keV, model_values, endpoint_keV=Q)
```bash
bs_pnpi --nuclide Tc99 -vv --output spectrum.csv # ENSDF lookup + debug export
bs_pnpi --input data/custom_input_example.json # JSON-driven configuration
```
CLI flags (`--e-step`, `--neutrino-mass`, `--intensity-cutoff`, `--csv-separator`, `--separate-branches`) override JSON input values only when explicitly given; every long option has a single-letter alias.
______________________________________________________________________
## Nuclear Data (paceENSDF)
`beta_spectrum.nuclear_data` retrieves decay data automatically: `get_decay_info_from_paceENSDF()`, `decay_info_to_config()`, and `create_config_from_source("paceENSDF", nuclide="Tc99")`. Z, A, endpoint energies, half-life, spin/parity, branch intensities, log*ft and forbiddenness are populated from ENSDF; per-branch endpoints are the measured transition endpoint energies (max endpoint across branches defines the spectrum endpoint). paceENSDF is an optional extra: `pip install ".[ensdf]"`.
______________________________________________________________________
## Implementation Status
@@ -181,9 +167,6 @@ ______________________________________________________________________
For a complete list of implemented and planned features, see [TODO.md](TODO.md). Key highlights:
- **Physics corrections**: phase space, Fermi function, finite size, screening, exchange, radiative (with delta_cut resummation)
- **Detector response**: Gaussian, Gaussian+tail, Tikhonov models with tabulated support
- **Multi-branch decay**: intensity-weighted sum of branch spectra, per-branch calculators with branch-specific endpoint, transition type (from ENSDF), and `--intensity-cutoff` CLI filter
- **Analysis tools**: χ² fitter (SpectrumFitter + CurveFitter), C(W) extraction pipeline, Kurie plot analysis, g_V/g_A extraction
- **Experimental data**: ExpSpectrum container, energy calibration, peak fitting, dead-time/pile-up correction
- **CLI**: `bs_pnpi --nuclide Tc99` — full paceENSDF integration (auto-lookup of Z, A, Q-value, transition type, half-life, branches, forbiddenness from ENSDF)
- **paceENSDF integration**: `get_decay_info_from_paceENSDF()`, `create_config_from_source("paceENSDF", nuclide="Tc99")`, `_parse_nuclide_symbol()` for Z=1..98, FORBIDDENNESS_MAP, `--nuclide` CLI flag
- **CLI**: `bs_pnpi` with full paceENSDF integration, structured logging (`-v`/`-vv`/`-q`), `--dry-run`, `--version`, `--log-file`
- **CSV metadata headers** with nuclide, decay and calculation parameters plus software version and git commit hash
+105 -286
View File
@@ -1,291 +1,119 @@
# Strategic Development Roadmap
## Scientific Goal
## Scientific Scope
Extract the shape factor C(W) from the experimentally measured beta spectrum of ⁹⁹Tc, and parametrize C(W) via the vector coupling constant g_V from the theory of beta decay.
High-precision calculation of beta-decay spectrum shapes. Given a nuclide, decay data and a set of correction toggles, the calculator produces C(W)-level spectral shapes accurate to a few parts in 10⁻⁴ down to 1 keV for low-to-medium Z nuclei (Hayen et al. approach).
This requires repeating the analysis pipeline of [Paulsen et al., Phys. Rev. C **110**, 055503 (2024)](https://doi.org/10.1103/PhysRevC/110/055503), which used Metallic Magnetic Calorimeters (MMCs) to measure the ⁹⁹Tc beta spectrum with sub-keV threshold and ~100 eV resolution.
See `docs/refs/2024_Paulsen_T99_beta_spectrum/summary.md` for full analysis details.
For forbidden nonunique decays the shape factor C(W) enters the calculated spectrum as an **input**. Extracting C(W) from measured spectra and parametrizing it through coupling constants (SSM/SSNME, g_V/g_A fits, detector response modeling) is analysis work that lives in separate dedicated projects and is **out of scope** for this repository since v0.5.0.
______________________________________________________________________
## Package Architecture (v0.4.0 — In Progress)
## Package Architecture (v0.5.0)
### Current State (v0.3.x)
Everything lives in `beta_spectrum/` — a monolithic package where theory, fitting, detector response, and analysis are intermingled. This is becoming unwieldy.
### Target Architecture (v0.4.0)
Single distributable, `beta_spectrum`:
```
beta_spectrum/ ← theory-only core
├── __init__.py
├── base.py ← SpectrumComponent base
├── constants.py ← physical constants
├── utils.py ← T_to_W, W_to_T, momentum
├── spectrum.py ← BetaSpectrum, SpectrumConfig, BranchConfig
├── components/ ← individual correction factors
│ ├── phase_space.py
│ ├── fermi.py
│ ├── finite_size.py
│ ├── screening.py
│ ├── exchange.py
│ ├── radiative.py
│ └── detector_response.py ← stays here (part of theory)
└── nuclear_data.py ← paceENSDF integration
exp_data/ ← experimental data handling
├── __init__.py
├── raw_reader.py ← raw data loading (ROOT, CSV, ASCII)
├── calibration.py ← energy calibration, line fitting
├── fitters.py ← simple Gaussian fitters for cal sources
├── corrections.py ← dead-time, pile-up, background
└── spectrum.py ← experimental spectrum container
fitter/ ← main analysis framework
├── __init__.py
├── model.py ← theoretical model + detector convolution
├── fit_engine.py ← multi-parameter fitting orchestration
├── extractor.py ← C(W), g_V, g_A extraction
├── result.py ← FitResult, analysis results
# └── report.py ← PDF report generation (deferred)
visualize/ ← visualization / plotting
└── visualize/__init__.py ← BetaSpectrumAnalyzer
beta_spectrum/ ← theory-only core package
│ ├── __init__.py ← public API
│ ├── base.py ← SpectrumComponent base
│ ├── constants.py ← physical constants
│ ├── utils.py ← T_to_W, W_to_T, momentum
│ ├── spectrum.py ← BetaSpectrum, SpectrumConfig, BranchConfig
│ ├── nuclear_data.py ← paceENSDF integration + JSON input
│ ├── cli.py ← bs_pnpi command-line interface
│ ├── logging_utils.py ← structured logging helpers
│ ├── components/ ← individual correction factors
│ │ ├── phase_space.py
│ │ ├── fermi.py
│ │ ├── finite_size.py
│ │ ├── screening.py
│ │ ├── exchange.py
│ │ └── radiative.py
│ ├── data/
│ │ └── exchange_coeff.csv
│ └── output/ ← visualization, CSV export subpackage
│ ├── core.py ← BetaSpectrumAnalyzer
│ ├── csv_export.py ← CSVEmitter
│ └── visualization.py ← Visualizer, parse_spectrum_csv
```
### Rationale
- **`beta_spectrum`** — pure theory. Given a nuclide and corrections, produces a theoretical spectrum. No fitting, no data, no detector convolution.
- **`exp_data`** — experimental data handling. Loading raw detector outputs, calibrating energy scale, fitting calibration peaks, applying instrumental corrections.
- **`fitter`** — the analysis workhorse. Takes theory model + detector response + experimental data → performs convolution + fitting → extracts physics parameters.
- **Pure theory.** Given a nuclide and corrections, the package produces a theoretical spectrum. No fitting, no experimental data, no detector convolution.
- **`beta_spectrum.output`** is a calculation-output subpackage (plotting and CSV export of computed spectra), not data analysis.
### Migration Plan
### Scope Reduction (v0.5.0)
#### Phase 1: Create `exp_data` module ✅ COMPLETE
- [x] `exp_data/spectrum.py` — `ExpSpectrum` dataclass: energies, counts, errors, metadata
- [x] `exp_data/calibration.py` — `EnergyCalibrator` + `CalibrationResult`: linear/quadratic channel→energy
- [x] `exp_data/fitters.py` — `GaussianFitter` + `PeakFitter`: peak detection and fitting
- [x] `exp_data/corrections.py` — `DeadTimeCorrection`, `PileUpCorrection`, `BackgroundSubtractor`
- [x] 43 tests, ruff + mypy clean
#### Phase 2: Create `fitter` module ✅ COMPLETE
- [x] `fitter/model.py` — `SpectrumModel`: wraps `BetaSpectrum` + `DetectorResponse`, callable model with convolution
- [x] `fitter/fit_engine.py` — `SpectrumFitter` + `FitConfig` + `FitResult`: χ² fitting, covariance, profile likelihood, confidence intervals
- [x] `fitter/extractor.py` — `CWExtractor` + `GVAExtractor`: C(W) and g_V/g_A extraction
- [x] `fitter/result.py` — `AnalysisResult` + `FitSummary`: physics-specific result container
- [x] 34 tests, ruff + mypy clean
- [x] Detector convolution handles W < 1.0 edge cases
#### Phase 3: Clean up `beta_spectrum` ✅ COMPLETE
- [x] Remove `cw_extractor.py` (moved to `fitter/extractor.py`)
- [x] Remove `fitter.py` (moved to `fitter/fit_engine.py`)
- [x] Clean up `__init__.py` — only exports theory classes
- [x] Add `CurveFitter` general-purpose fitter to `fitter/fit_engine.py` for backward compat
- [x] Add `CWExtractor.fit_parametrization()` and `fit_gV_gA()` compatibility methods
- [x] Remove old `test_cw_extractor.py` (replaced by `tests/fitter/test_extractor.py` — file was removed)
- [x] 322 tests pass, ruff + mypy clean on all 26 source files
#### Phase 4: Update package structure ✅ COMPLETE
- [x] `pyproject.toml` auto-discovers all 4 packages via `setuptools.packages.find`
- [x] `beta_spectrum.cli` keeps CLI entry point (no re-exports needed)
- [x] All imports updated across 26 source files
- [x] 322 tests pass
- [x] README updated with new architecture diagram and Quick Start examples
- [x] Version bumped to 0.4.0
- `exp_data/` and `fitter/` packages (experimental data handling, calibration, χ² fit engine, C(W)/g_V/g_A extraction), their tests, the `bs_exp` CLI and the ROOT extra: removed from this repository (restore from history up to `67de4e3` if an analysis project needs them).
- `detector_response.py`: removed earlier (up to `5f99566`); the deletion decision is now final.
- `data/raw/` local copies of detector runs and Am-241 calibration files: removed (canonical copies on experiment storage).
- `docs/gA-extraction/`, `docs/nuclear-structure/`, `docs/refs/`, `docs/corrections/07-detector-response.md`: removed; literature and nuclear-structure research is to be redone in dedicated projects.
______________________________________________________________________
## Track A: ⁹⁹Tc Spectrum-Shape Analysis
## Roadmap: Calculator Capability
### A1. Research & Data Collection
### C1. Nuclear Data & Input Flexibility
- [ ] Parse additional PDF sources on ⁹⁹Tc beta decay and spectrum-shape method
- Kostensalo & Suhonen, Phys. Rev. C **96**, 024317 (2017) — theoretical spectrum shape predictions
- Suhonen, Frontier. Phys. **5**, 55 (2017) — review of g_A quenching
- Haaranen et al., Phys. Rev. C **93**, 034308 (2016) — effective coupling constants
- Additional experimental ⁹⁹Tc spectra (historical data for comparison)
- [ ] Research methods to extract g_V from fitted C(W) shape
- Study the relationship between C(W) parametrization and g_V, g_A effective values
- Understand how CVC (Conserved Vector Current) constrains g_V
- Review the spectrum-shape method formalism (Behrens-Bühring + shell model)
- [ ] Document the full analysis pipeline: experimental data → theoretical model → C(W) extraction → g_V extraction
- [x] paceENSDF integration: `get_decay_info_from_paceENSDF()`, `decay_info_to_config()`, `create_config_from_source()`, FORBIDDENNESS_MAP, `--nuclide` CLI flag
- [x] Measured per-branch transition endpoints from ENSDF (max endpoint across branches defines the spectrum endpoint)
- [x] JSON custom input (`load_json_input()`, `json_to_config()`), CLI flags override file values only when explicitly given
- [ ] Input sanity checks:
- `|Z_parent - Z_daughter|` must equal 1 (beta decay)
- For `beta_minus`: Z_daughter = Z_parent + 1; for `beta_plus`/`ec`: Z_daughter = Z_parent - 1
- Warn if decay_type contradicts Z values
- Validate `endpoint_MeV > 0` and `endpoint_MeV > level_energy_keV/1000`
- Cross-check transition_type against ENSDF forbiddenness
### A2. Detector Response Function
### C2. Multiple Branch Decays
- [x] ~~Implement analytical detector response function for 4π semiconductor detector~~
- ~~Gaussian core with low-energy tail (charge collection effects)~~
- ~~Parameters: energy resolution σ(E), tail fraction, tail shape~~
- ~~Energy-dependent resolution σ(E) = a + b·√E~~
- ~~Implemented: Gaussian, Gaussian+tail, Tikhonov models with energy-dependent resolution and Fano factor~~
- [x] ~~Implement convolution routines~~
- ~~Convolve theoretical spectrum with detector response for comparison to data~~
- ~~Implemented: convolve() with tabulated response support~~
- [ ] Monte Carlo simulation (GEANT4) — out of scope for this project but noted for future
- Full detector geometry simulation
- Energy loss in source, dead layers, absorber
- Response matrix for unfolding (as done in Paulsen et al. with EGSNRC)
- [x] `BranchConfig` dataclass, multi-branch `BetaSpectrum` with per-branch calculators
- [x] Intensity normalization to 100% across branches; per-branch spectra masked beyond their endpoint
- [x] `--intensity-cutoff` CLI filter
- [x] Core invariant: phase space × Fermi function included unconditionally (`phase_space` and `fermi` have no toggles)
- [x] Physical branch spectra and universal factor in CSV export; `--separate-branches` normalization mode
### A3. Data Processing & Analysis
### C3. Recoil and Weak Magnetism
- [ ] Data processing pipeline for 1-hour run spectra
- Background subtraction
- Energy calibration (using known X-ray/gamma lines)
- Dead-time correction
- Pulse pile-up correction
- [ ] Endpoint fitting and energy calibration
- Kurie plot analysis for endpoint determination
- Polynomial calibration using known spectral lines
- Linearity correction across energy range
- [ ] Data quality assessment procedures
- Residual analysis (data vs model)
- χ²/ndf evaluation
- Consistency checks across multiple runs
- Statistical combination of long-exposure spectra
Theory documented in `docs/corrections/06-recoil.md`; implementation pending.
### A4. Fitter for C(W) Extraction
- [ ] Implement recoil correction (R_N, Q terms, weak magnetism) as a `SpectrumComponent`
- [x] ~~Implement fitter routine to extract experimental C(W) from data~~
- ~~χ² minimization: theoretical spectrum (with all corrections) vs measured spectrum~~
- ~~Free parameters: C(W) shape parameters, endpoint energy, normalization, background~~
- ~~Covariance matrix and uncertainty propagation~~
- ~~Implemented: `CurveFitter` with `least_squares` optimization, `FitResult` with covariance, chi2, residuals~~
- [x] ~~Parametrize C(W) in terms of g_V (and g_A)~~
- ~~Fit C(W) data to theoretical parametrization~~
- ~~Extract g_V^eff and g_A^eff from the fit~~
- ~~Compare with Paulsen et al. results: g_A^eff = 0.574(36), g_V^eff = 0.376(5)~~
- ~~Implemented: `CWExtractor` with Kurie plot analysis, parametrized fitting, g_V/g_A extraction~~
- [ ] Systematic uncertainty analysis
- Vary correction implementations within uncertainties
- Test sensitivity to detector response model
- Background model dependence
### C4. Beta+ / Electron Capture Support
______________________________________________________________________
- [ ] Fermi function sign change for positrons
- [ ] Radiative corrections for β⁺ (Sirlin corrections differ for e⁻ vs e⁺)
- [ ] Screening correction for positrons (different atomic interaction)
- [ ] Exchange correction (negligible for β⁺ but check)
## Track B: Package Usability Improvements
### C5. Neutrino Spectrum Support
### B1. Nuclear Data Integration
- [ ] Convenience analyser methods for plotting neutrino spectra (convert electron spectrum, plot both scaled)
- [ ] Neutrino radiative correction h(W̃, W0) per Sirlin 2011; conversion f_ν(W_ν) = f_e(W̃) · [1 + (α/2π)(h − g)]
- [x] ~~paceENSDF integration for automated nuclear data retrieval~~
- ~~`get_decay_info_from_paceENSDF(nuclide, decay_type)` — retrieves Q-value, half-life, spin/parity, branches, forbiddenness from ENSDF~~
- ~~`decay_info_to_config()` — converts DecayInfo to SpectrumConfig with branch support~~
- ~~`create_config_from_source("paceENSDF", nuclide="Tc99")` — one-liner config creation~~
- ~~`_parse_nuclide_symbol()` — parses "Tc99" → (Tc, 43, 99) with full Z lookup for Z=1..98~~
- ~~`_resolve_decay_index()` — finds correct decay index (ground state by default)~~
- ~~`_get_decay_mode_symbol()` — maps beta_minus→BM, beta_plus/ec→ECBP~~
- ~~FORBIDDENNESS_MAP — translates paceENSDF codes (0A, 1F, 2F, …) to transition_type~~
- ~~CLI `--nuclide Tc99` uses paceENSDF automatically~~
- ~~Branch intensities, log_ft, level energies, transition types all populated from ENSDF~~
- ~~Tested with Tc99 (2 branches, F2) and Co60 (multi-branch)~~
### C6. Neutrino Mass Effects
### B2. Input Flexibility
Implemented: `neutrino_mass_MeV` config field, modified phase space factor √(1 − m_ν²/(W₀−W)²), `--neutrino-mass` CLI option.
- [x] ~~Generalize parameter inputs via custom input file (YAML/JSON)~~
- ~~Declare isotope, transition type, detector parameters~~
- ~~Toggle corrections on/off per-component~~
- ~~Reproducible calculation configurations~~
- ~~Implemented: `load_json_input()`, `json_to_config()`, `DEFAULT_JSON_SCHEMA` with full detector param support, CLI `--input` flag~~
- [x] ~~Retrieve parameters directly from ENSDF database~~
- ~~Auto-populate W0, Z, A, transition type from isotope name~~
- ~~Reduce manual configuration errors~~
- ~~Implemented: `get_decay_info_from_paceENSDF()` returns DecayInfo with Z_parent, Z_daughter, A_number, endpoint_MeV, transition_type, forbiddenness_code, half_life, branches; CLI `--nuclide` uses this automatically~~
- [ ] Document which corrections need m_ν as an additional input parameter
- [ ] Study impact on radiative corrections (mass singularities)
### B3. Multiple Branch Decays
### C7. Automated Report Generation
- [x] ~~Add support for multiple branch decays~~
- ~~Calculate total spectral shape weighted by branch intensities~~
- ~~Handle decays with significant branching to excited states~~
- ~~Sum contributions from all branches with proper weighting~~
- ~~Implemented: `BranchConfig` dataclass, multi-branch `BetaSpectrum` with per-branch calculators~~
- ~~Branch data from paceENSDF: intensity, endpoint (Q-value - excitation energy), transition type~~
- ~~Intensity normalization to 100% across all branches~~
- ~~CSV export: total spectrum, per-branch spectra, per-branch components (PhaseSpace, Radiative), universal components (Fermi, Screening, etc.)~~
- ~~Plot: total spectrum + individual branches with legend (transition type, endpoint energy)~~
- ~~Debug plot: vertical layout with per-branch panels, universal components panel~~
- ~~Energy grid extends to max branch endpoint, per-branch spectra masked beyond their endpoint~~
- ~~Single-branch = trivial case of multi-branch (no separate mode)~~
- ~~Per-branch transition types extracted from ENSDF (not defaulting to 'A')~~
- ~~`--intensity-cutoff` CLI option to filter out negligible branches~~
- ~~Branch spectra plotted as raw (un-normalized) contributions so total = sum of branches~~
### B4. Beta+ Decay Support
- [ ] Modify corrections to support β⁺ decays
- Fermi function sign change for positrons
- Radiative corrections for β⁺ (Sirlin corrections differ for e⁻ vs e⁺)
- Screening correction for positrons (different atomic interaction)
- Exchange correction (negligible for β⁺ but check)
### B5. Neutrino Spectrum Support
- [ ] Add convenience analyser methods for plotting neutrino spectra
- Convert electron spectrum to neutrino spectrum
- Plot both on same figure with proper scaling
- [ ] Adopt corrections that differ for neutrino spectrum
- Implement neutrino radiative correction h(W̃, W0) per Sirlin 2011
- Conversion formula: f_ν(W_ν) = f_e(W̃) · [1 + (α/2π)(h − g)]
- Neutrino-specific corrections (no Fermi function, different radiative terms)
### B6. Neutrino Mass Effects
- [ ] Implement option to produce spectra with non-zero neutrino mass
- Modify phase space: (W0 − W)² → (W0 − W)² · √(1 − m_ν²/(W0−W)²)
- Add kink feature near endpoint for spectral distortion
- Useful for neutrino mass search applications
- [ ] Research and document implications of significant neutrino mass
- Which corrections need m_ν as input parameter?
- Impact on radiative corrections (mass singularities)
- Impact on endpoint fitting procedures
### B7. Automated Report Generation
- [ ] Implement extensive procedurally generated PDF report
- Comprehensive details of the calculation
- [ ] Procedurally generated PDF report for a calculation:
- Parameter table (nuclide, decay data, enabled corrections, version, commit)
- Component plots (phase space, Fermi, each correction factor)
- Total spectrum with all corrections applied
- Residual analysis, fit quality metrics
- Parameter table with uncertainties
- References to theoretical sources
- [ ] Extend report generation to fitters
- Fit results with covariance matrix
- Parameter correlations
- Confidence intervals
- Goodness-of-fit statistics
### B8. CLI & Output Improvements
### C8. CLI & Output Improvements
- [x] Implement structured logging system
- Log to stdout with configurable verbosity levels (-v=INFO, -vv=DEBUG)
- Visualize workflow steps: data loading, parameter resolution, calculation progress
- Log all used parameters: Z_parent, Z_daughter, A_number, endpoint, transition type, enabled corrections
- Optional log file output with timestamped filename
- Integrate Python `logging` module with custom formatter
- [x] Add metadata header to CSV output files
- Date and time of calculation (ISO 8601 format)
- Nuclide information: parent/daughter symbol, Z, A
- Decay/transition info: endpoint energy, transition type, forbiddenness
- Calculation parameters: energy step, enabled corrections, detector settings
- Software version and git commit hash (7 chars)
- Format: YAML-style comments at top of CSV file
- [ ] Optimize CLI argument design and add sanity checks
- Auto-deduce `transition_type` from `decay_type` and nuclear data — remove as explicit CLI parameter
- Auto-deduce `decay_type` from Z_parent vs Z_daughter difference — remove as explicit CLI parameter
- Add sanity checks for custom input:
- `|Z_parent - Z_daughter|` must equal 1 (beta decay)
- For `beta_minus`: Z_daughter must be Z_parent + 1
- For `beta_plus`/`ec`: Z_daughter must be Z_parent - 1
- Warn if decay_type contradicts Z values (e.g., beta+ with Z_daughter > Z_parent)
- Validate endpoint_MeV > 0 and endpoint_MeV > level_energy_keV/1000
- Cross-check transition_type against ENSDF forbiddenness (if paceENSDF source)
- [x] Add `--dry-run` option to validate input and display resolved parameters without calculation
- [x] Add `--version` flag
- [x] Add `-q/--quiet` flag to suppress terminal output
- [x] Add `./output/` directory for debug verification artifacts (gitignored except `.gitkeep`)
- [x] Add debug verification step to development workflow: `bs_pnpi -vv` with parameter consistency checks across all components, documented in DEVELOPMENT.md §4.5
- [x] Add plot output with ID tracking (commit hash + UTC timestamp) and two display modes:
- Normal mode (default): single spectrum plot with nuclear data header
- Debug mode (-vv): 4-panel view with all correction factors and deviations
- [ ] Auto-deduce `transition_type` from nuclear data (remove as explicit parameter where possible)
- [ ] Auto-deduce `decay_type` from Z_parent vs Z_daughter difference
- [x] Structured logging (`-v`, `-vv`, `-q`, `--log-file`)
- [x] CSV metadata headers (ISO timestamp, nuclide, decay, calculation params, version, commit hash)
- [x] `--dry-run`, `--version`, single-letter aliases for every long option
- [x] Plots built from the exported CSV; commit hash + UTC timestamp stamps; normal/debug display modes
______________________________________________________________________
@@ -293,55 +121,46 @@ ______________________________________________________________________
### Additional Corrections
Not implementing at this time. Additional corrections (weak magnetism, recoil kinematics, etc.) require a complex implementation with project-wide consequences. This is a far-future plan, irrelevant to the current ⁹⁹Tc analysis goal where the shape factor C(W) absorbs all unmodelled energy-dependent effects.
Additional corrections beyond the implemented set require complex implementation with project-wide consequences; deferred as far-future work.
### Nuclear Shape Factors C(Z,W)
### Theoretical Shape Factors C(Z,W)
Not implementing theoretical shape factor calculations. The entire point of this project is to **extract** C(W) from experimental data, not to compute it from nuclear theory (shell model, QRPA, etc.). Theoretical shape factor calculations require:
- Nuclear structure models (NUSHELLX, shell model codes)
- One-body transition densities (OBTDs)
- Coulomb displacement energy calculations
- Multiple effective interactions
These are handled by the nuclear physics community. Our role is to provide the analysis framework that extracts C(W) from data, then compare the extracted shape to theoretical predictions.
Never computed here. Shell-model / QRPA transition densities and effective interactions belong to nuclear-structure codes; this repository treats C(W) as an input parameter of the spectrum model. The dedicated research into nuclear shape factors will be redone in separate projects.
______________________________________________________________________
## Current Status
**Version:** 0.3.1 → **In progress: 0.4.0 — Package restructuring**
**Version:** v0.5.0 — theory-only scope reduction. Calculation logic unchanged from v0.4.1.
### Completed (v0.3.x)
- All theoretical corrections: phase space, Fermi function, finite size, screening, exchange, radiative (with delta_cut resummation)
- Detector response module with analytical models (Gaussian, Gaussian+tail, Tikhonov), convolution API
- χ² curve fitting framework (CurveFitter) with confidence intervals, profile likelihood, correlation analysis
- C(W) shape factor extraction pipeline (CWExtractor) with Kurie plot analysis, parametrized fitting, g_V/g_A extraction
### Completed (v0.3.x — theory core)
- All implemented theoretical corrections: phase space, Fermi function, finite size, screening, exchange, radiative (with delta_cut resummation)
- Multi-branch decay support (unified single/multi-branch)
- CLI interface (`bs_pnpi`) with paceENSDF integration (`--nuclide`), structured logging (-v/-vv/-q), --dry-run, --version, --log-file
- CLI (`bs_pnpi`) with paceENSDF integration, structured logging, `--dry-run`, `--version`, `--log-file`
- CSV metadata headers with branch info
- JSON input with full detector param support
- paceENSDF integration: `get_decay_info_from_paceENSDF()` returns DecayInfo with Z, A, endpoint, transition type, half-life, spin/parity, branches
- Comprehensive test suite (262 tests, including 6 paceENSDF integration tests)
- paceENSDF `DecayInfo` retrieval (Z, A, endpoint, transition type, half-life, spin/parity, branches)
- Notebook quality control with nbmake and auto-save plot hooks
- Plot output: unified multi-branch approach, externalized headers, compact legends, consistent titles
- Plot output: unified multi-branch approach, externalized headers, consistent titles
### Completed (v0.4.0)
- **Track B: Package restructuring** — separating into `beta_spectrum` (theory), `exp_data` (experimental data), `fitter` (analysis framework)
- [x] Phase 1: Create `exp_data` module (raw_data.py, calibration.py, fitters.py, corrections.py, spectrum.py) — 43 tests
- [x] Phase 2: Create `fitter` module (model.py, fit_engine.py, extractor.py, result.py) — 34 tests
- [x] Phase 3: Clean up `beta_spectrum` (remove cw_extractor.py, fitter.py, __init__.py cleanup) — 322 tests
- [x] Phase 4: Package auto-discovered by setuptools, all imports updated, black formatting applied
- **v0.4.0 is COMPLETE**
- 322 tests pass (262 original + 60 new)
- ruff + mypy clean on all 26 source files
- black formatting applied to all new code
- `pyproject.toml` auto-discovers all 4 packages: `beta_spectrum`, `beta_spectrum.visualize`, `exp_data`, `fitter`
- README updated with new architecture diagram and Quick Start examples
- Version bumped to 0.4.0
### Completed (v0.4.0 — package split, later obsolete)
- Interim split into `beta_spectrum` / `exp_data` / `fitter` packages
- Superseded by the v0.5.0 scope reduction: the analysis packages were removed entirely
### Completed (v0.4.1 — normalization & CLI)
- Core spectrum made unconditional (`phase_space × fermi`): core toggles removed, physical branch spectra in CSV export, plots built from the exported CSV
- Normalization audit fixes: lone surviving branch defines the spectrum, CLI options propagate through the JSON flow, positive-integral guard for both export modes, paceENSDF per-branch measured endpoints (Co60 fixed), single delegated paceENSDF config builder
- CLI surface refresh: `--separate-branches` rename, single-letter aliases, dry-run cleanup, stale detector_* JSON schema keys removed
### Completed (v0.5.0 — scope reduction)
- Removal of `exp_data/`, `fitter/`, raw detector data, analysis notebooks/scripts
- Documentation rewrite to theory-only scope
- 280 tests pass (theory-only suite), ruff + mypy clean
### Next Immediate Step
- **Track A: ⁹⁹Tc spectrum-shape analysis** — start real data processing pipeline
- Data processing pipeline for experimental runs
- Endpoint fitting and energy calibration
- Systematic uncertainty analysis
- C1 input sanity checks (decay/Z consistency, endpoint validation)
- C3 recoil & weak magnetism correction implementation
+2 -2
View File
@@ -11,7 +11,7 @@ from .components.exchange import ExchangeCorrection
from .components.radiative import RadiativeCorrection
from .spectrum import BetaSpectrum, SpectrumConfig, BranchConfig
from .visualize import BetaSpectrumAnalyzer
from .output import BetaSpectrumAnalyzer
from .nuclear_data import (
DecayInfo,
BranchInfo,
@@ -52,4 +52,4 @@ __all__ = [
"DEFAULT_JSON_SCHEMA",
]
__version__ = "0.4.0"
__version__ = "0.5.0"
+102 -63
View File
@@ -40,7 +40,6 @@ def _build_parser() -> argparse.ArgumentParser:
" bs_pnpi --nuclide Tc99 --output spectrum.csv\n"
" bs_pnpi --input custom_input.json --output spectrum.csv\n"
" bs_pnpi --nuclide Tc99 --plot spectrum.png --output spectrum.csv\n"
" bs_pnpi --nuclide Tc99 --detector gaussian --output convolved.csv\n"
" bs_pnpi --nuclide Tc99 --output spectrum.csv -vv --log-file /tmp/calc.log\n"
" bs_pnpi --nuclide Tc99 --dry-run\n"
),
@@ -66,12 +65,14 @@ def _build_parser() -> argparse.ArgumentParser:
# Source selection flags (backward compatible, mutually exclusive)
source_group = parser.add_mutually_exclusive_group(required=False)
source_group.add_argument(
"-N",
"--nuclide",
type=str,
help="Nuclide symbol for paceENSDF lookup (e.g. 'Tc99', 'Co60'). "
"Retrieves decay parameters from the ENSDF database via paceENSDF.",
)
source_group.add_argument(
"-i",
"--input",
type=str,
metavar="FILE",
@@ -80,14 +81,27 @@ def _build_parser() -> argparse.ArgumentParser:
# Common parameters
parser.add_argument(
"-s",
"--e-step",
type=float,
default=0.001,
help="Energy step size in MeV (default: 0.001). All energies are in MeV throughout the toolkit.",
default=None,
help="Energy step size in MeV (default: 0.001 for nuclide input; "
"for JSON input the file's e_step_MeV is used when the flag is "
"omitted). All energies are in MeV throughout the toolkit.",
)
parser.add_argument(
"-m",
"--neutrino-mass",
type=float,
default=None,
help="Neutrino mass in MeV (default: 0.0, or the JSON file's "
"neutrino_mass_MeV when omitted). Set to non-zero value to include "
"neutrino mass effects in phase space calculation.",
)
# Output
parser.add_argument(
"-o",
"--output",
type=str,
metavar="FILE",
@@ -96,6 +110,7 @@ def _build_parser() -> argparse.ArgumentParser:
# Plotting
parser.add_argument(
"-p",
"--plot",
type=str,
metavar="FILE",
@@ -119,6 +134,7 @@ def _build_parser() -> argparse.ArgumentParser:
help="Suppress all terminal output (only errors shown).",
)
log_group.add_argument(
"-L",
"--log-file",
type=str,
metavar="PATH",
@@ -126,19 +142,45 @@ def _build_parser() -> argparse.ArgumentParser:
"Log file is always opened in append mode at DEBUG level.",
)
log_group.add_argument(
"-d",
"--dry-run",
action="store_true",
help="Validate input and display resolved configuration without computing spectrum.",
)
log_group.add_argument(
"-c",
"--intensity-cutoff",
type=float,
default=0.0,
default=None,
metavar="FRAC",
help="Minimum branch intensity fraction to include in the spectrum "
"(default: 0.0 = include all branches). "
"Branches below this threshold are excluded from the calculation and plots.",
)
log_group.add_argument(
"-V",
"--csv-verbosity",
type=str,
default="full",
choices=["full", "concise"],
help="CSV output verbosity: 'full' (all components) or 'concise' (total spectrum and normalized branch components only) (default: full)",
)
log_group.add_argument(
"-S",
"--csv-separator",
type=str,
default=None,
help="CSV column separator (default: ','). Use 'tab' for tab separation.",
)
log_group.add_argument(
"-b",
"--separate-branches",
action="store_true",
default=None,
help="Emit separate branch spectra, each normalized to integrate to "
"1 (intensities ignored; no total_spectrum column). Default: total "
"spectrum with intensity-normalized branch columns.",
)
return parser
@@ -150,16 +192,11 @@ def _run(args: argparse.Namespace) -> None:
from beta_spectrum.logging_utils import get_logger
from beta_spectrum.nuclear_data import (
create_config_from_source,
get_decay_info_from_paceENSDF,
json_to_config,
load_json_input,
)
from beta_spectrum.spectrum import (
BetaSpectrum,
BranchConfig,
SpectrumConfig,
)
from beta_spectrum.visualize import BetaSpectrumAnalyzer
from beta_spectrum.spectrum import BetaSpectrum
from beta_spectrum.output import BetaSpectrumAnalyzer
# Determine logging level
if args.quiet:
@@ -207,57 +244,45 @@ def _run(args: argparse.Namespace) -> None:
else:
source_type = "paceENSDF"
# Build config
# Resolve optional CLI flags: None means "not provided". For paceENSDF
# they fall back to literal defaults; for JSON they let the file's own
# values through unless explicitly overridden.
e_step_MeV = 0.001 if args.e_step is None else float(args.e_step)
neutrino_mass_MeV = 0.0 if args.neutrino_mass is None else float(args.neutrino_mass)
intensity_cutoff = (
0.0 if args.intensity_cutoff is None else float(args.intensity_cutoff)
)
csv_separator = "," if args.csv_separator is None else args.csv_separator
separate_branches = bool(args.separate_branches)
# Build config — a single delegated path for every data source.
config: SpectrumConfig
if source_type == "paceENSDF":
logger.info("Source: paceENSDF nuclide=%s", source_value)
decay_info = get_decay_info_from_paceENSDF(source_value, "beta_minus")
# Normalize branch intensities to sum to 1.0
if decay_info.branches:
total_intensity = sum(b.intensity for b in decay_info.branches)
if total_intensity > 0:
branches = [
BranchConfig(
endpoint_MeV=(
(decay_info.endpoint_MeV - b.level_energy_keV / 1000.0)
if b.level_energy_keV > 0
else decay_info.endpoint_MeV
),
transition_type=b.transition_type,
intensity=b.intensity / total_intensity,
)
for b in decay_info.branches
if b.intensity / total_intensity >= args.intensity_cutoff
]
config = SpectrumConfig(
Z_parent=decay_info.Z_parent,
Z_daughter=decay_info.Z_daughter,
A_number=decay_info.A_number,
endpoint_MeV=decay_info.endpoint_MeV,
transition_type=decay_info.transition_type,
e_step_MeV=args.e_step,
branches=branches,
intensity_cutoff=args.intensity_cutoff,
)
else:
config = create_config_from_source(
"paceENSDF",
nuclide=source_value,
e_step_MeV=args.e_step,
intensity_cutoff=args.intensity_cutoff,
)
else:
config = create_config_from_source(
"paceENSDF",
nuclide=source_value,
e_step_MeV=args.e_step,
intensity_cutoff=args.intensity_cutoff,
e_step_MeV=e_step_MeV,
intensity_cutoff=intensity_cutoff,
neutrino_mass_MeV=neutrino_mass_MeV,
csv_separator=csv_separator,
separate_branches=separate_branches,
)
else:
logger.info("Source: JSON file=%s", source_value)
data = load_json_input(source_value)
config = json_to_config(data)
# CLI flags override the JSON file values only when explicitly
# provided; unset flags keep the file's own values.
if args.e_step is not None:
config.e_step_MeV = e_step_MeV
if args.neutrino_mass is not None:
config.neutrino_mass_MeV = neutrino_mass_MeV
if args.intensity_cutoff is not None:
config.intensity_cutoff = intensity_cutoff
if args.csv_separator is not None:
config.csv_separator = csv_separator
config.separate_branches = separate_branches
# Dry run: display resolved config and exit
if args.dry_run:
@@ -288,12 +313,25 @@ def _run(args: argparse.Namespace) -> None:
# Export to CSV
if args.output:
analyzer = BetaSpectrumAnalyzer(spectrum, config, logger=logger)
analyzer.export_to_csv(args.output, source_type=source_type)
analyzer.export_to_csv(
args.output, source_type=source_type, csv_verbosity=args.csv_verbosity
)
logger.info("Spectrum exported to %s", args.output)
# Generate plot
# Generate plot — always built from the exported CSV data (single
# source of truth): when a full-verbosity CSV was exported it is
# plotted directly from that file, otherwise via a temporary export.
if args.plot:
analyzer = BetaSpectrumAnalyzer(spectrum, config, logger=logger)
if args.output and args.csv_verbosity == "full":
from beta_spectrum.output.visualization import Visualizer
Visualizer.plot_from_csv(
args.output,
save_path=args.plot,
show_components=(args.verbose >= 2),
)
else:
analyzer.plot_analysis(
save_path=args.plot,
show_components=(args.verbose >= 2),
@@ -307,11 +345,8 @@ def _print_dry_run_output(config, source_type: str) -> None:
"""Print resolved configuration for dry-run mode."""
from beta_spectrum.logging_utils import get_git_short_hash
print("Core: phase_space × fermi (basic spectrum, always included)")
enabled = []
if config.use_phase_space:
enabled.append("phase_space")
if config.use_fermi:
enabled.append("fermi")
if config.use_screening:
enabled.append("screening")
if config.use_finite_size:
@@ -331,8 +366,8 @@ def _print_dry_run_output(config, source_type: str) -> None:
f"Nuclide: {_decay_notation(config.Z_parent, config.Z_daughter, config.A_number)}"
)
print(f"Endpoint: {config.endpoint_MeV * 1000:.1f} keV")
print(f"Transition: {config.transition_type}")
print(f"e_step: {config.e_step_MeV:.4f} MeV")
print(f"neutrino_mass_MeV: {config.neutrino_mass_MeV:.6f}")
print(f"Corrections: {', '.join(enabled) if enabled else 'none'}")
if config.branches and len(config.branches) > 1:
@@ -350,8 +385,6 @@ def _print_dry_run_output(config, source_type: str) -> None:
else:
print("Mode: single-branch")
# Detector response is a Phase 4 feature — not yet implemented
print("Detector: not configured (Phase 4)")
print("========================================")
@@ -473,10 +506,16 @@ def _decay_notation(Z_parent: int, Z_daughter: int, A: int) -> str:
return f"{_nuclide_symbol(Z_parent, A)} -> {_nuclide_symbol(Z_daughter, A)}"
def main() -> None:
"""Main entry point for the CLI."""
def main(argv: list[str] | None = None) -> None:
"""Main entry point for the CLI.
Parameters
----------
argv : list[str], optional
Explicit argument vector (for tests/embedding); None reads sys.argv.
"""
parser = _build_parser()
args = parser.parse_args()
args = parser.parse_args(argv)
try:
_run(args)
+21
View File
@@ -50,10 +50,26 @@ class PhaseSpace(SpectrumComponent):
w_e = W
p_e = momentum(W, self.m_e)
w_nu = self.W0 - W
# Calculate p_nu, handling the case where w_nu < self.m_nu
# In this case, the neutrino energy is less than its mass, which is unphysical
# The phase space should be 0 in this region
if self.m_nu > 0:
# Check where w_nu < self.m_nu
below_threshold = w_nu < self.m_nu
# Calculate p_nu only where it's physically valid
p_nu = np.zeros_like(w_nu)
valid_mask = ~below_threshold
if np.any(valid_mask):
p_nu[valid_mask] = momentum(w_nu[valid_mask], self.m_nu)
else:
below_threshold = np.zeros_like(w_nu, dtype=bool)
p_nu = momentum(w_nu, self.m_nu)
# Calculate phase space factor
phase_space = p_e * w_e * p_nu * w_nu
# Calculate forbid factor
if self.tr_type in ["A", "F1"]:
forbid_factor: float | np.ndarray = 1
elif self.tr_type in ["F1U", "F2"]:
@@ -66,6 +82,11 @@ class PhaseSpace(SpectrumComponent):
)
result = np.asarray(phase_space * forbid_factor, dtype=np.float64)
# For regions below the neutrino mass threshold, the phase space should be 0
if self.m_nu > 0:
result[below_threshold] = 0.0
if self._logger:
self._logger.debug(
"PhaseSpace: output range=[%.6e, %.6e]",
+55 -25
View File
@@ -96,6 +96,10 @@ class BranchInfo:
log(ft) value for this branch.
transition_type : str
Forbiddenness classification for this branch (A, F1, F1U, F2, ...).
endpoint_MeV : float
Measured beta endpoint energy of this transition in MeV (ENSDF
column 7). 0.0 means "not provided": consumers fall back to
base-endpoint minus level-energy subtraction.
"""
level_index: int
@@ -103,6 +107,7 @@ class BranchInfo:
intensity: float
log_ft: Optional[float]
transition_type: str = "A"
endpoint_MeV: float = 0.0
def _parse_nuclide_symbol(symbol: str) -> Tuple[str, int, int]:
@@ -384,24 +389,19 @@ def get_decay_info_from_paceENSDF(
# Extract Q-value from ground state branch (state[7] = endpoint energy in keV)
# and half-life from halflife_data
endpoint_keV = 0.0
half_life_str = "unknown"
# The spectrum endpoint is the maximum measured transition endpoint
# across all branches (ENSDF column 7, keV). Selecting the ground
# state branch or the first branch is wrong whenever those branches
# are absent or non-maximal (e.g. Co60, whose first listed branch
# endpoint is below higher-level transition endpoints).
endpoint_keV = 0.0
if beta_data:
for k, v in beta_data.items():
for _k, v in beta_data.items():
for state in v:
if state[0] == 0: # ground state branch
endpoint_keV = float(state[7])
break
if endpoint_keV > 0:
break
# No ground state branch — use first available branch
if endpoint_keV == 0.0:
for k, v in beta_data.items():
if v and len(v) > 0:
endpoint_keV = float(v[0][7])
break
if len(state) > 7 and state[7] is not None:
endpoint_keV = max(endpoint_keV, float(state[7]))
if halflife_data:
for k, v in halflife_data.items():
@@ -447,6 +447,11 @@ def get_decay_info_from_paceENSDF(
intensity=float(state[9]) / 100.0,
log_ft=float(state[11]) if state[11] is not None else None,
transition_type=branch_transition_type,
endpoint_MeV=(
float(state[7]) / 1000.0
if len(state) > 7 and state[7] is not None
else 0.0
),
)
branches.append(branch)
@@ -470,6 +475,9 @@ def decay_info_to_config(
info: DecayInfo,
e_step_MeV: float = 0.001,
intensity_cutoff: float = 0.0,
neutrino_mass_MeV: float = 0.0,
csv_separator: str = ",",
separate_branches: bool = False,
) -> SpectrumConfig:
"""
Convert DecayInfo to a SpectrumConfig.
@@ -482,18 +490,32 @@ def decay_info_to_config(
Energy step size in MeV.
intensity_cutoff : float
Minimum branch intensity fraction to include.
neutrino_mass_MeV : float
Neutrino mass in MeV.
csv_separator : str
CSV column separator.
separate_branches : bool
If True, emit separate branch spectra, each normalized to
integrate to 1 (intensity ignored; no total spectrum column).
Returns
-------
SpectrumConfig
Configuration ready for BetaSpectrum.from_config().
"""
# Convert BranchInfo -> BranchConfig for SpectrumConfig
# Convert BranchInfo -> BranchConfig for SpectrumConfig. Per-branch
# endpoints are the measured transition endpoint energies when
# available; the base-endpoint minus level-energy subtraction is only
# a legacy fallback for hand-built DecayInfo objects without them.
branches: Optional[List[BranchConfig]] = None
if info.branches:
branches = [
BranchConfig(
endpoint_MeV=info.endpoint_MeV - b.level_energy_keV / 1000.0,
endpoint_MeV=(
b.endpoint_MeV
if b.endpoint_MeV > 0.0
else info.endpoint_MeV - b.level_energy_keV / 1000.0
),
transition_type=b.transition_type,
intensity=b.intensity,
)
@@ -510,8 +532,11 @@ def decay_info_to_config(
endpoint_MeV=info.endpoint_MeV,
transition_type=info.transition_type,
e_step_MeV=e_step_MeV,
neutrino_mass_MeV=neutrino_mass_MeV,
branches=branches,
intensity_cutoff=intensity_cutoff,
csv_separator=csv_separator,
separate_branches=separate_branches,
)
@@ -529,14 +554,7 @@ DEFAULT_JSON_SCHEMA = {
"A_number": 99,
"transition_type": "F2",
"e_step_MeV": 0.001,
"detector_model": "gaussian",
"detector_sigma_a_keV": 1.0,
"detector_sigma_b": 0.0,
"detector_tail_fraction": 0.0,
"detector_tau_keV": 5.0,
"detector_fano_factor": 0.12,
"detector_n_channels": 4096,
"detector_channel_energy_range": [0.0, 0.35],
"neutrino_mass_MeV": 0.0,
}
@@ -602,8 +620,13 @@ def load_json_input(filepath: str) -> Dict[str, Any]:
if not path.exists():
raise FileNotFoundError(f"Input file not found: {filepath}")
with open(path, "r") as f:
try:
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
except OSError as exc:
raise FileNotFoundError(f"Input file not readable: {filepath}") from exc
except json.JSONDecodeError as exc:
raise ValueError(f"Invalid JSON in input file {filepath}: {exc}") from exc
# Fill in defaults for missing optional fields
for key, default in DEFAULT_JSON_SCHEMA.items():
@@ -638,6 +661,7 @@ def json_to_config(data: Dict[str, Any]) -> SpectrumConfig:
endpoint_MeV=float(data["endpoint_MeV"]),
transition_type=str(data.get("transition_type", "A")),
e_step_MeV=float(data.get("e_step_MeV", 0.001)),
neutrino_mass_MeV=float(data.get("neutrino_mass_MeV", 0.0)),
)
@@ -683,11 +707,17 @@ def create_config_from_source(
decay_index = kwargs.get("decay_index", None)
e_step = kwargs.get("e_step_MeV", 0.001)
intensity_cutoff = kwargs.get("intensity_cutoff", 0.0)
neutrino_mass = kwargs.get("neutrino_mass_MeV", 0.0)
csv_separator = kwargs.get("csv_separator", ",")
separate_branches = kwargs.get("separate_branches", False)
info = get_decay_info_from_paceENSDF(nuclide, decay_type, decay_index)
return decay_info_to_config(
info,
e_step_MeV=e_step,
intensity_cutoff=intensity_cutoff,
neutrino_mass_MeV=neutrino_mass,
csv_separator=csv_separator,
separate_branches=separate_branches,
)
elif source == "json":
if not json_path:
+20
View File
@@ -0,0 +1,20 @@
"""
Output module — BetaSpectrumAnalyzer class and related functionality.
Introspection, analysis, visualization and export tools for BetaSpectrum.
All nuclides (single- and multi-branch) are treated uniformly as one or more
branches — single-branch is just a special case with one branch.
"""
from __future__ import annotations
from .core import BetaSpectrumAnalyzer, _UnifiedBranch
from .csv_export import CSVEmitter
from .visualization import Visualizer
__all__ = [
"BetaSpectrumAnalyzer",
"CSVEmitter",
"Visualizer",
"_UnifiedBranch",
]
+339
View File
@@ -0,0 +1,339 @@
"""
Core analyzer module — BetaSpectrumAnalyzer base class.
Core introspection and analysis tools for BetaSpectrum.
All nuclides (single- and multi-branch) are treated uniformly as one or more
branches — single-branch is just a special case with one branch.
"""
from __future__ import annotations
import logging
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any, Dict, List, Optional
import numpy as np
from beta_spectrum.components.phase_space import PhaseSpace
from beta_spectrum.components.radiative import RadiativeCorrection
from beta_spectrum.utils import T_to_W
if TYPE_CHECKING:
from beta_spectrum.spectrum import BetaSpectrum, SpectrumConfig
class BetaSpectrumAnalyzer:
"""
Introspection, analysis and debugging tools for BetaSpectrum.
Parameters
----------
spectrum : BetaSpectrum
The spectrum to analyze.
config : SpectrumConfig
Configuration used to create the spectrum.
logger : logging.Logger, optional
Logger for progress output.
"""
def __init__(
self,
spectrum: BetaSpectrum,
config: SpectrumConfig,
logger: Optional[logging.Logger] = None,
):
"""
Initialize analyzer with a spectrum and configuration.
"""
self.spectrum = spectrum
self.config = config
self.W, self.energies_MeV = spectrum.get_energy_grid(config)
self._components_cache: Optional[Dict[str, np.ndarray]] = None
self._universal_cache: Optional[np.ndarray] = None
self._physical_branch_cache: Dict[int, np.ndarray] = {}
self._logger = logger
self._is_multi = spectrum._is_multi
self._timestamp: Optional[str] = None
# Unified branch view: single-branch treated as one-branch multi-branch.
# When config.branches carries exactly one branch, that branch (not the
# top-level decay fields) defines the endpoint and transition type.
if not self._is_multi and self.spectrum.branch_spectra == []:
endpoint_MeV = config.endpoint_MeV
transition_type = config.transition_type
if config.branches is not None and len(config.branches) == 1:
lone = config.branches[0]
endpoint_MeV = float(lone.endpoint_MeV)
transition_type = str(lone.transition_type)
self._unified_branches: List[_UnifiedBranch] = [
_UnifiedBranch(
endpoint_MeV=endpoint_MeV,
transition_type=transition_type,
intensity=1.0,
)
]
else:
self._unified_branches = [
_UnifiedBranch(
endpoint_MeV=b.endpoint_MeV,
transition_type=b.transition_type,
intensity=b.intensity,
)
for b in self.spectrum.branches
]
@property
def components(self) -> Dict[str, np.ndarray]:
if self._components_cache is None:
raw = self.spectrum.calculate_components(self.W)
# Normalize keys: single-branch has bare names,
# multi-branch has branch_0., branch_1., etc.
# We keep the raw keys for export_to_csv compatibility.
self._components_cache = raw
assert self._components_cache is not None
return self._components_cache
def universal_factor(self) -> np.ndarray:
"""Product of the components shared by all branches.
The universal factor always contains the Fermi function (the core
0-order Coulomb correction) and every enabled higher-order
correction that does not depend on the branch endpoint
(screening, finite size, charge distribution, exchange).
In single-branch mode this equals the product of all components
except the per-branch phase space and radiative corrections.
"""
if self._universal_cache is None:
prod: Optional[np.ndarray] = None
for comp in self.spectrum.components:
if not self._is_multi and isinstance(
comp, (PhaseSpace, RadiativeCorrection)
):
continue
values = comp(self.W)
prod = (
np.array(values, dtype=np.float64)
if prod is None
else prod * values
)
if prod is None:
prod = np.ones_like(self.W, dtype=np.float64)
self._universal_cache = prod
assert self._universal_cache is not None
return self._universal_cache
def physical_branch_spectrum(self, i: int) -> np.ndarray:
"""Physical spectrum of branch i: core × corrections, unnormalized.
The physical branch spectrum is
core(phase_space × fermi) × all enabled higher-order corrections,
masked to zero beyond the branch endpoint. This is the canonical
branch accessor: every consumer (CSV export, plots) must use it so
that the Fermi function is always present in branch-level spectra.
"""
if i in self._physical_branch_cache:
return self._physical_branch_cache[i]
if i < len(self.spectrum.branch_spectra):
raw = self.spectrum.branch_spectra[i](self.W)
W0 = T_to_W(self._unified_branches[i].endpoint_MeV)
raw = np.where(self.W <= W0, raw, 0.0)
physical = self.universal_factor() * raw
else:
# Single-branch mode: the spectrum itself is the full physical branch
physical = np.asarray(self.spectrum(self.W), dtype=np.float64)
self._physical_branch_cache[i] = physical
return physical
def total_integral(self) -> float:
"""Integral of the raw total spectrum over kinetic energy.
Raises
------
ValueError
If the integral is not positive (e.g. all branches are
degenerate because the neutrino mass exceeds every endpoint).
"""
total_raw = self.spectrum(self.W)
integral = float(np.trapezoid(total_raw, self.energies_MeV))
if integral <= 0.0:
raise ValueError(
"Total spectrum integral is non-positive: every branch is "
"degenerate (check neutrino_mass vs branch endpoints and "
"intensity_cutoff)"
)
return integral
def total_spectrum(self, normalize: bool = True) -> np.ndarray:
"""Calculate the total spectrum."""
total = self.spectrum(self.W)
if normalize:
integral = np.trapezoid(total, self.energies_MeV)
if integral <= 0.0:
raise ValueError(
"Total spectrum integral is non-positive: every branch "
"is degenerate (check neutrino_mass vs branch endpoints "
"and intensity_cutoff)"
)
total = total / integral
return total
# ---------------------------------------------------------------------------
# Unified branch view
# ---------------------------------------------------------------------------
@property
def _n_branches(self) -> int:
"""Number of branches (unified view)."""
return len(self._unified_branches)
def _get_component_key(self, i: int, name: str) -> str:
"""Get the canonical key for a per-branch component."""
if self._is_multi:
return f"branch_{i}.{name}"
return name
def _element_symbol(self, Z: int) -> str:
"""Return chemical symbol for atomic number Z."""
symbols = [
"",
"H",
"He",
"Li",
"Be",
"B",
"C",
"N",
"O",
"F",
"Ne",
"Na",
"Mg",
"Al",
"Si",
"P",
"S",
"Cl",
"Ar",
"K",
"Ca",
"Sc",
"Ti",
"V",
"Cr",
"Mn",
"Fe",
"Co",
"Ni",
"Cu",
"Zn",
"Ga",
"Ge",
"As",
"Se",
"Br",
"Kr",
"Rb",
"Sr",
"Y",
"Zr",
"Nb",
"Mo",
"Tc",
"Ru",
"Rh",
"Pd",
"Ag",
"Cd",
"In",
"Sn",
"Sb",
"Te",
"I",
"Xe",
"Cs",
"Ba",
"La",
"Ce",
"Pr",
"Nd",
"Pm",
"Sm",
"Eu",
"Gd",
"Tb",
"Dy",
"Ho",
"Er",
"Tm",
"Yb",
"Lu",
"Hf",
"Ta",
"W",
"Re",
"Os",
"Ir",
"Pt",
"Au",
"Hg",
"Tl",
"Pb",
"Bi",
"Po",
"At",
"Rn",
"Fr",
"Ra",
"Ac",
"Th",
"Pa",
"U",
"Np",
"Pu",
"Am",
"Cm",
"Bk",
"Cf",
"Es",
]
return symbols[Z] if Z < len(symbols) else f"Z{Z}"
def get_data(self) -> Dict[str, Any]:
"""Get all numerical data for custom analysis."""
return {
"energies_MeV": self.energies_MeV,
"energies_W": self.W,
"spectrum": self.total_spectrum(normalize=True),
"components": self.components,
"config": self.config,
}
def export_to_csv(
self, filename: str, source_type: str = "unknown", csv_verbosity: str = "full"
) -> None:
"""Export spectrum data to CSV with metadata header."""
from .csv_export import CSVEmitter
CSVEmitter.export_to_csv(self, filename, source_type, csv_verbosity)
def plot_analysis(
self,
save_path: Optional[str] = None,
show_components: bool = True,
) -> None:
"""Create visualization of the spectrum and all correction factors."""
from .visualization import Visualizer
Visualizer.plot_analysis(self, save_path, show_components)
@dataclass
class _UnifiedBranch:
"""Unified branch view for both single- and multi-branch modes."""
endpoint_MeV: float
transition_type: str
intensity: float
+242
View File
@@ -0,0 +1,242 @@
"""
CSV export module — CSV export functionality for BetaSpectrumAnalyzer.
Column semantics (v0.4.1 onward):
- The basic spectrum is ALWAYS core = phase_space x fermi; the Fermi function
is a core spectrum component, never an optional correction. Higher-order
corrections (finite size, charge distribution, screening, exchange,
radiative) are multiplied on top of the core.
- ``branch_{i}_spectrum`` columns carry the PHYSICAL branch spectrum
(core x all enabled corrections), weighted by intensity and
area-normalized so that the sum of branch columns equals total_spectrum
(the total column itself is normalized to integrate to 1). In the legacy
export branch columns contained only the per-branch phase-space x
radiative product WITHOUT the Fermi function — physically wrong.
- ``separate_branches`` mode: each branch column is normalized to
integrate to 1 individually, intensity is ignored, no total column.
"""
from __future__ import annotations
from datetime import datetime, timezone
from typing import TYPE_CHECKING, Dict
import numpy as np
import pandas as pd
from beta_spectrum.logging_utils import get_git_short_hash
if TYPE_CHECKING:
from beta_spectrum.output.core import BetaSpectrumAnalyzer
class CSVEmitter:
"""CSV export functionality for BetaSpectrumAnalyzer."""
@staticmethod
def export_to_csv(
analyzer: BetaSpectrumAnalyzer,
filename: str,
source_type: str = "unknown",
csv_verbosity: str = "full",
) -> None:
"""Export spectrum data to CSV with metadata header.
Parameters
----------
analyzer : BetaSpectrumAnalyzer
The analyzer instance to export data from.
filename : str
Output CSV file path.
source_type : str
Source type (paceENSDF, json, etc.).
csv_verbosity : str
Verbosity level: 'full' (all components) or 'concise' (total
spectrum and physical branch spectra only).
Raises
------
ValueError
If every branch spectrum integrates to a non-positive area
(e.g. all branch endpoints lie below the neutrino mass).
"""
components = analyzer.components
config = analyzer.config
separate = config.separate_branches
n_branches = analyzer._n_branches
# Physical branch spectra: core(phase_space x fermi) x corrections.
physical = [analyzer.physical_branch_spectrum(i) for i in range(n_branches)]
# Only higher-order corrections are toggleable; list them enabled.
enabled = []
if config.use_screening:
enabled.append("screening")
if config.use_finite_size:
enabled.append("finite_size")
if config.use_charge_dist:
enabled.append("charge_dist")
if config.use_radiative:
enabled.append("radiative")
if config.use_exchange:
enabled.append("exchange")
# The header endpoint must describe the spectrum that is actually
# computed. Single mode with exactly one configured branch follows
# the branch (the top-level endpoint may be stale from the parent
# decay, e.g. after intensity-cutoff filtering).
basic_endpoint_MeV = config.endpoint_MeV
if (
not analyzer._is_multi
and config.branches is not None
and len(config.branches) == 1
):
basic_endpoint_MeV = analyzer._unified_branches[0].endpoint_MeV
parent_symbol = analyzer._element_symbol(config.Z_parent)
daughter_symbol = analyzer._element_symbol(config.Z_daughter)
git_commit = get_git_short_hash()
if source_type == "paceENSDF":
source_line = "# source: paceENSDF (https://pypi.org/project/paceENSDF/)"
else:
source_line = f"# source: {source_type}"
header_lines = [
f"# beta-spectrum v{__import__('beta_spectrum').__version__}"
f"(build.{git_commit})",
f"# timestamp: {datetime.now(timezone.utc).isoformat()}",
source_line,
f"# nuclide: {parent_symbol}{config.A_number} "
f"-> {daughter_symbol}{config.A_number} "
f"(Z={config.Z_parent}->{config.Z_daughter})",
f"# basic_endpoint: {basic_endpoint_MeV * 1000:.1f} keV",
]
if config.neutrino_mass_MeV > 0:
header_lines.append(
f"# neutrino_mass_MeV: {config.neutrino_mass_MeV:.6f} "
f"(non-zero neutrino mass effects included)"
)
else:
header_lines.append(f"# neutrino_mass_MeV: {config.neutrino_mass_MeV:.6f}")
header_lines.append("# core: phase_space x fermi (basic spectrum)")
header_lines.append(f"# corrections: {', '.join(enabled)}")
header_lines.append(f"# e_step: {config.e_step_MeV:.4f} MeV")
header_lines.append(f"# n_points: {len(analyzer.energies_MeV)}")
if separate:
header_lines.append(
"# branch normalization: separate "
"(each branch integrates to 1, intensity ignored)"
)
else:
header_lines.append(
"# branch normalization: intensity "
"(sum of branch columns == total_spectrum)"
)
# Separate branches into active and excluded lists
active_branches = []
excluded_branches = []
for i, branch in enumerate(analyzer._unified_branches):
branch_endpoint_keV = branch.endpoint_MeV * 1000
neutrino_mass_keV = config.neutrino_mass_MeV * 1000
if branch_endpoint_keV <= neutrino_mass_keV:
excluded_branches.append(
f" branch_{i + 1}: E₀={branch_endpoint_keV:.1f} keV, "
f"transition={branch.transition_type}, "
f"intensity={branch.intensity:.4f} - excluded: endpoint < m_nu"
)
elif branch.intensity < config.intensity_cutoff:
excluded_branches.append(
f" branch_{i + 1}: E₀={branch_endpoint_keV:.1f} keV, "
f"transition={branch.transition_type}, "
f"intensity={branch.intensity:.4f} "
"- excluded: intensity < cutoff"
)
else:
active_branches.append(
f" branch_{i + 1}: E₀={branch_endpoint_keV:.1f} keV, "
f"transition={branch.transition_type}, "
f"intensity={branch.intensity:.4f}"
)
if active_branches:
header_lines.append("# active branches:")
header_lines.extend(f"# {b}" for b in active_branches)
if excluded_branches:
header_lines.append("# excluded branches:")
header_lines.extend(f"# {b}" for b in excluded_branches)
# ----- data columns -----
data: Dict[str, np.ndarray] = {
"energy_MeV": analyzer.energies_MeV,
}
# Positive-integral guard applies to BOTH normalization modes: a
# degenerate spectrum must never be written (not even as zeros).
total_raw = analyzer.spectrum(analyzer.W)
integral = float(np.trapezoid(total_raw, analyzer.energies_MeV))
if integral <= 0.0:
raise ValueError(
"Total spectrum integral is non-positive: every branch "
"is degenerate (check neutrino_mass vs branch endpoints "
"and intensity_cutoff)"
)
if not separate:
data["total_spectrum"] = total_raw / integral
for i in range(n_branches):
key_i = i + 1
if csv_verbosity == "concise":
col_name = f"branch_{key_i}"
else:
col_name = f"branch_{key_i}_spectrum"
if separate:
area = float(np.trapezoid(physical[i], analyzer.energies_MeV))
if area > 0.0:
data[col_name] = physical[i] / area
else:
data[col_name] = np.zeros_like(physical[i])
else:
intensity_i = analyzer._unified_branches[i].intensity
data[col_name] = physical[i] * intensity_i / integral
if csv_verbosity == "full":
# Per-branch component columns (factorized ingredients).
# Only multi-branch mode carries per-branch factors: in single
# mode the bare PhaseSpace/Radiative columns below already
# describe the single spectrum, and prefixed duplicates would
# silently disagree with them once renamed.
if analyzer._is_multi:
for i in range(n_branches):
for comp_name in ["PhaseSpace", "Radiative"]:
key = analyzer._get_component_key(i, comp_name)
if key in components:
data[f"branch_{i + 1}_{comp_name}"] = components[key]
# Universal components (no prefix): Fermi plus universal
# higher-order corrections. In single mode the core factors
# (PhaseSpace, Radiative) also appear here unprefixed.
for name, values in components.items():
if not name.startswith("branch_"):
data[name] = values
df = pd.DataFrame(data)
separator = "\t" if config.csv_separator == "tab" else config.csv_separator
try:
with open(filename, "w") as f:
f.write("\n".join(header_lines) + "\n")
df.to_csv(f, index=False, float_format="%.4e", sep=separator)
except OSError as exc:
raise ValueError(f"Cannot write spectrum CSV {filename}: {exc}") from exc
if analyzer._logger:
analyzer._logger.info("CSV exported to %s", filename)
+468
View File
@@ -0,0 +1,468 @@
"""
Visualization module — plotting built ON TOP OF the CSV export.
All graphical representations are produced from the exported CSV file
(data columns + metadata header) and never recomputed from the analyzer in
parallel. The CSV file is the single source of truth: a plot is produced
exactly from the bytes a researcher would download, which turns every plot
into an additional quality check of the export routine.
"""
from __future__ import annotations
import re
import shutil
import tempfile
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.figure import Figure
from .csv_export import CSVEmitter
if TYPE_CHECKING:
from beta_spectrum.output.core import BetaSpectrumAnalyzer
_BRANCH_LINE_RE = re.compile(
r"^\s*branch_(\d+):\s*E₀=([0-9.]+)\s*keV,\s*transition=(\w+),\s*"
r"intensity=([0-9.]+)(.*)$"
)
_EXCLUDED_SUFFIX_RE = re.compile(r"-\s*excluded:\s*(.*)$")
_BRANCH_COLORS = [
"#1f77b4",
"#ff7f0e",
"#2ca02c",
"#d62728",
"#9467bd",
"#8c564b",
"#e377c2",
"#7f7f7f",
"#bcbd22",
"#17becf",
]
def parse_spectrum_csv(
csv_path: str,
) -> Tuple[pd.DataFrame, Dict[str, Any]]:
"""Read an exported spectrum CSV and parse its metadata header.
Parameters
----------
csv_path : str
Path to a CSV file produced by CSVEmitter.export_to_csv.
Returns
-------
tuple[pandas.DataFrame, dict]
The data frame (comment lines skipped, separator auto-detected)
and a metadata dict with keys: version, build, timestamp, source,
nuclide, endpoint_keV, neutrino_mass_MeV, corrections, e_step_MeV,
n_points, normalization, branches.
Raises
------
ValueError
If the file cannot be read or parsed.
"""
try:
df = pd.read_csv(csv_path, sep=None, engine="python", comment="#")
raw_lines = []
with open(csv_path, "r") as f:
for line in f:
if line.startswith("#"):
raw_lines.append(line.rstrip("\n"))
else:
break # header block ended
except OSError as exc:
raise ValueError(f"Cannot read spectrum CSV {csv_path}: {exc}") from exc
meta: Dict[str, Any] = {
"version": None,
"build": None,
"timestamp": None,
"source": None,
"nuclide": None,
"endpoint_keV": None,
"neutrino_mass_MeV": None,
"corrections": [],
"e_step_MeV": None,
"n_points": None,
"normalization": None,
"branches": [],
}
for line in raw_lines:
body = line.lstrip("#").strip()
m = re.match(r"beta-spectrum v([\d.]+)\(build\.(\w+)\)", body)
if m:
meta["version"] = m.group(1)
meta["build"] = m.group(2)
continue
m = re.match(r"timestamp:\s*(.+)", body)
if m:
meta["timestamp"] = m.group(1).strip()
continue
m = re.match(r"source:\s*(\S+)", body)
if m:
meta["source"] = m.group(1)
continue
m = re.match(r"nuclide:\s*(.+?)\s+\(Z=", body)
if m:
meta["nuclide"] = m.group(1).strip()
continue
m = re.match(r"basic_endpoint:\s*([0-9.]+)\s*keV", body)
if m:
meta["endpoint_keV"] = float(m.group(1))
continue
m = re.match(r"neutrino_mass_MeV:\s*([0-9.]+)", body)
if m:
meta["neutrino_mass_MeV"] = float(m.group(1))
continue
m = re.match(r"corrections:\s*(.*)", body)
if m:
meta["corrections"] = [
c.strip() for c in m.group(1).split(",") if c.strip()
]
continue
m = re.match(r"e_step:\s*([0-9.]+)\s*MeV", body)
if m:
meta["e_step_MeV"] = float(m.group(1))
continue
m = re.match(r"n_points:\s*(\d+)", body)
if m:
meta["n_points"] = int(m.group(1))
continue
m = re.match(r"branch normalization:\s*(\S+)", body)
if m:
meta["normalization"] = m.group(1)
continue
m = _BRANCH_LINE_RE.match(body)
if m:
excluded = _EXCLUDED_SUFFIX_RE.search(m.group(5))
meta["branches"].append(
{
"index": int(m.group(1)),
"endpoint_keV": float(m.group(2)),
"transition": m.group(3),
"intensity": float(m.group(4)),
"excluded": None if excluded is None else excluded.group(1).strip(),
}
)
continue
if meta["n_points"] is None:
meta["n_points"] = len(df)
return df, meta
def _branch_column(df: pd.DataFrame, index: int) -> Optional[str]:
"""Find the branch spectrum column name for a 1-based branch index."""
for candidate in (f"branch_{index}_spectrum", f"branch_{index}"):
if candidate in df.columns:
return candidate
return None
def _active_branches(meta: Dict[str, Any]) -> List[Dict[str, Any]]:
return [b for b in meta["branches"] if b["excluded"] is None]
def _spectrum_title(meta: Dict[str, Any]) -> str:
n_branches = len(meta["branches"])
active = len(_active_branches(meta))
title = f"Beta-decay: {meta['nuclide']}"
if n_branches > 1:
if active < n_branches:
title += f" ({active} of {n_branches} branches)"
else:
title += f" ({n_branches} branches)"
return title
def _apply_figure_header(fig: Figure, meta: Dict[str, Any]) -> None:
"""Write the figure header/footer lines from the CSV metadata."""
e_step_keV = (meta["e_step_MeV"] or 0.0) * 1000.0
endpoint = meta["endpoint_keV"]
header = f"{meta['nuclide']} | E₀={endpoint:.1f} keV | ΔE={e_step_keV:.1f} keV"
fig.text(
0.01,
0.98,
header,
transform=fig.transFigure,
fontsize=9,
verticalalignment="top",
horizontalalignment="left",
fontfamily="monospace",
)
if meta["neutrino_mass_MeV"]:
fig.text(
0.01,
0.96,
f"| m_ν={meta['neutrino_mass_MeV']:.6f} MeV",
transform=fig.transFigure,
fontsize=8,
verticalalignment="top",
horizontalalignment="left",
fontfamily="monospace",
color="red",
)
if meta["build"]:
fig.text(
0.01,
0.01,
f"{meta['build']} | {meta['timestamp']}",
transform=fig.transFigure,
fontsize=8,
verticalalignment="bottom",
horizontalalignment="left",
fontfamily="monospace",
color="gray",
)
def _plot_spectra_view_from_df(
df: pd.DataFrame,
meta: Dict[str, Any],
save_path: Optional[str],
) -> Figure:
"""Plot total spectrum and per-branch spectra from CSV columns."""
energies = df["energy_MeV"].to_numpy()
fig, ax = plt.subplots(figsize=(12, 7))
if "total_spectrum" in df.columns:
ax.plot(
energies,
df["total_spectrum"].to_numpy(),
"k-",
lw=2.5,
label="Total spectrum",
zorder=10,
)
for color_i, b in enumerate(_active_branches(meta)):
col = _branch_column(df, int(b["index"]))
if col is None:
continue
ax.plot(
energies,
df[col].to_numpy(),
color=_BRANCH_COLORS[color_i % len(_BRANCH_COLORS)],
lw=1.5,
alpha=0.7,
label=(
f"Br. {b['index']}: {b['endpoint_keV']:.1f} keV "
f"({b['intensity'] * 100:.1f}%)"
),
)
ax.set_xlabel(r"Electron kinetic energy $E$ [MeV]", fontsize=12)
ax.set_ylabel(r"Normalized spectrum [MeV$^{-1}$]", fontsize=12)
fig.suptitle(_spectrum_title(meta), fontsize=14, fontweight="bold")
ax.set_yscale("log")
ax.grid(True, alpha=0.3)
ax.legend(loc="best", fontsize=9, ncol=2)
_apply_figure_header(fig, meta)
fig.tight_layout()
if save_path:
fig.savefig(save_path, dpi=150, bbox_inches="tight")
print(f"Figure saved to {save_path}")
return fig
def _plot_debug_view_from_df(
df: pd.DataFrame,
meta: Dict[str, Any],
save_path: Optional[str],
) -> Figure:
"""Debug view (4 panels) built entirely from CSV columns."""
energies = df["energy_MeV"].to_numpy()
active = _active_branches(meta)
fig, axes = plt.subplots(4, 1, figsize=(12, 16), sharex=True)
fig.suptitle(_spectrum_title(meta), fontsize=14, fontweight="bold")
# Panel 1: total spectrum + per-branch spectra
ax = axes[0]
if "total_spectrum" in df.columns:
ax.plot(
energies,
df["total_spectrum"].to_numpy(),
"k-",
lw=2,
label="Total spectrum",
zorder=10,
)
for color_i, b in enumerate(active):
col = _branch_column(df, int(b["index"]))
if col is None:
continue
ax.plot(
energies,
df[col].to_numpy(),
color=_BRANCH_COLORS[color_i % len(_BRANCH_COLORS)],
lw=1.5,
alpha=0.7,
label=f"Br. {b['index']} ({b['intensity'] * 100:.1f}%)",
)
ax.set_yscale("log")
ax.set_ylabel(r"Normalized spectrum [MeV$^{-1}$]", fontsize=10)
ax.set_title("Spectrum", fontsize=11)
ax.grid(True, alpha=0.3)
ax.legend(fontsize=8, loc="best", ncol=2)
# Panel 2: Fermi function + all phase spaces
ax = axes[1]
if "Fermi" in df.columns:
fermi = df["Fermi"].to_numpy()
fermi_scaled = fermi / np.max(fermi)
ax.plot(energies, fermi_scaled, "r-", lw=1.5, label="Fermi", alpha=0.8)
for color_i, b in enumerate(active):
col = f"branch_{b['index']}_PhaseSpace"
if col not in df.columns:
# Single-branch CSVs carry the core factors unprefixed.
col = "PhaseSpace"
if col not in df.columns:
continue
ps_vals = df[col].to_numpy()
ps_max = np.max(ps_vals) if np.any(ps_vals > 0) else 1.0
ax.plot(
energies,
ps_vals / ps_max if ps_max > 0 else ps_vals,
color=_BRANCH_COLORS[color_i % len(_BRANCH_COLORS)],
lw=1.5,
alpha=0.7,
label=f"PS Br. {b['index']}",
)
ax.set_ylabel("Normalized Factor", fontsize=10)
ax.set_title("Fermi & Phase Space", fontsize=11)
ax.grid(True, alpha=0.3)
ax.legend(fontsize=8, loc="best")
# Panel 3: universal higher-order corrections
ax = axes[2]
correction_colors = ["green", "orange", "purple", "brown"]
for corr_i, name in enumerate(
["Screening", "FiniteSizeL0", "ChargeDistributionU", "Exchange"]
):
if name in df.columns:
values = df[name].to_numpy()
values_scaled = values / np.max(values)
ax.plot(
energies,
values_scaled,
color=correction_colors[corr_i],
lw=1.5,
label=name,
alpha=0.8,
)
ax.set_ylabel("Normalized Factor", fontsize=10)
ax.set_title("Universal Corrections", fontsize=11)
ax.grid(True, alpha=0.3)
ax.legend(fontsize=8, loc="best")
# Panel 4: W0-dependent corrections (Radiative per branch)
ax = axes[3]
for color_i, b in enumerate(active):
col = f"branch_{b['index']}_Radiative"
if col not in df.columns:
# Single-branch CSVs carry the core factors unprefixed.
col = "Radiative"
if col not in df.columns:
continue
ax.plot(
energies,
df[col].to_numpy(),
color=_BRANCH_COLORS[color_i % len(_BRANCH_COLORS)],
lw=1.5,
alpha=0.7,
label=f"Br. {b['index']}",
)
ax.set_ylabel("Radiative Correction", fontsize=10)
ax.set_xlabel(r"Electron kinetic energy $E$ [MeV]", fontsize=10)
ax.set_title("Radiative Corrections", fontsize=11)
ax.grid(True, alpha=0.3)
ax.legend(fontsize=8, loc="best", ncol=2)
_apply_figure_header(fig, meta)
fig.tight_layout()
if save_path:
fig.savefig(save_path, dpi=150, bbox_inches="tight")
print(f"Figure saved to {save_path}")
return fig
class Visualizer:
"""Visualization built from the exported CSV data (single source of truth)."""
@staticmethod
def plot_from_csv(
csv_path: str,
save_path: Optional[str] = None,
show_components: bool = True,
) -> Figure:
"""Create a visualization from an exported CSV spectrum file.
Parameters
----------
csv_path : str
Path to a CSV exported by CSVEmitter.export_to_csv. The plots
are built exclusively from the file contents (data columns and
metadata header) — nothing is recomputed from the model.
save_path : str, optional
Path to save the figure.
show_components : bool
If True, produce the full 4-panel debug view; requires the
'full' CSV verbosity. If the file lacks component columns, a
warning is printed and the simple spectrum view is produced.
"""
df, meta = parse_spectrum_csv(csv_path)
if show_components and "Fermi" in df.columns:
return _plot_debug_view_from_df(df, meta, save_path)
if show_components:
print(
"Warning: requested component panels but CSV lacks component "
"columns (export with --csv-verbosity full); falling back to "
"simple spectrum view."
)
return _plot_spectra_view_from_df(df, meta, save_path)
@staticmethod
def plot_analysis(
analyzer: BetaSpectrumAnalyzer,
save_path: Optional[str] = None,
show_components: bool = True,
) -> Figure:
"""Create a visualization by exporting CSV and plotting from it.
The export goes to a temporary file using the 'full' verbosity so
that the plots exercise the same data pipeline (header + columns)
a researcher receives.
"""
tmp_dir = tempfile.mkdtemp(prefix="bs_plot_")
try:
csv_path = f"{tmp_dir}/spectrum.csv"
CSVEmitter.export_to_csv(
analyzer,
csv_path,
source_type="plot",
csv_verbosity="full",
)
return Visualizer.plot_from_csv(
csv_path,
save_path=save_path,
show_components=show_components,
)
finally:
shutil.rmtree(tmp_dir, ignore_errors=True)
+80 -40
View File
@@ -2,7 +2,7 @@
from __future__ import annotations
import logging
from typing import TYPE_CHECKING, Dict, List, Optional, Tuple
from typing import Dict, List, Optional, Tuple
from dataclasses import dataclass
import numpy as np
@@ -57,9 +57,6 @@ class SpectrumConfig:
"""
Configuration for beta spectrum calculation.
Supports declarative detector response specification:
to enable automatic detector smearing via convolve_with_detector().
Supports multi-branch decay via the `branches` field. When branches are
provided, the total spectrum is the intensity-weighted sum of individual
branch spectra. Each branch has its own endpoint energy and transition type.
@@ -71,10 +68,11 @@ class SpectrumConfig:
endpoint_MeV: float
transition_type: str = "A" # should be [A, F1, F1U, F2, F2U, F3, F3U, F4]
e_step_MeV: float = 0.001
neutrino_mass_MeV: float = 0.0
# Toggle components and corrections
use_phase_space: bool = True
use_fermi: bool = True
# Higher-order corrections. The core spectrum components (phase space
# and the Fermi function, both 0-order) are unconditional: there are
# deliberately no toggles for them.
use_screening: bool = True
use_finite_size: bool = True
use_charge_dist: bool = True
@@ -87,6 +85,28 @@ class SpectrumConfig:
0.0 # Ignore branches below this fraction (default: 0.0 = all)
)
# CSV output options
csv_separator: str = "," # Default separator for CSV output
separate_branches: bool = (
False # If True: emit separate branch spectra, each normalized to
)
# integrate to 1 (intensity ignored); no total spectrum column
def _effective_single_fields(config: SpectrumConfig) -> Tuple[float, str]:
"""Resolve (endpoint_MeV, transition_type) for single-branch mode.
When config.branches carries exactly one branch (e.g. after intensity
cutoff filtering removed all but one), that branch defines the spectrum:
its endpoint and transition type win over the (possibly stale) top-level
fields inherited from the parent decay. With branches=None the top-level
fields are used as before.
"""
if config.branches is not None and len(config.branches) == 1:
lone = config.branches[0]
return float(lone.endpoint_MeV), str(lone.transition_type)
return float(config.endpoint_MeV), str(config.transition_type)
class BetaSpectrum:
"""
@@ -245,16 +265,27 @@ class BetaSpectrum:
def _from_config_single(
cls, config: SpectrumConfig, logger: Optional[logging.Logger] = None
) -> BetaSpectrum:
"""Create single-branch spectrum calculator."""
"""Create single-branch spectrum calculator.
The core spectrum components (phase space x Fermi function, both
0-order) are always built; only higher-order corrections are
conditional.
"""
components: List[SpectrumComponent] = []
W0 = float(T_to_W(config.endpoint_MeV))
eff_endpoint, eff_transition = _effective_single_fields(config)
W0 = float(T_to_W(eff_endpoint))
if config.use_phase_space:
# Core spectrum components (0-order): always included.
# Convert neutrino mass from MeV to natural units
m_nu_natural = config.neutrino_mass_MeV / ME_MEV
components.append(
PhaseSpace(W0=W0, transition_type=config.transition_type, logger=logger)
PhaseSpace(
W0=W0,
transition_type=eff_transition,
m_nu=m_nu_natural,
logger=logger,
)
)
if config.use_fermi:
components.append(
FermiFunction(Z=config.Z_daughter, A=config.A_number, logger=logger)
)
@@ -308,6 +339,8 @@ class BetaSpectrum:
Note: Radiative correction is branch-dependent (depends on W0), so it goes
into each branch's calculator, not the universal components.
"""
if config.branches is None:
raise ValueError("branches must be provided for multi-branch mode")
if logger:
logger.info("Multi-branch mode: %d branches", len(config.branches))
@@ -323,36 +356,42 @@ class BetaSpectrum:
branch.intensity,
)
# Create a per-branch config with the branch's endpoint and transition type
branch_config = SpectrumConfig(
Z_parent=config.Z_parent,
Z_daughter=config.Z_daughter,
A_number=config.A_number,
endpoint_MeV=branch.endpoint_MeV,
# Per-branch calculator: the per-branch core phase space
# (unconditional) plus optional per-branch radiative
# corrections. The universal Fermi function and universal
# higher-order corrections are applied outside, so they must
# NOT be built here (a Fermi function per branch would
# double-apply it).
W0_branch = float(T_to_W(branch.endpoint_MeV))
m_nu_natural = config.neutrino_mass_MeV / ME_MEV
branch_components: List[SpectrumComponent] = [
PhaseSpace(
W0=W0_branch,
transition_type=branch.transition_type,
e_step_MeV=config.e_step_MeV,
# Branch-dependent components
use_phase_space=config.use_phase_space,
use_radiative=config.use_radiative,
# Universal components disabled (handled separately)
use_fermi=False,
use_screening=False,
use_finite_size=False,
use_charge_dist=False,
use_exchange=False,
m_nu=m_nu_natural,
logger=logger,
)
]
if config.use_radiative:
branch_components.append(
RadiativeCorrection(
W0=W0_branch,
Z=config.Z_daughter,
A=config.A_number,
use_endpoint_resummation=True,
logger=logger,
)
)
branch_spectrum = cls._from_config_single(branch_config, logger)
branch_spectra.append(branch_spectrum)
branch_spectra.append(cls(branch_components, logger=logger))
# Create universal components (shared across all branches)
# Note: Radiative is NOT included here because it depends on W0
universal_components: List[SpectrumComponent] = []
if config.use_fermi:
universal_components.append(
# The Fermi function is a core spectrum component (0-order Coulomb
# correction): always included, not a higher-order correction.
universal_components: List[SpectrumComponent] = [
FermiFunction(Z=config.Z_daughter, A=config.A_number, logger=logger)
)
]
if config.use_finite_size:
universal_components.append(
@@ -394,6 +433,11 @@ class BetaSpectrum:
"""
if self._is_multi and self.branches:
max_endpoint = max(b.endpoint_MeV for b in self.branches)
elif config.branches is not None and len(config.branches) == 1:
# Single-mode with exactly one configured branch: the branch
# endpoint defines the grid, not the (possibly stale) top-level
# endpoint from the parent decay.
max_endpoint = float(config.branches[0].endpoint_MeV)
else:
max_endpoint = config.endpoint_MeV
@@ -451,7 +495,3 @@ class BetaSpectrum:
branch_spectrum = branch_spec(W)
result.append(self.branches[i].intensity * np.maximum(0.0, branch_spectrum))
return result
-543
View File
@@ -1,543 +0,0 @@
"""
Visualize module — BetaSpectrumAnalyzer class.
Visualization and plotting tools for BetaSpectrum.
All nuclides (single- and multi-branch) are treated uniformly as one or more
branches — single-branch is just a special case with one branch.
"""
from __future__ import annotations
import logging
import pandas as pd
from datetime import datetime, timezone
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any, Dict, List, Optional
import numpy as np
import matplotlib.pyplot as plt
from beta_spectrum.logging_utils import get_git_short_hash
from beta_spectrum.utils import T_to_W
if TYPE_CHECKING:
from matplotlib.figure import Figure
from beta_spectrum.spectrum import BetaSpectrum, SpectrumConfig
class BetaSpectrumAnalyzer:
"""
Introspection, analysis and debugging tools for BetaSpectrum.
Parameters
----------
spectrum : BetaSpectrum
The spectrum to analyze.
config : SpectrumConfig
Configuration used to create the spectrum.
logger : logging.Logger, optional
Logger for progress output.
"""
def __init__(
self,
spectrum: BetaSpectrum,
config: SpectrumConfig,
logger: Optional[logging.Logger] = None,
):
"""
Initialize analyzer with a spectrum and configuration.
"""
self.spectrum = spectrum
self.config = config
self.W, self.energies_MeV = spectrum.get_energy_grid(config)
self._components_cache: Optional[Dict[str, np.ndarray]] = None
self._branch_spectra_cache: Optional[List[np.ndarray]] = None
self._logger = logger
self._is_multi = spectrum._is_multi
# Unified branch view: single-branch treated as one-branch multi-branch
if not self._is_multi and self.spectrum.branch_spectra == []:
self._unified_branches: List[_UnifiedBranch] = [
_UnifiedBranch(
endpoint_MeV=config.endpoint_MeV,
transition_type=config.transition_type,
intensity=1.0,
)
]
else:
self._unified_branches = [
_UnifiedBranch(
endpoint_MeV=b.endpoint_MeV,
transition_type=b.transition_type,
intensity=b.intensity,
)
for b in self.spectrum.branches
]
@property
def components(self) -> Dict[str, np.ndarray]:
if self._components_cache is None:
raw = self.spectrum.calculate_components(self.W)
# Normalize keys: single-branch has bare names,
# multi-branch has branch_0., branch_1., etc.
# We keep the raw keys for export_to_csv compatibility.
self._components_cache = raw
assert self._components_cache is not None
return self._components_cache
@property
def branch_spectra(self) -> List[np.ndarray]:
"""Intensity-weighted branch spectra."""
if self._branch_spectra_cache is None:
self._branch_spectra_cache = self.spectrum.get_branch_normalized_spectra(
self.W
)
assert self._branch_spectra_cache is not None
return self._branch_spectra_cache
def total_spectrum(self, normalize: bool = True) -> np.ndarray:
"""Calculate the total spectrum."""
total = self.spectrum(self.W)
if normalize:
integral = np.trapezoid(total, self.energies_MeV)
total = total / integral
return total
# ---------------------------------------------------------------------------
# Unified branch view
# ---------------------------------------------------------------------------
@property
def _n_branches(self) -> int:
"""Number of branches (unified view)."""
return len(self._unified_branches)
def _get_branch_spectrum(self, i: int) -> np.ndarray:
"""Get the spectrum array for branch i."""
if i < len(self.spectrum.branch_spectra):
bs_obj = self.spectrum.branch_spectra[i]
# branch_spectra stores BetaSpectrum objects — call them to get arrays
if hasattr(bs_obj, "__call__"):
val = bs_obj(self.W)
# Clamp to zero beyond endpoint (phase space goes negative)
W0 = T_to_W(self._unified_branches[i].endpoint_MeV)
return np.where(self.W <= W0, val, 0.0)
return np.asarray(bs_obj)
# Single-branch: the spectrum itself is the only branch
return self.spectrum(self.W)
def _get_component_key(self, i: int, name: str) -> str:
"""Get the canonical key for a per-branch component."""
if self._is_multi:
return f"branch_{i}.{name}"
return name
def _has_component(self, i: int, name: str) -> bool:
"""Check if branch i has a given component."""
key = self._get_component_key(i, name)
return key in self.components
def _get_component(self, i: int, name: str) -> np.ndarray:
"""Get a per-branch component array."""
key = self._get_component_key(i, name)
return self.components[key]
# ---------------------------------------------------------------------------
# Plotting helpers
# ---------------------------------------------------------------------------
def _build_figure_header(self, fig: "Figure") -> None:
"""Add a compact header with nuclide info, e_step, commit and timestamp outside axes."""
parent = self._element_symbol(self.config.Z_parent)
daughter = self._element_symbol(self.config.Z_daughter)
e_step_keV = self.config.e_step_MeV * 1000
header = (
f"{parent}{self.config.A_number} -> {daughter}{self.config.A_number} "
f"| E₀={self.config.endpoint_MeV * 1000:.1f} keV "
f"| ΔE={e_step_keV:.1f} keV"
)
fig.text(
0.01, 0.98, header,
transform=fig.transFigure, fontsize=9,
verticalalignment="top", horizontalalignment="left",
fontfamily="monospace",
)
id_text = f"{get_git_short_hash(6)} | {self._timestamp}"
fig.text(
0.01, 0.01, id_text,
transform=fig.transFigure, fontsize=8,
verticalalignment="bottom", horizontalalignment="left",
fontfamily="monospace", color="gray",
)
def _build_figure_title(self, fig: "Figure") -> str:
"""Build a unified figure title."""
parent = self._element_symbol(self.config.Z_parent)
daughter = self._element_symbol(self.config.Z_daughter)
n = self.config.A_number
title = f"Beta-decay: {n}{parent} -> {n}{daughter}"
if self._n_branches > 1:
title += f" ({self._n_branches} branches)"
return title
def _format_branch_label(self, i: int) -> str:
"""Abbreviated branch label for legend."""
branch = self._unified_branches[i]
e0 = branch.endpoint_MeV * 1000
intensity_pct = branch.intensity * 100
return f"Br. {i+1}: {e0:.1f} keV ({intensity_pct:.1f}%)"
# ---------------------------------------------------------------------------
# Plotting — unified multi-branch approach
# ---------------------------------------------------------------------------
def plot_analysis(
self,
save_path: Optional[str] = None,
show_components: bool = True,
) -> None:
"""
Create visualization of the spectrum and all correction factors.
Parameters
----------
save_path : str, optional
Path to save the figure.
show_components : bool
If True, show full debug view.
If False, show only the spectrum plot.
"""
total = self.total_spectrum(normalize=True)
components = self.components
self._timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC")
if show_components:
self._plot_debug_view(total, components, save_path)
else:
self._plot_spectra_view(total, components, save_path)
def _plot_spectra_view(
self,
total: np.ndarray,
components: Dict[str, np.ndarray],
save_path: Optional[str],
) -> None:
"""Plot total spectrum with individual branch decomposition."""
fig, ax = plt.subplots(figsize=(12, 7))
branch_colors = [
"#1f77b4", "#ff7f0e", "#2ca02c", "#d62728",
"#9467bd", "#8c564b", "#e377c2", "#7f7f7f",
"#bcbd22", "#17becf",
]
e_step_keV = self.config.e_step_MeV * 1000
# Compute branch contributions (universal * intensity * branch_spectrum)
branch_contribs = []
# Universal components are shared (no per-branch key prefix)
universal_names = [
"Fermi", "Screening", "FiniteSizeL0",
"ChargeDistributionU", "Exchange",
]
for i in range(self._n_branches):
universal_product = np.ones_like(self.W)
for name in universal_names:
key = self._get_component_key(i, name)
if key in components:
universal_product *= components[key]
elif name in components: # fallback: universal components
universal_product *= components[name]
bs = self._get_branch_spectrum(i)
contrib = universal_product * self._unified_branches[i].intensity * bs
branch_contribs.append(contrib)
total_raw = sum(branch_contribs)
ax.plot(
self.energies_MeV, total_raw, "k-", lw=2.5,
label="Total spectrum", zorder=10,
)
for i, contrib in enumerate(branch_contribs):
color = branch_colors[i % len(branch_colors)]
ax.plot(
self.energies_MeV, contrib, color=color, lw=1.5,
alpha=0.7, label=self._format_branch_label(i),
)
ax.set_xlabel(r"Electron kinetic energy $E$ [MeV]", fontsize=12)
ax.set_ylabel(
f"Normalized Counts / {e_step_keV:.1f} keV", fontsize=12
)
fig.suptitle(
self._build_figure_title(fig), fontsize=14, fontweight="bold"
)
ax.set_yscale("log")
ax.grid(True, alpha=0.3)
ax.legend(
loc="best", fontsize=9,
ncol=min(2, self._n_branches + 1),
)
self._build_figure_header(fig)
plt.tight_layout()
if save_path:
fig.savefig(save_path, dpi=150, bbox_inches="tight")
print(f"Figure saved to {save_path}")
def _plot_debug_view(
self,
total: np.ndarray,
components: Dict[str, np.ndarray],
save_path: Optional[str],
) -> None:
"""Debug view: vertical layout with 4 panels."""
n_branches = self._n_branches
e_step_keV = self.config.e_step_MeV * 1000
fig, axes = plt.subplots(4, 1, figsize=(12, 16), sharex=True)
fig.suptitle(
self._build_figure_title(fig), fontsize=14, fontweight="bold"
)
branch_colors = [
"#1f77b4", "#ff7f0e", "#2ca02c", "#d62728",
"#9467bd", "#8c564b", "#e377c2", "#7f7f7f",
"#bcbd22", "#17becf",
]
# Panel 1: Total spectrum + per-branch spectra
branch_contribs = []
universal_names = [
"Fermi", "Screening", "FiniteSizeL0",
"ChargeDistributionU", "Exchange",
]
for i in range(n_branches):
universal_product = np.ones_like(self.W)
for name in universal_names:
key = self._get_component_key(i, name)
if key in components:
universal_product *= components[key]
elif name in components:
universal_product *= components[name]
bs = self._get_branch_spectrum(i)
contrib = universal_product * self._unified_branches[i].intensity * bs
branch_contribs.append(contrib)
total_raw = sum(branch_contribs)
ax = axes[0]
ax.plot(
self.energies_MeV, total_raw, "k-", lw=2,
label="Total spectrum", zorder=10,
)
for i, contrib in enumerate(branch_contribs):
color = branch_colors[i % len(branch_colors)]
ax.plot(
self.energies_MeV, contrib, color=color, lw=1.5,
alpha=0.7, label=self._format_branch_label(i),
)
ax.set_yscale("log")
ax.set_ylabel(
f"Normalized Counts / {e_step_keV:.1f} keV", fontsize=10
)
ax.set_title("Spectrum", fontsize=11)
ax.grid(True, alpha=0.3)
ax.legend(fontsize=8, loc="best", ncol=min(2, n_branches + 1))
# Panel 2: Fermi function + all phase spaces
ax = axes[1]
if "Fermi" in components:
fermi_vals = components["Fermi"]
fermi_scaled = fermi_vals / np.max(fermi_vals)
ax.plot(
self.energies_MeV, fermi_scaled, "r-", lw=1.5,
label="Fermi", alpha=0.8,
)
for i in range(n_branches):
color = branch_colors[i % len(branch_colors)]
branch = self._unified_branches[i]
key = self._get_component_key(i, "PhaseSpace")
if key in components:
ps_vals = components[key]
W0_branch = T_to_W(branch.endpoint_MeV)
mask = self.W <= W0_branch
ps_masked = np.where(mask, ps_vals, 0.0)
ps_max = np.max(ps_masked)
if ps_max > 0:
ps_scaled = ps_masked / ps_max
else:
ps_scaled = ps_masked
ax.plot(
self.energies_MeV, ps_scaled, color=color, lw=1.5,
alpha=0.7, label=f"PS Br. {i+1}",
)
ax.set_ylabel("Normalized Factor", fontsize=10)
ax.set_title("Fermi & Phase Space", fontsize=11)
ax.grid(True, alpha=0.3)
ax.legend(fontsize=8, loc="best")
# Panel 3: Universal W0-independent corrections
ax = axes[2]
universal_components = [
"Screening", "FiniteSizeL0",
"ChargeDistributionU", "Exchange",
]
colors = ["green", "orange", "purple", "brown"]
for name, color in zip(universal_components, colors):
if name in components:
values = components[name]
values_scaled = values / np.max(values)
ax.plot(
self.energies_MeV, values_scaled, color=color, lw=1.5,
label=name, alpha=0.8,
)
ax.set_ylabel("Normalized Factor", fontsize=10)
ax.set_title("Universal Corrections", fontsize=11)
ax.grid(True, alpha=0.3)
ax.legend(fontsize=8, loc="best")
# Panel 4: W0-dependent corrections (Radiative for each branch)
ax = axes[3]
for i in range(n_branches):
color = branch_colors[i % len(branch_colors)]
key = self._get_component_key(i, "Radiative")
if key in components:
rad_vals = components[key]
ax.plot(
self.energies_MeV, rad_vals, color=color, lw=1.5,
alpha=0.7, label=f"Br. {i+1}",
)
ax.set_ylabel("Radiative Correction", fontsize=10)
ax.set_xlabel(r"Electron kinetic energy $E$ [MeV]", fontsize=10)
ax.set_title("Radiative Corrections", fontsize=11)
ax.grid(True, alpha=0.3)
ax.legend(fontsize=8, loc="best", ncol=min(2, n_branches + 1))
self._build_figure_header(fig)
plt.tight_layout()
if save_path:
fig.savefig(save_path, dpi=150, bbox_inches="tight")
print(f"Figure saved to {save_path}")
def _element_symbol(self, Z: int) -> str:
"""Return chemical symbol for atomic number Z."""
symbols = [
"", "H", "He", "Li", "Be", "B", "C", "N", "O", "F",
"Ne", "Na", "Mg", "Al", "Si", "P", "S", "Cl", "Ar", "K",
"Ca", "Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu",
"Zn", "Ga", "Ge", "As", "Se", "Br", "Kr", "Rb", "Sr", "Y",
"Zr", "Nb", "Mo", "Tc", "Ru", "Rh", "Pd", "Ag", "Cd", "In",
"Sn", "Sb", "Te", "I", "Xe", "Cs", "Ba", "La", "Ce", "Pr",
"Nd", "Pm", "Sm", "Eu", "Gd", "Tb", "Dy", "Ho", "Er", "Tm",
"Yb", "Lu", "Hf", "Ta", "W", "Re", "Os", "Ir", "Pt", "Au",
"Hg", "Tl", "Pb", "Bi", "Po", "At", "Rn", "Fr", "Ra", "Ac",
"Th", "Pa", "U", "Np", "Pu", "Am", "Cm", "Bk", "Cf", "Es",
]
return symbols[Z] if Z < len(symbols) else f"Z{Z}"
# ---------------------------------------------------------------------------
# Export
# ---------------------------------------------------------------------------
def export_to_csv(self, filename: str, source_type: str = "unknown") -> None:
"""Export spectrum data to CSV with metadata header."""
total = self.total_spectrum(normalize=True)
components = self.components
enabled = []
if self.config.use_phase_space:
enabled.append("phase_space")
if self.config.use_fermi:
enabled.append("fermi")
if self.config.use_screening:
enabled.append("screening")
if self.config.use_finite_size:
enabled.append("finite_size")
if self.config.use_charge_dist:
enabled.append("charge_dist")
if self.config.use_radiative:
enabled.append("radiative")
if self.config.use_exchange:
enabled.append("exchange")
parent_symbol = self._element_symbol(self.config.Z_parent)
daughter_symbol = self._element_symbol(self.config.Z_daughter)
header_lines = [
f"# beta-spectrum v{__import__('beta_spectrum').__version__}",
f"# timestamp: {datetime.now(timezone.utc).isoformat()}",
f"# source: {source_type}",
f"# nuclide: {parent_symbol}{self.config.A_number} -> {daughter_symbol}{self.config.A_number} (Z={self.config.Z_parent}->{self.config.Z_daughter})",
f"# endpoint: {self.config.endpoint_MeV * 1000:.1f} keV",
f"# transition: {self.config.transition_type}",
f"# corrections: {', '.join(enabled)}",
f"# e_step: {self.config.e_step_MeV:.4f} MeV",
]
# Branch info — unified view
for i, branch in enumerate(self._unified_branches):
header_lines.append(
f"# branch_{i+1}: E₀={branch.endpoint_MeV*1000:.1f} keV, "
f"transition={branch.transition_type}, "
f"intensity={branch.intensity:.4f}"
)
header_lines.append(f"# git_commit: {get_git_short_hash()}")
data: Dict[str, np.ndarray] = {
"energy_MeV": self.energies_MeV,
"spectrum": total,
}
# Per-branch spectra and components
for i in range(self._n_branches):
data[f"branch_{i+1}_spectrum"] = self._get_branch_spectrum(i)
for comp_name in ["PhaseSpace", "Radiative"]:
key = self._get_component_key(i, comp_name)
if key in components:
data[f"branch_{i+1}_{comp_name}"] = components[key]
# Universal components (no prefix)
for name, values in components.items():
if not name.startswith("branch_"):
data[name] = values
df = pd.DataFrame(data)
df.to_csv(filename, index=False, float_format="%.4e")
with open(filename, "r") as f:
content = f.read()
with open(filename, "w") as f:
f.write("\n".join(header_lines) + "\n" + content)
if self._logger:
self._logger.info("CSV exported to %s", filename)
def get_data(self) -> Dict[str, Any]:
"""Get all numerical data for custom analysis."""
return {
"energies_MeV": self.energies_MeV,
"energies_W": self.W,
"spectrum": self.total_spectrum(normalize=True),
"components": self.components,
"config": self.config,
}
@dataclass
class _UnifiedBranch:
"""Unified branch view for both single- and multi-branch modes."""
endpoint_MeV: float
transition_type: str
intensity: float
+1 -10
View File
@@ -5,14 +5,5 @@
"A_number": 99,
"endpoint_MeV": 0.294,
"transition_type": "A",
"e_step_MeV": 0.001,
"use_detector_response": false,
"detector_model": "gaussian",
"detector_sigma_a_keV": 1.0,
"detector_sigma_b": 0.0,
"detector_tail_fraction": 0.0,
"detector_tau_keV": 5.0,
"detector_fano_factor": 0.12,
"detector_n_channels": 4096,
"detector_channel_energy_range": [0.0, 0.35]
"e_step_MeV": 0.001
}
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More