Skip to content

Commit

Permalink
Unit tests (codeforboston#149)
Browse files Browse the repository at this point in the history
* Updated test_engine to use JSON inputs

Co-authored-by: Erika Nesse <[email protected]>
Co-authored-by: dwindleduck <[email protected]>
Co-authored-by: Alan Pinkert <[email protected]>
Co-authored-by: David Egan <[email protected]>
Co-authored-by: thatoldplatitude <[email protected]>

* Collected BalancePointGraph info within Home

Co-authored-by: dwindleduck <[email protected]>
Co-authored-by: David Egan <[email protected]>
Co-authored-by: harry <[email protected]>
Co-authored-by: Debajyoti Debnath <[email protected]>

* Added test_get_outputs_natural gas to test_examples, refactored test_engine

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

* Fixed bug in determining summer months.

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

* Updated test_engine test cases based on spreadsheet

Co-authored-by: dwindleduck <[email protected]>
Co-authored-by: Debajyoti Debnath <[email protected]>
Co-authored-by: harry <[email protected]>
Co-authored-by: Alan Pinkert <[email protected]>
Co-authored-by: Jonathan Kwan <[email protected]>
Co-authored-by: David Egan <[email protected]>

* Added asssertions for UA and standard deviation

* Fixed issues with balance point differences from test cases

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

* Removed print statement

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

* Changed point where balance point graph data is collected.

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

* Finished writing tests for summary input

Co-authored-by: harry <[email protected]>
Co-authored-by: David Egan <[email protected]>
Co-authored-by: thatoldplatitude <[email protected]>
Co-authored-by: dwindleduck <[email protected]>
Co-authored-by: AdamFinkle <[email protected]>

* reformatted

---------

Co-authored-by: Jonathan Kwan <[email protected]>
Co-authored-by: Erika Nesse <[email protected]>
Co-authored-by: dwindleduck <[email protected]>
Co-authored-by: Alan Pinkert <[email protected]>
Co-authored-by: David Egan <[email protected]>
Co-authored-by: thatoldplatitude <[email protected]>
Co-authored-by: harry <[email protected]>
Co-authored-by: Debajyoti Debnath <[email protected]>
Co-authored-by: Tyler Monaghan <[email protected]>
Co-authored-by: Chad Stoughton <[email protected]>
Co-authored-by: AdamFinkle <[email protected]>
  • Loading branch information
12 people authored Feb 21, 2024
1 parent a7095ac commit 9b56eaf
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 92 deletions.
34 changes: 20 additions & 14 deletions rules-engine/src/rules_engine/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import bisect
import statistics as sts
from datetime import date, timedelta
from pprint import pprint
from typing import Any, List, Optional, Tuple

from rules_engine.pydantic_models import (
Expand All @@ -26,7 +27,7 @@ def get_outputs_oil_propane(
dhw_input: Optional[DhwInput],
temperature_input: TemperatureInput,
oil_propane_billing_input: OilPropaneBillingInput,
) -> Tuple[SummaryOutput, BalancePointGraph]:
) -> SummaryOutput:
billing_periods: List[NormalizedBillingPeriodRecordInput] = []

last_date = oil_propane_billing_input.preceding_delivery_date
Expand Down Expand Up @@ -56,7 +57,7 @@ def get_outputs_natural_gas(
summary_input: SummaryInput,
temperature_input: TemperatureInput,
natural_gas_billing_input: NaturalGasBillingInput,
) -> Tuple[SummaryOutput, BalancePointGraph]:
) -> SummaryOutput:
billing_periods: List[NormalizedBillingPeriodRecordInput] = []

