- Add EULER_GAMMA, ME_KEV, R0_FM to constants.py - Replace hardcoded 0.511, 0.5772..., 1.2 with imports from constants - Fix T_to_W parameter name: T_kev -> T_MeV (it takes MeV, not keV) - Remove duplicate ME_ME from spectrum.py (import ME_MEV from constants) - Remove hardcoded r_0 from detector_response.py and radiative.py - Update CONVENTIONS.md to reference correct constant names
3.6 KiB
Project Conventions
This file defines the physical, mathematical, and architectural conventions that govern code correctness and consistency across the project.
1. Natural Units System
All internal representations, calculations, and definitions use natural units:
m_e = ℏ = c = 1
where m_e is the electron rest mass. This is the fundamental unit for energy, mass, momentum, and time throughout the codebase.
2. Energy Representation
Core Principle
Internal calculations use natural units (m_e). External interfaces (API signatures, plots, human-readable output) use eV-based units (MeV or keV).
API Signatures (Input)
Use human-readable MeV for parameters that come from databases or literature:
endpoint_MeV— Q-value / endpoint energy in MeVdetector_sigma_a_keV— resolution in keVdetector_tau_keV— tail decay constant in keV
Internal Representation
All internal computations use total energy W in m_e units:
W = T_to_W(T_MeV) # T (kinetic) in MeV → W (total) in m_e
W = T + 1.0 # W = T/m_e + 1 (since m_e = 1 in m_e units)
W = 1.0corresponds to zero kinetic energy (rest mass)W > 1.0corresponds to kinetic energy- All internal functions accept and operate on
W - Endpoint W0 is always
T_to_W(endpoint_MeV)
Output and Display
Convert back to eV units for anything intended for human perception:
- Plots: x-axis in keV or MeV (kinetic energy)
- ASCII output, CSV export: kinetic energy in keV or MeV
- Log messages, error messages: use keV/MeV with context
kinetic_keV = (W - 1.0) * ME_KEV # W in m_e → kinetic energy in keV
kinetic_MeV = (W - 1.0) * ME_ME # W in m_e → kinetic energy in MeV
Conversion Helpers
All conversions and constants must go through the central utilities:
from beta_spectrum.utils import T_to_W, W_to_T
from beta_spectrum.constants import ME_MEV, ME_KEV, ALPHA, MP_MEV
- Never hardcode physical constants — import from
constants.py - Never use manual arithmetic for unit conversion — use
T_to_W(),W_to_T() - Never mix MeV and m_e units in the same calculation
3. SpectrumComponent Design
All spectral components (Fermi function, screening, radiative correction, etc.) follow a common interface:
- Stateless or minimally stateful — no mutable state that depends on input
- Vectorized — accept and return
np.ndarray, support array broadcasting - Consistent energy variable — always use
W(total energy in m_e units) internally - Callable interface — implement
__call__(W)for uniform usage - Return correction factor — output is a multiplicative factor, typically ~0.1 to 10
class MyComponent(SpectrumComponent):
def __init__(self, param1: float, param2: float):
self.param1 = param1
self.param2 = param2
def __call__(self, W: np.ndarray) -> np.ndarray:
# All internal computation uses W in m_e units
return ...
4. Physics-Specific Testing Guidelines
When writing tests for physics components, verify the following:
- Physical constraints — values at thresholds, endpoints, and boundaries must match known physics behavior
- Monotonicity / positivity — spectrum components should not produce unphysical negative values; monotonicity should match physical expectations
- Shape correctness — output shape must match input shape (vectorized operation)
- Type safety — components accept and return
np.ndarray - Numerical stability — no NaN or inf in the physical energy range, except where physically expected (e.g., below kinematic threshold, near soft-photon endpoint divergence)