From 88569a1d4fcf99699791d763e69ed4b9991c2405 Mon Sep 17 00:00:00 2001 From: Eric Ringold Date: Wed, 13 Mar 2024 22:17:13 -0600 Subject: [PATCH 01/22] add schedule_day_populate_from_array_of_vals and schedule_ruleset_create_rules_from_day_list --- lib/openstudio-standards/schedules/modify.rb | 60 ++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/lib/openstudio-standards/schedules/modify.rb b/lib/openstudio-standards/schedules/modify.rb index ebd98fb5ed..190e71b80f 100644 --- a/lib/openstudio-standards/schedules/modify.rb +++ b/lib/openstudio-standards/schedules/modify.rb @@ -68,6 +68,24 @@ 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 + # + # @author Eric Ringold + # @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_vals(schedule_day, value_array) + schedule_day.clearValues + value_array.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 @@ -673,6 +691,48 @@ 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 + + p consec_days + # split consec_days into sub arrays of consecutive weeks by checking that all values in next array differ by seven from the value at the same index in this array + # consec_weeks = consec_days.chunk_while { |i, j| j.zip(i).map { |a| a.inject(:-) }.all?(7) }.to_a + 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 From f9a4b57da3fc6e95b417bbab1fc0c2cdc8031893 Mon Sep 17 00:00:00 2001 From: Eric Ringold Date: Wed, 13 Mar 2024 22:19:59 -0600 Subject: [PATCH 02/22] refactor spaces_get_occupancy_schedules --- lib/openstudio-standards/space/space.rb | 326 ++++++------------------ 1 file changed, 73 insertions(+), 253 deletions(-) diff --git a/lib/openstudio-standards/space/space.rb b/lib/openstudio-standards/space/space.rb index 5ebc63a4e1..74dab52f55 100644 --- a/lib/openstudio-standards/space/space.rb +++ b/lib/openstudio-standards/space/space.rb @@ -336,326 +336,146 @@ def self.spaces_hours_of_operation(spaces) # normalized_daily_range evaluates each value against the min/max range for the day. # The goal is a dynamic threshold that calibrates each day. # @return [] a ScheduleRuleset of fractional or discrete occupancy - # @todo Speed up this method. Bottleneck is ScheduleRule.getDaySchedules def self.spaces_get_occupancy_schedule(spaces, sch_name: nil, occupied_percentage_threshold: nil, threshold_calc_method: 'value') unless !spaces.empty? OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.space', 'Empty spaces array passed to spaces_get_occupancy_schedule method.') return false end - annual_normalized_tol = nil - if threshold_calc_method == 'normalized_annual_range' - # run this method without threshold to get annual min and max - temp_merged = spaces_get_occupancy_schedule(spaces) - tem_min_max = OpenstudioStandards::Schedules.schedule_ruleset_get_min_max(temp_merged) - annual_normalized_tol = tem_min_max['min'] + (tem_min_max['max'] - tem_min_max['min']) * occupied_percentage_threshold - temp_merged.remove + unless sch_name.nil? + OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.space', "Finding space schedules for #{sch_name}.") end + # Get all the occupancy schedules in spaces. # Include people added via the SpaceType and hard-assigned to the Space itself. - occ_schedules_num_occ = {} # hash of occupancy ScheduleRuleset => number of total number of people - max_occ_in_spaces = 0 + occ_schedules_num_occ = {} # hash of People ScheduleRuleset => design occupancy for that People object spaces.each do |space| # From the space type if space.spaceType.is_initialized space.spaceType.get.people.each do |people| num_ppl_sch = people.numberofPeopleSchedule next if num_ppl_sch.empty? - if num_ppl_sch.get.to_ScheduleRuleset.empty? # skip non-ruleset schedules OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.space', "People schedule #{num_ppl_sch.get.name} is not a Ruleset Schedule, it will not contribute to hours of operation") else num_ppl_sch = num_ppl_sch.get.to_ScheduleRuleset.get num_ppl = people.getNumberOfPeople(space.floorArea) occ_schedules_num_occ.key?(num_ppl_sch) ? occ_schedules_num_occ[num_ppl_sch] += num_ppl : occ_schedules_num_occ[num_ppl_sch] = num_ppl - max_occ_in_spaces += num_ppl end end end + # From the space space.people.each do |people| num_ppl_sch = people.numberofPeopleSchedule next if num_ppl_sch.empty? - if num_ppl_sch.get.to_ScheduleRuleset.empty? # skip non-ruleset schedules OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.space', "People schedule #{num_ppl_sch.get.name} is not a Ruleset Schedule, it will not contribute to hours of operation") else num_ppl_sch = num_ppl_sch.get.to_ScheduleRuleset.get num_ppl = people.getNumberOfPeople(space.floorArea) occ_schedules_num_occ.key?(num_ppl_sch) ? occ_schedules_num_occ[num_ppl_sch] += num_ppl : occ_schedules_num_occ[num_ppl_sch] = num_ppl - max_occ_in_spaces += num_ppl end end end - unless sch_name.nil? - OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.space', "Finding space schedules for #{sch_name}.") - end OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.space', "The #{spaces.size} spaces have #{occ_schedules_num_occ.size} unique occ schedules.") occ_schedules_num_occ.each do |occ_sch, num_occ| OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.space', "...#{occ_sch.name} - #{num_occ.round} people") end - OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.space', " Total #{max_occ_in_spaces.round} people in #{spaces.size} spaces.") - - # Store arrays of 365 day schedules used by each occ schedule once for later - # Store arrays of day schedule times for later - occ_schedules_day_schedules = {} - day_schedule_times = {} - year = spaces[0].model.getYearDescription - first_date_of_year = year.makeDate(1) - end_date_of_year = year.makeDate(365) + + # get nested array of 8760 values of the total occupancy at each hour of each schedule + all_schedule_hourly_occ = [] occ_schedules_num_occ.each do |occ_sch, num_occ| - day_schedules = occ_sch.getDaySchedules(first_date_of_year, end_date_of_year) - # Store array of day schedules - occ_schedules_day_schedules[occ_sch] = day_schedules - day_schedules.uniq.each do |day_sch| - # Skip schedules that have been stored previously - next unless day_schedule_times[day_sch].nil? - - # Store times - times = [] - day_sch.times.each do |time| - times << time.toString - end - day_schedule_times[day_sch] = times - end + all_schedule_hourly_occ << OpenstudioStandards::Schedules.schedule_get_hourly_values(occ_sch).map { |i| i * num_occ } end - # For each day of the year, determine time_value_pairs = [] - yearly_data = [] - (1..365).each do |i| - times_on_this_day = [] - os_date = year.makeDate(i) - day_of_week = os_date.dayOfWeek.valueName - - # Get the unique time indices and corresponding day schedules - day_sch_num_occ = {} - occ_schedules_num_occ.each do |occ_sch, num_occ| - daily_sch = occ_schedules_day_schedules[occ_sch][i - 1] - times_on_this_day += day_schedule_times[daily_sch] - day_sch_num_occ[daily_sch] = num_occ - end - - daily_normalized_tol = nil - if threshold_calc_method == 'normalized_daily_range' - # pre-process day to get daily min and max - daily_spaces_occ_frac = [] - times_on_this_day.uniq.sort.each do |time| - os_time = OpenStudio::Time.new(time) - # Total number of people at each time - tot_occ_at_time = 0 - day_sch_num_occ.each do |day_sch, num_occ| - occ_frac = day_sch.getValue(os_time) - tot_occ_at_time += occ_frac * num_occ - end - # Total fraction for the spaces at each time - daily_spaces_occ_frac << tot_occ_at_time / max_occ_in_spaces - daily_normalized_tol = daily_spaces_occ_frac.min + (daily_spaces_occ_frac.max - daily_spaces_occ_frac.min) * occupied_percentage_threshold - end - end - - # Determine the total fraction for the spaces at each time - daily_times = [] - daily_os_times = [] - daily_values = [] - daily_occs = [] - times_on_this_day.uniq.sort.each do |time| - os_time = OpenStudio::Time.new(time) - # Total number of people at each time - tot_occ_at_time = 0 - day_sch_num_occ.each do |day_sch, num_occ| - occ_frac = day_sch.getValue(os_time) - tot_occ_at_time += occ_frac * num_occ - end - - # Total fraction for the spaces at each time, - # rounded to avoid decimal precision issues - spaces_occ_frac = (tot_occ_at_time / max_occ_in_spaces).round(3) - - # If occupied_percentage_threshold is specified, schedule values are boolean - # Otherwise use the actual spaces_occ_frac - if occupied_percentage_threshold.nil? - occ_status = spaces_occ_frac - elsif threshold_calc_method == 'normalized_annual_range' - occ_status = 0 # unoccupied - if spaces_occ_frac >= annual_normalized_tol - occ_status = 1 - end - elsif threshold_calc_method == 'normalized_daily_range' - occ_status = 0 # unoccupied - if spaces_occ_frac >= daily_normalized_tol - occ_status = 1 - end - else - occ_status = 0 # unoccupied - if spaces_occ_frac >= occupied_percentage_threshold - occ_status = 1 - end - end - - # Add this data to the daily arrays - daily_times << time - daily_os_times << os_time - daily_values << occ_status - daily_occs << spaces_occ_frac.round(2) - end - - # Simplify the daily times to eliminate intermediate points with the same value as the following point - simple_daily_times = [] - simple_daily_os_times = [] - simple_daily_values = [] - simple_daily_occs = [] - daily_values.each_with_index do |value, j| - next if value == daily_values[j + 1] - - simple_daily_times << daily_times[j] - simple_daily_os_times << daily_os_times[j] - simple_daily_values << daily_values[j] - simple_daily_occs << daily_occs[j] - end + # total occupancy from all people + total_design_occ = occ_schedules_num_occ.values.sum + + OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.space', " Total #{total_design_occ.round} people in #{spaces.size} spaces.") + + # get one 8760 array of the sum of each schedule's hourly occupancy + combined_hourly_occ = all_schedule_hourly_occ.transpose.map(&:sum) + + # divide each hourly value by total occupancy - this is all spaces fractional occupancy + combined_occ_frac = combined_hourly_occ.map { |i| i / total_design_occ } + + # divide 8760 array into 365(or 366)x24 arrays + daily_combined_occ_fracs = combined_occ_frac.each_slice(24).to_a + + # If occupied_percentage_threshold is specified, schedule values are boolean + # Otherwise use the actual spaces_occ_frac + if occupied_percentage_threshold.nil? + occ_status_vals = daily_combined_occ_fracs + elsif threshold_calc_method == 'normalized_daily_range' + # calculate max/min values in each daily occ fraction array + daily_max_vals = daily_combined_occ_fracs.map(&:max) + daily_min_vals = daily_combined_occ_fracs.map(&:min) + # normalize threshold to daily min/max values + daily_normalized_thresholds = daily_min_vals.zip(daily_max_vals).map { |min_max| min_max[0] + (min_max[1] - min_max[0]) * occupied_percentage_threshold} + # if daily occ frac exceeds daily normalized threshold, set value to 1 + occ_status_vals = daily_combined_occ_fracs.each_with_index.map { |day_array, i| day_array.map{ |day_val| !day_val.zero? && day_val >= daily_normalized_thresholds[i] ? 1 : 0 } } + elsif threshold_calc_method == 'normalized_annual_range' + # calculate annual min/max values + annual_max = daily_combined_occ_fracs.max_by(&:max).max + annual_min = daily_combined_occ_fracs.min_by(&:min).min + # normalize threshold to annual min/max + annual_normalized_threshold = annual_min + (annual_max - annual_min) * occupied_percentage_threshold + # if vals exceed threshold, set val to 1 + occ_status_vals = daily_combined_occ_fracs.map { |day_array| day_array.map { |day_val| day_val >= annual_normalized_threshold ? 1 : 0 } } + else #threshold_calc_method == 'value' + occ_status_vals = daily_combined_occ_fracs.map { |day_array| day_array.map { |day_val| day_val >= occupied_percentage_threshold ? 1 : 0 } } + end - # Store the daily values - yearly_data << { 'date' => os_date, 'day_of_week' => day_of_week, 'times' => simple_daily_times, 'values' => simple_daily_values, 'daily_os_times' => simple_daily_os_times, 'daily_occs' => simple_daily_occs } + # get unique daily profiles + unique_profiles = occ_status_vals.uniq + profile_days_hash = {} # hash of unique profile => array of day indeces + unique_profiles.each do |day_profile| + days_with_profile = occ_status_vals.each_with_index.filter_map { |day, i| i + 1 if day == day_profile } + profile_days_hash[day_profile] = days_with_profile end - # Create a TimeSeries from the data - # time_series = OpenStudio::TimeSeries.new(times, values, 'unitless') - # Make a schedule ruleset + # create schedule if sch_name.nil? sch_name = "#{spaces.size} space(s) Occ Sch" end - sch_ruleset = OpenStudio::Model::ScheduleRuleset.new(spaces[0].model) - sch_ruleset.setName(sch_name.to_s) + schedule_ruleset = OpenStudio::Model::ScheduleRuleset.new(spaces[0].model) + schedule_ruleset.setName(sch_name.to_s) # add properties to schedule - props = sch_ruleset.additionalProperties - props.setFeature('max_occ_in_spaces', max_occ_in_spaces) + props = schedule_ruleset.additionalProperties + props.setFeature('max_occ_in_spaces', total_design_occ) props.setFeature('number_of_spaces_included', spaces.size) # nothing uses this but can make user be aware if this may be out of sync with current state of occupancy profiles props.setFeature('date_parent_object_last_edited', Time.now.getgm.to_s) props.setFeature('date_parent_object_created', Time.now.getgm.to_s) - # Default - All Occupied - day_sch = sch_ruleset.defaultDaySchedule - day_sch.setName("#{sch_name} Default") - day_sch.addValue(OpenStudio::Time.new(0, 24, 0, 0), 1) - # Winter Design Day - All Occupied - day_sch = OpenStudio::Model::ScheduleDay.new(spaces[0].model) - sch_ruleset.setWinterDesignDaySchedule(day_sch) - day_sch = sch_ruleset.winterDesignDaySchedule + schedule_ruleset.setWinterDesignDaySchedule(schedule_ruleset.winterDesignDaySchedule) + day_sch = schedule_ruleset.winterDesignDaySchedule day_sch.setName("#{sch_name} Winter Design Day") day_sch.addValue(OpenStudio::Time.new(0, 24, 0, 0), 1) # Summer Design Day - All Occupied - day_sch = OpenStudio::Model::ScheduleDay.new(spaces[0].model) - sch_ruleset.setSummerDesignDaySchedule(day_sch) - day_sch = sch_ruleset.summerDesignDaySchedule + schedule_ruleset.setSummerDesignDaySchedule(schedule_ruleset.summerDesignDaySchedule) + day_sch = schedule_ruleset.summerDesignDaySchedule day_sch.setName("#{sch_name} Summer Design Day") day_sch.addValue(OpenStudio::Time.new(0, 24, 0, 0), 1) - # Create ruleset schedules, attempting to create the minimum number of unique rules - ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'].each do |weekday| - end_of_prev_rule = yearly_data[0]['date'] - yearly_data.each_with_index do |daily_data, k| - # Skip unless it is the day of week - # currently under inspection - day = daily_data['day_of_week'] - next unless day == weekday - - date = daily_data['date'] - times = daily_data['times'] - values = daily_data['values'] - daily_os_times = daily_data['daily_os_times'] - - # If the next (Monday, Tuesday, etc.) is the same as today, keep going - # If the next is different, or if we've reached the end of the year, create a new rule - unless yearly_data[k + 7].nil? - next_day_times = yearly_data[k + 7]['times'] - next_day_values = yearly_data[k + 7]['values'] - next if times == next_day_times && values == next_day_values - end - - # If here, we need to make a rule to cover from the previous rule to today - OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.space', "Making a new rule for #{weekday} from #{end_of_prev_rule} to #{date}") - sch_rule = OpenStudio::Model::ScheduleRule.new(sch_ruleset) - sch_rule.setName("#{sch_name} #{weekday} Rule") - day_sch = sch_rule.daySchedule - day_sch.setName("#{sch_name} #{weekday}") - daily_os_times.each_with_index do |time, t| - value = values[t] - next if value == values[t + 1] # Don't add breaks if same value - - day_sch.addValue(time, value) - end - - # Set the dates when the rule applies - sch_rule.setStartDate(end_of_prev_rule) - # for end dates in last week of year force it to use 12/31. Avoids issues if year or start day of week changes - start_of_last_week = OpenStudio::Date.new(OpenStudio::MonthOfYear.new('December'), 25, year.assumedYear) - if date >= start_of_last_week - year_end_date = OpenStudio::Date.new(OpenStudio::MonthOfYear.new('December'), 31, year.assumedYear) - sch_rule.setEndDate(year_end_date) - else - sch_rule.setEndDate(date) - end - - # Individual Days - sch_rule.setApplyMonday(true) if weekday == 'Monday' - sch_rule.setApplyTuesday(true) if weekday == 'Tuesday' - sch_rule.setApplyWednesday(true) if weekday == 'Wednesday' - sch_rule.setApplyThursday(true) if weekday == 'Thursday' - sch_rule.setApplyFriday(true) if weekday == 'Friday' - sch_rule.setApplySaturday(true) if weekday == 'Saturday' - sch_rule.setApplySunday(true) if weekday == 'Sunday' - - # Reset the previous rule end date - end_of_prev_rule = date + OpenStudio::Time.new(0, 24, 0, 0) - end + # set most used profile to default day + most_used_profile = profile_days_hash.max_by { |k,v| v.size }.first + default_day = schedule_ruleset.defaultDaySchedule + default_day.setName("#{sch_name} Default") + Schedules.schedule_day_populate_from_array_of_vals(default_day, most_used_profile) + + # create rules from remaining profiles + remaining_profiles = profile_days_hash.slice(*profile_days_hash.keys.reject { |k| k == most_used_profile }) + remaining_profiles.each do |profile, days_used| + rules = Schedules.schedule_ruleset_create_rules_from_day_list(schedule_ruleset, days_used) + rules.each{|rule| Schedules.schedule_day_populate_from_array_of_vals(rule.daySchedule, profile)} end - # utilize default profile and common similar days of week for same date range - # todo - if move to method in Standards.ScheduleRuleset.rb udpate code to check if default profile is used before replacing it with lowest priority rule. - # todo - also merging non adjacent priority rules without getting rid of any rules between the two could create unexpected reults - prior_rules = [] - sch_ruleset.scheduleRules.each do |rule| - if prior_rules.empty? - prior_rules << rule - next - else - rules_combined = false - prior_rules.each do |prior_rule| - # see if they are similar - next if rules_combined - # @todo update to combine adjacent date ranges vs. just matching date ranges - next if prior_rule.startDate.get != rule.startDate.get - next if prior_rule.endDate.get != rule.endDate.get - next if prior_rule.daySchedule.times.to_a != rule.daySchedule.times.to_a - next if prior_rule.daySchedule.values.to_a != rule.daySchedule.values.to_a - - # combine dates of week - if rule.applyMonday then prior_rule.setApplyMonday(true) && rules_combined = true end - if rule.applyTuesday then prior_rule.setApplyTuesday(true) && rules_combined = true end - if rule.applyWednesday then prior_rule.setApplyWednesday(true) && rules_combined = true end - if rule.applyThursday then prior_rule.setApplyThursday(true) && rules_combined = true end - if rule.applyFriday then prior_rule.setApplyFriday(true) && rules_combined = true end - if rule.applySaturday then prior_rule.setApplySaturday(true) && rules_combined = true end - if rule.applySunday then prior_rule.setApplySunday(true) && rules_combined = true end - end - rules_combined ? rule.remove : prior_rules << rule - end - end - # replace unused default profile with lowest priority rule - values = prior_rules.last.daySchedule.values - times = prior_rules.last.daySchedule.times - prior_rules.last.remove - sch_ruleset.defaultDaySchedule.clearValues - values.size.times do |i| - sch_ruleset.defaultDaySchedule.addValue(times[i], values[i]) - end - - OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.space', "Created #{sch_ruleset.name} with #{OpenstudioStandards::Schedules.schedule_ruleset_get_equivalent_full_load_hours(sch_ruleset)} annual EFLH.") - - return sch_ruleset + return schedule_ruleset end # @!endgroup Space From de928422b5b4897d03dd74f6e207d1f286671a1e Mon Sep 17 00:00:00 2001 From: Eric Ringold Date: Wed, 13 Mar 2024 22:20:50 -0600 Subject: [PATCH 03/22] minor updates to test_spaces_get_occupancy_schedule --- test/modules/space/test_space.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/modules/space/test_space.rb b/test/modules/space/test_space.rb index d6b82a41fa..3e3f125fec 100644 --- a/test/modules/space/test_space.rb +++ b/test/modules/space/test_space.rb @@ -172,10 +172,10 @@ def test_spaces_get_occupancy_schedule assert_equal(summer_wkdy_hrly_vals.rindex(0.625), 11) assert_equal(summer_wkdy_hrly_vals.index(0.875), 12) assert_equal(summer_wkdy_hrly_vals.rindex(0.875), 15) - assert_equal(summer_wkdy_hrly_vals.rindex(0.538), 17) - assert_equal(summer_wkdy_hrly_vals.rindex(0.163), 19) + assert_equal(summer_wkdy_hrly_vals.rindex(0.5375), 17) + assert_equal(summer_wkdy_hrly_vals.rindex(0.1625), 19) - assert_in_delta(@sch.schedule_ruleset_get_equivalent_full_load_hours(occ_sch_fracs), 2290.28, 0.01) + assert_in_delta(@sch.schedule_ruleset_get_equivalent_full_load_hours(occ_sch_fracs), 2290.15, 0.01) # before refactor: 2290.28 # not normalized occ_sch_values = @space.spaces_get_occupancy_schedule([space1,space2], sch_name: 'test occupancy threshold', occupied_percentage_threshold: 0.3, threshold_calc_method: nil) From 464cab218042a1f3c57a78c98439411b7c5cb91e Mon Sep 17 00:00:00 2001 From: Eric Ringold Date: Tue, 19 Mar 2024 13:21:20 -0600 Subject: [PATCH 04/22] create schedule rules to match hours of operation and always be modified --- .../schedules/parametric.rb | 81 ++++++++++++------- 1 file changed, 52 insertions(+), 29 deletions(-) diff --git a/lib/openstudio-standards/schedules/parametric.rb b/lib/openstudio-standards/schedules/parametric.rb index ec3895dd17..01a4b7a95b 100644 --- a/lib/openstudio-standards/schedules/parametric.rb +++ b/lib/openstudio-standards/schedules/parametric.rb @@ -486,47 +486,71 @@ def self.gather_inputs_parametric_schedules(sch, load_inst, parametric_inputs, h # cleanup existing profiles OpenstudioStandards::Schedules.schedule_ruleset_cleanup_profiles(sch) - # gather profiles - daily_flhs = [] # will be used to tag, min,medium,max operation for non typical operations - schedule_days = {} # key is day_schedule value is rule index - sch.scheduleRules.each do |rule| - schedule_days[rule.daySchedule] = rule.ruleIndex - daily_flhs << OpenstudioStandards::Schedules.schedule_day_get_equivalent_full_load_hours(rule.daySchedule) + # get initial hash of schedule days => rule indices + schedule_days = schedule_ruleset_get_schedule_day_rule_indices(sch) + # get all day schedule equivalent full load hours to tag + daily_flhs = schedule_days.keys.map { |day_sch| schedule_day_get_equivalent_full_load_hours(day_sch) } + # collect initial rule index => array of days used hash + sch_ruleset_days_used = schedule_ruleset_get_annual_days_used(sch) + + # match up schedule rule days with hours of operation days + sch_day_map = {} + sch_ruleset_days_used.each do |sch_index, sch_days| + day_map = {} + sch_days.each do |day| + # find the hour of operation rule that contains the day number + hoo_key = hours_of_operation.find { |_, val| val[:days_used].include?(day) }&.first + day_map[day] = hoo_key + end + # group days with the same hour of operation index + grouped_days = Hash.new { |h, k| h[k] = [] } + day_map.each { |day, hoo_idx| grouped_days[hoo_idx] << day} + # group by schedule rule index + sch_day_map[sch_index] = grouped_days end - schedule_days[sch.defaultDaySchedule] = -1 - daily_flhs << OpenstudioStandards::Schedules.schedule_day_get_equivalent_full_load_hours(sch.defaultDaySchedule) - # get indices for current schedule - year_description = sch.model.yearDescription.get - 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) - indices_vector = sch.getActiveRuleIndices(year_start_date, year_end_date) + # create new rule corresponding to the hour of operation rules + new_rule_ct = 0 + sch_day_map.each do |sch_index, hoo_group| + hoo_group.each do |hoo_index, day_group| + # skip common default days + next if sch_index == -1 && hoo_index == -1 + # skip if rules already match + if (sch_ruleset_days_used[sch_index] - day_group).empty? + OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.Parametric.Schedules', "in #{__method__}: #{sch.name} rule #{sch_index} already matches hours of operation rule #{hoo_index}; new rule won't be created.") + next + end + # create new rules + new_rules = schedule_ruleset_create_rules_from_day_list(sch, day_group, schedule_day: schedule_days.key(sch_index)) + new_rule_ct += new_rules.size + end + end + # new rules are created at top of list - cleanup old rules + sch.scheduleRules[new_rule_ct..-1].each( &:remove ) unless new_rule_ct == 0 + + # re-collect new schedule rules + schedule_days = schedule_ruleset_get_schedule_day_rule_indices(sch) + # re-collect new rule index => days used array + sch_ruleset_days_used = schedule_ruleset_get_annual_days_used(sch) # step through profiles and add additional properties to describe profiles schedule_days.each_with_index do |(schedule_day, current_rule_index), i| - # loop through indices looking of rule in hoo that contains days in the rule + hoo_target_index = nil - days_used = [] - indices_vector.each_with_index do |profile_index, i| - if profile_index == current_rule_index then days_used << i + 1 end - end - # puts "#{__method__}>>> #{schedule_day.name} days_used: #{days_used}" + + days_used = sch_ruleset_days_used[current_rule_index] # find days_used in hoo profiles that contains all days used from this profile hoo_profile_match_hash = {} best_fit_check = {} - days_for_rule_not_in_hoo_profile = [] + # loop through indices looking of rule in hoo that contains all days in the rule hours_of_operation.each do |profile_index, value| - days_for_rule_not_in_hoo_profile = days_used - value[:days_used] - # puts "in loop: #{profile_index} - #{days_for_rule_not_in_hoo_profile}" - hoo_profile_match_hash[profile_index] = days_for_rule_not_in_hoo_profile - best_fit_check[profile_index] = days_for_rule_not_in_hoo_profile.size - if days_for_rule_not_in_hoo_profile.empty? + if (days_used - value[:days_used]).empty? hoo_target_index = profile_index end end + # if schedule day days used can't be mapped to single hours of operation then do not use hoo variables, otherwise would have ot split rule and alter model if hoo_target_index.nil? @@ -534,9 +558,8 @@ def self.gather_inputs_parametric_schedules(sch, load_inst, parametric_inputs, h hoo_end = nil occ = nil vac = nil - # OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Parametric.Schedules', "In #{__method__}, schedule #{schedule_day.name} has no hours_of_operation target index. Days for rule not in hoo profile: #{days_for_rule_not_in_hoo_profile}") - OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Parametric.Schedules', "In #{__method__}, schedule #{schedule_day.name} has no hours_of_operation target index. Won't be modified") - # @todo issue warning when this happens on any profile that isn't a constant value + OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Parametric.Schedules', "In #{__method__}, schedule #{schedule_day.name} has no hours_of_operation target index. Days for rule not in hoo profile: #{days_for_rule_not_in_hoo_profile}") + # OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Parametric.Schedules', "In #{__method__}, schedule #{schedule_day.name} has no hours_of_operation target index. Won't be modified") else # get hours of operation for this specific profile hoo_start = hours_of_operation[hoo_target_index][:hoo_start] From 06ca562e29dc755df73afe837a873cca2e392ead Mon Sep 17 00:00:00 2001 From: Eric Ringold Date: Tue, 19 Mar 2024 13:21:57 -0600 Subject: [PATCH 05/22] new methods to get schedule day/rule info --- .../schedules/information.rb | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/lib/openstudio-standards/schedules/information.rb b/lib/openstudio-standards/schedules/information.rb index 3f91f24e78..1526cdd8ed 100644 --- a/lib/openstudio-standards/schedules/information.rb +++ b/lib/openstudio-standards/schedules/information.rb @@ -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 + # @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 indeces associated with defaultDay and Rule days for a given ScheduleRuleset + # + # @param schedule_ruleset [OpenStudio::Model::ScheduleRuleset] + # @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 } + schedule_day_hash + end + # @!endgroup Information:ScheduleRuleset end end From 0d3e580146138a36e877b986d06b16603b232fb2 Mon Sep 17 00:00:00 2001 From: Eric Ringold Date: Tue, 19 Mar 2024 13:22:31 -0600 Subject: [PATCH 06/22] clean up create_rules_from_day_list --- lib/openstudio-standards/schedules/modify.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/openstudio-standards/schedules/modify.rb b/lib/openstudio-standards/schedules/modify.rb index 190e71b80f..8df9e003eb 100644 --- a/lib/openstudio-standards/schedules/modify.rb +++ b/lib/openstudio-standards/schedules/modify.rb @@ -704,9 +704,7 @@ def self.schedule_ruleset_create_rules_from_day_list(schedule_ruleset, days_used # split day_used into sub arrays of consecutive days consec_days = days_used.chunk_while { |i, j| i + 1 == j }.to_a - p consec_days - # split consec_days into sub arrays of consecutive weeks by checking that all values in next array differ by seven from the value at the same index in this array - # consec_weeks = consec_days.chunk_while { |i, j| j.zip(i).map { |a| a.inject(:-) }.all?(7) }.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 From 52c16d4f81006b4724b2e8a9d49816a018af8036 Mon Sep 17 00:00:00 2001 From: Eric Ringold Date: Tue, 19 Mar 2024 13:29:58 -0600 Subject: [PATCH 07/22] update gather_inputs_parametric_schedules test - now passing --- .../schedules/test_schedules_parametric.rb | 38 +++++++++++++------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/test/modules/schedules/test_schedules_parametric.rb b/test/modules/schedules/test_schedules_parametric.rb index 5d876b48fb..b8455bed11 100644 --- a/test/modules/schedules/test_schedules_parametric.rb +++ b/test/modules/schedules/test_schedules_parametric.rb @@ -120,21 +120,35 @@ def test_gather_inputs_parametric_schedules parametric_inputs = @sch.gather_inputs_parametric_schedules(lght_sch, lght, {}, hours_of_operation, ramp:true, min_ramp_dur_hr: 2.0, gather_data_only: false, hoo_var_method: 'hours') - # this will create additional properties which do not pass the assertions - # fixme: refactor method so that schedules with rules that don't match hours_of_operation will pass - # parametric_inputs = @sch.gather_inputs_parametric_schedules(clg_sch, nil, {}, hours_of_operation, ramp: true, min_ramp_dur_hr: 2.0, gather_data_only: false, hoo_var_method: 'hours') + parametric_inputs = @sch.gather_inputs_parametric_schedules(clg_sch, nil, {}, hours_of_operation, ramp: true, min_ramp_dur_hr: 2.0, gather_data_only: false, hoo_var_method: 'hours') # collect additional properties props = get_additional_properties(lght_sch.defaultDaySchedule.additionalProperties) - lght_sch.scheduleRules.each {|obj| props + get_additional_properties(obj.daySchedule.additionalProperties)} - props + get_additional_properties(clg_sch.defaultDaySchedule.additionalProperties) - clg_sch.scheduleRules.each {|obj| props + get_additional_properties(obj.daySchedule.additionalProperties)} - - # test that add'props have tags to modify the schedules - props.select{|p| p.include? 'param_day_profile'}.each do |formula| - assert(formula.include?('hoo_start'), "missing hoo_start: #{formula}") - assert(formula.include?('hoo_end'), "missing hoo_end: #{formula}") - end + lght_sch.scheduleRules.each {|obj| props += get_additional_properties(obj.daySchedule.additionalProperties)} + + # test formulas individually + lght_def_prop = get_additional_properties(lght_sch.defaultDaySchedule.additionalProperties) + def_param_profile = lght_def_prop.select { |prop| prop.include?('param_day_profile') }.first + assert(def_param_profile.include?('hoo_start'), "#{lght_sch.defaultDaySchedule.name.get} missing hoo_start: #{lght_def_prop}") + assert(def_param_profile.include?('hoo_end'), "#{lght_sch.defaultDaySchedule.name.get} missing hoo_end: #{lght_def_prop}") + lght_wknd_prop = get_additional_properties(lght_sch.scheduleRules.first.daySchedule.additionalProperties) + wkdn_param_profile = lght_wknd_prop.select { |prop| prop.include?('param_day_profile') }.first + assert(wkdn_param_profile.include?('hoo_start'), "#{lght_sch.scheduleRules.first.daySchedule.name.get} missing hoo_start: #{lght_def_prop}") + + props += get_additional_properties(clg_sch.defaultDaySchedule.additionalProperties) + clg_sch.scheduleRules.each {|obj| props += get_additional_properties(obj.daySchedule.additionalProperties)} + + # test formulas individually + clg_def_prop = get_additional_properties(clg_sch.defaultDaySchedule.additionalProperties) + def_param_profile = clg_def_prop.select { |prop| prop.include?('param_day_profile') }.first + assert(def_param_profile.include?('hoo_start'), "#{clg_sch.defaultDaySchedule.name.get} missing hoo_start: #{clg_def_prop}") + assert(def_param_profile.include?('hoo_end'), "#{clg_sch.defaultDaySchedule.name.get} missing hoo_end: #{clg_def_prop}") + clg_wknd_prop = get_additional_properties(clg_sch.scheduleRules.first.daySchedule.additionalProperties) + wknd_param_profile = clg_wknd_prop.select { |prop| prop.include? ('param_day_profile' ) }.first + assert(wknd_param_profile.include?('hoo_start +'), "#{clg_sch.scheduleRules.first.daySchedule.name.get} missing hoo_start: #{clg_wknd_prop}") + assert(wknd_param_profile.include?('hoo_start -'), "#{clg_sch.scheduleRules.first.daySchedule.name.get} missing hoo_end: #{clg_wknd_prop}") + + # props.each{|pr| p pr} end def test_schedule_ruleset_apply_parametric_inputs From 7c142843a39d6bdef567e0942a293ca14fffeaab Mon Sep 17 00:00:00 2001 From: Eric Ringold Date: Tue, 2 Apr 2024 15:08:39 -0600 Subject: [PATCH 08/22] adds method to adjust thermostats to follow user input hours of operation --- .../schedules/parametric.rb | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/openstudio-standards/schedules/parametric.rb b/lib/openstudio-standards/schedules/parametric.rb index 01a4b7a95b..2d68887128 100644 --- a/lib/openstudio-standards/schedules/parametric.rb +++ b/lib/openstudio-standards/schedules/parametric.rb @@ -206,11 +206,11 @@ def self.model_setup_parametric_schedules(model, step_ramp_logic: nil, infer_hoo thermostat = zone.thermostatSetpointDualSetpoint.get if thermostat.heatingSetpointTemperatureSchedule.is_initialized && thermostat.heatingSetpointTemperatureSchedule.get.to_ScheduleRuleset.is_initialized schedule = thermostat.heatingSetpointTemperatureSchedule.get.to_ScheduleRuleset.get - gather_inputs_parametric_schedules(schedule, thermostat, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: hoo_var_method) + gather_inputs_parametric_schedules(schedule, thermostat, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: 'tstat') end if thermostat.coolingSetpointTemperatureSchedule.is_initialized && thermostat.coolingSetpointTemperatureSchedule.get.to_ScheduleRuleset.is_initialized schedule = thermostat.coolingSetpointTemperatureSchedule.get.to_ScheduleRuleset.get - gather_inputs_parametric_schedules(schedule, thermostat, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: hoo_var_method) + gather_inputs_parametric_schedules(schedule, thermostat, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: 'tstat') end end end @@ -558,7 +558,7 @@ def self.gather_inputs_parametric_schedules(sch, load_inst, parametric_inputs, h hoo_end = nil occ = nil vac = nil - OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Parametric.Schedules', "In #{__method__}, schedule #{schedule_day.name} has no hours_of_operation target index. Days for rule not in hoo profile: #{days_for_rule_not_in_hoo_profile}") + OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Parametric.Schedules', "In #{__method__}, schedule #{schedule_day.name} has no hours_of_operation target index. Won't be modified") # OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Parametric.Schedules', "In #{__method__}, schedule #{schedule_day.name} has no hours_of_operation target index. Won't be modified") else # get hours of operation for this specific profile @@ -650,6 +650,9 @@ def self.gather_inputs_parametric_schedules(sch, load_inst, parametric_inputs, h # puts "#{schedule_day.name}: par_val_time_hash: #{par_val_time_hash}" raw_string = [] + # flags to control variable settings for tstats + start_set = false + end_set = false par_val_time_hash.sort.each do |time, value_array| # add in value variables # not currently using range, only using min max for constant schedules or schedules with just two values @@ -771,6 +774,15 @@ def self.gather_inputs_parametric_schedules(sch, load_inst, parametric_inputs, h end end + elsif hoo_var_method == 'tstat' + # puts formula_identifier + if min_key == 'start' && !start_set + time = 'hoo_start + 0' + start_set = true + else + time = 'hoo_end + 0' + end + end end From 72fbe20fd699a12e980842a871a09adf3b339297 Mon Sep 17 00:00:00 2001 From: Eric Ringold Date: Tue, 2 Apr 2024 15:10:47 -0600 Subject: [PATCH 09/22] add test for thermostat adjustment in simple comstock workflow --- .../schedules/test_schedules_parametric.rb | 162 ++++++++++++++++++ 1 file changed, 162 insertions(+) diff --git a/test/modules/schedules/test_schedules_parametric.rb b/test/modules/schedules/test_schedules_parametric.rb index b8455bed11..2df71bf86d 100644 --- a/test/modules/schedules/test_schedules_parametric.rb +++ b/test/modules/schedules/test_schedules_parametric.rb @@ -203,6 +203,168 @@ def test_schedule_ruleset_apply_parametric_inputs assert_equal(weekend_vals.rindex(1), 13) end + def create_simple_comstock_model_with_schedule_mod(type, wkdy_start, wkdy_dur, wknd_start, wknd_dur) + puts "-------------------------------------------------------------" + puts type + + template = 'ComStock DOE Ref 1980-2004' + climate_zone = 'ASHRAE 169-2013-5B' + + # create_bar + bar_args = { + climate_zone: climate_zone, + bldg_type_a: type, + bldg_subtype_a: 'largeoffice_default', + bldg_type_a_num_units: 1, + bldg_type_b: 'SecondarySchool', + bldg_subtype_b: 'NA', + bldg_type_b_fract_bldg_area: 0, + bldg_type_b_num_units: 1, + bldg_type_c: 'SecondarySchool', + bldg_subtype_c: 'NA', + bldg_type_c_fract_bldg_area: 0, + bldg_type_c_num_units: 1, + bldg_type_d: 'SecondarySchool', + bldg_subtype_d: 'NA', + bldg_type_d_fract_bldg_area: 0, + bldg_type_d_num_units: 1, + num_stories_below_grade: 0, + num_stories_above_grade: 1, + story_multiplier: 'None', + bar_division_method: 'Multiple Space Types - Individual Stories Sliced', + bottom_story_ground_exposed_floor: true, + top_story_exterior_exposed_roof: true, + make_mid_story_surfaces_adiabatic: true, + total_bldg_floor_area: 15000, + wwr: 0.38, + ns_to_ew_ratio: 3, + perim_mult: 0.0, + bar_width: 0.0, + bar_sep_dist_mult: 10.0, + building_rotation: 0.0, + template: template, + custom_height_bar: true, + floor_height: 0.0, + party_wall_fraction: 0.0, + party_wall_stories_north: 0, + party_wall_stories_south: 0, + party_wall_stories_east: 0, + party_wall_stories_west: 0, + double_loaded_corridor: 'Primary Space Type', + space_type_sort_logic: 'Building Type > Size', + single_floor_area: 0 + } + @model = OpenStudio::Model::Model.new + OpenstudioStandards::Geometry.create_bar_from_building_type_ratios(@model, bar_args) + + # simulation settings + dst_control = @model.getRunPeriodControlDaylightSavingTime + dst_control.setStartDate('2nd Sunday in March') + dst_control.setEndDate('1st Sunday in November') + + # set timestep + timestep = @model.getTimestep + timestep.setNumberOfTimestepsPerHour(4) + + # run period + run_period = @model.getRunPeriod + run_period.setBeginMonth(1) + run_period.setBeginDayOfMonth(1) + run_period.setEndMonth(12) + run_period.setEndDayOfMonth(31) + + # calendar year + yr_desc = @model.getYearDescription + yr_desc.setCalendarYear(2018) + + # weather file + weather_file_name = "USA_ID_Boise.Air.Terminal.726810_TMY3.epw" + weather_file_path = @weather.get_standards_weather_file_path(weather_file_name) + @weather.model_set_building_location(@model, weather_file_path: weather_file_path, ddy_list: nil) + + orig_dir = Dir.pwd + Dir.chdir(File.join((__dir__), '/output')) + + # create_typical with schedule modification, no hvac + OpenstudioStandards::CreateTypical.create_typical_building_from_model(@model, + template, + climate_zone: climate_zone, + # hvac_system_type: "VAV chiller with gas boiler reheat", + # hvac_delivery_type: "Inferred", + # heating_fuel: "Inferred", + # service_water_heating_fuel: "Inferred", + # cooling_fuel: "Inferred", + wkdy_op_hrs_start_time: wkdy_start, + wkdy_op_hrs_duration: wkdy_dur, + wknd_op_hrs_start_time: wknd_start, + wknd_op_hrs_duration: wknd_dur, + modify_wkdy_op_hrs: true, + modify_wknd_op_hrs: true, + add_hvac: false, + add_elevators: false + ) + + Dir.chdir(orig_dir) + end + + def test_comstock_schedule_mod + puts "\n######\nTEST:#{__method__}\n######\n" + + run_dir = "#{@test_dir}/schedules_modified" + + types = [] + types << 'SecondarySchool' + types << 'PrimarySchool' + types << 'SmallOffice' + types << 'MediumOffice' + types << 'LargeOffice' + types << 'SmallHotel' + types << 'LargeHotel' + types << 'Warehouse' + types << 'RetailStandalone' + types << 'RetailStripmall' + types << 'QuickServiceRestaurant' + types << 'FullServiceRestaurant' + types << 'Hospital' + types << 'Outpatient' + + types.each do |type| + + create_simple_comstock_model_with_schedule_mod(type, 8.0, 12.0, 10.0, 6.0) + + osm_path = "#{run_dir}/#{type}_modified.osm" + # assert(@model.save(osm_path, true)) + + # get thermostat schedules + tstat_schedules = [] + @model.getThermostatSetpointDualSetpoints.each do |tstat| + next if tstat.coolingSetpointTemperatureSchedule.empty? || tstat.heatingSetpointTemperatureSchedule.empty? + clg_sch = tstat.coolingSetpointTemperatureSchedule.get.to_ScheduleRuleset.get + htg_sch = tstat.heatingSetpointTemperatureSchedule.get.to_ScheduleRuleset.get + tstat_schedules << clg_sch unless tstat_schedules.include?(clg_sch) + tstat_schedules << htg_sch unless tstat_schedules.include?(clg_sch) + end + + day_schedules = [] + tstat_schedules.each do |sch_rule| + day_schedules << sch_rule.defaultDaySchedule + end + + # building hour of operation schedule + default_op_sch = @model.getBuilding.getDefaultSchedule(OpenStudio::Model::DefaultScheduleType.new('HoursofOperationSchedule')).get.to_ScheduleRuleset.get + op_times = default_op_sch.defaultDaySchedule.times.map(&:to_s) + + # only test that default days match + day_schedules.each do |day_sch| + times = day_sch.times.map(&:to_s) + assert(op_times.to_set.superset?(times.to_set), "For #{type}, expected #{op_times} to include times from thermostat schedule #{day_sch.name.get}: #{times}") + end + end + end + + + + end From 48cd9bb4085a2e185906c91fd07e1f920aaf4fe6 Mon Sep 17 00:00:00 2001 From: Eric Ringold Date: Tue, 2 Apr 2024 15:22:02 -0600 Subject: [PATCH 10/22] lil doc typo --- lib/openstudio-standards/schedules/information.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/openstudio-standards/schedules/information.rb b/lib/openstudio-standards/schedules/information.rb index 1526cdd8ed..6568963774 100644 --- a/lib/openstudio-standards/schedules/information.rb +++ b/lib/openstudio-standards/schedules/information.rb @@ -879,7 +879,7 @@ def self.schedule_ruleset_get_annual_days_used(schedule_ruleset) return days_used_hash end - # Returns the rule indeces associated with defaultDay and Rule days for a given ScheduleRuleset + # Returns the rule indices associated with defaultDay and Rule days for a given ScheduleRuleset # # @param schedule_ruleset [OpenStudio::Model::ScheduleRuleset] # @return [Hash] hash of ScheduleDay => rule index. Default day has rule index of -1 From af088120b81dbafc2721572e112d1198e44d7ff4 Mon Sep 17 00:00:00 2001 From: Eric Ringold Date: Tue, 2 Apr 2024 15:27:43 -0600 Subject: [PATCH 11/22] remove author tag --- lib/openstudio-standards/schedules/modify.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/openstudio-standards/schedules/modify.rb b/lib/openstudio-standards/schedules/modify.rb index 8df9e003eb..d973873c23 100644 --- a/lib/openstudio-standards/schedules/modify.rb +++ b/lib/openstudio-standards/schedules/modify.rb @@ -72,7 +72,6 @@ def self.schedule_day_set_hours_of_operation(schedule_day, start_time, end_time) # Sets the values of a day schedule from an array of values # Clears out existing time value pairs and sets to supplied values # - # @author Eric Ringold # @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] From e28716b1a696d5d6385f06176fd441c285f32d76 Mon Sep 17 00:00:00 2001 From: Matthew Dahlhausen Date: Thu, 4 Apr 2024 11:10:55 -0600 Subject: [PATCH 12/22] rubocop edits --- .../create_typical/create_typical.rb | 29 +++++++++---------- .../schedules/information.rb | 2 +- lib/openstudio-standards/schedules/modify.rb | 7 +++-- .../schedules/parametric.rb | 2 +- lib/openstudio-standards/space/space.rb | 18 +++++++----- .../schedules/test_schedules_parametric.rb | 13 --------- test/modules/space/test_space.rb | 2 +- 7 files changed, 30 insertions(+), 43 deletions(-) diff --git a/lib/openstudio-standards/create_typical/create_typical.rb b/lib/openstudio-standards/create_typical/create_typical.rb index c3a025d004..b818c7e57d 100644 --- a/lib/openstudio-standards/create_typical/create_typical.rb +++ b/lib/openstudio-standards/create_typical/create_typical.rb @@ -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| @@ -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. @@ -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. @@ -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| @@ -736,7 +734,6 @@ def self.create_typical_building_from_model(model, end end end - end # hours of operation diff --git a/lib/openstudio-standards/schedules/information.rb b/lib/openstudio-standards/schedules/information.rb index 6568963774..94c012966e 100644 --- a/lib/openstudio-standards/schedules/information.rb +++ b/lib/openstudio-standards/schedules/information.rb @@ -887,7 +887,7 @@ 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 } - schedule_day_hash + return schedule_day_hash end # @!endgroup Information:ScheduleRuleset diff --git a/lib/openstudio-standards/schedules/modify.rb b/lib/openstudio-standards/schedules/modify.rb index d973873c23..a27b84fb38 100644 --- a/lib/openstudio-standards/schedules/modify.rb +++ b/lib/openstudio-standards/schedules/modify.rb @@ -75,11 +75,12 @@ def self.schedule_day_set_hours_of_operation(schedule_day, start_time, end_time) # @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_vals(schedule_day, value_array) + def self.schedule_day_populate_from_array_of_values(schedule_day, value_array) schedule_day.clearValues value_array.each_with_index do |value, h| next if value == value_array[h + 1] - time = OpenStudio::Time.new(0, h+1, 0, 0) + + time = OpenStudio::Time.new(0, h + 1, 0, 0) schedule_day.addValue(time, value) end return schedule_day @@ -704,7 +705,7 @@ def self.schedule_ruleset_create_rules_from_day_list(schedule_ruleset, days_used 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 + 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 = [] diff --git a/lib/openstudio-standards/schedules/parametric.rb b/lib/openstudio-standards/schedules/parametric.rb index 6908844f90..6fe990d233 100644 --- a/lib/openstudio-standards/schedules/parametric.rb +++ b/lib/openstudio-standards/schedules/parametric.rb @@ -521,7 +521,7 @@ def self.gather_inputs_parametric_schedules(sch, load_inst, parametric_inputs, h next end # create new rules - new_rules = schedule_ruleset_create_rules_from_day_list(sch, day_group, schedule_day: schedule_days.key(sch_index)) + new_rules = OpenstudioStandards::Schedules.schedule_ruleset_create_rules_from_day_list(sch, day_group, schedule_day: schedule_days.key(sch_index)) new_rule_ct += new_rules.size end end diff --git a/lib/openstudio-standards/space/space.rb b/lib/openstudio-standards/space/space.rb index 74dab52f55..23fa9d1991 100644 --- a/lib/openstudio-standards/space/space.rb +++ b/lib/openstudio-standards/space/space.rb @@ -355,6 +355,7 @@ def self.spaces_get_occupancy_schedule(spaces, sch_name: nil, occupied_percentag space.spaceType.get.people.each do |people| num_ppl_sch = people.numberofPeopleSchedule next if num_ppl_sch.empty? + if num_ppl_sch.get.to_ScheduleRuleset.empty? # skip non-ruleset schedules OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.space', "People schedule #{num_ppl_sch.get.name} is not a Ruleset Schedule, it will not contribute to hours of operation") else @@ -369,6 +370,7 @@ def self.spaces_get_occupancy_schedule(spaces, sch_name: nil, occupied_percentag space.people.each do |people| num_ppl_sch = people.numberofPeopleSchedule next if num_ppl_sch.empty? + if num_ppl_sch.get.to_ScheduleRuleset.empty? # skip non-ruleset schedules OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.space', "People schedule #{num_ppl_sch.get.name} is not a Ruleset Schedule, it will not contribute to hours of operation") else @@ -413,9 +415,9 @@ def self.spaces_get_occupancy_schedule(spaces, sch_name: nil, occupied_percentag daily_max_vals = daily_combined_occ_fracs.map(&:max) daily_min_vals = daily_combined_occ_fracs.map(&:min) # normalize threshold to daily min/max values - daily_normalized_thresholds = daily_min_vals.zip(daily_max_vals).map { |min_max| min_max[0] + (min_max[1] - min_max[0]) * occupied_percentage_threshold} + daily_normalized_thresholds = daily_min_vals.zip(daily_max_vals).map { |min_max| min_max[0] + (min_max[1] - min_max[0]) * occupied_percentage_threshold } # if daily occ frac exceeds daily normalized threshold, set value to 1 - occ_status_vals = daily_combined_occ_fracs.each_with_index.map { |day_array, i| day_array.map{ |day_val| !day_val.zero? && day_val >= daily_normalized_thresholds[i] ? 1 : 0 } } + occ_status_vals = daily_combined_occ_fracs.each_with_index.map { |day_array, i| day_array.map { |day_val| !day_val.zero? && day_val >= daily_normalized_thresholds[i] ? 1 : 0 } } elsif threshold_calc_method == 'normalized_annual_range' # calculate annual min/max values annual_max = daily_combined_occ_fracs.max_by(&:max).max @@ -424,7 +426,7 @@ def self.spaces_get_occupancy_schedule(spaces, sch_name: nil, occupied_percentag annual_normalized_threshold = annual_min + (annual_max - annual_min) * occupied_percentage_threshold # if vals exceed threshold, set val to 1 occ_status_vals = daily_combined_occ_fracs.map { |day_array| day_array.map { |day_val| day_val >= annual_normalized_threshold ? 1 : 0 } } - else #threshold_calc_method == 'value' + else # threshold_calc_method == 'value' occ_status_vals = daily_combined_occ_fracs.map { |day_array| day_array.map { |day_val| day_val >= occupied_percentage_threshold ? 1 : 0 } } end @@ -463,16 +465,16 @@ def self.spaces_get_occupancy_schedule(spaces, sch_name: nil, occupied_percentag day_sch.addValue(OpenStudio::Time.new(0, 24, 0, 0), 1) # set most used profile to default day - most_used_profile = profile_days_hash.max_by { |k,v| v.size }.first + most_used_profile = profile_days_hash.max_by { |k, v| v.size }.first default_day = schedule_ruleset.defaultDaySchedule default_day.setName("#{sch_name} Default") - Schedules.schedule_day_populate_from_array_of_vals(default_day, most_used_profile) + OpenstudioStandards::Schedules.schedule_day_populate_from_array_of_values(default_day, most_used_profile) # create rules from remaining profiles remaining_profiles = profile_days_hash.slice(*profile_days_hash.keys.reject { |k| k == most_used_profile }) remaining_profiles.each do |profile, days_used| - rules = Schedules.schedule_ruleset_create_rules_from_day_list(schedule_ruleset, days_used) - rules.each{|rule| Schedules.schedule_day_populate_from_array_of_vals(rule.daySchedule, profile)} + rules = OpenstudioStandards::Schedules.schedule_ruleset_create_rules_from_day_list(schedule_ruleset, days_used) + rules.each { |rule| OpenstudioStandards::Schedules.schedule_day_populate_from_array_of_values(rule.daySchedule, profile) } end return schedule_ruleset @@ -501,7 +503,7 @@ def self.gather_inputs_parametric_load_inst_schedules(load_inst, parametric_inpu return nil end - Schedules.gather_inputs_parametric_schedules(opt_sch.get.to_ScheduleRuleset.get, load_inst, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: 'hours') + OpenstudioStandards::Schedules.gather_inputs_parametric_schedules(opt_sch.get.to_ScheduleRuleset.get, load_inst, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: 'hours') return parametric_inputs end diff --git a/test/modules/schedules/test_schedules_parametric.rb b/test/modules/schedules/test_schedules_parametric.rb index 2df71bf86d..2193fc5fe6 100644 --- a/test/modules/schedules/test_schedules_parametric.rb +++ b/test/modules/schedules/test_schedules_parametric.rb @@ -76,7 +76,6 @@ def test_model_infer_building_hours_of_operation assert_equal(hoo_default_vals.rindex(1.0), 17) assert_equal(hoo_default_vals.sum, 10) assert_equal(model.getBuilding.defaultScheduleSet.get.hoursofOperationSchedule.get, hours_of_operation_schedule) - end def test_gather_inputs_parametric_schedules @@ -147,8 +146,6 @@ def test_gather_inputs_parametric_schedules wknd_param_profile = clg_wknd_prop.select { |prop| prop.include? ('param_day_profile' ) }.first assert(wknd_param_profile.include?('hoo_start +'), "#{clg_sch.scheduleRules.first.daySchedule.name.get} missing hoo_start: #{clg_wknd_prop}") assert(wknd_param_profile.include?('hoo_start -'), "#{clg_sch.scheduleRules.first.daySchedule.name.get} missing hoo_end: #{clg_wknd_prop}") - - # props.each{|pr| p pr} end def test_schedule_ruleset_apply_parametric_inputs @@ -289,11 +286,6 @@ def create_simple_comstock_model_with_schedule_mod(type, wkdy_start, wkdy_dur, w OpenstudioStandards::CreateTypical.create_typical_building_from_model(@model, template, climate_zone: climate_zone, - # hvac_system_type: "VAV chiller with gas boiler reheat", - # hvac_delivery_type: "Inferred", - # heating_fuel: "Inferred", - # service_water_heating_fuel: "Inferred", - # cooling_fuel: "Inferred", wkdy_op_hrs_start_time: wkdy_start, wkdy_op_hrs_duration: wkdy_dur, wknd_op_hrs_start_time: wknd_start, @@ -329,7 +321,6 @@ def test_comstock_schedule_mod types << 'Outpatient' types.each do |type| - create_simple_comstock_model_with_schedule_mod(type, 8.0, 12.0, 10.0, 6.0) osm_path = "#{run_dir}/#{type}_modified.osm" @@ -361,10 +352,6 @@ def test_comstock_schedule_mod end end end - - - - end diff --git a/test/modules/space/test_space.rb b/test/modules/space/test_space.rb index 3e3f125fec..5a90ea360c 100644 --- a/test/modules/space/test_space.rb +++ b/test/modules/space/test_space.rb @@ -175,7 +175,7 @@ def test_spaces_get_occupancy_schedule assert_equal(summer_wkdy_hrly_vals.rindex(0.5375), 17) assert_equal(summer_wkdy_hrly_vals.rindex(0.1625), 19) - assert_in_delta(@sch.schedule_ruleset_get_equivalent_full_load_hours(occ_sch_fracs), 2290.15, 0.01) # before refactor: 2290.28 + assert_in_delta(@sch.schedule_ruleset_get_equivalent_full_load_hours(occ_sch_fracs), 2290.15, 0.01) # not normalized occ_sch_values = @space.spaces_get_occupancy_schedule([space1,space2], sch_name: 'test occupancy threshold', occupied_percentage_threshold: 0.3, threshold_calc_method: nil) From 19a6aea8ec86b629bd0a1057041c116c94f2f97b Mon Sep 17 00:00:00 2001 From: Matthew Dahlhausen Date: Thu, 4 Apr 2024 11:29:56 -0600 Subject: [PATCH 13/22] formatting edits --- .../schedules/information.rb | 4 +- .../schedules/parametric.rb | 149 ++++++++++-------- 2 files changed, 85 insertions(+), 68 deletions(-) diff --git a/lib/openstudio-standards/schedules/information.rb b/lib/openstudio-standards/schedules/information.rb index 94c012966e..66af109c1c 100644 --- a/lib/openstudio-standards/schedules/information.rb +++ b/lib/openstudio-standards/schedules/information.rb @@ -864,7 +864,7 @@ def self.schedule_ruleset_get_day_schedules(schedule_ruleset, include_design_day # Return the annual days of year that covered by each rule of a schedule ruleset # - # @param schedule_ruleset [OpenStudio::Model::ScheduleRuleset + # @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 @@ -881,7 +881,7 @@ def self.schedule_ruleset_get_annual_days_used(schedule_ruleset) # Returns the rule indices associated with defaultDay and Rule days for a given ScheduleRuleset # - # @param schedule_ruleset [OpenStudio::Model::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 = {} diff --git a/lib/openstudio-standards/schedules/parametric.rb b/lib/openstudio-standards/schedules/parametric.rb index 6fe990d233..08162857c7 100644 --- a/lib/openstudio-standards/schedules/parametric.rb +++ b/lib/openstudio-standards/schedules/parametric.rb @@ -180,7 +180,11 @@ def self.model_infer_hours_of_operation_building(model, fraction_of_daily_occ_ra # @param hoo_var_method [String] accepts 'hours' or 'fractional'. Any other value value will result in hour of operation variables not being applied # Options are 'hours', 'fractional' # @return [Hash] schedule is key, value is hash of number of objects - def self.model_setup_parametric_schedules(model, step_ramp_logic: nil, infer_hoo_for_non_assigned_objects: true, gather_data_only: false, hoo_var_method: 'hours') + def self.model_setup_parametric_schedules(model, + step_ramp_logic: nil, + infer_hoo_for_non_assigned_objects: true, + gather_data_only: false, + hoo_var_method: 'hours') parametric_inputs = {} default_sch_type = OpenStudio::Model::DefaultScheduleType.new('HoursofOperationSchedule') # thermal zones, air loops, plant loops will require some logic if they refer to more than one hours of operaiton schedule. @@ -190,27 +194,27 @@ def self.model_setup_parametric_schedules(model, step_ramp_logic: nil, infer_hoo # whatever approach is used for gathering parametric inputs for existing ruleset schedules should also be used for model_apply_parametric_schedules # loop through spaces (trace hours of operation back to space) - gather_inputs_parametric_space_space_type_schedules(model.getSpaces, parametric_inputs, gather_data_only) + OpenstudioStandards::Schedules.gather_inputs_parametric_space_space_type_schedules(model.getSpaces, parametric_inputs, gather_data_only) # loop through space types (trace hours of operation back to space type). - gather_inputs_parametric_space_space_type_schedules(model.getSpaceTypes, parametric_inputs, gather_data_only) + OpenstudioStandards::Schedules.gather_inputs_parametric_space_space_type_schedules(model.getSpaceTypes, parametric_inputs, gather_data_only) # loop through thermal zones (trace hours of operation back to spaces in thermal zone) thermal_zone_hash = {} # key is zone and hash is hours of operation model.getThermalZones.sort.each do |zone| # identify hours of operation - hours_of_operation = Space.spaces_hours_of_operation(zone.spaces) + hours_of_operation = OpenstudioStandards::Space.spaces_hours_of_operation(zone.spaces) thermal_zone_hash[zone] = hours_of_operation # get thermostat setpoint schedules if zone.thermostatSetpointDualSetpoint.is_initialized thermostat = zone.thermostatSetpointDualSetpoint.get if thermostat.heatingSetpointTemperatureSchedule.is_initialized && thermostat.heatingSetpointTemperatureSchedule.get.to_ScheduleRuleset.is_initialized schedule = thermostat.heatingSetpointTemperatureSchedule.get.to_ScheduleRuleset.get - gather_inputs_parametric_schedules(schedule, thermostat, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: 'tstat') + OpenstudioStandards::Schedules.gather_inputs_parametric_schedules(schedule, thermostat, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: 'tstat') end if thermostat.coolingSetpointTemperatureSchedule.is_initialized && thermostat.coolingSetpointTemperatureSchedule.get.to_ScheduleRuleset.is_initialized schedule = thermostat.coolingSetpointTemperatureSchedule.get.to_ScheduleRuleset.get - gather_inputs_parametric_schedules(schedule, thermostat, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: 'tstat') + OpenstudioStandards::Schedules.gather_inputs_parametric_schedules(schedule, thermostat, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: 'tstat') end end end @@ -223,11 +227,11 @@ def self.model_setup_parametric_schedules(model, step_ramp_logic: nil, infer_hoo air_loop.thermalZones.sort.each do |zone| air_loop_spaces += zone.spaces end - hours_of_operation = Space.spaces_hours_of_operation(air_loop_spaces) + hours_of_operation = OpenstudioStandards::Space.spaces_hours_of_operation(air_loop_spaces) air_loop_hash[air_loop] = hours_of_operation if air_loop.availabilitySchedule.to_ScheduleRuleset.is_initialized schedule = air_loop.availabilitySchedule.to_ScheduleRuleset.get - gather_inputs_parametric_schedules(schedule, air_loop, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: hoo_var_method) + OpenstudioStandards::Schedules.gather_inputs_parametric_schedules(schedule, air_loop, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: hoo_var_method) end avail_mgrs = air_loop.availabilityManagers avail_mgrs.sort.each do |avail_mgr| @@ -237,7 +241,7 @@ def self.model_setup_parametric_schedules(model, step_ramp_logic: nil, infer_hoo resources.sort.each do |resource| if resource.to_ScheduleRuleset.is_initialized schedule = resource.to_ScheduleRuleset.get - gather_inputs_parametric_schedules(schedule, avail_mgr, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: hoo_var_method) + OpenstudioStandards::Schedules.gather_inputs_parametric_schedules(schedule, avail_mgr, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: hoo_var_method) end end end @@ -295,7 +299,7 @@ def self.model_setup_parametric_schedules(model, step_ramp_logic: nil, infer_hoo OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Parametric.Model', "Cannot identify where #{component.name.get} is in system. Will not gather parametric inputs for #{schedule.name.get}") next end - gather_inputs_parametric_schedules(schedule, component, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: hoo_var_method) + OpenstudioStandards::Schedules.gather_inputs_parametric_schedules(schedule, component, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: hoo_var_method) end end @@ -312,12 +316,12 @@ def self.model_setup_parametric_schedules(model, step_ramp_logic: nil, infer_hoo opt_space = water_use_equipment.space if opt_space.is_initialized space = space.get - hours_of_operation = Space.space_hours_of_operation(space) - gather_inputs_parametric_schedules(schedule, water_use_equipment, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: hoo_var_method) + hours_of_operation = OpenstudioStandards::Space.space_hours_of_operation(space) + OpenstudioStandards::Schedules.gather_inputs_parametric_schedules(schedule, water_use_equipment, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: hoo_var_method) else - hours_of_operation = Space.spaces_hours_of_operation(model.getSpaces) + hours_of_operation = OpenstudioStandards::Space.spaces_hours_of_operation(model.getSpaces) if !hours_of_operation.nil? - gather_inputs_parametric_schedules(schedule, water_use_equipment, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: hoo_var_method) + OpenstudioStandards::Schedules.gather_inputs_parametric_schedules(schedule, water_use_equipment, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: hoo_var_method) end end @@ -336,12 +340,15 @@ def self.model_setup_parametric_schedules(model, step_ramp_logic: nil, infer_hoo # @note This measure will replace any prior chagnes made to ScheduleRule objects with new ScheduleRule values from # profile formulas # @author David Goldwasser - # @param model [OpenStudio::Model::Model] OpenStudio model object + # @param model [OpenStudio::Model::Model] OpenStudio Model object # @param ramp_frequency [Double] ramp frequency in minutes. If nil method will match simulation timestep # @param infer_hoo_for_non_assigned_objects [Boolean] # attempt to get hoo for objects like swh with and exterior lighting # @param error_on_out_of_order [Boolean] true will error if applying formula creates out of order values # @return [Array] of modified ScheduleRuleset objects - def self.model_apply_parametric_schedules(model, ramp_frequency: nil, infer_hoo_for_non_assigned_objects: true, error_on_out_of_order: true) + def self.model_apply_parametric_schedules(model, + ramp_frequency: nil, + infer_hoo_for_non_assigned_objects: true, + error_on_out_of_order: true) # get ramp frequency (fractional hour) from timestep if ramp_frequency.nil? steps_per_hour = if model.getSimulationControl.timestep.is_initialized @@ -353,7 +360,7 @@ def self.model_apply_parametric_schedules(model, ramp_frequency: nil, infer_hoo_ end # Go through model and create parametric formulas for all schedules - parametric_inputs = model_setup_parametric_schedules(model, gather_data_only: true) + parametric_inputs = OpenstudioStandards::Schedules.model_setup_parametric_schedules(model, gather_data_only: true) parametric_schedules = [] model.getScheduleRulesets.sort.each do |sch| @@ -367,7 +374,7 @@ def self.model_apply_parametric_schedules(model, ramp_frequency: nil, infer_hoo_ end # apply parametric inputs - schedule_ruleset_apply_parametric_inputs(sch, ramp_frequency, infer_hoo_for_non_assigned_objects, error_on_out_of_order, parametric_inputs) + OpenstudioStandards::Schedules.schedule_ruleset_apply_parametric_inputs(sch, ramp_frequency, infer_hoo_for_non_assigned_objects, error_on_out_of_order, parametric_inputs) # add schedule to array parametric_schedules << sch @@ -398,36 +405,36 @@ def self.gather_inputs_parametric_space_space_type_schedules(space_space_types, next end # loop through internal load instances - space_type.lights.each do |load_inst| - Space.gather_inputs_parametric_load_inst_schedules(load_inst, parametric_inputs, hours_of_operation, gather_data_only) + space_type.lights.each do |load_instance| + Space.gather_inputs_parametric_load_inst_schedules(load_instance, parametric_inputs, hours_of_operation, gather_data_only) end - space_type.luminaires.each do |load_inst| - Space.gather_inputs_parametric_load_inst_schedules(load_inst, parametric_inputs, hours_of_operation, gather_data_only) + space_type.luminaires.each do |load_instance| + Space.gather_inputs_parametric_load_inst_schedules(load_instance, parametric_inputs, hours_of_operation, gather_data_only) end - space_type.electricEquipment.each do |load_inst| - Space.gather_inputs_parametric_load_inst_schedules(load_inst, parametric_inputs, hours_of_operation, gather_data_only) + space_type.electricEquipment.each do |load_instance| + Space.gather_inputs_parametric_load_inst_schedules(load_instance, parametric_inputs, hours_of_operation, gather_data_only) end - space_type.gasEquipment.each do |load_inst| - Space.gather_inputs_parametric_load_inst_schedules(load_inst, parametric_inputs, hours_of_operation, gather_data_only) + space_type.gasEquipment.each do |load_instance| + Space.gather_inputs_parametric_load_inst_schedules(load_instance, parametric_inputs, hours_of_operation, gather_data_only) end - space_type.steamEquipment.each do |load_inst| - Space.gather_inputs_parametric_load_inst_schedules(load_inst, parametric_inputs, hours_of_operation, gather_data_only) + space_type.steamEquipment.each do |load_instance| + Space.gather_inputs_parametric_load_inst_schedules(load_instance, parametric_inputs, hours_of_operation, gather_data_only) end - space_type.otherEquipment.each do |load_inst| - Space.gather_inputs_parametric_load_inst_schedules(load_inst, parametric_inputs, hours_of_operation, gather_data_only) + space_type.otherEquipment.each do |load_instance| + Space.gather_inputs_parametric_load_inst_schedules(load_instance, parametric_inputs, hours_of_operation, gather_data_only) end - space_type.people.each do |load_inst| - Space.gather_inputs_parametric_load_inst_schedules(load_inst, parametric_inputs, hours_of_operation, gather_data_only) - if load_inst.activityLevelSchedule.is_initialized && load_inst.activityLevelSchedule.get.to_ScheduleRuleset.is_initialized - act_sch = load_inst.activityLevelSchedule.get.to_ScheduleRuleset.get - gather_inputs_parametric_schedules(act_sch, load_inst, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: 'hours') + space_type.people.each do |load_instance| + Space.gather_inputs_parametric_load_inst_schedules(load_instance, parametric_inputs, hours_of_operation, gather_data_only) + if load_instance.activityLevelSchedule.is_initialized && load_instance.activityLevelSchedule.get.to_ScheduleRuleset.is_initialized + act_sch = load_instance.activityLevelSchedule.get.to_ScheduleRuleset.get + OpenstudioStandards::Schedules.gather_inputs_parametric_schedules(act_sch, load_instance, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: 'hours') end end - space_type.spaceInfiltrationDesignFlowRates.each do |load_inst| - Space.gather_inputs_parametric_load_inst_schedules(load_inst, parametric_inputs, hours_of_operation, gather_data_only) + space_type.spaceInfiltrationDesignFlowRates.each do |load_instance| + Space.gather_inputs_parametric_load_inst_schedules(load_instance, parametric_inputs, hours_of_operation, gather_data_only) end - space_type.spaceInfiltrationEffectiveLeakageAreas.each do |load_inst| - Space.gather_inputs_parametric_load_inst_schedules(load_inst, parametric_inputs, hours_of_operation, gather_data_only) + space_type.spaceInfiltrationEffectiveLeakageAreas.each do |load_instance| + Space.gather_inputs_parametric_load_inst_schedules(load_instance, parametric_inputs, hours_of_operation, gather_data_only) end dsgn_spec_oa = space_type.designSpecificationOutdoorAir if dsgn_spec_oa.is_initialized @@ -445,8 +452,8 @@ def self.gather_inputs_parametric_space_space_type_schedules(space_space_types, # method to process load instance schedules for model_setup_parametric_schedules # # @author David Goldwasser - # @param sch [OpenStudio::Model::Schedule] - # @param load_inst [OpenStudio::Model::SpaceLoadInstance] + # @param schedule_ruleset [OpenStudio::Model::ScheduleRuleset] OpenStudio ScheduleRuleset object + # @param load_instance [OpenStudio::Model::SpaceLoadInstance] OpenStudio SpaceLoadInstance object # @param parametric_inputs [Hash] # @param hours_of_operation [Hash] hash, example: # { profile_index: { @@ -461,37 +468,41 @@ def self.gather_inputs_parametric_space_space_type_schedules(space_space_types, # @param gather_data_only [Boolean] # @param hoo_var_method [String] accepts hours and fractional. Any other value value will result in hoo variables not being applied # @return [Hash] - def self.gather_inputs_parametric_schedules(sch, load_inst, parametric_inputs, hours_of_operation, ramp: true, min_ramp_dur_hr: 2.0, gather_data_only: false, hoo_var_method: 'hours') - if parametric_inputs.key?(sch) - if hours_of_operation != parametric_inputs[sch][:hoo_inputs] # don't warn if the hours of operation between old and new schedule are equivalent - # OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Parametric.Schedule', "#{load_inst.name} uses #{sch.name} but parametric inputs have already been setup based on hours of operation for #{parametric_inputs[sch][:target].name}.") + def self.gather_inputs_parametric_schedules(schedule_ruleset, load_instance, parametric_inputs, hours_of_operation, + ramp: true, + min_ramp_dur_hr: 2.0, + gather_data_only: false, + hoo_var_method: 'hours') + if parametric_inputs.key?(schedule_ruleset) + if hours_of_operation != parametric_inputs[schedule_ruleset][:hoo_inputs] # don't warn if the hours of operation between old and new schedule are equivalent + # OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Parametric.Schedule', "#{load_instance.name} uses #{schedule_ruleset.name} but parametric inputs have already been setup based on hours of operation for #{parametric_inputs[schedule_ruleset][:target].name}.") return nil end end # gather and store data for scheduleRuleset - min_max = OpenstudioStandards::Schedules.schedule_ruleset_get_min_max(sch) - ruleset_hash = { floor: min_max['min'], ceiling: min_max['max'], target: load_inst, hoo_inputs: hours_of_operation } - parametric_inputs[sch] = ruleset_hash + min_max = OpenstudioStandards::Schedules.schedule_ruleset_get_min_max(schedule_ruleset) + ruleset_hash = { floor: min_max['min'], ceiling: min_max['max'], target: load_instance, hoo_inputs: hours_of_operation } + parametric_inputs[schedule_ruleset] = ruleset_hash # stop here if only gathering information otherwise will continue and generate additional parametric properties for schedules and rules if gather_data_only then return parametric_inputs end # set scheduleRuleset properties - props = sch.additionalProperties + props = schedule_ruleset.additionalProperties props.setFeature('param_sch_ver', '0.0.1') # this is needed to see if formulas are in sync with version of standards that processes them also used to flag schedule as parametric props.setFeature('param_sch_floor', min_max['min']) props.setFeature('param_sch_ceiling', min_max['max']) # cleanup existing profiles - OpenstudioStandards::Schedules.schedule_ruleset_cleanup_profiles(sch) + OpenstudioStandards::Schedules.schedule_ruleset_cleanup_profiles(schedule_ruleset) # get initial hash of schedule days => rule indices - schedule_days = schedule_ruleset_get_schedule_day_rule_indices(sch) + schedule_days = schedule_ruleset_get_schedule_day_rule_indices(schedule_ruleset) # get all day schedule equivalent full load hours to tag daily_flhs = schedule_days.keys.map { |day_sch| schedule_day_get_equivalent_full_load_hours(day_sch) } # collect initial rule index => array of days used hash - sch_ruleset_days_used = schedule_ruleset_get_annual_days_used(sch) + sch_ruleset_days_used = schedule_ruleset_get_annual_days_used(schedule_ruleset) # match up schedule rule days with hours of operation days sch_day_map = {} @@ -517,21 +528,21 @@ def self.gather_inputs_parametric_schedules(sch, load_inst, parametric_inputs, h next if sch_index == -1 && hoo_index == -1 # skip if rules already match if (sch_ruleset_days_used[sch_index] - day_group).empty? - OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.Parametric.Schedules', "in #{__method__}: #{sch.name} rule #{sch_index} already matches hours of operation rule #{hoo_index}; new rule won't be created.") + OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.Parametric.Schedules', "in #{__method__}: #{schedule_ruleset.name} rule #{sch_index} already matches hours of operation rule #{hoo_index}; new rule won't be created.") next end # create new rules - new_rules = OpenstudioStandards::Schedules.schedule_ruleset_create_rules_from_day_list(sch, day_group, schedule_day: schedule_days.key(sch_index)) + new_rules = OpenstudioStandards::Schedules.schedule_ruleset_create_rules_from_day_list(schedule_ruleset, day_group, schedule_day: schedule_days.key(sch_index)) new_rule_ct += new_rules.size end end # new rules are created at top of list - cleanup old rules - sch.scheduleRules[new_rule_ct..-1].each( &:remove ) unless new_rule_ct == 0 + schedule_ruleset.scheduleRules[new_rule_ct..-1].each( &:remove ) unless new_rule_ct == 0 # re-collect new schedule rules - schedule_days = schedule_ruleset_get_schedule_day_rule_indices(sch) + schedule_days = schedule_ruleset_get_schedule_day_rule_indices(schedule_ruleset) # re-collect new rule index => days used array - sch_ruleset_days_used = schedule_ruleset_get_annual_days_used(sch) + sch_ruleset_days_used = schedule_ruleset_get_annual_days_used(schedule_ruleset) # step through profiles and add additional properties to describe profiles schedule_days.each_with_index do |(schedule_day, current_rule_index), i| @@ -644,7 +655,7 @@ def self.gather_inputs_parametric_schedules(sch, load_inst, parametric_inputs, h if percent_change.abs > 0.05 # @todo this estimation can have flaws. Fix or remove it, make sure to update for secondary logic (if we implement that here) # post application checks compares against actual instead of estimated values - OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.Parametric.Schedule', "For day schedule #{schedule_day.name} in #{sch.name} there was a #{percent_change.round(4)}% change. Expected full load hours is #{daily_flh.round(4)}, but estimated value is #{est_daily_flh.round(4)}") + OpenStudio.logFree(OpenStudio::Debug, 'openstudio.standards.Parametric.Schedule', "For day schedule #{schedule_day.name} in #{schedule_ruleset.name} there was a #{percent_change.round(4)}% change. Expected full load hours is #{daily_flh.round(4)}, but estimated value is #{est_daily_flh.round(4)}") end # puts "#{schedule_day.name}: par_val_time_hash: #{par_val_time_hash}" @@ -847,7 +858,13 @@ def self.gather_inputs_parametric_schedules(sch, load_inst, parametric_inputs, h # @param sun_start_time [OpenStudio::Time] Sunday start time. If nil, no change will be made to this day. # @param sun_end_time [OpenStudio::Time] Sunday end time. If greater than 24:00, hours of operation will wrap over midnight. # @return [Boolean] returns true if successful, false if not - def self.schedule_ruleset_set_hours_of_operation(schedule_ruleset, wkdy_start_time: nil, wkdy_end_time: nil, sat_start_time: nil, sat_end_time: nil, sun_start_time: nil, sun_end_time: nil) + def self.schedule_ruleset_set_hours_of_operation(schedule_ruleset, + wkdy_start_time: nil, + wkdy_end_time: nil, + sat_start_time: nil, + sat_end_time: nil, + sun_start_time: nil, + sun_end_time: nil) # Default day is assumed to represent weekdays if wkdy_start_time && wkdy_end_time schedule_day_set_hours_of_operation(schedule_ruleset.defaultDaySchedule, wkdy_start_time, wkdy_end_time) @@ -880,16 +897,16 @@ def self.schedule_ruleset_set_hours_of_operation(schedule_ruleset, wkdy_start_ti # this will use parametric inputs contained in schedule and profiles along with inferred hours of operation to generate updated ruleset schedule profiles # # @author David Goldwasser - # @param schedule_ruleset [OpenStudio::Model::ScheduleRuleset] schedule ruleset object + # @param schedule_ruleset [OpenStudio::Model::ScheduleRuleset] OpenStudio ScheduleRuleset object # @param ramp_frequency [Double] ramp frequency in minutes # @param infer_hoo_for_non_assigned_objects [Boolean] attempt to get hoo for objects like swh with and exterior lighting # @param error_on_out_of_order [Boolean] true will error if applying formula creates out of order values - # @return [OpenStudio::Model::ScheduleRuleset] schedule ruleset object + # @return [OpenStudio::Model::ScheduleRuleset] OpenStudio ScheduleRuleset object def self.schedule_ruleset_apply_parametric_inputs(schedule_ruleset, ramp_frequency, infer_hoo_for_non_assigned_objects, error_on_out_of_order, parametric_inputs = nil) # Check if parametric inputs were supplied and generate them if not if parametric_inputs.nil? OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Parametric.ScheduleRuleset', "For #{schedule_ruleset.name}, no parametric inputs were not supplied so they will be generated now.") - parametric_inputs = model_setup_parametric_schedules(schedule.model, gather_data_only: true) + parametric_inputs = OpenstudioStandards::Schedules.model_setup_parametric_schedules(schedule.model, gather_data_only: true) end # Check that parametric inputs exist for this schedule after generation @@ -984,7 +1001,7 @@ def self.schedule_ruleset_apply_parametric_inputs(schedule_ruleset, ramp_frequen # puts hoo_end # update scheduleDay - schedule_day_adjust_from_parameters(sch_day, hoo_start, hoo_end, val_flr, val_clg, ramp_frequency, infer_hoo_for_non_assigned_objects, error_on_out_of_order) + OpenstudioStandards::Schedules.schedule_day_adjust_from_parameters(sch_day, hoo_start, hoo_end, val_flr, val_clg, ramp_frequency, infer_hoo_for_non_assigned_objects, error_on_out_of_order) # clone new rule if needed if clone_needed @@ -1046,7 +1063,7 @@ def self.schedule_ruleset_apply_parametric_inputs(schedule_ruleset, ramp_frequen hoo_end = hash[:hoo_end] # process new rule - schedule_day_adjust_from_parameters(sch_day_auto_gen, hoo_start, hoo_end, val_flr, val_clg, ramp_frequency, infer_hoo_for_non_assigned_objects, error_on_out_of_order) + OpenstudioStandards::Schedules.schedule_day_adjust_from_parameters(sch_day_auto_gen, hoo_start, hoo_end, val_flr, val_clg, ramp_frequency, infer_hoo_for_non_assigned_objects, error_on_out_of_order) end end @@ -1072,7 +1089,7 @@ def self.schedule_ruleset_apply_parametric_inputs(schedule_ruleset, ramp_frequen # adjust individual schedule profiles from parametric inputs # # @author David Goldwasser - # @param schedule_day [OpenStudio::Model::ScheduleDay] schedule day object + # @param schedule_day [OpenStudio::Model::ScheduleDay] OpenStudio ScheduleDay object # @param hoo_start [Double] hours of operation start # @param hoo_end [Double] hours of operation end # @param val_flr [Double] value floor @@ -1080,7 +1097,7 @@ def self.schedule_ruleset_apply_parametric_inputs(schedule_ruleset, ramp_frequen # @param ramp_frequency [Double] ramp frequency in minutes # @param infer_hoo_for_non_assigned_objects [Boolean] attempt to get hoo for objects like swh with and exterior lighting # @param error_on_out_of_order [Boolean] true will error if applying formula creates out of order values - # @return [OpenStudio::Model::ScheduleDay] schedule day + # @return [OpenStudio::Model::ScheduleDay] OpenStudio ScheduleDay object # @api private def self.schedule_day_adjust_from_parameters(schedule_day, hoo_start, hoo_end, val_flr, val_clg, ramp_frequency, infer_hoo_for_non_assigned_objects, error_on_out_of_order) # process hoo and floor/ceiling vars to develop formulas without variables From b9402e1da98499eba6ea52ef508b4dbedbd6c5e2 Mon Sep 17 00:00:00 2001 From: Eric Ringold Date: Thu, 4 Apr 2024 13:40:42 -0600 Subject: [PATCH 14/22] remove save navigation operator --- lib/openstudio-standards/schedules/parametric.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/openstudio-standards/schedules/parametric.rb b/lib/openstudio-standards/schedules/parametric.rb index 08162857c7..8a8c8f0adc 100644 --- a/lib/openstudio-standards/schedules/parametric.rb +++ b/lib/openstudio-standards/schedules/parametric.rb @@ -510,8 +510,13 @@ def self.gather_inputs_parametric_schedules(schedule_ruleset, load_instance, par day_map = {} sch_days.each do |day| # find the hour of operation rule that contains the day number - hoo_key = hours_of_operation.find { |_, val| val[:days_used].include?(day) }&.first - day_map[day] = hoo_key + hoo_keys = hours_of_operation.find { |_, val| val[:days_used].include?(day)} + unless hoo_keys.nil? + hoo_key = hoo_keys.first + day_map[day] = hoo_key + else + OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Parametric.Schedule', "In #{__method__}, cannot find schedule #{schedule_days.key(sch_index).name.get} day #{day} in hour of operation profiles. Something went wrong.") + end end # group days with the same hour of operation index grouped_days = Hash.new { |h, k| h[k] = [] } From 17d9f28bcdde732d576127251f19b731bbc2209d Mon Sep 17 00:00:00 2001 From: Eric Ringold Date: Thu, 4 Apr 2024 15:36:31 -0600 Subject: [PATCH 15/22] test_schedule_ruleset_get_annual_days_used --- .../schedules/test_schedules_information.rb | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/test/modules/schedules/test_schedules_information.rb b/test/modules/schedules/test_schedules_information.rb index 154f12080e..4a7c69d75e 100644 --- a/test/modules/schedules/test_schedules_information.rb +++ b/test/modules/schedules/test_schedules_information.rb @@ -1,4 +1,5 @@ require_relative '../../helpers/minitest_helper' +require 'date' class TestSchedulesInformation < Minitest::Test def setup @@ -412,4 +413,62 @@ def test_schedule_ruleset_get_timeseries assert(result.class.to_s == 'OpenStudio::TimeSeries') assert(result.values.length == (8760*2 - 365)) end + + def test_schedule_ruleset_get_annual_days_used + model = OpenStudio::Model::Model.new + model.getYearDescription.setCalendarYear(2018) # starts on a Monday + rules = [] + rules << ['SpringWeekends', '1/1-5/31', 'Sat/Sun', [10, 0], [18, 1], [24, 0]] + rules << ['SummmerWeekday', '6/1-8/31', 'Mon/Tue/Wed/Thu/Fri', [9, 0], [14, 1], [24, 0]] + rules << ['SummerWeekend', '6/1-8/31', 'Sat/Sun', [24, 0]] + rules << ['FallWeekends', '9/1-12/31', 'Sat/Sun', [10, 0], [18, 1], [24, 0]] + + test_options = { + 'name' => 'Test Complex', + 'winter_design_day' => [[24, 0]], + 'summer_design_day' => [[24, 1]], + 'default_day' => ['Test Complex Default', [8, 0], [16, 1], [24, 0]], + 'rules' => rules + } + + schedule_ruleset = @sch.create_complex_schedule(model, test_options) + annual_days_used = @sch.schedule_ruleset_get_annual_days_used(schedule_ruleset) + assert_equal(5, annual_days_used.keys.size) + + springfall_weekdays = [] + spring_weekends = [] + summer_weekdays = [] + summer_weekends = [] + fall_weekends = [] + + (Date.new(2018,1,1)..Date.new(2018,5,31)).each do |day| + if [0,6].include? day.wday + spring_weekends << day.yday + else + springfall_weekdays << day.yday + end + end + + (Date.new(2018,6,1)..Date.new(2018,8,31)).each do |day| + if [0,6].include? day.wday + summer_weekends << day.yday + else + summer_weekdays << day.yday + end + end + + (Date.new(2018,9,1)..Date.new(2018,12,31)).each do |day| + if [0,6].include? day.wday + fall_weekends << day.yday + else + springfall_weekdays << day.yday + end + end + + assert_equal(springfall_weekdays, annual_days_used[-1]) + assert_equal(spring_weekends, annual_days_used[3]) + assert_equal(summer_weekdays, annual_days_used[2]) + assert_equal(summer_weekends, annual_days_used[1]) + assert_equal(fall_weekends, annual_days_used[0]) + end end \ No newline at end of file From 8aefff15dccb495ede94cd430101c40c6b490d1b Mon Sep 17 00:00:00 2001 From: Eric Ringold Date: Thu, 4 Apr 2024 15:47:20 -0600 Subject: [PATCH 16/22] test_schedule_ruleset_get_schedule_day_rule_indices --- .../schedules/test_schedules_information.rb | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/modules/schedules/test_schedules_information.rb b/test/modules/schedules/test_schedules_information.rb index 4a7c69d75e..0236b95c12 100644 --- a/test/modules/schedules/test_schedules_information.rb +++ b/test/modules/schedules/test_schedules_information.rb @@ -471,4 +471,30 @@ def test_schedule_ruleset_get_annual_days_used assert_equal(summer_weekends, annual_days_used[1]) assert_equal(fall_weekends, annual_days_used[0]) end + + def test_schedule_ruleset_get_schedule_day_rule_indices + model = OpenStudio::Model::Model.new + model.getYearDescription.setCalendarYear(2018) # starts on a Monday + rules = [] + rules << ['SpringWeekends', '1/1-5/31', 'Sat/Sun', [10, 0], [18, 1], [24, 0]] + rules << ['SummmerWeekday', '6/1-8/31', 'Mon/Tue/Wed/Thu/Fri', [9, 0], [14, 1], [24, 0]] + rules << ['SummerWeekend', '6/1-8/31', 'Sat/Sun', [24, 0]] + rules << ['FallWeekends', '9/1-12/31', 'Sat/Sun', [10, 0], [18, 1], [24, 0]] + + test_options = { + 'name' => 'Test Complex', + 'winter_design_day' => [[24, 0]], + 'summer_design_day' => [[24, 1]], + 'default_day' => ['Test Complex Default', [8, 0], [16, 1], [24, 0]], + 'rules' => rules + } + schedule_ruleset = @sch.create_complex_schedule(model, test_options) + rule_index_hash = @sch.schedule_ruleset_get_schedule_day_rule_indices(schedule_ruleset) + + assert_equal(5, rule_index_hash.size) + rule_index_hash.keys.each { |k| assert(k.is_a?(OpenStudio::Model::ScheduleDay))} + assert_equal(-1, rule_index_hash[schedule_ruleset.defaultDaySchedule]) + assert_equal(2, rule_index_hash[model.getScheduleDayByName('Test Complex SummmerWeekday').get]) + end + end \ No newline at end of file From 796868be474024079003f5ed483fe20dd8761180 Mon Sep 17 00:00:00 2001 From: Eric Ringold Date: Thu, 4 Apr 2024 21:11:53 -0600 Subject: [PATCH 17/22] test_schedule_day_populate_from_array_of_values --- test/modules/schedules/test_schedules_modify.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/modules/schedules/test_schedules_modify.rb b/test/modules/schedules/test_schedules_modify.rb index 0934286485..316563f562 100644 --- a/test/modules/schedules/test_schedules_modify.rb +++ b/test/modules/schedules/test_schedules_modify.rb @@ -47,6 +47,23 @@ def test_schedule_day_set_hours_of_operation assert_equal(hourly_values.rindex(0.0), 9-1) end + def test_schedule_day_populate_from_array_of_values + model = OpenStudio::Model::Model.new + schedule_day = OpenStudio::Model::ScheduleDay.new(model) + value_array = [0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0] + schedule_day = @sch.schedule_day_populate_from_array_of_values(schedule_day, value_array) + assert(schedule_day.values.size == 3) + time_strings = schedule_day.times.map{|t| t.toString} + assert(time_strings == ['08:00:00', '18:00:00', '24:00:00']) + + value_array = [0,0,0,0,1,1,1] + schedule_day = @sch.schedule_day_populate_from_array_of_values(schedule_day, value_array) + assert(schedule_day.values.size == 3) + time_strings = schedule_day.times.map{|t| t.toString} + assert(time_strings == ['04:00:00', '07:00:00', '24:00:00']) + end + + def test_schedule_ruleset_add_rule model = OpenStudio::Model::Model.new test_options = { From 9a0ebdb5c695d3f7bdae64277c21b9895b95e39e Mon Sep 17 00:00:00 2001 From: Eric Ringold Date: Thu, 4 Apr 2024 21:12:22 -0600 Subject: [PATCH 18/22] schedule_day_populate_values_from_array_of_values enforce value_array size --- lib/openstudio-standards/schedules/modify.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/openstudio-standards/schedules/modify.rb b/lib/openstudio-standards/schedules/modify.rb index a27b84fb38..dd7aa45622 100644 --- a/lib/openstudio-standards/schedules/modify.rb +++ b/lib/openstudio-standards/schedules/modify.rb @@ -77,7 +77,11 @@ def self.schedule_day_set_hours_of_operation(schedule_day, start_time, end_time) # @return [OpenStudio::Model::ScheduleDay] def self.schedule_day_populate_from_array_of_values(schedule_day, value_array) schedule_day.clearValues - value_array.each_with_index do |value, h| + 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) From ff80d9601802bee31efbd2d9c50d2e494b9607e7 Mon Sep 17 00:00:00 2001 From: Eric Ringold Date: Fri, 5 Apr 2024 10:49:47 -0600 Subject: [PATCH 19/22] test_schedule_ruleset_create_rules_from_day_list --- .../schedules/test_schedules_modify.rb | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/test/modules/schedules/test_schedules_modify.rb b/test/modules/schedules/test_schedules_modify.rb index 316563f562..3767906a81 100644 --- a/test/modules/schedules/test_schedules_modify.rb +++ b/test/modules/schedules/test_schedules_modify.rb @@ -1,4 +1,5 @@ require_relative '../../helpers/minitest_helper' +require 'date' class TestSchedulesModify < Minitest::Test def setup @@ -184,4 +185,52 @@ def test_schedule_ruleset_cleanup_profiles assert(day_min_max['min'] == 0.1) assert(day_min_max['max'] == 0.8) end + + def test_schedule_ruleset_create_rules_from_day_list + model = OpenStudio::Model::Model.new + model.getYearDescription.setCalendarYear(2018) + test_options = { + 'name' => 'Simple Schedule', + 'winter_time_value_pairs' => { 24.0 => 0 }, + 'summer_time_value_pairs' => { 24.0 => 1 }, + 'default_time_value_pairs' => { 8.0 => 0, 16.0 => 1, 24.0 => 0 } + } + schedule_ruleset = @sch.create_simple_schedule(model, test_options) + + weekend_days = [] + summer_weekdays = [] + (Date.new(2018,1,1)..Date.new(2018,12,31)).each do |day| + if [0,6].include? day.wday + weekend_days << day.yday + end + end + (Date.new(2018,6,1)..Date.new(2018,8,31)).each do |day| + if ![0,6].include? day.wday + summer_weekdays << day.yday + end + end + + weekend_day = OpenStudio::Model::ScheduleDay.new(model) + weekend_day.addValue(OpenStudio::Time.new(0,12,0,0), 0) + weekend_day.addValue(OpenStudio::Time.new(0,18,0,0), 1) + + summer_weekday = OpenStudio::Model::ScheduleDay.new(model) + summer_weekday.addValue(OpenStudio::Time.new(0,11,0,0),0) + summer_weekday.addValue(OpenStudio::Time.new(0,15,0,0),1) + + weekend_rules = @sch.schedule_ruleset_create_rules_from_day_list(schedule_ruleset, weekend_days, schedule_day: weekend_day) + summer_rules = @sch.schedule_ruleset_create_rules_from_day_list(schedule_ruleset, summer_weekdays, schedule_day: summer_weekday) + + rules = schedule_ruleset.scheduleRules + assert_equal(2, rules.size) + assert(rules[0].applyWeekdays == true) + assert(rules[0].startDate.get.dayOfYear == OpenStudio::Date.new('2018/06/01').dayOfYear) + assert(rules[0].endDate.get.dayOfYear == OpenStudio::Date.new('2018/08/31').dayOfYear) + assert(rules[0].daySchedule.values == summer_weekday.values) + assert(rules[1].applyWeekends == true) + assert(rules[1].startDate.get.dayOfYear == OpenStudio::Date.new('2018/01/06').dayOfYear) + assert(rules[1].endDate.get.dayOfYear == OpenStudio::Date.new('2018/12/30').dayOfYear) + assert(rules[1].daySchedule.values == weekend_day.values) + end + end \ No newline at end of file From b4feb93bceac5e1bb2b043f429123ac2791978df Mon Sep 17 00:00:00 2001 From: Eric Ringold Date: Fri, 5 Apr 2024 11:29:10 -0600 Subject: [PATCH 20/22] reorder assert_equal statements --- test/modules/geometry/test_geometry_create.rb | 2 +- .../geometry/test_geometry_information.rb | 6 +- .../schedules/test_schedules_modify.rb | 10 +-- .../schedules/test_schedules_parametric.rb | 14 +-- test/modules/space/test_space.rb | 90 +++++++++---------- .../weather/test_weather_information.rb | 6 +- .../modules/weather/test_weather_stat_file.rb | 10 +-- 7 files changed, 69 insertions(+), 69 deletions(-) diff --git a/test/modules/geometry/test_geometry_create.rb b/test/modules/geometry/test_geometry_create.rb index 4849e3be00..1bdcfeeef9 100644 --- a/test/modules/geometry/test_geometry_create.rb +++ b/test/modules/geometry/test_geometry_create.rb @@ -14,7 +14,7 @@ def setup def test_space_create_point_at_center_of_floor space = @model.getSpaces[0] result = @geo.space_create_point_at_center_of_floor(space, 2.0) - assert_equal(result.z, 2.0) + assert_equal(2.0, result.z) end def test_sub_surface_create_point_at_specific_height diff --git a/test/modules/geometry/test_geometry_information.rb b/test/modules/geometry/test_geometry_information.rb index 208a8ce18f..52dc6291cd 100644 --- a/test/modules/geometry/test_geometry_information.rb +++ b/test/modules/geometry/test_geometry_information.rb @@ -22,10 +22,10 @@ def test_flat_map def test_aspect_ratio result = @geo.aspect_ratio(400.0, 80.0) - assert_equal(result, 1.0) + assert_equal(1.0, result) result = @geo.aspect_ratio(400.0, 160.0) - assert_equal(result.round(2), 13.93) + assert_equal(13.93, result.round(2)) end def test_wall_and_floor_intersection_length @@ -84,7 +84,7 @@ def test_surfaces_contain_point? point = OpenStudio::Point3d.new(-2.0, 4.0, 0.0) result = @geo.surfaces_contain_point?(surfaces, point) - assert_equal(result, false) + assert_equal(false, result) end # Information:SubSurface diff --git a/test/modules/schedules/test_schedules_modify.rb b/test/modules/schedules/test_schedules_modify.rb index 3767906a81..d68b507a6e 100644 --- a/test/modules/schedules/test_schedules_modify.rb +++ b/test/modules/schedules/test_schedules_modify.rb @@ -37,15 +37,15 @@ def test_schedule_day_set_hours_of_operation end_time = OpenStudio::Time.new(0,18,0,0) @sch.schedule_day_set_hours_of_operation(schedule_day, start_time, end_time) hourly_values = @sch.schedule_day_get_hourly_values(schedule_day) - assert_equal(hourly_values.index(1.0), 9) - assert_equal(hourly_values.rindex(1.0), 18-1) + assert_equal(9, hourly_values.index(1.0)) + assert_equal(18-1, hourly_values.rindex(1.0)) # test fromprevious day start_time = OpenStudio::Time.new(0,9,0,0) end_time = OpenStudio::Time.new(0,28,0,0) @sch.schedule_day_set_hours_of_operation(schedule_day, start_time, end_time) hourly_values = @sch.schedule_day_get_hourly_values(schedule_day) - assert_equal(hourly_values.index(0.0), 28-24) - assert_equal(hourly_values.rindex(0.0), 9-1) + assert_equal(28-24, hourly_values.index(0.0)) + assert_equal(9-1, hourly_values.rindex(0.0)) end def test_schedule_day_populate_from_array_of_values @@ -160,7 +160,7 @@ def test_schedule_ruleset_adjust_hours_of_operation basic_shift = { 'shift_hoo' => 2.0 } schedule = @sch.schedule_ruleset_adjust_hours_of_operation(schedule, basic_shift) hourly_values = @sch.schedule_day_get_hourly_values(schedule.defaultDaySchedule) - assert_equal(hourly_values.index(3.0), 8+2) + assert_equal(8+2, hourly_values.index(3.0)) end def test_schedule_ruleset_cleanup_profiles diff --git a/test/modules/schedules/test_schedules_parametric.rb b/test/modules/schedules/test_schedules_parametric.rb index 2193fc5fe6..842383226d 100644 --- a/test/modules/schedules/test_schedules_parametric.rb +++ b/test/modules/schedules/test_schedules_parametric.rb @@ -72,9 +72,9 @@ def test_model_infer_building_hours_of_operation hoo_default_vals = @sch.schedule_day_get_hourly_values(hours_of_operation_schedule.defaultDaySchedule) p hoo_default_vals - assert_equal(hoo_default_vals.index(1.0), 8) - assert_equal(hoo_default_vals.rindex(1.0), 17) - assert_equal(hoo_default_vals.sum, 10) + assert_equal(8, hoo_default_vals.index(1.0)) + assert_equal(17, hoo_default_vals.rindex(1.0)) + assert_equal(10, hoo_default_vals.sum) assert_equal(model.getBuilding.defaultScheduleSet.get.hoursofOperationSchedule.get, hours_of_operation_schedule) end @@ -193,11 +193,11 @@ def test_schedule_ruleset_apply_parametric_inputs ppl_sch = @sch.schedule_ruleset_apply_parametric_inputs(ppl_sch, ramp_frequency, infer_hoo_for_non_assigned_objects, error_on_out_of_order, parametric_inputs) default_vals = @sch.schedule_day_get_hourly_values(ppl_sch.defaultDaySchedule) - assert_equal(default_vals.index(0.8), 7) - assert_equal(default_vals.rindex(0.8), 19) + assert_equal(7, default_vals.index(0.8)) + assert_equal(19, default_vals.rindex(0.8)) weekend_vals = @sch.schedule_day_get_hourly_values(ppl_sch.scheduleRules.first.daySchedule) - assert_equal(weekend_vals.index(1), 10) - assert_equal(weekend_vals.rindex(1), 13) + assert_equal(10, weekend_vals.index(1)) + assert_equal(13, weekend_vals.rindex(1)) end def create_simple_comstock_model_with_schedule_mod(type, wkdy_start, wkdy_dur, wknd_start, wknd_dur) diff --git a/test/modules/space/test_space.rb b/test/modules/space/test_space.rb index 5a90ea360c..a69b5dca37 100644 --- a/test/modules/space/test_space.rb +++ b/test/modules/space/test_space.rb @@ -11,13 +11,13 @@ def test_space_plenum? space = OpenStudio::Model::Space.new(model) space.setName('some space') space.setPartofTotalFloorArea(false) - assert_equal(@space.space_plenum?(space), true) + assert_equal(true, @space.space_plenum?(space)) space.setPartofTotalFloorArea(true) space_type = OpenStudio::Model::SpaceType.new(model) space_type.setName('Plenum') space.setSpaceType(space_type) - assert_equal(@space.space_plenum?(space), true) + assert_equal(true, @space.space_plenum?(space)) end def test_space_residential? @@ -35,13 +35,13 @@ def test_space_residential? space_type = OpenStudio::Model::SpaceType.new(model) space_type.setName('MidriseApartment Apartment') space1.setSpaceType(space_type) - assert_equal(@space.space_residential?(space1), true) + assert_equal(true, @space.space_residential?(space1)) apt_ofc = OpenStudio::Model::SpaceType.new(model) apt_ofc.setName('MidriseApartment Office') ofc = OpenStudio::Model::Space.new(model) ofc.setSpaceType(apt_ofc) - assert_equal(@space.space_residential?(ofc), false) + assert_equal(false, @space.space_residential?(ofc)) # plenum from below space polygon = OpenStudio::Point3dVector.new @@ -53,7 +53,7 @@ def test_space_residential? space2 = OpenStudio::Model::Space.fromFloorPrint(polygon, 1.0, model).get space2.setPartofTotalFloorArea(false) space1.matchSurfaces(space2) - assert_equal(@space.space_residential?(space2), true) + assert_equal(true, @space.space_residential?(space2)) end def test_space_hours_of_operation @@ -79,14 +79,14 @@ def test_space_hours_of_operation space.setDefaultScheduleSet(default_schedule_set) hoo_hash = @space.space_hours_of_operation(space) # puts hoo_hash - assert_equal(hoo_hash[-1][:hoo_start], 8) - assert_equal(hoo_hash[-1][:hoo_end], 20) - assert_equal(hoo_hash[-1][:hoo_hours], 20-8) - assert_equal(hoo_hash[-1][:days_used].size, 261) - assert_equal(hoo_hash[0][:hoo_start], 10) - assert_equal(hoo_hash[0][:hoo_end], 14) - assert_equal(hoo_hash[0][:hoo_hours], 14-10) - assert_equal(hoo_hash[0][:days_used].size, 104) + assert_equal(8, hoo_hash[-1][:hoo_start]) + assert_equal(20, hoo_hash[-1][:hoo_end]) + assert_equal(20-8, hoo_hash[-1][:hoo_hours]) + assert_equal(261, hoo_hash[-1][:days_used].size) + assert_equal(10, hoo_hash[0][:hoo_start]) + assert_equal(14, hoo_hash[0][:hoo_end]) + assert_equal(14-10, hoo_hash[0][:hoo_hours]) + assert_equal(104, hoo_hash[0][:days_used].size) end def test_spaces_get_occupancy_schedule @@ -153,27 +153,27 @@ def test_spaces_get_occupancy_schedule spring_wkdy = occ_sch_fracs.getDaySchedules(OpenStudio::Date.new('2018-Apr-10'),OpenStudio::Date.new('2018-Apr-10')).first spring_wkdy_hrly_vals = @sch.schedule_day_get_hourly_values(spring_wkdy) - assert_equal(spring_wkdy_hrly_vals.index(0.25), 6) - assert_equal(spring_wkdy_hrly_vals.index(0.5), 8) - assert_equal(spring_wkdy_hrly_vals.rindex(0.5), 17) - assert_equal(spring_wkdy_hrly_vals.index(0.75), 12) - assert_equal(spring_wkdy_hrly_vals.rindex(0.75), 15) - assert_equal(spring_wkdy_hrly_vals.rindex(0.125), 21) + assert_equal(6, spring_wkdy_hrly_vals.index(0.25)) + assert_equal(8, spring_wkdy_hrly_vals.index(0.5)) + assert_equal(17, spring_wkdy_hrly_vals.rindex(0.5)) + assert_equal(12, spring_wkdy_hrly_vals.index(0.75)) + assert_equal(15, spring_wkdy_hrly_vals.rindex(0.75)) + assert_equal(21, spring_wkdy_hrly_vals.rindex(0.125)) spring_wknd = occ_sch_fracs.getDaySchedules(OpenStudio::Date.new('2018-Apr-14'),OpenStudio::Date.new('2018-Apr-14')).first spring_wknd_hrly_vals = @sch.schedule_day_get_hourly_values(spring_wknd) - assert_equal(spring_wknd_hrly_vals.index(0.25), 8) - assert_equal(spring_wknd_hrly_vals.rindex(0.25), 15) + assert_equal(8, spring_wknd_hrly_vals.index(0.25)) + assert_equal(15, spring_wknd_hrly_vals.rindex(0.25)) summer_wkdy = occ_sch_fracs.getDaySchedules(OpenStudio::Date.new('2018-Jul-23'),OpenStudio::Date.new('2018-Jul-23')).first summer_wkdy_hrly_vals = @sch.schedule_day_get_hourly_values(summer_wkdy) - assert_equal(summer_wkdy_hrly_vals.index(0.25), 6) - assert_equal(summer_wkdy_hrly_vals.index(0.5), 8) - assert_equal(summer_wkdy_hrly_vals.rindex(0.625), 11) - assert_equal(summer_wkdy_hrly_vals.index(0.875), 12) - assert_equal(summer_wkdy_hrly_vals.rindex(0.875), 15) - assert_equal(summer_wkdy_hrly_vals.rindex(0.5375), 17) - assert_equal(summer_wkdy_hrly_vals.rindex(0.1625), 19) + assert_equal(6, summer_wkdy_hrly_vals.index(0.25)) + assert_equal(8, summer_wkdy_hrly_vals.index(0.5)) + assert_equal(11, summer_wkdy_hrly_vals.rindex(0.625)) + assert_equal(12, summer_wkdy_hrly_vals.index(0.875)) + assert_equal(15, summer_wkdy_hrly_vals.rindex(0.875)) + assert_equal(17, summer_wkdy_hrly_vals.rindex(0.5375)) + assert_equal(19, summer_wkdy_hrly_vals.rindex(0.1625)) assert_in_delta(@sch.schedule_ruleset_get_equivalent_full_load_hours(occ_sch_fracs), 2290.15, 0.01) @@ -183,8 +183,8 @@ def test_spaces_get_occupancy_schedule spring_wkdy = occ_sch_values.getDaySchedules(OpenStudio::Date.new('2018-Apr-10'),OpenStudio::Date.new('2018-Apr-10')).first spring_wkdy_hrly_vals = @sch.schedule_day_get_hourly_values(spring_wkdy) - assert_equal(spring_wkdy_hrly_vals.index(1.0), 8) - assert_equal(spring_wkdy_hrly_vals.rindex(1.0), 17) + assert_equal(8, spring_wkdy_hrly_vals.index(1.0)) + assert_equal(17, spring_wkdy_hrly_vals.rindex(1.0)) spring_wknd = occ_sch_values.getDaySchedules(OpenStudio::Date.new('2018-Apr-14'),OpenStudio::Date.new('2018-Apr-14')).first spring_wknd_hrly_vals = @sch.schedule_day_get_hourly_values(spring_wknd) @@ -192,10 +192,10 @@ def test_spaces_get_occupancy_schedule summer_wkdy = occ_sch_values.getDaySchedules(OpenStudio::Date.new('2018-Jul-23'),OpenStudio::Date.new('2018-Jul-23')).first summer_wkdy_hrly_vals = @sch.schedule_day_get_hourly_values(summer_wkdy) - assert_equal(summer_wkdy_hrly_vals.index(1.0), 8) - assert_equal(summer_wkdy_hrly_vals.rindex(1.0), 17) + assert_equal(8, summer_wkdy_hrly_vals.index(1.0)) + assert_equal(17, summer_wkdy_hrly_vals.rindex(1.0)) - assert_equal(@sch.schedule_ruleset_get_equivalent_full_load_hours(occ_sch_values), 2610) + assert_equal(2610, @sch.schedule_ruleset_get_equivalent_full_load_hours(occ_sch_values)) # normalized daily occ_sch_daily = @space.spaces_get_occupancy_schedule([space1,space2], sch_name: 'test occupancy daily', occupied_percentage_threshold: 0.3, threshold_calc_method: 'normalized_daily_range') @@ -203,20 +203,20 @@ def test_spaces_get_occupancy_schedule spring_wkdy = occ_sch_daily.getDaySchedules(OpenStudio::Date.new('2018-Apr-10'),OpenStudio::Date.new('2018-Apr-10')).first spring_wkdy_hrly_vals = @sch.schedule_day_get_hourly_values(spring_wkdy) - assert_equal(spring_wkdy_hrly_vals.index(1.0), 6) - assert_equal(spring_wkdy_hrly_vals.rindex(1.0), 17) + assert_equal(6, spring_wkdy_hrly_vals.index(1.0)) + assert_equal(17, spring_wkdy_hrly_vals.rindex(1.0)) spring_wknd = occ_sch_daily.getDaySchedules(OpenStudio::Date.new('2018-Apr-14'),OpenStudio::Date.new('2018-Apr-14')).first spring_wknd_hrly_vals = @sch.schedule_day_get_hourly_values(spring_wknd) - assert_equal(spring_wknd_hrly_vals.index(1.0), 8) - assert_equal(spring_wknd_hrly_vals.rindex(1.0), 15) + assert_equal(8, spring_wknd_hrly_vals.index(1.0)) + assert_equal(15, spring_wknd_hrly_vals.rindex(1.0)) summer_wkdy = occ_sch_daily.getDaySchedules(OpenStudio::Date.new('2018-Jul-23'),OpenStudio::Date.new('2018-Jul-23')).first summer_wkdy_hrly_vals = @sch.schedule_day_get_hourly_values(summer_wkdy) - assert_equal(summer_wkdy_hrly_vals.index(1.0), 8) - assert_equal(summer_wkdy_hrly_vals.rindex(1.0), 17) + assert_equal(8, summer_wkdy_hrly_vals.index(1.0)) + assert_equal(17, summer_wkdy_hrly_vals.rindex(1.0)) - assert_equal(@sch.schedule_ruleset_get_equivalent_full_load_hours(occ_sch_daily), 3832) + assert_equal(3832, @sch.schedule_ruleset_get_equivalent_full_load_hours(occ_sch_daily)) # normalized annually occ_sch_annual = @space.spaces_get_occupancy_schedule([space1,space2], sch_name: 'test occupancy annual', occupied_percentage_threshold: 0.3, threshold_calc_method: 'normalized_annual_range') @@ -224,8 +224,8 @@ def test_spaces_get_occupancy_schedule spring_wkdy = occ_sch_annual.getDaySchedules(OpenStudio::Date.new('2018-Apr-10'),OpenStudio::Date.new('2018-Apr-10')).first spring_wkdy_hrly_vals = @sch.schedule_day_get_hourly_values(spring_wkdy) - assert_equal(spring_wkdy_hrly_vals.index(1.0), 8) - assert_equal(spring_wkdy_hrly_vals.rindex(1.0), 17) + assert_equal(8, spring_wkdy_hrly_vals.index(1.0)) + assert_equal(17, spring_wkdy_hrly_vals.rindex(1.0)) spring_wknd = occ_sch_annual.getDaySchedules(OpenStudio::Date.new('2018-Apr-14'),OpenStudio::Date.new('2018-Apr-14')).first spring_wknd_hrly_vals = @sch.schedule_day_get_hourly_values(spring_wknd) @@ -233,9 +233,9 @@ def test_spaces_get_occupancy_schedule summer_wkdy = occ_sch_annual.getDaySchedules(OpenStudio::Date.new('2018-Jul-23'),OpenStudio::Date.new('2018-Jul-23')).first summer_wkdy_hrly_vals = @sch.schedule_day_get_hourly_values(summer_wkdy) - assert_equal(summer_wkdy_hrly_vals.index(1.0), 8) - assert_equal(summer_wkdy_hrly_vals.rindex(1.0), 17) + assert_equal(8, summer_wkdy_hrly_vals.index(1.0)) + assert_equal(17, summer_wkdy_hrly_vals.rindex(1.0)) - assert_equal(@sch.schedule_ruleset_get_equivalent_full_load_hours(occ_sch_annual), 2610) + assert_equal(2610, @sch.schedule_ruleset_get_equivalent_full_load_hours(occ_sch_annual)) end end diff --git a/test/modules/weather/test_weather_information.rb b/test/modules/weather/test_weather_information.rb index 93d0357526..7e3b8a93b1 100644 --- a/test/modules/weather/test_weather_information.rb +++ b/test/modules/weather/test_weather_information.rb @@ -25,19 +25,19 @@ def test_model_get_climate_zone # test ASHRAE climate zone @weather.model_set_climate_zone(model, 'ASHRAE 169-2013-4A') result = @weather.model_get_climate_zone(model) - assert_equal(result, 'ASHRAE 169-2013-4A') + assert_equal('ASHRAE 169-2013-4A', result) # test CEC climate zone @weather.model_set_climate_zone(model, 'CEC T24-CEC3') result = @weather.model_get_climate_zone(model) - assert_equal(result, 'CEC T24-CEC3') + assert_equal('CEC T24-CEC3', result) end def test_model_get_ashrae_climate_zone_number model = OpenStudio::Model::Model.new @weather.model_set_climate_zone(model, 'ASHRAE 169-2013-4A') result = @weather.model_get_ashrae_climate_zone_number(model) - assert_equal(result, 4) + assert_equal(4, result) end def test_model_get_full_weather_file_path diff --git a/test/modules/weather/test_weather_stat_file.rb b/test/modules/weather/test_weather_stat_file.rb index 0b9f49962b..5869acb975 100644 --- a/test/modules/weather/test_weather_stat_file.rb +++ b/test/modules/weather/test_weather_stat_file.rb @@ -28,11 +28,11 @@ def test_stat_file # pp JSON.parse(json) assert_in_delta(stat_file.lat, 47.46, 0.01) assert_in_delta(stat_file.lon, -122.31, 0.01) - assert_equal(stat_file.gmt, -8.0) - assert_equal(stat_file.heating_design_info.size, 15) - assert_equal(stat_file.cooling_design_info.size, 32) - assert_equal(stat_file.extremes_design_info.size, 16) - assert_equal(stat_file.monthly_dry_bulb.size, 12) + assert_equal(-8.0, stat_file.gmt) + assert_equal(15, stat_file.heating_design_info.size) + assert_equal(32, stat_file.cooling_design_info.size) + assert_equal(16, stat_file.extremes_design_info.size) + assert_equal(12, stat_file.monthly_dry_bulb.size) end end end From 2a9e9ffeced688c27055ec0677f218361cce5607 Mon Sep 17 00:00:00 2001 From: Eric Ringold Date: Fri, 5 Apr 2024 11:54:06 -0600 Subject: [PATCH 21/22] add parameter descriptions --- lib/openstudio-standards/schedules/parametric.rb | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/openstudio-standards/schedules/parametric.rb b/lib/openstudio-standards/schedules/parametric.rb index 8a8c8f0adc..927d642cd0 100644 --- a/lib/openstudio-standards/schedules/parametric.rb +++ b/lib/openstudio-standards/schedules/parametric.rb @@ -391,9 +391,10 @@ def self.model_apply_parametric_schedules(model, # # @author David Goldwasser # @param space_space_types [Array] array of OpenStudio::Model::Space or OpenStudio::Model::SpaceType objects - # @param parametric_inputs [Hash] - # @param gather_data_only [Boolean] - # @return [Hash] + # @param parametric_inputs [Hash] parametric inputs hash of ScheduleRuleset => {floor: schedule floor, ceiling: schedule ceiling, target: load instance, hoo_inputs: hours_of_operation hash} + # @param gather_data_only [Boolean] if true, no changes will be made to schedules + # @return [Hash] parametric inputs hash of ScheduleRuleset => {floor: schedule floor, ceiling: schedule ceiling, target: load instance, hoo_inputs: hours_of_operation hash}. + # parametric formulas are encoded in AdditionalProperties objects attached to the ScheduleRuleset def self.gather_inputs_parametric_space_space_type_schedules(space_space_types, parametric_inputs, gather_data_only) space_space_types.each do |space_type| # get hours of operation for space type once @@ -463,11 +464,11 @@ def self.gather_inputs_parametric_space_space_type_schedules(space_space_types, # days_used: [Array] annual day indices # } # } - # @param ramp [Boolean] - # @param min_ramp_dur_hr [Double] - # @param gather_data_only [Boolean] + # @param ramp [Boolean] flag to add intermediate values ramp between input schedule values + # @param min_ramp_dur_hr [Double] minimum time difference to ramp between + # @param gather_data_only [Boolean] if true, no changes are made to schedules # @param hoo_var_method [String] accepts hours and fractional. Any other value value will result in hoo variables not being applied - # @return [Hash] + # @return [Hash] parametric inputs hash of ScheduleRuleset => {floor: schedule floor, ceiling: schedule ceiling, target: load instance, hoo_inputs: hours_of_operation hash} def self.gather_inputs_parametric_schedules(schedule_ruleset, load_instance, parametric_inputs, hours_of_operation, ramp: true, min_ramp_dur_hr: 2.0, From a2184de36becdcbbd8d6402f3eb750671cd8c3b0 Mon Sep 17 00:00:00 2001 From: Matthew Dahlhausen Date: Fri, 5 Apr 2024 12:51:08 -0600 Subject: [PATCH 22/22] parametric schedule method name changes - rename load_inst varaibles to space_load_instance - rename gather_inputs_parametric_load_inst_schedules to space_load_instance_get_parametric_schedule_inputs - rename gather_inputs_parametric_space_space_type_schedules to spaces_space_types_get_parametric_schedule_inputs - rename gather_inputs_parametric_schedules to schedule_ruleset_get_parametric_inputs --- .../schedules/parametric.rb | 94 +++++++++---------- lib/openstudio-standards/space/space.rb | 17 ++-- .../schedules/test_schedules_parametric.rb | 6 +- 3 files changed, 59 insertions(+), 58 deletions(-) diff --git a/lib/openstudio-standards/schedules/parametric.rb b/lib/openstudio-standards/schedules/parametric.rb index 927d642cd0..65202d78fe 100644 --- a/lib/openstudio-standards/schedules/parametric.rb +++ b/lib/openstudio-standards/schedules/parametric.rb @@ -194,10 +194,10 @@ def self.model_setup_parametric_schedules(model, # whatever approach is used for gathering parametric inputs for existing ruleset schedules should also be used for model_apply_parametric_schedules # loop through spaces (trace hours of operation back to space) - OpenstudioStandards::Schedules.gather_inputs_parametric_space_space_type_schedules(model.getSpaces, parametric_inputs, gather_data_only) + OpenstudioStandards::Schedules.spaces_space_types_get_parametric_schedule_inputs(model.getSpaces, parametric_inputs, gather_data_only) # loop through space types (trace hours of operation back to space type). - OpenstudioStandards::Schedules.gather_inputs_parametric_space_space_type_schedules(model.getSpaceTypes, parametric_inputs, gather_data_only) + OpenstudioStandards::Schedules.spaces_space_types_get_parametric_schedule_inputs(model.getSpaceTypes, parametric_inputs, gather_data_only) # loop through thermal zones (trace hours of operation back to spaces in thermal zone) thermal_zone_hash = {} # key is zone and hash is hours of operation @@ -210,11 +210,11 @@ def self.model_setup_parametric_schedules(model, thermostat = zone.thermostatSetpointDualSetpoint.get if thermostat.heatingSetpointTemperatureSchedule.is_initialized && thermostat.heatingSetpointTemperatureSchedule.get.to_ScheduleRuleset.is_initialized schedule = thermostat.heatingSetpointTemperatureSchedule.get.to_ScheduleRuleset.get - OpenstudioStandards::Schedules.gather_inputs_parametric_schedules(schedule, thermostat, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: 'tstat') + OpenstudioStandards::Schedules.schedule_ruleset_get_parametric_inputs(schedule, thermostat, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: 'tstat') end if thermostat.coolingSetpointTemperatureSchedule.is_initialized && thermostat.coolingSetpointTemperatureSchedule.get.to_ScheduleRuleset.is_initialized schedule = thermostat.coolingSetpointTemperatureSchedule.get.to_ScheduleRuleset.get - OpenstudioStandards::Schedules.gather_inputs_parametric_schedules(schedule, thermostat, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: 'tstat') + OpenstudioStandards::Schedules.schedule_ruleset_get_parametric_inputs(schedule, thermostat, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: 'tstat') end end end @@ -231,7 +231,7 @@ def self.model_setup_parametric_schedules(model, air_loop_hash[air_loop] = hours_of_operation if air_loop.availabilitySchedule.to_ScheduleRuleset.is_initialized schedule = air_loop.availabilitySchedule.to_ScheduleRuleset.get - OpenstudioStandards::Schedules.gather_inputs_parametric_schedules(schedule, air_loop, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: hoo_var_method) + OpenstudioStandards::Schedules.schedule_ruleset_get_parametric_inputs(schedule, air_loop, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: hoo_var_method) end avail_mgrs = air_loop.availabilityManagers avail_mgrs.sort.each do |avail_mgr| @@ -241,7 +241,7 @@ def self.model_setup_parametric_schedules(model, resources.sort.each do |resource| if resource.to_ScheduleRuleset.is_initialized schedule = resource.to_ScheduleRuleset.get - OpenstudioStandards::Schedules.gather_inputs_parametric_schedules(schedule, avail_mgr, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: hoo_var_method) + OpenstudioStandards::Schedules.schedule_ruleset_get_parametric_inputs(schedule, avail_mgr, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: hoo_var_method) end end end @@ -299,7 +299,7 @@ def self.model_setup_parametric_schedules(model, OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Parametric.Model', "Cannot identify where #{component.name.get} is in system. Will not gather parametric inputs for #{schedule.name.get}") next end - OpenstudioStandards::Schedules.gather_inputs_parametric_schedules(schedule, component, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: hoo_var_method) + OpenstudioStandards::Schedules.schedule_ruleset_get_parametric_inputs(schedule, component, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: hoo_var_method) end end @@ -317,11 +317,11 @@ def self.model_setup_parametric_schedules(model, if opt_space.is_initialized space = space.get hours_of_operation = OpenstudioStandards::Space.space_hours_of_operation(space) - OpenstudioStandards::Schedules.gather_inputs_parametric_schedules(schedule, water_use_equipment, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: hoo_var_method) + OpenstudioStandards::Schedules.schedule_ruleset_get_parametric_inputs(schedule, water_use_equipment, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: hoo_var_method) else hours_of_operation = OpenstudioStandards::Space.spaces_hours_of_operation(model.getSpaces) if !hours_of_operation.nil? - OpenstudioStandards::Schedules.gather_inputs_parametric_schedules(schedule, water_use_equipment, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: hoo_var_method) + OpenstudioStandards::Schedules.schedule_ruleset_get_parametric_inputs(schedule, water_use_equipment, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: hoo_var_method) end end @@ -385,18 +385,18 @@ def self.model_apply_parametric_schedules(model, # @!endgroup Parametric:Model - # @!group Parametric:Space + # @!group Parametric:Spaces - # gathers parametric inputs for all loads objects associated with spaces/space types in provided array + # Gathers parametric inputs for all loads objects associated with spaces/space types in provided array # # @author David Goldwasser - # @param space_space_types [Array] array of OpenStudio::Model::Space or OpenStudio::Model::SpaceType objects + # @param spaces_space_types [Array] array of OpenStudio::Model::Space or OpenStudio::Model::SpaceType objects # @param parametric_inputs [Hash] parametric inputs hash of ScheduleRuleset => {floor: schedule floor, ceiling: schedule ceiling, target: load instance, hoo_inputs: hours_of_operation hash} # @param gather_data_only [Boolean] if true, no changes will be made to schedules # @return [Hash] parametric inputs hash of ScheduleRuleset => {floor: schedule floor, ceiling: schedule ceiling, target: load instance, hoo_inputs: hours_of_operation hash}. # parametric formulas are encoded in AdditionalProperties objects attached to the ScheduleRuleset - def self.gather_inputs_parametric_space_space_type_schedules(space_space_types, parametric_inputs, gather_data_only) - space_space_types.each do |space_type| + def self.spaces_space_types_get_parametric_schedule_inputs(spaces_space_types, parametric_inputs, gather_data_only) + spaces_space_types.each do |space_type| # get hours of operation for space type once next if space_type.class == 'OpenStudio::Model::SpaceTypes' && space_type.floorArea == 0 @@ -406,55 +406,55 @@ def self.gather_inputs_parametric_space_space_type_schedules(space_space_types, next end # loop through internal load instances - space_type.lights.each do |load_instance| - Space.gather_inputs_parametric_load_inst_schedules(load_instance, parametric_inputs, hours_of_operation, gather_data_only) + space_type.lights.each do |space_load_instance| + OpenstudioStandards::Space.space_load_instance_get_parametric_schedule_inputs(space_load_instance, parametric_inputs, hours_of_operation, gather_data_only) end - space_type.luminaires.each do |load_instance| - Space.gather_inputs_parametric_load_inst_schedules(load_instance, parametric_inputs, hours_of_operation, gather_data_only) + space_type.luminaires.each do |space_load_instance| + OpenstudioStandards::Space.space_load_instance_get_parametric_schedule_inputs(space_load_instance, parametric_inputs, hours_of_operation, gather_data_only) end - space_type.electricEquipment.each do |load_instance| - Space.gather_inputs_parametric_load_inst_schedules(load_instance, parametric_inputs, hours_of_operation, gather_data_only) + space_type.electricEquipment.each do |space_load_instance| + OpenstudioStandards::Space.space_load_instance_get_parametric_schedule_inputs(space_load_instance, parametric_inputs, hours_of_operation, gather_data_only) end - space_type.gasEquipment.each do |load_instance| - Space.gather_inputs_parametric_load_inst_schedules(load_instance, parametric_inputs, hours_of_operation, gather_data_only) + space_type.gasEquipment.each do |space_load_instance| + OpenstudioStandards::Space.space_load_instance_get_parametric_schedule_inputs(space_load_instance, parametric_inputs, hours_of_operation, gather_data_only) end - space_type.steamEquipment.each do |load_instance| - Space.gather_inputs_parametric_load_inst_schedules(load_instance, parametric_inputs, hours_of_operation, gather_data_only) + space_type.steamEquipment.each do |space_load_instance| + OpenstudioStandards::Space.space_load_instance_get_parametric_schedule_inputs(space_load_instance, parametric_inputs, hours_of_operation, gather_data_only) end - space_type.otherEquipment.each do |load_instance| - Space.gather_inputs_parametric_load_inst_schedules(load_instance, parametric_inputs, hours_of_operation, gather_data_only) + space_type.otherEquipment.each do |space_load_instance| + OpenstudioStandards::Space.space_load_instance_get_parametric_schedule_inputs(space_load_instance, parametric_inputs, hours_of_operation, gather_data_only) end - space_type.people.each do |load_instance| - Space.gather_inputs_parametric_load_inst_schedules(load_instance, parametric_inputs, hours_of_operation, gather_data_only) - if load_instance.activityLevelSchedule.is_initialized && load_instance.activityLevelSchedule.get.to_ScheduleRuleset.is_initialized - act_sch = load_instance.activityLevelSchedule.get.to_ScheduleRuleset.get - OpenstudioStandards::Schedules.gather_inputs_parametric_schedules(act_sch, load_instance, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: 'hours') + space_type.people.each do |space_load_instance| + OpenstudioStandards::Space.space_load_instance_get_parametric_schedule_inputs(space_load_instance, parametric_inputs, hours_of_operation, gather_data_only) + if space_load_instance.activityLevelSchedule.is_initialized && space_load_instance.activityLevelSchedule.get.to_ScheduleRuleset.is_initialized + act_sch = space_load_instance.activityLevelSchedule.get.to_ScheduleRuleset.get + OpenstudioStandards::Schedules.schedule_ruleset_get_parametric_inputs(act_sch, space_load_instance, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: 'hours') end end - space_type.spaceInfiltrationDesignFlowRates.each do |load_instance| - Space.gather_inputs_parametric_load_inst_schedules(load_instance, parametric_inputs, hours_of_operation, gather_data_only) + space_type.spaceInfiltrationDesignFlowRates.each do |space_load_instance| + OpenstudioStandards::Space.space_load_instance_get_parametric_schedule_inputs(space_load_instance, parametric_inputs, hours_of_operation, gather_data_only) end - space_type.spaceInfiltrationEffectiveLeakageAreas.each do |load_instance| - Space.gather_inputs_parametric_load_inst_schedules(load_instance, parametric_inputs, hours_of_operation, gather_data_only) + space_type.spaceInfiltrationEffectiveLeakageAreas.each do |space_load_instance| + OpenstudioStandards::Space.space_load_instance_get_parametric_schedule_inputs(space_load_instance, parametric_inputs, hours_of_operation, gather_data_only) end dsgn_spec_oa = space_type.designSpecificationOutdoorAir if dsgn_spec_oa.is_initialized - Space.gather_inputs_parametric_load_inst_schedules(dsgn_spec_oa.get, parametric_inputs, hours_of_operation, gather_data_only) + OpenstudioStandards::Space.space_load_instance_get_parametric_schedule_inputs(dsgn_spec_oa.get, parametric_inputs, hours_of_operation, gather_data_only) end end return parametric_inputs end - # @!endgroup Parametric:Space + # @!endgroup Parametric:Spaces # @!group Parametric:Schedule - # method to process load instance schedules for model_setup_parametric_schedules + # Method to process space load instance schedules for model_setup_parametric_schedules # # @author David Goldwasser # @param schedule_ruleset [OpenStudio::Model::ScheduleRuleset] OpenStudio ScheduleRuleset object - # @param load_instance [OpenStudio::Model::SpaceLoadInstance] OpenStudio SpaceLoadInstance object + # @param space_load_instance [OpenStudio::Model::SpaceLoadInstance] OpenStudio SpaceLoadInstance object # @param parametric_inputs [Hash] # @param hours_of_operation [Hash] hash, example: # { profile_index: { @@ -469,21 +469,21 @@ def self.gather_inputs_parametric_space_space_type_schedules(space_space_types, # @param gather_data_only [Boolean] if true, no changes are made to schedules # @param hoo_var_method [String] accepts hours and fractional. Any other value value will result in hoo variables not being applied # @return [Hash] parametric inputs hash of ScheduleRuleset => {floor: schedule floor, ceiling: schedule ceiling, target: load instance, hoo_inputs: hours_of_operation hash} - def self.gather_inputs_parametric_schedules(schedule_ruleset, load_instance, parametric_inputs, hours_of_operation, + def self.schedule_ruleset_get_parametric_inputs(schedule_ruleset, space_load_instance, parametric_inputs, hours_of_operation, ramp: true, min_ramp_dur_hr: 2.0, gather_data_only: false, hoo_var_method: 'hours') if parametric_inputs.key?(schedule_ruleset) if hours_of_operation != parametric_inputs[schedule_ruleset][:hoo_inputs] # don't warn if the hours of operation between old and new schedule are equivalent - # OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Parametric.Schedule', "#{load_instance.name} uses #{schedule_ruleset.name} but parametric inputs have already been setup based on hours of operation for #{parametric_inputs[schedule_ruleset][:target].name}.") + # OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Parametric.Schedule', "#{space_load_instance.name} uses #{schedule_ruleset.name} but parametric inputs have already been setup based on hours of operation for #{parametric_inputs[schedule_ruleset][:target].name}.") return nil end end # gather and store data for scheduleRuleset min_max = OpenstudioStandards::Schedules.schedule_ruleset_get_min_max(schedule_ruleset) - ruleset_hash = { floor: min_max['min'], ceiling: min_max['max'], target: load_instance, hoo_inputs: hours_of_operation } + ruleset_hash = { floor: min_max['min'], ceiling: min_max['max'], target: space_load_instance, hoo_inputs: hours_of_operation } parametric_inputs[schedule_ruleset] = ruleset_hash # stop here if only gathering information otherwise will continue and generate additional parametric properties for schedules and rules @@ -499,11 +499,11 @@ def self.gather_inputs_parametric_schedules(schedule_ruleset, load_instance, par OpenstudioStandards::Schedules.schedule_ruleset_cleanup_profiles(schedule_ruleset) # get initial hash of schedule days => rule indices - schedule_days = schedule_ruleset_get_schedule_day_rule_indices(schedule_ruleset) + schedule_days = OpenstudioStandards::Schedules.schedule_ruleset_get_schedule_day_rule_indices(schedule_ruleset) # get all day schedule equivalent full load hours to tag - daily_flhs = schedule_days.keys.map { |day_sch| schedule_day_get_equivalent_full_load_hours(day_sch) } + daily_flhs = schedule_days.keys.map { |day_sch| OpenstudioStandards::Schedules.schedule_day_get_equivalent_full_load_hours(day_sch) } # collect initial rule index => array of days used hash - sch_ruleset_days_used = schedule_ruleset_get_annual_days_used(schedule_ruleset) + sch_ruleset_days_used = OpenstudioStandards::Schedules.schedule_ruleset_get_annual_days_used(schedule_ruleset) # match up schedule rule days with hours of operation days sch_day_map = {} @@ -546,9 +546,9 @@ def self.gather_inputs_parametric_schedules(schedule_ruleset, load_instance, par schedule_ruleset.scheduleRules[new_rule_ct..-1].each( &:remove ) unless new_rule_ct == 0 # re-collect new schedule rules - schedule_days = schedule_ruleset_get_schedule_day_rule_indices(schedule_ruleset) + schedule_days = OpenstudioStandards::Schedules.schedule_ruleset_get_schedule_day_rule_indices(schedule_ruleset) # re-collect new rule index => days used array - sch_ruleset_days_used = schedule_ruleset_get_annual_days_used(schedule_ruleset) + sch_ruleset_days_used = OpenstudioStandards::Schedules.schedule_ruleset_get_annual_days_used(schedule_ruleset) # step through profiles and add additional properties to describe profiles schedule_days.each_with_index do |(schedule_day, current_rule_index), i| diff --git a/lib/openstudio-standards/space/space.rb b/lib/openstudio-standards/space/space.rb index 23fa9d1991..516473ee1c 100644 --- a/lib/openstudio-standards/space/space.rb +++ b/lib/openstudio-standards/space/space.rb @@ -483,27 +483,28 @@ def self.spaces_get_occupancy_schedule(spaces, sch_name: nil, occupied_percentag # @!endgroup Space # @!group SpaceLoadInstance + # method to process load instance schedules for model_setup_parametric_schedules # # @author David Goldwasser - # @param load_inst [OpenStudio::Model::SpaceLoadInstance] + # @param space_load_instance [OpenStudio::Model::SpaceLoadInstance] OpenStudio SpaceLoadInstance object # @param parametric_inputs [Hash] # @param hours_of_operation [Hash] # @param gather_data_only [Boolean] # @return [Hash] - def self.gather_inputs_parametric_load_inst_schedules(load_inst, parametric_inputs, hours_of_operation, gather_data_only) - if load_inst.class.to_s == 'OpenStudio::Model::People' - opt_sch = load_inst.numberofPeopleSchedule - elsif load_inst.class.to_s == 'OpenStudio::Model::DesignSpecificationOutdoorAir' - opt_sch = load_inst.outdoorAirFlowRateFractionSchedule + def self.space_load_instance_get_parametric_schedule_inputs(space_load_instance, parametric_inputs, hours_of_operation, gather_data_only) + if space_load_instance.class.to_s == 'OpenStudio::Model::People' + opt_sch = space_load_instance.numberofPeopleSchedule + elsif space_load_instance.class.to_s == 'OpenStudio::Model::DesignSpecificationOutdoorAir' + opt_sch = space_load_instance.outdoorAirFlowRateFractionSchedule else - opt_sch = load_inst.schedule + opt_sch = space_load_instance.schedule end if !opt_sch.is_initialized || !opt_sch.get.to_ScheduleRuleset.is_initialized return nil end - OpenstudioStandards::Schedules.gather_inputs_parametric_schedules(opt_sch.get.to_ScheduleRuleset.get, load_inst, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: 'hours') + OpenstudioStandards::Schedules.schedule_ruleset_get_parametric_inputs(opt_sch.get.to_ScheduleRuleset.get, space_load_instance, parametric_inputs, hours_of_operation, gather_data_only: gather_data_only, hoo_var_method: 'hours') return parametric_inputs end diff --git a/test/modules/schedules/test_schedules_parametric.rb b/test/modules/schedules/test_schedules_parametric.rb index 842383226d..7deb177252 100644 --- a/test/modules/schedules/test_schedules_parametric.rb +++ b/test/modules/schedules/test_schedules_parametric.rb @@ -78,7 +78,7 @@ def test_model_infer_building_hours_of_operation assert_equal(model.getBuilding.defaultScheduleSet.get.hoursofOperationSchedule.get, hours_of_operation_schedule) end - def test_gather_inputs_parametric_schedules + def test_schedule_ruleset_get_parametric_inputs model = OpenStudio::Model::Model.new model.getYearDescription.setCalendarYear(2018) @@ -117,9 +117,9 @@ def test_gather_inputs_parametric_schedules @sch.model_infer_hours_of_operation_building(model) hours_of_operation = @spaces.spaces_hours_of_operation(model.getSpaces) - parametric_inputs = @sch.gather_inputs_parametric_schedules(lght_sch, lght, {}, hours_of_operation, ramp:true, min_ramp_dur_hr: 2.0, gather_data_only: false, hoo_var_method: 'hours') + parametric_inputs = @sch.schedule_ruleset_get_parametric_inputs(lght_sch, lght, {}, hours_of_operation, ramp:true, min_ramp_dur_hr: 2.0, gather_data_only: false, hoo_var_method: 'hours') - parametric_inputs = @sch.gather_inputs_parametric_schedules(clg_sch, nil, {}, hours_of_operation, ramp: true, min_ramp_dur_hr: 2.0, gather_data_only: false, hoo_var_method: 'hours') + parametric_inputs = @sch.schedule_ruleset_get_parametric_inputs(clg_sch, nil, {}, hours_of_operation, ramp: true, min_ramp_dur_hr: 2.0, gather_data_only: false, hoo_var_method: 'hours') # collect additional properties props = get_additional_properties(lght_sch.defaultDaySchedule.additionalProperties)