Skip to content

Commit

Permalink
Merge pull request #1535 from pnnl/develop_rct_tolerance
Browse files Browse the repository at this point in the history
Develop rct tolerance
  • Loading branch information
weilixu authored Sep 30, 2024
2 parents 3d980c7 + 24bebe8 commit bd3a5d7
Show file tree
Hide file tree
Showing 89 changed files with 1,616 additions and 273 deletions.
6 changes: 5 additions & 1 deletion rct229/rule_engine/partial_rule_definition.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from rct229.rule_engine.rule_base import RuleDefinitionBase
from typing import Mapping

from rct229.rule_engine.rule_base import RCTPrecision, RuleDefinitionBase


class PartialRuleDefinition(RuleDefinitionBase):
Expand All @@ -14,6 +16,7 @@ def __init__(
required_fields=None,
manual_check_required_msg="",
not_applicable_msg="",
precision: Mapping[str, RCTPrecision] = None,
):
"""Base class for all Partial Rule definitions (secondary)
Expand Down Expand Up @@ -58,6 +61,7 @@ def __init__(
ruleset_section_title=ruleset_section_title,
standard_section=standard_section,
is_primary_rule=is_primary_rule,
precision=precision,
)

def rule_check(self, context, calc_vals=None, data={}):
Expand Down
56 changes: 46 additions & 10 deletions rct229/rule_engine/rule_base.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
from functools import partial
from typing import TypedDict, Mapping

from jsonpointer import resolve_pointer
from rct229.rule_engine.rct_outcome_label import RCTOutcomeLabel
from rct229.rule_engine.ruleset_model_factory import RuleSetModels, get_rmd_instance
from rct229.schema.config import ureg
from rct229.utils.assertions import MissingKeyException, RCTFailureException
from rct229.utils.json_utils import slash_prefix_guarantee
from rct229.utils.jsonpath_utils import find_all
from rct229.utils.pint_utils import calcq_to_q
from rct229.utils.std_comparisons import std_equal_with_precision


class RCTPrecision(TypedDict):
precision: float
unit: str | None


class RuleDefinitionBase:
Expand All @@ -14,17 +24,18 @@ def __init__(
self,
rmds_used,
rmds_used_optional=None,
id=None,
description=None,
ruleset_section_title=None,
standard_section=None,
is_primary_rule=None,
rmd_context="",
id: str = None,
description: str = None,
ruleset_section_title: str = None,
standard_section: str = None,
is_primary_rule: bool = None,
rmd_context: str = "",
required_fields=None,
manual_check_required_msg="",
fail_msg="",
pass_msg="",
not_applicable_msg="",
manual_check_required_msg: str = "",
fail_msg: str = "",
pass_msg: str = "",
not_applicable_msg: str = "",
precision: Mapping[str, RCTPrecision] = None,
):
"""Base class for all Rule definitions
Expand Down Expand Up @@ -67,6 +78,15 @@ def __init__(
default message for PASS outcome
not_applicable_msg: string
default message for NOT_APPLICABLE outcome
precision: dict
precision value(s) in a dictionary
e.g.,
{
"subsurface_u_factor_b": {
"precision": 0.01,
"unit": "Btu/(hr*ft2*R)"
}
}
"""
self.rmds_used = rmds_used
self.rmds_used_optional = rmds_used_optional
Expand All @@ -85,6 +105,22 @@ def __init__(
self.not_applicable_msg = not_applicable_msg
self.fail_msg = fail_msg
self.pass_msg = pass_msg
self.precision_comparison = None

if precision:
self.precision_comparison = {
# if no unit, handle it as a dimensionless value.
key: partial(
std_equal_with_precision,
precision=val["precision"] * ureg(val["unit"])
if val.get("unit")
else val["precision"],
)
for key, val in precision.items()
}
else:
# default comparison to be strict equality comparison
self.precision_comparison = lambda val, std_val: val == std_val

def evaluate(self, rmds, data={}):
"""Generates the outcome dictionary for the rule
Expand Down
4 changes: 3 additions & 1 deletion rct229/rule_engine/rule_list_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def __init__(
# An example data object:
# {"cliimate_zone": ("baseline", weather/climate_zone")}
data_items=None,
precision=None,
):
self.each_rule = each_rule
self.data_items = data_items
Expand All @@ -40,6 +41,7 @@ def __init__(
ruleset_section_title=ruleset_section_title,
standard_section=standard_section,
is_primary_rule=is_primary_rule,
precision=precision,
)

def create_context_list(self, context, data):
Expand Down Expand Up @@ -167,7 +169,7 @@ def rule_check(self, context, calc_vals=None, data={}):
if self.list_filter(context_item, data)
]

