From 5c0ad3fb725f32cff3a60c7f53a6cf8ebe258d91 Mon Sep 17 00:00:00 2001 From: Andrii Prokhorov Date: Fri, 21 Nov 2025 18:15:11 +0200 Subject: [PATCH] feat: link turbine output to transferred heat --- src/reactor_sim/consumer.py | 22 ++++++++++++++-------- src/reactor_sim/reactor.py | 2 +- src/reactor_sim/thermal.py | 7 +++++-- src/reactor_sim/turbine.py | 4 +++- 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/reactor_sim/consumer.py b/src/reactor_sim/consumer.py index cb656ee..1c83a56 100644 --- a/src/reactor_sim/consumer.py +++ b/src/reactor_sim/consumer.py @@ -14,6 +14,7 @@ class ElectricalConsumer: demand_mw: float online: bool = False power_received_mw: float = 0.0 + _under_supply_logged: bool = False def request_power(self) -> float: return self.demand_mw if self.online else 0.0 @@ -30,11 +31,16 @@ class ElectricalConsumer: def update_power_received(self, supplied_mw: float) -> None: self.power_received_mw = supplied_mw - if supplied_mw < self.request_power(): - LOGGER.warning( - "%s under-supplied: %.1f/%.1f MW", - self.name, - supplied_mw, - self.request_power(), - ) - + if supplied_mw + 1e-6 < self.request_power(): + if not self._under_supply_logged: + LOGGER.warning( + "%s under-supplied: %.1f/%.1f MW", + self.name, + supplied_mw, + self.request_power(), + ) + self._under_supply_logged = True + else: + if self._under_supply_logged: + LOGGER.info("%s demand satisfied", self.name) + self._under_supply_logged = False diff --git a/src/reactor_sim/reactor.py b/src/reactor_sim/reactor.py index 9222302..7520c19 100644 --- a/src/reactor_sim/reactor.py +++ b/src/reactor_sim/reactor.py @@ -127,7 +127,7 @@ class Reactor: self.thermal.step_secondary(state.secondary_loop, transferred) if self.turbine_active: - self.turbine.step(state.secondary_loop, state.turbine, self.consumer) + self.turbine.step(state.secondary_loop, state.turbine, self.consumer, steam_power_mw=transferred) else: state.turbine.shaft_power_mw = 0.0 state.turbine.electrical_output_mw = 0.0 diff --git a/src/reactor_sim/thermal.py b/src/reactor_sim/thermal.py index adeda99..5212e1c 100644 --- a/src/reactor_sim/thermal.py +++ b/src/reactor_sim/thermal.py @@ -5,6 +5,8 @@ from __future__ import annotations from dataclasses import dataclass import logging +import math + from . import constants from .state import CoolantLoopState, CoreState @@ -14,8 +16,9 @@ LOGGER = logging.getLogger(__name__) def heat_transfer(primary: CoolantLoopState, secondary: CoolantLoopState, core_power_mw: float) -> float: """Return MW transferred to the secondary loop.""" delta_t = max(0.0, primary.temperature_out - secondary.temperature_in) - conductance = 0.05 # steam generator effectiveness - transferred = min(core_power_mw, conductance * delta_t) + conductance = 0.15 # steam generator effectiveness + efficiency = 1.0 - math.exp(-conductance * delta_t) + transferred = min(core_power_mw, core_power_mw * efficiency) LOGGER.debug("Heat transfer %.2f MW with ΔT=%.1fK", transferred, delta_t) return transferred diff --git a/src/reactor_sim/turbine.py b/src/reactor_sim/turbine.py index 1303d2d..2202b25 100644 --- a/src/reactor_sim/turbine.py +++ b/src/reactor_sim/turbine.py @@ -34,10 +34,12 @@ class Turbine: loop: CoolantLoopState, state: TurbineState, consumer: Optional[ElectricalConsumer] = None, + steam_power_mw: float = 0.0, ) -> None: enthalpy = 2_700.0 + loop.steam_quality * 600.0 mass_flow = loop.mass_flow_rate * 0.6 - shaft_power_mw = (enthalpy * mass_flow / 1_000.0) * self.mechanical_efficiency / 1_000.0 + available_power = max(steam_power_mw, (enthalpy * mass_flow / 1_000.0) / 1_000.0) + shaft_power_mw = available_power * self.mechanical_efficiency electrical = shaft_power_mw * self.generator_efficiency if consumer: load_demand = consumer.request_power()