EcosizerEngine (Top-Level API)

The EcosizerEngine class is the primary public interface. All sizing, simulation, and result-retrieval methods are accessed through it.

Module-level convenience functions are also re-exported from the top-level ecoengine package.

EcosizerEngine class

class ecoengine.interfaces.EcosizerEngine.EcosizerEngine(building_type: str, magnitude: int | float, zip_code_or_climate_zone, supply_temp_f: float, storage_temp_f: float, schematic: str, gpdpp: float | None = None, custom_peak_load_shape: list[float] | None = None, custom_avg_load_shape: list[float] | None = None, num_heaters: int = 1, hpwh_model: str | None = None, max_daily_run_hr: float = 16.0, defrost_factor: float = 1.0, aquastat_fract: float = 0.5, off_sensor_fract: float = 0.1, on_trigger_t_f: float | None = None, off_trigger_t_f: float | None = None, load_shift_schedule: list[int] | None = None, load_up_hours: int = 0, shed_aquastat_fract: float | None = None, load_up_aquastat_fract: float | None = None, shed_off_sensor_fract: float | None = None, load_up_off_sensor_fract: float | None = None, load_up_off_trigger_t_f: float | None = None, load_shift_percent: float = 0.95, return_temp_f: float | None = None, return_flow_gpm: float | None = None, tm_on_temp_f: float | None = None, tm_off_temp_f: float | None = None, tm_off_time_hr: float = 0.5, tm_safety_factor: float = 1.75, utility_cost_tracker=None, storage_volume_storageT_gal: float | None = None, heating_capacity_kbtuh: float | None = None, tm_storage_vol: float | None = None, tm_capacity_kbtuh: float | None = None, tm_model: str | None = None, num_tm_heaters: int = 1)[source]

Bases: object

Primary backend interface for the Ecosizer tool.

Accepts building and system parameters, builds and sizes the appropriate DHWSystem subclass, and runs simulations. All input-range validation is expected to be handled by the frontend before calling this class.

Construction immediately builds and sizes the system — no separate build() or size() calls are required:

