Skip to content

Commit

Permalink
Merge pull request #1718 from NREL/parametric_refactor2
Browse files Browse the repository at this point in the history
Refactor parametric schedules
  • Loading branch information
mdahlhausen authored Apr 5, 2024
2 parents 070512f + 91827c9 commit 2ec6da2
Show file tree
Hide file tree
Showing 13 changed files with 746 additions and 464 deletions.
29 changes: 13 additions & 16 deletions lib/openstudio-standards/create_typical/create_typical.rb
Original file line number Diff line number Diff line change
Expand Up @@ -630,9 +630,9 @@ def self.create_typical_building_from_model(model,
'hydronic'
end

# Group the zones by occupancy type. Only split out non-dominant groups if their total area exceeds the limit.
min_area_m2 = OpenStudio.convert(20_000, 'ft^2', 'm^2').get
sys_groups = OpenstudioStandards::Geometry.model_group_thermal_zones_by_occupancy_type(model, min_area_m2: min_area_m2)
# Group the zones by occupancy type. Only split out non-dominant groups if their total area exceeds the limit.
min_area_m2 = OpenStudio.convert(20_000, 'ft^2', 'm^2').get
sys_groups = OpenstudioStandards::Geometry.model_group_thermal_zones_by_occupancy_type(model, min_area_m2: min_area_m2)

# For each group, infer the HVAC system type.
sys_groups.each do |sys_group|
Expand All @@ -656,8 +656,8 @@ def self.create_typical_building_from_model(model,
sys_type # same as primary system type
end

# group zones
story_zone_lists = OpenstudioStandards::Geometry.model_group_thermal_zones_by_building_story(model, sys_group['zones'])
# group zones
story_zone_lists = OpenstudioStandards::Geometry.model_group_thermal_zones_by_building_story(model, sys_group['zones'])

# On each story, add the primary system to the primary zones
# and add the secondary system to any zones that are different.
Expand Down Expand Up @@ -696,14 +696,14 @@ def self.create_typical_building_from_model(model,
end
end

else
# HVAC system_type specified
# Group the zones by occupancy type. Only split out non-dominant groups if their total area exceeds the limit.
min_area_m2 = OpenStudio.convert(20_000, 'ft^2', 'm^2').get
sys_groups = OpenstudioStandards::Geometry.model_group_thermal_zones_by_occupancy_type(model, min_area_m2: min_area_m2)
sys_groups.each do |sys_group|
# group zones
story_zone_groups = OpenstudioStandards::Geometry.model_group_thermal_zones_by_building_story(model, sys_group['zones'])
else
# HVAC system_type specified
# Group the zones by occupancy type. Only split out non-dominant groups if their total area exceeds the limit.
min_area_m2 = OpenStudio.convert(20_000, 'ft^2', 'm^2').get
sys_groups = OpenstudioStandards::Geometry.model_group_thermal_zones_by_occupancy_type(model, min_area_m2: min_area_m2)
sys_groups.each do |sys_group|
# group zones
story_zone_groups = OpenstudioStandards::Geometry.model_group_thermal_zones_by_building_story(model, sys_group['zones'])

# Add the user specified HVAC system for each story.
# Single-zone systems will get one per zone.
Expand All @@ -714,9 +714,7 @@ def self.create_typical_building_from_model(model,
end
end
end

end

else
# If user specified a mapping of HVAC systems to zones
user_hvac_mapping['systems'].each do |system_hash|
Expand All @@ -736,7 +734,6 @@ def self.create_typical_building_from_model(model,
end
end
end

end

# hours of operation
Expand Down
28 changes: 28 additions & 0 deletions lib/openstudio-standards/schedules/information.rb
Original file line number Diff line number Diff line change
Expand Up @@ -862,6 +862,34 @@ def self.schedule_ruleset_get_day_schedules(schedule_ruleset, include_design_day
return profiles
end

# Return the annual days of year that covered by each rule of a schedule ruleset
#
# @param schedule_ruleset [OpenStudio::Model::ScheduleRuleset] OpenStudio ScheduleRuleset object
# @return [Hash] hash of rule_index => [days_used]. Default day has rule_index = -1
def self.schedule_ruleset_get_annual_days_used(schedule_ruleset)
year_description = schedule_ruleset.model.getYearDescription
year = year_description.assumedYear
year_start_date = OpenStudio::Date.new(OpenStudio::MonthOfYear.new('January'), 1, year)
year_end_date = OpenStudio::Date.new(OpenStudio::MonthOfYear.new('December'), 31, year)
sch_indices_vector = schedule_ruleset.getActiveRuleIndices(year_start_date, year_end_date)
days_used_hash = Hash.new { |h, k| h[k] = [] }
sch_indices_vector.uniq.sort.each do |rule_i|
sch_indices_vector.each_with_index { |rule, i| days_used_hash[rule_i] << i + 1 if rule_i == rule }
end
return days_used_hash
end

# Returns the rule indices associated with defaultDay and Rule days for a given ScheduleRuleset
#
# @param schedule_ruleset [OpenStudio::Model::ScheduleRuleset] OpenStudio ScheduleRuleset object
# @return [Hash] hash of ScheduleDay => rule index. Default day has rule index of -1
def self.schedule_ruleset_get_schedule_day_rule_indices(schedule_ruleset)
schedule_day_hash = {}
schedule_day_hash[schedule_ruleset.defaultDaySchedule] = -1
schedule_ruleset.scheduleRules.each { |rule| schedule_day_hash[rule.daySchedule] = rule.ruleIndex }
return schedule_day_hash
end

# @!endgroup Information:ScheduleRuleset
end
end
62 changes: 62 additions & 0 deletions lib/openstudio-standards/schedules/modify.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,28 @@ def self.schedule_day_set_hours_of_operation(schedule_day, start_time, end_time)
schedule_day.addValue(twenty_four_hours, 1) # 1 from start of today's hours until midnight
end
end

# Sets the values of a day schedule from an array of values
# Clears out existing time value pairs and sets to supplied values
#
# @param schedule_day [OpenStudio::Model::ScheduleDay] The day schedule to set.
# @param value_array [Array] Array of 24 values. Schedule times set based on value index. Identical values will be skipped.
# @return [OpenStudio::Model::ScheduleDay]
def self.schedule_day_populate_from_array_of_values(schedule_day, value_array)
schedule_day.clearValues
if value_array.size != 24
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Schedules.Modify', "#{__method__} expects value_array to contain 24 values, instead #{value_array.size} values were given. Resulting schedule will use first #{[24, value_array.size].min} values")
end

value_array[0..23].each_with_index do |value, h|
next if value == value_array[h + 1]

time = OpenStudio::Time.new(0, h + 1, 0, 0)
schedule_day.addValue(time, value)
end
return schedule_day
end

# @!endgroup Modify:ScheduleDay

# @!group Modify:ScheduleRuleset
Expand Down Expand Up @@ -673,6 +695,46 @@ def self.schedule_ruleset_cleanup_profiles(schedule_ruleset)
return schedule_ruleset
end

# creates a minimal set of ScheduleRules that applies to all days in a given array of day of year indices
#
# @param schedule_ruleset [OpenStudio::Model::ScheduleRuleset]
# @param days_used [Array] array of day of year integers
# @param schedule_day [OpenStudio::Model::ScheduleDay] optional day schedule to apply to new rule. A new default schedule will be created for each rule if nil
# @return [Array]
def self.schedule_ruleset_create_rules_from_day_list(schedule_ruleset, days_used, schedule_day: nil)
# get year from schedule_ruleset
year = schedule_ruleset.model.getYearDescription.assumedYear

# split day_used into sub arrays of consecutive days
consec_days = days_used.chunk_while { |i, j| i + 1 == j }.to_a

# split consec_days into sub arrays of consecutive weeks by checking that any value in next array differs by seven from a value in this array
consec_weeks = consec_days.chunk_while { |i, j| i.product(j).any? { |x, y| (x - y).abs == 7 } }.to_a

# make new rule for blocks of consectutive weeks
rules = []
consec_weeks.each do |week_group|
if schedule_day.nil?
OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.Parametric.ScheduleRuleset', 'Creating new Rule Schedule from days_used vector with new Day Schedule')
rule = OpenStudio::Model::ScheduleRule.new(schedule_ruleset)
else
OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.Parametric.ScheduleRuleset', "Creating new Rule Schedule from days_used vector with clone of Day Schedule: #{schedule_day.name.get}")
rule = OpenStudio::Model::ScheduleRule.new(schedule_ruleset, schedule_day)
end

# set day types and dates
dates = week_group.flatten.map { |d| OpenStudio::Date.fromDayOfYear(d, year) }
day_types = dates.map { |date| date.dayOfWeek.valueName }.uniq
day_types.each { |type| rule.send("setApply#{type}", true) }
rule.setStartDate(dates.min)
rule.setEndDate(dates.max)

rules << rule
end

return rules
end

# @!endgroup Modify:ScheduleRuleset
end
end
Loading

0 comments on commit 2ec6da2

Please sign in to comment.