Source code for ecoengine.objects.components.storage.StorageTank
from __future__ import annotations
from abc import ABC, abstractmethod
# ---------------------------------------------------------------------------
# Abstract base
# ---------------------------------------------------------------------------
[docs]
class StorageTank(ABC):
"""
Abstract base class for all storage tank models.
Defines the interface that WaterHeater, Controls, and DHWSystem rely on.
Concrete subclasses implement the thermal model (stratified vs. mixed).
Every subclass must expose ``total_volume_gal`` as a plain attribute and
implement the five simulation methods listed below.
"""
total_volume_gal: float
[docs]
@abstractmethod
def initialize(
self,
storage_temp_f: float,
cold_temp_f: float,
percent_useable: float,
) -> None:
"""Set initial tank thermal state before a simulation begins."""
[docs]
@abstractmethod
def get_temperature_at_fraction(self, fract: float) -> float:
"""
Return water temperature at fractional tank height (0=bottom, 1=top).
Used by Controls to decide whether to fire the heater.
"""
[docs]
@abstractmethod
def get_usable_volume_supplyT_gal(self, supply_temp_f: float) -> float:
"""Return gallons currently at or above supply temperature."""
[docs]
@abstractmethod
def draw(
self,
volume_supplyT_gal: float,
cold_temp_f: float,
supply_temp_f: float,
outlet_temp_f: float,
) -> None:
"""Remove DHW demand from the tank and replace with cold make-up water."""
[docs]
@abstractmethod
def heat(
self,
kbtuh: float,
duration_min: float,
outlet_temp_f: float,
) -> None:
"""Apply heat from active water heaters for one timestep."""
[docs]
@abstractmethod
def add_recirc_return(
self,
flow_gpm: float,
return_temp_f: float,
duration_min: float,
supply_temp_f: float | None = None,
) -> None:
"""
Apply recirculation loop heat loss to the tank.
Parameters
----------
flow_gpm : float
Recirculation loop flow rate [GPM].
return_temp_f : float
Temperature of water returning from the recirc loop [°F].
duration_min : float
Length of the timestep [minutes].
supply_temp_f : float | None
DHW delivery temperature [°F]. This determines how much heat the
recirc loop actually loses: ``loss = flow × rho_cp × (supply_temp - return_temp)``.
If ``None``, defaults to ``outlet_temp_f`` (the tank's current hot-end
temperature), which gives the correct result for TM tanks where the
supply and storage temperatures are equal. Pass explicitly for
primary storage tanks (SPRTP) where ``supply_temp_f < storage_temp_f``.
"""
[docs]
@abstractmethod
def get_average_draw_temp_f(self, draw_gal: float) -> float:
"""
Return the volume-weighted average temperature of the top ``draw_gal``
physical gallons.
Used by SwingSystem to determine the effective feed temperature from the
primary storage tank when the hot zone may be partially depleted.
"""
[docs]
@abstractmethod
def draw_physical_gal(
self,
gal: float,
inlet_temp_f: float,
supply_temp_f: float | None = None,
) -> None:
"""
Remove ``gal`` physical gallons from the top of the tank and replace
with cold make-up water at the bottom.
Unlike ``draw()``, no supply-temperature conversion is applied — the
caller provides physical gallons directly. ``supply_temp_f`` is used
by stratified tanks to floor ``_delta_gal`` at the no-usable-hot-water
state rather than the absolute fully-cold state.
"""