From 6c65d9089a8f1513817f4e236a237e1972a1c4dd Mon Sep 17 00:00:00 2001 From: yunjoonjung Date: Tue, 26 Dec 2023 07:49:29 -0800 Subject: [PATCH 01/18] initial commit --- .../ashrae9012019/section5/__init__.py | 1 + .../ashrae9012019/section5/section5rule1.py | 121 ++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 rct229/rulesets/ashrae9012019/section5/section5rule1.py diff --git a/rct229/rulesets/ashrae9012019/section5/__init__.py b/rct229/rulesets/ashrae9012019/section5/__init__.py index 60d7e214af..2d82c30e70 100644 --- a/rct229/rulesets/ashrae9012019/section5/__init__.py +++ b/rct229/rulesets/ashrae9012019/section5/__init__.py @@ -3,6 +3,7 @@ # TODO: Fix section5rule2 and section5rule49 - they currently cause exceptions __all__ = [ + "section5rule1", "section5rule2", "section5rule3", "section5rule4", diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule1.py b/rct229/rulesets/ashrae9012019/section5/section5rule1.py new file mode 100644 index 0000000000..215640ba81 --- /dev/null +++ b/rct229/rulesets/ashrae9012019/section5/section5rule1.py @@ -0,0 +1,121 @@ +from rct229.rule_engine.rule_base import RuleDefinitionBase +from rct229.rule_engine.rule_list_indexed_base import RuleDefinitionListIndexedBase +from rct229.rule_engine.ruleset_model_factory import produce_ruleset_model_instance +from rct229.rulesets.ashrae9012019 import ( + BASELINE_0, + BASELINE_90, + BASELINE_180, + BASELINE_270, + PROPOSED, + USER, +) +from rct229.rulesets.ashrae9012019.ruleset_functions.get_opaque_surface_type import ( + OpaqueSurfaceType as OST, +) +from rct229.rulesets.ashrae9012019.ruleset_functions.get_opaque_surface_type import ( + get_opaque_surface_type, +) +from rct229.rulesets.ashrae9012019.ruleset_functions.get_surface_conditioning_category_dict import ( + SurfaceConditioningCategory as SCC, +) +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.assertions import getattr_ +from rct229.utils.pint_utils import ZERO + +SUBSURFACE_CLASSIFICATION = SchemaEnums.schema_enums["SubsurfaceClassificationOptions"] +RULESET_MODEL = SchemaEnums.schema_enums["RulesetModelOptions2019ASHRAE901"] + + +class Section5Rule1(RuleDefinitionListIndexedBase): + """Rule 1 of ASHRAE 90.1-2019 Appendix G Section 5 (Envelope)""" + + def __init__(self): + super(Section5Rule1, self).__init__( + rmrs_used=produce_ruleset_model_instance( + USER=True, BASELINE_0=True, PROPOSED=True + ), + each_rule=Section5Rule1.SurfaceRule(), + index_rmr=BASELINE_0, + id="5-1", + description="There are four baseline rotations (i.e., four baseline models differing in azimuth by 90 degrees and four sets of baseline model results) if vertical fenestration area per each orientation differ by more than 5%.", + ruleset_section_title="Envelope", + standard_section="Table G3.1#5a baseline column", + is_primary_rule=True, + # rmr_context="ruleset_model_descriptions[0]", + ) + + # class RMDRule(RuleDefinitionListIndexedBase): + # def __init__(self): + # super(Section5Rule1.RMDRule, self).__init__( + # rmrs_used=produce_ruleset_model_instance( + # USER=True, BASELINE_0=True, PROPOSED=True + # ), + # each_rule=Section5Rule1.RMDRule.SurfaceRule(), + # index_rmr=BASELINE_0, + # list_path="$.buildings[*].building_segments[*].zones[*].surfaces[*]", + # ) + + def create_data(self, context, data): + RMD_u = context.USER + RMD_b0 = context.BASELINE_0 + RMD_b90 = context.BASELINE_90 + RMD_b180 = context.BASELINE_180 + RMD_b270 = context.BASELINE_270 + RMD_p = context.PROPOSED + + return + + class SurfaceRule(RuleDefinitionListIndexedBase): + def __init__(self): + super(Section5Rule1.SurfaceRule, self).__init__( + rmrs_used=produce_ruleset_model_instance( + USER=False, BASELINE_0=True, PROPOSED=False + ), + each_rule=Section5Rule1.SurfaceRule.SubsurfaceRule(), + index_rmr=BASELINE_0, + list_path="$.subsurfaces[*]", + ) + + def create_data(self, context, data): + surface_b = context.BASELINE_0 + + azimuth_fen_area_dict_b = {} + if get_opaque_surface_type(surface_b) == OST.ABOVE_GRADE_WALL: + surface_azimuth_b = getattr_(surface_b, "surfaces", "azimuth") + + if surface_azimuth_b not in azimuth_fen_area_dict_b: + azimuth_fen_area_dict_b[surface_azimuth_b] = 0 + + return {"azimuth_fen_area_dict_b": azimuth_fen_area_dict_b} + + class SubsurfaceRule(RuleDefinitionBase): + def __init__(self): + super( + Section5Rule1.SurfaceRule.SubsurfaceRule, + self, + ).__init__( + rmrs_used=produce_ruleset_model_instance( + USER=False, BASELINE_0=True, PROPOSED=False + ), + ) + + def get_calc_vals(self, context, data=None): + subsurface_b = context.BASELINE_0 + + total_surface_fenestration_area_b = ZERO.AREA + glazed_area_b = subsurface_b.get("glazed_area", ZERO.AREA) + opaque_area_b = subsurface_b.get("opaque_area", ZERO.AREA) + if subsurface_b["classification"] == SUBSURFACE_CLASSIFICATION.DOOR: + if glazed_area_b > opaque_area_b: + total_surface_fenestration_area_b += ( + glazed_area_b + opaque_area_b + ) + else: + total_surface_fenestration_area_b += ( + glazed_area_b + opaque_area_b + ) + + return + + def rule_check(self, context, calc_vals=None, data=None): + return From 6eec23d27cdf3460a7a9e54a1776a2f2e68b6239 Mon Sep 17 00:00:00 2001 From: yunjoonjung Date: Sun, 7 Jan 2024 17:28:46 -0800 Subject: [PATCH 02/18] Finished dev --- docs/section5/Rule5-1.md | 32 +-- .../ashrae9012019/section5/section5rule1.py | 254 +++++++++++++----- 2 files changed, 210 insertions(+), 76 deletions(-) diff --git a/docs/section5/Rule5-1.md b/docs/section5/Rule5-1.md index 78ff5cdf21..7d69dd807b 100644 --- a/docs/section5/Rule5-1.md +++ b/docs/section5/Rule5-1.md @@ -21,34 +21,34 @@ None ## Rule Logic: Get the fenestration area for each unique orientation (i.e., azimuth) and then check if the minimum and maximum areas differ by 5% or more -- Create a blank dictionary that will have all unique azimuths paired with the total vertical fenestration area: `azimuth_fen_area_dict = {}` +- Create a blank dictionary that will have all unique azimuths paired with the total vertical fenestration area: `azimuth_fen_area_dict_b = {}` - For each building in the B_RMD: `for bldg in B_RMD.buildings:` - For each building_segment in the bldg: `for bldg_seg in bldg.building_segments` - For each zone in the building_segment: `for zone in bldg_seg.zones` - For each surface in zone: `for surface in zone.surfaces:` - Check if surface is above-grade wall: `if get_opaque_surface_type(surface) == "ABOVE-GRADE WALL":` - Get the azimuth: `surface_azimuth = surface.azimuth` - - Check if the azimuth is not in a key in the dictionary of azimuths and fen area, if it is not then add it: `if surface_azimuth not in azimuth_fen_area_dict:` - - Add the unique azimuth to the dictionary as a key and set initial fen area to 0: `azimuth_fen_area_dict[surface_azimuth]= 0` + - Check if the azimuth is not in a key in the dictionary of azimuths and fen area, if it is not then add it: `if surface_azimuth not in azimuth_fen_area_dict_b:` + - Add the unique azimuth to the dictionary as a key and set initial fen area to 0: `azimuth_fen_area_dict_b[surface_azimuth]= 0` - Reset the total surface fenestration area variable to 0: `total_surface_fenestration_area = 0` - For each subsurface associated with the surface: `for sub_surface in surface.subsurfaces:` - Check if subsurface is door: `if sub_surface.classification == "DOOR":` - If glazed area in door is more than 50% of the total door area, add door area to total_surface_fenestration_area: `if sub_surface.glazed_area > subsurface.opaque_area: total_surface_fenestration_area += sub_surface.glazed_area + sub_surface.opaque_area` - Else, subsurface is not door, add total area to total_surface_fenestration_area (because we checked that the surface is an above grade wall we can assume that the subsurface classification is not and could not be a skylight so no need to check this): `total_surface_fenestration_area += subsurface.glazed_area + subsurface.opaque_area` - - Add the total_surface_fenestration_area summed for the surface to the total fen area associated with the azimuth: `azimuth_fen_area_dict[surface_azimuth] += total_surface_fenestration_area` + - Add the total_surface_fenestration_area summed for the surface to the total fen area associated with the azimuth: `azimuth_fen_area_dict_b[surface_azimuth] += total_surface_fenestration_area` -- Loop through the dictionary keys and put the area in bins depending on the azimuth (bins will be in 3 degree increments). Use this logic: if azimuth >= 0 and < 3 then put area in the 0-3 bin, if azimuth >=3 and < 6 then put the area in the the 3-6 bin, etc. `for azi in azimuth_fen_area_dict.keys():` +- Loop through the dictionary keys and put the area in bins depending on the azimuth (bins will be in 3 degree increments). Use this logic: if azimuth >= 0 and < 3 then put area in the 0-3 bin, if azimuth >=3 and < 6 then put the area in the the 3-6 bin, etc. `for azi in azimuth_fen_area_dict_b.keys():` - Lookup the bin that the azimuth falls into based on the value of the azimuth using this logic. Bin lookup table based on this logic: if azimuth >= 0 and < 3 then put the fen area in the 0-3 bin, if azimuth >=3 and < 6 then put the area in the the 3-6 bin, etc. (this assumes the RCT team will create a lookup table to make it easy to lookup the bin that the azimuth (azi) falls into): `bin = lookup(azi, lookuptable)` - - Add the area to the bin in a revised binned dictionary (bin is key, area is value in dictionary): `azimuth_fen_area__binned_dict[bin] += azimuth_fen_area_dict[azi]` + - Add the area to the bin in a revised binned dictionary (bin is key, area is value in dictionary): `azimuth_fen_area_dict_b[bin] += azimuth_fen_area_dict_b[azi]` Check if the area differs by 5 percent or more. -- Get the max fen area: `max_fen_area = azimuth_fen_area__binned_dict[max(azimuth_fen_area__binned_dict, key=azimuth_fen_area__binned_dict.get)]` -- Get the min fen area: `min_fen_area = azimuth_fen_area__binned_dict[min(azimuth_fen_area__binned_dict, key=azimuth_fen_area__binned_dict.get)]` +- Get the max fen area: `max_fen_area = azimuth_fen_area_dict_b[max(azimuth_fen_area_dict_b, key=azimuth_fen_area_dict_b.get)]` +- Get the min fen area: `min_fen_area = azimuth_fen_area_dict_b[min(azimuth_fen_area_dict_b, key=azimuth_fen_area_dict_b.get)]` - Calculate the % difference, take the maximum calculated: `percent_difference = max(abs(max_fen_area- min_fen_area)/max_fen_area,abs(min_fen_area- max_fen_area)/min_fen_area)` -- Check if the % difference is 5% or more, if it is then set rotation_expected boolean to TRUE: `if percent_difference >= 5%: rotation_expected = TRUE` -- Else, set rotation_expected to FALSE: `else: rotation_expected = FALSE` +- Check if the % difference is 5% or more, if it is then set rotation_expected_b boolean to TRUE: `if percent_difference >= 5%: rotation_expected_b = TRUE` +- Else, set rotation_expected_b to FALSE: `else: rotation_expected_b = FALSE` -- Set counter variable to 0 (counts the number of output instances): `counter =0` +- Set no_of_output_instance variable to 0 (counts the number of output instances): `no_of_output_instance =0` Determine which RMDs have been created/provided - Create variable for list of RMDs: `rmds = RulesetProjectDescription.ruleset_model_descriptions` - Check for user RMD: `has_user = any[rmd.type == USER for rmd in rmds]` @@ -57,19 +57,19 @@ Determine which RMDs have been created/provided - Check for baseline 90 degree RMD: `has_baseline_90 = any[rmd.type == BASELINE_90 for rmd in rmds]` - Check for baseline 180 degree RMD: `has_baseline_180 = any[rmd.type == BASELINE_180 for rmd in rmds]` - Check for baseline 270 degree RMD: `has_baseline_270 = any[rmd.type == BASELINE_270 for rmd in rmds]` -- Get the number of ruleset_model_descriptions: `number_of_rmds = len(rmds)` +- Get the number of ruleset_model_descriptions: `no_of_rmds = len(rmds)` -- Check that there is an output_instance associated with each RMD and add to the counter variable for each one `For rmd in rmds:` +- Check that there is an output_instance associated with each RMD and add to the no_of_output_instance variable for each one `For rmd in rmds:` - Check for proposed output: `if rmd.type == PROPOSED and rmd.output.Output2019ASHRAE901.output_instance != Null: has_proposed_output = TRUE` - Check for baseline 0 degree output: `if rmd.type == BASELINE_0 and rmd.output.Output2019ASHRAE901.output_instance != Null: has_baseline_0_output = TRUE` - Check for baseline 90 degree output: `if rmd.type == BASELINE_90 and rmd.output.Output2019ASHRAE901.output_instance != Null: has_baseline_90_output = TRUE` - Check for baseline 180 degree output: `if rmd.type == BASELINE_180 and rmd.output.Output2019ASHRAE901.output_instance != Null: has_baseline_180_output = TRUE` - Check for baseline 270 degree output: `if rmd.type == BASELINE_270 and rmd.output.Output2019ASHRAE901.output_instance != Null: has_baseline_270_output = TRUE` - - Check if there is an output_instance associated with the RMD: `if rmd.output.Output2019ASHRAE901.output_instance != Null: counter += 1 ` + - Check if there is an output_instance associated with the RMD: `if rmd.output.Output2019ASHRAE901.output_instance != Null: no_of_output_instance += 1 ` - **Rule Assertion:** -- Case 1: If the fenestration area differs by 5% or more by orientation and there are 6 RMIs (for user, proposed, baseline at 0 degrees, baseline at 90 degrees, baseline at 180 degrees, and baseline at 270 degrees) and 5 output files (excludes an output for the user model) then Pass: `if rotation_expected == TRUE and has_user == TRUE and has_proposed == TRUE and has_baseline_0 == TRUE and and has_baseline_90 == TRUE and and has_baseline_180 == TRUE and and has_baseline_270 == TRUE and has_proposed_output == TRUE and has_baseline_0_output == TRUE and and has_baseline_90_output == TRUE and and has_baseline_180_output == TRUE and and has_baseline_270_output == TRUE and number_of_rmds == 6 and counter == 5: outcome = "PASS" ` -- Case 2: Else if rotation is not expected then pass as long as they have the minimally required RMDs and outputs: `if rotation_expected == FALSE and has_user == TRUE and has_proposed == TRUE and has_baseline_0 == TRUE and has_proposed_output == TRUE and has_baseline_0_output == TRUE: outcome = "PASS" ` +- Case 1: If the fenestration area differs by 5% or more by orientation and there are 6 RMIs (for user, proposed, baseline at 0 degrees, baseline at 90 degrees, baseline at 180 degrees, and baseline at 270 degrees) and 5 output files (excludes an output for the user model) then Pass: `if rotation_expected_b == TRUE and has_user == TRUE and has_proposed == TRUE and has_baseline_0 == TRUE and has_baseline_90 == TRUE and has_baseline_180 == TRUE and has_baseline_270 == TRUE and has_proposed_output == TRUE and has_baseline_0_output == TRUE and has_baseline_90_output == TRUE and has_baseline_180_output == TRUE and has_baseline_270_output == TRUE and no_of_rmds == 6 and no_of_output_instance == 5: outcome = "PASS" ` +- Case 2: Else if rotation is not expected then pass as long as they have the minimally required RMDs and outputs: `if rotation_expected_b == FALSE and has_user == TRUE and has_proposed == TRUE and has_baseline_0 == TRUE and has_proposed_output == TRUE and has_baseline_0_output == TRUE: outcome = "PASS" ` - Case 43: Else: `Else: outcome = "FAIL" and raise_message "Fail unless Table G3.1#5a exception #2 is applicable and it can be demonstrated that the building orientation is dictated by site considerations.` diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule1.py b/rct229/rulesets/ashrae9012019/section5/section5rule1.py index 215640ba81..91d801e0b3 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule1.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule1.py @@ -1,3 +1,4 @@ +from pydash import filter_ from rct229.rule_engine.rule_base import RuleDefinitionBase from rct229.rule_engine.rule_list_indexed_base import RuleDefinitionListIndexedBase from rct229.rule_engine.ruleset_model_factory import produce_ruleset_model_instance @@ -15,15 +16,13 @@ from rct229.rulesets.ashrae9012019.ruleset_functions.get_opaque_surface_type import ( get_opaque_surface_type, ) -from rct229.rulesets.ashrae9012019.ruleset_functions.get_surface_conditioning_category_dict import ( - SurfaceConditioningCategory as SCC, -) from rct229.schema.schema_enums import SchemaEnums -from rct229.utils.assertions import getattr_ +from rct229.utils.jsonpath_utils import find_all, find_one from rct229.utils.pint_utils import ZERO SUBSURFACE_CLASSIFICATION = SchemaEnums.schema_enums["SubsurfaceClassificationOptions"] RULESET_MODEL = SchemaEnums.schema_enums["RulesetModelOptions2019ASHRAE901"] +ACCEPTABLE_FEN_PERCENTAGE_DIFFERENCE = 0.05 class Section5Rule1(RuleDefinitionListIndexedBase): @@ -32,90 +31,225 @@ class Section5Rule1(RuleDefinitionListIndexedBase): def __init__(self): super(Section5Rule1, self).__init__( rmrs_used=produce_ruleset_model_instance( - USER=True, BASELINE_0=True, PROPOSED=True + USER=True, + BASELINE_0=True, + PROPOSED=True, ), - each_rule=Section5Rule1.SurfaceRule(), + each_rule=Section5Rule1.RMDRule(), index_rmr=BASELINE_0, id="5-1", description="There are four baseline rotations (i.e., four baseline models differing in azimuth by 90 degrees and four sets of baseline model results) if vertical fenestration area per each orientation differ by more than 5%.", ruleset_section_title="Envelope", standard_section="Table G3.1#5a baseline column", is_primary_rule=True, - # rmr_context="ruleset_model_descriptions[0]", + list_path="ruleset_model_descriptions[0]", ) - # class RMDRule(RuleDefinitionListIndexedBase): - # def __init__(self): - # super(Section5Rule1.RMDRule, self).__init__( - # rmrs_used=produce_ruleset_model_instance( - # USER=True, BASELINE_0=True, PROPOSED=True - # ), - # each_rule=Section5Rule1.RMDRule.SurfaceRule(), - # index_rmr=BASELINE_0, - # list_path="$.buildings[*].building_segments[*].zones[*].surfaces[*]", - # ) - - def create_data(self, context, data): - RMD_u = context.USER - RMD_b0 = context.BASELINE_0 - RMD_b90 = context.BASELINE_90 - RMD_b180 = context.BASELINE_180 - RMD_b270 = context.BASELINE_270 - RMD_p = context.PROPOSED - - return - - class SurfaceRule(RuleDefinitionListIndexedBase): + class RMDRule(RuleDefinitionListIndexedBase): def __init__(self): - super(Section5Rule1.SurfaceRule, self).__init__( + super(Section5Rule1.RMDRule, self).__init__( rmrs_used=produce_ruleset_model_instance( - USER=False, BASELINE_0=True, PROPOSED=False + USER=True, BASELINE_0=True, PROPOSED=True ), - each_rule=Section5Rule1.SurfaceRule.SubsurfaceRule(), + each_rule=Section5Rule1.RMDRule.BuildingRule(), index_rmr=BASELINE_0, - list_path="$.subsurfaces[*]", + list_path="$.buildings[*]", ) def create_data(self, context, data): - surface_b = context.BASELINE_0 + rmd_b0 = context.BASELINE_0 + rmd_b90 = context.BASELINE_90 + rmd_b180 = context.BASELINE_180 + rmd_b270 = context.BASELINE_270 + rmd_p = context.PROPOSED + rmd_u = context.USER + + baseline_rmd_list = [rmd_b0, rmd_b90, rmd_b180, rmd_b270] + rmd_list = [rmd_b0, rmd_b90, rmd_b180, rmd_b270, rmd_p, rmd_u] + + # count the rmds that aren't None + no_of_rmds = len( + filter_( + rmd_list, + lambda rmd: rmd is not None, + ) + ) - azimuth_fen_area_dict_b = {} - if get_opaque_surface_type(surface_b) == OST.ABOVE_GRADE_WALL: - surface_azimuth_b = getattr_(surface_b, "surfaces", "azimuth") + # filter out baseline rmds that have the type key + baseline_list = [ + rmd_b for rmd_b in baseline_rmd_list if find_one("$.type", rmd_b, False) + ] - if surface_azimuth_b not in azimuth_fen_area_dict_b: - azimuth_fen_area_dict_b[surface_azimuth_b] = 0 + no_of_output_instance = len(baseline_list) - return {"azimuth_fen_area_dict_b": azimuth_fen_area_dict_b} + return { + "baseline_list": baseline_list, + "no_of_rmds": no_of_rmds, + "no_of_output_instance": no_of_output_instance, + } - class SubsurfaceRule(RuleDefinitionBase): + class BuildingRule(RuleDefinitionBase): def __init__(self): - super( - Section5Rule1.SurfaceRule.SubsurfaceRule, - self, - ).__init__( + super(Section5Rule1.RMDRule.BuildingRule, self).__init__( rmrs_used=produce_ruleset_model_instance( - USER=False, BASELINE_0=True, PROPOSED=False + USER=True, BASELINE_0=True, PROPOSED=True ), + required_fields={ + "$.building_segments[*].zones[*].surfaces[*]": ["azimuth"], + }, + 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.", ) def get_calc_vals(self, context, data=None): - subsurface_b = context.BASELINE_0 + building_b0 = context.BASELINE_0 + building_b90 = context.BASELINE_90 + building_b180 = context.BASELINE_180 + building_b270 = context.BASELINE_270 + building_p = context.PROPOSED + building_u = context.USER + + baseline_list = data["baseline_list"] + no_of_rmds = data["no_of_rmds"] + no_of_output_instance = data["no_of_output_instance"] + + has_proposed = building_p.get("type") == RULESET_MODEL.PROPOSED + has_user = building_u.get("type") == RULESET_MODEL.USER + has_baseline_0 = RULESET_MODEL.BASELINE_0 in baseline_list + has_baseline_90 = RULESET_MODEL.BASELINE_90 in baseline_list + has_baseline_180 = RULESET_MODEL.BASELINE_180 in baseline_list + has_baseline_270 = RULESET_MODEL.BASELINE_270 in baseline_list + + has_proposed_output = has_proposed and find_one( + "$.output.output_instance", building_p, False + ) + has_user_output = has_user and find_one( + "$.output.output_instance", building_u, False + ) + has_basseline_0_output = has_baseline_0 and find_one( + "$.output.output_instance", building_b0, False + ) + has_basseline_90_output = has_baseline_90 and find_one( + "$.output.output_instance", building_b90, False + ) + has_basseline_180_output = has_baseline_180 and find_one( + "$.output.output_instance", building_b180, False + ) + has_basseline_270_output = has_baseline_270 and find_one( + "$.output.output_instance", building_b270, False + ) + # define a function to find the azimuth's corresponding key + find_key_for_azi = lambda azi: next( + ( + key + for key, range_ in azimuth_fen_area_dict_b.items() + if int(key.split("-")[0]) <= azi <= int(key.split("-")[1]) + ), + None, + ) + + azimuth_fen_area_dict_b = { + f"{azi}-{azi+3}": ZERO.AREA for azi in range(3, 360, 3) + } total_surface_fenestration_area_b = ZERO.AREA - glazed_area_b = subsurface_b.get("glazed_area", ZERO.AREA) - opaque_area_b = subsurface_b.get("opaque_area", ZERO.AREA) - if subsurface_b["classification"] == SUBSURFACE_CLASSIFICATION.DOOR: - if glazed_area_b > opaque_area_b: - total_surface_fenestration_area_b += ( - glazed_area_b + opaque_area_b - ) - else: - total_surface_fenestration_area_b += ( - glazed_area_b + opaque_area_b - ) + for surface_b in find_all( + "$.building_segments[*].zones[*].surfaces[*]", building_b0 + ): + if get_opaque_surface_type(surface_b) == OST.ABOVE_GRADE_WALL: + surface_azimuth_b = surface_b["azimuth"] + + if surface_azimuth_b not in azimuth_fen_area_dict_b: + azimuth_fen_area_dict_b[surface_azimuth_b] = ZERO.AREA + + for subsurface_b in find_all("$.subsurfaces[*]", surface_b): + glazed_area_b = subsurface_b.get("glazed_area", ZERO.AREA) + opaque_area_b = subsurface_b.get("opaque_area", ZERO.AREA) + + if ( + subsurface_b["classification"] + == SUBSURFACE_CLASSIFICATION.DOOR + ): + if glazed_area_b > opaque_area_b: + total_surface_fenestration_area_b += ( + glazed_area_b + opaque_area_b + ) + else: + total_surface_fenestration_area_b += ( + glazed_area_b + opaque_area_b + ) + azimuth_fen_area_dict_b[ + find_key_for_azi(surface_azimuth_b) + ] += total_surface_fenestration_area_b + + max_fen_area_b = max( + azimuth_fen_area_dict_b, key=azimuth_fen_area_dict_b.get + ) + min_fen_area_b = min( + azimuth_fen_area_dict_b, key=azimuth_fen_area_dict_b.get + ) + + percent_difference = max( + abs(max_fen_area_b - min_fen_area_b) / max_fen_area_b, + abs(min_fen_area_b - max_fen_area_b) / min_fen_area_b, + ) + + rotation_expected_b = ( + percent_difference >= ACCEPTABLE_FEN_PERCENTAGE_DIFFERENCE + ) - return + return { + "no_of_rmds": no_of_rmds, + "no_of_output_instance": no_of_output_instance, + "rotation_expected_b": rotation_expected_b, + "has_proposed": has_proposed, + "has_user": has_user, + "has_baseline_0": has_baseline_0, + "has_baseline_90": has_baseline_90, + "has_baseline_180": has_baseline_180, + "has_proposed_output": has_proposed_output, + "has_user_output": has_user_output, + "has_basseline_0_output": has_basseline_0_output, + "has_basseline_90_output": has_basseline_90_output, + "has_basseline_180_output": has_basseline_180_output, + "has_basseline_270_output": has_basseline_270_output, + } def rule_check(self, context, calc_vals=None, data=None): - return + rotation_expected_b = calc_vals["rotation_expected_b"] + + no_of_rmds = calc_vals["no_of_rmds"] + no_of_output_instance = calc_vals["no_of_output_instance"] + + has_proposed = calc_vals["has_proposed"] + has_user = calc_vals["has_user"] + has_baseline_0 = calc_vals["has_baseline_0"] + has_baseline_90 = calc_vals["has_baseline_90"] + has_baseline_180 = calc_vals["has_baseline_180"] + has_baseline_270 = calc_vals["has_baseline_270"] + + has_proposed_output = calc_vals["has_proposed_output"] + has_basseline_0_output = calc_vals["has_basseline_0_output"] + has_basseline_90_output = calc_vals["has_basseline_90_output"] + has_basseline_180_output = calc_vals["has_basseline_180_output"] + has_basseline_270_output = calc_vals["has_basseline_270_output"] + + return ( + has_user + and has_proposed + and has_baseline_0 + and has_proposed_output + and has_basseline_0_output + ) and ( + ( + rotation_expected_b + and has_baseline_90 + and has_baseline_180 + and has_baseline_270 + and has_basseline_90_output + and has_basseline_180_output + and has_basseline_270_output + and no_of_rmds == 5 + and no_of_output_instance == 6 + ) + or (not rotation_expected_b) + ) From 3d1bfe195f7b4c95ba08a5ce385ddae56496f01a Mon Sep 17 00:00:00 2001 From: yunjoonjung Date: Sun, 7 Jan 2024 17:37:18 -0800 Subject: [PATCH 03/18] Fixed type key checking --- rct229/rulesets/ashrae9012019/section5/section5rule1.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule1.py b/rct229/rulesets/ashrae9012019/section5/section5rule1.py index 91d801e0b3..5b71efe767 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule1.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule1.py @@ -112,8 +112,8 @@ def get_calc_vals(self, context, data=None): no_of_rmds = data["no_of_rmds"] no_of_output_instance = data["no_of_output_instance"] - has_proposed = building_p.get("type") == RULESET_MODEL.PROPOSED - has_user = building_u.get("type") == RULESET_MODEL.USER + has_proposed = find_one("$.type", building_p, False) + has_user = find_one("$.type", building_u, False) has_baseline_0 = RULESET_MODEL.BASELINE_0 in baseline_list has_baseline_90 = RULESET_MODEL.BASELINE_90 in baseline_list has_baseline_180 = RULESET_MODEL.BASELINE_180 in baseline_list From e4fb89e899194a832c290e01742cddae3d27f88a Mon Sep 17 00:00:00 2001 From: yunjoonjung Date: Wed, 10 Jan 2024 09:42:32 -0800 Subject: [PATCH 04/18] Addressed PR comments and Fixed rultest_engine --- rct229/rule_engine/ruleset_model_factory.py | 4 +- .../ashrae9012019/section5/section5rule1.py | 63 +++++++++++++------ .../ruletest_engine/ruletest_rmd_factory.py | 52 ++++++++++++++- 3 files changed, 95 insertions(+), 24 deletions(-) diff --git a/rct229/rule_engine/ruleset_model_factory.py b/rct229/rule_engine/ruleset_model_factory.py index 2cd61eaa0f..56cc699673 100644 --- a/rct229/rule_engine/ruleset_model_factory.py +++ b/rct229/rule_engine/ruleset_model_factory.py @@ -1,6 +1,6 @@ from rct229.rule_engine.rulesets import RuleSet -from rct229.schema.schema_store import SchemaStore from rct229.schema.schema_enums import SchemaEnums +from rct229.schema.schema_store import SchemaStore from rct229.utils.assertions import assert_ ruleset_model_dict = {RuleSet.ASHRAE9012019_RULESET: "RulesetModelOptions2019ASHRAE901"} @@ -66,7 +66,7 @@ def produce_ruleset_model_instance(**kwargs): Parameters ---------- - kwargs: provide key-value inputs - for example USER=Fasle, BASELINE_0=TRUE + kwargs: provide key-value inputs - for example USER=False, BASELINE_0=TRUE Returns ------- diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule1.py b/rct229/rulesets/ashrae9012019/section5/section5rule1.py index 5b71efe767..5485c289f3 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule1.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule1.py @@ -16,6 +16,7 @@ from rct229.rulesets.ashrae9012019.ruleset_functions.get_opaque_surface_type import ( get_opaque_surface_type, ) +from rct229.schema.config import ureg from rct229.schema.schema_enums import SchemaEnums from rct229.utils.jsonpath_utils import find_all, find_one from rct229.utils.pint_utils import ZERO @@ -33,6 +34,9 @@ def __init__(self): rmrs_used=produce_ruleset_model_instance( USER=True, BASELINE_0=True, + BASELINE_90=True, + BASELINE_180=True, + BASELINE_270=True, PROPOSED=True, ), each_rule=Section5Rule1.RMDRule(), @@ -49,7 +53,12 @@ class RMDRule(RuleDefinitionListIndexedBase): def __init__(self): super(Section5Rule1.RMDRule, self).__init__( rmrs_used=produce_ruleset_model_instance( - USER=True, BASELINE_0=True, PROPOSED=True + USER=True, + BASELINE_0=True, + BASELINE_90=True, + BASELINE_180=True, + BASELINE_270=True, + PROPOSED=True, ), each_rule=Section5Rule1.RMDRule.BuildingRule(), index_rmr=BASELINE_0, @@ -64,6 +73,9 @@ def create_data(self, context, data): rmd_p = context.PROPOSED rmd_u = context.USER + has_proposed = True if find_one("$.type", rmd_p) else False + has_user = True if find_one("$.type", rmd_u, False) else False + baseline_rmd_list = [rmd_b0, rmd_b90, rmd_b180, rmd_b270] rmd_list = [rmd_b0, rmd_b90, rmd_b180, rmd_b270, rmd_p, rmd_u] @@ -77,12 +89,16 @@ def create_data(self, context, data): # filter out baseline rmds that have the type key baseline_list = [ - rmd_b for rmd_b in baseline_rmd_list if find_one("$.type", rmd_b, False) + find_one("$.type", rmd_b) + for rmd_b in baseline_rmd_list + if find_one("$.type", rmd_b, False) ] no_of_output_instance = len(baseline_list) return { + "has_proposed": has_proposed, + "has_user": has_user, "baseline_list": baseline_list, "no_of_rmds": no_of_rmds, "no_of_output_instance": no_of_output_instance, @@ -92,7 +108,12 @@ class BuildingRule(RuleDefinitionBase): def __init__(self): super(Section5Rule1.RMDRule.BuildingRule, self).__init__( rmrs_used=produce_ruleset_model_instance( - USER=True, BASELINE_0=True, PROPOSED=True + USER=True, + BASELINE_0=True, + BASELINE_90=True, + BASELINE_180=True, + BASELINE_270=True, + PROPOSED=True, ), required_fields={ "$.building_segments[*].zones[*].surfaces[*]": ["azimuth"], @@ -108,12 +129,12 @@ def get_calc_vals(self, context, data=None): building_p = context.PROPOSED building_u = context.USER + has_proposed = data["has_proposed"] + has_user = data["has_user"] baseline_list = data["baseline_list"] no_of_rmds = data["no_of_rmds"] no_of_output_instance = data["no_of_output_instance"] - has_proposed = find_one("$.type", building_p, False) - has_user = find_one("$.type", building_u, False) has_baseline_0 = RULESET_MODEL.BASELINE_0 in baseline_list has_baseline_90 = RULESET_MODEL.BASELINE_90 in baseline_list has_baseline_180 = RULESET_MODEL.BASELINE_180 in baseline_list @@ -142,14 +163,16 @@ def get_calc_vals(self, context, data=None): find_key_for_azi = lambda azi: next( ( key - for key, range_ in azimuth_fen_area_dict_b.items() - if int(key.split("-")[0]) <= azi <= int(key.split("-")[1]) + for key, _ in azimuth_fen_area_dict_b.items() + if int(key.split("-")[0]) + <= azi.to(ureg("degree")).m + <= int(key.split("-")[1]) ), None, ) azimuth_fen_area_dict_b = { - f"{azi}-{azi+3}": ZERO.AREA for azi in range(3, 360, 3) + f"{azi}-{azi+3}": ZERO.AREA for azi in range(0, 360, 3) } total_surface_fenestration_area_b = ZERO.AREA for surface_b in find_all( @@ -158,9 +181,6 @@ def get_calc_vals(self, context, data=None): if get_opaque_surface_type(surface_b) == OST.ABOVE_GRADE_WALL: surface_azimuth_b = surface_b["azimuth"] - if surface_azimuth_b not in azimuth_fen_area_dict_b: - azimuth_fen_area_dict_b[surface_azimuth_b] = ZERO.AREA - for subsurface_b in find_all("$.subsurfaces[*]", surface_b): glazed_area_b = subsurface_b.get("glazed_area", ZERO.AREA) opaque_area_b = subsurface_b.get("opaque_area", ZERO.AREA) @@ -181,16 +201,20 @@ def get_calc_vals(self, context, data=None): find_key_for_azi(surface_azimuth_b) ] += total_surface_fenestration_area_b - max_fen_area_b = max( - azimuth_fen_area_dict_b, key=azimuth_fen_area_dict_b.get - ) - min_fen_area_b = min( - azimuth_fen_area_dict_b, key=azimuth_fen_area_dict_b.get - ) + max_fen_area_b = azimuth_fen_area_dict_b[ + max(azimuth_fen_area_dict_b, key=azimuth_fen_area_dict_b.get) + ] + min_fen_area_b = azimuth_fen_area_dict_b[ + min(azimuth_fen_area_dict_b, key=azimuth_fen_area_dict_b.get) + ] percent_difference = max( - abs(max_fen_area_b - min_fen_area_b) / max_fen_area_b, - abs(min_fen_area_b - max_fen_area_b) / min_fen_area_b, + abs(max_fen_area_b - min_fen_area_b) / max_fen_area_b + if max_fen_area_b != ZERO.AREA + else 0.0, + abs(min_fen_area_b - max_fen_area_b) / min_fen_area_b + if min_fen_area_b != ZERO.AREA + else 0.0, ) rotation_expected_b = ( @@ -206,6 +230,7 @@ def get_calc_vals(self, context, data=None): "has_baseline_0": has_baseline_0, "has_baseline_90": has_baseline_90, "has_baseline_180": has_baseline_180, + "has_baseline_270": has_baseline_270, "has_proposed_output": has_proposed_output, "has_user_output": has_user_output, "has_basseline_0_output": has_basseline_0_output, diff --git a/rct229/ruletest_engine/ruletest_rmd_factory.py b/rct229/ruletest_engine/ruletest_rmd_factory.py index 0240a0dc78..ca381c4446 100644 --- a/rct229/ruletest_engine/ruletest_rmd_factory.py +++ b/rct229/ruletest_engine/ruletest_rmd_factory.py @@ -1,6 +1,15 @@ +import copy + from rct229.rule_engine.ruleset_model_factory import produce_ruleset_model_instance from rct229.rule_engine.rulesets import RuleSet +from rct229.rulesets.ashrae9012019 import ( + BASELINE_0, + BASELINE_90, + BASELINE_180, + BASELINE_270, +) from rct229.schema.schema_store import SchemaStore +from rct229.utils.assertions import getattr_ def get_ruletest_rmd_models(test_dict: dict): @@ -12,11 +21,13 @@ def get_ruletest_rmd_models(test_dict: dict): def get_9012019_rmd_models(test_dict: dict): - # Each of these will remain None unless it is specified in # rmr_transformations. user_rmr = None - baseline_rmr = None + baseline_0_rmr = None + baseline_90_rmr = None + baseline_180_rmr = None + baseline_270_rmr = None proposed_rmr = None # Read in transformations dictionary. This will perturb a template or fully define an RMR (if no template defined) @@ -30,9 +41,44 @@ def get_9012019_rmd_models(test_dict: dict): if "baseline" in rmr_transformations_dict: baseline_rmr = rmr_transformations_dict["baseline"] + for rmd_b in baseline_rmr["ruleset_model_descriptions"]: + type_b = getattr_(rmd_b, "RMD", "type") + if type_b == BASELINE_0: + baseline_0_rmr = copy.deepcopy(baseline_rmr) + baseline_0_rmr["ruleset_model_descriptions"] = [] + baseline_0_rmr["ruleset_model_descriptions"].append( + copy.deepcopy(rmd_b) + ) + + elif type_b == BASELINE_90: + baseline_90_rmr = copy.deepcopy(baseline_rmr) + baseline_90_rmr["ruleset_model_descriptions"] = [] + baseline_90_rmr["ruleset_model_descriptions"].append( + copy.deepcopy(rmd_b) + ) + + elif type_b == BASELINE_180: + baseline_180_rmr = copy.deepcopy(baseline_rmr) + baseline_180_rmr["ruleset_model_descriptions"] = [] + baseline_180_rmr["ruleset_model_descriptions"].append( + copy.deepcopy(rmd_b) + ) + + elif type_b == BASELINE_270: + baseline_270_rmr = copy.deepcopy(baseline_rmr) + baseline_270_rmr["ruleset_model_descriptions"] = [] + baseline_270_rmr["ruleset_model_descriptions"].append( + copy.deepcopy(rmd_b) + ) + if "proposed" in rmr_transformations_dict: proposed_rmr = rmr_transformations_dict["proposed"] return produce_ruleset_model_instance( - USER=user_rmr, BASELINE_0=baseline_rmr, PROPOSED=proposed_rmr + USER=user_rmr, + BASELINE_0=baseline_0_rmr, + BASELINE_90=baseline_90_rmr, + BASELINE_180=baseline_180_rmr, + BASELINE_270=baseline_270_rmr, + PROPOSED=proposed_rmr, ) From b1621ee4399cf86ec5a7e5eebcdf2a544796b09a Mon Sep 17 00:00:00 2001 From: yunjoonjung Date: Fri, 12 Jan 2024 12:22:21 -0800 Subject: [PATCH 05/18] Addressed PR comments --- .../ashrae9012019/section5/section5rule1.py | 139 +++++++++--------- 1 file changed, 72 insertions(+), 67 deletions(-) diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule1.py b/rct229/rulesets/ashrae9012019/section5/section5rule1.py index 5485c289f3..37aad219cc 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule1.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule1.py @@ -73,8 +73,31 @@ def create_data(self, context, data): rmd_p = context.PROPOSED rmd_u = context.USER - has_proposed = True if find_one("$.type", rmd_p) else False - has_user = True if find_one("$.type", rmd_u, False) else False + has_baseline_0 = rmd_b0 is not None + has_baseline_90 = rmd_b90 is not None + has_baseline_180 = rmd_b180 is not None + has_baseline_270 = rmd_b270 is not None + has_proposed = rmd_p is not None + has_user = rmd_u is not None + + has_basseline_0_output = has_baseline_0 and find_one( + "$.output.output_instance", rmd_b0, False + ) + has_basseline_90_output = has_baseline_90 and find_one( + "$.output.output_instance", rmd_b90, False + ) + has_basseline_180_output = has_baseline_180 and find_one( + "$.output.output_instance", rmd_b180, False + ) + has_basseline_270_output = has_baseline_270 and find_one( + "$.output.output_instance", rmd_b270, False + ) + has_proposed_output = has_proposed and find_one( + "$.output.output_instance", rmd_p, False + ) + has_user_output = has_user and find_one( + "$.output.output_instance", rmd_u, False + ) baseline_rmd_list = [rmd_b0, rmd_b90, rmd_b180, rmd_b270] rmd_list = [rmd_b0, rmd_b90, rmd_b180, rmd_b270, rmd_p, rmd_u] @@ -87,19 +110,24 @@ def create_data(self, context, data): ) ) - # filter out baseline rmds that have the type key - baseline_list = [ - find_one("$.type", rmd_b) - for rmd_b in baseline_rmd_list - if find_one("$.type", rmd_b, False) - ] - - no_of_output_instance = len(baseline_list) + # filter out baseline rmds that aren't None then count the length + no_of_output_instance = len( + [rmd_b["type"] for rmd_b in baseline_rmd_list if rmd_b is not None] + ) return { + "has_baseline_0": has_baseline_0, + "has_baseline_90": has_baseline_90, + "has_baseline_180": has_baseline_180, + "has_baseline_270": has_baseline_270, "has_proposed": has_proposed, "has_user": has_user, - "baseline_list": baseline_list, + "has_basseline_0_output": has_basseline_0_output, + "has_basseline_90_output": has_basseline_90_output, + "has_basseline_180_output": has_basseline_180_output, + "has_basseline_270_output": has_basseline_270_output, + "has_proposed_output": has_proposed_output, + "has_user_output": has_user_output, "no_of_rmds": no_of_rmds, "no_of_output_instance": no_of_output_instance, } @@ -108,12 +136,12 @@ class BuildingRule(RuleDefinitionBase): def __init__(self): super(Section5Rule1.RMDRule.BuildingRule, self).__init__( rmrs_used=produce_ruleset_model_instance( - USER=True, + USER=False, BASELINE_0=True, - BASELINE_90=True, - BASELINE_180=True, - BASELINE_270=True, - PROPOSED=True, + BASELINE_90=False, + BASELINE_180=False, + BASELINE_270=False, + PROPOSED=False, ), required_fields={ "$.building_segments[*].zones[*].surfaces[*]": ["azimuth"], @@ -123,63 +151,40 @@ def __init__(self): def get_calc_vals(self, context, data=None): building_b0 = context.BASELINE_0 - building_b90 = context.BASELINE_90 - building_b180 = context.BASELINE_180 - building_b270 = context.BASELINE_270 - building_p = context.PROPOSED - building_u = context.USER + has_baseline_0 = data["has_baseline_0"] + has_baseline_90 = data["has_baseline_90"] + has_baseline_180 = data["has_baseline_180"] + has_baseline_270 = data["has_baseline_270"] has_proposed = data["has_proposed"] has_user = data["has_user"] - baseline_list = data["baseline_list"] + has_basseline_0_output = data["has_basseline_0_output"] + has_basseline_90_output = data["has_basseline_90_output"] + has_basseline_180_output = data["has_basseline_180_output"] + has_basseline_270_output = data["has_basseline_270_output"] + has_proposed_output = data["has_proposed_output"] + has_user_output = data["has_user_output"] no_of_rmds = data["no_of_rmds"] no_of_output_instance = data["no_of_output_instance"] - has_baseline_0 = RULESET_MODEL.BASELINE_0 in baseline_list - has_baseline_90 = RULESET_MODEL.BASELINE_90 in baseline_list - has_baseline_180 = RULESET_MODEL.BASELINE_180 in baseline_list - has_baseline_270 = RULESET_MODEL.BASELINE_270 in baseline_list - - has_proposed_output = has_proposed and find_one( - "$.output.output_instance", building_p, False - ) - has_user_output = has_user and find_one( - "$.output.output_instance", building_u, False - ) - has_basseline_0_output = has_baseline_0 and find_one( - "$.output.output_instance", building_b0, False - ) - has_basseline_90_output = has_baseline_90 and find_one( - "$.output.output_instance", building_b90, False - ) - has_basseline_180_output = has_baseline_180 and find_one( - "$.output.output_instance", building_b180, False - ) - has_basseline_270_output = has_baseline_270 and find_one( - "$.output.output_instance", building_b270, False - ) - - # define a function to find the azimuth's corresponding key - find_key_for_azi = lambda azi: next( - ( - key - for key, _ in azimuth_fen_area_dict_b.items() - if int(key.split("-")[0]) - <= azi.to(ureg("degree")).m - <= int(key.split("-")[1]) - ), - None, - ) + # define a function to get the azimuth's corresponding key + def get_key_for_azi(azi): + azi_value = azi.to("degrees").m + low_bound = azi_value - (azi_value % 3) + high_bound = low_bound + 3 + return f"{low_bound}-{high_bound}" - azimuth_fen_area_dict_b = { - f"{azi}-{azi+3}": ZERO.AREA for azi in range(0, 360, 3) - } + azimuth_fen_area_dict_b = {} total_surface_fenestration_area_b = ZERO.AREA for surface_b in find_all( "$.building_segments[*].zones[*].surfaces[*]", building_b0 ): if get_opaque_surface_type(surface_b) == OST.ABOVE_GRADE_WALL: surface_azimuth_b = surface_b["azimuth"] + surface_azimuth_bin = get_key_for_azi(surface_azimuth_b) + + if surface_azimuth_bin not in azimuth_fen_area_dict_b: + azimuth_fen_area_dict_b[surface_azimuth_bin] = ZERO.AREA for subsurface_b in find_all("$.subsurfaces[*]", surface_b): glazed_area_b = subsurface_b.get("glazed_area", ZERO.AREA) @@ -198,7 +203,7 @@ def get_calc_vals(self, context, data=None): glazed_area_b + opaque_area_b ) azimuth_fen_area_dict_b[ - find_key_for_azi(surface_azimuth_b) + surface_azimuth_bin ] += total_surface_fenestration_area_b max_fen_area_b = azimuth_fen_area_dict_b[ @@ -225,18 +230,18 @@ def get_calc_vals(self, context, data=None): "no_of_rmds": no_of_rmds, "no_of_output_instance": no_of_output_instance, "rotation_expected_b": rotation_expected_b, - "has_proposed": has_proposed, - "has_user": has_user, "has_baseline_0": has_baseline_0, "has_baseline_90": has_baseline_90, "has_baseline_180": has_baseline_180, "has_baseline_270": has_baseline_270, - "has_proposed_output": has_proposed_output, - "has_user_output": has_user_output, + "has_proposed": has_proposed, + "has_user": has_user, "has_basseline_0_output": has_basseline_0_output, "has_basseline_90_output": has_basseline_90_output, "has_basseline_180_output": has_basseline_180_output, "has_basseline_270_output": has_basseline_270_output, + "has_proposed_output": has_proposed_output, + "has_user_output": has_user_output, } def rule_check(self, context, calc_vals=None, data=None): @@ -273,8 +278,8 @@ def rule_check(self, context, calc_vals=None, data=None): and has_basseline_90_output and has_basseline_180_output and has_basseline_270_output - and no_of_rmds == 5 - and no_of_output_instance == 6 + and no_of_rmds == 6 + and no_of_output_instance == 5 ) or (not rotation_expected_b) ) From 7f34b4c70d6dca27a1a4f00d0d2033d0108178b4 Mon Sep 17 00:00:00 2001 From: yunjoonjung Date: Fri, 12 Jan 2024 12:39:19 -0800 Subject: [PATCH 06/18] Added missing bool() --- .../ashrae9012019/section5/section5rule1.py | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule1.py b/rct229/rulesets/ashrae9012019/section5/section5rule1.py index 37aad219cc..ea76b1e241 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule1.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule1.py @@ -80,27 +80,27 @@ def create_data(self, context, data): has_proposed = rmd_p is not None has_user = rmd_u is not None - has_basseline_0_output = has_baseline_0 and find_one( - "$.output.output_instance", rmd_b0, False + has_basseline_0_output = has_baseline_0 and bool( + find_one("$.output.output_instance", rmd_b0) ) - has_basseline_90_output = has_baseline_90 and find_one( - "$.output.output_instance", rmd_b90, False + has_basseline_90_output = has_baseline_90 and bool( + find_one("$.output.output_instance", rmd_b90) ) - has_basseline_180_output = has_baseline_180 and find_one( - "$.output.output_instance", rmd_b180, False + has_basseline_180_output = has_baseline_180 and bool( + find_one("$.output.output_instance", rmd_b180) ) - has_basseline_270_output = has_baseline_270 and find_one( - "$.output.output_instance", rmd_b270, False + has_basseline_270_output = has_baseline_270 and bool( + find_one("$.output.output_instance", rmd_b270) ) - has_proposed_output = has_proposed and find_one( - "$.output.output_instance", rmd_p, False + has_proposed_output = has_proposed and bool( + find_one("$.output.output_instance", rmd_p) ) - has_user_output = has_user and find_one( - "$.output.output_instance", rmd_u, False + has_user_output = has_user and bool( + find_one("$.output.output_instance", rmd_u) ) - baseline_rmd_list = [rmd_b0, rmd_b90, rmd_b180, rmd_b270] rmd_list = [rmd_b0, rmd_b90, rmd_b180, rmd_b270, rmd_p, rmd_u] + rmd_list_no_user = [rmd_b0, rmd_b90, rmd_b180, rmd_b270, rmd_p] # count the rmds that aren't None no_of_rmds = len( @@ -112,7 +112,7 @@ def create_data(self, context, data): # filter out baseline rmds that aren't None then count the length no_of_output_instance = len( - [rmd_b["type"] for rmd_b in baseline_rmd_list if rmd_b is not None] + [rmd["type"] for rmd in rmd_list_no_user if rmd is not None] ) return { @@ -250,18 +250,18 @@ def rule_check(self, context, calc_vals=None, data=None): no_of_rmds = calc_vals["no_of_rmds"] no_of_output_instance = calc_vals["no_of_output_instance"] - has_proposed = calc_vals["has_proposed"] - has_user = calc_vals["has_user"] has_baseline_0 = calc_vals["has_baseline_0"] has_baseline_90 = calc_vals["has_baseline_90"] has_baseline_180 = calc_vals["has_baseline_180"] has_baseline_270 = calc_vals["has_baseline_270"] + has_proposed = calc_vals["has_proposed"] + has_user = calc_vals["has_user"] - has_proposed_output = calc_vals["has_proposed_output"] has_basseline_0_output = calc_vals["has_basseline_0_output"] has_basseline_90_output = calc_vals["has_basseline_90_output"] has_basseline_180_output = calc_vals["has_basseline_180_output"] has_basseline_270_output = calc_vals["has_basseline_270_output"] + has_proposed_output = calc_vals["has_proposed_output"] return ( has_user From 316558406912a17fe067aea1739a23bfa14bcd2a Mon Sep 17 00:00:00 2001 From: yunjoonjung Date: Fri, 12 Jan 2024 13:16:13 -0800 Subject: [PATCH 07/18] Updated the files to be able to deal with optional BASELINE 90, 180, 270 rmds --- rct229/rule_engine/engine.py | 2 +- rct229/rule_engine/rule_base.py | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/rct229/rule_engine/engine.py b/rct229/rule_engine/engine.py index 2562bea924..051c560c40 100644 --- a/rct229/rule_engine/engine.py +++ b/rct229/rule_engine/engine.py @@ -142,7 +142,7 @@ def evaluate_rules( rmds_used = get_rmd_instance() for rule in rules_list: for rule_model in rmds.get_ruleset_model_types(): - if rule.rmrs_used[rule_model]: + if rule.rmrs_used[rule_model] and rmds[rule_model] is not None: rmds_used[rule_model] = True for rule_model in rmds.get_ruleset_model_types(): diff --git a/rct229/rule_engine/rule_base.py b/rct229/rule_engine/rule_base.py index 5967a8b194..726a8e98e5 100644 --- a/rct229/rule_engine/rule_base.py +++ b/rct229/rule_engine/rule_base.py @@ -1,11 +1,11 @@ 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.rulesets.ashrae9012019 import BASELINE_90, BASELINE_180, BASELINE_270 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.rule_engine.ruleset_model_factory import RuleSetModels, get_rmd_instance class RuleDefinitionBase: @@ -286,7 +286,9 @@ def get_context(self, rmds, data=None): if self.rmrs_used[ruleset_model] and ( rmds[ruleset_model] is None or not rmds[ruleset_model] ): - missing_contexts.append(ruleset_model) + # BASELINE 90, 180, and 270 are optional + if ruleset_model not in [BASELINE_90, BASELINE_180, BASELINE_270]: + missing_contexts.append(ruleset_model) if len(missing_contexts) > 0: retval = "MISSING_" + "_".join(missing_contexts) From d626675e6fa0794d32569c319d20a8356cf2d3f7 Mon Sep 17 00:00:00 2001 From: yunjoonjung Date: Fri, 12 Jan 2024 14:59:35 -0800 Subject: [PATCH 08/18] Addressed Weili's comments --- .../ashrae9012019/section5/section5rule1.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule1.py b/rct229/rulesets/ashrae9012019/section5/section5rule1.py index ea76b1e241..3621b36063 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule1.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule1.py @@ -1,4 +1,4 @@ -from pydash import filter_ +from pydash import compact from rct229.rule_engine.rule_base import RuleDefinitionBase from rct229.rule_engine.rule_list_indexed_base import RuleDefinitionListIndexedBase from rct229.rule_engine.ruleset_model_factory import produce_ruleset_model_instance @@ -103,16 +103,16 @@ def create_data(self, context, data): rmd_list_no_user = [rmd_b0, rmd_b90, rmd_b180, rmd_b270, rmd_p] # count the rmds that aren't None - no_of_rmds = len( - filter_( - rmd_list, - lambda rmd: rmd is not None, - ) - ) + no_of_rmds = len(compact(rmd_list)) - # filter out baseline rmds that aren't None then count the length + # filter out rmds that aren't None then count the length no_of_output_instance = len( - [rmd["type"] for rmd in rmd_list_no_user if rmd is not None] + compact( + [ + find_one("$.output.output_instance", rmd) + for rmd in rmd_list_no_user + ] + ) ) return { From ff54428a0f5fc89fd621deb49adae629fb154b4f Mon Sep 17 00:00:00 2001 From: yunjoonjung Date: Mon, 15 Jan 2024 09:03:34 -0800 Subject: [PATCH 09/18] Addressed comments --- .../ashrae9012019/section5/section5rule1.py | 51 +++++++++---------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule1.py b/rct229/rulesets/ashrae9012019/section5/section5rule1.py index 3621b36063..59ad646de6 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule1.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule1.py @@ -16,7 +16,6 @@ from rct229.rulesets.ashrae9012019.ruleset_functions.get_opaque_surface_type import ( get_opaque_surface_type, ) -from rct229.schema.config import ureg from rct229.schema.schema_enums import SchemaEnums from rct229.utils.jsonpath_utils import find_all, find_one from rct229.utils.pint_utils import ZERO @@ -80,16 +79,16 @@ def create_data(self, context, data): has_proposed = rmd_p is not None has_user = rmd_u is not None - has_basseline_0_output = has_baseline_0 and bool( + has_baseline_0_output = has_baseline_0 and bool( find_one("$.output.output_instance", rmd_b0) ) - has_basseline_90_output = has_baseline_90 and bool( + has_baseline_90_output = has_baseline_90 and bool( find_one("$.output.output_instance", rmd_b90) ) - has_basseline_180_output = has_baseline_180 and bool( + has_baseline_180_output = has_baseline_180 and bool( find_one("$.output.output_instance", rmd_b180) ) - has_basseline_270_output = has_baseline_270 and bool( + has_baseline_270_output = has_baseline_270 and bool( find_one("$.output.output_instance", rmd_b270) ) has_proposed_output = has_proposed and bool( @@ -122,10 +121,10 @@ def create_data(self, context, data): "has_baseline_270": has_baseline_270, "has_proposed": has_proposed, "has_user": has_user, - "has_basseline_0_output": has_basseline_0_output, - "has_basseline_90_output": has_basseline_90_output, - "has_basseline_180_output": has_basseline_180_output, - "has_basseline_270_output": has_basseline_270_output, + "has_baseline_0_output": has_baseline_0_output, + "has_baseline_90_output": has_baseline_90_output, + "has_baseline_180_output": has_baseline_180_output, + "has_baseline_270_output": has_baseline_270_output, "has_proposed_output": has_proposed_output, "has_user_output": has_user_output, "no_of_rmds": no_of_rmds, @@ -158,10 +157,10 @@ def get_calc_vals(self, context, data=None): has_baseline_270 = data["has_baseline_270"] has_proposed = data["has_proposed"] has_user = data["has_user"] - has_basseline_0_output = data["has_basseline_0_output"] - has_basseline_90_output = data["has_basseline_90_output"] - has_basseline_180_output = data["has_basseline_180_output"] - has_basseline_270_output = data["has_basseline_270_output"] + has_baseline_0_output = data["has_baseline_0_output"] + has_baseline_90_output = data["has_baseline_90_output"] + has_baseline_180_output = data["has_baseline_180_output"] + has_baseline_270_output = data["has_baseline_270_output"] has_proposed_output = data["has_proposed_output"] has_user_output = data["has_user_output"] no_of_rmds = data["no_of_rmds"] @@ -175,12 +174,12 @@ def get_key_for_azi(azi): return f"{low_bound}-{high_bound}" azimuth_fen_area_dict_b = {} - total_surface_fenestration_area_b = ZERO.AREA for surface_b in find_all( "$.building_segments[*].zones[*].surfaces[*]", building_b0 ): if get_opaque_surface_type(surface_b) == OST.ABOVE_GRADE_WALL: surface_azimuth_b = surface_b["azimuth"] + total_surface_fenestration_area_b = ZERO.AREA surface_azimuth_bin = get_key_for_azi(surface_azimuth_b) if surface_azimuth_bin not in azimuth_fen_area_dict_b: @@ -236,10 +235,10 @@ def get_key_for_azi(azi): "has_baseline_270": has_baseline_270, "has_proposed": has_proposed, "has_user": has_user, - "has_basseline_0_output": has_basseline_0_output, - "has_basseline_90_output": has_basseline_90_output, - "has_basseline_180_output": has_basseline_180_output, - "has_basseline_270_output": has_basseline_270_output, + "has_baseline_0_output": has_baseline_0_output, + "has_baseline_90_output": has_baseline_90_output, + "has_baseline_180_output": has_baseline_180_output, + "has_baseline_270_output": has_baseline_270_output, "has_proposed_output": has_proposed_output, "has_user_output": has_user_output, } @@ -257,10 +256,10 @@ def rule_check(self, context, calc_vals=None, data=None): has_proposed = calc_vals["has_proposed"] has_user = calc_vals["has_user"] - has_basseline_0_output = calc_vals["has_basseline_0_output"] - has_basseline_90_output = calc_vals["has_basseline_90_output"] - has_basseline_180_output = calc_vals["has_basseline_180_output"] - has_basseline_270_output = calc_vals["has_basseline_270_output"] + has_baseline_0_output = calc_vals["has_baseline_0_output"] + has_baseline_90_output = calc_vals["has_baseline_90_output"] + has_baseline_180_output = calc_vals["has_baseline_180_output"] + has_baseline_270_output = calc_vals["has_baseline_270_output"] has_proposed_output = calc_vals["has_proposed_output"] return ( @@ -268,16 +267,16 @@ def rule_check(self, context, calc_vals=None, data=None): and has_proposed and has_baseline_0 and has_proposed_output - and has_basseline_0_output + and has_baseline_0_output ) and ( ( rotation_expected_b and has_baseline_90 and has_baseline_180 and has_baseline_270 - and has_basseline_90_output - and has_basseline_180_output - and has_basseline_270_output + and has_baseline_90_output + and has_baseline_180_output + and has_baseline_270_output and no_of_rmds == 6 and no_of_output_instance == 5 ) From fe33932834418fc50725d4bd5fb1dec09e48236c Mon Sep 17 00:00:00 2001 From: yunjoonjung Date: Mon, 15 Jan 2024 09:07:58 -0800 Subject: [PATCH 10/18] Formatted section22 rules --- rct229/rulesets/ashrae9012019/section22/section22rule35.py | 5 ++++- rct229/rulesets/ashrae9012019/section22/section22rule37.py | 5 ++++- rct229/rulesets/ashrae9012019/section22/section22rule38.py | 5 ++++- rct229/rulesets/ashrae9012019/section22/section22rule39.py | 5 ++++- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/rct229/rulesets/ashrae9012019/section22/section22rule35.py b/rct229/rulesets/ashrae9012019/section22/section22rule35.py index 63dc1385b6..0cd1697091 100644 --- a/rct229/rulesets/ashrae9012019/section22/section22rule35.py +++ b/rct229/rulesets/ashrae9012019/section22/section22rule35.py @@ -31,7 +31,10 @@ def __init__(self): class RulesetModelInstanceRule(PartialRuleDefinition): def __init__(self): - super(Section22Rule35.RulesetModelInstanceRule, self,).__init__( + super( + Section22Rule35.RulesetModelInstanceRule, + self, + ).__init__( rmrs_used=produce_ruleset_model_instance( USER=False, BASELINE_0=True, PROPOSED=False ), diff --git a/rct229/rulesets/ashrae9012019/section22/section22rule37.py b/rct229/rulesets/ashrae9012019/section22/section22rule37.py index aaffa4ab00..755855a80e 100644 --- a/rct229/rulesets/ashrae9012019/section22/section22rule37.py +++ b/rct229/rulesets/ashrae9012019/section22/section22rule37.py @@ -31,7 +31,10 @@ def __init__(self): class RulesetModelInstanceRule(PartialRuleDefinition): def __init__(self): - super(Section22Rule37.RulesetModelInstanceRule, self,).__init__( + super( + Section22Rule37.RulesetModelInstanceRule, + self, + ).__init__( rmrs_used=produce_ruleset_model_instance( USER=False, BASELINE_0=True, PROPOSED=False ), diff --git a/rct229/rulesets/ashrae9012019/section22/section22rule38.py b/rct229/rulesets/ashrae9012019/section22/section22rule38.py index c53d427bb9..25924f1a45 100644 --- a/rct229/rulesets/ashrae9012019/section22/section22rule38.py +++ b/rct229/rulesets/ashrae9012019/section22/section22rule38.py @@ -32,7 +32,10 @@ def __init__(self): class RulesetModelInstanceRule(PartialRuleDefinition): def __init__(self): - super(Section22Rule38.RulesetModelInstanceRule, self,).__init__( + super( + Section22Rule38.RulesetModelInstanceRule, + self, + ).__init__( rmrs_used=produce_ruleset_model_instance( USER=False, BASELINE_0=True, PROPOSED=False ), diff --git a/rct229/rulesets/ashrae9012019/section22/section22rule39.py b/rct229/rulesets/ashrae9012019/section22/section22rule39.py index 4fa0533b52..b47fd056a6 100644 --- a/rct229/rulesets/ashrae9012019/section22/section22rule39.py +++ b/rct229/rulesets/ashrae9012019/section22/section22rule39.py @@ -31,7 +31,10 @@ def __init__(self): class RulesetModelInstanceRule(PartialRuleDefinition): def __init__(self): - super(Section22Rule39.RulesetModelInstanceRule, self,).__init__( + super( + Section22Rule39.RulesetModelInstanceRule, + self, + ).__init__( rmrs_used=produce_ruleset_model_instance( USER=False, BASELINE_0=True, PROPOSED=False ), From ff6f383e3fc20127630e65d204bcafbe103a7be7 Mon Sep 17 00:00:00 2001 From: yunjoonjung Date: Fri, 19 Jan 2024 11:31:37 -0800 Subject: [PATCH 11/18] revert to the original for engine and rule_base changes --- rct229/rule_engine/engine.py | 2 +- rct229/rule_engine/rule_base.py | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/rct229/rule_engine/engine.py b/rct229/rule_engine/engine.py index 23ab7c8976..0f783c8804 100644 --- a/rct229/rule_engine/engine.py +++ b/rct229/rule_engine/engine.py @@ -148,7 +148,7 @@ def evaluate_rules( rmds_used = get_rmd_instance() for rule in rules_list: for rule_model in rmds.get_ruleset_model_types(): - if rule.rmrs_used[rule_model] and rmds[rule_model] is not None: + if rule.rmrs_used[rule_model]: rmds_used[rule_model] = True for rule_model in rmds.get_ruleset_model_types(): diff --git a/rct229/rule_engine/rule_base.py b/rct229/rule_engine/rule_base.py index cab94edd39..2d27188fba 100644 --- a/rct229/rule_engine/rule_base.py +++ b/rct229/rule_engine/rule_base.py @@ -298,8 +298,6 @@ def get_context(self, rmds, data=None): # and rmds[ruleset_model] is None or empty and (rmds[ruleset_model] is None or not rmds[ruleset_model]) ): - # BASELINE 90, 180, and 270 are optional - if ruleset_model not in [BASELINE_90, BASELINE_180, BASELINE_270]: missing_contexts.append(ruleset_model) if len(missing_contexts) > 0: From 3ddf87b471d4b2baa1e315eae1d044800f1c1101 Mon Sep 17 00:00:00 2001 From: yunjoonjung Date: Fri, 19 Jan 2024 11:33:53 -0800 Subject: [PATCH 12/18] black rule_base --- rct229/rule_engine/rule_base.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rct229/rule_engine/rule_base.py b/rct229/rule_engine/rule_base.py index 2d27188fba..964ff7a650 100644 --- a/rct229/rule_engine/rule_base.py +++ b/rct229/rule_engine/rule_base.py @@ -1,7 +1,6 @@ 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.rulesets.ashrae9012019 import BASELINE_90, BASELINE_180, BASELINE_270 from rct229.utils.assertions import MissingKeyException, RCTFailureException from rct229.utils.json_utils import slash_prefix_guarantee from rct229.utils.jsonpath_utils import find_all @@ -298,7 +297,7 @@ def get_context(self, rmds, data=None): # and rmds[ruleset_model] is None or empty and (rmds[ruleset_model] is None or not rmds[ruleset_model]) ): - missing_contexts.append(ruleset_model) + missing_contexts.append(ruleset_model) if len(missing_contexts) > 0: retval = "MISSING_" + "_".join(missing_contexts) From a519cad4c7d83f2dd37cbc1f0516efff222f5514 Mon Sep 17 00:00:00 2001 From: yunjoonjung Date: Fri, 19 Jan 2024 11:36:03 -0800 Subject: [PATCH 13/18] Formatted 22-29 rule --- rct229/rulesets/ashrae9012019/section22/section22rule39.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/rct229/rulesets/ashrae9012019/section22/section22rule39.py b/rct229/rulesets/ashrae9012019/section22/section22rule39.py index b47fd056a6..4fa0533b52 100644 --- a/rct229/rulesets/ashrae9012019/section22/section22rule39.py +++ b/rct229/rulesets/ashrae9012019/section22/section22rule39.py @@ -31,10 +31,7 @@ def __init__(self): class RulesetModelInstanceRule(PartialRuleDefinition): def __init__(self): - super( - Section22Rule39.RulesetModelInstanceRule, - self, - ).__init__( + super(Section22Rule39.RulesetModelInstanceRule, self,).__init__( rmrs_used=produce_ruleset_model_instance( USER=False, BASELINE_0=True, PROPOSED=False ), From cf4fb68eaf0fcff3491948fae033f9509ce5bd58 Mon Sep 17 00:00:00 2001 From: yunjoonjung Date: Fri, 19 Jan 2024 11:37:39 -0800 Subject: [PATCH 14/18] Formatted 22-35,37 and 38 --- rct229/rulesets/ashrae9012019/section22/section22rule35.py | 5 +---- rct229/rulesets/ashrae9012019/section22/section22rule37.py | 5 +---- rct229/rulesets/ashrae9012019/section22/section22rule38.py | 5 +---- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/rct229/rulesets/ashrae9012019/section22/section22rule35.py b/rct229/rulesets/ashrae9012019/section22/section22rule35.py index 0cd1697091..63dc1385b6 100644 --- a/rct229/rulesets/ashrae9012019/section22/section22rule35.py +++ b/rct229/rulesets/ashrae9012019/section22/section22rule35.py @@ -31,10 +31,7 @@ def __init__(self): class RulesetModelInstanceRule(PartialRuleDefinition): def __init__(self): - super( - Section22Rule35.RulesetModelInstanceRule, - self, - ).__init__( + super(Section22Rule35.RulesetModelInstanceRule, self,).__init__( rmrs_used=produce_ruleset_model_instance( USER=False, BASELINE_0=True, PROPOSED=False ), diff --git a/rct229/rulesets/ashrae9012019/section22/section22rule37.py b/rct229/rulesets/ashrae9012019/section22/section22rule37.py index 755855a80e..aaffa4ab00 100644 --- a/rct229/rulesets/ashrae9012019/section22/section22rule37.py +++ b/rct229/rulesets/ashrae9012019/section22/section22rule37.py @@ -31,10 +31,7 @@ def __init__(self): class RulesetModelInstanceRule(PartialRuleDefinition): def __init__(self): - super( - Section22Rule37.RulesetModelInstanceRule, - self, - ).__init__( + super(Section22Rule37.RulesetModelInstanceRule, self,).__init__( rmrs_used=produce_ruleset_model_instance( USER=False, BASELINE_0=True, PROPOSED=False ), diff --git a/rct229/rulesets/ashrae9012019/section22/section22rule38.py b/rct229/rulesets/ashrae9012019/section22/section22rule38.py index 25924f1a45..c53d427bb9 100644 --- a/rct229/rulesets/ashrae9012019/section22/section22rule38.py +++ b/rct229/rulesets/ashrae9012019/section22/section22rule38.py @@ -32,10 +32,7 @@ def __init__(self): class RulesetModelInstanceRule(PartialRuleDefinition): def __init__(self): - super( - Section22Rule38.RulesetModelInstanceRule, - self, - ).__init__( + super(Section22Rule38.RulesetModelInstanceRule, self,).__init__( rmrs_used=produce_ruleset_model_instance( USER=False, BASELINE_0=True, PROPOSED=False ), From 8095f1897e3fc818a83a39134fb99126ebb83f5f Mon Sep 17 00:00:00 2001 From: yunjoonjung Date: Fri, 19 Jan 2024 12:12:44 -0800 Subject: [PATCH 15/18] Added rmrs_used_optional --- rct229/rule_engine/engine.py | 4 +++- .../rulesets/ashrae9012019/section5/section5rule1.py | 11 ++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/rct229/rule_engine/engine.py b/rct229/rule_engine/engine.py index bb2c4ebcd7..de9698171f 100644 --- a/rct229/rule_engine/engine.py +++ b/rct229/rule_engine/engine.py @@ -180,7 +180,9 @@ def evaluate_rules( rmds_used[rule_model] = True for rule_model in rmds.get_ruleset_model_types(): - if rmds_used[rule_model]: + if rmds_used[rule_model] and not ( + rule.rmrs_used_optional and rule.rmrs_used_optional[rule_model] + ): rmd_validation = validate_rmr(rmds[rule_model], test) if rmd_validation["passed"] is not True: invalid_rmds[rule_model] = rmd_validation["error"] diff --git a/rct229/rulesets/ashrae9012019/section5/section5rule1.py b/rct229/rulesets/ashrae9012019/section5/section5rule1.py index 59ad646de6..63b96f3e79 100644 --- a/rct229/rulesets/ashrae9012019/section5/section5rule1.py +++ b/rct229/rulesets/ashrae9012019/section5/section5rule1.py @@ -38,6 +38,9 @@ def __init__(self): BASELINE_270=True, PROPOSED=True, ), + rmrs_used_optional=produce_ruleset_model_instance( + BASELINE_90=True, BASELINE_180=True, BASELINE_270=True + ), each_rule=Section5Rule1.RMDRule(), index_rmr=BASELINE_0, id="5-1", @@ -59,6 +62,9 @@ def __init__(self): BASELINE_270=True, PROPOSED=True, ), + rmrs_used_optional=produce_ruleset_model_instance( + BASELINE_90=True, BASELINE_180=True, BASELINE_270=True + ), each_rule=Section5Rule1.RMDRule.BuildingRule(), index_rmr=BASELINE_0, list_path="$.buildings[*]", @@ -135,12 +141,7 @@ class BuildingRule(RuleDefinitionBase): def __init__(self): super(Section5Rule1.RMDRule.BuildingRule, self).__init__( rmrs_used=produce_ruleset_model_instance( - USER=False, BASELINE_0=True, - BASELINE_90=False, - BASELINE_180=False, - BASELINE_270=False, - PROPOSED=False, ), required_fields={ "$.building_segments[*].zones[*].surfaces[*]": ["azimuth"], From 7e819c4960cc078d7c87f7296b91378949c4af5c Mon Sep 17 00:00:00 2001 From: yunjoonjung Date: Fri, 19 Jan 2024 12:13:28 -0800 Subject: [PATCH 16/18] Added Jackson's TCD --- .../ashrae9012019/section5/rule_5_1.json | 872 ++++++++++++++++++ 1 file changed, 872 insertions(+) create mode 100644 rct229/ruletest_engine/ruletest_jsons/ashrae9012019/section5/rule_5_1.json diff --git a/rct229/ruletest_engine/ruletest_jsons/ashrae9012019/section5/rule_5_1.json b/rct229/ruletest_engine/ruletest_jsons/ashrae9012019/section5/rule_5_1.json new file mode 100644 index 0000000000..eb9f63f96e --- /dev/null +++ b/rct229/ruletest_engine/ruletest_jsons/ashrae9012019/section5/rule_5_1.json @@ -0,0 +1,872 @@ +{ + "rule-5-1-a": { + "Section": 5, + "Rule": 1, + "Test": "a", + "test_description": "Project has one building segment with two above-grade wall surfaces at different azimuth angles. Each wall has a window and the windows differ in area by more than 5%. Four sets of baseline model results are appropriately provided.", + "expected_rule_outcome": "pass", + "standard": { + "rule_id": "5-1", + "ruleset_reference": "Table G3.1(5) Baseline Building Performance (a)", + "rule_description": "There are four baseline rotations (i.e., four baseline models differing in azimuth by 90 degrees and four sets of baseline model results) if vertical fenestration area per each orientation differ by more than 5%.", + "applicable_rmr": "Baseline Model", + "rule_assertion": "=", + "comparison_value": "Expected Value", + "primary_rule": "Full", + "schema_version": "0.0.29" + }, + "rmr_transformations": { + "user": { + "id": "ASHRAE229", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "output": { + "id": "Output 1", + "output_instance": { + "id": "Output Instance 1", + "unmet_load_hours": 0, + "unmet_load_hours_heating": 0, + "unmet_load_hours_cooling": 0, + "unmet_occupied_load_hours_heating": 0, + "unmet_occupied_load_hours_cooling": 0, + "building_peak_cooling_load": 25000 + } + }, + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Zone 1", + "thermostat_cooling_setpoint_schedule": "Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Heating Schedule 1", + "surfaces": [ + { + "id": "Surface 1", + "azimuth": 20, + "adjacent_to": "EXTERIOR", + "tilt": 90, + "area": 92.90303999999999, + "subsurfaces": [ + { + "id": "Window 1", + "classification": "WINDOW", + "glazed_area": 1.3935456 + } + ] + }, + { + "id": "Surface 2", + "azimuth": 190, + "adjacent_to": "EXTERIOR", + "tilt": 90, + "area": 92.90303999999999, + "subsurfaces": [ + { + "id": "Window 2", + "classification": "WINDOW", + "glazed_area": 2.3225759999999998 + } + ] + } + ] + } + ] + } + ] + } + ], + "type": "USER" + } + ] + }, + "proposed": { + "id": "ASHRAE229", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "output": { + "id": "Output 1", + "output_instance": { + "id": "Output Instance 1", + "unmet_load_hours": 0, + "unmet_load_hours_heating": 0, + "unmet_load_hours_cooling": 0, + "unmet_occupied_load_hours_heating": 0, + "unmet_occupied_load_hours_cooling": 0, + "building_peak_cooling_load": 25000 + } + }, + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Zone 1", + "thermostat_cooling_setpoint_schedule": "Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Heating Schedule 1", + "surfaces": [ + { + "id": "Surface 1", + "azimuth": 20, + "adjacent_to": "EXTERIOR", + "tilt": 90, + "area": 92.90303999999999, + "subsurfaces": [ + { + "id": "Window 1", + "classification": "WINDOW", + "glazed_area": 1.3935456 + } + ] + }, + { + "id": "Surface 2", + "azimuth": 190, + "adjacent_to": "EXTERIOR", + "tilt": 90, + "area": 92.90303999999999, + "subsurfaces": [ + { + "id": "Window 2", + "classification": "WINDOW", + "glazed_area": 2.3225759999999998 + } + ] + } + ] + } + ] + } + ] + } + ], + "type": "PROPOSED" + } + ] + }, + "baseline": { + "id": "ASHRAE229", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "output": { + "id": "Output 1", + "output_instance": { + "id": "Output Instance 1", + "unmet_load_hours": 0, + "unmet_load_hours_heating": 0, + "unmet_load_hours_cooling": 0, + "unmet_occupied_load_hours_heating": 0, + "unmet_occupied_load_hours_cooling": 0, + "building_peak_cooling_load": 25000 + } + }, + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Zone 1", + "thermostat_cooling_setpoint_schedule": "Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Heating Schedule 1", + "surfaces": [ + { + "id": "Surface 1", + "azimuth": 20, + "adjacent_to": "EXTERIOR", + "tilt": 90, + "area": 92.90303999999999, + "subsurfaces": [ + { + "id": "Window 1", + "classification": "WINDOW", + "glazed_area": 1.3935456 + } + ] + }, + { + "id": "Surface 2", + "azimuth": 190, + "adjacent_to": "EXTERIOR", + "tilt": 90, + "area": 92.90303999999999, + "subsurfaces": [ + { + "id": "Window 2", + "classification": "WINDOW", + "glazed_area": 2.3225759999999998 + } + ] + } + ] + } + ] + } + ] + } + ], + "type": "BASELINE_0" + }, + { + "id": "RMD 1", + "output": { + "id": "Output 1", + "output_instance": { + "id": "Output Instance 1", + "unmet_load_hours": 0, + "unmet_load_hours_heating": 0, + "unmet_load_hours_cooling": 0, + "unmet_occupied_load_hours_heating": 0, + "unmet_occupied_load_hours_cooling": 0, + "building_peak_cooling_load": 25000 + } + }, + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Zone 1", + "thermostat_cooling_setpoint_schedule": "Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Heating Schedule 1", + "surfaces": [ + { + "id": "Surface 1", + "azimuth": 110, + "adjacent_to": "EXTERIOR", + "tilt": 90, + "area": 92.90303999999999, + "subsurfaces": [ + { + "id": "Window 1", + "classification": "WINDOW", + "glazed_area": 1.3935456 + } + ] + }, + { + "id": "Surface 2", + "azimuth": 280, + "adjacent_to": "EXTERIOR", + "tilt": 90, + "area": 92.90303999999999, + "subsurfaces": [ + { + "id": "Window 2", + "classification": "WINDOW", + "glazed_area": 2.3225759999999998 + } + ] + } + ] + } + ] + } + ] + } + ], + "type": "BASELINE_90" + }, + { + "id": "RMD 1", + "output": { + "id": "Output 1", + "output_instance": { + "id": "Output Instance 1", + "unmet_load_hours": 0, + "unmet_load_hours_heating": 0, + "unmet_load_hours_cooling": 0, + "unmet_occupied_load_hours_heating": 0, + "unmet_occupied_load_hours_cooling": 0, + "building_peak_cooling_load": 25000 + } + }, + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Zone 1", + "thermostat_cooling_setpoint_schedule": "Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Heating Schedule 1", + "surfaces": [ + { + "id": "Surface 1", + "azimuth": 200, + "adjacent_to": "EXTERIOR", + "tilt": 90, + "area": 92.90303999999999, + "subsurfaces": [ + { + "id": "Window 1", + "classification": "WINDOW", + "glazed_area": 1.3935456 + } + ] + }, + { + "id": "Surface 2", + "azimuth": 10, + "adjacent_to": "EXTERIOR", + "tilt": 90, + "area": 92.90303999999999, + "subsurfaces": [ + { + "id": "Window 2", + "classification": "WINDOW", + "glazed_area": 2.3225759999999998 + } + ] + } + ] + } + ] + } + ] + } + ], + "type": "BASELINE_180" + }, + { + "id": "RMD 1", + "output": { + "id": "Output 1", + "output_instance": { + "id": "Output Instance 1", + "unmet_load_hours": 0, + "unmet_load_hours_heating": 0, + "unmet_load_hours_cooling": 0, + "unmet_occupied_load_hours_heating": 0, + "unmet_occupied_load_hours_cooling": 0, + "building_peak_cooling_load": 25000 + } + }, + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Zone 1", + "thermostat_cooling_setpoint_schedule": "Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Heating Schedule 1", + "surfaces": [ + { + "id": "Surface 1", + "azimuth": 290, + "adjacent_to": "EXTERIOR", + "tilt": 90, + "area": 92.90303999999999, + "subsurfaces": [ + { + "id": "Window 1", + "classification": "WINDOW", + "glazed_area": 1.3935456 + } + ] + }, + { + "id": "Surface 2", + "azimuth": 100, + "adjacent_to": "EXTERIOR", + "tilt": 90, + "area": 92.90303999999999, + "subsurfaces": [ + { + "id": "Window 2", + "classification": "WINDOW", + "glazed_area": 2.3225759999999998 + } + ] + } + ] + } + ] + } + ] + } + ], + "type": "BASELINE_270" + } + ] + } + } + }, + "rule-5-1-b": { + "Section": 5, + "Rule": 1, + "Test": "b", + "test_description": "Project has one building segment with two above-grade wall surfaces at different azimuth angles. Each wall has a window and the windows differ in area by less than 5%. One set of baseline model results is appropriately provided.", + "expected_rule_outcome": "pass", + "standard": { + "rule_id": "5-1", + "ruleset_reference": "Table G3.1(5) Baseline Building Performance (a)", + "rule_description": "There are four baseline rotations (i.e., four baseline models differing in azimuth by 90 degrees and four sets of baseline model results) if vertical fenestration area per each orientation differ by more than 5%.", + "applicable_rmr": "Baseline Model", + "rule_assertion": "=", + "comparison_value": "Expected Value", + "primary_rule": "Full", + "schema_version": "0.0.29" + }, + "rmr_transformations": { + "user": { + "id": "ASHRAE229", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "output": { + "id": "Output 1", + "output_instance": { + "id": "Output Instance 1", + "unmet_load_hours": 0, + "unmet_load_hours_heating": 0, + "unmet_load_hours_cooling": 0, + "unmet_occupied_load_hours_heating": 0, + "unmet_occupied_load_hours_cooling": 0, + "building_peak_cooling_load": 25000 + } + }, + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Zone 1", + "thermostat_cooling_setpoint_schedule": "Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Heating Schedule 1", + "surfaces": [ + { + "id": "Surface 1", + "azimuth": 20, + "adjacent_to": "EXTERIOR", + "tilt": 90, + "area": 92.90303999999999, + "subsurfaces": [ + { + "id": "Window 1", + "classification": "WINDOW", + "glazed_area": 2.22967296 + } + ] + }, + { + "id": "Surface 2", + "azimuth": 190, + "adjacent_to": "EXTERIOR", + "tilt": 90, + "area": 92.90303999999999, + "subsurfaces": [ + { + "id": "Window 2", + "classification": "WINDOW", + "glazed_area": 2.3225759999999998 + } + ] + } + ] + } + ] + } + ] + } + ], + "type": "USER" + } + ] + }, + "proposed": { + "id": "ASHRAE229", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "output": { + "id": "Output 1", + "output_instance": { + "id": "Output Instance 1", + "unmet_load_hours": 0, + "unmet_load_hours_heating": 0, + "unmet_load_hours_cooling": 0, + "unmet_occupied_load_hours_heating": 0, + "unmet_occupied_load_hours_cooling": 0, + "building_peak_cooling_load": 25000 + } + }, + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Zone 1", + "thermostat_cooling_setpoint_schedule": "Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Heating Schedule 1", + "surfaces": [ + { + "id": "Surface 1", + "azimuth": 20, + "adjacent_to": "EXTERIOR", + "tilt": 90, + "area": 92.90303999999999, + "subsurfaces": [ + { + "id": "Window 1", + "classification": "WINDOW", + "glazed_area": 2.22967296 + } + ] + }, + { + "id": "Surface 2", + "azimuth": 190, + "adjacent_to": "EXTERIOR", + "tilt": 90, + "area": 92.90303999999999, + "subsurfaces": [ + { + "id": "Window 2", + "classification": "WINDOW", + "glazed_area": 2.3225759999999998 + } + ] + } + ] + } + ] + } + ] + } + ], + "type": "PROPOSED" + } + ] + }, + "baseline": { + "id": "ASHRAE229", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "output": { + "id": "Output 1", + "output_instance": { + "id": "Output Instance 1", + "unmet_load_hours": 0, + "unmet_load_hours_heating": 0, + "unmet_load_hours_cooling": 0, + "unmet_occupied_load_hours_heating": 0, + "unmet_occupied_load_hours_cooling": 0, + "building_peak_cooling_load": 25000 + } + }, + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Zone 1", + "thermostat_cooling_setpoint_schedule": "Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Heating Schedule 1", + "surfaces": [ + { + "id": "Surface 1", + "azimuth": 20, + "adjacent_to": "EXTERIOR", + "tilt": 90, + "area": 92.90303999999999, + "subsurfaces": [ + { + "id": "Window 1", + "classification": "WINDOW", + "glazed_area": 2.22967296 + } + ] + }, + { + "id": "Surface 2", + "azimuth": 190, + "adjacent_to": "EXTERIOR", + "tilt": 90, + "area": 92.90303999999999, + "subsurfaces": [ + { + "id": "Window 2", + "classification": "WINDOW", + "glazed_area": 2.3225759999999998 + } + ] + } + ] + } + ] + } + ] + } + ], + "type": "BASELINE_0" + } + ] + } + } + }, + "rule-5-1-c": { + "Section": 5, + "Rule": 1, + "Test": "c", + "test_description": "Project has one building segment with two above-grade wall surfaces at different azimuth angles. Each wall has a window and the windows differ in area by more than 5%. One set of baseline model results is provided, when four sets are expected.", + "expected_rule_outcome": "fail", + "standard": { + "rule_id": "5-1", + "ruleset_reference": "Table G3.1(5) Baseline Building Performance (a)", + "rule_description": "There are four baseline rotations (i.e., four baseline models differing in azimuth by 90 degrees and four sets of baseline model results) if vertical fenestration area per each orientation differ by more than 5%.", + "applicable_rmr": "Baseline Model", + "rule_assertion": "=", + "comparison_value": "Expected Value", + "primary_rule": "Full", + "schema_version": "0.0.29" + }, + "rmr_transformations": { + "user": { + "id": "ASHRAE229", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "output": { + "id": "Output 1", + "output_instance": { + "id": "Output Instance 1", + "unmet_load_hours": 0, + "unmet_load_hours_heating": 0, + "unmet_load_hours_cooling": 0, + "unmet_occupied_load_hours_heating": 0, + "unmet_occupied_load_hours_cooling": 0, + "building_peak_cooling_load": 25000 + } + }, + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Zone 1", + "thermostat_cooling_setpoint_schedule": "Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Heating Schedule 1", + "surfaces": [ + { + "id": "Surface 1", + "azimuth": 20, + "adjacent_to": "EXTERIOR", + "tilt": 90, + "area": 92.90303999999999, + "subsurfaces": [ + { + "id": "Window 1", + "classification": "WINDOW", + "glazed_area": 1.3935456 + } + ] + }, + { + "id": "Surface 2", + "azimuth": 190, + "adjacent_to": "EXTERIOR", + "tilt": 90, + "area": 92.90303999999999, + "subsurfaces": [ + { + "id": "Window 2", + "classification": "WINDOW", + "glazed_area": 2.3225759999999998 + } + ] + } + ] + } + ] + } + ] + } + ], + "type": "USER" + } + ] + }, + "proposed": { + "id": "ASHRAE229", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "output": { + "id": "Output 1", + "output_instance": { + "id": "Output Instance 1", + "unmet_load_hours": 0, + "unmet_load_hours_heating": 0, + "unmet_load_hours_cooling": 0, + "unmet_occupied_load_hours_heating": 0, + "unmet_occupied_load_hours_cooling": 0, + "building_peak_cooling_load": 25000 + } + }, + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Zone 1", + "thermostat_cooling_setpoint_schedule": "Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Heating Schedule 1", + "surfaces": [ + { + "id": "Surface 1", + "azimuth": 20, + "adjacent_to": "EXTERIOR", + "tilt": 90, + "area": 92.90303999999999, + "subsurfaces": [ + { + "id": "Window 1", + "classification": "WINDOW", + "glazed_area": 1.3935456 + } + ] + }, + { + "id": "Surface 2", + "azimuth": 190, + "adjacent_to": "EXTERIOR", + "tilt": 90, + "area": 92.90303999999999, + "subsurfaces": [ + { + "id": "Window 2", + "classification": "WINDOW", + "glazed_area": 2.3225759999999998 + } + ] + } + ] + } + ] + } + ] + } + ], + "type": "PROPOSED" + } + ] + }, + "baseline": { + "id": "ASHRAE229", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "output": { + "id": "Output 1", + "output_instance": { + "id": "Output Instance 1", + "unmet_load_hours": 0, + "unmet_load_hours_heating": 0, + "unmet_load_hours_cooling": 0, + "unmet_occupied_load_hours_heating": 0, + "unmet_occupied_load_hours_cooling": 0, + "building_peak_cooling_load": 25000 + } + }, + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Zone 1", + "thermostat_cooling_setpoint_schedule": "Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Heating Schedule 1", + "surfaces": [ + { + "id": "Surface 1", + "azimuth": 20, + "adjacent_to": "EXTERIOR", + "tilt": 90, + "area": 92.90303999999999, + "subsurfaces": [ + { + "id": "Window 1", + "classification": "WINDOW", + "glazed_area": 1.3935456 + } + ] + }, + { + "id": "Surface 2", + "azimuth": 190, + "adjacent_to": "EXTERIOR", + "tilt": 90, + "area": 92.90303999999999, + "subsurfaces": [ + { + "id": "Window 2", + "classification": "WINDOW", + "glazed_area": 2.3225759999999998 + } + ] + } + ] + } + ] + } + ] + } + ], + "type": "BASELINE_0" + } + ] + } + } + } +} \ No newline at end of file From 7cd1396700e2d9330c5daa7f84b5e6258224a34c Mon Sep 17 00:00:00 2001 From: yunjoonjung Date: Fri, 19 Jan 2024 12:22:52 -0800 Subject: [PATCH 17/18] Corrected 5-31 TCD --- .../ruletest_jsons/ashrae9012019/section5/rule_5_31.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rct229/ruletest_engine/ruletest_jsons/ashrae9012019/section5/rule_5_31.json b/rct229/ruletest_engine/ruletest_jsons/ashrae9012019/section5/rule_5_31.json index b7963581ff..5ef5790187 100644 --- a/rct229/ruletest_engine/ruletest_jsons/ashrae9012019/section5/rule_5_31.json +++ b/rct229/ruletest_engine/ruletest_jsons/ashrae9012019/section5/rule_5_31.json @@ -82,7 +82,7 @@ ] } ], - "type": "USER" + "type": "BASELINE_0" } ] } @@ -171,7 +171,7 @@ ] } ], - "type": "PROPOSED" + "type": "BASELINE_0" } ] } From a6450a862a5899c00b6638bd2ee9159f7e511862 Mon Sep 17 00:00:00 2001 From: Jackson Jarboe <122476654+JacksonJ-KC@users.noreply.github.com> Date: Sat, 20 Jan 2024 12:18:05 -0500 Subject: [PATCH 18/18] recreate 5-31 --- .../ashrae9012019/section5/rule_5_31.json | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/rct229/ruletest_engine/ruletest_jsons/ashrae9012019/section5/rule_5_31.json b/rct229/ruletest_engine/ruletest_jsons/ashrae9012019/section5/rule_5_31.json index b7963581ff..7b2a3aef55 100644 --- a/rct229/ruletest_engine/ruletest_jsons/ashrae9012019/section5/rule_5_31.json +++ b/rct229/ruletest_engine/ruletest_jsons/ashrae9012019/section5/rule_5_31.json @@ -3,7 +3,7 @@ "Section": 5, "Rule": 31, "Test": "a", - "test_description": "Project has one building segment and includes a space with a roof surface. The roof surface thermal emittance in the proposed and user models is equal to 0.9", + "test_description": "Project has one building segment and includes a space with a roof surface. The baseline roof emittance is correctly set to 0.9", "expected_rule_outcome": "pass", "standard": { "rule_id": "5-31", @@ -11,7 +11,7 @@ "rule_description": "The baseline roof surfaces shall be modeled using a solar reflectance of 0.30.", "applicable_rmr": "Baseline Model", "rule_assertion": "=", - "comparison_value": "User Model", + "comparison_value": "Expected Value", "primary_rule": "Full", "schema_version": "0.0.29" }, @@ -60,6 +60,10 @@ "optical_properties": { "id": "Surface Optical Properties 1", "absorptance_solar_exterior": 0.7 + }, + "construction": { + "id": "Construction 1", + "u_factor": 3.2366105565544463 } } ] @@ -74,7 +78,7 @@ }, "heating_system": { "id": "Heating System 1", - "design_capacity": 9378.275555555556 + "design_capacity": 0.0 } } ] @@ -82,7 +86,7 @@ ] } ], - "type": "USER" + "type": "BASELINE_0" } ] } @@ -92,15 +96,15 @@ "Section": 5, "Rule": 31, "Test": "b", - "test_description": "Project has one building segment and includes a space with a roof surface. The roof surface solar reflectance in the proposed and user models is equal to 0.7", - "expected_rule_outcome": "undetermined", + "test_description": "Project has one building segment and includes a space with a roof surface. The baseline roof emittance is incorrectly set to 0.5 ", + "expected_rule_outcome": "fail", "standard": { "rule_id": "5-31", "ruleset_reference": "Table G3.1(5) Baseline Building Performance", "rule_description": "The baseline roof surfaces shall be modeled using a solar reflectance of 0.30.", "applicable_rmr": "Baseline Model", "rule_assertion": "=", - "comparison_value": "User Model", + "comparison_value": "Expected Value", "primary_rule": "Full", "schema_version": "0.0.29" }, @@ -149,6 +153,10 @@ "optical_properties": { "id": "Surface Optical Properties 1", "absorptance_solar_exterior": 0.5 + }, + "construction": { + "id": "Construction 1", + "u_factor": 3.2366105565544463 } } ] @@ -163,7 +171,7 @@ }, "heating_system": { "id": "Heating System 1", - "design_capacity": 9378.275555555556 + "design_capacity": 0.0 } } ] @@ -171,7 +179,7 @@ ] } ], - "type": "PROPOSED" + "type": "BASELINE_0" } ] }