Commit Graph
109 Commits
Author SHA1 Message Date
unzhakov 2fedd03d92 fix: bump __version__ to 0.4.0 to match pyproject.toml 2026-05-22 15:18:09 +03:00
unzhakov fd76eff109 docs: add docs_check.py to quality gates, add bs_exp CLI, document pytest markers 2026-05-22 15:18:00 +03:00
unzhakov 5dd9c7977a feat: add docs_check.py for automated documentation consistency checks 2026-05-22 15:17:52 +03:00
unzhakov d8838d19f4 chore: remove obsolete notebooks and add uv.lock 2026-05-22 12:20:36 +03:00
unzhakov ab164fbf98 fix: robust Am-241 calibration with width-constrained peak detection
- Use find_peaks width=(5,50) to reject noise bumps
- Simplify peak matching: find two strongest peaks with largest
  channel separation, assign min/max known energies
- Require absolute threshold of 400 counts to avoid noise peaks
- Calibration now correctly maps: ch 97→13.0 keV, ch 537→59.54 keV
- Tc-99 endpoint at ch 2750 → 293.6 keV (expected ~294)
- Add .rpiv/ to .gitignore
2026-05-21 18:21:51 +03:00
unzhakov 8952d0f789 fix: robust Am-241 peak detection for energy calibration
- Search full spectrum (exclude pulser region) instead of just low-energy
- Include 59.54 keV line in known Am-241 energies
- Brute-force peak-to-energy matching with physical gain constraint
  (3-40 ch/keV) to reject spurious fits
- Fixed find_peaks parameters: height=100, distance=5 for accurate
  peak detection
- Calibration now correctly maps: ch 97→13 keV, ch 134→17.4 keV,
  ch 537→59.6 keV, giving Tc-99 endpoint at ch 2750→291.6 keV
- Add .rpiv/ to .gitignore
2026-05-21 16:43:10 +03:00
unzhakov 5ec6305e09 fix: conditional matplotlib backend for interactive + headless use 2026-05-21 16:05:58 +03:00
unzhakov fa4e61a9f6 chore: add .rpiv/ to .gitignore 2026-05-21 16:03:20 +03:00
unzhakov 5c2b12c0e6 feat: experimental data calibration pipeline with ROOT I/O
- Fix chi2 calculation bug in calibration.py (remove incorrect
  coeffs[::-1] reversal in np.polyval)
- Fix broken beta_spectrum/cli.py (remove orphaned detector response
  add_argument calls and unused imports)
- Add ExpSpectrum.apply_calibration() and EnergyCalibrator.from_am241_spectrum()
- Add LivetimeDetermination class for pulser-based livetime
- Add SpectrumPipeline orchestration (read→subtract→calibrate→livetime→validate)
- Add ROOT I/O with PyROOT primary + uproot fallback
- Add bs_exp CLI with process, livetime, export subcommands
- Add exp_calibration notebook with pre-calibration visualization
- Add comprehensive tests (73 passing)
- Add bs_exp console script and [root] extra to pyproject.toml
- Remove obsolete detector_response module and unused notebooks
2026-05-21 16:01:25 +03:00
unzhakov caa4a77ebd fix: correct energy calibration from Am-241 peaks
- Am-241 peaks at ch 134=13.8, ch 163=17.8, ch 218=26.3 keV
- Calibration: E = 0.397 + 0.0995 × ch (previously wrong)
- Kurie fit endpoint 425 keV vs Q=297.5 keV — spectrum extends
  beyond MCA range (407 keV), simple extrapolation unreliable
- Proper endpoint extraction requires theory model + detector
  convolution (fitter module), not simple Kurie plot
2026-05-18 00:01:28 +03:00
unzhakov 42dcad56a2 fix: add data/raw/ to .gitignore 2026-05-17 22:57:19 +03:00
unzhakov 5f70e3a15a feat: add raw binary spectrum reader and data quality analysis
- exp_data/raw_reader.py: read binary MCA spectra (128-byte header,
  int32 counts), subtract cumulative spectra, Am-241 calibration