engine = EcosizerEngine(
    building_type            = "multi_family",
    magnitude                = 100,
    zip_code_or_climate_zone = {"design_oat_f": 47, "design_inlet_water_temp_f": 47},
    supply_temp_f            = 125.0,
    storage_temp_f           = 150.0,
    schematic                = "primary_no_recirc",
)
result = engine.simulate_3day()
result.to_plotly(filepath="output.html")
__init__(building_type: str, magnitude: int | float, zip_code_or_climate_zone, supply_temp_f: float, storage_temp_f: float, schematic: str, gpdpp: float | None = None, custom_peak_load_shape: list[float] | None = None, custom_avg_load_shape: list[float] | None = None, num_heaters: int = 1, hpwh_model: str | None = None, max_daily_run_hr: float = 16.0, defrost_factor: float = 1.0, aquastat_fract: float = 0.5, off_sensor_fract: float = 0.1, on_trigger_t_f: float | None = None, off_trigger_t_f: float | None = None, load_shift_schedule: list[int] | None = None, load_up_hours: int = 0, shed_aquastat_fract: float | None = None, load_up_aquastat_fract: float | None = None, shed_off_sensor_fract: float | None = None, load_up_off_sensor_fract: float | None = None, load_up_off_trigger_t_f: float | None = None, load_shift_percent: float = 0.95, return_temp_f: float | None = None, return_flow_gpm: float | None = None, tm_on_temp_f: float | None = None, tm_off_temp_f: float | None = None, tm_off_time_hr: float = 0.5, tm_safety_factor: float = 1.75, utility_cost_tracker=None, storage_volume_storageT_gal: float | None = None, heating_capacity_kbtuh: float | None = None, tm_storage_vol: float | None = None, tm_capacity_kbtuh: float | None = None, tm_model: str | None = None, num_tm_heaters: int = 1)[source]
Parameters:
  • building_type (str) – Building use type (e.g. 'multi_family', 'office').

  • magnitude (int or float) – Occupancy metric appropriate to the building type (people, units, etc.).

  • zip_code_or_climate_zone (str | int | dict) –

    Climate lookup key. Accepted forms:

    • 5-digit CA zip code string or int → ClimateZone.from_zip_code()

    • Integer 1–96 → ClimateZone.from_zone_id()

    • Non-numeric string → ClimateZone.from_weather_station()

    • Dict with keys 'design_oat_f' and/or 'design_inlet_water_temp_f'ClimateZone.from_design_conditions()

  • supply_temp_f (float) – DHW delivery temperature [°F].

  • storage_temp_f (float) – Hot water storage setpoint [°F].

  • schematic (str) –

    Piping configuration. Supported values:

    • 'primary_no_recirc' — base DHWSystem (heat pump + stratified tank)

    • 'parallel_loop' — separate TM tank in parallel for recirc losses

    • 'swing_tank' — swing-tank recirc system

    • 'single_pass_rtp' — single-pass return-to-primary

    • 'multi_pass_rtp' — multi-pass return-to-primary

    • 'instant_wh' — instantaneous (tankless) water heater, no storage

  • gpdpp (float, optional) – Gallons per person per day. If None, building-type defaults are used.

  • custom_peak_load_shape (list[float], optional) – 24-element normalized peak-hour DHW load shape. Required when building_type is None.

  • custom_avg_load_shape (list[float], optional) – 24-element normalized average-hour DHW load shape. Used for annual simulation load profiles.

  • num_heaters (int) – Number of primary HPWH units. Default 1.

  • hpwh_model (str, optional) – HPWH model name for performance map lookup (future use).

  • max_daily_run_hr (float) – Maximum hours the primary heating system may run per day. Default 16. NOT YET USED FOR MPRTP

  • defrost_factor (float) – Fraction of rated capacity available after defrost cycles (0–1). Default 1.0.

  • aquastat_fract (float) – Fractional tank height of the ON aquastat (0–1). Default 0.5.

  • off_sensor_fract (float) – Fractional tank height of the OFF aquastat (0–1). Default 0.1.

  • on_trigger_t_f (float, optional) – Temperature at which the ON aquastat fires. Defaults to supply_temp_f.

  • off_trigger_t_f (float, optional) – Temperature at which the OFF aquastat fires. Defaults to storage_temp_f.

  • load_shift_schedule (list[int], optional) – 24-element list (0 = shed hour, 1 = run hour). None → no load shifting.

  • load_up_hours (int) – Hours spent in load-up mode before the first shed window.

  • shed_aquastat_fract (float, optional) – ON aquastat fraction during shed hours (higher → more storage).

  • load_up_aquastat_fract (float, optional) – ON aquastat fraction during load-up hours (lower → fires sooner).

  • shed_off_sensor_fract (float, optional) – OFF aquastat fraction during shed hours. Defaults to off_sensor_fract.

  • load_up_off_sensor_fract (float, optional) – OFF aquastat fraction during load-up hours. Defaults to off_sensor_fract.

  • load_up_off_trigger_t_f (float, optional) – OFF trigger temperature during load-up hours. Defaults to off_trigger_t_f.

  • load_shift_percent (float) – Percentile of days the load-shift sizing must cover [0.25, 1.0]. Default 0.95 — size for 95% of days, accepting that the highest-demand 5% of days may occasionally breach the shed window. Only applied during load-shift sizing; normal sizing always uses the full daily demand.

  • return_temp_f (float, optional) – Recirc loop return temperature [°F]. Required for recirc schematics.

  • return_flow_gpm (float, optional) – Recirc loop flow rate [GPM]. Required for recirc schematics.

  • tm_on_temp_f (float, optional) – TM element turn-on temperature [°F]. Defaults to supply_temp_f − 5.

  • tm_off_temp_f (float, optional) – TM element turn-off temperature [°F]. Defaults to supply_temp_f.

  • tm_off_time_hr (float) – Max TM heater off-cycle duration [hr]. Default 0.5.

  • tm_safety_factor (float) – TM capacity safety multiplier (> 1.0). Default 1.2.

  • utility_cost_tracker (UtilityCostTracker, optional) – Attached to the building for annual cost estimates.

  • storage_volume_storageT_gal (float, optional) – Pre-sized primary storage tank volume at storage temperature [gallons]. When provided together with heating_capacity_kbtuh (or hpwh_model), the sizing step is skipped entirely.

  • heating_capacity_kbtuh (float, optional) – Pre-sized total primary heating capacity [kBTU/hr]. Must be paired with storage_volume_storageT_gal (except for instant_wh). For multi-heater systems this is the combined fleet capacity; each WaterHeater is assigned heating_capacity_kbtuh / num_heaters. Ignored when hpwh_model is also provided.

  • tm_storage_vol (float, optional) – Pre-sized TM tank volume [gallons]. Required for parallel_loop and swing_tank when using the pre-sized path.

  • tm_capacity_kbtuh (float, optional) – Pre-sized TM heater capacity [kBTU/hr]. Required for parallel_loop and swing_tank when tm_model is not provided.

  • tm_model (str, optional) – HPWH model name for the TM heater’s performance map. When provided, tm_capacity_kbtuh is used only as the ER fallback capacity. return_temp_f is used as design_inlet_temp_f.

  • num_tm_heaters (int) – Number of identical TM heater units for parallel_loop schematics. The single tm_water_heater object represents one unit; its output is multiplied by this value before being applied to the TM tank and recorded. For the sized path, per-unit capacity = _minimum_tm_capacity_kbtuh / num_tm_heaters; for the pre-sized path with tm_capacity_kbtuh, per-unit = tm_capacity_kbtuh / num_tm_heaters. Default 1.

