Skip to content

Commit

Permalink
Made convert_to_intermediate_billing_periods
Browse files Browse the repository at this point in the history
Added test for convert_to_intermediate_billing_periods
Filled out date_to_analysis_type
Removed numpy

Co-authored-by: harry <[email protected]>
Co-authored-by: Debajyoti Debnath <[email protected]>
Co-authored-by: dwindleduck <[email protected]>
Co-authored-by: Alan Pinkert <[email protected]>
Co-authored-by: wertheis <[email protected]>
  • Loading branch information
6 people authored and jkwan2011 committed Jan 3, 2024
1 parent f810766 commit 151660b
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 40 deletions.
83 changes: 48 additions & 35 deletions rules-engine/src/rules_engine/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@
from datetime import date, timedelta
from typing import Any, List, Optional, Tuple

import numpy as np

from rules_engine.pydantic_models import (
AnalysisType,
BalancePointGraph,
BalancePointGraphRow,
Constants,
DhwInput,
FuelType,
Expand Down Expand Up @@ -81,31 +80,10 @@ def get_outputs_normalized(
temperature_input: TemperatureInput,
billing_periods: List[NormalizedBillingPeriodRecordInput],
) -> Tuple[SummaryOutput, BalancePointGraph]:
# Build a list of lists of temperatures, where each list of temperatures contains all the temperatures
# in the corresponding billing period
intermediate_billing_periods = []
initial_balance_point = 60

for billing_period in billing_periods:
# the HEAT Excel sheet is inclusive of the temperatures that fall on both the start and end dates
start_idx = bisect.bisect_left(
temperature_input.dates, billing_period.period_start_date
)
end_idx = (
bisect.bisect_left(temperature_input.dates, billing_period.period_end_date)
+ 1
)

analysis_type = date_to_analysis_type(billing_period.period_end_date)
if billing_period.inclusion_override:
analysis_type = billing_period.inclusion_override

intermediate_billing_period = BillingPeriod(
avg_temps=temperature_input.temperatures[start_idx:end_idx],
usage=billing_period.usage,
analysis_type=analysis_type,
)
intermediate_billing_periods.append(intermediate_billing_period)
intermediate_billing_periods = convert_to_intermediate_billing_periods(
temperature_input=temperature_input, billing_periods=billing_periods
)

home = Home(
summary_input=summary_input,
Expand Down Expand Up @@ -145,8 +123,42 @@ def get_outputs_normalized(
average_heat_load=average_heat_load,
maximum_heat_load=maximum_heat_load,
)
# return summary_output # TODO: add BalancePointGraph
raise NotImplementedError

# TODO: fill out balance point graph
balance_point_graph = BalancePointGraph(records=[])
return (summary_output, balance_point_graph)


def convert_to_intermediate_billing_periods(
temperature_input: TemperatureInput,
billing_periods: List[NormalizedBillingPeriodRecordInput],
) -> List[BillingPeriod]:
# Build a list of lists of temperatures, where each list of temperatures contains all the temperatures
# in the corresponding billing period
intermediate_billing_periods = []

for billing_period in billing_periods:
# the HEAT Excel sheet is inclusive of the temperatures that fall on both the start and end dates
start_idx = bisect.bisect_left(
temperature_input.dates, billing_period.period_start_date
)
end_idx = (
bisect.bisect_left(temperature_input.dates, billing_period.period_end_date)
+ 1
)

analysis_type = date_to_analysis_type(billing_period.period_end_date)
if billing_period.inclusion_override:
analysis_type = billing_period.inclusion_override

intermediate_billing_period = BillingPeriod(
avg_temps=temperature_input.temperatures[start_idx:end_idx],
usage=billing_period.usage,
analysis_type=analysis_type,
)
intermediate_billing_periods.append(intermediate_billing_period)

return intermediate_billing_periods


def date_to_analysis_type(d: date) -> AnalysisType:
Expand All @@ -164,9 +176,7 @@ def date_to_analysis_type(d: date) -> AnalysisType:
11: AnalysisType.DO_NOT_INCLUDE,
12: AnalysisType.INCLUDE,
}

# TODO: finish implementation and unit test
raise NotImplementedError
return months[d.month]


def hdd(avg_temp: float, balance_point: float) -> float:
Expand Down Expand Up @@ -213,7 +223,7 @@ def get_average_indoor_temperature(
"""
if setback_temperature is None:
setback_temperature = thermostat_set_point

if setback_hours_per_day is None:
setback_hours_per_day = 0

Expand Down Expand Up @@ -370,9 +380,9 @@ def _calculate_balance_point_and_ua(
self._refine_balance_point(initial_balance_point_sensitivity)

while self.stdev_pct > stdev_pct_max:
biggest_outlier_idx = np.argmax(
[abs(bill.ua - self.avg_ua) for bill in self.bills_winter]
)
outliers = [abs(bill.ua - self.avg_ua) for bill in self.bills_winter]
biggest_outlier = max(outliers)
biggest_outlier_idx = outliers.index(biggest_outlier)
outlier = self.bills_winter.pop(
biggest_outlier_idx
) # removes the biggest outlier
Expand Down Expand Up @@ -420,6 +430,9 @@ def _refine_balance_point(self, balance_point_sensitivity: float) -> None:
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?
self.balance_point, self.avg_ua, self.stdev_pct = (
bp_i,
avg_ua_i,
Expand Down
12 changes: 7 additions & 5 deletions rules-engine/src/rules_engine/pydantic_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,13 @@ class SummaryOutput(BaseModel):
class BalancePointGraphRow(BaseModel):
"""From Summary page"""

balance_pt: float = Field(description="Summary!G33:35")
ua: float = Field(description="Summary!H33:35")
change_in_ua: float = Field(description="Summary!I33:35")
pct_change: float = Field(description="Summary!J33:35")
std_dev: float = Field(description="Summary!K33:35")
balance_point: float = Field(description="Summary!G33:35") # degree F
heat_loss_rate: float = Field(description="Summary!H33:35") # BTU / (hr-deg. F)
change_in_heat_loss_rate: float = Field(
description="Summary!I33:35"
) # BTU / (hr-deg. F)
percent_change_in_heat_loss_rate: float = Field(description="Summary!J33:35")
standard_deviation: float = Field(description="Summary!K33:35")


class BalancePointGraph(BaseModel):
Expand Down
120 changes: 120 additions & 0 deletions rules-engine/tests/test_rules_engine/test_engine.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from datetime import date

import pytest
from pytest import approx

Expand All @@ -8,8 +10,10 @@
DhwInput,
FuelType,
NaturalGasBillingInput,
NormalizedBillingPeriodRecordInput,
SummaryInput,
SummaryOutput,
TemperatureInput,
)


Expand Down Expand Up @@ -37,6 +41,20 @@ def test_period_hdd(temps, expected_result):
assert engine.period_hdd(temps, 60) == expected_result


def test_date_to_analysis_type():
test_date = date.fromisoformat("2019-01-04")
assert engine.date_to_analysis_type(test_date) == AnalysisType.INCLUDE

dates = ["2019-01-04", "2019-07-04", "2019-12-04"]
types = [engine.date_to_analysis_type(date.fromisoformat(d)) for d in dates]
expected_types = [
AnalysisType.INCLUDE,
AnalysisType.INCLUDE_IN_OTHER_ANALYSIS,
AnalysisType.INCLUDE,
]
assert types == expected_types


def test_get_average_indoor_temperature():
set_temp = 68
setback = 62
Expand Down Expand Up @@ -132,3 +150,105 @@ def test_bp_ua_with_outlier():
assert ua_3 == approx(1479.6, abs=1)
assert home.avg_ua == approx(1515.1, abs=1)
assert home.stdev_pct == approx(0.0474, abs=0.01)


def test_convert_to_intermediate_billing_periods():
temperature_input = TemperatureInput(
dates=[
date(2022, 12, 1),
date(2022, 12, 2),
date(2022, 12, 3),
date(2022, 12, 4),
date(2023, 1, 1),
date(2023, 1, 2),
date(2023, 1, 3),
date(2023, 1, 4),
date(2023, 2, 1),
date(2023, 2, 2),
date(2023, 2, 3),
date(2023, 2, 4),
date(2023, 3, 1),
date(2023, 3, 2),
date(2023, 3, 3),
date(2023, 3, 4),
date(2023, 4, 1),
date(2023, 4, 2),
date(2023, 4, 3),
date(2023, 4, 4),
],
temperatures=[
41.7,
41.6,
32,
25.4,
28,
29,
30,
29,
32,
35,
35,
38,
41,
43,
42,
42,
72,
71,
70,
69,
],
)

billing_periods = [
NormalizedBillingPeriodRecordInput(
period_start_date=date(2022, 12, 1),
period_end_date=date(2022, 12, 4),
usage=60,
inclusion_override=None,
),
NormalizedBillingPeriodRecordInput(
period_start_date=date(2023, 1, 1),
period_end_date=date(2023, 1, 4),
usage=50,
inclusion_override=None,
),
NormalizedBillingPeriodRecordInput(
period_start_date=date(2023, 2, 1),
period_end_date=date(2023, 2, 4),
usage=45,
inclusion_override=None,
),
NormalizedBillingPeriodRecordInput(
period_start_date=date(2023, 3, 1),
period_end_date=date(2023, 3, 4),
usage=30,
inclusion_override=None,
),
NormalizedBillingPeriodRecordInput(
period_start_date=date(2023, 4, 1),
period_end_date=date(2023, 4, 4),
usage=0.96,
inclusion_override=None,
),
]

results = engine.convert_to_intermediate_billing_periods(
temperature_input, billing_periods
)

expected_results = [
engine.BillingPeriod([41.7, 41.6, 32, 25.4], 60, AnalysisType.INCLUDE),
engine.BillingPeriod([28, 29, 30, 29], 50, AnalysisType.INCLUDE),
engine.BillingPeriod([32, 35, 35, 38], 45, AnalysisType.INCLUDE),
engine.BillingPeriod([41, 43, 42, 42], 30, AnalysisType.INCLUDE),
engine.BillingPeriod([72, 71, 70, 69], 0.96, AnalysisType.DO_NOT_INCLUDE),
]

for i in range(len(expected_results)):
result = results[i]
expected_result = expected_results[i]

assert result.avg_temps == expected_result.avg_temps
assert result.usage == expected_result.usage
assert result.analysis_type == expected_result.analysis_type

0 comments on commit 151660b

Please sign in to comment.