124 lines
4.6 KiB
Python
124 lines
4.6 KiB
Python
"""Dataclasses that capture the thermal-hydraulic state of the plant."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field, asdict
|
|
|
|
from .generator import GeneratorState
|
|
|
|
|
|
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
|
|
fission_product_inventory: dict[str, float] = field(default_factory=dict)
|
|
emitted_particles: dict[str, float] = field(default_factory=dict)
|
|
|
|
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)
|
|
|
|
def add_products(self, products: dict[str, float]) -> None:
|
|
for element, amount in products.items():
|
|
self.fission_product_inventory[element] = self.fission_product_inventory.get(element, 0.0) + amount
|
|
|
|
def add_emitted_particles(self, particles: dict[str, float]) -> None:
|
|
for name, amount in particles.items():
|
|
self.emitted_particles[name] = self.emitted_particles.get(name, 0.0) + amount
|
|
|
|
|
|
@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 PumpState:
|
|
active: bool
|
|
flow_rate: float
|
|
pressure: float
|
|
|
|
|
|
@dataclass
|
|
class PlantState:
|
|
core: CoreState
|
|
primary_loop: CoolantLoopState
|
|
secondary_loop: CoolantLoopState
|
|
turbines: list[TurbineState]
|
|
primary_pumps: list[PumpState] = field(default_factory=list)
|
|
secondary_pumps: list[PumpState] = field(default_factory=list)
|
|
generators: list[GeneratorState] = field(default_factory=list)
|
|
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.total_electrical_output(),
|
|
"products": self.core.fission_product_inventory,
|
|
"particles": self.core.emitted_particles,
|
|
"primary_pumps": [pump.active for pump in self.primary_pumps],
|
|
"secondary_pumps": [pump.active for pump in self.secondary_pumps],
|
|
"generators": [gen.running or gen.starting for gen in self.generators],
|
|
}
|
|
|
|
def total_electrical_output(self) -> float:
|
|
return sum(t.electrical_output_mw for t in self.turbines)
|
|
|
|
def to_dict(self) -> dict:
|
|
return asdict(self)
|
|
|
|
@classmethod
|
|
def from_dict(cls, data: dict) -> "PlantState":
|
|
core_blob = dict(data["core"])
|
|
inventory = core_blob.pop("fission_product_inventory", {})
|
|
particles = core_blob.pop("emitted_particles", {})
|
|
turbines_blob = data.get("turbines")
|
|
if turbines_blob is None:
|
|
# Compatibility with previous single-turbine snapshots.
|
|
old_turbine = data.get("turbine")
|
|
turbines_blob = [old_turbine] if old_turbine else []
|
|
turbines = [TurbineState(**t) for t in turbines_blob]
|
|
prim_pumps_blob = data.get("primary_pumps", [])
|
|
sec_pumps_blob = data.get("secondary_pumps", [])
|
|
generators_blob = data.get("generators", [])
|
|
generators = [GeneratorState(**g) for g in generators_blob]
|
|
return cls(
|
|
core=CoreState(**core_blob, fission_product_inventory=inventory, emitted_particles=particles),
|
|
primary_loop=CoolantLoopState(**data["primary_loop"]),
|
|
secondary_loop=CoolantLoopState(**data["secondary_loop"]),
|
|
turbines=turbines,
|
|
primary_pumps=[PumpState(**p) for p in prim_pumps_blob],
|
|
secondary_pumps=[PumpState(**p) for p in sec_pumps_blob],
|
|
generators=generators,
|
|
time_elapsed=data.get("time_elapsed", 0.0),
|
|
)
|