Add condenser realism and clean dashboard metrics
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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}"),
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user