Skip to content

Degree Days

calc_daily_dbs(hpxml)

Calculate daily average dry bulb temperatures from EPW weather data.

This function computes daily average dry bulb temperatures in both Celsius and Fahrenheit from the EPW weather data contained in the provided HPXML document.

Parameters:

Name Type Description Default
hpxml HpxmlDoc

HPXML document object containing weather data.

required

Returns:

Type Description
namedtuple

Named tuple with fields 'c' (Celsius) and 'f' (Fahrenheit), each as a pandas Series.

Source code in src/openstudio_hpxml_calibration/weather_normalization/degree_days.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def calc_daily_dbs(hpxml: HpxmlDoc) -> namedtuple:
    """
    Calculate daily average dry bulb temperatures from EPW weather data.

    This function computes daily average dry bulb temperatures in both Celsius and Fahrenheit
    from the EPW weather data contained in the provided HPXML document.

    :param hpxml: HPXML document object containing weather data.
    :type hpxml: HpxmlDoc
    :return: Named tuple with fields 'c' (Celsius) and 'f' (Fahrenheit), each as a pandas Series.
    :rtype: namedtuple
    """
    DailyTemps = namedtuple("DailyTemps", ["c", "f"])
    epw, _ = hpxml.get_epw_data(coerce_year=2007)
    epw_daily_avg_temp_c = epw["temp_air"].groupby(pd.Grouper(freq="D")).mean()
    epw_daily_avg_temp_f = convert_units(epw_daily_avg_temp_c, "c", "f")
    return DailyTemps(c=epw_daily_avg_temp_c, f=epw_daily_avg_temp_f)

calc_degree_days(daily_dbs, base_temp_f, is_heating)

Calculate degree days from daily temperature data.

This function computes the total heating or cooling degree days for a given base temperature using daily average dry bulb temperatures.

Parameters:

Name Type Description Default
daily_dbs Series

Series of daily average dry bulb temperatures (°F).

required
base_temp_f float

Base temperature in Fahrenheit for degree day calculation.

required
is_heating bool

If True, calculates heating degree days; if False, cooling degree days.

required

Returns:

Type Description
float

Total degree days for the specified base temperature and mode.

Source code in src/openstudio_hpxml_calibration/weather_normalization/degree_days.py
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
def calc_degree_days(daily_dbs: pd.Series, base_temp_f: float, is_heating: bool) -> float:
    """
    Calculate degree days from daily temperature data.

    This function computes the total heating or cooling degree days for a given base temperature
    using daily average dry bulb temperatures.

    :param daily_dbs: Series of daily average dry bulb temperatures (°F).
    :type daily_dbs: pd.Series
    :param base_temp_f: Base temperature in Fahrenheit for degree day calculation.
    :type base_temp_f: float
    :param is_heating: If True, calculates heating degree days; if False, cooling degree days.
    :type is_heating: bool
    :return: Total degree days for the specified base temperature and mode.
    :rtype: float
    """

    deg_days = []
    for temp in daily_dbs:
        if is_heating and temp < base_temp_f:
            deg_days.append(base_temp_f - temp)
        elif not is_heating and temp > base_temp_f:
            deg_days.append(temp - base_temp_f)

    if len(deg_days) == 0:
        return 0.0

    deg_days_sum = round(sum(deg_days), 2)
    return deg_days_sum

calc_heat_cool_degree_days(dailydbs)

Calculate heating and cooling degree days from daily temperature data.

This function returns a dictionary containing heating and cooling degree days (HDD65F, CDD65F) for the provided daily average dry bulb temperatures.

Parameters:

Name Type Description Default
dailydbs Series

Series of daily average dry bulb temperatures (°F).

required

Returns:

Type Description
dict

Dictionary with keys 'HDD65F' and 'CDD65F' and their respective degree day values.

