feat: make phase space and Fermi function unconditional core components

This commit is contained in:
2026-09-30 03:04:18 +03:00
parent 8bc9b0fd87
commit bab19de458
+41 -33
View File
@@ -73,9 +73,9 @@ class SpectrumConfig:
e_step_MeV: float = 0.001 e_step_MeV: float = 0.001
neutrino_mass_MeV: float = 0.0 neutrino_mass_MeV: float = 0.0
# Toggle components and corrections # Higher-order corrections. The core spectrum components (phase space
use_phase_space: bool = True # and the Fermi function, both 0-order) are unconditional: there are
use_fermi: bool = True # deliberately no toggles for them.
use_screening: bool = True use_screening: bool = True
use_finite_size: bool = True use_finite_size: bool = True
use_charge_dist: bool = True use_charge_dist: bool = True
@@ -252,11 +252,16 @@ class BetaSpectrum:
def _from_config_single( def _from_config_single(
cls, config: SpectrumConfig, logger: Optional[logging.Logger] = None cls, config: SpectrumConfig, logger: Optional[logging.Logger] = None
) -> BetaSpectrum: ) -> BetaSpectrum:
"""Create single-branch spectrum calculator.""" """Create single-branch spectrum calculator.
The core spectrum components (phase space x Fermi function, both
0-order) are always built; only higher-order corrections are
conditional.
"""
components: List[SpectrumComponent] = [] components: List[SpectrumComponent] = []
W0 = float(T_to_W(config.endpoint_MeV)) W0 = float(T_to_W(config.endpoint_MeV))
if config.use_phase_space: # Core spectrum components (0-order): always included.
# Convert neutrino mass from MeV to natural units # Convert neutrino mass from MeV to natural units
m_nu_natural = config.neutrino_mass_MeV / ME_MEV m_nu_natural = config.neutrino_mass_MeV / ME_MEV
components.append( components.append(
@@ -267,8 +272,6 @@ class BetaSpectrum:
logger=logger, logger=logger,
) )
) )
if config.use_fermi:
components.append( components.append(
FermiFunction(Z=config.Z_daughter, A=config.A_number, logger=logger) FermiFunction(Z=config.Z_daughter, A=config.A_number, logger=logger)
) )
@@ -322,13 +325,13 @@ class BetaSpectrum:
Note: Radiative correction is branch-dependent (depends on W0), so it goes Note: Radiative correction is branch-dependent (depends on W0), so it goes
into each branch's calculator, not the universal components. into each branch's calculator, not the universal components.
""" """
if config.branches is None:
raise ValueError("branches must be provided for multi-branch mode")
if logger: if logger:
logger.info("Multi-branch mode: %d branches", len(config.branches)) logger.info("Multi-branch mode: %d branches", len(config.branches))
# Create per-branch calculators # Create per-branch calculators
branch_spectra: List[BetaSpectrum] = [] branch_spectra: List[BetaSpectrum] = []
if config.branches is None:
raise ValueError("branches must be provided for multi-branch mode")
for i, branch in enumerate(config.branches): for i, branch in enumerate(config.branches):
if logger: if logger:
logger.info( logger.info(
@@ -339,37 +342,42 @@ class BetaSpectrum:
branch.intensity, branch.intensity,
) )
# Create a per-branch config with the branch's endpoint and transition type # Per-branch calculator: the per-branch core phase space
branch_config = SpectrumConfig( # (unconditional) plus optional per-branch radiative
Z_parent=config.Z_parent, # corrections. The universal Fermi function and universal
Z_daughter=config.Z_daughter, # higher-order corrections are applied outside, so they must
A_number=config.A_number, # NOT be built here (a Fermi function per branch would
endpoint_MeV=branch.endpoint_MeV, # double-apply it).
W0_branch = float(T_to_W(branch.endpoint_MeV))
m_nu_natural = config.neutrino_mass_MeV / ME_MEV
branch_components: List[SpectrumComponent] = [
PhaseSpace(
W0=W0_branch,
transition_type=branch.transition_type, transition_type=branch.transition_type,
e_step_MeV=config.e_step_MeV, m_nu=m_nu_natural,
neutrino_mass_MeV=config.neutrino_mass_MeV, logger=logger,
# Branch-dependent components )
use_phase_space=config.use_phase_space, ]
use_radiative=config.use_radiative, if config.use_radiative:
# Universal components disabled (handled separately) branch_components.append(
use_fermi=False, RadiativeCorrection(
use_screening=False, W0=W0_branch,
use_finite_size=False, Z=config.Z_daughter,
use_charge_dist=False, A=config.A_number,
use_exchange=False, use_endpoint_resummation=True,
logger=logger,
)
) )
branch_spectrum = cls._from_config_single(branch_config, logger) branch_spectra.append(cls(branch_components, logger=logger))
branch_spectra.append(branch_spectrum)
# Create universal components (shared across all branches) # Create universal components (shared across all branches)
# Note: Radiative is NOT included here because it depends on W0 # Note: Radiative is NOT included here because it depends on W0
universal_components: List[SpectrumComponent] = [] # The Fermi function is a core spectrum component (0-order Coulomb
# correction): always included, not a higher-order correction.
if config.use_fermi: universal_components: List[SpectrumComponent] = [
universal_components.append(
FermiFunction(Z=config.Z_daughter, A=config.A_number, logger=logger) FermiFunction(Z=config.Z_daughter, A=config.A_number, logger=logger)
) ]
if config.use_finite_size: if config.use_finite_size:
universal_components.append( universal_components.append(