"""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 @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 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 LOGGER.info("Generator stopped") def step(self, state: GeneratorState, load_demand_mw: float, dt: float) -> float: """Advance generator dynamics and return delivered power.""" 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)) if state.spool_remaining <= 0.0: state.starting = False state.running = True 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) else: state.power_output_mw = 0.0 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.01 * dt) else: state.battery_charge = max(0.0, state.battery_charge - 0.001 * dt) return state.power_output_mw