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.
Beta Spectrum Calculation Toolkit
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
- Selective inclusion of higher-order corrections via
SpectrumConfigtoggles - Clean, composable component architecture
- Built-in visualization and CSV export via
BetaSpectrumAnalyzer- 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/ - Debug verification workflow:
bs_pnpi -vvvalidates all parameters end-to-end
Core Concept
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. 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.
Project Structure
beta_shape_pnpi/
├── pyproject.toml
├── 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/
│ └── 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
Architecture
SpectrumComponent (base class)
Defined in beta_spectrum/base.py. All physics corrections inherit from this abstract base and implement:
def __call__(self, W: np.ndarray) -> np.ndarray
This makes every component stateless (or minimally stateful), vectorized, and composable.
BetaSpectrum
Defined in beta_spectrum/spectrum.py. Orchestrates the calculation:
- Combines components multiplicatively via
__call__(W) - Factory method
from_config(config)for declarative setup - Generates energy grids with
get_energy_grid(config) - Returns individual component values via
calculate_components(W)
BetaSpectrumAnalyzer
In beta_spectrum.output. Provides debugging and visualization tools:
total_spectrum(normalize=True/False)— computed spectrum array; raisesValueErrorwhen 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
- 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}_spectrumcolumns contain the physical branch spectra weighted by intensity and normalized so that the sum of branch columns equalstotal_spectrum- with
separate_branches=True(CLI--separate-branches) each branch column integrates to 1 (intensity ignored, no total_spectrum column)
- the basic spectrum is always the core
parse_spectrum_csv(path)— read an exported CSV and its metadata header (module function inbeta_spectrum.output.visualization)get_data()— return all numerical data for downstream analysis
Quick Start
from beta_spectrum import SpectrumConfig, BetaSpectrum
from beta_spectrum.output import BetaSpectrumAnalyzer
config = SpectrumConfig(
Z_parent=90, # Thorium-232 (example)
Z_daughter=91,
A_number=232,
endpoint_MeV=4.8, # Q-value
transition_type="A", # Allowed transition (default)
)
spectrum = BetaSpectrum.from_config(config)
W, E_MeV = spectrum.get_energy_grid(config)
values = spectrum(W)
analyzer = BetaSpectrumAnalyzer(spectrum, config)
analyzer.plot_analysis("analysis.png") # Normal mode (single spectrum plot)
analyzer.plot_analysis("debug.png", show_components=True) # Debug mode (4-panel)
analyzer.export_to_csv("spectrum.csv") # Export data to CSV
From the command line
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
For a complete list of implemented and planned features, see TODO.md. Key highlights:
- Physics corrections: phase space, Fermi function, finite size, screening, exchange, radiative (with delta_cut resummation)
- Multi-branch decay: intensity-weighted sum of branch spectra, per-branch calculators with branch-specific endpoint, transition type (from ENSDF), and
--intensity-cutoffCLI filter - CLI:
bs_pnpiwith 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