Add condenser realism and clean dashboard metrics

This commit is contained in:
Codex Agent
2025-11-26 23:03:58 +01:00
parent 311263b86f
commit 79f83c56d2
4 changed files with 37 additions and 14 deletions

View File

@@ -37,6 +37,10 @@ TURBINE_THROTTLE_EFFICIENCY_DROP = 0.15 # efficiency loss when at minimum throt
CONDENSER_BASE_PRESSURE_MPA = 0.01
CONDENSER_MAX_PRESSURE_MPA = 0.3
CONDENSER_BACKPRESSURE_PENALTY = 0.35 # fractional power loss at max back-pressure
CONDENSER_VACUUM_PUMP_RATE = 0.05 # MPa per second drawdown toward base when below max load
CONDENSER_COOLING_WATER_TEMP_K = 295.0 # cooling sink temperature
CONDENSER_FOULING_RATE = 0.00002 # incremental penalty per second of hot operation
CONDENSER_FOULING_MAX_PENALTY = 0.2 # max additional backpressure penalty from fouling
GENERATOR_SPOOL_TIME = 10.0 # seconds to reach full output
# Auxiliary power assumptions
PUMP_POWER_MW = 12.0 # MW draw per pump unit

View File

@@ -450,7 +450,6 @@ class ReactorDashboard:
("Outlet Temp", f"{state.primary_loop.temperature_out:7.1f} K (Target {constants.PRIMARY_OUTLET_TARGET_K:4.0f})"),
("Pressure", f"{state.primary_loop.pressure:5.2f}/{constants.MAX_PRESSURE:4.1f} MPa"),
("Pressurizer", f"{self.reactor.pressurizer_level*100:6.1f}% @ {constants.PRIMARY_PRESSURIZER_SETPOINT_MPA:4.1f} MPa"),
("Loop Energy", f"{state.primary_loop.energy_j/1e6:7.0f} MJ"),
("Relief", "OPEN" if self.reactor.primary_relief_open else "CLOSED"),
],
)
@@ -470,11 +469,6 @@ class ReactorDashboard:
("Outlet Temp", f"{state.secondary_loop.temperature_out:7.1f} K (Target {constants.SECONDARY_OUTLET_TARGET_K:4.0f})"),
("Pressure", f"{state.secondary_loop.pressure:5.2f}/{constants.MAX_PRESSURE:4.1f} MPa"),
("Steam Quality", f"{state.secondary_loop.steam_quality:5.2f}/1.00"),
("Drum Energy", f"{state.secondary_loop.energy_j/1e6:7.0f} MJ"),
(
"Spec Enthalpy",
f"{(state.secondary_loop.energy_j / max(1e-6, state.secondary_loop.inventory_kg))/1e3:7.0f} kJ/kg",
),
("Relief", "OPEN" if self.reactor.secondary_relief_open else "CLOSED"),
],
)
@@ -503,6 +497,15 @@ class ReactorDashboard:
f"{state.turbines[2].electrical_output_mw:7.1f} MW" if len(state.turbines) > 2 else "n/a",
),
("Throttle", f"{self.reactor.turbines[0].throttle:5.2f}" if self.reactor.turbines else "n/a"),
(
"Condenser",
(
f"P={state.turbines[0].condenser_pressure:4.2f} MPa T={state.turbines[0].condenser_temperature:6.1f}K "
f"Foul={state.turbines[0].fouling_penalty*100:4.1f}%"
)
if state.turbines
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}"),

View File

@@ -71,6 +71,8 @@ class TurbineState:
shaft_power_mw: float
electrical_output_mw: float
condenser_temperature: float
condenser_pressure: float = constants.CONDENSER_BASE_PRESSURE_MPA
fouling_penalty: float = 0.0
load_demand_mw: float = 0.0
load_supplied_mw: float = 0.0
status: str = "OFF"

View File

