-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RCT/YJ/Rule 12-3 #1409
RCT/YJ/Rule 12-3 #1409
Changes from all commits
d744aa7
b61f494
04cb834
f934658
f9ff99d
90ff732
6a47ed6
b0879ae
fca10e8
6720d29
c3b6634
e3d62bf
6c25996
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ | |
|
||
__all__ = [ | ||
"section12rule1", | ||
"section12rule3", | ||
"section12rule4", | ||
] | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,244 @@ | ||
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.rule_engine.rulesets import LeapYear | ||
from rct229.rulesets.ashrae9012019 import PROPOSED | ||
from rct229.rulesets.ashrae9012019.ruleset_functions.compare_schedules import ( | ||
compare_schedules, | ||
) | ||
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_schedule | ||
|
||
LIGHTING_SPACE = SchemaEnums.schema_enums["LightingSpaceOptions2019ASHRAE901TG37"] | ||
|
||
|
||
EXPECTED_RECEPTACLE_CONTROL_SPACE_TYPES = [ | ||
LIGHTING_SPACE.OFFICE_ENCLOSED, | ||
LIGHTING_SPACE.CONFERENCE_MEETING_MULTIPURPOSE_ROOM, | ||
LIGHTING_SPACE.COPY_PRINT_ROOM, | ||
LIGHTING_SPACE.LOUNGE_BREAKROOM_HEALTH_CARE_FACILITY, | ||
LIGHTING_SPACE.LOUNGE_BREAKROOM_ALL_OTHERS, | ||
LIGHTING_SPACE.CLASSROOM_LECTURE_HALL_TRAINING_ROOM_PENITENTIARY, | ||
LIGHTING_SPACE.CLASSROOM_LECTURE_HALL_TRAINING_ROOM_SCHOOL, | ||
LIGHTING_SPACE.CLASSROOM_LECTURE_HALL_TRAINING_ROOM_ALL_OTHER, | ||
LIGHTING_SPACE.OFFICE_OPEN_PLAN, | ||
] | ||
|
||
|
||
class Section12Rule3(RuleDefinitionListIndexedBase): | ||
"""Rule 3 of ASHRAE 90.1-2019 Appendix G Section 12 (Receptacle)""" | ||
|
||
def __init__(self): | ||
super(Section12Rule3, self).__init__( | ||
rmds_used=produce_ruleset_model_description( | ||
USER=False, BASELINE_0=True, PROPOSED=True | ||
), | ||
each_rule=Section12Rule3.RuleSetModelDescriptionRule(), | ||
index_rmd=PROPOSED, | ||
id="12-3", | ||
description="When receptacle controls are specified in the proposed building design for spaces where not required by Standard 90.1 2019 Section 8.4.2, " | ||
"the hourly receptacle schedule shall be reduced as specified in Standard 90.1-2019 Table G3.1 Section 12 Proposed Building Performance column.", | ||
ruleset_section_title="Receptacle", | ||
standard_section="Table G3.1-12 Proposed Building Performance column", | ||
is_primary_rule=True, | ||
list_path="ruleset_model_descriptions[0]", | ||
required_fields={"$": ["calendar"], "$.calendar": ["is_leap_year"]}, | ||
data_items={"is_leap_year": (PROPOSED, "calendar/is_leap_year")}, | ||
) | ||
|
||
class RuleSetModelDescriptionRule(RuleDefinitionListIndexedBase): | ||
def __init__(self): | ||
super(Section12Rule3.RuleSetModelDescriptionRule, self).__init__( | ||
rmds_used=produce_ruleset_model_description( | ||
USER=False, BASELINE_0=True, PROPOSED=True | ||
), | ||
each_rule=Section12Rule3.RuleSetModelDescriptionRule.SpaceRule(), | ||
index_rmd=PROPOSED, | ||
list_path="$.buildings[*].building_segments[*].zones[*].spaces[*]", | ||
) | ||
|
||
def is_applicable(self, context, data=None): | ||
rmd_p = context.PROPOSED | ||
|
||
spaces_with_receptacle_controls_beyond_req = [] | ||
for space_p in find_all( | ||
"$.buildings[*].building_segments[*].zones[*].spaces[*]", | ||
rmd_p, | ||
): | ||
lighting_space_type_p = getattr_( | ||
space_p, "spaces", "lighting_space_type" | ||
) | ||
if lighting_space_type_p not in EXPECTED_RECEPTACLE_CONTROL_SPACE_TYPES: | ||
for misc_equip_p in find_all( | ||
"$.miscellaneous_equipment[*]", space_p | ||
): | ||
if misc_equip_p.get("has_automatic_control"): | ||
spaces_with_receptacle_controls_beyond_req.append( | ||
misc_equip_p["id"] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is ok to change from RDS, but the name of the parameter should be updated to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed. |
||
) | ||
|
||
return spaces_with_receptacle_controls_beyond_req | ||
|
||
def create_data(self, context, data): | ||
rmd_b = context.BASELINE_0 | ||
rmd_p = context.PROPOSED | ||
|
||
schedule_b = { | ||
mult_sch_b: find_exactly_one_schedule(rmd_b, mult_sch_b)[ | ||
"hourly_values" | ||
] | ||
for mult_sch_b in find_all( | ||
"$.buildings[*].building_segments[*].zones[*].spaces[*].miscellaneous_equipment[*].multiplier_schedule", | ||
rmd_b, | ||
) | ||
} | ||
schedule_p = { | ||
mult_sch_p: find_exactly_one_schedule(rmd_p, mult_sch_p)[ | ||
"hourly_values" | ||
] | ||
for mult_sch_p in find_all( | ||
"$.buildings[*].building_segments[*].zones[*].spaces[*].miscellaneous_equipment[*].multiplier_schedule", | ||
rmd_p, | ||
) | ||
} | ||
|
||
return {"schedule_b": schedule_b, "schedule_p": schedule_p} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You will need a list path to filter out the spaces in the expected receptacle control space types here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't this already considered in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The In here, the purpose to add Check it again, the |
||
|
||
def list_filter(self, context_item, data): | ||
space_p = context_item.PROPOSED | ||
lighting_space_type_p = getattr_(space_p, "spaces", "lighting_space_type") | ||
return lighting_space_type_p not in EXPECTED_RECEPTACLE_CONTROL_SPACE_TYPES | ||
|
||
class SpaceRule(RuleDefinitionListIndexedBase): | ||
def __init__(self): | ||
super( | ||
Section12Rule3.RuleSetModelDescriptionRule.SpaceRule, self | ||
).__init__( | ||
rmds_used=produce_ruleset_model_description( | ||
USER=False, BASELINE_0=True, PROPOSED=True | ||
), | ||
each_rule=Section12Rule3.RuleSetModelDescriptionRule.SpaceRule.MiscEquipRule(), | ||
index_rmd=PROPOSED, | ||
list_path="$.miscellaneous_equipment[*]", | ||
) | ||
|
||
def create_data(self, context, data): | ||
space_p = context.PROPOSED | ||
|
||
return {"space_type_p": space_p["lighting_space_type"]} | ||
|
||
class MiscEquipRule(RuleDefinitionBase): | ||
def __init__(self): | ||
super( | ||
Section12Rule3.RuleSetModelDescriptionRule.SpaceRule.MiscEquipRule, | ||
self, | ||
).__init__( | ||
rmds_used=produce_ruleset_model_description( | ||
USER=False, BASELINE_0=True, PROPOSED=True | ||
), | ||
required_fields={ | ||
"$": [ | ||
"has_automatic_control", | ||
"multiplier_schedule", | ||
] | ||
}, | ||
manual_check_required_msg="Credit for automatic receptacle controls was expected, but baseline and proposed miscellaneous equipment schedules are identical.", | ||
) | ||
|
||
def is_applicable(self, context, data=None): | ||
misc_equip_p = context.PROPOSED | ||
return misc_equip_p.get("has_automatic_control") | ||
|
||
def get_not_applicable_msg(self, context, data=None): | ||
misc_equip_p = context.PROPOSED | ||
space_type_p = data["space_type_p"] | ||
return f"Misc equipment {misc_equip_p['id']} is in a {space_type_p} space but it does not has automatic control." | ||
|
||
def get_calc_vals(self, context, data=None): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should do a not applicable here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point! Addressed. |
||
misc_equip_b = context.BASELINE_0 | ||
misc_equip_p = context.PROPOSED | ||
|
||
is_leap_year = data["is_leap_year"] | ||
space_type_p = data["space_type_p"] | ||
schedule_b = data["schedule_b"] | ||
schedule_p = data["schedule_p"] | ||
|
||
expected_receptacle_power_credit = 0.1 * getattr_( | ||
misc_equip_p, | ||
"miscellaneous_equipment", | ||
"automatic_controlled_percentage", | ||
) | ||
|
||
hourly_multiplier_schedule_b = misc_equip_b["multiplier_schedule"] | ||
hourly_multiplier_schedule_p = misc_equip_p["multiplier_schedule"] | ||
|
||
expected_hourly_values = [ | ||
hour_value * (1 - expected_receptacle_power_credit) | ||
for hour_value in schedule_b[hourly_multiplier_schedule_b] | ||
] | ||
|
||
mask_schedule = ( | ||
[1] * LeapYear.LEAP_YEAR_HOURS | ||
if is_leap_year | ||
else [1] * LeapYear.REGULAR_YEAR_HOURS | ||
) | ||
|
||
credit_comparison_data = compare_schedules( | ||
expected_hourly_values, | ||
schedule_p[hourly_multiplier_schedule_p], | ||
mask_schedule, | ||
is_leap_year, | ||
)["total_hours_matched"] | ||
|
||
no_credit_comparison_data = compare_schedules( | ||
schedule_b[hourly_multiplier_schedule_b], | ||
schedule_p[hourly_multiplier_schedule_p], | ||
mask_schedule, | ||
is_leap_year, | ||
)["total_hours_matched"] | ||
|
||
return { | ||
"expected_hourly_values_len": len(expected_hourly_values), | ||
"credit_comparison_total_hours_matched": credit_comparison_data, | ||
"no_credit_comparison_total_hours_matched": no_credit_comparison_data, | ||
"hourly_multiplier_schedule_len_b": len( | ||
schedule_b[hourly_multiplier_schedule_b] | ||
), | ||
"hourly_multiplier_schedule_len_p": len( | ||
schedule_p[hourly_multiplier_schedule_p] | ||
), | ||
} | ||
|
||
def manual_check_required(self, context, calc_vals=None, data=None): | ||
no_credit_comparison_total_hours_matched = calc_vals[ | ||
"no_credit_comparison_total_hours_matched" | ||
] | ||
hourly_multiplier_schedule_len_b = calc_vals[ | ||
"hourly_multiplier_schedule_len_b" | ||
] | ||
hourly_multiplier_schedule_len_p = calc_vals[ | ||
"hourly_multiplier_schedule_len_p" | ||
] | ||
|
||
return ( | ||
no_credit_comparison_total_hours_matched | ||
== hourly_multiplier_schedule_len_b | ||
== hourly_multiplier_schedule_len_p | ||
) | ||
|
||
def rule_check(self, context, calc_vals=None, data=None): | ||
expected_hourly_values_len = calc_vals["expected_hourly_values_len"] | ||
credit_comparison_total_hours_matched = calc_vals[ | ||
"credit_comparison_total_hours_matched" | ||
] | ||
hourly_multiplier_schedule_len_p = calc_vals[ | ||
"hourly_multiplier_schedule_len_p" | ||
] | ||
|
||
return ( | ||
credit_comparison_total_hours_matched | ||
== hourly_multiplier_schedule_len_p | ||
== expected_hourly_values_len | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's use
space_p.get("lighting_space_type", None)
instead ofgetattr_
.Main reason is this
getattr_
will triggerUNDETERMINED
outcome, which may cause this rule to be undetermined every time.Also,
None
should be sufficient forlighting_space_type_p not in EXPECTED_RECEPTACLE_CONTROL_SPACE_TYPES
logic.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. Addressed.