Add enthalpy tracking and dashboard metrics

This commit is contained in:
Codex Agent
2025-11-25 20:23:25 +01:00
parent 3cb72f7ff0
commit 327fca7096
9 changed files with 124 additions and 61 deletions

View File

@@ -37,6 +37,8 @@ class Reactor:
consumer: ElectricalConsumer | None = None
health_monitor: HealthMonitor = field(default_factory=HealthMonitor)
pressurizer_level: float = 0.6
allow_external_aux: bool = False
relaxed_npsh: bool = False
primary_pump_active: bool = True
secondary_pump_active: bool = True
primary_pump_units: list[bool] = field(default_factory=lambda: [True, True])
@@ -255,6 +257,8 @@ class Reactor:
turbine_electrical = state.total_electrical_output()
generator_power = self._step_generators(state, aux_demand, turbine_electrical, dt)
aux_available = turbine_electrical + generator_power
if self.allow_external_aux:
aux_available = max(aux_available, aux_demand)
supplied = aux_available if aux_demand <= 0 else min(aux_available, aux_demand)
power_ratio = 1.0 if aux_demand <= 0 else min(1.0, supplied / max(1e-6, aux_demand))
if aux_demand > 0 and aux_available < 0.99 * aux_demand:
@@ -273,7 +277,9 @@ class Reactor:
total_flow = 0.0
base_flow, base_head = self.primary_pump.performance(pump_demand)
target_flow = base_flow * power_ratio
loop_pressure = max(0.1, saturation_pressure(state.primary_loop.temperature_out))
loop_pressure = max(
state.primary_loop.pressure, saturation_pressure(state.primary_loop.temperature_out), 0.1
)
target_pressure = max(0.5, base_head * power_ratio)
primary_flow_scale = min(
self._inventory_flow_scale(state.primary_loop), self._npsh_factor(state.primary_loop)
@@ -329,7 +335,9 @@ class Reactor:
demand = 0.75
base_flow, base_head = self.secondary_pump.performance(demand)
target_pressure = max(0.5, base_head * power_ratio)
loop_pressure = max(0.1, saturation_pressure(state.secondary_loop.temperature_out))
loop_pressure = max(
state.secondary_loop.pressure, saturation_pressure(state.secondary_loop.temperature_out), 0.1
)
target_flow = base_flow * power_ratio
secondary_flow_scale = min(
self._inventory_flow_scale(state.secondary_loop), self._npsh_factor(state.secondary_loop)
@@ -390,14 +398,17 @@ class Reactor:
transferred = 0.0
else:
transferred = heat_transfer(state.primary_loop, state.secondary_loop, total_power)
self.thermal.step_core(state.core, state.primary_loop, total_power, dt)
residual = max(0.0, total_power - transferred)
self.thermal.step_core(state.core, state.primary_loop, total_power, dt, residual_power_mw=residual)
self.thermal.step_secondary(state.secondary_loop, transferred, dt)
self._apply_secondary_boiloff(state, dt)
self._update_loop_inventory(
state.secondary_loop, constants.SECONDARY_LOOP_VOLUME_M3, constants.SECONDARY_INVENTORY_TARGET, dt
)
self._step_turbine_bank(state, transferred, dt)
steam_draw = self._step_turbine_bank(state, transferred, dt)
if steam_draw > 0.0:
self.thermal.remove_steam_energy(state.secondary_loop, steam_draw, dt)
self._maintenance_tick(state, dt)
if (not self.secondary_pump_active or state.secondary_loop.mass_flow_rate <= 1.0) and total_power > 50.0:
@@ -472,9 +483,10 @@ class Reactor:
sum(t.load_demand_mw for t in state.turbines),
)
def _step_turbine_bank(self, state: PlantState, steam_power_mw: float, dt: float) -> None:
def _step_turbine_bank(self, state: PlantState, steam_power_mw: float, dt: float) -> float:
if not state.turbines:
return
return 0.0
steam_draw_mw = 0.0
active_indices = [
idx for idx, active in enumerate(self.turbine_unit_active) if active and idx < len(state.turbines)
]
@@ -495,10 +507,13 @@ class Reactor:
turbine_state.status = "STARTING"
else:
turbine_state.status = "RUN"
total_eff = max(1e-6, turbine.generator_efficiency * turbine.mechanical_efficiency)
steam_draw_mw += turbine_state.electrical_output_mw / total_eff
else:
self._spin_down_turbine(turbine_state, dt, turbine.spool_time)
turbine_state.status = "STOPPING" if turbine_state.electrical_output_mw > 0.1 else "OFF"
self._dispatch_consumer_load(state, active_indices)
return steam_draw_mw
def _reset_turbine_state(self, turbine_state: TurbineState) -> None:
turbine_state.shaft_power_mw = 0.0
@@ -571,11 +586,13 @@ class Reactor:
return 1.0
def _npsh_factor(self, loop: CoolantLoopState) -> float:
if self.relaxed_npsh:
return 1.0
vapor_pressure = saturation_pressure(loop.temperature_in)
available = max(0.0, loop.pressure - vapor_pressure)
if available <= 0.0:
return 0.0
return max(0.0, min(1.0, available / constants.NPSH_REQUIRED_MPA))
return 0.001
return max(0.001, min(1.0, available / constants.NPSH_REQUIRED_MPA))
def _apply_pressurizer(self, primary: CoolantLoopState, dt: float) -> None:
if self.shutdown and primary.mass_flow_rate <= 100.0: