Class: BuildingSync::LoadsSystem

Inherits:
BuildingSystem show all
Includes:
Helper
Defined in:
lib/buildingsync/model_articulation/loads_system.rb

Overview

LoadsSystem class that manages internal and external loads

Instance Method Summary collapse

Methods included from Helper

#help_calculate_hours, #help_convert, #help_count_number_of_days, #help_element_class_type_check, #help_get_attribute_value, #help_get_default_schedule_set, #help_get_duration, #help_get_end_time, #help_get_end_time_sat, #help_get_end_time_sun, #help_get_end_time_weekday, #help_get_or_create, #help_get_schedule_rule_set_from_schedule, #help_get_start_time, #help_get_start_time_sat, #help_get_start_time_sun, #help_get_start_time_weekday, #help_get_text_value, #help_get_text_value_as_bool, #help_get_text_value_as_date, #help_get_text_value_as_datetime, #help_get_text_value_as_float, #help_get_text_value_as_integer, #help_get_zone_name_list, #help_load_doc, #help_print_all_schedules, #help_print_schedule, #help_write_profile

Methods included from XmlGetSet

#get_prefix, #xget_attribute_for_element, #xget_element, #xget_id, #xget_idrefs, #xget_linked_premises, #xget_name, #xget_or_create, #xget_plurals_text_value, #xget_text, #xget_text_as_bool, #xget_text_as_date, #xget_text_as_dt, #xget_text_as_float, #xget_text_as_integer, #xset_or_create, #xset_text

Constructor Details

#initialize(system_element = '', ns = '') ⇒ LoadsSystem

initialize

Parameters:

  • system_element (REXML::Element) (defaults to: '')
  • ns (String) (defaults to: '')


51
52
53
# File 'lib/buildingsync/model_articulation/loads_system.rb', line 51

def initialize(system_element = '', ns = '')
  # code to initialize
end

Instance Method Details

#add_elevator(model, standard) ⇒ Object

add elevator

Parameters:

  • model (OpenStudio::Model)
  • standard (Standard)

Returns:

  • boolean



263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
# File 'lib/buildingsync/model_articulation/loads_system.rb', line 263

def add_elevator(model, standard)
  # remove elevators as spaceLoads or exteriorLights
  model.getSpaceLoads.each do |instance|
    next if !instance.name.to_s.include?('Elevator') # most prototype building types model exterior elevators with name Elevator

    instance.remove
  end
  model.getExteriorLightss.each do |ext_light|
    next if !ext_light.name.to_s.include?('Fuel equipment') # some prototype building types model exterior elevators by this name

    ext_light.remove
  end

  elevators = standard.model_add_elevators(model)
  if elevators.nil?
    OpenStudio.logFree(OpenStudio::Info, 'BuildingSync.LoadsSystem.add_elevator', 'No elevators added to the building.')
  else
    elevator_def = elevators.electricEquipmentDefinition
    design_level = elevator_def.designLevel.get
    OpenStudio.logFree(OpenStudio::Info, 'BuildingSync.LoadsSystem.add_elevator', "Adding #{elevators.multiplier.round(1)} elevators each with power of #{OpenStudio.toNeatString(design_level, 0, true)} (W), plus lights and fans.")
  end
  return true
end

#add_internal_loads(model, standard, template, building_sections, remove_objects) ⇒ Boolean

add internal loads from standard definitions

Parameters:

  • model (OpenStudio::Model)
  • standard (Standard)
  • template (String)
  • building_sections (REXML:Element)
  • remove_objects (Boolean)

Returns:

  • (Boolean)


62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/buildingsync/model_articulation/loads_system.rb', line 62