- exp_data/spectrum.py: allow negative counts for difference spectra
- scripts/analyze_raw.py: analyze 236 individual runs from cumulative
  data, compute rates and endpoint drift
- Am-241 calibration: E = -0.96 + 0.451 * ch (CAM1),
  E = 0.75 + 0.442 * ch (CAM2)
- Results: stable rate ~2M counts/run, stable endpoint ~374 keV
  (smeared Q-value), spike at runs 214-219 (acquisition glitch)
2026-05-17 22:55:52 +03:00
unzhakov 3c0a43ad4d docs: update TODO and AGENTS for v0.4.0 completion
- Mark v0.4.0 as complete in TODO.md with all phases checked
- Update Phase 4 items with actual outcomes
- Next step: Track A 99Tc spectrum-shape analysis
- Add AGENTS instruction to update project metadata on significant
  structural changes (package additions, version bumps, etc.)
2026-05-17 19:31:39 +03:00
Evgeniy Unzhakov 27a6993419 Merge pull request #3 from unzhakov/dev/phase2-fitter
AreDev/phase2 fitter
2026-05-17 19:21:14 +03:00
unzhakov 38b44b56e6 feat: v0.4.0 package restructuring — split into 4 modules
Separate monolithic beta_spectrum package into:
  beta_spectrum   — pure theory (components, spectrum, CLI, visualize)
  exp_data        — experimental data handling (spectrum, calibration,
                    fitters, corrections)
  fitter          — analysis framework (model, fit_engine, extractor,
                    result)
  beta_spectrum.visualize — plotting (BetaSpectrumAnalyzer)

Phase 1 — exp_data module:
  ExpSpectrum dataclass with metadata, normalization, serialization
  EnergyCalibrator (linear/quadratic calibration)
  GaussianFitter + PeakFitter for calibration sources
  DeadTimeCorrection, PileUpCorrection, BackgroundSubtractor
  43 new tests, ruff + mypy clean

Phase 2 — fitter module:
  SpectrumModel wraps BetaSpectrum + DetectorResponse with convolution
  SpectrumFitter with χ² minimization, covariance, profile likelihood,
  confidence intervals, robust loss functions
  CWExtractor + GVAExtractor for C(W) and g_V/g_A extraction
  AnalysisResult + FitSummary for physics-specific results
  CurveFitter general-purpose callable model fitter
  34 new tests, ruff + mypy clean

Phase 3 — clean up beta_spectrum:
  Remove cw_extractor.py and fitter.py (moved to fitter/)
  Clean __init__.py to export only theory classes
  Add backward-compat methods to CWExtractor
  Fix BetaSpectrumAnalyzer._get_branch_spectrum returning BetaSpectrum
  objects instead of numpy arrays (plot fix)

Phase 4 — package structure:
  Auto-discovered by setuptools (4 packages)
  All imports updated across 26 source files
  Black formatting applied
  322 tests pass (262 original + 60 new)
  ruff + mypy clean on all source files

Bug fixes:
  - Fix multi-branch spectrum plotting: universal component keys
    (Fermi, Screening, etc.) don't have per-branch prefix; fixed
    fallback lookup.
  - Fix negative phase space beyond endpoint: BetaSpectrum returns
    negative values for W > W0; clamp branch spectra to zero.
