From 3b452e98c2e7ed1bc01c2bc01798f224babb257e Mon Sep 17 00:00:00 2001 From: Alan Pinkert Date: Tue, 10 Oct 2023 22:44:23 -0400 Subject: [PATCH] More examples for rules engine tests (#86) * create test_ua_gas() in rules engine * first pass at fixing bug from example 4 Co-authored-by: Erika Nesse Co-authored-by: Debajyoti Debnath Co-authored-by: Nate * satisfy black and mypy * satisfy black again * add rules engine test examples: breslow, cali, feldman * remove debugging code pass in BP sensitivity use os.walk() to parameterize test, filtering out example 2 * Change example 4's sensitivity to 0.5 so it will converge to expected BP --------- Co-authored-by: axiomizer Co-authored-by: Erika Nesse Co-authored-by: Debajyoti Debnath Co-authored-by: Nate Co-authored-by: Nate <138620999+axiomizer@users.noreply.github.com> --- .../cases/examples/breslow/natural-gas.csv | 36 +++++++++++ .../cases/examples/breslow/summary.json | 21 +++++++ .../cases/examples/cali/natural-gas.csv | 37 +++++++++++ .../cases/examples/cali/summary.json | 21 +++++++ .../cases/examples/example-4/natural-gas.csv | 21 +++++++ .../cases/examples/example-4/summary.json | 21 +++++++ .../cases/examples/feldman/natural-gas.csv | 37 +++++++++++ .../cases/examples/feldman/summary.json | 21 +++++++ .../tests/test_rules_engine/test_examples.py | 61 ++++++++++++------- 9 files changed, 255 insertions(+), 21 deletions(-) create mode 100644 rules-engine/tests/test_rules_engine/cases/examples/breslow/natural-gas.csv create mode 100644 rules-engine/tests/test_rules_engine/cases/examples/breslow/summary.json create mode 100644 rules-engine/tests/test_rules_engine/cases/examples/cali/natural-gas.csv create mode 100644 rules-engine/tests/test_rules_engine/cases/examples/cali/summary.json create mode 100644 rules-engine/tests/test_rules_engine/cases/examples/example-4/natural-gas.csv create mode 100644 rules-engine/tests/test_rules_engine/cases/examples/example-4/summary.json create mode 100644 rules-engine/tests/test_rules_engine/cases/examples/feldman/natural-gas.csv create mode 100644 rules-engine/tests/test_rules_engine/cases/examples/feldman/summary.json diff --git a/rules-engine/tests/test_rules_engine/cases/examples/breslow/natural-gas.csv b/rules-engine/tests/test_rules_engine/cases/examples/breslow/natural-gas.csv new file mode 100644 index 00000000..f85c55d9 --- /dev/null +++ b/rules-engine/tests/test_rules_engine/cases/examples/breslow/natural-gas.csv @@ -0,0 +1,36 @@ +start_date,end_date,days_in_bill,usage,inclusion_code,inclusion_override,avg_daily_usage,daily_htg_usage +3/3/2019,3/29/2019,27,120,1,,4.44,4.03 +3/31/2019,4/30/2019,31,88,0,,2.84,2.43 +5/2/2019,6/3/2019,33,66,0,,2.00,1.59 +6/5/2019,6/27/2019,23,18,0,,0.78,0.37 +6/29/2019,7/31/2019,33,14,-1,,0.42,0.01 +8/2/2019,8/28/2019,27,13,-1,,0.48,0.07 +8/30/2019,10/1/2019,33,34,0,,1.03,0.62 +10/3/2019,10/28/2019,26,50,0,,1.92,1.51 +10/30/2019,11/27/2019,29,118,0,,4.07,3.66 +11/29/2019,12/30/2019,32,158,1,,4.94,4.53 +1/1/2020,1/29/2020,29,138,1,,4.76,4.35 +1/31/2020,2/26/2020,27,140,1,,5.19,4.78 +2/28/2020,3/30/2020,32,127,1,,3.97,3.56 +4/1/2020,4/28/2020,28,86,0,,3.07,2.66 +4/30/2020,5/27/2020,28,44,0,,1.57,1.16 +5/29/2020,6/26/2020,29,14,0,,0.48,0.07 +6/28/2020,7/29/2020,32,11,-1,,0.34,-0.07 +7/31/2020,8/27/2020,28,9,-1,,0.32,-0.09 +8/29/2020,9/28/2020,31,22,-1,,0.71,0.30 +9/30/2020,10/27/2020,28,36,0,,1.29,0.88 +10/29/2020,11/30/2020,33,106,0,,3.21,2.80 +12/2/2020,12/28/2020,27,131,1,,4.85,4.44 +12/30/2020,1/28/2021,30,122,1,,4.07,3.66 +1/30/2021,2/26/2021,28,153,1,,5.46,5.05 +2/28/2021,3/30/2021,31,141,0,0,4.55,4.14 +4/1/2021,4/29/2021,29,77,0,,2.66,2.25 +5/1/2021,5/28/2021,28,38,0,,1.36,0.95 +5/30/2021,6/29/2021,31,23,0,,0.74,0.33 +7/1/2021,7/29/2021,29,10,-1,,0.34,-0.07 +7/31/2021,8/31/2021,32,10,-1,,0.31,-0.10 +9/2/2021,9/28/2021,27,9,-1,,0.33,-0.08 +9/30/2021,10/28/2021,29,26,0,,0.90,0.49 +10/30/2021,11/29/2021,31,102,0,,3.29,2.88 +12/1/2021,12/27/2021,27,111,1,,4.11,3.70 +12/29/2021,1/27/2022,30,166,1,,5.53,5.12 \ No newline at end of file diff --git a/rules-engine/tests/test_rules_engine/cases/examples/breslow/summary.json b/rules-engine/tests/test_rules_engine/cases/examples/breslow/summary.json new file mode 100644 index 00000000..fd72d226 --- /dev/null +++ b/rules-engine/tests/test_rules_engine/cases/examples/breslow/summary.json @@ -0,0 +1,21 @@ +{ + "local_weather_station": "KBED-Bedford", + "design_temperature_override": null, + "living_area": 1250, + "fuel_type": "GAS", + "heating_system_efficiency": 0.85, + "other_fuel_usage": 0.41, + "other_fuel_usage_override": null, + "thermostat_set_point": 68.0, + "setback_temp": 62.0, + "setback_hours_per_day": 12.0, + "estimated_balance_point": 68.0, + "balance_point_sensitivity": 2.0, + "average_indoor_temperature": 65.0, + "difference_between_ti_and_tbp": -3.0, + "design_temperature": 8.4, + "whole_home_ua": 444, + "standard_deviation_of_ua": 0.0787, + "avg_heat_load": 28672, + "max_heat_load": 27340 +} \ No newline at end of file diff --git a/rules-engine/tests/test_rules_engine/cases/examples/cali/natural-gas.csv b/rules-engine/tests/test_rules_engine/cases/examples/cali/natural-gas.csv new file mode 100644 index 00000000..927afed5 --- /dev/null +++ b/rules-engine/tests/test_rules_engine/cases/examples/cali/natural-gas.csv @@ -0,0 +1,37 @@ +start_date,end_date,days_in_bill,usage,inclusion_override,inclusion_code,avg_daily_usage,daily_htg_usage +2/28/2018,3/27/2018,28,132,,1,4.71,4.29 +3/28/2018,4/30/2018,34,98,,0,2.88,2.46 +5/1/2018,5/30/2018,30,23,,0,0.77,0.34 +5/31/2018,6/28/2018,29,16,,0,0.55,0.13 +6/29/2018,7/29/2018,31,16,,-1,0.52,0.09 +7/30/2018,8/27/2018,29,12,,-1,0.41,-0.01 +8/28/2018,9/26/2018,30,16,,-1,0.53,0.11 +9/27/2018,10/25/2018,29,37,,0,1.28,0.85 +10/26/2018,11/27/2018,33,119,,0,3.61,3.18 +11/28/2018,12/28/2018,31,151,,1,4.87,4.45 +12/29/2018,1/29/2019,32,183,,1,5.72,5.29 +1/30/2019,2/28/2019,30,158,,1,5.27,4.84 +3/1/2019,3/27/2019,27,128,,1,4.74,4.32 +3/28/2019,4/29/2019,33,64,,0,1.94,1.52 +4/30/2019,6/3/2019,35,41,,0,1.17,0.75 +6/4/2019,6/27/2019,24,11,,0,0.46,0.03 +6/28/2019,7/31/2019,34,13,,-1,0.38,-0.04 +8/1/2019,8/28/2019,28,11,,-1,0.39,-0.03 +8/29/2019,9/30/2019,33,15,,-1,0.45,0.03 +10/1/2019,10/25/2019,25,14,,0,0.56,0.14 +10/26/2019,11/27/2019,33,100,,0,3.03,2.61 +11/28/2019,1/2/2020,36,163,,1,4.53,4.10 +1/3/2020,1/30/2020,28,124,,1,4.43,4.00 +1/31/2020,2/28/2020,29,118,,1,4.07,3.65 +2/29/2020,3/30/2020,31,85,0,0,2.74,2.32 +3/31/2020,4/30/2020,31,93,,0,3.00,2.58 +5/1/2020,5/28/2020,28,36,,0,1.29,0.86 +5/29/2020,6/29/2020,32,13,,0,0.41,-0.02 +6/30/2020,7/29/2020,30,9,,-1,0.30,-0.12 +7/30/2020,8/27/2020,29,12,,-1,0.41,-0.01 +8/28/2020,9/28/2020,32,13,,-1,0.41,-0.02 +9/29/2020,10/27/2020,29,33,,0,1.14,0.71 +10/28/2020,11/30/2020,34,100,,0,2.94,2.52 +12/1/2020,12/29/2020,29,133,,1,4.59,4.16 +12/30/2020,1/29/2021,31,172,0,0,5.55,5.12 +1/30/2021,2/25/2021,27,167,,1,6.19,5.76 \ No newline at end of file diff --git a/rules-engine/tests/test_rules_engine/cases/examples/cali/summary.json b/rules-engine/tests/test_rules_engine/cases/examples/cali/summary.json new file mode 100644 index 00000000..b802a340 --- /dev/null +++ b/rules-engine/tests/test_rules_engine/cases/examples/cali/summary.json @@ -0,0 +1,21 @@ +{ + "local_weather_station": "KBED-Bedford", + "design_temperature_override": null, + "living_area": 3000, + "fuel_type": "GAS", + "heating_system_efficiency": 0.93, + "other_fuel_usage": 0.42, + "other_fuel_usage_override": null, + "thermostat_set_point": 69.0, + "setback_temp": 62.0, + "setback_hours_per_day": 8.0, + "estimated_balance_point": 56.0, + "balance_point_sensitivity": 2.0, + "average_indoor_temperature": 66.7, + "difference_between_ti_and_tbp": 10.7, + "design_temperature": 8.4, + "whole_home_ua": 733, + "standard_deviation_of_ua": 0.0651, + "avg_heat_load": 37318, + "max_heat_load": 45133 +} \ No newline at end of file diff --git a/rules-engine/tests/test_rules_engine/cases/examples/example-4/natural-gas.csv b/rules-engine/tests/test_rules_engine/cases/examples/example-4/natural-gas.csv new file mode 100644 index 00000000..942bb7f9 --- /dev/null +++ b/rules-engine/tests/test_rules_engine/cases/examples/example-4/natural-gas.csv @@ -0,0 +1,21 @@ +start_date,end_date,days_in_bill,usage,inclusion_code,inclusion_override,avg_daily_usage,daily_htg_usage +11/14/2019,12/16/2019,33,216,1,,6.55,6.28 +12/17/2019,1/15/2020,30,194,1,,6.47,6.20 +1/16/2020,2/13/2020,29,220,1,,7.59,7.32 +2/14/2020,3/16/2020,32,176,1,,5.50,5.23 +3/17/2020,4/16/2020,31,144,0,,4.65,4.38 +4/17/2020,5/14/2020,28,95,0,,3.39,3.13 +5/15/2020,6/15/2020,32,25,0,,0.78,0.52 +6/16/2020,7/16/2020,31,13,-1,,0.42,0.15 +7/17/2020,8/14/2020,29,9,-1,,0.31,0.04 +8/15/2020,9/14/2020,31,5,-1,,0.16,-0.10 +9/15/2020,10/13/2020,29,42,0,,1.45,1.18 +10/14/2020,11/13/2020,31,101,0,,3.26,2.99 +11/14/2020,12/16/2020,33,192,1,,5.82,5.55 +12/17/2020,1/14/2021,29,221,1,,7.62,7.35 +1/15/2021,2/12/2021,29,227,1,,7.83,7.56 +2/13/2021,3/16/2021,32,230,1,,7.19,6.92 +3/17/2021,4/16/2021,31,113,0,,3.65,3.38 +4/17/2021,5/18/2021,32,79,0,,2.47,2.20 +5/19/2021,6/16/2021,29,19,0,,0.66,0.39 +6/17/2021,7/19/2021,33,6,-1,,0.18,-0.08 \ No newline at end of file diff --git a/rules-engine/tests/test_rules_engine/cases/examples/example-4/summary.json b/rules-engine/tests/test_rules_engine/cases/examples/example-4/summary.json new file mode 100644 index 00000000..25caa94b --- /dev/null +++ b/rules-engine/tests/test_rules_engine/cases/examples/example-4/summary.json @@ -0,0 +1,21 @@ +{ + "local_weather_station": "KBED-Bedford", + "design_temperature_override": null, + "living_area": 2140, + "fuel_type": "GAS", + "heating_system_efficiency": 0.96, + "other_fuel_usage": 0.27, + "other_fuel_usage_override": null, + "thermostat_set_point": 70.0, + "setback_temp": 68.0, + "setback_hours_per_day": 6.0, + "estimated_balance_point": 62.5, + "balance_point_sensitivity": 0.5, + "average_indoor_temperature": 69.5, + "difference_between_ti_and_tbp": 7.0, + "design_temperature": 8.4, + "whole_home_ua": 913, + "standard_deviation_of_ua": 0.0369, + "avg_heat_load": 49826, + "max_heat_load": 56214 +} \ No newline at end of file diff --git a/rules-engine/tests/test_rules_engine/cases/examples/feldman/natural-gas.csv b/rules-engine/tests/test_rules_engine/cases/examples/feldman/natural-gas.csv new file mode 100644 index 00000000..0fdd2b71 --- /dev/null +++ b/rules-engine/tests/test_rules_engine/cases/examples/feldman/natural-gas.csv @@ -0,0 +1,37 @@ +start_date,end_date,days_in_bill,usage,inclusion_override,inclusion_code,avg_daily_usage,daily_htg_usage +1/23/2019,2/19/2019,28,200,,1,7.14,6.69 +2/20/2019,3/20/2019,29,191,,1,6.59,6.13 +3/21/2019,4/19/2019,30,115,,0,3.83,3.38 +4/20/2019,5/17/2019,28,67,,0,2.39,1.94 +5/18/2019,6/18/2019,32,30,,0,0.94,0.48 +6/19/2019,7/18/2019,30,13,,-1,0.43,-0.02 +7/19/2019,8/19/2019,32,14,,-1,0.44,-0.02 +8/20/2019,9/17/2019,29,13,,-1,0.45,-0.01 +9/18/2019,10/22/2019,35,43,,0,1.23,0.77 +10/23/2019,11/20/2019,29,115,,0,3.97,3.51 +11/21/2019,12/17/2019,27,161,,1,5.96,5.51 +12/18/2019,1/21/2020,35,234,,1,6.69,6.23 +1/22/2020,2/19/2020,29,177,,1,6.10,5.65 +2/20/2020,3/19/2020,29,146,,1,5.03,4.58 +3/20/2020,4/17/2020,29,118,,0,4.07,3.61 +4/18/2020,5/19/2020,32,93,,0,2.91,2.45 +5/20/2020,6/18/2020,30,25,,0,0.83,0.38 +6/19/2020,7/17/2020,29,16,,-1,0.55,0.10 +7/18/2020,8/18/2020,32,16,,-1,0.50,0.04 +8/19/2020,9/17/2020,30,15,,-1,0.50,0.04 +9/18/2020,10/20/2020,33,43,,0,1.30,0.85 +10/21/2020,11/18/2020,29,91,,0,3.14,2.68 +11/19/2020,12/21/2020,33,186,,1,5.64,5.18 +12/22/2020,1/20/2021,30,189,,1,6.30,5.84 +1/21/2021,2/18/2021,29,229,,1,7.90,7.44 +2/19/2021,3/17/2021,27,164,,1,6.07,5.62 +3/18/2021,4/16/2021,30,96,,0,3.20,2.74 +4/17/2021,5/18/2021,32,68,,0,2.13,1.67 +5/19/2021,6/17/2021,30,23,,0,0.77,0.31 +6/18/2021,7/19/2021,32,11,,-1,0.34,-0.11 +7/20/2021,8/17/2021,29,14,,-1,0.48,0.03 +8/18/2021,9/17/2021,31,13,,-1,0.42,-0.04 +9/18/2021,10/19/2021,32,28,,0,0.88,0.42 +10/20/2021,11/18/2021,30,89,,0,2.97,2.51 +11/19/2021,12/17/2021,29,124,,1,4.28,3.82 +12/18/2021,1/18/2022,32,184,,1,5.75,5.29 \ No newline at end of file diff --git a/rules-engine/tests/test_rules_engine/cases/examples/feldman/summary.json b/rules-engine/tests/test_rules_engine/cases/examples/feldman/summary.json new file mode 100644 index 00000000..49935843 --- /dev/null +++ b/rules-engine/tests/test_rules_engine/cases/examples/feldman/summary.json @@ -0,0 +1,21 @@ +{ + "local_weather_station": "KBED-Bedford", + "design_temperature_override": null, + "living_area": 1662, + "fuel_type": "GAS", + "heating_system_efficiency": 0.90, + "other_fuel_usage": 0.46, + "other_fuel_usage_override": null, + "thermostat_set_point": 67.0, + "setback_temp": 63.0, + "setback_hours_per_day": 7.0, + "estimated_balance_point": 61.0, + "balance_point_sensitivity": 1.0, + "average_indoor_temperature": 65.8, + "difference_between_ti_and_tbp": 4.8, + "design_temperature": 8.4, + "whole_home_ua": 775, + "standard_deviation_of_ua": 0.0776, + "avg_heat_load": 43987, + "max_heat_load": 47732 +} \ No newline at end of file diff --git a/rules-engine/tests/test_rules_engine/test_examples.py b/rules-engine/tests/test_rules_engine/test_examples.py index 6835bc4e..005f35cd 100644 --- a/rules-engine/tests/test_rules_engine/test_examples.py +++ b/rules-engine/tests/test_rules_engine/test_examples.py @@ -15,7 +15,8 @@ # Test inputs are provided as separate directory within the "cases/examples" directory # Each subdirectory contains a JSON file (named summary.json) which specifies the inputs for the test runner ROOT_DIR = pathlib.Path(__file__).parent / "cases" / "examples" -INPUT_DATA = next(os.walk(ROOT_DIR))[1] +# 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]) def validate_fuel_type(value): @@ -146,6 +147,20 @@ def test_average_indoor_temp(data: Example) -> None: 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.fuel_type, @@ -155,29 +170,33 @@ def test_ua(data: Example) -> None: temps = [] usages = [] inclusion_codes = [] - if data.summary.fuel_type is engine.FuelType.GAS: - for usage in data.natural_gas_usage: - 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 - temps.append(temps_for_period) - usages.append(usage.usage) - if usage.inclusion_override is not None: - inclusion_codes.append(usage.inclusion_override) - else: - inclusion_codes.append(usage.inclusion_code) - else: - raise NotImplementedError("Fuel type {}".format(data.summary.fuel_type)) + for usage in usage_data: + 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() + 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_ua, abs=1) assert home.stdev_pct == approx(data.summary.standard_deviation_of_ua, abs=0.01) # TODO: check average heat load and max heat load