78 lines
2.6 KiB
Python
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
|