Quantize manual rod steps to 0.025
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
40
tests/test_control.py
Normal 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
|
||||||
Reference in New Issue
Block a user