Files
beta_shape_pnpi/tests/quality/test_api_detector_response.py
T
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

133 lines
4.6 KiB
Python

"""
API / quality tests for DetectorResponse.
Tests factory methods, repr, error handling.
"""
import numpy as np
import pytest
try:
from beta_spectrum.components.detector_response import DetectorResponse
except ImportError:
DetectorResponse = None # type: ignore[misc, assignment]
@pytest.mark.skipif(DetectorResponse is None, reason="DetectorResponse not yet implemented")
class TestFactoryMethods:
"""Test convenience factory methods."""
def test_from_gaussian_params(self):
"""from_gaussian_params should create correct DetectorResponse."""
resp = DetectorResponse.from_gaussian_params(
channel_energy_range=(0.0, 5.0),
n_channels=1024,
sigma_a=0.005,
sigma_b=0.01,
tail_fraction=0.2,
tau=0.01,
model="gaussian_tail",
fano_factor=0.12,
)
assert resp.n_channels == 1024
assert np.isclose(resp.channel_energies[0], 0.0)
assert np.isclose(resp.channel_energies[-1], 5.0)
assert resp._mode == "analytical"
assert resp.tail_fraction == 0.2
def test_from_mc_simulation(self):
"""from_mc_simulation should create tabulated DetectorResponse."""
channel_energies = np.linspace(0.0, 5.0, 512)
calib_energies = np.array([1.0, 2.0, 3.0])
response_matrix = np.zeros((3, 512))
for i, e in enumerate(calib_energies):
idx = np.argmin(np.abs(channel_energies - e))
response_matrix[i, idx] = 1.0
resp = DetectorResponse.from_mc_simulation(
channel_energies=channel_energies,
response_matrix=response_matrix,
calibration_energies=calib_energies,
)
assert resp._mode == "tabulated"
assert resp.n_channels == 512
assert len(resp.calibration_energies) == 3
@pytest.mark.skipif(DetectorResponse is None, reason="DetectorResponse not yet implemented")
class TestEdgeCases:
"""Test edge cases and error handling."""
def test_repr_analytical(self):
"""__repr__ should show analytical mode info."""
channel_energies = np.linspace(0.0, 5.0, 100)
resp = DetectorResponse(channel_energies, model="gaussian", sigma_a=0.01)
repr_str = repr(resp)
assert "analytical" in repr_str
assert "gaussian" in repr_str
def test_repr_tabulated(self):
"""__repr__ should show tabulated mode info."""
channel_energies = np.linspace(0.0, 5.0, 100)
calib_energies = np.array([1.0, 2.0])
response_matrix = np.zeros((2, 100))
resp = DetectorResponse(
channel_energies=channel_energies,
response_matrix=response_matrix,
calibration_energies=calib_energies,
)
repr_str = repr(resp)
assert "tabulated" in repr_str
def test_invalid_model(self):
"""Should reject unknown model names."""
channel_energies = np.linspace(0.0, 5.0, 100)
resp = DetectorResponse(channel_energies, model="unknown_model")
try:
resp.get_response(2.0)
assert False, "Should have raised ValueError"
except ValueError as e:
assert "Unknown model" in str(e)
def test_convolve_mismatched_lengths(self):
"""Should reject mismatched W and spectrum_values lengths."""
channel_energies = np.linspace(0.0, 5.0, 100)
resp = DetectorResponse(channel_energies, model="gaussian")
W = np.linspace(0.0, 5.0, 100)
spectrum = np.zeros(50)
try:
resp.convolve(W, spectrum)
assert False, "Should have raised ValueError"
except ValueError:
pass
def test_non_1d_input(self):
"""Should reject non-1D inputs."""
channel_energies = np.linspace(0.0, 5.0, 100)
resp = DetectorResponse(channel_energies, model="gaussian")
W = np.array([[0.0, 5.0]])
spectrum = np.array([[1.0, 1.0]])
try:
resp.convolve(W, spectrum)
assert False, "Should have raised ValueError"
except ValueError:
pass
def test_tabulated_validation_wrong_columns(self):
"""Should reject response matrix with wrong column count."""
channel_energies = np.linspace(0.0, 5.0, 100)
calib_energies = np.array([1.0, 2.0])
response_matrix = np.zeros((2, 200))
try:
DetectorResponse(
channel_energies=channel_energies,
response_matrix=response_matrix,
calibration_energies=calib_energies,
)
assert False, "Should have raised ValueError"
except ValueError:
pass