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: object

Holds 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_f degrees below supply_temp_f.

Returns True (and sets stopped_early) when the consecutive deficit duration exceeds outlet_deficit_max_min, signalling the simulation loop to halt. Resets the counter whenever the condition is not met.

Parameters:
  • top_tank_temp_f (float) – Current temperature at the top of the storage tank [°F].

  • supply_temp_f (float) – System hot water delivery temperature [°F].

Returns:

True if the simulation should stop immediately.

Return type:

bool

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.

Parameters:

max_outage_min (int) – Maximum allowable cumulative outage [minutes]. Default 0 (no outages).

Return type:

bool

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:

str

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:

float

get_peak_demand_kw() float[source]

Return peak instantaneous power draw observed during the simulation [kW].

Return type:

float

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:

dict

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 plotly package:

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:

float

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:

dict

get_monthly_cost_breakdown_detailed(uc) dict[source]

Like get_monthly_cost_breakdown but splits energy and demand charges into peak and off-peak components.

Returns:

Keys: energy_peak, energy_off_peak, demand_peak, demand_off_peak (each a 12-element list) and base (float).

Return type:

dict

get_monthly_energy_kwh() list[float][source]

Break down energy consumption by calendar month (annual runs only).

For non-annual simulations, returns a 12-element list of zeros.

Returns:

12-element list of energy [kWh] per month, January through December.

Return type:

list[float]

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:
  1. Delegates to DHWSystem.simulate_step() which queries the Building, updates heater states, applies heating, and draws from the tank.

  2. Records the returned per-step metrics into the SimulationRun.

  3. 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:

SimulationRun

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:
  • dhw_system (DHWSystem)

  • building (Building)

  • **sim_run_kwargs – Forwarded to SimulationRun.__init__() (e.g. outlet_deficit_threshold_f).

Return type:

SimulationRun

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:
  • dhw_system (DHWSystem)

  • building (Building)

  • **sim_run_kwargs – Forwarded to SimulationRun.__init__().

Return type:

SimulationRun