Building & Climate

Building

class ecoengine.objects.building.Building.Building(climate_zone: ClimateZone | None, daily_dhw_use_supplyT_gal: float, peak_load_shape: list[float] | np.ndarray, avg_load_shape: list[float] | np.ndarray, utility_cost_tracker: UtilityCostTracker | None = None, building_type: str = '', design_oat_f: float | None = None, design_inlet_water_temp_f: float | None = None)[source]

Bases: object

Stores information about the building and surrounding environment where a DHW system will be sized and/or simulated.

Holds a ClimateZone (ambient conditions), a UtilityCostTracker (billing structure), and the building’s DHW load data (daily use and load shapes).

__init__(climate_zone: ClimateZone | None, daily_dhw_use_supplyT_gal: float, peak_load_shape: list[float] | np.ndarray, avg_load_shape: list[float] | np.ndarray, utility_cost_tracker: UtilityCostTracker | None = None, building_type: str = '', design_oat_f: float | None = None, design_inlet_water_temp_f: float | None = None) None[source]
Parameters:
  • climate_zone (ClimateZone | None) – Ambient OAT and cold water temperature data for this location. If None and design conditions are provided, a constant-value ClimateZone is created automatically from those conditions.

  • daily_dhw_use_supplyT_gal (float) – Design daily hot water consumption at supply temperature [gallons].

  • peak_load_shape (list[float] | np.ndarray) – Normalized 24-hour load profile for the design (peaky) day. Each value is the fraction of daily DHW use in that hour.

  • avg_load_shape (list[float] | np.ndarray) – Normalized 24-hour load profile for an average day.

  • utility_cost_tracker (UtilityCostTracker | None)

  • building_type (str) – Label for the building type (e.g. ‘multi_family’, ‘multi_use’).

  • design_oat_f (float | None) – Design outdoor air temperature [°F]. Used only when climate_zone is None; creates a constant-OAT ClimateZone that returns this value for every timestep and as the design condition.

  • design_inlet_water_temp_f (float | None) – Design cold-water inlet temperature [°F]. Used only when climate_zone is None; creates a constant-inlet-temp ClimateZone that returns this value for every timestep and as the design condition.

classmethod from_building_type(building_type: str | None | list[str | None], magnitude: float | None | list, climate_zone: ClimateZone | None, gpdpp: float = 25, standard_gpd: str | None = None, n_br: list[int] | None = None, custom_peak_load_shape: list[float] | list[list[float] | None] | None = None, custom_avg_load_shape: list[float] | list[list[float] | None] | None = None, annual: bool = False, **kwargs) Building[source]

Construct a Building from a named building type (or a list of types for multi-use buildings).

Parameters:
  • building_type (str | None | list[str | None]) – Named type(s): ‘multi_family’, ‘apartment’, ‘motel’, ‘office_building’, ‘mens_dorm’, ‘womens_dorm’, ‘nursing_home’, ‘food_service_a’, ‘food_service_b’, ‘elementary_school’, ‘junior_high’, ‘senior_high’. Use None for a custom-load-shape-only building (requires custom_peak_load_shape). Pass a list for a multi-use building.

  • magnitude (float | None | list) – Occupancy metric matching the building type(s). For multi-use, pass a list of the same length as building_type. An individual entry may be None only when building_type is also None for that slot (magnitude is then derived from the sum of the unnormalized custom load shape).

  • climate_zone (ClimateZone | None)

  • gpdpp (float) – Gallons per person per day. Used for ‘multi_family’ when standard_gpd is None.

  • standard_gpd (str | None) – Standard GPD key for ‘multi_family’: ‘ashLow’, ‘ashMed’, ‘ecoMark’, or ‘ca’. Overrides gpdpp when provided.

  • n_br (list[int] | None) – Unit counts by bedroom size [0BR-5BR]. Required for standard_gpd=’ca’.

  • custom_peak_load_shape (list[float] | list[list[float] | None] | None) – 24-element normalized peak load shape override. For multi-use, pass a list of shapes (or None entries to use the default for that type).

  • custom_avg_load_shape (list[float] | list[list[float] | None] | None) – Same as custom_peak_load_shape but for the average day.

  • annual (bool) – If True, load the 8760-hour annual load shape instead of the 24-hour daily shape. Only supported for multi_family buildings.

  • **kwargs – Forwarded to Building.__init__ (e.g. design_oat_f, utility_cost_tracker).

Return type:

Building

Raises:

ValueError

is_annual_load_shape() bool[source]

Return True if the building is currently using the 8760-hour annual load shape.

set_to_annual_load_shape() None[source]

Switch to the full 8760-hour annual load shape.

