Add mild thermal/measurement lag and expose margins on dashboard
This commit is contained in:
@@ -79,6 +79,10 @@ HX_FOULING_HEAL_RATE = 5e-6 # cleaning/settling when cool/low steam
|
||||
HX_FOULING_MAX_PENALTY = 0.25 # fractional UA loss cap
|
||||
BORON_WORTH_PER_PPM = 8e-6 # delta rho per ppm relative to baseline boron
|
||||
BORON_TRIM_RATE_PPM_PER_S = 0.02 # slow boron trim toward setpoint when near target
|
||||
# Mild thermal/measurement lags
|
||||
FUEL_TO_CLAD_TIME_CONSTANT = 0.3 # seconds (mild lag)
|
||||
CLAD_TO_COOLANT_TIME_CONSTANT = 0.2 # seconds (mild lag)
|
||||
POWER_MEASUREMENT_TIME_CONSTANT = 6.0 # seconds
|
||||
# Threshold inventories (event counts) for flagging common poisons in diagnostics.
|
||||
KEY_POISON_THRESHOLDS = {
|
||||
"Xe": 1e20, # xenon
|
||||
|
||||
@@ -41,7 +41,8 @@ class ControlSystem:
|
||||
if self._filtered_power_mw <= 0.0:
|
||||
self._filtered_power_mw = raw_power
|
||||
if raw_power > 0.7 * self.setpoint_mw:
|
||||
alpha = clamp(dt / 6.0, 0.0, 1.0) # ~6s time constant
|
||||
tau = constants.POWER_MEASUREMENT_TIME_CONSTANT
|
||||
alpha = clamp(dt / max(1e-6, tau), 0.0, 1.0)
|
||||
self._filtered_power_mw += alpha * (raw_power - self._filtered_power_mw)
|
||||
measured_power = self._filtered_power_mw
|
||||
else:
|
||||
|
||||
@@ -784,6 +784,15 @@ class ReactorDashboard:
|
||||
reliefs.append("Secondary")
|
||||
relief_attr = curses.color_pair(2) | curses.A_BOLD if reliefs else 0
|
||||
lines.append(("Relief valves", ", ".join(reliefs) if reliefs else "Closed", relief_attr))
|
||||
lines.append(("DNB margin", f"{state.core.dnb_margin:5.2f}" if state.core.dnb_margin is not None else "n/a"))
|
||||
lines.append(("Subcooling", f"{state.core.subcooling_margin:5.1f} K" if state.core.subcooling_margin is not None else "n/a"))
|
||||
lines.append(
|
||||
(
|
||||
"SG level",
|
||||
f"{state.secondary_loop.level*100:5.1f}%",
|
||||
)
|
||||
)
|
||||
lines.append(("SG pressure", f"{state.secondary_loop.pressure:5.2f}/{constants.MAX_PRESSURE:4.1f} MPa"))
|
||||
lines.append(
|
||||
(
|
||||
"SCRAM trips",
|
||||
|
||||
@@ -114,8 +114,16 @@ class ThermalSolver:
|
||||
dt: float,
|
||||
residual_power_mw: float | None = None,
|
||||
) -> None:
|
||||
def _lag(prev: float, new: float, tau: float) -> float:
|
||||
if tau <= 0.0:
|
||||
return new
|
||||
alpha = min(1.0, max(0.0, dt / max(1e-6, tau)))
|
||||
return prev + alpha * (new - prev)
|
||||
|
||||
if residual_power_mw is None:
|
||||
residual_power_mw = power_mw
|
||||
prev_fuel = core.fuel_temperature
|
||||
prev_clad = core.clad_temperature or primary.temperature_out
|
||||
temp_rise = temperature_rise(power_mw, primary.mass_flow_rate)
|
||||
primary.temperature_out = primary.temperature_in + temp_rise
|
||||
# Fuel heats from total fission power (even when most is convected) plus any residual left in the coolant.
|
||||
@@ -136,6 +144,9 @@ class ThermalSolver:
|
||||
# Keep temperatures bounded and never below coolant outlet.
|
||||
core.fuel_temperature = min(core.fuel_temperature, constants.MAX_CORE_TEMPERATURE)
|
||||
core.clad_temperature = min(clad, constants.MAX_CORE_TEMPERATURE)
|
||||
# Apply mild lags so heat moves from fuel to clad to coolant over a short time constant.
|
||||
core.fuel_temperature = _lag(prev_fuel, core.fuel_temperature, constants.FUEL_TO_CLAD_TIME_CONSTANT)
|
||||
core.clad_temperature = _lag(prev_clad, core.clad_temperature or prev_clad, constants.CLAD_TO_COOLANT_TIME_CONSTANT)
|
||||
core.subcooling_margin = max(0.0, saturation_temperature(primary.pressure) - primary.temperature_out)
|
||||
chf = self._critical_heat_flux(primary)
|
||||
heat_flux = (power_mw * constants.MEGAWATT) / max(1.0, self._core_surface_area())
|
||||
|
||||
Reference in New Issue
Block a user