Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calculate boiler usage #157

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 32 additions & 24 deletions rules-engine/src/rules_engine/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,8 @@ def get_outputs_normalized(
home = Home(
summary_input=summary_input,
billing_periods=intermediate_billing_periods,
dhw_input=dhw_input,
initial_balance_point=initial_balance_point,
has_boiler_for_dhw=dhw_input is not None,
same_fuel_dhw_heating=dhw_input is not None,
)
home.calculate()

Expand Down Expand Up @@ -277,6 +276,30 @@ def get_maximum_heat_load(
return (design_set_point - design_temp) * ua


def calculate_dhw_usage(dhw_input: DhwInput, heating_system_efficiency: float) -> float:
"""
Calculate non-heating usage with oil or propane
"""
if dhw_input.estimated_water_heating_efficiency is not None:
heating_system_efficiency = dhw_input.estimated_water_heating_efficiency

stand_by_losses = Constants.DEFAULT_STAND_BY_LOSSES
if dhw_input.stand_by_losses is not None:
stand_by_losses = dhw_input.stand_by_losses

daily_fuel_oil_use_for_dhw = (
dhw_input.number_of_occupants
* Constants.DAILY_DHW_CONSUMPTION_PER_OCCUPANT
* Constants.WATER_WEIGHT
* (Constants.LEAVING_WATER_TEMPERATURE - Constants.ENTERING_WATER_TEMPERATURE)
* Constants.SPECIFIC_HEAT_OF_WATER
/ Constants.FUEL_OIL_BTU_PER_GAL
/ (heating_system_efficiency * (1 - stand_by_losses))
)

return daily_fuel_oil_use_for_dhw


class Home:
"""
Defines attributes and methods for calculating home heat metrics.
Expand All @@ -291,16 +314,14 @@ def __init__(
self,
summary_input: SummaryInput,
billing_periods: List[BillingPeriod],
dhw_input: Optional[DhwInput],
initial_balance_point: float = 60,
has_boiler_for_dhw: bool = False,
same_fuel_dhw_heating: bool = False,
):
self.fuel_type = summary_input.fuel_type
self.heat_sys_efficiency = summary_input.heating_system_efficiency
self.thermostat_set_point = summary_input.thermostat_set_point
self.balance_point = initial_balance_point
self.has_boiler_for_dhw = has_boiler_for_dhw
self.same_fuel_dhw_heating = same_fuel_dhw_heating
self.dhw_input = dhw_input
self._initialize_billing_periods(billing_periods)

def _initialize_billing_periods(self, billing_periods: List[BillingPeriod]) -> None:
Expand Down Expand Up @@ -335,31 +356,18 @@ def _calculate_avg_summer_usage(self) -> None:
else:
self.avg_summer_usage = 0

def _calculate_boiler_usage(self, fuel_multiplier: float) -> float:
"""
Calculate boiler usage with oil or propane
Args:
fuel_multiplier: a constant that's determined by the fuel
type
"""

# self.num_occupants: the number of occupants in Home
# self.water_heat_efficiency: a number indicating how efficient the heating system is

return 0 * fuel_multiplier

def _calculate_avg_non_heating_usage(self) -> None:
"""
Calculate avg non heating usage for this home
"""

if self.fuel_type == FuelType.GAS:
self.avg_non_heating_usage = self.avg_summer_usage
elif self.has_boiler_for_dhw and self.same_fuel_dhw_heating:
fuel_multiplier = 1 # default multiplier, for oil, placeholder number
if self.fuel_type == FuelType.PROPANE:
fuel_multiplier = 2 # a placeholder number
self.avg_non_heating_usage = self._calculate_boiler_usage(fuel_multiplier)
elif self.dhw_input is not None and self.fuel_type == FuelType.OIL:
# TODO: support non-heating usage for Propane in addition to fuel oil
self.avg_non_heating_usage = calculate_dhw_usage(
self.dhw_input, self.heat_sys_efficiency
)
else:
self.avg_non_heating_usage = 0

Expand Down
15 changes: 11 additions & 4 deletions rules-engine/src/rules_engine/pydantic_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ class DhwInput(BaseModel):
"""From DHW (Domestic Hot Water) Tab"""

number_of_occupants: int = Field(description="DHW!B4")
estimated_water_heating_efficiency: float = Field(description="DHW!B5")
stand_by_losses: float = Field(description="DHW!B6")
estimated_water_heating_efficiency: Optional[float] = Field(description="DHW!B5")
stand_by_losses: Optional[float] = Field(description="DHW!B6")


class OilPropaneBillingRecordInput(BaseModel):
Expand Down Expand Up @@ -107,7 +107,7 @@ class SummaryOutput(BaseModel):
estimated_balance_point: float = Field(
description="Summary!B20"
) # This is hand-calculated in the spreadsheet
other_fuel_usage: float = Field(description="Summary!B15")
other_fuel_usage: Optional[float] = Field(description="Summary!B15")
average_indoor_temperature: float = Field(description="Summary!B24")
difference_between_ti_and_tbp: float = Field(description="Summary!B25")
design_temperature: float = Field(description="Summary!B26")
Expand Down Expand Up @@ -144,4 +144,11 @@ class BalancePointGraph(BaseModel):
@dataclass
class Constants:
BALANCE_POINT_SENSITIVITY: float = 0.5
DESIGN_SET_POINT: float = 70
DESIGN_SET_POINT: float = 70 # deg. F
DAILY_DHW_CONSUMPTION_PER_OCCUPANT: float = 15.78 # Gal/day/person
WATER_WEIGHT: float = 8.33 # lbs/gal
ENTERING_WATER_TEMPERATURE: float = 55 # deg. F
LEAVING_WATER_TEMPERATURE: float = 125 # deg. F
SPECIFIC_HEAT_OF_WATER: float = 1.00 # BTU/lbs-deg. F
DEFAULT_STAND_BY_LOSSES: float = 0.05 #
FUEL_OIL_BTU_PER_GAL: float = 139000
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
date,gallons,inclusion_override
10/26/2020,108.3,
12/18/2020,126.6,
1/30/2021,184,
3/9/2021,164.5,
6/17/2021,122.4,
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
date,gallons,inclusion_override
12/28/2017,220.10,
8/7/2018,316.6,1
12/31/2018,229.0,
7/12/2019,299.6,1
1/3/2020,238.5,
9/17/2020,265.0,1
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"local_weather_station": "KBED-Bedford",
"design_temperature_override": null,
"living_area": 1600,
"fuel_type": "PROPANE",
"heating_system_efficiency": 0.93,
"other_fuel_usage": null,
"other_fuel_usage_override": 0.10,
"thermostat_set_point": 68.0,
"setback_temperature": null,
"setback_hours_per_day": null,
"estimated_balance_point": 58.0,
"balance_point_sensitivity": 2.0,
"average_indoor_temperature": 68.0,
"difference_between_ti_and_tbp": 10.0,
"design_temperature": 8.4,
"whole_home_heat_loss_rate": 356,
"standard_deviation_of_heat_loss_rate": 0.0037,
"average_heat_load": 18370,
"maximum_heat_load": 21931
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
date,gallons,inclusion_override
1/6/2019,83.90,
1/23/2019,115.0,
2/6/2019,86.0,
2/20/2019,68.6,0
3/13/2019,119.2,
4/13/2019,68.5,
7/11/2019,69.3,
11/5/2019,158.1,
12/2/2019,120.4,
12/26/2019,124.0,
1/23/2020,131.6,
2/23/2020,184.9,0
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"local_weather_station": "KBED-Bedford",
"design_temperature_override": null,
"living_area": 2765,
"fuel_type": "OIL",
"heating_system_efficiency": 0.78,
"other_fuel_usage": null,
"other_fuel_usage_override": 0.10,
"thermostat_set_point": 68.0,
"setback_temperature": null,
"setback_hours_per_day": null,
"estimated_balance_point": 58.0,
"balance_point_sensitivity": 2.0,
"average_indoor_temperature": 68.0,
"difference_between_ti_and_tbp": 10.0,
"design_temperature": 8.4,
"whole_home_heat_loss_rate": 900,
"standard_deviation_of_heat_loss_rate": 0.0418,
"average_heat_load": 46453,
"maximum_heat_load": 55455
}
61 changes: 61 additions & 0 deletions rules-engine/tests/test_rules_engine/test_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ def test_bp_ua_estimates(sample_summary_inputs, sample_billing_periods):
home = engine.Home(
sample_summary_inputs,
sample_billing_periods,
dhw_input=None,
initial_balance_point=58,
)

Expand All @@ -229,6 +230,7 @@ def test_bp_ua_with_outlier(sample_summary_inputs, sample_billing_periods_with_o
home = engine.Home(
sample_summary_inputs,
sample_billing_periods_with_outlier,
dhw_input=None,
initial_balance_point=58,
)

Expand Down Expand Up @@ -284,3 +286,62 @@ def test_get_outputs_normalized(
assert summary_output.standard_deviation_of_heat_loss_rate == approx(
0.0463, abs=0.01
)


@pytest.mark.parametrize(
"sample_dhw_inputs, summary_input_heating_system_efficiency, expected_fuel_oil_usage",
[
(
DhwInput(
number_of_occupants=2,
estimated_water_heating_efficiency=None,
stand_by_losses=None,
),
0.80,
0.17,
),
(
DhwInput(
number_of_occupants=2,
estimated_water_heating_efficiency=0.8,
stand_by_losses=None,
),
0.85,
0.17,
),
(
DhwInput(
number_of_occupants=4,
estimated_water_heating_efficiency=0.8,
stand_by_losses=None,
),
0.84,
0.35,
),
(
DhwInput(
number_of_occupants=5,
estimated_water_heating_efficiency=0.8,
stand_by_losses=None,
),
0.83,
0.43,
),
(
DhwInput(
number_of_occupants=5,
estimated_water_heating_efficiency=0.8,
stand_by_losses=0.10,
),
0.82,
0.46,
),
],
)
def test_calculate_dhw_usage(
sample_dhw_inputs, summary_input_heating_system_efficiency, expected_fuel_oil_usage
):
fuel_oil_usage = engine.calculate_dhw_usage(
sample_dhw_inputs, summary_input_heating_system_efficiency
)
assert fuel_oil_usage == approx(expected_fuel_oil_usage, abs=0.01)
Loading
Loading