Only supported for multi_family buildings, which have an ‘Annual_Normalized’ profile in their load shape JSON. The annual shape serves as both peak and avg (there is no separate peaky day for an annual simulation).

Raises:

ValueError – If called on a non-multi_family building type.

set_to_daily_load_shape() None[source]

Switch back to the 24-hour daily load shapes (Stream / Stream_Avg).

Only supported for multi_family buildings.

Raises:

ValueError – If called on a non-multi_family building type.

get_dhw_load_supplyT_gal(timestep_interval: int, interval_min: int = 1, use_avg: bool = False) float[source]

Return the DHW load in gallons at supply temperature for one timestep.

The load shape describes what fraction of the daily total falls in each hour. The returned value is scaled down to the interval duration so that summing over all intervals in a day always equals daily_dhw_use_supplyT_gal.

Parameters:
  • timestep_interval (int) – Number of intervals elapsed from the start of the simulation day/year.

  • interval_min (int) – Length of each interval in minutes. Defaults to 1 (minute-resolution).

  • use_avg (bool) – If True, use avg_load_shape; otherwise use peak_load_shape.

Returns:

Gallons of DHW load at supply temperature for this interval.

Return type:

float

get_oat_f(timestep_interval: int, interval_min: int = 1) float[source]

Return outdoor air temperature at the given timestep via ClimateZone.

Parameters:
  • timestep_interval (int) – Number of intervals elapsed from the start of the simulation.

  • interval_min (int) – Length of each interval in minutes. Defaults to 1 (minute-resolution). Example: timestep_interval=3, interval_min=15 → minute 45.

Returns:

Outdoor air temperature [°F].

Return type:

float

get_inlet_water_temp_f(timestep_interval: int, interval_min: int = 1) float[source]

Return cold/inlet water temperature at the given timestep via ClimateZone.

Parameters:
  • timestep_interval (int) – Number of intervals elapsed from the start of the simulation.

  • interval_min (int) – Length of each interval in minutes. Defaults to 1 (minute-resolution). Example: timestep_interval=3, interval_min=15 → minute 45.

Returns:

Inlet water temperature [°F].

Return type:

float

get_design_oat_f() float | None[source]

Return the design-day outdoor air temperature [°F].

Delegates to ClimateZone.get_design_oat_f(). Returns None if no climate zone (real or design-condition) was provided at construction.

get_design_inlet_water_temp_f() float | None[source]

Return the design-day cold/inlet water temperature [°F].

Delegates to ClimateZone.get_design_inlet_water_temp_f(). Returns None if no climate zone was provided at construction.

ClimateZone

class ecoengine.objects.building.ClimateZone.ClimateZone(zone_id: int | None, oat_f_by_hour: list[float] | None, inlet_water_temp_f_by_month: list[float] | None, constant_oat_f: float | None = None, constant_inlet_water_temp_f: float | None = None)[source]

Bases: object

Stores outdoor air temperature (OAT) and cold/inlet water temperature for a building’s location over a typical meteorological year.

OAT is stored hourly (8,760 values — one per hour of the year). Inlet water temp is stored monthly (12 values — one per month).

The simulator queries this object at every timestep to obtain ambient conditions for heat-pump performance and load calculations.

__init__(zone_id: int | None, oat_f_by_hour: list[float] | None, inlet_water_temp_f_by_month: list[float] | None, constant_oat_f: float | None = None, constant_inlet_water_temp_f: float | None = None) None[source]
Parameters:
  • zone_id (int | None) – Numeric climate zone identifier (1-96 for OAT data; 1-19 for full CA data including kG/kWh). None for design-condition zones.

  • oat_f_by_hour (list[float] | None) – Outdoor air temperatures for every hour of a typical year [°F]. 8,760 elements for real zones; None for design-condition zones.

  • inlet_water_temp_f_by_month (list[float] | None) – Average cold-water inlet temperature for each calendar month [°F]. 12 elements for real zones; None for design-condition zones.

  • constant_oat_f (float | None) – If set, all OAT queries return this value instead of reading from oat_f_by_hour. Set automatically by from_design_conditions().

  • constant_inlet_water_temp_f (float | None) – If set, all inlet water temp queries return this value instead of reading from inlet_water_temp_f_by_month. Set automatically by from_design_conditions().

classmethod from_zip_code(zip_code: str | int) ClimateZone[source]

Construct a ClimateZone by looking up the CA climate zone for a zip code and loading the corresponding weather data.

Parameters:

zip_code (str | int) – A California 5-digit zip code.

Return type:

ClimateZone

Raises:

ValueError – If the zip code is not found in the CA lookup table.

classmethod from_weather_station(station_id: str) ClimateZone[source]

Construct a ClimateZone from a named weather station.

