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"]
Averaging options = [
----------------------------
"AVERAGE OVER ENERGY Ranges",
"GROUP AVERAGE OVER Energy ranges",
"ENERGY AVERAGE USING constant flux",
----------------------------
"MAXWELLIAN-AVERAGED capture cross sections",
"CALCULATE MAXWELLIAN averages after reconstr",
"MAKE NO CORRECTIONS to theoretical values",
"ADD CROSS SECTIONS From endf/b file 3",
"PRINT AVERAGED SENSItivities for endf parame",
----------------------------
]
"""
[docs]
class AveragesOptions(BaseModel):
model_config = ConfigDict(validate_default=True)
# Average type options (mutually exclusive)
average_over_energy_ranges: bool = Field(
default=False,
description="Produce energy-averaged experimental and theoretical values (histogram-based, use with caution for theoretical)",
)
group_average_over_energy_ranges: bool = Field(
default=False,
description="Produce Bondarenko-weighted multigroup cross sections",
)
energy_average_using_constant_flux: bool = Field(
default=False,
description="Produce unweighted energy average of theoretical cross sections (constant flux)",
)
# Maxwellian average options
maxwellian_averaged_capture_cross_sections: bool = Field(
default=False,
description="Generate stellar (Maxwellian) averaged capture cross sections",
)
calculate_maxwellian_averages_after_reconstruction: bool = Field(
default=False,
description="Calculate stellar averages using an energy grid reconstructed by SAMMY (NJOY method)",
)
# Additional averaging options
make_no_corrections_to_theoretical_values: bool = Field(
default=False,
description="Do not perform Doppler/resolution broadening or apply normalization/backgrounds before averaging",
)
add_cross_sections_from_endf_b_file_3: bool = Field(
default=False,
description="For stellar averages, add smooth cross sections from an ENDF File 3 to resolved resonance calculation",
)
print_averaged_sensitivities_for_endf_parameters: bool = Field(
default=False,
description="Output SAMAVG.COV, SAMSEN.DAT, SAMMY.LLL, SAMMY.MGS for multigroup averages",
)
# Define mutually exclusive groups as a class attribute
mutually_exclusive_groups: List[List[str]] = [
[
"average_over_energy_ranges",
"group_average_over_energy_ranges",
"energy_average_using_constant_flux",
],
]
[docs]
@model_validator(mode="after")
def validate_dependencies(self) -> "AveragesOptions":
"""Validate logical dependencies between options."""
# Check mutual exclusivity of average types
average_types_selected = 0
if self.average_over_energy_ranges:
average_types_selected += 1
if self.group_average_over_energy_ranges:
average_types_selected += 1
if self.energy_average_using_constant_flux:
average_types_selected += 1
if average_types_selected > 1:
raise ValueError(
"Only one of AVERAGE OVER ENERGY RANGES, GROUP AVERAGE OVER ENERGY RANGES, "
"or ENERGY AVERAGE USING CONSTANT FLUX can be enabled"
)
# Check if make_no_corrections_to_theoretical_values requires an average type
if self.make_no_corrections_to_theoretical_values:
if not (
self.average_over_energy_ranges
or self.group_average_over_energy_ranges
or self.energy_average_using_constant_flux
):
raise ValueError(
"MAKE NO CORRECTIONS TO THEORETICAL VALUES requires one of the average types to be enabled"
)
# Check if add_cross_sections_from_endf_b_file_3 requires a Maxwellian option
if self.add_cross_sections_from_endf_b_file_3:
if not (
self.maxwellian_averaged_capture_cross_sections
or self.calculate_maxwellian_averages_after_reconstruction
):
raise ValueError(
"ADD CROSS SECTIONS FROM ENDF/B FILE 3 requires MAXWELLIAN-AVERAGED CAPTURE CROSS SECTIONS "
"or CALCULATE MAXWELLIAN AVERAGES AFTER RECONSTRUCTION to be enabled"
)
return self
[docs]
def get_alphanumeric_commands(self) -> List[str]:
"""Return the list of alphanumeric commands based on the selected options."""
commands = []
if self.average_over_energy_ranges:
commands.append("AVERAGE OVER ENERGY RANGES")
if self.group_average_over_energy_ranges:
commands.append("GROUP AVERAGE OVER ENERGY RANGES")
if self.energy_average_using_constant_flux:
commands.append("ENERGY AVERAGE USING CONSTANT FLUX")
if self.maxwellian_averaged_capture_cross_sections:
commands.append("MAXWELLIAN-AVERAGED CAPTURE CROSS SECTIONS")
if self.calculate_maxwellian_averages_after_reconstruction:
commands.append("CALCULATE MAXWELLIAN AVERAGES AFTER RECONSTRUCTION")
if self.make_no_corrections_to_theoretical_values:
commands.append("MAKE NO CORRECTIONS TO THEORETICAL VALUES")
if self.add_cross_sections_from_endf_b_file_3:
commands.append("ADD CROSS SECTIONS FROM ENDF/B FILE 3")
if self.print_averaged_sensitivities_for_endf_parameters:
commands.append("PRINT AVERAGED SENSITIVITIES FOR ENDF PARAMETERS")
return commands
# Example usage
if __name__ == "__main__":
try:
# Example valid configuration
options = AveragesOptions(
average_over_energy_ranges=True,
make_no_corrections_to_theoretical_values=True,
print_averaged_sensitivities_for_endf_parameters=True,
)
print("Valid configuration:")
print(options.get_alphanumeric_commands())
# Example with mutually exclusive error
options = AveragesOptions(
average_over_energy_ranges=True,
group_average_over_energy_ranges=True, # This should fail
)
except ValueError as e:
print(f"Validation error: {e}")