Source code in src/openstudio_hpxml_calibration/weather_normalization/degree_days.py
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
def calc_heat_cool_degree_days(dailydbs: pd.Series) -> dict:
    """
    Calculate heating and cooling degree days from daily temperature data.

    This function returns a dictionary containing heating and cooling degree days (HDD65F, CDD65F)
    for the provided daily average dry bulb temperatures.

    :param dailydbs: Series of daily average dry bulb temperatures (°F).
    :type dailydbs: pd.Series
    :return: Dictionary with keys 'HDD65F' and 'CDD65F' and their respective degree day values.
    :rtype: dict
    """
    degree_days = {}
    degree_days["HDD65F"] = calc_degree_days(dailydbs, 65, True)
    # degree_days["HDD50F"] = calc_degree_days(dailydbs, 50, True)
    degree_days["CDD65F"] = calc_degree_days(dailydbs, 65, False)
    # degree_days["CDD50F"] = calc_degree_days(dailydbs, 50, False)
    return degree_days

calculate_annual_degree_days(hpxml)

Calculate annual heating and cooling degree days for each fuel type.

This function computes the total heating degree days (HDD) and cooling degree days (CDD) for the actual period and the TMY period, for each fuel type present in the HPXML document.

Parameters:

Name Type Description Default
hpxml HpxmlDoc

HPXML document object containing weather and fuel information.

required

Returns:

Type Description
tuple[dict, dict]

Tuple containing dictionaries of total period TMY degree days and actual degree days by fuel type.

Source code in src/openstudio_hpxml_calibration/weather_normalization/degree_days.py
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
def calculate_annual_degree_days(hpxml: HpxmlDoc) -> tuple[dict, dict]:
    """
    Calculate annual heating and cooling degree days for each fuel type.

    This function computes the total heating degree days (HDD) and cooling degree days (CDD)
    for the actual period and the TMY period, for each fuel type present in the HPXML document.

    :param hpxml: HPXML document object containing weather and fuel information.
    :type hpxml: HpxmlDoc
    :return: Tuple containing dictionaries of total period TMY degree days and actual degree days by fuel type.
    :rtype: tuple[dict, dict]
    """
    tmy_dry_bulb_temps_f = calc_daily_dbs(hpxml).f
    bills_by_fuel_type, _, _ = ud.get_bills_from_hpxml(hpxml)
    lat, lon = hpxml.get_lat_lon()
    bill_tmy_degree_days = {}
    total_period_actual_dd = {}

    # Use day-of-year because TMY data contains multiple years
    tmy_temp_index_doy = tmy_dry_bulb_temps_f.index.dayofyear

    for fuel_type, bills in bills_by_fuel_type.items():
        # format fuel type for dictionary keys
        fuel_type_name = fuel_type.name.lower().replace("_", " ")
        # Get degree days of actual weather during bill periods
        _, actual_temp_f = ud.join_bills_weather(bills, lat, lon)
        daily_actual_temps = actual_temp_f.resample("D").mean()
        actual_degree_days = calc_heat_cool_degree_days(daily_actual_temps)
        actual_degree_days = {k: round(v) for k, v in actual_degree_days.items()}
        total_period_actual_dd[fuel_type_name] = actual_degree_days

        # Get degree days of TMY weather
        bill_results = []
        for _, row in bills.iterrows():
            start_doy = row["start_day_of_year"]
            end_doy = row["end_day_of_year"]

            # Handle bills that wrap around the end of the year
            if start_doy <= end_doy:
                mask = (tmy_temp_index_doy >= start_doy) & (tmy_temp_index_doy <= end_doy)
            else:
                mask = (tmy_temp_index_doy >= start_doy) | (tmy_temp_index_doy <= end_doy)

            # Select the dry bulb temperatures for the bill period
            bill_dry_bulbs_tmy = tmy_dry_bulb_temps_f[mask]
            tmy_degree_days = calc_heat_cool_degree_days(bill_dry_bulbs_tmy)
            bill_results.append(
                {
                    "start_date": row["start_date"],
                    "end_date": row["end_date"],
                    **tmy_degree_days,
                }
            )
        bill_tmy_degree_days[fuel_type_name] = bill_results

    total_period_tmy_dd = {}
    for fuel, bill_list in bill_tmy_degree_days.items():
        hdd_total = round(sum(bill.get("HDD65F", 0) for bill in bill_list))
        cdd_total = round(sum(bill.get("CDD65F", 0) for bill in bill_list))
        total_period_tmy_dd[fuel] = {"HDD65F": hdd_total, "CDD65F": cdd_total}

    return total_period_tmy_dd, total_period_actual_dd