# Evalutate the subrule for each item in the context list
# Evaluate the subrule for each item in the context list
outcomes = []
for ubp in filtered_context_list:
item_outcome = self.each_rule.evaluate(ubp, data)
Expand Down
2 changes: 2 additions & 0 deletions rct229/rule_engine/rule_list_indexed_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ def __init__(
manual_check_required_msg="Manual Check Required",
not_applicable_msg="Not Applicable",
data_items=None,
precision=None,
):
self.index_rmd = index_rmd
self.list_path = list_path
Expand All @@ -90,6 +91,7 @@ def __init__(
ruleset_section_title=ruleset_section_title,
standard_section=standard_section,
is_primary_rule=is_primary_rule,
precision=precision,
)

def create_context_list(self, context, data):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def aggregate_min_OA_schedule_across_zones(
)

if len(zone_OA_CFM_list_of_schedules) == 1:
# one schedule sceanrio
# one schedule scenario
hourly_zone_running_OA_CFM_total = zone_OA_CFM_list_of_schedules[0]
else:
# multi-schedule scenario
Expand Down
12 changes: 11 additions & 1 deletion rct229/rulesets/ashrae9012019/section18/section18rule1.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,12 @@ def __init__(self):
rmds_used=produce_ruleset_model_description(
USER=False, BASELINE_0=True, PROPOSED=True
),
precision={
"building_total_lab_exhaust_p": {
"precision": 1,
"unit": "cfm",
},
},
)

def get_calc_vals(self, context, data=None):
Expand Down Expand Up @@ -169,7 +175,11 @@ def manual_check_required(self, context, calc_vals=None, data=None):
is_undetermined = False
if system_origin_b == SYSTEMORIGIN.G311D:
is_undetermined = (
building_total_lab_exhaust_p <= EXHAUST_AIRFLOW_15000
building_total_lab_exhaust_p < EXHAUST_AIRFLOW_15000
or self.precision_comparison["building_total_lab_exhaust_p"](
building_total_lab_exhaust_p,
EXHAUST_AIRFLOW_15000,
)
)
elif system_origin_b == SYSTEMORIGIN.G311E:
is_undetermined = is_zone_likely_a_vestibule_b[zone_id_b]
Expand Down
28 changes: 24 additions & 4 deletions rct229/rulesets/ashrae9012019/section19/section19rule1.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,14 @@ def __init__(self):
rmds_used=produce_ruleset_model_description(
USER=False, BASELINE_0=True, PROPOSED=False
),
precision={
"heating_oversizing_factor": {
"precision": 0.01,
},
"cooling_oversizing_factor": {
"precision": 0.01,
},
},
)

def is_applicable(self, context, data=None):
Expand Down Expand Up @@ -153,18 +161,30 @@ def rule_check(self, context, calc_vals=None, data=None):

return (
(
REQ_HEATING_OVERSIZING_FACTOR == heating_oversizing_factor
and REQ_COOLING_OVERSIZING_FACTOR == cooling_oversizing_factor
self.precision_comparison["heating_oversizing_factor"](
heating_oversizing_factor,
REQ_HEATING_OVERSIZING_FACTOR,
)
and self.precision_comparison["cooling_oversizing_factor"](
cooling_oversizing_factor,
REQ_COOLING_OVERSIZING_FACTOR,
)
and heating_is_sized_based_on_design_day
and cooling_is_sized_based_on_design_day
)
or (
REQ_HEATING_OVERSIZING_FACTOR == heating_oversizing_factor
self.precision_comparison["heating_oversizing_factor"](
heating_oversizing_factor,
REQ_HEATING_OVERSIZING_FACTOR,
)
and heating_is_sized_based_on_design_day
and not cooling_oversizing_applicable
)
or (
REQ_COOLING_OVERSIZING_FACTOR == cooling_oversizing_factor
self.precision_comparison["cooling_oversizing_factor"](
cooling_oversizing_factor,
REQ_COOLING_OVERSIZING_FACTOR,
)
and cooling_is_sized_based_on_design_day
and not heating_oversizing_applicable
)
Expand Down
19 changes: 19 additions & 0 deletions rct229/rulesets/ashrae9012019/section19/section19rule12.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ def __init__(self):
required_fields={
"$": ["fan_system"],
},
precision={
"high_limit_temp_b": {
"precision": 0.1,
"unit": "K",
},
},
)

def is_applicable(self, context, data=None):
Expand Down Expand Up @@ -103,6 +109,19 @@ def rule_check(self, context, calc_vals=None, data=None):
high_limit_temp_b = calc_vals["high_limit_temp_b"]
air_economizer_type_b = calc_vals["air_economizer_type_b"]

return (
self.precision_comparison["high_limit_temp_b"](
high_limit_temp_b.to(ureg.kelvin),
req_high_limit_temp.to(ureg.kelvin),
)
and air_economizer_type_b == AIR_ECONOMIZER.TEMPERATURE
)

def is_tolerance_fail(self, context, calc_vals=None, data=None):
req_high_limit_temp = calc_vals["req_high_limit_temp"]
high_limit_temp_b = calc_vals["high_limit_temp_b"]
air_economizer_type_b = calc_vals["air_economizer_type_b"]

