Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
02c4605
Merge pull request #1757 from pnnl/develop
weilixu Jun 25, 2025
69cf209
update version
weilixu Jun 25, 2025
dea6a04
fix ends with.
weilixu Jun 25, 2025
5484711
handle 24/7 fan operation
JacksonJ-KC Aug 27, 2025
20dd864
address review comments
JacksonJ-KC Sep 3, 2025
3d333bb
apply similar fixes to other rules
JacksonJ-KC Sep 3, 2025
59c084f
update test jsons
JacksonJ-KC Sep 3, 2025
b0478a6
assume no fan schedule is always on
JacksonJ-KC Sep 3, 2025
1251c1c
also modify 19-31 and 19-32
JacksonJ-KC Sep 4, 2025
1246aa7
black format
JacksonJ-KC Sep 4, 2025
0555c8a
also modify 19-31 and 19-32 ruletests
JacksonJ-KC Sep 4, 2025
ebbb270
remove required fields to allow rule's None handling
JacksonJ-KC Sep 4, 2025
70c87e3
Developed 6-12 rule
yunjoonjung-PNNL Sep 5, 2025
1050580
adjust manual_check_required
JacksonJ-KC Sep 5, 2025
57413e0
adjust expectation for cooling_system
JacksonJ-KC Sep 5, 2025
6953bf2
Merge pull request #1817 from pnnl/RCT/JDJ/12-2-undetermined-bugfix
weilixu Sep 8, 2025
bd1f76d
Merge pull request #1818 from pnnl/RCT/JDJ/19-16-undetermined-bugfix
weilixu Sep 8, 2025
29e94ca
Merge pull request #1814 from pnnl/RCT/JDJ/19-5-req-fields-bugfix
weilixu Sep 8, 2025
3fb12dd
Merge pull request #1807 from pnnl/RCT/JDJ/handle-always-on-section19
weilixu Sep 8, 2025
d13e626
Merge branch 'master' of https://github.com/pnnl/ruleset-checking-too…
weilixu Sep 8, 2025
044cef3
update version to 0.4.1
weilixu Sep 8, 2025
524a723
update rct version
weilixu Sep 8, 2025
bcc874d
First cut at 6-12 rule test for ASHRAE 90.1-2022
jugonzal07 Aug 19, 2025
652d72b
Added 6-12 to ASHRAE 2019 as well.
jugonzal07 Sep 9, 2025
8c810dc
Merge branch 'RCT/YJ/rule_testing_2022' of https://github.com/pnnl/ru…
yunjoonjung-PNNL Sep 15, 2025
4d30233
Fixed 6-12 rule
yunjoonjung-PNNL Sep 15, 2025
91d7c73
Fixed issues in 6-12 a, b, and c. Some were related to test and stand…
jugonzal07 Sep 16, 2025
57b91f2
Merge pull request #1806 from pnnl/RT/JG/ruletest_6_12
yunjoonjung-PNNL Sep 16, 2025
f8f8e21
Added 6-12 2019 rule
yunjoonjung-PNNL Sep 16, 2025
0012782
Merge branch 'feature/ashrae-9012022' of https://github.com/pnnl/rule…
yunjoonjung-PNNL Sep 16, 2025
f884d79
Updated rule no. to 6-13
yunjoonjung-PNNL Sep 30, 2025
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
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "ruleset-checking-tool"
version = "0.3.1"
version = "0.4.1"
description = "PNNL ruleset checking tool"
authors = ["Weili Xu <[email protected]>", "Charlie Holly <[email protected]>", "Juan Gonzalez <[email protected]>", "Yun Joon Jung <[email protected]>", "Jiarong Xie <[email protected]>", "Jackson Jarbose <[email protected]>", "Karen Walkerman <[email protected]>"]
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion rct229/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.3.0"
__version__ = "0.4.1"
1 change: 1 addition & 0 deletions rct229/rulesets/ashrae9012019/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
"prm9012019rule66m62": "section6rule7",
"prm9012019rule16x33": "section6rule8",
"prm9012019rule22c86": "section6rule9",
"PRM9012019Rule86d29": "section6rule12",
"prm9012019rule86j27": "section10rule1",
"prm9012019rule34l50": "section10rule7",
"prm9012019rule73m45": "section10rule10",
Expand Down
5 changes: 2 additions & 3 deletions rct229/rulesets/ashrae9012019/section12/section12rule2.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,8 @@ def manual_check_required(self, context, calc_vals=None, data=None):
auto_receptacle_control_b = calc_vals["auto_receptacle_control_b"]
auto_receptacle_control_p = calc_vals["auto_receptacle_control_p"]
space_type_b = calc_vals["space_type_b"]
return (
eflh_difference > 0
and (
return eflh_difference > 0 and (
(
auto_receptacle_control_p
and space_type_b in EXPECTED_RECEPTACLE_CONTROL_SPACE_TYPES
and not auto_receptacle_control_b
Expand Down
12 changes: 10 additions & 2 deletions rct229/rulesets/ashrae9012019/section16/section16rule2.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,21 @@ def create_data(self, context, data):
rmd_p = context.PROPOSED

motor_use_schedule_b = {
sch_id: find_exactly_one_schedule(rmd_b, sch_id)["hourly_values"]
sch_id: getattr_(
find_exactly_one_schedule(rmd_b, sch_id),
"Schedule",
"hourly_values",
)
for sch_id in find_all(
"buildings[*].elevators[*].cab_motor_multiplier_schedule", rmd_b
)
}
motor_use_schedule_p = {
sch_id: find_exactly_one_schedule(rmd_p, sch_id)["hourly_values"]
sch_id: getattr_(
find_exactly_one_schedule(rmd_p, sch_id),
"Schedule",
"hourly_values",
)
for sch_id in find_all(
"buildings[*].elevators[*].cab_motor_multiplier_schedule", rmd_p
)
Expand Down
12 changes: 10 additions & 2 deletions rct229/rulesets/ashrae9012019/section16/section16rule3.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,22 @@ def create_data(self, context, data):
rmd_p = context.PROPOSED

cab_ventilation_schedule_p = {
sch_id: find_exactly_one_schedule(rmd_p, sch_id)["hourly_values"]
sch_id: getattr_(
find_exactly_one_schedule(rmd_p, sch_id),
"Schedule",
"hourly_values",
)
for sch_id in find_all(
"buildings[*].elevators[*].cab_ventilation_fan_multiplier_schedule",
rmd_p,
)
}
motor_use_schedule_p = {
sch_id: find_exactly_one_schedule(rmd_p, sch_id)["hourly_values"]
sch_id: getattr_(
find_exactly_one_schedule(rmd_p, sch_id),
"Schedule",
"hourly_values",
)
for sch_id in find_all(
"buildings[*].elevators[*].cab_motor_multiplier_schedule",
rmd_p,
Expand Down
12 changes: 10 additions & 2 deletions rct229/rulesets/ashrae9012019/section16/section16rule4.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,22 @@ def create_data(self, context, data):
rmd_p = context.PROPOSED

cab_lighting_schedule_p = {
sch_id: find_exactly_one_schedule(rmd_p, sch_id)["hourly_values"]
sch_id: getattr_(
find_exactly_one_schedule(rmd_p, sch_id),
"Schedule",
"hourly_values",
)
for sch_id in find_all(
"buildings[*].elevators[*].cab_lighting_multiplier_schedule",
rmd_p,
)
}
motor_use_schedule_p = {
sch_id: find_exactly_one_schedule(rmd_p, sch_id)["hourly_values"]
sch_id: getattr_(
find_exactly_one_schedule(rmd_p, sch_id),
"Schedule",
"hourly_values",
)
for sch_id in find_all(
"buildings[*].elevators[*].cab_motor_multiplier_schedule",
rmd_p,
Expand Down
12 changes: 10 additions & 2 deletions rct229/rulesets/ashrae9012019/section16/section16rule5.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,22 @@ def create_data(self, context, data):
rmd_b = context.BASELINE_0

cab_ventilation_fan_multiplier_schedule_b = {
sch_id: find_exactly_one_schedule(rmd_b, sch_id)["hourly_values"]
sch_id: getattr_(
find_exactly_one_schedule(rmd_b, sch_id),
"Schedule",
"hourly_values",
)
for sch_id in find_all(
"buildings[*].elevators[*].cab_ventilation_fan_multiplier_schedule",
rmd_b,
)
}
cab_lighting_multiplier_schedule_b = {
sch_id: find_exactly_one_schedule(rmd_b, sch_id)["hourly_values"]
sch_id: getattr_(
find_exactly_one_schedule(rmd_b, sch_id),
"Schedule",
"hourly_values",
)
for sch_id in find_all(
"buildings[*].elevators[*].cab_lighting_multiplier_schedule", rmd_b
)
Expand Down
2 changes: 1 addition & 1 deletion rct229/rulesets/ashrae9012019/section19/section19rule16.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def create_data(self, context, data):
}

hvac_has_non_mech_cooling_list_p = [
getattr_(hvac_p, "HVAC", "cooling_system", "type")
hvac_p.get("cooling_system", {}).get("type")
== COOLING_SYSTEM.NON_MECHANICAL
for hvac_p in find_all(
"$.buildings[*].building_segments[*].heating_ventilating_air_conditioning_systems[*]",
Expand Down
39 changes: 33 additions & 6 deletions rct229/rulesets/ashrae9012019/section19/section19rule26.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
get_hvac_systems_serving_zone_health_safety_vent_reqs,
)
from rct229.schema.schema_enums import SchemaEnums
from rct229.utils.assertions import getattr_
from rct229.utils.utility_functions import find_exactly_one_schedule
from rct229.utils.jsonpath_utils import find_all
from rct229.utils.pint_utils import ZERO

FAN_SYSTEM_OPERATION = SchemaEnums.schema_enums["FanSystemOperationOptions"]
Expand Down Expand Up @@ -35,8 +38,20 @@ def create_data(self, context, data):
applicable_hvac_systems_list_p = (
get_hvac_systems_serving_zone_health_safety_vent_reqs(rmd_p)
)
fan_operating_schedules_p = {
sch_id: getattr_(
find_exactly_one_schedule(rmd_p, sch_id), "Schedule", "hourly_values"
)
for sch_id in find_all(
"buildings[*].building_segments[*].heating_ventilating_air_conditioning_systems[*].fan_system.operating_schedule",
rmd_p,
)
}

return {"applicable_hvac_systems_list_p": applicable_hvac_systems_list_p}
return {
"applicable_hvac_systems_list_p": applicable_hvac_systems_list_p,
"fan_operating_schedules_p": fan_operating_schedules_p,
}

class HVACRule(RuleDefinitionBase):
def __init__(self):
Expand All @@ -47,7 +62,6 @@ def __init__(self):
required_fields={
"$": ["fan_system"],
"fan_system": [
"operation_during_unoccupied",
"minimum_outdoor_airflow",
],
},
Expand All @@ -57,15 +71,28 @@ def is_applicable(self, context, data=None):
hvac_p = context.PROPOSED
hvac_id_p = hvac_p["id"]
applicable_hvac_systems_list_p = data["applicable_hvac_systems_list_p"]
fan_operating_schedules_p = data["fan_operating_schedules_p"]

fan_operating_schedule_id_p = hvac_p["fan_system"].get("operating_schedule")
if fan_operating_schedule_id_p is not None:
fan_operating_schedule_vals_p = fan_operating_schedules_p[
fan_operating_schedule_id_p
]
always_on = sum(fan_operating_schedule_vals_p) == len(
fan_operating_schedule_vals_p
)

return hvac_id_p in applicable_hvac_systems_list_p
else:
always_on = True # If no schedule is defined, assume always on

return hvac_id_p in applicable_hvac_systems_list_p and not always_on

def get_calc_vals(self, context, data=None):
hvac_p = context.PROPOSED

operation_during_unoccupied_p = hvac_p["fan_system"][
"operation_during_unoccupied"
]
operation_during_unoccupied_p = getattr_(
hvac_p["fan_system"], "FanSystem", "operation_during_unoccupied"
)
minimum_outdoor_airflow_p = hvac_p["fan_system"]["minimum_outdoor_airflow"]

return {
Expand Down
40 changes: 34 additions & 6 deletions rct229/rulesets/ashrae9012019/section19/section19rule27.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
get_hvac_systems_serving_zone_health_safety_vent_reqs,
)
from rct229.schema.schema_enums import SchemaEnums
from rct229.utils.assertions import getattr_
from rct229.utils.utility_functions import find_exactly_one_schedule
from rct229.utils.jsonpath_utils import find_all
from rct229.utils.pint_utils import ZERO

FAN_SYSTEM_OPERATION = SchemaEnums.schema_enums["FanSystemOperationOptions"]
Expand Down Expand Up @@ -35,8 +38,20 @@ def create_data(self, context, data):
applicable_hvac_systems_list_b = (
get_hvac_systems_serving_zone_health_safety_vent_reqs(rmd_b)
)
fan_operating_schedules_b = {
sch_id: getattr_(
find_exactly_one_schedule(rmd_b, sch_id), "Schedule", "hourly_values"
)
for sch_id in find_all(
"buildings[*].building_segments[*].heating_ventilating_air_conditioning_systems[*].fan_system.operating_schedule",
rmd_b,
)
}

return {"applicable_hvac_systems_list_b": applicable_hvac_systems_list_b}
return {
"applicable_hvac_systems_list_b": applicable_hvac_systems_list_b,
"fan_operating_schedules_b": fan_operating_schedules_b,
}

class HVACRule(RuleDefinitionBase):
def __init__(self):
Expand All @@ -47,7 +62,6 @@ def __init__(self):
required_fields={
"$": ["fan_system"],
"fan_system": [
"operation_during_unoccupied",
"minimum_outdoor_airflow",
],
},
Expand All @@ -57,15 +71,29 @@ def is_applicable(self, context, data=None):
hvac_b = context.BASELINE_0
hvac_id_b = hvac_b["id"]
applicable_hvac_systems_list_b = data["applicable_hvac_systems_list_b"]
fan_operating_schedules_b = data["fan_operating_schedules_b"]
always_on = False

fan_operating_schedule_id_b = hvac_b["fan_system"].get("operating_schedule")
if fan_operating_schedule_id_b is not None:
fan_operating_schedule_vals_b = fan_operating_schedules_b[
fan_operating_schedule_id_b
]
always_on = sum(fan_operating_schedule_vals_b) == len(
fan_operating_schedule_vals_b
)

return hvac_id_b in applicable_hvac_systems_list_b
else:
always_on = True # If no schedule is defined, assume always on

return hvac_id_b in applicable_hvac_systems_list_b and not always_on

def get_calc_vals(self, context, data=None):
hvac_b = context.BASELINE_0

operation_during_unoccupied_b = hvac_b["fan_system"][
"operation_during_unoccupied"
]
operation_during_unoccupied_b = getattr_(
hvac_b["fan_system"], "FanSystem", "operation_during_unoccupied"
)
minimum_outdoor_airflow_b = hvac_b["fan_system"]["minimum_outdoor_airflow"]

return {
Expand Down
42 changes: 34 additions & 8 deletions rct229/rulesets/ashrae9012019/section19/section19rule28.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
from rct229.rulesets.ashrae9012019.ruleset_functions.get_hvac_systems_serving_zone_health_safety_vent_reqs import (
get_hvac_systems_serving_zone_health_safety_vent_reqs,
)
from rct229.utils.assertions import getattr_
from rct229.utils.utility_functions import find_exactly_one_schedule
from rct229.utils.jsonpath_utils import find_all
from rct229.schema.schema_enums import SchemaEnums

FAN_SYSTEM_OPERATION = SchemaEnums.schema_enums["FanSystemOperationOptions"]
Expand Down Expand Up @@ -43,8 +46,20 @@ def create_data(self, context, data):
+ get_hvac_systems_serving_zone_health_safety_vent_reqs(rmd_p)
)
)
fan_operating_schedules_p = {
sch_id: getattr_(
find_exactly_one_schedule(rmd_p, sch_id), "Schedule", "hourly_values"
)
for sch_id in find_all(
"buildings[*].building_segments[*].heating_ventilating_air_conditioning_systems[*].fan_system.operating_schedule",
rmd_p,
)
}

return {"inapplicable_hvac_sys_list_p": inapplicable_hvac_sys_list_p}
return {
"inapplicable_hvac_sys_list_p": inapplicable_hvac_sys_list_p,
"fan_operating_schedules_p": fan_operating_schedules_p,
}

class HVACRule(RuleDefinitionBase):
def __init__(self):
Expand All @@ -54,25 +69,36 @@ def __init__(self):
),
required_fields={
"$": ["fan_system"],
"fan_system": [
"operation_during_unoccupied",
],
},
)

def is_applicable(self, context, data=None):
hvac_p = context.PROPOSED
hvac_id_p = hvac_p["id"]
inapplicable_hvac_sys_list_p = data["inapplicable_hvac_sys_list_p"]
fan_operating_schedules_p = data["fan_operating_schedules_p"]
always_on = False

fan_operating_schedule_id_p = hvac_p["fan_system"].get("operating_schedule")
if fan_operating_schedule_id_p is not None:
fan_operating_schedule_vals_p = fan_operating_schedules_p[
fan_operating_schedule_id_p
]
always_on = sum(fan_operating_schedule_vals_p) == len(
fan_operating_schedule_vals_p
)

return hvac_id_p not in inapplicable_hvac_sys_list_p
else:
always_on = True # If no schedule is defined, assume always on

return hvac_id_p not in inapplicable_hvac_sys_list_p and not always_on

def get_calc_vals(self, context, data=None):
hvac_p = context.PROPOSED

operation_during_unoccupied_p = hvac_p["fan_system"][
"operation_during_unoccupied"
]
operation_during_unoccupied_p = getattr_(
hvac_p["fan_system"], "FanSystem", "operation_during_unoccupied"
)

return {"operation_during_unoccupied_p": operation_during_unoccupied_p}

Expand Down
Loading
Loading