Add enthalpy tracking and dashboard metrics
This commit is contained in:
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user