diff --git a/rct229/rulesets/ashrae9012022/data/ashrae_90_1_lighting_to_hvac_map.json b/rct229/rulesets/ashrae9012022/data/ashrae_90_1_lighting_to_hvac_map.json new file mode 100644 index 0000000000..22a19e8c7c --- /dev/null +++ b/rct229/rulesets/ashrae9012022/data/ashrae_90_1_lighting_to_hvac_map.json @@ -0,0 +1,35 @@ +{ + "AUTOMOTIVE_FACILITY": "OTHER_NON_RESIDENTIAL", + "CONVENTION_CENTER": "PUBLIC_ASSEMBLY", + "COURTHOUSE": "OTHER_NON_RESIDENTIAL", + "DINING_BAR_LOUNGE_LEISURE": "OTHER_NON_RESIDENTIAL", + "DINING_CAFETERIA_FAST_FOOD": "OTHER_NON_RESIDENTIAL", + "DINING_FAMILY": "OTHER_NON_RESIDENTIAL", + "DORMITORY": "RESIDENTIAL", + "EXERCISE_CENTER": "OTHER_NON_RESIDENTIAL", + "FIRE_STATION": "OTHER_NON_RESIDENTIAL", + "GYMNASIUM": "PUBLIC_ASSEMBLY", + "HEALTH_CARE_CLINIC": "HOSPITAL", + "HOSPITAL": "HOSPITAL", + "HOTEL_MOTEL": "RESIDENTIAL", + "LIBRARY": "OTHER_NON_RESIDENTIAL", + "MANUFACTURING_FACILITY": "OTHER_NON_RESIDENTIAL", + "MOTION_PICTURE_THEATER": "PUBLIC_ASSEMBLY", + "MULTIFAMILY": "RESIDENTIAL", + "MUSEUM": "PUBLIC_ASSEMBLY", + "OFFICE": "OTHER_NON_RESIDENTIAL", + "PARKING_GARAGE": "HEATED-ONLY_STORAGE", + "PENITENTIARY": "OTHER_NON_RESIDENTIAL", + "PERFORMING_ARTS_THEATER": "PUBLIC_ASSEMBLY", + "POLICE_STATION": "OTHER_NON_RESIDENTIAL", + "POST_OFFICE": "OTHER_NON_RESIDENTIAL", + "RELIGIOUS_FACILITY": "PUBLIC_ASSEMBLY", + "RETAIL": "RETAIL", + "SCHOOL_UNIVERSITY": "OTHER_NON_RESIDENTIAL", + "SPORTS_ARENA": "PUBLIC_ASSEMBLY", + "TOWN_HALL": "OTHER_NON_RESIDENTIAL", + "TRANSPORTATION": "OTHER_NON_RESIDENTIAL", + "WAREHOUSE": "HEATED-ONLY_STORAGE", + "WORKSHOP": "OTHER_NON_RESIDENTIAL", + "NONE": "NONE" +} \ No newline at end of file diff --git a/rct229/rulesets/ashrae9012022/data/ashrae_90_1_space_lighting_to_hvac_map.json b/rct229/rulesets/ashrae9012022/data/ashrae_90_1_space_lighting_to_hvac_map.json new file mode 100644 index 0000000000..bfe20523ee --- /dev/null +++ b/rct229/rulesets/ashrae9012022/data/ashrae_90_1_space_lighting_to_hvac_map.json @@ -0,0 +1,104 @@ +{ + "ATRIUM_LOW_MEDIUM": "OTHER_UNDETERMINED", + "ATRIUM_HIGH": "OTHER_UNDETERMINED", + "AUDIENCE_SEATING_AREA_AUDITORIUM": "PUBLIC_ASSEMBLY", + "AUDIENCE_SEATING_AREA_EXERCISE_CENTER": "OTHER_NON_RESIDENTIAL", + "AUDIENCE_SEATING_AREA_GYMNASIUM": "PUBLIC_ASSEMBLY", + "AUDIENCE_SEATING_AREA_MOTION_PICTURE_THEATER": "PUBLIC_ASSEMBLY", + "AUDIENCE_SEATING_AREA_PENITENTIARY": "OTHER_NON_RESIDENTIAL", + "AUDIENCE_SEATING_AREA_PERFORMING_ARTS_THEATER": "PUBLIC_ASSEMBLY", + "AUDIENCE_SEATING_AREA_RELIGIOUS_FACILITY": "PUBLIC_ASSEMBLY", + "AUDIENCE_SEATING_AREA_SPORTS_ARENA": "PUBLIC_ASSEMBLY", + "AUDIENCE_SEATING_AREA_TRANSPORTATION_FACILITY": "OTHER_NON_RESIDENTIAL", + "AUDIENCE_SEATING_AREA_ALL_OTHER": "PUBLIC_ASSEMBLY", + "AUDIENCE_SEATING_AREA_CONVENTION_CENTER": "PUBLIC_ASSEMBLY", + "BANKING_ACTIVITY_AREA": "OTHER_NON_RESIDENTIAL", + "CLASSROOM_LECTURE_HALL_TRAINING_ROOM_PENITENTIARY": "OTHER_NON_RESIDENTIAL", + "CLASSROOM_LECTURE_HALL_TRAINING_ROOM_SCHOOL": "OTHER_NON_RESIDENTIAL", + "CLASSROOM_LECTURE_HALL_TRAINING_ROOM_ALL_OTHER": "OTHER_NON_RESIDENTIAL", + "CONFERENCE_MEETING_MULTIPURPOSE_ROOM": "OTHER_NON_RESIDENTIAL", + "CONFINEMENT_CELLS": "OTHER_NON_RESIDENTIAL", + "COPY_PRINT_ROOM": "OTHER_UNDETERMINED", + "CORRIDOR_FACILITY_FOR_THE_VISUALLY_IMPAIRED": "OTHER_UNDETERMINED", + "CORRIDOR_HOSPITAL": "HOSPITAL", + "CORRIDOR_MANUFACTURING_FACILITY": "OTHER_NON_RESIDENTIAL", + "CORRIDOR_ALL_OTHERS": "OTHER_UNDETERMINED", + "COURT_ROOM": "OTHER_NON_RESIDENTIAL", + "COMPUTER_ROOM": "OTHER_UNDETERMINED", + "DINING_AREA_PENITENTIARY": "OTHER_NON_RESIDENTIAL", + "DINING_AREA_FACILITY_FOR_THE_VISUALLY_IMPAIRED": "OTHER_NON_RESIDENTIAL", + "DINING_AREA_BAR_LOUNGE_OR_LEISURE_DINING": "OTHER_NON_RESIDENTIAL", + "DINING_AREA_CAFETERIA_OR_FAST_FOOD_DINING": "OTHER_NON_RESIDENTIAL", + "DINING_AREA_FAMILY_DINING": "OTHER_NON_RESIDENTIAL", + "DINING_AREA_ALL_OTHERS": "OTHER_NON_RESIDENTIAL", + "ELECTRICAL_MECHANICAL_ROOM": "OTHER_UNDETERMINED", + "EMERGENCY_VEHICLE_GARAGE": "HEATED-ONLY_STORAGE", + "FOOD_PREPARATION_AREA": "OTHER_NON_RESIDENTIAL", + "GUEST_ROOM": "RESIDENTIAL", + "JUDGES_CHAMBERS": "OTHER_NON_RESIDENTIAL", + "DWELLING_UNIT": "RESIDENTIAL", + "LABORATORY_EXCEPT_IN_OR_AS_A_CLASSROOM": "OTHER_NON_RESIDENTIAL", + "LAUNDRY_WASHING_AREA": "OTHER_NON_RESIDENTIAL", + "LOADING_DOCK_INTERIOR": "HEATED-ONLY_STORAGE", + "LOBBY_FACILITY_FOR_THE_VISUALLY_IMPAIRED": "OTHER_NON_RESIDENTIAL", + "LOBBY_ELEVATOR": "OTHER_UNDETERMINED", + "LOBBY_HOTEL": "OTHER_NON_RESIDENTIAL", + "LOBBY_MOTION_PICTURE_THEATER": "PUBLIC_ASSEMBLY", + "LOBBY_PERFORMING_ARTS_THEATER": "PUBLIC_ASSEMBLY", + "LOBBY_ALL_OTHERS": "OTHER_UNDETERMINED", + "LOCKER_ROOM": "OTHER_UNDETERMINED", + "LOUNGE_BREAKROOM_HEALTH_CARE_FACILITY": "HOSPITAL", + "LOUNGE_BREAKROOM_ALL_OTHERS": "OTHER_NON_RESIDENTIAL", + "OFFICE_ENCLOSED": "OTHER_NON_RESIDENTIAL", + "OFFICE_OPEN_PLAN": "OTHER_NON_RESIDENTIAL", + "PARKING_AREA_INTERIOR": "HEATED-ONLY_STORAGE", + "PHARMACY_AREA": "OTHER_NON_RESIDENTIAL", + "RESTROOM_FACILITY_FOR_THE_VISUALLY_IMPAIRED": "OTHER_UNDETERMINED", + "RESTROOM_ALL_OTHERS": "OTHER_UNDETERMINED", + "SALES_AREA": "RETAIL", + "SEATING_AREA_GENERAL": "PUBLIC_ASSEMBLY", + "STAIRWELL": "OTHER_UNDETERMINED", + "STORAGE_ROOM_HOSPITAL": "HOSPITAL", + "STORAGE_ROOM_SMALL": "OTHER_UNDETERMINED", + "STORAGE_ROOM_LARGE": "OTHER_UNDETERMINED", + "VEHICULAR_MAINTENANCE_AREA": "HEATED_ONLY_STORAGE", + "ASSISTED_LIVING_FACILITY_CHAPEL": "PUBLIC_ASSEMBLY", + "ASSISTED_LIVING_FACILITY_RECREATION_ROOM_COMMON_LIVING_ROOM": "OTHER_NON_RESIDENTIAL", + "CONVENTION_CENTER_EXHIBIT_SPACE": "PUBLIC_ASSEMBLY", + "DORMITORY_LIVING_QUARTERS": "RESIDENTIAL", + "FIRE_STATION_SLEEPING_QUARTERS": "RESIDENTIAL", + "GYMNASIUM_FITNESS_CENTER_EXERCISE_AREA": "PUBLIC_ASSEMBLY", + "GYMNASIUM_FITNESS_CENTER_PLAYING_AREA": "PUBLIC_ASSEMBLY", + "HEALTHCARE_FACILITY_EMERGENCY_ROOM": "HOSPITAL", + "HEALTHCARE_FACILITY_EXAM_TREATMENT_ROOM": "HOSPITAL", + "HEALTHCARE_FACILITY_NURSERY": "HOSPITAL", + "HEALTHCARE_FACILITY_NURSES_STATION": "HOSPITAL", + "HEALTHCARE_FACILITY_OPERATING_ROOM": "HOSPITAL", + "HEALTHCARE_FACILITY_PATIENT_ROOM": "HOSPITAL", + "HEALTHCARE_FACILITY_PHYSICAL_THERAPY_ROOM": "HOSPITAL", + "HEALTHCARE_FACILITY_RECOVERY_ROOM": "HOSPITAL", + "LIBRARY_READING_AREA": "OTHER_NON_RESIDENTIAL", + "LIBRARY_STACKS": "OTHER_NON_RESIDENTIAL", + "MANUFACTURING_FACILITY_DETAILED_MANUFACTURING_AREA": "OTHER_NON_RESIDENTIAL", + "MANUFACTURING_FACILITY_EQUIPMENTROOM": "OTHER_NON_RESIDENTIAL", + "MANUFACTURING_FACILITY_EXTRA_HIGH_BAY_AREA": "OTHER_NON_RESIDENTIAL", + "MANUFACTURING_FACILITY_HIGH_BAY_AREA": "OTHER_NON_RESIDENTIAL", + "MANUFACTURING_FACILITY_LOW_BAY_AREA": "OTHER_NON_RESIDENTIAL", + "MUSEUM_GENERAL_EXHIBITION_AREA": "PUBLIC_ASSEMBLY", + "MUSEUM_RESTORATION_ROOM": "PUBLIC_ASSEMBLY", + "POST_OFFICE_SORTING_AREA": "OTHER_NON_RESIDENTIAL", + "RELIGIOUS_FACILITY_FELLOWSHIP_HALL": "PUBLIC_ASSEMBLY", + "RELIGIOUS_FACILITY_WORSHIP_PULPIT_CHOIR_AREA": "PUBLIC_ASSEMBLY", + "RETAIL_FACILITIES_DRESSING_FITTING_ROOM": "RETAIL", + "RETAIL_FACILITIES_MALL_CONCOURSE": "RETAIL", + "SPORTS_ARENA_PLAYING_AREA_CLASS_I_FACILITY": "PUBLIC_ASSEMBLY", + "SPORTS_ARENA_PLAYING_AREA_CLASS_II_FACILITY": "PUBLIC_ASSEMBLY", + "SPORTS_ARENA_PLAYING_AREA_CLASS_III_FACILITY": "PUBLIC_ASSEMBLY", + "SPORTS_ARENA_PLAYING_AREA_CLASS_IV_FACILITY": "PUBLIC_ASSEMBLY", + "TRANSPORTATION_FACILITY_BAGGAGE_CAROUSEL_AREA": "OTHER_NON_RESIDENTIAL", + "TRANSPORTATION_FACILITY_AIRPORT_CONCOURSE": "OTHER_NON_RESIDENTIAL", + "TRANSPORTATION_FACILITY_TICKET_COUNTER": "OTHER_NON_RESIDENTIAL", + "WAREHOUSE_STORAGE_AREA_MEDIUM_TO_BULKY_PALLETIZED_ITEMS": "HEATED_ONLY_STORAGE", + "WAREHOUSE_STORAGE_AREA_SMALLER_HAND_CARRIED_ITEMS": "HEATED_ONLY_STORAGE", + "NONE": "NONE" +} \ No newline at end of file diff --git a/rct229/rulesets/ashrae9012022/data_fns/table_lighting_to_hvac_bat_map_fns.py b/rct229/rulesets/ashrae9012022/data_fns/table_lighting_to_hvac_bat_map_fns.py new file mode 100644 index 0000000000..5b1d3fa358 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/data_fns/table_lighting_to_hvac_bat_map_fns.py @@ -0,0 +1,20 @@ +from rct229.rulesets.ashrae9012022.data import data +from rct229.utils.assertions import assert_ + + +def building_lighting_to_hvac_bat(lighting_bat): + bat_conversion_table = data["ashrae_90_1_lighting_to_hvac_map"] + assert_( + lighting_bat in bat_conversion_table, + f"Lighting area type {lighting_bat} does not exist in ashrae_90_1_lighting_to_hvac_map", + ) + return bat_conversion_table[lighting_bat] + + +def space_lighting_to_hvac_bat(lighting_bat): + bat_conversion_table = data["ashrae_90_1_space_lighting_to_hvac_map"] + assert_( + lighting_bat in bat_conversion_table, + f"Lighting area type {lighting_bat} does not exist in ashrae_90_1_lighting_to_hvac_map", + ) + return bat_conversion_table[lighting_bat] diff --git a/rct229/rulesets/ashrae9012022/data_fns/table_lighting_to_hvac_bat_map_fns_test.py b/rct229/rulesets/ashrae9012022/data_fns/table_lighting_to_hvac_bat_map_fns_test.py new file mode 100644 index 0000000000..f7b99a2cba --- /dev/null +++ b/rct229/rulesets/ashrae9012022/data_fns/table_lighting_to_hvac_bat_map_fns_test.py @@ -0,0 +1,12 @@ +from rct229.rulesets.ashrae9012022.data_fns.table_lighting_to_hvac_bat_map_fns import ( + building_lighting_to_hvac_bat, + space_lighting_to_hvac_bat, +) + + +def test__lighting_to_hvac_bat__DORMITORY(): + assert building_lighting_to_hvac_bat("DORMITORY") == "RESIDENTIAL" + + +def test__space_lighting_to_hvac_bat__ATRIUM_HIGH(): + assert space_lighting_to_hvac_bat("ATRIUM_HIGH") == "OTHER_UNDETERMINED" diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_system_type_compare.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_system_type_compare.py new file mode 100644 index 0000000000..a33c0295d5 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_system_type_compare.py @@ -0,0 +1,58 @@ +from typing import Optional, Type + +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, + HVAC_SYSTEM_TYPE_DICTIONARY, +) +from rct229.utils.assertions import assert_ + + +def baseline_system_type_compare( + system_type: HVAC_SYS, + target_system_type: HVAC_SYS, + exact_match: Optional[bool] = True, +) -> bool: + """ + Parameters + ---------- + system_type: String the enum indicating the hvac system type example: SYS_5 or SYS_8c. + This will usually be the value given by the function get_baseline_system_types + A None is acceptable and the function will raise RCTException which result in UNDETERMINED outcome. + + target_system_type: String the enum indicating the target system type, will usually be SYS_# (primary baseline + system types) without a further number or letter modifier. + + exact_match: Bool TRUE or FALSE. if exact_match is TRUE, then system_type must match exactly the enum given + by target_system_type. If false, an approximate match will return true. This would be used in the case where + the user wants any hvac system ot type 8, without having to type in 8a, 8b, 8c. In this case, SYS_8 would be + passed to the function and the function would return TRUE for any system 8, regardless of it's 8a, 8b, or 8c + + Returns + ------- + True or False, indicating whether the hvac system type matches the hvac_system_type. + """ + + # A list of the available target system + available_target_system_list = HVAC_SYSTEM_TYPE_DICTIONARY + available_system_types = [ + system + for key in HVAC_SYSTEM_TYPE_DICTIONARY + for system in HVAC_SYSTEM_TYPE_DICTIONARY[key] + ] + available_system_types.extend(available_target_system_list) + + assert_( + system_type in available_system_types, + f"{system_type} does not match any baseline HVAC system type", + ) + assert_( + target_system_type in available_target_system_list, + f"{target_system_type} does not match any primary " + f"baseline HVAC system type", + ) + + return ( + system_type == target_system_type + if exact_match + else system_type in HVAC_SYSTEM_TYPE_DICTIONARY[target_system_type] + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/__init__.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/__init__.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_chw_loops_purcahsed_cooling.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_chw_loops_purcahsed_cooling.py new file mode 100644 index 0000000000..2ced7bded1 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_chw_loops_purcahsed_cooling.py @@ -0,0 +1,49 @@ +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.assertions import getattr_ +from rct229.utils.jsonpath_utils import find_all +from rct229.utils.utility_functions import ( + find_exactly_one_fluid_loop, + find_exactly_one_terminal_unit, +) + +EXTERNAL_FLUID_SOURCE = SchemaEnums.schema_enums["ExternalFluidSourceOptions"] +FLUID_LOOP = SchemaEnums.schema_enums["FluidLoopOptions"] + + +def are_all_terminal_chw_loops_purchased_cooling(rmd_b, terminal_unit_id_list): + """Returns TRUE if the fluid loop associated with the cooling_from_loop associated with each terminal unit is purchased CHW. Returns FALSE if this is not the case. + ---------- + rmd_b : json + RMD at RuleSetModelDescription level + terminal_unit_id_list : list + List of terminal units IDs + Returns + ------- + bool + True: the fluid loop associated with the cooling_from_loop associated with each terminal unit is purchased CHW. + False: otherwise + """ + are_all_terminal_chw_loops_purchased_cooling_flag = True + + # get the list of loop ids if the external fluid source matches to either hot water or steam + purchased_cooling_loop_id_list_b = find_all( + f'external_fluid_sources[*][?(@.type="{EXTERNAL_FLUID_SOURCE.CHILLED_WATER}")].loop', + rmd_b, + ) + + for terminal_b_id in terminal_unit_id_list: + terminal_b = find_exactly_one_terminal_unit(rmd_b, terminal_b_id) + cooling_from_loop_id = terminal_b.get("cooling_from_loop") + if cooling_from_loop_id: + fluid_loop = find_exactly_one_fluid_loop(rmd_b, cooling_from_loop_id) + if ( + getattr_(fluid_loop, "fluid loop", "type") != FLUID_LOOP.COOLING + or cooling_from_loop_id not in purchased_cooling_loop_id_list_b + ): + are_all_terminal_chw_loops_purchased_cooling_flag = False + break + else: + are_all_terminal_chw_loops_purchased_cooling_flag = False + break + + return are_all_terminal_chw_loops_purchased_cooling_flag diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_cool_sources_chilled_water.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_cool_sources_chilled_water.py new file mode 100644 index 0000000000..34cfa99a21 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_cool_sources_chilled_water.py @@ -0,0 +1,25 @@ +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.utility_functions import find_exactly_one_terminal_unit + +COOLING_SOURCE = SchemaEnums.schema_enums["CoolingSourceOptions"] + + +def are_all_terminal_cool_sources_chilled_water(rmd_b, terminal_unit_id_list): + """Returns TRUE if the cool source associated with all terminal units is CHILLED_WATER. It returns FALSE if any terminal unit has a cool source other than CHILLED_WATER. + ---------- + rmd_b : json + RMD at RuleSetModelDescription level + terminal_unit_id_list : list + List of terminal units IDs + Returns + ------- + bool + True: cool source associated with all terminal units sent to this function is CHILLED_WATER + False: any terminal unit has a cool source other than CHILLED_WATER + """ + # all terminal cool sources should be chilled water, false otherwise + return all( + find_exactly_one_terminal_unit(rmd_b, terminal_b_id).get("cooling_source") + == COOLING_SOURCE.CHILLED_WATER + for terminal_b_id in terminal_unit_id_list + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_cool_sources_none_or_null.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_cool_sources_none_or_null.py new file mode 100644 index 0000000000..026467b434 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_cool_sources_none_or_null.py @@ -0,0 +1,27 @@ +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.utility_functions import find_exactly_one_terminal_unit + +COOLING_SOURCE = SchemaEnums.schema_enums["CoolingSourceOptions"] + + +def are_all_terminal_cool_sources_none_or_null(rmd_b, terminal_unit_id_list): + """Returns TRUE if the cool source associated with all terminal units input to this function are None or Null. It returns FALSE if any terminal unit has a cool source other than None or Null. + ---------- + rmd_b : json + RMD at RuleSetModelDescription level + terminal_unit_id_list : list + List of terminal units IDs + Returns + ------- + bool + True: the cool source associated with all terminal units input to this function are None or Null + False: any terminal unit has a cool source other than None or Null + """ + are_all_terminal_cool_sources_none_or_null_flag = True + for terminal_b_id in terminal_unit_id_list: + terminal_b = find_exactly_one_terminal_unit(rmd_b, terminal_b_id) + if terminal_b.get("cooling_source") not in [None, COOLING_SOURCE.NONE]: + are_all_terminal_cool_sources_none_or_null_flag = False + break + + return are_all_terminal_cool_sources_none_or_null_flag diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_fan_configs_parallel.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_fan_configs_parallel.py new file mode 100644 index 0000000000..b0f5796c4e --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_fan_configs_parallel.py @@ -0,0 +1,27 @@ +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.utility_functions import find_exactly_one_terminal_unit + +TERMINAL_FAN_CONFIGURATION = SchemaEnums.schema_enums["TerminalFanConfigurationOptions"] + + +def are_all_terminal_fan_configs_parallel(rmd_b, terminal_unit_id_list): + """Returns TRUE if the fan configuration associated with all terminal units input to this function are parallel. + It returns FALSE if any terminal unit has a fan configuration other than parallel. + + ---------- + rmd_b : json + RMD at RuleSetModelDescription level + terminal_unit_id_list : list + List of terminal units IDs + Returns + ------- + bool + True: fan configuration associated with all terminal units input to this function are parallel + False: any terminal unit has a fan configuration other than parallel + """ + # all terminal's fan configuration should be parallel, and false otherwise. + return all( + find_exactly_one_terminal_unit(rmd_b, terminal_b_id).get("fan_configuration") + == TERMINAL_FAN_CONFIGURATION.PARALLEL + for terminal_b_id in terminal_unit_id_list + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_fans_null.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_fans_null.py new file mode 100644 index 0000000000..eafebe6941 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_fans_null.py @@ -0,0 +1,24 @@ +from rct229.utils.utility_functions import find_exactly_one_terminal_unit + + +def are_all_terminal_fans_null(rmd_b, terminal_unit_id_list): + """Returns TRUE if the fan data element associated with all terminal units input to this function are equal to Null. It returns FALSE if any terminal unit has a fan data element not equal to Null. + ---------- + rmd_b : json + RMD at RuleSetModelDescription level + terminal_unit_id_list : list + List of terminal units IDs + Returns + ------- + bool + True: fan data element associated with all terminal units input to this function are equal to Null + False: any terminal unit has a fan data element not equal to Null. + """ + are_all_terminal_fans_null_flag = True + for terminal_b_id in terminal_unit_id_list: + terminal_b = find_exactly_one_terminal_unit(rmd_b, terminal_b_id) + if terminal_b.get("fan") is not None: + are_all_terminal_fans_null_flag = False + break + + return are_all_terminal_fans_null_flag diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_heat_sources_electric.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_heat_sources_electric.py new file mode 100644 index 0000000000..fe56307e13 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_heat_sources_electric.py @@ -0,0 +1,28 @@ +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.utility_functions import find_exactly_one_terminal_unit + +HEATING_SOURCE = SchemaEnums.schema_enums["HeatingSourceOptions"] + + +def are_all_terminal_heat_sources_electric(rmd_b, terminal_unit_id_list): + """Returns TRUE if the heat source associated with all terminal units input to this function are electric. It + returns FALSE if any terminal unit has a heat source other than electric. + + Parameters + ---------- + rmd_b : json + RMD at RuleSetModelDescription level + terminal_unit_id_list : list + List of terminal units IDs + + Returns + ------- + bool + True: heat source associated with all terminal units input to this function are electric + False: any terminal unit has a heat source other than electric + """ + return all( + find_exactly_one_terminal_unit(rmd_b, terminal_b_id).get("heating_source") + == HEATING_SOURCE.ELECTRIC + for terminal_b_id in terminal_unit_id_list + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_heat_sources_hot_water.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_heat_sources_hot_water.py new file mode 100644 index 0000000000..0633669039 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_heat_sources_hot_water.py @@ -0,0 +1,34 @@ +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.utility_functions import find_exactly_one_terminal_unit + +HEATING_SOURCE = SchemaEnums.schema_enums["HeatingSourceOptions"] + + +def are_all_terminal_heat_sources_hot_water(rmd_b, terminal_unit_id_list): + """Returns TRUE if the heat source associated with all terminal units input to this function is HOT_WATER. It returns FALSE if any terminal unit has a heat source other than HOT_WATER. + + Parameters + ---------- + rmd_b : json + RMD at RuleSetModelDescription level + terminal_unit_id_list : list + List of terminal units IDs + + Returns + ------- + bool + True: the heat source associated with all terminal units input to this function is HOT_WATER + False: any terminal unit has a heat source other than HOT_WATER. + """ + are_all_terminal_heat_sources_hot_water_flag = True + for terminal_b_id in terminal_unit_id_list: + terminal_b = find_exactly_one_terminal_unit(rmd_b, terminal_b_id) + if terminal_b.get("heating_source") != HEATING_SOURCE.HOT_WATER: + are_all_terminal_heat_sources_hot_water_flag = False + break + + return all( + find_exactly_one_terminal_unit(rmd_b, terminal_b_id).get("heating_source") + == HEATING_SOURCE.HOT_WATER + for terminal_b_id in terminal_unit_id_list + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_heat_sources_none_or_null.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_heat_sources_none_or_null.py new file mode 100644 index 0000000000..99b121bcc6 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_heat_sources_none_or_null.py @@ -0,0 +1,30 @@ +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.utility_functions import find_exactly_one_terminal_unit + +HEATING_SOURCE = SchemaEnums.schema_enums["HeatingSourceOptions"] + + +def are_all_terminal_heat_sources_none_or_null(rmd_b, terminal_unit_id_list): + """Returns TRUE if the heat source associated with all terminal units input to this function are None or Null. It returns FALSE if any terminal unit has a heat source other than None or Null. + + Parameters + ---------- + rmd_b : json + RMD at RuleSetModelDescription level + terminal_unit_id_list : list + List of terminal units IDs + + Returns + ------- + bool + True: the heat source associated with all terminal units input to this function are None or Null + False: any terminal unit has a heat source other than None or Null + """ + are_all_terminal_heat_sources_none_or_null_flag = True + for terminal_b_id in terminal_unit_id_list: + terminal_b = find_exactly_one_terminal_unit(rmd_b, terminal_b_id) + if terminal_b.get("heating_source") not in [None, HEATING_SOURCE.NONE]: + are_all_terminal_heat_sources_none_or_null_flag = False + break + + return are_all_terminal_heat_sources_none_or_null_flag diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_heating_loops_attached_to_boiler.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_heating_loops_attached_to_boiler.py new file mode 100644 index 0000000000..369c27cc6a --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_heating_loops_attached_to_boiler.py @@ -0,0 +1,50 @@ +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.utility_functions import ( + find_exactly_one_fluid_loop, + find_exactly_one_terminal_unit, +) + +FLUID_LOOP_TYPE = SchemaEnums.schema_enums["FluidLoopOptions"] + + +def are_all_terminal_heating_loops_attached_to_boiler(rmd_b, terminal_unit_id_list): + """Returns TRUE if the fluid loop associated with the heating_from_loop associated with each terminal unit is attached to a boiler. Returns FALSE if this is not the case. + + Parameters + ---------- + rmd_b : json + RMD at RuleSetModelDescription level + terminal_unit_id_list : list + List of terminal units IDs + + Returns + ------- + bool + True: fluid loop associated with the heating_from_loop associated with each terminal unit is attached to a boiler + False: otherwise + """ + are_all_terminal_heating_loops_attached_to_boiler_flag = True + loop_boiler_id_list = [ + getattr_(boiler_b, "boiler", "loop") + for boiler_b in find_all("$.boilers[*]", rmd_b) + ] + + for terminal_b_id in terminal_unit_id_list: + terminal_b = find_exactly_one_terminal_unit(rmd_b, terminal_b_id) + heating_from_loop_id = terminal_b.get("heating_from_loop") + # cond 1 allows heating_from_loop_id to be None, cond 2 allows fluid_loop to be None. + if ( + heating_from_loop_id not in loop_boiler_id_list + # return None if terminal_b has no heating_from_loop field, or + # the heating_from_loop id cannot be found in fluid_loops + or find_one( + "type", find_exactly_one_fluid_loop(rmd_b, heating_from_loop_id) + ) + != FLUID_LOOP_TYPE.HEATING + ): + are_all_terminal_heating_loops_attached_to_boiler_flag = False + break + + return are_all_terminal_heating_loops_attached_to_boiler_flag diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_heating_loops_purchased_heating.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_heating_loops_purchased_heating.py new file mode 100644 index 0000000000..66130bb9b4 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_heating_loops_purchased_heating.py @@ -0,0 +1,52 @@ +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.assertions import getattr_ +from rct229.utils.jsonpath_utils import find_all +from rct229.utils.utility_functions import ( + find_exactly_one_fluid_loop, + find_exactly_one_terminal_unit, +) + +EXTERNAL_FLUID_SOURCE = SchemaEnums.schema_enums["ExternalFluidSourceOptions"] +FLUID_LOOP_TYPE = SchemaEnums.schema_enums["FluidLoopOptions"] + + +def are_all_terminal_heating_loops_purchased_heating(rmd_b, terminal_unit_id_list): + """Returns TRUE if the fluid loop associated with the heating_from_loop associated with each terminal unit is purchased heating. Returns FALSE if this is not the case. + ---------- + rmd_b : json + RMD at RuleSetModelDescription level + terminal_unit_id_list : list + List of terminal units IDs + Returns + ------- + bool + True: fluid loop associated with the heating_from_loop associated with each terminal unit is purchased heating + False: otherwise + """ + are_all_terminal_heating_loops_purchased_heating_flag = True + + # get the list of loop ids if the external fluid source matches to either hot water or steam + purchased_heating_loop_id_list_b = [ + *find_all( + f'external_fluid_sources[*][?(@.type="{EXTERNAL_FLUID_SOURCE.HOT_WATER}"), ?(@.type="{EXTERNAL_FLUID_SOURCE.STEAM}")].loop', + rmd_b, + ) + ] + + for terminal_b_id in terminal_unit_id_list: + terminal_b = find_exactly_one_terminal_unit(rmd_b, terminal_b_id) + heating_from_loop_id = terminal_b.get("heating_from_loop") + if ( + are_all_terminal_heating_loops_purchased_heating_flag + and heating_from_loop_id + ): + fluid_loop = find_exactly_one_fluid_loop(rmd_b, heating_from_loop_id) + if ( + getattr_(fluid_loop, "fluid loop", "type") != FLUID_LOOP_TYPE.HEATING + or heating_from_loop_id not in purchased_heating_loop_id_list_b + ): + are_all_terminal_heating_loops_purchased_heating_flag = False + else: + are_all_terminal_heating_loops_purchased_heating_flag = False + + return are_all_terminal_heating_loops_purchased_heating_flag diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_supplies_ducted.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_supplies_ducted.py new file mode 100644 index 0000000000..3c36805546 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_supplies_ducted.py @@ -0,0 +1,30 @@ +from rct229.utils.utility_functions import find_exactly_one_terminal_unit + + +def are_all_terminal_supplies_ducted(rmd_b, terminal_unit_id_list): + """Returns TRUE if all of the terminal supplies are ducted (i.e., is_supply_ducted = TRUE) for the list of + terminal units input to the function. It returns FALSE if any of the terminal supplies are not ducted (i.e., + is_supply_ducted = FALSE). + + Parameters + ---------- + rmd_b : json + RMD at RuleSetModelDescription level + terminal_unit_id_list : list + List of terminal units IDs + + Returns + ------- + bool + True: all of the terminal supplies are ducted (i.e., is_supply_ducted = TRUE) for the list of terminal units input to the function + False: any of the terminal supplies are not ducted (i.e., is_supply_ducted = FALSE) + """ + are_all_terminal_supplies_ducted_flag = True + for terminal_b_id in terminal_unit_id_list: + terminal_b = find_exactly_one_terminal_unit(rmd_b, terminal_b_id) + # Set flag to False if is_suppy_ducted is False or is missing + if not terminal_b.get("is_supply_ducted"): + are_all_terminal_supplies_ducted_flag = False + break + + return are_all_terminal_supplies_ducted_flag diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_types_CAV.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_types_CAV.py new file mode 100644 index 0000000000..f6e75c7561 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_types_CAV.py @@ -0,0 +1,36 @@ +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.utility_functions import find_exactly_one_terminal_unit + +TERMINAL_TYPE = SchemaEnums.schema_enums["TerminalOptions"] + + +def are_all_terminal_types_cav(rmd_b, terminal_unit_id_list): + """Returns TRUE if all of the terminal unit types input to this function are constant air volume (CAV) or if this + data element is undefined. It returns FALSE if any of the terminal units are of a type other than constant air + volume (CAV). + + Parameters + ---------- + rmd_b : json + RMD at RuleSetModelDescription level + terminal_unit_id_list : list + List of terminal units IDs + + Returns + ------- + bool + True: all of the terminal unit types input to this function are constant air volume (CAV) + False: any of the terminal units are of a type other than constant air volume (CAV). + """ + are_all_terminal_types_cav_flag = True + + for terminal_b_id in terminal_unit_id_list: + terminal_b = find_exactly_one_terminal_unit(rmd_b, terminal_b_id) + if ( + terminal_b.get("type") is not None + and terminal_b["type"] != TERMINAL_TYPE.CONSTANT_AIR_VOLUME + ): + are_all_terminal_types_cav_flag = False + break + + return are_all_terminal_types_cav_flag diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_types_CAV_with_none_equal_to_null.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_types_CAV_with_none_equal_to_null.py new file mode 100644 index 0000000000..fe00c09a23 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_types_CAV_with_none_equal_to_null.py @@ -0,0 +1,29 @@ +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.utility_functions import find_exactly_one_terminal_unit + +TERMINAL_TYPE = SchemaEnums.schema_enums["TerminalOptions"] + + +def are_all_terminal_types_cav_with_none_equal_to_null(rmd_b, terminal_unit_id_list): + """Returns TRUE if all of the terminal unit types input to this function are constant air volume (CAV). + It returns FALSE if any of the terminal units are of a type other than constant air volume (CAV). + + Parameters + ---------- + rmd_b : json + RMD at RuleSetModelDescription level + terminal_unit_id_list : list + List of terminal units IDs + + Returns + ------- + bool + True: all of the terminal unit types input to this function are constant air volume (CAV) (null or missing is false) + False: any of the terminal units are of a type other than constant air volume (CAV). + """ + + return all( + find_exactly_one_terminal_unit(rmd_b, terminal_b_id).get("type") + == TERMINAL_TYPE.CONSTANT_AIR_VOLUME + for terminal_b_id in terminal_unit_id_list + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_types_VAV.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_types_VAV.py new file mode 100644 index 0000000000..10e5fa6bd7 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/are_all_terminal_types_VAV.py @@ -0,0 +1,30 @@ +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.utility_functions import find_exactly_one_terminal_unit + +TERMINAL_TYPE = SchemaEnums.schema_enums["TerminalOptions"] + + +def are_all_terminal_types_VAV(rmd: dict, terminal_unit_id_list: list[str]) -> bool: + """It returns FALSE if no terminal unit in the list or any of the terminal units are of a type other than variable air volume (VAV) or null. + + Parameters + ---------- + rmd : dict + RMD at RuleSetModelDescription level + terminal_unit_id_list : list + List of terminal units IDs + + Returns + ------- + bool + True: all of the terminal unit types input to this function are variable air volume (VAV) + False: any of the terminal units are of a type other than variable air volume (VAV) + """ + + return len(terminal_unit_id_list) > 0 and all( + [ + find_exactly_one_terminal_unit(rmd, terminal_id).get("type") + in [None, TERMINAL_TYPE.VARIABLE_AIR_VOLUME] + for terminal_id in terminal_unit_id_list + ] + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/do_all_terminals_have_one_fan.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/do_all_terminals_have_one_fan.py new file mode 100644 index 0000000000..f293d37de5 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/do_all_terminals_have_one_fan.py @@ -0,0 +1,25 @@ +from rct229.utils.utility_functions import find_exactly_one_terminal_unit + + +def do_all_terminals_have_one_fan(rmd_b, terminal_unit_id_list): + """Returns TRUE if a fan data element associated with all terminal units input to this function. + It returns FALSE if any terminal unit has no fan data element not equal to one. + + ---------- + rmd_b : json + RMD at RuleSetModelDescription level + terminal_unit_id_list : list + List of terminal units IDs + Returns + ------- + bool + True: there is a fan data element associated with all terminal units input to this function. + False: any terminal unit a no fan data. + """ + + return all( + [ + find_exactly_one_terminal_unit(rmd_b, terminal_b_id).get("fan") is not None + for terminal_b_id in terminal_unit_id_list + ] + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/does_each_zone_have_only_one_terminal.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/does_each_zone_have_only_one_terminal.py new file mode 100644 index 0000000000..b8d8d5a572 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/does_each_zone_have_only_one_terminal.py @@ -0,0 +1,23 @@ +from rct229.utils.utility_functions import find_exactly_one_zone + + +def does_each_zone_have_only_one_terminal(rmd_b, zone_id_list): + """Returns TRUE if each zone input to this function only has one terminal unit. It returns FALSE if any zone has more than one terminal unit. + ---------- + rmd_b : json + RMD at RuleSetModelDescription level + zone_id_list : list + List of zones to evaluate. + Returns + ------- + bool + True: each zone input to this function only has one terminal unit or no terminal + False: any zone has more than one terminal + """ + does_each_zone_have_only_one_terminal_flag = True + for zone_id in zone_id_list: + zone = find_exactly_one_zone(rmd_b, zone_id) + if zone.get("terminals") is None or len(zone["terminals"]) != 1: + does_each_zone_have_only_one_terminal_flag = False + break + return does_each_zone_have_only_one_terminal_flag diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/does_hvac_system_serve_single_zone.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/does_hvac_system_serve_single_zone.py new file mode 100644 index 0000000000..7e991a8a12 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/does_hvac_system_serve_single_zone.py @@ -0,0 +1,17 @@ +def does_hvac_system_serve_single_zone(rmd_b, zone_id_list): + """Returns TRUE if the HVAC system serves a single zone. Returns FALSE if the HVAC system serves multiple zones. + + Parameters + ---------- + rmd_b : json + RMD at RuleSetModelDescription level + zone_id_list : str + list of zone IDs associated with the HVAC system to be evaluated + + Returns + ------- + bool + True: HVAC system serves a single zone + False: HVAC system serves multiple zones + """ + return len(zone_id_list) == 1 diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/get_dict_with_terminal_units_and_zones.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/get_dict_with_terminal_units_and_zones.py new file mode 100644 index 0000000000..b46cd3bfc0 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/get_dict_with_terminal_units_and_zones.py @@ -0,0 +1,26 @@ +from rct229.utils.jsonpath_utils import find_all + + +def get_dict_with_terminal_units_and_zones(rmd: dict) -> dict: + """ + Returns a dictionary of zone IDs associated with each terminal unit in the RMD. + + Parameters + ---------- + rmd: json + + Returns ------- dict a dictionary of zones associated with each terminal unit in the rmd, {terminal_unit_1.id: [ + zone_1.id, zone_2.id, zone_3.id], terminal_unit_2.id: [zone_4.id, zone_9.id, zone_30.id]} + + """ + return { + terminal["id"]: find_all( + # search all zones that have terminal(s) match to this terminal id + # and return them in a list. + f'$.buildings[*].building_segments[*].zones[*][?(@.terminals[*].id="{terminal["id"]}")].id', + rmd, + ) + for terminal in find_all( + "$.buildings[*].building_segments[*].zones[*].terminals[*]", rmd + ) + } diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_cooling_type_DX.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_cooling_type_DX.py new file mode 100644 index 0000000000..759bdd1cc8 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_cooling_type_DX.py @@ -0,0 +1,30 @@ +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.jsonpath_utils import find_one +from rct229.utils.utility_functions import find_exactly_one_hvac_system + +COOLING_SYSTEM_TYPE = SchemaEnums.schema_enums["CoolingSystemOptions"] + + +def is_hvac_sys_cooling_type_dx(rmd_b, hvac_b_id): + """Returns TRUE if the HVAC system has DX cooling. Returns FALSE if the HVAC system has anything other than DX cooling. + + Parameters + ---------- + rmd_b : json + RMD at RuleSetModelDescription level + hvac_b_id : str + The HVAC system ID. + + Returns + ------- + bool + True: the HVAC system has DX cooling + False: the HVAC system has a cooling system type other than DX + """ + # Get the hvac system + hvac_b = find_exactly_one_hvac_system(rmd_b, hvac_b_id) + + return ( + find_one("$.cooling_system.type", hvac_b) + == COOLING_SYSTEM_TYPE.DIRECT_EXPANSION + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_cooling_type_fluid_loop.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_cooling_type_fluid_loop.py new file mode 100644 index 0000000000..25751eea36 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_cooling_type_fluid_loop.py @@ -0,0 +1,35 @@ +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.jsonpath_utils import find_one +from rct229.utils.utility_functions import find_exactly_one_hvac_system + +COOLING_SYSTEM_TYPE = SchemaEnums.schema_enums["CoolingSystemOptions"] + + +def is_hvac_sys_cooling_type_fluid_loop(rmd_b, hvac_b_id): + """Returns TRUE if the HVAC system has fluid_loop cooling. Returns FALSE if the HVAC system has anything other + than fluid_loop cooling or if it has more than 1 cooling system. + + Parameters + ---------- + rmd_b : json + RMD at RuleSetModelDescription level + hvac_b_id : str + The HVAC system ID. + + Returns + ------- + bool + True: HVAC system has fluid_loop cooling + False: HVAC system has a cooling system type other than fluid_loop + """ + # Get the hvac system + hvac_b = find_exactly_one_hvac_system(rmd_b, hvac_b_id) + # Check if hvac_b has preheat system + cooling_system = hvac_b.get("cooling_system") + is_hvac_sys_cooling_type_fluid_loop_flag = ( + cooling_system is not None + and cooling_system.get("chilled_water_loop") is not None + and find_one("type", cooling_system) == COOLING_SYSTEM_TYPE.FLUID_LOOP + ) + + return is_hvac_sys_cooling_type_fluid_loop_flag diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_cooling_type_none.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_cooling_type_none.py new file mode 100644 index 0000000000..54f12629f3 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_cooling_type_none.py @@ -0,0 +1,30 @@ +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.jsonpath_utils import find_one +from rct229.utils.utility_functions import find_exactly_one_hvac_system + +COOLING_SYSTEM_TYPE = SchemaEnums.schema_enums["CoolingSystemOptions"] + + +def is_hvac_sys_cooling_type_none(rmd_b, hvac_b_id): + """Returns TRUE if the HVAC system cooling type is None or Null. Returns FALSE if the HVAC system has anything + other than None or Null for the cooling type. + + Parameters + ---------- + rmd_b : json + RMD at RuleSetModelDescription level + hvac_b_id : str + The HVAC system ID. + + Returns + ------- + bool + True: HVAC system cooling type is None or Null + False: HVAC system has anything other than None or Null for the cooling type + """ + # Get the hvac system + hvac_b = find_exactly_one_hvac_system(rmd_b, hvac_b_id) + # Check if hvac_b has preheat system + cooling_system_type = find_one("$.cooling_system.type", hvac_b) + + return cooling_system_type in [COOLING_SYSTEM_TYPE.NONE, None] diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_cooling_type_none_or_non_mechanical.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_cooling_type_none_or_non_mechanical.py new file mode 100644 index 0000000000..c34e91404b --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_cooling_type_none_or_non_mechanical.py @@ -0,0 +1,34 @@ +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.jsonpath_utils import find_one +from rct229.utils.utility_functions import find_exactly_one_hvac_system + +COOLING_SYSTEM_TYPE = SchemaEnums.schema_enums["CoolingSystemOptions"] + + +def is_hvac_sys_cooling_type_none_or_non_mechanical(rmd_b, hvac_b_id): + """Returns TRUE if the HVAC system cooling type is None or Null or non_mechanical. Returns FALSE if the HVAC system has anything + other than None or Null for the cooling type. + + Parameters + ---------- + rmd_b : json + RMD at RuleSetModelDescription level + hvac_b_id : str + The HVAC system ID. + + Returns + ------- + bool + True: HVAC system cooling type is None or Null + False: HVAC system has anything other than None or Null for the cooling type + """ + # Get the hvac system + hvac_b = find_exactly_one_hvac_system(rmd_b, hvac_b_id) + # Check if hvac_b has preheat system + cooling_system_type = find_one("$.cooling_system.type", hvac_b) + + return cooling_system_type in [ + COOLING_SYSTEM_TYPE.NONE, + COOLING_SYSTEM_TYPE.NON_MECHANICAL, + None, + ] diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_fan_sys_CV.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_fan_sys_CV.py new file mode 100644 index 0000000000..c5c04b3972 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_fan_sys_CV.py @@ -0,0 +1,32 @@ +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.jsonpath_utils import find_one +from rct229.utils.utility_functions import find_exactly_one_hvac_system + +FAN_SYSTEM_SUPPLY_FAN_CONTROL = SchemaEnums.schema_enums[ + "FanSystemSupplyFanControlOptions" +] + + +def is_hvac_sys_fan_sys_cv(rmd_b, hvac_b_id): + """Returns TRUE if the HVAC system fan system is constant volume. Returns FALSE if the HVAC system fan system is anything other than constant volume. + + Parameters + ---------- + rmd_b : json + RMD at RuleSetModelDescription level + hvac_b_id : str + The HVAC system ID. + + Returns + ------- + bool + True: the HVAC system fan system has constant volume + False: the HVAC system has a fan system that is anything other than constant volume + """ + # Get the hvac system + hvac_b = find_exactly_one_hvac_system(rmd_b, hvac_b_id) + + return ( + find_one("$.fan_system.fan_control", hvac_b) + == FAN_SYSTEM_SUPPLY_FAN_CONTROL.CONSTANT + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_fan_sys_VSD.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_fan_sys_VSD.py new file mode 100644 index 0000000000..9983c5911b --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_fan_sys_VSD.py @@ -0,0 +1,35 @@ +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.utility_functions import find_exactly_one_hvac_system + +FAN_SYSTEM_SUPPLY_FAN_CONTROL = SchemaEnums.schema_enums[ + "FanSystemSupplyFanControlOptions" +] + + +def is_hvac_sys_fan_sys_vsd(rmd_b, hvac_b_id): + """Returns TRUE if the HVAC system fan system is variable speed drive controlled. Returns FALSE if the HVAC system fan system is anything other than variable speed drive controlled. + + Parameters + ---------- + rmd_b : json + RMD at RuleSetModelDescription level + hvac_b_id : str + The HVAC system ID. + + Returns + ------- + bool + True: HVAC system fan system his variable speed drive control + False: the HVAC system has a fan system that is anything other than variable speed drive controlled + """ + + # Get the hvac system + hvac_b = find_exactly_one_hvac_system(rmd_b, hvac_b_id) + fan_system = hvac_b.get("fan_system") + is_hvac_sys_fan_sys_vsd_flag = ( + fan_system is not None + and fan_system.get("fan_control") + == FAN_SYSTEM_SUPPLY_FAN_CONTROL.VARIABLE_SPEED_DRIVE + ) + + return is_hvac_sys_fan_sys_vsd_flag diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_fluid_loop_attached_to_boiler.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_fluid_loop_attached_to_boiler.py new file mode 100644 index 0000000000..68e56a157a --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_fluid_loop_attached_to_boiler.py @@ -0,0 +1,38 @@ +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.jsonpath_utils import find_all, find_one +from rct229.utils.utility_functions import ( + find_exactly_one_fluid_loop, + find_exactly_one_hvac_system, +) + +FLUID_LOOP = SchemaEnums.schema_enums["FluidLoopOptions"] + + +def is_hvac_sys_fluid_loop_attached_to_boiler(rmd_b, hvac_b_id): + """Returns TRUE if the fluid loop associated with the heating system associated with the HVAC system is attached to a boiler. Returns FALSE if this is not the case. + + Parameters + ---------- + rmd_b : json + RMD at RuleSetModelDescription level + hvac_b_id : str + The HVAC system ID. + + Returns + ------- + bool + True: the fluid loop associated with the heating system associated with the HVAC system is attached to a boiler + False: otherwise + """ + is_hvac_sys_fluid_loop_attached_to_boiler_flag = False + boiler_loop_ids = find_all("$.boilers[*].loop", rmd_b) + # Get the hvac system + hvac_b = find_exactly_one_hvac_system(rmd_b, hvac_b_id) + hot_water_loop_id = find_one("heating_system.hot_water_loop", hvac_b) + if hot_water_loop_id in boiler_loop_ids: + hot_water_loop = find_exactly_one_fluid_loop(rmd_b, hot_water_loop_id) + is_hvac_sys_fluid_loop_attached_to_boiler_flag = ( + find_one("type", hot_water_loop) == FLUID_LOOP.HEATING + ) + + return is_hvac_sys_fluid_loop_attached_to_boiler_flag diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_fluid_loop_attached_to_chiller.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_fluid_loop_attached_to_chiller.py new file mode 100644 index 0000000000..14bc953d37 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_fluid_loop_attached_to_chiller.py @@ -0,0 +1,57 @@ +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.utility_functions import ( + find_exactly_one_child_loop, + find_exactly_one_fluid_loop, + find_exactly_one_hvac_system, +) + +FLUID_LOOP = SchemaEnums.schema_enums["FluidLoopOptions"] + + +def is_hvac_sys_fluid_loop_attached_to_chiller(rmd_b, hvac_b_id): + """Returns TRUE if the fluid loop associated with the cooling system associated with the HVAC system is attached to a chiller. Returns FALSE if this is not the case. + + Parameters + ---------- + rmd_b : json + RMD at RuleSetModelDescription level + hvac_b_id : str + The HVAC system ID. + + Returns + ------- + bool + True: fluid loop associated with the cooling system associated with the HVAC system is attached to a chiller + False: otherwise + """ + is_hvac_sys_fluid_loop_attached_to_chiller_flag = False + chillers = find_all("$.chillers[*]", rmd_b) + primary_cooling_loop_ids = [ + getattr_(chiller_b, "chiller", "cooling_loop") for chiller_b in chillers + ] + secondary_cooling_loop_ids = [ + secondary_loop["id"] + for primary_cooling_loop_id in primary_cooling_loop_ids + for secondary_loop in find_all( + "$.child_loops[*]", + find_exactly_one_fluid_loop(rmd_b, primary_cooling_loop_id), + ) + ] + + # Get the hvac system + hvac_b = find_exactly_one_hvac_system(rmd_b, hvac_b_id) + + # Allow single loop and primary/secondary loop configuration as true. + chilled_water_loop_id = find_one("cooling_system.chilled_water_loop", hvac_b) + water_loop = None + if chilled_water_loop_id in primary_cooling_loop_ids: + water_loop = find_exactly_one_fluid_loop(rmd_b, chilled_water_loop_id) + elif chilled_water_loop_id in secondary_cooling_loop_ids: + water_loop = find_exactly_one_child_loop(rmd_b, chilled_water_loop_id) + is_hvac_sys_fluid_loop_attached_to_chiller_flag = ( + water_loop is not None and find_one("type", water_loop) == FLUID_LOOP.COOLING + ) + + return is_hvac_sys_fluid_loop_attached_to_chiller_flag diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_fluid_loop_purchased_CHW.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_fluid_loop_purchased_CHW.py new file mode 100644 index 0000000000..28af205fd3 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_fluid_loop_purchased_CHW.py @@ -0,0 +1,38 @@ +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.jsonpath_utils import find_all, find_one +from rct229.utils.utility_functions import find_exactly_one_hvac_system + +EXTERNAL_FLUID_SOURCE = SchemaEnums.schema_enums["ExternalFluidSourceOptions"] + + +def is_hvac_sys_fluid_loop_purchased_chw(rmd_b, hvac_b_id): + """Returns TRUE if the fluid loop associated with the cooling system associated with the HVAC system is attached to an external purchased chilled water loop. Returns FALSE if this is not the case. + Parameters + ---------- + rmd_b : json + RMD at RuleSetModelDescription level + hvac_b_id : str + The HVAC system ID. + Returns + ------- + bool + True: the fluid loop associated with the cooling system associated with the HVAC system is attached to an external purchased cooling loop + False: otherwise + """ + purchased_cooling_loop_id_list_b = [ + *find_all( + f'external_fluid_sources[*][?(@.type="{EXTERNAL_FLUID_SOURCE.CHILLED_WATER}")].loop', + rmd_b, + ) + ] + + # Get the hvac system + hvac_b = find_exactly_one_hvac_system(rmd_b, hvac_b_id) + # the hvac_sys has a cooling system and the cooling system has a chilled_water_loop and + # the loop id is in the purchased_cooling_loop_id_list + is_hvac_sys_fluid_loop_purchased_chw_flag = ( + find_one("cooling_system.chilled_water_loop", hvac_b) + in purchased_cooling_loop_id_list_b + ) + + return is_hvac_sys_fluid_loop_purchased_chw_flag diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_fluid_loop_purchased_heating.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_fluid_loop_purchased_heating.py new file mode 100644 index 0000000000..852cd74b01 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_fluid_loop_purchased_heating.py @@ -0,0 +1,40 @@ +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.jsonpath_utils import find_all, find_one +from rct229.utils.utility_functions import find_exactly_one_hvac_system + +EXTERNAL_FLUID_SOURCE = SchemaEnums.schema_enums["ExternalFluidSourceOptions"] + + +def is_hvac_sys_fluid_loop_purchased_heating(rmd_b, hvac_b_id): + """Returns TRUE if the fluid loop associated with the heating system associated with the HVAC system is attached to an external purchased heating loop. Returns FALSE if this is not the case. + + Parameters + ---------- + rmd_b : json + RMD at RuleSetModelDescription level + hvac_b_id : str + The HVAC system ID. + + Returns + ------- + bool + True: the fluid loop associated with the heating system associated with the HVAC system is attached to an external purchased heating loop + False: otherwise + """ + # Get a list of loop ids in external fluid sources whose type are either hot water or steam + purchased_heating_loop_id_list_b = find_all( + f'$.external_fluid_sources[*][?(@.type="{EXTERNAL_FLUID_SOURCE.HOT_WATER}"), ?(@.type="{EXTERNAL_FLUID_SOURCE.STEAM}")].loop', + rmd_b, + ) + + # Get the hvac system + hvac_b = find_exactly_one_hvac_system(rmd_b, hvac_b_id) + + # the hvac_sys has a heating system and the heating system has a hot_water_loop and + # the loop id is in the purchased_heating_loop_id_list + is_hvac_sys_fluid_loop_purchased_heating_flag = ( + find_one("heating_system.hot_water_loop", hvac_b) + in purchased_heating_loop_id_list_b + ) + + return is_hvac_sys_fluid_loop_purchased_heating_flag diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_heating_type_elec_resistance.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_heating_type_elec_resistance.py new file mode 100644 index 0000000000..df54e9c883 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_heating_type_elec_resistance.py @@ -0,0 +1,28 @@ +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.jsonpath_utils import find_one +from rct229.utils.utility_functions import find_exactly_one_hvac_system + +HEATING_SYSTEM = SchemaEnums.schema_enums["HeatingSystemOptions"] + + +def is_hvac_sys_heating_type_elec_resistance(rmd_b, hvac_b_id): + """Returns TRUE if the HVAC system heating system heating type is ELECTRIC_RESISTANCE. Returns FALSE if the HVAC system heating system has anything other than ELECTRIC_RESISTANCE. + + Parameters + ---------- + rmd_b : json + RMD at RuleSetModelDescription level + hvac_b_id : str + The HVAC system ID. + + Returns + ------- + bool + True: the HVAC system heating system has ELECTRIC_RESISTANCE as the heating type + False: the HVAC system has a heating system type other than ELECTRIC_RESISTANCE + """ + hvac_b = find_exactly_one_hvac_system(rmd_b, hvac_b_id) + + return ( + find_one("$.heating_system.type", hvac_b) == HEATING_SYSTEM.ELECTRIC_RESISTANCE + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_heating_type_fluid_loop.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_heating_type_fluid_loop.py new file mode 100644 index 0000000000..472983c69e --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_heating_type_fluid_loop.py @@ -0,0 +1,33 @@ +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.jsonpath_utils import find_one +from rct229.utils.utility_functions import find_exactly_one_hvac_system + +HEATING_SYSTEM = SchemaEnums.schema_enums["HeatingSystemOptions"] + + +def is_hvac_sys_heating_type_fluid_loop(rmd_b: dict, hvac_b_id: str) -> bool: + """Returns TRUE if the HVAC system heating system heating type is fluid loop. Returns FALSE if the HVAC system + heating system has anything other than fluid loop or if it has more than 1 heating system. + + Parameters + ---------- + rmd_b : json + RMD at RuleSetModelDescription level + hvac_b_id : str + The HVAC system ID. + + Returns + ------- + bool + True: HVAC system heating system has fluid loop as the heating type + False: HVAC system has a heating system type other than fluid loop + """ + hvac_b = find_exactly_one_hvac_system(rmd_b, hvac_b_id) + heating_system = hvac_b.get("heating_system") + is_hvac_sys_heating_type_fluid_loop_flag = ( + heating_system is not None + and heating_system.get("hot_water_loop") is not None + and find_one("type", heating_system) == HEATING_SYSTEM.FLUID_LOOP + ) + + return is_hvac_sys_heating_type_fluid_loop_flag diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_heating_type_furnace.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_heating_type_furnace.py new file mode 100644 index 0000000000..8244c25a9f --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_heating_type_furnace.py @@ -0,0 +1,26 @@ +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.jsonpath_utils import find_one +from rct229.utils.utility_functions import find_exactly_one_hvac_system + +HEATING_SYSTEM = SchemaEnums.schema_enums["HeatingSystemOptions"] + + +def is_hvac_sys_heating_type_furnace(rmd_b, hvac_b_id): + """Returns TRUE if the HVAC system heating system heating type is furnace. Returns FALSE if the HVAC system + heating system has anything other than furnace or if it has more than 1 heating system. + + Parameters + ---------- + rmd_b : json + RMD at RuleSetModelDescription level + hvac_b_id : str + The HVAC system ID. + Returns + ------- + bool + True: the HVAC system heating system has furnace as the heating type + False: the HVAC system has a heating system type other than furnace + """ + hvac_b = find_exactly_one_hvac_system(rmd_b, hvac_b_id) + + return find_one("$.heating_system.type", hvac_b) == HEATING_SYSTEM.FURNACE diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_heating_type_heat_pump.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_heating_type_heat_pump.py new file mode 100644 index 0000000000..417480cd71 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_heating_type_heat_pump.py @@ -0,0 +1,27 @@ +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.jsonpath_utils import find_one +from rct229.utils.utility_functions import find_exactly_one_hvac_system + +HEATING_SYSTEM = SchemaEnums.schema_enums["HeatingSystemOptions"] + + +def is_hvac_sys_heating_type_heat_pump(rmd_b, hvac_b_id): + """Returns TRUE if the HVAC system has heat pump as the heating system type. Returns FALSE if the HVAC system has + anything other than heat pump as the heating system type or if it has more than 1 heating system. + + Parameters + ---------- + rmd_b : json + RMD at RuleSetModelDescription level + hvac_b_id : str + The HVAC system ID. + + Returns + ------- + bool + True: the HVAC system has heat pump as the heating system type + False: the HVAC system has a heating system type other than heat pump as the heating system type + """ + hvac_b = find_exactly_one_hvac_system(rmd_b, hvac_b_id) + + return find_one("$.heating_system.type", hvac_b) == HEATING_SYSTEM.HEAT_PUMP diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_preheat_fluid_loop_attached_to_boiler.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_preheat_fluid_loop_attached_to_boiler.py new file mode 100644 index 0000000000..f376a216f6 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_preheat_fluid_loop_attached_to_boiler.py @@ -0,0 +1,40 @@ +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.jsonpath_utils import find_all, find_one +from rct229.utils.utility_functions import ( + find_exactly_one_fluid_loop, + find_exactly_one_hvac_system, +) + +FLUID_LOOP = SchemaEnums.schema_enums["FluidLoopOptions"] + + +def is_hvac_sys_preheat_fluid_loop_attached_to_boiler(rmd_b, hvac_b_id): + """Returns True if the fluid loop associated with preheat system associated with the HVAC system is attached to a boiler. + Returns False if this is not the case. + + Parameters + ---------- + rmd_b : json + RMD at RuleSetModelDescription level + hvac_b_id : str + The HVAC system ID. + + Returns + ------- + bool + True: preheat system is attached to a boiler + False: otherwise + """ + is_hvac_sys_preheat_fluid_loop_attached_to_boiler_flag = False + loop_boiler_id_list = find_all("$.boilers[*].loop", rmd_b) + # Get the hvac system + hvac_b = find_exactly_one_hvac_system(rmd_b, hvac_b_id) + # hot_water_loop_id can be None + hot_water_loop_id = find_one("$.preheat_system.hot_water_loop", hvac_b) + if hot_water_loop_id in loop_boiler_id_list: + hot_water_loop = find_exactly_one_fluid_loop(rmd_b, hot_water_loop_id) + is_hvac_sys_preheat_fluid_loop_attached_to_boiler_flag = ( + find_one("$.type", hot_water_loop) == FLUID_LOOP.HEATING + ) + + return is_hvac_sys_preheat_fluid_loop_attached_to_boiler_flag diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_preheat_fluid_loop_purchased_heating.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_preheat_fluid_loop_purchased_heating.py new file mode 100644 index 0000000000..753a0c3c95 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_preheat_fluid_loop_purchased_heating.py @@ -0,0 +1,41 @@ +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.jsonpath_utils import find_all +from rct229.utils.utility_functions import find_exactly_one_hvac_system + +EXTERNAL_FLUID_SOURCE = SchemaEnums.schema_enums["ExternalFluidSourceOptions"] + + +def is_hvac_sys_preheat_fluid_loop_purchased_heating(rmd_b, hvac_b_id): + """Returns TRUE if the fluid loop associated with the preheating system associated with the HVAC system is + attached to an external purchased heating loop. Returns FALSE if this is not the case. + + Parameters + ---------- + rmd_b : json + RMD at RuleSetModelDescription level + hvac_b_id : str + The HVAC system ID. + + Returns + ------- + bool + True: fluid loop associated with the preheating system associated with the HVAC system is attached to an external purchased heating loop + False: otherwise + """ + purchased_heating_loop_list_b = [ + *find_all( + f'external_fluid_sources[*][?(@.type="{EXTERNAL_FLUID_SOURCE.HOT_WATER}"), ?(@.type="{EXTERNAL_FLUID_SOURCE.STEAM}")].loop', + rmd_b, + ) + ] + + # Get the hvac system + hvac_b = find_exactly_one_hvac_system(rmd_b, hvac_b_id) + # Check if hvac_b has preheat system + preheat_system = hvac_b.get("preheat_system") + is_hvac_sys_preheat_fluid_loop_purchased_heating_flag = ( + preheat_system is not None + and preheat_system.get("hot_water_loop") in purchased_heating_loop_list_b + ) + + return is_hvac_sys_preheat_fluid_loop_purchased_heating_flag diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_preheating_type_elec_resistance.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_preheating_type_elec_resistance.py new file mode 100644 index 0000000000..17ad21ed7f --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_preheating_type_elec_resistance.py @@ -0,0 +1,30 @@ +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.jsonpath_utils import find_one +from rct229.utils.utility_functions import find_exactly_one_hvac_system + +HEATING_SYSTEM = SchemaEnums.schema_enums["HeatingSystemOptions"] + + +def is_hvac_sys_preheating_type_elec_resistance(rmd_b, hvac_b_id): + """Returns TRUE if the HVAC system preheating system heating type is ELECTRIC_RESISTANCE. Returns FALSE if the + HVAC system preheating system has anything other than ELECTRIC_RESISTANCE. + + Parameters + ---------- + rmd_b : json + RMD at RuleSetModelDescription level + hvac_b_id : str + The HVAC system ID. + + Returns + ------- + bool + True: HVAC system preheating system has ELECTRIC_RESISTANCE as the heating type + False: HVAC system has a preheating system type other than ELECTRIC_RESISTANCE + """ + # Get the hvac system + hvac_b = find_exactly_one_hvac_system(rmd_b, hvac_b_id) + + return ( + find_one("$.preheat_system.type", hvac_b) == HEATING_SYSTEM.ELECTRIC_RESISTANCE + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_preheating_type_fluid_loop.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_preheating_type_fluid_loop.py new file mode 100644 index 0000000000..2549e4e3ca --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_sys_preheating_type_fluid_loop.py @@ -0,0 +1,36 @@ +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.jsonpath_utils import find_one +from rct229.utils.utility_functions import find_exactly_one_hvac_system + +HEATING_SYSTEM = SchemaEnums.schema_enums["HeatingSystemOptions"] + + +def is_hvac_sys_preheating_type_fluid_loop(rmd_b: dict, hvac_b_id: str) -> bool: + """Returns TRUE if the HVAC system preheating system heating type is fluid loop. Returns FALSE if the HVAC system + preheating system has anything other than fluid loop. + + Parameters + ---------- + rmd_b : json + RMD at RuleSetModelDescription level + hvac_b_id : str + The HVAC system ID. + + Returns + ------- + bool + True: HVAC system preheating system has fluid loop as the heating type + False: otherwise + """ + # Get the hvac system + hvac_b = find_exactly_one_hvac_system(rmd_b, hvac_b_id) + # get preheat system from the HVAC + preheat_system = hvac_b.get("preheat_system") + is_hvac_sys_preheating_type_fluid_loop_flag = ( + preheat_system is not None + and preheat_system.get("hot_water_loop") is not None + # Silence fail if heating system type data is not in RMD + and find_one("$.type", preheat_system) == HEATING_SYSTEM.FLUID_LOOP + ) + + return is_hvac_sys_preheating_type_fluid_loop_flag diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_system_multizone.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_system_multizone.py new file mode 100644 index 0000000000..6ea6bf03bf --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/is_hvac_system_multizone.py @@ -0,0 +1,16 @@ +def is_hvac_system_multizone(rmd_b, zone_id_list): + """ + Returns TRUE if the HVAC system serves multiple zones. Returns FALSE if the HVAC system serves a single or no zones. + + Parameters + ---------- + rmd_b: json + zone_id_list: list[string] zone ids + + Returns + ------- + bool + True: HVAC system serves a multiple zones. + False: HVAC system serves zero or one zone. + """ + return len(zone_id_list) > 1 diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_chw_loops_purchased_cooling.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_chw_loops_purchased_cooling.py new file mode 100644 index 0000000000..5eed035211 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_chw_loops_purchased_cooling.py @@ -0,0 +1,71 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_chw_loops_purcahsed_cooling import ( + are_all_terminal_chw_loops_purchased_cooling, +) + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "zones": [ + { + "id": "zone 1", + "terminals": [ + {"id": "terminal_1", "cooling_from_loop": "CHW_Loop_1"}, + {"id": "terminal_2", "cooling_from_loop": "HW_Loop_1"}, + {"id": "terminal_3"}, + ], + }, + ], + } + ], + } + ], + "fluid_loops": [ + {"id": "HW_Loop_1", "type": "HEATING"}, + {"id": "CHW_Loop_1", "type": "COOLING"}, + ], + "external_fluid_sources": [ + {"id": "fluid_loop_1", "loop": "HW_Loop_1", "type": "HOT_WATER"}, + {"id": "fluid_loop_2", "loop": "ST_Loop_1", "type": "STEAM"}, + {"id": "fluid_loop_3", "loop": "CHW_Loop_1", "type": "CHILLED_WATER"}, + ], + "type": "BASELINE_0", +} + +TEST_RPD_FULL = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__all_terminal_cooling_loops_purchased_cooling_all_are_cooling(): + assert ( + are_all_terminal_chw_loops_purchased_cooling(TEST_RMD, ["terminal_1"]) == True + ) + + +def test__all_terminal_cooling_loops_purchased_cooling_not_all_are(): + assert ( + are_all_terminal_chw_loops_purchased_cooling( + TEST_RMD, ["terminal_1", "terminal_2"] + ) + == False + ) + + +def test__all_terminal_cooling_loops_purchased_cooling_null(): + assert ( + are_all_terminal_chw_loops_purchased_cooling(TEST_RMD, ["terminal_3"]) == False + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_cool_sources_chilled_water.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_cool_sources_chilled_water.py new file mode 100644 index 0000000000..2e35a8a7b4 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_cool_sources_chilled_water.py @@ -0,0 +1,62 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_cool_sources_chilled_water import ( + are_all_terminal_cool_sources_chilled_water, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "zones": [ + { + "id": "zone 1", + "terminals": [ + {"id": "terminal_1", "cooling_source": "NONE"}, + {"id": "terminal_2", "cooling_source": "CHILLED_WATER"}, + {"id": "terminal_3"}, + ], + }, + ], + } + ], + } + ], + "type": "BASELINE_0", +} + +TEST_RPD_FULL = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__all_terminal_heat_source_chilled_water_none(): + assert ( + are_all_terminal_cool_sources_chilled_water( + TEST_RMD, ["terminal_1", "terminal_3"] + ) + == False + ) + + +def test__all_terminal_heat_source_chilled_water(): + assert are_all_terminal_cool_sources_chilled_water(TEST_RMD, ["terminal_2"]) == True diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_cool_sources_none_or_null.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_cool_sources_none_or_null.py new file mode 100644 index 0000000000..565df26cab --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_cool_sources_none_or_null.py @@ -0,0 +1,67 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_cool_sources_none_or_null import ( + are_all_terminal_cool_sources_none_or_null, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "zones": [ + { + "id": "zone 1", + "terminals": [ + {"id": "terminal_1", "cooling_source": "NONE"}, + {"id": "terminal_2", "cooling_source": "CHILLED_WATER"}, + {"id": "terminal_3"}, + ], + }, + ], + } + ], + } + ], + "type": "BASELINE_0", +} + +TEST_RPD_FULL = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__all_terminal_heat_source__none_or_null(): + assert ( + are_all_terminal_cool_sources_none_or_null( + TEST_RMD, ["terminal_1", "terminal_3"] + ) + == True + ) + + +def test__all_terminal_heat_source__none_or_null_one_hw(): + assert ( + are_all_terminal_cool_sources_none_or_null( + TEST_RMD, ["terminal_2", "terminal_3"] + ) + == False + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_fan_configs_parallel.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_fan_configs_parallel.py new file mode 100644 index 0000000000..5c9c4bbfdd --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_fan_configs_parallel.py @@ -0,0 +1,66 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_fan_configs_parallel import ( + are_all_terminal_fan_configs_parallel, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "zones": [ + { + "id": "zone 1", + "terminals": [ + {"id": "terminal_1", "fan_configuration": "PARALLEL"}, + {"id": "terminal_2"}, + {"id": "terminal_3", "fan_configuration": "SERIES"}, + ], + }, + ], + } + ], + } + ], + "type": "BASELINE_0", +} + +TEST_RPD_FULL = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__all_terminal_fans_parallel(): + assert are_all_terminal_fan_configs_parallel(TEST_RMD, ["terminal_1"]) == True + + +def test__one_terminal_fans_none_parallel(): + assert ( + are_all_terminal_fan_configs_parallel( + TEST_RMD, ["terminal_1", "terminal_2", "terminal_3"] + ) + == False + ) + + +def test__all_terminal_fans_null(): + assert are_all_terminal_fan_configs_parallel(TEST_RMD, ["terminal_2"]) == False diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_fans_null.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_fans_null.py new file mode 100644 index 0000000000..7d0b9a8dda --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_fans_null.py @@ -0,0 +1,64 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_fans_null import ( + are_all_terminal_fans_null, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "zones": [ + { + "id": "zone 1", + "terminals": [ + {"id": "terminal_1", "fan": {"id": "fan_1"}}, + {"id": "terminal_2"}, + {"id": "terminal_3", "fan": {"id": "fan_2"}}, + ], + }, + ], + } + ], + } + ], + "type": "BASELINE_0", +} + +TEST_RPD_FULL = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__all_terminal_fans__not_null(): + assert are_all_terminal_fans_null(TEST_RMD, ["terminal_1"]) == False + + +def test__one_terminal_fans__not_null(): + assert ( + are_all_terminal_fans_null(TEST_RMD, ["terminal_1", "terminal_2", "terminal_3"]) + == False + ) + + +def test__all_terminal_fans__null(): + assert are_all_terminal_fans_null(TEST_RMD, ["terminal_2"]) == True diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_heat_sources_electric.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_heat_sources_electric.py new file mode 100644 index 0000000000..47fbc00ac5 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_heat_sources_electric.py @@ -0,0 +1,61 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_heat_sources_electric import ( + are_all_terminal_heat_sources_electric, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "zones": [ + { + "id": "zone 1", + "terminals": [ + {"id": "terminal_1"}, + {"id": "terminal_2", "heating_source": "HOT_WATER"}, + {"id": "terminal_3", "heating_source": "ELECTRIC"}, + ], + }, + ], + } + ], + } + ], + "type": "BASELINE_0", +} + +TEST_RPD_FULL = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__all_terminal_heat_source__hot_water(): + assert are_all_terminal_heat_sources_electric(TEST_RMD, ["terminal_2"]) == False + + +def test__all_terminal_heat_source__electric(): + assert are_all_terminal_heat_sources_electric(TEST_RMD, ["terminal_3"]) == True + + +def test__all_terminal_heat_source__none(): + assert are_all_terminal_heat_sources_electric(TEST_RMD, ["terminal_1"]) == False diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_heat_sources_hot_water.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_heat_sources_hot_water.py new file mode 100644 index 0000000000..8b2848f80b --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_heat_sources_hot_water.py @@ -0,0 +1,67 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_heat_sources_hot_water import ( + are_all_terminal_heat_sources_hot_water, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "zones": [ + { + "id": "zone 1", + "terminals": [ + {"id": "terminal_1"}, + {"id": "terminal_2", "heating_source": "HOT_WATER"}, + {"id": "terminal_3", "heating_source": "ELECTRIC"}, + ], + }, + ], + } + ], + } + ], + "type": "BASELINE_0", +} + +TEST_RPD_FULL = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__all_terminal_heat_source__hot_water(): + assert are_all_terminal_heat_sources_hot_water(TEST_RMD, ["terminal_2"]) == True + + +def test__all_terminal_heat_source__hot_water_or_null(): + assert ( + are_all_terminal_heat_sources_hot_water(TEST_RMD, ["terminal_1", "terminal_2"]) + == False + ) + + +def test__all_terminal_heat_source__not_hot_water(): + assert ( + are_all_terminal_heat_sources_hot_water(TEST_RMD, ["terminal_2", "terminal_3"]) + == False + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_heat_sources_none_or_null.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_heat_sources_none_or_null.py new file mode 100644 index 0000000000..f057be796e --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_heat_sources_none_or_null.py @@ -0,0 +1,67 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_heat_sources_none_or_null import ( + are_all_terminal_heat_sources_none_or_null, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "zones": [ + { + "id": "zone 1", + "terminals": [ + {"id": "terminal_1", "heating_source": "NONE"}, + {"id": "terminal_2", "heating_source": "HOT_WATER"}, + {"id": "terminal_3"}, + ], + }, + ], + } + ], + } + ], + "type": "BASELINE_0", +} + +TEST_RPD_FULL = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__all_terminal_heat_source__none_or_null(): + assert ( + are_all_terminal_heat_sources_none_or_null( + TEST_RMD, ["terminal_1", "terminal_3"] + ) + == True + ) + + +def test__all_terminal_heat_source__none_or_null_one_hw(): + assert ( + are_all_terminal_heat_sources_none_or_null( + TEST_RMD, ["terminal_2", "terminal_3"] + ) + == False + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_heating_loops_attached_to_boiler.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_heating_loops_attached_to_boiler.py new file mode 100644 index 0000000000..6a6a3ff968 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_heating_loops_attached_to_boiler.py @@ -0,0 +1,79 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_heating_loops_attached_to_boiler import ( + are_all_terminal_heating_loops_attached_to_boiler, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "zones": [ + { + "id": "zone 1", + "terminals": [ + {"id": "terminal_1", "heating_from_loop": "HW_Loop_1"}, + {"id": "terminal_2", "heating_from_loop": "HW_Loop_2"}, + {"id": "terminal_3"}, + ], + }, + ], + } + ], + } + ], + "boilers": [{"id": "boiler_1", "loop": "HW_Loop_1"}], + "fluid_loops": [ + {"id": "HW_Loop_1", "type": "HEATING"}, + {"id": "HW_Loop_2", "type": "HEATING"}, + {"id": "CHW_Loop_1", "type": "COOLING"}, + ], + "external_fluid_sources": [ + {"id": "fluid_loop_1", "loop": "HW_Loop_2", "type": "HOT_WATER"}, + ], + "type": "BASELINE_0", +} + +TEST_RPD_FULL = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__all_terminal_heating_loops_boiler(): + assert ( + are_all_terminal_heating_loops_attached_to_boiler(TEST_RMD, ["terminal_1"]) + == True + ) + + +def test__not_all_terminal_heating_source_boiler(): + assert ( + are_all_terminal_heating_loops_attached_to_boiler(TEST_RMD, ["terminal_2"]) + == False + ) + + +def test__not_all_terminal_heating_source_boiler_null(): + assert ( + are_all_terminal_heating_loops_attached_to_boiler(TEST_RMD, ["terminal_3"]) + == False + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_heating_loops_purchased_heating.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_heating_loops_purchased_heating.py new file mode 100644 index 0000000000..282b9abdb9 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_heating_loops_purchased_heating.py @@ -0,0 +1,91 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_heating_loops_purchased_heating import ( + are_all_terminal_heating_loops_purchased_heating, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "zones": [ + { + "id": "zone 1", + "terminals": [ + {"id": "terminal_1", "heating_from_loop": "HW_Loop_1"}, + {"id": "terminal_2", "heating_from_loop": "CHW_Loop_1"}, + {"id": "terminal_3"}, + ], + }, + { + "id": "zone 2", + "terminals": [ + { + "id": "terminal_4" + }, # intentionally omit `heating_from_loop` key to test out `if are_all_terminal_heating_loops_purchased_heating_flag and heating_from_loop_id:` false case. + ], + }, + ], + } + ], + } + ], + "fluid_loops": [ + {"id": "HW_Loop_1", "type": "HEATING"}, + {"id": "CHW_Loop_1", "type": "COOLING"}, + ], + "external_fluid_sources": [ + {"id": "fluid_loop_1", "loop": "HW_Loop_1", "type": "HOT_WATER"}, + {"id": "fluid_loop_2", "loop": "ST_Loop_1", "type": "STEAM"}, + {"id": "fluid_loop_3", "loop": "CHW_Loop_1", "type": "CHILLED_WATER"}, + ], + "type": "BASELINE_0", +} + +TEST_RPD = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__are_all_terminal_heating_loops_purchased_heating__all_terminal_heating_loops_purchased_heating(): + assert ( + are_all_terminal_heating_loops_purchased_heating(TEST_RMD, ["terminal_1"]) + == True + ) + + +def test__are_all_terminal_heating_loops_purchased_heating__all_terminal_heating_source_not_purchased_heating(): + assert ( + are_all_terminal_heating_loops_purchased_heating( + TEST_RMD, ["terminal_2", "terminal_3"] + ) + == False + ) + + +def test__are_all_terminal_heating_loops_purchased_heating__not_all_terminal_heating_source_purchased_heating(): + assert ( + are_all_terminal_heating_loops_purchased_heating( + TEST_RMD, ["terminal_1", "terminal_2"] + ) + == False + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_supplies_ducted.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_supplies_ducted.py new file mode 100644 index 0000000000..dd0ca4da03 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_supplies_ducted.py @@ -0,0 +1,61 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_supplies_ducted import ( + are_all_terminal_supplies_ducted, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "zones": [ + { + "id": "zone 1", + "terminals": [ + {"id": "terminal_1", "is_supply_ducted": True}, + {"id": "terminal_2", "is_supply_ducted": False}, + {"id": "terminal_3"}, + ], + }, + ], + } + ], + } + ], + "type": "BASELINE_0", +} + +TEST_RPD_FULL = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__all_terminal_supplies_ducted_true(): + assert are_all_terminal_supplies_ducted(TEST_RMD, ["terminal_1"]) == True + + +def test__all_terminal_supplies_ducted_false(): + assert are_all_terminal_supplies_ducted(TEST_RMD, ["terminal_2"]) == False + + +def test__all_terminal_supplies_ducted_null(): + assert are_all_terminal_supplies_ducted(TEST_RMD, ["terminal_3"]) == False diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_types_CAV.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_types_CAV.py new file mode 100644 index 0000000000..3026b38a18 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_types_CAV.py @@ -0,0 +1,85 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_types_CAV import ( + are_all_terminal_types_cav, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_types_CAV_with_none_equal_to_null import ( + are_all_terminal_types_cav_with_none_equal_to_null, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "zones": [ + { + "id": "zone 1", + "terminals": [ + {"id": "terminal_1", "type": "CONSTANT_AIR_VOLUME"}, + {"id": "terminal_2", "type": "VARIABLE_AIR_VOLUME"}, + {"id": "terminal_3"}, + ], + }, + ], + } + ], + } + ], + "type": "BASELINE_0", +} + +TEST_RPD_FULL = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__all_terminal_type_CAV(): + assert are_all_terminal_types_cav(TEST_RMD, ["terminal_1"]) == True + + +def test__not_all_terminal_type_CAV(): + assert are_all_terminal_types_cav(TEST_RMD, ["terminal_2"]) == False + + +def test__none_terminal_type_CAV(): + assert are_all_terminal_types_cav(TEST_RMD, ["terminal_3"]) == True + + +def test__all_terminal_type_CAV_none_equal_null(): + assert ( + are_all_terminal_types_cav_with_none_equal_to_null(TEST_RMD, ["terminal_1"]) + == True + ) + + +def test__not_all_terminal_type_CAV_none_equal_null(): + assert ( + are_all_terminal_types_cav_with_none_equal_to_null(TEST_RMD, ["terminal_2"]) + == False + ) + + +def test__none_terminal_type_CAV_none_equal_null(): + assert ( + are_all_terminal_types_cav_with_none_equal_to_null(TEST_RMD, ["terminal_3"]) + == False + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_types_VAV.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_types_VAV.py new file mode 100644 index 0000000000..c05cabb653 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_are_all_terminal_types_VAV.py @@ -0,0 +1,65 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_types_VAV import ( + are_all_terminal_types_VAV, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "zones": [ + { + "id": "zone 1", + "terminals": [ + {"id": "terminal_1", "type": "VARIABLE_AIR_VOLUME"}, + {"id": "terminal_2", "type": "CONSTANT_AIR_VOLUME"}, + {"id": "terminal_3"}, + ], + }, + ], + } + ], + } + ], + "type": "BASELINE_0", +} + +TEST_RPD_FULL = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__all_terminal_type_VAV(): + assert are_all_terminal_types_VAV(TEST_RMD, ["terminal_1"]) == True + + +def test__not_all_terminal_type_VAV(): + assert are_all_terminal_types_VAV(TEST_RMD, ["terminal_2"]) == False + + +def test__none_terminal_type_VAV(): + assert are_all_terminal_types_VAV(TEST_RMD, ["terminal_3"]) == True + + +def test__empty_terminal_type_VAV(): + assert are_all_terminal_types_VAV(TEST_RMD, []) == False diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_do_all_terminals_have_one_fan.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_do_all_terminals_have_one_fan.py new file mode 100644 index 0000000000..17c538b6fe --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_do_all_terminals_have_one_fan.py @@ -0,0 +1,70 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.do_all_terminals_have_one_fan import ( + do_all_terminals_have_one_fan, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "zones": [ + { + "id": "zone 1", + "terminals": [ + { + "id": "terminal_1", + "fan": {"id": "fan 1"}, + }, + { + "id": "terminal_2", + "fan": {"id": "fan 2"}, + }, + { + "id": "terminal_3", + }, + ], + }, + ], + } + ], + } + ], + "type": "BASELINE_0", +} + +TEST_RPD_FULL = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__all_terminal_have_one_fan(): + assert do_all_terminals_have_one_fan(TEST_RMD, ["terminal_1", "terminal_2"]) == True + + +def test__not_all_terminal_have_one_fan(): + assert ( + do_all_terminals_have_one_fan( + TEST_RMD, ["terminal_1", "terminal_2", "terminal_3"] + ) + == False + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_does_each_zone_have_only_one_terminal.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_does_each_zone_have_only_one_terminal.py new file mode 100644 index 0000000000..00bd4cb273 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_does_each_zone_have_only_one_terminal.py @@ -0,0 +1,81 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.does_each_zone_have_only_one_terminal import ( + does_each_zone_have_only_one_terminal, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "zones": [ + { + # case with one terminal + "id": "zone_1", + "terminals": [ + {"id": "terminal_1", "cooling_source": "NONE"} + ], + }, + { + # case with more than one terminal + "id": "zone_2", + "terminals": [ + {"id": "terminal_1", "cooling_source": "NONE"}, + {"id": "terminal_2", "cooling_source": "NONE"}, + ], + }, + { + # case with empty terminal + "id": "zone_3", + "terminals": [], + }, + { + # case with no terminal + "id": "zone_4", + }, + ], + } + ], + } + ], + "type": "BASELINE_0", +} + +TEST_RPD_FULL = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__each_zone_have_only_one_terminal(): + assert does_each_zone_have_only_one_terminal(TEST_RMD, ["zone_1"]) == True + + +def test__zone_have_two_terminal(): + assert does_each_zone_have_only_one_terminal(TEST_RMD, ["zone_2"]) == False + + +def test__zone_have_empty_terminal(): + assert does_each_zone_have_only_one_terminal(TEST_RMD, ["zone_3"]) == False + + +def test__zone_have_no_terminal(): + assert does_each_zone_have_only_one_terminal(TEST_RMD, ["zone_4"]) == False diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_get_dict_with_terminal_units_and_zones.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_get_dict_with_terminal_units_and_zones.py new file mode 100644 index 0000000000..755d5d936f --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_get_dict_with_terminal_units_and_zones.py @@ -0,0 +1,251 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.get_dict_with_terminal_units_and_zones import ( + get_dict_with_terminal_units_and_zones, +) +from rct229.schema.validate import schema_validate_rpd + +rpd_model = { + "id": "ASHRAE229 1", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Building Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 1", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "VAV Air Terminal 1", + "is_supply_ducted": True, + "heating_from_loop": "Boiler Loop 1", + "heating_source": "HOT_WATER", + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 7", + } + ], + }, + { + "id": "Thermal Zone 2", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "VAV Air Terminal 2", + "is_supply_ducted": True, + "heating_from_loop": "Boiler Loop 1", + "heating_source": "HOT_WATER", + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 7a", + } + ], + }, + { + "id": "Thermal Zone 3", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "VAV Air Terminal 3", + "is_supply_ducted": True, + "heating_from_loop": "Purchased HW Loop 1", + "heating_source": "HOT_WATER", + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 7b", + } + ], + }, + { + "id": "Thermal Zone 4", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "VAV Air Terminal 4", + "is_supply_ducted": True, + "heating_from_loop": "Purchased HW Loop 1", + "heating_source": "HOT_WATER", + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 7c", + } + ], + }, + ], + "heating_ventilating_air_conditioning_systems": [ + { + "id": "System 7", + "preheat_system": { + "id": "Preheat Coil 1", + "type": "FLUID_LOOP", + "hot_water_loop": "Boiler Loop 1", + }, + "cooling_system": { + "id": "Cooling Coil 1", + "type": "FLUID_LOOP", + "chilled_water_loop": "Chiller Loop 1", + }, + "fan_system": { + "id": "VAV Fan System 1", + "fan_control": "VARIABLE_SPEED_DRIVE", + "supply_fans": [{"id": "Supply Fan 1"}], + "return_fans": [{"id": "Return Fan 1"}], + }, + }, + { + "id": "System 7a", + "preheat_system": { + "id": "Preheat Coil 2", + "type": "FLUID_LOOP", + "hot_water_loop": "Boiler Loop 1", + }, + "cooling_system": { + "id": "Cooling Coil 2", + "type": "FLUID_LOOP", + "chilled_water_loop": "Purchased CHW Loop 1", + }, + "fan_system": { + "id": "VAV Fan System 2", + "fan_control": "VARIABLE_SPEED_DRIVE", + "supply_fans": [{"id": "Supply Fan 2"}], + "return_fans": [{"id": "Return Fan 2"}], + }, + }, + { + "id": "System 7b", + "preheat_system": { + "id": "Preheat Coil 3", + "type": "FLUID_LOOP", + "hot_water_loop": "Purchased HW Loop 1", + }, + "cooling_system": { + "id": "Cooling Coil 3", + "type": "FLUID_LOOP", + "chilled_water_loop": "Chiller Loop 1", + }, + "fan_system": { + "id": "VAV Fan System 3", + "fan_control": "VARIABLE_SPEED_DRIVE", + "supply_fans": [{"id": "Supply Fan 3"}], + "return_fans": [{"id": "Return Fan 3"}], + }, + }, + { + "id": "System 7c", + "preheat_system": { + "id": "Preheat Coil 4", + "type": "FLUID_LOOP", + "hot_water_loop": "Purchased HW Loop 1", + }, + "cooling_system": { + "id": "Cooling Coil 4", + "type": "FLUID_LOOP", + "chilled_water_loop": "Purchased CHW Loop 1", + }, + "fan_system": { + "id": "VAV Fan System 4", + "fan_control": "VARIABLE_SPEED_DRIVE", + "supply_fans": [{"id": "Supply Fan 4"}], + "return_fans": [{"id": "Return Fan 4"}], + }, + }, + ], + } + ], + } + ], + "boilers": [ + { + "id": "Boiler 1", + "loop": "Boiler Loop 1", + "design_capacity": 117228.44444444445, + } + ], + "chillers": [ + { + "id": "Chiller 1", + "cooling_loop": "Chiller Loop 1", + "condensing_loop": "Condenser Loop 1", + } + ], + "pumps": [ + { + "id": "Boiler Pump 1", + "loop_or_piping": "Boiler Loop 1", + "speed_control": "FIXED_SPEED", + }, + { + "id": "Chiller Pump 1", + "loop_or_piping": "Chiller Loop 1", + "speed_control": "FIXED_SPEED", + }, + ], + "fluid_loops": [ + { + "id": "Boiler Loop 1", + "type": "HEATING", + "heating_design_and_control": { + "id": "DAC1", + "minimum_flow_fraction": 0.25, + }, + }, + { + "id": "Chiller Loop 1", + "type": "COOLING", + }, + { + "id": "Purchased HW Loop 1", + "type": "HEATING", + }, + { + "id": "Purchased CHW Loop 1", + "type": "COOLING", + }, + ], + "external_fluid_sources": [ + { + "id": "Purchased HW", + "loop": "Purchased HW Loop 1", + "type": "HOT_WATER", + }, + { + "id": "Purchased CHW", + "loop": "Purchased CHW Loop 1", + "type": "CHILLED_WATER", + }, + ], + "type": "BASELINE_0", + } + ], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(rpd_model) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test_get_dict_with_terminal_units_and_zones(): + assert get_dict_with_terminal_units_and_zones( + rpd_model["ruleset_model_descriptions"][0] + ) == { + "VAV Air Terminal 1": ["Thermal Zone 1"], + "VAV Air Terminal 2": ["Thermal Zone 2"], + "VAV Air Terminal 3": ["Thermal Zone 3"], + "VAV Air Terminal 4": ["Thermal Zone 4"], + } diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_cooling_type_DX.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_cooling_type_DX.py new file mode 100644 index 0000000000..458fda5b47 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_cooling_type_DX.py @@ -0,0 +1,64 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_cooling_type_DX import ( + is_hvac_sys_cooling_type_dx, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "heating_ventilating_air_conditioning_systems": [ + { + "id": "hvac_1", + "cooling_system": { + "id": "cooling_1", + "type": "DIRECT_EXPANSION", + }, + }, + { + "id": "hvac_2", + "cooling_system": { + "id": "cooling_2", + "type": "NON_MECHANICAL", + }, + }, + ], + } + ], + } + ], + "type": "BASELINE_0", +} + + +TEST_RPD_FULL = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test_hvac_sys_cooling_type_DX_true(): + assert is_hvac_sys_cooling_type_dx(TEST_RMD, "hvac_1") == True + + +def test_hvac_sys_cooling_type_DX_false(): + assert is_hvac_sys_cooling_type_dx(TEST_RMD, "hvac_2") == False diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_cooling_type_fluid_loop.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_cooling_type_fluid_loop.py new file mode 100644 index 0000000000..c671e46299 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_cooling_type_fluid_loop.py @@ -0,0 +1,80 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_cooling_type_fluid_loop import ( + is_hvac_sys_cooling_type_fluid_loop, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "heating_ventilating_air_conditioning_systems": [ + { + "id": "hvac_1", + "cooling_system": { + "id": "cooling_1", + "type": "FLUID_LOOP", + "chilled_water_loop": "CHW_Loop_1", + }, + }, + { + # Case where the preheat system has a wrong hot_water_loop id + "id": "hvac_2", + "cooling_system": { + "id": "cooling_2", + "type": "DIRECT_EXPANSION", + }, + }, + { + # Case where there is no preheat system + "id": "hvac_3", + "cooling_system": {"id": "cooling_3"}, + }, + ], + } + ], + } + ], + "fluid_loops": [ + { + "id": "CHW_Loop_1", + "type": "COOLING", + } + ], + "type": "BASELINE_0", +} + +TEST_RPD_FULL = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test_hvac_sys_cooling_type_fluid_loop(): + assert is_hvac_sys_cooling_type_fluid_loop(TEST_RMD, "hvac_1") == True + + +def test__hvac_sys_cooling_type_DX(): + assert is_hvac_sys_cooling_type_fluid_loop(TEST_RMD, "hvac_2") == False + + +def test__hvac_sys_cooling_type_no_cooling_system(): + assert is_hvac_sys_cooling_type_fluid_loop(TEST_RMD, "hvac_3") == False diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_cooling_type_none.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_cooling_type_none.py new file mode 100644 index 0000000000..4939a24a7a --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_cooling_type_none.py @@ -0,0 +1,80 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_cooling_type_none import ( + is_hvac_sys_cooling_type_none, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "heating_ventilating_air_conditioning_systems": [ + { + "id": "hvac_1", + "cooling_system": { + "id": "cooling_1", + "type": "NONE", + "chilled_water_loop": "CHW_Loop_1", + }, + }, + { + # Case where the preheat system has a wrong hot_water_loop id + "id": "hvac_2", + "cooling_system": { + "id": "cooling_2", + "type": "DIRECT_EXPANSION", + }, + }, + { + # Case where there is no preheat system + "id": "hvac_3", + "cooling_system": {"id": "cooling_3"}, + }, + ], + } + ], + } + ], + "fluid_loops": [ + { + "id": "CHW_Loop_1", + "type": "COOLING", + } + ], + "type": "BASELINE_0", +} + +TEST_RPD_FULL = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test_hvac_sys_cooling_type_none(): + assert is_hvac_sys_cooling_type_none(TEST_RMD, "hvac_1") == True + + +def test__hvac_sys_cooling_type_DX(): + assert is_hvac_sys_cooling_type_none(TEST_RMD, "hvac_2") == False + + +def test__hvac_sys_cooling_type_null(): + assert is_hvac_sys_cooling_type_none(TEST_RMD, "hvac_3") == True diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_cooling_type_none_or_non_mechanical.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_cooling_type_none_or_non_mechanical.py new file mode 100644 index 0000000000..5d4ae2ba0d --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_cooling_type_none_or_non_mechanical.py @@ -0,0 +1,92 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_cooling_type_none_or_non_mechanical import ( + is_hvac_sys_cooling_type_none_or_non_mechanical, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "heating_ventilating_air_conditioning_systems": [ + { + "id": "hvac_1", + "cooling_system": { + "id": "cooling_1", + "type": "NONE", + "chilled_water_loop": "CHW_Loop_1", + }, + }, + { + # Case where the preheat system has a wrong hot_water_loop id + "id": "hvac_2", + "cooling_system": { + "id": "cooling_2", + "type": "DIRECT_EXPANSION", + }, + }, + { + # Case where there is no cooling system + "id": "hvac_3", + "cooling_system": {"id": "cooling_3"}, + }, + { + # Case where there is no cooling system + "id": "hvac_4", + "cooling_system": { + "id": "cooling_4", + "type": "NON_MECHANICAL", + }, + }, + ], + } + ], + } + ], + "fluid_loops": [ + { + "id": "CHW_Loop_1", + "type": "COOLING", + } + ], + "type": "BASELINE_0", +} + +TEST_RPD_FULL = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test_hvac_sys_cooling_type_non_mechanical_none(): + assert is_hvac_sys_cooling_type_none_or_non_mechanical(TEST_RMD, "hvac_1") == True + + +def test__hvac_sys_cooling_type_non_mechanical_DX(): + assert is_hvac_sys_cooling_type_none_or_non_mechanical(TEST_RMD, "hvac_2") == False + + +def test__hvac_sys_cooling_type_non_mechanical_null(): + assert is_hvac_sys_cooling_type_none_or_non_mechanical(TEST_RMD, "hvac_3") == True + + +def test__hvac_sys_cooling_type_non_mechanical(): + assert is_hvac_sys_cooling_type_none_or_non_mechanical(TEST_RMD, "hvac_4") == True diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_fan_sys_CV.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_fan_sys_CV.py new file mode 100644 index 0000000000..3ca2668777 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_fan_sys_CV.py @@ -0,0 +1,73 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fan_sys_CV import ( + is_hvac_sys_fan_sys_cv, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "heating_ventilating_air_conditioning_systems": [ + { + "id": "hvac_1", + "fan_system": { + "id": "fan_1", + "fan_control": "CONSTANT", + }, + }, + { + # Case where the preheat system has a wrong hot_water_loop id + "id": "hvac_2", + "fan_system": { + "id": "fan_2", + "fan_control": "MULTISPEED", + }, + }, + { + # Case where there is no preheat system + "id": "hvac_3", + "fan_system": {"id": "fan_3"}, + }, + ], + } + ], + } + ], + "type": "BASELINE_0", +} + +TEST_RPD_FULL = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test_hvac_sys_fan_control_cd(): + assert is_hvac_sys_fan_sys_cv(TEST_RMD, "hvac_1") == True + + +def test_hvac_sys_fan_control_ms(): + assert is_hvac_sys_fan_sys_cv(TEST_RMD, "hvac_2") == False + + +def test_hvac_sys_fan_control_null(): + assert is_hvac_sys_fan_sys_cv(TEST_RMD, "hvac_3") == False diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_fan_sys_vsd.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_fan_sys_vsd.py new file mode 100644 index 0000000000..8300c5f2db --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_fan_sys_vsd.py @@ -0,0 +1,74 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fan_sys_VSD import ( + is_hvac_sys_fan_sys_vsd, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "heating_ventilating_air_conditioning_systems": [ + { + "id": "hvac_1", + "fan_system": { + "id": "fan_1", + "fan_control": "VARIABLE_SPEED_DRIVE", + }, + }, + { + # Case where the preheat system has a wrong hot_water_loop id + "id": "hvac_2", + "fan_system": { + "id": "fan_2", + "fan_control": "MULTISPEED", + }, + }, + { + # Case where there is no preheat system + "id": "hvac_3", + "fan_system": {"id": "fan_3"}, + }, + ], + } + ], + } + ], + "type": "BASELINE_0", +} + + +TEST_RPD_FULL = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test_hvac_sys_fan_control_vsd(): + assert is_hvac_sys_fan_sys_vsd(TEST_RMD, "hvac_1") == True + + +def test_hvac_sys_fan_control_ms(): + assert is_hvac_sys_fan_sys_vsd(TEST_RMD, "hvac_2") == False + + +def test_hvac_sys_fan_control_null(): + assert is_hvac_sys_fan_sys_vsd(TEST_RMD, "hvac_3") == False diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_fluid_loop_attached_to_boiler.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_fluid_loop_attached_to_boiler.py new file mode 100644 index 0000000000..55319d4b87 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_fluid_loop_attached_to_boiler.py @@ -0,0 +1,104 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fluid_loop_attached_to_boiler import ( + is_hvac_sys_fluid_loop_attached_to_boiler, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "heating_ventilating_air_conditioning_systems": [ + { + "id": "hvac_1", + "heating_system": { + "id": "heating_system", + "hot_water_loop": "HW_Loop_1", + }, + }, + { + # Case where the preheat system has a wrong hot_water_loop id + "id": "hvac_2", + "heating_system": { + "id": "heating_system_2", + "hot_water_loop": "HW_Loop_2", + }, + }, + { + # Case where the hot water type fluid type is not heating + "id": "hvac_3", + "heating_system": { + "id": "heating_system_3", + "hot_water_loop": "HW_Loop_3", + }, + }, + { + # Case where there is no preheat system + "id": "hvac_4" + }, + ], + } + ], + } + ], + "boilers": [ + { + "id": "boiler_1", + "loop": "HW_Loop_1", + }, + { + "id": "boiler_1", + "loop": "HW_Loop_3", + }, + ], + "fluid_loops": [ + { + "id": "HW_Loop_1", + "type": "HEATING", + }, + { + "id": "HW_Loop_3", + "type": "HEATING_AND_COOLING", + }, + ], + "type": "BASELINE_0", +} + +TEST_RPD_FULL = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__heating_attached_to_boiler(): + assert is_hvac_sys_fluid_loop_attached_to_boiler(TEST_RMD, "hvac_1") == True + + +def test__preheat_attached_to_boiler_failed_fluidloop(): + assert is_hvac_sys_fluid_loop_attached_to_boiler(TEST_RMD, "hvac_2") == False + + +def test__preheat_attached_to_boiler_failed_no_preheat(): + assert is_hvac_sys_fluid_loop_attached_to_boiler(TEST_RMD, "hvac_4") == False + + +def test__preheat_attached_to_boiler_failed_type_not_heating(): + assert is_hvac_sys_fluid_loop_attached_to_boiler(TEST_RMD, "hvac_3") == False diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_fluid_loop_attached_to_chiller.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_fluid_loop_attached_to_chiller.py new file mode 100644 index 0000000000..8cfcdcb5e9 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_fluid_loop_attached_to_chiller.py @@ -0,0 +1,90 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fluid_loop_attached_to_chiller import ( + is_hvac_sys_fluid_loop_attached_to_chiller, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "heating_ventilating_air_conditioning_systems": [ + { + "id": "hvac_1", + "cooling_system": { + "id": "cooling_system", + "chilled_water_loop": "Secondary_Loop_1", + }, + }, + { + # Case where the cooling system has a wrong chilled_water_loop id + "id": "hvac_2", + "cooling_system": { + "id": "cooling_system", + "chilled_water_loop": "CHW_Loop_2", + }, + }, + { + # Case where there is no cooling system + "id": "hvac_3", + }, + ], + } + ], + } + ], + "chillers": [ + { + "id": "chiller_1", + "cooling_loop": "CHW_Loop_1", + }, + { + "id": "chiller_2", + "cooling_loop": "CHW_Loop_2", + }, + ], + "fluid_loops": [ + { + "id": "CHW_Loop_1", + "type": "COOLING", + "child_loops": [{"id": "Secondary_Loop_1", "type": "COOLING"}], + }, + {"id": "CHW_Loop_2"}, + ], + "type": "BASELINE_0", +} + +TEST_RPD_FULL = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__cooling_attached_to_chiller(): + assert is_hvac_sys_fluid_loop_attached_to_chiller(TEST_RMD, "hvac_1") == True + + +def test__cooling_attached_to_chiller_failed_fluidloop(): + assert is_hvac_sys_fluid_loop_attached_to_chiller(TEST_RMD, "hvac_2") == False + + +def test__cooling_attached_to_chiller_failed_no_cooling(): + assert is_hvac_sys_fluid_loop_attached_to_chiller(TEST_RMD, "hvac_3") == False diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_fluid_loop_purchased_chw.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_fluid_loop_purchased_chw.py new file mode 100644 index 0000000000..ac07066fdb --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_fluid_loop_purchased_chw.py @@ -0,0 +1,80 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fluid_loop_purchased_CHW import ( + is_hvac_sys_fluid_loop_purchased_chw, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "heating_ventilating_air_conditioning_systems": [ + { + "id": "hvac_1", + "cooling_system": { + "id": "cooling_system_1", + "chilled_water_loop": "CHW_Loop_1", + }, + }, + { + # Case where the cooling system has a wrong hot_water_loop id + "id": "hvac_2", + "cooling_system": { + "id": "cooling_system_2", + "chilled_water_loop": "HW_Loop_1", + }, + }, + { + # Case where there is no cooling system + "id": "hvac_3" + }, + ], + } + ], + } + ], + "fluid_loops": [ + {"id": "HW_Loop_1", "type": "HEATING"}, + {"id": "CHW_Loop_1", "type": "COOLING"}, + ], + "external_fluid_sources": [ + {"id": "fluid_loop_2", "loop": "HW_Loop_1", "type": "HOT_WATER"}, + {"id": "fluid_loop_1", "loop": "CHW_Loop_1", "type": "CHILLED_WATER"}, + ], + "type": "BASELINE_0", +} + +TEST_RPD_FULL = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__cooling_fluid_loop_purchased_cooling_chw(): + assert is_hvac_sys_fluid_loop_purchased_chw(TEST_RMD, "hvac_1") == True + + +def test__cooling_fluid_loop_purchased_cooling_wrong_fluid_loop(): + assert is_hvac_sys_fluid_loop_purchased_chw(TEST_RMD, "hvac_2") == False + + +def test__cooling_fluid_loop_purchased_cooling_no_cooling_sys(): + assert is_hvac_sys_fluid_loop_purchased_chw(TEST_RMD, "hvac_3") == False diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_fluid_loop_purchased_heating.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_fluid_loop_purchased_heating.py new file mode 100644 index 0000000000..60e5980ac0 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_fluid_loop_purchased_heating.py @@ -0,0 +1,94 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fluid_loop_purchased_heating import ( + is_hvac_sys_fluid_loop_purchased_heating, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "heating_ventilating_air_conditioning_systems": [ + { + "id": "hvac_1", + "heating_system": { + "id": "heating_system_1", + "hot_water_loop": "HW_Loop_1", + }, + }, + { + # Case where the preheat system has a wrong hot_water_loop id + "id": "hvac_2", + "heating_system": { + "id": "heating_system_2", + "hot_water_loop": "ST_Loop_1", + }, + }, + { + # Case where there is no preheat system + "id": "hvac_3", + "heating_system": { + "id": "heating_system_3", + "hot_water_loop": "CHW_Loop_1", + }, + }, + { + # Case where there is no preheat system + "id": "hvac_4" + }, + ], + } + ], + } + ], + "fluid_loops": [ + {"id": "HW_Loop_1", "type": "HEATING"}, + {"id": "ST_Loop_1", "type": "HEATING"}, + {"id": "CHW_Loop_1", "type": "COOLING"}, + ], + "external_fluid_sources": [ + {"id": "fluid_loop_1", "loop": "HW_Loop_1", "type": "HOT_WATER"}, + {"id": "fluid_loop_2", "loop": "ST_Loop_1", "type": "STEAM"}, + {"id": "fluid_loop_3", "loop": "CHW_Loop_1", "type": "CHILLED_WATER"}, + ], + "type": "BASELINE_0", +} + +TEST_RPD_FULL = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__fluid_loop_purchased_heating_hw(): + assert is_hvac_sys_fluid_loop_purchased_heating(TEST_RMD, "hvac_1") == True + + +def test__fluid_loop_purchased_heating_steam(): + assert is_hvac_sys_fluid_loop_purchased_heating(TEST_RMD, "hvac_2") == True + + +def test__fluid_loop_purchased_heating_not_external(): + assert is_hvac_sys_fluid_loop_purchased_heating(TEST_RMD, "hvac_3") == False + + +def test__fluid_loop_purchased_heating_no_hot_water(): + assert is_hvac_sys_fluid_loop_purchased_heating(TEST_RMD, "hvac_4") == False diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_heating_type_elec_resistance.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_heating_type_elec_resistance.py new file mode 100644 index 0000000000..c5156f5d32 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_heating_type_elec_resistance.py @@ -0,0 +1,85 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_heating_type_elec_resistance import ( + is_hvac_sys_heating_type_elec_resistance, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "heating_ventilating_air_conditioning_systems": [ + { + "id": "hvac_1", + "heating_system": { + "id": "heating_1", + "type": "ELECTRIC_RESISTANCE", + }, + }, + { + # Case where the preheat system has a wrong hot_water_loop id + "id": "hvac_2", + "heating_system": { + "id": "heating_2", + "type": "FLUID_LOOP", + "hot_water_loop": "HW_Loop_2", + }, + }, + { + # Case where there is no preheat system + "id": "hvac_3", + }, + ], + } + ], + } + ], + "boilers": [ + { + "id": "boiler_1", + "loop": "HW_Loop_1", + } + ], + "fluid_loops": [ + { + "id": "HW_Loop_1", + "type": "HEATING", + } + ], + "type": "BASELINE_0", +} + +TEST_RPD_FULL = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test_hvac_sys_heating_type_electric_resistance(): + assert is_hvac_sys_heating_type_elec_resistance(TEST_RMD, "hvac_1") == True + + +def test__hvac_sys_heating_type_fluid_loop(): + assert is_hvac_sys_heating_type_elec_resistance(TEST_RMD, "hvac_2") == False + + +def test__hvac_sys_heating_type_no_heating_system(): + assert is_hvac_sys_heating_type_elec_resistance(TEST_RMD, "hvac_3") == False diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_heating_type_fluid_loop.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_heating_type_fluid_loop.py new file mode 100644 index 0000000000..83dd32012f --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_heating_type_fluid_loop.py @@ -0,0 +1,86 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_heating_type_fluid_loop import ( + is_hvac_sys_heating_type_fluid_loop, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "heating_ventilating_air_conditioning_systems": [ + { + "id": "hvac_1", + "heating_system": { + "id": "heating_1", + "type": "FLUID_LOOP", + "hot_water_loop": "HW_Loop_1", + }, + }, + { + # Case where the preheat system has a wrong hot_water_loop id + "id": "hvac_2", + "heating_system": { + "id": "heating_2", + "type": "HEAT_PUMP", + "hot_water_loop": "HW_Loop_2", + }, + }, + { + # Case where there is no preheat system + "id": "hvac_3", + }, + ], + } + ], + } + ], + "boilers": [ + { + "id": "boiler_1", + "loop": "HW_Loop_1", + } + ], + "fluid_loops": [ + { + "id": "HW_Loop_1", + "type": "HEATING", + } + ], + "type": "BASELINE_0", +} + +TEST_RPD_FULL = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test_hvac_sys_heating_type_fluid_loop(): + assert is_hvac_sys_heating_type_fluid_loop(TEST_RMD, "hvac_1") == True + + +def test__hvac_sys_heating_type_heat_pump(): + assert is_hvac_sys_heating_type_fluid_loop(TEST_RMD, "hvac_2") == False + + +def test__hvac_sys_heating_type_no_heating_system(): + assert is_hvac_sys_heating_type_fluid_loop(TEST_RMD, "hvac_3") == False diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_heating_type_furnace.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_heating_type_furnace.py new file mode 100644 index 0000000000..9dd762d200 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_heating_type_furnace.py @@ -0,0 +1,85 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_heating_type_furnace import ( + is_hvac_sys_heating_type_furnace, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "heating_ventilating_air_conditioning_systems": [ + { + "id": "hvac_1", + "heating_system": { + "id": "heating_1", + "type": "FURNACE", + }, + }, + { + # Case where the preheat system has a wrong hot_water_loop id + "id": "hvac_2", + "heating_system": { + "id": "heating_2", + "type": "FLUID_LOOP", + "hot_water_loop": "HW_Loop_2", + }, + }, + { + # Case where there is no preheat system + "id": "hvac_3", + }, + ], + } + ], + } + ], + "boilers": [ + { + "id": "boiler_1", + "loop": "HW_Loop_1", + } + ], + "fluid_loops": [ + { + "id": "HW_Loop_1", + "type": "HEATING", + } + ], + "type": "BASELINE_0", +} + +TEST_RPD_FULL = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test_hvac_sys_heating_type_furnace_true(): + assert is_hvac_sys_heating_type_furnace(TEST_RMD, "hvac_1") == True + + +def test__hvac_sys_heating_type_furnace_false(): + assert is_hvac_sys_heating_type_furnace(TEST_RMD, "hvac_2") == False + + +def test__hvac_sys_heating_type_furnace_no_heating_system(): + assert is_hvac_sys_heating_type_furnace(TEST_RMD, "hvac_3") == False diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_heating_type_heat_pump.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_heating_type_heat_pump.py new file mode 100644 index 0000000000..ceb4cb8e12 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_heating_type_heat_pump.py @@ -0,0 +1,86 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_heating_type_heat_pump import ( + is_hvac_sys_heating_type_heat_pump, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "heating_ventilating_air_conditioning_systems": [ + { + "id": "hvac_1", + "heating_system": { + "id": "heating_1", + "type": "HEAT_PUMP", + "hot_water_loop": "HW_Loop_1", + }, + }, + { + # Case where the preheat system has a wrong hot_water_loop id + "id": "hvac_2", + "heating_system": { + "id": "heating_2", + "type": "FLUID_LOOP", + "hot_water_loop": "HW_Loop_2", + }, + }, + { + # Case where there is no preheat system + "id": "hvac_3", + }, + ], + } + ], + } + ], + "boilers": [ + { + "id": "boiler_1", + "loop": "HW_Loop_1", + } + ], + "fluid_loops": [ + { + "id": "HW_Loop_1", + "type": "HEATING", + } + ], + "type": "BASELINE_0", +} + +TEST_RPD_FULL = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test_hvac_sys_heating_type_heat_pump_true(): + assert is_hvac_sys_heating_type_heat_pump(TEST_RMD, "hvac_1") == True + + +def test__hvac_sys_heating_type_heat_pump_false(): + assert is_hvac_sys_heating_type_heat_pump(TEST_RMD, "hvac_2") == False + + +def test__hvac_sys_heating_type_heat_pump_no_heating_system(): + assert is_hvac_sys_heating_type_heat_pump(TEST_RMD, "hvac_3") == False diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_preheat_fluid_loop_attached_to_boiler.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_preheat_fluid_loop_attached_to_boiler.py new file mode 100644 index 0000000000..0b97f3dd0d --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_preheat_fluid_loop_attached_to_boiler.py @@ -0,0 +1,110 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_preheat_fluid_loop_attached_to_boiler import ( + is_hvac_sys_preheat_fluid_loop_attached_to_boiler, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "heating_ventilating_air_conditioning_systems": [ + { + "id": "hvac_1", + "preheat_system": { + "id": "preheat_system", + "hot_water_loop": "HW_Loop_1", + }, + }, + { + # Case where the preheat system has a wrong hot_water_loop id + "id": "hvac_2", + "preheat_system": { + "id": "preheat_system_2", + "hot_water_loop": "HW_Loop_2", + }, + }, + { + # Case where the hot water type fluid type is not heating + "id": "hvac_3", + "preheat_system": { + "id": "preheat_system_3", + "hot_water_loop": "HW_Loop_3", + }, + }, + { + # Case where there is no preheat system + "id": "hvac_4" + }, + ], + } + ], + } + ], + "boilers": [ + { + "id": "boiler_1", + "loop": "HW_Loop_1", + }, + { + "id": "boiler_1", + "loop": "HW_Loop_3", + }, + ], + "fluid_loops": [ + { + "id": "HW_Loop_1", + "type": "HEATING", + }, + { + "id": "HW_Loop_3", + "type": "HEATING_AND_COOLING", + }, + ], + "type": "BASELINE_0", +} + +TEST_RPD_FULL = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__preheat_attached_to_boiler(): + assert is_hvac_sys_preheat_fluid_loop_attached_to_boiler(TEST_RMD, "hvac_1") == True + + +def test__preheat_attached_to_boiler_failed_fluidloop(): + assert ( + is_hvac_sys_preheat_fluid_loop_attached_to_boiler(TEST_RMD, "hvac_2") == False + ) + + +def test__preheat_attached_to_boiler_failed_no_preheat(): + assert ( + is_hvac_sys_preheat_fluid_loop_attached_to_boiler(TEST_RMD, "hvac_4") == False + ) + + +def test__preheat_attached_to_boiler_failed_type_not_heating(): + assert ( + is_hvac_sys_preheat_fluid_loop_attached_to_boiler(TEST_RMD, "hvac_3") == False + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_preheat_fluid_loop_purchased_heating.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_preheat_fluid_loop_purchased_heating.py new file mode 100644 index 0000000000..4e08605339 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_preheat_fluid_loop_purchased_heating.py @@ -0,0 +1,94 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_preheat_fluid_loop_purchased_heating import ( + is_hvac_sys_preheat_fluid_loop_purchased_heating, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "heating_ventilating_air_conditioning_systems": [ + { + "id": "hvac_1", + "preheat_system": { + "id": "preheat_system_1", + "hot_water_loop": "HW_Loop_1", + }, + }, + { + # Case where the preheat system has a wrong hot_water_loop id + "id": "hvac_2", + "preheat_system": { + "id": "preheat_system_2", + "hot_water_loop": "ST_Loop_1", + }, + }, + { + # Case where there is no preheat system + "id": "hvac_3", + "preheat_system": { + "id": "preheat_system_3", + "hot_water_loop": "CHW_Loop_1", + }, + }, + { + # Case where there is no preheat system + "id": "hvac_4" + }, + ], + } + ], + } + ], + "fluid_loops": [ + {"id": "HW_Loop_1", "type": "HEATING"}, + {"id": "ST_Loop_1", "type": "HEATING"}, + {"id": "CHW_Loop_1", "type": "COOLING"}, + ], + "external_fluid_sources": [ + {"id": "fluid_loop_1", "loop": "HW_Loop_1", "type": "HOT_WATER"}, + {"id": "fluid_loop_2", "loop": "ST_Loop_1", "type": "STEAM"}, + {"id": "fluid_loop_3", "loop": "CHW_Loop_1", "type": "CHILLED_WATER"}, + ], + "type": "BASELINE_0", +} + +TEST_RPD_FULL = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__preheat_fluid_loop_purchased_heating_hw(): + assert is_hvac_sys_preheat_fluid_loop_purchased_heating(TEST_RMD, "hvac_1") == True + + +def test__preheat_fluid_loop_purchased_heating_steam(): + assert is_hvac_sys_preheat_fluid_loop_purchased_heating(TEST_RMD, "hvac_2") == True + + +def test__preheat_fluid_loop_purchased_heating_not_external(): + assert is_hvac_sys_preheat_fluid_loop_purchased_heating(TEST_RMD, "hvac_3") == False + + +def test__preheat_fluid_loop_purchased_heating_no_preheat_sys(): + assert is_hvac_sys_preheat_fluid_loop_purchased_heating(TEST_RMD, "hvac_4") == False diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_preheating_type_elec_resistance.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_preheating_type_elec_resistance.py new file mode 100644 index 0000000000..19d5f4ccd3 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_preheating_type_elec_resistance.py @@ -0,0 +1,85 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_preheating_type_elec_resistance import ( + is_hvac_sys_preheating_type_elec_resistance, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "heating_ventilating_air_conditioning_systems": [ + { + "id": "hvac_1", + "preheat_system": { + "id": "preheat_system", + "hot_water_loop": "HW_Loop_1", + "type": "FLUID_LOOP", + }, + }, + { + # Case where the preheat system has a wrong hot_water_loop id + "id": "hvac_2", + "preheat_system": { + "id": "preheat_system", + "type": "ELECTRIC_RESISTANCE", + }, + }, + { + # Case where there is no preheat system + "id": "hvac_3", + }, + ], + } + ], + } + ], + "boilers": [ + { + "id": "boiler_1", + "loop": "HW_Loop_1", + } + ], + "fluid_loops": [ + { + "id": "HW_Loop_1", + "type": "HEATING", + } + ], + "type": "BASELINE_0", +} + +TEST_RPD_FULL = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__preheatheating_type__fluid_loop(): + assert is_hvac_sys_preheating_type_elec_resistance(TEST_RMD, "hvac_1") == False + + +def test__preheatheating_type__electric_resistance(): + assert is_hvac_sys_preheating_type_elec_resistance(TEST_RMD, "hvac_2") == True + + +def test__preheatheating_type__data_missing(): + assert is_hvac_sys_preheating_type_elec_resistance(TEST_RMD, "hvac_3") == False diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_preheating_type_fluid_loop.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_preheating_type_fluid_loop.py new file mode 100644 index 0000000000..8f7bb150d8 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_sub_functions/test_is_hvac_sys_preheating_type_fluid_loop.py @@ -0,0 +1,86 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_preheating_type_fluid_loop import ( + is_hvac_sys_preheating_type_fluid_loop, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "heating_ventilating_air_conditioning_systems": [ + { + "id": "hvac_1", + "preheat_system": { + "id": "preheat_system", + "hot_water_loop": "HW_Loop_1", + "type": "FLUID_LOOP", + }, + }, + { + # Case where the preheat system has a wrong hot_water_loop id + "id": "hvac_2", + "preheat_system": { + "id": "preheat_system", + "hot_water_loop": "HW_Loop_2", + "type": "HEAT_PUMP", + }, + }, + { + # Case where there is no preheat system + "id": "hvac_3", + }, + ], + } + ], + } + ], + "boilers": [ + { + "id": "boiler_1", + "loop": "HW_Loop_1", + } + ], + "fluid_loops": [ + { + "id": "HW_Loop_1", + "type": "HEATING", + } + ], + "type": "BASELINE_0", +} + +TEST_RPD_FULL = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__preheatheating_type_fluid_loop(): + assert is_hvac_sys_preheating_type_fluid_loop(TEST_RMD, "hvac_1") == True + + +def test__preheatheating_type_heat_pump(): + assert is_hvac_sys_preheating_type_fluid_loop(TEST_RMD, "hvac_2") == False + + +def test__preheatheating_type_data_missing(): + assert is_hvac_sys_preheating_type_fluid_loop(TEST_RMD, "hvac_3") == False diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_test_util.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_test_util.py new file mode 100644 index 0000000000..8e00f205ef --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_hvac_test_util.py @@ -0,0 +1,190 @@ +import json +import os +from pathlib import Path + +from rct229.schema.schema_utils import quantify_rmd +from rct229.schema.validate import schema_validate_rpd +from rct229.utils.utility_functions import ( + has_cooling_system, + has_fan_system, + has_heating_system, + has_preheat_system, +) + +SYSTEM_TYPE_TEST_FILE_PATH = os.path.join( + Path(os.path.dirname(__file__)).parent.parent.parent.parent, + "ruletest_engine", + "ruletest_jsons", + "scripts", + "resources", + "system_types", + "90.1_system_types", +) + + +def load_system_test_file(file_name: str): + with open(os.path.join(SYSTEM_TYPE_TEST_FILE_PATH, file_name)) as f: + system_test_json = json.load(f) + + assert system_test_json, f"Error loading system testing json file: #{file_name}" + return quantify_rmd(system_test_json) + + +TEST_RMD_PASS = { + "id": "ASHRAE229 1", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Building Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 1", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "PTHP Terminal 1", + "is_supply_ducted": True, + "type": "CONSTANT_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "PTHP 1", + } + ], + } + ], + "heating_ventilating_air_conditioning_systems": [ + { + "id": "PTHP 1", + "cooling_system": { + "id": "HP Cooling Coil 1", + "type": "DIRECT_EXPANSION", + }, + "preheat_system": { + "id": "Preheat Coil 1", + "type": "ELECTRIC_RESISTANCE", + }, + "heating_system": { + "id": "HP Heating Coil 1", + "type": "HEAT_PUMP", + }, + "fan_system": { + "id": "CAV Fan System 1", + "fan_control": "CONSTANT", + "supply_fans": [{"id": "Supply Fan 1"}], + }, + } + ], + } + ], + } + ], + } + ], +} + +TEST_RMD_FAIL = { + "id": "ASHRAE229 1", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Building Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 1", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "PTHP Terminal 1", + "is_supply_ducted": True, + "type": "CONSTANT_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "PTHP 1", + } + ], + } + ], + "heating_ventilating_air_conditioning_systems": [ + { + "id": "PTHP 1", + } + ], + } + ], + } + ], + } + ], +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RMD_PASS) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test_has_heating_system_true(): + assert ( + has_heating_system(TEST_RMD_PASS["ruleset_model_descriptions"][0], "PTHP 1") + == True + ) + + +def test_has_heating_system_fail(): + assert ( + has_heating_system(TEST_RMD_FAIL["ruleset_model_descriptions"][0], "PTHP 1") + == False + ) + + +def test_has_cooling_system_true(): + assert ( + has_cooling_system(TEST_RMD_PASS["ruleset_model_descriptions"][0], "PTHP 1") + == True + ) + + +def test_has_cooling_system_fail(): + assert ( + has_cooling_system(TEST_RMD_FAIL["ruleset_model_descriptions"][0], "PTHP 1") + == False + ) + + +def test_has_preheat_system_true(): + assert ( + has_preheat_system(TEST_RMD_PASS["ruleset_model_descriptions"][0], "PTHP 1") + == True + ) + + +def test_has_preheat_system_fail(): + assert ( + has_preheat_system(TEST_RMD_FAIL["ruleset_model_descriptions"][0], "PTHP 1") + == False + ) + + +def test_has_fan_system_true(): + assert ( + has_fan_system(TEST_RMD_PASS["ruleset_model_descriptions"][0], "PTHP 1") == True + ) + + +def test_has_fan_system_fail(): + assert ( + has_fan_system(TEST_RMD_FAIL["ruleset_model_descriptions"][0], "PTHP 1") + == False + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_system_util.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_system_util.py new file mode 100644 index 0000000000..e023a67626 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/baseline_system_util.py @@ -0,0 +1,70 @@ +class HVAC_SYS: + """Class holding HVAC system type value""" + + SYS_1 = "Sys-1" + SYS_1A = "Sys-1a" + SYS_1B = "Sys-1b" + SYS_1C = "Sys-1c" + SYS_2 = "Sys-2" + SYS_3 = "Sys-3" + SYS_3A = "Sys-3a" + SYS_3B = "Sys-3b" + SYS_3C = "Sys-3c" + SYS_4 = "Sys-4" + SYS_5 = "Sys-5" + SYS_5B = "Sys-5b" + SYS_6 = "Sys-6" + SYS_6B = "Sys-6b" + SYS_7 = "Sys-7" + SYS_7A = "Sys-7a" + SYS_7B = "Sys-7b" + SYS_7C = "Sys-7c" + SYS_8 = "Sys-8" + SYS_8A = "Sys-8a" + SYS_8B = "Sys-8b" + SYS_8C = "Sys-8c" + SYS_9 = "Sys-9" + SYS_9B = "Sys-9b" + SYS_10 = "Sys-10" + SYS_11_1 = "Sys-11.1" + SYS_11_1A = "Sys-11.1a" + SYS_11_1B = "Sys-11.1b" + SYS_11_1C = "Sys-11.1c" + SYS_11_2 = "Sys-11.2" + SYS_11_2A = "Sys-11.2a" + SYS_12 = "Sys-12" + SYS_12A = "Sys-12a" + SYS_12B = "Sys-12b" + SYS_13 = "Sys-13" + SYS_13A = "Sys-13a" + UNMATCHED = "Not_Sys" + + +HVAC_SYSTEM_TYPE_DICTIONARY = { + HVAC_SYS.SYS_1: [HVAC_SYS.SYS_1, HVAC_SYS.SYS_1A, HVAC_SYS.SYS_1B, HVAC_SYS.SYS_1C], + HVAC_SYS.SYS_2: [HVAC_SYS.SYS_2], + HVAC_SYS.SYS_3: [HVAC_SYS.SYS_3, HVAC_SYS.SYS_3A, HVAC_SYS.SYS_3B, HVAC_SYS.SYS_3C], + HVAC_SYS.SYS_4: [HVAC_SYS.SYS_4], + HVAC_SYS.SYS_5: [HVAC_SYS.SYS_5, HVAC_SYS.SYS_5B], + HVAC_SYS.SYS_6: [HVAC_SYS.SYS_6, HVAC_SYS.SYS_6B], + HVAC_SYS.SYS_7: [HVAC_SYS.SYS_7, HVAC_SYS.SYS_7A, HVAC_SYS.SYS_7B, HVAC_SYS.SYS_7C], + HVAC_SYS.SYS_8: [HVAC_SYS.SYS_8, HVAC_SYS.SYS_8A, HVAC_SYS.SYS_8B, HVAC_SYS.SYS_8C], + HVAC_SYS.SYS_9: [HVAC_SYS.SYS_9, HVAC_SYS.SYS_9B], + HVAC_SYS.SYS_10: [HVAC_SYS.SYS_10], + HVAC_SYS.SYS_11_1: [ + HVAC_SYS.SYS_11_1, + HVAC_SYS.SYS_11_1A, + HVAC_SYS.SYS_11_1B, + HVAC_SYS.SYS_11_1C, + ], + HVAC_SYS.SYS_11_2: [ + HVAC_SYS.SYS_11_2, + HVAC_SYS.SYS_11_2A, + ], + HVAC_SYS.SYS_12: [ + HVAC_SYS.SYS_12, + HVAC_SYS.SYS_12A, + HVAC_SYS.SYS_12B, + ], + HVAC_SYS.SYS_13: [HVAC_SYS.SYS_13, HVAC_SYS.SYS_13A], +} diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_1.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_1.py new file mode 100644 index 0000000000..c8c0b085ee --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_1.py @@ -0,0 +1,105 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_cool_sources_none_or_null import ( + are_all_terminal_cool_sources_none_or_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_fans_null import ( + are_all_terminal_fans_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_heat_sources_none_or_null import ( + are_all_terminal_heat_sources_none_or_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_supplies_ducted import ( + are_all_terminal_supplies_ducted, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_types_CAV import ( + are_all_terminal_types_cav, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.does_each_zone_have_only_one_terminal import ( + does_each_zone_have_only_one_terminal, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.does_hvac_system_serve_single_zone import ( + does_hvac_system_serve_single_zone, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_cooling_type_DX import ( + is_hvac_sys_cooling_type_dx, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fan_sys_CV import ( + is_hvac_sys_fan_sys_cv, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fluid_loop_attached_to_boiler import ( + is_hvac_sys_fluid_loop_attached_to_boiler, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fluid_loop_purchased_heating import ( + is_hvac_sys_fluid_loop_purchased_heating, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_heating_type_fluid_loop import ( + is_hvac_sys_heating_type_fluid_loop, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.is_baseline_system_1_a import ( + is_baseline_system_1_a, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.is_baseline_system_1_c import ( + is_baseline_system_1_c, +) +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.utility_functions import has_preheat_system + +HEATING_SYSTEM = SchemaEnums.schema_enums["HeatingSystemOptions"] + + +def is_baseline_system_1(rmd_b, hvac_b_id, terminal_unit_id_list, zone_id_list): + """ + Get either Sys-1, Sys-1a, Sys-1b, Sys-1c, or Not_Sys_1 string output which indicates whether the HVAC system is + ASHRAE 90.1 2019 Appendix G system 1 (PTAC), system 1a (system 1 with purchased CHW), system 1b (system 1 with + purchased heating), system 1c (system 1 with purchased CHW and purchased HW). + + Parameters + ---------- + rmd_b JSON, To evaluate if the hvac system is modeled as either Sys-1, Sys-1a, Sys-1b, Sys-1c, or Not_Sys_1 in the B_RMD. + hvac_b_id String, The id of the hvac system to evaluate. + terminal_unit_id_list List, list of terminal unit IDs associated with the HVAC system to be evaluated. These are + sent to this function from the master get_baseline_system_types function. + zone_id_list List, list of zone IDs associated with the HVAC system to be evaluated. These are sent to this + function from the master get_baseline_system_types function. + + Returns The function returns either Sys-1, Sys-1a, Sys-1b, Sys-1c, or Not_Sys_1 string output which indicates + whether the HVAC system is ASHRAE 90.1 2019 Appendix G system 1 (PTAC), system 1a (system 1 with purchased CHW), + system 1b (system 1 with purchased heating), system 1c (system 1 with purchased CHW and purchased HW). ------- + + """ + + is_baseline_system_1_str = HVAC_SYS.UNMATCHED + + if is_baseline_system_1_c(rmd_b, hvac_b_id, terminal_unit_id_list, zone_id_list): + is_baseline_system_1_str = HVAC_SYS.SYS_1C + elif is_baseline_system_1_a(rmd_b, hvac_b_id, terminal_unit_id_list, zone_id_list): + is_baseline_system_1_str = HVAC_SYS.SYS_1A + else: + # check if the hvac system has the required sub systems for system type 1 + # if preheat system DOESN'T exist, has_required_sys=True, else, False + has_required_sys = not has_preheat_system(rmd_b, hvac_b_id) + + are_sys_data_matched = ( + # short-circuit the logic if no required data is found. + has_required_sys + # sub functions handles missing required sys, and return False. + and is_hvac_sys_heating_type_fluid_loop(rmd_b, hvac_b_id) + and is_hvac_sys_fan_sys_cv(rmd_b, hvac_b_id) + and does_hvac_system_serve_single_zone(rmd_b, zone_id_list) + and does_each_zone_have_only_one_terminal(rmd_b, zone_id_list) + and are_all_terminal_heat_sources_none_or_null(rmd_b, terminal_unit_id_list) + and are_all_terminal_cool_sources_none_or_null(rmd_b, terminal_unit_id_list) + and are_all_terminal_fans_null(rmd_b, terminal_unit_id_list) + and are_all_terminal_types_cav(rmd_b, terminal_unit_id_list) + and is_hvac_sys_cooling_type_dx(rmd_b, hvac_b_id) + ) + if are_sys_data_matched: + if is_hvac_sys_fluid_loop_attached_to_boiler(rmd_b, hvac_b_id): + is_baseline_system_1_str = HVAC_SYS.SYS_1 + elif is_hvac_sys_fluid_loop_purchased_heating( + rmd_b, hvac_b_id + ) and not are_all_terminal_supplies_ducted(rmd_b, terminal_unit_id_list): + is_baseline_system_1_str = HVAC_SYS.SYS_1B + return is_baseline_system_1_str diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_10.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_10.py new file mode 100644 index 0000000000..09bb8c6c93 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_10.py @@ -0,0 +1,123 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_cool_sources_none_or_null import ( + are_all_terminal_cool_sources_none_or_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_fans_null import ( + are_all_terminal_fans_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_heat_sources_electric import ( + are_all_terminal_heat_sources_electric, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_heat_sources_none_or_null import ( + are_all_terminal_heat_sources_none_or_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_types_CAV import ( + are_all_terminal_types_cav, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_types_CAV_with_none_equal_to_null import ( + are_all_terminal_types_cav_with_none_equal_to_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.do_all_terminals_have_one_fan import ( + do_all_terminals_have_one_fan, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.does_each_zone_have_only_one_terminal import ( + does_each_zone_have_only_one_terminal, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.does_hvac_system_serve_single_zone import ( + does_hvac_system_serve_single_zone, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_cooling_type_none_or_non_mechanical import ( + is_hvac_sys_cooling_type_none_or_non_mechanical, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fan_sys_CV import ( + is_hvac_sys_fan_sys_cv, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_heating_type_elec_resistance import ( + is_hvac_sys_heating_type_elec_resistance, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.utility_functions import ( + has_fan_system, + has_heating_system, + has_preheat_system, +) + +HEATING_SYSTEM = SchemaEnums.schema_enums["HeatingSystemOptions"] + + +def is_baseline_system_10(rmd_b, hvac_b_id, terminal_unit_id_list, zone_id_list): + """ + Get either Sys-10 or Not_Sys_10 string output which indicates whether the HVAC system is ASHRAE 90.1 2019 Appendix G system 10 (Heating and Ventilation with electric heating). + + Parameters + ---------- + rmd_b json + To evaluate if the hvac system is modeled as either Sys-10 or Not_Sys_10 in the B_RMD. + hvac_b_id list + The id of the hvac system to evaluate. + terminal_unit_id_list + List list of terminal unit IDs associated with the HVAC system to be evaluated. These are sent to this function from the master get_baseline_system_types function. + zone_id_list list + list of zone IDs associated with the HVAC system to be evaluated. These are sent to this function from the master get_baseline_system_types function. + + Returns + ------- + The function returns either Sys-10 or Not_Sys_10 string output which indicates whether the HVAC system is ASHRAE 90.1 2019 Appendix G system 10 (Heating and Ventilation with electric heating). + """ + + is_baseline_system_10 = HVAC_SYS.UNMATCHED + + # check if the hvac system has the required sub systems for system type 10 + # if preheat, heating, and fan systems DON'T exist, has_required_sys=True, else, False + has_required_sys = not ( + has_preheat_system(rmd_b, hvac_b_id) + or has_heating_system(rmd_b, hvac_b_id) + or has_fan_system(rmd_b, hvac_b_id) + ) + + are_sys_10_data_matched = ( + # short-circuit the logic if no required data is found. + has_required_sys + # sub functions handles missing required sys, and return False. + and is_hvac_sys_cooling_type_none_or_non_mechanical(rmd_b, hvac_b_id) + and does_each_zone_have_only_one_terminal(rmd_b, zone_id_list) + and are_all_terminal_heat_sources_electric(rmd_b, terminal_unit_id_list) + and are_all_terminal_cool_sources_none_or_null(rmd_b, terminal_unit_id_list) + and do_all_terminals_have_one_fan(rmd_b, terminal_unit_id_list) + and are_all_terminal_types_cav_with_none_equal_to_null( + rmd_b, terminal_unit_id_list + ) + ) + + if are_sys_10_data_matched: + is_baseline_system_10 = HVAC_SYS.SYS_10 + return is_baseline_system_10 + + # When the first logic of are_sys_10_data_matched is false + # if preheat system DOESN'T exist and heating/fan systems exist, has_required_sys=True, else, False + has_required_sys = ( + not has_preheat_system(rmd_b, hvac_b_id) + and has_heating_system(rmd_b, hvac_b_id) + and has_fan_system(rmd_b, hvac_b_id) + ) + + are_sys_10_data_matched = ( + has_required_sys + # sub functions handles missing required sys, and return False. + and is_hvac_sys_fan_sys_cv(rmd_b, hvac_b_id) + and does_hvac_system_serve_single_zone(rmd_b, zone_id_list) + and does_each_zone_have_only_one_terminal(rmd_b, zone_id_list) + and are_all_terminal_heat_sources_none_or_null(rmd_b, terminal_unit_id_list) + and are_all_terminal_cool_sources_none_or_null(rmd_b, terminal_unit_id_list) + and are_all_terminal_fans_null(rmd_b, terminal_unit_id_list) + and are_all_terminal_types_cav(rmd_b, terminal_unit_id_list) + and is_hvac_sys_cooling_type_none_or_non_mechanical(rmd_b, hvac_b_id) + and is_hvac_sys_heating_type_elec_resistance(rmd_b, hvac_b_id) + ) + + if are_sys_10_data_matched: + is_baseline_system_10 = HVAC_SYS.SYS_10 + + return is_baseline_system_10 diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_11_1.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_11_1.py new file mode 100644 index 0000000000..45efd6699e --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_11_1.py @@ -0,0 +1,107 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_cool_sources_none_or_null import ( + are_all_terminal_cool_sources_none_or_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_fans_null import ( + are_all_terminal_fans_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_heat_sources_none_or_null import ( + are_all_terminal_heat_sources_none_or_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_types_VAV import ( + are_all_terminal_types_VAV, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.does_each_zone_have_only_one_terminal import ( + does_each_zone_have_only_one_terminal, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.does_hvac_system_serve_single_zone import ( + does_hvac_system_serve_single_zone, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_cooling_type_fluid_loop import ( + is_hvac_sys_cooling_type_fluid_loop, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fan_sys_VSD import ( + is_hvac_sys_fan_sys_vsd, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fluid_loop_attached_to_chiller import ( + is_hvac_sys_fluid_loop_attached_to_chiller, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fluid_loop_purchased_CHW import ( + is_hvac_sys_fluid_loop_purchased_chw, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fluid_loop_purchased_heating import ( + is_hvac_sys_fluid_loop_purchased_heating, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_heating_type_elec_resistance import ( + is_hvac_sys_heating_type_elec_resistance, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_heating_type_fluid_loop import ( + is_hvac_sys_heating_type_fluid_loop, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.utility_functions import has_preheat_system + +HEATING_SYSTEM = SchemaEnums.schema_enums["HeatingSystemOptions"] + + +def is_baseline_system_11_1(rmd_b, hvac_b_id, terminal_unit_id_list, zone_id_list): + """ + Get either Sys-11.1, Sys-11.1a, Sys-11b, Sys-11c, or Not_Sys_11.1 string output which indicates whether the HVAC + system is ASHRAE 90.1 2019 Appendix G system 11.1 (Single Zone VAV System with Electric Resistance Heating), + system 11.1a (system 11.1 with purchased CHW), system 11b (system 11.1 with purchased heating), or system 11c ( + system 11.1 with purchased CHW and purchased heating). + + Parameters ---------- + rmd_b json To evaluate if the hvac system is modeled as either Sys-11.1, Sys-11.1a, + Sys-11b, Sys-11c, or Not_Sys_11.1 in the B_RMD. + + hvac_b_id list The id of the hvac system to evaluate. + + terminal_unit_id_list list list of terminal unit IDs associated with the HVAC system to be evaluated. These are + sent to this function from the master get_baseline_system_types function. + + zone_id_list list list of zone IDs associated with the HVAC system to be evaluated. These are sent to this + function from the master get_baseline_system_types function. + + Returns ------- The function returns either Sys-11.1, Sys-11.1a, Sys-11b, Sys-11c, or Not_Sys_11.1 string output + which indicates whether the HVAC system is ASHRAE 90.1 2019 Appendix G system 11.1 (Single Zone VAV System with + Electric Resistance Heating), system 11.1a (system 11.1 with purchased CHW), system 11b (system 11.1 with + purchased heating), or system 11c (system 11.1 with purchased CHW and purchased heating). + """ + is_baseline_system_11_1 = HVAC_SYS.UNMATCHED + + # check if the hvac system has the required sub systems for system type 11.1 + # if preheat system DOESN'T exist, has_required_sys=True, else, False + has_required_sys = not has_preheat_system(rmd_b, hvac_b_id) + + are_sys_data_matched = ( + # short-circuit the logic if no required data is found. + has_required_sys + # sub functions handles missing required sys, and return False. + and is_hvac_sys_cooling_type_fluid_loop(rmd_b, hvac_b_id) + and is_hvac_sys_fan_sys_vsd(rmd_b, hvac_b_id) + and does_hvac_system_serve_single_zone(rmd_b, zone_id_list) + and does_each_zone_have_only_one_terminal(rmd_b, zone_id_list) + and are_all_terminal_cool_sources_none_or_null(rmd_b, terminal_unit_id_list) + and are_all_terminal_heat_sources_none_or_null(rmd_b, terminal_unit_id_list) + and are_all_terminal_fans_null(rmd_b, terminal_unit_id_list) + and are_all_terminal_types_VAV(rmd_b, terminal_unit_id_list) + ) + + if are_sys_data_matched: + if is_hvac_sys_heating_type_elec_resistance(rmd_b, hvac_b_id): + if is_hvac_sys_fluid_loop_attached_to_chiller(rmd_b, hvac_b_id): + is_baseline_system_11_1 = HVAC_SYS.SYS_11_1 + elif is_hvac_sys_fluid_loop_purchased_chw(rmd_b, hvac_b_id): + is_baseline_system_11_1 = HVAC_SYS.SYS_11_1A + elif is_hvac_sys_heating_type_fluid_loop( + rmd_b, hvac_b_id + ) and is_hvac_sys_fluid_loop_purchased_heating(rmd_b, hvac_b_id): + if is_hvac_sys_fluid_loop_attached_to_chiller(rmd_b, hvac_b_id): + is_baseline_system_11_1 = HVAC_SYS.SYS_11_1B + elif is_hvac_sys_fluid_loop_purchased_chw(rmd_b, hvac_b_id): + is_baseline_system_11_1 = HVAC_SYS.SYS_11_1C + + return is_baseline_system_11_1 diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_11_2.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_11_2.py new file mode 100644 index 0000000000..79dad8ba99 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_11_2.py @@ -0,0 +1,88 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_cool_sources_none_or_null import ( + are_all_terminal_cool_sources_none_or_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_fans_null import ( + are_all_terminal_fans_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_heat_sources_none_or_null import ( + are_all_terminal_heat_sources_none_or_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_types_VAV import ( + are_all_terminal_types_VAV, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.does_each_zone_have_only_one_terminal import ( + does_each_zone_have_only_one_terminal, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.does_hvac_system_serve_single_zone import ( + does_hvac_system_serve_single_zone, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_cooling_type_fluid_loop import ( + is_hvac_sys_cooling_type_fluid_loop, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fan_sys_VSD import ( + is_hvac_sys_fan_sys_vsd, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fluid_loop_attached_to_boiler import ( + is_hvac_sys_fluid_loop_attached_to_boiler, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fluid_loop_attached_to_chiller import ( + is_hvac_sys_fluid_loop_attached_to_chiller, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fluid_loop_purchased_CHW import ( + is_hvac_sys_fluid_loop_purchased_chw, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.utility_functions import has_preheat_system + +HEATING_SYSTEM = SchemaEnums.schema_enums["HeatingSystemOptions"] + + +def is_baseline_system_11_2(rmd_b, hvac_b_id, terminal_unit_id_list, zone_id_list): + """ + Get either Sys-11.2, Sys-11.2a or Not_Sys_11.2 string output which indicates whether the HVAC system is ASHRAE + 90.1 2019 Appendix G system 11.2 (Single Zone VAV System with Hot Water Heating (Boiler)) or system 11.2a (system + 11.2 with purchased CHW). + + Parameters + ---------- + rmd_b: json To evaluate if the hvac system is modeled as either Sys-11.2, Sys-11.2a or Not_Sys_11.2 in the B_rmd. + hvac_b_id: String, The id of the hvac system to evaluate. + terminal_unit_id_list: List, list of terminal unit IDs associated with the HVAC system to be evaluated. These are + sent to this function from the master get_baseline_system_types function. + zone_id_list: List, list of zone IDs associated with the HVAC system to be evaluated. These are sent to this + function from the master get_baseline_system_types function. + + Returns string The function returns either Sys-11.2, Sys-11.2a or Not_Sys string output which indicates + whether the HVAC system is ASHRAE 90.1 2019 Appendix G 11.2 (Single Zone VAV System with Hot Water Heating ( + Boiler)) or system 11.2a (system 11.2 with purchased CHW). ------- + """ + is_baseline_system_11_2_str = HVAC_SYS.UNMATCHED + + # check if the hvac system has the required sub systems for system type 11.2 + # if preheat system DOESN'T exist, has_required_sys=True, else, False + has_required_sys = not has_preheat_system(rmd_b, hvac_b_id) + + are_sys_data_matched = ( + has_required_sys + and is_hvac_sys_cooling_type_fluid_loop(rmd_b, hvac_b_id) + and is_hvac_sys_fan_sys_vsd(rmd_b, hvac_b_id) + and does_hvac_system_serve_single_zone(rmd_b, zone_id_list) + and does_each_zone_have_only_one_terminal(rmd_b, zone_id_list) + and are_all_terminal_cool_sources_none_or_null(rmd_b, terminal_unit_id_list) + and are_all_terminal_heat_sources_none_or_null(rmd_b, terminal_unit_id_list) + and are_all_terminal_fans_null(rmd_b, terminal_unit_id_list) + and are_all_terminal_types_VAV(rmd_b, terminal_unit_id_list) + ) + + if are_sys_data_matched and is_hvac_sys_fluid_loop_attached_to_boiler( + rmd_b, hvac_b_id + ): + if is_hvac_sys_fluid_loop_attached_to_chiller(rmd_b, hvac_b_id): + is_baseline_system_11_2_str = HVAC_SYS.SYS_11_2 + elif is_hvac_sys_fluid_loop_purchased_chw(rmd_b, hvac_b_id): + is_baseline_system_11_2_str = HVAC_SYS.SYS_11_2A + + return is_baseline_system_11_2_str diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_12.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_12.py new file mode 100644 index 0000000000..51a2bb2e8e --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_12.py @@ -0,0 +1,106 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_cool_sources_none_or_null import ( + are_all_terminal_cool_sources_none_or_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_fans_null import ( + are_all_terminal_fans_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_heat_sources_none_or_null import ( + are_all_terminal_heat_sources_none_or_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_types_CAV import ( + are_all_terminal_types_cav, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.does_each_zone_have_only_one_terminal import ( + does_each_zone_have_only_one_terminal, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.does_hvac_system_serve_single_zone import ( + does_hvac_system_serve_single_zone, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_cooling_type_fluid_loop import ( + is_hvac_sys_cooling_type_fluid_loop, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fan_sys_CV import ( + is_hvac_sys_fan_sys_cv, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fluid_loop_attached_to_boiler import ( + is_hvac_sys_fluid_loop_attached_to_boiler, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fluid_loop_attached_to_chiller import ( + is_hvac_sys_fluid_loop_attached_to_chiller, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fluid_loop_purchased_CHW import ( + is_hvac_sys_fluid_loop_purchased_chw, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fluid_loop_purchased_heating import ( + is_hvac_sys_fluid_loop_purchased_heating, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_heating_type_fluid_loop import ( + is_hvac_sys_heating_type_fluid_loop, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.utility_functions import has_preheat_system + +HEATING_SYSTEM = SchemaEnums.schema_enums["HeatingSystemOptions"] + + +def is_baseline_system_12(rmd_b, hvac_b_id, terminal_unit_id_list, zone_id_list): + """ + Get either Sys-12, Sys-12a, Sys-12b, Sys-12c, or Not_Sys_12 string output which indicates whether the HVAC system is ASHRAE 90.1 2019 Appendix G system 12 (Single Zone Constant Volume System + with CHW and HW), system 12a (system 12 with purchased CHW), system 12b (system 12 with purchased heating), system 12c (system 12 with purchased CHW and purchased HW). + + Parameters + ---------- + rmd_b: json + To evaluate if the hvac system is modeled as either Sys-12, Sys-12a, Sys-12b, Sys-12c, or Not_Sys_12 in the B_RMD. + + hvac_b_id: list + The id of the hvac system to evaluate. + + terminal_unit_id_list: list + List of terminal unit IDs associated with the HVAC system to be evaluated. These are sent to this function from the mater get_baseline_system_types function. + + zone_id_list: list + List of zone IDs associated with the HVAC system to be evaluated. These are sent to this function from the mater get_baseline_system_types function. + + Returns + ------- + The function returns either Sys-12, Sys-12a, Sys-12b, Sys-12c, or Not_Sys_12 string output which indicates whether the HVAC system is ASHRAE 90.1 2019 Appendix G system 12 (Single Zone Constant Volume System + with CHW and HW), system 12a (system 12 with purchased CHW), system 12b (system 12 with purchased heating), system 12c (system 12 with purchased CHW and purchased HW). + """ + + is_baseline_system_12 = HVAC_SYS.UNMATCHED + + # check if the hvac system has the required sub systems for system type 12 + # if preheat system DOESN'T exist, has_required_sys=True, else, False + has_required_sys = not has_preheat_system(rmd_b, hvac_b_id) + + are_sys_data_matched = ( + # short-circuit the logic if no required data is found. + has_required_sys + # sub functions handles missing required sys, and return False. + and is_hvac_sys_heating_type_fluid_loop(rmd_b, hvac_b_id) + and is_hvac_sys_cooling_type_fluid_loop(rmd_b, hvac_b_id) + and is_hvac_sys_fan_sys_cv(rmd_b, hvac_b_id) + and does_hvac_system_serve_single_zone(rmd_b, zone_id_list) + and does_each_zone_have_only_one_terminal(rmd_b, zone_id_list) + and are_all_terminal_heat_sources_none_or_null(rmd_b, terminal_unit_id_list) + and are_all_terminal_cool_sources_none_or_null(rmd_b, terminal_unit_id_list) + and are_all_terminal_fans_null(rmd_b, terminal_unit_id_list) + and are_all_terminal_types_cav(rmd_b, terminal_unit_id_list) + ) + + if are_sys_data_matched: + if is_hvac_sys_fluid_loop_attached_to_chiller(rmd_b, hvac_b_id): + if is_hvac_sys_fluid_loop_attached_to_boiler(rmd_b, hvac_b_id): + is_baseline_system_12 = HVAC_SYS.SYS_12 + elif is_hvac_sys_fluid_loop_purchased_heating(rmd_b, hvac_b_id): + is_baseline_system_12 = HVAC_SYS.SYS_12B + elif is_hvac_sys_fluid_loop_purchased_chw( + rmd_b, hvac_b_id + ) and is_hvac_sys_fluid_loop_attached_to_boiler(rmd_b, hvac_b_id): + is_baseline_system_12 = HVAC_SYS.SYS_12A + + return is_baseline_system_12 diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_13.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_13.py new file mode 100644 index 0000000000..00805971c3 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_13.py @@ -0,0 +1,94 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_cool_sources_none_or_null import ( + are_all_terminal_cool_sources_none_or_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_fans_null import ( + are_all_terminal_fans_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_heat_sources_none_or_null import ( + are_all_terminal_heat_sources_none_or_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_types_CAV import ( + are_all_terminal_types_cav, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.does_each_zone_have_only_one_terminal import ( + does_each_zone_have_only_one_terminal, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.does_hvac_system_serve_single_zone import ( + does_hvac_system_serve_single_zone, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_cooling_type_fluid_loop import ( + is_hvac_sys_cooling_type_fluid_loop, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fan_sys_CV import ( + is_hvac_sys_fan_sys_cv, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fluid_loop_attached_to_chiller import ( + is_hvac_sys_fluid_loop_attached_to_chiller, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fluid_loop_purchased_CHW import ( + is_hvac_sys_fluid_loop_purchased_chw, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_heating_type_elec_resistance import ( + is_hvac_sys_heating_type_elec_resistance, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.utility_functions import has_preheat_system + +HEATING_SYSTEM = SchemaEnums.schema_enums["HeatingSystemOptions"] + + +def is_baseline_system_13(rmd_b, hvac_b_id, terminal_unit_id_list, zone_id_list): + """ + Get either Sys-13, Sys-13a, or Not_Sys_13 string output which indicates whether the HVAC system is ASHRAE 90.1 2019 Appendix G system 13 + (Single Zone Constant Volume System with CHW and Electric Resistance) or system 13a (system 13 with purchased CHW). + + Parameters + ---------- + rmd_b: json + To evaluate if the hvac system is modeled as either Sys-13, Sys-13a,or Not_Sys_13 in the B_RMD. + + hvac_b_id: list + The id of the hvac system to evaluate. + + terminal_unit_id_list: list + List of terminal unit IDs associated with the HVAC system to be evaluated. These are sent to this function from the mater get_baseline_system_types function. + + zone_id_list: list + list of zone IDs associated with the HVAC system to be evaluated. These are sent to this function from the mater get_baseline_system_types function. + + Returns + ------- + The function returns either Sys-13, Sys-13a, or Not_Sys_13 string output which indicates whether the HVAC system is ASHRAE 90.1 2019 Appendix G system 13 (Single Zone Constant Volume System with CHW and Electric Resistance) or system 13a (system 13 with purchased CHW). + """ + + is_baseline_system_13 = HVAC_SYS.UNMATCHED + + # check if the hvac system has the required sub systems for system type 13 + # if preheat system DOESN'T exist, has_required_sys=True, else, False + has_required_sys = not has_preheat_system(rmd_b, hvac_b_id) + + are_sys_data_matched = ( + # short-circuit the logic if no required data is found. + has_required_sys + # sub functions handles missing required sys, and return False. + and is_hvac_sys_heating_type_elec_resistance(rmd_b, hvac_b_id) + and is_hvac_sys_cooling_type_fluid_loop(rmd_b, hvac_b_id) + and is_hvac_sys_fan_sys_cv(rmd_b, hvac_b_id) + and does_hvac_system_serve_single_zone(rmd_b, zone_id_list) + and does_each_zone_have_only_one_terminal(rmd_b, zone_id_list) + and are_all_terminal_heat_sources_none_or_null(rmd_b, terminal_unit_id_list) + and are_all_terminal_cool_sources_none_or_null(rmd_b, terminal_unit_id_list) + and are_all_terminal_fans_null(rmd_b, terminal_unit_id_list) + and are_all_terminal_types_cav(rmd_b, terminal_unit_id_list) + ) + + if are_sys_data_matched: + if is_hvac_sys_fluid_loop_attached_to_chiller(rmd_b, hvac_b_id): + is_baseline_system_13 = HVAC_SYS.SYS_13 + elif is_hvac_sys_fluid_loop_purchased_chw(rmd_b, hvac_b_id): + is_baseline_system_13 = HVAC_SYS.SYS_13A + + return is_baseline_system_13 diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_1_a.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_1_a.py new file mode 100644 index 0000000000..f9ddbbeea0 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_1_a.py @@ -0,0 +1,79 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_chw_loops_purcahsed_cooling import ( + are_all_terminal_chw_loops_purchased_cooling, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_cool_sources_chilled_water import ( + are_all_terminal_cool_sources_chilled_water, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_heat_sources_hot_water import ( + are_all_terminal_heat_sources_hot_water, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_heating_loops_attached_to_boiler import ( + are_all_terminal_heating_loops_attached_to_boiler, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_types_CAV_with_none_equal_to_null import ( + are_all_terminal_types_cav_with_none_equal_to_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.do_all_terminals_have_one_fan import ( + do_all_terminals_have_one_fan, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.does_each_zone_have_only_one_terminal import ( + does_each_zone_have_only_one_terminal, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_cooling_type_none import ( + is_hvac_sys_cooling_type_none, +) +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.utility_functions import ( + has_fan_system, + has_heating_system, + has_preheat_system, +) + +HEATING_SYSTEM = SchemaEnums.schema_enums["HeatingSystemOptions"] + + +def is_baseline_system_1_a(rmd_b, hvac_b_id, terminal_unit_id_list, zone_id_list): + """ + Returns true or false to whether the baseline system type is 1a (system 1 with purchased CHW and HW served by a boiler). + + Parameters + ---------- + rmd_b JSON, To evaluate if the hvac system is modeled as either Sys-1c in the B_RMD. + + hvac_b_id String, The id of the hvac system to evaluate. + + terminal_unit_id_list List, list of terminal unit IDs associated with the HVAC system to be evaluated. + These are sent to this function from the is_baseline_system_1 function. + + zone_id_list List, list of zone IDs associated with the HVAC system to be evaluated. TThese are sent to + this function from the is_baseline_system_1 function. + + Returns true or false to whether the baseline system type is 1a (system 1 with purchased CHW and HW served by a boiler). + ------- + """ + + # check if the hvac system has the required sub systems for system type 1 a + # if preheat, heating, and fan systems DON'T exist, has_required_sys=True, else, False + has_required_sys = not ( + has_preheat_system(rmd_b, hvac_b_id) + and has_heating_system(rmd_b, hvac_b_id) + and has_fan_system(rmd_b, hvac_b_id) + ) + + return ( + # short-circuit the logic if no required data is found. + has_required_sys + # sub functions handles missing required sys, and return False. + and is_hvac_sys_cooling_type_none(rmd_b, hvac_b_id) + and does_each_zone_have_only_one_terminal(rmd_b, zone_id_list) + and are_all_terminal_heat_sources_hot_water(rmd_b, terminal_unit_id_list) + and are_all_terminal_cool_sources_chilled_water(rmd_b, terminal_unit_id_list) + and do_all_terminals_have_one_fan(rmd_b, terminal_unit_id_list) + and are_all_terminal_types_cav_with_none_equal_to_null( + rmd_b, terminal_unit_id_list + ) + and are_all_terminal_heating_loops_attached_to_boiler( + rmd_b, terminal_unit_id_list + ) + and are_all_terminal_chw_loops_purchased_cooling(rmd_b, terminal_unit_id_list) + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_1_c.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_1_c.py new file mode 100644 index 0000000000..7fd6d03dde --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_1_c.py @@ -0,0 +1,79 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_chw_loops_purcahsed_cooling import ( + are_all_terminal_chw_loops_purchased_cooling, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_cool_sources_chilled_water import ( + are_all_terminal_cool_sources_chilled_water, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_heat_sources_hot_water import ( + are_all_terminal_heat_sources_hot_water, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_heating_loops_purchased_heating import ( + are_all_terminal_heating_loops_purchased_heating, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_types_CAV_with_none_equal_to_null import ( + are_all_terminal_types_cav_with_none_equal_to_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.do_all_terminals_have_one_fan import ( + do_all_terminals_have_one_fan, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.does_each_zone_have_only_one_terminal import ( + does_each_zone_have_only_one_terminal, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_cooling_type_none import ( + is_hvac_sys_cooling_type_none, +) +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.utility_functions import ( + has_fan_system, + has_heating_system, + has_preheat_system, +) + +HEATING_SYSTEM = SchemaEnums.schema_enums["HeatingSystemOptions"] + + +def is_baseline_system_1_c(rmd_b, hvac_b_id, terminal_unit_id_list, zone_id_list): + """ + Returns true or false to whether the baseline system type is 1c (system 1 with purchased CHW and purchased HW). + + Parameters + ---------- + rmd_b JSON, To evaluate if the hvac system is modeled as either Sys-1c in the B_RMD. + + hvac_b_id String, The id of the hvac system to evaluate. + + terminal_unit_id_list List, list of terminal unit IDs associated with the HVAC system to be evaluated. + These are sent to this function from the is_baseline_system_1 function. + + zone_id_list List, list of zone IDs associated with the HVAC system to be evaluated. TThese are sent to + this function from the is_baseline_system_1 function. + + Returns true or false to whether the baseline system type is 1c (system 1 with purchased CHW and purchased HW). + ------- + """ + + # check if the hvac system has the required sub systems for system type 1 c + # if preheat, heating, and fan systems DON'T exist, has_required_sys=True, else, False + has_required_sys = not ( + has_preheat_system(rmd_b, hvac_b_id) + and has_heating_system(rmd_b, hvac_b_id) + and has_fan_system(rmd_b, hvac_b_id) + ) + + return ( + # short-circuit the logic if no required data is found. + has_required_sys + # sub functions handles missing required sys, and return False. + and is_hvac_sys_cooling_type_none(rmd_b, hvac_b_id) + and does_each_zone_have_only_one_terminal(rmd_b, zone_id_list) + and are_all_terminal_heat_sources_hot_water(rmd_b, terminal_unit_id_list) + and are_all_terminal_cool_sources_chilled_water(rmd_b, terminal_unit_id_list) + and do_all_terminals_have_one_fan(rmd_b, terminal_unit_id_list) + and are_all_terminal_types_cav_with_none_equal_to_null( + rmd_b, terminal_unit_id_list + ) + and are_all_terminal_heating_loops_purchased_heating( + rmd_b, terminal_unit_id_list + ) + and are_all_terminal_chw_loops_purchased_cooling(rmd_b, terminal_unit_id_list) + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_2.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_2.py new file mode 100644 index 0000000000..0607d1e72e --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_2.py @@ -0,0 +1,83 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_cool_sources_none_or_null import ( + are_all_terminal_cool_sources_none_or_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_fans_null import ( + are_all_terminal_fans_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_heat_sources_none_or_null import ( + are_all_terminal_heat_sources_none_or_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_supplies_ducted import ( + are_all_terminal_supplies_ducted, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_types_CAV import ( + are_all_terminal_types_cav, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.does_each_zone_have_only_one_terminal import ( + does_each_zone_have_only_one_terminal, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.does_hvac_system_serve_single_zone import ( + does_hvac_system_serve_single_zone, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_cooling_type_DX import ( + is_hvac_sys_cooling_type_dx, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fan_sys_CV import ( + is_hvac_sys_fan_sys_cv, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_heating_type_heat_pump import ( + is_hvac_sys_heating_type_heat_pump, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.utility_functions import has_preheat_system + +HEATING_SYSTEM = SchemaEnums.schema_enums["HeatingSystemOptions"] + + +def is_baseline_system_2(rmd_b, hvac_b_id, terminal_unit_id_list, zone_id_list): + """ + Get either Sys-2 or Not_Sys_2 string output which indicates whether the HVAC system is ASHRAE 90.1 2019 Appendix G system 2 (PTHP). + + Parameters + ---------- + rmd_b json + To evaluate if the hvac system is modeled as Sys-2 in the B_RMD. + + hvac_b_id list + The id of the hvac system to evaluate. + + terminal_unit_id_list + List list of terminal unit IDs associated with the HVAC system to be evaluated. These are sent to this function from the master get_baseline_system_types function. + + zone_id_list list + list of zone IDs associated with the HVAC system to be evaluated. These are sent to this function from the master get_baseline_system_types function. + + Returns + ------- + The function returns either Sys-2 or Not_Sys_2 string output which indicates whether the HVAC system is ASHRAE 90.1 2019 Appendix G system 2 (PTHP). + """ + + # check if the hvac system has the required sub systems for system type 2 + # if preheat DOESN'T exist, has_required_sys=True, else, False + has_required_sys = not has_preheat_system(rmd_b, hvac_b_id) + + are_sys_data_matched = ( + # short-circuit the logic if no required data is found. + has_required_sys + # sub functions handles missing required sys, and return False. + and is_hvac_sys_heating_type_heat_pump(rmd_b, hvac_b_id) + and is_hvac_sys_cooling_type_dx(rmd_b, hvac_b_id) + and is_hvac_sys_fan_sys_cv(rmd_b, hvac_b_id) + and does_hvac_system_serve_single_zone(rmd_b, zone_id_list) + and does_each_zone_have_only_one_terminal(rmd_b, zone_id_list) + and are_all_terminal_heat_sources_none_or_null(rmd_b, terminal_unit_id_list) + and are_all_terminal_cool_sources_none_or_null(rmd_b, terminal_unit_id_list) + and are_all_terminal_fans_null(rmd_b, terminal_unit_id_list) + and not are_all_terminal_supplies_ducted(rmd_b, terminal_unit_id_list) + and are_all_terminal_types_cav(rmd_b, terminal_unit_id_list) + ) + + return HVAC_SYS.SYS_2 if are_sys_data_matched else HVAC_SYS.UNMATCHED diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_3.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_3.py new file mode 100644 index 0000000000..ca6f337978 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_3.py @@ -0,0 +1,112 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_cool_sources_none_or_null import ( + are_all_terminal_cool_sources_none_or_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_fans_null import ( + are_all_terminal_fans_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_heat_sources_none_or_null import ( + are_all_terminal_heat_sources_none_or_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_supplies_ducted import ( + are_all_terminal_supplies_ducted, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_types_CAV import ( + are_all_terminal_types_cav, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.does_each_zone_have_only_one_terminal import ( + does_each_zone_have_only_one_terminal, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.does_hvac_system_serve_single_zone import ( + does_hvac_system_serve_single_zone, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_cooling_type_DX import ( + is_hvac_sys_cooling_type_dx, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_cooling_type_fluid_loop import ( + is_hvac_sys_cooling_type_fluid_loop, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fan_sys_CV import ( + is_hvac_sys_fan_sys_cv, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fluid_loop_purchased_CHW import ( + is_hvac_sys_fluid_loop_purchased_chw, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fluid_loop_purchased_heating import ( + is_hvac_sys_fluid_loop_purchased_heating, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_heating_type_fluid_loop import ( + is_hvac_sys_heating_type_fluid_loop, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_heating_type_furnace import ( + is_hvac_sys_heating_type_furnace, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.utility_functions import has_preheat_system + +HEATING_SYSTEM = SchemaEnums.schema_enums["HeatingSystemOptions"] + + +def is_baseline_system_3(rmd_b, hvac_b_id, terminal_unit_id_list, zone_id_list): + """ + Get either Sys-3, Sys-3a, Sys-3b, Sys-3c, or Not_Sys_3 string output which indicates whether the HVAC system is ASHRAE 90.1 2019 Appendix G system 3 (PSZ), system 3a (system 3 with purchased CHW), + system 3b (system 3 with purchased heating), system 3c (system 3 with purchased CHW and purchased HW). + + Parameters + ---------- + rmd_b json + To evaluate if the hvac system is modeled as either Sys-3, Sys-3a, Sys-3b, Sys-3c, or Not_Sys_3 in the B_RMD. + + hvac_b_id list + The id of the hvac system to evaluate. + + terminal_unit_id_list + List list of terminal unit IDs associated with the HVAC system to be evaluated. These are sent to this function from the master get_baseline_system_types function. + + zone_id_list list + list of zone IDs associated with the HVAC system to be evaluated. These are sent to this function from the master get_baseline_system_types function. + + Returns + ------- + The function returns either Sys-3, Sys-3a, Sys-3b, Sys-3c, or Not_Sys_3 string output which indicates whether the HVAC system is ASHRAE 90.1 2019 Appendix G system 3 (PSZ), + system 3a (system 3 with purchased CHW), system 3b (system 3 with purchased heating), system 3c (system 3 with purchased CHW and purchased HW). + """ + is_baseline_system_3 = HVAC_SYS.UNMATCHED + + # check if the hvac system has the required sub systems for system type 3 + # if preheat DOESN'T exist, has_required_sys=True, else, False + has_required_sys = not has_preheat_system(rmd_b, hvac_b_id) + + are_sys_data_matched = ( + # short-circuit the logic if no required data is found. + has_required_sys + # sub functions handles missing required sys, and return False. + and is_hvac_sys_fan_sys_cv(rmd_b, hvac_b_id) + and does_hvac_system_serve_single_zone(rmd_b, zone_id_list) + and does_each_zone_have_only_one_terminal(rmd_b, zone_id_list) + and are_all_terminal_heat_sources_none_or_null(rmd_b, terminal_unit_id_list) + and are_all_terminal_cool_sources_none_or_null(rmd_b, terminal_unit_id_list) + and are_all_terminal_fans_null(rmd_b, terminal_unit_id_list) + and are_all_terminal_types_cav(rmd_b, terminal_unit_id_list) + and are_all_terminal_supplies_ducted(rmd_b, terminal_unit_id_list) + ) + + if are_sys_data_matched: + if is_hvac_sys_cooling_type_dx(rmd_b, hvac_b_id): + if is_hvac_sys_heating_type_furnace(rmd_b, hvac_b_id): + is_baseline_system_3 = HVAC_SYS.SYS_3 + elif is_hvac_sys_heating_type_fluid_loop( + rmd_b, hvac_b_id + ) and is_hvac_sys_fluid_loop_purchased_heating(rmd_b, hvac_b_id): + is_baseline_system_3 = HVAC_SYS.SYS_3B + elif is_hvac_sys_cooling_type_fluid_loop( + rmd_b, hvac_b_id + ) and is_hvac_sys_fluid_loop_purchased_chw(rmd_b, hvac_b_id): + if is_hvac_sys_heating_type_furnace(rmd_b, hvac_b_id): + is_baseline_system_3 = HVAC_SYS.SYS_3A + elif is_hvac_sys_fluid_loop_purchased_heating(rmd_b, hvac_b_id): + is_baseline_system_3 = HVAC_SYS.SYS_3C + + return is_baseline_system_3 diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_4.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_4.py new file mode 100644 index 0000000000..dab153b0b7 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_4.py @@ -0,0 +1,80 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_cool_sources_none_or_null import ( + are_all_terminal_cool_sources_none_or_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_fans_null import ( + are_all_terminal_fans_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_heat_sources_none_or_null import ( + are_all_terminal_heat_sources_none_or_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_supplies_ducted import ( + are_all_terminal_supplies_ducted, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_types_CAV import ( + are_all_terminal_types_cav, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.does_each_zone_have_only_one_terminal import ( + does_each_zone_have_only_one_terminal, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.does_hvac_system_serve_single_zone import ( + does_hvac_system_serve_single_zone, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_cooling_type_DX import ( + is_hvac_sys_cooling_type_dx, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fan_sys_CV import ( + is_hvac_sys_fan_sys_cv, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_heating_type_heat_pump import ( + is_hvac_sys_heating_type_heat_pump, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.utility_functions import has_preheat_system + +HEATING_SYSTEM = SchemaEnums.schema_enums["HeatingSystemOptions"] + + +def is_baseline_system_4(rmd_b, hvac_b_id, terminal_unit_id_list, zone_id_list): + """ + Get either Sys-4 or Not_Sys_4 string output which indicates whether the HVAC system is ASHRAE 90.1 2019 Appendix G system 4 (PSZ-HP). + + Parameters + ---------- + rmd_b json + To evaluate if the hvac system is modeled as Sys-4 in the B_rmd. + hvac_b_id list + The id of the hvac system to evaluate. + terminal_unit_id_list + List list of terminal unit IDs associated with the HVAC system to be evaluated. These are sent to this function from the master get_baseline_system_types function. + zone_id_list list + list of zone IDs associated with the HVAC system to be evaluated. These are sent to this function from the master get_baseline_system_types function. + + Returns + ------- + The function returns either Sys-4 or Not_Sys_4 string output which indicates whether the HVAC system is ASHRAE 90.1 2019 Appendix G system 4 (PSZ-HP). + """ + + # check if the hvac system has the required sub systems for system type 4 + # if preheat DOESN'T exist, has_required_sys=True, else, False + has_required_sys = not has_preheat_system(rmd_b, hvac_b_id) + + are_sys_data_matched = ( + # short-circuit the logic if no required data is found. + has_required_sys + # sub functions handles missing required sys, and return False. + and is_hvac_sys_heating_type_heat_pump(rmd_b, hvac_b_id) + and is_hvac_sys_cooling_type_dx(rmd_b, hvac_b_id) + and is_hvac_sys_fan_sys_cv(rmd_b, hvac_b_id) + and does_hvac_system_serve_single_zone(rmd_b, zone_id_list) + and does_each_zone_have_only_one_terminal(rmd_b, zone_id_list) + and are_all_terminal_heat_sources_none_or_null(rmd_b, terminal_unit_id_list) + and are_all_terminal_cool_sources_none_or_null(rmd_b, terminal_unit_id_list) + and are_all_terminal_fans_null(rmd_b, terminal_unit_id_list) + and are_all_terminal_supplies_ducted(rmd_b, terminal_unit_id_list) + and are_all_terminal_types_cav(rmd_b, terminal_unit_id_list) + ) + + return HVAC_SYS.SYS_4 if are_sys_data_matched else HVAC_SYS.UNMATCHED diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_5.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_5.py new file mode 100644 index 0000000000..6ffeb8c88c --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_5.py @@ -0,0 +1,108 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_cool_sources_none_or_null import ( + are_all_terminal_cool_sources_none_or_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_fans_null import ( + are_all_terminal_fans_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_heat_sources_hot_water import ( + are_all_terminal_heat_sources_hot_water, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_heating_loops_attached_to_boiler import ( + are_all_terminal_heating_loops_attached_to_boiler, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_heating_loops_purchased_heating import ( + are_all_terminal_heating_loops_purchased_heating, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_types_VAV import ( + are_all_terminal_types_VAV, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.does_each_zone_have_only_one_terminal import ( + does_each_zone_have_only_one_terminal, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_cooling_type_DX import ( + is_hvac_sys_cooling_type_dx, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fan_sys_VSD import ( + is_hvac_sys_fan_sys_vsd, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_preheat_fluid_loop_attached_to_boiler import ( + is_hvac_sys_preheat_fluid_loop_attached_to_boiler, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_preheat_fluid_loop_purchased_heating import ( + is_hvac_sys_preheat_fluid_loop_purchased_heating, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_preheating_type_fluid_loop import ( + is_hvac_sys_preheating_type_fluid_loop, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.utility_functions import ( + has_cooling_system, + has_heating_system, + has_preheat_system, +) + +HEATING_SYSTEM = SchemaEnums.schema_enums["HeatingSystemOptions"] +COOLING_SYSTEM = SchemaEnums.schema_enums["CoolingSystemOptions"] + + +def is_baseline_system_5(rmd_b, hvac_b_id, terminal_unit_id_list, zone_id_list): + """ + Get either Sys-5, Sys-5b, or Not_Sys_5 string output which indicates whether the HVAC system is ASHRAE 90.1 2019 Appendix G system 5 (Package VAV with Reheat) or system 5b (system 5 with purchased heating). + + Parameters + ---------- + rmd_b json + To evaluate if the hvac system is modeled as either Sys-5, Sys-5b, or Not_Sys_5 in the B_RMD. + hvac_b_id list + The id of the hvac system to evaluate. + terminal_unit_id_list + List list of terminal unit IDs associated with the HVAC system to be evaluated. These are sent to this function from the master get_baseline_system_types function. + zone_id_list list + list of zone IDs associated with the HVAC system to be evaluated. These are sent to this function from the master get_baseline_system_types function. + + Returns + ------- + The function returns either Sys-5, Sys-5b, or Not_Sys_5 string output which indicates whether the HVAC system is ASHRAE 90.1 2019 Appendix G system 5 (Package VAV with Reheat) or system 5b (system 5 with purchased heating). + """ + + is_baseline_system_5 = HVAC_SYS.UNMATCHED + + # check if the hvac system has the required sub systems for system type 5 + # if heating system DOESN'T exist and preheat/cooling systems exist, has_required_sys=True, else, False + has_required_sys = ( + has_preheat_system(rmd_b, hvac_b_id) + and not has_heating_system(rmd_b, hvac_b_id) + and has_cooling_system(rmd_b, hvac_b_id) + ) + + are_sys_data_matched = ( + # short-circuit the logic if no required data is found. + has_required_sys + # sub functions handles missing required sys, and return False. + and is_hvac_sys_preheating_type_fluid_loop(rmd_b, hvac_b_id) + and is_hvac_sys_cooling_type_dx(rmd_b, hvac_b_id) + and is_hvac_sys_fan_sys_vsd(rmd_b, hvac_b_id) + and does_each_zone_have_only_one_terminal(rmd_b, zone_id_list) + and are_all_terminal_heat_sources_hot_water(rmd_b, terminal_unit_id_list) + and are_all_terminal_cool_sources_none_or_null(rmd_b, terminal_unit_id_list) + and are_all_terminal_fans_null(rmd_b, terminal_unit_id_list) + and are_all_terminal_types_VAV(rmd_b, terminal_unit_id_list) + ) + if are_sys_data_matched: + if is_hvac_sys_preheat_fluid_loop_attached_to_boiler( + rmd_b, hvac_b_id + ) and are_all_terminal_heating_loops_attached_to_boiler( + rmd_b, terminal_unit_id_list + ): + is_baseline_system_5 = HVAC_SYS.SYS_5 + elif is_hvac_sys_preheat_fluid_loop_purchased_heating( + rmd_b, hvac_b_id + ) and are_all_terminal_heating_loops_purchased_heating( + rmd_b, terminal_unit_id_list + ): + is_baseline_system_5 = HVAC_SYS.SYS_5B + + return is_baseline_system_5 diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_6.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_6.py new file mode 100644 index 0000000000..533b729f58 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_6.py @@ -0,0 +1,112 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_cool_sources_none_or_null import ( + are_all_terminal_cool_sources_none_or_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_fan_configs_parallel import ( + are_all_terminal_fan_configs_parallel, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_heat_sources_electric import ( + are_all_terminal_heat_sources_electric, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_heat_sources_hot_water import ( + are_all_terminal_heat_sources_hot_water, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_heating_loops_purchased_heating import ( + are_all_terminal_heating_loops_purchased_heating, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_types_VAV import ( + are_all_terminal_types_VAV, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.do_all_terminals_have_one_fan import ( + do_all_terminals_have_one_fan, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.does_each_zone_have_only_one_terminal import ( + does_each_zone_have_only_one_terminal, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_cooling_type_DX import ( + is_hvac_sys_cooling_type_dx, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fan_sys_VSD import ( + is_hvac_sys_fan_sys_vsd, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_preheat_fluid_loop_purchased_heating import ( + is_hvac_sys_preheat_fluid_loop_purchased_heating, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_preheating_type_elec_resistance import ( + is_hvac_sys_preheating_type_elec_resistance, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_preheating_type_fluid_loop import ( + is_hvac_sys_preheating_type_fluid_loop, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.utility_functions import ( + has_cooling_system, + has_heating_system, + has_preheat_system, +) + +HEATING_SYSTEM = SchemaEnums.schema_enums["HeatingSystemOptions"] +COOLING_SYSTEM = SchemaEnums.schema_enums["CoolingSystemOptions"] + + +def is_baseline_system_6(rmd_b, hvac_b_id, terminal_unit_id_list, zone_id_list): + """ + Get either Sys-6, Sys-6b, or Not_Sys_6 string output which indicates whether the HVAC system is ASHRAE 90.1 2019 Appendix G system 6 (Package VAV with PFP Boxes) or system 6b (system 6 with purchased heating). + + Parameters + ---------- + rmd_b json + To evaluate if the hvac system is modeled as either Sys-6, Sys-6b, or Not_Sys_6 in the B_RMD. + hvac_b_id list + The id of the hvac system to evaluate. + terminal_unit_id_list + List list of terminal unit IDs associated with the HVAC system to be evaluated. These are sent to this function from the master get_baseline_system_types function. + zone_id_list list + list of zone IDs associated with the HVAC system to be evaluated. These are sent to this function from the master get_baseline_system_types function. + + Returns + ------- + The function returns either Sys-6, Sys-6b, or Not_Sys_6 string output which indicates whether the HVAC system is ASHRAE 90.1 2019 Appendix G system 6 (Package VAV with PFP Boxes) or system 6b (system 6 with purchased heating). + """ + + is_baseline_system_6 = HVAC_SYS.UNMATCHED + + # check if the hvac system has the required sub systems for system type 6 + # if heating system DOESN'T exist and preheat/cooling systems exist, has_required_sys=True, else, False. + has_required_sys = ( + has_preheat_system(rmd_b, hvac_b_id) + and not has_heating_system(rmd_b, hvac_b_id) + and has_cooling_system(rmd_b, hvac_b_id) + ) + + are_sys_data_matched = ( + # short-circuit the logic if no required data is found. + has_required_sys + # sub functions handles missing required sys, and return False. + and is_hvac_sys_cooling_type_dx(rmd_b, hvac_b_id) + and is_hvac_sys_fan_sys_vsd(rmd_b, hvac_b_id) + and does_each_zone_have_only_one_terminal(rmd_b, zone_id_list) + and are_all_terminal_cool_sources_none_or_null(rmd_b, terminal_unit_id_list) + and do_all_terminals_have_one_fan(rmd_b, terminal_unit_id_list) + and are_all_terminal_types_VAV(rmd_b, terminal_unit_id_list) + and are_all_terminal_fan_configs_parallel(rmd_b, terminal_unit_id_list) + ) + + if are_sys_data_matched: + if is_hvac_sys_preheating_type_elec_resistance( + rmd_b, hvac_b_id + ) and are_all_terminal_heat_sources_electric(rmd_b, terminal_unit_id_list): + is_baseline_system_6 = HVAC_SYS.SYS_6 + elif ( + is_hvac_sys_preheating_type_fluid_loop(rmd_b, hvac_b_id) + and is_hvac_sys_preheat_fluid_loop_purchased_heating(rmd_b, hvac_b_id) + and are_all_terminal_heat_sources_hot_water(rmd_b, terminal_unit_id_list) + and are_all_terminal_heating_loops_purchased_heating( + rmd_b, terminal_unit_id_list + ) + ): + is_baseline_system_6 = HVAC_SYS.SYS_6B + + return is_baseline_system_6 diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_7.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_7.py new file mode 100644 index 0000000000..ad9c291a15 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_7.py @@ -0,0 +1,127 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_cool_sources_none_or_null import ( + are_all_terminal_cool_sources_none_or_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_fans_null import ( + are_all_terminal_fans_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_heat_sources_hot_water import ( + are_all_terminal_heat_sources_hot_water, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_heating_loops_attached_to_boiler import ( + are_all_terminal_heating_loops_attached_to_boiler, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_heating_loops_purchased_heating import ( + are_all_terminal_heating_loops_purchased_heating, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_types_VAV import ( + are_all_terminal_types_VAV, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.does_each_zone_have_only_one_terminal import ( + does_each_zone_have_only_one_terminal, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_cooling_type_fluid_loop import ( + is_hvac_sys_cooling_type_fluid_loop, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fan_sys_VSD import ( + is_hvac_sys_fan_sys_vsd, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fluid_loop_attached_to_chiller import ( + is_hvac_sys_fluid_loop_attached_to_chiller, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fluid_loop_purchased_CHW import ( + is_hvac_sys_fluid_loop_purchased_chw, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_preheat_fluid_loop_attached_to_boiler import ( + is_hvac_sys_preheat_fluid_loop_attached_to_boiler, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_preheat_fluid_loop_purchased_heating import ( + is_hvac_sys_preheat_fluid_loop_purchased_heating, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_preheating_type_fluid_loop import ( + is_hvac_sys_preheating_type_fluid_loop, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.utility_functions import ( + has_cooling_system, + has_heating_system, + has_preheat_system, +) + +HEATING_SYSTEM = SchemaEnums.schema_enums["HeatingSystemOptions"] + + +def is_baseline_system_7(rmd_b, hvac_b_id, terminal_unit_id_list, zone_id_list): + """ + Get either Sys-7, Sys-7a, Sys-7b, Sys-7c or Not_Sys_7 string output which indicates whether the HVAC system is + ASHRAE 90.1 2019 Appendix G system 7 (VAV with Reheat), system 7a (system 7 with purchased CHW), system 7b ( + system 7 with purchased heating), or system 7c (system 7 with purchased heating and purchased CHW). + + Parameters + ---------- + rmd_b: JSON, To evaluate if the hvac system is modeled as either Sys-7, Sys-7a, Sys-7b, Sys-7c or Not_Sys_7 in the B_rmd. + hvac_b_id: String, The id of the hvac system to evaluate. + terminal_unit_id_list: List, list of terminal unit IDs associated with the HVAC system to be evaluated. These are + sent to this function from the master get_baseline_system_types function. + zone_id_list: List, list of zone IDs associated with the HVAC system to be evaluated. These are sent to this + function from the master get_baseline_system_types function. + + Returns: The function returns either Sys-7, Sys-7a, Sys-7b, Sys-7c or Not_Sys_7 string output which indicates + whether the HVAC system is ASHRAE 90.1 2019 Appendix G system 7 (VAV with Reheat), system 7a (system 7 with + purchased CHW), system 7b (system 7 with purchased heating), pr system 7c (system 7 with purchased heating and + purchased CHW). ------- + """ + is_baseline_system_7 = HVAC_SYS.UNMATCHED + + # check if the hvac system has the required sub systems for system type 7 + # if heating system DOESN'T exist and preheat/cooling systems exist, has_required_sys=True, else, False. + has_required_sys = ( + has_preheat_system(rmd_b, hvac_b_id) + and not has_heating_system(rmd_b, hvac_b_id) + and has_cooling_system(rmd_b, hvac_b_id) + ) + + are_sys_data_matched = ( + # short-circuit the logic if no required data is found. + has_required_sys + # sub functions handles missing required sys, and return False. + and is_hvac_sys_preheating_type_fluid_loop(rmd_b, hvac_b_id) + and is_hvac_sys_cooling_type_fluid_loop(rmd_b, hvac_b_id) + and is_hvac_sys_fan_sys_vsd(rmd_b, hvac_b_id) + and does_each_zone_have_only_one_terminal(rmd_b, zone_id_list) + and are_all_terminal_heat_sources_hot_water(rmd_b, terminal_unit_id_list) + and are_all_terminal_cool_sources_none_or_null(rmd_b, terminal_unit_id_list) + and are_all_terminal_fans_null(rmd_b, terminal_unit_id_list) + and are_all_terminal_types_VAV(rmd_b, terminal_unit_id_list) + ) + + if are_sys_data_matched: + # Confirm required data for Sys-7, now to decide which system type 7 + is_hvac_sys_fluid_loop_attached_to_chiller_flag = ( + is_hvac_sys_fluid_loop_attached_to_chiller(rmd_b, hvac_b_id) + ) + is_hvac_sys_fluid_loop_purchased_chw_flag = ( + is_hvac_sys_fluid_loop_purchased_chw(rmd_b, hvac_b_id) + ) + if is_hvac_sys_preheat_fluid_loop_attached_to_boiler( + rmd_b, hvac_b_id + ) and are_all_terminal_heating_loops_attached_to_boiler( + rmd_b, terminal_unit_id_list + ): + if is_hvac_sys_fluid_loop_attached_to_chiller_flag: + is_baseline_system_7 = HVAC_SYS.SYS_7 + elif is_hvac_sys_fluid_loop_purchased_chw_flag: + is_baseline_system_7 = HVAC_SYS.SYS_7A + elif is_hvac_sys_preheat_fluid_loop_purchased_heating( + rmd_b, hvac_b_id + ) and are_all_terminal_heating_loops_purchased_heating( + rmd_b, terminal_unit_id_list + ): + if is_hvac_sys_fluid_loop_attached_to_chiller_flag: + is_baseline_system_7 = HVAC_SYS.SYS_7B + elif is_hvac_sys_fluid_loop_purchased_chw_flag: + is_baseline_system_7 = HVAC_SYS.SYS_7C + + return is_baseline_system_7 diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_8.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_8.py new file mode 100644 index 0000000000..1acaeba8aa --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_8.py @@ -0,0 +1,119 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_cool_sources_none_or_null import ( + are_all_terminal_cool_sources_none_or_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_fan_configs_parallel import ( + are_all_terminal_fan_configs_parallel, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_heat_sources_electric import ( + are_all_terminal_heat_sources_electric, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_heat_sources_hot_water import ( + are_all_terminal_heat_sources_hot_water, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_heating_loops_purchased_heating import ( + are_all_terminal_heating_loops_purchased_heating, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_types_VAV import ( + are_all_terminal_types_VAV, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.do_all_terminals_have_one_fan import ( + do_all_terminals_have_one_fan, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.does_each_zone_have_only_one_terminal import ( + does_each_zone_have_only_one_terminal, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_cooling_type_fluid_loop import ( + is_hvac_sys_cooling_type_fluid_loop, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fan_sys_VSD import ( + is_hvac_sys_fan_sys_vsd, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fluid_loop_attached_to_chiller import ( + is_hvac_sys_fluid_loop_attached_to_chiller, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fluid_loop_purchased_CHW import ( + is_hvac_sys_fluid_loop_purchased_chw, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_preheat_fluid_loop_purchased_heating import ( + is_hvac_sys_preheat_fluid_loop_purchased_heating, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_preheating_type_elec_resistance import ( + is_hvac_sys_preheating_type_elec_resistance, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_preheating_type_fluid_loop import ( + is_hvac_sys_preheating_type_fluid_loop, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.utility_functions import has_heating_system, has_preheat_system + +HEATING_SYSTEM = SchemaEnums.schema_enums["HeatingSystemOptions"] + + +def is_baseline_system_8(rmd_b, hvac_b_id, terminal_unit_id_list, zone_id_list): + """ + Get either Sys-8, Sys-8a, Sys-8b, Sys-8c, or Not_Sys_8 string output which indicates whether the HVAC system is ASHRAE 90.1 2019 Appendix G system 8 (VAV with Parallel Fan-Powered Boxes and Reheat), system 8a (system 8 with purchased CHW), system 8b (system 8 with purchased heating), or 8c (system 8 with purchased heating and purchased chilled water). + + Parameters + ---------- + rmd_b json + To evaluate if the hvac system is modeled as either Sys-8, Sys-8a, Sys-8b, Sys-8c, or Not_Sys_8 in the B_RMD. + hvac_b_id list + The id of the hvac system to evaluate. + terminal_unit_id_list + List list of terminal unit IDs associated with the HVAC system to be evaluated. These are sent to this function from the master get_baseline_system_types function. + zone_id_list list + list of zone IDs associated with the HVAC system to be evaluated. These are sent to this function from the master get_baseline_system_types function. + + Returns + ------- + The function returns either Sys-8, Sys-8a, Sys-8b, Sys-8c, or Not_Sys_8 string output which indicates whether the HVAC system is ASHRAE 90.1 2019 Appendix G system 8 (VAV with Parallel Fan-Powered Boxes and Reheat ), system 8a (system 8 with purchased CHW), system 8b (system 8 with purchased heating), or 8c (system 8 with purchased heating and purchased chilled water). + """ + + is_baseline_system_8 = HVAC_SYS.UNMATCHED + + # check if the hvac system has the required sub systems for system type 8 + # If heating system DOESN'T exist and preheat system exists, has_required_sys=True, else, False + has_required_sys = not has_heating_system(rmd_b, hvac_b_id) and has_preheat_system( + rmd_b, hvac_b_id + ) + + are_sys_data_matched = ( + # short-circuit the logic if no required data is found. + has_required_sys + # sub functions handles missing required sys, and return False. + and is_hvac_sys_cooling_type_fluid_loop(rmd_b, hvac_b_id) + and is_hvac_sys_fan_sys_vsd(rmd_b, hvac_b_id) + and does_each_zone_have_only_one_terminal(rmd_b, zone_id_list) + and are_all_terminal_cool_sources_none_or_null(rmd_b, terminal_unit_id_list) + and do_all_terminals_have_one_fan(rmd_b, terminal_unit_id_list) + and are_all_terminal_types_VAV(rmd_b, terminal_unit_id_list) + and are_all_terminal_fan_configs_parallel(rmd_b, terminal_unit_id_list) + ) + + if are_sys_data_matched: + if is_hvac_sys_preheating_type_elec_resistance( + rmd_b, hvac_b_id + ) and are_all_terminal_heat_sources_electric(rmd_b, terminal_unit_id_list): + if is_hvac_sys_fluid_loop_attached_to_chiller(rmd_b, hvac_b_id): + is_baseline_system_8 = HVAC_SYS.SYS_8 + elif is_hvac_sys_fluid_loop_purchased_chw(rmd_b, hvac_b_id): + is_baseline_system_8 = HVAC_SYS.SYS_8A + elif is_hvac_sys_preheating_type_fluid_loop(rmd_b, hvac_b_id): + if ( + is_hvac_sys_preheat_fluid_loop_purchased_heating(rmd_b, hvac_b_id) + and are_all_terminal_heat_sources_hot_water( + rmd_b, terminal_unit_id_list + ) + and are_all_terminal_heating_loops_purchased_heating( + rmd_b, terminal_unit_id_list + ) + ): + if is_hvac_sys_fluid_loop_attached_to_chiller(rmd_b, hvac_b_id): + is_baseline_system_8 = HVAC_SYS.SYS_8B + elif is_hvac_sys_fluid_loop_purchased_chw(rmd_b, hvac_b_id): + is_baseline_system_8 = HVAC_SYS.SYS_8C + + return is_baseline_system_8 diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_9.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_9.py new file mode 100644 index 0000000000..f7bfc8319e --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_9.py @@ -0,0 +1,86 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_cool_sources_none_or_null import ( + are_all_terminal_cool_sources_none_or_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_fans_null import ( + are_all_terminal_fans_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_heat_sources_none_or_null import ( + are_all_terminal_heat_sources_none_or_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_types_CAV import ( + are_all_terminal_types_cav, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.does_each_zone_have_only_one_terminal import ( + does_each_zone_have_only_one_terminal, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.does_hvac_system_serve_single_zone import ( + does_hvac_system_serve_single_zone, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_cooling_type_none_or_non_mechanical import ( + is_hvac_sys_cooling_type_none_or_non_mechanical, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_fan_sys_CV import ( + is_hvac_sys_fan_sys_cv, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_heating_type_furnace import ( + is_hvac_sys_heating_type_furnace, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.is_baseline_system_9b import ( + is_baseline_system_9b, +) +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.utility_functions import has_preheat_system + +HEATING_SYSTEM = SchemaEnums.schema_enums["HeatingSystemOptions"] + + +def is_baseline_system_9(rmd_b, hvac_b_id, terminal_unit_id_list, zone_id_list): + """ + Get either Sys-9, Sys-9b, or Not_Sys_9 string output which indicates whether the HVAC system is ASHRAE 90.1 2019 Appendix G system 9 (Heating and Ventilation) or system 9b (system 9 with purchased heating). + + Parameters + ---------- + rmd_b json + To evaluate if the hvac system is modeled as either Sys-9, Sys-9b, or Not_Sys_9 in the B_RMD. + hvac_b_id list + The id of the hvac system to evaluate. + terminal_unit_id_list + List list of terminal unit IDs associated with the HVAC system to be evaluated. These are sent to this function from the master get_baseline_system_types function. + zone_id_list list + list of zone IDs associated with the HVAC system to be evaluated. These are sent to this function from the master get_baseline_system_types function. + + Returns + ------- + The function returns either Sys-9, Sys-9b, or Not_Sys_9 string output which indicates whether the HVAC system is ASHRAE 90.1 2019 Appendix G system 9 (Heating and Ventilation) or system 9b (system 9 with purchased heating). + """ + + is_baseline_system_9 = HVAC_SYS.UNMATCHED + + if is_baseline_system_9b(rmd_b, hvac_b_id, terminal_unit_id_list, zone_id_list): + is_baseline_system_9 = HVAC_SYS.SYS_9B + else: + # if preheat system DOESN'T exist, has_required_sys=True, else, False + has_required_sys = not has_preheat_system(rmd_b, hvac_b_id) + + are_sys_data_matched = ( + # short-circuit the logic if no required data is found. + has_required_sys + # sub functions handles missing required sys, and return False. + and is_hvac_sys_fan_sys_cv(rmd_b, hvac_b_id) + and does_hvac_system_serve_single_zone(rmd_b, zone_id_list) + and does_each_zone_have_only_one_terminal(rmd_b, zone_id_list) + and are_all_terminal_heat_sources_none_or_null(rmd_b, terminal_unit_id_list) + and are_all_terminal_cool_sources_none_or_null(rmd_b, terminal_unit_id_list) + and are_all_terminal_fans_null(rmd_b, terminal_unit_id_list) + and are_all_terminal_types_cav(rmd_b, terminal_unit_id_list) + and is_hvac_sys_cooling_type_none_or_non_mechanical(rmd_b, hvac_b_id) + and is_hvac_sys_heating_type_furnace(rmd_b, hvac_b_id) + ) + + if are_sys_data_matched: + is_baseline_system_9 = HVAC_SYS.SYS_9 + + return is_baseline_system_9 diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_9b.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_9b.py new file mode 100644 index 0000000000..1cc55f6425 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/is_baseline_system_9b.py @@ -0,0 +1,82 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_chw_loops_purcahsed_cooling import ( + are_all_terminal_chw_loops_purchased_cooling, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_cool_sources_chilled_water import ( + are_all_terminal_cool_sources_chilled_water, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_heat_sources_hot_water import ( + are_all_terminal_heat_sources_hot_water, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_heating_loops_purchased_heating import ( + are_all_terminal_heating_loops_purchased_heating, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.are_all_terminal_types_CAV_with_none_equal_to_null import ( + are_all_terminal_types_cav_with_none_equal_to_null, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.do_all_terminals_have_one_fan import ( + do_all_terminals_have_one_fan, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.does_each_zone_have_only_one_terminal import ( + does_each_zone_have_only_one_terminal, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_sub_functions.is_hvac_sys_cooling_type_none_or_non_mechanical import ( + is_hvac_sys_cooling_type_none_or_non_mechanical, +) +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.utility_functions import ( + has_fan_system, + has_heating_system, + has_preheat_system, +) + +HEATING_SYSTEM = SchemaEnums.schema_enums["HeatingSystemOptions"] + + +def is_baseline_system_9b(rmd_b, hvac_b_id, terminal_unit_id_list, zone_id_list): + """ + Returns true or false to whether the baseline system type is 9b (system 9 with purchased HW). + + Parameters + ---------- + rmd_b json + To evaluate if the hvac system is modeled as either Sys-9bin the B_RMD. + hvac_b_id list + The id of the hvac system to evaluate. + terminal_unit_id_list + List list of terminal unit IDs associated with the HVAC system to be evaluated. These are sent to this function from the master get_baseline_system_types function. + zone_id_list list + list of zone IDs associated with the HVAC system to be evaluated. These are sent to this function from the master get_baseline_system_types function. + + Returns + ------- + Returns true or false to whether the baseline system type is 9b (system 9 with purchased HW). + """ + + # if preheat, heating, and fan systems DON'T exist, has_required_sys=True, else, False + has_required_sys = not ( + has_preheat_system(rmd_b, hvac_b_id) + and has_heating_system(rmd_b, hvac_b_id) + and has_fan_system(rmd_b, hvac_b_id) + ) + + return ( + # short-circuit the logic if no required data is found. + has_required_sys + # sub functions handles missing required sys, and return False. + and is_hvac_sys_cooling_type_none_or_non_mechanical(rmd_b, hvac_b_id) + and does_each_zone_have_only_one_terminal(rmd_b, zone_id_list) + and are_all_terminal_heat_sources_hot_water(rmd_b, terminal_unit_id_list) + and do_all_terminals_have_one_fan(rmd_b, terminal_unit_id_list) + and are_all_terminal_types_cav_with_none_equal_to_null( + rmd_b, terminal_unit_id_list + ) + and are_all_terminal_heating_loops_purchased_heating( + rmd_b, terminal_unit_id_list + ) + and not are_all_terminal_cool_sources_chilled_water( + rmd_b, terminal_unit_id_list + ) + and not are_all_terminal_chw_loops_purchased_cooling( + rmd_b, terminal_unit_id_list + ) + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_1.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_1.py new file mode 100644 index 0000000000..b2ee7c50e7 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_1.py @@ -0,0 +1,314 @@ +# hvac_id = "PTAC 1" => Sys_1, [Thermal Zone 1], [PTAC Terminal 1] +# hvac_id = "PTAC 1a" => Sys_1a, [Thermal Zone 1a], [PTAC Terminal 1a] +# hvac_id = "PTAC 1b" => Sys_1b, [Thermal Zone 1b], [PTAC Terminal 1b] +# hvac_id = "PTAC 1c" => Sys_1c, [Thermal Zone 1c], [PTAC Terminal 1c] +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_test_util import ( + load_system_test_file, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.is_baseline_system_1 import ( + is_baseline_system_1, +) +from rct229.schema.validate import schema_validate_rpd + +SYS_1_TEST_RMD = { + "id": "ASHRAE229 1", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Building Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 1", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "PTAC Terminal 1", + "is_supply_ducted": False, + "type": "CONSTANT_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "PTAC 1", + } + ], + }, + { + "id": "Thermal Zone 1b", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "PTAC Terminal 1b", + "is_supply_ducted": False, + "type": "CONSTANT_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "PTAC 1b", + } + ], + }, + { + "id": "Thermal Zone 1a", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "PTAC Terminal 1a", + "is_supply_ducted": False, + "type": "CONSTANT_AIR_VOLUME", + "fan": {"id": "fan 1a"}, + "heating_source": "HOT_WATER", + "heating_from_loop": "Boiler Loop 1", + "cooling_source": "CHILLED_WATER", + "cooling_from_loop": "Purchased CHW Loop 1", + # this is necessary in the current setup. + # This parameter connects hvac_b with specific terminal so that the + # function is_baseline_system_1 can execute within the correct zone list. + "served_by_heating_ventilating_air_conditioning_system": "PTAC 1a", + } + ], + }, + { + "id": "Thermal Zone 1c", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "PTAC Terminal 1c", + "is_supply_ducted": False, + "type": "CONSTANT_AIR_VOLUME", + "fan": {"id": "fan 1c"}, + "heating_source": "HOT_WATER", + "heating_from_loop": "Purchased HW Loop 1", + "cooling_source": "CHILLED_WATER", + "cooling_from_loop": "Purchased CHW Loop 1", + # this is necessary in the current setup. + # This parameter connects hvac_b with specific terminal so that the + # function is_baseline_system_1 can execute within the correct zone list. + "served_by_heating_ventilating_air_conditioning_system": "PTAC 1c", + } + ], + }, + ], + "heating_ventilating_air_conditioning_systems": [ + { + "id": "PTAC 1", + "cooling_system": { + "id": "DX Coil 1", + "type": "DIRECT_EXPANSION", + }, + "heating_system": { + "id": "HHW Coil 1", + "type": "FLUID_LOOP", + "hot_water_loop": "Boiler Loop 1", + }, + "fan_system": { + "id": "CAV Fan System 1", + "fan_control": "CONSTANT", + "supply_fans": [{"id": "Supply Fan 1"}], + }, + }, + { + "id": "PTAC 1b", + "cooling_system": { + "id": "DX Coil 1b", + "type": "DIRECT_EXPANSION", + }, + "heating_system": { + "id": "HHW Coil 1b", + "type": "FLUID_LOOP", + "hot_water_loop": "Purchased HW Loop 1", + }, + "fan_system": { + "id": "CAV Fan System 1b", + "fan_control": "CONSTANT", + "supply_fans": [{"id": "Supply Fan 1b"}], + }, + }, + { + "id": "PTAC 1a", + }, + { + "id": "PTAC 1c", + }, + ], + } + ], + } + ], + "chillers": [ + { + "id": "Chiller 1", + "cooling_loop": "Chiller Loop 1", + "condensing_loop": "Condenser Loop 1", + } + ], + "boilers": [ + { + "id": "Boiler 1", + "loop": "Boiler Loop 1", + "energy_source_type": "NATURAL_GAS", + } + ], + "fluid_loops": [ + { + "id": "Boiler Loop 1", + "type": "HEATING", + "heating_design_and_control": { + "id": "DAC1", + "minimum_flow_fraction": 0.25, + }, + }, + { + "id": "Purchased HW Loop 1", + "type": "HEATING", + }, + { + "id": "Purchased CHW Loop 1", + "type": "COOLING", + }, + { + "id": "Chiller Loop 1", + "type": "COOLING", + }, + ], + "external_fluid_sources": [ + { + "id": "Purchased HW", + "loop": "Purchased HW Loop 1", + "type": "HOT_WATER", + }, + { + "id": "Purchased CHW", + "loop": "Purchased CHW Loop 1", + "type": "CHILLED_WATER", + }, + ], + "type": "BASELINE_0", + } + ], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RMD_baseline_system_1__is_valid(): + schema_validation_result = schema_validate_rpd(SYS_1_TEST_RMD) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__is_baseline_system_1__true(): + assert ( + is_baseline_system_1( + SYS_1_TEST_RMD["ruleset_model_descriptions"][0], + "PTAC 1", + ["PTAC Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_1 + ) + + +def test__is_baseline_system_1A__true(): + assert ( + is_baseline_system_1( + SYS_1_TEST_RMD["ruleset_model_descriptions"][0], + "PTAC 1a", + ["PTAC Terminal 1a"], + ["Thermal Zone 1a"], + ) + == HVAC_SYS.SYS_1A + ) + + +def test__is_baseline_system_1A__test_json_true(): + assert ( + is_baseline_system_1( + load_system_test_file("System_1a_PTAC.json")["ruleset_model_descriptions"][ + 0 + ], + "PTAC 1", + ["PTAC Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_1A + ) + + +def test__is_baseline_system_1B__true(): + assert ( + is_baseline_system_1( + SYS_1_TEST_RMD["ruleset_model_descriptions"][0], + "PTAC 1b", + ["PTAC Terminal 1b"], + ["Thermal Zone 1b"], + ) + == HVAC_SYS.SYS_1B + ) + + +def test__is_baseline_system_1B__test_json_true(): + assert ( + is_baseline_system_1( + load_system_test_file("System_1b_PTAC.json")["ruleset_model_descriptions"][ + 0 + ], + "PTAC 1", + ["PTAC Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_1B + ) + + +def test__is_baseline_system_1C__true(): + assert ( + is_baseline_system_1( + SYS_1_TEST_RMD["ruleset_model_descriptions"][0], + "PTAC 1c", + ["PTAC Terminal 1c"], + ["Thermal Zone 1c"], + ) + == HVAC_SYS.SYS_1C + ) + + +def test__is_baseline_system_1__test_json_true(): + assert ( + is_baseline_system_1( + load_system_test_file("System_1_PTAC.json")["ruleset_model_descriptions"][ + 0 + ], + "PTAC 1", + ["PTAC Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_1 + ) + + +def test__is_baseline_system_1C__test_json_true(): + assert ( + is_baseline_system_1( + load_system_test_file("System_1c_PTAC.json")["ruleset_model_descriptions"][ + 0 + ], + "PTAC 1", + ["PTAC Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_1C + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_10.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_10.py new file mode 100644 index 0000000000..0c5fb8f176 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_10.py @@ -0,0 +1,179 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_test_util import ( + load_system_test_file, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.is_baseline_system_10 import ( + is_baseline_system_10, +) +from rct229.schema.validate import schema_validate_rpd + +# The first logic means the first half of the `is_baseline_system_10'. The first logic is used when there are NO preheat/heating and fan systems. +# The second logic means the second half of the `is_baseline_system_10'. The second logic is used when there is NO preheat system and there is heating/fan systems. +SYS_10_FIRST_LOGIC_TEST_RMD = { + "id": "ASHRAE229 1", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Building Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 1", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "Air Terminal 1", + "is_supply_ducted": True, + "type": "CONSTANT_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 10", + "heating_source": "ELECTRIC", + "fan": {"id": "Terminal Fan 1"}, + } + ], + }, + ], + "heating_ventilating_air_conditioning_systems": [ + { + "id": "System 10", + }, + ], + } + ], + } + ], + "type": "BASELINE_0", + } + ], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +SYS_10_SECOND_LOGIC_TEST_RMD = { + "id": "ASHRAE229 1", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Building Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 2", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "Air Terminal 2", + "is_supply_ducted": True, + "type": "CONSTANT_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 10", + } + ], + }, + ], + "heating_ventilating_air_conditioning_systems": [ + { + "id": "System 10", + "cooling_system": { + "id": "Cooling Coil 1", + "type": "NONE", + }, + "heating_system": { + "id": "HHW Coil 1A", + "type": "ELECTRIC_RESISTANCE", + }, + "fan_system": { + "id": "VAV Fan System 1", + "fan_control": "CONSTANT", + "supply_fans": [{"id": "Supply Fan 1"}], + "return_fans": [{"id": "Return Fan 1"}], + }, + }, + ], + } + ], + } + ], + "type": "BASELINE_0", + } + ], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RMD_baseline_system_10__is_first_logic_valid(): + schema_validation_result = schema_validate_rpd(SYS_10_FIRST_LOGIC_TEST_RMD) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__TEST_RMD_baseline_system_10__is_second_logic_valid(): + schema_validation_result = schema_validate_rpd(SYS_10_FIRST_LOGIC_TEST_RMD) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__is_baseline_system_10__first_logic_true(): + assert ( + is_baseline_system_10( + SYS_10_FIRST_LOGIC_TEST_RMD["ruleset_model_descriptions"][0], + "System 10", + ["Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_10 + ) + + +def test__is_baseline_system_10__second_logic_true(): + assert ( + is_baseline_system_10( + SYS_10_SECOND_LOGIC_TEST_RMD["ruleset_model_descriptions"][0], + "System 10", + ["Air Terminal 2"], + ["Thermal Zone 2"], + ) + == HVAC_SYS.SYS_10 + ) + + +def test__is_baseline_system_10__test_json_true(): + assert ( + is_baseline_system_10( + load_system_test_file("System_10_Warm_Air_Furnace_Elec.json")[ + "ruleset_model_descriptions" + ][0], + "System 10", + ["Air Terminal"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_10 + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_11_1.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_11_1.py new file mode 100644 index 0000000000..86886f0643 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_11_1.py @@ -0,0 +1,490 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_test_util import ( + load_system_test_file, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.is_baseline_system_11_1 import ( + is_baseline_system_11_1, +) +from rct229.schema.validate import schema_validate_rpd + +SYS_11_1_TEST_RMD = { + "id": "ASHRAE229 1", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Building Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 1B", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "VAV Air Terminal 1B", + "is_supply_ducted": True, + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System Type 11B", + } + ], + }, + { + "id": "Thermal Zone 1", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "VAV Air Terminal 1", + "is_supply_ducted": True, + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System Type 11", + } + ], + }, + { + "id": "Thermal Zone 1A", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "VAV Air Terminal 1A", + "is_supply_ducted": True, + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System Type 11A", + } + ], + }, + { + "id": "Thermal Zone 1C", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "VAV Air Terminal 1C", + "is_supply_ducted": True, + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System Type 11C", + } + ], + }, + ], + "heating_ventilating_air_conditioning_systems": [ + { + "id": "System Type 11B", + "cooling_system": { + "id": "CHW Coil 1B", + "type": "FLUID_LOOP", + "chilled_water_loop": "Secondary Loop 1", + }, + "heating_system": { + "id": "HHW Coil 1B", + "type": "FLUID_LOOP", + "hot_water_loop": "Purchased HW Loop 1", + }, + "fan_system": { + "id": "VAV Fan System 1B", + "fan_control": "VARIABLE_SPEED_DRIVE", + "supply_fans": [{"id": "Supply Fan 1B"}], + "return_fans": [{"id": "Return Fan 1B"}], + }, + }, + { + "id": "System Type 11", + "cooling_system": { + "id": "CHW Coil 1", + "type": "FLUID_LOOP", + "chilled_water_loop": "Secondary Loop 1", + }, + "heating_system": { + "id": "HHW Coil 1", + "type": "ELECTRIC_RESISTANCE", + }, + "fan_system": { + "id": "VAV Fan System 1", + "fan_control": "VARIABLE_SPEED_DRIVE", + "supply_fans": [{"id": "Supply Fan 1"}], + "return_fans": [{"id": "Return Fan 1"}], + }, + }, + { + "id": "System Type 11A", + "cooling_system": { + "id": "CHW Coil 1A", + "type": "FLUID_LOOP", + "chilled_water_loop": "Purchased CHW Loop 1", + }, + "heating_system": { + "id": "HHW Coil 1A", + "type": "ELECTRIC_RESISTANCE", + }, + "fan_system": { + "id": "VAV Fan System 1A", + "fan_control": "VARIABLE_SPEED_DRIVE", + "supply_fans": [{"id": "Supply Fan 1A"}], + "return_fans": [{"id": "Return Fan 1A"}], + }, + }, + { + "id": "System Type 11C", + "cooling_system": { + "id": "CHW Coil 1C", + "type": "FLUID_LOOP", + "chilled_water_loop": "Purchased CHW Loop 1", + }, + "heating_system": { + "id": "HHW Coil 1C", + "type": "FLUID_LOOP", + "hot_water_loop": "Purchased HW Loop 1", + }, + "fan_system": { + "id": "VAV Fan System 1C", + "fan_control": "VARIABLE_SPEED_DRIVE", + "supply_fans": [{"id": "Supply Fan 1C"}], + "return_fans": [{"id": "Return Fan 1C"}], + }, + }, + ], + } + ], + } + ], + "chillers": [ + { + "id": "Chiller 1", + "cooling_loop": "Chiller Loop 1", + "energy_source_type": "ELECTRICITY", + } + ], + "external_fluid_sources": [ + { + "id": "Purchased HW 1", + "loop": "Purchased HW Loop 1", + "type": "HOT_WATER", + }, + { + "id": "Purchased CHW", + "loop": "Purchased CHW Loop 1", + "type": "CHILLED_WATER", + }, + ], + "pumps": [ + { + "id": "HW Pump 1", + "loop_or_piping": "Purchased HW Loop 1", + "speed_control": "FIXED_SPEED", + }, + { + "id": "Chiller Pump 1", + "loop_or_piping": "Chiller Loop 1", + "speed_control": "FIXED_SPEED", + }, + ], + "fluid_loops": [ + {"id": "Purchased HW Loop 1", "type": "HEATING"}, + { + "id": "Chiller Loop 1", + "type": "COOLING", + "child_loops": [{"id": "Secondary Loop 1", "type": "COOLING"}], + }, + { + "id": "Purchased CHW Loop 1", + "type": "COOLING", + }, + ], + "type": "BASELINE_0", + } + ], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + +SYS_11_1_TEST_UNMATCHED_RMD = { + "id": "ASHRAE229 1", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Building Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 1", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "Air Terminal 1", + "is_supply_ducted": True, + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System Type 11A", + } + ], + }, + { + "id": "Thermal Zone 2", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "Air Terminal 2", + "is_supply_ducted": True, + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System Type 11C", + } + ], + }, + ], + "heating_ventilating_air_conditioning_systems": [ + { + "id": "System Type Unmatched 1", + "cooling_system": { + "id": "CHW Coil 1A", + "type": "FLUID_LOOP", + "chilled_water_loop": "CHW Loop 1", + }, + "heating_system": { + "id": "HHW Coil 1A", + "type": "ELECTRIC_RESISTANCE", + }, + "fan_system": { + "id": "VAV Fan System 1A", + "fan_control": "VARIABLE_SPEED_DRIVE", + "supply_fans": [{"id": "Supply Fan 1A"}], + "return_fans": [{"id": "Return Fan 1A"}], + }, + }, + { + "id": "System Type Unmatched 2", + "cooling_system": { + "id": "CHW Coil 1C", + "type": "FLUID_LOOP", + "chilled_water_loop": "CHW Loop 1", + }, + "heating_system": { + "id": "HHW Coil 1C", + "type": "FLUID_LOOP", + "hot_water_loop": "Purchased HW Loop 1", + }, + "fan_system": { + "id": "VAV Fan System 1C", + "fan_control": "VARIABLE_SPEED_DRIVE", + "supply_fans": [{"id": "Supply Fan 1C"}], + "return_fans": [{"id": "Return Fan 1C"}], + }, + }, + ], + } + ], + } + ], + "chillers": [ + { + "id": "Chiller 1", + "cooling_loop": "Chiller Loop 1", + "energy_source_type": "ELECTRICITY", + } + ], + "external_fluid_sources": [ + { + "id": "Purchased HW 1", + "loop": "Purchased HW Loop 1", + "type": "HOT_WATER", + }, + ], + "pumps": [ + { + "id": "HW Pump 1", + "loop_or_piping": "Purchased HW Loop 1", + "speed_control": "FIXED_SPEED", + }, + { + "id": "Chiller Pump 1", + "loop_or_piping": "Chiller Loop 1", + "speed_control": "FIXED_SPEED", + }, + ], + "fluid_loops": [ + {"id": "Purchased HW Loop 1", "type": "HEATING"}, + { + "id": "Chiller Loop 1", + "type": "COOLING", + "child_loops": [{"id": "Secondary Loop 1", "type": "COOLING"}], + }, + { + "id": "CHW Loop 1", + "type": "COOLING", + }, + ], + "type": "BASELINE_0", + } + ], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RMD_baseline_system_11_1__is_valid(): + schema_validation_result = schema_validate_rpd(SYS_11_1_TEST_RMD) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__TEST_RMD_baseline_system_11_1__is_unmatched_valid(): + schema_validation_result = schema_validate_rpd(SYS_11_1_TEST_UNMATCHED_RMD) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__is_baseline__system_11_1__true(): + assert ( + is_baseline_system_11_1( + SYS_11_1_TEST_RMD["ruleset_model_descriptions"][0], + "System Type 11", + ["VAV Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_11_1 + ) + + +def test__is_baseline_system_11_1__test_json_true(): + assert ( + is_baseline_system_11_1( + load_system_test_file("System_11.1_VAV_SZ.json")[ + "ruleset_model_descriptions" + ][0], + "System 11", + ["VAV Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_11_1 + ) + + +def test__is_baseline_system_11_1A__true(): + assert ( + is_baseline_system_11_1( + SYS_11_1_TEST_RMD["ruleset_model_descriptions"][0], + "System Type 11A", + ["VAV Air Terminal 1A"], + ["Thermal Zone 1A"], + ) + == HVAC_SYS.SYS_11_1A + ) + + +def test__is_baseline_system_11_1A__test_json_true(): + assert ( + is_baseline_system_11_1( + load_system_test_file("System_11.1a_VAV_SZ.json")[ + "ruleset_model_descriptions" + ][0], + "System 11", + ["VAV Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_11_1A + ) + + +def test__is_baseline_system_11_1B__true(): + assert ( + is_baseline_system_11_1( + SYS_11_1_TEST_RMD["ruleset_model_descriptions"][0], + "System Type 11B", + ["VAV Air Terminal 1B"], + ["Thermal Zone 1B"], + ) + == HVAC_SYS.SYS_11_1B + ) + + +def test__is_baseline_system_11_1B__test_json_true(): + assert ( + is_baseline_system_11_1( + load_system_test_file("System_11.1b_VAV_SZ.json")[ + "ruleset_model_descriptions" + ][0], + "System 11", + ["VAV Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_11_1B + ) + + +def test__is_baseline_system_11_1C__true(): + assert ( + is_baseline_system_11_1( + SYS_11_1_TEST_RMD["ruleset_model_descriptions"][0], + "System Type 11C", + ["VAV Air Terminal 1C"], + ["Thermal Zone 1C"], + ) + == HVAC_SYS.SYS_11_1C + ) + + +def test__is_baseline_system_11_1C__test_json_true(): + assert ( + is_baseline_system_11_1( + load_system_test_file("System_11.1c_VAV_SZ.json")[ + "ruleset_model_descriptions" + ][0], + "System 11", + ["VAV Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_11_1C + ) + + +def test__is_baseline_system_11_1__no_ays_true(): + # `no_sys` means there is no matched system and this is for testing when there is no matching system (11.1, 11.1A, 11.1B, 11.1C) + assert ( + is_baseline_system_11_1( + SYS_11_1_TEST_UNMATCHED_RMD["ruleset_model_descriptions"][0], + "System Type Unmatched 1", + ["Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.UNMATCHED + ) + + +def test__is_baseline_system_11_1__no_ays_true(): + # `no_sys` means there is no matched system and this is for testing when there is no matching system (11.1, 11.1A, 11.1B, 11.1C) + assert ( + is_baseline_system_11_1( + SYS_11_1_TEST_UNMATCHED_RMD["ruleset_model_descriptions"][0], + "System Type Unmatched 2", + ["Air Terminal 2"], + ["Thermal Zone 2"], + ) + == HVAC_SYS.UNMATCHED + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_11_2.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_11_2.py new file mode 100644 index 0000000000..611a8755b5 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_11_2.py @@ -0,0 +1,341 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_test_util import ( + load_system_test_file, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.is_baseline_system_11_2 import ( + is_baseline_system_11_2, +) +from rct229.schema.validate import schema_validate_rpd + +SYS_11_2_TEST_RMD = { + "id": "ASHRAE229 1", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Building Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 1", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "VAV Air Terminal 1", + "is_supply_ducted": True, + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System Type 11.2", + } + ], + }, + { + "id": "Thermal Zone 1A", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "VAV Air Terminal 1A", + "is_supply_ducted": True, + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System Type 11.2A", + } + ], + }, + ], + "heating_ventilating_air_conditioning_systems": [ + { + "id": "System Type 11.2", + "cooling_system": { + "id": "CHW Coil 1", + "type": "FLUID_LOOP", + "chilled_water_loop": "Secondary Loop 1", + }, + "heating_system": { + "id": "HHW Coil 1", + "type": "FLUID_LOOP", + "hot_water_loop": "Boiler Loop 1", + }, + "fan_system": { + "id": "VAV Fan System 1", + "fan_control": "VARIABLE_SPEED_DRIVE", + "supply_fans": [{"id": "Supply Fan 1"}], + "return_fans": [{"id": "Return Fan 1"}], + }, + }, + { + "id": "System Type 11.2A", + "cooling_system": { + "id": "CHW Coil 1A", + "type": "FLUID_LOOP", + "chilled_water_loop": "Purchased CHW Loop 1", + }, + "heating_system": { + "id": "HHW Coil 1A", + "type": "FLUID_LOOP", + "hot_water_loop": "Boiler Loop 1", + }, + "fan_system": { + "id": "VAV Fan System 1A", + "fan_control": "VARIABLE_SPEED_DRIVE", + "supply_fans": [{"id": "Supply Fan 1A"}], + "return_fans": [{"id": "Return Fan 1A"}], + }, + }, + ], + } + ], + } + ], + "boilers": [ + { + "id": "Boiler 1", + "loop": "Boiler Loop 1", + "design_capacity": 117228.44444444445, + } + ], + "chillers": [ + { + "id": "Chiller 1", + "cooling_loop": "Chiller Loop 1", + "energy_source_type": "ELECTRICITY", + } + ], + "external_fluid_sources": [ + { + "id": "Purchased CHW 1", + "loop": "Purchased CHW Loop 1", + "type": "CHILLED_WATER", + } + ], + "pumps": [ + { + "id": "HW Pump 1", + "loop_or_piping": "Boiler Loop 1", + "speed_control": "FIXED_SPEED", + }, + { + "id": "Chiller Pump 1", + "loop_or_piping": "Chiller Loop 1", + "speed_control": "FIXED_SPEED", + }, + ], + "fluid_loops": [ + { + "id": "Boiler Loop 1", + "type": "HEATING", + "heating_design_and_control": { + "id": "DAC1", + "minimum_flow_fraction": 0.25, + }, + }, + { + "id": "Chiller Loop 1", + "type": "COOLING", + "child_loops": [{"id": "Secondary Loop 1", "type": "COOLING"}], + }, + {"id": "Purchased CHW Loop 1", "type": "COOLING"}, + ], + "type": "BASELINE_0", + } + ], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + +SYS_11_2_TEST_UNMATCHED_RMD = { + "id": "ASHRAE229 1", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Building Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 1", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "Air Terminal 1", + "is_supply_ducted": True, + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System Type Unmatched", + } + ], + }, + ], + "heating_ventilating_air_conditioning_systems": [ + { + "id": "System Type Unmatched", + "cooling_system": { + "id": "CHW Coil 1A", + "type": "FLUID_LOOP", + "chilled_water_loop": "CHW Loop 1", + }, + "heating_system": { + "id": "HHW Coil 1A", + "type": "FLUID_LOOP", + "hot_water_loop": "Boiler Loop 1", + }, + "fan_system": { + "id": "VAV Fan System 1", + "fan_control": "VARIABLE_SPEED_DRIVE", + "supply_fans": [{"id": "Supply Fan 1"}], + "return_fans": [{"id": "Return Fan 1"}], + }, + }, + ], + } + ], + } + ], + "boilers": [ + { + "id": "Boiler 1", + "loop": "Boiler Loop 1", + "design_capacity": 117228.44444444445, + } + ], + "chillers": [ + { + "id": "Chiller 1", + "cooling_loop": "Chiller Loop 1", + "energy_source_type": "ELECTRICITY", + } + ], + "pumps": [ + { + "id": "HW Pump 1", + "loop_or_piping": "Boiler Loop 1", + "speed_control": "FIXED_SPEED", + }, + { + "id": "Chiller Pump 1", + "loop_or_piping": "Chiller Loop 1", + "speed_control": "FIXED_SPEED", + }, + ], + "fluid_loops": [ + { + "id": "Boiler Loop 1", + "type": "HEATING", + "heating_design_and_control": { + "id": "DAC1", + "minimum_flow_fraction": 0.25, + }, + }, + { + "id": "Chiller Loop 1", + "type": "COOLING", + "child_loops": [{"id": "Secondary Loop 1", "type": "COOLING"}], + }, + {"id": "CHW Loop 1", "type": "COOLING"}, + ], + "type": "BASELINE_0", + } + ], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RMD_baseline_system_11_2__is_valid(): + schema_validation_result = schema_validate_rpd(SYS_11_2_TEST_RMD) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__TEST_RMD_baseline_system_11_2__is_unmatched_valid(): + schema_validation_result = schema_validate_rpd(SYS_11_2_TEST_UNMATCHED_RMD) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__is_baseline_system_11_2__true(): + assert ( + is_baseline_system_11_2( + SYS_11_2_TEST_RMD["ruleset_model_descriptions"][0], + "System Type 11.2", + ["VAV Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_11_2 + ) + + +def test__is_baseline_system_11_2__test_json_true(): + assert ( + is_baseline_system_11_2( + load_system_test_file("System_11.2_VAV_SZ.json")[ + "ruleset_model_descriptions" + ][0], + "System 11", + ["VAV Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_11_2 + ) + + +def test__is_baseline_system_11_2A__true(): + assert ( + is_baseline_system_11_2( + SYS_11_2_TEST_RMD["ruleset_model_descriptions"][0], + "System Type 11.2A", + ["VAV Air Terminal 1A"], + ["Thermal Zone 1A"], + ) + == HVAC_SYS.SYS_11_2A + ) + + +def test__is_baseline_system_11_2A__test_json_true(): + assert ( + is_baseline_system_11_2( + load_system_test_file("System_11.2a_VAV_SZ.json")[ + "ruleset_model_descriptions" + ][0], + "System 11", + ["VAV Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_11_2A + ) + + +def test__is_baseline_system_unmatched__true(): + assert ( + is_baseline_system_11_2( + SYS_11_2_TEST_UNMATCHED_RMD["ruleset_model_descriptions"][0], + "System Type Unmatched", + ["Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.UNMATCHED + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_12.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_12.py new file mode 100644 index 0000000000..f24933a4f1 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_12.py @@ -0,0 +1,449 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_test_util import ( + load_system_test_file, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.is_baseline_system_12 import ( + is_baseline_system_12, +) +from rct229.schema.validate import schema_validate_rpd + +SYS_12_TEST_RMD = { + "id": "ASHRAE229 1", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Building Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 1", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "CAV Air Terminal 1", + "is_supply_ducted": True, + "type": "CONSTANT_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 12", + } + ], + }, + { + "id": "Thermal Zone 2", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "CAV Air Terminal 2", + "is_supply_ducted": True, + "type": "CONSTANT_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 12a", + } + ], + }, + { + "id": "Thermal Zone 3", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "CAV Air Terminal 3", + "is_supply_ducted": True, + "type": "CONSTANT_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 12b", + } + ], + }, + { + "id": "Thermal Zone 4", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "CAV Air Terminal 4", + "is_supply_ducted": True, + "type": "CONSTANT_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 12d", + } + ], + }, + ], + "heating_ventilating_air_conditioning_systems": [ + { + "id": "System 12", + "cooling_system": { + "id": "CHW Coil 1", + "type": "FLUID_LOOP", + "chilled_water_loop": "Secondary CHW Loop 1", + }, + "heating_system": { + "id": "Boiler Coil 1", + "type": "FLUID_LOOP", + "hot_water_loop": "Boiler Loop 1", + }, + "fan_system": { + "id": "CAV Fan System 1", + "fan_control": "CONSTANT", + "supply_fans": [{"id": "Supply Fan 1"}], + "return_fans": [{"id": "Return Fan 1"}], + }, + }, + { + "id": "System 12a", + "cooling_system": { + "id": "CHW Coil 1", + "type": "FLUID_LOOP", + "chilled_water_loop": "Chilled Water Loop 1", + }, + "heating_system": { + "id": "Boiler Coil 1", + "type": "FLUID_LOOP", + "hot_water_loop": "Boiler Loop 1", + }, + "fan_system": { + "id": "CAV Fan System 1", + "fan_control": "CONSTANT", + "supply_fans": [{"id": "Supply Fan 1"}], + "return_fans": [{"id": "Return Fan 1"}], + }, + }, + { + "id": "System 12b", + "cooling_system": { + "id": "CHW Coil 1", + "type": "FLUID_LOOP", + "chilled_water_loop": "Secondary CHW Loop 1", + }, + "heating_system": { + "id": "HHW Coil 1", + "type": "FLUID_LOOP", + "hot_water_loop": "Purchased HW Loop 1", + }, + "fan_system": { + "id": "CAV Fan System 1", + "fan_control": "CONSTANT", + "supply_fans": [{"id": "Supply Fan 1"}], + "return_fans": [{"id": "Return Fan 1"}], + }, + }, + { + "id": "System 12c", + "cooling_system": { + "id": "CHW Coil 1", + "type": "FLUID_LOOP", + "chilled_water_loop": "Chilled Water Loop 1", + }, + "heating_system": { + "id": "HHW Coil 1", + "type": "FLUID_LOOP", + "hot_water_loop": "Purchased HW Loop 1", + }, + "fan_system": { + "id": "CAV Fan System 1", + "fan_control": "CONSTANT", + "supply_fans": [{"id": "Supply Fan 1"}], + "return_fans": [{"id": "Return Fan 1"}], + }, + }, + ], + } + ], + } + ], + "boilers": [ + { + "id": "Boiler 1", + "loop": "Boiler Loop 1", + "energy_source_type": "NATURAL_GAS", + } + ], + "external_fluid_sources": [ + { + "id": "Purchased CW 1", + "loop": "Chilled Water Loop 1", + "type": "CHILLED_WATER", + }, + { + "id": "Purchased HW 1", + "loop": "Purchased HW Loop 1", + "type": "HOT_WATER", + }, + ], + "chillers": [{"id": "Chiller 1", "cooling_loop": "Chiller Loop 1"}], + "pumps": [ + { + "id": "Boiler Pump 1", + "loop_or_piping": "Boiler Loop 1", + "speed_control": "FIXED_SPEED", + }, + { + "id": "Chiller Pump 1", + "loop_or_piping": "Chiller Loop 1", + "speed_control": "FIXED_SPEED", + }, + { + "id": "Secondary CHW Pump", + "loop_or_piping": "Secondary CHW Loop 1", + "speed_control": "VARIABLE_SPEED", + }, + { + "id": "CHW Pump 1", + "loop_or_piping": "Chilled Water Loop 1", + "speed_control": "FIXED_SPEED", + }, + { + "id": "HW Pump 1", + "loop_or_piping": "Purchased HW Loop 1", + "speed_control": "FIXED_SPEED", + }, + ], + "fluid_loops": [ + {"id": "Boiler Loop 1", "type": "HEATING"}, + { + "id": "Chiller Loop 1", + "type": "COOLING", + "child_loops": [{"id": "Secondary CHW Loop 1", "type": "COOLING"}], + }, + {"id": "Chilled Water Loop 1", "type": "COOLING"}, + {"id": "Purchased HW Loop 1", "type": "HEATING"}, + ], + "type": "BASELINE_0", + } + ], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + +SYS_12_TEST_UNMATCHED_RMD = { + "id": "ASHRAE229 1", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Building Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 12", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "Air Terminal 12", + "is_supply_ducted": True, + "type": "CONSTANT_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System Type Unmatched", + } + ], + }, + ], + "heating_ventilating_air_conditioning_systems": [ + { + "id": "System Type Unmatched", + "cooling_system": { + "id": "CHW Coil 1", + "type": "FLUID_LOOP", + "chilled_water_loop": "Secondary CHW Loop 1", + }, + "heating_system": { + "id": "HHW Coil 1", + "type": "FLUID_LOOP", + "hot_water_loop": "HW Loop 1", + }, + "fan_system": { + "id": "CAV Fan System 1", + "fan_control": "CONSTANT", + "supply_fans": [{"id": "Supply Fan 1"}], + "return_fans": [{"id": "Return Fan 1"}], + }, + }, + ], + } + ], + } + ], + "boilers": [ + { + "id": "Boiler 1", + "loop": "Boiler Loop 1", + "energy_source_type": "NATURAL_GAS", + } + ], + "external_fluid_sources": [ + { + "id": "Purchased CW 1", + "loop": "Chilled Water Loop 1", + "type": "CHILLED_WATER", + }, + ], + "chillers": [{"id": "Chiller 1", "cooling_loop": "Chiller Loop 1"}], + "pumps": [ + { + "id": "Boiler Pump 1", + "loop_or_piping": "Boiler Loop 1", + "speed_control": "FIXED_SPEED", + }, + { + "id": "Chiller Pump 1", + "loop_or_piping": "Chiller Loop 1", + "speed_control": "FIXED_SPEED", + }, + { + "id": "Secondary CHW Pump", + "loop_or_piping": "Secondary CHW Loop 1", + "speed_control": "VARIABLE_SPEED", + }, + { + "id": "CHW Pump 1", + "loop_or_piping": "Chilled Water Loop 1", + "speed_control": "FIXED_SPEED", + }, + { + "id": "HW Pump 1", + "loop_or_piping": "HW Loop 1", + "speed_control": "FIXED_SPEED", + }, + ], + "fluid_loops": [ + {"id": "Boiler Loop 1", "type": "HEATING"}, + { + "id": "Chiller Loop 1", + "type": "COOLING", + "child_loops": [{"id": "Secondary CHW Loop 1", "type": "COOLING"}], + }, + {"id": "Chilled Water Loop 1", "type": "COOLING"}, + {"id": "HW Loop 1", "type": "HEATING"}, + ], + "type": "BASELINE_0", + } + ], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RMD_baseline_system_12__is_valid(): + schema_validation_result = schema_validate_rpd(SYS_12_TEST_RMD) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__TEST_RMD_baseline_system_12__is_unmatched_valid(): + schema_validation_result = schema_validate_rpd(SYS_12_TEST_UNMATCHED_RMD) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__is_baseline_system_12__true(): + assert ( + is_baseline_system_12( + SYS_12_TEST_RMD["ruleset_model_descriptions"][0], + "System 12", + ["CAV Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_12 + ) + + +def test__is_baseline_system_12__test_json_true(): + assert ( + is_baseline_system_12( + load_system_test_file("System 12_CAV_SZ_HW.json")[ + "ruleset_model_descriptions" + ][0], + "System 12", + ["CAV Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_12 + ) + + +def test__is_baseline_system_12A__true(): + assert ( + is_baseline_system_12( + SYS_12_TEST_RMD["ruleset_model_descriptions"][0], + "System 12a", + ["CAV Air Terminal 2"], + ["Thermal Zone 2"], + ) + == HVAC_SYS.SYS_12A + ) + + +def test__is_baseline_system_12A__test_json_true(): + assert ( + is_baseline_system_12( + load_system_test_file("System 12a_CAV_SZ_HW.json")[ + "ruleset_model_descriptions" + ][0], + "System 12", + ["CAV Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_12A + ) + + +def test__is_baseline_system_12B__true(): + assert ( + is_baseline_system_12( + SYS_12_TEST_RMD["ruleset_model_descriptions"][0], + "System 12b", + ["CAV Air Terminal 3"], + ["Thermal Zone 3"], + ) + == HVAC_SYS.SYS_12B + ) + + +def test__is_baseline_system_12B__test_json_true(): + assert ( + is_baseline_system_12( + load_system_test_file("System 12b_CAV_SZ_HW.json")[ + "ruleset_model_descriptions" + ][0], + "System 12", + ["CAV Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_12B + ) + + +def test__is_baseline_system_unmatched__true(): + assert ( + is_baseline_system_12( + SYS_12_TEST_UNMATCHED_RMD["ruleset_model_descriptions"][0], + "System Type Unmatched", + ["Air Terminal 12"], + ["Thermal Zone 12"], + ) + == HVAC_SYS.UNMATCHED + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_13.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_13.py new file mode 100644 index 0000000000..6baedd37af --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_13.py @@ -0,0 +1,303 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_test_util import ( + load_system_test_file, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.is_baseline_system_13 import ( + is_baseline_system_13, +) +from rct229.schema.validate import schema_validate_rpd + +SYS_13_TEST_RMD = { + "id": "ASHRAE229 1", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Building Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 1", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "CAV Air Terminal 1", + "is_supply_ducted": True, + "type": "CONSTANT_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 13", + }, + ], + }, + { + "id": "Thermal Zone 2", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "CAV Air Terminal 2", + "is_supply_ducted": True, + "type": "CONSTANT_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 13s", + }, + ], + }, + ], + "heating_ventilating_air_conditioning_systems": [ + { + "id": "System 13", + "cooling_system": { + "id": "CHW Coil 1", + "type": "FLUID_LOOP", + "chilled_water_loop": "Secondary CHW Loop 1", + }, + "heating_system": { + "id": "Heating Coil 1", + "type": "ELECTRIC_RESISTANCE", + }, + "fan_system": { + "id": "CAV Fan System 1", + "fan_control": "CONSTANT", + "supply_fans": [{"id": "Supply Fan 1"}], + "return_fans": [{"id": "Return Fan 1"}], + }, + }, + { + "id": "System 13a", + "cooling_system": { + "id": "CHW Coil 1", + "type": "FLUID_LOOP", + "chilled_water_loop": "Purchased CHW Loop 1", + }, + "heating_system": { + "id": "Heating Coil 1", + "type": "ELECTRIC_RESISTANCE", + }, + "fan_system": { + "id": "CAV Fan System 1", + "fan_control": "CONSTANT", + "supply_fans": [{"id": "Supply Fan 1"}], + "return_fans": [{"id": "Return Fan 1"}], + }, + }, + ], + } + ], + } + ], + "external_fluid_sources": [ + { + "id": "Purchased CW 1", + "loop": "Purchased CHW Loop 1", + "type": "CHILLED_WATER", + } + ], + "chillers": [{"id": "Chiller 1", "cooling_loop": "Chiller Loop 1"}], + "pumps": [ + { + "id": "Chiller Pump 1", + "loop_or_piping": "Chiller Loop 1", + "speed_control": "FIXED_SPEED", + }, + { + "id": "Secondary CHW Pump", + "loop_or_piping": "Secondary CHW Loop 1", + "speed_control": "VARIABLE_SPEED", + }, + ], + "fluid_loops": [ + { + "id": "Chiller Loop 1", + "type": "COOLING", + "child_loops": [{"id": "Secondary CHW Loop 1", "type": "COOLING"}], + }, + {"id": "Purchased CHW Loop 1", "type": "COOLING"}, + ], + "type": "BASELINE_0", + } + ], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + +SYS_13_TEST_UNMATCHED_RMD = { + "id": "ASHRAE229 1", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Building Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 2", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "Air Terminal 2", + "is_supply_ducted": True, + "type": "CONSTANT_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 13s", + }, + ], + }, + ], + "heating_ventilating_air_conditioning_systems": [ + { + "id": "System Type Unmatched", + "cooling_system": { + "id": "CHW Coil 1", + "type": "FLUID_LOOP", + "chilled_water_loop": "CHW Loop 1", + }, + "heating_system": { + "id": "Heating Coil 1", + "type": "ELECTRIC_RESISTANCE", + }, + "fan_system": { + "id": "CAV Fan System 1", + "fan_control": "CONSTANT", + "supply_fans": [{"id": "Supply Fan 1"}], + "return_fans": [{"id": "Return Fan 1"}], + }, + }, + ], + } + ], + } + ], + # "external_fluid_sources": [ + # { + # "id": "Purchased CW 1", + # "loop": "Purchased CHW Loop 1", + # "type": "CHILLED_WATER", + # } + # ], + "chillers": [{"id": "Chiller 1", "cooling_loop": "Chiller Loop 1"}], + "pumps": [ + { + "id": "Chiller Pump 1", + "loop_or_piping": "Chiller Loop 1", + "speed_control": "FIXED_SPEED", + }, + { + "id": "Secondary CHW Pump", + "loop_or_piping": "Secondary CHW Loop 1", + "speed_control": "VARIABLE_SPEED", + }, + ], + "fluid_loops": [ + { + "id": "Chiller Loop 1", + "type": "COOLING", + "child_loops": [{"id": "Secondary CHW Loop 1", "type": "COOLING"}], + }, + {"id": "CHW Loop 1", "type": "COOLING"}, + ], + "type": "BASELINE_0", + } + ], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RMD_baseline_system_13__is_valid(): + schema_validation_result = schema_validate_rpd(SYS_13_TEST_RMD) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__TEST_RMD_baseline_system_13__is_unmatched_valid(): + schema_validation_result = schema_validate_rpd(SYS_13_TEST_UNMATCHED_RMD) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__is_baseline_system_13__true(): + assert ( + is_baseline_system_13( + SYS_13_TEST_RMD["ruleset_model_descriptions"][0], + "System 13", + ["CAV Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_13 + ) + + +def test__is_baseline_system_13__test_json_true(): + assert ( + is_baseline_system_13( + load_system_test_file("System 13_CAV_SZ_ER.json")[ + "ruleset_model_descriptions" + ][0], + "System 13", + ["CAV Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_13 + ) + + +def test__is_baseline_system_13A__true(): + assert ( + is_baseline_system_13( + SYS_13_TEST_RMD["ruleset_model_descriptions"][0], + "System 13a", + ["CAV Air Terminal 2"], + ["Thermal Zone 2"], + ) + == HVAC_SYS.SYS_13A + ) + + +def test__is_baseline_system_13A__test_json_true(): + assert ( + is_baseline_system_13( + load_system_test_file("System 13a_CAV_SZ_ER.json")[ + "ruleset_model_descriptions" + ][0], + "System 13", + ["CAV Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_13A + ) + + +def test__is_baseline_system_unmatched__true(): + assert ( + is_baseline_system_13( + SYS_13_TEST_UNMATCHED_RMD["ruleset_model_descriptions"][0], + "System Type Unmatched", + ["Air Terminal 2"], + ["Thermal Zone 2"], + ) + == HVAC_SYS.UNMATCHED + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_2.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_2.py new file mode 100644 index 0000000000..d5100d23b6 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_2.py @@ -0,0 +1,105 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_test_util import ( + load_system_test_file, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.is_baseline_system_2 import ( + is_baseline_system_2, +) +from rct229.schema.validate import schema_validate_rpd + +SYS_2_TEST_RMD = { + "id": "ASHRAE229 1", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Building Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 1", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "PTHP Terminal 1", + "is_supply_ducted": False, + "type": "CONSTANT_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "PTHP 1", + } + ], + } + ], + "heating_ventilating_air_conditioning_systems": [ + { + "id": "PTHP 1", + "cooling_system": { + "id": "HP Cooling Coil 1", + "type": "DIRECT_EXPANSION", + }, + "heating_system": { + "id": "HP Heating Coil 1", + "type": "HEAT_PUMP", + }, + "fan_system": { + "id": "CAV Fan System 1", + "fan_control": "CONSTANT", + "supply_fans": [{"id": "Supply Fan 1"}], + }, + } + ], + } + ], + } + ], + "type": "BASELINE_0", + } + ], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RMD_baseline_system_2__is_valid(): + schema_validation_result = schema_validate_rpd(SYS_2_TEST_RMD) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__is_baseline_system_2__true(): + assert ( + is_baseline_system_2( + SYS_2_TEST_RMD["ruleset_model_descriptions"][0], + "PTHP 1", + ["PTHP Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_2 + ) + + +def test__is_baseline_system_2__test_json_true(): + assert ( + is_baseline_system_2( + load_system_test_file("System_2_PTHP.json")["ruleset_model_descriptions"][ + 0 + ], + "PTHP 1", + ["PTHP Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_2 + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_3.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_3.py new file mode 100644 index 0000000000..5212933f4b --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_3.py @@ -0,0 +1,392 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_test_util import ( + load_system_test_file, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.is_baseline_system_3 import ( + is_baseline_system_3, +) +from rct229.schema.validate import schema_validate_rpd + +SYS_3_TEST_RMD = { + "id": "ASHRAE229 1", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Building Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 3", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "Air Terminal 3", + "is_supply_ducted": True, + "type": "CONSTANT_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System Type 3", + } + ], + }, + { + "id": "Thermal Zone 3a", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "Air Terminal 3a", + "is_supply_ducted": True, + "type": "CONSTANT_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System Type 3a", + } + ], + }, + { + "id": "Thermal Zone 3b", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "Air Terminal 3b", + "is_supply_ducted": True, + "type": "CONSTANT_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System Type 3b", + } + ], + }, + { + "id": "Thermal Zone 3c", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "Air Terminal 3c", + "is_supply_ducted": True, + "type": "CONSTANT_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System Type 3c", + } + ], + }, + ], + "heating_ventilating_air_conditioning_systems": [ + { + "id": "System Type 3", + "cooling_system": { + "id": "DX Coil 3", + "type": "DIRECT_EXPANSION", + }, + "heating_system": { + "id": "Furnace Coil 3", + "type": "FURNACE", + }, + "fan_system": { + "id": "CAV Fan System 3", + "fan_control": "CONSTANT", + "supply_fans": [{"id": "Supply Fan 1"}], + }, + }, + { + "id": "System Type 3a", + "cooling_system": { + "id": "Cooling Coil 3a", + "type": "FLUID_LOOP", + "chilled_water_loop": "Purchased CHW Loop 1", + }, + "heating_system": { + "id": "Furnace Coil 3a", + "type": "FURNACE", + }, + "fan_system": { + "id": "CAV Fan System 3a", + "fan_control": "CONSTANT", + "supply_fans": [{"id": "Supply Fan 3a"}], + }, + }, + { + "id": "System Type 3b", + "cooling_system": { + "id": "DX Coil 3b", + "type": "DIRECT_EXPANSION", + }, + "heating_system": { + "id": "Heating Coil 3b", + "type": "FLUID_LOOP", + "hot_water_loop": "Purchased HW Loop 1", + }, + "fan_system": { + "id": "CAV Fan System 3b", + "fan_control": "CONSTANT", + "supply_fans": [{"id": "Supply Fan 3b"}], + }, + }, + { + "id": "System Type 3c", + "cooling_system": { + "id": "Cooling Coil 3c", + "type": "FLUID_LOOP", + "chilled_water_loop": "Purchased CHW Loop 1", + }, + "heating_system": { + "id": "Heating Coil 3c", + "type": "FLUID_LOOP", + "hot_water_loop": "Purchased HW Loop 1", + }, + "fan_system": { + "id": "CAV Fan System 3c", + "fan_control": "CONSTANT", + "supply_fans": [{"id": "Supply Fan 3c"}], + }, + }, + ], + } + ], + } + ], + "fluid_loops": [ + { + "id": "Purchased HW Loop 1", + "type": "HEATING", + }, + { + "id": "Purchased CHW Loop 1", + "type": "COOLING", + }, + ], + "external_fluid_sources": [ + { + "id": "Purchased HW", + "loop": "Purchased HW Loop 1", + "type": "HOT_WATER", + }, + { + "id": "Purchased CHW", + "loop": "Purchased CHW Loop 1", + "type": "CHILLED_WATER", + }, + ], + "type": "BASELINE_0", + } + ], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +SYS_3_TEST_UNMATCHED_RMD = { + "id": "ASHRAE229 1", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Building Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 3", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "Air Terminal 3", + "is_supply_ducted": True, + "type": "CONSTANT_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System Type 3c", + } + ], + }, + ], + "heating_ventilating_air_conditioning_systems": [ + { + "id": "System Type Unmatched", + "cooling_system": { + "id": "Cooling Coil", + "type": "FLUID_LOOP", + "chilled_water_loop": "Purchased CHW Loop 1", + }, + "heating_system": { + "id": "Heating Coil", + "type": "OTHER", + }, + "fan_system": { + "id": "CAV Fan System", + "fan_control": "CONSTANT", + "supply_fans": [{"id": "Supply Fan 3c"}], + }, + }, + ], + } + ], + } + ], + "fluid_loops": [ + { + "id": "Purchased CHW Loop 1", + "type": "COOLING", + }, + ], + "external_fluid_sources": [ + { + "id": "Purchased CHW", + "loop": "Purchased CHW Loop 1", + "type": "CHILLED_WATER", + }, + ], + "type": "BASELINE_0", + } + ], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RMD_baseline_system_3__is_valid(): + schema_validation_result = schema_validate_rpd(SYS_3_TEST_RMD) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__TEST_RMD_baseline_system_3__is_unmatched_valid(): + schema_validation_result = schema_validate_rpd(SYS_3_TEST_UNMATCHED_RMD) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__is_baseline_system_3__true(): + assert ( + is_baseline_system_3( + SYS_3_TEST_RMD["ruleset_model_descriptions"][0], + "System Type 3", + ["Air Terminal 3"], + ["Thermal Zone 3"], + ) + == HVAC_SYS.SYS_3 + ) + + +def test__is_baseline_system_3__test_json_true(): + assert ( + is_baseline_system_3( + load_system_test_file("System_3_PSZ_AC_Gas_Furnace.json")[ + "ruleset_model_descriptions" + ][0], + "System Type 3", + ["Air Terminal"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_3 + ) + + +def test__is_baseline_system_3A__true(): + assert ( + is_baseline_system_3( + SYS_3_TEST_RMD["ruleset_model_descriptions"][0], + "System Type 3a", + ["Air Terminal 3a"], + ["Thermal Zone 3a"], + ) + == HVAC_SYS.SYS_3A + ) + + +def test__is_baseline_system_3A__test_json_true(): + assert ( + is_baseline_system_3( + load_system_test_file("System_3a_PSZ_AC_Gas_Furnace.json")[ + "ruleset_model_descriptions" + ][0], + "System Type 3", + ["Air Terminal"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_3A + ) + + +def test__is_baseline_system_3B__true(): + assert ( + is_baseline_system_3( + SYS_3_TEST_RMD["ruleset_model_descriptions"][0], + "System Type 3b", + ["Air Terminal 3b"], + ["Thermal Zone 3b"], + ) + == HVAC_SYS.SYS_3B + ) + + +def test__is_baseline_system_3B__test_json_true(): + assert ( + is_baseline_system_3( + load_system_test_file("System_3b_PSZ_AC_Gas_Furnace.json")[ + "ruleset_model_descriptions" + ][0], + "System Type 3", + ["Air Terminal"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_3B + ) + + +def test__is_baseline_system_3C__true(): + assert ( + is_baseline_system_3( + SYS_3_TEST_RMD["ruleset_model_descriptions"][0], + "System Type 3c", + ["Air Terminal 3c"], + ["Thermal Zone 3c"], + ) + == HVAC_SYS.SYS_3C + ) + + +def test__is_baseline_system_3C_test__json_true(): + assert ( + is_baseline_system_3( + load_system_test_file("System_3c_PSZ_AC_Gas_Furnace.json")[ + "ruleset_model_descriptions" + ][0], + "System Type 3", + ["Air Terminal"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_3C + ) + + +def test__is_baseline_system_unmatched__true(): + assert ( + is_baseline_system_3( + SYS_3_TEST_UNMATCHED_RMD["ruleset_model_descriptions"][0], + "System Type Unmatched", + ["Air Terminal 3"], + ["Thermal Zone 3"], + ) + == HVAC_SYS.UNMATCHED + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_4.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_4.py new file mode 100644 index 0000000000..c8d1267a69 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_4.py @@ -0,0 +1,106 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_test_util import ( + load_system_test_file, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.is_baseline_system_4 import ( + is_baseline_system_4, +) +from rct229.schema.validate import schema_validate_rpd + +SYS_4_TEST_RMD = { + "id": "ASHRAE229 1", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Building Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 1", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "Air Terminal", + "is_supply_ducted": True, + "type": "CONSTANT_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System Type 4", + } + ], + } + ], + "heating_ventilating_air_conditioning_systems": [ + { + "id": "System Type 4", + "cooling_system": { + "id": "DX Coil 1", + "type": "DIRECT_EXPANSION", + }, + "heating_system": { + "id": "HP Coil 1", + "type": "HEAT_PUMP", + }, + "fan_system": { + "id": "CAV Fan System 1", + "fan_control": "CONSTANT", + "supply_fans": [{"id": "Supply Fan 1"}], + "return_fans": [{"id": "Return Fan 1"}], + }, + } + ], + } + ], + } + ], + "type": "BASELINE_0", + } + ], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RMD_baseline_system_4__is_valid(): + schema_validation_result = schema_validate_rpd(SYS_4_TEST_RMD) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__is_baseline_system_4__true(): + assert ( + is_baseline_system_4( + SYS_4_TEST_RMD["ruleset_model_descriptions"][0], + "System Type 4", + ["Air Terminal"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_4 + ) + + +def test__is_baseline_system_4_testing_json__true(): + assert ( + is_baseline_system_4( + load_system_test_file("System_4_PSZ_HP.json")["ruleset_model_descriptions"][ + 0 + ], + "System Type 4", + ["Air Terminal"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_4 + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_5.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_5.py new file mode 100644 index 0000000000..3b7f7f403a --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_5.py @@ -0,0 +1,297 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_test_util import ( + load_system_test_file, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.is_baseline_system_5 import ( + is_baseline_system_5, +) +from rct229.schema.validate import schema_validate_rpd + +SYS_5_TEST_RMD = { + "id": "ASHRAE229 1", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Building Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 1", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "VAV Air Terminal 1", + "is_supply_ducted": True, + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 5", + "heating_source": "HOT_WATER", + "heating_from_loop": "Boiler Loop 1", + } + ], + }, + { + "id": "Thermal Zone 2", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "VAV Air Terminal 2", + "is_supply_ducted": True, + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 5B", + "heating_source": "HOT_WATER", + "heating_from_loop": "Purchased HW Loop 1", + } + ], + }, + ], + "heating_ventilating_air_conditioning_systems": [ + { + "id": "System 5", + "cooling_system": { + "id": "DX Coil 1", + "type": "DIRECT_EXPANSION", + }, + "preheat_system": { + "id": "Preheat Coil 1", + "type": "FLUID_LOOP", + "hot_water_loop": "Boiler Loop 1", + }, + "fan_system": { + "id": "VAV Fan System 1", + "fan_control": "VARIABLE_SPEED_DRIVE", + "supply_fans": [{"id": "Supply Fan 1"}], + "return_fans": [{"id": "Return Fan 1"}], + }, + }, + { + "id": "System 5B", + "cooling_system": { + "id": "DX Coil 2", + "type": "DIRECT_EXPANSION", + }, + "preheat_system": { + "id": "Preheat Coil 2", + "type": "FLUID_LOOP", + "hot_water_loop": "Purchased HW Loop 1", + }, + "fan_system": { + "id": "VAV Fan System 2", + "fan_control": "VARIABLE_SPEED_DRIVE", + "supply_fans": [{"id": "Supply Fan 2"}], + "return_fans": [{"id": "Return Fan 2"}], + }, + }, + ], + } + ], + } + ], + "boilers": [ + { + "id": "Boiler 1", + "loop": "Boiler Loop 1", + "energy_source_type": "NATURAL_GAS", + } + ], + "pumps": [ + { + "id": "Boiler Pump 1", + "loop_or_piping": "Boiler Loop 1", + "speed_control": "FIXED_SPEED", + } + ], + "fluid_loops": [ + {"id": "Boiler Loop 1", "type": "HEATING"}, + {"id": "Purchased HW Loop 1", "type": "HEATING"}, + ], + "external_fluid_sources": [ + { + "id": "Purchased HW 1", + "loop": "Purchased HW Loop 1", + "type": "HOT_WATER", + } + ], + "type": "BASELINE_0", + } + ], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + +SYS_5_TEST_UNMATCHED_RMD = { + "id": "ASHRAE229 1", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Building Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 1", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "VAV Air Terminal 1", + "is_supply_ducted": True, + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 5", + "heating_source": "HOT_WATER", + "heating_from_loop": "Boiler Loop 1", + } + ], + }, + ], + "heating_ventilating_air_conditioning_systems": [ + { + "id": "System 5 Unmatched", + "cooling_system": { + "id": "DX Coil 1", + "type": "DIRECT_EXPANSION", + }, + "preheat_system": { + "id": "Preheat Coil 1", + "type": "FLUID_LOOP", + "hot_water_loop": "Preheat Loop 1", + }, + "fan_system": { + "id": "VAV Fan System 1", + "fan_control": "VARIABLE_SPEED_DRIVE", + "supply_fans": [{"id": "Supply Fan 1"}], + "return_fans": [{"id": "Return Fan 1"}], + }, + }, + ], + } + ], + } + ], + "boilers": [ + { + "id": "Boiler 1", + "loop": "Boiler Loop 1", + "energy_source_type": "NATURAL_GAS", + } + ], + "pumps": [ + { + "id": "Boiler Pump 1", + "loop_or_piping": "Boiler Loop 1", + "speed_control": "FIXED_SPEED", + } + ], + "fluid_loops": [ + {"id": "Boiler Loop 1", "type": "HEATING"}, + {"id": "Purchased HW Loop 1", "type": "HEATING"}, + {"id": "Preheat Loop 1", "type": "HEATING"}, + ], + "type": "BASELINE_0", + } + ], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RMD_baseline_system_5__is_valid(): + schema_validation_result = schema_validate_rpd(SYS_5_TEST_RMD) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__TEST_RMD_baseline_system_5__is_unmatched_valid(): + schema_validation_result = schema_validate_rpd(SYS_5_TEST_UNMATCHED_RMD) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__is_baseline_system_5__true(): + assert ( + is_baseline_system_5( + SYS_5_TEST_RMD["ruleset_model_descriptions"][0], + "System 5", + ["VAV Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_5 + ) + + +def test__is_baseline_system_5__test_json_true(): + assert ( + is_baseline_system_5( + load_system_test_file("System_5_PVAV_HW_Reheat.json")[ + "ruleset_model_descriptions" + ][0], + "System 5", + ["VAV Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_5 + ) + + +def test__is_baseline_system_5B__true(): + assert ( + is_baseline_system_5( + SYS_5_TEST_RMD["ruleset_model_descriptions"][0], + "System 5B", + ["VAV Air Terminal 2"], + ["Thermal Zone 2"], + ) + == HVAC_SYS.SYS_5B + ) + + +def test__is_baseline_system_5B_test__json_true(): + assert ( + is_baseline_system_5( + load_system_test_file("System_5b_PVAV_HW_Reheat.json")[ + "ruleset_model_descriptions" + ][0], + "System 5", + ["VAV Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_5B + ) + + +def test__is_baseline_system_unmatched__true(): + assert ( + is_baseline_system_5( + SYS_5_TEST_UNMATCHED_RMD["ruleset_model_descriptions"][0], + "System 5 Unmatched", + ["VAV Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.UNMATCHED + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_6.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_6.py new file mode 100644 index 0000000000..ffca74b890 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_6.py @@ -0,0 +1,276 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_test_util import ( + load_system_test_file, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.is_baseline_system_6 import ( + is_baseline_system_6, +) +from rct229.schema.validate import schema_validate_rpd + +SYS_6_TEST_RMD = { + "id": "ASHRAE229 1", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Building Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 1", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "VAV Air Terminal 1", + "is_supply_ducted": True, + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 6", + "heating_source": "ELECTRIC", + "fan": { + "id": "Terminal Fan 1", + }, + "fan_configuration": "PARALLEL", + } + ], + }, + { + "id": "Thermal Zone 2", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "VAV Air Terminal 2", + "is_supply_ducted": True, + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 6B", + "heating_source": "HOT_WATER", + "heating_from_loop": "Purchased HW Loop 1", + "fan": { + "id": "Terminal Fan 2", + }, + "fan_configuration": "PARALLEL", + } + ], + }, + ], + "heating_ventilating_air_conditioning_systems": [ + { + "id": "System 6", + "cooling_system": { + "id": "DX Coil 1", + "type": "DIRECT_EXPANSION", + }, + "preheat_system": { + "id": "Preheat Coil 1", + "type": "ELECTRIC_RESISTANCE", + }, + "fan_system": { + "id": "VAV Fan System 1", + "fan_control": "VARIABLE_SPEED_DRIVE", + "supply_fans": [{"id": "Supply Fan 1"}], + "return_fans": [{"id": "Return Fan 1"}], + }, + }, + { + "id": "System 6B", + "cooling_system": { + "id": "DX Coil 2", + "type": "DIRECT_EXPANSION", + }, + "preheat_system": { + "id": "Preheat Coil 2", + "type": "FLUID_LOOP", + "hot_water_loop": "Purchased HW Loop 1", + }, + "fan_system": { + "id": "VAV Fan System 2", + "fan_control": "VARIABLE_SPEED_DRIVE", + "supply_fans": [{"id": "Supply Fan 2"}], + "return_fans": [{"id": "Return Fan 2"}], + }, + }, + ], + } + ], + } + ], + "external_fluid_sources": [ + { + "id": "Purchased HW 1", + "loop": "Purchased HW Loop 1", + "type": "HOT_WATER", + } + ], + "pumps": [ + { + "id": "HW Pump 1", + "loop_or_piping": "Purchased HW Loop 1", + "speed_control": "FIXED_SPEED", + } + ], + "fluid_loops": [{"id": "Purchased HW Loop 1", "type": "HEATING"}], + "type": "BASELINE_0", + } + ], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + +SYS_6_TEST_UNMATCHED_RMD = { + "id": "ASHRAE229 1", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Building Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 3", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "VAV Air Terminal 3", + "is_supply_ducted": True, + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 6 Unmatched", + "heating_source": "HOT_WATER", + "fan": { + "id": "Terminal Fan 3", + }, + "fan_configuration": "PARALLEL", + } + ], + }, + ], + "heating_ventilating_air_conditioning_systems": [ + { + "id": "System 6 Unmatched", + "cooling_system": { + "id": "DX Coil 2", + "type": "DIRECT_EXPANSION", + }, + "preheat_system": { + "id": "Preheat Coil 3", + "type": "OTHER", + }, + "fan_system": { + "id": "VAV Fan System 3", + "fan_control": "VARIABLE_SPEED_DRIVE", + "supply_fans": [{"id": "Supply Fan 3"}], + "return_fans": [{"id": "Return Fan 3"}], + }, + }, + ], + } + ], + } + ], + "type": "BASELINE_0", + } + ], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RMD_baseline_system_6__is_valid(): + schema_validation_result = schema_validate_rpd(SYS_6_TEST_RMD) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__TEST_RMD_baseline_system_6__is_unmatched_valid(): + schema_validation_result = schema_validate_rpd(SYS_6_TEST_UNMATCHED_RMD) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__is_baseline_system_6__true(): + assert ( + is_baseline_system_6( + SYS_6_TEST_RMD["ruleset_model_descriptions"][0], + "System 6", + ["VAV Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_6 + ) + + +def test__is_baseline_system_6_test_json__true(): + assert ( + is_baseline_system_6( + load_system_test_file("System_6_PVAV_Elec_Reheat.json")[ + "ruleset_model_descriptions" + ][0], + "System 6", + ["VAV Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_6 + ) + + +def test__is_baseline_system_6B__true(): + assert ( + is_baseline_system_6( + SYS_6_TEST_RMD["ruleset_model_descriptions"][0], + "System 6B", + ["VAV Air Terminal 2"], + ["Thermal Zone 2"], + ) + == HVAC_SYS.SYS_6B + ) + + +def test_is_baseline_system_6B__test_json_true(): + assert ( + is_baseline_system_6( + load_system_test_file("System_6b_PVAV_Elec_Reheat.json")[ + "ruleset_model_descriptions" + ][0], + "System 6", + ["VAV Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_6B + ) + + +def test__is_baseline_system_unmatched__true(): + assert ( + is_baseline_system_6( + SYS_6_TEST_UNMATCHED_RMD["ruleset_model_descriptions"][0], + "System 6 Unmatched", + ["VAV Air Terminal 3"], + ["Thermal Zone 3"], + ) + == HVAC_SYS.UNMATCHED + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_7.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_7.py new file mode 100644 index 0000000000..a154e93b40 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_7.py @@ -0,0 +1,594 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_test_util import ( + load_system_test_file, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.is_baseline_system_7 import ( + is_baseline_system_7, +) +from rct229.schema.validate import schema_validate_rpd + +SYS_7_TEST_RMD = { + "id": "ASHRAE229 1", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Building Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 1", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "VAV Air Terminal 1", + "is_supply_ducted": True, + "heating_from_loop": "Boiler Loop 1", + "heating_source": "HOT_WATER", + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 7", + } + ], + }, + { + "id": "Thermal Zone 2", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "VAV Air Terminal 2", + "is_supply_ducted": True, + "heating_from_loop": "Boiler Loop 1", + "heating_source": "HOT_WATER", + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 7a", + } + ], + }, + { + "id": "Thermal Zone 3", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "VAV Air Terminal 3", + "is_supply_ducted": True, + "heating_from_loop": "Purchased HW Loop 1", + "heating_source": "HOT_WATER", + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 7b", + } + ], + }, + { + "id": "Thermal Zone 4", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "VAV Air Terminal 4", + "is_supply_ducted": True, + "heating_from_loop": "Purchased HW Loop 1", + "heating_source": "HOT_WATER", + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 7c", + } + ], + }, + ], + "heating_ventilating_air_conditioning_systems": [ + { + "id": "System 7", + "preheat_system": { + "id": "Preheat Coil 1", + "type": "FLUID_LOOP", + "hot_water_loop": "Boiler Loop 1", + }, + "cooling_system": { + "id": "Cooling Coil 1", + "type": "FLUID_LOOP", + "chilled_water_loop": "Secondary Loop 1", + }, + "fan_system": { + "id": "VAV Fan System 1", + "fan_control": "VARIABLE_SPEED_DRIVE", + "supply_fans": [{"id": "Supply Fan 1"}], + "return_fans": [{"id": "Return Fan 1"}], + }, + }, + { + "id": "System 7a", + "preheat_system": { + "id": "Preheat Coil 2", + "type": "FLUID_LOOP", + "hot_water_loop": "Boiler Loop 1", + }, + "cooling_system": { + "id": "Cooling Coil 2", + "type": "FLUID_LOOP", + "chilled_water_loop": "Purchased CHW Loop 1", + }, + "fan_system": { + "id": "VAV Fan System 2", + "fan_control": "VARIABLE_SPEED_DRIVE", + "supply_fans": [{"id": "Supply Fan 2"}], + "return_fans": [{"id": "Return Fan 2"}], + }, + }, + { + "id": "System 7b", + "preheat_system": { + "id": "Preheat Coil 3", + "type": "FLUID_LOOP", + "hot_water_loop": "Purchased HW Loop 1", + }, + "cooling_system": { + "id": "Cooling Coil 3", + "type": "FLUID_LOOP", + "chilled_water_loop": "Secondary Loop 1", + }, + "fan_system": { + "id": "VAV Fan System 3", + "fan_control": "VARIABLE_SPEED_DRIVE", + "supply_fans": [{"id": "Supply Fan 3"}], + "return_fans": [{"id": "Return Fan 3"}], + }, + }, + { + "id": "System 7c", + "preheat_system": { + "id": "Preheat Coil 4", + "type": "FLUID_LOOP", + "hot_water_loop": "Purchased HW Loop 1", + }, + "cooling_system": { + "id": "Cooling Coil 4", + "type": "FLUID_LOOP", + "chilled_water_loop": "Purchased CHW Loop 1", + }, + "fan_system": { + "id": "VAV Fan System 4", + "fan_control": "VARIABLE_SPEED_DRIVE", + "supply_fans": [{"id": "Supply Fan 4"}], + "return_fans": [{"id": "Return Fan 4"}], + }, + }, + ], + } + ], + } + ], + "boilers": [ + { + "id": "Boiler 1", + "loop": "Boiler Loop 1", + "design_capacity": 117228.44444444445, + } + ], + "chillers": [ + { + "id": "Chiller 1", + "cooling_loop": "Chiller Loop 1", + "condensing_loop": "Condenser Loop 1", + } + ], + "pumps": [ + { + "id": "Boiler Pump 1", + "loop_or_piping": "Boiler Loop 1", + "speed_control": "FIXED_SPEED", + }, + { + "id": "Chiller Pump 1", + "loop_or_piping": "Chiller Loop 1", + "speed_control": "FIXED_SPEED", + }, + ], + "fluid_loops": [ + { + "id": "Boiler Loop 1", + "type": "HEATING", + "heating_design_and_control": { + "id": "DAC1", + "minimum_flow_fraction": 0.25, + }, + }, + { + "id": "Chiller Loop 1", + "type": "COOLING", + "child_loops": [{"id": "Secondary Loop 1", "type": "COOLING"}], + }, + { + "id": "Purchased HW Loop 1", + "type": "HEATING", + }, + { + "id": "Purchased CHW Loop 1", + "type": "COOLING", + }, + ], + "external_fluid_sources": [ + { + "id": "Purchased HW", + "loop": "Purchased HW Loop 1", + "type": "HOT_WATER", + }, + { + "id": "Purchased CHW", + "loop": "Purchased CHW Loop 1", + "type": "CHILLED_WATER", + }, + ], + "type": "BASELINE_0", + } + ], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + +SYS_7_TEST_UNMATCHED_RMD = { + "id": "ASHRAE229 1", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Building Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 1", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "Air Terminal 1", + "is_supply_ducted": True, + "heating_from_loop": "Boiler Loop 1", + "heating_source": "HOT_WATER", + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 7a", + } + ], + }, + { + "id": "Thermal Zone 2", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "Air Terminal 2", + "is_supply_ducted": True, + "heating_from_loop": "Purchased HW Loop 1", + "heating_source": "HOT_WATER", + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System Type Unmatched 2", + } + ], + }, + { + "id": "Thermal Zone 3", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "Air Terminal 3", + "is_supply_ducted": True, + "heating_from_loop": "Purchased HW Loop 1", + "heating_source": "HOT_WATER", + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System Type Unmatched 3", + } + ], + }, + ], + "heating_ventilating_air_conditioning_systems": [ + { + "id": "System Type Unmatched 1", + "preheat_system": { + "id": "Preheat Coil 1", + "type": "FLUID_LOOP", + "hot_water_loop": "Boiler Loop 1", + }, + "cooling_system": { + "id": "Cooling Coil 1", + "type": "FLUID_LOOP", + "chilled_water_loop": "CHW Loop 1", + }, + "fan_system": { + "id": "VAV Fan System 1", + "fan_control": "VARIABLE_SPEED_DRIVE", + "supply_fans": [{"id": "Supply Fan 1"}], + "return_fans": [{"id": "Return Fan 1"}], + }, + }, + { + "id": "System Type Unmatched 2", + "preheat_system": { + "id": "Preheat Coil 3", + "type": "FLUID_LOOP", + "hot_water_loop": "HW Loop 1", + }, + "cooling_system": { + "id": "Cooling Coil 3", + "type": "FLUID_LOOP", + "chilled_water_loop": "Secondary Loop 1", + }, + "fan_system": { + "id": "VAV Fan System 2", + "fan_control": "VARIABLE_SPEED_DRIVE", + "supply_fans": [{"id": "Supply Fan 2"}], + "return_fans": [{"id": "Return Fan 2"}], + }, + }, + { + "id": "System Type Unmatched 3", + "preheat_system": { + "id": "Preheat Coil 3", + "type": "FLUID_LOOP", + "hot_water_loop": "Purchased HW Loop 1", + }, + "cooling_system": { + "id": "Cooling Coil 4", + "type": "FLUID_LOOP", + "chilled_water_loop": "CHW Loop 1", + }, + "fan_system": { + "id": "VAV Fan System 3", + "fan_control": "VARIABLE_SPEED_DRIVE", + "supply_fans": [{"id": "Supply Fan 3"}], + "return_fans": [{"id": "Return Fan 3"}], + }, + }, + ], + } + ], + } + ], + "boilers": [ + { + "id": "Boiler 1", + "loop": "Boiler Loop 1", + "design_capacity": 117228.44444444445, + } + ], + "chillers": [ + { + "id": "Chiller 1", + "cooling_loop": "Chiller Loop 1", + "condensing_loop": "Condenser Loop 1", + } + ], + "pumps": [ + { + "id": "Boiler Pump 1", + "loop_or_piping": "Boiler Loop 1", + "speed_control": "FIXED_SPEED", + }, + { + "id": "Chiller Pump 1", + "loop_or_piping": "Chiller Loop 1", + "speed_control": "FIXED_SPEED", + }, + ], + "fluid_loops": [ + { + "id": "Boiler Loop 1", + "type": "HEATING", + "heating_design_and_control": { + "id": "DAC1", + "minimum_flow_fraction": 0.25, + }, + }, + { + "id": "Chiller Loop 1", + "type": "COOLING", + "child_loops": [{"id": "Secondary Loop 1", "type": "COOLING"}], + }, + { + "id": "HW Loop 1", + "type": "HEATING", + }, + { + "id": "Purchased HW Loop 1", + "type": "HEATING", + }, + { + "id": "CHW Loop 1", + "type": "COOLING", + }, + ], + "external_fluid_sources": [ + { + "id": "Purchased HW", + "loop": "Purchased HW Loop 1", + "type": "HOT_WATER", + }, + { + "id": "Purchased CHW", + "loop": "Purchased CHW Loop 1", + "type": "CHILLED_WATER", + }, + ], + "type": "BASELINE_0", + } + ], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RMD_baseline_system_7__is_valid(): + schema_validation_result = schema_validate_rpd(SYS_7_TEST_RMD) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__TEST_RMD_baseline_system_7__is_unmatched_valid(): + schema_validation_result = schema_validate_rpd(SYS_7_TEST_UNMATCHED_RMD) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__is_baseline_system_7__true(): + assert ( + is_baseline_system_7( + SYS_7_TEST_RMD["ruleset_model_descriptions"][0], + "System 7", + ["VAV Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_7 + ) + + +def test__is_baseline_system_7__test_json_true(): + assert ( + is_baseline_system_7( + load_system_test_file("System_7_VAV_HW_Reheat.json")[ + "ruleset_model_descriptions" + ][0], + "System 7", + ["VAV Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_7 + ) + + +def test__is_baseline_system_7A__true(): + assert ( + is_baseline_system_7( + SYS_7_TEST_RMD["ruleset_model_descriptions"][0], + "System 7a", + ["VAV Air Terminal 2"], + ["Thermal Zone 2"], + ) + == HVAC_SYS.SYS_7A + ) + + +def test__is_baseline_system_7A__test_json_true(): + assert ( + is_baseline_system_7( + load_system_test_file("System_7a_VAV_HW_Reheat.json")[ + "ruleset_model_descriptions" + ][0], + "System 7", + ["VAV Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_7A + ) + + +def test__is_baseline_system_7B__true(): + assert ( + is_baseline_system_7( + SYS_7_TEST_RMD["ruleset_model_descriptions"][0], + "System 7b", + ["VAV Air Terminal 3"], + ["Thermal Zone 3"], + ) + == HVAC_SYS.SYS_7B + ) + + +def test__is_baseline_system_7B__test_json_true(): + assert ( + is_baseline_system_7( + load_system_test_file("System_7b_VAV_HW_Reheat.json")[ + "ruleset_model_descriptions" + ][0], + "System 7", + ["VAV Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_7B + ) + + +def test__is_baseline_system_7C_true(): + assert ( + is_baseline_system_7( + SYS_7_TEST_RMD["ruleset_model_descriptions"][0], + "System 7c", + ["VAV Air Terminal 4"], + ["Thermal Zone 4"], + ) + == HVAC_SYS.SYS_7C + ) + + +def test__is_baseline_system_7C__test_json_true(): + assert ( + is_baseline_system_7( + load_system_test_file("System_7c_VAV_HW_Reheat.json")[ + "ruleset_model_descriptions" + ][0], + "System 7", + ["VAV Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_7C + ) + + +def test__is_baseline_system_unmatched1__true(): + assert ( + is_baseline_system_7( + SYS_7_TEST_UNMATCHED_RMD["ruleset_model_descriptions"][0], + "System Type Unmatched 1", + ["Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.UNMATCHED + ) + + +def test__is_baseline_system_unmatched2__true(): + assert ( + is_baseline_system_7( + SYS_7_TEST_UNMATCHED_RMD["ruleset_model_descriptions"][0], + "System Type Unmatched 2", + ["Air Terminal 2"], + ["Thermal Zone 2"], + ) + == HVAC_SYS.UNMATCHED + ) + + +def test__is_baseline_system_unmatched3__true(): + assert ( + is_baseline_system_7( + SYS_7_TEST_UNMATCHED_RMD["ruleset_model_descriptions"][0], + "System Type Unmatched 3", + ["Air Terminal 3"], + ["Thermal Zone 3"], + ) + == HVAC_SYS.UNMATCHED + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_8.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_8.py new file mode 100644 index 0000000000..91a88d76da --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_8.py @@ -0,0 +1,625 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_test_util import ( + load_system_test_file, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.is_baseline_system_8 import ( + is_baseline_system_8, +) +from rct229.schema.validate import schema_validate_rpd + +SYS_8_TEST_RMD = { + "id": "ASHRAE229 1", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Building Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 8", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "VAV Air Terminal 8", + "is_supply_ducted": True, + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 8", + "heating_source": "ELECTRIC", + "heating_from_loop": "Boiler Loop 1", + "fan": {"id": "fan 8"}, + "fan_configuration": "PARALLEL", + } + ], + }, + { + "id": "Thermal Zone 8a", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "VAV Air Terminal 8a", + "is_supply_ducted": True, + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 8a", + "heating_source": "ELECTRIC", + "heating_from_loop": "Boiler Loop 1", + "fan": {"id": "fan 8a"}, + "fan_configuration": "PARALLEL", + } + ], + }, + { + "id": "Thermal Zone 8b", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "VAV Air Terminal 8b", + "is_supply_ducted": True, + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 8b", + "heating_source": "HOT_WATER", + "heating_from_loop": "Purchased HW Loop 1", + "fan": {"id": "fan 8b"}, + "fan_configuration": "PARALLEL", + } + ], + }, + { + "id": "Thermal Zone 8c", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "VAV Air Terminal 8c", + "is_supply_ducted": True, + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 8c", + "heating_source": "HOT_WATER", + "heating_from_loop": "Purchased HW Loop 1", + "fan": {"id": "fan 8c"}, + "fan_configuration": "PARALLEL", + } + ], + }, + ], + "heating_ventilating_air_conditioning_systems": [ + { + "id": "System 8", + "cooling_system": { + "id": "CHW Coil 1", + "type": "FLUID_LOOP", + "chilled_water_loop": "Secondary CHW Loop 1", + }, + "preheat_system": { + "id": "Preheat Coil 1", + "type": "ELECTRIC_RESISTANCE", + }, + "fan_system": { + "id": "VAV Fan System 8", + "fan_control": "VARIABLE_SPEED_DRIVE", + "supply_fans": [{"id": "Supply Fan 1"}], + "return_fans": [{"id": "Return Fan 1"}], + }, + }, + { + "id": "System 8a", + "cooling_system": { + "id": "CHW Coil 8a", + "type": "FLUID_LOOP", + "chilled_water_loop": "Purchased Chilled Water Loop 1", + }, + "preheat_system": { + "id": "Preheat Coil 8a", + "type": "ELECTRIC_RESISTANCE", + }, + "fan_system": { + "id": "VAV Fan System 8a", + "fan_control": "VARIABLE_SPEED_DRIVE", + "supply_fans": [{"id": "Supply Fan 8a"}], + "return_fans": [{"id": "Return Fan 8a"}], + }, + }, + { + "id": "System 8b", + "cooling_system": { + "id": "CHW Coil 8b", + "type": "FLUID_LOOP", + "chilled_water_loop": "Secondary CHW Loop 1", + }, + "preheat_system": { + "id": "Preheat Coil 8b", + "type": "FLUID_LOOP", + "hot_water_loop": "Purchased HW Loop 1", + }, + "fan_system": { + "id": "VAV Fan System 8b", + "fan_control": "VARIABLE_SPEED_DRIVE", + "supply_fans": [{"id": "Supply Fan 1"}], + "return_fans": [{"id": "Return Fan 1"}], + }, + }, + { + "id": "System 8c", + "cooling_system": { + "id": "CHW Coil 8C", + "type": "FLUID_LOOP", + "chilled_water_loop": "Purchased Chilled Water Loop 1", + }, + "preheat_system": { + "id": "Preheat Coil 8C", + "type": "FLUID_LOOP", + "hot_water_loop": "Purchased HW Loop 1", + }, + "fan_system": { + "id": "VAV Fan System 8C", + "fan_control": "VARIABLE_SPEED_DRIVE", + "supply_fans": [{"id": "Supply Fan 1"}], + "return_fans": [{"id": "Return Fan 1"}], + }, + }, + ], + } + ], + } + ], + "boilers": [ + { + "id": "Boiler 1", + "loop": "Boiler Loop 1", + "energy_source_type": "NATURAL_GAS", + } + ], + "external_fluid_sources": [ + { + "id": "Purchased CW 1", + "loop": "Purchased Chilled Water Loop 1", + "type": "CHILLED_WATER", + }, + { + "id": "Purchased HW 1", + "loop": "Purchased HW Loop 1", + "type": "HOT_WATER", + }, + { + "id": "Purchased CW 2", + "loop": "Chilled Water Loop 2", + "type": "CHILLED_WATER", + }, + ], + "chillers": [{"id": "Chiller 1", "cooling_loop": "Chiller Loop 1"}], + "pumps": [ + { + "id": "Boiler Pump 1", + "loop_or_piping": "Boiler Loop 1", + "speed_control": "FIXED_SPEED", + }, + { + "id": "Chiller Pump 1", + "loop_or_piping": "Chiller Loop 1", + "speed_control": "FIXED_SPEED", + }, + { + "id": "Secondary CHW Pump", + "loop_or_piping": "Secondary CHW Loop 1", + "speed_control": "VARIABLE_SPEED", + }, + ], + "fluid_loops": [ + {"id": "Boiler Loop 1", "type": "HEATING"}, + {"id": "Purchased HW Loop 1", "type": "HEATING"}, + { + "id": "Chiller Loop 1", + "type": "COOLING", + "child_loops": [{"id": "Secondary CHW Loop 1", "type": "COOLING"}], + }, + {"id": "Purchased Chilled Water Loop 1", "type": "COOLING"}, + ], + "type": "BASELINE_0", + } + ], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + +SYS_8_TEST_UNMATCHED_RMD = { + "id": "ASHRAE229 1", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Building Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 1", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "Air Terminal 1", + "is_supply_ducted": True, + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System Type Unmatched 1", + "heating_source": "ELECTRIC", + "heating_from_loop": "Boiler Loop 1", + "fan": {"id": "fan 1"}, + "fan_configuration": "PARALLEL", + } + ], + }, + { + "id": "Thermal Zone 2", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "Air Terminal 2", + "is_supply_ducted": True, + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System Type Unmatched", + "heating_source": "HOT_WATER", + "heating_from_loop": "Purchased HW Loop 1", + "fan": {"id": "fan 2"}, + "fan_configuration": "PARALLEL", + } + ], + }, + { + "id": "Thermal Zone 3", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "Air Terminal 3", + "is_supply_ducted": True, + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System Type Unmatched 3", + "heating_source": "ELECTRIC", + # "heating_from_loop": "Purchased HW Loop 1", + "fan": {"id": "fan 3"}, + "fan_configuration": "PARALLEL", + } + ], + }, + { + "id": "Thermal Zone 4", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "Air Terminal 4", + "is_supply_ducted": True, + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System Type Unmatched 4", + "heating_source": "HOT_WATER", + "heating_from_loop": "Purchased HW Loop 1", + "fan": {"id": "fan 4"}, + "fan_configuration": "PARALLEL", + } + ], + }, + ], + "heating_ventilating_air_conditioning_systems": [ + { + "id": "System Type Unmatched 1", + "cooling_system": { + "id": "CHW Coil 1", + "type": "FLUID_LOOP", + "chilled_water_loop": "Chilled Water Loop 1", + }, + "preheat_system": { + "id": "Preheat Coil 1", + "type": "ELECTRIC_RESISTANCE", + }, + "fan_system": { + "id": "VAV Fan System 1", + "fan_control": "VARIABLE_SPEED_DRIVE", + "supply_fans": [{"id": "Supply Fan 1"}], + "return_fans": [{"id": "Return Fan 1"}], + }, + }, + { + "id": "System Type Unmatched 2", + "cooling_system": { + "id": "CHW Coil 8b", + "type": "FLUID_LOOP", + "chilled_water_loop": "Secondary CHW Loop 1", + }, + "preheat_system": { + "id": "Preheat Coil 8b", + "type": "OTHER", + }, + "fan_system": { + "id": "VAV Fan System 8b", + "fan_control": "VARIABLE_SPEED_DRIVE", + "supply_fans": [{"id": "Supply Fan 1"}], + "return_fans": [{"id": "Return Fan 1"}], + }, + }, + { + "id": "System Type Unmatched 3", + "cooling_system": { + "id": "CHW Coil 8b", + "type": "FLUID_LOOP", + "chilled_water_loop": "Secondary CHW Loop 1", + }, + "preheat_system": { + "id": "Preheat Coil 3", + "type": "FLUID_LOOP", + "hot_water_loop": "Purchased HW Loop 1", + }, + "fan_system": { + "id": "VAV Fan System 3", + "fan_control": "VARIABLE_SPEED_DRIVE", + "supply_fans": [{"id": "Supply Fan 1"}], + "return_fans": [{"id": "Return Fan 1"}], + }, + }, + { + "id": "System Type Unmatched 4", + "cooling_system": { + "id": "CHW Coil 4", + "type": "FLUID_LOOP", + "chilled_water_loop": "Chilled Water Loop 1", + }, + "preheat_system": { + "id": "Preheat Coil 4", + "type": "FLUID_LOOP", + "hot_water_loop": "Purchased HW Loop 1", + }, + "fan_system": { + "id": "VAV Fan System 4", + "fan_control": "VARIABLE_SPEED_DRIVE", + "supply_fans": [{"id": "Supply Fan 1"}], + "return_fans": [{"id": "Return Fan 1"}], + }, + }, + ], + } + ], + } + ], + "boilers": [ + { + "id": "Boiler 1", + "loop": "Boiler Loop 1", + "energy_source_type": "NATURAL_GAS", + } + ], + "external_fluid_sources": [ + { + "id": "Purchased HW 1", + "loop": "Purchased HW Loop 1", + "type": "HOT_WATER", + }, + { + "id": "Purchased CW 2", + "loop": "Chilled Water Loop 2", + "type": "CHILLED_WATER", + }, + ], + "chillers": [{"id": "Chiller 1", "cooling_loop": "Chiller Loop 1"}], + "pumps": [ + { + "id": "Boiler Pump 1", + "loop_or_piping": "Boiler Loop 1", + "speed_control": "FIXED_SPEED", + }, + { + "id": "Chiller Pump 1", + "loop_or_piping": "Chiller Loop 1", + "speed_control": "FIXED_SPEED", + }, + { + "id": "Secondary CHW Pump", + "loop_or_piping": "Secondary CHW Loop 1", + "speed_control": "VARIABLE_SPEED", + }, + ], + "fluid_loops": [ + {"id": "Boiler Loop 1", "type": "HEATING"}, + {"id": "Purchased HW Loop 1", "type": "HEATING"}, + { + "id": "Chiller Loop 1", + "type": "COOLING", + "child_loops": [{"id": "Secondary CHW Loop 1", "type": "COOLING"}], + }, + {"id": "Chilled Water Loop 1", "type": "COOLING"}, + ], + "type": "BASELINE_0", + } + ], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RMD_baseline_system_8__is_valid(): + schema_validation_result = schema_validate_rpd(SYS_8_TEST_RMD) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__TEST_RMD_baseline_system_8__is_unmatched_valid(): + schema_validation_result = schema_validate_rpd(SYS_8_TEST_UNMATCHED_RMD) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__is_baseline_system_8__true(): + assert ( + is_baseline_system_8( + SYS_8_TEST_RMD["ruleset_model_descriptions"][0], + "System 8", + ["VAV Air Terminal 8"], + ["Thermal Zone 8"], + ) + == HVAC_SYS.SYS_8 + ) + + +def test__is_baseline_system_8__test_json_true(): + assert ( + is_baseline_system_8( + load_system_test_file("System_8_PFP_Reheat.json")[ + "ruleset_model_descriptions" + ][0], + "System 8", + ["VAV Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_8 + ) + + +def test__is_baseline_system_8A__true(): + assert ( + is_baseline_system_8( + SYS_8_TEST_RMD["ruleset_model_descriptions"][0], + "System 8a", + ["VAV Air Terminal 8a"], + ["Thermal Zone 8a"], + ) + == HVAC_SYS.SYS_8A + ) + + +def test__is_baseline_system_8A__test_json_true(): + assert ( + is_baseline_system_8( + load_system_test_file("System_8a_PFP_Reheat.json")[ + "ruleset_model_descriptions" + ][0], + "System 8", + ["VAV Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_8A + ) + + +def test__is_baseline_system_8B__true(): + assert ( + is_baseline_system_8( + SYS_8_TEST_RMD["ruleset_model_descriptions"][0], + "System 8b", + ["VAV Air Terminal 8b"], + ["Thermal Zone 8b"], + ) + == HVAC_SYS.SYS_8B + ) + + +def test__is_baseline_system_8B__test_json_true(): + assert ( + is_baseline_system_8( + load_system_test_file("System_8b_PFP_Reheat.json")[ + "ruleset_model_descriptions" + ][0], + "System 8", + ["VAV Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_8B + ) + + +def test__is_baseline_system_8C__true(): + assert ( + is_baseline_system_8( + SYS_8_TEST_RMD["ruleset_model_descriptions"][0], + "System 8c", + ["VAV Air Terminal 8c"], + ["Thermal Zone 8c"], + ) + == HVAC_SYS.SYS_8C + ) + + +def test__is_baseline_system_8C__test_json_true(): + assert ( + is_baseline_system_8( + load_system_test_file("System_8c_PFP_Reheat.json")[ + "ruleset_model_descriptions" + ][0], + "System 8", + ["VAV Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_8C + ) + + +def test__is_baseline_system_unmatched1__true(): + assert ( + is_baseline_system_8( + SYS_8_TEST_UNMATCHED_RMD["ruleset_model_descriptions"][0], + "System Type Unmatched 1", + ["Air Terminal 1"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.UNMATCHED + ) + + +def test__is_baseline_system_unmatched2__true(): + assert ( + is_baseline_system_8( + SYS_8_TEST_UNMATCHED_RMD["ruleset_model_descriptions"][0], + "System Type Unmatched 2", + ["Air Terminal 2"], + ["Thermal Zone 2"], + ) + == HVAC_SYS.UNMATCHED + ) + + +def test__is_baseline_system_unmatched3__true(): + assert ( + is_baseline_system_8( + SYS_8_TEST_UNMATCHED_RMD["ruleset_model_descriptions"][0], + "System Type Unmatched 3", + ["Air Terminal 3"], + ["Thermal Zone 3"], + ) + == HVAC_SYS.UNMATCHED + ) + + +def test__is_baseline_system_unmatched4__true(): + assert ( + is_baseline_system_8( + SYS_8_TEST_UNMATCHED_RMD["ruleset_model_descriptions"][0], + "System Type Unmatched 4", + ["Air Terminal 4"], + ["Thermal Zone 4"], + ) + == HVAC_SYS.UNMATCHED + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_9.py b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_9.py new file mode 100644 index 0000000000..133cb72988 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/baseline_systems/test_is_baseline_system_9.py @@ -0,0 +1,203 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_hvac_test_util import ( + load_system_test_file, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.is_baseline_system_9 import ( + is_baseline_system_9, +) +from rct229.schema.validate import schema_validate_rpd + +SYS_9_TEST_RMD = { + "id": "ASHRAE229 1", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Building Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 9", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "Constant Air Terminal 9", + "is_supply_ducted": True, + "type": "CONSTANT_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 9", + } + ], + } + ], + "heating_ventilating_air_conditioning_systems": [ + { + "id": "System 9", + "heating_system": { + "id": "Furnace Coil 1", + "type": "FURNACE", + "energy_source_type": "NATURAL_GAS", + }, + "fan_system": { + "id": "CAV Fan System 1", + "fan_control": "CONSTANT", + "supply_fans": [{"id": "Supply Fan 1"}], + "return_fans": [{"id": "Return Fan 1"}], + }, + } + ], + } + ], + } + ], + "type": "BASELINE_0", + } + ], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + +SYS_9B_TEST_RMD = { + "id": "ASHRAE229 1", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Building Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 9B", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "terminals": [ + { + "id": "Constant Air Terminal 9B", + "is_supply_ducted": True, + "type": "CONSTANT_AIR_VOLUME", + "heating_source": "HOT_WATER", + "fan": {"id": "fan 1"}, + "heating_from_loop": "Purchased HW Loop 1", + "served_by_heating_ventilating_air_conditioning_system": "System 9B", + } + ], + } + ], + "heating_ventilating_air_conditioning_systems": [ + { + "id": "System 9B", + } + ], + } + ], + } + ], + "external_fluid_sources": [ + { + "id": "Purchased HW 1", + "loop": "Purchased HW Loop 1", + "type": "HOT_WATER", + } + ], + "pumps": [ + { + "id": "HW Pump 1", + "loop_or_piping": "Purchased HW Loop 1", + "speed_control": "FIXED_SPEED", + } + ], + "fluid_loops": [{"id": "Purchased HW Loop 1", "type": "HEATING"}], + "type": "BASELINE_0", + } + ], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RMD_baseline_system_9__is_valid(): + schema_validation_result = schema_validate_rpd(SYS_9_TEST_RMD) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__TEST_RMD_baseline_system_9B__is_valid(): + schema_validation_result = schema_validate_rpd(SYS_9B_TEST_RMD) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__is_baseline_system_9__true(): + assert ( + is_baseline_system_9( + SYS_9_TEST_RMD["ruleset_model_descriptions"][0], + "System 9", + ["Constant Air Terminal 9"], + ["Thermal Zone 9"], + ) + == HVAC_SYS.SYS_9 + ) + + +def test__is_baseline_system_9__test_json_true(): + assert ( + is_baseline_system_9( + load_system_test_file("System_9_Warm_Air_Furnace_Gas.json")[ + "ruleset_model_descriptions" + ][0], + "System 9", + ["Air Terminal"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_9 + ) + + +def test__is_baseline_system_9B_true(): + assert ( + is_baseline_system_9( + SYS_9B_TEST_RMD["ruleset_model_descriptions"][0], + "System 9B", + ["Constant Air Terminal 9B"], + ["Thermal Zone 9B"], + ) + == HVAC_SYS.SYS_9B + ) + + +def test__is_baseline_system_9B__test_json_true(): + assert ( + is_baseline_system_9( + load_system_test_file("System_9b_Warm_Air_Furnace_Gas.json")[ + "ruleset_model_descriptions" + ][0], + "System 9B", + ["Air Terminal"], + ["Thermal Zone 1"], + ) + == HVAC_SYS.SYS_9B + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/__init__.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/does_zone_meet_G3_2_1_2_a.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/does_zone_meet_G3_2_1_2_a.py new file mode 100644 index 0000000000..5054753cd5 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/does_zone_meet_G3_2_1_2_a.py @@ -0,0 +1,168 @@ +from typing import Literal, Type + +import numpy as np +from pydash import map_ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_system_type_compare import ( + baseline_system_type_compare, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.get_zone_eflh import ( + get_zone_eflh, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.get_zones_computer_rooms import ( + get_zone_computer_rooms, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.get_zones_on_same_floor_list import ( + get_zones_on_same_floor_list, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.get_zone_peak_internal_load_floor_area_dict import ( + get_zone_peak_internal_load_floor_area_dict, +) +from rct229.schema.config import ureg +from rct229.utils.jsonpath_utils import find_all +from rct229.utils.pint_utils import ZERO + +ELIGIBLE_PRIMARY_SYSTEM_TYPES = [ + HVAC_SYS.SYS_5, + HVAC_SYS.SYS_6, + HVAC_SYS.SYS_7, + HVAC_SYS.SYS_8, +] + +NUMBER_OF_WEEKS_IN_YEAR = 52.1429 +NUMBER_OF_WEEKS_IN_LEAP_YEAR = 52.2857 +LOAD_THRESHOLD = 10 * ureg("Btu/hr/ft2") +EFLH_THRESHOLD = 40 + + +def does_zone_meet_g3_2_1_2_a( + rmd: dict, + zone_id: str, + zones_and_systems: dict[str, dict[Literal["expected_system_type"], str]], +) -> bool: + """ + Determines whether a given zone meets the G3_2_1_2_a exception "If the baseline HVAC system type is 5, 6, 7, + 8 use separate single-zone systems conforming with the requirements of system 3 or system 4 (depending on + building heating source) for any spaces that have occupancy or process loads or schedules that differ + significantly from the rest of the building. Peak thermal loads that differ by 10 Btu/h·ft2 (2.930710 W/sf) or + more from the average of other spaces served by the system, or schedules that differ by more than 40 equivalent + full-load hours per week from other spaces served by the system, are considered to differ significantly. Examples + where this exception may be applicable include but are not limited to natatoriums and continually occupied + security areas. This exception does not apply to computer rooms." + + Parameters + ---------- + rmd dict + A dictionary representing a ruleset model description as defined by the ASHRAE229 schema + zone_id str + zone id + is_leap_year boolean + flag indicates whether the model is simulated in a leap year + zones_and_systems dict + a dict map zone to an object that carries expected_system_type and system type string + + Returns + ------- + boolean, true or false + """ + # Infer number of hours in the year (from any valid schedule) + num_hours = None + for sched in find_all("$.schedules[*].hourly_values", rmd): + if isinstance(sched, list) and len(sched) > 0: + num_hours = len(sched) + break + if num_hours is None: + num_hours = 8760 # fallback default + + num_weeks = num_hours / 168.0 + + def get_zone_weekly_eflh(z_id: str) -> float: + zone_eflh = get_zone_eflh(rmd, z_id) + return zone_eflh / num_weeks if num_weeks else 0.0 + + expected_system_type = zones_and_systems[zone_id]["expected_system_type"] + system_matched = any( + [ + baseline_system_type_compare( + expected_system_type, + target_system_type, + exact_match=False, + ) + for target_system_type in ELIGIBLE_PRIMARY_SYSTEM_TYPES + ] + ) + meet_g3_1_1c_flag = False + if system_matched: + zones_on_same_floor_ids = get_zones_on_same_floor_list(rmd, zone_id) + # drop zone_id in the list + if zone_id in zones_on_same_floor_ids: + # in case when floor name is not provided in the RPD. + zones_on_same_floor_ids.remove(zone_id) + # keep only matched system type + zones_same_floor_same_system_type = list( + filter( + lambda other_zone_id: zones_and_systems.get(other_zone_id) + and zones_and_systems[other_zone_id]["expected_system_type"] + == expected_system_type, + zones_on_same_floor_ids, + ) + ) + + # calculate the zone internal loads and eflh + zone_internal_loads = get_zone_peak_internal_load_floor_area_dict(rmd, zone_id) + zone_eflh = get_zone_weekly_eflh(zone_id) + + # In here, the function assumes the zones_and_systems keys are + # a list of conditioned or semi-conditioned zones only + if zones_same_floor_same_system_type: + zone_load_and_eflh_list = [ + ( + # tuple, 0 is peak load dict, 1 is weekly eflh + get_zone_peak_internal_load_floor_area_dict( + rmd, other_match_zone_id + ), + get_zone_weekly_eflh(other_match_zone_id), + ) + for other_match_zone_id in zones_same_floor_same_system_type + ] + else: + # if this is only zone on the same floor has the same system type + # calculate it using the zone itself. + zone_load_and_eflh_list = [(zone_internal_loads, zone_eflh)] + + system_total_area = sum(map_(zone_load_and_eflh_list, "0.area"), ZERO.AREA) + system_total_load = sum(map_(zone_load_and_eflh_list, "0.peak"), ZERO.POWER) + + avg_eflh = ( + ( + np.dot( + map_(zone_load_and_eflh_list, lambda zl: zl[1]), + map_(zone_load_and_eflh_list, lambda zl: zl[0]["area"].magnitude), + ) + / system_total_area.magnitude + ) + if system_total_area != ZERO.AREA + else 0.0 + ) + + avg_internal_load = ( + system_total_load / system_total_area + if system_total_area != ZERO.AREA + else ZERO.POWER_PER_AREA + ) + + meet_g3_1_1c_flag = ( + abs( + zone_internal_loads["peak"] / zone_internal_loads["area"] + - avg_internal_load + ) + > LOAD_THRESHOLD + or zone_eflh - avg_eflh > EFLH_THRESHOLD + ) + + if meet_g3_1_1c_flag: + meet_g3_1_1c_flag = zone_id not in get_zone_computer_rooms(rmd) + + return meet_g3_1_1c_flag diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/does_zone_meet_G3_2_1_2_a_test.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/does_zone_meet_G3_2_1_2_a_test.py new file mode 100644 index 0000000000..1dcb0a5c56 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/does_zone_meet_G3_2_1_2_a_test.py @@ -0,0 +1,396 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.does_zone_meet_G3_2_1_2_a import ( + does_zone_meet_g3_2_1_2_a, +) +from rct229.schema.schema_utils import quantify_rmd +from rct229.schema.validate import schema_validate_rpd + +TEST_RPD_FULL = { + "id": "ASHRAE229 1", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Building Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + # Regular case - False + { + "id": "Thermal Zone 1", + "floor_name": "floor_1", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "spaces": [ + { + "id": "space 1", + "floor_area": 100, + "number_of_occupants": 5, + "interior_lighting": [ + { + "id": "interior_lighting_1", + "lighting_multiplier_schedule": "lighting_schedule_1", + "power_per_area": 1.0, + } + ], + "miscellaneous_equipment": [ + { + "id": "miscellaneous_equipment_1", + "multiplier_schedule": "miscellaneous_equipment_schedule_1", + "power": 5000, + } + ], + "occupant_multiplier_schedule": "occupant_schedule_1", + "occupant_sensible_heat_gain": 125, + "occupant_latent_heat_gain": 125, + } + ], + "terminals": [ + { + "id": "VAV Air Terminal 1", + "is_supply_ducted": True, + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 7", + "heating_source": "HOT_WATER", + "heating_from_loop": "Boiler Loop 1", + } + ], + }, + # Case it is high internal load + { + "id": "Thermal Zone 2", + "floor_name": "floor_1", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "spaces": [ + { + "id": "space 2", + "floor_area": 500, + "number_of_occupants": 5, + "interior_lighting": [ + { + "id": "interior_lighting_2", + "lighting_multiplier_schedule": "lighting_schedule_1", + "power_per_area": 1.0, + } + ], + "miscellaneous_equipment": [ + { + "id": "miscellaneous_equipment_2", + "multiplier_schedule": "miscellaneous_equipment_schedule_1", + "power": 50000, + } + ], + "occupant_multiplier_schedule": "occupant_schedule_1", + "occupant_sensible_heat_gain": 125, + "occupant_latent_heat_gain": 125, + } + ], + "terminals": [ + { + "id": "VAV Air Terminal 2", + "is_supply_ducted": True, + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 7", + "heating_source": "HOT_WATER", + "heating_from_loop": "Boiler Loop 1", + } + ], + }, + # Case EFLH is higher + { + "id": "Thermal Zone 3", + "floor_name": "floor_2", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "spaces": [ + { + "id": "space 3", + "floor_area": 200, + "number_of_occupants": 5, + "interior_lighting": [ + { + "id": "interior_lighting_3", + "lighting_multiplier_schedule": "lighting_schedule_2", + "power_per_area": 1.0, + } + ], + "miscellaneous_equipment": [ + { + "id": "miscellaneous_equipment_2", + "multiplier_schedule": "miscellaneous_equipment_schedule_2", + "power": 5000, + } + ], + "occupant_multiplier_schedule": "occupant_schedule_2", + "occupant_sensible_heat_gain": 125, + "occupant_latent_heat_gain": 125, + } + ], + "terminals": [ + { + "id": "VAV Air Terminal 3", + "is_supply_ducted": True, + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 7", + "heating_source": "HOT_WATER", + "heating_from_loop": "Boiler Loop 1", + } + ], + }, + # Dummy thermal zone for thermal zone 3 test + { + "id": "Thermal Zone 4", + "floor_name": "floor_2", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "spaces": [ + { + "id": "space 4", + "floor_area": 200, + "number_of_occupants": 5, + "interior_lighting": [ + { + "id": "interior_lighting_4", + "lighting_multiplier_schedule": "lighting_schedule_1", + "power_per_area": 1.0, + } + ], + "miscellaneous_equipment": [ + { + "id": "miscellaneous_equipment_4", + "multiplier_schedule": "miscellaneous_equipment_schedule_1", + "power": 5000, + } + ], + "occupant_multiplier_schedule": "occupant_schedule_1", + "occupant_sensible_heat_gain": 125, + "occupant_latent_heat_gain": 125, + } + ], + "terminals": [ + { + "id": "VAV Air Terminal 4", + "is_supply_ducted": True, + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 7", + "heating_source": "HOT_WATER", + "heating_from_loop": "Boiler Loop 1", + } + ], + }, + # Dummy zone for thermal zone 1 test + { + "id": "Thermal Zone 5", + "floor_name": "floor_1", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "spaces": [ + { + "id": "space 5", + "floor_area": 200, + "number_of_occupants": 5, + "interior_lighting": [ + { + "id": "interior_lighting_5", + "lighting_multiplier_schedule": "lighting_schedule_1", + "power_per_area": 1.0, + } + ], + "miscellaneous_equipment": [ + { + "id": "miscellaneous_equipment_5", + "multiplier_schedule": "miscellaneous_equipment_schedule_1", + "power": 5000, + } + ], + "occupant_multiplier_schedule": "occupant_schedule_1", + "occupant_sensible_heat_gain": 125, + "occupant_latent_heat_gain": 125, + } + ], + "terminals": [ + { + "id": "VAV Air Terminal 5", + "is_supply_ducted": True, + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 7", + "heating_source": "HOT_WATER", + "heating_from_loop": "Boiler Loop 1", + } + ], + }, + ], + "heating_ventilating_air_conditioning_systems": [ + { + "id": "System 7", + "cooling_system": { + "id": "CHW Coil 1", + "type": "FLUID_LOOP", + "chilled_water_loop": "Secondary CHW Loop 1", + }, + "preheat_system": { + "id": "Preheat Coil 1", + "type": "FLUID_LOOP", + "hot_water_loop": "Boiler Loop 1", + }, + "fan_system": { + "id": "VAV Fan System 1", + "fan_control": "VARIABLE_SPEED_DRIVE", + "operating_schedule": "Operation Schedule 1", + "supply_fans": [{"id": "Supply Fan 1"}], + "return_fans": [{"id": "Return Fan 1"}], + }, + } + ], + } + ], + } + ], + "boilers": [ + { + "id": "Boiler 1", + "loop": "Boiler Loop 1", + "energy_source_type": "NATURAL_GAS", + } + ], + "chillers": [{"id": "Chiller 1", "cooling_loop": "Chiller Loop 1"}], + "pumps": [ + { + "id": "Boiler Pump 1", + "loop_or_piping": "Boiler Loop 1", + "speed_control": "FIXED_SPEED", + }, + { + "id": "Chiller Pump 1", + "loop_or_piping": "Chiller Loop 1", + "speed_control": "FIXED_SPEED", + }, + { + "id": "Secondary CHW Pump", + "loop_or_piping": "Secondary CHW Loop 1", + "speed_control": "VARIABLE_SPEED", + }, + ], + "fluid_loops": [ + {"id": "Boiler Loop 1", "type": "HEATING"}, + { + "id": "Chiller Loop 1", + "type": "COOLING", + "child_loops": [{"id": "Secondary CHW Loop 1", "type": "COOLING"}], + }, + ], + "schedules": [ + { + "id": "lighting_schedule_1", + "hourly_values": [1.0] * 6000 + [0.0] * 2760, + "hourly_heating_design_day": [0.0] * 24, + "hourly_cooling_design_day": [1.0] * 24, + }, + { + "id": "miscellaneous_equipment_schedule_1", + "hourly_values": [1.0] * 6000 + [0.0] * 2760, + "hourly_heating_design_day": [0.0] * 24, + "hourly_cooling_design_day": [1.0] * 24, + }, + { + "id": "occupant_schedule_1", + "hourly_values": [1.0] * 6000 + [0.0] * 2760, + "hourly_heating_design_day": [0.0] * 24, + "hourly_cooling_design_day": [1.0] * 24, + }, + { + "id": "lighting_schedule_2", + "hourly_values": [1.0] * 8760, + "hourly_heating_design_day": [0.0] * 24, + "hourly_cooling_design_day": [1.0] * 24, + }, + { + "id": "miscellaneous_equipment_schedule_2", + "hourly_values": [1.0] * 8760, + "hourly_heating_design_day": [0.0] * 24, + "hourly_cooling_design_day": [1.0] * 24, + }, + { + "id": "occupant_schedule_2", + "hourly_values": [1.0] * 8760, + "hourly_heating_design_day": [0.0] * 24, + "hourly_cooling_design_day": [1.0] * 24, + }, + { + "id": "Operation Schedule 1", + "hourly_values": [1.0] * 8760, + "hourly_heating_design_day": [0.0] * 24, + "hourly_cooling_design_day": [1.0] * 24, + }, + ], + "type": "BASELINE_0", + } + ], +} + + +TEST_RMD_UNIT = quantify_rmd(TEST_RPD_FULL)["ruleset_model_descriptions"][0] + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__does_zone_meet_g_3_1_1c_thermal_zone_1__false(): + assert ( + does_zone_meet_g3_2_1_2_a( + TEST_RMD_UNIT, + "Thermal Zone 1", + { + "Thermal Zone 1": {"expected_system_type": HVAC_SYS.SYS_7}, + "Thermal Zone 2": {"expected_system_type": HVAC_SYS.SYS_7}, + "Thermal Zone 3": {"expected_system_type": HVAC_SYS.SYS_7}, + "Thermal Zone 4": {"expected_system_type": HVAC_SYS.SYS_7}, + "Thermal Zone 5": {"expected_system_type": HVAC_SYS.SYS_7}, + }, + ) + == False + ) + + +def test__does_zone_meet_g_3_1_1c_thermal_zone_2__true(): + assert ( + does_zone_meet_g3_2_1_2_a( + TEST_RMD_UNIT, + "Thermal Zone 2", + { + "Thermal Zone 1": {"expected_system_type": HVAC_SYS.SYS_7}, + "Thermal Zone 2": {"expected_system_type": HVAC_SYS.SYS_7}, + "Thermal Zone 3": {"expected_system_type": HVAC_SYS.SYS_7}, + "Thermal Zone 4": {"expected_system_type": HVAC_SYS.SYS_7}, + "Thermal Zone 5": {"expected_system_type": HVAC_SYS.SYS_7}, + }, + ) + == True + ) + + +def test__does_zone_meet_g_3_1_1c_thermal_zone_3__true(): + assert ( + does_zone_meet_g3_2_1_2_a( + TEST_RMD_UNIT, + "Thermal Zone 3", + { + "Thermal Zone 1": {"expected_system_type": HVAC_SYS.SYS_7}, + "Thermal Zone 2": {"expected_system_type": HVAC_SYS.SYS_7}, + "Thermal Zone 3": {"expected_system_type": HVAC_SYS.SYS_7}, + "Thermal Zone 4": {"expected_system_type": HVAC_SYS.SYS_7}, + "Thermal Zone 5": {"expected_system_type": HVAC_SYS.SYS_7}, + }, + ) + == True + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/does_zone_meet_G3_2_1_2_b.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/does_zone_meet_G3_2_1_2_b.py new file mode 100644 index 0000000000..2c3189a82c --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/does_zone_meet_G3_2_1_2_b.py @@ -0,0 +1,144 @@ +from pint import Quantity +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.get_building_lab_zones_list import ( + get_building_lab_zones_list, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.get_building_total_lab_exhaust_from_zone_exhaust_fans import ( + get_building_total_lab_exhaust_from_zone_exhaust_fans, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.get_dict_of_zones_and_terminal_units_served_by_hvac_sys import ( + get_dict_of_zones_and_terminal_units_served_by_hvac_sys, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.get_list_hvac_systems_associated_with_zone import ( + get_list_hvac_systems_associated_with_zone, +) +from rct229.schema.config import ureg +from rct229.utils.jsonpath_utils import find_all, find_one +from rct229.utils.pint_utils import ZERO +from rct229.utils.utility_functions import ( + find_exactly_one_hvac_system, + find_exactly_one_terminal_unit, + find_exactly_one_zone, +) + +BUILDING_TOTAL_LAB_EXHAUST_CFM_THRESHOLD = 15_000 * ureg("ft^3 / min") + + +def does_zone_meet_g3_2_1_2_b(rmd: dict, zone_id: str) -> bool: + """ + Determines whether a given zone meets the G3_1_1d exception "For laboratory spaces in a building having a total + laboratory exhaust rate greater than 15,000 cfm, use a single system of type 5 or 7 serving only those spaces." + + + Parameters + ---------- + rmd dict + A dictionary representing a ruleset model description as defined by the ASHRAE229 schema + zone_id str + zone id + + Returns + ------- + boolean True or False + + """ + + def sum_total_primary_airflow_from_terminals_func(terminal_list: list) -> Quantity: + """ + Return sum of the primary airflow from all terminals in the terminal list + Return ZERO.FLOW if none is found. + """ + return sum( + [ + find_exactly_one_terminal_unit(rmd, terminal_id).get( + "primary_airflow", ZERO.FLOW + ) + for terminal_id in terminal_list + ], + ZERO.FLOW, + ) + + def sum_zone_primary_airflow_from_terminals_func( + terminal_list: list, zone: dict + ) -> Quantity: + """ + Return sum of the primary airflow from all terminals exist both in the terminal_list and zone + Return ZERO.FLOW if none is found. + """ + return sum( + [ + find_exactly_one_terminal_unit(rmd, terminal_id).get( + "primary_airflow", ZERO.FLOW + ) + for terminal_id in terminal_list + if find_one(f'$.terminals[*][?(@.id="{terminal_id}")]', zone) + ], + ZERO.FLOW, + ) + + def sum_hvac_total_exhaust_air_func(hvac_id: str) -> Quantity: + """Return sum of the design airflow from all exhaust fans in the HVAC.""" + return sum( + find_all( + "$.fan_system.exhaust_fans[*].design_airflow", + find_exactly_one_hvac_system(rmd, hvac_id), + ), + ZERO.FLOW, + ) + + laboratory_zones_list = get_building_lab_zones_list(rmd) + building_total_lab_exhaust = get_building_total_lab_exhaust_from_zone_exhaust_fans( + rmd + ) + + dict_of_zones_and_terminal_units_served_by_hvac_sys = ( + get_dict_of_zones_and_terminal_units_served_by_hvac_sys(rmd) + ) + + if building_total_lab_exhaust <= BUILDING_TOTAL_LAB_EXHAUST_CFM_THRESHOLD: + for lab_zone_id in laboratory_zones_list: + lab_zone = find_exactly_one_zone(rmd, lab_zone_id) + hvac_sys_list_serving_zone = get_list_hvac_systems_associated_with_zone( + rmd, lab_zone_id + ) + + zone_total_exhaust = ZERO.FLOW + for hvac_id in hvac_sys_list_serving_zone: + # The hvac_id seems to be guaranteed in this dictionary. + # One alternative is to use getattr_ to avoid any possible errors. + terminal_list_hvac_sys = ( + dict_of_zones_and_terminal_units_served_by_hvac_sys[hvac_id][ + "terminal_unit_list" + ] + ) + + # sum of the exhaust air in the HVAC. + hvac_system_total_exhaust_airflow = sum_hvac_total_exhaust_air_func( + hvac_id + ) + + # sum of primary airflow from terminals that have association with the HVAC. + total_terminal_air_flow = sum_total_primary_airflow_from_terminals_func( + terminal_list_hvac_sys + ) + + # sum of primary airflow from terminals that have association with the lab zone + zone_primary_air_flow = sum_zone_primary_airflow_from_terminals_func( + terminal_list_hvac_sys, lab_zone + ) + + if zone_primary_air_flow > ZERO.FLOW: + # note: total_terminal_air_flow > zone_primary_air_flow > ZERO.FLOW + zone_total_exhaust += ( + hvac_system_total_exhaust_airflow + * zone_primary_air_flow + / total_terminal_air_flow + ) + else: + zone_total_exhaust += hvac_system_total_exhaust_airflow + + building_total_lab_exhaust += zone_total_exhaust + + return ( + zone_id in laboratory_zones_list + and building_total_lab_exhaust > BUILDING_TOTAL_LAB_EXHAUST_CFM_THRESHOLD + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/does_zone_meet_G3_2_1_2_b_test.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/does_zone_meet_G3_2_1_2_b_test.py new file mode 100644 index 0000000000..ab5a9b7b0d --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/does_zone_meet_G3_2_1_2_b_test.py @@ -0,0 +1,91 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.does_zone_meet_G3_2_1_2_b import ( + does_zone_meet_g3_2_1_2_b, +) +from rct229.schema.schema_utils import quantify_rmd +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "Building 1", + "building_segments": [ + { + "id": "Building Segment 1", + "heating_ventilating_air_conditioning_systems": [ + { + "id": "hvac 1", + "fan_system": { + "id": "fan_system_1", + "exhaust_fans": [ + { + "id": "exhaust_fans 1", + "design_airflow": 7500, + } + ], + }, + } + ], + "zones": [ + { + "id": "Thermal Zone 1", + "spaces": [ + { + "id": "Space 1", + "function": "LABORATORY", + }, + ], + "terminals": [ + { + "id": "Terminal 1", + "served_by_heating_ventilating_air_conditioning_system": "hvac 1", + "primary_airflow": 6500, + } + ], + "zonal_exhaust_fans": [ + { + "id": "Exhaust Fan 1", + "design_airflow": 1000, + } + ], + }, + { + "id": "Thermal Zone 2", + "terminals": [ + { + "id": "Terminal 2", + "served_by_heating_ventilating_air_conditioning_system": "hvac 1", + "primary_airflow": 1000, + } + ], + }, + ], + }, + ], + }, + ], + "type": "BASELINE_0", +} + + +TEST_RPD_FULL = { + "id": "229", + "ruleset_model_descriptions": [TEST_RMD], +} + +TEST_RMD_UNIT = quantify_rmd(TEST_RPD_FULL)["ruleset_model_descriptions"][0] + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__does_zone_meet_g3_1_1_d__hvac_100_percent_terminals__success(): + assert does_zone_meet_g3_2_1_2_b(TEST_RMD_UNIT, "Thermal Zone 1") is True + + +def test__does_zone_meet_g3_1_1_d__not_lab__failed(): + assert does_zone_meet_g3_2_1_2_b(TEST_RMD_UNIT, "Thermal Zone 2") is False diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/does_zone_meet_G3_2_1_2_c.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/does_zone_meet_G3_2_1_2_c.py new file mode 100644 index 0000000000..f05a390291 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/does_zone_meet_G3_2_1_2_c.py @@ -0,0 +1,63 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.is_zone_likely_a_vestibule import ( + is_zone_likely_a_vestibule, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.is_zone_mechanically_heated_and_not_cooled import ( + is_zone_mechanically_heated_and_not_cooled, +) +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.jsonpath_utils import find_all +from rct229.utils.utility_functions import find_exactly_one_zone + +LightingSpaceOptions2019ASHRAE901TG37 = SchemaEnums.schema_enums[ + "LightingSpaceOptions2019ASHRAE901TG37" +] + +ELIGIBLE_SPACE_TYPES = [ + LightingSpaceOptions2019ASHRAE901TG37.STORAGE_ROOM_HOSPITAL, + LightingSpaceOptions2019ASHRAE901TG37.STORAGE_ROOM_SMALL, + LightingSpaceOptions2019ASHRAE901TG37.STORAGE_ROOM_LARGE, + LightingSpaceOptions2019ASHRAE901TG37.WAREHOUSE_STORAGE_AREA_MEDIUM_TO_BULKY_PALLETIZED_ITEMS, + LightingSpaceOptions2019ASHRAE901TG37.WAREHOUSE_STORAGE_AREA_SMALLER_HAND_CARRIED_ITEMS, + LightingSpaceOptions2019ASHRAE901TG37.STAIRWELL, + LightingSpaceOptions2019ASHRAE901TG37.ELECTRICAL_MECHANICAL_ROOM, + LightingSpaceOptions2019ASHRAE901TG37.RESTROOM_FACILITY_FOR_THE_VISUALLY_IMPAIRED, + LightingSpaceOptions2019ASHRAE901TG37.RESTROOM_ALL_OTHERS, +] + + +def does_zone_meet_g3_2_1_2_c(rmd_b: dict, rmd_p: dict, zone_id: str) -> bool: + """ + determines whether a given zone meets the G3_2_1_2c exception "Thermal zones designed with heating-only systems in + the proposed design serving storage rooms, stairwells, vestibules, electrical/mechanical rooms, and restrooms not + exhausting or transferring air from mechanically cooled thermal zones in the proposed design shall use system + type 9 or 10 in the baseline building design." + + Parameters + ---------- + rmd_b dict + A dictionary representing a ruleset model description as defined by the ASHRAE229 schema, baseline model + rmd_p dict + A dictionary representing a ruleset model description as defined by the ASHRAE229 schema, baseline model + zone_id str + zone id + + Returns + ------- + + """ + zone_b = find_exactly_one_zone(rmd_b, zone_id) + + zone_meet_g_3_1_1e = all( + [ + space_b.get("lighting_space_type") in ELIGIBLE_SPACE_TYPES + for space_b in find_all("$.spaces[*]", zone_b) + ] + ) + + if not zone_meet_g_3_1_1e: + zone_meet_g_3_1_1e = is_zone_likely_a_vestibule(rmd_b, zone_id) + + if zone_meet_g_3_1_1e: + zone_meet_g_3_1_1e = is_zone_mechanically_heated_and_not_cooled(rmd_p, zone_id) + + return zone_meet_g_3_1_1e diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/does_zone_meet_G3_2_1_2_c_test.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/does_zone_meet_G3_2_1_2_c_test.py new file mode 100644 index 0000000000..89bb008372 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/does_zone_meet_G3_2_1_2_c_test.py @@ -0,0 +1,123 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.does_zone_meet_G3_2_1_2_c import ( + does_zone_meet_g3_2_1_2_c, +) +from rct229.schema.schema_utils import quantify_rmd +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "schedules": [{"id": "schedule_1", "hourly_values": [1.2] * 8670}], + "buildings": [ + { + "id": "Building 1", + "building_segments": [ + { + "id": "Building Segment 1", + "heating_ventilating_air_conditioning_systems": [ + { + "id": "System 1", + "cooling_system": { + "id": "csys_1_1_1", + "type": "NON_MECHANICAL", + }, + "heating_system": { + "id": "csys_1_1_2", + "type": "ELECTRIC_RESISTANCE", + }, + }, + { + "id": "System 2", + "heating_system": {"id": "csys_2_1_2", "type": "FURNACE"}, + }, + ], + "zones": [ + # Not a vestibule, zone has mechanical heating and cooling, space types meet 3_1_1e eligible + { + "id": "Thermal Zone 1", + "floor_name": "FLOOR 1", + "spaces": [ + { + "id": "Space 1", + "floor_area": 1000, + "lighting_space_type": "STORAGE_ROOM_HOSPITAL", + }, + { + "id": "Space 2", + "floor_area": 100, + "lighting_space_type": "STORAGE_ROOM_HOSPITAL", + }, + ], + "terminals": [ + { + "id": "terminal_1", + "served_by_heating_ventilating_air_conditioning_system": "System 1", + }, + ], + }, + # Likely a vestibule, zone has mechanical heating and no cooling, + # space type does not meet 3_1_1e eligible + { + # case has door but the space area is greater than 50 or 2% of the floor area + "id": "Thermal Zone 2", + "floor_name": "FLOOR 1", + "terminals": [ + { + "id": "terminal_2_1", + "served_by_heating_ventilating_air_conditioning_system": "System 2", + }, + ], + "spaces": [ + {"id": "Space 3", "floor_area": 5}, + ], + "surfaces": [ + { + "id": "surface 2", + "adjacent_to": "EXTERIOR", + "subsurfaces": [ + { + "id": "subsurface 3", + "classification": "DOOR", + "glazed_area": 10, + "opaque_area": 2, + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + "type": "BASELINE_0", +} + +TEST_RPD_FULL = { + "id": "229", + "ruleset_model_descriptions": [TEST_RMD], +} + +TEST_RMD_UNIT = quantify_rmd(TEST_RPD_FULL)["ruleset_model_descriptions"][0] + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__does_zone_meet_g3_1_1e__zone_1_false(): + # false due to zone is mechanically cooled + assert ( + does_zone_meet_g3_2_1_2_c(TEST_RMD_UNIT, TEST_RMD_UNIT, "Thermal Zone 1") + is False + ) + + +def test__does_zone_meet_g3_1_1e__zone_2_true(): + # true because zone is vestibule and no cooling + assert ( + does_zone_meet_g3_2_1_2_c(TEST_RMD_UNIT, TEST_RMD_UNIT, "Thermal Zone 2") + is True + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/__init__.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/expected_system_type_from_table_g311a_dict.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/expected_system_type_from_table_g311a_dict.py new file mode 100644 index 0000000000..7d667698bf --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/expected_system_type_from_table_g311a_dict.py @@ -0,0 +1,158 @@ +from typing import Literal + +import pydash +from pint import Quantity +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.is_cz_0_to_3a_bool import ( + is_cz_0_to_3a_bool, +) +from rct229.schema.config import ureg +from rct229.schema.schema_enums import SchemaEnums + +PUBLIC_ASSEMBLY_BUILDING_AREA_THRESHOLD = 120_000 * ureg("ft2") +RETAIL_FLOOR_NUMBER_THRESHOLD = 3 +HOSPITAL_BUILDING_AREA_THRESHOLD = 150_000 * ureg("ft2") +HOSPITAL_FLOOR_NUMBER_THRESHOLD = 5 +OTHER_NON_RESIDENTIAL_BUILDING_AREA_LOWER_THRESHOLD = 25_000 * ureg("ft2") +OTHER_NON_RESIDENTIAL_BUILDING_AREA_HIGHER_THRESHOLD = 150_000 * ureg("ft2") +OTHER_NON_RESIDENTIAL_FLOOR_NUMBER_LOWER_THRESHOLD = 4 +OTHER_NON_RESIDENTIAL_FLOOR_NUMBER_HIGHER_THRESHOLD = 5 +HVAC_BUILDING_AREA_TYPE_OPTIONS = SchemaEnums.schema_enums[ + "HeatingVentilatingAirConditioningBuildingAreaOptions2019ASHRAE901" +] + +# True message, false message, condition +message_currier = pydash.curry( + lambda condition, true_input, false_input: true_input if condition else false_input +) + + +def expected_system_type_from_table_g3_1_1_dict( + building_area_type: str, + climate_zone: str, + number_of_floors: int, + building_area: Quantity, +) -> dict[Literal["expected_system_type", "system_origin"], [str, str]]: + """ + + Parameters + ---------- + building_area_type: ("RESIDENTIAL", "PUBLIC_ASSEMBLY", "RETAIL", "HOSPITAL", "OTHER_NON_RESIDENTIAL", "HEATED-ONLY_STORAGE") + climate_zone: + number_of_floors: this is the number of floors in the building (should be an integer greater than 0) + building_area: this is the total area of the building + + Returns + ------- + a dict indicating the expected system type from table G3_1_1 ("SYS-1", "SYS-2", etc) + and a string that indicates how the system was chosen. An example of the return value: + {"EXPECTED_SYSTEM_TYPE": "SYS-4", "SYSTEM_ORIGIN": "PUBLIC_ASSEMBLY CZ_0_to_3a < 120,000 ft2"} + - does not take into account G3.1.1 b-g + """ + is_cz_0_to_3a_flag = is_cz_0_to_3a_bool(climate_zone) + climate_zone_currier = message_currier(is_cz_0_to_3a_flag) + + # Initialize the strings + building_area_string = "" + number_of_floors_string = "" + expected_system_type = "" + climate_zone_category = climate_zone_currier("CZ_0_to_3a", "CZ_3b_3c_or_4_to_8") + + if building_area_type == HVAC_BUILDING_AREA_TYPE_OPTIONS.RETAIL: + # Retail case - put it on top because this case could change the building_area_type + if number_of_floors >= RETAIL_FLOOR_NUMBER_THRESHOLD: + building_area_type = HVAC_BUILDING_AREA_TYPE_OPTIONS.OTHER_NON_RESIDENTIAL + else: + number_of_floors_string = message_currier( + number_of_floors < RETAIL_FLOOR_NUMBER_THRESHOLD, "1 or 2 floors", "" + ) + expected_system_type = climate_zone_currier(HVAC_SYS.SYS_4, HVAC_SYS.SYS_3) + + if building_area_type == HVAC_BUILDING_AREA_TYPE_OPTIONS.RESIDENTIAL: + # Residential case + expected_system_type = climate_zone_currier(HVAC_SYS.SYS_2, HVAC_SYS.SYS_1) + if building_area_type == HVAC_BUILDING_AREA_TYPE_OPTIONS.PUBLIC_ASSEMBLY: + # Public assembly case + public_assembly_area_currier = message_currier( + building_area < PUBLIC_ASSEMBLY_BUILDING_AREA_THRESHOLD + ) + building_area_string = public_assembly_area_currier( + "< 120,000 ft2", ">= 120,000 ft2" + ) + + # true, sys 4 or sys3, false sys 13 or sys 12. + expected_system_type = public_assembly_area_currier( + climate_zone_currier(HVAC_SYS.SYS_4, HVAC_SYS.SYS_3), + climate_zone_currier(HVAC_SYS.SYS_13, HVAC_SYS.SYS_12), + ) + + if building_area_type == HVAC_BUILDING_AREA_TYPE_OPTIONS.HEATED_ONLY_STORAGE: + # Heated only storage case + expected_system_type = climate_zone_currier(HVAC_SYS.SYS_10, HVAC_SYS.SYS_9) + + if building_area_type == HVAC_BUILDING_AREA_TYPE_OPTIONS.HOSPITAL: + # Reset the climate zone category because hospital does not require climate zone + climate_zone_category = "" + hospital_area_currier = message_currier( + building_area > HOSPITAL_BUILDING_AREA_THRESHOLD + or number_of_floors > HOSPITAL_FLOOR_NUMBER_THRESHOLD + ) + + building_area_string = hospital_area_currier( + "> 150,000 ft2 or > 5 floors", "All Other" + ) + expected_system_type = hospital_area_currier(HVAC_SYS.SYS_7, HVAC_SYS.SYS_5) + + if building_area_type == HVAC_BUILDING_AREA_TYPE_OPTIONS.OTHER_NON_RESIDENTIAL: + building_area_low_area_flag = ( + building_area < OTHER_NON_RESIDENTIAL_BUILDING_AREA_LOWER_THRESHOLD + ) + building_area_medium_area_flag = ( + OTHER_NON_RESIDENTIAL_BUILDING_AREA_LOWER_THRESHOLD + <= building_area + <= OTHER_NON_RESIDENTIAL_BUILDING_AREA_HIGHER_THRESHOLD + ) + building_area_large_area_flag = ( + building_area > OTHER_NON_RESIDENTIAL_BUILDING_AREA_HIGHER_THRESHOLD + ) + number_of_floors_low_flag = ( + number_of_floors < OTHER_NON_RESIDENTIAL_FLOOR_NUMBER_LOWER_THRESHOLD + ) + number_of_floors_medium_flag = ( + number_of_floors <= OTHER_NON_RESIDENTIAL_FLOOR_NUMBER_HIGHER_THRESHOLD + ) + number_of_floors_high_flag = ( + number_of_floors > OTHER_NON_RESIDENTIAL_FLOOR_NUMBER_HIGHER_THRESHOLD + ) + + # building area string + if building_area_low_area_flag: + # building_area < 25000 + building_area_string = "< 25,000 ft2" + if number_of_floors_low_flag: + number_of_floors_string = "3 floors or fewer" + expected_system_type = climate_zone_currier( + HVAC_SYS.SYS_4, HVAC_SYS.SYS_3 + ) + elif number_of_floors_medium_flag: + number_of_floors_string = "4-5 floors" + expected_system_type = climate_zone_currier( + HVAC_SYS.SYS_6, HVAC_SYS.SYS_5 + ) + elif building_area_medium_area_flag and number_of_floors_medium_flag: + # (building_area >= 25000) && (building_area < 150000) && (number_of_floors <= 5) + building_area_string = ">=25,000 ft2 AND <=150,000 ft2" + number_of_floors_string = "< 6 floors" + expected_system_type = climate_zone_currier(HVAC_SYS.SYS_6, HVAC_SYS.SYS_5) + elif building_area_large_area_flag or number_of_floors_high_flag: + # (building_area > 150000) || (number_of_floors > 5) + building_area_string = ">150,000 ft2 or > 5 floors" + expected_system_type = climate_zone_currier(HVAC_SYS.SYS_8, HVAC_SYS.SYS_7) + + details_of_system_selection = f"{building_area_type} {climate_zone_category} {building_area_string} {number_of_floors_string}" + return { + "expected_system_type": expected_system_type, + "system_origin": " ".join(details_of_system_selection.split()), + } diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/expected_system_type_from_table_g311a_dict_test.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/expected_system_type_from_table_g311a_dict_test.py new file mode 100644 index 0000000000..0612417ad5 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/expected_system_type_from_table_g311a_dict_test.py @@ -0,0 +1,166 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.expected_system_type_from_table_g311a_dict import ( + expected_system_type_from_table_g3_1_1_dict, +) +from rct229.schema.config import ureg + + +def test__expect_system_type_4_public_assembly_cz1a__success(): + # PUBLIC_ASSEMBLY + # area < 120,000 ft2 + assert expected_system_type_from_table_g3_1_1_dict( + "PUBLIC_ASSEMBLY", "CZ1A", 3, 110_000 * ureg("ft2") + ) == { + "expected_system_type": HVAC_SYS.SYS_4, + "system_origin": "PUBLIC_ASSEMBLY CZ_0_to_3a < 120,000 ft2", + } + + +def test__expect_system_type_13_public_assembly_cz1a__success(): + assert expected_system_type_from_table_g3_1_1_dict( + "PUBLIC_ASSEMBLY", "CZ1A", 3, 130_000 * ureg("ft2") + ) == { + "expected_system_type": HVAC_SYS.SYS_13, + "system_origin": "PUBLIC_ASSEMBLY CZ_0_to_3a >= 120,000 ft2", + } + + +def test__expect_system_type_12_public_assembly_cz3b__success(): + assert expected_system_type_from_table_g3_1_1_dict( + "PUBLIC_ASSEMBLY", "CZ3B", 3, 130_000 * ureg("ft2") + ) == { + "expected_system_type": HVAC_SYS.SYS_12, + "system_origin": "PUBLIC_ASSEMBLY CZ_3b_3c_or_4_to_8 >= 120,000 ft2", + } + + +def test__expect_system_type_5_hospital_cz1a__success(): + # building area <= 150,0000 ft2 AND building has fewer than 5 floors + assert expected_system_type_from_table_g3_1_1_dict( + "HOSPITAL", "CZ1A", 3, 130_000 * ureg("ft2") + ) == {"expected_system_type": HVAC_SYS.SYS_5, "system_origin": "HOSPITAL All Other"} + + +def test__expect_system_type_7_hospital_cz1a__success(): + # area > 150,000 ft2 OR there are more than 5 floors + assert expected_system_type_from_table_g3_1_1_dict( + "HOSPITAL", "CZ1A", 3, 160_000 * ureg("ft2") + ) == { + "expected_system_type": HVAC_SYS.SYS_7, + "system_origin": "HOSPITAL > 150,000 ft2 or > 5 floors", + } + + +def test__expect_system_type_4_retail_cz1a__success(): + # has one or two floors + assert expected_system_type_from_table_g3_1_1_dict( + "RETAIL", "CZ1A", 1, 110_000 * ureg("ft2") + ) == { + "expected_system_type": HVAC_SYS.SYS_4, + "system_origin": "RETAIL CZ_0_to_3a 1 or 2 floors", + } + + +def test__expect_system_type_3_retail_cz3b__success(): + # has one or two floors + assert expected_system_type_from_table_g3_1_1_dict( + "RETAIL", "CZ3B", 1, 110_000 * ureg("ft2") + ) == { + "expected_system_type": HVAC_SYS.SYS_3, + "system_origin": "RETAIL CZ_3b_3c_or_4_to_8 1 or 2 floors", + } + + +def test__expect_system_type_4_other_non_residential_cz1a__success(): + # area < 25,000 ft2, has fewer than 3 floors + assert expected_system_type_from_table_g3_1_1_dict( + "OTHER_NON_RESIDENTIAL", "CZ1A", 1, 20_000 * ureg("ft2") + ) == { + "expected_system_type": HVAC_SYS.SYS_4, + "system_origin": "OTHER_NON_RESIDENTIAL CZ_0_to_3a < 25,000 ft2 3 floors or fewer", + } + + +def test__expect_system_type_3_other_non_residential_cz3b__success(): + # area < 25,000 ft2, has fewer than 3 floors + assert expected_system_type_from_table_g3_1_1_dict( + "OTHER_NON_RESIDENTIAL", "CZ3B", 1, 20_000 * ureg("ft2") + ) == { + "expected_system_type": HVAC_SYS.SYS_3, + "system_origin": "OTHER_NON_RESIDENTIAL CZ_3b_3c_or_4_to_8 < 25,000 ft2 3 floors or fewer", + } + + +def test__expect_system_type_6_other_non_residential_cz1a__success(): + # area < 25,000 ft2, has 4 or 5 floors + assert expected_system_type_from_table_g3_1_1_dict( + "OTHER_NON_RESIDENTIAL", "CZ1A", 5, 20_000 * ureg("ft2") + ) == { + "expected_system_type": HVAC_SYS.SYS_6, + "system_origin": "OTHER_NON_RESIDENTIAL CZ_0_to_3a < 25,000 ft2 4-5 floors", + } + + +def test__expect_system_type_5_other_non_residential_cz3b__success(): + # area < 25,000 ft2, has 4 or 5 floors + assert expected_system_type_from_table_g3_1_1_dict( + "OTHER_NON_RESIDENTIAL", "CZ3B", 5, 20_000 * ureg("ft2") + ) == { + "expected_system_type": HVAC_SYS.SYS_5, + "system_origin": "OTHER_NON_RESIDENTIAL CZ_3b_3c_or_4_to_8 < 25,000 ft2 4-5 floors", + } + + +def test__expect_system_type_6_other_non_residential_cz1a_5_floors__success(): + # area >= 25,000 ft2 AND <=150,000 ft2, has 5 or fewer floors + assert expected_system_type_from_table_g3_1_1_dict( + "OTHER_NON_RESIDENTIAL", "CZ1A", 5, 30_000 * ureg("ft2") + ) == { + "expected_system_type": HVAC_SYS.SYS_6, + "system_origin": "OTHER_NON_RESIDENTIAL CZ_0_to_3a >=25,000 ft2 AND <=150,000 ft2 < 6 floors", + } + + +def test__expect_system_type_5_other_non_residential_cz3b_5_floors__success(): + # area >= 25,000 ft2 AND <=150,000 ft2, has 5 or fewer floors + assert expected_system_type_from_table_g3_1_1_dict( + "OTHER_NON_RESIDENTIAL", "CZ3B", 5, 30_000 * ureg("ft2") + ) == { + "expected_system_type": HVAC_SYS.SYS_5, + "system_origin": "OTHER_NON_RESIDENTIAL CZ_3b_3c_or_4_to_8 >=25,000 ft2 AND <=150,000 ft2 < 6 floors", + } + + +def test__expect_system_type_8_other_non_residential_cz1a_6_floors__success(): + # area > 150,000 ft2 OR > 5 floors (need to be higher than 25,000 sqft) + # This case matches to > 5 floors + assert expected_system_type_from_table_g3_1_1_dict( + "OTHER_NON_RESIDENTIAL", "CZ1A", 6, 30_000 * ureg("ft2") + ) == { + "expected_system_type": HVAC_SYS.SYS_8, + "system_origin": "OTHER_NON_RESIDENTIAL CZ_0_to_3a >150,000 ft2 or > 5 floors", + } + + +def test__expect_system_type_8_other_non_residential_cz1a_4_floors__success(): + # area > 150,000 ft2 OR > 5 floors (need to be higher than 25,000 sqft) + # This case matches to area > 150,000 + assert expected_system_type_from_table_g3_1_1_dict( + "OTHER_NON_RESIDENTIAL", "CZ1A", 4, 230_000 * ureg("ft2") + ) == { + "expected_system_type": HVAC_SYS.SYS_8, + "system_origin": "OTHER_NON_RESIDENTIAL CZ_0_to_3a >150,000 ft2 or > 5 floors", + } + + +def test__expect_system_type_7_other_non_residential_cz3b_6_floors__success(): + # area > 150,000 ft2 OR > 5 floors (need to be higher than 25,000 sqft) + # This case matches to > 5 floors + assert expected_system_type_from_table_g3_1_1_dict( + "OTHER_NON_RESIDENTIAL", "CZ3B", 6, 30_000 * ureg("ft2") + ) == { + "expected_system_type": HVAC_SYS.SYS_7, + "system_origin": "OTHER_NON_RESIDENTIAL CZ_3b_3c_or_4_to_8 >150,000 ft2 or > 5 floors", + } diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_building_lab_zones_list.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_building_lab_zones_list.py new file mode 100644 index 0000000000..e4a3e93dc2 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_building_lab_zones_list.py @@ -0,0 +1,37 @@ +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.jsonpath_utils import find_all + +SpaceFunctionOptions = SchemaEnums.schema_enums["SpaceFunctionOptions"] +LightingSpaceOptions = SchemaEnums.schema_enums["LightingSpaceOptions2019ASHRAE901TG37"] + + +def get_building_lab_zones_list(rmd: dict) -> list[str]: + """ + returns a list of all of the zones in the building that include a laboratory space + + Parameters + ---------- + rmd dict + A dictionary representing a ruleset model description as defined by the ASHRAE229 schema + + Returns + ------- + a list of zone.ids for all zones that have a laboratory space in the building + """ + laboratory_zone_list = [] + for zone in find_all("$.buildings[*].building_segments[*].zones[*]", rmd): + if any( + [ + space.get("function") == SpaceFunctionOptions.LABORATORY + and space.get("lighting_space_type") is None + or space.get("function") is None + and space.get("lighting_space_type") + == LightingSpaceOptions.LABORATORY_EXCEPT_IN_OR_AS_A_CLASSROOM + or space.get("function") == SpaceFunctionOptions.LABORATORY + and LightingSpaceOptions.LABORATORY_EXCEPT_IN_OR_AS_A_CLASSROOM + for space in find_all("$.spaces[*]", zone) + ] + ): + laboratory_zone_list.append(zone["id"]) + + return laboratory_zone_list diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_building_lab_zones_list_test.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_building_lab_zones_list_test.py new file mode 100644 index 0000000000..22dc473b9f --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_building_lab_zones_list_test.py @@ -0,0 +1,112 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.get_building_lab_zones_list import ( + get_building_lab_zones_list, +) +from rct229.schema.schema_utils import quantify_rmd +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "Building 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 1", + "spaces": [ + { + "id": "Space 1", + "function": "LABORATORY", + }, + ], + }, + { + "id": "Thermal Zone 2", + "spaces": [ + { + "id": "Space 2", + "function": "LABORATORY", + }, + { + "id": "Space 3", + "function": "KITCHEN", + }, + ], + }, + { + "id": "Thermal Zone 3", + "spaces": [ + { + "id": "Space 4", + "function": "LABORATORY", + }, + { + "id": "Space 5", + }, + ], + }, + { + "id": "Thermal Zone 4", + "spaces": [ + { + "id": "Space 6", + "function": "KITCHEN", + }, + { + "id": "Space 7", + }, + ], + }, + { + "id": "Thermal Zone 5", + "spaces": [ + { + "id": "Space 8", + "lighting_space_type": "LABORATORY_EXCEPT_IN_OR_AS_A_CLASSROOM", + }, + ], + }, + { + "id": "Thermal Zone 6", + "spaces": [ + { + "id": "Space 9", + "function": "LABORATORY", + "lighting_space_type": "LABORATORY_EXCEPT_IN_OR_AS_A_CLASSROOM", + }, + ], + }, + ], + }, + ], + }, + ], + "type": "BASELINE_0", +} + + +TEST_RPD_FULL = { + "id": "229", + "ruleset_model_descriptions": [TEST_RMD], +} + +TEST_RMD_UNIT = quantify_rmd(TEST_RPD_FULL)["ruleset_model_descriptions"][0] + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__get_building_lab_zone_list__success(): + assert get_building_lab_zones_list(TEST_RMD_UNIT) == [ + "Thermal Zone 1", + "Thermal Zone 2", + "Thermal Zone 3", + "Thermal Zone 5", + "Thermal Zone 6", + ] diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_building_total_lab_exhaust_from_zone_exhaust_fans.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_building_total_lab_exhaust_from_zone_exhaust_fans.py new file mode 100644 index 0000000000..539278e120 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_building_total_lab_exhaust_from_zone_exhaust_fans.py @@ -0,0 +1,36 @@ +from pint import Quantity +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.get_building_lab_zones_list import ( + get_building_lab_zones_list, +) +from rct229.utils.jsonpath_utils import find_all +from rct229.utils.pint_utils import ZERO +from rct229.utils.utility_functions import find_exactly_one_zone + + +def get_building_total_lab_exhaust_from_zone_exhaust_fans(rmd: dict) -> Quantity: + """ + Determines the total exhaust air flow rate for zone exhaust fans in zones that have laboratory spaces + The function returns either ZERO.FLOW or exhaust flow, which means it won't fail if data is missing or error. + + Parameters + ---------- + rmd dict + A dictionary representing a ruleset model description as defined by the ASHRAE229 schema + + Returns + ------- + A numerical value indicating the total building exhaust airflow for zone exhaust fans in zones that have laboratory spaces + """ + total_exhaust = ZERO.FLOW + laboratory_zone_list = get_building_lab_zones_list(rmd) + for zone_id in laboratory_zone_list: + design_airflow = sum( + find_all( + "$.zonal_exhaust_fans[*].design_airflow", + find_exactly_one_zone(rmd, zone_id), + ) + ) + if design_airflow: + total_exhaust += design_airflow + + return total_exhaust diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_building_total_lab_exhaust_from_zone_exhaust_fans_test.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_building_total_lab_exhaust_from_zone_exhaust_fans_test.py new file mode 100644 index 0000000000..08b5692641 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_building_total_lab_exhaust_from_zone_exhaust_fans_test.py @@ -0,0 +1,101 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.get_building_total_lab_exhaust_from_zone_exhaust_fans import ( + get_building_total_lab_exhaust_from_zone_exhaust_fans, +) +from rct229.schema.config import ureg +from rct229.schema.schema_utils import quantify_rmd +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "Building 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 1", + "spaces": [ + { + "id": "Space 1", + "function": "LABORATORY", + }, + ], + "zonal_exhaust_fans": [ + { + "id": "Exhaust Fan 1", + "design_airflow": 1000, + } + ], + }, + { + "id": "Thermal Zone 2", + "spaces": [ + { + "id": "Space 2", + "function": "LABORATORY", + }, + { + "id": "Space 3", + "function": "KITCHEN", + }, + ], + "zonal_exhaust_fans": [ + { + "id": "Exhaust Fan 2", + } + ], + }, + { + "id": "Thermal Zone 3", + "spaces": [ + { + "id": "Space 4", + "function": "LABORATORY", + }, + { + "id": "Space 5", + }, + ], + }, + { + "id": "Thermal Zone 4", + "spaces": [ + { + "id": "Space 6", + "function": "KITCHEN", + }, + { + "id": "Space 7", + }, + ], + }, + ], + }, + ], + }, + ], + "type": "BASELINE_0", +} + + +TEST_RPD_FULL = { + "id": "229", + "ruleset_model_descriptions": [TEST_RMD], +} + +TEST_RMD_UNIT = quantify_rmd(TEST_RPD_FULL)["ruleset_model_descriptions"][0] + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__get_building_total_lab_exhaust_from_zone_exhaust_fan__success(): + assert get_building_total_lab_exhaust_from_zone_exhaust_fans( + TEST_RMD_UNIT + ) == 1000 * ureg("liter / second").to("ft^3 / min") diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_computer_zones_peak_cooling_load.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_computer_zones_peak_cooling_load.py new file mode 100644 index 0000000000..60413a8534 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_computer_zones_peak_cooling_load.py @@ -0,0 +1,32 @@ +from pint import Quantity +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.get_zones_computer_rooms import ( + get_zone_computer_rooms, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.get_zone_peak_internal_load_floor_area_dict import ( + get_zone_peak_internal_load_floor_area_dict, +) +from rct229.utils.pint_utils import ZERO + + +def get_total_computer_zones_peak_cooling_load(rmd: dict) -> Quantity: + """ + Return peak load of computer zones in the building + + Parameters + ---------- + rmd dict + A dictionary representing a ruleset model description as defined by the ASHRAE229 schema + + Returns + ------- + Peak load + """ + computer_room_zones_dict = get_zone_computer_rooms(rmd) + total_computer_peak_cooling_load = ZERO.POWER + for computer_zone_id in computer_room_zones_dict: + zone_peak_internal_load = get_zone_peak_internal_load_floor_area_dict( + rmd, computer_zone_id + )["peak"] + total_computer_peak_cooling_load += zone_peak_internal_load + + return total_computer_peak_cooling_load diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_computer_zones_peak_cooling_load_test.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_computer_zones_peak_cooling_load_test.py new file mode 100644 index 0000000000..8154617e80 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_computer_zones_peak_cooling_load_test.py @@ -0,0 +1,88 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.get_computer_zones_peak_cooling_load import ( + get_total_computer_zones_peak_cooling_load, +) +from rct229.schema.config import ureg +from rct229.schema.schema_utils import quantify_rmd +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "Building 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 1", + "spaces": [ + { + "id": "Space 1", + "lighting_space_type": "COMPUTER_ROOM", + "floor_area": 500, + "interior_lighting": [ + { + "id": "interior_lighting_1", + "lighting_multiplier_schedule": "lighting_schedule_1", + "power_per_area": 1.0, + } + ], + "miscellaneous_equipment": [ + { + "id": "miscellaneous_equipment_1", + "multiplier_schedule": "miscellaneous_equipment_schedule_1", + "power": 500, + } + ], + "occupant_multiplier_schedule": "occupant_schedule_1", + "occupant_sensible_heat_gain": 125, + "occupant_latent_heat_gain": 125, + }, + ], + }, + ], + }, + ], + }, + ], + "schedules": [ + {"id": "schedule_1", "hourly_values": [1.2] * 8670}, + {"id": "lighting_schedule_1", "hourly_cooling_design_day": [1] * 24}, + { + "id": "miscellaneous_equipment_schedule_1", + "hourly_cooling_design_day": [1] * 24, + }, + {"id": "occupant_schedule_1", "hourly_cooling_design_day": [1] * 23 + [2] * 1}, + ], + "type": "BASELINE_0", +} + + +TEST_RPD_FULL = { + "id": "229", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + +TEST_RMD_UNIT = quantify_rmd(TEST_RPD_FULL)["ruleset_model_descriptions"][0] + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__get_computer_zones_peak_cooling_load__success(): + assert get_total_computer_zones_peak_cooling_load(TEST_RMD_UNIT) == 1500 * ureg( + "watt" + ).to("Btu/hr") diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_hvac_building_area_types_and_zones_dict.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_hvac_building_area_types_and_zones_dict.py new file mode 100644 index 0000000000..2d7213ba4a --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_hvac_building_area_types_and_zones_dict.py @@ -0,0 +1,203 @@ +import logging +from typing import TypedDict + +from pint import Quantity +from pydash import curry, filter_, flatten_deep, flow, map_ +from rct229.rulesets.ashrae9012022.data_fns.table_lighting_to_hvac_bat_map_fns import ( + building_lighting_to_hvac_bat, + space_lighting_to_hvac_bat, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.get_zone_conditioning_category_dict import ( + ZoneConditioningCategory, + get_zone_conditioning_category_rmd_dict, +) +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.assertions import assert_ +from rct229.utils.jsonpath_utils import find_all +from rct229.utils.pint_utils import ZERO + +OTHER_UNDETERMINED = "OTHER_UNDETERMINED" +HVAC_BUILDING_AREA_TYPE_OPTIONS = SchemaEnums.schema_enums[ + "HeatingVentilatingAirConditioningBuildingAreaOptions2019ASHRAE901" +] + + +class ClassificationSource: + BUILDING_SEGMENT_HVAC_BAT = "BUILDING_SEGMENT_HVAC_BAT" + BUILDING_SEGMENT_LIGHTING = "BUILDING_SEGMENT_LIGHTING" + SPACE_LIGHTING = "SPACE_LIGHTING" + + +logger = logging.getLogger(__name__) + +# create currier that merges two building area type values +bat_val_merge_curry = curry( + lambda a, b: { + "zone_ids": [*a["zone_ids"], *b["zone_ids"]], + "floor_area": a["floor_area"] + b["floor_area"], + } +) +# create currier that retrieves a data form a dictionary with +# default handling of non-existence errors. +get_bat_val_func_curry = curry( + lambda bat_dict, key: bat_dict.get(key, {"zone_ids": [], "floor_area": ZERO.AREA}) +) + + +class BuildingAreaTypesWithTotalAreaZones(TypedDict): + floor_area: Quantity + zone_ids: list[str] + + +def get_hvac_building_area_types_and_zones_dict( + climate_zone: str, rmd: dict +) -> dict[str, BuildingAreaTypesWithTotalAreaZones]: + """ + + Parameters + ---------- + climate_zone str + One of the ClimateZoneOptions2019ASHRAE901 enumerated values + rmd dict + A dictionary representing a ruleset model description as defined by the ASHRAE229 schema + + Returns + ------- + + """ + zone_conditioning_category_dict = get_zone_conditioning_category_rmd_dict( + climate_zone, rmd + ) + + building_area_types_with_total_area_and_zones_dict = {} + # create a new currier to get building area type value from building_area_types_with_total_area_and_zones_dict + bat_dict_currier = get_bat_val_func_curry( + building_area_types_with_total_area_and_zones_dict + ) + + for building_segment in find_all("$.buildings[*].building_segments[*]", rmd): + if building_segment.get( + "area_type_heating_ventilating_air_conditioning_system" + ): + building_segment_hvac_bat = building_segment[ + "area_type_heating_ventilating_air_conditioning_system" + ] + classification_source = ClassificationSource.BUILDING_SEGMENT_HVAC_BAT + elif building_segment.get("lighting_building_area_type"): + building_segment_hvac_bat = building_lighting_to_hvac_bat( + building_segment["lighting_building_area_type"] + ) + classification_source = ClassificationSource.BUILDING_SEGMENT_LIGHTING + else: + building_segment_space_types_areas_dict = {} + for space in find_all("$.zones[*].spaces[*]", building_segment): + space_hvac_bat = space.get("lighting_space_type") + if space_hvac_bat: + building_segment_space_types_areas_dict[ + space_hvac_bat + ] = building_segment_space_types_areas_dict.get( + space_hvac_bat, ZERO.AREA + ) + space.get( + "floor_area", ZERO.AREA + ) + + # Raise assertion if no space type matched from data (empty dictionary) + assert_( + building_segment_space_types_areas_dict, + f"Failed to determine hvac area type for building segment: {building_segment['id']}. Verify the model inputs and make sure it contains either of area_type_heating_ventilating_air_conditioning_system, lighting_building_area_type or space.lighting_space_type.", + ) + + building_segment_hvac_bat = space_lighting_to_hvac_bat( + max( + building_segment_space_types_areas_dict, + key=building_segment_space_types_areas_dict.get, + ) + ) + classification_source = ClassificationSource.SPACE_LIGHTING + + # Log the data for debug purpose + logger.info( + f"building segment {building_segment['id']} is determined as {building_segment_hvac_bat}. The classification source is {classification_source}" + ) + + # filter zones + # only conditioned (mixed, residential & nonresidential) zones in this list. + filtered_zones_list = filter_( + find_all("$.zones[*]", building_segment), + lambda zone: zone_conditioning_category_dict[zone["id"]] + in [ + ZoneConditioningCategory.CONDITIONED_MIXED, + ZoneConditioningCategory.CONDITIONED_NON_RESIDENTIAL, + ZoneConditioningCategory.CONDITIONED_RESIDENTIAL, + ], + ) + + if filtered_zones_list: + # if there are conditioned zones + # add new hvac bat val to the existing/new hvac bat + building_area_types_with_total_area_and_zones_dict[ + building_segment_hvac_bat + ] = flow( + bat_dict_currier, + bat_val_merge_curry( + { + "zone_ids": map_(filtered_zones_list, "id"), + "floor_area": flow( + # create a 2d list [[zone -> space floor area list]] + lambda zones: map_( + zones, + lambda zone: find_all("$.spaces[*].floor_area", zone), + ), + flatten_deep, + sum, + )(filtered_zones_list), + } + ), + )( + building_segment_hvac_bat + ) + + # check other undetermined + if OTHER_UNDETERMINED in building_area_types_with_total_area_and_zones_dict: + # find predominate hvac bat by the largest floor_area + predominate_hvac_bat = sorted( + building_area_types_with_total_area_and_zones_dict.items(), + key=lambda x: x[1]["floor_area"], + reverse=True, + )[0][0] + # pop the OTHER UNDETERMINED val + other_undetermined_val = building_area_types_with_total_area_and_zones_dict.pop( + OTHER_UNDETERMINED + ) + # create currier for building area types value merge with other undetermined data + other_undetermined_bat_val_merge_currier = bat_val_merge_curry( + other_undetermined_val + ) + # create a new flow to add/update building area types value with the other undetermined data. + assign_bat_val_flow = flow( + bat_dict_currier, other_undetermined_bat_val_merge_currier + ) + + if ( + predominate_hvac_bat == OTHER_UNDETERMINED + or predominate_hvac_bat == HVAC_BUILDING_AREA_TYPE_OPTIONS.RESIDENTIAL + ): + # case to merge other undetermined to other non residential + building_area_types_with_total_area_and_zones_dict[ + HVAC_BUILDING_AREA_TYPE_OPTIONS.OTHER_NON_RESIDENTIAL + ] = assign_bat_val_flow( + HVAC_BUILDING_AREA_TYPE_OPTIONS.OTHER_NON_RESIDENTIAL + ) + else: + # case to merge other undetermined to predominate hvac bat + building_area_types_with_total_area_and_zones_dict[ + predominate_hvac_bat + ] = assign_bat_val_flow(predominate_hvac_bat) + + assert_( + building_area_types_with_total_area_and_zones_dict, + "No building area is found in the model. Please make " + "sure there are building_segments data group in the " + "model", + ) + return building_area_types_with_total_area_and_zones_dict diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_hvac_building_area_types_and_zones_dict_test.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_hvac_building_area_types_and_zones_dict_test.py new file mode 100644 index 0000000000..19cbfd2fbc --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_hvac_building_area_types_and_zones_dict_test.py @@ -0,0 +1,188 @@ +from copy import deepcopy + +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.get_hvac_building_area_types_and_zones_dict import ( + get_hvac_building_area_types_and_zones_dict, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.get_zone_conditioning_category_dict import ( + CAPACITY_THRESHOLD as CAPACITY_THRESHOLD_QUANTITY, +) +from rct229.schema.config import ureg +from rct229.schema.schema_utils import quantify_rmd +from rct229.schema.validate import schema_validate_rpd + +POWER_DELTA = 1 +POWER_THRESHOLD_100 = (CAPACITY_THRESHOLD_QUANTITY * 100 * ureg("m2")).to("W").magnitude + +TEST_RMD = { + "id": "test_rmd", + "constructions": [ + { + "id": "construction_1", + "u_factor": 1.2, + } + ], + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Building Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "heating_ventilating_air_conditioning_systems": [ + # directly conditioned zone + { + "id": "hvac_1_1", + "cooling_system": { + "id": "csys_1_1_1", + "design_sensible_cool_capacity": 2 * POWER_THRESHOLD_100 + + POWER_DELTA, + }, + } + ], + "zones": [ + { + "id": "Thermal Zone 1", + "spaces": [ + { + "id": "Space 1", + "floor_area": 100, + "lighting_space_type": "ATRIUM_LOW_MEDIUM", + } + ], + "terminals": [ + { + "id": "Terminal 1-1", + "served_by_heating_ventilating_air_conditioning_system": "hvac_1_1", + } + ], + }, + ], + }, + { + "id": "Building Segment 2", + "heating_ventilating_air_conditioning_systems": [ + # directly conditioned zone + { + "id": "hvac_2_1", + "cooling_system": { + "id": "csys_2_1_1", + "design_sensible_cool_capacity": 2 * POWER_THRESHOLD_100 + + POWER_DELTA, + }, + } + ], + "zones": [ + { + "id": "Thermal Zone 2", + "spaces": [ + { + "id": "Space 2", + "floor_area": 200, + "lighting_space_type": "ATRIUM_LOW_MEDIUM", + } + ], + "terminals": [ + { + "id": "Terminal 2-1", + "served_by_heating_ventilating_air_conditioning_system": "hvac_2_1", + } + ], + }, + ], + }, + { + "id": "Building Segment 3", + "zones": [ + { + "id": "Thermal Zone 3", + "volume": 100, + "spaces": [ + { + "id": "Space 3", + "floor_area": 200, + "lighting_space_type": "LOBBY_HOTEL", + } + ], + "surfaces": [ + # Adds to zone_other_ua + { + "id": "surface_3_1_1", + "adjacent_to": "INTERIOR", + "adjacent_zone": "zone_1_2", # semi-heated + "area": 10, # m2 + "tilt": 90, # wall + "construction": "construction_1", + } + ], + }, + ], + }, + ], + }, + ], + "constructions": [ + { + "id": "construction_1", + "u_factor": 1.2, + }, + ], + "type": "BASELINE_0", +} + + +TEST_RPD_FULL = { + "id": "229", + "ruleset_model_descriptions": [TEST_RMD], +} + +TEST_RMD_UNIT = quantify_rmd(TEST_RPD_FULL)["ruleset_model_descriptions"][0] + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__get_hvac_building_area_types_and_zones_dict__undetermined_predominante_success(): + assert get_hvac_building_area_types_and_zones_dict("CZ4A", TEST_RMD_UNIT) == { + "OTHER_NON_RESIDENTIAL": { + "zone_ids": ["Thermal Zone 2", "Thermal Zone 1"], + "floor_area": 300 * ureg("m2").to("ft2"), + } + } + + +def test__get_hvac_building_area_types_and_zones_dict__residential_predominate_success(): + test_rmd_unit_residential = deepcopy(TEST_RMD_UNIT) + test_rmd_unit_residential["buildings"][0]["building_segments"][1][ + "lighting_building_area_type" + ] = "MULTIFAMILY" + assert get_hvac_building_area_types_and_zones_dict( + "CZ4A", test_rmd_unit_residential + ) == { + "RESIDENTIAL": { + "zone_ids": ["Thermal Zone 2"], + "floor_area": 200 * ureg("m2").to("ft2"), + }, + "OTHER_NON_RESIDENTIAL": { + "zone_ids": ["Thermal Zone 1"], + "floor_area": 100 * ureg("m2").to("ft2"), + }, + } + + +def test__get_hvac_building_area_types_and_zones_dict__public_assembly_predominate_success(): + test_rmd_unit_residential = deepcopy(TEST_RMD_UNIT) + test_rmd_unit_residential["buildings"][0]["building_segments"][1][ + "lighting_building_area_type" + ] = "RELIGIOUS_FACILITY" + assert get_hvac_building_area_types_and_zones_dict( + "CZ4A", test_rmd_unit_residential + ) == { + "PUBLIC_ASSEMBLY": { + "zone_ids": ["Thermal Zone 1", "Thermal Zone 2"], + "floor_area": 300 * ureg("m2").to("ft2"), + } + } diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_number_of_floors.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_number_of_floors.py new file mode 100644 index 0000000000..3ac124f45d --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_number_of_floors.py @@ -0,0 +1,81 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.get_zone_conditioning_category_dict import ( + ZoneConditioningCategory as ZCC, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.get_zone_conditioning_category_dict import ( + get_zone_conditioning_category_rmd_dict, +) +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.jsonpath_utils import find_all + +LightingSpaceOptions2019ASHRAE901TG37 = SchemaEnums.schema_enums[ + "LightingSpaceOptions2019ASHRAE901TG37" +] + + +def get_number_of_floors(climate_zone: str, rmd: dict) -> int: + """ + gets the number of floors in the building. Parking Garages are not counted + + Parameters + ---------- + climate_zone: str + One of the ClimateZoneOptions2019ASHRAE901 enumerated values + rmd dict + A dictionary representing a ruleset model description as defined by the ASHRAE229 schema + + Returns + ------- + number_of_floors Integer + number of floors + """ + + number_of_floors = sum( + [ + building.get("number_of_floors_above_grade", 0) + + building.get("number_of_floors_below_grade", 0) + for building in find_all("$.buildings[*]", rmd) + ] + ) + + if number_of_floors <= 0: + zone_conditioning_category_dict = get_zone_conditioning_category_rmd_dict( + climate_zone, rmd + ) + + def is_zone_conditioned(zone): + """ + Function returns a boolean. True if a zone is conditioned mixed, conditioned residential + conditioned non residential or semi-heated + """ + zcc = zone_conditioning_category_dict[zone["id"]] + return ( + zcc == ZCC.CONDITIONED_MIXED + or zcc == ZCC.CONDITIONED_RESIDENTIAL + or zcc == ZCC.CONDITIONED_NON_RESIDENTIAL + or zcc == ZCC.SEMI_HEATED + ) + + def any_space_in_zone_parking_garage(zone): + "Function returns a boolean. True if any space in a zone are not parking area interior, False otherwise" + return any( + [ + space.get("lighting_space_type", None) + != LightingSpaceOptions2019ASHRAE901TG37.PARKING_AREA_INTERIOR + for space in find_all("$.spaces[*]", zone) + ] + ) + + zone_list = find_all("$.buildings[*].building_segments[*].zones[*]", rmd) + conditioned_zone_list = filter(is_zone_conditioned, zone_list) + no_parking_conditioned_zone_list = filter( + any_space_in_zone_parking_garage, conditioned_zone_list + ) + floor_names = [ + zone["floor_name"] + # remove None + for zone in no_parking_conditioned_zone_list + if zone.get("floor_name") + ] + number_of_floors = len(list(set(floor_names))) + + return number_of_floors diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_number_of_floors_test.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_number_of_floors_test.py new file mode 100644 index 0000000000..a5cfc28724 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_number_of_floors_test.py @@ -0,0 +1,159 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.get_number_of_floors import ( + get_number_of_floors, +) +from rct229.schema.schema_utils import quantify_rmd +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD_HAS_NOF = { + "id": "test_rmd", + "buildings": [ + { + "id": "Building 1", + "number_of_floors_above_grade": 1, + "number_of_floors_below_grade": 1, + }, + ], + "type": "BASELINE_0", +} + +TEST_RMD_FLOOR_NAMES = { + "id": "ASHRAE229", + "ruleset_model_descriptions": [ + { + "id": "rmd 1", + "buildings": [ + { + "id": "Building 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Zone 1", + "floor_name": "test floor 1", + "spaces": [ + { + "id": "Space 1", + "lighting_space_type": "DWELLING_UNIT", + "floor_area": 10, + } + ], + "terminals": [ + { + "id": "Terminal 1", + "served_by_heating_ventilating_air_conditioning_system": "HVAC 1", + } + ], + }, + { + "id": "Zone 2", + "floor_name": "test floor 2", + "spaces": [ + { + "id": "Space 2", + "lighting_space_type": "DWELLING_UNIT", + "floor_area": 4, + } + ], + "terminals": [ + { + "id": "Terminal 2", + "served_by_heating_ventilating_air_conditioning_system": "HVAC 1", + } + ], + }, + { + "id": "Zone 3", + "floor_name": "test floor 3", + "spaces": [ + { + "id": "Space 3", + "lighting_space_type": "PARKING_AREA_INTERIOR", + "floor_area": 4, + } + ], + "terminals": [ + { + "id": "Terminal 3", + "served_by_heating_ventilating_air_conditioning_system": "HVAC 1", + } + ], + }, + { + "id": "Zone 4", + "floor_name": "test floor 4", + "spaces": [ + { + "id": "Space 4", + "lighting_space_type": "PARKING_AREA_INTERIOR", + "floor_area": 2, + }, + { + "id": "Space 5", + "lighting_space_type": "DWELLING_UNIT", + "floor_area": 2, + }, + ], + "terminals": [ + { + "id": "Terminal 4", + "served_by_heating_ventilating_air_conditioning_system": "HVAC 1", + } + ], + }, + ], + "heating_ventilating_air_conditioning_systems": [ + { + "id": "HVAC 1", + "cooling_system": { + "id": "Cooling System 1", + "design_sensible_cool_capacity": 17585.0, + }, + "heating_system": { + "id": "Heating System 1", + "design_capacity": 10257.488888888889, + }, + } + ], + } + ], + } + ], + "type": "BASELINE_0", + } + ], +} + + +TEST_RPD_HAS_NOF = { + "id": "229", + "ruleset_model_descriptions": [TEST_RMD_HAS_NOF], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + +TEST_RMD_HAS_NOF_UNIT = quantify_rmd(TEST_RPD_HAS_NOF)["ruleset_model_descriptions"][0] +TEST_RMD_FLOOR_NAMES_UNIT = quantify_rmd(TEST_RMD_FLOOR_NAMES)[ + "ruleset_model_descriptions" +][0] + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RMD_FLOOR_NAMES) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__get_number_of_floors_with_nof(): + assert get_number_of_floors("CZ0A", TEST_RMD_HAS_NOF_UNIT) == 2 + + +def test__get_number_of_floors_with_floor_names(): + assert get_number_of_floors("CZ0A", TEST_RMD_FLOOR_NAMES_UNIT) == 3 diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_predominant_hvac_building_area_type.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_predominant_hvac_building_area_type.py new file mode 100644 index 0000000000..39a0dc31a8 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_predominant_hvac_building_area_type.py @@ -0,0 +1,36 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.get_hvac_building_area_types_and_zones_dict import ( + get_hvac_building_area_types_and_zones_dict, +) + + +def get_predominant_hvac_building_area_type(climate_zone: str, rmd: dict) -> str: + """ + + Sort the building area type and zones and return the building type that has the largest floor area + Used to verify the correct type of HVAC baseline system (or systems) + + Parameters + ---------- + climate_zone str + One of the ClimateZoneOptions2019ASHRAE901 enumerated values + rmd dict + A dictionary representing a ruleset model instance as defined by the ASHRAE229 schema + + Returns + ------- + string, HVAC building area type. + """ + # building_area_types_and_zones_dict is guaranteed non-empty dict. + building_area_types_and_zones_dict = get_hvac_building_area_types_and_zones_dict( + climate_zone, rmd + ) + # the sorted_hvac_building_area_type is a list of tuples that consist of two elements, + # 0 index is the key in the original dict + # 1 index is the value in the original dict + sorted_hvac_building_area_type = sorted( + building_area_types_and_zones_dict.items(), + key=lambda x: x[1]["floor_area"], + reverse=True, + ) + # Taking out the first element (one with the largest floor_area) in the list and its first element (key) + return sorted_hvac_building_area_type[0][0] diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_predominant_hvac_building_area_type_test.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_predominant_hvac_building_area_type_test.py new file mode 100644 index 0000000000..9a2747119f --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_predominant_hvac_building_area_type_test.py @@ -0,0 +1,164 @@ +from copy import deepcopy + +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.get_predominant_hvac_building_area_type import ( + get_predominant_hvac_building_area_type, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.get_zone_conditioning_category_dict import ( + CAPACITY_THRESHOLD as CAPACITY_THRESHOLD_QUANTITY, +) +from rct229.schema.config import ureg +from rct229.schema.schema_utils import quantify_rmd +from rct229.schema.validate import schema_validate_rpd + +POWER_DELTA = 1 +POWER_THRESHOLD_100 = (CAPACITY_THRESHOLD_QUANTITY * 100 * ureg("m2")).to("W").magnitude + +TEST_RMD = { + "id": "test_rmd", + "constructions": [ + { + "id": "construction_1", + "u_factor": 1.2, + } + ], + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Building Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "heating_ventilating_air_conditioning_systems": [ + # directly conditioned zone + { + "id": "hvac_1_1", + "cooling_system": { + "id": "csys_1_1_1", + "design_sensible_cool_capacity": 2 * POWER_THRESHOLD_100 + + POWER_DELTA, + }, + } + ], + "zones": [ + { + "id": "Thermal Zone 1", + "spaces": [ + { + "id": "Space 1", + "floor_area": 100, + "lighting_space_type": "ATRIUM_LOW_MEDIUM", + } + ], + "terminals": [ + { + "id": "Terminal 1-1", + "served_by_heating_ventilating_air_conditioning_system": "hvac_1_1", + } + ], + }, + ], + }, + { + "id": "Building Segment 2", + "heating_ventilating_air_conditioning_systems": [ + # directly conditioned zone + { + "id": "hvac_2_1", + "cooling_system": { + "id": "csys_2_1_1", + "design_sensible_cool_capacity": 2 * POWER_THRESHOLD_100 + + POWER_DELTA, + }, + } + ], + "zones": [ + { + "id": "Thermal Zone 2", + "spaces": [ + { + "id": "Space 2", + "floor_area": 200, + "lighting_space_type": "ATRIUM_LOW_MEDIUM", + } + ], + "terminals": [ + { + "id": "Terminal 2-1", + "served_by_heating_ventilating_air_conditioning_system": "hvac_2_1", + } + ], + }, + ], + }, + { + "id": "Building Segment 3", + "zones": [ + { + "id": "Thermal Zone 3", + "volume": 100, + "spaces": [ + { + "id": "Space 3", + "floor_area": 200, + "lighting_space_type": "LOBBY_HOTEL", + } + ], + "surfaces": [ + # Adds to zone_other_ua + { + "id": "surface_3_1_1", + "adjacent_to": "INTERIOR", + "adjacent_zone": "zone_1_2", # semi-heated + "area": 10, # m2 + "tilt": 90, # wall + "construction": "construction_1", + } + ], + }, + ], + }, + ], + }, + ], + "constructions": [ + { + "id": "construction_1", + "u_factor": 1.2, + } + ], + "type": "BASELINE_0", +} + + +TEST_RPD_FULL = { + "id": "229", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + +TEST_RMD_UNIT = quantify_rmd(TEST_RPD_FULL)["ruleset_model_descriptions"][0] + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__get_predominant_hvac_building_area_type__residential(): + test_rmd_unit_residential = deepcopy(TEST_RMD_UNIT) + test_rmd_unit_residential["buildings"][0]["building_segments"][1][ + "lighting_building_area_type" + ] = "MULTIFAMILY" + assert ( + get_predominant_hvac_building_area_type("CZ4A", test_rmd_unit_residential) + == "RESIDENTIAL" + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_zone_eflh.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_zone_eflh.py new file mode 100644 index 0000000000..7016132eb7 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_zone_eflh.py @@ -0,0 +1,173 @@ +from pydash import flow, map_ +from rct229.rule_engine.rulesets import LeapYear +from rct229.rulesets.ashrae9012022.ruleset_functions.get_list_hvac_systems_associated_with_zone import ( + get_list_hvac_systems_associated_with_zone, +) +from rct229.utils.assertions import assert_ +from rct229.utils.jsonpath_utils import find_all, find_one +from rct229.utils.schedule_utils import ( + get_max_schedule_multiplier_cooling_design_hourly_value_or_default, + get_max_schedule_multiplier_heating_design_hourly_value_or_default, + get_max_schedule_multiplier_hourly_value_or_default, + get_schedule_multiplier_hourly_value_or_default, +) +from rct229.utils.utility_functions import ( + find_exactly_one_hvac_system, + find_exactly_one_zone, +) + +ZONE_OCCUPANTS_RATIO_THRESHOLD = 0.05 + + +def get_zone_eflh(rmd: dict, zone_id: str) -> int: + """ + provides the equivalent full load hours of the zone. Equivalent full load hours are defined as: any hour where + the occupancy fraction is greater than 5% AND the HVAC system is in occupied mode. For this function, + we are recognizing the HVAC system as being in occupied mode if ANY of the HVAC systems serving the zone are in + occupied mode. + + Applicability Note: hvac_system.fan_system.operation_schedule is being used as the HVAC operation + schedule. Therefore, this check will not work for radiant systems or other systems that do not include a fan + + Parameters + ---------- + rmd dict + A dictionary representing a ruleset model description as defined by the ASHRAE229 schema + zone_id str + zone id + + Returns + ------- + flh int a number equal to the total equivalent full load hours for the year + """ + # 1. get data needed for processing + thermal_zone = find_exactly_one_zone(rmd, zone_id) + hvac_systems_list = get_list_hvac_systems_associated_with_zone(rmd, zone_id) + + num_hours = None + for hvac_id in hvac_systems_list: + hvac = find_exactly_one_hvac_system(rmd, hvac_id) + sched_id = find_one("$.fan_system.operating_schedule", hvac) + values = find_one(f'$.schedules[*][?(@.id="{sched_id}")].hourly_values', rmd) + if values: + num_hours = len(values) + break + + if num_hours is None: + for space in find_all("$.spaces[*]", thermal_zone): + sched_id = space.get("occupant_multiplier_schedule") + values = find_one( + f'$.schedules[*][?(@.id="{sched_id}")].hourly_values', rmd + ) + if values: + num_hours = len(values) + break + + if num_hours is None: + num_hours = 8760 # fallback default + + # 2. functions + # get fan operation schedule from an HVAC, + # missing data (fan) is handled and return as [1.0] * num_hours + get_fan_operation_schedule_func = flow( + lambda hvac_id: find_exactly_one_hvac_system(rmd, hvac_id), + lambda hvac: find_one("$.fan_system.operating_schedule", hvac), + lambda operation_schedule_id: find_one( + f'$.schedules[*][?(@.id="{operation_schedule_id}")].hourly_values', rmd + ), + lambda hourly_values: hourly_values if hourly_values else [1.0] * num_hours, + ) + + # 3. Calculating values + # list of list of HVAC annual operation schedule + # [[0,0,0,1,1,1,...], [0,0,0,1,1,1,...]...] + hvac_operation_schedule_list = list( + map(lambda hvac_id: get_fan_operation_schedule_func(hvac_id), hvac_systems_list) + ) + + # make sure all operation schedule has the same hours and they are equal to num_hours + assert_( + all( + map( + lambda schedule: len(schedule) == num_hours, + hvac_operation_schedule_list, + ) + ), + f"Not all HVAC operation schedules have ${num_hours} hours", + ) + + # list of integers that contains the maximum number of occupants per space. + # [10,12,22...] + num_of_occupant_per_space_list = list( + map( + lambda space: max( + get_max_schedule_multiplier_hourly_value_or_default( + rmd, find_one("$.occupant_multiplier_schedule", space), 1.0 + ), + get_max_schedule_multiplier_heating_design_hourly_value_or_default( + rmd, find_one("$.occupant_multiplier_schedule", space), 1.0 + ), + get_max_schedule_multiplier_cooling_design_hourly_value_or_default( + rmd, find_one("$.occupant_multiplier_schedule", space), 1.0 + ), + 1.0, + ) + * find_one("$.number_of_occupants", space, 0.0), + find_all("$.spaces[*]", thermal_zone), + ) + ) + + # sum of the maximum number of occupants + total_zone_occupants = sum(num_of_occupant_per_space_list) + + # list of list of annual hourly_values per space. + # this shall guarantee the num_hours length per hourly_values list. + # [[0,0,0.2,0.2...], [0,0,0.2,0.2...]...] + occupant_annual_hourly_value_per_space_list = list( + map( + lambda space: get_schedule_multiplier_hourly_value_or_default( + rmd, space.get("occupant_multiplier_schedule"), [1.0] * num_hours + ), + find_all("$.spaces[*]", thermal_zone), + ) + ) + + # make sure all operation schedules have the same hours + assert_( + all( + map( + lambda schedule: len(schedule) == num_hours, + occupant_annual_hourly_value_per_space_list, + ) + ), + f"Not all occupant schedules have {num_hours} hours", + ) + + flh = 0 + for hour in range(num_hours): + # at this hour, the total number of occupants from spaces. + occupants_this_hour = sum( + [ + num_occupant * hourly_values[hour] + for num_occupant, hourly_values in zip( + num_of_occupant_per_space_list, + occupant_annual_hourly_value_per_space_list, + ) + ] + ) + + # 0.0 is falsy, 1.0 is truthy + hvac_systems_operational_this_hour = any( + map(lambda schedule: schedule[hour], hvac_operation_schedule_list) + ) + + # Allow plenum as indirectly conditioned zone but has 0.0 occupants. + # In such case, we do not add flh value + if ( + total_zone_occupants > 0 + and occupants_this_hour / total_zone_occupants + > ZONE_OCCUPANTS_RATIO_THRESHOLD + and hvac_systems_operational_this_hour + ): + flh += 1 + return flh diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_zone_eflh_test.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_zone_eflh_test.py new file mode 100644 index 0000000000..771492c82f --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_zone_eflh_test.py @@ -0,0 +1,146 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.get_zone_eflh import ( + get_zone_eflh, +) +from rct229.schema.schema_utils import quantify_rmd +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "schedules": [ + { + "id": "Operation Schedule 1", + "hourly_values": [1.0] * 8760, + "hourly_heating_design_day": [1.0] * 24, + "hourly_cooling_design_day": [1.0] * 24, + }, + { + "id": "Operation Schedule 2", + "hourly_values": [0.0] * 8760, + }, + ], + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "heating_ventilating_air_conditioning_systems": [ + { + "id": "System 1", + "fan_system": { + "id": "Fan System 1", + "operating_schedule": "Operation Schedule 1", + }, + }, + { + "id": "System 2", + "fan_system": { + "id": "Fan System 2", + "operating_schedule": "Operation Schedule 2", + }, + }, + { + "id": "System 3", + "fan_system": { + "id": "Fan System 3", + }, + }, + ], + "zones": [ + { + "id": "Zone 1", + "terminals": [ + { + "id": "terminal_1", + "served_by_heating_ventilating_air_conditioning_system": "System 1", + }, + { + "id": "terminal_2", + "served_by_heating_ventilating_air_conditioning_system": "System 2", + }, + ], + "spaces": [ + { + "id": "Space 1", + "occupant_multiplier_schedule": "Operation Schedule 1", + "number_of_occupants": 5, + }, + { + "id": "Space 2", + "occupant_multiplier_schedule": "Operation Schedule 1", + "number_of_occupants": 5, + }, + { + "id": "Space 3", + "number_of_occupants": 5, + }, + ], + }, + { + # this case, we miss the operation schedule in spaces + # so the occupants are 1.0 constant + "id": "Zone 2", + "terminals": [ + { + "id": "terminal_3", + "served_by_heating_ventilating_air_conditioning_system": "System 2", + }, + ], + "spaces": [ + { + "id": "Space 4", + "number_of_occupants": 5, + }, + ], + }, + { + # this case, we miss the operation schedule in HVAC + # so the HVAC is running constant 1.0 + "id": "Zone 3", + "terminals": [ + { + "id": "terminal_4", + "served_by_heating_ventilating_air_conditioning_system": "System 3", + }, + ], + "spaces": [ + { + "id": "Space 5", + "number_of_occupants": 5, + }, + ], + }, + ], + } + ], + } + ], + "type": "BASELINE_0", +} + + +TEST_RPD_FULL = { + "id": "229", + "ruleset_model_descriptions": [TEST_RMD], +} + +TEST_RMD_UNIT = quantify_rmd(TEST_RPD_FULL)["ruleset_model_descriptions"][0] + + +def test__TEST_RMD_FIXED_TYPE__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__get_zone_eflh_thermal_zone_1__success(): + assert get_zone_eflh(TEST_RMD_UNIT, "Zone 1") == 8760 + + +def test__get_zone_eflh_thermal_zone_2__success(): + assert get_zone_eflh(TEST_RMD_UNIT, "Zone 2") == 0 + + +def test__get_zone_eflh_thermal_zone_3__success(): + assert get_zone_eflh(TEST_RMD_UNIT, "Zone 3") == 8760 diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_zone_hvac_bat.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_zone_hvac_bat.py new file mode 100644 index 0000000000..b2fd6d658e --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_zone_hvac_bat.py @@ -0,0 +1,40 @@ +from pint import Quantity +from rct229.rulesets.ashrae9012022.data_fns.table_lighting_to_hvac_bat_map_fns import ( + space_lighting_to_hvac_bat, +) +from rct229.utils.jsonpath_utils import find_all +from rct229.utils.pint_utils import ZERO +from rct229.utils.utility_functions import find_exactly_one_zone + + +def get_zone_hvac_bat_dict(rmd: dict, zone_id: str) -> dict[str, Quantity]: + """ + Get a dictionary of the HVAC_BAT and areas for a given zone. + - used to verify the correct type of HVAC baseline system (or systems) + - The function looks at the space lighting type. + + Parameters + ---------- + rmd dict + A dictionary representing a ruleset model description as defined by the ASHRAE229 schema + zone_id str + zone id + + Returns + ------- + zone_hvac_bat_dict dict A dict for the zone that saves the HVAC_BAT as keys and the areas as the + values. Example: {OTHER_NON_RESIDENTIAL: 500, PUBLIC_ASSEMBLY: 2000} + + """ + zone_hvac_bat_dict = dict() + zone = find_exactly_one_zone(rmd, zone_id) + for space in find_all("$.spaces[*]", zone): + # set default to None to not fail the data retrieving (space could have no lighting space type) + space_hvac_bat = space_lighting_to_hvac_bat( + space.get("lighting_space_type", "NONE") + ) + # Default missing floor area to ZERO Area + zone_hvac_bat_dict[space_hvac_bat] = zone_hvac_bat_dict.get( + space_hvac_bat, ZERO.AREA + ) + space.get("floor_area", ZERO.AREA) + return zone_hvac_bat_dict diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_zone_hvac_bat_test.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_zone_hvac_bat_test.py new file mode 100644 index 0000000000..17ca9c2c4f --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_zone_hvac_bat_test.py @@ -0,0 +1,73 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.get_zone_hvac_bat import ( + get_zone_hvac_bat_dict, +) +from rct229.schema.config import ureg +from rct229.schema.schema_utils import quantify_rmd +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "Building 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 1", + "spaces": [ + # ATRIUM_LOW_MEDIUM - OTHER_UNDETERMINED + { + "id": "Space 1", + "floor_area": 100, + "lighting_space_type": "ATRIUM_LOW_MEDIUM", + }, + { + "id": "Space 2", + "lighting_space_type": "ATRIUM_LOW_MEDIUM", + }, + # JUDGES_CHAMBERS "OTHER_NON_RESIDENTIAL" + { + "id": "Space 3", + "floor_area": 200, + "lighting_space_type": "JUDGES_CHAMBERS", + }, + # SEATING_AREA_GENERAL PUBLIC_ASSEMBLY + { + "id": "Space 4", + "floor_area": 0, + "lighting_space_type": "SEATING_AREA_GENERAL", + }, + ], + }, + ], + }, + ], + }, + ], + "type": "BASELINE_0", +} + + +TEST_RPD_FULL = { + "id": "229", + "ruleset_model_descriptions": [TEST_RMD], +} + +TEST_RMD_UNIT = quantify_rmd(TEST_RPD_FULL)["ruleset_model_descriptions"][0] + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__zone_hvac_bat__success(): + assert get_zone_hvac_bat_dict(TEST_RMD_UNIT, "Thermal Zone 1") == { + "OTHER_UNDETERMINED": 100 * ureg("m2").to("ft**2"), + "OTHER_NON_RESIDENTIAL": 200 * ureg("m2").to("ft**2"), + "PUBLIC_ASSEMBLY": 0.0 * ureg("ft**2"), + } diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_zone_peak_internal_load_floor_area_dict.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_zone_peak_internal_load_floor_area_dict.py new file mode 100644 index 0000000000..c06a6dae53 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_zone_peak_internal_load_floor_area_dict.py @@ -0,0 +1,99 @@ +from typing import Literal + +from pint import Quantity +from rct229.utils.assertions import getattr_ +from rct229.utils.jsonpath_utils import find_all +from rct229.utils.pint_utils import ZERO +from rct229.utils.utility_functions import ( + find_exactly_one_schedule, + find_exactly_one_zone, +) + + +def get_zone_peak_internal_load_floor_area_dict( + rmd: dict, zone_id: str +) -> dict[Literal["peak", "area"], Quantity]: + """ + Finds the peak coincident internal loads of a zone and returns the value with a load unit. + The function returns a dict giving 2 values: {"peak":total peak (load unit) in the zone, "area":total zone area} the + total peak is the internal non-coincident peak loads in all spaces in the zone + + The function does not raise exception for missing values rather it applies default values. + + Parameters + ---------- + rmd: dict + A dictionary representing a RuleModelInstance object as defined by the ASHRAE229 schema + zone_id: string + zone id + + Returns + ------- + result: dict + a dictionary that contains two keys, peak, and area. + """ + zone = find_exactly_one_zone(rmd, zone_id) + zone_area = ZERO.AREA + zone_load = ZERO.POWER + + for space in find_all("$.spaces[*]", zone): + space_area = space.get("floor_area", ZERO.AREA) + zone_area += space_area + for light in find_all("$.interior_lighting[*]", space): + # default value + lighting_max_schedule_fraction = 1.0 + if light.get("lighting_multiplier_schedule"): + lighting_max_schedule_fraction = max( + getattr_( + find_exactly_one_schedule( + rmd, + light["lighting_multiplier_schedule"], + ), + "Schedule", + "hourly_cooling_design_day", + ) + ) + + zone_load += ( + light.get("power_per_area", ZERO.POWER_PER_AREA) + * space_area + * lighting_max_schedule_fraction + ) + + for equipment in find_all("$.miscellaneous_equipment[*]", space): + # default value + equipment_max_schedule_fraction = 1.0 + if equipment.get("multiplier_schedule"): + equipment_max_schedule_fraction = max( + getattr_( + find_exactly_one_schedule( + rmd, + equipment["multiplier_schedule"], + ), + "Schedule", + "hourly_cooling_design_day", + ) + ) + + zone_load += ( + equipment.get("power", ZERO.POWER) * equipment_max_schedule_fraction + ) + + # allows no occupants data in a zone + occupant_max_schedule_fraction = 1.0 + if space.get("occupant_multiplier_schedule"): + occupant_max_schedule_fraction = max( + getattr_( + find_exactly_one_schedule( + rmd, space["occupant_multiplier_schedule"] + ), + "Schedule", + "hourly_cooling_design_day", + ) + ) + zone_load += ( + space.get("occupant_sensible_heat_gain", ZERO.POWER) + + space.get("occupant_latent_heat_gain", ZERO.POWER) + ) * occupant_max_schedule_fraction + + return {"peak": zone_load, "area": zone_area} diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_zone_peak_internal_load_floor_area_dict_test.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_zone_peak_internal_load_floor_area_dict_test.py new file mode 100644 index 0000000000..967502ecf2 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_zone_peak_internal_load_floor_area_dict_test.py @@ -0,0 +1,158 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.get_zone_peak_internal_load_floor_area_dict import ( + get_zone_peak_internal_load_floor_area_dict, +) +from rct229.schema.config import ureg +from rct229.schema.schema_utils import quantify_rmd +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Building Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + # this zone shall give a total floor area of 500 + # total zone peak load of 1500 W + { + "id": "Thermal Zone 1", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "spaces": [ + { + "id": "space 1", + "floor_area": 500, + "interior_lighting": [ + { + "id": "interior_lighting_1", + "lighting_multiplier_schedule": "lighting_schedule_1", + "power_per_area": 1.0, + } + ], + "miscellaneous_equipment": [ + { + "id": "miscellaneous_equipment_1", + "multiplier_schedule": "miscellaneous_equipment_schedule_1", + "power": 500, + } + ], + "occupant_multiplier_schedule": "occupant_schedule_1", + "occupant_sensible_heat_gain": 125, + "occupant_latent_heat_gain": 125, + } + ], + }, + # this zone shall give a total floor area of 500 + # total zone peak load of 1000 W due to no lights + { + "id": "Thermal Zone 2", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "spaces": [ + { + "id": "space 2", + "floor_area": 500, + "miscellaneous_equipment": [ + { + "id": "miscellaneous_equipment_1", + "multiplier_schedule": "miscellaneous_equipment_schedule_1", + "power": 500, + } + ], + "occupant_multiplier_schedule": "occupant_schedule_1", + "occupant_sensible_heat_gain": 125, + "occupant_latent_heat_gain": 125, + } + ], + }, + # this zone shall give a total floor area of 500 + # total zone peak load of 1000 W due to no occupants + { + "id": "Thermal Zone 3", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "spaces": [ + { + "id": "space 3", + "floor_area": 500, + "interior_lighting": [ + { + "id": "interior_lighting_1", + "lighting_multiplier_schedule": "lighting_schedule_1", + "power_per_area": 1.0, + } + ], + "miscellaneous_equipment": [ + { + "id": "miscellaneous_equipment_1", + "multiplier_schedule": "miscellaneous_equipment_schedule_1", + "power": 500, + } + ], + } + ], + }, + { + # this zone has no space, space area is 0 + # total zone peak load of 0 W + "id": "Thermal Zone 4", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + }, + ], + }, + ], + } + ], + "schedules": [ + {"id": "lighting_schedule_1", "hourly_cooling_design_day": [1] * 24}, + { + "id": "miscellaneous_equipment_schedule_1", + "hourly_cooling_design_day": [1] * 24, + }, + {"id": "occupant_schedule_1", "hourly_cooling_design_day": [1] * 23 + [2]}, + ], + "type": "BASELINE_0", +} + + +TEST_RPD_FULL = { + "id": "229", + "ruleset_model_descriptions": [TEST_RMD], +} + +TEST_RMD = quantify_rmd(TEST_RPD_FULL)["ruleset_model_descriptions"][0] + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__get_zone_peak_internal_load_floor_area_dict__all_three_component(): + zone_info = get_zone_peak_internal_load_floor_area_dict(TEST_RMD, "Thermal Zone 1") + assert abs(zone_info["peak"] - 1500 * ureg("watt")) < 0.00001 * ureg("watt") + assert abs(zone_info["area"] - 500 * ureg("m2")) < 0.00001 * ureg("m2") + + +def test__get_zone_peak_internal_load_floor_area_dict__no_lighting_component(): + zone_info = get_zone_peak_internal_load_floor_area_dict(TEST_RMD, "Thermal Zone 2") + assert abs(zone_info["peak"] - 1000 * ureg("watt")) < 0.00001 * ureg("watt") + assert abs(zone_info["area"] - 500 * ureg("m2")) < 0.00001 * ureg("m2") + + +def test__get_zone_peak_internal_load_floor_area_dict__no_occupants_component(): + zone_info = get_zone_peak_internal_load_floor_area_dict(TEST_RMD, "Thermal Zone 3") + assert abs(zone_info["peak"] - 1000 * ureg("watt")) < 0.00001 * ureg("watt") + assert abs(zone_info["area"] - 500 * ureg("m2")) < 0.00001 * ureg("m2") + + +def test__get_zone_peak_internal_load_floor_area_dict__no_space_component(): + zone_info = get_zone_peak_internal_load_floor_area_dict(TEST_RMD, "Thermal Zone 4") + assert abs(zone_info["peak"] - 0 * ureg("watt")) < 0.00001 * ureg("watt") + assert abs(zone_info["area"] - 0 * ureg("m2")) < 0.00001 * ureg("m2") diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_zones_computer_rooms.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_zones_computer_rooms.py new file mode 100644 index 0000000000..92a8b2f4ac --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_zones_computer_rooms.py @@ -0,0 +1,68 @@ +from typing import Literal + +from pint import Quantity +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.is_space_a_computer_room import ( + is_space_a_computer_room, +) +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.jsonpath_utils import find_all +from rct229.utils.pint_utils import ZERO + +LightingSpaceOptions2019ASHRAE901TG37 = SchemaEnums.schema_enums[ + "LightingSpaceOptions2019ASHRAE901TG37" +] + + +def get_zone_computer_rooms( + rmd: dict, +) -> dict[ + str, + dict[Literal["total_zone_floor_area", "zone_computer_room_floor_area"], Quantity], +]: + """ + Returns a dictionary with the zones that have at least one computer room space associated with them in the rmd as the keys. + The values associated with each key are in a dict form. The dict associated with each key contains the computer room + floor area as the first item and the total zone floor area as the second item. + + Parameters + ---------- + rmd dict + A dictionary representing a RuleModelDescription object as defined by the ASHRAE229 schema + + Returns + ------- + zones_with_computer_room_dict + A dictionary with the zones that have at least one computer room space associated with them in the rmd as the keys. + The values associated with each key are in a dict form. The dict associated with each key contains the computer room + floor area as the first item and the total zone floor area as the second item. + """ + + zone_with_computer_room_dict = {} + for zone in find_all("$.buildings[*].building_segments[*].zones[*]", rmd): + zone_has_computer_room_check = any( + [ + is_space_a_computer_room(rmd, space["id"]) + for space in find_all("$.spaces[*]", zone) + ] + ) + + if zone_has_computer_room_check: + zone_with_computer_room_dict[zone["id"]] = { + "zone_computer_room_floor_area": sum( + [ + space.get("floor_area", ZERO.AREA) + for space in find_all("$.spaces[*]", zone) + if is_space_a_computer_room(rmd, space["id"]) + ], + ZERO.AREA, + ), + "total_zone_floor_area": sum( + [ + space.get("floor_area", ZERO.AREA) + for space in find_all("$.spaces[*]", zone) + ], + ZERO.AREA, + ), + } + + return zone_with_computer_room_dict diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_zones_computer_rooms_test.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_zones_computer_rooms_test.py new file mode 100644 index 0000000000..d3c09e9920 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_zones_computer_rooms_test.py @@ -0,0 +1,103 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.get_zones_computer_rooms import ( + get_zone_computer_rooms, +) +from rct229.schema.config import ureg +from rct229.schema.schema_utils import quantify_rmd +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "schedules": [{"id": "schedule_1", "hourly_values": [1.2] * 8670}], + "buildings": [ + { + "id": "Building 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 1", + "spaces": [ + { + "id": "Space 1", + "lighting_space_type": "COMPUTER_ROOM", + "floor_area": 100, + }, + { + "id": "Space 2", + "floor_area": 100, + }, + ], + }, + { + "id": "Thermal Zone 2", + "spaces": [ + { + "id": "Space 3", + "floor_area": 10, + "miscellaneous_equipment": [ + { + "id": "misc_equipment 1", + "power": 800, + "energy_type": "ELECTRICITY", + "multiplier_schedule": "schedule_1", + "type": "INFORMATION_TECHNOLOGY_EQUIPMENT", + }, + { + "id": "misc_equipment 2", + "power": 1500, + "energy_type": "ELECTRICITY", + "multiplier_schedule": "schedule_1", + "type": "INFORMATION_TECHNOLOGY_EQUIPMENT", + }, + ], + }, + { + "id": "Space 4", + "floor_area": 10, + }, + ], + }, + ], + }, + ], + }, + ], + "type": "BASELINE_0", +} + + +TEST_RPD_FULL = { + "id": "229", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + +TEST_RMD_UNIT = quantify_rmd(TEST_RPD_FULL)["ruleset_model_descriptions"][0] + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__get_zones_computer_rooms__success(): + assert get_zone_computer_rooms(TEST_RMD_UNIT) == { + "Thermal Zone 1": { + "zone_computer_room_floor_area": 100 * ureg("m2"), + "total_zone_floor_area": 200 * ureg("m2"), + }, + "Thermal Zone 2": { + "zone_computer_room_floor_area": 10 * ureg("m2"), + "total_zone_floor_area": 20 * ureg("m2"), + }, + } diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_zones_on_same_floor_list.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_zones_on_same_floor_list.py new file mode 100644 index 0000000000..a56dbdaa93 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_zones_on_same_floor_list.py @@ -0,0 +1,29 @@ +from rct229.utils.jsonpath_utils import find_all, find_one +from rct229.utils.utility_functions import find_exactly_one_zone + + +def get_zones_on_same_floor_list(rmd: dict, source_zone_id: str) -> list[str]: + """ + Provides a list of zone ids that are on the floor as the source zone (including the source zone id) + + Parameters + ---------- + rmd dict + A dictionary representing a RuleModelInstance object as defined by the ASHRAE229 schema + + source_zone_id string + The zone id for which we want to find all other zones on the same floor + + Returns + ------- + a list of zone ids that are on the same floor as the starting zone. The list will include the starting zone. + """ + # not to raise exception if the zone is missing a floor_name + # This will still result an empty list if no floor_name is found. + source_zone_floor_name = find_one( + "$.floor_name", find_exactly_one_zone(rmd, source_zone_id) + ) + return find_all( + f'$.buildings[*].building_segments[*].zones[*][?(@.floor_name="{source_zone_floor_name}")].id', + rmd, + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_zones_on_same_floor_list_test.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_zones_on_same_floor_list_test.py new file mode 100644 index 0000000000..b96bb5e3fe --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/get_zones_on_same_floor_list_test.py @@ -0,0 +1,54 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.get_zones_on_same_floor_list import ( + get_zones_on_same_floor_list, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "zones": [ + {"id": "zone 1", "floor_name": "floor 1"}, + {"id": "zone 2", "floor_name": "floor 2"}, + ], + }, + { + "id": "building_segment_2", + "zones": [ + {"id": "zone 2-1", "floor_name": "floor 1"}, + {"id": "zone 2-2", "floor_name": "floor 1"}, + ], + }, + ], + } + ], + "type": "BASELINE_0", +} + +TEST_RPD = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], +} + + +def test__TEST_RPD_FIXED_TYPE__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__get_zones_on_floor_1__success(): + assert get_zones_on_same_floor_list(TEST_RMD, "zone 1") == [ + "zone 1", + "zone 2-1", + "zone 2-2", + ] + + +def test__get_zones_on_floor_2__success(): + assert get_zones_on_same_floor_list(TEST_RMD, "zone 2") == ["zone 2"] diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/is_space_a_computer_room.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/is_space_a_computer_room.py new file mode 100644 index 0000000000..0f926c6844 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/is_space_a_computer_room.py @@ -0,0 +1,70 @@ +from rct229.schema.config import ureg +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.assertions import assert_, getattr_ +from rct229.utils.jsonpath_utils import find_all +from rct229.utils.pint_utils import ZERO +from rct229.utils.schedule_utils import ( + get_max_schedule_multiplier_hourly_value_or_default, +) +from rct229.utils.utility_functions import find_exactly_one_space + +COMPUTER_ROOM_MISC_POWER_DENSITY_THRESHOLD = 20 * ureg("watt/ft2") + +LightingSpaceOptions2019ASHRAE901TG37 = SchemaEnums.schema_enums[ + "LightingSpaceOptions2019ASHRAE901TG37" +] +MISCELLANEOUS_EQUIPMENT = SchemaEnums.schema_enums["MiscellaneousEquipmentOptions"] + +EnergySourceOptions = SchemaEnums.schema_enums["EnergySourceOptions"] + + +def is_space_a_computer_room(rmd: dict, space_id: str) -> bool: + """ + Returns true or false as to whether space is a computer room. The criteria is such that it is considered a computer room if the total of misc INFORMATION_TECHNOLOGY_EQUIPMENT Power density in W/sf exceeds 20 W/sf per the definition of a computer room in 90.1 Section 3. + + Parameters + ---------- + rmd: dict + RMD at RuleSetModelDescription level + space_id: str + space id + + Returns + ------- + is_space_a_computer_room_flag: bool + The function returns true or false as to whether space is a computer room. The criteria is such that it is considered a computer room if the total of misc INFORMATION_TECHNOLOGY_EQUIPMENT Power density in W/sf exceeds 20 W/sf per the definition of a computer room in 90.1 Section 3. + """ + space = find_exactly_one_space(rmd, space_id) + is_space_a_computer_room_flag = ( + space.get("lighting_space_type") + == LightingSpaceOptions2019ASHRAE901TG37.COMPUTER_ROOM + ) + + if not is_space_a_computer_room_flag: + total_space_misc_wattage_including_multiplier = sum( + [ + misc_equip.get("power", ZERO.POWER) + * max( + 1.0, + get_max_schedule_multiplier_hourly_value_or_default( + rmd, misc_equip.get("multiplier_schedule"), 1.0 + ), + ) + for misc_equip in find_all("$.miscellaneous_equipment[*]", space) + if misc_equip.get("energy_type") == EnergySourceOptions.ELECTRICITY + and misc_equip.get("type") + == MISCELLANEOUS_EQUIPMENT.INFORMATION_TECHNOLOGY_EQUIPMENT + ], + ZERO.POWER, + ) + + space_floor_area = getattr_(space, "spaces", "floor_area") + # exception handling if the space has zero floor area + assert_(space_floor_area > ZERO.AREA, f"Space {space_id} has zero floor area") + + space_epd = total_space_misc_wattage_including_multiplier / space_floor_area + is_space_a_computer_room_flag = ( + space_epd > COMPUTER_ROOM_MISC_POWER_DENSITY_THRESHOLD + ) + + return is_space_a_computer_room_flag diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/is_space_a_computer_room_test.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/is_space_a_computer_room_test.py new file mode 100644 index 0000000000..28413a932f --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/is_space_a_computer_room_test.py @@ -0,0 +1,98 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.is_space_a_computer_room import ( + is_space_a_computer_room, +) +from rct229.schema.schema_utils import quantify_rmd +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "schedules": [{"id": "schedule_1", "hourly_values": [1.2] * 8670}], + "buildings": [ + { + "id": "Building 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 1", + "spaces": [ + { + "id": "Space 1", + "function": "LABORATORY", + "lighting_space_type": "COMPUTER_ROOM", + "floor_area": 100, + }, + { + "id": "Space 2", + "floor_area": 10, + "miscellaneous_equipment": [ + { + "id": "misc_equipment 1", + "energy_type": "ELECTRICITY", + "power": 1000, + "type": "INFORMATION_TECHNOLOGY_EQUIPMENT", + }, + { + "id": "misc_equipment 2", + "energy_type": "ELECTRICITY", + "power": 2000, + "type": "INFORMATION_TECHNOLOGY_EQUIPMENT", + }, + ], + }, + { + # max multiplier is 0.7 - does not meet requirements (19W/ft2) + "id": "Space 3", + "floor_area": 10, + "miscellaneous_equipment": [ + { + "id": "misc_equipment 1", + "power": 600, + "energy_type": "ELECTRICITY", + "multiplier_schedule": "schedule_1", + }, + { + "id": "misc_equipment 2", + "power": 1000, + "energy_type": "ELECTRICITY", + "multiplier_schedule": "schedule_1", + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + "type": "BASELINE_0", +} + + +TEST_RPD_FULL = { + "id": "229", + "ruleset_model_descriptions": [TEST_RMD], +} + +TEST_RMD_UNIT = quantify_rmd(TEST_RPD_FULL)["ruleset_model_descriptions"][0] + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__is_space_a_compuetr_room_space_type_computer_room__success(): + assert is_space_a_computer_room(TEST_RMD_UNIT, "Space 1") == True + + +def test__is_space_a_compuetr_room_no_schedule_power_over_threshold__success(): + assert is_space_a_computer_room(TEST_RMD_UNIT, "Space 2") == True + + +def test__is_space_a_compuetr_room_has_schedules_power_below_threshold__failed(): + assert is_space_a_computer_room(TEST_RMD_UNIT, "Space 3") == False diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/is_zone_likely_a_vestibule.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/is_zone_likely_a_vestibule.py new file mode 100644 index 0000000000..2afaa06714 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/is_zone_likely_a_vestibule.py @@ -0,0 +1,111 @@ +from pydash import filter_, flat_map +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.get_zones_on_same_floor_list import ( + get_zones_on_same_floor_list, +) +from rct229.schema.config import ureg +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.jsonpath_utils import find_all +from rct229.utils.pint_utils import ZERO +from rct229.utils.utility_functions import find_exactly_one_zone + +LIGHTING_SPACE_OPTIONS = SchemaEnums.schema_enums[ + "LightingSpaceOptions2019ASHRAE901TG37" +] +SURFACE_ADJACENT_TO_OPTIONS = SchemaEnums.schema_enums["SurfaceAdjacencyOptions"] + +SUBSURFACE_CLASSIFICATION_OPTIONS = SchemaEnums.schema_enums[ + "SubsurfaceClassificationOptions" +] + + +ALLOWABLE_SPACE_LIGHTING_TYPES = [ + LIGHTING_SPACE_OPTIONS.CORRIDOR_FACILITY_FOR_THE_VISUALLY_IMPAIRED, + LIGHTING_SPACE_OPTIONS.CORRIDOR_HOSPITAL, + LIGHTING_SPACE_OPTIONS.CORRIDOR_ALL_OTHERS, + LIGHTING_SPACE_OPTIONS.LOBBY_FACILITY_FOR_THE_VISUALLY_IMPAIRED, + LIGHTING_SPACE_OPTIONS.LOBBY_HOTEL, + LIGHTING_SPACE_OPTIONS.LOBBY_MOTION_PICTURE_THEATER, + LIGHTING_SPACE_OPTIONS.LOBBY_PERFORMING_ARTS_THEATER, + LIGHTING_SPACE_OPTIONS.LOBBY_ALL_OTHERS, + LIGHTING_SPACE_OPTIONS.STAIRWELL, +] + +VESTIBULE_AREA_THRESHOLD = 50 * ureg("ft^2") +VESTIBULE_AREA_MULTIPLIER_THRESHOLD = 0.2 + + +def is_zone_likely_a_vestibule(rmd: dict, zone_id: str) -> bool: + """ + following the guidelines in ASHRAE that a vestibule is defined as a sapce with at least one exterior door and with a surface area of no more than the greater of 50ft2 or 2% of the total area of the floor. There is no 100% check for a vestibule, so a space that meets these requirements and also has only 6 surfaces (floor, ceiling and 4 walls) will return False + + Parameters + ---------- + rmd: dict + A dictionary representing a RuleModelInstance object as defined by the ASHRAE229 schema + zone_id: string + zone id + + Returns + ------- + boolean, False means NO, True means MAYBE + + """ + zone = find_exactly_one_zone(rmd, zone_id) + + # The RDS interpretation is: + # The flag is True when all spaces has either no lighting_space_type data or space.get("lighting_space_type") is in + # ALLOWABLE_SPACE_LIGHTING_TYPES + is_likely_a_vestibule = all( + [ + # this covers both Null and in allowable list + space.get("lighting_space_type") is None + or space.get("lighting_space_type") in ALLOWABLE_SPACE_LIGHTING_TYPES + for space in find_all("$.spaces[*]", zone) + ] + ) + + if is_likely_a_vestibule: + exterior_surfaces = filter_( + find_all("$.surfaces[*]", zone), + {"adjacent_to": SURFACE_ADJACENT_TO_OPTIONS.EXTERIOR}, + ) + exterior_subsurfaces = flat_map( + exterior_surfaces, lambda surface: find_all("$.subsurfaces[*]", surface) + ) + exterior_doors = filter_( + exterior_subsurfaces, + {"classification": SUBSURFACE_CLASSIFICATION_OPTIONS.DOOR}, + ) + exterior_door_surface_area = sum( + [ + door.get("glazed_area", ZERO.AREA) + door.get("opaque_area", ZERO.AREA) + for door in exterior_doors + ], + ZERO.AREA, + ) + + zone_ids_on_same_floor = get_zones_on_same_floor_list(rmd, zone_id) + spaces_on_same_floor = flat_map( + zone_ids_on_same_floor, + lambda zone_id: find_all( + "$.spaces[*]", find_exactly_one_zone(rmd, zone_id) + ), + ) + + floor_area = sum( + [space.get("floor_area", ZERO.AREA) for space in spaces_on_same_floor], + ZERO.AREA, + ) + + zone_area = sum(find_all("$.spaces[*].floor_area", zone), ZERO.AREA) + + is_likely_a_vestibule = ( + exterior_door_surface_area > ZERO.AREA + and zone_area + <= max( + VESTIBULE_AREA_THRESHOLD, + VESTIBULE_AREA_MULTIPLIER_THRESHOLD * floor_area, + ) + ) + + return is_likely_a_vestibule diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/is_zone_likely_a_vestibule_test.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/is_zone_likely_a_vestibule_test.py new file mode 100644 index 0000000000..9e5f1568f4 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/is_zone_likely_a_vestibule_test.py @@ -0,0 +1,125 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.is_zone_likely_a_vestibule import ( + is_zone_likely_a_vestibule, +) +from rct229.schema.schema_utils import quantify_rmd +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "schedules": [{"id": "schedule_1", "hourly_values": [1.2] * 8670}], + "buildings": [ + { + "id": "Building 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 1", + "floor_name": "FLOOR 1", + # case has no other data provided - likely not a vestibule + "spaces": [ + { + "id": "Space 1", + "floor_area": 1000, + }, + { + "id": "Space 2", + "floor_area": 100, + }, + ], + }, + { + # case has door but the space area is greater than 50 or 2% of the floor area + "id": "Thermal Zone 2", + "floor_name": "FLOOR 1", + "spaces": [ + { + "id": "Space 3", + "floor_area": 1000, + }, + ], + "surfaces": [ + { + "id": "surface 1", + "adjacent_to": "EXTERIOR", + "subsurfaces": [ + { + "id": "subsurface 1", + "classification": "DOOR", + "glazed_area": 10, + "opaque_area": 2, + }, + { + "id": "subsurface 2", + "classification": "WINDOW", + "glazed_area": 10, + "opaque_area": 2, + }, + ], + }, + { + "id": "surface 1", + "adjacent_to": "INTERIOR", + }, + ], + }, + { + # case has door but the space area is greater than 50 or 2% of the floor area + "id": "Thermal Zone 3", + "floor_name": "FLOOR 1", + "spaces": [ + { + "id": "Space 4", + "floor_area": 5, + }, + ], + "surfaces": [ + { + "id": "surface 2", + "adjacent_to": "EXTERIOR", + "subsurfaces": [ + { + "id": "subsurface 3", + "classification": "DOOR", + "glazed_area": 10, + "opaque_area": 2, + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + "type": "BASELINE_0", +} + + +TEST_RPD_FULL = { + "id": "229", + "ruleset_model_descriptions": [TEST_RMD], +} + +TEST_RMD_UNIT = quantify_rmd(TEST_RPD_FULL)["ruleset_model_descriptions"][0] + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test_is_zone_likely_a_vestibule_thermal_zone_1__success(): + assert is_zone_likely_a_vestibule(TEST_RMD_UNIT, "Thermal Zone 1") is False + + +def test_is_zone_likely_a_vestibule_thermal_zone_2__success(): + assert is_zone_likely_a_vestibule(TEST_RMD_UNIT, "Thermal Zone 2") is False + + +def test_is_zone_likely_a_vestibule_thermal_zone_3__success(): + assert is_zone_likely_a_vestibule(TEST_RMD_UNIT, "Thermal Zone 3") is True diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/is_zone_mechanically_cooled.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/is_zone_mechanically_cooled.py new file mode 100644 index 0000000000..1a74f475fc --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/is_zone_mechanically_cooled.py @@ -0,0 +1,76 @@ +from pydash import flat_map +from rct229.rulesets.ashrae9012022.ruleset_functions.get_list_hvac_systems_associated_with_zone import ( + get_list_hvac_systems_associated_with_zone, +) +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 +from rct229.utils.utility_functions import ( + find_exactly_one_hvac_system, + find_exactly_one_zone, +) + +CoolingSystemOptions = SchemaEnums.schema_enums["CoolingSystemOptions"] +CoolingSourceOptions = SchemaEnums.schema_enums["CoolingSourceOptions"] + + +def is_zone_mechanically_cooled(rmd: dict, zone_id: str) -> bool: + """ + Function determines whether a zone is cooled. Checks for transfer air + + Parameters + ---------- + rmd dict + A dictionary representing a ruleset model description as defined by the ASHRAE229 schema + zone_id str + zone id + + Returns + ------- + Boolean True if it is determined to be cooled, False otherwise. + """ + list_hvac_system_ids = get_list_hvac_systems_associated_with_zone(rmd, zone_id) + + def does_hvac_has_cooling_sys(hvac_system_id: str) -> bool: + hvac = find_exactly_one_hvac_system(rmd, hvac_system_id) + cooling_type = find_one("$.cooling_system.type", hvac) + return cooling_type not in [None, CoolingSourceOptions.NONE] + + def does_zone_terminals_have_cooling_type(thermal_zone_id: str) -> bool: + thermal_zone = find_exactly_one_zone(rmd, thermal_zone_id) + terminal_list = find_all("$.terminals[*]", thermal_zone) + return any( + [ + find_one("$.cooling_source", terminal) + not in [None, CoolingSourceOptions.NONE] + for terminal in terminal_list + ] + ) + + has_cooling_system = any( + flat_map( + list_hvac_system_ids, + lambda hvac_system_id: does_hvac_has_cooling_sys(hvac_system_id), + ) + ) or does_zone_terminals_have_cooling_type(zone_id) + + if not has_cooling_system: + zone = find_exactly_one_zone(rmd, zone_id) + if zone.get("transfer_airflow_rate", ZERO.FLOW) > ZERO.FLOW: + # in this case, we are checking the source zone + transfer_source_zone_id = getattr_( + zone, "Zone", "transfer_airflow_source_zone" + ) + # get the HVAC system list from the source zone + list_hvac_system_ids = get_list_hvac_systems_associated_with_zone( + rmd, transfer_source_zone_id + ) + has_cooling_system = any( + flat_map( + list_hvac_system_ids, + lambda hvac_system_id: does_hvac_has_cooling_sys(hvac_system_id), + ) + ) or does_zone_terminals_have_cooling_type(transfer_source_zone_id) + + return has_cooling_system diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/is_zone_mechanically_cooled_test.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/is_zone_mechanically_cooled_test.py new file mode 100644 index 0000000000..d2894ac54e --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/is_zone_mechanically_cooled_test.py @@ -0,0 +1,102 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.is_zone_mechanically_cooled import ( + is_zone_mechanically_cooled, +) +from rct229.schema.schema_utils import quantify_rmd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "heating_ventilating_air_conditioning_systems": [ + { + "id": "System 1", + "cooling_system": { + "id": "csys_1_1_1", + "type": "NON_MECHANICAL", + }, + }, + { + "id": "System 2", + "cooling_system": { + "id": "csys_2_1_1", + }, + }, + ], + "zones": [ + { + "id": "zone 1", + "terminals": [ + { + "id": "terminal_1", + "served_by_heating_ventilating_air_conditioning_system": "System 1", + }, + ], + }, + { + "id": "zone 2", + "terminals": [ + { + "id": "terminal_2_1", + "cooling_source": "NONE", + "served_by_heating_ventilating_air_conditioning_system": "System 2", + }, + { + "id": "terminal_2_2", + "cooling_source": "CHILLED_WATER", + "served_by_heating_ventilating_air_conditioning_system": "System 2", + }, + ], + }, + { + "id": "zone 3", + "transfer_airflow_rate": 1000, + "transfer_airflow_source_zone": "zone 2", + "terminals": [ + { + "id": "terminal_3_1", + "served_by_heating_ventilating_air_conditioning_system": "System 2", + }, + { + "id": "terminal_3_2", + "served_by_heating_ventilating_air_conditioning_system": "System 2", + }, + ], + }, + ], + } + ], + } + ], + "type": "BASELINE_0", +} + +TEST_RPD_FULL = { + "id": "229", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + +TEST_RMD_UNIT = quantify_rmd(TEST_RPD_FULL)["ruleset_model_descriptions"][0] + + +def test_is_zone_mechanically_cooled_zone_1__success(): + assert is_zone_mechanically_cooled(TEST_RMD_UNIT, "zone 1") == True + + +def test_is_zone_mechanically_cooled_zone_2__success(): + assert is_zone_mechanically_cooled(TEST_RMD_UNIT, "zone 2") == True + + +def test_is_zone_mechanically_cooled_zone_3__success(): + assert is_zone_mechanically_cooled(TEST_RMD_UNIT, "zone 3") == True diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/is_zone_mechanically_heated_and_not_cooled.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/is_zone_mechanically_heated_and_not_cooled.py new file mode 100644 index 0000000000..67d39f2b86 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/is_zone_mechanically_heated_and_not_cooled.py @@ -0,0 +1,62 @@ +from pydash import flat_map +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.is_zone_mechanically_cooled import ( + is_zone_mechanically_cooled, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.get_list_hvac_systems_associated_with_zone import ( + get_list_hvac_systems_associated_with_zone, +) +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.jsonpath_utils import find_all, find_one +from rct229.utils.utility_functions import ( + find_exactly_one_hvac_system, + find_exactly_one_zone, +) + +HeatingSystemOptions = SchemaEnums.schema_enums["HeatingSystemOptions"] +HeatingSourceOptions = SchemaEnums.schema_enums["HeatingSourceOptions"] + + +def is_zone_mechanically_heated_and_not_cooled(rmd: dict, zone_id: str) -> bool: + """ + Determines whether a zone is mechanically heated, but not cooled. Checks for transfer air + + Parameters + ---------- + rmd dict + A dictionary representing a ruleset model description as defined by the ASHRAE229 schema + zone_id str + zone id + + Returns + ------- + Boolean True if it is determined to be heated, but not cooled, False otherwise. + """ + list_hvac_system_ids = get_list_hvac_systems_associated_with_zone(rmd, zone_id) + + def does_hvac_has_heating_sys(hvac_system_id: str) -> bool: + hvac = find_exactly_one_hvac_system(rmd, hvac_system_id) + heating_type = find_one("$.heating_system.type", hvac) + return heating_type not in [None, HeatingSourceOptions.NONE] + + def does_zone_terminals_have_heating_type(thermal_zone_id: str) -> bool: + thermal_zone = find_exactly_one_zone(rmd, thermal_zone_id) + terminal_list = find_all("$.terminals[*]", thermal_zone) + return any( + [ + find_one("$.heating_source", terminal) + not in [None, HeatingSourceOptions.NONE] + for terminal in terminal_list + ] + ) + + is_heated = any( + flat_map( + list_hvac_system_ids, + lambda hvac_system_id: does_hvac_has_heating_sys(hvac_system_id), + ) + ) or does_zone_terminals_have_heating_type(zone_id) + + # Check if a zone is mechanically cooled + is_cooled = is_zone_mechanically_cooled(rmd, zone_id) + + return is_heated and not is_cooled diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/is_zone_mechanically_heated_and_not_cooled_test.py b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/is_zone_mechanically_heated_and_not_cooled_test.py new file mode 100644 index 0000000000..7223584892 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/g3212_exceptions/g3212_sub_functions/is_zone_mechanically_heated_and_not_cooled_test.py @@ -0,0 +1,139 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.is_zone_mechanically_heated_and_not_cooled import ( + is_zone_mechanically_heated_and_not_cooled, +) +from rct229.schema.schema_utils import quantify_rmd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "heating_ventilating_air_conditioning_systems": [ + { + "id": "System 1", + "cooling_system": { + "id": "csys_1_1_1", + "type": "NON_MECHANICAL", + }, + "heating_system": { + "id": "csys_1_1_2", + "type": "ELECTRIC_RESISTANCE", + }, + }, + { + "id": "System 2", + "cooling_system": { + "id": "csys_2_1_1", + }, + "heating_system": {"id": "csys_2_1_2", "type": "NONE"}, + }, + { + "id": "System 3", + "heating_system": {"id": "csys_3_1_2", "type": "NONE"}, + }, + { + "id": "System 4", + "heating_system": {"id": "csys_4_1_2", "type": "FURNACE"}, + }, + ], + "zones": [ + # Has cooling system, has heating system - return False + { + "id": "zone 1", + "terminals": [ + { + "id": "terminal_1", + "served_by_heating_ventilating_air_conditioning_system": "System 1", + }, + ], + }, + { + # Has cooling system, has no heating system - return False + "id": "zone 2", + "terminals": [ + { + "id": "terminal_2_1", + "cooling_source": "NONE", + "served_by_heating_ventilating_air_conditioning_system": "System 2", + }, + { + "id": "terminal_2_2", + "cooling_source": "CHILLED_WATER", + "served_by_heating_ventilating_air_conditioning_system": "System 2", + }, + ], + }, + { + # Has cooling system (through transfer air), has no heating system - return False + "id": "zone 3", + "transfer_airflow_rate": 1000, + "transfer_airflow_source_zone": "zone 2", + "terminals": [ + { + "id": "terminal_3_1", + "served_by_heating_ventilating_air_conditioning_system": "System 2", + }, + { + "id": "terminal_3_2", + "served_by_heating_ventilating_air_conditioning_system": "System 2", + }, + ], + }, + { + # Has no cooling system, heating system from terminal - return True + "id": "zone 4", + "terminals": [ + { + "id": "terminal_4_1", + "heating_source": "HOT_WATER", + "served_by_heating_ventilating_air_conditioning_system": "System 3", + }, + ], + }, + { + # Has no cooling system, heating system from system - return True + "id": "zone 5", + "terminals": [ + { + "id": "terminal_5_1", + "served_by_heating_ventilating_air_conditioning_system": "System 4", + }, + ], + }, + ], + } + ], + } + ], + "type": "BASELINE_0", +} + +TEST_RPD_FULL = { + "id": "229", + "ruleset_model_descriptions": [TEST_RMD], +} + +TEST_RMD_UNIT = quantify_rmd(TEST_RPD_FULL)["ruleset_model_descriptions"][0] + + +def test_is_zone_mechanically_heated_and_not_cooled_zone_1__false(): + assert is_zone_mechanically_heated_and_not_cooled(TEST_RMD_UNIT, "zone 1") is False + + +def test_is_zone_mechanically_heated_and_not_cooled_zone_2__false(): + assert is_zone_mechanically_heated_and_not_cooled(TEST_RMD_UNIT, "zone 2") is False + + +def test_is_zone_mechanically_heated_and_not_cooled_zone_3__false(): + assert is_zone_mechanically_heated_and_not_cooled(TEST_RMD_UNIT, "zone 3") is False + + +def test_is_zone_mechanically_heated_and_not_cooled_zone_4__true(): + assert is_zone_mechanically_heated_and_not_cooled(TEST_RMD_UNIT, "zone 4") is True + + +def test_is_zone_mechanically_heated_and_not_cooled_zone_5__true(): + assert is_zone_mechanically_heated_and_not_cooled(TEST_RMD_UNIT, "zone 5") is True diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/get_baseline_system_types.py b/rct229/rulesets/ashrae9012022/ruleset_functions/get_baseline_system_types.py new file mode 100644 index 0000000000..d4e1a021aa --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/get_baseline_system_types.py @@ -0,0 +1,131 @@ +import inspect + +from rct229.rule_engine.memoize import memoize +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.is_baseline_system_1 import ( + is_baseline_system_1, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.is_baseline_system_2 import ( + is_baseline_system_2, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.is_baseline_system_3 import ( + is_baseline_system_3, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.is_baseline_system_4 import ( + is_baseline_system_4, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.is_baseline_system_5 import ( + is_baseline_system_5, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.is_baseline_system_6 import ( + is_baseline_system_6, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.is_baseline_system_7 import ( + is_baseline_system_7, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.is_baseline_system_8 import ( + is_baseline_system_8, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.is_baseline_system_9 import ( + is_baseline_system_9, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.is_baseline_system_10 import ( + is_baseline_system_10, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.is_baseline_system_11_1 import ( + is_baseline_system_11_1, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.is_baseline_system_11_2 import ( + is_baseline_system_11_2, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.is_baseline_system_12 import ( + is_baseline_system_12, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.is_baseline_system_13 import ( + is_baseline_system_13, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.get_dict_of_zones_and_terminal_units_served_by_hvac_sys import ( + get_dict_of_zones_and_terminal_units_served_by_hvac_sys, +) +from rct229.utils.assertions import assert_ +from rct229.utils.jsonpath_utils import find_all + + +@memoize +def get_baseline_system_types(rmd_b: dict) -> dict[HVAC_SYS, list[str]]: + """ + Identify all the baseline system types modeled in a B-RMD. + + Parameters + ---------- + rmd_b json The B-RMD that needs to get the list of all HVAC system types. + + Returns dictionary saves all baseline HVAC system types in B-RMD with their IDs + i.e. {"SYS-3": ["hvac_id_1", "hvac_id_10"], "SYS-7A": ["hvac_id_3", "hvac_id_17", "hvac_id_6], "SYS-9": ["hvac_id_2"]} + ------- + """ + + baseline_system_type_checks = [ + is_baseline_system_1, + is_baseline_system_2, + is_baseline_system_3, + is_baseline_system_4, + is_baseline_system_5, + is_baseline_system_6, + is_baseline_system_7, + is_baseline_system_8, + is_baseline_system_9, + is_baseline_system_10, + is_baseline_system_11_1, + is_baseline_system_11_2, + is_baseline_system_12, + is_baseline_system_13, + ] + + # A list of the attribute values from the HVAC_SYS class + hvac_sys_list = [ + i[1] + for i in inspect.getmembers(HVAC_SYS) + if type(i[0]) is str and i[0].startswith("SYS") + ] + + baseline_hvac_system_dict = {sys_type: [] for sys_type in hvac_sys_list} + + dict_of_zones_and_terminal_units_served_by_hvac_sys = ( + get_dict_of_zones_and_terminal_units_served_by_hvac_sys(rmd_b) + ) + + for hvac_b in find_all( + "$.buildings[*].building_segments[*].heating_ventilating_air_conditioning_systems[*]", + rmd_b, + ): + hvac_b_id = hvac_b["id"] + assert_( + dict_of_zones_and_terminal_units_served_by_hvac_sys.get(hvac_b_id), + f"HVAC system {hvac_b_id} is missing in the HeatingVentilatingAiConditioningSystems data group.", + ) + + terminal_unit_id_list = dict_of_zones_and_terminal_units_served_by_hvac_sys[ + hvac_b_id + ]["terminal_unit_list"] + zone_id_list = dict_of_zones_and_terminal_units_served_by_hvac_sys[hvac_b_id][ + "zone_list" + ] + + sys_found = False + for sys_check in baseline_system_type_checks: + hvac_sys = sys_check(rmd_b, hvac_b_id, terminal_unit_id_list, zone_id_list) + + if hvac_sys != HVAC_SYS.UNMATCHED: + baseline_hvac_system_dict[hvac_sys].append(hvac_b_id) + sys_found = True + # break # TODO: This line must be uncommented before we ship the software. The reason why we commented this line is because an edge case HVAC system could (potentially) match multiple HVAC basseline systems, which require RCT developers to refine the `is_baseline_system` logic. + + assert_( + sys_found, + f"Error: HVAC {hvac_b_id} does not match any baseline system type.", + ) + + return baseline_hvac_system_dict diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/get_baseline_system_types_test.py b/rct229/rulesets/ashrae9012022/ruleset_functions/get_baseline_system_types_test.py new file mode 100644 index 0000000000..62c53a360b --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/get_baseline_system_types_test.py @@ -0,0 +1,468 @@ +import inspect + +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.test_is_baseline_system_1 import ( + SYS_1_TEST_RMD, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.test_is_baseline_system_2 import ( + SYS_2_TEST_RMD, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.test_is_baseline_system_3 import ( + SYS_3_TEST_RMD, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.test_is_baseline_system_4 import ( + SYS_4_TEST_RMD, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.test_is_baseline_system_5 import ( + SYS_5_TEST_RMD, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.test_is_baseline_system_6 import ( + SYS_6_TEST_RMD, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.test_is_baseline_system_7 import ( + SYS_7_TEST_RMD, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.test_is_baseline_system_8 import ( + SYS_8_TEST_RMD, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.test_is_baseline_system_9 import ( + SYS_9_TEST_RMD, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.test_is_baseline_system_10 import ( + SYS_10_FIRST_LOGIC_TEST_RMD, + SYS_10_SECOND_LOGIC_TEST_RMD, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.test_is_baseline_system_11_1 import ( + SYS_11_1_TEST_RMD, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.test_is_baseline_system_11_2 import ( + SYS_11_2_TEST_RMD, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.get_baseline_system_types import ( + get_baseline_system_types, +) + + +def exclude_sys_types(exclude_type: list[str]) -> list[str]: + return [ + getattr(HVAC_SYS, sys_type[0]) + for sys_type in inspect.getmembers(HVAC_SYS) + if sys_type[1] not in exclude_type + ] + + +def available_type_lists(baseline_system_types_dict: dict) -> list: + return [ + hvac_type + for hvac_type in baseline_system_types_dict + if len(baseline_system_types_dict[hvac_type]) > 0 + ] + + +def test_get_baseline_system_types__system_1_true(): + baseline_system_types_dict = get_baseline_system_types( + SYS_1_TEST_RMD["ruleset_model_descriptions"][0] + ) + test_types = [HVAC_SYS.SYS_1, HVAC_SYS.SYS_1A, HVAC_SYS.SYS_1B, HVAC_SYS.SYS_1C] + assert any( + [ + available_type in test_types + for available_type in available_type_lists(baseline_system_types_dict) + ] + ) + + +def test_get_baseline_system_types__system_1_false(): + baseline_system_types_dict = get_baseline_system_types( + SYS_1_TEST_RMD["ruleset_model_descriptions"][0] + ) + test_types = exclude_sys_types( + [ + HVAC_SYS.SYS_1, + HVAC_SYS.SYS_1A, + HVAC_SYS.SYS_1B, + HVAC_SYS.SYS_1C, + ] + ) + assert ( + any( + [ + available_type in test_types + for available_type in available_type_lists(baseline_system_types_dict) + ] + ) + == False + ) + + +def test_get_baseline_system_types__system_2_true(): + baseline_system_types_dict = get_baseline_system_types( + SYS_2_TEST_RMD["ruleset_model_descriptions"][0] + ) + test_types = [HVAC_SYS.SYS_2] + assert any( + [ + available_type in test_types + for available_type in available_type_lists(baseline_system_types_dict) + ] + ) + + +def test_get_baseline_system_types__system_2_false(): + baseline_system_types_dict = get_baseline_system_types( + SYS_2_TEST_RMD["ruleset_model_descriptions"][0] + ) + test_types = exclude_sys_types([HVAC_SYS.SYS_2]) + assert ( + any( + [ + available_type in test_types + for available_type in available_type_lists(baseline_system_types_dict) + ] + ) + == False + ) + + +def test_get_baseline_system_types__system_3_true(): + baseline_system_types_dict = get_baseline_system_types( + SYS_3_TEST_RMD["ruleset_model_descriptions"][0] + ) + test_types = [HVAC_SYS.SYS_3, HVAC_SYS.SYS_3A, HVAC_SYS.SYS_3B, HVAC_SYS.SYS_3C] + assert any( + [ + available_type in test_types + for available_type in available_type_lists(baseline_system_types_dict) + ] + ) + + +def test_get_baseline_system_types__system_3_false(): + baseline_system_types_dict = get_baseline_system_types( + SYS_3_TEST_RMD["ruleset_model_descriptions"][0] + ) + test_types = exclude_sys_types( + [ + HVAC_SYS.SYS_3, + HVAC_SYS.SYS_3A, + HVAC_SYS.SYS_3B, + HVAC_SYS.SYS_3C, + ] + ) + assert ( + any( + [ + available_type in test_types + for available_type in available_type_lists(baseline_system_types_dict) + ] + ) + == False + ) + + +def test_get_baseline_system_types__system_4_true(): + baseline_system_types_dict = get_baseline_system_types( + SYS_4_TEST_RMD["ruleset_model_descriptions"][0] + ) + test_types = [HVAC_SYS.SYS_4] + assert any( + [ + available_type in test_types + for available_type in available_type_lists(baseline_system_types_dict) + ] + ) + + +def test_get_baseline_system_types__system_4_false(): + baseline_system_types_dict = get_baseline_system_types( + SYS_4_TEST_RMD["ruleset_model_descriptions"][0] + ) + test_types = exclude_sys_types([HVAC_SYS.SYS_4]) + assert ( + any( + [ + available_type in test_types + for available_type in available_type_lists(baseline_system_types_dict) + ] + ) + == False + ) + + +def test_get_baseline_system_types__system_5_true(): + baseline_system_types_dict = get_baseline_system_types( + SYS_5_TEST_RMD["ruleset_model_descriptions"][0] + ) + test_types = [HVAC_SYS.SYS_5, HVAC_SYS.SYS_5B] + assert any( + [ + available_type in test_types + for available_type in available_type_lists(baseline_system_types_dict) + ] + ) + + +def test_get_baseline_system_types__system_5_false(): + baseline_system_types_dict = get_baseline_system_types( + SYS_5_TEST_RMD["ruleset_model_descriptions"][0] + ) + test_types = exclude_sys_types([HVAC_SYS.SYS_5, HVAC_SYS.SYS_5B]) + assert ( + any( + [ + available_type in test_types + for available_type in available_type_lists(baseline_system_types_dict) + ] + ) + == False + ) + + +def test_get_baseline_system_types__system_6_true(): + baseline_system_types_dict = get_baseline_system_types( + SYS_6_TEST_RMD["ruleset_model_descriptions"][0] + ) + test_types = [HVAC_SYS.SYS_6, HVAC_SYS.SYS_6B] + assert any( + [ + available_type in test_types + for available_type in available_type_lists(baseline_system_types_dict) + ] + ) + + +def test_get_baseline_system_types__system_6_false(): + baseline_system_types_dict = get_baseline_system_types( + SYS_6_TEST_RMD["ruleset_model_descriptions"][0] + ) + test_types = exclude_sys_types([HVAC_SYS.SYS_6, HVAC_SYS.SYS_6B]) + assert ( + any( + [ + available_type in test_types + for available_type in available_type_lists(baseline_system_types_dict) + ] + ) + == False + ) + + +def test_get_baseline_system_types__system_7_true(): + baseline_system_types_dict = get_baseline_system_types( + SYS_7_TEST_RMD["ruleset_model_descriptions"][0] + ) + test_types = [HVAC_SYS.SYS_7, HVAC_SYS.SYS_7A, HVAC_SYS.SYS_7B, HVAC_SYS.SYS_7C] + assert any( + [ + available_type in test_types + for available_type in available_type_lists(baseline_system_types_dict) + ] + ) + + +def test_get_baseline_system_types__system_7_false(): + baseline_system_types_dict = get_baseline_system_types( + SYS_7_TEST_RMD["ruleset_model_descriptions"][0] + ) + test_types = exclude_sys_types( + [HVAC_SYS.SYS_7, HVAC_SYS.SYS_7A, HVAC_SYS.SYS_7B, HVAC_SYS.SYS_7C] + ) + assert ( + any( + [ + available_type in test_types + for available_type in available_type_lists(baseline_system_types_dict) + ] + ) + == False + ) + + +def test_get_baseline_system_types__system_8_true(): + baseline_system_types_dict = get_baseline_system_types( + SYS_8_TEST_RMD["ruleset_model_descriptions"][0] + ) + test_types = [HVAC_SYS.SYS_8, HVAC_SYS.SYS_8A, HVAC_SYS.SYS_8B, HVAC_SYS.SYS_8C] + assert any( + [ + available_type in test_types + for available_type in available_type_lists(baseline_system_types_dict) + ] + ) + + +def test_get_baseline_system_types__system_8_false(): + baseline_system_types_dict = get_baseline_system_types( + SYS_8_TEST_RMD["ruleset_model_descriptions"][0] + ) + test_types = exclude_sys_types( + [HVAC_SYS.SYS_8, HVAC_SYS.SYS_8A, HVAC_SYS.SYS_8B, HVAC_SYS.SYS_8C] + ) + assert ( + any( + [ + available_type in test_types + for available_type in available_type_lists(baseline_system_types_dict) + ] + ) + == False + ) + + +def test_get_baseline_system_types__system_9_true(): + baseline_system_types_dict = get_baseline_system_types( + SYS_9_TEST_RMD["ruleset_model_descriptions"][0] + ) + test_types = [HVAC_SYS.SYS_9, HVAC_SYS.SYS_9B] + assert any( + [ + available_type in test_types + for available_type in available_type_lists(baseline_system_types_dict) + ] + ) + + +def test_get_baseline_system_types__system_9_false(): + baseline_system_types_dict = get_baseline_system_types( + SYS_9_TEST_RMD["ruleset_model_descriptions"][0] + ) + test_types = exclude_sys_types([HVAC_SYS.SYS_9, HVAC_SYS.SYS_9B]) + assert ( + any( + [ + available_type in test_types + for available_type in available_type_lists(baseline_system_types_dict) + ] + ) + == False + ) + + +def test_get_baseline_system_types__system_10_first_logic_true(): + baseline_system_types_dict = get_baseline_system_types( + SYS_10_FIRST_LOGIC_TEST_RMD["ruleset_model_descriptions"][0] + ) + test_types = [HVAC_SYS.SYS_10] + assert any( + [ + available_type in test_types + for available_type in available_type_lists(baseline_system_types_dict) + ] + ) + + +def test_get_baseline_system_types__system_10_first_logic_false(): + baseline_system_types_dict = get_baseline_system_types( + SYS_10_FIRST_LOGIC_TEST_RMD["ruleset_model_descriptions"][0] + ) + test_types = exclude_sys_types([HVAC_SYS.SYS_10]) + assert ( + any( + [ + available_type in test_types + for available_type in available_type_lists(baseline_system_types_dict) + ] + ) + == False + ) + + +def test_get_baseline_system_types__system_10_second_logic_true(): + baseline_system_types_dict = get_baseline_system_types( + SYS_10_SECOND_LOGIC_TEST_RMD["ruleset_model_descriptions"][0] + ) + test_types = [HVAC_SYS.SYS_10] + assert any( + [ + available_type in test_types + for available_type in available_type_lists(baseline_system_types_dict) + ] + ) + + +def test_get_baseline_system_types__system_10_second_logic_false(): + baseline_system_types_dict = get_baseline_system_types( + SYS_10_SECOND_LOGIC_TEST_RMD["ruleset_model_descriptions"][0] + ) + test_types = exclude_sys_types([HVAC_SYS.SYS_10]) + assert ( + any( + [ + available_type in test_types + for available_type in available_type_lists(baseline_system_types_dict) + ] + ) + == False + ) + + +def test_get_baseline_system_types__system_11_1_true(): + baseline_system_types_dict = get_baseline_system_types( + SYS_11_1_TEST_RMD["ruleset_model_descriptions"][0] + ) + test_types = [ + HVAC_SYS.SYS_11_1, + HVAC_SYS.SYS_11_1A, + HVAC_SYS.SYS_11_1B, + HVAC_SYS.SYS_11_1C, + ] + assert any( + [ + available_type in test_types + for available_type in available_type_lists(baseline_system_types_dict) + ] + ) + + +def test_get_baseline_system_types__system_11_1_false(): + baseline_system_types_dict = get_baseline_system_types( + SYS_11_1_TEST_RMD["ruleset_model_descriptions"][0] + ) + test_types = exclude_sys_types( + [ + HVAC_SYS.SYS_11_1, + HVAC_SYS.SYS_11_1A, + HVAC_SYS.SYS_11_1B, + HVAC_SYS.SYS_11_1C, + ] + ) + assert ( + any( + [ + available_type in test_types + for available_type in available_type_lists(baseline_system_types_dict) + ] + ) + == False + ) + + +def test_get_baseline_system_types__system_11_2_true(): + baseline_system_types_dict = get_baseline_system_types( + SYS_11_2_TEST_RMD["ruleset_model_descriptions"][0] + ) + test_types = [HVAC_SYS.SYS_11_2, HVAC_SYS.SYS_11_2A] + assert any( + [ + available_type in test_types + for available_type in available_type_lists(baseline_system_types_dict) + ] + ) + + +def test_get_baseline_system_types__system_11_2_false(): + baseline_system_types_dict = get_baseline_system_types( + SYS_11_2_TEST_RMD["ruleset_model_descriptions"][0] + ) + test_types = exclude_sys_types([HVAC_SYS.SYS_11_2, HVAC_SYS.SYS_11_2A]) + assert ( + any( + [ + available_type in test_types + for available_type in available_type_lists(baseline_system_types_dict) + ] + ) + == False + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/get_dict_of_zones_and_terminal_units_served_by_hvac_sys.py b/rct229/rulesets/ashrae9012022/ruleset_functions/get_dict_of_zones_and_terminal_units_served_by_hvac_sys.py new file mode 100644 index 0000000000..6fb9b72556 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/get_dict_of_zones_and_terminal_units_served_by_hvac_sys.py @@ -0,0 +1,74 @@ +from typing import TypedDict + +from rct229.utils.assertions import assert_ +from rct229.utils.jsonpath_utils import find_all + + +class ZonesTerminalUnitsServedByHVACSys(TypedDict): + terminal_unit_list: list[str] + zone_list: list[str] + + +def get_dict_of_zones_and_terminal_units_served_by_hvac_sys( + rmd: dict, +) -> dict[str, ZonesTerminalUnitsServedByHVACSys]: + """ + Returns a dictionary of zones and terminal unit IDs associated with each HVAC system in the RMD. + + Parameters + ---------- + rmd: dict + A dictionary representing a RuleModelDescription object as defined by the ASHRAE229 schema + + Returns ------- dict: a dictionary of zones and terminal unit IDs associated with each HVAC system in the RMD, + {hvac_system_1.id: {"zone_list": [zone_1.id, zone_2.id, zone_3.id], "terminal_unit_list": [terminal_1.id, + terminal_2.id, terminal_3.id]}, hvac_system_2.id: {"zone_list": [zone_4.id, zone_9.id, zone_30.id], + "terminal_unit_list": [terminal_10.id, terminal_20.id, terminal_30.id]}} + """ + dict_of_zones_and_terminal_units_served_by_hvac_sys = {} + for zone in find_all("$.buildings[*].building_segments[*].zones[*]", rmd): + zone_id = zone["id"] + for terminal in find_all("$.terminals[*]", zone): + terminal_id = terminal["id"] + hvac_sys_id = terminal.get( + "served_by_heating_ventilating_air_conditioning_system" + ) + if hvac_sys_id: + if ( + hvac_sys_id + not in dict_of_zones_and_terminal_units_served_by_hvac_sys + ): + dict_of_zones_and_terminal_units_served_by_hvac_sys[hvac_sys_id] = { + "terminal_unit_list": [], + "zone_list": [], + } + + zone_list = dict_of_zones_and_terminal_units_served_by_hvac_sys[ + hvac_sys_id + ]["zone_list"] + if zone_id not in zone_list: + zone_list.append(zone_id) + + terminal_unit_list = ( + dict_of_zones_and_terminal_units_served_by_hvac_sys[hvac_sys_id][ + "terminal_unit_list" + ] + ) + if terminal_id not in terminal_unit_list: + terminal_unit_list.append(terminal_id) + + # verification - make sure all hvac ids in the zone.terminals are associated with the hvac systems in the data group + hvac_id_list = find_all( + "$.buildings[*].building_segments[*].heating_ventilating_air_conditioning_systems[*].id", + rmd, + ) + missing_hvac_id_list = [] + for key in dict_of_zones_and_terminal_units_served_by_hvac_sys: + if key not in hvac_id_list: + missing_hvac_id_list.append(key) + assert_( + len(missing_hvac_id_list) == 0, + f"HVAC systems {missing_hvac_id_list} are missing in the HeatingVentilatingAirConditioningSystems data group.", + ) + + return dict_of_zones_and_terminal_units_served_by_hvac_sys diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/get_dict_of_zones_and_terminal_units_served_by_hvac_sys_test.py b/rct229/rulesets/ashrae9012022/ruleset_functions/get_dict_of_zones_and_terminal_units_served_by_hvac_sys_test.py new file mode 100644 index 0000000000..759ed4476e --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/get_dict_of_zones_and_terminal_units_served_by_hvac_sys_test.py @@ -0,0 +1,119 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.get_dict_of_zones_and_terminal_units_served_by_hvac_sys import ( + get_dict_of_zones_and_terminal_units_served_by_hvac_sys, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_BUILDING = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "heating_ventilating_air_conditioning_systems": [ + {"id": "hvac_1"}, + {"id": "hvac_2"}, + {"id": "hvac_3"}, + {"id": "hvac_4"}, + ], + "zones": [ + { + "id": "zone_1", + "terminals": [ + { + "id": "terminal_1", + "served_by_heating_ventilating_air_conditioning_system": "hvac_1", + }, + { + "id": "terminal_2", + "served_by_heating_ventilating_air_conditioning_system": "hvac_2", + }, + { + "id": "terminal_3", + "served_by_heating_ventilating_air_conditioning_system": "hvac_3", + }, + { + "id": "terminal_4", + # intentionally omit `served_by_heating_ventilating_air_conditioning_system` key to test 'if hvac_sys_id:' condition in get_dict_of_zones_and_terminal_units_served_by_hvac_sys.py + }, + { + # intentionally added the duplicate to test 'if terminal_id not in terminal_unit_list:' condition in get_dict_of_zones_and_terminal_units_served_by_hvac_sys.py + "id": "terminal_1", + "served_by_heating_ventilating_air_conditioning_system": "hvac_1", + }, + ], + }, + { + "id": "zone_2", + "terminals": [ + { + "id": "terminal_4", + "served_by_heating_ventilating_air_conditioning_system": "hvac_4", + }, + { + "id": "terminal_5", + "served_by_heating_ventilating_air_conditioning_system": "hvac_2", + }, + { + "id": "terminal_6", + "served_by_heating_ventilating_air_conditioning_system": "hvac_4", + }, + ], + }, + ], + } + ], + } + ], + "type": "BASELINE_0", +} + + +TEST_RMD = { + "id": "ASHRAE229", + "ruleset_model_descriptions": [TEST_BUILDING], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RMD) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test_get_hvac_zone_terminals(): + assert ordered( + get_dict_of_zones_and_terminal_units_served_by_hvac_sys(TEST_BUILDING) + ) == ordered( + { + "hvac_1": {"terminal_unit_list": ["terminal_1"], "zone_list": ["zone_1"]}, + "hvac_2": { + "terminal_unit_list": ["terminal_2", "terminal_5"], + "zone_list": ["zone_1", "zone_2"], + }, + "hvac_3": {"terminal_unit_list": ["terminal_3"], "zone_list": ["zone_1"]}, + "hvac_4": { + "terminal_unit_list": ["terminal_4", "terminal_6"], + "zone_list": ["zone_2"], + }, + } + ) + + +def ordered(obj): + if isinstance(obj, dict): + return sorted((k, ordered(v)) for k, v in obj.items()) + if isinstance(obj, list): + return sorted(ordered(x) for x in obj) + else: + return obj diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/get_lab_zone_hvac_systems.py b/rct229/rulesets/ashrae9012022/ruleset_functions/get_lab_zone_hvac_systems.py new file mode 100644 index 0000000000..faea478977 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/get_lab_zone_hvac_systems.py @@ -0,0 +1,76 @@ +from typing import TypedDict +from pydash import map_ + +from rct229.rulesets.ashrae9012022.ruleset_functions.get_dict_of_zones_and_terminal_units_served_by_hvac_sys import ( + get_dict_of_zones_and_terminal_units_served_by_hvac_sys, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.get_zone_target_baseline_system import ( + SYSTEMORIGIN, + get_zone_target_baseline_system, +) + + +class HVACServeLabZoneDict(TypedDict): + lab_zones_only: list[str] + lab_and_other: list[str] + + +def get_lab_zone_hvac_systems( + rmd_b: dict, rmd_p: dict, climate_zone: str +) -> HVACServeLabZoneDict: + """ + returns a list of HVAC systems serving only lab zones + + Parameters + ---------- + rmd_b: json + RMD at RuleSetModelDescription level + rmd_p: json + RMD at RuleSetModelDescription level + climate_zone: str + baseline climate zone + + Returns + ------- + hvac_systems_serving_lab_zones: A dictionary consisting of two lists of hvac system ids. + One list ["lab_zones_only"] is a list of hvac system ids where each hvac system serves only lab zones. + The other ["lab_and_other"] is a list of hvac system ids where systems serve both labs and other zones. + """ + + target_baseline_systems = get_zone_target_baseline_system( + rmd_b, rmd_p, climate_zone + ) + + # find zone ids whose `system_origin` is `SYSTEMORIGIN.G311D` + building_lab_zones = [ + zone_id_b + for zone_id_b in target_baseline_systems + if target_baseline_systems[zone_id_b]["system_origin"] == SYSTEMORIGIN.G3212B + ] + + hvac_systems_serving_lab_zones: HVACServeLabZoneDict = { + "lab_zones_only": [], + "lab_and_other": [], + } + if building_lab_zones: + dict_of_zones_and_hvac_systems_b = ( + get_dict_of_zones_and_terminal_units_served_by_hvac_sys(rmd_b) + ) + + for hvac_id_b in dict_of_zones_and_hvac_systems_b: + zones_served_by_hvac_system_b = dict_of_zones_and_hvac_systems_b[hvac_id_b][ + "zone_list" + ] + + is_zone_in_building_lab_zones_b = map_( + zones_served_by_hvac_system_b, + lambda zone_id_b: zone_id_b in building_lab_zones, + ) + + if all(is_zone_in_building_lab_zones_b): + hvac_systems_serving_lab_zones["lab_zones_only"].append(hvac_id_b) + + elif any(is_zone_in_building_lab_zones_b): + hvac_systems_serving_lab_zones["lab_and_other"].append(hvac_id_b) + + return hvac_systems_serving_lab_zones diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/get_lab_zone_hvac_systems_test.py b/rct229/rulesets/ashrae9012022/ruleset_functions/get_lab_zone_hvac_systems_test.py new file mode 100644 index 0000000000..b4574ea7e7 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/get_lab_zone_hvac_systems_test.py @@ -0,0 +1,609 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.get_lab_zone_hvac_systems import ( + get_lab_zone_hvac_systems, +) +from rct229.schema.schema_utils import quantify_rmd +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD_B_ALL_ZONES_G311D_ONE_SYSTEM = { + "id": "test_rmd", + "constructions": [ + { + "id": "Construction 3", + "u_factor": 0.35773064046128095, + } + ], + "buildings": [ + { + "id": "Building 1", + "building_segments": [ + { + "id": "Building Segment 1", + "heating_ventilating_air_conditioning_systems": [ + { + "id": "hvac 1", + "cooling_system": { + "id": "Cooling system 1", + "design_sensible_cool_capacity": 100000, + }, + "heating_system": { + "id": "Heating system 1", + "design_capacity": 10000, + }, + "fan_system": { + "id": "fan_system_1", + "exhaust_fans": [ + { + "id": "exhaust_fans 1", + "design_airflow": 7500, + } + ], + }, + } + ], + "zones": [ + { + "id": "Thermal Zone 1", + "volume": 1000, + "spaces": [ + { + "id": "Space 1", + "function": "LABORATORY", + "floor_area": 1000, + "lighting_space_type": "CORRIDOR_MANUFACTURING_FACILITY", + }, + ], + "terminals": [ + { + "id": "Terminal 1", + "served_by_heating_ventilating_air_conditioning_system": "hvac 1", + "primary_airflow": 6500, + } + ], + "surfaces": [ + { + "id": "Surface 1_1", + "adjacent_to": "INTERIOR", + "area": 100, + "tilt": 0, + "adjacent_zone": "Thermal Zone 1", + "construction": "Construction 3", + "subsurfaces": [ + { + "id": "subsurface_1_1_1", + "glazed_area": 0, + "opaque_area": 10, + "u_factor": 0.5, + } + ], + } + ], + "zonal_exhaust_fans": [ + { + "id": "Exhaust Fan 1", + "design_airflow": 1000, + } + ], + }, + { + "id": "Thermal Zone 2", + "volume": 3000, + "spaces": [ + { + "id": "Space 2", + "function": "LABORATORY", + "floor_area": 3000, + "lighting_space_type": "CORRIDOR_MANUFACTURING_FACILITY", + }, + ], + "terminals": [ + { + "id": "Terminal 2", + "served_by_heating_ventilating_air_conditioning_system": "hvac 1", + "primary_airflow": 6500, + } + ], + "surfaces": [ + { + "id": "Surface 2_1", + "adjacent_to": "INTERIOR", + "area": 100, + "tilt": 0, + "adjacent_zone": "Thermal Zone 2", + "construction": "Construction 3", + "subsurfaces": [ + { + "id": "subsurface_2_1_1", + "glazed_area": 0, + "opaque_area": 10, + "u_factor": 0.5, + } + ], + } + ], + "zonal_exhaust_fans": [ + { + "id": "Exhaust Fan 1", + "design_airflow": 1000, + } + ], + }, + ], + }, + ], + }, + ], + "constructions": [ + { + "id": "Construction 3", + "u_factor": 0.35773064046128095, + } + ], + "type": "BASELINE_0", +} + + +TEST_RMD_B_ALL_ZONES_G311D_ONE_SYSTEM2_TWO_SYSTEMS = { + "id": "test_rmd", + "buildings": [ + { + "id": "Building 1", + "building_segments": [ + { + "id": "Building Segment 1", + "heating_ventilating_air_conditioning_systems": [ + { + "id": "hvac 1", + "cooling_system": { + "id": "Cooling system 1", + "design_sensible_cool_capacity": 100000, + }, + "heating_system": { + "id": "Heating system 1", + "design_capacity": 10000, + }, + "fan_system": { + "id": "fan_system_1", + "exhaust_fans": [ + { + "id": "exhaust_fans 1", + "design_airflow": 7500, + } + ], + }, + }, + { + "id": "hvac 2", + "cooling_system": { + "id": "Cooling system 2", + "design_sensible_cool_capacity": 200000, + }, + "heating_system": { + "id": "Heating system 2", + "design_capacity": 20000, + }, + "fan_system": { + "id": "fan_system_2", + "exhaust_fans": [ + { + "id": "exhaust_fans 2", + "design_airflow": 9500, + } + ], + }, + }, + ], + "zones": [ + { + "id": "Thermal Zone 1", + "volume": 1000, + "spaces": [ + { + "id": "Space 1", + "function": "LABORATORY", + "floor_area": 1000, + "lighting_space_type": "CORRIDOR_MANUFACTURING_FACILITY", + }, + ], + "terminals": [ + { + "id": "Terminal 1", + "served_by_heating_ventilating_air_conditioning_system": "hvac 1", + "primary_airflow": 6500, + } + ], + "surfaces": [ + { + "id": "Surface 1_1", + "adjacent_to": "INTERIOR", + "area": 100, + "tilt": 0, + "adjacent_zone": "Thermal Zone 1", + "construction": "Construction 3", + "subsurfaces": [ + { + "id": "subsurface_1_1_1", + "glazed_area": 0, + "opaque_area": 10, + "u_factor": 0.5, + } + ], + } + ], + "zonal_exhaust_fans": [ + { + "id": "Exhaust Fan 1", + "design_airflow": 1000, + } + ], + }, + { + "id": "Thermal Zone 2", + "volume": 3000, + "spaces": [ + { + "id": "Space 2", + "function": "LABORATORY", + "floor_area": 3000, + "lighting_space_type": "CORRIDOR_MANUFACTURING_FACILITY", + }, + ], + "terminals": [ + { + "id": "Terminal 2", + "served_by_heating_ventilating_air_conditioning_system": "hvac 2", + "primary_airflow": 6500, + } + ], + "surfaces": [ + { + "id": "Surface 2_1", + "adjacent_to": "INTERIOR", + "area": 100, + "tilt": 0, + "adjacent_zone": "Thermal Zone 2", + "construction": "Construction 3", + "subsurfaces": [ + { + "id": "subsurface_2_1_1", + "glazed_area": 0, + "opaque_area": 10, + "u_factor": 0.5, + } + ], + } + ], + "zonal_exhaust_fans": [ + { + "id": "Exhaust Fan 1", + "design_airflow": 1000, + } + ], + }, + ], + }, + ], + }, + ], + "constructions": [ + { + "id": "Construction 3", + "u_factor": 0.35773064046128095, + } + ], + "type": "BASELINE_0", +} + +TEST_RMD_B_G311D_G311F = { + "id": "test_rmd", + "schedules": [{"id": "schedule_1", "hourly_values": [1.2] * 8670}], + "buildings": [ + { + "id": "Building 1", + "building_segments": [ + { + "id": "Building Segment 1", + "heating_ventilating_air_conditioning_systems": [ + { + "id": "hvac 1", + "cooling_system": { + "id": "Cooling system 1", + "design_sensible_cool_capacity": 100000, + }, + "heating_system": { + "id": "Heating system 1", + "design_capacity": 10000, + }, + "fan_system": { + "id": "fan_system_1", + "exhaust_fans": [ + { + "id": "exhaust_fans 1", + "design_airflow": 7500, + } + ], + }, + }, + { + "id": "hvac 2", + "cooling_system": { + "id": "Cooling System 1", + "type": "DIRECT_EXPANSION", + "design_sensible_cool_capacity": 100000, + }, + "heating_system": { + "id": "csys_2_1_2", + "type": "FURNACE", + "design_capacity": 1000000, + }, + }, + ], + "zones": [ + { + "id": "Thermal Zone 1", + "volume": 1000, + "spaces": [ + { + "id": "Space 1", + "function": "LABORATORY", + "floor_area": 1000, + "lighting_space_type": "CORRIDOR_MANUFACTURING_FACILITY", + }, + ], + "terminals": [ + { + "id": "Terminal 1", + "served_by_heating_ventilating_air_conditioning_system": "hvac 1", + "primary_airflow": 6500, + } + ], + "surfaces": [ + { + "id": "Surface 1_1", + "adjacent_to": "INTERIOR", + "area": 100, + "tilt": 0, + "adjacent_zone": "Thermal Zone 1", + "construction": "Construction 3", + "subsurfaces": [ + { + "id": "subsurface_1_1_1", + "glazed_area": 0, + "opaque_area": 10, + "u_factor": 0.5, + } + ], + } + ], + "zonal_exhaust_fans": [ + { + "id": "Exhaust Fan 1", + "design_airflow": 1000, + } + ], + }, + { + "id": "Thermal Zone 2", + "floor_name": "FLOOR 1", + "volume": 1000, + "terminals": [ + { + "id": "terminal_2_1", + "served_by_heating_ventilating_air_conditioning_system": "hvac 2", + "heating_source": "ELECTRIC", + }, + ], + "spaces": [ + { + "id": "Space 3", + "floor_area": 5, + "lighting_space_type": "STORAGE_ROOM_HOSPITAL", + }, + ], + "surfaces": [ + { + "id": "surface 2", + "adjacent_to": "EXTERIOR", + "subsurfaces": [ + { + "id": "subsurface 3", + "classification": "DOOR", + "glazed_area": 10, + "opaque_area": 2, + "u_factor": 0.2, + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + "constructions": [ + { + "id": "Construction 3", + "u_factor": 0.35773064046128095, + } + ], + "type": "BASELINE_0", +} + + +TEST_RMD_P = { + "id": "ASHRAE229 1", + "ruleset_model_descriptions": [ + { + "id": "RMD 1", + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Building Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + { + "id": "Thermal Zone 1", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "volume": 2000, + "floor_name": "FL 1", + "spaces": [{"id": "Space 1", "floor_area": 50}], + "terminals": [ + { + "id": "VAV Air Terminal 1", + "is_supply_ducted": True, + "heating_source": "HOT_WATER", + "type": "VARIABLE_AIR_VOLUME", + "served_by_heating_ventilating_air_conditioning_system": "System 7", + "heating_from_loop": "Boiler Loop 1", + } + ], + }, + { + "id": "Thermal Zone 2", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "volume": 2000, + "floor_name": "FL 1", + "spaces": [ + { + "id": "Space 2", + "floor_area": 500, + } + ], + "terminals": [ + { + "id": "VAV Air Terminal 2", + "served_by_heating_ventilating_air_conditioning_system": "Testing Sys", + } + ], + }, + ], + "heating_ventilating_air_conditioning_systems": [ + { + "id": "Testing Sys", + "heating_system": { + "id": "Preheat Coil 1", + "type": "FURNACE", + }, + "cooling_system": { + "id": "CHW Coil 1", + "type": "NONE", + }, + } + ], + } + ], + } + ], + "type": "PROPOSED", + } + ], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + +TEST_RMD_B_FULL_G311D_ONE_SYS = { + "id": "229", + "ruleset_model_descriptions": [TEST_RMD_B_ALL_ZONES_G311D_ONE_SYSTEM], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} +TEST_RMD_B_UNIT_G311D_ONE_SYS = quantify_rmd(TEST_RMD_B_FULL_G311D_ONE_SYS)[ + "ruleset_model_descriptions" +][0] +TEST_RMD_B_FULL_G311D_TWO_SYS = { + "id": "229", + "ruleset_model_descriptions": [TEST_RMD_B_ALL_ZONES_G311D_ONE_SYSTEM2_TWO_SYSTEMS], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} +TEST_RMD_B_UNIT_G311D_TWO_SYS = quantify_rmd(TEST_RMD_B_FULL_G311D_TWO_SYS)[ + "ruleset_model_descriptions" +][0] + +TEST_RMD_B_FULL_G311D_G311F = { + "id": "229", + "ruleset_model_descriptions": [TEST_RMD_B_G311D_G311F], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} +TEST_RMD_B_UNIT_G311D_G311F = quantify_rmd(TEST_RMD_B_FULL_G311D_G311F)[ + "ruleset_model_descriptions" +][0] + +TEST_RMD_UNIT_P = quantify_rmd(TEST_RMD_P)["ruleset_model_descriptions"][0] + + +def test__TEST_RMD_B_ALL_ZONES_G311D_ONE_SYSTEM__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RMD_B_FULL_G311D_ONE_SYS) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__TEST_RMD_B_ALL_ZONES_G311D_ONE_SYSTEM2_TWO_SYSTEMS__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RMD_B_FULL_G311D_TWO_SYS) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__TEST_RMD_P_is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RMD_P) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__get_lab_zone_hvac_systems_all_zones_g311d_one_system__true(): + assert get_lab_zone_hvac_systems( + TEST_RMD_B_UNIT_G311D_ONE_SYS, TEST_RMD_UNIT_P, "CZ4A" + ) == { + "lab_zones_only": ["hvac 1"], + "lab_and_other": [], + } + + +def test__get_lab_zone_hvac_systems_all_zones_g311d_two_systems__true(): + assert get_lab_zone_hvac_systems( + TEST_RMD_B_UNIT_G311D_TWO_SYS, TEST_RMD_UNIT_P, "CZ4A" + ) == { + "lab_zones_only": ["hvac 1", "hvac 2"], + "lab_and_other": [], + } + + +def test__get_lab_zone_hvac_systems__zones_g311d_g311f_two_systems__true(): + assert get_lab_zone_hvac_systems( + TEST_RMD_B_UNIT_G311D_G311F, TEST_RMD_UNIT_P, "CZ4A" + ) == { + "lab_zones_only": ["hvac 1"], + "lab_and_other": [], + } diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/get_list_hvac_systems_associated_with_zone.py b/rct229/rulesets/ashrae9012022/ruleset_functions/get_list_hvac_systems_associated_with_zone.py new file mode 100644 index 0000000000..9b1152b5b7 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/get_list_hvac_systems_associated_with_zone.py @@ -0,0 +1,34 @@ +from rct229.utils.assertions import getattr_ +from rct229.utils.jsonpath_utils import find_all + + +def get_list_hvac_systems_associated_with_zone(rmd: dict, zone_id: str) -> list[str]: + """ + Get the list of the heating ventilation and cooling system ids associated with a zone in either the U_RMD, P_RMD, or B_RMD. + + Parameters + ---------- + rmd: dict RMD at RuleSetModelDescription level + zone_id: str Zone id + + Returns: list A list that saves all the HVAC systems associated with the zone. + ------- + + """ + return sorted( + list( + set( + [ + getattr_( + terminal, + "Terminal", + "served_by_heating_ventilating_air_conditioning_system", + ) + for terminal in find_all( + f'$.buildings[*].building_segments[*].zones[*][?(@.id="{zone_id}")].terminals[*]', + rmd, + ) + ] + ) + ) + ) diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/get_list_hvac_systems_associated_with_zone_test.py b/rct229/rulesets/ashrae9012022/ruleset_functions/get_list_hvac_systems_associated_with_zone_test.py new file mode 100644 index 0000000000..ce72dcf251 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/get_list_hvac_systems_associated_with_zone_test.py @@ -0,0 +1,73 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.get_list_hvac_systems_associated_with_zone import ( + get_list_hvac_systems_associated_with_zone, +) +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "building_1", + "building_segments": [ + { + "id": "building_segment_1", + "zones": [ + { + "id": "zone 1", + "terminals": [ + { + "id": "terminal_1", + "served_by_heating_ventilating_air_conditioning_system": "System 1", + }, + { + "id": "terminal_2", + "served_by_heating_ventilating_air_conditioning_system": "System 2", + }, + { + "id": "terminal_3", + "served_by_heating_ventilating_air_conditioning_system": "System 1", + }, + ], + }, + ], + } + ], + } + ], + "type": "BASELINE_0", +} + +TEST_RPD = { + "id": "229_01", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + + +def test__TEST_RPD_FIXED_TYPE__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__all_terminal_fans_parallel(): + assert get_list_hvac_systems_associated_with_zone(TEST_RMD, "zone 1") == [ + "System 1", + "System 2", + ] + + +def test__all_terminal_fans_parallel__wrong_output(): + assert get_list_hvac_systems_associated_with_zone(TEST_RMD, "zone 1") != [ + "System 1", + "System 2", + "System 1", + ] diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/get_surface_conditioning_category_dict.py b/rct229/rulesets/ashrae9012022/ruleset_functions/get_surface_conditioning_category_dict.py new file mode 100644 index 0000000000..bec4ce8ba5 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/get_surface_conditioning_category_dict.py @@ -0,0 +1,183 @@ +from typing import TypedDict + +import pandas as pd +from rct229.rulesets.ashrae9012022.ruleset_functions.get_zone_conditioning_category_dict import ( + ZoneConditioningCategory as ZCC, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.get_zone_conditioning_category_dict import ( + get_zone_conditioning_category_dict, +) +from rct229.schema.schema_enums import SchemaEnums +from rct229.utils.assertions import getattr_ +from rct229.utils.jsonpath_utils import find_all, find_exactly_required_fields + +# Constants +# TODO: These should directly from the enumerations +SurfaceAdjacency = SchemaEnums.schema_enums["SurfaceAdjacencyOptions"] + + +# Intended for export and internal use +class SurfaceConditioningCategory: + """Enumeration class for zone conditioning categories""" + + # Surface conditioning categories (export these) + EXTERIOR_MIXED: str = "EXTERIOR MIXED" + EXTERIOR_NON_RESIDENTIAL: str = "EXTERIOR NON-RESIDENTIAL" + EXTERIOR_RESIDENTIAL: str = "EXTERIOR RESIDENTIAL" + SEMI_EXTERIOR: str = "SEMI-EXTERIOR" + UNREGULATED: str = "UNREGULATED" + + +class ZoneConditioningDataDict(TypedDict): + EXTERIOR_RESIDENTIAL: float + EXTERIOR_NON_RESIDENTIAL: float + EXTERIOR_MIXED: float + SEMI_EXTERIOR: float + + +SCC_DATA_FRAME = pd.DataFrame( + data={ + ZCC.CONDITIONED_RESIDENTIAL: [ + SurfaceConditioningCategory.UNREGULATED, + SurfaceConditioningCategory.UNREGULATED, + SurfaceConditioningCategory.UNREGULATED, + SurfaceConditioningCategory.SEMI_EXTERIOR, + SurfaceConditioningCategory.EXTERIOR_RESIDENTIAL, + SurfaceConditioningCategory.SEMI_EXTERIOR, + ], + ZCC.CONDITIONED_NON_RESIDENTIAL: [ + SurfaceConditioningCategory.UNREGULATED, + SurfaceConditioningCategory.UNREGULATED, + SurfaceConditioningCategory.UNREGULATED, + SurfaceConditioningCategory.SEMI_EXTERIOR, + SurfaceConditioningCategory.EXTERIOR_NON_RESIDENTIAL, + SurfaceConditioningCategory.SEMI_EXTERIOR, + ], + ZCC.CONDITIONED_MIXED: [ + SurfaceConditioningCategory.UNREGULATED, + SurfaceConditioningCategory.UNREGULATED, + SurfaceConditioningCategory.UNREGULATED, + SurfaceConditioningCategory.SEMI_EXTERIOR, + SurfaceConditioningCategory.EXTERIOR_MIXED, + SurfaceConditioningCategory.SEMI_EXTERIOR, + ], + ZCC.SEMI_HEATED: [ + SurfaceConditioningCategory.SEMI_EXTERIOR, + SurfaceConditioningCategory.SEMI_EXTERIOR, + SurfaceConditioningCategory.SEMI_EXTERIOR, + SurfaceConditioningCategory.UNREGULATED, + SurfaceConditioningCategory.SEMI_EXTERIOR, + SurfaceConditioningCategory.SEMI_EXTERIOR, + ], + ZCC.UNENCLOSED: [ + SurfaceConditioningCategory.EXTERIOR_RESIDENTIAL, + SurfaceConditioningCategory.EXTERIOR_NON_RESIDENTIAL, + SurfaceConditioningCategory.EXTERIOR_MIXED, + SurfaceConditioningCategory.SEMI_EXTERIOR, + SurfaceConditioningCategory.UNREGULATED, + SurfaceConditioningCategory.UNREGULATED, + ], + ZCC.UNCONDITIONED: [ + SurfaceConditioningCategory.SEMI_EXTERIOR, + SurfaceConditioningCategory.SEMI_EXTERIOR, + SurfaceConditioningCategory.SEMI_EXTERIOR, + SurfaceConditioningCategory.SEMI_EXTERIOR, + SurfaceConditioningCategory.UNREGULATED, + SurfaceConditioningCategory.UNREGULATED, + ], + SurfaceAdjacency.EXTERIOR: [ + SurfaceConditioningCategory.EXTERIOR_RESIDENTIAL, + SurfaceConditioningCategory.EXTERIOR_NON_RESIDENTIAL, + SurfaceConditioningCategory.EXTERIOR_MIXED, + SurfaceConditioningCategory.SEMI_EXTERIOR, + SurfaceConditioningCategory.UNREGULATED, + SurfaceConditioningCategory.UNREGULATED, + ], + SurfaceAdjacency.GROUND: [ + SurfaceConditioningCategory.EXTERIOR_RESIDENTIAL, + SurfaceConditioningCategory.EXTERIOR_NON_RESIDENTIAL, + SurfaceConditioningCategory.EXTERIOR_MIXED, + SurfaceConditioningCategory.SEMI_EXTERIOR, + SurfaceConditioningCategory.UNREGULATED, + SurfaceConditioningCategory.UNREGULATED, + ], + }, + index=[ + ZCC.CONDITIONED_RESIDENTIAL, + ZCC.CONDITIONED_NON_RESIDENTIAL, + ZCC.CONDITIONED_MIXED, + ZCC.SEMI_HEATED, + ZCC.UNENCLOSED, + ZCC.UNCONDITIONED, + ], +) + +# Intended for internal use +GET_SURFACE_CONDITIONING_CATEGORY_DICT__REQUIRED_FIELDS = { + "building": { + "$..surface[*]": ["adjacent_to"], + } +} + + +def get_surface_conditioning_category_dict(climate_zone, building, constructions): + """Determines the surface conditioning category for every surface in a building + + Parameters + ---------- + climate_zone : str + One of the ClimateZoneOptions2019ASHRAE901 enumerated values + building : dict + A dictionary representing a building as defined by the ASHRAE229 schema + constructions : list + A list of construction dictionaries as defined by the ASHRAE229 schema + Returns + ------- + dict + A dictionary that maps surfaces to one of the conditioning categories: + EXTERIOR_RESIDENTIAL, EXTERIOR_NON_RESIDENTIAL, EXTERIOR_MIXED, + SEMI_EXTERIOR, UNREGULATED + """ + find_exactly_required_fields( + GET_SURFACE_CONDITIONING_CATEGORY_DICT__REQUIRED_FIELDS["building"], building + ) + + # The dictionary to be returned + surface_conditioning_category_dict = {} + + # Get the conditioning category for all the zones in the building + zcc_dict = get_zone_conditioning_category_dict( + climate_zone, building, constructions + ) + + # Loop through all the zones in the building + for zone in find_all("building_segments[*].zones[*]", building): + # Zone conditioning category + zcc = zcc_dict[zone["id"]] + + # Loop through all the surfaces in the zone + for surface in find_all("surfaces[*]", zone): + surface_adjacent_to = surface["adjacent_to"] + adjacency = ( + zcc_dict[getattr_(surface, "surface", "adjacent_zone")] + if surface_adjacent_to == SurfaceAdjacency.INTERIOR + else surface_adjacent_to + ) + + if adjacency in [SurfaceAdjacency.IDENTICAL, SurfaceAdjacency.UNDEFINED]: + surface_conditioning_category_dict[ + surface["id"] + ] = SurfaceConditioningCategory.UNREGULATED + + elif zcc in SCC_DATA_FRAME.index and adjacency in SCC_DATA_FRAME.columns: + surface_conditioning_category_dict[surface["id"]] = SCC_DATA_FRAME.at[ + zcc, # row index + adjacency, # column index + ] + + else: + raise ValueError( + f"Combination of zone conditioning category '{zcc}' and surface adjacency '{adjacency}' has no mapping to a surface conditioning category" + ) + + return surface_conditioning_category_dict diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/get_zone_conditioning_category_dict.py b/rct229/rulesets/ashrae9012022/ruleset_functions/get_zone_conditioning_category_dict.py index aabcee57de..ed40da81ba 100644 --- a/rct229/rulesets/ashrae9012022/ruleset_functions/get_zone_conditioning_category_dict.py +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/get_zone_conditioning_category_dict.py @@ -23,6 +23,7 @@ class ZoneConditioningCategory: CONDITIONED_MIXED: str = "CONDITIONED MIXED" CONDITIONED_NON_RESIDENTIAL: str = "CONDITIONED NON-RESIDENTIAL" CONDITIONED_RESIDENTIAL: str = "CONDITIONED RESIDENTIAL" + CONDITIONED_RESIDENTIAL_ASSOCIATED: str = "CONDITIONED RESIDENTIAL-ASSOCIATED" SEMI_HEATED: str = "SEMI-HEATED" UNCONDITIONED: str = "UNCONDITIONED" UNENCLOSED: str = "UNENCLOSED" @@ -323,7 +324,7 @@ def get_zone_conditioning_category_dict( # Taking stock: # To this point, we have determined which zones are directly conditioned, # semi-heated, or indirectly conditioned. - # Next we determine whether the zone is residential, non-residential, or mixed. + # Next we determine whether the zone is residential, residential-associated, non-residential, or mixed. for building_segment in find_all("building_segments[*]", building): # Set building_segment_is_residential and building_segment_is_nonresidential flags building_segment_is_residential = False @@ -451,6 +452,103 @@ def get_zone_conditioning_category_dict( zone_id ] = ZoneConditioningCategory.UNCONDITIONED # zone_1_9 + # Now we must post-process to assign CONDITIONED_RESIDENTIAL_ASSOCIATED after all zones have been categorized + + # Build floor → {residential_area, total_conditioned_area} map + floor_area_accounting = {} + + for building_segment in find_all("building_segments[*]", building): + segment_is_hospital = ( + building_segment.get("lighting_building_area_type") == "HOSPITAL" + ) + + for zone in find_all("zones[*]", building_segment): + zone_id = zone["id"] + category = zone_conditioning_category_dict.get(zone_id) + + # Only consider directly or indirectly conditioned zones for floor-area ratio + if category not in [ + ZoneConditioningCategory.CONDITIONED_RESIDENTIAL, + ZoneConditioningCategory.CONDITIONED_NON_RESIDENTIAL, + ZoneConditioningCategory.CONDITIONED_MIXED, + ]: + continue + + floor = zone.get("floor_name") + if floor is None: + continue + + zone_area = sum(find_all("spaces[*].floor_area", zone), ZERO.AREA) + + is_res = category == ZoneConditioningCategory.CONDITIONED_RESIDENTIAL + + if floor not in floor_area_accounting: + floor_area_accounting[floor] = { + "res_area": ZERO.AREA, + "total_area": ZERO.AREA, + } + + floor_area_accounting[floor]["total_area"] += zone_area + if is_res: + floor_area_accounting[floor]["res_area"] += zone_area + + # Assign CONDITIONED_RESIDENTIAL_ASSOCIATED where applicable + residential_support_space_types = { + "CORRIDOR_ALL_OTHERS", + "FACILITY_FOR_VISUALLY_IMPAIRED_CORRIDOR", + "HEALTHCARE_FACILITY_HOSPITAL_CORRIDOR", + "LOBBY_ELEVATOR", + "STAIRWELL", + "RESTROOM_ALL_OTHERS", + "FACILITY_FOR_VISUALLY_IMPAIRED_RESTROOM", + "NONE", + } + + for building_segment in find_all("building_segments[*]", building): + segment_is_hospital = ( + building_segment.get("lighting_building_area_type") == "HOSPITAL" + ) + if segment_is_hospital: + continue # Rule does not apply in hospitals + + for zone in find_all("zones[*]", building_segment): + zone_id = zone["id"] + if ( + zone_conditioning_category_dict.get(zone_id) + != ZoneConditioningCategory.CONDITIONED_NON_RESIDENTIAL + ): + continue + + floor = zone.get("floor_name", None) + if floor is None or floor not in floor_area_accounting: + continue + + tally = floor_area_accounting[floor] + if tally["total_area"] == ZERO.AREA: + continue + + res_ratio = tally["res_area"] / tally["total_area"] + if res_ratio <= 0.75: + continue # floor not predominantly residential + + # Determine if this zone is composed primarily of support spaces + zone_space_types = [ + space.get("lighting_space_type") + for space in find_all("spaces[*]", zone) + ] + + if not zone_space_types: + continue + + # Zone is considered "support" if ALL space types fall into known support categories + if all( + space_type in residential_support_space_types + for space_type in zone_space_types + ): + zone_conditioning_category_dict[ + zone_id + ] = ZoneConditioningCategory.CONDITIONED_RESIDENTIAL_ASSOCIATED + return zone_conditioning_category_dict diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/get_zone_peak_internal_load_floor_area_dict.py b/rct229/rulesets/ashrae9012022/ruleset_functions/get_zone_peak_internal_load_floor_area_dict.py new file mode 100644 index 0000000000..c80ca6c6be --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/get_zone_peak_internal_load_floor_area_dict.py @@ -0,0 +1,94 @@ +from typing import Literal + +from pint import Quantity +from rct229.utils.assertions import getattr_ +from rct229.utils.jsonpath_utils import find_all +from rct229.utils.pint_utils import ZERO +from rct229.utils.utility_functions import ( + find_exactly_one_schedule, + find_exactly_one_zone, +) + + +def get_zone_peak_internal_load_floor_area_dict( + rmd: dict, zone_id: str +) -> dict[Literal["peak", "area"], Quantity]: + """ + Finds the peak coincident internal loads of a zone and returns the value in btu/h/ft2 + The function returns a dict giving 2 values: {"PEAK":total peak btu/h/ft2 in the zone, "AREA":total zone area} the + total peak btu/sf is the internal non-coincident peak loads in all spaces in the zone + + The function does not raise exception for missing values rather it applies default values. + + Parameters + ---------- + rmd: dict + A dictionary representing a RuleModelInstance object as defined by the ASHRAE229 schema + zone_id: string + zone id + + Returns + ------- + result: dict + a dictionary that contains two keys, peak, and area. + """ + zone = find_exactly_one_zone(rmd, zone_id) + zone_area = ZERO.AREA + zone_load = ZERO.POWER + + for space in find_all("$.spaces[*]", zone): + space_area = space.get("floor_area", ZERO.AREA) + zone_area += space_area + for light in find_all("$.interior_lighting[*]", space): + lighting_design_schedule = find_exactly_one_schedule( + rmd, + getattr_(light, "interior_lighting", "lighting_multiplier_schedule"), + ) + lighting_max_schedule_fraction = max( + getattr_( + lighting_design_schedule, + "lighting_multiplier_schedule", + "hourly_cooling_design_day", + ) + ) + zone_load += ( + light.get("power_per_area", ZERO.POWER_PER_AREA) + * space_area + * lighting_max_schedule_fraction + ) + + for equipment in find_all("$.miscellaneous_equipment[*]", space): + equipment_design_schedule = find_exactly_one_schedule( + rmd, + getattr_(equipment, "miscellaneous_equipment", "multiplier_schedule"), + ) + equipment_max_schedule_fraction = max( + getattr_( + equipment_design_schedule, + "multiplier_schedule", + "hourly_cooling_design_day", + ) + ) + zone_load += ( + equipment.get("power", ZERO.POWER) * equipment_max_schedule_fraction + ) + + # allows no occupants data in a zone + occupant_max_schedule_fraction = 0.0 + if space.get("occupant_multiplier_schedule"): + occupant_design_schedule = find_exactly_one_schedule( + rmd, space["occupant_multiplier_schedule"] + ) + occupant_max_schedule_fraction = max( + getattr_( + occupant_design_schedule, + "occupant_multiplier", + "hourly_cooling_design_day", + ) + ) + zone_load += ( + space.get("occupant_sensible_heat_gain", ZERO.POWER) + + space.get("occupant_latent_heat_gain", ZERO.POWER) + ) * occupant_max_schedule_fraction + + return {"peak": zone_load, "area": zone_area} diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/get_zone_peak_internal_load_floor_area_dict_test.py b/rct229/rulesets/ashrae9012022/ruleset_functions/get_zone_peak_internal_load_floor_area_dict_test.py new file mode 100644 index 0000000000..c2cb6733e0 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/get_zone_peak_internal_load_floor_area_dict_test.py @@ -0,0 +1,165 @@ +from rct229.rulesets.ashrae9012022.ruleset_functions.get_zone_peak_internal_load_floor_area_dict import ( + get_zone_peak_internal_load_floor_area_dict, +) +from rct229.schema.config import ureg +from rct229.schema.schema_utils import quantify_rmd +from rct229.schema.validate import schema_validate_rpd + +TEST_RMD = { + "id": "test_rmd", + "buildings": [ + { + "id": "Building 1", + "building_open_schedule": "Required Building Schedule 1", + "building_segments": [ + { + "id": "Building Segment 1", + "zones": [ + # this zone shall give a total floor area of 500 + # total zone peak load of 1500 W + { + "id": "Thermal Zone 1", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "spaces": [ + { + "id": "space 1", + "floor_area": 500, + "interior_lighting": [ + { + "id": "interior_lighting_1", + "lighting_multiplier_schedule": "lighting_schedule_1", + "power_per_area": 1.0, + } + ], + "miscellaneous_equipment": [ + { + "id": "miscellaneous_equipment_1", + "multiplier_schedule": "miscellaneous_equipment_schedule_1", + "power": 500, + } + ], + "occupant_multiplier_schedule": "occupant_schedule_1", + "occupant_sensible_heat_gain": 125, + "occupant_latent_heat_gain": 125, + } + ], + }, + # this zone shall give a total floor area of 500 + # total zone peak load of 1000 W due to no lights + { + "id": "Thermal Zone 2", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "spaces": [ + { + "id": "space 2", + "floor_area": 500, + "miscellaneous_equipment": [ + { + "id": "miscellaneous_equipment_1", + "multiplier_schedule": "miscellaneous_equipment_schedule_1", + "power": 500, + } + ], + "occupant_multiplier_schedule": "occupant_schedule_1", + "occupant_sensible_heat_gain": 125, + "occupant_latent_heat_gain": 125, + } + ], + }, + # this zone shall give a total floor area of 500 + # total zone peak load of 1000 W due to no occupants + { + "id": "Thermal Zone 3", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + "spaces": [ + { + "id": "space 3", + "floor_area": 500, + "interior_lighting": [ + { + "id": "interior_lighting_1", + "lighting_multiplier_schedule": "lighting_schedule_1", + "power_per_area": 1.0, + } + ], + "miscellaneous_equipment": [ + { + "id": "miscellaneous_equipment_1", + "multiplier_schedule": "miscellaneous_equipment_schedule_1", + "power": 500, + } + ], + } + ], + }, + { + # this zone has no space, space area is 0 + # total zone peak load of 0 W + "id": "Thermal Zone 4", + "thermostat_cooling_setpoint_schedule": "Required Cooling Schedule 1", + "thermostat_heating_setpoint_schedule": "Required Heating Schedule 1", + }, + ], + }, + ], + } + ], + "schedules": [ + {"id": "lighting_schedule_1", "hourly_cooling_design_day": [1] * 24}, + { + "id": "miscellaneous_equipment_schedule_1", + "hourly_cooling_design_day": [1] * 24, + }, + {"id": "occupant_schedule_1", "hourly_cooling_design_day": [1] * 23 + [2]}, + ], + "type": "BASELINE_0", +} + +TEST_RPD_FULL = { + "id": "229", + "ruleset_model_descriptions": [TEST_RMD], + "metadata": { + "schema_author": "ASHRAE SPC 229 Schema Working Group", + "schema_name": "Ruleset Evaluation Schema", + "schema_version": "0.1.3", + "author": "author_example", + "description": "description_example", + "time_of_creation": "2024-02-12T09:00Z", + }, +} + +TEST_RMD = quantify_rmd(TEST_RPD_FULL)["ruleset_model_descriptions"][0] + + +def test__TEST_RPD__is_valid(): + schema_validation_result = schema_validate_rpd(TEST_RPD_FULL) + assert schema_validation_result[ + "passed" + ], f"Schema error: {schema_validation_result['error']}" + + +def test__get_zone_peak_internal_load_floor_area_dict__all_three_component(): + zone_info = get_zone_peak_internal_load_floor_area_dict(TEST_RMD, "Thermal Zone 1") + assert abs(zone_info["peak"] - 1500 * ureg("watt")) < 0.00001 * ureg("watt") + assert abs(zone_info["area"] - 500 * ureg("m2")) < 0.00001 * ureg("m2") + + +def test__get_zone_peak_internal_load_floor_area_dict__no_lighting_component(): + zone_info = get_zone_peak_internal_load_floor_area_dict(TEST_RMD, "Thermal Zone 2") + assert abs(zone_info["peak"] - 1000 * ureg("watt")) < 0.00001 * ureg("watt") + assert abs(zone_info["area"] - 500 * ureg("m2")) < 0.00001 * ureg("m2") + + +def test__get_zone_peak_internal_load_floor_area_dict__no_occupants_component(): + zone_info = get_zone_peak_internal_load_floor_area_dict(TEST_RMD, "Thermal Zone 3") + assert abs(zone_info["peak"] - 1000 * ureg("watt")) < 0.00001 * ureg("watt") + assert abs(zone_info["area"] - 500 * ureg("m2")) < 0.00001 * ureg("m2") + + +def test__get_zone_peak_internal_load_floor_area_dict__no_space_component(): + zone_info = get_zone_peak_internal_load_floor_area_dict(TEST_RMD, "Thermal Zone 4") + assert abs(zone_info["peak"] - 0 * ureg("watt")) < 0.00001 * ureg("watt") + assert abs(zone_info["area"] - 0 * ureg("m2")) < 0.00001 * ureg("m2") diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/get_zone_target_baseline_system.py b/rct229/rulesets/ashrae9012022/ruleset_functions/get_zone_target_baseline_system.py new file mode 100644 index 0000000000..b4d478bed9 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/get_zone_target_baseline_system.py @@ -0,0 +1,268 @@ +from typing import TypedDict + +from pydash import juxtapose +from rct229.rule_engine.memoize import memoize +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( + HVAC_SYS, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.does_zone_meet_G3_2_1_2_a import ( + does_zone_meet_g3_2_1_2_a, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.does_zone_meet_G3_2_1_2_b import ( + does_zone_meet_g3_2_1_2_b, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.does_zone_meet_G3_2_1_2_c import ( + does_zone_meet_g3_2_1_2_c, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.expected_system_type_from_table_g311a_dict import ( + expected_system_type_from_table_g3_1_1_dict, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.get_zones_computer_rooms import ( + get_zone_computer_rooms, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.get_zone_peak_internal_load_floor_area_dict import ( + get_zone_peak_internal_load_floor_area_dict, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.get_computer_zones_peak_cooling_load import ( + get_total_computer_zones_peak_cooling_load, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.get_hvac_building_area_types_and_zones_dict import ( + get_hvac_building_area_types_and_zones_dict, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.get_number_of_floors import ( + get_number_of_floors, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.get_predominant_hvac_building_area_type import ( + get_predominant_hvac_building_area_type, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.get_zone_hvac_bat import ( + get_zone_hvac_bat_dict, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.g3212_exceptions.g3212_sub_functions.is_zone_mechanically_cooled import ( + is_zone_mechanically_cooled, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.get_zone_conditioning_category_dict import ( + ZoneConditioningCategory as ZCC, + get_zone_conditioning_category_rmd_dict, +) +from rct229.rulesets.ashrae9012022.ruleset_functions.is_cz_0_to_3a_bool import ( + is_cz_0_to_3a_bool, +) +from rct229.schema.config import ureg +from rct229.utils.jsonpath_utils import find_all + +BUILDING_AREA_20000_ft2 = 20000 * ureg("ft2") +BUILDING_AREA_40000_ft2 = 40000 * ureg("ft2") +BUILDING_AREA_150000_ft2 = 150000 * ureg("ft2") +REQ_FL_6 = 6 +COMPUTER_ROOM_PEAK_COOLING_LOAD_600000_BTUH = 600000 * ureg("Btu/hr") +COMPUTER_ROOM_PEAK_COOLING_LOAD_3000000_BTUH = 3000000 * ureg("Btu/hr") + + +class ZoneandSystem(TypedDict): + expected_system_type: str + system_origin: str + + +class SYSTEMORIGIN: + G311 = "G3_1_1" + G3212A = "G3_2_1_2a" # exception for zones with different peak internal loads + G3212B = "G3_2_1_2b" # exception for laboratory exhaust + G3212C = "G3_2_1_2c" # exception for heating-only stairwells, vestibules, etc + G3212D = ( + "G3_2_1_2d" # exception for heating-only zones with cooling in proposed design + ) + G3212E = "G3_2_1_2e" # exception for computer rooms. mislabeled in the standard as a duplicated G3.1.1.2d. Corrected here to e. + G3212F = "G3_2_1_2f" # exception for residential-associated zones. Corrected here to f due to mis-labelling above. + + +@memoize +def get_zone_target_baseline_system( + rmd_b: dict, rmd_p: dict, climate_zone_b: str +) -> dict[str, ZoneandSystem]: + """ + Following G3.1.1, determines the baseline system type for each zone in a building + + Parameters + ---------- + rmd_b: json + RMD at RuleSetModelDescription level + rmd_p: json + RMD at RuleSetModelDescription level + climate_zone_b: str + baseline climate zone + + + Returns + ------- + zones_and_systems: a dictionary with zone / list pairs where the first value in the list is the expected system type (ex SYS_3) and the second value is the rule used to choose the system, (eg "G3_1_1e"): zones_and_systems[zone]["EXPECTED_SYSTEM_TYPE"] = SYS_3; zones_and_systems[zone]["SYSTEM_ORIGIN"] = "G3_1_1e" + + """ + + zone_conditioning_category_dict = get_zone_conditioning_category_rmd_dict( + climate_zone_b, rmd_b + ) + + ( + list_building_area_types_and_zones_b, + predominant_building_area_type_b, + num_floors_b, + ) = juxtapose( + lambda cz, rmd: get_hvac_building_area_types_and_zones_dict(cz, rmd), + lambda cz, rmd: get_predominant_hvac_building_area_type(cz, rmd), + lambda cz, rmd: get_number_of_floors(cz, rmd), + )( + climate_zone_b, rmd_b + ) + + floor_area_b = sum( + [ + list_building_area_types_and_zones_b[bat]["floor_area"] + for bat in list_building_area_types_and_zones_b + ] + ) + expected_system_type_dict_b = expected_system_type_from_table_g3_1_1_dict( + predominant_building_area_type_b, climate_zone_b, num_floors_b, floor_area_b + ) + + is_cz_0_to_3a_result_bool = is_cz_0_to_3a_bool(climate_zone_b) + + zones_and_systems_b = { + zone_b["id"]: expected_system_type_dict_b + for zone_b in find_all("$.buildings[*].building_segments[*].zones[*]", rmd_b) + if zone_conditioning_category_dict[zone_b["id"]] + in ( + ZCC.CONDITIONED_RESIDENTIAL, + ZCC.CONDITIONED_NON_RESIDENTIAL, + ZCC.CONDITIONED_MIXED, + ) + } + + total_computer_zones_peak_cooling_load_b = ( + get_total_computer_zones_peak_cooling_load(rmd_b) + ) + + # go through each exception to Table G3.1.1 in order + # G3.1.1b + if floor_area_b > BUILDING_AREA_40000_ft2: + for building_area_type in list_building_area_types_and_zones_b: + if building_area_type != predominant_building_area_type_b and ( + list_building_area_types_and_zones_b[building_area_type]["floor_area"] + >= BUILDING_AREA_20000_ft2 + ): + secondary_system_type_b = expected_system_type_from_table_g3_1_1_dict( + building_area_type, + climate_zone_b, + num_floors_b, + floor_area_b, + ) + for zone_id_b in zones_and_systems_b: + if ( + zone_id_b + in list_building_area_types_and_zones_b[building_area_type][ + "zone_ids" + ] + ): + zones_and_systems_b[zone_id_b] = { + "expected_system_type": secondary_system_type_b[ + "expected_system_type" + ], + "system_origin": SYSTEMORIGIN.G311, + } + + for zone_id_b in zones_and_systems_b: + # G3.2.1.2.a + if does_zone_meet_g3_2_1_2_a(rmd_b, zone_id_b, zones_and_systems_b): + zones_and_systems_b[zone_id_b] = { + "system_origin": SYSTEMORIGIN.G3212A, + "expected_system_type": HVAC_SYS.SYS_4 + if is_cz_0_to_3a_result_bool + else HVAC_SYS.SYS_3, + } + + # G3.2.1.2.b + if does_zone_meet_g3_2_1_2_b(rmd_b, zone_id_b): + zones_and_systems_b[zone_id_b] = { + "system_origin": SYSTEMORIGIN.G3212B, + "expected_system_type": HVAC_SYS.SYS_5 + if num_floors_b < REQ_FL_6 and floor_area_b < BUILDING_AREA_150000_ft2 + else HVAC_SYS.SYS_7, + } + + # G3.2.1.2.c + if does_zone_meet_g3_2_1_2_c(rmd_b, rmd_p, zone_id_b): + zones_and_systems_b[zone_id_b] = { + "system_origin": SYSTEMORIGIN.G3212C, + "expected_system_type": HVAC_SYS.SYS_10 + if is_cz_0_to_3a_result_bool + else HVAC_SYS.SYS_9, + } + + # G3.2.1.2d HVAC zones designed with heating-only systems in the proposed design serving storage rooms, + # stairwells, vestibules, electrical/mechanical rooms, and restrooms not exhausting or transferring air from + # mechanically cooled thermal zones in the proposed design + if is_zone_mechanically_cooled(rmd_b, zone_id_b) and zones_and_systems_b[ + zone_id_b + ]["expected_system_type"] in ( + HVAC_SYS.SYS_9, + HVAC_SYS.SYS_10, + ): + zone_hvac_bat_dict_b = get_zone_hvac_bat_dict(rmd_b, zone_id_b) + + zones_and_systems_b[zone_id_b] = { + "system_origin": SYSTEMORIGIN.G3212D, + "expected_system_type": expected_system_type_from_table_g3_1_1_dict( + max(zone_hvac_bat_dict_b, key=zone_hvac_bat_dict_b.get), + climate_zone_b, + num_floors_b, + floor_area_b, + )["expected_system_type"], + } + + computer_room_zones_dict_b = get_zone_computer_rooms(rmd_b) + # G3.2.1.2e The baseline HVAC system serving HVAC zones that include computer rooms + if zone_id_b in computer_room_zones_dict_b: + if ( + total_computer_zones_peak_cooling_load_b + > COMPUTER_ROOM_PEAK_COOLING_LOAD_3000000_BTUH + ): + zones_and_systems_b[zone_id_b] = { + "expected_system_type": HVAC_SYS.SYS_11_1, + "system_origin": SYSTEMORIGIN.G3212E, + } + + elif get_zone_peak_internal_load_floor_area_dict(rmd_b, zone_id_b)[ + "peak" + ] > COMPUTER_ROOM_PEAK_COOLING_LOAD_600000_BTUH and ( + zones_and_systems_b[zone_id_b]["expected_system_type"] + in ( + HVAC_SYS.SYS_7, + HVAC_SYS.SYS_8, + ) + ): + zones_and_systems_b[zone_id_b] = { + "expected_system_type": HVAC_SYS.SYS_11_1, + "system_origin": SYSTEMORIGIN.G3212E, + } + + else: + zones_and_systems_b[zone_id_b] = { + "expected_system_type": HVAC_SYS.SYS_4 + if is_cz_0_to_3a_result_bool + else HVAC_SYS.SYS_3, + "system_origin": SYSTEMORIGIN.G3212E, + } + + # G3.2.1.2f + if ( + zone_conditioning_category_dict[zone_id_b] + == ZCC.CONDITIONED_RESIDENTIAL_ASSOCIATED + ): + zones_and_systems_b[zone_id_b] = { + "expected_system_type": HVAC_SYS.SYS_4 + if is_cz_0_to_3a_result_bool + else HVAC_SYS.SYS_3, + "system_origin": SYSTEMORIGIN.G3212F, + } + + return zones_and_systems_b diff --git a/rct229/rulesets/ashrae9012022/ruleset_functions/is_cz_0_to_3a_bool.py b/rct229/rulesets/ashrae9012022/ruleset_functions/is_cz_0_to_3a_bool.py new file mode 100644 index 0000000000..03049223f7 --- /dev/null +++ b/rct229/rulesets/ashrae9012022/ruleset_functions/is_cz_0_to_3a_bool.py @@ -0,0 +1,30 @@ +from rct229.schema.schema_enums import SchemaEnums + +CLIMATE_ZONE_ASHRAE901_2019 = SchemaEnums.schema_enums[ + "ClimateZoneOptions2019ASHRAE901" +] + +APPLICABLE_LIST = [ + CLIMATE_ZONE_ASHRAE901_2019.CZ0A, + CLIMATE_ZONE_ASHRAE901_2019.CZ0B, + CLIMATE_ZONE_ASHRAE901_2019.CZ1A, + CLIMATE_ZONE_ASHRAE901_2019.CZ1B, + CLIMATE_ZONE_ASHRAE901_2019.CZ2A, + CLIMATE_ZONE_ASHRAE901_2019.CZ2B, + CLIMATE_ZONE_ASHRAE901_2019.CZ3A, +] + + +def is_cz_0_to_3a_bool(climate_zone: str) -> bool: + """ + Determines whether the building is in climate zone 0 to 3a - used for Appendix G Table G3.1.1-3 + + Parameters + ---------- + climate_zone: Str One of the option in the `ClimateZoneOptions2019ASHRAE901` from ASHRAE 901 2019 sub schema + + Returns + ------- + Bool: true if it matches to the criteria, false otherwise. + """ + return climate_zone in APPLICABLE_LIST diff --git a/rct229/rulesets/ashrae9012022/section12/section12rule5.py b/rct229/rulesets/ashrae9012022/section12/section12rule5.py index 34bb173865..00992149f2 100644 --- a/rct229/rulesets/ashrae9012022/section12/section12rule5.py +++ b/rct229/rulesets/ashrae9012022/section12/section12rule5.py @@ -2,7 +2,7 @@ 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_description -from rct229.rulesets.ashrae9012019 import PROPOSED +from rct229.rulesets.ashrae9012022 import PROPOSED from rct229.schema.config import ureg from rct229.schema.schema_enums import SchemaEnums from rct229.utils.assertions import getattr_ diff --git a/rct229/rulesets/ashrae9012022/section21/section21rule19.py b/rct229/rulesets/ashrae9012022/section21/section21rule19.py index 04eaf01674..94259f689e 100644 --- a/rct229/rulesets/ashrae9012022/section21/section21rule19.py +++ b/rct229/rulesets/ashrae9012022/section21/section21rule19.py @@ -1,11 +1,11 @@ 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_description -from rct229.rulesets.ashrae9012019 import BASELINE_0 -from rct229.rulesets.ashrae9012019.ruleset_functions.baseline_systems.baseline_system_util import ( +from rct229.rulesets.ashrae9012022 import BASELINE_0 +from rct229.rulesets.ashrae9012022.ruleset_functions.baseline_systems.baseline_system_util import ( HVAC_SYS, ) -from rct229.rulesets.ashrae9012019.ruleset_functions.get_baseline_system_types import ( +from rct229.rulesets.ashrae9012022.ruleset_functions.get_baseline_system_types import ( get_baseline_system_types, ) from rct229.schema.schema_enums import SchemaEnums diff --git a/rct229/rulesets/ashrae9012022/section5/section5rule42.py b/rct229/rulesets/ashrae9012022/section5/section5rule42.py index ac14defdb5..6d4a34e302 100644 --- a/rct229/rulesets/ashrae9012022/section5/section5rule42.py +++ b/rct229/rulesets/ashrae9012022/section5/section5rule42.py @@ -1,11 +1,11 @@ from rct229.rule_engine.partial_rule_definition import PartialRuleDefinition from rct229.rule_engine.rule_list_indexed_base import RuleDefinitionListIndexedBase from rct229.rule_engine.ruleset_model_factory import produce_ruleset_model_description -from rct229.rulesets.ashrae9012019 import PROPOSED -from rct229.rulesets.ashrae9012019.ruleset_functions.get_surface_conditioning_category_dict import ( +from rct229.rulesets.ashrae9012022 import PROPOSED +from rct229.rulesets.ashrae9012022.ruleset_functions.get_surface_conditioning_category_dict import ( SurfaceConditioningCategory as SCC, ) -from rct229.rulesets.ashrae9012019.ruleset_functions.get_surface_conditioning_category_dict import ( +from rct229.rulesets.ashrae9012022.ruleset_functions.get_surface_conditioning_category_dict import ( get_surface_conditioning_category_dict, )