Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ def baseline_system_type_compare(
system_type: HVAC_SYS,
target_system_type: HVAC_SYS,
exact_match: Optional[bool] = True,
use_assert: Optional[bool] = False,
) -> bool:
"""
Parameters
Expand Down Expand Up @@ -41,10 +42,11 @@ def baseline_system_type_compare(
]
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",
)
if use_assert:
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 "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
from rct229.rulesets.ashrae9012019.ruleset_functions.baseline_systems.baseline_system_util import (
HVAC_SYS,
)
from rct229.utils.assertions import RCTException
from rct229.utils.assertions import RCTException, RCTFailureException


def test_baseline_system_type_compare_test_exact_match__exception_sys_type():
with pytest.raises(
RCTException, match="SYS123 does not match any baseline HVAC system type"
RCTFailureException,
match="Not_Sys does not match any primary baseline HVAC system type",
):
baseline_system_type_compare("SYS123", HVAC_SYS.SYS_1)
assert baseline_system_type_compare("SYS123", HVAC_SYS.UNMATCHED)


def test_baseline_system_type_compare_test_exact_match__exception_target_sys_type():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@


@memoize
def get_baseline_system_types(rmd_b: dict) -> dict[HVAC_SYS, list[str]]:
def get_baseline_system_types(
rmd_b: dict, use_assert: bool = False
) -> dict[HVAC_SYS, list[str]]:
"""
Identify all the baseline system types modeled in a B-RMD.

Expand Down Expand Up @@ -89,7 +91,7 @@ def get_baseline_system_types(rmd_b: dict) -> dict[HVAC_SYS, list[str]]:
i[1]
for i in inspect.getmembers(HVAC_SYS)
if type(i[0]) is str and i[0].startswith("SYS")
]
] + [HVAC_SYS.UNMATCHED]

baseline_hvac_system_dict = {sys_type: [] for sys_type in hvac_sys_list}

Expand Down Expand Up @@ -123,9 +125,13 @@ def get_baseline_system_types(rmd_b: dict) -> dict[HVAC_SYS, list[str]]:
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.",
)
if use_assert:
assert_(
sys_found,
f"Error: HVAC {hvac_b_id} does not match any baseline system type.",
)
else:
if not sys_found:
baseline_hvac_system_dict[HVAC_SYS.UNMATCHED].append(hvac_b_id)

return baseline_hvac_system_dict
113 changes: 73 additions & 40 deletions rct229/rulesets/ashrae9012019/section18/section18rule2.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,51 @@ def create_data(self, context, data):
"$.buildings[*].building_segments[*].heating_ventilating_air_conditioning_systems[*].id",
rmd_b,
):
sys_type = next(
(
sys_type
for sys_type, hvac_id_list in baseline_system_types_dict_b.items()
if hvac_id_b in hvac_id_list
),
HVAC_SYS.UNMATCHED,
)

zones_served_by_system = zones_and_terminal_unit_list_dict_b.get(
hvac_id_b, {}
).get("zone_list", [])

zones_on_floor = (
get_zones_on_same_floor_list(rmd_b, zones_served_by_system[0])
if zones_served_by_system
else []
)

hvac_lab_zones_only_b = lab_zone_hvac_systems["lab_zones_only"]

hvac_data_b[hvac_id_b] = {
"sys_type": sys_type,
"is_sys_single_zone_sys_b": (
len(zones_served_by_system) == 1
if zones_served_by_system
else False
),
"does_two_sys_exist_on_same_fl_b": "undetermined",
"does_sys_only_serve_lab_b": hvac_id_b in hvac_lab_zones_only_b,
"does_sys_part_of_serve_lab_b": (
hvac_id_b in hvac_lab_zones_only_b
and len(hvac_lab_zones_only_b) > 1
),
"does_sys_serve_lab_and_other_b": (
hvac_id_b in lab_zone_hvac_systems["lab_and_other"]
),
"hvac_sys2_id_b": None,
"does_sys_serve_one_floor": (
bool(zones_served_by_system)
and set(zones_served_by_system).issubset(set(zones_on_floor))
),
"do_multi_zone_evaluation": bool(len(zones_served_by_system) > 1),
}

if hvac_id_b in applicable_hvac_sys_ids_b:
hvac_lab_zones_only_b = lab_zone_hvac_systems["lab_zones_only"]
hvac_data_b[hvac_id_b] = {
Expand All @@ -142,14 +187,7 @@ def create_data(self, context, data):
)
]
),
"sys_type": next(
(
sys_type
for sys_type, hvac_id_list in baseline_system_types_dict_b.items()
if hvac_id_b in hvac_id_list
),
None,
),
"sys_type": sys_type,
"does_sys_only_serve_lab_b": (
hvac_id_b in hvac_lab_zones_only_b
and len(hvac_lab_zones_only_b) == 1
Expand All @@ -168,11 +206,7 @@ def create_data(self, context, data):
}

