feat: add reactor control persistence and tests

This commit is contained in:
Andrii Prokhorov
2025-11-21 17:11:00 +02:00
commit cc7fba4e7a
43 changed files with 1435 additions and 0 deletions

77
src/reactor_sim/state.py Normal file
View File

@@ -0,0 +1,77 @@
"""Dataclasses that capture the thermal-hydraulic state of the plant."""
from __future__ import annotations
from dataclasses import dataclass, field, asdict
def clamp(value: float, min_value: float, max_value: float) -> float:
return max(min_value, min(max_value, value))
@dataclass
class CoreState:
fuel_temperature: float # Kelvin
neutron_flux: float # neutrons/cm^2-s equivalent
reactivity_margin: float # delta rho
power_output_mw: float # MW thermal
burnup: float # fraction of fuel consumed
def update_burnup(self, dt: float) -> None:
produced_energy_mwh = self.power_output_mw * (dt / 3600.0)
self.burnup = clamp(self.burnup + produced_energy_mwh * 1e-5, 0.0, 0.99)
@dataclass
class CoolantLoopState:
temperature_in: float # K
temperature_out: float # K
pressure: float # MPa
mass_flow_rate: float # kg/s
steam_quality: float # fraction of vapor
def average_temperature(self) -> float:
return 0.5 * (self.temperature_in + self.temperature_out)
@dataclass
class TurbineState:
steam_enthalpy: float # kJ/kg
shaft_power_mw: float
electrical_output_mw: float
condenser_temperature: float
load_demand_mw: float = 0.0
load_supplied_mw: float = 0.0
@dataclass
class PlantState:
core: CoreState
primary_loop: CoolantLoopState
secondary_loop: CoolantLoopState
turbine: TurbineState
time_elapsed: float = field(default=0.0)
def snapshot(self) -> dict[str, float]:
return {
"time_elapsed": self.time_elapsed,
"core_temp": self.core.fuel_temperature,
"core_power": self.core.power_output_mw,
"neutron_flux": self.core.neutron_flux,
"primary_outlet_temp": self.primary_loop.temperature_out,
"secondary_pressure": self.secondary_loop.pressure,
"turbine_electric": self.turbine.electrical_output_mw,
}
def to_dict(self) -> dict:
return asdict(self)
@classmethod
def from_dict(cls, data: dict) -> "PlantState":
return cls(
core=CoreState(**data["core"]),
primary_loop=CoolantLoopState(**data["primary_loop"]),
secondary_loop=CoolantLoopState(**data["secondary_loop"]),
turbine=TurbineState(**data["turbine"]),
time_elapsed=data.get("time_elapsed", 0.0),
)