- 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
85 lines
2.2 KiB
Python
85 lines
2.2 KiB
Python
# conftest.py — shared fixtures for pytest
|
|
|
|
import numpy as np
|
|
import pytest
|
|
|
|
from exp_data.spectrum import ExpSpectrum
|
|
from exp_data.calibration import CalibrationResult
|
|
|
|
|
|
@pytest.fixture()
|
|
def W_low():
|
|
"""Low-energy total energy: T ≈ 10 keV → W = 1 + 0.01/0.511 ≈ 1.02."""
|
|
return np.array([1.02])
|
|
|
|
|
|
@pytest.fixture()
|
|
def W_mid():
|
|
"""Mid-range energies (bulk of spectrum)."""
|
|
return np.linspace(1.1, 2.0, 5)
|
|
|
|
|
|
@pytest.fixture()
|
|
def W_high():
|
|
"""High-energy total energy near endpoint: T ≈ 3 MeV → W ≈ 7."""
|
|
return np.array([6.8])
|
|
|
|
|
|
@pytest.fixture()
|
|
def W_full():
|
|
"""Full spectrum grid from ~1 keV to just below endpoint."""
|
|
# Endpoint at T = 2.5 MeV → W0 ≈ 5.9, so we go up to W=5.8
|
|
return np.linspace(1.003, 5.8, 100)
|
|
|
|
|
|
@pytest.fixture()
|
|
def large_W():
|
|
"""Large array for performance/shape checks."""
|
|
return np.linspace(1.002, 4.9, 1000)
|
|
|
|
|
|
@pytest.fixture()
|
|
def synthetic_spectrum():
|
|
"""Create a synthetic ExpSpectrum (4096 channels)."""
|
|
n_channels = 4096
|
|
energies = np.arange(n_channels, dtype=np.float64)
|
|
counts = np.exp(-energies / 500) * 1000 + np.random.poisson(5, n_channels)
|
|
return ExpSpectrum(
|
|
energies=energies,
|
|
counts=counts,
|
|
source="Tc99",
|
|
run_id="synthetic",
|
|
)
|
|
|
|
|
|
@pytest.fixture()
|
|
def calibration_result():
|
|
"""Create a synthetic CalibrationResult (linear, E = 0.4 + 0.1*ch)."""
|
|
coeffs = np.array([0.1, 0.4]) # polyfit order: [slope, intercept]
|
|
cov = np.eye(2) * 1e-6
|
|
return CalibrationResult(
|
|
coefficients=coeffs,
|
|
order=1,
|
|
chi2_per_dof=0.5,
|
|
n_points=2,
|
|
covariance=cov,
|
|
)
|
|
|
|
|
|
@pytest.fixture()
|
|
def full_spectrum():
|
|
"""Create a full ExpSpectrum with all fields populated."""
|
|
return ExpSpectrum(
|
|
energies=np.arange(4096, dtype=np.float64),
|
|
counts=np.exp(-np.arange(4096) / 500) * 1000 + np.random.poisson(5, 4096),
|
|
source="Tc99",
|
|
run_id="run_042",
|
|
date="2025-01-15",
|
|
dead_time=0.05,
|
|
live_time=3420.0,
|
|
metadata={
|
|
"calibration": {"type": "linear", "coefficients": [0.1, 0.4]},
|
|
"livetime": {"fraction": 0.95, "method": "pulser"},
|
|
},
|
|
)
|