Parameters:

station_id (str) – Weather station name exactly as it appears in the lookup CSV (e.g. 'ID - Boise Air Terminal').

Return type:

ClimateZone

Raises:

ValueError – If the station name is not found in the lookup table.

classmethod from_zone_id(zone_id: int) ClimateZone[source]

Construct a ClimateZone directly from a numeric climate zone ID.

Parameters:

zone_id (int) – Climate zone number (1-96 for OAT data; 1-19 for full CA data).

Return type:

ClimateZone

classmethod from_design_conditions(design_oat_f: float | None = None, design_inlet_water_temp_f: float | None = None) ClimateZone[source]

Create a constant-condition ClimateZone for use when real climate data is unavailable.

Every OAT query returns design_oat_f; every inlet water temp query returns design_inlet_water_temp_f. The design values are also returned by get_design_oat_f() and get_design_inlet_water_temp_f() directly.

Parameters:
  • design_oat_f (float | None) – Constant outdoor air temperature to return for all timesteps [°F].

  • design_inlet_water_temp_f (float | None) – Constant cold-water inlet temperature to return for all timesteps [°F].

Return type:

ClimateZone

get_oat_f(timestep_interval: int, interval_min: int = 1) float[source]

Return outdoor air temperature at the given simulation timestep.

OAT data is stored hourly, so this rounds down to the nearest hour. Wraps around yearly (safe for multi-year or 3-day simulations that start mid-year).

Parameters:
  • timestep_interval (int) – Number of intervals elapsed from the start of the simulation.

  • interval_min (int) – Length of each interval in minutes. Defaults to 1 (minute-resolution). Example: timestep_interval=3, interval_min=15 → minute 45.

Returns:

Outdoor air temperature [°F].

Return type:

float

get_inlet_water_temp_f(timestep_interval: int, interval_min: int = 1) float[source]

Return cold/inlet water temperature at the given simulation timestep.

Inlet water temperature is stored as a monthly average, so the timestep is converted to a day-of-year and then to a month.

Parameters:
  • timestep_interval (int) – Number of intervals elapsed from the start of the simulation.

  • interval_min (int) – Length of each interval in minutes. Defaults to 1 (minute-resolution). Example: timestep_interval=3, interval_min=15 → minute 45.

Returns:

Inlet water temperature [°F].

Return type:

float

get_design_oat_f() float[source]

Return the design-day outdoor air temperature [°F].

For real climate zones: the annual minimum OAT (worst-case heating condition for heat-pump sizing). For design-condition zones: the constant value set at construction.

get_design_inlet_water_temp_f() float[source]

Return the design-day cold water inlet temperature [°F].

For real climate zones: the coldest monthly average (worst-case sizing condition). For design-condition zones: the constant value set at construction.

get_oat_buckets() dict[float, int][source]

Return the distribution of daily average outdoor air temperatures across 5°F buckets for the full typical meteorological year.

For each of the 365 days in the year the 24 hourly OAT values are averaged to produce a daily mean, which is then assigned to the nearest lower multiple of 5°F (e.g. 62.4°F → 60.0°F bucket).

Returns:

Mapping of bucket temperature [°F] → number of days in that bucket. Only buckets with at least one day are included.

Return type:

dict[float, int]

Raises:

ValueError – If this ClimateZone was created with from_design_conditions() and has no real hourly OAT data.

UtilityCostTracker

class ecoengine.objects.building.UtilityCostTracker.UtilityCostTracker(demand_period_chart: list[int], demand_charge_map: dict[int, float], energy_charge_map: dict[int, float], is_peak_map: dict[int, bool], is_discount_map: dict[int, bool], monthly_base_charge: float, energy_charge_by_hour: list[float] | None = None, include_discount: bool = False)[source]

Bases: object

Stores a utility billing structure and provides per-timestep rate lookups.

Supports monthly base charges, peak/off-peak demand charges ($/kW), and peak/off-peak energy charges ($/kWh), with optional seasonal variation and an optional third discount period (e.g. overnight super-off-peak tariffs).

Period key scheme

Without discount (num_periods = 2):

month × 2 → off-peak month × 2 + 1 → peak

With discount (num_periods = 3):

month × 3 → off-peak month × 3 + 1 → peak month × 3 + 2 → discount

Construction

Use the factory classmethods rather than calling __init__ directly:

uc = UtilityCostTracker.from_params(
    monthly_base_charge=190.0,
    pk_start_hour=16,
    pk_end_hour=21,
    pk_demand_charge=38.75,
    pk_energy_charge=0.21585,
    off_pk_demand_charge=30.20,
    off_pk_energy_charge=0.14341,
)

