Skip to content

Commit

Permalink
Calculate boiler usage (#153)
Browse files Browse the repository at this point in the history
* Implemented calculate_boiler_usage for fuel oil

Co-authored-by: thatoldplatitude <[email protected]>
Co-authored-by: dwindleduck <[email protected]>
Co-authored-by: Alan Pinkert <[email protected]>
Co-authored-by: Debajyoti Debnath <[email protected]>
Co-authored-by: David Egan <[email protected]>
Co-authored-by: Chad Stoughton <[email protected]>

* Renamed _calculate_boiler_usage to calculate_dhw_usage
Added unit test for calculate_dhw_usage

* Fixed static checks

---------

Co-authored-by: thatoldplatitude <[email protected]>
Co-authored-by: dwindleduck <[email protected]>
Co-authored-by: Alan Pinkert <[email protected]>
Co-authored-by: Debajyoti Debnath <[email protected]>
Co-authored-by: David Egan <[email protected]>
Co-authored-by: Chad Stoughton <[email protected]>
  • Loading branch information
7 people authored Feb 28, 2024
1 parent 176a9cb commit 11748ee
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 27 deletions.
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
13 changes: 10 additions & 3 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 @@ -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
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)

0 comments on commit 11748ee

Please sign in to comment.