def add_internal_loads(model, standard, template, building_sections, remove_objects)
  # remove internal loads
  if remove_objects
    model.getSpaceLoads.each do |instance|
      next if instance.name.to_s.include?('Elevator') # most prototype building types model exterior elevators with name Elevator
      next if instance.to_InternalMass.is_initialized
      next if instance.to_WaterUseEquipment.is_initialized

      instance.remove
    end
    model.getDesignSpecificationOutdoorAirs.each(&:remove)
    model.getDefaultScheduleSets.each(&:remove)
  end

  OpenStudio.logFree(OpenStudio::Warn, 'BuildingSync.LoadsSystem.add_internal_loads', 'Adding internal loads')
  count_not_found = 0
  model.getSpaceTypes.each do |space_type|
    data = standard.space_type_get_standards_data(space_type)
    if data.empty?
      original_building_type = space_type.standardsBuildingType.get
      alternate_building_type = standard.model_get_lookup_name(original_building_type)
      if alternate_building_type != original_building_type
        space_type.setStandardsBuildingType(alternate_building_type)
        data = standard.space_type_get_standards_data(space_type)
        if data.empty?
          OpenStudio.logFree(OpenStudio::Warn, 'BuildingSync.LoadsSystem.add_internal_loads', "Unable to get standards data for Space Type: #{space_type.name}.  Tried standards building type: #{original_building_type} and #{alternate_building_type} with standards space type: #{space_type.standardsSpaceType.get}")
          count_not_found += 1
          next
        else
          OpenStudio.logFree(OpenStudio::Warn, 'BuildingSync.LoadsSystem.add_internal_loads', "Space Type: #{space_type.name}. Standards building type changed from #{original_building_type} to #{alternate_building_type} with standards space type: #{space_type.standardsSpaceType.get}")
        end
      else
        OpenStudio.logFree(OpenStudio::Warn, 'BuildingSync.LoadsSystem.add_internal_loads', "Unable to get standards data for Space Type: #{space_type.name}.  Standards building type: #{space_type.standardsBuildingType.get}, space type: #{space_type.standardsSpaceType.get}")
        count_not_found += 1
        next
      end
    end
    # Don't add infiltration here; will be added later in the script
    test = standard.space_type_apply_internal_loads(space_type, true, true, true, true, true, false)
    if test == false
      OpenStudio.logFree(OpenStudio::Warn, 'BuildingSync.LoadsSystem.add_internal_loads', "Could not add loads for #{space_type.name}. Not expected for #{template}")
      next
    end

    # apply internal load schedules
    # the last bool test it to make thermostat schedules. They are now added in HVAC section instead of here
    success = standard.space_type_apply_internal_load_schedules(space_type, true, true, true, true, true, true, false)
    if !success
      OpenStudio.logFree(OpenStudio::Warn, 'BuildingSync.LoadsSystem.add_internal_loads', "space_type_apply_internal_load_schedules unsuccessful for #{space_type.name}")
    end

    # here we adjust the people schedules according to user input of hours per week and weeks per year
    if !building_sections.empty?
      adjust_schedules(standard, space_type, get_building_occupancy_hours(building_sections), model)
    end
    # extend space type name to include the template. Consider this as well for load defs
    space_type.setName("#{space_type.name} - #{template}")
    OpenStudio.logFree(OpenStudio::Info, 'BuildingSync.LoadsSystem.add_internal_loads', "Adding loads to space type named #{space_type.name}")
  end

  if count_not_found > 0
    OpenStudio.logFree(OpenStudio::Warn, 'BuildingSync.LoadsSystem.add_internal_loads', "#{count_not_found} of #{model.getSpaceTypes.size} Space Types have no internal loads")
  end

  # warn if spaces in model without space type
  spaces_without_space_types = []
  model.getSpaces.each do |space|
    next if space.spaceType.is_initialized

    spaces_without_space_types << space
  end
  if !spaces_without_space_types.empty?
    OpenStudio.logFree(OpenStudio::Warn, 'BuildingSync.LoadsSystem.add_internal_loads', "#{spaces_without_space_types.size} spaces do not have space types assigned, and wont' receive internal loads from standards space type lookups.")
  end
  return true
end

#adjust_occupancy_peak(model, new_occupancy_peak, area, space_types) ⇒ Object

add occupancy peak

Parameters:

  • model (OpenStudio::Model)
  • new_occupancy_peak (String)
  • area (String)
  • space_types (REXML:Element)


144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/buildingsync/model_articulation/loads_system.rb', line 144

def adjust_occupancy_peak(model, new_occupancy_peak, area, space_types)
  # we assume that the standard always generate people per area
  sum_of_people_per_area = 0.0
  count = 0
  if !space_types.nil?
    sorted_space_types = model.getSpaceTypes.sort
    sorted_space_types.each do |space_type|
      if space_types.include? space_type
        peoples = space_type.people
        peoples.each do |people|
          sum_of_people_per_area += people.peoplePerFloorArea.get
          count += 1
        end
      end
    end
    average_people_per_area = sum_of_people_per_area / count
    puts "existing occupancy: #{average_people_per_area} new target value: #{new_occupancy_peak.to_f / area.to_f}"
    new_sum_of_people_per_area = 0.0
    sorted_space_types.each do |space_type|
      if space_types.include? space_type
        peoples = space_type.people
        peoples.each do |people|
          ratio = people.peoplePerFloorArea.get.to_f / average_people_per_area.to_f
          new_value = ratio * new_occupancy_peak.to_f / area.to_f
          puts "adjusting occupancy per area value from: #{people.peoplePerFloorArea.get} by ratio #{ratio} to #{new_value}"
          people.peopleDefinition.setPeopleperSpaceFloorArea(new_value)
          new_sum_of_people_per_area += new_value
        end
      end
    end
    puts "resulting total absolute occupancy value: #{new_sum_of_people_per_area * area.to_f} occupancy per area value: #{new_sum_of_people_per_area / count}"
  else
    puts 'space types are empty'
  end