2026-05-17 18:58:04 +03:00
unzhakov 745bbae4cd refactor: rename 'analyzer' to 'visualize' — analysis belongs to fitter module 2026-05-17 01:33:13 +03:00
unzhakov 10ad3a867b docs: restructure TODO.md with v0.4.0 package architecture plan 2026-05-17 00:34:16 +03:00
Evgeniy Unzhakov 0d51c68bcb Merge pull request #2 from unzhakov/dev/move-analyzer-to-separate-file
Dev/move analyzer to separate file
2026-05-16 23:59:41 +03:00
unzhakov 64785f4aa9 style: remove transition type from plot header — shown in branch legends instead 2026-05-16 23:24:33 +03:00
unzhakov 55cfb30b2b refactor: unify plotting — single-branch treated as multi-branch with one branch; remove old single-branch methods 2026-05-16 22:59:48 +03:00
unzhakov b3d6abb4d5 polish: unify plot output — external headers, compact legends, consistent titles 2026-05-16 21:21:28 +03:00
unzhakov f05a2de1b8 refactor: move BetaSpectrumAnalyzer to separate analyzer module 2026-05-16 20:41:58 +03:00
unzhakov 8314cee29c refactor: restructure metadata files for context efficiency
- Shrink global AGENTS.md: 153 → 56 lines (−63%)
  Removed: git workflow, TDD, overthinking guards, code style, reasoning protocol
  Kept: personality, git basics, thinking levels, general principles, backend
- Create project AGENTS.md: 71 lines
  Added: stack/commands, GitHub Flow git workflow, Conventional Commits,
  TDD (Python-specific), quality gates, debug verification
- Trim CONVENTIONS.md: 161 → 68 lines (−58%)
  Removed: verbose examples, redundant unit convention section,
  lengthy SpectrumComponent code example
- Delete DEVELOPMENT.md: 238 lines → 0 (migrated to AGENTS.md)
  Setup/release checklist removed (not agent concerns)
  Git workflow, TDD, quality gates, notebook QC, debug verification
  all migrated to AGENTS.md
- Update README.md project structure
Total per-session metadata: 552 → 195 lines (−65%)
2026-05-16 19:51:37 +03:00
unzhakov 1a23283cf1 fix: decay_info_to_config() now passes branches to SpectrumConfig; update docs for paceENSDF completion
- Fixed bug: decay_info_to_config() was dropping branches, causing
  create_config_from_source('paceENSDF') to return single-branch config
- Added BranchConfig import to nuclear_data.py
- Marked B1 (paceENSDF integration) as complete in TODO.md
- Updated README.md with paceENSDF details in Implementation Status
- Updated TODO.md Current Status with v0.3.1 notes
2026-05-16 18:50:06 +03:00
unzhakov 5276dcc851 docs: mark all migration phases complete in TODO 2026-05-15 16:54:45 +03:00
unzhakov 76d183b913 test: Phase 5 — advanced improvements
1. pytest-timeout (30s, thread-based) added to pyproject.toml
   - Prevents hangs in CI
   - timeout_method='thread' for clean cancellation

2. hypothesis added for property-based edge case discovery
   - tests/common/test_hypothesis_tests.py: 5 tests
   - Fermi: random Z, A, W — checks stability, monotonicity
   - Phase space: random W0, W — checks threshold/endpoint properties
   - Hypothesis already found real bugs:
     * Fermi monotonicity fails for Z < 10 (expected — finite-size dominates)
     * Phase space negative when W > W0 (test constrains W < W0)

3. pytest.mark.requires_ensdf marker added to TestPaceENSDFIntegration
   - Replaces bare @pytest.mark.skipif with documented marker
   - Allows running only ENSDF tests: pytest -m requires_ensdf

4. Updated test-migration-todo.md: Phase 5 complete

Final state: 256 tests passing (250 regular + 6 hypothesis + 6 skipped ENSDF)
2026-05-15 16:48:46 +03:00
unzhakov c96fb6175a test: add .gitkeep files and update TODO with phase 1-4 complete
- Add .gitkeep to tests/common/ and tests/integration/ directories
- Mark phases 1-4 as complete in test-migration-todo.md
- All migration work done: 251 tests passing
- Final structure:
  - tests/common/ — 3 files (shared helpers)
  - tests/physics/ — 10 files (physics behavior)
  - tests/quality/ — 8 files (API contracts)
  - tests/integration/ — 2 files (end-to-end)