build() None[source]

Build the Building and DHWSystem (including sizing) from stored params.

get_sizing_results() dict[source]

Return the sizing results from the built DHWSystem.

Returns:

Always contains 'min_capacity_kbtuh' and 'min_storage_storageT_gal'. Parallel loop systems also include 'min_tm_volume_gal' and 'min_tm_capacity_kbtuh'.

Return type:

dict

simulate_3day(**sim_run_kwargs)[source]

Run a 3-day design-day simulation at 1-minute timesteps.

Parameters:

**sim_run_kwargs – Optional keyword arguments forwarded to SimulationRun.__init__(), e.g. outlet_deficit_threshold_f=5.0, outlet_deficit_max_min=10.

Return type:

SimulationRun

simulate_annual(**sim_run_kwargs)[source]

Run a full annual simulation at 10-minute timesteps.

Parameters:

**sim_run_kwargs – Optional keyword arguments forwarded to SimulationRun.__init__().

Return type:

SimulationRun

get_simulation_summary(simulation_run) dict[source]

Return a summary dict from a completed SimulationRun.

Parameters:

simulation_run (SimulationRun)

Returns:

Keys: ‘successful’, ‘total_outage_min’, ‘total_energy_kwh’, ‘peak_demand_kw’, ‘num_steps_recorded’, ‘stopped_early’.

Return type:

dict

get_annual_cost_estimate(simulation_run) dict[source]

Compute annual utility cost from a completed annual SimulationRun.

Parameters:

simulation_run (SimulationRun)

Returns:

Monthly energy breakdown (kWh) by month.

Return type:

dict

get_hw_magnitude() float[source]

Return the total daily DHW usage for the building [gallons at supply temperature].

Return type:

float

plot_simulation(title: str = 'Simulation Results', filepath: str | None = None, include_temperatures: bool = False, return_as_div: bool = False)[source]

Run a 3-day design-day simulation and return a Plotly figure of the results.

Parameters:
  • title (str) – Figure title. Default "Simulation Results".

  • filepath (str, optional) – If provided, write the figure to this path as a self-contained HTML file. The figure is also returned regardless.

  • include_temperatures (bool) – If True, add a right Y axis showing OAT, inlet water temperature, and all tank node temperatures. Default False.

  • return_as_div (bool) – If True, return the figure as an HTML <div> string instead of a plotly.graph_objects.Figure. Default False.