end

#adjust_schedules(standard, space_type, building_occupant_hours_per_week, model) ⇒ Object

adjust schedules

Parameters:

  • standard (Standard)
  • space_type (OpenStudio::Model::SpaceType)
  • building_occupant_hours_per_week (Float)
  • model (OpenStudio::Model)

Returns:

  • boolean



204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'lib/buildingsync/model_articulation/loads_system.rb', line 204

def adjust_schedules(standard, space_type, building_occupant_hours_per_week, model)
  # this uses code from https://github.com/NREL/openstudio-extension-gem/blob/6f8f7a46de496c3ab95ed9c72d4d543bd4b67740/lib/openstudio/extension/core/os_lib_model_generation.rb#L3007
  #
  # currently this works for all schedules in the model
  # in the future we would want to make this more flexible to adjusted based on space_types or building sections
  return unless !building_occupant_hours_per_week.nil?
  hours_per_week = building_occupant_hours_per_week

  default_schedule_set = help_get_default_schedule_set(model)
  existing_number_of_people_sched = help_get_schedule_rule_set_from_schedule(default_schedule_set.numberofPeopleSchedule)
  return false if existing_number_of_people_sched.nil?
  calc_hours_per_week = help_calculate_hours(existing_number_of_people_sched)
  ratio_hours_per_week = hours_per_week / calc_hours_per_week

  wkdy_start_time = help_get_start_time_weekday(existing_number_of_people_sched)
  wkdy_end_time = help_get_end_time_weekday(existing_number_of_people_sched)
  wkdy_hours = wkdy_end_time - wkdy_start_time

  sat_start_time = help_get_start_time_sat(existing_number_of_people_sched)
  sat_end_time = help_get_end_time_sat(existing_number_of_people_sched)
  sat_hours = sat_end_time - sat_start_time

  sun_start_time = help_get_start_time_sun(existing_number_of_people_sched)
  sun_end_time = help_get_end_time_sun(existing_number_of_people_sched)
  sun_hours = sun_end_time - sun_start_time

  # determine new end times via ratios
  wkdy_end_time = wkdy_start_time + OpenStudio::Time.new(ratio_hours_per_week * wkdy_hours.totalDays)
  sat_end_time = sat_start_time + OpenStudio::Time.new(ratio_hours_per_week * sat_hours.totalDays)
  sun_end_time = sun_start_time + OpenStudio::Time.new(ratio_hours_per_week * sun_hours.totalDays)

  # Infer the current hours of operation schedule for the building
  op_sch = standard.model_infer_hours_of_operation_building(model)
  default_schedule_set.setHoursofOperationSchedule(op_sch)

  # help_print_all_schedules("org_schedules-#{space_type.name}.csv", default_schedule_set)

  # Convert existing schedules in the model to parametric schedules based on current hours of operation
  standard.model_setup_parametric_schedules(model)

  # Modify hours of operation, using weekdays values for all weekdays and weekend values for Saturday and Sunday
  standard.schedule_ruleset_set_hours_of_operation(op_sch,
                                                   wkdy_start_time: wkdy_start_time,
                                                   wkdy_end_time: wkdy_end_time,
                                                   sat_start_time: sat_start_time,
                                                   sat_end_time: sat_end_time,
                                                   sun_start_time: sun_start_time,
                                                   sun_end_time: sun_end_time)

  # Apply new operating hours to parametric schedules to make schedules in model reflect modified hours of operation
  parametric_schedules = standard.model_apply_parametric_schedules(model, error_on_out_of_order: false)
  puts "Updated #{parametric_schedules.size} schedules with new hours of operation."
  return true
end

#get_building_occupancy_hours(building_sections) ⇒ Float

get building occupancy hours

Parameters:

  • building_sections (array)

Returns:

  • (Float)


183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/buildingsync/model_articulation/loads_system.rb', line 183

def get_building_occupancy_hours(building_sections)
  if building_sections.count == 1
    return building_sections[0].typical_occupant_usage_value_hours.to_f
  end
  occupancy_hours = nil
  count = 0.0
  building_sections.each do |section|
    occupancy_hours = 0.0 if occupancy_hours.nil?
    occupancy_hours += section.typical_occupant_usage_value_hours.to_f if section.typical_occupant_usage_value_hours.nil?
    count += 1 if section.typical_occupant_usage_value_hours.nil?
  end
  return nil if occupancy_hours.nil?
  return occupancy_hours / count
end