2026-05-15 15:56:28 +03:00
unzhakov c78d0655a0 test: complete migration to structured test suite
Phase 1: Removed 6 duplicate old test files (fermi, finite_size, screening,
exchange, radiative, phase_space) — their tests are now in physics/ and quality/
using shared helpers from common/.

Phase 2: Migrated remaining 7 old test files:
- test_spectrum.py → integration/test_spectrum_pipeline.py (integration tests)
- test_fitter.py → physics/test_fitter.py (fit quality tests)
- test_cw_extractor.py → physics/test_cw_extractor.py (extraction accuracy)
- test_multi_branch.py → physics/test_multi_branch.py (branching physics)
- test_detector_response.py → physics/ + quality/ (split into physics + API)
- test_nuclear_data.py → quality/test_nuclear_data.py (parsing/API)
- test_logging_utils.py → quality/test_logging_utils.py (API tests)

Removed 12 old flat files, created 4 new files.
Total: 251 tests passing (6 skipped), down from 230 → 251 (net +21
from better coverage, not duplicates).

Test structure:
- tests/common/ — shared property/API helpers (eliminates ~18 duplicates)
- tests/physics/ — 10 files, unique physics behavior tests
- tests/quality/ — 8 files, API contract + parsing tests
- tests/integration/ — 1 file, end-to-end pipeline tests
2026-05-15 15:52:50 +03:00
unzhakov fea151d8d7 docs: add test migration TODO with 5-phase plan
- Phase 1: Remove duplicate old tests (6 files)
- Phase 2: Migrate remaining 7 old test files to new structure
- Phase 3: Create integration tests
- Phase 4: Clean up and verify
- Phase 5: Advanced improvements (slow markers, hypothesis, CI)
- Total: from 230 flat tests → ~240 organized tests in 3 directories
2026-05-15 15:09:13 +03:00
unzhakov 4b08df3873 test: create new test architecture with shared property/API tests
- Add tests/common/ with parametrized property tests (positivity, shape, NaN, stability)
- Add tests/physics/ with unique physics behavior tests for 6 correction components
- Add tests/quality/ with API contract tests for 6 correction components
- Add pytest markers in pyproject.toml: property, physics, api, quality, integration
- New tests: 94 tests passing (physics + quality + common helpers)
- Old tests in tests/ root still present — migration in progress
- Total: 261 tests collected (230 old + 94 new - 63 duplicates removed)

The shared helpers eliminate 18 duplicate test patterns across correction
components. Each component now has:
  - tests/physics/test_<name>.py — unique physics behavior
  - tests/quality/test_api_<name>.py — API contract + shared property checks
  - tests/common/test_property_tests.py — shared helpers (imported, not duplicated)
2026-05-15 15:04:52 +03:00
unzhakov bd14e3bb7a docs: add test architecture documentation
- Document current test suite state (230 tests, 79 CQT + 151 PT)
- Propose new directory structure: tests/{common,physics,quality,integration}
- Identify 18 duplicate test patterns across correction components
- Define pytest markers for test categorization
- Create implementation plan with 5 phases
2026-05-15 14:29:53 +03:00
unzhakov 4fb52fce2f docs/refs: flatten directory structure — move summary.md to parent folder
- Rename all summary.md files to YEAR_Author_Title.md in their parent folder
- Remove 2017_Hayen_analytical_beta_shape/references.bib
- Remove 2024_Paulsen_T99_beta_spectrum/img/ directory
- Eliminate one level of directory nesting for cleaner refs layout
2026-05-15 13:55:16 +03:00
unzhakov 030ae3e4b9 docs: restructure knowledge base into tree layout + gA extraction module
- Organize into tree structure: corrections/, nuclear-structure/, gA-extraction/, refs/
- Move all correction notes to corrections/ (01-09)
- Move nuclear structure to nuclear-structure/ with index
- Add gA-extraction/ with SSM algorithm, sNME/ESSM formalism, Tc99 case study, and implementation TODO
- Reorganize refs by topic (electromagnetic/, forbidden-decay/)
- Update all wikilinks to new paths
- Remove radiative_spec.md (code algorithm, not reference)
- Lint: all wikilinks verified valid
- Comprehensive gA extraction research based on Paulsen et al. (2024) and Song et al. (2025)
2026-05-14 19:46:39 +03:00
Evgeniy Unzhakov efcd6a524a Merge pull request #1 from unzhakov/pi-dev/gA-shape-factor-parameterization
docs: add gA-driven shape factor parameterization (SSM) note
2026-05-13 15:03:25 +03:00
unzhakov 3acb23987a docs: add gA-driven shape factor parameterization (SSM) note
Comprehensive documentation on parameterizing the shape factor C(Z,W)
via g_A for forbidden nonunique beta decays, based on:
- Kostensalo et al. PRC 95, 044313 (2017) - SSM introduction
- Kostensalo & Suhonen PRC 96, 024317 (2017) - gA-driven evolution
- Paulsen et al. PRC 110, 055503 (2024) - 99Tc high-precision MMC measurement