Returns:

A Plotly figure, or an HTML div string when return_as_div=True.

Return type:

plotly.graph_objects.Figure or str

Raises:

ImportError – If plotly is not installed.

plot_sizing_curve(title: str = 'Primary Sizing Curve', filepath: str | None = None, strat_slope: float = 2.8, return_as_div: bool = False, return_with_x_y_points: bool = False)[source]

Return a Plotly figure of the sizing curve for the built system.

For systems without load shifting, produces the primary sizing curve (storage volume vs. heating capacity as a function of daily run hours).

For systems with load shifting (load_shift_schedule was provided), produces the load-shift sizing curve (storage volume vs. coverage percentile), with a slider to explore the capacity/storage trade-off.

The recommended point — corresponding to the max_daily_run_hr or load_shift_percent passed to the engine — is highlighted on the curve at page load.

Parameters:
  • title (str) – Figure title. Default "Primary Sizing Curve".

  • filepath (str, optional) – If provided, write the figure to this path as a self-contained HTML file. The figure is also returned regardless.

  • strat_slope (float) – Temperature gradient [°F / %-height] for stratification factor calculation. Default 2.8.

  • return_as_div (bool) – If True, return the figure as an HTML <div> string instead of a plotly.graph_objects.Figure. Default False.

  • return_with_x_y_points (bool) – If True, return [figure_or_div, x_values, y_values, start_index] instead of just the figure/div, where x_values and y_values are the plot coordinates and start_index is the recommended slider position. Default False.

Returns:

  • plotly.graph_objects.Figure – When both flags are False.

  • str – HTML div string when return_as_div=True and return_with_x_y_points=False.

  • list[figure_or_div, x_values, y_values, start_index] when return_with_x_y_points=True.

Raises:

ImportError – If plotly is not installed.

Module-level functions

ecoengine.interfaces.EcosizerEngine.get_oat_buckets(zip_code: str | int | None = None, zone_id: int | None = None, weather_station: str | None = None) dict[float, int][source]

Return the distribution of daily average outdoor air temperatures across 5°F buckets for a given location’s typical meteorological year.

Exactly one of zip_code, zone_id, or weather_station must be provided to identify the climate zone.

Parameters:
  • zip_code (str | int | None) – A California 5-digit zip code.

  • zone_id (int | None) – A numeric climate zone ID (1-96).

  • weather_station (str | None) – A weather station name as it appears in the lookup table.

Returns:

Mapping of bucket temperature [°F] → number of days per year with a daily average OAT in that 5°F bucket. Only populated buckets are included.

Return type:

dict[float, int]

Examples

>>> get_oat_buckets(zip_code=90210)
{50.0: 46, 55.0: 83, ...}
>>> get_oat_buckets(zone_id=19)
{65.0: 25, ...}
ecoengine.interfaces.EcosizerEngine.get_list_of_models(multi_pass: bool = False, include_residential: bool = True, exclude_models: list[str] | None = None, sgip_models_only: bool = True) list[list[str]][source]

Return available HPWH model codes and display names.

Parameters:
  • multi_pass (bool) – True → return only multi-pass (_MP) models. False → return only single-pass (_SP) models.

  • include_residential (bool) – True → include residential (_R_) models. False → commercial (_C_) models only.

  • exclude_models (list[str] | None) – Model codes to omit from the result. Defaults to no exclusions.

  • sgip_models_only (bool) – True (default) → restrict to models flagged SGIP_avail in the performance-map database.

Returns:

Each element is [model_code, display_name] where model_code is the string accepted by WaterHeater.from_model_name() and display_name is the human-readable label.

Return type:

list[list[str]]

ecoengine.interfaces.EcosizerEngine.get_weather_stations(exclude_zones: list[int] | None = None) list[list][source]

Return all available weather stations and their corresponding climate zone IDs.

Parameters:

