Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
weilixu committed Jan 31, 2025
2 parents 951991c + f4fe688 commit 5268917
Show file tree
Hide file tree
Showing 260 changed files with 179,180 additions and 214,557 deletions.
1 change: 1 addition & 0 deletions .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ jobs:
- name: Install Poetry
uses: snok/install-poetry@v1
with:
version: 1.8.4
virtualenvs-create: true
virtualenvs-in-project: true
installer-parallel: true
Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
# is_space_a_computer_room

**Description:** 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 lighting space type definition equals COMPUTER_ROOM, or, if the space type is not defined, if the total misc W/sf exceeds 20 W/sf per the definition of a computer room in 90.1 Section 3.
**Description:** 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.

**Inputs:**
- **B-RMI,P-RMI**: The applicable ruleset model instance.
- **Space_obj**: The space to assess whether or not it is a computer room.

**Returns:**
- **is_space_a_computer_room**: 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 lighting space type definition equals COMPUTER_ROOM, or, if the space type is not defined, if the total misc W/sf exceeds 20 W/sf per the definition of a computer room in 90.1 Section 3.
- **is_space_a_computer_room**: 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.


**Function Call:** None


## Logic:
- Set is_space_a_computer_room to false: `is_space_a_computer_room = false`
- Check if the lighting space type classification equals computer room: `if space_p.lighting_space_type == "COMPUTER_ROOM": is_space_a_computer_room = true`
- Else, carry on with logic: `Else:`
- Reset the total_space_misc_Wattage_including_multiplier variable: `total_space_misc_Wattage_including_multiplier = 0`
- For each miscellaneous equipment id in the space: `For misc_p in space_p.miscellaneous_equipment:`
- Check if the energy type is electricity, if not then skip this misc_p: `if misc_p.energy_type = "ELECTRICITY":`
- Set is_space_a_computer_room to false: `is_space_a_computer_room = false`
- Set the total_space_misc_Wattage_including_multiplier variable: `total_space_misc_Wattage_including_multiplier = 0`
- For each miscellaneous equipment id in the space: `For misc_p in space_p.miscellaneous_equipment:`
- Check if the energy type is electricity, if not then skip this misc_p: `if misc_p.energy_type = "ELECTRICITY":`
- Check if the energy type is INFORMATION_TECHNOLOGY_EQUIPMENT: `if misc_p.type == "INFORMATION_TECHNOLOGY_EQUIPMENT":`
- Get the miscellaneous equipment Wattage: `misc_p_power = misc_p.power`
- Get the maximum of (1.0 and the maximum value in the miscellaneous equipment multiplier schedule) (convert the schedule to an 8760 schedule using a function if needed): `misc_p_multiplier_value = MAX(1,Max(misc_p.multiplier_schedule.hourly_values))`
- Calculate (misc_p_power * misc_p_multiplier_value) for this misc_p: `misc_p_total_Wattage = misc_p_power * misc_p_multiplier_value`
- Add to the running total of Wattage associated with the space: `total_space_misc_Wattage_including_multiplier = total_space_misc_Wattage_including_multiplier + misc_p_total_Wattage`
- Get the square footage of the space: `floor_area = space_p.floor_area`
- Calculate the maximum equipment power density for the space (total design Wattage/space sf): `space_EPD = total_space_misc_Wattage_including_multiplier/floor_area`
- Check if it is greater than 20 W/sf: `if space_EPD > 20: is_space_a_computer_room = true`
- Get the square footage of the space: `floor_area = space_p.floor_area`
- Calculate the maximum equipment power density for the space (total design Wattage/space sf): `space_EPD = total_space_misc_Wattage_including_multiplier/floor_area`
- Check if it is greater than 20 W/sf: `if space_EPD > 20: is_space_a_computer_room = true`

**Returns** `is_space_a_computer_room`

Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
# are_all_terminal_types_VAV

**Description:** Returns TRUE if all of the terminal unit types input to this function are variable air volume (VAV). It returns FALSE if any of the terminal units are of a type other than variable air volume (VAV).
**Description:** Returns TRUE if all of the terminal unit types input to this function are variable air volume (VAV) or are equal to null. It returns FALSE if any of the terminal units are of a type other than variable air volume (VAV) or null.

**Inputs:**
- **B-RMR**: To evaluate if the terminal unit types are variable air volume (VAV).
- **terminal_unit_id_list**: List of terminal units to assess.

