-
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 14 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 |
---|---|---|
|
@@ -11,6 +11,7 @@ | |
"section5", | ||
"section6", | ||
"section10", | ||
"section12", | ||
"section16", | ||
"section18", | ||
"section19", | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,30 @@ | ||
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 USER | ||
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): | ||
|
@@ -11,46 +33,204 @@ class Section12Rule3(RuleDefinitionListIndexedBase): | |
def __init__(self): | ||
super(Section12Rule3, self).__init__( | ||
rmds_used=produce_ruleset_model_description( | ||
USER=True, BASELINE_0=False, PROPOSED=True | ||
USER=False, BASELINE_0=True, PROPOSED=True | ||
), | ||
each_rule=Section12Rule3.BuildingRule(), | ||
index_rmd=USER, | ||
each_rule=Section12Rule3.RuleSetModelDescriptionRule(), | ||
index_rmd=PROPOSED, | ||
id="12-3", | ||
description="User RMD Space ID in Proposed RMD", | ||
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="Section Table G3.1-12 Receptacles: Modeling Requirements for the Proposed design", | ||
standard_section="Table G3.1-12 Proposed Building Performance column", | ||
is_primary_rule=True, | ||
rmd_context="ruleset_model_descriptions/0/buildings", | ||
list_path="ruleset_model_descriptions[0]", | ||
required_fields={"$": ["calendar"], "$.calendar": ["is_leap_year"]}, | ||
data_items={"is_leap_year": (PROPOSED, "calendar/is_leap_year")}, | ||
) | ||
|
||
class BuildingRule(RuleDefinitionListIndexedBase): | ||
class RuleSetModelDescriptionRule(RuleDefinitionListIndexedBase): | ||
def __init__(self): | ||
super(Section12Rule3.BuildingRule, self).__init__( | ||
super(Section12Rule3.RuleSetModelDescriptionRule, self).__init__( | ||
rmds_used=produce_ruleset_model_description( | ||
USER=True, BASELINE_0=False, PROPOSED=True | ||
USER=False, BASELINE_0=True, PROPOSED=True | ||
), | ||
each_rule=Section12Rule3.BuildingRule.SpaceRule(), | ||
index_rmd=USER, | ||
list_path="$..spaces[*]", # All spaces in the building | ||
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): | ||
# Get the Proposed space id values | ||
return {"proposed_space_ids": find_all("$..spaces[*].id", context.PROPOSED)} | ||
rmd_b = context.BASELINE_0 | ||
rmd_p = context.PROPOSED | ||
|
||
class SpaceRule(RuleDefinitionBase): | ||
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 |
||
|
||
class SpaceRule(RuleDefinitionListIndexedBase): | ||
def __init__(self): | ||
super(Section12Rule3.BuildingRule.SpaceRule, self).__init__( | ||
# No longer need the proposed RMD | ||
super( | ||
Section12Rule3.RuleSetModelDescriptionRule.SpaceRule, self | ||
).__init__( | ||
rmds_used=produce_ruleset_model_description( | ||
USER=True, BASELINE_0=False, PROPOSED=False | ||
USER=False, BASELINE_0=True, PROPOSED=True | ||
), | ||
each_rule=Section12Rule3.RuleSetModelDescriptionRule.SpaceRule.MiscEquipRule(), | ||
index_rmd=PROPOSED, | ||
list_path="$.miscellaneous_equipment[*]", | ||
) | ||
|
||
def get_calc_vals(self, context, data=None): | ||
def create_data(self, context, data): | ||
space_p = context.PROPOSED | ||
|
||
return { | ||
"user_space_id": context.USER["id"], | ||
"space_type_p": getattr_(space_p, "spaces", "lighting_space_type") | ||
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. with the list path function in the RMD class, in here you can directly use 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. |
||
} | ||
|
||
def rule_check(self, context, calc_vals, data): | ||
return calc_vals["user_space_id"] in data["proposed_space_ids"] | ||
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 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"] | ||
|
||
if ( | ||
space_type_p not in EXPECTED_RECEPTACLE_CONTROL_SPACE_TYPES | ||
and misc_equip_p["has_automatic_control"] | ||
): | ||
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. Do list path and is applicable functions, then you do not need this if statement here anymore. 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. |
||
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] | ||
] | ||
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. @JacksonJ-KC @KarenWGard expected_hourly_values_in_b = [
min(1, hour_value * (1 + expected_receptacle_power_credit))
for hour_value in schedule_p[hourly_multiplier_schedule_p]
]
credit_comparison_data = compare_schedules(
expected_hourly_values_in_p,
schedule_b[hourly_multiplier_schedule_b],
mask_schedule,
is_leap_year,
)["total_hours_matched"] 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 am not following the question or the proposed change to the RDS. These are the 3 cases.
The expected proposed hourly values are the baseline values * (1 - expected_receptacle_power_credit) 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. @JacksonJ-KC If the multiplier value is 1.0 in an hour on a day in the proposed model, then the baseline value would be 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. If the proposed model used 1 as the multiplier value then that means that they forgot to apply the credit and they should fail the rule unless every hourly values matches the baseline and case 2 is met so they get undetermined. 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. 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. Note this can still lead to expected values that are greater than 1 and you should not replace the expected values greater than 1 with 1. If an expected value is greater than 1 then either Case 2 is met or Fail. 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 would be a problem since it relates to software capabilities and circular reference in the standard. 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. Note to discuss at the next RDS meeting 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 next RDS meeting won't happen in two weeks so I opened an issue #1510 for the discussion later. 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'll address this issue when the issue is resolved. |
||
|
||
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_data": credit_comparison_data, | ||
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. this is confusing - use 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. |
||
"no_credit_comparison_data": 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_data = calc_vals["no_credit_comparison_data"] | ||
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_data | ||
== 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_data = calc_vals["credit_comparison_data"] | ||
hourly_multiplier_schedule_len_p = calc_vals[ | ||
"hourly_multiplier_schedule_len_p" | ||
] | ||
|
||
return ( | ||
credit_comparison_data | ||
== 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.