Add chemistry-driven fouling and HX/condenser penalties
This commit is contained in:
@@ -397,7 +397,12 @@ class Reactor:
|
||||
if not self.secondary_pump_active or state.secondary_loop.mass_flow_rate <= 1.0:
|
||||
transferred = 0.0
|
||||
else:
|
||||
transferred = heat_transfer(state.primary_loop, state.secondary_loop, total_power)
|
||||
transferred = heat_transfer(
|
||||
state.primary_loop,
|
||||
state.secondary_loop,
|
||||
total_power,
|
||||
fouling_factor=getattr(state, "hx_fouling", 0.0),
|
||||
)
|
||||
residual = max(0.0, total_power - transferred)
|
||||
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)
|
||||
@@ -425,6 +430,7 @@ class Reactor:
|
||||
self.control.safety_backoff(state.core.subcooling_margin, state.core.dnb_margin, dt)
|
||||
self._apply_secondary_boiloff(state, dt)
|
||||
self._update_secondary_level(state, dt)
|
||||
self._update_chemistry(state, dt)
|
||||
|
||||
steam_draw = self._step_turbine_bank(state, transferred, dt)
|
||||
if steam_draw > 0.0:
|
||||
@@ -652,6 +658,36 @@ class Reactor:
|
||||
loop.level = min(1.2, max(0.0, loop.inventory_kg / nominal_mass))
|
||||
self._last_steam_out_kg_s = steam_out
|
||||
|
||||
def _update_chemistry(self, state: PlantState, dt: float) -> None:
|
||||
"""Track dissolved species and fouling impacts on HX and condenser."""
|
||||
env = constants.ENVIRONMENT_TEMPERATURE
|
||||
steam_out = state.secondary_loop.mass_flow_rate * max(0.0, state.secondary_loop.steam_quality)
|
||||
temp = state.secondary_loop.temperature_out
|
||||
temp_factor = max(0.0, (temp - env) / 300.0)
|
||||
impurity_load = max(0.0, state.dissolved_oxygen_ppm + 0.5 * state.sodium_ppm)
|
||||
fouling_rate = constants.HX_FOULING_RATE * temp_factor * impurity_load
|
||||
heal = constants.HX_FOULING_HEAL_RATE * (1.0 if steam_out < 200.0 or temp_factor < 0.2 else 0.0)
|
||||
state.hx_fouling = max(
|
||||
0.0,
|
||||
min(constants.HX_FOULING_MAX_PENALTY, state.hx_fouling + (fouling_rate - heal) * dt),
|
||||
)
|
||||
# Degas oxygen with steam production; small impurity ingress over time (worse when venting).
|
||||
degas = 0.0005 * steam_out * dt / max(1.0, constants.SECONDARY_LOOP_VOLUME_M3)
|
||||
state.dissolved_oxygen_ppm = max(0.0, state.dissolved_oxygen_ppm - degas)
|
||||
ingress = (0.01 if self.secondary_relief_open else 0.002) * dt
|
||||
state.sodium_ppm = min(constants.CHEM_MAX_PPM, state.sodium_ppm + ingress)
|
||||
state.boron_ppm = max(0.0, state.boron_ppm - 0.001 * dt)
|
||||
chem_penalty = constants.CONDENSER_CHEM_FOULING_RATE * impurity_load / 1_000.0
|
||||
for turb_state in state.turbines:
|
||||
turb_state.fouling_penalty = min(
|
||||
constants.CONDENSER_FOULING_MAX_PENALTY,
|
||||
max(0.0, turb_state.fouling_penalty + chem_penalty * dt),
|
||||
)
|
||||
backpressure = constants.CONDENSER_CHEM_BACKPRESSURE_FACTOR * impurity_load * dt
|
||||
turb_state.condenser_pressure = min(
|
||||
constants.CONDENSER_MAX_PRESSURE_MPA, turb_state.condenser_pressure + backpressure
|
||||
)
|
||||
|
||||
def _inventory_flow_scale(self, loop: CoolantLoopState) -> float:
|
||||
if loop.level <= constants.LOW_LEVEL_FLOW_FLOOR:
|
||||
return 0.0
|
||||
|
||||
Reference in New Issue
Block a user