Quantize manual rod steps to 0.025

This commit is contained in:
Codex Agent
2025-11-23 23:12:13 +01:00
parent d6bb0543b6
commit 2c3f9e3b45
4 changed files with 52 additions and 4 deletions

View File

@@ -14,6 +14,7 @@ MAX_PRESSURE = 15.0 # MPa typical PWR primary loop limit
CONTROL_ROD_SPEED = 0.03 # fraction insertion per second CONTROL_ROD_SPEED = 0.03 # fraction insertion per second
CONTROL_ROD_WORTH = 0.042 # delta rho contribution when fully withdrawn CONTROL_ROD_WORTH = 0.042 # delta rho contribution when fully withdrawn
CONTROL_ROD_BANK_WEIGHTS = (0.4, 0.35, 0.25) CONTROL_ROD_BANK_WEIGHTS = (0.4, 0.35, 0.25)
ROD_MANUAL_STEP = 0.025
STEAM_TURBINE_EFFICIENCY = 0.34 STEAM_TURBINE_EFFICIENCY = 0.34
GENERATOR_EFFICIENCY = 0.96 GENERATOR_EFFICIENCY = 0.96
ENVIRONMENT_TEMPERATURE = 295.0 # K ENVIRONMENT_TEMPERATURE = 295.0 # K

View File

@@ -45,7 +45,7 @@ class ControlSystem:
return self.rod_fraction return self.rod_fraction
def set_rods(self, fraction: float) -> float: def set_rods(self, fraction: float) -> float:
self.rod_target = clamp(fraction, 0.0, 0.95) self.rod_target = self._quantize_manual(fraction)
self._advance_banks(self.rod_target, 0.0) self._advance_banks(self.rod_target, 0.0)
LOGGER.info("Manual rod target set to %.3f", self.rod_target) LOGGER.info("Manual rod target set to %.3f", self.rod_target)
return self.rod_target return self.rod_target
@@ -127,6 +127,11 @@ class ControlSystem:
def _sync_fraction(self) -> None: def _sync_fraction(self) -> None:
self.rod_fraction = self.effective_insertion() self.rod_fraction = self.effective_insertion()
def _quantize_manual(self, fraction: float) -> float:
step = constants.ROD_MANUAL_STEP
quantized = round(fraction / step) * step
return clamp(quantized, 0.0, 0.95)
def save_state( def save_state(
self, self,

View File

@@ -177,10 +177,10 @@ class ReactorDashboard:
self._toggle_turbine_unit(idx) self._toggle_turbine_unit(idx)
elif ch in (ord("+"), ord("=")): elif ch in (ord("+"), ord("=")):
# Insert rods (increase fraction) # Insert rods (increase fraction)
self._queue_command(ReactorCommand(rod_position=self._clamped_rod(0.05))) self._queue_command(ReactorCommand(rod_position=self._clamped_rod(constants.ROD_MANUAL_STEP)))
elif ch == ord("-"): elif ch == ord("-"):
# Withdraw rods (decrease fraction) # Withdraw rods (decrease fraction)
self._queue_command(ReactorCommand(rod_position=self._clamped_rod(-0.05))) self._queue_command(ReactorCommand(rod_position=self._clamped_rod(-constants.ROD_MANUAL_STEP)))
elif ch == ord("["): elif ch == ord("["):
demand = self._current_demand() - 50.0 demand = self._current_demand() - 50.0
self._queue_command(ReactorCommand(consumer_demand=max(0.0, demand))) self._queue_command(ReactorCommand(consumer_demand=max(0.0, demand)))
@@ -710,7 +710,9 @@ class ReactorDashboard:
def _clamped_rod(self, delta: float) -> float: def _clamped_rod(self, delta: float) -> float:
new_fraction = self.reactor.control.rod_fraction + delta new_fraction = self.reactor.control.rod_fraction + delta
return max(0.0, min(0.95, new_fraction)) step = constants.ROD_MANUAL_STEP
quantized = round(new_fraction / step) * step
return max(0.0, min(0.95, quantized))
def _install_log_capture(self) -> None: def _install_log_capture(self) -> None:
if self._log_handler: if self._log_handler:

40
tests/test_control.py Normal file
View File

@@ -0,0 +1,40 @@
import pytest
from reactor_sim.control import ControlSystem
from reactor_sim import constants
from reactor_sim.state import CoreState
def _core_state() -> CoreState:
return CoreState(
fuel_temperature=300.0,
neutron_flux=1e5,
reactivity_margin=0.0,
power_output_mw=0.0,
burnup=0.0,
)
def test_manual_rods_quantized_to_step():
control = ControlSystem()
control.manual_control = True
core = _core_state()
control.set_rods(0.333)
assert control.rod_target == 0.325
control.update_rods(core, dt=100.0)
assert control.rod_fraction == pytest.approx(0.325, rel=1e-6)
control.increment_rods(0.014)
assert control.rod_target == pytest.approx(0.35)
control.update_rods(core, dt=100.0)
assert control.rod_fraction == pytest.approx(0.35, rel=1e-6)
# Clamp upper bound
control.set_rods(1.0)
control.update_rods(core, dt=100.0)
assert control.rod_fraction == 0.95
def test_dashboard_step_constant_exposed():
assert constants.ROD_MANUAL_STEP == 0.025