Revert schematic to metrics view and park F2 plan

This commit is contained in:
Codex Agent
2025-11-26 22:49:58 +01:00
parent abc1cb79e1
commit b2427fc797
2 changed files with 4 additions and 63 deletions

View File

@@ -7,11 +7,7 @@
- [ ] Introduce CHF/DNB margin, clad/fuel split temps, and SCRAM matrix for subcooling loss or SG level/pressure trips. - [ ] Introduce CHF/DNB margin, clad/fuel split temps, and SCRAM matrix for subcooling loss or SG level/pressure trips.
- [ ] Flesh out condenser behavior: vacuum pump limits, cooling water temperature coupling, and dynamic back-pressure with fouling. - [ ] Flesh out condenser behavior: vacuum pump limits, cooling water temperature coupling, and dynamic back-pressure with fouling.
- [ ] Dashboard polish: compact turbine/generator rows, color critical warnings (SCRAM/heat-sink), and reduce repeated log noise. - [ ] Dashboard polish: compact turbine/generator rows, color critical warnings (SCRAM/heat-sink), and reduce repeated log noise.
- [ ] Dashboard multi-page view (F1/F2): - [ ] Dashboard multi-page view (F1/F2): retain numeric view on F1; future F2 schematic should mirror real PWR layout with ASCII art, flow/relief status, and minimal animations; add help/status hints and size checks; keep perf sane.
- F1 retains current numeric layout.
- F2 adds an ASCII schematic of the plant (core, primary pumps/pressurizer/HX, secondary pumps/drum, turbines/gens/consumer, reliefs/condenser) with inline key values (flows, pressures, steam quality/enthalpy, MW) and simple animations for flow/status.
- Add page indicator/status hint and size checks; keep updates performant (prebuilt template, minimal redraws).
- Optional: color-coded states (RUN/START/CAV/RELIEF) and blinking for alarms.
- [ ] Incremental realism plan: - [ ] Incremental realism plan:
- Add stored enthalpy for primary/secondary loops and a steam-drum mass/energy balance (sensible + latent) while keeping existing pump logic and tests passing. Target representative PWR conditions: primary 1516 MPa, 290320 °C inlet/320330 °C outlet, secondary saturation ~67 MPa with boil at ~490510 K. - Add stored enthalpy for primary/secondary loops and a steam-drum mass/energy balance (sensible + latent) while keeping existing pump logic and tests passing. Target representative PWR conditions: primary 1516 MPa, 290320 °C inlet/320330 °C outlet, secondary saturation ~67 MPa with boil at ~490510 K.
- Adjust HX/pressure handling to use stored energy (saturation clamp and pressure rise) and validate steam formation with both pumps at ~3 GW. Use realistic tube-side material assumptions (Inconel 690/SS cladding) and clamp steam quality to phase-equilibrium enthalpy. - Adjust HX/pressure handling to use stored energy (saturation clamp and pressure rise) and validate steam formation with both pumps at ~3 GW. Use realistic tube-side material assumptions (Inconel 690/SS cladding) and clamp steam quality to phase-equilibrium enthalpy.

View File

