-
Notifications
You must be signed in to change notification settings - Fork 7
Rule 5-1 Updates #1852
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Rule 5-1 Updates #1852
Changes from all commits
148bfa1
9a88b9f
a0e6f91
1075ffc
7ec2a6a
d9780b2
5dd52b6
1015545
eee61ef
b06bccb
9c5a9f4
d7098e9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,7 +2,7 @@ | |
| **Schema Version** 0.0.33 | ||
| **Primary Rule** True | ||
| **Rule ID:** 5-1 | ||
| **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%. | ||
| **Rule Description:** The baseline building performance shall be generated by simulating the building with its actual orientation and again after rotating the entire building 90, 180, and 270 degrees, then averaging the results. Exceptions: 1. If it can be demonstrated to the satisfaction of the rat-ing authority that the building orientation is dictated bysite considerations. 2. Buildings where the vertical fenestration area on eachorientation varies by less than 5%. | ||
| **Rule Assertion:** Options are PASS/FAIL | ||
| **Appendix G Section:** Table G3.1#5a baseline column | ||
| **90.1 Section Reference:** None | ||
|
|
@@ -20,7 +20,7 @@ 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 | ||
| Get the fenestration area for each unique orientation. | ||
| - 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` | ||
|
|
@@ -37,8 +37,9 @@ Get the fenestration area for each unique orientation (i.e., azimuth) and then c | |
| - 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_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_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)` | ||
| Put the areas 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 3-6 bin, etc. | ||
| - Loop through the dictionary keys: `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 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_dict_b[bin] += azimuth_fen_area_dict_b[azi]` | ||
|
|
||
| Check if the area differs by 5 percent or more. | ||
|
|
@@ -48,34 +49,29 @@ Check if the area differs by 5 percent or more. | |
| - 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 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]` | ||
| - Check for proposed RMD: `has_proposed = any[rmd.type == PROPOSED for rmd in rmds]` | ||
| - Check for baseline 0 degree RMD: `has_baseline_0 = any[rmd.type == BASELINE_0 for rmd in rmds]` | ||
| - 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: `no_of_rmds = len(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: no_of_output_instance += 1 ` | ||
| Check the given BBP and calculate the expected BBP based on the output source results annual cost from the baseline RPD(s). | ||
| - Keep a set to store unique values of baseline building performance energy cost: `bbp_values = set()` | ||
| - Get the total annual cost for all source results in the baseline 0 RPD: `baseline_0_total_annual_cost = sum(rpd_b0.output.source_results.annual_cost)` | ||
| - Keep a list to store the total annual cost for baseline rotation RPDs (if they exist): `baseline_rotation_total_annual_costs = []` | ||
| - Iterate through the RPDs: `for rpd in (rpd_u, rpd_b0, rpd_b90, rpd_b180, rpd_b270, rpd_p):` | ||
| - If the RPD exists, check the output baseline building performance energy cost and add it to the set: `if rpd != Null: bbp_values.add(rpd.output.baseline_building_performance_energy_cost)` | ||
| - If the RPD exists, and it is a baseline rotation RPD, add the total annual cost to the list: `if rpd != Null and rpd.type in [BASELINE_90, BASELINE_180, BASELINE_270]: baseline_rotation_total_annual_costs.append(sum(rpd.output.source_results.annual_cost))` | ||
| - If the length of the set is greater than one, exit evaluation early and return UNDETERMINED with a message: `if len(bbp_values) > 1: UNDETERMINED raise_message "Ruleset expects exactly one BBP value to be used in the project."` | ||
| - Get the single BBP value from the set: `bbp = bbp_values.pop()` | ||
| - Calculate the average of the total annual costs for the baseline RPDs: `avg_baseline_total_annual_cost = (sum(baseline_rotation_total_annual_costs) + baseline_0_total_annual_cost) / (len(baseline_rotation_total_annual_costs) + 1)` | ||
|
|
||
| - **Rule Assertion:** | ||
| - Case 1: If the fenestration area differs by 5% or more by orientation and there are 6 RMDs (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.` | ||
|
|
||
| - **Rule Assertion:** | ||
| - Case 1: Baseline rotations are expected based on the fenestration areas by azimuth and the baseline building performance matches the average of the total source results annual cost from the 4 baseline RMDs: PASS: `if rotation_expected_b and bbp == avg_baseline_total_annual_cost and len(baseline_rotation_total_annual_costs) == 3: PASS` | ||
| - Case 2: Baseline rotations are not expected based on the fenestration areas by azimuth and the baseline building performance matches the total source results annual cost from the BASELINE_0 RMD: PASS: `elif !rotation_expected_b and bbp == baseline_0_total_annual_cost: PASS` | ||
| - Case 3: Baseline rotations are not expected based on the fenestration areas by azimuth and the baseline building performance does not match the total source results annual cost from the BASELINE_0 RMD: FAIL: `elif !rotation_expected_b and bbp != baseline_0_total_annual_cost: FAIL` | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same question here, would it be incorrect if a RPD GS is writing 4 baseline RPDs - to be exactly the same?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same response! if the values are the same it is not a problem |
||
| - Case 4: Baseline rotations are expected based on the fenestration areas by azimuth and the baseline building performance does not match either the average of the total source results annual cost from the baseline RMDs or the BASELINE_0 RMD: FAIL: `elif rotation_expected_b and bbp != avg_baseline_total_annual_cost and bbp != baseline_0_total_annual_cost: FAIL` | ||
| - Case 5: Baseline rotations are expected based on the fenestration areas by azimuth and the baseline building performance matches the total source results annual cost from the BASELINE_0 RMD: UNDETERMINED "Perform a manual check to verify whether Table G3.1#5a exception #2 is applicable, and it can be demonstrated that the building orientation is dictated by site considerations.": `elif rotation_expected_b and bbp == baseline_0_total_annual_cost: UNDETERMINED raise_message "Perform a manual check to verify whether Table G3.1#5a exception #2 is applicable, and it can be demonstrated that the building orientation is dictated by site considerations."` | ||
|
|
||
|
|
||
| **Notes/Questions:** | ||
| None | ||
| 1. BBUEC, BBREC should probably also be the averages of all present baseline RMDs sums of regulated and unregulated end use costs, however we cannot determine end use costs from the schema | ||
| 2. the language in 90.1 only includes the defined term "baseline building performance" in the direction to average results from different orientations, it does not include BBUEC and BBREC so maybe #1 is not an issue | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am okay with how you have done it based on the exact language and limitations. |
||
|
|
||
|
|
||
| **[Back](_toc.md)** | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we should add a note to the schema about this. It is not necessarily intuituve that this should be identical for all the RPD files.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@claperle and @JacksonJ-KC if I understand correctly, the code in line 59 is checking for the presence of 1 baseline RPD- could it be the case that a RPD GS generates 4 baseline RPDs which are exactly the same?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@supriyagoel, yes this would be allowed. A python "set" only keeps track of unique values so the length would be 1 if the values are all the same.