hvac_data_by_id_b = hvac_data_b[hvac_id_b]
# if a system is a single zone system -> PASS
# or the system only serves the only lab zone -> PASS/UNDETERMINED based on total exhaust
# or the system is one of the HVAC syss serving lab zones -> FAIL
# or the system serves lab and other zones -> FAIL
# All above scenario lands on a decision which allows to skip the multi-zone evaluation

hvac_data_by_id_b["do_multi_zone_evaluation"] = not any(
[
hvac_data_by_id_b["is_sys_single_zone_sys_b"],
Expand Down Expand Up @@ -200,7 +234,7 @@ def create_data(self, context, data):
).issubset(set(zones_on_floor)) and any(
[
baseline_system_type_compare(
hvac_data_by_id_b["sys_type"], target_sys_type, False
sys_type, target_sys_type, False
)
for target_sys_type in EXCEPTION_SYS_TYPES
]
Expand All @@ -209,15 +243,7 @@ def create_data(self, context, data):
hvac_data_by_id_b["do_multi_zone_evaluation"]
and hvac_data_by_id_b["does_sys_serve_one_floor"]
):
# check if there are any other systems of the same system type that serve zones on this floor
same_sys_type_list = next(
(
hvac_id_list
for hvac_id_list in baseline_system_types_dict_b.values()
if hvac_id_b in hvac_id_list
),
None,
)
same_sys_type_list = baseline_system_types_dict_b[sys_type]
for hvac_sys2_id_b in same_sys_type_list:
if hvac_sys2_id_b != hvac_id_b:
zones_served_by_system2 = (
Expand All @@ -244,20 +270,14 @@ def create_data(self, context, data):
# The two systems have overlaps in the same floor
# But the other system is serving lab zones only
# use lab zone exhaust to determine TRUE or UNDETERMINED
if (
building_total_lab_zone_exhaust_b
hvac_data_b[hvac_id_b][
"does_two_sys_exist_on_same_fl_b"
] = (
"true"
if building_total_lab_zone_exhaust_b
> AIRFLOW_15000_CFM
):
hvac_data_b[hvac_id_b][
"does_two_sys_exist_on_same_fl_b"
] = "true"
hvac_data_b[hvac_id_b][
"hvac_sys2_id_b"
] = hvac_sys2_id_b
else:
hvac_data_b[hvac_id_b][
"does_two_sys_exist_on_same_fl_b"
] = "undetermined"
else "undetermined"
)
hvac_data_b[hvac_id_b][
"hvac_sys2_id_b"
] = hvac_sys2_id_b
Expand Down Expand Up @@ -328,11 +348,18 @@ def manual_check_required(self, context, calc_vals=None, data=None):

return (
(
(does_sys_only_serve_lab_b or does_sys_serve_lab_and_other_b)
and building_total_lab_zone_exhaust_b <= AIRFLOW_15000_CFM
(
(
does_sys_only_serve_lab_b
or does_sys_serve_lab_and_other_b
)
and building_total_lab_zone_exhaust_b <= AIRFLOW_15000_CFM
)
# Note: does_two_sys_exist_on_same_fl_b already did the air flow check
)
# Note: does_two_sys_exist_on_same_fl_b already did the air flow check
) or does_two_sys_exist_on_same_fl_b == "undetermined"
or does_two_sys_exist_on_same_fl_b == "undetermined"
or calc_vals["sys_type"] == HVAC_SYS.UNMATCHED
)

def get_manual_check_required_msg(self, context, calc_vals=None, data=None):
does_sys_only_serve_lab_b = calc_vals["does_sys_only_serve_lab_b"]
Expand All @@ -347,6 +374,12 @@ def get_manual_check_required_msg(self, context, calc_vals=None, data=None):
undetermined_msg = ""
# the building total lab zone exhaust is guaranteed to be <= 15,000 CFM due to the preconditioned in the `manual_check_required` function
# for does_sys_only_serve_lab_b and does_sys_serve_lab_and_other_b.
if calc_vals["sys_type"] == HVAC_SYS.UNMATCHED:
undetermined_msg = (
"The system type for this HVAC system could not be determined from the inputs provided. "
"Please verify that the system type is one of the applicable system types (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, or 13) "
"and that the zones served by this system are correctly assigned."
)
if does_sys_only_serve_lab_b:
undetermined_msg = (
"This system serves only lab zones, which is correct if the building has total lab exhaust greater than 15,000 cfm. "
Expand Down
Loading