Files
Reactor-Sim/src/reactor_sim/generator.py
2025-11-22 23:23:06 +01:00

78 lines
2.6 KiB
Python

"""Auxiliary diesel generator model with spool dynamics."""
from __future__ import annotations
from dataclasses import dataclass
import logging
from . import constants
LOGGER = logging.getLogger(__name__)
@dataclass
class GeneratorState:
running: bool
starting: bool
spool_remaining: float
power_output_mw: float
battery_charge: float
status: str = "OFF"
battery_output_mw: float = 0.0
@dataclass
class DieselGenerator:
rated_output_mw: float = 50.0
spool_time: float = constants.GENERATOR_SPOOL_TIME
def start(self, state: GeneratorState) -> None:
if state.running or state.starting:
return
if state.battery_charge <= 0.05:
LOGGER.warning("Generator start failed: insufficient battery")
return
state.starting = True
state.spool_remaining = self.spool_time
state.status = "STARTING"
LOGGER.info("Generator starting (spool %.0fs)", self.spool_time)
def stop(self, state: GeneratorState) -> None:
if not (state.running or state.starting):
return
state.running = False
state.starting = False
state.spool_remaining = 0.0
state.power_output_mw = 0.0
state.status = "OFF"
LOGGER.info("Generator stopped")
def step(self, state: GeneratorState, load_demand_mw: float, dt: float) -> float:
"""Advance generator dynamics and return delivered power."""
state.battery_output_mw = 0.0
if state.starting:
state.spool_remaining = max(0.0, state.spool_remaining - dt)
state.power_output_mw = self.rated_output_mw * (1.0 - state.spool_remaining / max(self.spool_time, 1e-6))
state.battery_output_mw = min(0.5, load_demand_mw)
if state.spool_remaining <= 0.0:
state.starting = False
state.running = True
state.status = "RUN"
LOGGER.info("Generator online at %.1f MW", self.rated_output_mw)
elif state.running:
available = self.rated_output_mw
state.power_output_mw = min(available, load_demand_mw)
state.status = "RUN" if state.power_output_mw > 0 else "IDLE"
else:
state.power_output_mw = 0.0
state.status = "OFF"
if state.running:
state.battery_charge = min(1.0, state.battery_charge + 0.02 * dt)
elif state.starting:
state.battery_charge = max(0.0, state.battery_charge - 0.003 * dt)
else:
state.battery_charge = max(0.0, state.battery_charge - 0.00005 * dt)
return state.power_output_mw