@@ -76,7 +76,7 @@ class ReactorDashboard:
self.sim: Optional[ReactorSimulation] = None self.sim: Optional[ReactorSimulation] = None
self.quit_requested = False self.quit_requested = False
self.reset_requested = False self.reset_requested = False
self.page = 1 # 1=metrics, 2=schematic self.page = 1 # 1=metrics, 2=schematic (placeholder)
self._last_state: Optional[PlantState] = None self._last_state: Optional[PlantState] = None
self._trend_history: deque[tuple[float, float, float]] = deque(maxlen=120) self._trend_history: deque[tuple[float, float, float]] = deque(maxlen=120)
self.log_buffer: deque[str] = deque(maxlen=8) self.log_buffer: deque[str] = deque(maxlen=8)
@@ -215,10 +215,6 @@ class ReactorDashboard:
self._queue_command(ReactorCommand(generator_auto=not self.reactor.generator_auto)) self._queue_command(ReactorCommand(generator_auto=not self.reactor.generator_auto))
elif ch in (ord("t"), ord("T")): elif ch in (ord("t"), ord("T")):
self._queue_command(ReactorCommand(turbine_on=not self.reactor.turbine_active)) self._queue_command(ReactorCommand(turbine_on=not self.reactor.turbine_active))
elif ch == curses.KEY_F1:
self.page = 1
elif ch == curses.KEY_F2:
self.page = 2
elif keyname and keyname.decode(errors="ignore") in ("!", "@", "#", '"'): elif keyname and keyname.decode(errors="ignore") in ("!", "@", "#", '"'):
name = keyname.decode(errors="ignore") name = keyname.decode(errors="ignore")
turbine_hotkeys = {"!": 0, "@": 1, "#": 2, '"': 1} turbine_hotkeys = {"!": 0, "@": 1, "#": 2, '"': 1}
@@ -234,7 +230,7 @@ class ReactorDashboard:
self._queue_command(ReactorCommand(rod_position=target, rod_manual=True)) self._queue_command(ReactorCommand(rod_position=target, rod_manual=True))
elif ch in _NUMPAD_ROD_KEYS: elif ch in _NUMPAD_ROD_KEYS:
self._queue_command(ReactorCommand(rod_position=_NUMPAD_ROD_KEYS[ch], rod_manual=True)) self._queue_command(ReactorCommand(rod_position=_NUMPAD_ROD_KEYS[ch], rod_manual=True))
elif curses.KEY_F3 <= ch <= curses.KEY_F9: elif curses.KEY_F1 <= ch <= curses.KEY_F9:
target = (ch - curses.KEY_F1 + 1) / 10.0 target = (ch - curses.KEY_F1 + 1) / 10.0
self._queue_command(ReactorCommand(rod_position=target, rod_manual=True)) self._queue_command(ReactorCommand(rod_position=target, rod_manual=True))
elif ch in (ord("+"), ord("=")): elif ch in (ord("+"), ord("=")):
@@ -385,10 +381,7 @@ class ReactorDashboard:
help_win = stdscr.derwin(data_height, right_width, 0, left_width + gap) help_win = stdscr.derwin(data_height, right_width, 0, left_width + gap)
status_win = stdscr.derwin(status_height, width, data_height, 0) status_win = stdscr.derwin(status_height, width, data_height, 0)
if self.page == 1:
self._draw_data_panel(data_win, state) self._draw_data_panel(data_win, state)
else:
self._draw_schematic_panel(data_win, state)
self._draw_help_panel(help_win) self._draw_help_panel(help_win)
self._draw_status_panel(status_win, state) self._draw_status_panel(status_win, state)
stdscr.refresh() stdscr.refresh()
@@ -640,54 +633,6 @@ class ReactorDashboard:
return "-" return "-"
return "·" return "·"
def _draw_schematic_panel(self, win: "curses._CursesWindow", state: PlantState) -> None:
win.erase()
win.box()
try:
win.addstr(0, 2, " Plant Schematic ", curses.color_pair(1) | curses.A_BOLD)
except curses.error:
pass
height, width = win.getmaxyx()
prim = state.primary_loop
sec = state.secondary_loop
p_pumps = state.primary_pumps if state.primary_pumps else []
s_pumps = state.secondary_pumps if state.secondary_pumps else []
p1 = p_pumps[0] if len(p_pumps) > 0 else None
p2 = p_pumps[1] if len(p_pumps) > 1 else None
s1 = s_pumps[0] if len(s_pumps) > 0 else None
s2 = s_pumps[1] if len(s_pumps) > 1 else None
steam_avail = self._steam_available_power(state)
enthalpy = state.turbines[0].steam_enthalpy if state.turbines else 0.0
lines = [
f"CORE {state.core.power_output_mw:5.0f}MW {state.core.fuel_temperature:5.0f}K | Rods {self.reactor.control.rod_fraction:.2f} ({'AUTO' if not self.reactor.control.manual_control else 'MAN'})",
f"Primary Flow: {self._flow_arrow(prim.mass_flow_rate)} {prim.mass_flow_rate:7.0f} kg/s | ΔT hx={state.primary_to_secondary_delta_t:4.0f}K eff={state.heat_exchanger_efficiency*100:5.1f}%",
f"Pumps P1[{self._pump_glyph(p1)}]{(p1.flow_rate if p1 else 0):6.0f}kg/s P2[{self._pump_glyph(p2)}]{(p2.flow_rate if p2 else 0):6.0f}kg/s Relief:{'OPEN' if self.reactor.primary_relief_open else 'CLOSED'}",
f"Ppri={prim.pressure:4.1f}MPa | Tin={prim.temperature_in:6.1f}K Tout={prim.temperature_out:6.1f}K",
"-" * max(10, min(width - 4, 70)),
f"Secondary Flow: {self._flow_arrow(sec.mass_flow_rate)} {sec.mass_flow_rate:7.0f} kg/s | Steam q={sec.steam_quality:4.2f} h={enthalpy:5.0f} kJ/kg avail={steam_avail:5.1f}MW",
f"Pumps S1[{self._pump_glyph(s1)}]{(s1.flow_rate if s1 else 0):6.0f}kg/s S2[{self._pump_glyph(s2)}]{(s2.flow_rate if s2 else 0):6.0f}kg/s Relief:{'OPEN' if self.reactor.secondary_relief_open else 'CLOSED'}",
f"Psec={sec.pressure:4.1f}MPa | Tin={sec.temperature_in:6.1f}K Tout={sec.temperature_out:6.1f}K",
f"Drum Level={sec.level*100:5.1f}% Energy={sec.energy_j/1e6:7.0f} MJ",
]
turbine_bits = []
for idx, t_state in enumerate(state.turbines):
turbine_bits.append(f"T{idx+1}:{t_state.electrical_output_mw:4.0f}MW")
lines.append("Turbines " + " ".join(turbine_bits) if turbine_bits else "Turbines n/a")
consumer_status = "OFF"
demand = 0.0
if self.reactor.consumer:
consumer_status = "ONLINE" if self.reactor.consumer.online else "OFF"
demand = self.reactor.consumer.demand_mw
lines.append(f"Consumer {consumer_status} demand={demand:5.0f}MW supplied={state.total_electrical_output():5.0f}MW")
for row, text in enumerate(lines, start=1):
if row >= height - 1:
break
try:
win.addstr(row, 2, text[: max(1, width - 3)])
except curses.error:
continue
def _turbine_status_lines(self) -> list[str]: def _turbine_status_lines(self) -> list[str]:
if not self.reactor.turbine_unit_active: if not self.reactor.turbine_unit_active: