diff --git a/src/reactor_sim/dashboard.py b/src/reactor_sim/dashboard.py index c0b8c54..1d0a8ee 100644 --- a/src/reactor_sim/dashboard.py +++ b/src/reactor_sim/dashboard.py @@ -275,7 +275,8 @@ class ReactorDashboard: y, "Primary Loop", [ - ("Pump", "ON" if self.reactor.primary_pump_active else "OFF"), + ("Pump1", self._pump_status(state.primary_pumps, 0)), + ("Pump2", self._pump_status(state.primary_pumps, 1)), ("Flow", f"{state.primary_loop.mass_flow_rate:7.0f} kg/s"), ("Inlet Temp", f"{state.primary_loop.temperature_in:7.1f} K"), ("Outlet Temp", f"{state.primary_loop.temperature_out:7.1f} K"), @@ -287,7 +288,8 @@ class ReactorDashboard: y, "Secondary Loop", [ - ("Pump", "ON" if self.reactor.secondary_pump_active else "OFF"), + ("Pump1", self._pump_status(state.secondary_pumps, 0)), + ("Pump2", self._pump_status(state.secondary_pumps, 1)), ("Flow", f"{state.secondary_loop.mass_flow_rate:7.0f} kg/s"), ("Inlet Temp", f"{state.secondary_loop.temperature_in:7.1f} K"), ("Pressure", f"{state.secondary_loop.pressure:5.2f} MPa"), @@ -305,6 +307,15 @@ class ReactorDashboard: "Turbine / Grid", [ ("Turbines", " ".join(self._turbine_status_lines())), + ("Unit1 Elec", f"{state.turbines[0].electrical_output_mw:7.1f} MW" if state.turbines else "n/a"), + ( + "Unit2 Elec", + f"{state.turbines[1].electrical_output_mw:7.1f} MW" if len(state.turbines) > 1 else "n/a", + ), + ( + "Unit3 Elec", + f"{state.turbines[2].electrical_output_mw:7.1f} MW" if len(state.turbines) > 2 else "n/a", + ), ("Electrical", f"{state.total_electrical_output():7.1f} MW"), ("Load", f"{self._total_load_supplied(state):7.1f}/{self._total_load_demand(state):7.1f} MW"), ("Consumer", f"{consumer_status}"), @@ -421,6 +432,12 @@ class ReactorDashboard: return [("Active", "None")] return [(comp, "IN PROGRESS") for comp in sorted(self.reactor.maintenance_active)] + def _pump_status(self, pumps: list, index: int) -> str: + if index >= len(pumps): + return "n/a" + state = pumps[index] + return f"{'ON ' if state.active else 'OFF'} {state.flow_rate:6.0f} kg/s" + def _draw_health_bar(self, win: "curses._CursesWindow", start_y: int) -> None: height, width = win.getmaxyx() if start_y >= height - 2: diff --git a/src/reactor_sim/reactor.py b/src/reactor_sim/reactor.py index 907ea78..ddf3ab1 100644 --- a/src/reactor_sim/reactor.py +++ b/src/reactor_sim/reactor.py @@ -14,7 +14,7 @@ from .control import ControlSystem from .failures import HealthMonitor from .fuel import FuelAssembly, decay_heat_fraction from .neutronics import NeutronDynamics -from .state import CoolantLoopState, CoreState, PlantState, TurbineState +from .state import CoolantLoopState, CoreState, PlantState, PumpState, TurbineState from .thermal import ThermalSolver, heat_transfer from .turbine import SteamGenerator, Turbine @@ -102,6 +102,8 @@ class Reactor: mass_flow_rate=0.0, steam_quality=0.0, ) + primary_pumps = [PumpState(active=self.primary_pump_active, flow_rate=0.0, pressure=0.5) for _ in range(2)] + secondary_pumps = [PumpState(active=self.secondary_pump_active, flow_rate=0.0, pressure=0.5) for _ in range(2)] turbine_states = [ TurbineState( steam_enthalpy=2_000.0, @@ -113,7 +115,14 @@ class Reactor: ) for _ in self.turbines ] - return PlantState(core=core, primary_loop=primary, secondary_loop=secondary, turbines=turbine_states) + return PlantState( + core=core, + primary_loop=primary, + secondary_loop=secondary, + turbines=turbine_states, + primary_pumps=primary_pumps, + secondary_pumps=secondary_pumps, + ) def step(self, state: PlantState, dt: float, command: ReactorCommand | None = None) -> None: if self.shutdown: @@ -151,15 +160,41 @@ class Reactor: pump_demand = overrides.get("coolant_demand", self.control.coolant_demand(state.primary_loop)) if self.primary_pump_active: - self.primary_pump.step(state.primary_loop, pump_demand) + total_flow = 0.0 + pressure = 12.0 * pump_demand + 2.0 + for pump_state in state.primary_pumps: + flow = self.primary_pump.flow_rate(pump_demand) + pump_state.active = True + pump_state.flow_rate = flow + pump_state.pressure = pressure + total_flow += flow + state.primary_loop.mass_flow_rate = total_flow + state.primary_loop.pressure = pressure else: state.primary_loop.mass_flow_rate = 0.0 state.primary_loop.pressure = 0.5 + for pump_state in state.primary_pumps: + pump_state.active = False + pump_state.flow_rate = 0.0 + pump_state.pressure = state.primary_loop.pressure if self.secondary_pump_active: - self.secondary_pump.step(state.secondary_loop, 0.75) + total_flow = 0.0 + pressure = 12.0 * 0.75 + 2.0 + for pump_state in state.secondary_pumps: + flow = self.secondary_pump.flow_rate(0.75) + pump_state.active = True + pump_state.flow_rate = flow + pump_state.pressure = pressure + total_flow += flow + state.secondary_loop.mass_flow_rate = total_flow + state.secondary_loop.pressure = pressure else: state.secondary_loop.mass_flow_rate = 0.0 state.secondary_loop.pressure = 0.5 + for pump_state in state.secondary_pumps: + pump_state.active = False + pump_state.flow_rate = 0.0 + pump_state.pressure = state.secondary_loop.pressure 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: diff --git a/src/reactor_sim/state.py b/src/reactor_sim/state.py index a9aba1a..110a4e7 100644 --- a/src/reactor_sim/state.py +++ b/src/reactor_sim/state.py @@ -54,12 +54,21 @@ class TurbineState: 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) time_elapsed: float = field(default=0.0) def snapshot(self) -> dict[str, float]: @@ -73,6 +82,8 @@ class PlantState: "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], } def total_electrical_output(self) -> float: @@ -92,10 +103,14 @@ class PlantState: 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", []) 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], time_elapsed=data.get("time_elapsed", 0.0), )