Covers: shape factor decomposition, fitting algorithm, uncertainty budget,
implementation plan for ShapeFactorGA module, and literature summary.
2026-05-13 14:46:58 +03:00
unzhakov 984c4f6884 Add regression test for Tc99 ENSDF intensity values 2026-05-08 16:11:06 +03:00
unzhakov 620488860b Fix Tc99 branch intensity from paceENSDF
paceENSDF returns intensity at state[9] (percent), not state[8] (ENSDF
normalization field, ~1.0 for all branches). This was causing ~50/50
split for Tc99 instead of the correct 99.9984%/0.0016%.
2026-05-08 16:08:41 +03:00
unzhakov c404d83418 Remove 'universal_' prefix from CSV column names in multi-branch mode
Universal components (Fermi, Screening, etc.) are exported under their
plain names — no redundant prefix needed.
2026-05-08 15:54:13 +03:00
unzhakov a5144f2c33 Merge branch 'pi-dev/csv-branch-info-in-header' 2026-05-08 15:41:16 +03:00
unzhakov b02159ba5c Remove repeated per-branch metadata columns from CSV export
Branch intensity, endpoint, and transition are constant per branch and
already documented in the CSV header. No need to repeat them as columns
for every energy row.
2026-05-08 15:41:10 +03:00
unzhakov 9eb58bf101 feat: multi-branch decay support (B3) — per-branch transition types, intensity cutoff, raw-value plotting
- Extract per-branch forbiddenness codes from ENSDF (not defaulting to 'A')
- BranchConfig dataclass with endpoint, transition type, intensity validation
- Multi-branch BetaSpectrum with per-branch calculators and universal components
- Raw (un-normalized) plotting so total = arithmetic sum of branches
- --intensity-cutoff CLI option to filter negligible branches
- Branch-aware CSV export with per-branch spectra and components
- 30 comprehensive tests covering validation, calculation, export, edge cases
2026-05-08 08:36:29 +03:00
unzhakov ef337073d6 docs: update TODO and README for B3 completion and BranchConfig export
- Cross out B3 in TODO.md with completion details
- Export BranchConfig in __init__.py public API
- Update README to mention per-branch transition types and intensity cutoff
2026-05-08 08:35:01 +03:00
unzhakov 69eca97736 test: comprehensive multi-branch decay test suite (30 tests)
- BranchConfig validation (intensity bounds, positive endpoint)
- Single-branch via multi-branch path compatibility
- Multi-branch spectrum calculation and intensity weighting
- Per-branch spectra with correct endpoint and transition type
- Component extraction (universal vs branch-dependent)
- CSV export with total, per-branch, and universal components
- Transition type propagation from BranchInfo
- Branch values <= total spectrum verification
- Branch integral proportionality to intensity
- High-Z multi-branch stability
- No-branches defaults to single-branch mode
2026-05-08 08:34:30 +03:00
unzhakov 6293710089 feat: multi-branch CLI with intensity cutoff and per-branch info
- Add --intensity-cutoff CLI option to filter negligible branches
- Fetch DecayInfo from paceENSDF and construct SpectrumConfig with
  branches including per-branch transition types