for input_val in natural_gas_billing_input.records:
Expand All @@ -79,7 +80,7 @@ def get_outputs_normalized(
dhw_input: Optional[DhwInput],
temperature_input: TemperatureInput,
billing_periods: List[NormalizedBillingPeriodRecordInput],
) -> Tuple[SummaryOutput, BalancePointGraph]:
) -> SummaryOutput:
initial_balance_point = 60
intermediate_billing_periods = convert_to_intermediate_billing_periods(
temperature_input=temperature_input, billing_periods=billing_periods
Expand Down Expand Up @@ -125,7 +126,8 @@ def get_outputs_normalized(
)

balance_point_graph = home.balance_point_graph
return (summary_output, balance_point_graph)

return summary_output


def convert_to_intermediate_billing_periods(
Expand Down Expand Up @@ -363,7 +365,7 @@ def _calculate_avg_non_heating_usage(self) -> None:

def _calculate_balance_point_and_ua(
self,
initial_balance_point_sensitivity: float = 2,
initial_balance_point_sensitivity: float = 0.5,
stdev_pct_max: float = 0.10,
max_stdev_pct_diff: float = 0.01,
next_balance_point_sensitivity: float = 0.5,
Expand Down Expand Up @@ -440,17 +442,21 @@ def _refine_balance_point(self, balance_point_sensitivity: float) -> None:
avg_ua_i = sts.mean(uas_i)
stdev_pct_i = sts.pstdev(uas_i) / avg_ua_i

change_in_heat_loss_rate = avg_ua_i - self.avg_ua
percent_change_in_heat_loss_rate = 100 * change_in_heat_loss_rate / avg_ua_i

balance_point_graph_row = BalancePointGraphRow(
balance_point=bp_i,
heat_loss_rate=avg_ua_i,
change_in_heat_loss_rate=change_in_heat_loss_rate,
percent_change_in_heat_loss_rate=percent_change_in_heat_loss_rate,
standard_deviation=stdev_pct_i,
)
self.balance_point_graph.records.append(balance_point_graph_row)

if stdev_pct_i >= self.stdev_pct:
directions_to_check.pop(0)
else:
# TODO: For balance point graph, store the old balance
# point in a list to keep track of all intermediate balance
# point temperatures?

change_in_heat_loss_rate = avg_ua_i - self.avg_ua
percent_change_in_heat_loss_rate = (
100 * change_in_heat_loss_rate / avg_ua_i
)
self.balance_point, self.avg_ua, self.stdev_pct = (
bp_i,
avg_ua_i,
Expand All @@ -475,7 +481,7 @@ def _refine_balance_point(self, balance_point_sensitivity: float) -> None:

def calculate(
self,
initial_balance_point_sensitivity: float = 2,
initial_balance_point_sensitivity: float = 0.5,
stdev_pct_max: float = 0.10,
max_stdev_pct_diff: float = 0.01,
next_balance_point_sensitivity: float = 0.5,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
"thermostat_set_point": 69.0,
"setback_temperature": 62.0,
"setback_hours_per_day": 8.0,
"estimated_balance_point": 56.0,
"estimated_balance_point": 55.5,
"balance_point_sensitivity": 2.0,
"average_indoor_temperature": 66.7,
"difference_between_ti_and_tbp": 10.7,
"difference_between_ti_and_tbp": 11.2,
"design_temperature": 8.4,
"whole_home_heat_loss_rate": 733,
"whole_home_heat_loss_rate": 748,
"standard_deviation_of_heat_loss_rate": 0.0651,
"average_heat_load": 37318,
"maximum_heat_load": 45133
"average_heat_load": 37743,
"maximum_heat_load": 46099
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
"thermostat_set_point": 67.0,
"setback_temperature": 63.0,
"setback_hours_per_day": 7.0,
"estimated_balance_point": 61.0,
"estimated_balance_point": 61.5,
"balance_point_sensitivity": 1.0,
"average_indoor_temperature": 65.8,
"difference_between_ti_and_tbp": 4.8,
"difference_between_ti_and_tbp": 4.3,
"design_temperature": 8.4,
"whole_home_heat_loss_rate": 775,
"whole_home_heat_loss_rate": 761,
"standard_deviation_of_heat_loss_rate": 0.0776,
"average_heat_load": 43987,
"maximum_heat_load": 47732
"average_heat_load": 43564,
"maximum_heat_load": 46860
}
14 changes: 7 additions & 7 deletions rules-engine/tests/test_rules_engine/test_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,12 +217,12 @@ def test_bp_ua_estimates(sample_summary_inputs, sample_billing_periods):

ua_1, ua_2, ua_3 = [bill.ua for bill in home.bills_winter]

assert home.balance_point == 60
assert ua_1 == approx(1478.50, abs=0.01)
assert ua_2 == approx(1650.00, abs=0.01)
assert ua_3 == approx(1527.78, abs=0.01)
assert home.avg_ua == approx(1552.09, abs=0.01)
assert home.stdev_pct == approx(0.0465, abs=0.01)
assert home.balance_point == 60.5
assert ua_1 == approx(1455.03, abs=0.01)
assert ua_2 == approx(1617.65, abs=0.01)
assert ua_3 == approx(1486.49, abs=0.01)
assert home.avg_ua == approx(1519.72, abs=1)
assert home.stdev_pct == approx(0.0463, abs=0.01)


def test_bp_ua_with_outlier(sample_summary_inputs, sample_billing_periods_with_outlier):
Expand Down Expand Up @@ -272,7 +272,7 @@ def test_convert_to_intermediate_billing_periods(
def test_get_outputs_normalized(
sample_summary_inputs, sample_temp_inputs, sample_normalized_billing_periods
):
summary_output, balance_point_graph = engine.get_outputs_normalized(
summary_output = engine.get_outputs_normalized(
sample_summary_inputs,
None,
sample_temp_inputs,
Expand Down
120 changes: 59 additions & 61 deletions rules-engine/tests/test_rules_engine/test_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

# Filter out example 2 for now, since it's for oil fuel type
INPUT_DATA = filter(lambda d: d != "example-2", next(os.walk(ROOT_DIR))[1])
# INPUT_DATA = filter(lambda d: d == "cali", next(os.walk(ROOT_DIR))[1])


class Summary(SummaryInput, SummaryOutput):
Expand Down Expand Up @@ -112,67 +113,64 @@ def test_average_indoor_temp(data: Example) -> None:
assert data.summary.average_indoor_temperature == approx(avg_indoor_temp, rel=0.01)


def test_get_outputs_natural_gas(data: Example) -> None:
summary_output, balance_point_graph = engine.get_outputs_natural_gas(
def test_balance_point_natural_gas(data: Example) -> None:
summary_output = engine.get_outputs_natural_gas(
data.summary, data.temperature_data, data.natural_gas_usage
)
assert data.summary.estimated_balance_point == approx(
summary_output.estimated_balance_point, rel=0.05
)


# def test_ua(data: Example) -> None:
# """
# Test how the rules engine calculates UA from energy bills.

# Pulls in data and pre-calculated results from example spreadsheets
# and compares them to the UA calculated from that data by the
# engine.
# """
# # TODO: Handle oil and propane fuel types too
# usage_data = None
# if data.summary.fuel_type is engine.FuelType.GAS:
# usage_data = data.natural_gas_usage
# else:
# raise NotImplementedError("Fuel type {}".format(data.summary.fuel_type))

# # build Home instance - input summary information and bills
# home = engine.Home(
# data.summary,
# data.summary.fuel_type,
# data.summary.heating_system_efficiency,
# thermostat_set_point=data.summary.thermostat_set_point,
# )
# temps = []
# usages = []
# inclusion_codes = []
# for usage in usage_data.records:
# temps_for_period = []
# for i in range(usage.days_in_bill):
# date_in_period = usage.start_date + timedelta(days=i)
# matching_records = [
# d for d in data.temperature_data if d.date == date_in_period
# ]
# assert len(matching_records) == 1
# temps_for_period.append(matching_records[0].temperature)
# assert date_in_period == usage.end_date

# inclusion_code = usage.inclusion_code
# if usage.inclusion_override is not None:
# inclusion_code = usage.inclusion_override

# temps.append(temps_for_period)
# usages.append(usage.usage)
# inclusion_codes.append(inclusion_code)

# home.initialize_billing_periods(temps, usages, inclusion_codes)

# # now check outputs
# home.calculate_balance_point_and_ua(
# initial_balance_point_sensitivity=data.summary.balance_point_sensitivity
# )

# assert home.balance_point == approx(data.summary.estimated_balance_point, abs=0.01)
# assert home.avg_ua == approx(data.summary.whole_home_heat_loss_rate, abs=1)
# assert home.stdev_pct == approx(data.summary.standard_deviation_of_heat_loss_rate, abs=0.01)
# # TODO: check average heat load and max heat load
summary_output.estimated_balance_point, abs=0.1
)


def test_whole_home_heat_loss_rate_natural_gas(data: Example) -> None:
summary_output = engine.get_outputs_natural_gas(
data.summary, data.temperature_data, data.natural_gas_usage
)
assert summary_output.whole_home_heat_loss_rate == approx(
data.summary.whole_home_heat_loss_rate, abs=1
)


def test_standard_deviation_of_heat_loss_rate_natural_gas(data: Example) -> None:
summary_output = engine.get_outputs_natural_gas(
data.summary, data.temperature_data, data.natural_gas_usage
)
assert summary_output.standard_deviation_of_heat_loss_rate == approx(
data.summary.standard_deviation_of_heat_loss_rate, abs=0.01
)


def test_difference_between_ti_and_tbp_natural_gas(data: Example) -> None:
summary_output = engine.get_outputs_natural_gas(
data.summary, data.temperature_data, data.natural_gas_usage
)
assert summary_output.difference_between_ti_and_tbp == approx(
data.summary.difference_between_ti_and_tbp, abs=0.1
)


def test_average_heat_load_natural_gas(data: Example) -> None:
summary_output = engine.get_outputs_natural_gas(
data.summary, data.temperature_data, data.natural_gas_usage
)
assert summary_output.average_heat_load == approx(
data.summary.average_heat_load, abs=1
)


def test_design_temperaure_natural_gas(data: Example) -> None:
summary_output = engine.get_outputs_natural_gas(
data.summary, data.temperature_data, data.natural_gas_usage
)
assert summary_output.design_temperature == approx(
data.summary.design_temperature, abs=0.1
)


def test_maximum_heat_load_natural_gas(data: Example) -> None:
summary_output = engine.get_outputs_natural_gas(
data.summary, data.temperature_data, data.natural_gas_usage
)
assert summary_output.maximum_heat_load == approx(
data.summary.maximum_heat_load, abs=1
)

0 comments on commit 9b56eaf

Please sign in to comment.