Refine relief venting and pump pressure caps
This commit is contained in:
@@ -281,6 +281,8 @@ class Reactor:
|
|||||||
state.primary_loop.pressure, saturation_pressure(state.primary_loop.temperature_out), 0.1
|
state.primary_loop.pressure, saturation_pressure(state.primary_loop.temperature_out), 0.1
|
||||||
)
|
)
|
||||||
target_pressure = max(0.5, base_head * power_ratio)
|
target_pressure = max(0.5, base_head * power_ratio)
|
||||||
|
if self.primary_relief_open:
|
||||||
|
target_pressure = min(target_pressure, 1.0)
|
||||||
primary_flow_scale = min(
|
primary_flow_scale = min(
|
||||||
self._inventory_flow_scale(state.primary_loop), self._npsh_factor(state.primary_loop)
|
self._inventory_flow_scale(state.primary_loop), self._npsh_factor(state.primary_loop)
|
||||||
)
|
)
|
||||||
@@ -335,6 +337,8 @@ class Reactor:
|
|||||||
demand = 0.75
|
demand = 0.75
|
||||||
base_flow, base_head = self.secondary_pump.performance(demand)
|
base_flow, base_head = self.secondary_pump.performance(demand)
|
||||||
target_pressure = max(0.5, base_head * power_ratio)
|
target_pressure = max(0.5, base_head * power_ratio)
|
||||||
|
if self.secondary_relief_open:
|
||||||
|
target_pressure = min(target_pressure, 1.0)
|
||||||
loop_pressure = max(
|
loop_pressure = max(
|
||||||
state.secondary_loop.pressure, saturation_pressure(state.secondary_loop.temperature_out), 0.1
|
state.secondary_loop.pressure, saturation_pressure(state.secondary_loop.temperature_out), 0.1
|
||||||
)
|
)
|
||||||
@@ -398,14 +402,25 @@ class Reactor:
|
|||||||
self.thermal.step_core(state.core, state.primary_loop, total_power, dt, residual_power_mw=residual)
|
self.thermal.step_core(state.core, state.primary_loop, total_power, dt, residual_power_mw=residual)
|
||||||
self.thermal.step_secondary(state.secondary_loop, transferred, dt)
|
self.thermal.step_secondary(state.secondary_loop, transferred, dt)
|
||||||
if self.primary_relief_open:
|
if self.primary_relief_open:
|
||||||
state.primary_loop.pressure = max(
|
self._vent_relief(
|
||||||
0.1, min(state.primary_loop.pressure, saturation_pressure(state.primary_loop.temperature_out))
|
state.primary_loop,
|
||||||
|
target_pressure=1.0,
|
||||||
|
vent_rate_max=0.02,
|
||||||
|
ramp_time=12.0,
|
||||||
|
dt=dt,
|
||||||
)
|
)
|
||||||
|
for pump_state in state.primary_pumps:
|
||||||
|
pump_state.pressure = state.primary_loop.pressure
|
||||||
if self.secondary_relief_open:
|
if self.secondary_relief_open:
|
||||||
state.secondary_loop.pressure = max(
|
self._vent_relief(
|
||||||
0.1, min(state.secondary_loop.pressure, saturation_pressure(state.secondary_loop.temperature_out))
|
state.secondary_loop,
|
||||||
|
target_pressure=1.0,
|
||||||
|
vent_rate_max=0.05,
|
||||||
|
ramp_time=10.0,
|
||||||
|
dt=dt,
|
||||||
)
|
)
|
||||||
self._vent_secondary_relief(state.secondary_loop, dt)
|
for pump_state in state.secondary_pumps:
|
||||||
|
pump_state.pressure = state.secondary_loop.pressure
|
||||||
if not self.control.manual_control and not self.shutdown:
|
if not self.control.manual_control and not self.shutdown:
|
||||||
self.control.safety_backoff(state.core.subcooling_margin, state.core.dnb_margin, dt)
|
self.control.safety_backoff(state.core.subcooling_margin, state.core.dnb_margin, dt)
|
||||||
self._apply_secondary_boiloff(state, dt)
|
self._apply_secondary_boiloff(state, dt)
|
||||||
@@ -799,15 +814,21 @@ class Reactor:
|
|||||||
remaining = max(0.0, remaining - delivered)
|
remaining = max(0.0, remaining - delivered)
|
||||||
return total_power
|
return total_power
|
||||||
|
|
||||||
def _vent_secondary_relief(self, loop: CoolantLoopState, dt: float) -> None:
|
def _vent_relief(
|
||||||
|
self,
|
||||||
|
loop: CoolantLoopState,
|
||||||
|
target_pressure: float,
|
||||||
|
vent_rate_max: float,
|
||||||
|
ramp_time: float,
|
||||||
|
dt: float,
|
||||||
|
) -> None:
|
||||||
"""Model relief valve venting: gradual depressurization with mass/enthalpy loss."""
|
"""Model relief valve venting: gradual depressurization with mass/enthalpy loss."""
|
||||||
target_pressure = 1.0 # MPa surrogate relief sink (not full vacuum)
|
|
||||||
if loop.inventory_kg <= 0.0:
|
if loop.inventory_kg <= 0.0:
|
||||||
loop.pressure = max(target_pressure, loop.pressure)
|
loop.pressure = max(target_pressure, loop.pressure)
|
||||||
return
|
return
|
||||||
# Vent rate scales with overpressure; capped to keep a multi-second depressurization.
|
# Vent rate scales with overpressure; capped to keep a multi-second depressurization.
|
||||||
overfrac = max(0.0, (loop.pressure - target_pressure) / max(1e-6, loop.pressure))
|
overfrac = max(0.0, (loop.pressure - target_pressure) / max(1e-6, loop.pressure))
|
||||||
vent_rate = min(0.05, 0.01 + 0.1 * overfrac) # fraction of mass per second
|
vent_rate = min(vent_rate_max, 0.01 + vent_rate_max * overfrac) # fraction of mass per second
|
||||||
vent_mass = min(loop.inventory_kg, loop.inventory_kg * vent_rate * dt)
|
vent_mass = min(loop.inventory_kg, loop.inventory_kg * vent_rate * dt)
|
||||||
if vent_mass > 0.0:
|
if vent_mass > 0.0:
|
||||||
specific_enthalpy = (
|
specific_enthalpy = (
|
||||||
@@ -816,9 +837,18 @@ class Reactor:
|
|||||||
)
|
)
|
||||||
loop.inventory_kg = max(0.0, loop.inventory_kg - vent_mass)
|
loop.inventory_kg = max(0.0, loop.inventory_kg - vent_mass)
|
||||||
loop.energy_j = max(0.0, loop.energy_j - vent_mass * specific_enthalpy)
|
loop.energy_j = max(0.0, loop.energy_j - vent_mass * specific_enthalpy)
|
||||||
# Pressure ramps toward target with a ~10s time constant.
|
# Pressure ramps toward target with the requested time constant.
|
||||||
ramp = min(1.0, dt / 10.0)
|
ramp = min(1.0, dt / max(1e-6, ramp_time))
|
||||||
loop.pressure = max(target_pressure, loop.pressure - (loop.pressure - target_pressure) * ramp)
|
loop.pressure = max(target_pressure, loop.pressure - (loop.pressure - target_pressure) * ramp)
|
||||||
|
# Cool toward saturation at the new pressure to avoid re-pressurizing from superheat.
|
||||||
|
sat_target = saturation_temperature(target_pressure)
|
||||||
|
if loop.temperature_out > sat_target:
|
||||||
|
temp_drop = (loop.temperature_out - sat_target) * ramp
|
||||||
|
loop.temperature_out -= temp_drop
|
||||||
|
loop.temperature_in = min(loop.temperature_in, loop.temperature_out)
|
||||||
|
loop.steam_quality = 0.0
|
||||||
|
cp = constants.COOLANT_HEAT_CAPACITY
|
||||||
|
loop.energy_j = max(0.0, loop.inventory_kg * cp * loop.average_temperature())
|
||||||
# Re-resolve temperature/quality/pressure to reflect the vented state.
|
# Re-resolve temperature/quality/pressure to reflect the vented state.
|
||||||
try:
|
try:
|
||||||
self.thermal._resolve_secondary_state(loop) # type: ignore[attr-defined]
|
self.thermal._resolve_secondary_state(loop) # type: ignore[attr-defined]
|
||||||
|
|||||||
Reference in New Issue
Block a user