Files
beta_shape_pnpi/tests/integration/test_spectrum_pipeline.py
T

162 lines
5.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Integration tests for the full BetaSpectrum pipeline.
These are integration tests that verify end-to-end workflows.
They are NOT component-level tests — they test that components
work together correctly.
"""
import numpy as np
from beta_spectrum import BetaSpectrum, SpectrumConfig
class TestBetaSpectrumIntegration:
"""Full pipeline integration tests."""
def test_from_config_basic(self):
"""The most basic use case: create a spectrum from config and evaluate it.
This is the entry point shown in README.md — if this fails, nothing works.
We test with a medium-Z nucleus (Z=20 calcium) which exercises all components.
"""
config = SpectrumConfig(
Z_parent=19, Z_daughter=20, A_number=40, endpoint_MeV=2.5
)
spectrum = BetaSpectrum.from_config(config)
W, E_MeV = spectrum.get_energy_grid(config)
assert len(W) == len(E_MeV), "Energy grids must have same length"
assert len(W) > 0, "Grid must not be empty"
def test_spectrum_evaluates_without_error(self):
"""Calling the spectrum object should return a valid numpy array."""
config = SpectrumConfig(
Z_parent=19, Z_daughter=20, A_number=40, endpoint_MeV=2.5
)
spectrum = BetaSpectrum.from_config(config)
W, _ = spectrum.get_energy_grid(config)
values = spectrum(W)
assert isinstance(values, np.ndarray), "Output must be a numpy array"
assert values.shape == W.shape, "Output shape must match input shape"
def test_all_components_positive(self):
"""Every component and the total product must be positive."""
config = SpectrumConfig(
Z_parent=19, Z_daughter=20, A_number=40, endpoint_MeV=2.5
)
spectrum = BetaSpectrum.from_config(config)
W, _ = spectrum.get_energy_grid(config)
# Filter out values exactly at the endpoint (where phase space is zero)
mask = np.isfinite(spectrum(W)) & (spectrum(W) > 0)
assert np.any(mask), "At least some finite positive values expected"
class TestBetaSpectrumToggles:
"""Test that component toggling works correctly."""
def test_all_disabled(self):
"""With all corrections disabled, the core remains — still valid output."""
config = SpectrumConfig(
Z_parent=19,
Z_daughter=20,
A_number=40,
endpoint_MeV=2.5,
use_screening=False,
use_finite_size=False,
use_charge_dist=False,
use_exchange=False,
use_radiative=False,
)
spectrum = BetaSpectrum.from_config(config)
W, _ = spectrum.get_energy_grid(config)
values = spectrum(W)
assert len(values) > 0, "Should return non-empty array"
assert np.all(np.isfinite(values)), "All values must be finite"
def test_core_equals_phase_space_times_fermi(self):
"""Core components are unconditional: with corrections disabled the
spectrum equals phase space × Fermi function exactly."""
config_minimal = SpectrumConfig(
Z_parent=19,
Z_daughter=20,
A_number=40,
endpoint_MeV=2.5,
use_screening=False,
use_finite_size=False,
use_charge_dist=False,
use_exchange=False,
use_radiative=False,
)
spectrum = BetaSpectrum.from_config(config_minimal)
W, _ = spectrum.get_energy_grid(config_minimal)
values = spectrum(W)
components = spectrum.calculate_components(W)
assert set(components.keys()) == {"PhaseSpace", "Fermi"}
expected = components["PhaseSpace"] * components["Fermi"]
assert np.allclose(values, expected, rtol=1e-10)
assert np.all(np.isfinite(values)), "Core-only output must be finite"
class TestBetaSpectrumComponentExtraction:
"""Test calculate_components() returns correct per-component data."""
def test_component_names_match(self):
"""Component names should correspond to class names (minus suffixes)."""
config = SpectrumConfig(
Z_parent=19, Z_daughter=20, A_number=40, endpoint_MeV=2.5
)
spectrum = BetaSpectrum.from_config(config)
W, _ = spectrum.get_energy_grid(config)
components = spectrum.calculate_components(W)
assert isinstance(components, dict), "Must return a dictionary"
assert len(components) > 0, "Should have at least one component"
class TestBetaSpectrumRealisticConfig:
"""Test with realistic physics parameters."""
def test_uranium_spectrum(self):
"""Heavy nucleus (Z=92) — tests numerical stability at extreme Z.
Uranium-238 decays via alpha, but we can still evaluate the beta spectrum
shape for testing purposes. High Z stresses the Fermi function's loggamma.
"""
config = SpectrumConfig(
Z_parent=91, Z_daughter=92, A_number=238, endpoint_MeV=0.5
)
spectrum = BetaSpectrum.from_config(config)
W, E = spectrum.get_energy_grid(config)
values = spectrum(W)
assert np.all(np.isfinite(values)), "Uranium must not produce NaN/inf"
assert len(values) > 10, f"Grid should have many points: got {len(values)}"
def test_low_Z_spectrum(self):
"""Light nucleus (Z=6 carbon-14 beta decay)."""
config = SpectrumConfig(
Z_parent=5,
Z_daughter=6,
A_number=14,
endpoint_MeV=0.156, # ~156 keV for C-14
)
spectrum = BetaSpectrum.from_config(config)
W, E = spectrum.get_energy_grid(config)
values = spectrum(W)
assert np.all(np.isfinite(values)), "C-14 must not produce NaN/inf"
# Phase space should peak in the bulk for low-Q decay
assert np.any(values > 0), "Should have non-zero spectrum values"