@@ -6,7 +6,7 @@ from dataclasses import dataclass
import logging
from . import constants
from .thermal import saturation_temperature
from .thermal import saturation_temperature, saturation_pressure
from .state import CoolantLoopState, TurbineState
LOGGER = logging.getLogger(__name__)
@@ -45,7 +45,9 @@ class Turbine:
state.load_demand_mw = 0.0
state.load_supplied_mw = 0.0
state.steam_enthalpy = 0.0
state.condenser_temperature = max(305.0, loop.temperature_in - 20.0)
state.condenser_temperature = max(constants.CONDENSER_COOLING_WATER_TEMP_K, loop.temperature_in - 20.0)
state.condenser_pressure = max(constants.CONDENSER_BASE_PRESSURE_MPA, state.condenser_pressure - 0.01 * dt)
state.fouling_penalty = max(0.0, state.fouling_penalty - 0.0001 * dt)
return
throttle = min(constants.TURBINE_THROTTLE_MAX, max(constants.TURBINE_THROTTLE_MIN, self.throttle))
@@ -58,13 +60,24 @@ class Turbine:
computed_power = (enthalpy * mass_flow) / 1_000.0 # MW from enthalpy flow
available_power = steam_power_mw if steam_power_mw > 0 else computed_power
available_power = min(available_power, computed_power)
backpressure_loss = 1.0 - _backpressure_penalty(loop)
backpressure_loss = 1.0 - _backpressure_penalty(state)
shaft_power_mw = available_power * self.mechanical_efficiency * throttle_eff * backpressure_loss
electrical = shaft_power_mw * self.generator_efficiency
if electrical > self.rated_output_mw:
electrical = self.rated_output_mw
shaft_power_mw = electrical / max(1e-6, self.generator_efficiency)
condenser_temp = max(305.0, loop.temperature_in - 20.0)
condenser_temp = max(constants.CONDENSER_COOLING_WATER_TEMP_K, loop.temperature_in - 20.0)
# Vacuum pump tends toward base pressure; fouling raises it slowly when hot.
target_pressure = constants.CONDENSER_BASE_PRESSURE_MPA
if condenser_temp > constants.CONDENSER_COOLING_WATER_TEMP_K + 20.0:
state.fouling_penalty = min(
constants.CONDENSER_FOULING_MAX_PENALTY,
state.fouling_penalty + constants.CONDENSER_FOULING_RATE * dt,
)
state.condenser_pressure = max(
target_pressure,
min(constants.CONDENSER_MAX_PRESSURE_MPA, state.condenser_pressure - constants.CONDENSER_VACUUM_PUMP_RATE * dt),
)
state.steam_enthalpy = enthalpy
state.shaft_power_mw = _ramp(state.shaft_power_mw, shaft_power_mw, dt, self.spool_time)
state.electrical_output_mw = _ramp(state.electrical_output_mw, electrical, dt, self.spool_time)
@@ -84,11 +97,12 @@ def _ramp(current: float, target: float, dt: float, time_constant: float) -> flo
return current + (target - current) * alpha
def _backpressure_penalty(loop: CoolantLoopState) -> float:
def _backpressure_penalty(state: TurbineState) -> float:
base = constants.CONDENSER_BASE_PRESSURE_MPA
max_p = constants.CONDENSER_MAX_PRESSURE_MPA
pressure = max(base, min(max_p, loop.pressure))
pressure = max(base, min(max_p, state.condenser_pressure))
if pressure <= base:
return 0.0
return min(constants.CONDENSER_BACKPRESSURE_PENALTY, state.fouling_penalty)
frac = (pressure - base) / max(1e-6, max_p - base)
return min(constants.CONDENSER_BACKPRESSURE_PENALTY, frac * constants.CONDENSER_BACKPRESSURE_PENALTY)
penalty = frac * constants.CONDENSER_BACKPRESSURE_PENALTY
return min(constants.CONDENSER_BACKPRESSURE_PENALTY + state.fouling_penalty, penalty + state.fouling_penalty)