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/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/rule_engine/rule_base.py b/rct229/rule_engine/rule_base.py index e80cb4cc59..8c798a96d0 100644 --- a/rct229/rule_engine/rule_base.py +++ b/rct229/rule_engine/rule_base.py @@ -1,11 +1,10 @@ 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.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: 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/__init__.py b/rct229/rulesets/ashrae9012019/section5/__init__.py index 7d098fbb57..d15464c0ab 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..63b96f3e79 --- /dev/null +++ b/rct229/rulesets/ashrae9012019/section5/section5rule1.py @@ -0,0 +1,285 @@ +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 +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.schema.schema_enums import SchemaEnums +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): + """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, + BASELINE_90=True, + BASELINE_180=True, + 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", + 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, + 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, + BASELINE_90=True, + BASELINE_180=True, + 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[*]", + ) + + def create_data(self, context, data): + 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 + + 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_baseline_0_output = has_baseline_0 and bool( + find_one("$.output.output_instance", rmd_b0) + ) + has_baseline_90_output = has_baseline_90 and bool( + find_one("$.output.output_instance", rmd_b90) + ) + has_baseline_180_output = has_baseline_180 and bool( + find_one("$.output.output_instance", rmd_b180) + ) + has_baseline_270_output = has_baseline_270 and bool( + find_one("$.output.output_instance", rmd_b270) + ) + has_proposed_output = has_proposed and bool( + find_one("$.output.output_instance", rmd_p) + ) + has_user_output = has_user and bool( + find_one("$.output.output_instance", rmd_u) + ) + + 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(compact(rmd_list)) + + # filter out rmds that aren't None then count the length + no_of_output_instance = len( + compact( + [ + find_one("$.output.output_instance", rmd) + for rmd in rmd_list_no_user + ] + ) + ) + + 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, + "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, + "no_of_output_instance": no_of_output_instance, + } + + class BuildingRule(RuleDefinitionBase): + def __init__(self): + super(Section5Rule1.RMDRule.BuildingRule, self).__init__( + rmrs_used=produce_ruleset_model_instance( + BASELINE_0=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): + building_b0 = context.BASELINE_0 + + 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"] + 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"] + no_of_output_instance = data["no_of_output_instance"] + + # 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 = {} + 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: + 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) + 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[ + surface_azimuth_bin + ] += total_surface_fenestration_area_b + + 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 + 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 = ( + percent_difference >= ACCEPTABLE_FEN_PERCENTAGE_DIFFERENCE + ) + + return { + "no_of_rmds": no_of_rmds, + "no_of_output_instance": no_of_output_instance, + "rotation_expected_b": rotation_expected_b, + "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, + "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, + } + + def rule_check(self, context, calc_vals=None, data=None): + 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_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_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 ( + has_user + and has_proposed + and has_baseline_0 + and has_proposed_output + and has_baseline_0_output + ) and ( + ( + rotation_expected_b + and has_baseline_90 + and has_baseline_180 + and has_baseline_270 + 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 + ) + or (not rotation_expected_b) + ) 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 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" } ] } 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, )