Add spool dynamics for pumps and turbines

This commit is contained in:
Codex Agent
2025-11-22 19:13:57 +01:00
parent e18f100e15
commit e81a9fdbe3
6 changed files with 144 additions and 38 deletions

View File

@@ -167,50 +167,78 @@ class Reactor:
pump_demand = overrides.get("coolant_demand", self.control.coolant_demand(state.primary_loop))
if self.primary_pump_active:
total_flow = 0.0
pressure = 12.0 * pump_demand + 2.0
target_pressure = 12.0 * pump_demand + 2.0
loop_pressure = 0.5
target_flow = self.primary_pump.flow_rate(pump_demand)
for idx, pump_state in enumerate(state.primary_pumps):
if idx < len(self.primary_pump_units) and self.primary_pump_units[idx]:
flow = self.primary_pump.flow_rate(pump_demand)
pump_state.active = True
pump_state.flow_rate = flow
pump_state.pressure = pressure
total_flow += flow
else:
pump_state.active = False
pump_state.flow_rate = 0.0
pump_state.pressure = state.primary_loop.pressure
unit_enabled = idx < len(self.primary_pump_units) and self.primary_pump_units[idx]
desired_flow = target_flow if unit_enabled else 0.0
desired_pressure = target_pressure if unit_enabled else 0.5
pump_state.flow_rate = self._ramp_value(
pump_state.flow_rate, desired_flow, dt, self.primary_pump.spool_time
)
pump_state.pressure = self._ramp_value(
pump_state.pressure, desired_pressure, dt, self.primary_pump.spool_time
)
pump_state.active = unit_enabled or pump_state.flow_rate > 1.0
total_flow += pump_state.flow_rate
loop_pressure = max(loop_pressure, pump_state.pressure)
state.primary_loop.mass_flow_rate = total_flow
state.primary_loop.pressure = pressure
state.primary_loop.pressure = loop_pressure if total_flow > 0 else self._ramp_value(
state.primary_loop.pressure, 0.5, dt, self.primary_pump.spool_time
)
else:
state.primary_loop.mass_flow_rate = 0.0
state.primary_loop.pressure = 0.5
state.primary_loop.mass_flow_rate = self._ramp_value(
state.primary_loop.mass_flow_rate, 0.0, dt, self.primary_pump.spool_time
)
state.primary_loop.pressure = self._ramp_value(
state.primary_loop.pressure, 0.5, dt, self.primary_pump.spool_time
)
for pump_state in state.primary_pumps:
pump_state.active = False
pump_state.flow_rate = 0.0
pump_state.pressure = state.primary_loop.pressure
pump_state.flow_rate = self._ramp_value(
pump_state.flow_rate, 0.0, dt, self.primary_pump.spool_time
)
pump_state.pressure = self._ramp_value(
pump_state.pressure, state.primary_loop.pressure, dt, self.primary_pump.spool_time
)
if self.secondary_pump_active:
total_flow = 0.0
pressure = 12.0 * 0.75 + 2.0
target_pressure = 12.0 * 0.75 + 2.0
loop_pressure = 0.5
target_flow = self.secondary_pump.flow_rate(0.75)
for idx, pump_state in enumerate(state.secondary_pumps):
if idx < len(self.secondary_pump_units) and self.secondary_pump_units[idx]:
flow = self.secondary_pump.flow_rate(0.75)
pump_state.active = True
pump_state.flow_rate = flow
pump_state.pressure = pressure
total_flow += flow
else:
pump_state.active = False
pump_state.flow_rate = 0.0
pump_state.pressure = state.secondary_loop.pressure
unit_enabled = idx < len(self.secondary_pump_units) and self.secondary_pump_units[idx]
desired_flow = target_flow if unit_enabled else 0.0
desired_pressure = target_pressure if unit_enabled else 0.5
pump_state.flow_rate = self._ramp_value(
pump_state.flow_rate, desired_flow, dt, self.secondary_pump.spool_time
)
pump_state.pressure = self._ramp_value(
pump_state.pressure, desired_pressure, dt, self.secondary_pump.spool_time
)
pump_state.active = unit_enabled or pump_state.flow_rate > 1.0
total_flow += pump_state.flow_rate
loop_pressure = max(loop_pressure, pump_state.pressure)
state.secondary_loop.mass_flow_rate = total_flow
state.secondary_loop.pressure = pressure
state.secondary_loop.pressure = loop_pressure if total_flow > 0 else self._ramp_value(
state.secondary_loop.pressure, 0.5, dt, self.secondary_pump.spool_time
)
else:
state.secondary_loop.mass_flow_rate = 0.0
state.secondary_loop.pressure = 0.5
state.secondary_loop.mass_flow_rate = self._ramp_value(
state.secondary_loop.mass_flow_rate, 0.0, dt, self.secondary_pump.spool_time
)
state.secondary_loop.pressure = self._ramp_value(
state.secondary_loop.pressure, 0.5, dt, self.secondary_pump.spool_time
)
for pump_state in state.secondary_pumps:
pump_state.active = False
pump_state.flow_rate = 0.0
pump_state.pressure = state.secondary_loop.pressure
pump_state.flow_rate = self._ramp_value(
pump_state.flow_rate, 0.0, dt, self.secondary_pump.spool_time
)
pump_state.pressure = self._ramp_value(
pump_state.pressure, state.secondary_loop.pressure, dt, self.secondary_pump.spool_time
)
self.thermal.step_core(state.core, state.primary_loop, total_power, dt)
if not self.secondary_pump_active or state.secondary_loop.mass_flow_rate <= 1.0:
@@ -219,7 +247,7 @@ class Reactor:
transferred = heat_transfer(state.primary_loop, state.secondary_loop, total_power)
self.thermal.step_secondary(state.secondary_loop, transferred)
self._step_turbine_bank(state, transferred)
self._step_turbine_bank(state, transferred, 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:
@@ -253,7 +281,7 @@ class Reactor:
sum(t.load_demand_mw for t in state.turbines),
)
def _step_turbine_bank(self, state: PlantState, steam_power_mw: float) -> None:
def _step_turbine_bank(self, state: PlantState, steam_power_mw: float, dt: float) -> None:
if not state.turbines:
return
active_indices = [
@@ -265,9 +293,9 @@ class Reactor:
break
turbine_state = state.turbines[idx]
if idx in active_indices:
turbine.step(state.secondary_loop, turbine_state, steam_power_mw=power_per_unit)
turbine.step(state.secondary_loop, turbine_state, steam_power_mw=power_per_unit, dt=dt)
else:
self._reset_turbine_state(turbine_state)
self._spin_down_turbine(turbine_state, dt, turbine.spool_time)
self._dispatch_consumer_load(state, active_indices)
def _reset_turbine_state(self, turbine_state: TurbineState) -> None:
@@ -276,6 +304,23 @@ class Reactor:
turbine_state.load_demand_mw = 0.0
turbine_state.load_supplied_mw = 0.0
@staticmethod
def _ramp_value(current: float, target: float, dt: float, time_constant: float) -> float:
if time_constant <= 0.0:
return target
alpha = min(1.0, max(0.0, dt / time_constant))
return current + (target - current) * alpha
def _spin_down_turbine(self, turbine_state: TurbineState, dt: float, time_constant: float) -> None:
turbine_state.shaft_power_mw = self._ramp_value(turbine_state.shaft_power_mw, 0.0, dt, time_constant)
turbine_state.electrical_output_mw = self._ramp_value(
turbine_state.electrical_output_mw, 0.0, dt, time_constant
)
turbine_state.load_demand_mw = 0.0
turbine_state.load_supplied_mw = self._ramp_value(
turbine_state.load_supplied_mw, 0.0, dt, time_constant
)
def _dispatch_consumer_load(self, state: PlantState, active_indices: list[int]) -> None:
total_electrical = sum(state.turbines[idx].electrical_output_mw for idx in active_indices)
if self.consumer: