Source code for pleiades.experimental.models

from enum import Enum
from typing import List, Optional

from pydantic import BaseModel, Field, model_validator

# Import VaryFlag from helper module
from pleiades.utils.helper import VaryFlag

# NOTE: Some physics parameters are not supported in the current version of the code


[docs] class EnergyParameters(BaseModel): """Parameters for energy bounds and sampling Args: BaseModel (_type_): _description_ Raises: ValueError: _description_ ValueError: _description_ Returns: _type_: _description_ """ min_energy: float = Field(default=0.0, description="Minimum energy for this data set(eV)") max_energy: float = Field(default=0.0, description="Maximum energy (eV)") number_of_energy_points: int = Field( description="Number of points to be used in generating artificial energy grid", default=10001 ) number_of_extra_points: int = Field( description="Number of extra points to be added between each pair of data points for auxiliary energy grid", default=0, ) number_of_small_res_points: int = Field( description="Number of points to be added to auxiliary energy grid across small resonances", default=0 )
[docs] class NormalizationParameters(BaseModel): """Parameters for normalization and background for one angle. Contains: - Normalization and background values - Their uncertainties (optional) - Flags indicating whether each parameter should be varied Note on fixed-width format: Each numeric field in the file uses a 10-column width with a 9+1 pattern: - 9 characters for the actual numeric data (e.g. "1.2340E+00") - 1 character for space separator """ # Main parameters anorm: float = Field(default=None, description="Normalization (dimensionless)") backa: float = Field(default=None, description="Constant background") backb: float = Field(default=None, description="Background proportional to 1/E") backc: float = Field(default=None, description="Background proportional to √E") backd: float = Field(default=None, description="Exponential background coefficient") backf: float = Field(default=None, description="Exponential decay constant") # Optional uncertainties d_anorm: Optional[float] = Field(None, description="Uncertainty on ANORM") d_backa: Optional[float] = Field(None, description="Uncertainty on BACKA") d_backb: Optional[float] = Field(None, description="Uncertainty on BACKB") d_backc: Optional[float] = Field(None, description="Uncertainty on BACKC") d_backd: Optional[float] = Field(None, description="Uncertainty on BACKD") d_backf: Optional[float] = Field(None, description="Uncertainty on BACKF") # Vary flags flag_anorm: VaryFlag = Field(default=VaryFlag.NO) flag_backa: VaryFlag = Field(default=VaryFlag.NO) flag_backb: VaryFlag = Field(default=VaryFlag.NO) flag_backc: VaryFlag = Field(default=VaryFlag.NO) flag_backd: VaryFlag = Field(default=VaryFlag.NO) flag_backf: VaryFlag = Field(default=VaryFlag.NO) # Define a print method for the class
[docs] def __str__(self) -> str: """Return a text table representation of the NormalizationParameters object.""" headers = ["Parameter", "Value", "Uncertainty", "Vary"] rows = [ ["Normalization", self.anorm, self.d_anorm, self.flag_anorm], ["Const. Background", self.backa, self.d_backa, self.flag_backa], ["1/E Background", self.backb, self.d_backb, self.flag_backb], ["sqrt(E) Background", self.backc, self.d_backc, self.flag_backc], ["Exp. Background", self.backd, self.d_backd, self.flag_backd], ["Exp. Decay Const.", self.backf, self.d_backf, self.flag_backf], ] # Calculate column widths col_widths = [ max(len(str(cell)) for cell in [header] + [row[i] for row in rows]) for i, header in enumerate(headers) ] # Build table lines = [] header_line = " | ".join(header.ljust(col_widths[i]) for i, header in enumerate(headers)) sep_line = " | ".join("-" * col_widths[i] for i in range(len(headers))) lines.append(header_line) lines.append(sep_line) for row in rows: lines.append(" | ".join(str(cell).ljust(col_widths[i]) for i, cell in enumerate(row))) return "\n".join(lines)
[docs] class BroadeningParameters(BaseModel): """Container for a single set of broadening parameters. Contains all parameters from a single card set 4 entry including: - Main parameters (CRFN, TEMP, etc.) - Their uncertainties - Additional Gaussian parameters - Flags indicating whether each parameter should be varied Note on fixed-width format: Each numeric field in the file uses a 10-column width with a 9+1 pattern: - 9 characters for the actual numeric data (e.g. "1.2340E+00") - 1 character for space separator This format ensures human readability while maintaining proper fixed-width alignment. """ # Main parameters temp: float = Field(default=None, description="Effective temperature (K)") dist: float = Field(default=None, description="Flight-path length (m)") crfn: float = Field(default=None, description="Matching radius (F)") thick: float = Field(default=None, description="Sample thickness (atoms/barn)") deltal: float = Field(default=None, description="Spread in flight-path length (m)") deltag: float = Field(default=None, description="Gaussian resolution width (μs)") deltae: float = Field(default=None, description="e-folding width of exponential resolution (μs)") # Optional uncertainties for main parameters d_temp: Optional[float] = Field(None, description="Uncertainty on TEMP") d_dist: Optional[float] = Field(None, description="Uncertainty on DIST") d_crfn: Optional[float] = Field(None, description="Uncertainty on CRFN") d_thick: Optional[float] = Field(None, description="Uncertainty on THICK") d_deltal: Optional[float] = Field(None, description="Uncertainty on DELTAL") d_deltag: Optional[float] = Field(None, description="Uncertainty on DELTAG") d_deltae: Optional[float] = Field(None, description="Uncertainty on DELTAE") # Optional additional Gaussian parameters deltc1: Optional[float] = Field(None, description="Width of Gaussian, constant in energy (eV)") deltc2: Optional[float] = Field(None, description="Width of Gaussian, linear in energy (unitless)") d_deltc1: Optional[float] = Field(None, description="Uncertainty on DELTC1") d_deltc2: Optional[float] = Field(None, description="Uncertainty on DELTC2") # Vary flags for all parameters flag_crfn: VaryFlag = Field(default=VaryFlag.NO) flag_temp: VaryFlag = Field(default=VaryFlag.NO) flag_thick: VaryFlag = Field(default=VaryFlag.NO) flag_deltal: VaryFlag = Field(default=VaryFlag.NO) flag_deltag: VaryFlag = Field(default=VaryFlag.NO) flag_deltae: VaryFlag = Field(default=VaryFlag.NO) flag_deltc1: Optional[VaryFlag] = Field(None) flag_deltc2: Optional[VaryFlag] = Field(None)
[docs] @model_validator(mode="after") def validate_gaussian_parameters(self) -> "BroadeningParameters": """Validate that if any Gaussian parameter is present, both are present.""" has_deltc1 = self.deltc1 is not None has_deltc2 = self.deltc2 is not None if has_deltc1 != has_deltc2: raise ValueError("Both DELTC1 and DELTC2 must be present if either is present") if has_deltc1: if self.flag_deltc1 is None or self.flag_deltc2 is None: raise ValueError("Flags must be specified for DELTC1 and DELTC2 if present") return self
# define a print method for the class that prints a table
[docs] def __str__(self) -> str: """Return a text table representation of the BroadeningParameters object.""" headers = ["Parameter", "Value", "Uncertainty", "Vary"] rows = [ ["CRFN", self.crfn, self.d_crfn, self.flag_crfn], ["TEMP", self.temp, self.d_temp, self.flag_temp], ["THICK", self.thick, self.d_thick, self.flag_thick], ["DELTAL", self.deltal, self.d_deltal, self.flag_deltal], ["DELTAG", self.deltag, self.d_deltag, self.flag_deltag], ["DELTAE", self.deltae, self.d_deltae, self.flag_deltae], ["DELT1", self.deltc1, self.d_deltc1, self.flag_deltc1], ["DELT2", self.deltc2, self.d_deltc2, self.flag_deltc2], ] # Calculate column widths col_widths = [ max(len(str(cell)) for cell in [header] + [row[i] for row in rows]) for i, header in enumerate(headers) ] # Build table lines = [] header_line = " | ".join(header.ljust(col_widths[i]) for i, header in enumerate(headers)) sep_line = " | ".join("-" * col_widths[i] for i in range(len(headers))) lines.append(header_line) lines.append(sep_line) for row in rows: lines.append(" | ".join(str(cell).ljust(col_widths[i]) for i, cell in enumerate(row))) return "\n".join(lines)
[docs] class UserResolutionParameters(BaseModel): """Container for User-Defined Resolution Function parameters. Attributes: type: Parameter type identifier (always "USER") burst_width: Square burst width value (ns), optional burst_uncertainty: Uncertainty on burst width, optional burst_flag: Flag for varying burst width channel_energies: List of energies for channel widths channel_widths: List of channel width values channel_uncertainties: List of uncertainties on channel widths channel_flags: List of flags for varying channel widths filenames: List of data file names """
[docs] class UserDefinedResolutionType(str, Enum): """Enumeration of user defined resolution types.""" USER = "USER" # Header identifier BURST = "BURST" CHANN = "CHANN" FILE = "FILE="
type: UserDefinedResolutionType = UserDefinedResolutionType.USER burst_width: Optional[float] = Field(default=None, description="Square burst width (ns)") burst_uncertainty: Optional[float] = Field(default=None, description="Uncertainty on burst width") burst_flag: VaryFlag = Field(default=VaryFlag.NO, description="Flag for burst width") channel_energies: List[float] = Field(default_factory=list, description="Energies for channels") channel_widths: List[float] = Field(default_factory=list, description="Channel width values") channel_uncertainties: List[Optional[float]] = Field(default_factory=list, description="Channel uncertainties") channel_flags: List[VaryFlag] = Field(default_factory=list, description="Channel flags") filenames: List[str] = Field(default_factory=list, description="Resolution function filenames")
[docs] class PhysicsParameters(BaseModel): """Container for all physics parameters. Attributes: energy_parameters: Parameters for energy bounds and sampling normalization_parameters: Parameters for normalization and background broadening_parameters: Parameters for broadening user_resolution_parameters: User-defined resolution function parameters """ energy_parameters: EnergyParameters = Field(default_factory=EnergyParameters, description="Energy parameters") normalization_parameters: NormalizationParameters = Field( default_factory=NormalizationParameters, description="Normalization parameters" ) broadening_parameters: BroadeningParameters = Field( default_factory=BroadeningParameters, description="Broadening parameters" ) user_resolution_parameters: UserResolutionParameters = Field( default_factory=UserResolutionParameters, description="User-defined resolution function parameters" ) def __init__(self, **data): super().__init__(**data) self.validate_gaussian_parameters()
[docs] def validate_gaussian_parameters(self) -> None: """Validate that if any Gaussian parameter is present, both are present.""" self.broadening_parameters.validate_gaussian_parameters()