uc = UtilityCostTracker.from_csv("utility_rates.csv")
__init__(demand_period_chart: list[int], demand_charge_map: dict[int, float], energy_charge_map: dict[int, float], is_peak_map: dict[int, bool], is_discount_map: dict[int, bool], monthly_base_charge: float, energy_charge_by_hour: list[float] | None = None, include_discount: bool = False) None[source]
classmethod from_params(monthly_base_charge: float, pk_start_hour, pk_end_hour, pk_demand_charge, pk_energy_charge, off_pk_demand_charge, off_pk_energy_charge, start_month=0, end_month=12, include_discount: bool = False, dscnt_start_hour=None, dscnt_end_hour=None, discnt_demand_charge=None, discnt_energy_charge=None) UtilityCostTracker[source]

Build a UtilityCostTracker from rate parameters.

All rate/hour/month parameters may be scalars (uniform year-round rate) or lists (seasonal variation). When lists are supplied they must all have the same length n, where each element describes one contiguous block of months. start_month and end_month must partition [0, 12] without gaps or overlaps: start_month[0] must be 0, end_month[-1] must be 12, and each start_month[i] must equal end_month[i-1].

Parameters:
  • monthly_base_charge (float) – Fixed monthly connection charge [$].

  • pk_start_hour (int or list[int]) – Hour of day (0–23) when the peak period begins.

  • pk_end_hour (int or list[int]) – Hour of day (pk_start_hour–24) when the peak period ends. pk_end_hour == pk_start_hour → no peak period. pk_start_hour == 0, pk_end_hour == 24 → all-peak day.

  • pk_demand_charge (float or list[float]) – Peak demand charge [$/kW].

  • pk_energy_charge (float or list[float]) – Peak energy rate [$/kWh].

  • off_pk_demand_charge (float or list[float]) – Off-peak demand charge [$/kW].

  • off_pk_energy_charge (float or list[float]) – Off-peak energy rate [$/kWh].

  • start_month (int or list[int]) – 0-based month index where each rate block begins. Default 0.

  • end_month (int or list[int]) – 0-based month index where each rate block ends (exclusive). Default 12.

  • include_discount (bool) – If True, a third discount period is added within each month.

  • dscnt_start_hour (int or list[int] or None) – Discount period start hour.

  • dscnt_end_hour (int or list[int] or None) – Discount period end hour.

  • discnt_demand_charge (float or list[float] or None) – Discount demand charge [$/kW].

  • discnt_energy_charge (float or list[float] or None) – Discount energy rate [$/kWh].

Return type:

UtilityCostTracker

Raises:

ValueError – If any parameter fails validation.

classmethod from_csv(csv_path: str | None = None, csv_file: TextIOWrapper | None = None) UtilityCostTracker[source]

Build a UtilityCostTracker from an 8760-row CSV file.

The CSV must have a header row with (at minimum) these columns: Energy Rate ($/kWh), Demand Rate ($/kW), Demand Period, Monthly Base Charge.

Monthly Base Charge is read from the first data row only. Demand and energy rates are read from the first row in which each Demand Period value appears; subsequent rows may leave those columns blank. Demand periods with odd labels are treated as peak; even labels as off-peak.

Parameters:
  • csv_path (str or None)

  • csv_file (TextIOWrapper or None)

Return type:

UtilityCostTracker

Raises:

ValueError – On missing columns, wrong row count, or bad cell values.

get_energy_charge_at_step(step: int, timestep_min: int) float[source]

Return the energy rate [$/kWh] for simulation step step.

Parameters:
  • step (int) – 0-based step index from the simulation.

  • timestep_min (int) – Simulation timestep size [minutes] (e.g. 1, 10, or 60).

Return type:

float

get_demand_period_at_step(step: int, timestep_min: int) int[source]

Return the demand period key for simulation step step.

Parameters:
  • step (int)

  • timestep_min (int)

Return type:

int

get_demand_charge_for_period(period_key: int, max_kw: float) float[source]

Return the demand charge [$] for period_key given max_kw.

Parameters:
  • period_key (int)

  • max_kw (float) – Peak power draw observed during the period [kW].

Return type:

float

Raises:

ValueError – If period_key is not a defined period.

get_all_demand_period_keys() list[int][source]

Return all defined demand period keys (one per month×period type).

get_yearly_base_charge() float[source]

Return the annual base charge [$] (monthly_base_charge × 12).

to_csv(csv_path: str) None[source]

Export the rate structure to a CSV file readable by from_csv.

Produces 8760 data rows (one per hour of a non-leap year) plus a header. Energy and demand rates are written on the first occurrence of each demand period; subsequent rows leave those fields blank. The monthly base charge appears only in the first data row.

Parameters:

csv_path (str) – Destination file path.