from typing import List
from pydantic import BaseModel, ConfigDict, Field, model_validator
"""
These notes are taken from the SAMMY manual.
- * denotes a default options
- Mutually exclusive options are grouped together starting with ------ and ending with ------
- options can be written out multiple ways indicated with ["Default","Alternate 1","Alternate 2"]
Cross section calculation details = [
----------------------------
"USE POLAR COORDINATES for fission widths",
"NUMERICAL DERIVATIVES for resonance parameters",
----------------------------
* "DO NOT USE S-WAVE CUtoff",
"USE S-WAVE CUTOFF",
"USE NO CUTOFFS FOR DErivatives or cross sections",
----------------------------
"USE ALTERNATIVE COULomb functions",
"ADD DIRECT CAPTURE Component to cross section",
----------------------------
* "LAB NON COULOMB EXCItation energies",
"CM NON COULOMB EXCITation energies",
----------------------------
* "LAB COULOMB EXCITATIon energies",
"CM COULOMB EXCITATIOn energies",
----------------------------
"ADD ELIMINATED CAPTUre channel to final state",
----------------------------
]
"""
[docs]
class CrossSectionOptions(BaseModel):
model_config = ConfigDict(validate_default=True)
# Fission and resonance params options
use_polar_coordinates_for_fission_widths: bool = Field(
default=False, description="USE POLAR COORDINATES for fission widths"
)
numerical_derivatives_for_resonance_parameters: bool = Field(
default=False, description="NUMERICAL DERIVATIVES for resonance parameters"
)
# Cutoff options
do_not_use_s_wave_cutoff: bool = Field(default=True, description="DO NOT USE S-WAVE CUtoff")
use_s_wave_cutoff: bool = Field(default=False, description="USE S-WAVE CUTOFF")
use_no_cutoffs_for_derivatives: bool = Field(
default=False, description="USE NO CUTOFFS FOR DErivatives or cross sections"
)
# Function options
use_alternative_coulomb_functions: bool = Field(default=False, description="USE ALTERNATIVE COULomb functions")
add_direct_capture_component: bool = Field(
default=False, description="ADD DIRECT CAPTURE Component to cross section"
)
# Excitation energy options (non-Coulomb)
lab_non_coulomb_excitation_energies: bool = Field(default=True, description="LAB NON COULOMB EXCItation energies")
cm_non_coulomb_excitation_energies: bool = Field(default=False, description="CM NON COULOMB EXCITation energies")
# Excitation energy options (Coulomb)
lab_coulomb_excitation_energies: bool = Field(default=True, description="LAB COULOMB EXCITATIon energies")
cm_coulomb_excitation_energies: bool = Field(default=False, description="CM COULOMB EXCITATIOn energies")
# Eliminated capture channel option
add_eliminated_capture_channel: bool = Field(
default=False, description="ADD ELIMINATED CAPTUre channel to final state"
)
# Define mutually exclusive groups as a class attribute
mutually_exclusive_groups: List[List[str]] = [
["do_not_use_s_wave_cutoff", "use_s_wave_cutoff", "use_no_cutoffs_for_derivatives"],
["lab_non_coulomb_excitation_energies", "cm_non_coulomb_excitation_energies"],
["lab_coulomb_excitation_energies", "cm_coulomb_excitation_energies"],
]
[docs]
@model_validator(mode="after")
def enforce_exclusivity(self) -> "CrossSectionOptions":
for group in self.mutually_exclusive_groups:
true_fields = [f for f in group if getattr(self, f)]
if not true_fields:
continue
user_true = [f for f in true_fields if f in self.model_fields_set]
default_true = [f for f in true_fields if f not in self.model_fields_set]
# If >1 user-specified in same group => error
if len(user_true) > 1:
raise ValueError(
f"Multiple user-specified fields {user_true} are True in group {group}. Only one allowed."
)
# If exactly 1 user-specified => turn off all defaults in that group
if len(user_true) == 1:
for f in default_true:
setattr(self, f, False)
continue
# If all True fields are defaults, and more than 1 => error
if len(default_true) > 1:
raise ValueError(f"Multiple default fields {default_true} are True in group {group}. Only one allowed.")
return self
[docs]
def get_alphanumeric_commands(self) -> List[str]:
"""Return the list of alphanumeric commands based on the selected options."""
commands = []
if self.use_polar_coordinates_for_fission_widths:
commands.append("USE POLAR COORDINATES FOR FISSION WIDTHS")
if self.numerical_derivatives_for_resonance_parameters:
commands.append("NUMERICAL DERIVATIVES FOR RESONANCE PARAMETERS")
if self.do_not_use_s_wave_cutoff:
commands.append("DO NOT USE S-WAVE CUTOFF")
if self.use_s_wave_cutoff:
commands.append("USE S-WAVE CUTOFF")
if self.use_no_cutoffs_for_derivatives:
commands.append("USE NO CUTOFFS FOR DERIVATIVES OR CROSS SECTIONS")
if self.use_alternative_coulomb_functions:
commands.append("USE ALTERNATIVE COULOMB FUNCTIONS")
if self.add_direct_capture_component:
commands.append("ADD DIRECT CAPTURE COMPONENT TO CROSS SECTION")
if self.lab_non_coulomb_excitation_energies:
commands.append("LAB NON COULOMB EXCITATION ENERGIES")
if self.cm_non_coulomb_excitation_energies:
commands.append("CM NON COULOMB EXCITATION ENERGIES")
if self.lab_coulomb_excitation_energies:
commands.append("LAB COULOMB EXCITATION ENERGIES")
if self.cm_coulomb_excitation_energies:
commands.append("CM COULOMB EXCITATION ENERGIES")
if self.add_eliminated_capture_channel:
commands.append("ADD ELIMINATED CAPTURE CHANNEL TO FINAL STATE")
return commands
# Example usage
if __name__ == "__main__":
try:
options = CrossSectionOptions(do_not_use_s_wave_cutoff=True, use_s_wave_cutoff=True)
except ValueError as e:
print(e)