Simulation¶
SimulationRun¶
SimulationRun accumulates
per-timestep outputs and provides summary, cost, and plotting methods.
- class ecoengine.objects.simulation.SimulationRun.SimulationRun(duration_min: int, timestep_min: int, outlet_deficit_threshold_f: float = 5.0, outlet_deficit_max_min: int = 10)[source]¶
Bases:
objectHolds all per-timestep outputs from a simulation of a DHWSystem in a Building.
Stores time-series data (energy use, tank volume, DHW demand, capacity) and provides methods to assess whether the system successfully met demand and to compute summary metrics for visualization and cost/emissions comparisons.
- __init__(duration_min: int, timestep_min: int, outlet_deficit_threshold_f: float = 5.0, outlet_deficit_max_min: int = 10) None[source]¶
- Parameters:
duration_min (int) – Total simulation duration in minutes.
timestep_min (int) – Size of each simulation timestep in minutes (1 for 3-day, 10 for annual).
outlet_deficit_threshold_f (float) – Degrees below supply temperature at which the top-of-tank outlet is considered to be in deficit. Default 5 °F.
outlet_deficit_max_min (int) – Maximum consecutive minutes of outlet deficit before the simulation is halted early. Default 10 minutes.
- record_timestep(dhw_demand_supplyT_gal: float, usable_volume_supplyT_gal: float, heater_output_kbtuh: float, heater_power_in_kw: float | None, oat_f: float, inlet_water_temp_f: float, tank_temps_f: list[float], mode: str = 'normal', tm_tank_temp_f: float | None = None, tm_heater_output_kbtuh: float | None = None, tm_heater_input_kw: float | None = None) None[source]¶
Append one timestep’s worth of data to the run record.
- Parameters:
dhw_demand_supplyT_gal (float) – Hot water drawn this timestep at supply temperature [gallons].
usable_volume_supplyT_gal (float) – Gallons at or above supply temperature remaining in tank after draw.
heater_output_kbtuh (float) – Total heat delivered by active heaters this timestep [kBTU/hr].
heater_power_in_kw (float | None) – Electrical power consumed by active heaters [kW]. None when no real performance map is available (e.g. NominalPerformanceMap).
oat_f (float) – Outdoor air temperature this timestep [°F].
inlet_water_temp_f (float) – Cold water inlet temperature this timestep [°F].
tank_temps_f (list[float]) – Temperatures at each tank node (6 values, bottom to top) [°F]. Must have the same length as _TANK_NODE_FRACTS.
tm_tank_temp_f (float | None) – Current temperature of the TM (swing) tank [°F]. Only provided by SwingSystem; None for all other system types.
tm_heater_output_kbtuh (float | None) – Heat output of the TM element this timestep [kBTU/hr]. None for non-swing systems.
tm_heater_input_kw (float | None) – Electrical power consumed by TM element [kW]. None when no real performance map is available (e.g. NominalPerformanceMap).
- check_outlet_deficit(top_tank_temp_f: float, supply_temp_f: float) bool[source]¶
Track consecutive minutes where the top-of-tank temperature is more than
outlet_deficit_threshold_fdegrees belowsupply_temp_f.Returns True (and sets
stopped_early) when the consecutive deficit duration exceedsoutlet_deficit_max_min, signalling the simulation loop to halt. Resets the counter whenever the condition is not met.
- record_outage(duration_min: int) None[source]¶
Record a DHW outage (demand could not be met from tank).
- Parameters:
duration_min (int) – Duration of this outage event [minutes].
- is_successful(max_outage_min: int = 0) bool[source]¶
Return True if total outage time is within the acceptable threshold.
- get_failure_message() str[source]¶
Return a human-readable description of a simulation failure.
Finds the first timestep with zero usable volume to identify the day of failure, then computes the average outdoor air temperature across all outage timesteps.
- Returns:
A descriptive failure message, or a success message if no outage occurred.
- Return type:
- get_total_energy_kwh() float[source]¶
Return total electrical energy consumed over the simulation [kWh].
Includes primary heater energy and, when present, TM heater energy (from
tm_heater_input_kw). Timesteps with no power data (None) are treated as 0 kW.- Return type:
- get_peak_demand_kw() float[source]¶
Return peak instantaneous power draw observed during the simulation [kW].
- Return type:
- get_summary() dict[source]¶
Return a dict summarizing key simulation metrics.
- Returns:
Keys: ‘successful’, ‘total_outage_min’, ‘total_energy_kwh’, ‘peak_demand_kw’, ‘num_steps_recorded’, ‘stopped_early’.
- Return type:
- to_csv(filepath: str) None[source]¶
Write all per-timestep values to a CSV file.
Columns: timestep, time_min, dhw_demand_supplyT_gal, usable_volume_supplyT_gal, heater_output_kbtuh, heater_power_in_kw, oat_f, inlet_water_temp_f, tank_temp_0pct, tank_temp_20pct, …, tank_temp_100pct.
- Parameters:
filepath (str) – Destination file path (e.g.
'simulation_output.csv').
- to_plotly(title: str = 'Simulation Results', filepath: str | None = None, include_temperatures: bool = False) plotly.graph_objects.Figure[source]¶
Return a Plotly figure with gallons/flow on the left Y axis and temperature on the right Y axis, both plotted against time (minutes).
Left axis (Y1) — Volume / Flow Rate¶
Usable tank volume at supply temperature [gal] (green, solid)
DHW demand [gal/hr] (blue, solid)
Heater generation [gal/hr] (red, solid)
Right axis (Y2) — Temperature¶
Outdoor air temperature [°F] (orange, solid)
Inlet water temperature [°F] (steelblue, solid)
Tank temperatures at 0%, 20%, 40%, 60%, 80%, 100% (blue→red gradient, dashed)
Requires the
plotlypackage:pip install plotly
- param title:
Figure title.
- type title:
str
- param filepath:
If provided, write the figure to this path as a self-contained HTML file (e.g.
'output/simulation.html'). The figure is also returned regardless.- type filepath:
str | None
- param include_temperatures:
If True, add a right Y axis with OAT, inlet water temperature, and all six tank node temperatures. If False (default), only the left gallons/flow-rate axis is shown.
- type include_temperatures:
bool
- rtype:
plotly.graph_objects.Figure
- get_annual_utility_cost(uc) float[source]¶
Compute total annual utility cost from this simulation run.
Cost = energy charges + demand charges (max kW per period) + yearly base.
- Parameters:
uc (UtilityCostTracker) – Rate structure to apply.
- Returns:
Total annual cost [$].
- Return type:
- get_monthly_cost_breakdown(uc) dict[source]¶
Break down annual utility costs by month and charge type.
- Parameters:
uc (UtilityCostTracker)
- Returns:
{'energy': [12 floats], 'demand': [12 floats], 'base': float}Indices 0–11 correspond to January–December.'base'is the per-month base charge (same for all months).- Return type:
Simulator¶
Module-level functions that drive the simulation loop.
- ecoengine.interfaces.Simulator.simulate(dhw_system: DHWSystem, building: Building, duration: str = '3day', **sim_run_kwargs) SimulationRun[source]¶
Run a time-step simulation of a sized DHWSystem in a Building.
- At every timestep the simulator:
Delegates to DHWSystem.simulate_step() which queries the Building, updates heater states, applies heating, and draws from the tank.
Records the returned per-step metrics into the SimulationRun.
Checks for a DHW outage (usable tank volume <= 0).
The storage tank is initialized before the loop at a charge level corresponding to the normal Controls on-aquastat fraction. If no Controls are present, the tank starts fully charged.
- Parameters:
dhw_system (DHWSystem) – A sized DHWSystem instance (size() must have been called and the storage_tank must not be None).
building (Building) – The building to simulate the system in.
duration (str) – ‘3day’ for a 3-day design-day simulation (1-minute steps) or ‘annual’ for a full-year simulation (10-minute steps).
- Returns:
Object containing per-timestep outputs and summary metrics.
- Return type:
- Raises:
ValueError – If duration is not ‘3day’ or ‘annual’.
- ecoengine.interfaces.Simulator.simulate_3day(dhw_system: DHWSystem, building: Building, **sim_run_kwargs) SimulationRun[source]¶
Convenience wrapper: run a 3-day simulation at 1-minute timesteps.
- Parameters:
- Return type:
- ecoengine.interfaces.Simulator.simulate_annual(dhw_system: DHWSystem, building: Building, **sim_run_kwargs) SimulationRun[source]¶
Convenience wrapper: run a full annual simulation at 10-minute timesteps.
- Parameters:
- Return type: