diff --git a/AGENTS.md b/AGENTS.md index 7bbd231..da72e93 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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/`. diff --git a/CONVENTIONS.md b/CONVENTIONS.md index 7e369cd..d4edd69 100644 --- a/CONVENTIONS.md +++ b/CONVENTIONS.md @@ -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 diff --git a/README.md b/README.md index 7f792d2..4aa2294 100644 --- a/README.md +++ b/README.md @@ -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,67 +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 -├── beta_spectrum/output/ ← output, plotting, visualization, CSV export -│ ├── __init__.py # BetaSpectrumAnalyzer, CSVEmitter, Visualizer -│ ├── core.py # BetaSpectrumAnalyzer base class -│ ├── csv_export.py # CSV export functionality -│ └── visualization.py # Plotting and visualization functionality -├── 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 ``` ______________________________________________________________________ @@ -134,7 +117,7 @@ In `beta_spectrum.output`. Provides debugging and visualization tools: - `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 custom analysis +- `get_data()` — return all numerical data for downstream analysis ______________________________________________________________________ @@ -162,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 @@ -190,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**: not implemented — `SpectrumModel` keeps a duck-typed optional convolution hook; the analytical models were removed (restore-or-delete decision tracked in TODO.md) - **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); for JSON input the file's values are used and CLI flags (`--e-step`, `--neutrino-mass`, `--intensity-cutoff`, `--csv-separator`, `--separate-branches`) override them only when explicitly given; every long option has a single-letter alias (`-N`, `-i`, `-s`, `-m`, `-o`, `-p`, `-L`, `-d`, `-c`, `-V`, `-S`, `-b`) -- **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; per-branch endpoints are the measured transition endpoint energies from ENSDF (max endpoint across branches defines the spectrum endpoint) +- **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 diff --git a/TODO.md b/TODO.md index c08a927..ff64b0e 100644 --- a/TODO.md +++ b/TODO.md @@ -1,22 +1,16 @@ # 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 @@ -25,279 +19,101 @@ beta_spectrum/ ← theory-only core ├── 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 -│ └── (detector_response.py — removed; restore-or-delete pending below) -└── 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) - -output/ ← output, visualization, CSV export -├── __init__.py ← BetaSpectrumAnalyzer, CSVEmitter, Visualizer -├── core.py ← BetaSpectrumAnalyzer base class -├── csv_export.py ← CSV export functionality -└── visualization.py ← Plotting and visualization functionality +│ └── radiative.py +├── data/ +│ └── exchange_coeff.csv ← X(Z,W) fit coefficients, Z=2..120 +└── output/ ← visualization, CSV export + ├── 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 -- [ ] Decide: restore or delete the `detector_response` module — it was removed from - `beta_spectrum/components/` together with its tests; `fitter/model.py` keeps a - duck-typed optional hook only (`DEFAULT_JSON_SCHEMA` detector keys and the CLI - dry-run detector line were removed with audit cleanup). - Restore from git history (commits up to `5f99566`) if convolution is needed again +- `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 ______________________________________________________________________ @@ -305,64 +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.4.0 → **v0.4.1 — CLI options refresh & normalization fixes** (released on `dev/core-fermi-csv`) +**Version:** v0.5.0 — theory-only scope reduction. Calculation logic unchanged from v0.4.1. -### Completed (v0.3.x) +### Completed (v0.3.x — theory core) -- 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 +- 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) +### Completed (v0.4.0 — package split, later obsolete) -- **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.output`, `exp_data`, `fitter` - - README updated with new architecture diagram and Quick Start examples - - Version bumped to 0.4.0 +- 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) +### 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: `--normalize-to-one` renamed to `--separate-branches` (no alias), single-letter aliases for every option, dry-run drops the redundant top-level `Transition:` line, stale detector_* JSON schema keys removed +- 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