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.
This commit is contained in:
2026-10-05 17:20:27 +03:00
parent e8b73eb20f
commit 6aced1d8ac
4 changed files with 147 additions and 376 deletions
+86 -288
View File
@@ -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