return (
std_equal(
req_high_limit_temp.to(ureg.kelvin),
Expand Down
72 changes: 62 additions & 10 deletions rct229/rulesets/ashrae9012019/section19/section19rule13.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from rct229.schema.config import ureg
from rct229.schema.schema_enums import SchemaEnums
from rct229.utils.assertions import getattr_
from rct229.utils.compare_standard_val import std_le
from rct229.utils.jsonpath_utils import find_all, find_one
from rct229.utils.pint_utils import ZERO, CalcQ
from rct229.utils.std_comparisons import std_equal
Expand Down Expand Up @@ -64,6 +65,16 @@ def __init__(self):
is_primary_rule=True,
rmd_context="ruleset_model_descriptions/0",
list_path="$.buildings[*].building_segments[*].heating_ventilating_air_conditioning_systems[*]",
precision={
"delta_supply_and_design_heating_temp": {
"precision": 1,
"unit": "delta_degF",
},
"delta_supply_and_design_cooling_temp": {
"precision": 1,
"unit": "delta_degF",
},
},
)

def is_applicable(self, context, data=None):
Expand Down Expand Up @@ -153,13 +164,17 @@ def create_data(self, context, data):
"all_design_setpoints_delta_Ts_are_per_reqs"
] = all(
[
std_equal(
LABORATORY_TEMP_DELTA,
self.precision_comparison[
"delta_supply_and_design_heating_temp"
](
delta_supply_and_design_heating_temp,
),
std_equal(
LABORATORY_TEMP_DELTA,
),
self.precision_comparison[
"delta_supply_and_design_cooling_temp"
](
delta_supply_and_design_cooling_temp,
LABORATORY_TEMP_DELTA,
),
]
)
Expand All @@ -168,13 +183,17 @@ def create_data(self, context, data):
"all_design_setpoints_delta_Ts_are_per_reqs"
] = all(
[
std_equal(
GENERAL_TEMP_DELTA,
self.precision_comparison[
"delta_supply_and_design_heating_temp"
](
delta_supply_and_design_heating_temp,
),
std_equal(
GENERAL_TEMP_DELTA,
),
self.precision_comparison[
"delta_supply_and_design_cooling_temp"
](
delta_supply_and_design_cooling_temp,
GENERAL_TEMP_DELTA,
),
]
)
Expand Down Expand Up @@ -210,6 +229,12 @@ def __init__(self):
"$": ["fan_system"],
"fan_system": ["minimum_outdoor_airflow"],
},
precision={
"supply_fans_airflow_b": {
"precision": 1,
"unit": "cfm",
},
},
)

def get_calc_vals(self, context, data=None):
Expand Down Expand Up @@ -249,8 +274,12 @@ def manual_check_required(self, context, calc_vals=None, data=None):
"all_design_setpoints_delta_Ts_are_per_reqs_b"
]

return not all_design_setpoints_delta_Ts_are_per_reqs_b and std_equal(
zone_info["supply_flow_p"], supply_fans_airflow_b
return (
not all_design_setpoints_delta_Ts_are_per_reqs_b
and self.precision_comparison["supply_fans_airflow_b"](
supply_fans_airflow_b,
zone_info["supply_flow_p"],
)
)

def get_manual_check_required_msg(self, context, calc_vals=None, data=None):
Expand All @@ -274,6 +303,29 @@ def rule_check(self, context, calc_vals=None, data=None):
all_design_setpoints_delta_Ts_are_per_reqs_b
and are_all_hvac_sys_fan_objs_autosized_b
and supply_fans_airflow_b >= fan_minimum_outdoor_airflow_b
) or (
(
not all_design_setpoints_delta_Ts_are_per_reqs_b
and fan_minimum_outdoor_airflow_b == supply_fans_airflow_b
)
)

def is_tolerance_fail(self, context, calc_vals=None, data=None):
all_design_setpoints_delta_Ts_are_per_reqs_b = calc_vals[
"all_design_setpoints_delta_Ts_are_per_reqs_b"
]
are_all_hvac_sys_fan_objs_autosized_b = calc_vals[
"are_all_hvac_sys_fan_objs_autosized_b"
]
supply_fans_airflow_b = calc_vals["supply_fans_airflow_b"]
fan_minimum_outdoor_airflow_b = calc_vals["fan_minimum_outdoor_airflow_b"]

return (
all_design_setpoints_delta_Ts_are_per_reqs_b
and are_all_hvac_sys_fan_objs_autosized_b
and std_le(
val=supply_fans_airflow_b, std_val=fan_minimum_outdoor_airflow_b
)
) or (
(
not all_design_setpoints_delta_Ts_are_per_reqs_b
Expand Down
Loading

0 comments on commit bd3a5d7

Please sign in to comment.