diff --git a/docs/ashrae_90p1_2019/section6/Rule6-5.md b/docs/ashrae_90p1_2019/section6/Rule6-5.md index f7afe6c8a2..edae8a2404 100644 --- a/docs/ashrae_90p1_2019/section6/Rule6-5.md +++ b/docs/ashrae_90p1_2019/section6/Rule6-5.md @@ -6,9 +6,8 @@ **Appendix G Section:** Section G3.1-6 Modeling Requirements for the Baseline building **Appendix G Section Reference:** None -**Applicability:** All required data elements exist for B_RMR -**Applicability Checks:** - +**Applicability:** All required data elements exist for B_RMD +**Applicability Checks:** 1. Building total area is more than 5,000sq.ft. **Manual Check:** Yes @@ -30,24 +29,30 @@ - **Applicability Check 1:**`if building_total_area_b > 5000:` - For each space in building_b: `space_b in building_b...spaces:` + - Get total lighting power density in space: `total_space_LPD_b = sum(interior_lighting.power_per_area for interior_lighting in space_b.interior_lighting)` + + - Skip evaluating plenums, crawlspaces, and interstitial spaces that have no lighting power: `if total_space_LPD_b ==0 and space_b.function in [PLENUM, CRAWL_SPACE, INTERSTITIAL_SPACE]: continue` - Get normalized space lighting schedule: `normalized_schedule_b = normalize_interior_lighting_schedules(space_b.interior_lighting, false)` - - Get matching space in P_RMR: `space_p = match_data_element(P_RMR, Spaces, space_b.id)` + - Get matching space in P_RMD: `space_p = match_data_element(P_RMD, Spaces, space_b.id)` - - Get normalized space lighting schedule in P_RMR: `normalized_schedule_p = normalize_interior_lighting_schedules(space_p.interior_lighting, false)` + - Get normalized space lighting schedule in P_RMD: `normalized_schedule_p = normalize_interior_lighting_schedules(space_p.interior_lighting, false)` - - Check if automatic shutoff control is modeled in space during building closed hours (i.e. if lighting schedule hourly value in B_RMR is equal to P_RMR during building closed hours): `schedule_comparison_result = compare_schedules(normalized_schedule_b, normalized_schedule_p, inverse(building_open_schedule_b))` + - Check if automatic shutoff control is modeled in space during building closed hours (i.e. if lighting schedule hourly value in B_RMD is equal to P_RMD during building closed hours): `schedule_comparison_result = compare_schedules(normalized_schedule_b, normalized_schedule_p, inverse(building_open_schedule_b))` **Rule Assertion:** - - Case 1: For building closed hours, if lighting schedule hourly value in B_RMR is equal to P_RMR: `if schedule_comparison_result["total_hours_compared"] == schedule_comparison_result["total_hours_matched"]: PASS` + - Case 1: If space has lighting power and is crawl space, interstitial space, or plenum: UNDETERMINED `if ( total_space_LPD_b > 0 ) AND ( space_function_b in [CRAWL_SPACE, INTERSTITIAL_SPACE, PLENUM] ): UNDETERMINED and raise_warning f"Space function is {space_function_b} and has lighting power modeled. Unable to determine whether this space should be included in the check."` + + - Case 2: For building closed hours, if lighting schedule hourly value in B_RMD is equal to P_RMD: `if schedule_comparison_result["total_hours_compared"] == schedule_comparison_result["total_hours_matched"]: PASS` - - Case 2: Else: `else: Failed` + - Case 3: Else: `else: Failed` **Notes:** 1. Updated the Rule ID from 6-9 to 6-6 on 6/3/2022 - 2. Updated the Rule ID from 6-6 to 6-5 on 6/8/2022 + 2. Updated the Rule ID from 6-6 to 6-5 on 6/8/2022 + 3. Updated to exclude crawl space, plenum or interstitial space on 9/29/2025. **[Back](../_toc.md)** diff --git a/rct229/rulesets/ashrae9012019/section6/section6rule5.py b/rct229/rulesets/ashrae9012019/section6/section6rule5.py index e1eb15166f..16a4e1d8bf 100644 --- a/rct229/rulesets/ashrae9012019/section6/section6rule5.py +++ b/rct229/rulesets/ashrae9012019/section6/section6rule5.py @@ -16,8 +16,21 @@ from rct229.utils.jsonpath_utils import find_all, find_exactly_one_with_field_value from rct229.utils.masks import invert_mask from rct229.utils.pint_utils import ZERO +from rct229.schema.schema_enums import SchemaEnums +SpaceFunctionOptions = SchemaEnums.schema_enums["SpaceFunctionOptions"] + +NA_SPACE_FUNCTIONS = [ + SpaceFunctionOptions.PLENUM, + SpaceFunctionOptions.CRAWL_SPACE, + SpaceFunctionOptions.INTERSTITIAL_SPACE, +] BUILDING_AREA_CUTTOFF = ureg("5000 ft2") +NA_SPACE_MSG = ( + "Space is a crawl space, plenum, interstitial space, or an unoccupiable " + "space type with lighting power modeled. Unable to determine whether this " + "space should be included in the check." +) class PRM9012019Rule08a45(RuleDefinitionListIndexedBase): @@ -110,6 +123,27 @@ def create_data(self, context, data=None): "avg_zone_height_p": get_avg_zone_height(zone_p), } + def list_filter(self, context_item, data): + space_b = context_item.BASELINE_0 + + total_space_lpd_b = sum( + find_all("$.interior_lighting[*].power_per_area", space_b), + ZERO.POWER_PER_AREA, + ) + + space_function_b = space_b.get("function") + lighting_space_type_b = space_b.get("lighting_space_type") + + is_na_or_none = ( + space_function_b in NA_SPACE_FUNCTIONS + or lighting_space_type_b == "NONE" + ) + + # Exclude ONLY NA or NONE spaces with zero lighting + return not ( + is_na_or_none and total_space_lpd_b == ZERO.POWER_PER_AREA + ) + class SpaceRule(RuleDefinitionBase): def __init__(self): super( @@ -119,6 +153,7 @@ def __init__(self): rmds_used=produce_ruleset_model_description( USER=False, BASELINE_0=True, PROPOSED=True ), + manual_check_required_msg=NA_SPACE_MSG, ) def is_applicable(self, context, data=None): @@ -176,6 +211,23 @@ def get_calc_vals(self, context, data=None): "schedule_comparison_result": schedule_comparison_result } + def manual_check_required(self, context, calc_vals=None, data=None): + space_b = context.BASELINE_0 + + total_space_lpd_b = sum( + find_all("$.interior_lighting[*].power_per_area", space_b), + ZERO.POWER_PER_AREA, + ) + + space_function_b = space_b.get("function") + lighting_space_type_b = space_b.get("lighting_space_type") + + # NA or NONE space WITH lighting + return ( + space_function_b in NA_SPACE_FUNCTIONS + or lighting_space_type_b == "NONE" + ) and total_space_lpd_b > ZERO.POWER_PER_AREA + def rule_check(self, context, calc_vals=None, data=None): schedule_comparison_result = calc_vals[ "schedule_comparison_result"