Skip to content

Commit

Permalink
Added whole home heat loss rate to billing period outputs (#175)
Browse files Browse the repository at this point in the history
* added whole home heat loss rate to billing period outputs

Co-authored-by: Debajyoti Debnath <[email protected]>
Co-authored-by: eriksynn <[email protected]>
Co-authored-by: AdamFinkle <[email protected]>

* lint

---------

Co-authored-by: Debajyoti Debnath <[email protected]>
Co-authored-by: eriksynn <[email protected]>
Co-authored-by: AdamFinkle <[email protected]>
  • Loading branch information
4 people authored May 14, 2024
1 parent 702ff96 commit 7bd21d9
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 4 deletions.
32 changes: 30 additions & 2 deletions rules-engine/src/rules_engine/engine.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
"""
TODO: Add module description
"""
from __future__ import annotations

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 @@ -30,6 +32,9 @@ def get_outputs_oil_propane(
temperature_input: TemperatureInput,
oil_propane_billing_input: OilPropaneBillingInput,
) -> RulesEngineResult:
"""
Analyze the heat load for a home that is using oil or propane as its current heating system fuel.
"""
billing_periods: List[NormalizedBillingPeriodRecordBase] = []

last_date = oil_propane_billing_input.preceding_delivery_date
Expand Down Expand Up @@ -61,6 +66,9 @@ def get_outputs_natural_gas(
temperature_input: TemperatureInput,
natural_gas_billing_input: NaturalGasBillingInput,
) -> RulesEngineResult:
"""
Analyze the heat load for a home that is using natural gas as its current heating system fuel.
"""
billing_periods: List[NormalizedBillingPeriodRecordBase] = []

for input_val in natural_gas_billing_input.records:
Expand All @@ -85,6 +93,9 @@ def get_outputs_normalized(
temperature_input: TemperatureInput,
billing_periods: List[NormalizedBillingPeriodRecordBase],
) -> RulesEngineResult:
"""
Analyze the heat load for a home based on normalized, fuel-type-agnostic billing records.
"""
initial_balance_point = 60
intermediate_billing_periods = convert_to_intermediate_billing_periods(
temperature_input=temperature_input, billing_periods=billing_periods
Expand Down Expand Up @@ -146,6 +157,7 @@ def get_outputs_normalized(
analysis_type=billing_period.analysis_type,
default_inclusion_by_calculation=default_inclusion_by_calculation,
eliminated_as_outlier=billing_period.eliminated_as_outlier,
whole_home_heat_loss_rate=billing_period.ua,
)
billing_records.append(billing_record)

Expand All @@ -161,6 +173,11 @@ def convert_to_intermediate_billing_periods(
temperature_input: TemperatureInput,
billing_periods: List[NormalizedBillingPeriodRecordBase],
) -> List[BillingPeriod]:
"""
Converts temperature data and billing period inputs into internal classes used for heat loss calculations.
TODO: Extract this method to another class or make it private
"""
# Build a list of lists of temperatures, where each list of temperatures contains all the temperatures
# in the corresponding billing period
intermediate_billing_periods = []
Expand Down Expand Up @@ -191,6 +208,11 @@ def convert_to_intermediate_billing_periods(


def date_to_analysis_type(d: date) -> AnalysisType:
"""
Converts the date from a billing period into an enum representing the period's usage in the rules engine.
TODO: Extract this method to another class or make it private.
"""
months = {
1: AnalysisType.ALLOWED_HEATING_USAGE,
2: AnalysisType.ALLOWED_HEATING_USAGE,
Expand Down Expand Up @@ -570,11 +592,16 @@ def calculate_partial_ua(self, billing_period: BillingPeriod) -> float:


class BillingPeriod:
"""
An internal class storing data whence heating usage per billing
period is calculated.
"""

input: NormalizedBillingPeriodRecordBase
avg_heating_usage: float
balance_point: float
partial_ua: float
ua: float
ua: Optional[float]
total_hdd: float
eliminated_as_outlier: bool

Expand All @@ -590,6 +617,7 @@ def __init__(
self.usage = usage
self.analysis_type = analysis_type
self.eliminated_as_outlier = False
self.ua = None

self.days = len(self.avg_temps)

Expand Down
4 changes: 2 additions & 2 deletions rules-engine/src/rules_engine/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
import csv
import io
from datetime import datetime, timedelta
from enum import StrEnum
from enum import Enum

from .pydantic_models import NaturalGasBillingInput, NaturalGasBillingRecordInput


class NaturalGasCompany(StrEnum):
class NaturalGasCompany(str, Enum):
EVERSOURCE = "eversource"
NATIONAL_GRID = "national_grid"

Expand Down
1 change: 1 addition & 0 deletions rules-engine/src/rules_engine/pydantic_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ class NormalizedBillingPeriodRecord(NormalizedBillingPeriodRecordBase):
analysis_type: AnalysisType = Field(frozen=True)
default_inclusion_by_calculation: bool = Field(frozen=True)
eliminated_as_outlier: bool = Field(frozen=True)
whole_home_heat_loss_rate: Optional[float] = Field(frozen=True)


class TemperatureInput(BaseModel):
Expand Down
10 changes: 10 additions & 0 deletions rules-engine/tests/test_rules_engine/test_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,13 @@ def sample_normalized_billing_periods() -> list[NormalizedBillingPeriodRecordBas
"analysis_type_override": None,
"inclusion_override": True,
},
{
"period_start_date": "2023-05-01",
"period_end_date": "2023-05-04",
"usage": 0.96,
"analysis_type_override": None,
"inclusion_override": True,
},
]

billing_periods = [
Expand Down Expand Up @@ -372,6 +379,9 @@ def test_get_outputs_normalized(
rules_engine_result.summary_output.standard_deviation_of_heat_loss_rate
== approx(0.0463, abs=0.01)
)
assert rules_engine_result.billing_records[0].usage == 60
assert rules_engine_result.billing_records[0].whole_home_heat_loss_rate != None
assert rules_engine_result.billing_records[5].whole_home_heat_loss_rate == None


@pytest.mark.parametrize(
Expand Down

0 comments on commit 7bd21d9

Please sign in to comment.