**Returns:**
- **are_all_terminal_types_VAV**: The function returns TRUE if all of the terminal unit types input to this function are variable air volume (VAV). It returns FALSE if any of the terminal units are of a type other than variable air volume (VAV).
- **are_all_terminal_types_VAV**: The function returns TRUE if all of the terminal unit types input to this function are variable air volume (VAV) or are equal to null. It returns FALSE if any of the terminal units are of a type other than variable air volume (VAV) or null.

**Function Call:** None

## Logic:
- Set are_all_terminal_types_VAV = TRUE: `are_all_terminal_types_VAV = TRUE`
- For each terminal_b in the list of terminal units: `For terminal_b in terminal_unit_id_list:`
- Create an object for the terminal unit: `terminal_b = terminal_b.id`
- Check if the terminal unit type is not variable air volume (VAV): `if terminal_b.type != "VARIABLE_AIR_VOLUME":`
- Check if the terminal unit type is not variable air volume (VAV) or Null: `if terminal_b.type != "VARIABLE_AIR_VOLUME" AND terminal_b.type != Null:`
- Set are_all_terminal_types_VAV = FALSE: `are_all_terminal_types_VAV = FALSE`
- Leave the loop: `break`

**Returns** `return are_all_terminal_types_VAV`
**Returns** `return are_all_terminal_types_VAV`

**Notes:**
1. This function is always called after "if is_hvac_sys_fan_sys_VSD(B_RMR, hvac_b.id) == TRUE", so we are not requiring that VAV is defined both on system and terminal level.

**[Back](../../../_toc.md)**
**[Back](../_toc.md)**
64 changes: 64 additions & 0 deletions docs/ruleset_functions/compare_swh_dist_systems_and_components.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
## compare_swh_dist_systems_and_components

Description: This function compares all sub-components of a given SWH Distribution System ID between two models. The function also accepts

Inputs:
- **RMD1**
- **RMD2**
- **compare_context_str**
- **swh_distribution_id**

Returns:
- **errors**: An array of errors encountered in this function and in compare_context_pair. If errors is an empty array, then all elements that were expected to match do match

Function Call:

- **compare_context_pair** - there is no RDS for this function, but it is a function developed for Rule 1-6 that compares two elements
- **get_component_by_id**

Data Lookup: None

Logic:
- create the list errors: `errors = []`
- use the function get_swh_components_associated_with_each_swh_distribution_system to get a dictionary of SWH equipment associated with each Distribution System for model 1 (pumps, tanks, SWH use, distribution system, etc): `RMD1_swh_system_and_equip_dict = get_swh_components_associated_with_each_swh_distribution_system(RMD1)`
- use the function get_swh_components_associated_with_each_swh_distribution_system to get a dictionary of SWH equipment associated with each Distribution System for model 2 (pumps, tanks, SWH use, distribution system, etc): `RMD2_swh_system_and_equip_dict = get_swh_components_associated_with_each_swh_distribution_system(RMD2)`

- check if this distribution ID is in model 1: `if RMD1_swh_system_and_equip_dict[swh_distribution_id]:`
- get the dictionary of all SWH equipment (pumps, tanks, SWH use, dist system, etc) for the given swh_distribution_id for RMD1: `RMD1_swh_equipment_dict = RMD1_swh_system_and_equip_dict[swh_distribution_id]`
- get the actual distribution equipment associated with this ID from model 1: `RMD1_swh_distribution = get_component_by_id(RMD1, swh_distribution_id)`
- otherwise, add an error to the list: `else: errors.append("SWH Distribution System " + swh_distribution_id + " not found in one of the two RMDs")`

- check if this distribution ID is in model 2: `if RMD2_swh_system_and_equip_dict[swh_distribution_id]:`
- get the dictionary of all SWH equipment (pumps, tanks, SWH use, dist system, etc) for the given swh_distribution_id for RMD2: `RMD2_swh_equipment_dict = RMD2_swh_system_and_equip_dict[swh_distribution_id]`
- get the actual distribution equipment associated with this ID from model 2: `RMD2_swh_distribution = get_component_by_id(RMD2, swh_distribution_id)`
- otherwise...: `else:`
- add an error an error to the list: `errors.append("SWH Distribution System " + swh_distribution_id + " not found in one of the two RMDs")`


