From 6b456ede24a5992beecd37ba87436eabde8b55b9 Mon Sep 17 00:00:00 2001 From: Kyle Benne Date: Mon, 1 Jun 2026 11:38:58 -0500 Subject: [PATCH 01/11] Add NFP for ScheduleRuleset --- design/FY2026/NFP-ScheduleRuleset.md | 317 +++++++++++++++++++++++++++ 1 file changed, 317 insertions(+) create mode 100644 design/FY2026/NFP-ScheduleRuleset.md diff --git a/design/FY2026/NFP-ScheduleRuleset.md b/design/FY2026/NFP-ScheduleRuleset.md new file mode 100644 index 00000000000..4bc5c434d8b --- /dev/null +++ b/design/FY2026/NFP-ScheduleRuleset.md @@ -0,0 +1,317 @@ +Schedule Ruleset +================ + +**Joe Robertson, National Laboratory of the Rockies** + + - May 1, 2026 - Initial Draft + +## Justification for New Feature ## + +For support of OpenStudio SDK <-> EnergyPlus Alignment. +`OS:Schedule:Ruleset` is widely used; breaking API here would have too much impact. + +EnergyPlus currently requires users to decompose a yearly schedule into a hierarchy of objects: `Schedule:Day:*` → `Schedule:Week:*` → `Schedule:Year`. +This is expressive but verbose and non-intuitive: a typical weekday/weekend/holiday schedule that a user might describe in a few sentences requires many objects and careful date-range bookkeeping. + +The OpenStudio SDK has long offered `OS:Schedule:Ruleset` as a higher-level, rule-based abstraction: the user defines a default day schedule and a prioritized list of override rules, each specifying which days of the week and which date range (or specific dates) the rule applies to. +Special design day schedules (summer, winter, holiday, custom) are also first-class fields. +This model is far closer to how schedules are actually specified in building standards, energy codes, and operator manuals. + +Today, the OpenStudio ForwardTranslator expands `OS:Schedule:Ruleset` into the `Schedule:Year` + `Schedule:Week:Daily` + `Schedule:Day:Interval` hierarchy before writing an IDF. +Adding `Schedule:Ruleset` natively to EnergyPlus would allow users who author IDF/epJSON files directly to use this intuitive representation, and would eventually allow the OpenStudio ForwardTranslator to emit the compact ruleset form instead of the expanded one. + +## E-mail and Conference Call Conclusions ## + +Our generalized set of options for OS <-> E+ Alignment: +- Option 1 - Reverse translate from IDF to OSM (one time up front), and then forward translate from OSM to IDF (one time back end) +- Option 2 - Develop and push new objects into E+ +- Option 3 - Drop support for an OS model object API; measures need to deal with the deprecation +- Option 4 - Data is backed; RT/FT on the fly (probably very expensive) + +For handling ScheduleRuleset, we ultimately arrived at Option 2: +- It has widespread use and popularity in OS +- We'd just be adding new standalone object(s) in E+ +- It is convenient and easily extensible (i.e., an attractive schedule type) +- The advantages outweigh the other options from above + +## Overview ## + +Add a new detailed schedule type. +The new schedule type is an alternative to, e.g., `Schedule:Year` and `Schedule:Compact`, for describing detailed schedules. +The new schedule type involves a parent "ruleset" object along with 1 to many "rule" objects. + +The parent `Schedule:Ruleset` object: +- Requires references to schedule type limits and a default day schedule +- Optionally references summer, winter, holiday, custom 1, and custom 2 design day schedules + +The child(ren) `Schedule:Rule` object(s): +- Requires reference to the parent `Schedule:Ruleset` object +- Requires an "order" be specified for determining rule index amongst other rules +- Requires reference to a day schedule +- Specifies which day(s) of the week for which the rule applies +- Specifies the start month/day and end month/day (date range) or month/day (specific dates) for which the rule applies + +During input processing, EnergyPlus will resolve the ruleset into the same internal `ScheduleDetailed` structure used by `Schedule:Year` and `Schedule:Compact`. +No downstream code changes are required; the new objects are transparent to all schedule consumers. + +## Approach ## + +For the most part, follows the logic and implementation in OS. + +Add new `Schedule:Ruleset` and `Schedule:Rule` objects to the IDD. + +Make updates and additions to ScheduleManager.hh and ScheduleManager.cc: +- Get all `Schedule:Rule` objects up front; move field values into structs +- Loop through each `Schedule:Ruleset` object + - Call `AddScheduleDetailed` for creating a new detailed schedule + - For every day of the year: + - Get the "priority" schedule rule, i.e., the one whose (A) applicable dates contain the day and (B) has the least rule order value (0 = highest priority) + - Either get an existing, or add a new, week schedule + - Update the (12) day schedules according to the ruleset's special day schedules and rule's properties + - Assign the week schedule to the week schedule's array for the year + +It is not required that a day has a rule defined for it (i.e., a `Schedule:Ruleset` may have no attached `Schedule:Rule` objects). +In this case, either: +- The day is Feb 29 and is just made equal to Feb 28, or +- The day falls back to the default day schedule as defined by the ruleset. + +Special design day slots (summer, winter, holiday, custom 1, custom 2) are set on all `WeekSchedule` objects. +If a special day schedule is not provided, the default day schedule is used as fallback for that slot. + +Allow `Schedule:Ruleset` objects to be actuated by adding the following at the end of the `CurrentModuleObject = "Schedule:Ruleset";` block: +``` + if (s_glob->AnyEnergyManagementSystemInModel) { // setup constant schedules as actuators + SetupEMSActuator(state, "Schedule:Ruleset", sched->Name, "Schedule Value", "[ ]", sched->EMSActuatedOn, sched->EMSVal); + } +``` + +Downstream `GetSchedule` will find the detailed schedules created from `Schedule:Ruleset` and `Schedule:Rule`. + +No new data structures are needed in `ScheduleManager.hh`. +The resolution is purely a parse-time operation that flattens the ruleset into the existing `ScheduleDetailed.weekScheds[367]` array. + +No existing objects are changed. +No transition is required. + +## Testing/Validation/Data Sources ## + +Several new unit tests in `tst/EnergyPlus/unit/ScheduleManager.unit.cc` covering: + +- A ruleset with two rules demonstrating priority (lower `Rule Order` wins when date ranges overlap). +- `DateRange` specification: correct day assignment across a date range, including wrap-around (e.g., Nov 1 – Jan 31). +- `SpecificDates` specification: only the listed dates get the rule's day schedule. +- Fallback to default day schedule when no rule matches. +- Summer/winter/holiday design day overrides; fallback to default day schedule when not specified. +- Validation errors: unknown `Day Schedule Name`, duplicate `Rule Order` values within a ruleset (warning), missing Default Day Schedule. + +A new test file `_ResidentialBaseScheduleRuleset.idf`, where all `Schedule:Year` / `Schedule:Week:Daily` objects are replaced with equivalent `Schedule:Ruleset` / `Schedule:Rule` objects. + +## Input Output Reference Documentation ## + +Update `doc/input-output-reference/src/overview/group-schedules.tex` with new `Schedule:Ruleset` and `Schedule:Rule` subsections placed directly following `Schedule:Year` and `Schedule:Compact`. +Key points to document: + +- The priority model: rules are evaluated in ascending `Rule Order`; the first matching rule wins. +- The `DateRange` vs `SpecificDates` date specification types and their respective fields. +- The special-day schedule fields and their fallback behavior. +- The relationship between `Schedule:Ruleset`, `Schedule:Rule`, and `Schedule:Day:*` objects. + +## Input Description ## + +IDD: + +``` +Schedule:Ruleset, + \memo A Schedule:Ruleset defines a yearly schedule using a default day profile and + \memo a prioritized list of override rules (Schedule:Rule objects). Rules are evaluated + \memo in ascending Rule Order; the first matching rule for a given day is used. + \memo If no rule matches, the Default Day Schedule is used. + \min-fields 3 + A1 , \field Name + \type alpha + \required-field + \reference ScheduleNames + \reference ScheduleRulesetNames + A2 , \field Schedule Type Limits Name + \type object-list + \object-list ScheduleTypeLimitsNames + A3 , \field Default Day Schedule Name + \required-field + \type object-list + \object-list DayScheduleNames + A4 , \field Summer Design Day Schedule Name + \note If blank, the Default Day Schedule is used for Summer Design Days. + \type object-list + \object-list DayScheduleNames + A5 , \field Winter Design Day Schedule Name + \note If blank, the Default Day Schedule is used for Winter Design Days. + \type object-list + \object-list DayScheduleNames + A6 , \field Holiday Schedule Name + \note If blank, the Default Day Schedule is used for Holidays. + \type object-list + \object-list DayScheduleNames + A7 , \field Custom Day 1 Schedule Name + \note If blank, the Default Day Schedule is used for Custom Day 1. + \type object-list + \object-list DayScheduleNames + A8 ; \field Custom Day 2 Schedule Name + \note If blank, the Default Day Schedule is used for Custom Day 2. + \type object-list + \object-list DayScheduleNames + +Schedule:Rule, + \memo A Schedule:Rule defines one override rule for a Schedule:Ruleset. + \memo Rules are evaluated in ascending Rule Order; the first matching rule wins. + \memo A rule matches a day if: (a) the day falls within the date specification, + \memo AND (b) the corresponding Apply field is Yes. + \extensible:2 - repeat last two fields (Specific Month, Specific Day) + \min-fields 12 + A1 , \field Name + \type alpha + \required-field + \reference ScheduleRuleNames + A2 , \field Schedule Ruleset Name + \required-field + \type object-list + \object-list ScheduleRulesetNames + N1 , \field Rule Order + \note Lower values have higher priority. Must be unique within a Schedule:Ruleset. + \type integer + \required-field + \minimum 0 + A3 , \field Day Schedule Name + \required-field + \type object-list + \object-list DayScheduleNames + A4 , \field Apply Sunday + \type choice + \default No + \key Yes + \key No + A5 , \field Apply Monday + \type choice + \default No + \key Yes + \key No + A6 , \field Apply Tuesday + \type choice + \default No + \key Yes + \key No + A7 , \field Apply Wednesday + \type choice + \default No + \key Yes + \key No + A8 , \field Apply Thursday + \type choice + \default No + \key Yes + \key No + A9 , \field Apply Friday + \type choice + \default No + \key Yes + \key No + A10, \field Apply Saturday + \type choice + \default No + \key Yes + \key No + A11, \field Date Specification Type + \type choice + \default DateRange + \key DateRange + \key SpecificDates + N2 , \field Start Month + \note Used only when Date Specification Type = DateRange. + \type integer + \minimum 1 + \maximum 12 + \default 1 + N3 , \field Start Day + \note Used only when Date Specification Type = DateRange. + \type integer + \minimum 1 + \maximum 31 + \default 1 + N4 , \field End Month + \note Used only when Date Specification Type = DateRange. + \type integer + \minimum 1 + \maximum 12 + \default 12 + N5 , \field End Day + \note Used only when Date Specification Type = DateRange. + \type integer + \minimum 1 + \maximum 31 + \default 31 + N6 , \field Specific Month + \note Used only when Date Specification Type = SpecificDates. + \note Repeat the Specific Month / Specific Day pair for each date. + \begin-extensible + \type integer + \minimum 1 + \maximum 12 + N7 ; \field Specific Day + \note Used only when Date Specification Type = SpecificDates. + \type integer + \minimum 1 + \maximum 31 +``` + +Example IDF snippet: + +``` + Schedule:Ruleset, + occupants schedule ruleset, !- Name + Fractional, !- Schedule Type Limits Name + occupants schedule default day, !- Default Day Schedule Name + occupants schedule default day, !- Summer Design Day Schedule Name + occupants schedule default day, !- Winter Design Day Schedule Name + occupants schedule default day, !- Holiday Schedule Name + occupants schedule default day, !- Custom Day 1 Schedule Name + occupants schedule default day; !- Custom Day 2 Schedule Name + + Schedule:Rule, + occupants schedule rule, !- Name + occupants schedule ruleset, !- Schedule Ruleset Name + 0, !- Rule Order + occupants schedule day, !- Day Schedule Name + No, !- Apply Sunday + Yes, !- Apply Monday + No, !- Apply Tuesday + Yes, !- Apply Wednesday + No, !- Apply Thursday + Yes, !- Apply Friday + No, !- Apply Saturday + DateRange, !- Date Specification Type + 1, !- Start Month + 1, !- Start Day + 12, !- End Month + 31; !- End Day +``` + +## Outputs Description ## + +No new output variables are added. +`Schedule:Ruleset` objects will appear in existing schedule reporting (e.g., `Output:Schedules`) under their name, identical to `Schedule:Year`. + +## Engineering Reference ## + +No changes to the Engineering Reference are required. +The rule-resolution algorithm is a straightforward date and day-of-week lookup with no new physics. + +## Example File and Transition Changes ## + +A new example file `_ResidentialBaseScheduleRuleset.idf` will be added, replacing all `Schedule:Year` / `Schedule:Week:Daily` objects with equivalent `Schedule:Ruleset` / `Schedule:Rule` objects to demonstrate parity. + +No transition rules are required; this is a purely additive feature. + +## References ## + +- OpenStudio Model API - `OS:Schedule:Ruleset` / `OS:Schedule:Rule` +- OpenStudio ForwardTranslator — current expansion to `Schedule:Year`: `ForwardTranslateScheduleRuleset.cpp` +- EnergyPlus Engineering Reference, Schedules chapter From bcc43d3f47912c82713a847b6bd511eaf18d0612 Mon Sep 17 00:00:00 2001 From: Joe Robertson Date: Mon, 15 Jun 2026 10:56:30 -0700 Subject: [PATCH 02/11] Remove a couple of irrelevant items. --- design/FY2026/NFP-ScheduleRuleset.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/design/FY2026/NFP-ScheduleRuleset.md b/design/FY2026/NFP-ScheduleRuleset.md index 4bc5c434d8b..84c915f24ad 100644 --- a/design/FY2026/NFP-ScheduleRuleset.md +++ b/design/FY2026/NFP-ScheduleRuleset.md @@ -4,12 +4,10 @@ Schedule Ruleset **Joe Robertson, National Laboratory of the Rockies** - May 1, 2026 - Initial Draft + - June 15, 2026 - Revised Draft ## Justification for New Feature ## -For support of OpenStudio SDK <-> EnergyPlus Alignment. -`OS:Schedule:Ruleset` is widely used; breaking API here would have too much impact. - EnergyPlus currently requires users to decompose a yearly schedule into a hierarchy of objects: `Schedule:Day:*` → `Schedule:Week:*` → `Schedule:Year`. This is expressive but verbose and non-intuitive: a typical weekday/weekend/holiday schedule that a user might describe in a few sentences requires many objects and careful date-range bookkeeping. From 11184793b204fde4ea1775fb0d682589da36b9cd Mon Sep 17 00:00:00 2001 From: Joe Robertson Date: Mon, 15 Jun 2026 10:58:48 -0700 Subject: [PATCH 03/11] Remove the bit about non-intuitive schedules. --- design/FY2026/NFP-ScheduleRuleset.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/design/FY2026/NFP-ScheduleRuleset.md b/design/FY2026/NFP-ScheduleRuleset.md index 84c915f24ad..b94520dc753 100644 --- a/design/FY2026/NFP-ScheduleRuleset.md +++ b/design/FY2026/NFP-ScheduleRuleset.md @@ -9,7 +9,7 @@ Schedule Ruleset ## Justification for New Feature ## EnergyPlus currently requires users to decompose a yearly schedule into a hierarchy of objects: `Schedule:Day:*` → `Schedule:Week:*` → `Schedule:Year`. -This is expressive but verbose and non-intuitive: a typical weekday/weekend/holiday schedule that a user might describe in a few sentences requires many objects and careful date-range bookkeeping. +This is expressive but verbose: a typical weekday/weekend/holiday schedule that a user might describe in a few sentences requires many objects and careful date-range bookkeeping. The OpenStudio SDK has long offered `OS:Schedule:Ruleset` as a higher-level, rule-based abstraction: the user defines a default day schedule and a prioritized list of override rules, each specifying which days of the week and which date range (or specific dates) the rule applies to. Special design day schedules (summer, winter, holiday, custom) are also first-class fields. From 0efa1925fc1f288313977f112bb96f31af7d2b78 Mon Sep 17 00:00:00 2001 From: Joe Robertson Date: Mon, 15 Jun 2026 11:03:38 -0700 Subject: [PATCH 04/11] List all the existing schedule types. --- design/FY2026/NFP-ScheduleRuleset.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/design/FY2026/NFP-ScheduleRuleset.md b/design/FY2026/NFP-ScheduleRuleset.md index b94520dc753..dbcf5aa4781 100644 --- a/design/FY2026/NFP-ScheduleRuleset.md +++ b/design/FY2026/NFP-ScheduleRuleset.md @@ -10,6 +10,12 @@ Schedule Ruleset EnergyPlus currently requires users to decompose a yearly schedule into a hierarchy of objects: `Schedule:Day:*` → `Schedule:Week:*` → `Schedule:Year`. This is expressive but verbose: a typical weekday/weekend/holiday schedule that a user might describe in a few sentences requires many objects and careful date-range bookkeeping. +The existing schedule types are: +- `Schedule:Year` +- `Schedule:Compact` +- `Schedule:Constant` +- `Schedule:File` +- `Schedule:File:Shading` The OpenStudio SDK has long offered `OS:Schedule:Ruleset` as a higher-level, rule-based abstraction: the user defines a default day schedule and a prioritized list of override rules, each specifying which days of the week and which date range (or specific dates) the rule applies to. Special design day schedules (summer, winter, holiday, custom) are also first-class fields. From 288362f5cb215972759e5fe750bf59e38f65b140 Mon Sep 17 00:00:00 2001 From: Joe Robertson Date: Mon, 15 Jun 2026 11:10:31 -0700 Subject: [PATCH 05/11] Rename Schedule:Ruleset to Schedule:Year:Rules. --- ...uleRuleset.md => NFP-ScheduleYearRules.md} | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) rename design/FY2026/{NFP-ScheduleRuleset.md => NFP-ScheduleYearRules.md} (81%) diff --git a/design/FY2026/NFP-ScheduleRuleset.md b/design/FY2026/NFP-ScheduleYearRules.md similarity index 81% rename from design/FY2026/NFP-ScheduleRuleset.md rename to design/FY2026/NFP-ScheduleYearRules.md index dbcf5aa4781..0af916331df 100644 --- a/design/FY2026/NFP-ScheduleRuleset.md +++ b/design/FY2026/NFP-ScheduleYearRules.md @@ -1,5 +1,5 @@ -Schedule Ruleset -================ +Schedule Year Rules +=================== **Joe Robertson, National Laboratory of the Rockies** @@ -22,7 +22,7 @@ Special design day schedules (summer, winter, holiday, custom) are also first-cl This model is far closer to how schedules are actually specified in building standards, energy codes, and operator manuals. Today, the OpenStudio ForwardTranslator expands `OS:Schedule:Ruleset` into the `Schedule:Year` + `Schedule:Week:Daily` + `Schedule:Day:Interval` hierarchy before writing an IDF. -Adding `Schedule:Ruleset` natively to EnergyPlus would allow users who author IDF/epJSON files directly to use this intuitive representation, and would eventually allow the OpenStudio ForwardTranslator to emit the compact ruleset form instead of the expanded one. +Adding `Schedule:Year:Rules` natively to EnergyPlus would allow users who author IDF/epJSON files directly to use this intuitive representation, and would eventually allow the OpenStudio ForwardTranslator to emit the compact ruleset form instead of the expanded one. ## E-mail and Conference Call Conclusions ## @@ -32,7 +32,7 @@ Our generalized set of options for OS <-> E+ Alignment: - Option 3 - Drop support for an OS model object API; measures need to deal with the deprecation - Option 4 - Data is backed; RT/FT on the fly (probably very expensive) -For handling ScheduleRuleset, we ultimately arrived at Option 2: +For handling `Schedule:Year:Rules`, we ultimately arrived at Option 2: - It has widespread use and popularity in OS - We'd just be adding new standalone object(s) in E+ - It is convenient and easily extensible (i.e., an attractive schedule type) @@ -42,57 +42,57 @@ For handling ScheduleRuleset, we ultimately arrived at Option 2: Add a new detailed schedule type. The new schedule type is an alternative to, e.g., `Schedule:Year` and `Schedule:Compact`, for describing detailed schedules. -The new schedule type involves a parent "ruleset" object along with 1 to many "rule" objects. +The new schedule type involves a parent "rules" object along with 1 to many "rule" objects. -The parent `Schedule:Ruleset` object: +The parent `Schedule:Year:Rules` object: - Requires references to schedule type limits and a default day schedule - Optionally references summer, winter, holiday, custom 1, and custom 2 design day schedules The child(ren) `Schedule:Rule` object(s): -- Requires reference to the parent `Schedule:Ruleset` object +- Requires reference to the parent `Schedule:Year:Rules` object - Requires an "order" be specified for determining rule index amongst other rules - Requires reference to a day schedule - Specifies which day(s) of the week for which the rule applies - Specifies the start month/day and end month/day (date range) or month/day (specific dates) for which the rule applies -During input processing, EnergyPlus will resolve the ruleset into the same internal `ScheduleDetailed` structure used by `Schedule:Year` and `Schedule:Compact`. +During input processing, EnergyPlus will resolve the parent rules object into the same internal `ScheduleDetailed` structure used by `Schedule:Year` and `Schedule:Compact`. No downstream code changes are required; the new objects are transparent to all schedule consumers. ## Approach ## For the most part, follows the logic and implementation in OS. -Add new `Schedule:Ruleset` and `Schedule:Rule` objects to the IDD. +Add new `Schedule:Year:Rules` and `Schedule:Rule` objects to the IDD. Make updates and additions to ScheduleManager.hh and ScheduleManager.cc: - Get all `Schedule:Rule` objects up front; move field values into structs -- Loop through each `Schedule:Ruleset` object +- Loop through each `Schedule:Year:Rules` object - Call `AddScheduleDetailed` for creating a new detailed schedule - For every day of the year: - Get the "priority" schedule rule, i.e., the one whose (A) applicable dates contain the day and (B) has the least rule order value (0 = highest priority) - Either get an existing, or add a new, week schedule - - Update the (12) day schedules according to the ruleset's special day schedules and rule's properties + - Update the (12) day schedules according to the parent rule's special day schedules and rule's properties - Assign the week schedule to the week schedule's array for the year -It is not required that a day has a rule defined for it (i.e., a `Schedule:Ruleset` may have no attached `Schedule:Rule` objects). +It is not required that a day has a rule defined for it (i.e., a `Schedule:Year:Rules` may have no attached `Schedule:Rule` objects). In this case, either: - The day is Feb 29 and is just made equal to Feb 28, or -- The day falls back to the default day schedule as defined by the ruleset. +- The day falls back to the default day schedule as defined by the parent rules object. Special design day slots (summer, winter, holiday, custom 1, custom 2) are set on all `WeekSchedule` objects. If a special day schedule is not provided, the default day schedule is used as fallback for that slot. -Allow `Schedule:Ruleset` objects to be actuated by adding the following at the end of the `CurrentModuleObject = "Schedule:Ruleset";` block: +Allow `Schedule:Year:Rules` objects to be actuated by adding the following at the end of the `CurrentModuleObject = "Schedule:Year:Rules";` block: ``` if (s_glob->AnyEnergyManagementSystemInModel) { // setup constant schedules as actuators - SetupEMSActuator(state, "Schedule:Ruleset", sched->Name, "Schedule Value", "[ ]", sched->EMSActuatedOn, sched->EMSVal); + SetupEMSActuator(state, "Schedule:Year:Rules", sched->Name, "Schedule Value", "[ ]", sched->EMSActuatedOn, sched->EMSVal); } ``` -Downstream `GetSchedule` will find the detailed schedules created from `Schedule:Ruleset` and `Schedule:Rule`. +Downstream `GetSchedule` will find the detailed schedules created from `Schedule:Year:Rules` and `Schedule:Rule`. No new data structures are needed in `ScheduleManager.hh`. -The resolution is purely a parse-time operation that flattens the ruleset into the existing `ScheduleDetailed.weekScheds[367]` array. +The resolution is purely a parse-time operation that flattens the parent rules into the existing `ScheduleDetailed.weekScheds[367]` array. No existing objects are changed. No transition is required. @@ -101,32 +101,32 @@ No transition is required. Several new unit tests in `tst/EnergyPlus/unit/ScheduleManager.unit.cc` covering: -- A ruleset with two rules demonstrating priority (lower `Rule Order` wins when date ranges overlap). +- A parent rules object with two rules demonstrating priority (lower `Rule Order` wins when date ranges overlap). - `DateRange` specification: correct day assignment across a date range, including wrap-around (e.g., Nov 1 – Jan 31). - `SpecificDates` specification: only the listed dates get the rule's day schedule. - Fallback to default day schedule when no rule matches. - Summer/winter/holiday design day overrides; fallback to default day schedule when not specified. -- Validation errors: unknown `Day Schedule Name`, duplicate `Rule Order` values within a ruleset (warning), missing Default Day Schedule. +- Validation errors: unknown `Day Schedule Name`, duplicate `Rule Order` values within a parent rules object (warning), missing Default Day Schedule. -A new test file `_ResidentialBaseScheduleRuleset.idf`, where all `Schedule:Year` / `Schedule:Week:Daily` objects are replaced with equivalent `Schedule:Ruleset` / `Schedule:Rule` objects. +A new test file `_ResidentialBaseScheduleRuleset.idf`, where all `Schedule:Year` / `Schedule:Week:Daily` objects are replaced with equivalent `Schedule:Year:Rules` / `Schedule:Rule` objects. ## Input Output Reference Documentation ## -Update `doc/input-output-reference/src/overview/group-schedules.tex` with new `Schedule:Ruleset` and `Schedule:Rule` subsections placed directly following `Schedule:Year` and `Schedule:Compact`. +Update `doc/input-output-reference/src/overview/group-schedules.tex` with new `Schedule:Year:Rules` and `Schedule:Rule` subsections placed directly following `Schedule:Year` and `Schedule:Compact`. Key points to document: - The priority model: rules are evaluated in ascending `Rule Order`; the first matching rule wins. - The `DateRange` vs `SpecificDates` date specification types and their respective fields. - The special-day schedule fields and their fallback behavior. -- The relationship between `Schedule:Ruleset`, `Schedule:Rule`, and `Schedule:Day:*` objects. +- The relationship between `Schedule:Year:Rules`, `Schedule:Rule`, and `Schedule:Day:*` objects. ## Input Description ## IDD: ``` -Schedule:Ruleset, - \memo A Schedule:Ruleset defines a yearly schedule using a default day profile and +Schedule:Year:Rules, + \memo A Schedule:Year:Rules defines a yearly schedule using a default day profile and \memo a prioritized list of override rules (Schedule:Rule objects). Rules are evaluated \memo in ascending Rule Order; the first matching rule for a given day is used. \memo If no rule matches, the Default Day Schedule is used. @@ -135,7 +135,7 @@ Schedule:Ruleset, \type alpha \required-field \reference ScheduleNames - \reference ScheduleRulesetNames + \reference ScheduleYearRulesNames A2 , \field Schedule Type Limits Name \type object-list \object-list ScheduleTypeLimitsNames @@ -165,7 +165,7 @@ Schedule:Ruleset, \object-list DayScheduleNames Schedule:Rule, - \memo A Schedule:Rule defines one override rule for a Schedule:Ruleset. + \memo A Schedule:Rule defines one override rule for a Schedule:Year:Rules. \memo Rules are evaluated in ascending Rule Order; the first matching rule wins. \memo A rule matches a day if: (a) the day falls within the date specification, \memo AND (b) the corresponding Apply field is Yes. @@ -175,12 +175,12 @@ Schedule:Rule, \type alpha \required-field \reference ScheduleRuleNames - A2 , \field Schedule Ruleset Name + A2 , \field Schedule Year Rules Name \required-field \type object-list - \object-list ScheduleRulesetNames + \object-list ScheduleYearRulesNames N1 , \field Rule Order - \note Lower values have higher priority. Must be unique within a Schedule:Ruleset. + \note Lower values have higher priority. Must be unique within a Schedule:Year:Rules. \type integer \required-field \minimum 0 @@ -269,8 +269,8 @@ Schedule:Rule, Example IDF snippet: ``` - Schedule:Ruleset, - occupants schedule ruleset, !- Name + Schedule:Year:Rules, + occupants schedule year rules, !- Name Fractional, !- Schedule Type Limits Name occupants schedule default day, !- Default Day Schedule Name occupants schedule default day, !- Summer Design Day Schedule Name @@ -281,7 +281,7 @@ Example IDF snippet: Schedule:Rule, occupants schedule rule, !- Name - occupants schedule ruleset, !- Schedule Ruleset Name + occupants schedule year rules, !- Schedule Year Rules Name 0, !- Rule Order occupants schedule day, !- Day Schedule Name No, !- Apply Sunday @@ -301,7 +301,7 @@ Example IDF snippet: ## Outputs Description ## No new output variables are added. -`Schedule:Ruleset` objects will appear in existing schedule reporting (e.g., `Output:Schedules`) under their name, identical to `Schedule:Year`. +`Schedule:Year:Rules` objects will appear in existing schedule reporting (e.g., `Output:Schedules`) under their name, identical to `Schedule:Year`. ## Engineering Reference ## @@ -310,7 +310,7 @@ The rule-resolution algorithm is a straightforward date and day-of-week lookup w ## Example File and Transition Changes ## -A new example file `_ResidentialBaseScheduleRuleset.idf` will be added, replacing all `Schedule:Year` / `Schedule:Week:Daily` objects with equivalent `Schedule:Ruleset` / `Schedule:Rule` objects to demonstrate parity. +A new example file `_ResidentialBaseScheduleRuleset.idf` will be added, replacing all `Schedule:Year` / `Schedule:Week:Daily` objects with equivalent `Schedule:Year:Rules` / `Schedule:Rule` objects to demonstrate parity. No transition rules are required; this is a purely additive feature. From ce75ebfb7ad9acd42491b0eea61a6994d69abe6c Mon Sep 17 00:00:00 2001 From: Joe Robertson Date: Mon, 15 Jun 2026 11:11:56 -0700 Subject: [PATCH 06/11] Clean up language around new schedule type. --- design/FY2026/NFP-ScheduleYearRules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/design/FY2026/NFP-ScheduleYearRules.md b/design/FY2026/NFP-ScheduleYearRules.md index 0af916331df..0da9a684152 100644 --- a/design/FY2026/NFP-ScheduleYearRules.md +++ b/design/FY2026/NFP-ScheduleYearRules.md @@ -19,7 +19,7 @@ The existing schedule types are: The OpenStudio SDK has long offered `OS:Schedule:Ruleset` as a higher-level, rule-based abstraction: the user defines a default day schedule and a prioritized list of override rules, each specifying which days of the week and which date range (or specific dates) the rule applies to. Special design day schedules (summer, winter, holiday, custom) are also first-class fields. -This model is far closer to how schedules are actually specified in building standards, energy codes, and operator manuals. +This model is perhaps closer to how schedules are actually specified in building standards, energy codes, and operator manuals. Today, the OpenStudio ForwardTranslator expands `OS:Schedule:Ruleset` into the `Schedule:Year` + `Schedule:Week:Daily` + `Schedule:Day:Interval` hierarchy before writing an IDF. Adding `Schedule:Year:Rules` natively to EnergyPlus would allow users who author IDF/epJSON files directly to use this intuitive representation, and would eventually allow the OpenStudio ForwardTranslator to emit the compact ruleset form instead of the expanded one. From 66bf9b704f39e184e526752abce4e1f45a110d3c Mon Sep 17 00:00:00 2001 From: Joe Robertson Date: Mon, 15 Jun 2026 11:13:29 -0700 Subject: [PATCH 07/11] Change field name Rule Order to Rule Priority Order. --- design/FY2026/NFP-ScheduleYearRules.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/design/FY2026/NFP-ScheduleYearRules.md b/design/FY2026/NFP-ScheduleYearRules.md index 0da9a684152..e07a401cff1 100644 --- a/design/FY2026/NFP-ScheduleYearRules.md +++ b/design/FY2026/NFP-ScheduleYearRules.md @@ -101,7 +101,7 @@ No transition is required. Several new unit tests in `tst/EnergyPlus/unit/ScheduleManager.unit.cc` covering: -- A parent rules object with two rules demonstrating priority (lower `Rule Order` wins when date ranges overlap). +- A parent rules object with two rules demonstrating priority (lower `Rule Priority Order` wins when date ranges overlap). - `DateRange` specification: correct day assignment across a date range, including wrap-around (e.g., Nov 1 – Jan 31). - `SpecificDates` specification: only the listed dates get the rule's day schedule. - Fallback to default day schedule when no rule matches. @@ -115,7 +115,7 @@ A new test file `_ResidentialBaseScheduleRuleset.idf`, where all `Schedule:Year` Update `doc/input-output-reference/src/overview/group-schedules.tex` with new `Schedule:Year:Rules` and `Schedule:Rule` subsections placed directly following `Schedule:Year` and `Schedule:Compact`. Key points to document: -- The priority model: rules are evaluated in ascending `Rule Order`; the first matching rule wins. +- The priority model: rules are evaluated in ascending `Rule Priority Order`; the first matching rule wins. - The `DateRange` vs `SpecificDates` date specification types and their respective fields. - The special-day schedule fields and their fallback behavior. - The relationship between `Schedule:Year:Rules`, `Schedule:Rule`, and `Schedule:Day:*` objects. @@ -179,7 +179,7 @@ Schedule:Rule, \required-field \type object-list \object-list ScheduleYearRulesNames - N1 , \field Rule Order + N1 , \field Rule Priority Order \note Lower values have higher priority. Must be unique within a Schedule:Year:Rules. \type integer \required-field From 25a7dbfc8c030da813eb1af8a04eb60a90b51b51 Mon Sep 17 00:00:00 2001 From: Joe Robertson Date: Mon, 15 Jun 2026 11:20:04 -0700 Subject: [PATCH 08/11] Remove specific date fields and instead have extensible date ranges. --- design/FY2026/NFP-ScheduleYearRules.md | 39 ++++++-------------------- 1 file changed, 8 insertions(+), 31 deletions(-) diff --git a/design/FY2026/NFP-ScheduleYearRules.md b/design/FY2026/NFP-ScheduleYearRules.md index e07a401cff1..83384bf1c69 100644 --- a/design/FY2026/NFP-ScheduleYearRules.md +++ b/design/FY2026/NFP-ScheduleYearRules.md @@ -53,7 +53,7 @@ The child(ren) `Schedule:Rule` object(s): - Requires an "order" be specified for determining rule index amongst other rules - Requires reference to a day schedule - Specifies which day(s) of the week for which the rule applies -- Specifies the start month/day and end month/day (date range) or month/day (specific dates) for which the rule applies +- Specifies the start month/day and end month/day (date ranges) for which the rule applies During input processing, EnergyPlus will resolve the parent rules object into the same internal `ScheduleDetailed` structure used by `Schedule:Year` and `Schedule:Compact`. No downstream code changes are required; the new objects are transparent to all schedule consumers. @@ -102,8 +102,7 @@ No transition is required. Several new unit tests in `tst/EnergyPlus/unit/ScheduleManager.unit.cc` covering: - A parent rules object with two rules demonstrating priority (lower `Rule Priority Order` wins when date ranges overlap). -- `DateRange` specification: correct day assignment across a date range, including wrap-around (e.g., Nov 1 – Jan 31). -- `SpecificDates` specification: only the listed dates get the rule's day schedule. +- Date range specification: correct day assignment across a date range, including wrap-around (e.g., Nov 1 – Jan 31). - Fallback to default day schedule when no rule matches. - Summer/winter/holiday design day overrides; fallback to default day schedule when not specified. - Validation errors: unknown `Day Schedule Name`, duplicate `Rule Order` values within a parent rules object (warning), missing Default Day Schedule. @@ -116,7 +115,6 @@ Update `doc/input-output-reference/src/overview/group-schedules.tex` with new `S Key points to document: - The priority model: rules are evaluated in ascending `Rule Priority Order`; the first matching rule wins. -- The `DateRange` vs `SpecificDates` date specification types and their respective fields. - The special-day schedule fields and their fallback behavior. - The relationship between `Schedule:Year:Rules`, `Schedule:Rule`, and `Schedule:Day:*` objects. @@ -166,11 +164,11 @@ Schedule:Year:Rules, Schedule:Rule, \memo A Schedule:Rule defines one override rule for a Schedule:Year:Rules. - \memo Rules are evaluated in ascending Rule Order; the first matching rule wins. - \memo A rule matches a day if: (a) the day falls within the date specification, + \memo Rules are evaluated in ascending Rule Priority Order; the first matching rule wins. + \memo A rule matches a day if: (a) the day falls within the specified date ranges, \memo AND (b) the corresponding Apply field is Yes. - \extensible:2 - repeat last two fields (Specific Month, Specific Day) - \min-fields 12 + \extensible:4 - repeat last four fields (Start Month, Start Day, End Month, End Day) + \min-fields 11 A1 , \field Name \type alpha \required-field @@ -223,47 +221,27 @@ Schedule:Rule, \default No \key Yes \key No - A11, \field Date Specification Type - \type choice - \default DateRange - \key DateRange - \key SpecificDates N2 , \field Start Month - \note Used only when Date Specification Type = DateRange. + \begin-extensible \type integer \minimum 1 \maximum 12 \default 1 N3 , \field Start Day - \note Used only when Date Specification Type = DateRange. \type integer \minimum 1 \maximum 31 \default 1 N4 , \field End Month - \note Used only when Date Specification Type = DateRange. \type integer \minimum 1 \maximum 12 \default 12 - N5 , \field End Day - \note Used only when Date Specification Type = DateRange. + N5 ; \field End Day \type integer \minimum 1 \maximum 31 \default 31 - N6 , \field Specific Month - \note Used only when Date Specification Type = SpecificDates. - \note Repeat the Specific Month / Specific Day pair for each date. - \begin-extensible - \type integer - \minimum 1 - \maximum 12 - N7 ; \field Specific Day - \note Used only when Date Specification Type = SpecificDates. - \type integer - \minimum 1 - \maximum 31 ``` Example IDF snippet: @@ -291,7 +269,6 @@ Example IDF snippet: No, !- Apply Thursday Yes, !- Apply Friday No, !- Apply Saturday - DateRange, !- Date Specification Type 1, !- Start Month 1, !- Start Day 12, !- End Month From fc55defc5b5ff3ea4b70e1e38bbcf49d0687ff19 Mon Sep 17 00:00:00 2001 From: Joe Robertson Date: Mon, 15 Jun 2026 11:22:14 -0700 Subject: [PATCH 09/11] Rename Schedule:Rule to Schedule:Week:Rule. --- design/FY2026/NFP-ScheduleYearRules.md | 28 +++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/design/FY2026/NFP-ScheduleYearRules.md b/design/FY2026/NFP-ScheduleYearRules.md index 83384bf1c69..d8103290ce8 100644 --- a/design/FY2026/NFP-ScheduleYearRules.md +++ b/design/FY2026/NFP-ScheduleYearRules.md @@ -48,7 +48,7 @@ The parent `Schedule:Year:Rules` object: - Requires references to schedule type limits and a default day schedule - Optionally references summer, winter, holiday, custom 1, and custom 2 design day schedules -The child(ren) `Schedule:Rule` object(s): +The child(ren) `Schedule:Week:Rule` object(s): - Requires reference to the parent `Schedule:Year:Rules` object - Requires an "order" be specified for determining rule index amongst other rules - Requires reference to a day schedule @@ -62,10 +62,10 @@ No downstream code changes are required; the new objects are transparent to all For the most part, follows the logic and implementation in OS. -Add new `Schedule:Year:Rules` and `Schedule:Rule` objects to the IDD. +Add new `Schedule:Year:Rules` and `Schedule:Week:Rule` objects to the IDD. Make updates and additions to ScheduleManager.hh and ScheduleManager.cc: -- Get all `Schedule:Rule` objects up front; move field values into structs +- Get all `Schedule:Week:Rule` objects up front; move field values into structs - Loop through each `Schedule:Year:Rules` object - Call `AddScheduleDetailed` for creating a new detailed schedule - For every day of the year: @@ -74,7 +74,7 @@ Make updates and additions to ScheduleManager.hh and ScheduleManager.cc: - Update the (12) day schedules according to the parent rule's special day schedules and rule's properties - Assign the week schedule to the week schedule's array for the year -It is not required that a day has a rule defined for it (i.e., a `Schedule:Year:Rules` may have no attached `Schedule:Rule` objects). +It is not required that a day has a rule defined for it (i.e., a `Schedule:Year:Rules` may have no attached `Schedule:Week:Rule` objects). In this case, either: - The day is Feb 29 and is just made equal to Feb 28, or - The day falls back to the default day schedule as defined by the parent rules object. @@ -89,7 +89,7 @@ Allow `Schedule:Year:Rules` objects to be actuated by adding the following at th } ``` -Downstream `GetSchedule` will find the detailed schedules created from `Schedule:Year:Rules` and `Schedule:Rule`. +Downstream `GetSchedule` will find the detailed schedules created from `Schedule:Year:Rules` and `Schedule:Week:Rule`. No new data structures are needed in `ScheduleManager.hh`. The resolution is purely a parse-time operation that flattens the parent rules into the existing `ScheduleDetailed.weekScheds[367]` array. @@ -107,16 +107,16 @@ Several new unit tests in `tst/EnergyPlus/unit/ScheduleManager.unit.cc` covering - Summer/winter/holiday design day overrides; fallback to default day schedule when not specified. - Validation errors: unknown `Day Schedule Name`, duplicate `Rule Order` values within a parent rules object (warning), missing Default Day Schedule. -A new test file `_ResidentialBaseScheduleRuleset.idf`, where all `Schedule:Year` / `Schedule:Week:Daily` objects are replaced with equivalent `Schedule:Year:Rules` / `Schedule:Rule` objects. +A new test file `_ResidentialBaseScheduleRuleset.idf`, where all `Schedule:Year` / `Schedule:Week:Daily` objects are replaced with equivalent `Schedule:Year:Rules` / `Schedule:Week:Rule` objects. ## Input Output Reference Documentation ## -Update `doc/input-output-reference/src/overview/group-schedules.tex` with new `Schedule:Year:Rules` and `Schedule:Rule` subsections placed directly following `Schedule:Year` and `Schedule:Compact`. +Update `doc/input-output-reference/src/overview/group-schedules.tex` with new `Schedule:Year:Rules` and `Schedule:Week:Rule` subsections placed directly following `Schedule:Year` and `Schedule:Compact`. Key points to document: - The priority model: rules are evaluated in ascending `Rule Priority Order`; the first matching rule wins. - The special-day schedule fields and their fallback behavior. -- The relationship between `Schedule:Year:Rules`, `Schedule:Rule`, and `Schedule:Day:*` objects. +- The relationship between `Schedule:Year:Rules`, `Schedule:Week:Rule`, and `Schedule:Day:*` objects. ## Input Description ## @@ -125,7 +125,7 @@ IDD: ``` Schedule:Year:Rules, \memo A Schedule:Year:Rules defines a yearly schedule using a default day profile and - \memo a prioritized list of override rules (Schedule:Rule objects). Rules are evaluated + \memo a prioritized list of override rules (Schedule:Week:Rule objects). Rules are evaluated \memo in ascending Rule Order; the first matching rule for a given day is used. \memo If no rule matches, the Default Day Schedule is used. \min-fields 3 @@ -162,8 +162,8 @@ Schedule:Year:Rules, \type object-list \object-list DayScheduleNames -Schedule:Rule, - \memo A Schedule:Rule defines one override rule for a Schedule:Year:Rules. +Schedule:Week:Rule, + \memo A Schedule:Week:Rule defines one override rule for a Schedule:Year:Rules. \memo Rules are evaluated in ascending Rule Priority Order; the first matching rule wins. \memo A rule matches a day if: (a) the day falls within the specified date ranges, \memo AND (b) the corresponding Apply field is Yes. @@ -257,8 +257,8 @@ Example IDF snippet: occupants schedule default day, !- Custom Day 1 Schedule Name occupants schedule default day; !- Custom Day 2 Schedule Name - Schedule:Rule, - occupants schedule rule, !- Name + Schedule:Week:Rule, + occupants schedule week rule, !- Name occupants schedule year rules, !- Schedule Year Rules Name 0, !- Rule Order occupants schedule day, !- Day Schedule Name @@ -287,7 +287,7 @@ The rule-resolution algorithm is a straightforward date and day-of-week lookup w ## Example File and Transition Changes ## -A new example file `_ResidentialBaseScheduleRuleset.idf` will be added, replacing all `Schedule:Year` / `Schedule:Week:Daily` objects with equivalent `Schedule:Year:Rules` / `Schedule:Rule` objects to demonstrate parity. +A new example file `_ResidentialBaseScheduleRuleset.idf` will be added, replacing all `Schedule:Year` / `Schedule:Week:Daily` objects with equivalent `Schedule:Year:Rules` / `Schedule:Week:Rule` objects to demonstrate parity. No transition rules are required; this is a purely additive feature. From 7b683e4e4ca9ef0ae5ca4dba78cde14641d34ddd Mon Sep 17 00:00:00 2001 From: Joe Robertson Date: Mon, 15 Jun 2026 11:25:47 -0700 Subject: [PATCH 10/11] Remove bits about adding a new example file. --- design/FY2026/NFP-ScheduleYearRules.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/design/FY2026/NFP-ScheduleYearRules.md b/design/FY2026/NFP-ScheduleYearRules.md index d8103290ce8..0c8dda612a6 100644 --- a/design/FY2026/NFP-ScheduleYearRules.md +++ b/design/FY2026/NFP-ScheduleYearRules.md @@ -107,8 +107,6 @@ Several new unit tests in `tst/EnergyPlus/unit/ScheduleManager.unit.cc` covering - Summer/winter/holiday design day overrides; fallback to default day schedule when not specified. - Validation errors: unknown `Day Schedule Name`, duplicate `Rule Order` values within a parent rules object (warning), missing Default Day Schedule. -A new test file `_ResidentialBaseScheduleRuleset.idf`, where all `Schedule:Year` / `Schedule:Week:Daily` objects are replaced with equivalent `Schedule:Year:Rules` / `Schedule:Week:Rule` objects. - ## Input Output Reference Documentation ## Update `doc/input-output-reference/src/overview/group-schedules.tex` with new `Schedule:Year:Rules` and `Schedule:Week:Rule` subsections placed directly following `Schedule:Year` and `Schedule:Compact`. @@ -287,7 +285,7 @@ The rule-resolution algorithm is a straightforward date and day-of-week lookup w ## Example File and Transition Changes ## -A new example file `_ResidentialBaseScheduleRuleset.idf` will be added, replacing all `Schedule:Year` / `Schedule:Week:Daily` objects with equivalent `Schedule:Year:Rules` / `Schedule:Week:Rule` objects to demonstrate parity. +No new example files are added; feature is purely additive so there is no need to demonstrate parity on an ongoing basis. No transition rules are required; this is a purely additive feature. From 95b1b9812819f8a69a8d8b293d6420aa163f3bb9 Mon Sep 17 00:00:00 2001 From: Joe Robertson Date: Mon, 15 Jun 2026 11:28:47 -0700 Subject: [PATCH 11/11] Clean up language around the why. --- design/FY2026/NFP-ScheduleYearRules.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/design/FY2026/NFP-ScheduleYearRules.md b/design/FY2026/NFP-ScheduleYearRules.md index 0c8dda612a6..08a4a1f3d41 100644 --- a/design/FY2026/NFP-ScheduleYearRules.md +++ b/design/FY2026/NFP-ScheduleYearRules.md @@ -32,11 +32,8 @@ Our generalized set of options for OS <-> E+ Alignment: - Option 3 - Drop support for an OS model object API; measures need to deal with the deprecation - Option 4 - Data is backed; RT/FT on the fly (probably very expensive) -For handling `Schedule:Year:Rules`, we ultimately arrived at Option 2: -- It has widespread use and popularity in OS -- We'd just be adding new standalone object(s) in E+ -- It is convenient and easily extensible (i.e., an attractive schedule type) -- The advantages outweigh the other options from above +For handling `Schedule:Year:Rules`, we ultimately arrived at Option 2 (develop and push new objects into EnergyPlus). +The schedule we propose to add is convenient and easily extensible (i.e., an attractive schedule type). ## Overview ##