- Normalize branch intensities to sum to 1.0, filter by cutoff
- Display branch info in dry-run output and logging
- Add intensity_cutoff to SpectrumConfig
2026-05-08 08:33:39 +03:00
unzhakov 3216c50943 feat: per-branch transition types from ENSDF and raw-value plotting
- Add transition_type field to BranchInfo and extract per-branch
  forbiddenness codes from paceENSDF (not defaulting all to 'A')
- Fix _multi_branch_call to use np.maximum instead of np.where mask
  so branches smoothly approach zero at endpoint
- Rewrite _plot_multi_branch_spectra and _plot_multi_branch_debug Panel 1
  to plot raw (un-normalized) branch contributions so total = sum of branches
- Remove independent unit-area normalization that made small branches
  appear larger than total
2026-05-08 08:33:12 +03:00
unzhakov 572e222c78 Merge dev/cli-logging-refinement to main
- Fix paceENSDF Q-value extraction (was reading half-life as Q-value)
- Fix RadiativeCorrection Z/A parameters
- Add debug verification workflow (bs_pnpi -vv)
- Add plot output with ID tracking and two display modes
- Fix pyright/mypy type errors across codebase
- Add ./output/ directory for test artifacts
- Update docs and development workflow
2026-05-05 14:35:36 +03:00
unzhakov 4a3d31de83 Update docs and TODOs for CLI & plot improvements
- README.md: Add features for plot modes, traceability, debug workflow
- README.md: Update BetaSpectrumAnalyzer docs with show_components param
- README.md: Update Quick Start with both plot modes
- README.md: Add output/ to project structure
- TODO.md: Mark B8.4 plot output as completed
- TODO.md: Update Current Status with B8.4
2026-05-05 14:35:11 +03:00
unzhakov 5ac004352b Fix mypy type errors and black formatting
- spectrum.py: Use Axes from TYPE_CHECKING instead of plt.Axes
- fitter.py: Remove unused type: ignore comment
- phase_space.py: Black formatting
2026-05-05 14:01:28 +03:00
unzhakov d6328d4b24 Enhance plot output with ID tracking and two display modes
- Add commit hash (6 chars) and UTC timestamp to all plots via text boxes
- Add nuclear data header (nuclide, endpoint, transition, corrections) to plots
- Two display modes:
  - DEBUG (-vv): 4-panel debug view with all correction factors
  - Normal (default): single spectrum plot with nuclear data header
- CLI: --plot now passes show_components=(args.verbose >= 2)
2026-05-05 13:33:46 +03:00
unzhakov 3749220efc Fix pyright type errors and correct paceENSDF Q-value extraction
- nuclear_data.py: Extract Q-value from ground state branch state[7] instead of
  halflife_data (was reading half-life seconds as Q-value)
- nuclear_data.py: Use ground state branch for forbiddenness code, with fallback
- nuclear_data.py: Add None guards for beta_data and pairs
- spectrum.py: Pass Z=config.Z_daughter, A=config.A_number to RadiativeCorrection
- phase_space.py: Add __init__ debug logging (W0, transition_type, m_e, m_nu)
- phase_space.py: Add output range logging in __call__
- phase_space.py: Fix possibly unbound forbid_factor variable
- logging_utils.py: Fix broken get_git_short_hash indentation
- cli.py: Add explicit SpectrumConfig annotation to fix pyright type identity
- cw_extractor.py: Fix T_to_W ndarray→float conversions (6 locations)
- cw_extractor.py: Fix FermiFunction() calls — was passing Z as __call__ arg
- fitter.py: Add explicit dict type annotation for least_squares kwargs
2026-05-05 12:43:43 +03:00