- the following two lines set the longer of the two swh_distribution lists to the index_content_list and the shorter to the compare_context_list
- `if len(RMD1_swh_distribution) > len(RMD2_swh_distribution): index_content_list = RMD1_swh_distribution, compare_context_list = RMD2_swh_distribution`
- `else: index_content_list = RMD2_swh_distribution, compare_context_list = RMD1_swh_distribution`
- Compare the distribution in the two models using the function compare_context_pair. compare_context_pair is recursive, so by sending the function the distribution systems, it is also checking the tanks and piping that are child objects of the distribution systems. If the comparison fails, we add an error to the errors list indicating the id of the swh distribution system that failed comparison : `compare_context_pair(index_context=index_content_list, compare_context=compare_context_list, extra_schema='extra_schema_for_SWH_comparison.json', error_msg_list=errors, search_key = compare_context_str)`
- In addition to the equipment connected to the distribution system, there's also equipment that's part of the service water heating distribution system that are not direct child objects of the distribution system. We need to check these objects.
- first, check ServiceWaterHeatingEquipment - when we execute compare_context_pair, this will also check any child objects that exist (SolarThermal and SWH validation point). Start by checking if there are the same number of objects in models 1 and 2. We need to do the length check here because it's not checked implicitly as part of compare_context_pair. For example, if there are more pieces of equipment in the model 1 than model 2, comparing each item found in model 2 could return a false positive: `if len(RMD1_swh_equipment_dict["SWHHeatingEq"]) == len(RMD2_swh_equipment_dict["SWHHeatingEq"]):`
- look at each SWHEquipment in the model 1: `for swh_eq_id in RMD1_swh_equipment_dict["SWHHeatingEq"]:`
- get the SWH equipment for models 1 and 2: `swh_eq_1 = get_component_by_id(RMD1, swh_eq_id), swh_eq_2 = get_component_by_id(RMD2, swh_eq_id)`
- the following two lines set the longer of the two equipment lists to the index_content_list and the shorter to the compare_context_list
- `if len(swh_eq_1) > len(swh_eq_2): index_content_list = swh_eq_1, compare_context_list = swh_eq_2`
- `else: index_content_list = swh_eq_2, compare_context_list = swh_eq_1`
- compare the two SWH equipment using compare_context_pair. By passing in the same errors list, any new errors get appended to the list. If the comparison fails, we add an error to the errors list indicating the id of the swh equipment that failed comparison: `compare_context_pair(index_context=index_content_list, compare_context=compare_context_list, extra_schema='extra_schema_for_SWH_comparison.json', error_msg_list=errors, search_key = compare_context_str): errors.append("Comparison of SWH Equipment: " + swh_eq_id + " failed.")`
- otherwise there is an unequal number of equipment entries. Add our own error to the errors list: `else: errors.append("Unequal numbers of SWH Equipment between the two models for " + swh_distribution_id(`
- next, check Pumps - this will also recursively check PumpOutputValidationPointPumpOutputValidationPoint: `if len(RMD1_swh_equipment_dict["Pumps"]) == len(RMD2_swh_equipment_dict["Pumps"]):`
- look at each SWHEquipment in the proposed model: `for pump_id in RMD1_swh_equipment_dict["Pumps"]:`
- get the pumps for models 1 and 2: `pump_1 = get_component_by_id(RMD1, pump_id), pump_2 = get_component_by_id(RMD2, pump_id)`
- the following two lines set the longer of the two pump lists to the index_content_list and the shorter to the compare_context_list
- `if len(pump_1) > len(pump_2): index_content_list = pump_1, compare_context_list = pump_2`
- `else: index_content_list = pump_2, compare_context_list = pump_1`
- compare the two pumps using compare_context_pair. If the comparison fails, we add an error to the errors list indicating the id of the swh pump that failed comparison: `compare_context_pair(index_context=index_content_list, compare_context=compare_context_list, extra_schema='extra_schema_for_SWH_comparison.json', error_msg_list=errors, search_key = compare_context_str)`
- otherwise, there are an an unequal number of pump entries: `else: errors.append("Unequal number of pumps in the two models for SWH Distribution System " + swh_distribution_id)`

**Returns** errors

**[Back](../_toc.md)**

**Notes:**
5 changes: 4 additions & 1 deletion docs/ruleset_functions/get_swh_equipment_type.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## get_swh_equipment_type

Description: This function determines whether the swh equipment type is one of: (ELECTRIC_RESISTANCE_INSTANTANEOUS, ELECTRIC_RESISTANCE_STORAGE, GAS_STORAGE, OTHER)
Description: This function determines whether the swh equipment type is one of: (ELECTRIC_RESISTANCE_INSTANTANEOUS, ELECTRIC_RESISTANCE_STORAGE, GAS_STORAGE, PROPANE_INSTANTANEOUS, PROPANE_STORAGE, OTHER)