exclude_zones (list[int], optional) – Climate zone IDs to omit from the result. Defaults to [96].

Returns:

Each element is [weather_station_name, climate_zone_id] where weather_station_name is the string accepted by ClimateZone.from_weather_station() and climate_zone_id is the corresponding integer zone number. Weather data sourced from https://energyplus.net/weather.

Return type:

list[list]

ecoengine.interfaces.EcosizerEngine.get_hpwh_output_capacity(model: str, oat_f: float, inlet_water_temp_f: float, outlet_water_temp_f: float, num_heaters: int = 1, return_as_kw: bool = True, defrost_derate: float = 0.0) float[source]

Return the output capacity of an HPWH model at the given operating conditions.

Parameters:
  • model (str) – Model code string (see get_list_of_models()).

  • oat_f (float) – Outdoor air temperature [°F].

  • inlet_water_temp_f (float) – Incoming cold-water temperature [°F].

  • outlet_water_temp_f (float) – Hot storage outlet temperature [°F].

  • num_heaters (int) – Number of HPWH units. Output is multiplied by this. Default 1.

  • return_as_kw (bool) – If True (default), return output in kW. If False, return in kBTU/hr.

  • defrost_derate (float) – Fractional capacity reduction due to defrost [0.0–1.0]. Default 0.0.

Returns:

Output capacity after defrost derate in the requested unit.

Return type:

float

Raises:

ValueError – If defrost_derate is outside [0, 1] or num_heaters < 1.

ecoengine.interfaces.EcosizerEngine.get_sizing_curve_plot(x: list, y: list, start_index: int, load_shifting: bool = False, er_sized: bool = False, return_as_div: bool = False)[source]

Build a Plotly sizing-curve figure from pre-computed x/y points.

This is a lightweight alternative to EcosizerEngine.plot_sizing_curve() for callers that already have the curve data (e.g. from EcosizerEngine.plot_sizing_curve(return_with_x_y_points=True)).

Parameters:
  • x (list) –

    X-axis values. Interpretation depends on the mode:

    • Normal sizing — storage volume [gal at storage temperature]

    • Load-shifting — load-shift coverage percentile [%]

    • ER sizing — percent of building covered [%]

  • y (list) –

    Y-axis values. Interpretation depends on the mode:

    • Normal sizing — heating capacity [kBTU/hr]

    • Load-shifting — storage volume [gal at storage temperature]

    • ER sizing — ER element capacity [kW]

  • start_index (int) – Index into x/y where the recommended-size diamond starts.

  • load_shifting (bool) – If True, use load-shift axis labels. Default False.

  • er_sized (bool) – If True, use ER-sizing axis labels (overrides load_shifting). Default False.

  • return_as_div (bool) – If True, return an HTML <div> string instead of a Figure. Default False.

Return type:

plotly.graph_objects.Figure or str

Raises:

ImportError – If plotly is not installed.

ecoengine.interfaces.EcosizerEngine.get_annual_utility_comparison_graph(sim_run_hp, sim_run_iwh, utility_cost_tracker, return_as_div: bool = False, return_as_array: bool = False)[source]

Build a stacked bar chart comparing monthly utility costs for an HPWH system vs. an instantaneous electric resistance (UER) baseline.

Each month shows two adjacent bars — one for the HP system, one for UER — stacked by charge type: base, energy, and demand.

Parameters:
  • sim_run_hp (SimulationRun) – Annual simulation result for the heat pump system.

  • sim_run_iwh (SimulationRun) – Annual simulation result for the instantaneous/UER baseline.

  • utility_cost_tracker (UtilityCostTracker) – Rate structure applied to both runs.

  • return_as_div (bool) – If True, return an HTML <div> string instead of a Figure.

  • return_as_array (bool) – If True, return (hp_breakdown, iwh_breakdown) dicts instead of a figure. Each dict has keys 'energy' (12 floats), 'demand' (12 floats), and 'base' (float).

Return type:

plotly.graph_objects.Figure, str, or tuple