diff --git a/rct229/rule_engine/partial_rule_definition.py b/rct229/rule_engine/partial_rule_definition.py index 556bc01bcb..ccd9ba3a0b 100644 --- a/rct229/rule_engine/partial_rule_definition.py +++ b/rct229/rule_engine/partial_rule_definition.py @@ -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): @@ -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) @@ -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={}): diff --git a/rct229/rule_engine/rule_base.py b/rct229/rule_engine/rule_base.py index 9178ced3b1..b49a428cf6 100644 --- a/rct229/rule_engine/rule_base.py +++ b/rct229/rule_engine/rule_base.py @@ -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: @@ -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 @@ -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 @@ -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 diff --git a/rct229/rule_engine/rule_list_base.py b/rct229/rule_engine/rule_list_base.py index 64846da2d0..c26806c5dd 100644 --- a/rct229/rule_engine/rule_list_base.py +++ b/rct229/rule_engine/rule_list_base.py @@ -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 @@ -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): @@ -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) diff --git a/rct229/rule_engine/rule_list_indexed_base.py b/rct229/rule_engine/rule_list_indexed_base.py index 6283305b06..89acc933ad 100644 --- a/rct229/rule_engine/rule_list_indexed_base.py +++ b/rct229/rule_engine/rule_list_indexed_base.py @@ -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 @@ -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): diff --git a/rct229/rulesets/ashrae9012019/ruleset_functions/aggregate_min_OA_schedule_across_zones.py b/rct229/rulesets/ashrae9012019/ruleset_functions/aggregate_min_OA_schedule_across_zones.py index 1fdd66199c..9528e78020 100644 --- a/rct229/rulesets/ashrae9012019/ruleset_functions/aggregate_min_OA_schedule_across_zones.py +++ b/rct229/rulesets/ashrae9012019/ruleset_functions/aggregate_min_OA_schedule_across_zones.py @@ -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 diff --git a/rct229/rulesets/ashrae9012019/section18/section18rule1.py b/rct229/rulesets/ashrae9012019/section18/section18rule1.py index 1172c71887..ca93c76583 100644 --- a/rct229/rulesets/ashrae9012019/section18/section18rule1.py +++ b/rct229/rulesets/ashrae9012019/section18/section18rule1.py @@ -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): @@ -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] diff --git a/rct229/rulesets/ashrae9012019/section19/section19rule1.py b/rct229/rulesets/ashrae9012019/section19/section19rule1.py index b8bc88e616..05bb2051ff 100644 --- a/rct229/rulesets/ashrae9012019/section19/section19rule1.py +++ b/rct229/rulesets/ashrae9012019/section19/section19rule1.py @@ -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): @@ -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 ) diff --git a/rct229/rulesets/ashrae9012019/section19/section19rule12.py b/rct229/rulesets/ashrae9012019/section19/section19rule12.py index d6c821f814..941593b7ee 100644 --- a/rct229/rulesets/ashrae9012019/section19/section19rule12.py +++ b/rct229/rulesets/ashrae9012019/section19/section19rule12.py @@ -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): @@ -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), diff --git a/rct229/rulesets/ashrae9012019/section19/section19rule13.py b/rct229/rulesets/ashrae9012019/section19/section19rule13.py index 078c881978..e6a8972e19 100644 --- a/rct229/rulesets/ashrae9012019/section19/section19rule13.py +++ b/rct229/rulesets/ashrae9012019/section19/section19rule13.py @@ -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 @@ -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): @@ -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, ), ] ) @@ -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, ), ] ) @@ -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): @@ -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): @@ -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 diff --git a/rct229/rulesets/ashrae9012019/section19/section19rule14.py b/rct229/rulesets/ashrae9012019/section19/section19rule14.py index befb1505fb..965d10bbea 100644 --- a/rct229/rulesets/ashrae9012019/section19/section19rule14.py +++ b/rct229/rulesets/ashrae9012019/section19/section19rule14.py @@ -126,6 +126,12 @@ def __init__(self): required_fields={ "$": ["fan_system"], }, + precision={ + "modeled_cfm": { + "precision": 1, + "unit": "cfm", + }, + }, ) def is_applicable(self, context, data=None): @@ -237,6 +243,36 @@ def rule_check(self, context, calc_vals=None, data=None): is_modeled_with_return_fan_in_p = calc_vals["is_modeled_with_return_fan_p"] is_modeled_with_relief_fan_p = calc_vals["is_modeled_with_relief_fan_p"] + return ( + baseline_modeled_return_as_expected + and baseline_modeled_relief_as_expected + and (is_modeled_with_return_fan_in_p or is_modeled_with_relief_fan_p) + and self.precision_comparison["modeled_cfm"]( + modeled_cfm, + max(supply_minus_OA_flow, supply_cfm_90_percent), + ) + ) or ( + not is_modeled_with_return_fan_in_p + and not is_modeled_with_relief_fan_p + and std_equal(ZERO.FLOW, return_fans_airflow) + and std_equal(ZERO.FLOW, relief_fans_airflow) + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): + return_fans_airflow = calc_vals["return_fans_airflow"] + relief_fans_airflow = calc_vals["relief_fans_airflow"] + baseline_modeled_return_as_expected = calc_vals[ + "baseline_modeled_return_as_expected" + ] + baseline_modeled_relief_as_expected = calc_vals[ + "baseline_modeled_relief_as_expected" + ] + modeled_cfm = calc_vals["modeled_cfm"] + supply_minus_OA_flow = calc_vals["supply_minus_OA_flow"] + supply_cfm_90_percent = calc_vals["supply_cfm_90_percent"] + is_modeled_with_return_fan_in_p = calc_vals["is_modeled_with_return_fan_p"] + is_modeled_with_relief_fan_p = calc_vals["is_modeled_with_relief_fan_p"] + return ( baseline_modeled_return_as_expected and baseline_modeled_relief_as_expected diff --git a/rct229/rulesets/ashrae9012019/section19/section19rule15.py b/rct229/rulesets/ashrae9012019/section19/section19rule15.py index bf6a9155fc..50fd3edb84 100644 --- a/rct229/rulesets/ashrae9012019/section19/section19rule15.py +++ b/rct229/rulesets/ashrae9012019/section19/section19rule15.py @@ -22,6 +22,7 @@ ) from rct229.schema.config import ureg from rct229.utils.assertions import getattr_ +from rct229.utils.compare_standard_val import std_lt from rct229.utils.jsonpath_utils import find_all from rct229.utils.pint_utils import ZERO from rct229.utils.std_comparisons import std_equal @@ -136,6 +137,16 @@ def __init__(self): "$": ["fan_system"], "fan_system": ["minimum_outdoor_airflow"], }, + precision={ + "supply_fan_airflow_b": { + "precision": 1, + "unit": "cfm", + }, + "proposed_supply_flow": { + "precision": 1, + "unit": "cfm", + }, + }, ) def is_applicable(self, context, data=None): @@ -187,7 +198,7 @@ def manual_check_required(self, context, calc_vals=None, data=None): return ( supply_fan_qty_b == 1 and not all_design_setpoints_105 - and std_equal( + and self.precision_comparison["proposed_supply_flow"]( proposed_supply_flow, supply_fan_airflow_b, ) @@ -207,7 +218,7 @@ def get_manual_check_required_msg(self, context, calc_vals=None, data=None): if ( supply_fan_qty_b == 1 and not all_design_setpoints_105 - and std_equal( + and self.precision_comparison["proposed_supply_flow"]( proposed_supply_flow, supply_fan_airflow_b, ) @@ -234,6 +245,28 @@ def rule_check(self, context, calc_vals=None, data=None): all_design_setpoints_105 and supply_fan_airflow_b > minimum_outdoor_airflow_b ) + or ( + not all_design_setpoints_105 + and minimum_outdoor_airflow_b == supply_fan_airflow_b + ) + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): + hvac_id_b = calc_vals["hvac_id_b"] + supply_fan_qty_b = calc_vals["supply_fan_qty_b"] + supply_fan_airflow_b = calc_vals["supply_fan_airflow_b"] + minimum_outdoor_airflow_b = calc_vals["minimum_outdoor_airflow_b"] + all_design_setpoints_105 = data["hvac_info_dict_b"][hvac_id_b][ + "all_design_setpoints_105" + ] + + return supply_fan_qty_b == 1 and ( + ( + all_design_setpoints_105 + and std_lt( + val=supply_fan_airflow_b, std_val=minimum_outdoor_airflow_b + ) + ) or ( not all_design_setpoints_105 and std_equal(minimum_outdoor_airflow_b, supply_fan_airflow_b) diff --git a/rct229/rulesets/ashrae9012019/section19/section19rule17.py b/rct229/rulesets/ashrae9012019/section19/section19rule17.py index 611a97ee62..2bb90ec908 100644 --- a/rct229/rulesets/ashrae9012019/section19/section19rule17.py +++ b/rct229/rulesets/ashrae9012019/section19/section19rule17.py @@ -95,6 +95,12 @@ def __init__(self): required_fields={ "$": ["fan_system"], }, + precision={ + "fan_power_airflow": { + "precision": 0.1, + "unit": "W/cfm", + }, + }, ) def is_applicable(self, context, data=None): @@ -142,4 +148,10 @@ def get_calc_vals(self, context, data=None): def rule_check(self, context, calc_vals=None, data=None): fan_power_airflow = calc_vals["fan_power_airflow"] - return fan_power_airflow <= FAN_POWER_AIRFLOW_LIMIT + return ( + fan_power_airflow < FAN_POWER_AIRFLOW_LIMIT + or self.precision_comparison["fan_power_airflow"]( + fan_power_airflow, + FAN_POWER_AIRFLOW_LIMIT, + ) + ) diff --git a/rct229/rulesets/ashrae9012019/section19/section19rule18.py b/rct229/rulesets/ashrae9012019/section19/section19rule18.py index 1c9ea7b148..9e4ff1127b 100644 --- a/rct229/rulesets/ashrae9012019/section19/section19rule18.py +++ b/rct229/rulesets/ashrae9012019/section19/section19rule18.py @@ -139,6 +139,12 @@ def __init__(self): required_fields={ "$": ["fan_system"], }, + precision={ + "total_fan_power_b": { + "precision": 10, + "unit": "W", + }, + }, ) def is_applicable(self, context, data=None): @@ -321,7 +327,10 @@ def rule_check(self, context, calc_vals=None, data=None): total_fan_power_b = calc_vals["total_fan_power_b"] expected_fan_wattage_b = calc_vals["expected_fan_wattage_b"] - return std_equal(expected_fan_wattage_b, total_fan_power_b) + return self.precision_comparison["total_fan_power_b"]( + total_fan_power_b, + expected_fan_wattage_b, + ) def get_fail_msg(self, context, calc_vals=None, data=None): total_fan_power_b = calc_vals["total_fan_power_b"] diff --git a/rct229/rulesets/ashrae9012019/section19/section19rule19.py b/rct229/rulesets/ashrae9012019/section19/section19rule19.py index 1914855dec..4a9d20cffb 100644 --- a/rct229/rulesets/ashrae9012019/section19/section19rule19.py +++ b/rct229/rulesets/ashrae9012019/section19/section19rule19.py @@ -163,6 +163,12 @@ def __init__(self): required_fields={ "$": ["fan_system"], }, + precision={ + "fan_power_per_flow_b": { + "precision": 0.1, + "unit": "W/cfm", + }, + }, ) def is_applicable(self, context, data=None): @@ -250,7 +256,13 @@ def manual_check_required(self, context, calc_vals=None, data=None): ] return more_than_one_supply_fan_b or ( - fan_power_per_flow_b >= REQ_FAN_POWER_FLOW_RATIO + ( + fan_power_per_flow_b > REQ_FAN_POWER_FLOW_RATIO + or self.precision_comparison["fan_power_per_flow_b"]( + fan_power_per_flow_b, + REQ_FAN_POWER_FLOW_RATIO, + ) + ) and ( zone_hvac_has_non_mech_cooling_p or zones_served_by_hvac_has_non_mech_cooling_bool_p @@ -278,6 +290,24 @@ def rule_check(self, context, calc_vals=None, data=None): ] fan_power_per_flow_b = calc_vals["fan_power_per_flow_b"] + return ( + not zone_hvac_has_non_mech_cooling_p + and not zones_served_by_hvac_has_non_mech_cooling_bool_p + and self.precision_comparison["fan_power_per_flow_b"]( + fan_power_per_flow_b, + REQ_FAN_POWER_FLOW_RATIO, + ) + ) or (fan_power_per_flow_b < REQ_FAN_POWER_FLOW_RATIO) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): + zones_served_by_hvac_has_non_mech_cooling_bool_p = calc_vals[ + "zones_served_by_hvac_has_non_mech_cooling_bool_p" + ] + zone_hvac_has_non_mech_cooling_p = calc_vals[ + "zone_hvac_has_non_mech_cooling_p" + ] + fan_power_per_flow_b = calc_vals["fan_power_per_flow_b"] + return ( not zone_hvac_has_non_mech_cooling_p and not zones_served_by_hvac_has_non_mech_cooling_bool_p diff --git a/rct229/rulesets/ashrae9012019/section19/section19rule20.py b/rct229/rulesets/ashrae9012019/section19/section19rule20.py index c450ea0e5f..d3735cd226 100644 --- a/rct229/rulesets/ashrae9012019/section19/section19rule20.py +++ b/rct229/rulesets/ashrae9012019/section19/section19rule20.py @@ -56,6 +56,24 @@ def __init__(self): required_fields={ "$": ["fan_system"], }, + precision={ + "hvac_sys_total_supply_fan_power_b": { + "precision": 10, + "unit": "W", + }, + "hvac_sys_total_return_fan_power_b": { + "precision": 10, + "unit": "W", + }, + "hvac_sys_total_exhaust_fan_power_b": { + "precision": 10, + "unit": "W", + }, + "hvac_sys_total_relief_fan_power_b": { + "precision": 10, + "unit": "W", + }, + }, ) def get_calc_vals(self, context, data=None): @@ -163,6 +181,50 @@ def get_calc_vals(self, context, data=None): } def rule_check(self, context, calc_vals=None, data=None): + hvac_sys_total_supply_fan_power_b = calc_vals[ + "hvac_sys_total_supply_fan_power_b" + ] + hvac_sys_total_return_fan_power_b = calc_vals[ + "hvac_sys_total_return_fan_power_b" + ] + hvac_sys_total_exhaust_fan_power_b = calc_vals[ + "hvac_sys_total_exhaust_fan_power_b" + ] + hvac_sys_total_relief_fan_power_b = calc_vals[ + "hvac_sys_total_relief_fan_power_b" + ] + expected_baseline_fan_power_supply = calc_vals[ + "expected_baseline_fan_power_supply" + ] + expected_baseline_fan_power_return = calc_vals[ + "expected_baseline_fan_power_return" + ] + expected_baseline_fan_power_exhaust = calc_vals[ + "expected_baseline_fan_power_exhaust" + ] + expected_baseline_fan_power_relief = calc_vals[ + "expected_baseline_fan_power_relief" + ] + return ( + self.precision_comparison["hvac_sys_total_supply_fan_power_b"]( + hvac_sys_total_supply_fan_power_b, + expected_baseline_fan_power_supply, + ) + and self.precision_comparison["hvac_sys_total_supply_fan_power_b"]( + hvac_sys_total_return_fan_power_b, + expected_baseline_fan_power_return, + ) + and self.precision_comparison["hvac_sys_total_exhaust_fan_power_b"]( + hvac_sys_total_exhaust_fan_power_b, + expected_baseline_fan_power_exhaust, + ) + and self.precision_comparison["hvac_sys_total_relief_fan_power_b"]( + hvac_sys_total_relief_fan_power_b, + expected_baseline_fan_power_relief, + ) + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): hvac_sys_total_supply_fan_power_b = calc_vals[ "hvac_sys_total_supply_fan_power_b" ] diff --git a/rct229/rulesets/ashrae9012019/section19/section19rule21.py b/rct229/rulesets/ashrae9012019/section19/section19rule21.py index 7af29a1404..53ca1bfdf2 100644 --- a/rct229/rulesets/ashrae9012019/section19/section19rule21.py +++ b/rct229/rulesets/ashrae9012019/section19/section19rule21.py @@ -246,6 +246,12 @@ def __init__(self): "maximum_outdoor_airflow", ], }, + precision={ + "OA_fraction_b": { + "precision": 0.01, + }, + "supply_airflow_b": {"precision": 1, "unit": "cfm"}, + }, ) def get_calc_vals(self, context, data=None): @@ -358,7 +364,9 @@ def get_calc_vals(self, context, data=None): } def manual_check_required(self, context, calc_vals=None, data=None): - OA_fraction_b = calc_vals["OA_fraction_b"] + OA_fraction_b = calc_vals[ + "OA_fraction_b" + ].magnitude # .magnitude is because `OA_fraction_b` is a `dimensionless` unit in pint supply_airflow_b = calc_vals["supply_airflow_b"] ER_modeled_b = calc_vals["ER_modeled_b"] ER_modeled_p = calc_vals["ER_modeled_p"] @@ -369,8 +377,20 @@ def manual_check_required(self, context, calc_vals=None, data=None): maximum_hvac_exhaust_b = calc_vals["maximum_hvac_exhaust_b"] return ( - OA_fraction_b >= OA_fraction_b_70 - and supply_airflow_b >= SUPPLY_AIRFLOW_5000CFM + ( + OA_fraction_b > OA_fraction_b_70 + or self.precision_comparison["OA_fraction_b"]( + OA_fraction_b, + OA_fraction_b_70, + ) + ) + and ( + supply_airflow_b > SUPPLY_AIRFLOW_5000CFM + or self.precision_comparison["supply_airflow_b"]( + supply_airflow_b, + SUPPLY_AIRFLOW_5000CFM, + ) + ) and not ER_modeled_b ) and ( # Case 7 @@ -399,7 +419,9 @@ def get_manual_check_required_msg(self, context, calc_vals=None, data=None): return UNDETERMINED_MSG def rule_check(self, context, calc_vals=None, data=None): - OA_fraction_b = calc_vals["OA_fraction_b"] + OA_fraction_b = calc_vals[ + "OA_fraction_b" + ].magnitude # .magnitude is because `OA_fraction_b` is a `dimensionless` unit in pint supply_airflow_b = calc_vals["supply_airflow_b"] ER_modeled_b = calc_vals["ER_modeled_b"] exception_1_applies = calc_vals["exception_1_applies"] @@ -418,8 +440,20 @@ def rule_check(self, context, calc_vals=None, data=None): return ( ( - OA_fraction_b >= OA_fraction_b_70 - and supply_airflow_b >= SUPPLY_AIRFLOW_5000CFM + ( + OA_fraction_b > OA_fraction_b_70 + or self.precision_comparison["OA_fraction_b"]( + OA_fraction_b, + OA_fraction_b_70, + ) + ) + and ( + supply_airflow_b > SUPPLY_AIRFLOW_5000CFM + or self.precision_comparison["supply_airflow_b"]( + supply_airflow_b, + SUPPLY_AIRFLOW_5000CFM, + ) + ) and ( # CASE 1 (ER_modeled_b) diff --git a/rct229/rulesets/ashrae9012019/section19/section19rule5.py b/rct229/rulesets/ashrae9012019/section19/section19rule5.py index 2d3df7c8cd..8d8858d46b 100644 --- a/rct229/rulesets/ashrae9012019/section19/section19rule5.py +++ b/rct229/rulesets/ashrae9012019/section19/section19rule5.py @@ -31,6 +31,13 @@ def __init__(self): ], }, manual_check_required_msg=UNDETERMINED_MSG, + precision={ + "coincident_unmet_load_hours_p": {"precision": 1, "unit": "hour"}, + "unmet_load_hours_heating_p + unmet_load_hours_cooling_p": { + "precision": 1, + "unit": "hour", + }, + }, ) def get_calc_vals(self, context, data=None): @@ -63,9 +70,21 @@ def rule_check(self, context, calc_vals=None, data=None): return ( coincident_unmet_load_hours_p is not None - and coincident_unmet_load_hours_p <= MAX_COINCIDENT_UNMET_LOAD_HOUR + and ( + coincident_unmet_load_hours_p < MAX_COINCIDENT_UNMET_LOAD_HOUR + or self.precision_comparison["coincident_unmet_load_hours_p"]( + coincident_unmet_load_hours_p, + MAX_COINCIDENT_UNMET_LOAD_HOUR, + ) + ) ) or ( # we are certain at this step, both unmet_load_hours_heating_p and unmet_load_hours_cooling_p can't be None unmet_load_hours_heating_p + unmet_load_hours_cooling_p - <= MAX_SUM_HEATING_COOLING_UNMET_HOUR + < MAX_SUM_HEATING_COOLING_UNMET_HOUR + or self.precision_comparison[ + "unmet_load_hours_heating_p + unmet_load_hours_cooling_p" + ]( + unmet_load_hours_heating_p + unmet_load_hours_cooling_p, + MAX_SUM_HEATING_COOLING_UNMET_HOUR, + ) ) diff --git a/rct229/rulesets/ashrae9012019/section19/section19rule6.py b/rct229/rulesets/ashrae9012019/section19/section19rule6.py index 51b7a7b830..55d3919998 100644 --- a/rct229/rulesets/ashrae9012019/section19/section19rule6.py +++ b/rct229/rulesets/ashrae9012019/section19/section19rule6.py @@ -31,6 +31,13 @@ def __init__(self): ], }, manual_check_required_msg=UNDETERMINED_MSG, + precision={ + "coincident_unmet_load_hours_b": {"precision": 1, "unit": "hour"}, + "unmet_load_hours_heating_b + unmet_load_hours_cooling_b": { + "precision": 1, + "unit": "hour", + }, + }, ) def get_calc_vals(self, context, data=None): @@ -63,9 +70,21 @@ def rule_check(self, context, calc_vals=None, data=None): return ( coincident_unmet_load_hours_b is not None - and coincident_unmet_load_hours_b <= MAX_COINCIDENT_UNMET_LOAD_HOUR + and ( + coincident_unmet_load_hours_b < MAX_COINCIDENT_UNMET_LOAD_HOUR + or self.precision_comparison["coincident_unmet_load_hours_b"]( + coincident_unmet_load_hours_b, + MAX_COINCIDENT_UNMET_LOAD_HOUR, + ) + ) ) or ( # we are certain at this step, both unmet_load_hours_heating_b and unmet_load_hours_cooling_b can't be None unmet_load_hours_heating_b + unmet_load_hours_cooling_b - <= MAX_SUM_HEATING_COOLING_UNMET_HOUR + < MAX_SUM_HEATING_COOLING_UNMET_HOUR + or self.precision_comparison[ + "unmet_load_hours_heating_b + unmet_load_hours_cooling_b" + ]( + unmet_load_hours_heating_b + unmet_load_hours_cooling_b, + MAX_SUM_HEATING_COOLING_UNMET_HOUR, + ) ) diff --git a/rct229/rulesets/ashrae9012019/section19/section19rule7.py b/rct229/rulesets/ashrae9012019/section19/section19rule7.py index ab4273e512..dfbff1c7df 100644 --- a/rct229/rulesets/ashrae9012019/section19/section19rule7.py +++ b/rct229/rulesets/ashrae9012019/section19/section19rule7.py @@ -172,6 +172,12 @@ def __init__(self): rmds_used=produce_ruleset_model_description( USER=False, BASELINE_0=True, PROPOSED=False ), + precision={ + "aggregated_min_OA_schedule_across_zones_b": { + "precision": 1, + "unit": "cfm", + }, + }, ) def is_applicable(self, context, data=None): @@ -203,9 +209,16 @@ def get_calc_vals(self, context, data=None): "zone_air_distribution_effectiveness_greater_than_1" ] - OA_CFM_schedule_match = ( - aggregated_min_OA_schedule_across_zones_b - == aggregated_min_OA_schedule_across_zones_p + OA_CFM_schedule_match = all( + [ + self.precision_comparison[ + "aggregated_min_OA_schedule_across_zones_b" + ]( + aggregated_min_OA_schedule_across_zones_b[i], + aggregated_min_OA_schedule_across_zones_p[i], + ) + for i in range(len(aggregated_min_OA_schedule_across_zones_b)) + ] ) modeled_baseline_total_zone_min_OA_CFM = sum( @@ -316,7 +329,6 @@ def get_fail_msg(self, context, calc_vals=None, data=None): was_DCV_modeled_baseline = calc_vals["was_DCV_modeled_baseline"] was_DCV_modeled_proposed = calc_vals["was_DCV_modeled_proposed"] - Fail_msg = "" if ( modeled_baseline_total_zone_min_OA_CFM > modeled_proposed_total_zone_min_OA_CFM diff --git a/rct229/rulesets/ashrae9012019/section19/section19rule8.py b/rct229/rulesets/ashrae9012019/section19/section19rule8.py index 5517c15ec4..790564175a 100644 --- a/rct229/rulesets/ashrae9012019/section19/section19rule8.py +++ b/rct229/rulesets/ashrae9012019/section19/section19rule8.py @@ -79,6 +79,10 @@ def __init__(self): "$": ["fan_system"], "fan_system": ["minimum_outdoor_airflow"], }, + precision={ + "hvac_min_OA_flow": {"precision": 0.1, "unit": "cfm"}, + "avg_occ_density": {"precision": 0.1, "unit": "1/ft2"}, + }, ) def get_calc_vals(self, context, data=None): @@ -132,11 +136,29 @@ def rule_check(self, context, calc_vals=None, data=None): is_DCV_modeled_b = calc_vals["is_DCV_modeled_b"] return ( - hvac_min_OA_flow > MIN_OA_CFM + ( + hvac_min_OA_flow > MIN_OA_CFM + or self.precision_comparison["hvac_min_OA_flow"]( + hvac_min_OA_flow, + MIN_OA_CFM, + ) + ) and avg_occ_density > OCCUPANT_DENSITY_LIMIT and is_DCV_modeled_b ) or ( - hvac_min_OA_flow <= MIN_OA_CFM - and avg_occ_density >= OCCUPANT_DENSITY_LIMIT + ( + hvac_min_OA_flow < MIN_OA_CFM + or self.precision_comparison["hvac_min_OA_flow"]( + hvac_min_OA_flow, + MIN_OA_CFM, + ) + ) + and ( + avg_occ_density > OCCUPANT_DENSITY_LIMIT + or self.precision_comparison["avg_occ_density"]( + avg_occ_density, + OCCUPANT_DENSITY_LIMIT, + ) + ) and not is_DCV_modeled_b ) diff --git a/rct229/rulesets/ashrae9012019/section21/section21rule17.py b/rct229/rulesets/ashrae9012019/section21/section21rule17.py index 3729b468b4..c2279a11c4 100644 --- a/rct229/rulesets/ashrae9012019/section21/section21rule17.py +++ b/rct229/rulesets/ashrae9012019/section21/section21rule17.py @@ -75,6 +75,11 @@ def __init__(self): required_fields={ "$": ["rated_capacity", "efficiency_metric", "efficiency"], }, + precision={ + "boiler_efficiency_b": { + "precision": 0.01, + }, + }, ) def get_calc_vals(self, context, data=None): @@ -99,17 +104,26 @@ def rule_check(self, context, calc_vals=None, data=None): boiler_rated_capacity_b < BOILER_RATED_CAPACITY_LOW_LIMIT and boiler_efficiency_metric_b == BOILER_EFFICIENCY_METRIC.ANNUAL_FUEL_UTILIZATION - and boiler_efficiency_b == BOILER_EFFICIENCY_80 + and self.precision_comparison["boiler_efficiency_b"]( + boiler_efficiency_b, + BOILER_EFFICIENCY_80, + ) ) or ( boiler_rated_capacity_b <= BOILER_RATED_CAPACITY_HIGH_LIMIT and boiler_efficiency_metric_b == BOILER_EFFICIENCY_METRIC.THERMAL - and boiler_efficiency_b == BOILER_EFFICIENCY_75 + and self.precision_comparison["boiler_efficiency_b"]( + boiler_efficiency_b, + BOILER_EFFICIENCY_75, + ) ) or ( boiler_rated_capacity_b > BOILER_RATED_CAPACITY_HIGH_LIMIT and boiler_efficiency_metric_b == BOILER_EFFICIENCY_METRIC.COMBUSTION - and boiler_efficiency_b == BOILER_EFFICIENCY_80 + and self.precision_comparison["boiler_efficiency_b"]( + boiler_efficiency_b, + BOILER_EFFICIENCY_80, + ) ) ) diff --git a/rct229/rulesets/ashrae9012019/section21/section21rule5.py b/rct229/rulesets/ashrae9012019/section21/section21rule5.py index 30ebad8cf7..25f43ec8bd 100644 --- a/rct229/rulesets/ashrae9012019/section21/section21rule5.py +++ b/rct229/rulesets/ashrae9012019/section21/section21rule5.py @@ -64,6 +64,12 @@ def __init__(self): rmds_used=produce_ruleset_model_description( USER=False, BASELINE_0=True, PROPOSED=False ), + precision={ + "heating_loop_conditioned_zone_area": { + "precision": 1, + "unit": "ft2", + } + }, ) def is_applicable(self, context, data=None): @@ -162,8 +168,14 @@ def rule_check(self, context, calc_vals=None, data=None): num_boilers = calc_vals["num_boilers"] boiler_capacity_list = calc_vals["boiler_capacity_list"] return ( - heating_loop_conditioned_zone_area - <= HEATING_LOOP_CONDITIONED_AREA_THRESHOLD + ( + heating_loop_conditioned_zone_area + < HEATING_LOOP_CONDITIONED_AREA_THRESHOLD + or self.precision_comparison["heating_loop_conditioned_zone_area"]( + heating_loop_conditioned_zone_area, + HEATING_LOOP_CONDITIONED_AREA_THRESHOLD, + ) + ) and num_boilers == 1 ) or ( heating_loop_conditioned_zone_area diff --git a/rct229/rulesets/ashrae9012019/section21/section21rule6.py b/rct229/rulesets/ashrae9012019/section21/section21rule6.py index dcff34195c..2f511ed94c 100644 --- a/rct229/rulesets/ashrae9012019/section21/section21rule6.py +++ b/rct229/rulesets/ashrae9012019/section21/section21rule6.py @@ -84,6 +84,16 @@ def __init__(self): rmds_used=produce_ruleset_model_description( USER=False, BASELINE_0=True, PROPOSED=False ), + precision={ + "boiler_1_operation_upper_limit": { + "precision": 1, + "unit": "Btu/hr", + }, + "boiler_2_operation_upper_limit": { + "precision": 1, + "unit": "Btu/hr", + }, + }, ) def is_applicable(self, context, data=None): @@ -127,6 +137,32 @@ def rule_check(self, context, calc_vals=None, data=None): boiler_2_operation_upper_limit = calc_vals["boiler_2_operation_upper_limit"] boiler_2_rated_capacity = calc_vals["boiler_2_rated_capacity"] + return ( + boiler_1_operation_lower_limit == ZERO.POWER + and boiler_1_operation_upper_limit == boiler_1_rated_capacity + and boiler_2_operation_lower_limit == boiler_1_rated_capacity + and self.precision_comparison["boiler_2_operation_upper_limit"]( + boiler_2_operation_upper_limit, + boiler_1_rated_capacity + boiler_2_rated_capacity, + ) + ) or ( + boiler_2_operation_lower_limit == ZERO.POWER + and boiler_2_operation_upper_limit == boiler_2_rated_capacity + and boiler_1_operation_lower_limit == boiler_2_rated_capacity + and self.precision_comparison["boiler_1_operation_upper_limit"]( + boiler_1_operation_upper_limit, + boiler_2_rated_capacity + boiler_1_rated_capacity, + ) + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): + boiler_1_operation_lower_limit = calc_vals["boiler_1_operation_lower_limit"] + boiler_1_operation_upper_limit = calc_vals["boiler_1_operation_upper_limit"] + boiler_1_rated_capacity = calc_vals["boiler_1_rated_capacity"] + boiler_2_operation_lower_limit = calc_vals["boiler_2_operation_lower_limit"] + boiler_2_operation_upper_limit = calc_vals["boiler_2_operation_upper_limit"] + boiler_2_rated_capacity = calc_vals["boiler_2_rated_capacity"] + return ( boiler_1_operation_lower_limit == ZERO.POWER and std_equal(boiler_1_operation_upper_limit, boiler_1_rated_capacity) diff --git a/rct229/rulesets/ashrae9012019/section21/section21rule7.py b/rct229/rulesets/ashrae9012019/section21/section21rule7.py index a3a5f8f770..cf4ff83e9a 100644 --- a/rct229/rulesets/ashrae9012019/section21/section21rule7.py +++ b/rct229/rulesets/ashrae9012019/section21/section21rule7.py @@ -86,6 +86,16 @@ def __init__(self): "design_return_temperature", ], }, + precision={ + "design_supply_temperature": { + "precision": 0.1, + "unit": "K", + }, + "design_return_temperature": { + "precision": 0.1, + "unit": "K", + }, + }, ) def get_calc_vals(self, context, data=None): @@ -112,6 +122,23 @@ def rule_check(self, context, calc_vals=None, data=None): design_return_temperature = calc_vals["design_return_temperature"] required_supply_temperature = calc_vals["required_supply_temperature"] required_return_temperature = calc_vals["required_return_temperature"] + + return design_supply_temperature.to( + ureg.kelvin + ) == required_supply_temperature.to( + ureg.kelvin + ) and design_return_temperature.to( + ureg.kelvin + ) == required_return_temperature.to( + ureg.kelvin + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): + design_supply_temperature = calc_vals["design_supply_temperature"] + design_return_temperature = calc_vals["design_return_temperature"] + required_supply_temperature = calc_vals["required_supply_temperature"] + required_return_temperature = calc_vals["required_return_temperature"] + return std_equal( design_supply_temperature.to(ureg.kelvin), required_supply_temperature.to(ureg.kelvin), diff --git a/rct229/rulesets/ashrae9012019/section21/section21rule8.py b/rct229/rulesets/ashrae9012019/section21/section21rule8.py index 291d571c19..4574af862d 100644 --- a/rct229/rulesets/ashrae9012019/section21/section21rule8.py +++ b/rct229/rulesets/ashrae9012019/section21/section21rule8.py @@ -80,6 +80,24 @@ def __init__(self): "loop_supply_temperature_at_outdoor_low", ], }, + precision={ + "design_outdoor_high_for_loop_supply_reset_temperature_b": { + "precision": 0.1, + "unit": "K", + }, + "design_outdoor_low_for_loop_supply_reset_temperature_b": { + "precision": 0.1, + "unit": "K", + }, + "design_supply_temperature_at_outdoor_high_b": { + "precision": 0.1, + "unit": "K", + }, + "design_supply_temperature_at_outdoor_low_b": { + "precision": 0.1, + "unit": "K", + }, + }, ) def get_calc_vals(self, context, data=None): @@ -156,8 +174,10 @@ def rule_check(self, context, calc_vals=None, data=None): "required_supply_temperature_at_outdoor_low_b" ] return ( - (temperature_reset_type_b == DESIGN_TEMP_RESET_TYPE.OUTSIDE_AIR_RESET) - and std_equal( + temperature_reset_type_b == DESIGN_TEMP_RESET_TYPE.OUTSIDE_AIR_RESET + and self.precision_comparison[ + "design_outdoor_high_for_loop_supply_reset_temperature_b" + ]( design_outdoor_high_for_loop_supply_reset_temperature_b.to( ureg.kelvin ), @@ -165,7 +185,9 @@ def rule_check(self, context, calc_vals=None, data=None): ureg.kelvin ), ) - and std_equal( + and self.precision_comparison[ + "design_outdoor_low_for_loop_supply_reset_temperature_b" + ]( design_outdoor_low_for_loop_supply_reset_temperature_b.to( ureg.kelvin ), @@ -173,11 +195,15 @@ def rule_check(self, context, calc_vals=None, data=None): ureg.kelvin ), ) - and std_equal( + and self.precision_comparison[ + "design_supply_temperature_at_outdoor_high_b" + ]( design_supply_temperature_at_outdoor_high_b.to(ureg.kelvin), required_supply_temperature_at_outdoor_high_b.to(ureg.kelvin), ) - and std_equal( + and self.precision_comparison[ + "design_supply_temperature_at_outdoor_low_b" + ]( design_supply_temperature_at_outdoor_low_b.to(ureg.kelvin), required_supply_temperature_at_outdoor_low_b.to(ureg.kelvin), ) diff --git a/rct229/rulesets/ashrae9012019/section21/section21rule9.py b/rct229/rulesets/ashrae9012019/section21/section21rule9.py index 467680e477..07eda4eed3 100644 --- a/rct229/rulesets/ashrae9012019/section21/section21rule9.py +++ b/rct229/rulesets/ashrae9012019/section21/section21rule9.py @@ -96,6 +96,13 @@ def get_calc_vals(self, context, data=None): } def rule_check(self, context, calc_vals=None, data=None): + pump_power_per_flow_rate = calc_vals["pump_power_per_flow_rate"] + required_pump_power_per_flow_rate = calc_vals[ + "required_pump_power_per_flow_rate" + ] + return required_pump_power_per_flow_rate == pump_power_per_flow_rate + + def is_tolerance_fail(self, context, calc_vals=None, data=None): pump_power_per_flow_rate = calc_vals["pump_power_per_flow_rate"] required_pump_power_per_flow_rate = calc_vals[ "required_pump_power_per_flow_rate" diff --git a/rct229/rulesets/ashrae9012019/section22/section22rule1.py b/rct229/rulesets/ashrae9012019/section22/section22rule1.py index b01edff27f..c839d5856f 100644 --- a/rct229/rulesets/ashrae9012019/section22/section22rule1.py +++ b/rct229/rulesets/ashrae9012019/section22/section22rule1.py @@ -97,6 +97,12 @@ def __init__(self): "design_supply_temperature" ], }, + precision={ + "design_supply_temperature": { + "precision": 0.1, + "unit": "K", + }, + }, ) def get_calc_vals(self, context, data=None): @@ -115,6 +121,15 @@ def get_calc_vals(self, context, data=None): def rule_check(self, context, calc_vals=None, data=None): design_supply_temperature = calc_vals["design_supply_temperature"] required_supply_temperature = calc_vals["required_supply_temperature"] + + return self.precision_comparison["design_supply_temperature"]( + design_supply_temperature.to(ureg.kelvin), + required_supply_temperature.to(ureg.kelvin), + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): + design_supply_temperature = calc_vals["design_supply_temperature"] + required_supply_temperature = calc_vals["required_supply_temperature"] return std_equal( design_supply_temperature.to(ureg.kelvin), required_supply_temperature.to(ureg.kelvin), diff --git a/rct229/rulesets/ashrae9012019/section22/section22rule11.py b/rct229/rulesets/ashrae9012019/section22/section22rule11.py index e4d9753f59..c8c1506cac 100644 --- a/rct229/rulesets/ashrae9012019/section22/section22rule11.py +++ b/rct229/rulesets/ashrae9012019/section22/section22rule11.py @@ -104,6 +104,12 @@ def __init__(self): required_fields={ "$": ["pump_power_per_flow_rate"], }, + precision={ + "secondary_loop_pump_power_per_flow_rate": { + "precision": 1, + "unit": "W/gpm", + }, + }, ) def get_calc_vals(self, context, data=None): @@ -129,6 +135,19 @@ def rule_check(self, context, calc_vals=None, data=None): ] req_pump_flow_rate = calc_vals["req_pump_flow_rate"] + return self.precision_comparison[ + "secondary_loop_pump_power_per_flow_rate" + ]( + secondary_loop_pump_power_per_flow_rate, + req_pump_flow_rate, + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): + secondary_loop_pump_power_per_flow_rate = calc_vals[ + "secondary_loop_pump_power_per_flow_rate" + ] + req_pump_flow_rate = calc_vals["req_pump_flow_rate"] + return std_equal( secondary_loop_pump_power_per_flow_rate, req_pump_flow_rate ) diff --git a/rct229/rulesets/ashrae9012019/section22/section22rule14.py b/rct229/rulesets/ashrae9012019/section22/section22rule14.py index ea01922e7c..38023e8c77 100644 --- a/rct229/rulesets/ashrae9012019/section22/section22rule14.py +++ b/rct229/rulesets/ashrae9012019/section22/section22rule14.py @@ -71,6 +71,12 @@ def __init__(self): required_fields={ "$": ["range"], }, + precision={ + "heat_rejection_range": { + "precision": 0.1, + "unit": "K", + }, + }, ) def get_calc_vals(self, context, data=None): @@ -88,6 +94,16 @@ def get_calc_vals(self, context, data=None): def rule_check(self, context, calc_vals=None, data=None): heat_rejection_range = calc_vals["heat_rejection_range"] required_heat_rejection_range = calc_vals["required_heat_rejection_range"] + + return self.precision_comparison["heat_rejection_range"]( + heat_rejection_range.to(ureg.kelvin), + required_heat_rejection_range.to(ureg.kelvin), + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): + heat_rejection_range = calc_vals["heat_rejection_range"] + required_heat_rejection_range = calc_vals["required_heat_rejection_range"] + return std_equal( heat_rejection_range.to(ureg.kelvin), required_heat_rejection_range.to(ureg.kelvin), diff --git a/rct229/rulesets/ashrae9012019/section22/section22rule15.py b/rct229/rulesets/ashrae9012019/section22/section22rule15.py index 649cc65575..ed18a5e031 100644 --- a/rct229/rulesets/ashrae9012019/section22/section22rule15.py +++ b/rct229/rulesets/ashrae9012019/section22/section22rule15.py @@ -49,6 +49,12 @@ def __init__(self): required_fields={ "$": ["approach", "loop", "design_wetbulb_temperature"], }, + precision={ + "approach_b": { + "precision": 0.1, + "unit": "K", + }, + }, ) def is_applicable(self, context, data=None): @@ -78,4 +84,6 @@ def rule_check(self, context, calc_vals=None, data=None): approach_b = calc_vals["approach_b"] target_approach_b = calc_vals["target_approach_b"] - return std_equal(target_approach_b.to(ureg.kelvin), approach_b) + return self.precision_comparison["approach_b"]( + target_approach_b.to(ureg.kelvin), approach_b + ) diff --git a/rct229/rulesets/ashrae9012019/section22/section22rule16.py b/rct229/rulesets/ashrae9012019/section22/section22rule16.py index 1a1b26d145..c9a11a0b4d 100644 --- a/rct229/rulesets/ashrae9012019/section22/section22rule16.py +++ b/rct229/rulesets/ashrae9012019/section22/section22rule16.py @@ -88,6 +88,12 @@ def __init__(self): rmds_used=produce_ruleset_model_description( USER=False, BASELINE_0=True, PROPOSED=False ), + precision={ + "design_supply_temperature_b": { + "precision": 0.1, + "unit": "K", + }, + }, ) def get_calc_vals(self, context, data=None): @@ -118,7 +124,8 @@ def rule_check(self, context, calc_vals=None, data=None): design_wetbulb_temperature = calc_vals["design_wetbulb_temperature_b"] approach_b = calc_vals["approach_b"] design_supply_temperature_b = calc_vals["design_supply_temperature_b"] - return std_equal( + + return self.precision_comparison["design_supply_temperature_b"]( design_supply_temperature_b.to(ureg.kelvin), design_wetbulb_temperature.to(ureg.kelvin) + approach_b.to(ureg.kelvin), ) diff --git a/rct229/rulesets/ashrae9012019/section22/section22rule17.py b/rct229/rulesets/ashrae9012019/section22/section22rule17.py index e1f46d8f00..ecd327bce9 100644 --- a/rct229/rulesets/ashrae9012019/section22/section22rule17.py +++ b/rct229/rulesets/ashrae9012019/section22/section22rule17.py @@ -35,6 +35,7 @@ def __init__(self): def is_applicable(self, context, data=None): rmd_b = context.BASELINE_0 + return bool(find_all("$.heat_rejections[*]", rmd_b)) def create_data(self, context, data): @@ -54,6 +55,12 @@ def __init__(self): required_fields={ "$": ["loop", "rated_water_flowrate"], }, + precision={ + "heat_rejection_efficiency_b": { + "precision": 0.1, + "unit": "gpm/hp", + }, + }, ) def is_applicable(self, context, data=None): @@ -109,11 +116,12 @@ def get_manual_check_required_msg(self, context, calc_vals=None, data=None): ) ) heat_rejection_efficiency_b = calc_vals["heat_rejection_efficiency_b"] - heat_rejection_efficiency_in_gpm_per_hp_b = round( - heat_rejection_efficiency_b.to(ureg("gpm/hp")).magnitude, 1 - ) + heat_rejection_efficiency_in_gpm_per_hp_b = heat_rejection_efficiency_b - if HEAT_REJ_EFF_LIMIT == heat_rejection_efficiency_b: + if self.precision_comparison["heat_rejection_efficiency_b"]( + heat_rejection_efficiency_b, + HEAT_REJ_EFF_LIMIT, + ): undetermined_msg = ( f"The project includes a cooling tower. We calculated the cooling tower efficiency to be correct at 38.2 gpm/hp. " f"However, it was not possible to verify that the modeling inputs correspond to the rating conditions in Table 6.8.1-7. " diff --git a/rct229/rulesets/ashrae9012019/section22/section22rule2.py b/rct229/rulesets/ashrae9012019/section22/section22rule2.py index 75fcc6d13e..46312bee80 100644 --- a/rct229/rulesets/ashrae9012019/section22/section22rule2.py +++ b/rct229/rulesets/ashrae9012019/section22/section22rule2.py @@ -97,6 +97,12 @@ def __init__(self): "design_return_temperature" ], }, + precision={ + "design_return_temperature": { + "precision": 1, + "unit": "K", + }, + }, ) def get_calc_vals(self, context, data=None): @@ -115,6 +121,15 @@ def get_calc_vals(self, context, data=None): def rule_check(self, context, calc_vals=None, data=None): design_return_temperature = calc_vals["design_return_temperature"] required_return_temperature = calc_vals["required_return_temperature"] + + return self.precision_comparison["design_return_temperature"]( + design_return_temperature.to(ureg.kelvin), + required_return_temperature.to(ureg.kelvin), + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): + design_return_temperature = calc_vals["design_return_temperature"] + required_return_temperature = calc_vals["required_return_temperature"] return std_equal( design_return_temperature.to(ureg.kelvin), required_return_temperature.to(ureg.kelvin), diff --git a/rct229/rulesets/ashrae9012019/section22/section22rule20.py b/rct229/rulesets/ashrae9012019/section22/section22rule20.py index 99cf5a5756..3d8770dca8 100644 --- a/rct229/rulesets/ashrae9012019/section22/section22rule20.py +++ b/rct229/rulesets/ashrae9012019/section22/section22rule20.py @@ -82,6 +82,12 @@ def __init__(self): required_fields={ "$": ["leaving_water_setpoint_temperature"], }, + precision={ + "tower_leaving_temperature_b": { + "precision": 0.1, + "unit": "K", + }, + }, ) def get_calc_vals(self, context, data=None): @@ -108,6 +114,18 @@ def rule_check(self, context, calc_vals=None, data=None): leaving_water_setpoint_temperature_b = calc_vals[ "leaving_water_setpoint_temperature_b" ] + + return self.precision_comparison["tower_leaving_temperature_b"]( + tower_leaving_temperature_b.to(ureg.kelvin), + leaving_water_setpoint_temperature_b.to(ureg.kelvin), + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): + tower_leaving_temperature_b = calc_vals["tower_leaving_temperature_b"] + leaving_water_setpoint_temperature_b = calc_vals[ + "leaving_water_setpoint_temperature_b" + ] + return std_equal( tower_leaving_temperature_b.to(ureg.kelvin), leaving_water_setpoint_temperature_b.to(ureg.kelvin), diff --git a/rct229/rulesets/ashrae9012019/section22/section22rule22.py b/rct229/rulesets/ashrae9012019/section22/section22rule22.py index a7e8be3692..639ec19b8a 100644 --- a/rct229/rulesets/ashrae9012019/section22/section22rule22.py +++ b/rct229/rulesets/ashrae9012019/section22/section22rule22.py @@ -70,6 +70,11 @@ def __init__(self): required_fields={ "$": ["compressor_type", "rated_capacity", "full_load_efficiency"], }, + precision={ + "full_load_efficiency_b": { + "precision": 0.1, + }, + }, ) def get_calc_vals(self, context, data=None): @@ -93,6 +98,18 @@ def get_calc_vals(self, context, data=None): } def rule_check(self, context, calc_vals=None, data=None): + full_load_efficiency_b = calc_vals["full_load_efficiency_b"].magnitude + required_kw_ton_full_load_b = calc_vals["required_kw_ton_full_load_b"] + required_cop_full_load_b = ( + 1.0 / required_kw_ton_full_load_b.to("kilowatt / kilowatt").magnitude + ) # .magnitude is because `required_cop_full_load_b` is still a `dimensionless` pint quantify + + return self.precision_comparison["full_load_efficiency_b"]( + full_load_efficiency_b, + required_cop_full_load_b, + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): full_load_efficiency_b = calc_vals["full_load_efficiency_b"] required_kw_ton_full_load_b = calc_vals["required_kw_ton_full_load_b"] required_cop_full_load_b = 1.0 / required_kw_ton_full_load_b.to( diff --git a/rct229/rulesets/ashrae9012019/section22/section22rule25.py b/rct229/rulesets/ashrae9012019/section22/section22rule25.py index 6dc432de6b..4064b3be4d 100644 --- a/rct229/rulesets/ashrae9012019/section22/section22rule25.py +++ b/rct229/rulesets/ashrae9012019/section22/section22rule25.py @@ -102,6 +102,12 @@ def __init__(self): required_fields={ "$": ["pump_power_per_flow_rate"], }, + precision={ + "primary_pump_power_per_flow_rate": { + "precision": 1, + "unit": "W/gpm", + }, + }, ) def get_calc_vals(self, context, data=None): @@ -125,6 +131,19 @@ def rule_check(self, context, calc_vals=None, data=None): required_pump_power_per_flow_rate = calc_vals[ "required_pump_power_per_flow_rate" ] + + return self.precision_comparison["primary_pump_power_per_flow_rate"]( + required_pump_power_per_flow_rate, + primary_pump_power_per_flow_rate, + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): + primary_pump_power_per_flow_rate = calc_vals[ + "primary_pump_power_per_flow_rate" + ] + required_pump_power_per_flow_rate = calc_vals[ + "required_pump_power_per_flow_rate" + ] return std_equal( required_pump_power_per_flow_rate, primary_pump_power_per_flow_rate ) diff --git a/rct229/rulesets/ashrae9012019/section22/section22rule26.py b/rct229/rulesets/ashrae9012019/section22/section22rule26.py index cb11cf549e..d11297eb40 100644 --- a/rct229/rulesets/ashrae9012019/section22/section22rule26.py +++ b/rct229/rulesets/ashrae9012019/section22/section22rule26.py @@ -83,6 +83,12 @@ def __init__(self): required_fields={ "$": ["pump_power_per_flow_rate"], }, + precision={ + "primary_pump_power_per_flow_rate": { + "precision": 1, + "unit": "W/gpm", + }, + }, ) def get_calc_vals(self, context, data=None): @@ -106,6 +112,19 @@ def rule_check(self, context, calc_vals=None, data=None): required_pump_power_per_flow_rate = calc_vals[ "required_pump_power_per_flow_rate" ] + + return self.precision_comparison["primary_pump_power_per_flow_rate"]( + required_pump_power_per_flow_rate, + primary_pump_power_per_flow_rate, + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): + primary_pump_power_per_flow_rate = calc_vals[ + "primary_pump_power_per_flow_rate" + ] + required_pump_power_per_flow_rate = calc_vals[ + "required_pump_power_per_flow_rate" + ] return std_equal( required_pump_power_per_flow_rate, primary_pump_power_per_flow_rate ) diff --git a/rct229/rulesets/ashrae9012019/section22/section22rule29.py b/rct229/rulesets/ashrae9012019/section22/section22rule29.py index a5f56257c9..45d193730a 100644 --- a/rct229/rulesets/ashrae9012019/section22/section22rule29.py +++ b/rct229/rulesets/ashrae9012019/section22/section22rule29.py @@ -93,6 +93,12 @@ def __init__(self): required_fields={ "$": ["pump_power_per_flow_rate"], }, + precision={ + "pump_power_per_flow_rate": { + "precision": 1, + "unit": "W/gpm", + }, + }, ) def get_calc_vals(self, context, data=None): @@ -113,4 +119,13 @@ def rule_check(self, context, calc_vals=None, data=None): pump_power_per_flow_rate = calc_vals["pump_power_per_flow_rate"] required_pump_power = calc_vals["required_pump_power"] + return self.precision_comparison["pump_power_per_flow_rate"]( + pump_power_per_flow_rate, + required_pump_power, + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): + pump_power_per_flow_rate = calc_vals["pump_power_per_flow_rate"] + required_pump_power = calc_vals["required_pump_power"] + return std_equal(pump_power_per_flow_rate, required_pump_power) diff --git a/rct229/rulesets/ashrae9012019/section22/section22rule30.py b/rct229/rulesets/ashrae9012019/section22/section22rule30.py index a2873eab6b..8dde4d2bf3 100644 --- a/rct229/rulesets/ashrae9012019/section22/section22rule30.py +++ b/rct229/rulesets/ashrae9012019/section22/section22rule30.py @@ -73,6 +73,12 @@ def __init__(self): rmds_used=produce_ruleset_model_description( USER=False, BASELINE_0=True, PROPOSED=False ), + precision={ + "pump_power_per_flow_rate": { + "precision": 1, + "unit": "W/gpm", + }, + }, ) def get_calc_vals(self, context, data=None): @@ -93,4 +99,13 @@ def rule_check(self, context, calc_vals=None, data=None): pump_power_per_flow_rate = calc_vals["pump_power_per_flow_rate"] required_pump_power = calc_vals["required_pump_power"] + return self.precision_comparison["pump_power_per_flow_rate"]( + pump_power_per_flow_rate, + required_pump_power, + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): + pump_power_per_flow_rate = calc_vals["pump_power_per_flow_rate"] + required_pump_power = calc_vals["required_pump_power"] + return std_equal(pump_power_per_flow_rate, required_pump_power) diff --git a/rct229/rulesets/ashrae9012019/section22/section22rule31.py b/rct229/rulesets/ashrae9012019/section22/section22rule31.py index d0dd39bf42..20bde0a362 100644 --- a/rct229/rulesets/ashrae9012019/section22/section22rule31.py +++ b/rct229/rulesets/ashrae9012019/section22/section22rule31.py @@ -45,6 +45,12 @@ def __init__(self): required_fields={ "$": ["output"], }, + precision={ + "building_peak_load_b": { + "precision": 1, + "unit": "ton", + }, + }, ) def is_applicable(self, context, data=None): @@ -75,7 +81,13 @@ def get_calc_vals(self, context, data=None): "building_peak_cooling_load", ) - if building_peak_load_b <= REQUIRED_BUILDING_PEAK_LOAD_300: + if ( + building_peak_load_b < REQUIRED_BUILDING_PEAK_LOAD_300 + or self.precision_comparison["building_peak_load_b"]( + building_peak_load_b, + REQUIRED_BUILDING_PEAK_LOAD_300, + ) + ): target_chiller_number = 1 elif building_peak_load_b < REQUIRED_BUILDING_PEAK_LOAD_600: target_chiller_number = 2 diff --git a/rct229/rulesets/ashrae9012019/section22/section22rule32.py b/rct229/rulesets/ashrae9012019/section22/section22rule32.py index 22669d00b8..0042a49718 100644 --- a/rct229/rulesets/ashrae9012019/section22/section22rule32.py +++ b/rct229/rulesets/ashrae9012019/section22/section22rule32.py @@ -74,6 +74,11 @@ def __init__(self): rmds_used=produce_ruleset_model_description( USER=False, BASELINE_0=True, PROPOSED=False ), + precision={ + "chiller_part_load_efficiency": { + "precision": 0.001, + }, + }, ) def get_calc_vals(self, context, data=None): @@ -102,6 +107,25 @@ def get_calc_vals(self, context, data=None): } def rule_check(self, context, calc_vals=None, data=None): + chiller_part_load_efficiency = calc_vals[ + "chiller_part_load_efficiency" + ].magnitude + target_part_load_efficiency = calc_vals["target_part_load_efficiency"] + target_cop_part_load_efficiency = ( + 1.0 / target_part_load_efficiency.to("kilowatt / kilowatt").magnitude + ) # .magnitude is becuase `target_cop_part_load_efficiency` is still a `dimensionless` pint quantity + part_load_efficiency_metric = calc_vals["part_load_efficiency_metric"] + + return ( + self.precision_comparison["chiller_part_load_efficiency"]( + chiller_part_load_efficiency, + target_cop_part_load_efficiency, + ) + and part_load_efficiency_metric + == CHILLER_PART_LOAD_EFFICIENCY_METRIC.INTEGRATED_PART_LOAD_VALUE + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): chiller_part_load_efficiency = calc_vals["chiller_part_load_efficiency"] target_part_load_efficiency = calc_vals["target_part_load_efficiency"] target_cop_part_load_efficiency = 1.0 / target_part_load_efficiency.to( diff --git a/rct229/rulesets/ashrae9012019/section22/section22rule4.py b/rct229/rulesets/ashrae9012019/section22/section22rule4.py index 0f0714640a..ce0d440480 100644 --- a/rct229/rulesets/ashrae9012019/section22/section22rule4.py +++ b/rct229/rulesets/ashrae9012019/section22/section22rule4.py @@ -101,6 +101,24 @@ def __init__(self): "loop_supply_temperature_at_outdoor_low", ], }, + precision={ + "outdoor_high_for_loop_supply_reset_temperature": { + "precision": 0.1, + "unit": "K", + }, + "outdoor_low_for_loop_supply_reset_temperature": { + "precision": 0.1, + "unit": "K", + }, + "loop_supply_temperature_at_outdoor_high": { + "precision": 0.1, + "unit": "K", + }, + "loop_supply_temperature_at_outdoor_low": { + "precision": 0.1, + "unit": "K", + }, + }, ) def get_calc_vals(self, context, data=None): @@ -171,6 +189,61 @@ def rule_check(self, context, calc_vals=None, data=None): "required_loop_supply_temperature_at_outdoor_low" ] + return ( + self.precision_comparison[ + "outdoor_high_for_loop_supply_reset_temperature" + ]( + outdoor_high_for_loop_supply_reset_temperature.to(ureg.kelvin), + required_outdoor_high_for_loop_supply_reset_temperature.to( + ureg.kelvin + ), + ) + and self.precision_comparison[ + "outdoor_low_for_loop_supply_reset_temperature" + ]( + outdoor_low_for_loop_supply_reset_temperature.to(ureg.kelvin), + required_outdoor_low_for_loop_supply_reset_temperature.to( + ureg.kelvin + ), + ) + and self.precision_comparison[ + "loop_supply_temperature_at_outdoor_high" + ]( + loop_supply_temperature_at_outdoor_high.to(ureg.kelvin), + required_loop_supply_temperature_at_outdoor_high.to(ureg.kelvin), + ) + and self.precision_comparison["loop_supply_temperature_at_outdoor_low"]( + loop_supply_temperature_at_outdoor_low.to(ureg.kelvin), + required_loop_supply_temperature_at_outdoor_low.to(ureg.kelvin), + ) + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): + outdoor_high_for_loop_supply_reset_temperature = calc_vals[ + "outdoor_high_for_loop_supply_reset_temperature" + ] + required_outdoor_high_for_loop_supply_reset_temperature = calc_vals[ + "required_outdoor_high_for_loop_supply_reset_temperature" + ] + outdoor_low_for_loop_supply_reset_temperature = calc_vals[ + "outdoor_low_for_loop_supply_reset_temperature" + ] + required_outdoor_low_for_loop_supply_reset_temperature = calc_vals[ + "required_outdoor_low_for_loop_supply_reset_temperature" + ] + loop_supply_temperature_at_outdoor_high = calc_vals[ + "loop_supply_temperature_at_outdoor_high" + ] + required_loop_supply_temperature_at_outdoor_high = calc_vals[ + "required_loop_supply_temperature_at_outdoor_high" + ] + loop_supply_temperature_at_outdoor_low = calc_vals[ + "loop_supply_temperature_at_outdoor_low" + ] + required_loop_supply_temperature_at_outdoor_low = calc_vals[ + "required_loop_supply_temperature_at_outdoor_low" + ] + return ( std_equal( outdoor_high_for_loop_supply_reset_temperature.to(ureg.kelvin), diff --git a/rct229/rulesets/ashrae9012019/section22/section22rule6.py b/rct229/rulesets/ashrae9012019/section22/section22rule6.py index c335229cbf..cefe9c0fb7 100644 --- a/rct229/rulesets/ashrae9012019/section22/section22rule6.py +++ b/rct229/rulesets/ashrae9012019/section22/section22rule6.py @@ -78,6 +78,12 @@ def __init__(self): "loop_supply_temperature_at_low_load", ], }, + precision={ + "loop_supply_temperature_at_low_load": { + "precision": 1, + "unit": "K", + }, + }, ) def get_calc_vals(self, context, data=None): @@ -103,6 +109,19 @@ def rule_check(self, context, calc_vals=None, data=None): "required_loop_supply_temperature_at_low_load" ] + return self.precision_comparison["loop_supply_temperature_at_low_load"]( + loop_supply_temperature_at_low_load.to(ureg.kelvin), + required_loop_supply_temperature_at_low_load.to(ureg.kelvin), + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): + loop_supply_temperature_at_low_load = calc_vals[ + "loop_supply_temperature_at_low_load" + ] + required_loop_supply_temperature_at_low_load = calc_vals[ + "required_loop_supply_temperature_at_low_load" + ] + return std_equal( loop_supply_temperature_at_low_load.to(ureg.kelvin), required_loop_supply_temperature_at_low_load.to(ureg.kelvin), diff --git a/rct229/rulesets/ashrae9012019/section22/section22rule8.py b/rct229/rulesets/ashrae9012019/section22/section22rule8.py index 6fde1d1434..f4eea5a03e 100644 --- a/rct229/rulesets/ashrae9012019/section22/section22rule8.py +++ b/rct229/rulesets/ashrae9012019/section22/section22rule8.py @@ -50,6 +50,12 @@ def __init__(self): standard_section="Section G3.1.3.10 Chilled-water pumps (System 7, 8, 11, 12 and 13)", is_primary_rule=True, list_path="ruleset_model_descriptions[0].fluid_loops[*]", + precision={ + "chw_loop_capacity": { + "precision": 1, + "unit": "ton", + }, + }, ) def is_applicable(self, context, data=None): @@ -108,10 +114,13 @@ def list_filter(self, context_item, data): primary_loop_ids = data["primary_loop_ids"] chw_loop_capacity_dict = data["chw_loop_capacity_dict"] - return ( - fluid_loop_b["id"] in primary_loop_ids - and chw_loop_capacity_dict[fluid_loop_b["id"]] - >= MIN_CHW_PRIMARY_LOOP_COOLING_CAPACITY + return fluid_loop_b["id"] in primary_loop_ids and ( + chw_loop_capacity_dict[fluid_loop_b["id"]] + > MIN_CHW_PRIMARY_LOOP_COOLING_CAPACITY + or self.precision_comparison["chw_loop_capacity"]( + chw_loop_capacity_dict[fluid_loop_b["id"]], + MIN_CHW_PRIMARY_LOOP_COOLING_CAPACITY, + ) ) class PrimaryFluidLoopRule(RuleDefinitionListIndexedBase): diff --git a/rct229/rulesets/ashrae9012019/section22/section22rule9.py b/rct229/rulesets/ashrae9012019/section22/section22rule9.py index 29dc4d5ef1..4d335bc0da 100644 --- a/rct229/rulesets/ashrae9012019/section22/section22rule9.py +++ b/rct229/rulesets/ashrae9012019/section22/section22rule9.py @@ -128,6 +128,11 @@ def __init__(self): "minimum_flow_fraction" ], }, + precision={ + "min_flow_fraction": { + "precision": 0.1, + }, + }, ) def get_calc_vals(self, context, data=None): @@ -141,4 +146,9 @@ def get_calc_vals(self, context, data=None): def rule_check(self, context, calc_vals=None, data=None): min_flow_fraction = calc_vals["min_flow_fraction"] - return min_flow_fraction == REQUIRED_MIN_FLOW_FRACTION + # return min_flow_fraction == REQUIRED_MIN_FLOW_FRACTION + + return self.precision_comparison["min_flow_fraction"]( + min_flow_fraction, + REQUIRED_MIN_FLOW_FRACTION, + ) diff --git a/rct229/rulesets/ashrae9012019/section23/section23rule1.py b/rct229/rulesets/ashrae9012019/section23/section23rule1.py index b614ddeb15..5835d04e3a 100644 --- a/rct229/rulesets/ashrae9012019/section23/section23rule1.py +++ b/rct229/rulesets/ashrae9012019/section23/section23rule1.py @@ -90,6 +90,12 @@ def __init__(self): required_fields={ "$": ["heating_system"], }, + precision={ + "heatpump_low_shutoff_b": { + "precision": 0.1, + "unit": "K", + }, + }, ) def get_calc_vals(self, context, data=None): @@ -119,6 +125,7 @@ def get_calc_vals(self, context, data=None): def manual_check_required(self, context, calc_vals=None, data=None): heatpump_low_shutoff_b = calc_vals["heatpump_low_shutoff_temperature"] hvac_type_b = calc_vals["hvac_type"] + return ( hvac_type_b == HVAC_SYS.SYS_2 and HEATPUMP_LOW_SHUTOFF_LOWER @@ -128,21 +135,37 @@ def manual_check_required(self, context, calc_vals=None, data=None): def get_manual_check_required_msg(self, context, calc_vals=None, data=None): heatpump_low_shutoff_b = calc_vals["heatpump_low_shutoff_temperature"] + return ( f"Undetermined because the low temperature shutoff is between 17F and 25F for System Type 2. " - f"Check with the rating authority to ensure correct shutoff temperature. Low shutoff temperature " + f"Check with the rating authority to ensure correct shutoff temperature. Low shutoff temperature " f"is currently modeled at: {heatpump_low_shutoff_b}." ) def rule_check(self, context, calc_vals=None, data=None): - heatpump_low_shutoff_b = calc_vals["heatpump_low_shutoff_temperature"] + heatpump_low_shutoff_b = calc_vals["heatpump_low_shutoff_temperature"].to( + ureg.kelvin + ) hvac_type_b = calc_vals["hvac_type"] + return ( hvac_type_b == HVAC_SYS.SYS_2 - and heatpump_low_shutoff_b <= HEATPUMP_LOW_SHUTOFF_LOWER + and ( + heatpump_low_shutoff_b < HEATPUMP_LOW_SHUTOFF_LOWER + or self.precision_comparison["heatpump_low_shutoff_b"]( + heatpump_low_shutoff_b, + HEATPUMP_LOW_SHUTOFF_LOWER.to(ureg.kelvin), + ) + ) ) or ( hvac_type_b == HVAC_SYS.SYS_4 - and heatpump_low_shutoff_b <= HEATPUMP_LOW_SHUTOFF_LOWER_SYS4 + and ( + heatpump_low_shutoff_b < HEATPUMP_LOW_SHUTOFF_LOWER_SYS4 + or self.precision_comparison["heatpump_low_shutoff_b"]( + heatpump_low_shutoff_b, + HEATPUMP_LOW_SHUTOFF_LOWER_SYS4.to(ureg.kelvin), + ) + ) ) def get_fail_msg(self, context, calc_vals=None, data=None): diff --git a/rct229/rulesets/ashrae9012019/section23/section23rule10.py b/rct229/rulesets/ashrae9012019/section23/section23rule10.py index 14b4607072..30b83d6f78 100644 --- a/rct229/rulesets/ashrae9012019/section23/section23rule10.py +++ b/rct229/rulesets/ashrae9012019/section23/section23rule10.py @@ -82,6 +82,11 @@ def __init__(self): required_fields={ "$": ["fan_system"], }, + precision={ + "fan_volume_reset_fraction_b": { + "precision": 0.1, + }, + }, ) def is_applicable(self, context, data=None): @@ -103,9 +108,12 @@ def get_calc_vals(self, context, data=None): def rule_check(self, context, calc_vals=None, data=None): fan_volume_reset_fraction_b = calc_vals["fan_volume_reset_fraction"] fan_volume_reset_type_b = calc_vals["fan_volume_reset_type"] + return ( fan_volume_reset_type_b == FAN_SYSTEM_SUPPLY_FAN_VOLUME_RESET.DESIGN_LOAD_RESET - and fan_volume_reset_fraction_b - == FAN_SYSTEM_SUPPLY_FAN_VOLUME_RESET_FRACTION + and self.precision_comparison["fan_volume_reset_fraction_b"]( + fan_volume_reset_fraction_b, + FAN_SYSTEM_SUPPLY_FAN_VOLUME_RESET_FRACTION, + ) ) diff --git a/rct229/rulesets/ashrae9012019/section23/section23rule11.py b/rct229/rulesets/ashrae9012019/section23/section23rule11.py index 0a059d93ab..b812af3ce3 100644 --- a/rct229/rulesets/ashrae9012019/section23/section23rule11.py +++ b/rct229/rulesets/ashrae9012019/section23/section23rule11.py @@ -15,7 +15,7 @@ from rct229.schema.schema_enums import SchemaEnums from rct229.utils.assertions import getattr_ from rct229.utils.pint_utils import CalcQ -from rct229.utils.std_comparisons import std_equal + APPLICABLE_SYS_TYPES = [ HVAC_SYS.SYS_11_1, @@ -92,6 +92,15 @@ def __init__(self): required_fields={ "$": ["fan_system"], }, + precision={ + "supply_air_temp_reset_load_frac_b": { + "precision": 0.1, + }, + "reset_differential_temperature_b": { + "precision": 0.1, + "unit": "K", + }, + }, ) def get_calc_vals(self, context, data=None): @@ -139,11 +148,14 @@ def rule_check(self, context, calc_vals=None, data=None): return ( temperature_control_b == FanSystemTemperatureControlOptions.LOAD_RESET_TO_SPACE_TEMPERATURE - and supply_air_temp_reset_load_frac_b - == TARGET_SUPPLY_AIR_TEMP_RESET_LOAD_FRAC + and self.precision_comparison["supply_air_temp_reset_load_frac_b"]( + supply_air_temp_reset_load_frac_b, + TARGET_SUPPLY_AIR_TEMP_RESET_LOAD_FRAC, + ) ) or ( temperature_control_b == FanSystemTemperatureControlOptions.ZONE_RESET - and std_equal( - TARGET_RESET_DIFFERENTIAL_TEMP, reset_differential_temperature_b + and self.precision_comparison["reset_differential_temperature_b"]( + TARGET_RESET_DIFFERENTIAL_TEMP, + reset_differential_temperature_b, ) ) diff --git a/rct229/rulesets/ashrae9012019/section23/section23rule16.py b/rct229/rulesets/ashrae9012019/section23/section23rule16.py index 846a579ced..12359e34ad 100644 --- a/rct229/rulesets/ashrae9012019/section23/section23rule16.py +++ b/rct229/rulesets/ashrae9012019/section23/section23rule16.py @@ -15,7 +15,6 @@ from rct229.schema.schema_enums import SchemaEnums from rct229.utils.jsonpath_utils import find_all from rct229.utils.pint_utils import ZERO, CalcQ -from rct229.utils.std_comparisons import std_equal from rct229.utils.utility_functions import find_exactly_one_fluid_loop APPLICABLE_SYS_TYPES = [ @@ -134,6 +133,12 @@ def __init__(self): "heating_coil_setpoint", ], }, + precision={ + "heating_coil_setpoint": { + "precision": 0.1, + "unit": "delta_degC", + }, + }, ) def get_calc_vals(self, context, data=None): @@ -167,7 +172,7 @@ def rule_check(self, context, calc_vals=None, data=None): return ( heating_system_type_b == HEATING_SYSTEM.FLUID_LOOP and hot_water_loop_type == FLUID_LOOP.HEATING - and std_equal( + and self.precision_comparison["heating_coil_setpoint"]( heating_coil_setpoint, hvac_max_zone_setpoint - REQUIRED_SET_POINT_REDUCTION, ) diff --git a/rct229/rulesets/ashrae9012019/section23/section23rule2.py b/rct229/rulesets/ashrae9012019/section23/section23rule2.py index 17ec4da6ea..35c3da1de0 100644 --- a/rct229/rulesets/ashrae9012019/section23/section23rule2.py +++ b/rct229/rulesets/ashrae9012019/section23/section23rule2.py @@ -96,6 +96,12 @@ def __init__(self): "reset_differential_temperature", ], }, + precision={ + "reset_differential_temperature_b": { + "precision": 0.1, + "unit": "K", + }, + }, ) def get_calc_vals(self, context, data=None): @@ -122,6 +128,20 @@ def rule_check(self, context, calc_vals=None, data=None): "reset_differential_temperature_b" ] + return ( + temperature_control_b == FanSystemTemperatureControl.ZONE_RESET + and self.precision_comparison["reset_differential_temperature_b"]( + reset_differential_temperature_b, + REQUIRED_RESET_DIFF_TEMP, + ) + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): + temperature_control_b = calc_vals["temperature_control_b"] + reset_differential_temperature_b = calc_vals[ + "reset_differential_temperature_b" + ] + return ( temperature_control_b == FanSystemTemperatureControl.ZONE_RESET and std_equal( diff --git a/rct229/rulesets/ashrae9012019/section23/section23rule3.py b/rct229/rulesets/ashrae9012019/section23/section23rule3.py index 34ab93d3dc..7a2758722b 100644 --- a/rct229/rulesets/ashrae9012019/section23/section23rule3.py +++ b/rct229/rulesets/ashrae9012019/section23/section23rule3.py @@ -92,6 +92,12 @@ def __init__(self): "primary_airflow", ], }, + precision={ + "minimum_airflow_b": { + "precision": 0.1, + "unit": "cfm", + }, + }, ) def get_calc_vals(self, context, data=None): @@ -113,6 +119,16 @@ def rule_check(self, context, calc_vals=None, data=None): primary_airflow_b = calc_vals["primary_airflow_b"] minimum_outdoor_airflow_b = calc_vals["minimum_outdoor_airflow_b"] + return self.precision_comparison["minimum_airflow_b"]( + minimum_airflow_b, + max(primary_airflow_b * 0.3, minimum_outdoor_airflow_b), + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): + minimum_airflow_b = calc_vals["minimum_airflow_b"] + primary_airflow_b = calc_vals["primary_airflow_b"] + minimum_outdoor_airflow_b = calc_vals["minimum_outdoor_airflow_b"] + return std_equal( minimum_airflow_b, max(primary_airflow_b * 0.3, minimum_outdoor_airflow_b), diff --git a/rct229/rulesets/ashrae9012019/section23/section23rule6.py b/rct229/rulesets/ashrae9012019/section23/section23rule6.py index 33d8ffd0ce..78b891ddfd 100644 --- a/rct229/rulesets/ashrae9012019/section23/section23rule6.py +++ b/rct229/rulesets/ashrae9012019/section23/section23rule6.py @@ -19,6 +19,7 @@ HVAC_SYS.SYS_6, HVAC_SYS.SYS_8, ] +REQUIRED_DESIGN_ELEC_POWER_DESIGN_AIRFLOW_RATIO = 0.35 * ureg("W/cfm") class Section23Rule6(RuleDefinitionListIndexedBase): @@ -91,6 +92,16 @@ def __init__(self): "design_electric_power", ], }, + precision={ + "design_airflow_b": { + "precision": 0.1, + "unit": "cfm", + }, + "design_electric_power_b/design_airflow_b": { + "precision": 0.01, + "unit": "W/cfm", + }, + }, ) def get_calc_vals(self, context, data=None): @@ -112,6 +123,19 @@ def rule_check(self, context, calc_vals=None, data=None): primary_airflow_b = calc_vals["primary_airflow_b"] design_electric_power_b = calc_vals["design_electric_power_b"] + return self.precision_comparison["design_airflow_b"]( + design_airflow_b, + 0.5 * primary_airflow_b, + ) and self.precision_comparison["design_electric_power_b/design_airflow_b"]( + design_electric_power_b / design_airflow_b, + REQUIRED_DESIGN_ELEC_POWER_DESIGN_AIRFLOW_RATIO, + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): + design_airflow_b = calc_vals["design_airflow_b"] + primary_airflow_b = calc_vals["primary_airflow_b"] + design_electric_power_b = calc_vals["design_electric_power_b"] + return std_equal(design_airflow_b, 0.5 * primary_airflow_b) and std_equal( design_electric_power_b / design_airflow_b, 0.35 * ureg("W/cfm") ) diff --git a/rct229/rulesets/ashrae9012019/section23/section23rule8.py b/rct229/rulesets/ashrae9012019/section23/section23rule8.py index d50e4cd942..f98e1d1a78 100644 --- a/rct229/rulesets/ashrae9012019/section23/section23rule8.py +++ b/rct229/rulesets/ashrae9012019/section23/section23rule8.py @@ -104,6 +104,16 @@ def __init__(self): "output_validation_points", ], }, + precision={ + "airflow": { + "precision": 1, + "unit": "cfm", + }, + "result": { + "precision": 10, + "unit": "W", + }, + }, ) def get_calc_vals(self, context, data=None): @@ -139,6 +149,28 @@ def rule_check(self, context, calc_vals=None, data=None): output_validation_points = calc_vals["output_validation_points"] target_validation_points = calc_vals["target_validation_points"] + return len( + output_validation_points + ) == VALIDATION_POINTS_LENGTH and all( + [ + self.precision_comparison["airflow"]( + ovp[0], + tvp[0], + ) + and self.precision_comparison["result"]( + ovp[1], + tvp[1], + ) + for ovp, tvp in zip( + output_validation_points, target_validation_points + ) + ] + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): + output_validation_points = calc_vals["output_validation_points"] + target_validation_points = calc_vals["target_validation_points"] + return len( output_validation_points ) == VALIDATION_POINTS_LENGTH and all( diff --git a/rct229/rulesets/ashrae9012019/section23/section23rule9.py b/rct229/rulesets/ashrae9012019/section23/section23rule9.py index c7adf0a844..7931d372cc 100644 --- a/rct229/rulesets/ashrae9012019/section23/section23rule9.py +++ b/rct229/rulesets/ashrae9012019/section23/section23rule9.py @@ -97,6 +97,12 @@ def __init__(self): "to this value. We are not able to determine the airflow required to comply with codes or " "accreditation standards at this time, please double check that there are no additional " "codes or accreditation standards in regards to airflow. ", + precision={ + "minimum_airflow_b": { + "precision": 1, + "unit": "cfm", + }, + }, ) def get_calc_vals(self, context, data=None): @@ -127,6 +133,7 @@ def manual_check_required(self, context, calc_vals=None, data=None): minimum_airflow_b = calc_vals["minimum_airflow"] minimum_ventilation_airflow_b = calc_vals["minimum_ventilation_airflow"] max_supply_airflow_b = calc_vals["max_supply_airflow"] + return minimum_airflow_b > max( minimum_ventilation_airflow_b, VENT_THRESHOLD_FACTOR * max_supply_airflow_b, @@ -136,7 +143,11 @@ def rule_check(self, context, calc_vals=None, data=None): minimum_airflow_b = calc_vals["minimum_airflow"] minimum_ventilation_airflow_b = calc_vals["minimum_ventilation_airflow"] max_supply_airflow_b = calc_vals["max_supply_airflow"] - return minimum_airflow_b == max( - minimum_ventilation_airflow_b, - VENT_THRESHOLD_FACTOR * max_supply_airflow_b, + + return self.precision_comparison["minimum_airflow_b"]( + minimum_airflow_b, + max( + minimum_ventilation_airflow_b, + VENT_THRESHOLD_FACTOR * max_supply_airflow_b, + ), ) diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule1.py b/rct229/rulesets/ashrae9012019/section5/section5rule1.py index c0fded9058..9bdcae54e1 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule1.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule1.py @@ -146,6 +146,12 @@ def __init__(self): required_fields={ "$.building_segments[*].zones[*].surfaces[*]": ["azimuth"], }, + precision={ + "percent_difference_max_min_fen_area_per_orientation": { + "precision": 0.01, + "unit": "", + } + }, fail_msg="Fail unless Table G3.1#5a exception #2 is applicable and it can be demonstrated that the building orientation is dictated by site considerations.", ) @@ -223,7 +229,13 @@ def get_key_for_azi(azi): ) rotation_expected_b = ( - percent_difference >= ACCEPTABLE_FEN_PERCENTAGE_DIFFERENCE + percent_difference > ACCEPTABLE_FEN_PERCENTAGE_DIFFERENCE + or self.precision_comparison[ + "percent_difference_max_min_fen_area_per_orientation" + ]( + percent_difference.magnitude, + ACCEPTABLE_FEN_PERCENTAGE_DIFFERENCE, + ) ) return { diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule10.py b/rct229/rulesets/ashrae9012019/section5/section5rule10.py index 919882cbb8..c3fe890441 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule10.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule10.py @@ -80,6 +80,12 @@ def __init__(self): "$": ["construction"], "construction": ["u_factor"], }, + precision={ + "floor_u_factor_b": { + "precision": 0.001, + "unit": "Btu/(hr*ft2*R)", + } + }, ) def get_calc_vals(self, context, data=None): @@ -132,7 +138,11 @@ def manual_check_required(self, context, calc_vals=None, data=None): ) def rule_check(self, context, calc_vals=None, data=None): + return self.precision_comparison["floor_u_factor_b"]( + calc_vals["floor_u_factor"], calc_vals["target_u_factor"] + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): floor_u_factor = calc_vals["floor_u_factor"] target_u_factor = calc_vals["target_u_factor"] - - return std_equal(val=floor_u_factor, std_val=target_u_factor) + return std_equal(floor_u_factor, target_u_factor) diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule12.py b/rct229/rulesets/ashrae9012019/section5/section5rule12.py index 2d730985fa..0ae920be75 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule12.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule12.py @@ -91,6 +91,12 @@ def __init__(self): }, manual_check_required_msg=MANUAL_CHECK_REQUIRED_MSG, fail_msg=FAIL_MSG, + precision={ + "floor_f_factor_b": { + "precision": 0.001, + "unit": "Btu/(hr*ft*R)", + } + }, ) def get_calc_vals(self, context, data=None): @@ -147,9 +153,14 @@ def manual_check_required(self, context, calc_vals=None, data=None): return target_f_factor_res != target_f_factor_nonres def rule_check(self, context, calc_vals=None, data=None): + return self.precision_comparison["floor_f_factor_b"]( + calc_vals["target_f_factor"], + calc_vals["slab_on_grade_floor_f_factor"], + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): target_f_factor = calc_vals["target_f_factor"] slab_on_grade_floor_f_factor = calc_vals["slab_on_grade_floor_f_factor"] - return std_equal( std_val=target_f_factor, val=slab_on_grade_floor_f_factor ) diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule13.py b/rct229/rulesets/ashrae9012019/section5/section5rule13.py index 562ad1c86f..3d31adce74 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule13.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule13.py @@ -16,7 +16,6 @@ ) from rct229.utils.assertions import getattr_ from rct229.utils.pint_utils import CalcQ -from rct229.utils.std_comparisons import std_equal class Section5Rule13(RuleDefinitionListIndexedBase): @@ -76,6 +75,20 @@ def __init__(self): USER=False, BASELINE_0=True, PROPOSED=True ), required_fields={"$": ["construction"]}, + precision={ + "surface_u_factor_b": { + "precision": 0.001, + "unit": "Btu/(hr*ft2*R)", + }, + "surface_c_factor_b": { + "precision": 0.001, + "unit": "Btu/(hr*ft2*R)", + }, + "surface_f_factor_b": { + "precision": 0.001, + "unit": "Btu/(hr*ft*R)", + }, + }, ) def get_calc_vals(self, context, data=None): @@ -157,17 +170,20 @@ def rule_check(self, context, calc_vals=None, data=None): return False if baseline_surface_type in [OST.ABOVE_GRADE_WALL, OST.FLOOR, OST.ROOF]: - return std_equal( + return self.precision_comparison["surface_u_factor_b"]( calc_vals["baseline_surface_u_factor"], calc_vals["proposed_surface_u_factor"], ) + elif baseline_surface_type in [OST.UNHEATED_SOG, OST.HEATED_SOG]: - return std_equal( + return self.precision_comparison["surface_f_factor_b"]( calc_vals["baseline_surface_f_factor"], calc_vals["proposed_surface_f_factor"], ) + elif baseline_surface_type == OST.BELOW_GRADE_WALL: - return std_equal( + + return self.precision_comparison["surface_c_factor_b"]( calc_vals["baseline_surface_c_factor"], calc_vals["proposed_surface_c_factor"], ) diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule14.py b/rct229/rulesets/ashrae9012019/section5/section5rule14.py index eec132c5f8..3898548e83 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule14.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule14.py @@ -110,6 +110,12 @@ def __init__(self): rmds_used=produce_ruleset_model_description( USER=False, BASELINE_0=True, PROPOSED=False ), + precision={ + "area_type_wwr_b": { + "precision": 0.01, + "unit": "", + } + }, ) def get_calc_vals(self, context, data=None): @@ -156,6 +162,12 @@ def get_manual_check_required_msg(self, context, calc_vals=None, data=None): return manual_check_msg def rule_check(self, context, calc_vals=None, data=None): + return self.precision_comparison["area_type_wwr_b"]( + calc_vals["area_type_wwr"].magnitude, + calc_vals["area_type_target_wwr"], + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): area_type_wwr = calc_vals["area_type_wwr"] area_type_target_wwr = calc_vals["area_type_target_wwr"] - return std_equal(area_type_target_wwr, area_type_wwr) + return std_equal(area_type_wwr, area_type_target_wwr) diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule15.py b/rct229/rulesets/ashrae9012019/section5/section5rule15.py index 9a51489e10..3f6fc29c3b 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule15.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule15.py @@ -53,6 +53,12 @@ def __init__(self): "area_type_vertical_fenestration", ], }, + precision={ + "wwr_b": { + "precision": 0.01, + "unit": "", + } + }, ) def is_applicable(self, context, data=None): @@ -109,6 +115,10 @@ def get_manual_check_required_msg(self, context, calc_vals=None, data=None): return manual_check_msg def rule_check(self, context, calc_vals=None, data=None): - return not calc_vals["manual_check_flag"] and std_equal( - calc_vals["wwr_b"], min(calc_vals["wwr_p"], WWR_THRESHOLD) + return self.precision_comparison["wwr_b"]( + calc_vals["wwr_b"].magnitude, + min(calc_vals["wwr_p"].magnitude, WWR_THRESHOLD), ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): + return std_equal(calc_vals["wwr_b"], min(calc_vals["wwr_p"], WWR_THRESHOLD)) diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule16.py b/rct229/rulesets/ashrae9012019/section5/section5rule16.py index d71e0a67b3..b33964eb9f 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule16.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule16.py @@ -109,6 +109,12 @@ def __init__(self): "$": ["construction"], "construction": ["u_factor"], }, + precision={ + "total_fenestration_area_surface_b / total_fenstration_area_b": { + "precision": 0.01, + "unit": "", + } + }, fail_msg=FAIL_MSG, ) @@ -169,7 +175,17 @@ def rule_check(self, context, calc_vals=None, data=None): return ( total_fenestration_area_b == ZERO.AREA and total_fenestration_area_p == ZERO.AREA - ) or std_equal( - (total_fenestration_area_surface_b / total_fenestration_area_b), - (total_fenestration_area_surface_p / total_fenestration_area_p), + ) or ( + self.precision_comparison[ + "total_fenestration_area_surface_b / total_fenstration_area_b" + ]( + ( + total_fenestration_area_surface_b + / total_fenestration_area_b + ).magnitude, + ( + total_fenestration_area_surface_p + / total_fenestration_area_p + ).magnitude, + ) ) diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule19.py b/rct229/rulesets/ashrae9012019/section5/section5rule19.py index 6000dc7a7f..89204ff056 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule19.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule19.py @@ -2,6 +2,7 @@ from rct229.rule_engine.rule_list_indexed_base import RuleDefinitionListIndexedBase from rct229.rule_engine.ruleset_model_factory import produce_ruleset_model_description from rct229.rulesets.ashrae9012019 import BASELINE_0 +from rct229.schema.schema_enums import SchemaEnums from rct229.rulesets.ashrae9012019.data_fns.table_G3_4_fns import table_G34_lookup from rct229.rulesets.ashrae9012019.ruleset_functions.get_building_scc_window_wall_ratios_dict import ( get_building_scc_window_wall_ratios_dict, @@ -18,7 +19,6 @@ from rct229.rulesets.ashrae9012019.ruleset_functions.get_surface_conditioning_category_dict import ( get_surface_conditioning_category_dict, ) -from rct229.schema.schema_enums import SchemaEnums from rct229.utils.jsonpath_utils import find_all from rct229.utils.pint_utils import ZERO, CalcQ from rct229.utils.std_comparisons import std_equal @@ -264,6 +264,12 @@ def __init__(self): rmds_used=produce_ruleset_model_description( USER=False, BASELINE_0=True, PROPOSED=False ), + precision={ + "subsurface_u_factor_b": { + "precision": 0.01, + "unit": "Btu/(hr*ft2*R)", + } + }, ) def get_calc_vals(self, context, data=None): @@ -293,6 +299,11 @@ def get_calc_vals(self, context, data=None): } def rule_check(self, context, calc_vals=None, data=None): + return self.precision_comparison["subsurface_u_factor_b"]( + calc_vals["target_u_factor"], calc_vals["subsurface_u_factor"] + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): target_u_factor = calc_vals["target_u_factor"] subsurface_u_factor = calc_vals["subsurface_u_factor"] return std_equal(std_val=target_u_factor, val=subsurface_u_factor) diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule20.py b/rct229/rulesets/ashrae9012019/section5/section5rule20.py index 58f49c7f23..c536f1552f 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule20.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule20.py @@ -251,6 +251,12 @@ def __init__(self): rmds_used=produce_ruleset_model_description( USER=False, BASELINE_0=True, PROPOSED=False ), + precision={ + "subsurface_shgc_b": { + "precision": 0.01, + "unit": "", + } + }, ) def manual_check_required(self, context, calc_vals=None, data=None): @@ -282,6 +288,14 @@ def get_calc_vals(self, context, data=None): def rule_check(self, context, calc_vals=None, data=None): target_shgc = calc_vals["target_shgc"] subsurface_shgc = calc_vals["subsurface_shgc"] + + return target_shgc is not None and self.precision_comparison[ + "subsurface_shgc_b" + ](subsurface_shgc, target_shgc) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): + target_shgc = calc_vals["target_shgc"] + subsurface_shgc = calc_vals["subsurface_shgc"] return target_shgc is not None and std_equal( std_val=target_shgc, val=subsurface_shgc ) diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule21.py b/rct229/rulesets/ashrae9012019/section5/section5rule21.py index fcad192ac5..f3ba1514bd 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule21.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule21.py @@ -9,7 +9,7 @@ get_surface_conditioning_category_dict, ) from rct229.utils.pint_utils import CalcQ -from rct229.utils.std_comparisons import std_equal + FAIL_MSG = "Subsurface that is not regulated (Not part of building envelope) is not modeled with the same area, U-factor and SHGC in the baseline as in the propsoed design." @@ -83,6 +83,24 @@ def __init__(self): rmds_used=produce_ruleset_model_description( USER=False, BASELINE_0=True, PROPOSED=True ), + precision={ + "subsurface_u_factor_b": { + "precision": 0.01, + "unit": "Btu/(hr*ft2*R)", + }, + "subsurface_shgc_b": { + "precision": 0.01, + "unit": "", + }, + "subsurface_glazed_area_b": { + "precision": 1, + "unit": "ft2", + }, + "subsurface_opaque_area_b": { + "precision": 1, + "unit": "ft2", + }, + }, fail_msg=FAIL_MSG, ) @@ -119,19 +137,19 @@ def get_calc_vals(self, context, data=None): def rule_check(self, context, calc_vals=None, data=None): return ( - std_equal( + self.precision_comparison["subsurface_u_factor_b"]( calc_vals["subsurface_u_factor_b"], calc_vals["subsurface_u_factor_p"], ) - and std_equal( + and self.precision_comparison["subsurface_shgc_b"]( calc_vals["subsurface_shgc_b"], calc_vals["subsurface_shgc_p"], ) - and std_equal( + and self.precision_comparison["subsurface_glazed_area_b"]( calc_vals["subsurface_glazed_area_b"], calc_vals["subsurface_glazed_area_p"], ) - and std_equal( + and self.precision_comparison["subsurface_opaque_area_b"]( calc_vals["subsurface_opaque_area_b"], calc_vals["subsurface_opaque_area_p"], ) diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule23.py b/rct229/rulesets/ashrae9012019/section5/section5rule23.py index 9737ba89ea..242df1c37b 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule23.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule23.py @@ -3,6 +3,7 @@ from rct229.rule_engine.ruleset_model_factory import produce_ruleset_model_description from rct229.rulesets.ashrae9012019 import BASELINE_0 from rct229.utils.jsonpath_utils import find_all +from rct229.utils.std_comparisons import std_equal MANUAL_CHECK_MSG = "Surface in P-RMD has subsurfaces modeled with different manual shade status. Verify if subsurfaces manual shade status in B-RMD are modeled the same as in P-RMD" diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule24.py b/rct229/rulesets/ashrae9012019/section5/section5rule24.py index a8186d4a49..e74c53bf27 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule24.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule24.py @@ -63,6 +63,12 @@ def __init__(self): rmds_used=produce_ruleset_model_description( USER=False, BASELINE_0=True, PROPOSED=True ), + precision={ + "skylight_roof_ratio_b": { + "precision": 0.01, + "unit": "", + } + }, ) def is_applicable(self, context, data=None): @@ -112,6 +118,7 @@ def get_calc_vals(self, context, data=None): } def rule_check(self, context, calc_vals=None, data=None): - skylight_roof_ratio_b = calc_vals["skylight_roof_ratio_b"] - skylight_roof_ratio_p = calc_vals["skylight_total_roof_ratio_p"] - return std_equal(skylight_roof_ratio_b, skylight_roof_ratio_p) + return self.precision_comparison["skylight_roof_ratio_b"]( + calc_vals["skylight_roof_ratio_b"].magnitude, + calc_vals["skylight_total_roof_ratio_p"].magnitude, + ) diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule25.py b/rct229/rulesets/ashrae9012019/section5/section5rule25.py index bdb5084116..6a05ab73c4 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule25.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule25.py @@ -63,6 +63,12 @@ def __init__(self): rmds_used=produce_ruleset_model_description( USER=False, BASELINE_0=True, PROPOSED=True ), + precision={ + "skylight_roof_ratio_b": { + "precision": 0.01, + "unit": "", + } + }, ) def is_applicable(self, context, data=None): @@ -110,5 +116,10 @@ def get_calc_vals(self, context, data=None): } def rule_check(self, context, calc_vals=None, data=None): + return self.precision_comparison["skylight_roof_ratio_b"]( + calc_vals["skylight_roof_ratio_b"].magnitude, SKYLIGHT_THRESHOLD + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): skylight_roof_ratio_b = calc_vals["skylight_roof_ratio_b"] - return std_equal(skylight_roof_ratio_b, 0.03) + return std_equal(skylight_roof_ratio_b, SKYLIGHT_THRESHOLD) diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule26.py b/rct229/rulesets/ashrae9012019/section5/section5rule26.py index f7cda70179..101ddf2df1 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule26.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule26.py @@ -131,6 +131,12 @@ def __init__(self): rmds_used=produce_ruleset_model_description( USER=False, BASELINE_0=True, PROPOSED=True ), + precision={ + "total_skylight_area_surface_b / total_skylight_area_b": { + "precision": 0.01, + "unit": "", + } + }, ) def get_calc_vals(self, context, data=None): @@ -188,8 +194,14 @@ def rule_check(self, context, calc_vals=None, data=None): # product to ensure neither is 0 & short-circuit logic if either of them is 0. total_skylight_area_b * total_skylight_area_p > 0 # both segments' skylight area ratios are the same - and std_equal( - total_skylight_area_surface_b / total_skylight_area_b, - total_skylight_area_surface_p / total_skylight_area_p, + and self.precision_comparison[ + "total_skylight_area_surface_b / total_skylight_area_b" + ]( + ( + total_skylight_area_surface_b / total_skylight_area_b + ).magnitude, + ( + total_skylight_area_surface_p / total_skylight_area_p + ).magnitude, ) ) diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule27.py b/rct229/rulesets/ashrae9012019/section5/section5rule27.py index 31ae771d64..8520e30803 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule27.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule27.py @@ -2,6 +2,7 @@ from rct229.rule_engine.rule_list_indexed_base import RuleDefinitionListIndexedBase from rct229.rule_engine.ruleset_model_factory import produce_ruleset_model_description from rct229.rulesets.ashrae9012019 import BASELINE_0 +from rct229.schema.schema_enums import SchemaEnums from rct229.rulesets.ashrae9012019.data_fns.table_G3_4_fns import table_G34_lookup from rct229.rulesets.ashrae9012019.ruleset_functions.get_building_scc_skylight_roof_ratios_dict import ( get_building_scc_skylight_roof_ratios_dict, @@ -18,7 +19,6 @@ from rct229.rulesets.ashrae9012019.ruleset_functions.get_surface_conditioning_category_dict import ( get_surface_conditioning_category_dict, ) -from rct229.schema.schema_enums import SchemaEnums from rct229.utils.pint_utils import ZERO, CalcQ from rct229.utils.std_comparisons import std_equal @@ -227,6 +227,12 @@ def __init__(self): "u_factor", ] }, + precision={ + "subsurface_u_factor_b": { + "precision": 0.01, + "unit": "Btu/(hr*ft2*R)", + } + }, ) def is_applicable(self, context, data=None): @@ -265,6 +271,11 @@ def get_calc_vals(self, context, data=None): } def rule_check(self, context, calc_vals=None, data=None): + return self.precision_comparison["subsurface_u_factor_b"]( + calc_vals["subsurface_b_u_factor"], calc_vals["target_u_factor"] + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): subsurface_b_u_factor = calc_vals["subsurface_b_u_factor"] target_u_factor = calc_vals["target_u_factor"] - return std_equal(std_val=target_u_factor, val=subsurface_b_u_factor) + return std_equal(target_u_factor, subsurface_b_u_factor) diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule28.py b/rct229/rulesets/ashrae9012019/section5/section5rule28.py index f6c91a2ec0..4cd7f56f95 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule28.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule28.py @@ -196,6 +196,12 @@ def __init__(self): required_fields={ "$": ["classification", "glazed_area", "opaque_area"] }, + precision={ + "subsurface_shgc_b": { + "precision": 0.01, + "unit": "", + } + }, ) def is_applicable(self, context, data=None): @@ -240,7 +246,11 @@ def get_calc_vals(self, context, data=None): } def rule_check(self, context, calc_vals=None, data=None): + return self.precision_comparison["subsurface_shgc_b"]( + calc_vals["subsurface_shgc_b"], calc_vals["target_shgc"] + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): target_shgc = calc_vals["target_shgc"] subsurface_shgc_b = calc_vals["subsurface_shgc_b"] - return std_equal(target_shgc, subsurface_shgc_b) diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule29.py b/rct229/rulesets/ashrae9012019/section5/section5rule29.py index 5fe04f2d05..63ff716eea 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule29.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule29.py @@ -78,6 +78,12 @@ def __init__(self): "$": ["optical_properties"], "optical_properties": ["absorptance_thermal_exterior"], }, + precision={ + "absorptance_thermal_exterior_b": { + "precision": 0.01, + "unit": "", + } + }, ) def get_calc_vals(self, context, data=None): @@ -89,6 +95,12 @@ def get_calc_vals(self, context, data=None): } def rule_check(self, context, calc_vals=None, data=None): + return self.precision_comparison["absorptance_thermal_exterior_b"]( + calc_vals["absorptance_thermal_exterior"], + TARGET_ABSORPTANCE_THERMAL_EXTERIOR, + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): return std_equal( TARGET_ABSORPTANCE_THERMAL_EXTERIOR, calc_vals["absorptance_thermal_exterior"], diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule30.py b/rct229/rulesets/ashrae9012019/section5/section5rule30.py index cfff84ffee..3e0d9997f0 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule30.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule30.py @@ -14,6 +14,7 @@ from rct229.rulesets.ashrae9012019.ruleset_functions.get_surface_conditioning_category_dict import ( get_surface_conditioning_category_dict, ) +from rct229.utils.std_comparisons import std_equal ABSORPTION_THERMAL_EXTERIOR = 0.9 UNDETERMINED_MSG = ( @@ -83,6 +84,12 @@ def __init__(self): "$": ["optical_properties"], "optical_properties": ["absorptance_thermal_exterior"], }, + precision={ + "absorptance_thermal_exterior_p": { + "precision": 0.01, + "unit": "", + } + }, ) def get_calc_vals(self, context, data=None): @@ -119,9 +126,9 @@ def get_manual_check_required_msg(self, context, calc_vals=None, data=None): ) def rule_check(self, context, calc_vals=None, data=None): - return ( - calc_vals["absorptance_thermal_exterior_p"] - == ABSORPTION_THERMAL_EXTERIOR + return self.precision_comparison["absorptance_thermal_exterior_p"]( + calc_vals["absorptance_thermal_exterior_p"], + ABSORPTION_THERMAL_EXTERIOR, ) def get_pass_msg(self, context, calc_vals=None, data=None): @@ -142,3 +149,9 @@ def get_pass_msg(self, context, calc_vals=None, data=None): ) return pass_msg + + def is_tolerance_fail(self, context, calc_vals=None, data=None): + return std_equal( + calc_vals["absorptance_thermal_exterior_p"], + ABSORPTION_THERMAL_EXTERIOR, + ) diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule31.py b/rct229/rulesets/ashrae9012019/section5/section5rule31.py index f57412fdd1..369e4c926a 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule31.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule31.py @@ -78,6 +78,12 @@ def __init__(self): "$": ["optical_properties"], "optical_properties": ["absorptance_solar_exterior"], }, + precision={ + "absorptance_solar_exterior_b": { + "precision": 0.01, + "unit": "", + } + }, ) def get_calc_vals(self, context, data=None): @@ -89,6 +95,12 @@ def get_calc_vals(self, context, data=None): } def rule_check(self, context, calc_vals=None, data=None): + return self.precision_comparison["absorptance_solar_exterior_b"]( + calc_vals["absorptance_solar_exterior"], + TARGET_ABSORPTANCE_SOLAR_EXTERIOR, + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): return std_equal( TARGET_ABSORPTANCE_SOLAR_EXTERIOR, calc_vals["absorptance_solar_exterior"], diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule32.py b/rct229/rulesets/ashrae9012019/section5/section5rule32.py index 6d460566d9..8add039bf4 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule32.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule32.py @@ -14,6 +14,7 @@ from rct229.rulesets.ashrae9012019.ruleset_functions.get_surface_conditioning_category_dict import ( get_surface_conditioning_category_dict, ) +from rct229.utils.std_comparisons import std_equal ABSORPTANCE_SOLAR_EXTERIOR = 0.7 @@ -78,6 +79,12 @@ def __init__(self): "$": ["optical_properties"], "optical_properties": ["absorptance_solar_exterior"], }, + precision={ + "absorptance_solar_exterior_p": { + "precision": 0.01, + "unit": "", + } + }, ) def get_calc_vals(self, context, data=None): @@ -110,9 +117,15 @@ def get_manual_check_required_msg(self, context, calc_vals=None, data=None): ) def rule_check(self, context, calc_vals=None, data=None): - return ( - calc_vals["absorptance_solar_exterior_p"] - == ABSORPTANCE_SOLAR_EXTERIOR + return self.precision_comparison["absorptance_solar_exterior_p"]( + calc_vals["absorptance_solar_exterior_p"], + ABSORPTANCE_SOLAR_EXTERIOR, + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): + return std_equal( + calc_vals["absorptance_solar_exterior_p"], + ABSORPTANCE_SOLAR_EXTERIOR, ) def get_pass_msg(self, context, calc_vals=None, data=None): diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule35.py b/rct229/rulesets/ashrae9012019/section5/section5rule35.py index c9ce92f6d8..3a45997725 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule35.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule35.py @@ -54,6 +54,12 @@ def __init__(self): USER=False, BASELINE_0=True, PROPOSED=False ), required_fields={"$.building_segments[*].zones[*]": ["surfaces"]}, + precision={ + "building_total_air_leakage_rate": { + "precision": 1, + "unit": "cfm", + } + }, ) def get_calc_vals(self, context, data=None): @@ -96,14 +102,20 @@ def get_calc_vals(self, context, data=None): return { "building_total_air_leakage_rate": CalcQ( - "volumetric_flow_rate", building_total_air_leakage_rate + "air_flow_rate", building_total_air_leakage_rate ), "target_air_leakage_rate_75pa_b": CalcQ( - "volumetric_flow_rate", target_air_leakage_rate_75pa_b + "air_flow_rate", target_air_leakage_rate_75pa_b ), } def rule_check(self, context, calc_vals=None, data=None): + return self.precision_comparison["building_total_air_leakage_rate"]( + calc_vals["building_total_air_leakage_rate"], + calc_vals["target_air_leakage_rate_75pa_b"] * TOTAL_AIR_LEAKAGE_FACTOR, + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): building_total_air_leakage_rate = calc_vals[ "building_total_air_leakage_rate" ] diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule36.py b/rct229/rulesets/ashrae9012019/section5/section5rule36.py index 8309d472a6..1be7444db2 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule36.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule36.py @@ -71,6 +71,12 @@ def __init__(self): "$": ["infiltration"], "infiltration": ["flow_rate"], }, + precision={ + "total_infiltration_rate_b": { + "precision": 0.1, + "unit": "cfm", + } + }, ) def get_calc_vals(self, context, data=None): @@ -82,15 +88,15 @@ def get_calc_vals(self, context, data=None): return { "baseline_infiltration": CalcQ( - "volumetric_flow_rate", zone_infiltration_flow_rate_b + "air_flow_rate", zone_infiltration_flow_rate_b ), "proposed_infiltration": CalcQ( - "volumetric_flow_rate", zone_infiltration_flow_rate_p + "air_flow_rate", zone_infiltration_flow_rate_p ), } def rule_check(self, context, calc_vals=None, data=None): - return ( - calc_vals["baseline_infiltration"] - == calc_vals["proposed_infiltration"] + return self.precision_comparison["total_infiltration_rate_b"]( + calc_vals["baseline_infiltration"], + calc_vals["proposed_infiltration"], ) diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule37.py b/rct229/rulesets/ashrae9012019/section5/section5rule37.py index c22800ed1b..1b039c0381 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule37.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule37.py @@ -57,6 +57,12 @@ def __init__(self): USER=False, BASELINE_0=False, PROPOSED=True ), required_fields={"$..zones[*]": ["surfaces"]}, + precision={ + "building_total_air_leakage_rate_b": { + "precision": 1, + "unit": "cfm", + } + }, ) def get_calc_vals(self, context, data=None): @@ -109,13 +115,13 @@ def get_calc_vals(self, context, data=None): return { "building_total_air_leakage_rate": CalcQ( - "volumetric_flow_rate", building_total_air_leakage_rate + "air_flow_rate", building_total_air_leakage_rate ), "building_total_measured_air_leakage_rate": CalcQ( - "volumetric_flow_rate", building_total_measured_air_leakage_rate + "air_flow_rate", building_total_measured_air_leakage_rate ), "target_air_leakage_rate_75pa_p": CalcQ( - "volumetric_flow_rate", target_air_leakage_rate_75pa_p + "air_flow_rate", target_air_leakage_rate_75pa_p ), "empty_measured_air_leakage_rate_flow_flag": empty_measured_air_leakage_rate_flow_flag, } @@ -130,7 +136,7 @@ def manual_check_required(self, context, calc_vals=None, data=None): ] return ( - not std_equal( + not self.precision_comparison["building_total_air_leakage_rate_b"]( building_total_air_leakage_rate, TOTAL_AIR_LEAKAGE_COEFF * target_air_leakage_rate_75pa_p, ) @@ -149,15 +155,38 @@ def rule_check(self, context, calc_vals=None, data=None): "empty_measured_air_leakage_rate_flow_flag" ] - return std_equal( + return self.precision_comparison["building_total_air_leakage_rate_b"]( building_total_air_leakage_rate, TOTAL_AIR_LEAKAGE_COEFF * target_air_leakage_rate_75pa_p, ) or ( - building_total_air_leakage_rate - != TOTAL_AIR_LEAKAGE_COEFF * target_air_leakage_rate_75pa_p - and empty_measured_air_leakage_rate_flow_flag == False - and std_equal( + empty_measured_air_leakage_rate_flow_flag == False + and self.precision_comparison["building_total_air_leakage_rate_b"]( building_total_air_leakage_rate, TOTAL_AIR_LEAKAGE_COEFF * building_total_measured_air_leakage_rate, ) ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): + building_total_air_leakage_rate = calc_vals[ + "building_total_air_leakage_rate" + ] + building_total_measured_air_leakage_rate = calc_vals[ + "building_total_measured_air_leakage_rate" + ] + target_air_leakage_rate_75pa_p = calc_vals["target_air_leakage_rate_75pa_p"] + empty_measured_air_leakage_rate_flow_flag = calc_vals[ + "empty_measured_air_leakage_rate_flow_flag" + ] + + return std_equal( + building_total_air_leakage_rate, + TOTAL_AIR_LEAKAGE_COEFF * target_air_leakage_rate_75pa_p, + ) or ( + not std_equal( + building_total_air_leakage_rate, + TOTAL_AIR_LEAKAGE_COEFF * target_air_leakage_rate_75pa_p, + ) + and empty_measured_air_leakage_rate_flow_flag == False + and building_total_air_leakage_rate + == TOTAL_AIR_LEAKAGE_COEFF * building_total_measured_air_leakage_rate + ) diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule39.py b/rct229/rulesets/ashrae9012019/section5/section5rule39.py index 8291a3fc31..523a356f68 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule39.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule39.py @@ -110,6 +110,12 @@ def __init__(self): required_fields={ "$": ["subclassification", "u_factor"], }, + precision={ + "subsurface_u_factor_b": { + "precision": 0.01, + "unit": "Btu/(hr*ft2*R)", + } + }, ) def get_calc_vals(self, context, data=None): @@ -223,6 +229,11 @@ def get_manual_check_required_msg( return manual_check_required_msg def rule_check(self, context, calc_vals=None, data=None): + return self.precision_comparison["subsurface_u_factor_b"]( + calc_vals["u_factor_b"], calc_vals["target_u_factor_b"] + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): u_factor_b = calc_vals["u_factor_b"] target_u_factor_b = calc_vals["target_u_factor_b"] return std_equal(target_u_factor_b, u_factor_b) diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule4.py b/rct229/rulesets/ashrae9012019/section5/section5rule4.py index 8b20489000..b3dbd1bfb4 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule4.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule4.py @@ -81,6 +81,12 @@ def __init__(self): "$": ["construction"], "construction": ["u_factor"], }, + precision={ + "roof_u_factor_b": { + "precision": 0.001, + "unit": "Btu/(hr*ft2*R)", + } + }, ) def get_calc_vals(self, context, data=None): @@ -133,7 +139,11 @@ def manual_check_required(self, context=None, calc_vals=None, data=None): ) def rule_check(self, context=None, calc_vals=None, data=None): + return self.precision_comparison["roof_u_factor_b"]( + calc_vals["roof_u_factor"], calc_vals["target_u_factor"] + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): roof_u_factor = calc_vals["roof_u_factor"] target_u_factor = calc_vals["target_u_factor"] - - return std_equal(val=roof_u_factor, std_val=target_u_factor) + return std_equal(roof_u_factor, target_u_factor) diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule40.py b/rct229/rulesets/ashrae9012019/section5/section5rule40.py index 57c9bcb885..3142b113f1 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule40.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule40.py @@ -15,6 +15,7 @@ get_surface_conditioning_category_dict, ) from rct229.utils.jsonpath_utils import find_one +from rct229.utils.std_comparisons import std_equal class Section5Rule40(RuleDefinitionListIndexedBase): @@ -71,6 +72,16 @@ def __init__(self): rmds_used=produce_ruleset_model_description( USER=False, BASELINE_0=True, PROPOSED=True ), + precision={ + "absorptance_thermal_exterior_b": { + "precision": 0.01, + "unit": "", + }, + "absorptance_solar_exterior_b": { + "precision": 0.01, + "unit": "", + }, + }, ) def get_calc_vals(self, context, data=None): @@ -119,7 +130,8 @@ def rule_check(self, context, calc_vals=None, data=None): absorptance_thermal_exterior_p = calc_vals[ "absorptance_thermal_exterior_p" ] - return ( - absorptance_solar_exterior_b == absorptance_solar_exterior_p - and absorptance_thermal_exterior_b == absorptance_thermal_exterior_p + return self.precision_comparison["absorptance_solar_exterior_b"]( + absorptance_solar_exterior_b, absorptance_solar_exterior_p + ) and self.precision_comparison["absorptance_thermal_exterior_b"]( + absorptance_thermal_exterior_b, absorptance_thermal_exterior_p ) diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule6.py b/rct229/rulesets/ashrae9012019/section5/section5rule6.py index f378e59df3..29bb95e93c 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule6.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule6.py @@ -80,6 +80,12 @@ def __init__(self): "$": ["construction"], "construction": ["c_factor"], }, + precision={ + "bg_wall_c_factor_b": { + "precision": 0.001, + "unit": "Btu/(hr*ft2*R)", + } + }, ) def get_calc_vals(self, context, data=None): @@ -136,7 +142,11 @@ def manual_check_required(self, context, calc_vals=None, data=None): ) def rule_check(self, context, calc_vals=None, data=None): + return self.precision_comparison["bg_wall_c_factor_b"]( + calc_vals["below_grade_wall_c_factor"], calc_vals["target_c_factor"] + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): below_grade_wall_c_factor = calc_vals["below_grade_wall_c_factor"] target_c_factor = calc_vals["target_c_factor"] - - return std_equal(val=below_grade_wall_c_factor, std_val=target_c_factor) + return std_equal(below_grade_wall_c_factor, target_c_factor) diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule8.py b/rct229/rulesets/ashrae9012019/section5/section5rule8.py index 2ba78e665e..816e161f46 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule8.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule8.py @@ -77,6 +77,12 @@ def __init__(self): USER=False, BASELINE_0=True, PROPOSED=False ), required_fields={}, + precision={ + "ag_wall_u_factor_b": { + "precision": 0.001, + "unit": "Btu/(hr*ft2*R)", + } + }, ) def get_calc_vals(self, context, data=None): @@ -129,7 +135,11 @@ def manual_check_required(self, context, calc_vals=None, data=None): return target_u_factor_res != target_u_factor_nonres def rule_check(self, context, calc_vals=None, data=None): + return self.precision_comparison["ag_wall_u_factor_b"]( + calc_vals["above_grade_wall_u_factor"], calc_vals["target_u_factor"] + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): above_grade_wall_u_factor = calc_vals["above_grade_wall_u_factor"] target_u_factor = calc_vals["target_u_factor"] - - return std_equal(val=above_grade_wall_u_factor, std_val=target_u_factor) + return std_equal(above_grade_wall_u_factor, target_u_factor) diff --git a/rct229/rulesets/ashrae9012019/section6/section6rule1.py b/rct229/rulesets/ashrae9012019/section6/section6rule1.py index 20b3aea0cc..f37ac493b6 100644 --- a/rct229/rulesets/ashrae9012019/section6/section6rule1.py +++ b/rct229/rulesets/ashrae9012019/section6/section6rule1.py @@ -40,6 +40,16 @@ def __init__(self): USER=False, BASELINE_0=False, PROPOSED=True ), required_fields={"$.zones[*]": ["volume"]}, + precision={ + "building_segment_design_lighting_wattage_area": { + "precision": 0.01, + "unit": "W", + }, + "building_segment_design_lighting_wattage_space": { + "precision": 0.01, + "unit": "W", + }, + }, ) def get_calc_vals(self, context, data=None): @@ -118,10 +128,22 @@ def rule_check(self, context, calc_vals=None, data=None): return ( (allowable_LPD_BAM or not check_BAM_flag) - and building_segment_design_lighting_wattage - <= allowable_LPD_wattage_BAM - or building_segment_design_lighting_wattage - <= allowable_lighting_wattage_SBS + and ( + building_segment_design_lighting_wattage < allowable_LPD_wattage_BAM + ) + or self.precision_comparison[ + "building_segment_design_lighting_wattage_area" + ](building_segment_design_lighting_wattage, allowable_LPD_wattage_BAM) + or ( + building_segment_design_lighting_wattage + < allowable_lighting_wattage_SBS + or self.precision_comparison[ + "building_segment_design_lighting_wattage_space" + ]( + building_segment_design_lighting_wattage, + allowable_lighting_wattage_SBS, + ) + ) ) def get_pass_msg(self, context, calc_vals=None, data=None): diff --git a/rct229/rulesets/ashrae9012019/section6/section6rule2.py b/rct229/rulesets/ashrae9012019/section6/section6rule2.py index 91b088bd16..d71ca76d5f 100644 --- a/rct229/rulesets/ashrae9012019/section6/section6rule2.py +++ b/rct229/rulesets/ashrae9012019/section6/section6rule2.py @@ -7,6 +7,7 @@ from rct229.schema.schema_enums import SchemaEnums from rct229.utils.jsonpath_utils import find_all from rct229.utils.pint_utils import ZERO, CalcQ +from rct229.utils.std_comparisons import std_equal GUEST_ROOM = SchemaEnums.schema_enums[ "LightingSpaceOptions2019ASHRAE901TG37" @@ -61,6 +62,12 @@ def __init__(self): "$": ["interior_lighting"], "interior_lighting[*]": ["power_per_area"], }, + precision={ + "space_lighting_power_per_area_p": { + "precision": 0.01, + "unit": "W/ft2", + } + }, ) def is_applicable(self, context, data=None): @@ -113,6 +120,21 @@ def rule_check(self, context, calc_vals=None, data=None): "space_lighting_power_per_area_u" ] - return space_lighting_power_per_area_p == max( - lighting_power_allowance_p, space_lighting_power_per_area_u + return self.precision_comparison["space_lighting_power_per_area_p"]( + space_lighting_power_per_area_p, + max(lighting_power_allowance_p, space_lighting_power_per_area_u), + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): + lighting_power_allowance_p = calc_vals["lighting_power_allowance_p"] + space_lighting_power_per_area_p = calc_vals[ + "space_lighting_power_per_area_p" + ] + space_lighting_power_per_area_u = calc_vals[ + "space_lighting_power_per_area_u" + ] + + return std_equal( + space_lighting_power_per_area_p, + max(lighting_power_allowance_p, space_lighting_power_per_area_u), ) diff --git a/rct229/rulesets/ashrae9012019/section6/section6rule3.py b/rct229/rulesets/ashrae9012019/section6/section6rule3.py index d927b38f57..b1e481784b 100644 --- a/rct229/rulesets/ashrae9012019/section6/section6rule3.py +++ b/rct229/rulesets/ashrae9012019/section6/section6rule3.py @@ -63,6 +63,12 @@ def __init__(self): rmds_used=produce_ruleset_model_description( USER=True, BASELINE_0=False, PROPOSED=True ), + precision={ + "total_space_lpd_p": { + "precision": 0.01, + "unit": "W/ft2", + } + }, ) def get_calc_vals(self, context, data=None): @@ -96,6 +102,6 @@ def manual_check_required(self, context, calc_vals=None, data=None): ) def rule_check(self, context, calc_vals=None, data=None): - total_space_lpd_u = calc_vals["total_space_lpd_u"] - total_space_lpd_p = calc_vals["total_space_lpd_p"] - return std_equal(total_space_lpd_u, total_space_lpd_p) + return self.precision_comparison["total_space_lpd_p"]( + calc_vals["total_space_lpd_p"], calc_vals["total_space_lpd_u"] + ) diff --git a/rct229/rulesets/ashrae9012019/section6/section6rule4.py b/rct229/rulesets/ashrae9012019/section6/section6rule4.py index 64e446cb7a..d468c67ba8 100644 --- a/rct229/rulesets/ashrae9012019/section6/section6rule4.py +++ b/rct229/rulesets/ashrae9012019/section6/section6rule4.py @@ -128,6 +128,34 @@ def rule_check(self, context, calc_vals=None, data=None): total_space_lpd_b = calc_vals["total_space_lpd_b"] lpd_allowance_b = calc_vals["lpd_allowance_b"] + return ( + # Not Case 1 + not ( + space_lighting_status_type_p + == LightingStatusType.AS_DESIGNED_OR_AS_EXISTING + and not lighting_space_type_b + ) + # Passes for both values of space_lighting_status_type_p + and ( + space_lighting_status_type_p + in [ + LightingStatusType.AS_DESIGNED_OR_AS_EXISTING, + LightingStatusType.NOT_YET_DESIGNED_OR_MATCH_TABLE_9_5_1, + ] + and total_space_lpd_b == lpd_allowance_b + ) + ) + + def is_tolerance_fail(self, context, calc_vals=None, data=None): + space_b = context.BASELINE_0 + lighting_space_type_b = space_b.get("lighting_space_type") + + space_lighting_status_type_p = calc_vals[ + "space_lighting_status_type_p" + ] + total_space_lpd_b = calc_vals["total_space_lpd_b"] + lpd_allowance_b = calc_vals["lpd_allowance_b"] + return ( # Not Case 1 not ( diff --git a/rct229/rulesets/ashrae9012019/section6/section6rule5.py b/rct229/rulesets/ashrae9012019/section6/section6rule5.py index 0b446a4033..17d5406d5f 100644 --- a/rct229/rulesets/ashrae9012019/section6/section6rule5.py +++ b/rct229/rulesets/ashrae9012019/section6/section6rule5.py @@ -185,7 +185,6 @@ def rule_check(self, context, calc_vals=None, data=None): schedule_comparison_result = calc_vals[ "schedule_comparison_result" ] - return ( schedule_comparison_result["total_hours_compared"] == schedule_comparison_result["total_hours_matched"] diff --git a/rct229/ruletest_engine/ruletest_jsons/ashrae9012019/section19/rule_19_7.json b/rct229/ruletest_engine/ruletest_jsons/ashrae9012019/section19/rule_19_7.json index b11d98648a..12e834b39a 100644 --- a/rct229/ruletest_engine/ruletest_jsons/ashrae9012019/section19/rule_19_7.json +++ b/rct229/ruletest_engine/ruletest_jsons/ashrae9012019/section19/rule_19_7.json @@ -25,15 +25,12 @@ "buildings": [ { "id": "Building 1", - "building_open_schedule": "Required Building Schedule 1", "building_segments": [ { "id": "Building Segment 1", "zones": [ { "id": "Thermal Zone 1", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 1", @@ -57,8 +54,6 @@ }, { "id": "Thermal Zone 2", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 2", @@ -8941,15 +8936,12 @@ "buildings": [ { "id": "Building 1", - "building_open_schedule": "Required Building Schedule 1", "building_segments": [ { "id": "Building Segment 1", "zones": [ { "id": "Thermal Zone 1", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 1", @@ -8973,8 +8965,6 @@ }, { "id": "Thermal Zone 2", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 2", @@ -17876,15 +17866,12 @@ "buildings": [ { "id": "Building 1", - "building_open_schedule": "Required Building Schedule 1", "building_segments": [ { "id": "Building Segment 1", "zones": [ { "id": "Thermal Zone 1", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 1", @@ -17908,8 +17895,6 @@ }, { "id": "Thermal Zone 2", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 2", @@ -26791,15 +26776,12 @@ "buildings": [ { "id": "Building 1", - "building_open_schedule": "Required Building Schedule 1", "building_segments": [ { "id": "Building Segment 1", "zones": [ { "id": "Thermal Zone 1", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 1", @@ -26823,8 +26805,6 @@ }, { "id": "Thermal Zone 2", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 2", @@ -35725,15 +35705,12 @@ "buildings": [ { "id": "Building 1", - "building_open_schedule": "Required Building Schedule 1", "building_segments": [ { "id": "Building Segment 1", "zones": [ { "id": "Thermal Zone 1", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 1", @@ -35759,8 +35736,6 @@ }, { "id": "Thermal Zone 2", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 2", @@ -44597,15 +44572,12 @@ "buildings": [ { "id": "Building 1", - "building_open_schedule": "Required Building Schedule 1", "building_segments": [ { "id": "Building Segment 1", "zones": [ { "id": "Thermal Zone 1", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 1", @@ -44631,8 +44603,6 @@ }, { "id": "Thermal Zone 2", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 2", @@ -53488,15 +53458,12 @@ "buildings": [ { "id": "Building 1", - "building_open_schedule": "Required Building Schedule 1", "building_segments": [ { "id": "Building Segment 1", "zones": [ { "id": "Thermal Zone 1", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 1", @@ -53523,8 +53490,6 @@ }, { "id": "Thermal Zone 2", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 2", @@ -62362,15 +62327,12 @@ "buildings": [ { "id": "Building 1", - "building_open_schedule": "Required Building Schedule 1", "building_segments": [ { "id": "Building Segment 1", "zones": [ { "id": "Thermal Zone 1", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 1", @@ -62397,8 +62359,6 @@ }, { "id": "Thermal Zone 2", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 2", @@ -71255,15 +71215,12 @@ "buildings": [ { "id": "Building 1", - "building_open_schedule": "Required Building Schedule 1", "building_segments": [ { "id": "Building Segment 1", "zones": [ { "id": "Thermal Zone 1", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 1", @@ -71290,8 +71247,6 @@ }, { "id": "Thermal Zone 2", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 2", @@ -80128,15 +80083,12 @@ "buildings": [ { "id": "Building 1", - "building_open_schedule": "Required Building Schedule 1", "building_segments": [ { "id": "Building Segment 1", "zones": [ { "id": "Thermal Zone 1", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 1", @@ -80163,8 +80115,6 @@ }, { "id": "Thermal Zone 2", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 2", @@ -89020,15 +88970,12 @@ "buildings": [ { "id": "Building 1", - "building_open_schedule": "Required Building Schedule 1", "building_segments": [ { "id": "Building Segment 1", "zones": [ { "id": "Thermal Zone 1", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 1", @@ -89052,8 +88999,6 @@ }, { "id": "Thermal Zone 2", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 2", @@ -97936,15 +97881,12 @@ "buildings": [ { "id": "Building 1", - "building_open_schedule": "Required Building Schedule 1", "building_segments": [ { "id": "Building Segment 1", "zones": [ { "id": "Thermal Zone 1", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 1", @@ -97968,8 +97910,6 @@ }, { "id": "Thermal Zone 2", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 2", @@ -106871,15 +106811,12 @@ "buildings": [ { "id": "Building 1", - "building_open_schedule": "Required Building Schedule 1", "building_segments": [ { "id": "Building Segment 1", "zones": [ { "id": "Thermal Zone 1", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 1", @@ -106903,8 +106840,6 @@ }, { "id": "Thermal Zone 2", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 2", @@ -115786,15 +115721,12 @@ "buildings": [ { "id": "Building 1", - "building_open_schedule": "Required Building Schedule 1", "building_segments": [ { "id": "Building Segment 1", "zones": [ { "id": "Thermal Zone 1", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 1", @@ -115818,8 +115750,6 @@ }, { "id": "Thermal Zone 2", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 2", @@ -124720,15 +124650,12 @@ "buildings": [ { "id": "Building 1", - "building_open_schedule": "Required Building Schedule 1", "building_segments": [ { "id": "Building Segment 1", "zones": [ { "id": "Thermal Zone 1", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 1", @@ -124752,8 +124679,6 @@ }, { "id": "Thermal Zone 2", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 2", @@ -133636,15 +133561,12 @@ "buildings": [ { "id": "Building 1", - "building_open_schedule": "Required Building Schedule 1", "building_segments": [ { "id": "Building Segment 1", "zones": [ { "id": "Thermal Zone 1", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 1", @@ -133668,8 +133590,6 @@ }, { "id": "Thermal Zone 2", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 2", @@ -142571,15 +142491,12 @@ "buildings": [ { "id": "Building 1", - "building_open_schedule": "Required Building Schedule 1", "building_segments": [ { "id": "Building Segment 1", "zones": [ { "id": "Thermal Zone 1", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 1", @@ -142603,8 +142520,6 @@ }, { "id": "Thermal Zone 2", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 2", @@ -151487,15 +151402,12 @@ "buildings": [ { "id": "Building 1", - "building_open_schedule": "Required Building Schedule 1", "building_segments": [ { "id": "Building Segment 1", "zones": [ { "id": "Thermal Zone 1", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 1", @@ -151519,8 +151431,6 @@ }, { "id": "Thermal Zone 2", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 2", @@ -160422,15 +160332,12 @@ "buildings": [ { "id": "Building 1", - "building_open_schedule": "Required Building Schedule 1", "building_segments": [ { "id": "Building Segment 1", "zones": [ { "id": "Thermal Zone 1", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 1", @@ -160440,7 +160347,7 @@ "heating_source": "HOT_WATER", "heating_from_loop": "Boiler Loop 1", "has_demand_control_ventilation": false, - "minimum_outdoor_airflow": 117.98686079999996, + "minimum_outdoor_airflow": 115.62712358399996, "minimum_outdoor_airflow_multiplier_schedule": "Outdoor Airflow Multiplier Schedule 1" } ], @@ -160454,8 +160361,6 @@ }, { "id": "Thermal Zone 2", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 2", @@ -160465,7 +160370,7 @@ "heating_source": "HOT_WATER", "heating_from_loop": "Boiler Loop 1", "has_demand_control_ventilation": false, - "minimum_outdoor_airflow": 117.98686079999996, + "minimum_outdoor_airflow": 115.62712358399996, "minimum_outdoor_airflow_multiplier_schedule": "Outdoor Airflow Multiplier Schedule 1" } ], @@ -169338,15 +169243,12 @@ "buildings": [ { "id": "Building 1", - "building_open_schedule": "Required Building Schedule 1", "building_segments": [ { "id": "Building Segment 1", "zones": [ { "id": "Thermal Zone 1", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 1", @@ -169370,8 +169272,6 @@ }, { "id": "Thermal Zone 2", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 2", @@ -178272,15 +178172,12 @@ "buildings": [ { "id": "Building 1", - "building_open_schedule": "Required Building Schedule 1", "building_segments": [ { "id": "Building Segment 1", "zones": [ { "id": "Thermal Zone 1", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 1", @@ -178304,8 +178201,6 @@ }, { "id": "Thermal Zone 2", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 2", @@ -187187,15 +187082,12 @@ "buildings": [ { "id": "Building 1", - "building_open_schedule": "Required Building Schedule 1", "building_segments": [ { "id": "Building Segment 1", "zones": [ { "id": "Thermal Zone 1", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 1", @@ -187219,8 +187111,6 @@ }, { "id": "Thermal Zone 2", - "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", - "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", "terminals": [ { "id": "VAV Air Terminal 2", diff --git a/rct229/ruletest_engine/ruletest_jsons/ashrae9012019/section5/rule_5_15.json b/rct229/ruletest_engine/ruletest_jsons/ashrae9012019/section5/rule_5_15.json index ac0c1a5da5..728f9123e1 100644 --- a/rct229/ruletest_engine/ruletest_jsons/ashrae9012019/section5/rule_5_15.json +++ b/rct229/ruletest_engine/ruletest_jsons/ashrae9012019/section5/rule_5_15.json @@ -283,7 +283,7 @@ "id": "SubSurface 1", "classification": "WINDOW", "u_factor": 3.2366105565544463, - "glazed_area": 2.7870912, + "glazed_area": 5.5741824, "opaque_area": 7.432243199999999 } ], @@ -302,7 +302,7 @@ "id": "SubSurface 2", "classification": "WINDOW", "u_factor": 3.2366105565544463, - "glazed_area": 2.7870912, + "glazed_area": 5.5741824, "opaque_area": 7.432243199999999 } ],