## Inputs:
- **RMD**
Expand Down Expand Up @@ -31,6 +31,9 @@ Description: This function determines whether the swh equipment type is one of:
- `elsif fule_type == "NATURAL_GAS":`
- `if swh_tank_type == "INSTANTANEOUS": type = "GAS_INSTANTANEOUS"`
- `elsif swh_tank_type == "STORAGE": type = "GAS_STORAGE"`
- `elsif fule_type == "PROPANE":`
- `if swh_tank_type == "INSTANTANEOUS": type = "PROPANE_INSTANTANEOUS"`
- `elsif swh_tank_type == "STORAGE": type = "PROPANE_STORAGE"`
- `elsif fule_type == "FUEL_OIL":`
- `if swh_tank_type == "INSTANTANEOUS": type = "OIL_INSTANTANEOUS"`
- `elsif swh_tank_type == "STORAGE": type = "OIL_STORAGE"`
Expand Down
4 changes: 2 additions & 2 deletions docs/section19/Rule19-18.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@

- **Rule Assertion:**
- Case 1: If fan wattage expected equals total fan power: `if more_than_one_exhaust_fan_and_energy_rec_is_relevant_b == false and more_than_one_supply_fan_b == false and total_fan_power_b == expected_fan_wattage_b: outcome = "PASS"`
- Case 2: Elif conservative comparison equals true and fan is less than expected outcome is Pass:`elif more_than_one_supply_fan_b == false and AHJ_RA_compare == True and total_fan_power_b < expected_fan_wattage_b: PASS and raise_message "The total fan power for <insert hvac.id> is modeled as <insert total_fan_power_b> kW which is less than the expected including pressure drop adjustments for exhaust air energy recovery and MERV filters as applicable which was calculated as <insert min_fan_wattage_b> kW. Pass because this is generally considered more conservative."` `
- Case 3: Elif conservative comparison is false and fan is less than expected outcome is Fail:`elif more_than_one_supply_fan_b == false and AHJ_RA_compare == False and total_fan_power_b < expected_fan_wattage_b: "FAIL" and raise_message "The total fan power for <insert hvac.id> is modeled as <insert total_fan_power_b> kW which is less than the expected including pressure drop adjustments for exhaust air energy recovery and MERV filters as applicable which was calculated as <insert min_fan_wattage_b> kW ."`
- Case 2: Elif conservative comparison equals true and fan is less than expected outcome is Pass:`elif more_than_one_supply_fan_b == false and AHJ_RA_compare == True and total_fan_power_b < expected_fan_wattage_b: PASS and raise_message "The total fan power for <insert hvac.id> is modeled as <insert total_fan_power_b> kW which is less than the expected including pressure drop adjustments for exhaust air energy recovery and MERV filters as applicable which was calculated as <insert expected_fan_wattage_b> kW. Pass because this is generally considered more conservative."` `
- Case 3: Elif conservative comparison is false and fan is less than expected outcome is Fail:`elif more_than_one_supply_fan_b == false and AHJ_RA_compare == False and total_fan_power_b < expected_fan_wattage_b: "FAIL" and raise_message "The total fan power for <insert hvac.id> is modeled as <insert total_fan_power_b> kW which is less than the expected including pressure drop adjustments for exhaust air energy recovery and MERV filters as applicable which was calculated as <insert expected_fan_wattage_b> kW ."`
- Case 4: Elif more_than_one_supply_fan_b equals true: `elif more_than_one_supply_fan_b == true or more_than_one_exhaust_fan_and_energy_rec_is_relevant_b == true : outcome = "UNDETERMINED" and raise_message "<insert hvac.id> has more than one <insert "supply fan" if more_than_one_supply_fan_b == true> and/or more than one <insert "exhaust fan" if more_than_one_exhaust_fan_and_energy_rec_is_relevant_b == true> associated with the HVAC system in the baseline and therefore this check could not be conducted for this HVAC system. Conduct manual check for compliance with G3.1.2.9."`
- Case 5: Else, undetermined: `Else: outcome = "UNDETERMINED" and raise_message "Fan power for <hvac.id> is greater than expected per Section and Table G3.1.2.9 assuming no pressure drop adjustments (e.g., sound attenuation, air filtration, fully ducted return when required by code, airflow control devices, carbon and other gas-phase air cleaners, coil runaround loops, evaporative humidifier/coolers in series with another cooling coil, exhaust systems serving fume hoods, and laboratory and vivarium exhaust systems in high-rise buildings) per Table 6.5.3.1-2 other than <if more_than_one_exhaust_fan_and_energy_rec_is_relevant_b == false then include "energy recovery" otherwise exclude energy recovery> and MERV filters defined in the RMD (if modeled). Expected Wattage = <insert expected_fan_wattage_b> however not all pressure drop adjustments are able to be captured in the RMD so conduct manual check."`

Expand Down
Loading

0 comments on commit 5268917

Please sign in to comment.