Class: BuildingSync::SpatialElement

Inherits:
Object
  • Object
show all
Includes:
Helper, XmlGetSet, OsLib_ModelGeneration
Defined in:
lib/buildingsync/model_articulation/spatial_element.rb

Overview

base class for objects that will configure workflows based on building sync files

Direct Known Subclasses

BuildingSection, LocationElement

Instance Attribute Summary collapse

Instance Method Summary collapse

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

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

Constructor Details

#initialize(base_xml, ns) ⇒ SpatialElement

initialize SpatialElement class

Parameters:

  • base_xml (REXML::Element)

    an element corresponding to a spatial element, either an auc:Site, auc:Building, auc:Section

  • ns (String)

    namespace, likely 'auc'



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/buildingsync/model_articulation/spatial_element.rb', line 57

def initialize(base_xml, ns)
  @base_xml = base_xml
  @ns = ns

  @total_floor_area = nil
  @standards_building_type = nil
  @system_type = nil
  @bar_division_method = nil
  @space_types = {}
  @fraction_area = nil
  @space_types_floor_area = nil
  @conditioned_floor_area_heated_only = nil
  @conditioned_floor_area_cooled_only = nil
  @conditioned_floor_area_heated_cooled = 0
  @custom_conditioned_above_grade_floor_area = nil
  @custom_conditioned_below_grade_floor_area = nil

  @user_defined_fields = REXML::Element.new("#{@ns}:UserDefinedFields")
end

Instance Attribute Details

#space_typesObject (readonly)

Returns the value of attribute space_types.



351
352
353
# File 'lib/buildingsync/model_articulation/spatial_element.rb', line 351

def space_types
  @space_types
end

#standards_building_typeObject (readonly)

Returns the value of attribute standards_building_type.



351
352
353
# File 'lib/buildingsync/model_articulation/spatial_element.rb', line 351

def standards_building_type
  @standards_building_type
end

#system_typeObject (readonly)

Returns the value of attribute system_type.



351
352
353
# File 'lib/buildingsync/model_articulation/spatial_element.rb', line 351

def system_type
  @system_type
end

#total_floor_areaObject (readonly)

Returns the value of attribute total_floor_area.



351
352
353
# File 'lib/buildingsync/model_articulation/spatial_element.rb', line 351

def total_floor_area
  @total_floor_area
end

Instance Method Details

#add_user_defined_field_to_xml_file(field_name, field_value) ⇒ Object

add user defined field to xml file

Parameters:

  • field_name (String)
  • field_value (String)


319
320
321
322
323
324
325
326
327
328
329
330
331
332
# File 'lib/buildingsync/model_articulation/spatial_element.rb', line 319

def add_user_defined_field_to_xml_file(field_name, field_value)
  user_defined_field = REXML::Element.new("#{@ns}:UserDefinedField")
  field_name_element = REXML::Element.new("#{@ns}:FieldName")
  field_value_element = REXML::Element.new("#{@ns}:FieldValue")

  if !field_value.nil?
    @user_defined_fields.add_element(user_defined_field)
    user_defined_field.add_element(field_name_element)
    user_defined_field.add_element(field_value_element)

    field_name_element.text = field_name
    field_value_element.text = field_value
  end
end

#create_space_types(model, total_bldg_floor_area, total_number_floors, standard_template, open_studio_standard) ⇒ Object

create space types

Parameters:

  • model (OpenStudio::Model)
  • total_bldg_floor_area (Float)
  • total_number_floors (Integer)
  • standard_template (String)
  • open_studio_standard (Standard)

Returns:

  • hash



266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
# File 'lib/buildingsync/model_articulation/spatial_element.rb', line 266

def create_space_types(model, total_bldg_floor_area, total_number_floors, standard_template, open_studio_standard)
  # create space types from section type
  # mapping lookup_name name is needed for a few methods
  set_bldg_and_system_type(xget_text('OccupancyClassification'), total_bldg_floor_area, total_number_floors, false) if @standards_building_type.nil?
  if open_studio_standard.nil?
    begin
      open_studio_standard = Standard.build("#{standard_template}_#{@standards_building_type}")
    rescue StandardError => e
      # if the combination of standard type and bldg type fails we try the standard type alone.
      puts "could not find open studio standard for template #{standard_template} and bldg type: #{@standards_building_type}, trying the standard type alone"
      open_studio_standard = Standard.build(standard_template)
      raise(e)
    end
  end

  @space_types = get_space_types_from_building_type(@standards_building_type, standard_template, true)
  puts "BuildingSync.SpatialElement.create_space_types - Space types: #{@space_types} selected for building type: #{@standards_building_type} and standard template: #{standard_template}"
  # create space_type_map from array
  sum_of_ratios = 0.0

  @space_types.each do |space_type_name, hash|
    # create space type
    space_type = OpenStudio::Model::SpaceType.new(model)
    space_type.setStandardsBuildingType(@standards_building_type)
    space_type.setStandardsSpaceType(space_type_name)
    space_type.setName("#{@standards_building_type} #{space_type_name}")

    # set color
    test = open_studio_standard.space_type_apply_rendering_color(space_type) # this uses openstudio-standards
    OpenStudio.logFree(OpenStudio::Warn, 'BuildingSync.SpatialElement.create_space_types', "Warning: Could not find color for #{space_type.name}") if !test
    # extend hash to hold new space type object
    hash[:space_type] = space_type

    # add to sum_of_ratios counter for adjustment multiplier
    sum_of_ratios += hash[:ratio]
  end

  # store multiplier needed to adjust sum of ratios to equal 1.0
  @ratio_adjustment_multiplier = 1.0 / sum_of_ratios

  @space_types_floor_area = {}
  @space_types.each do |space_type_name, hash|
    ratio_of_bldg_total = hash[:ratio] * @ratio_adjustment_multiplier * @fraction_area
    final_floor_area = ratio_of_bldg_total * total_bldg_floor_area # I think I can just pass ratio but passing in area is cleaner
    @space_types_floor_area[hash[:space_type]] = { floor_area: final_floor_area }
  end
  puts 'BuildingSync.SpatialElement.create_space_types'
  return @space_types_floor_area
end

#prepare_final_xml_for_spatial_elementObject

write parameters to xml for spatial element



335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
# File 'lib/buildingsync/model_articulation/spatial_element.rb', line 335

def prepare_final_xml_for_spatial_element
  add_user_defined_field_to_xml_file('StandardsBuildingType', @standards_building_type)
  add_user_defined_field_to_xml_file('SystemType', @system_type)
  add_user_defined_field_to_xml_file('BarDivisionMethod', @bar_division_method)
  add_user_defined_field_to_xml_file('FractionArea', @fraction_area)
  add_user_defined_field_to_xml_file('SpaceTypesFloorArea', @space_types_floor_area)
  add_user_defined_field_to_xml_file('TotalFloorArea(m^2)', @total_floor_area)
  add_user_defined_field_to_xml_file('ConditionedFloorArea(m^2)', @conditioned_floor_area_heated_cooled) if !@conditioned_floor_area_heated_cooled.nil?
  add_user_defined_field_to_xml_file('HeatedFloorArea(m^2)', @conditioned_floor_area_heated_only) if !@conditioned_floor_area_heated_only.nil?
  add_user_defined_field_to_xml_file('CooledFloorArea(m^2)', @conditioned_floor_area_cooled_only) if !@conditioned_floor_area_cooled_only.nil?
  add_user_defined_field_to_xml_file('ConditionedAboveGradeFloorArea(m^2)', @custom_conditioned_above_grade_floor_area) if !@custom_conditioned_above_grade_floor_area.nil?
  add_user_defined_field_to_xml_file('ConditionedBelowGradeFloorArea(m^2)', @custom_conditioned_below_grade_floor_area) if !@custom_conditioned_below_grade_floor_area.nil?

  @base_xml.add_element(@user_defined_fields)
end

#process_bldg_and_system_type(building_and_system_types, occupancy_classification, total_floor_area, total_number_floors) ⇒ Boolean

Determine the standards_building_type, bar_division_method, and system_type given:

  • occupancy_classification, total_floor_area, total_number_floors

Parameters:

  • building_and_system_types (Hash)

    a read in of the building_and_system_types.json file

  • occupancy_classification (String)

    value of OccupancyClassification element

  • total_floor_area (Float)
  • total_number_floors (Integer)

Returns:

  • (Boolean)


189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
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
# File 'lib/buildingsync/model_articulation/spatial_element.rb', line 189

def process_bldg_and_system_type(building_and_system_types, occupancy_classification, total_floor_area, total_number_floors)
  OpenStudio.logFree(OpenStudio::Info, 'BuildingSync.SpatialElement.process_bldg_and_system_type', "Element ID: #{xget_id} started with occupancy_classification #{occupancy_classification} and total floor area: #{total_floor_area}")
  puts "Element ID: #{xget_id} started with occupancy_classification #{occupancy_classification} and total floor area: #{total_floor_area}"

  # if building_and_system_types doesn't contain occupancy_classification, there's nothing we can do.
  occ_types = building_and_system_types[:"#{occupancy_classification}"]
  if occ_types.nil?
    raise "BuildingSync Occupancy type #{occupancy_classification} is not available in the building_and_system_types.json dictionary"
  end

  # if theres only one, we chose it indiscriminately
  # TODO: idk if we should do this but its what the tests want
  if occ_types.length == 1
    return sets_occupancy_bldg_system_types(occ_types[0])
  end

  # Try on each occ_type in the occupancy_classification for size
  occ_types.each do |occ_type|
    # if occ_type has a specified floor area range, see if it matches up
    if occ_type[:min_floor_area] || occ_type[:max_floor_area]
      min_floor_area = occ_type[:min_floor_area].nil? ?
        nil :
        OpenStudio.convert(occ_type[:min_floor_area].to_f, 'ft^2', 'm^2').get
      max_floor_area = occ_type[:max_floor_area].nil? ?
        nil :
        OpenStudio.convert(occ_type[:max_floor_area].to_f, 'ft^2', 'm^2').get

      too_small = min_floor_area && total_floor_area < min_floor_area
      too_big = max_floor_area && total_floor_area >= max_floor_area
      if !too_big && !too_small
        puts "selected the following standards_building_type: #{occ_type[:standards_building_type]}"
        return sets_occupancy_bldg_system_types(occ_type)
      end

    # else, if occ_type a specified floor number range, see if it matches up
    elsif occ_type[:min_number_floors] || occ_type[:max_number_floors]
      min_number_floors = occ_type[:min_number_floors].nil? ? nil : occ_type[:min_number_floors].to_i
      max_number_floors = occ_type[:max_number_floors].nil? ? nil : occ_type[:max_number_floors].to_i

      too_small = min_number_floors && total_number_floors < min_number_floors
      too_big = max_number_floors && total_number_floors >= max_number_floors
      if !too_big && !too_small
        puts "selected the following standards_building_type: #{occ_type[:standards_building_type]}"
        return sets_occupancy_bldg_system_types(occ_type)
      end
    end
  end

  # no occ_type fit! We must give up
  return false
end

#read_floor_areas(parent_total_floor_area) ⇒ Object

read floor areas

Parameters:

  • parent_total_floor_area (Float)


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
138
139
140
141
# File 'lib/buildingsync/model_articulation/spatial_element.rb', line 79

def read_floor_areas(parent_total_floor_area)
  @base_xml.elements.each("#{@ns}:FloorAreas/#{@ns}:FloorArea") do |floor_area_element|
    next if !floor_area_element.elements["#{@ns}:FloorAreaValue"]
    floor_area = floor_area_element.elements["#{@ns}:FloorAreaValue"].text.to_f
    next if floor_area.nil?

    floor_area_type = floor_area_element.elements["#{@ns}:FloorAreaType"].text
    if floor_area_type == 'Gross'
      @total_floor_area = OpenStudio.convert(validate_positive_number_excluding_zero('gross_floor_area', floor_area), 'ft^2', 'm^2').get
    elsif floor_area_type == 'Footprint'
      @footprint_floor_area = OpenStudio.convert(validate_positive_number_excluding_zero('@footprint_floor_area', floor_area), 'ft^2', 'm^2').get
    elsif floor_area_type == 'Conditioned' || floor_area_type == 'Common' || floor_area_type == 'Heated and Cooled'
      @conditioned_floor_area_heated_cooled += OpenStudio.convert(validate_positive_number_excluding_zero('@conditioned_floor_area_heated_cooled', floor_area), 'ft^2', 'm^2').get
    elsif floor_area_type == 'Heated Only'
      @conditioned_floor_area_heated_only = OpenStudio.convert(validate_positive_number_excluding_zero('@heated_only_floor_area', floor_area), 'ft^2', 'm^2').get
    elsif floor_area_type == 'Cooled Only'
      @conditioned_floor_area_cooled_only = OpenStudio.convert(validate_positive_number_excluding_zero('@cooled_only_floor_area', floor_area), 'ft^2', 'm^2').get
    elsif floor_area_type == 'Custom'
      if floor_area_element.elements["#{@ns}:FloorAreaCustomName"].text == 'Conditioned above grade'
        @custom_conditioned_above_grade_floor_area = OpenStudio.convert(validate_positive_number_excluding_zero('@custom_conditioned_above_grade_floor_area', floor_area), 'ft^2', 'm^2').get
      elsif floor_area_element.elements["#{@ns}:FloorAreaCustomName"].text == 'Conditioned below grade'
        @custom_conditioned_below_grade_floor_area = OpenStudio.convert(validate_positive_number_excluding_zero('@custom_conditioned_below_grade_floor_area', floor_area), 'ft^2', 'm^2').get
      end
    else
      OpenStudio.logFree(OpenStudio::Warn, 'BuildingSync.SpatialElement.generate_baseline_osm', "Unsupported floor area type found: #{floor_area_type}")
    end
  end

  if @total_floor_area.nil? || @total_floor_area == 0
    # if the total floor area is null, we try to calculate the total area, from various conditioned areas
    running_floor_area = 0
    if !@conditioned_floor_area_cooled_only.nil? && @conditioned_floor_area_cooled_only > 0
      running_floor_area += @conditioned_floor_area_cooled_only
    end
    if !@conditioned_floor_area_heated_only.nil? && @conditioned_floor_area_heated_only > 0
      running_floor_area += @conditioned_floor_area_heated_only
    end
    if !@conditioned_floor_area_heated_cooled.nil? && @conditioned_floor_area_heated_cooled > 0
      running_floor_area += @conditioned_floor_area_heated_cooled
    end
    if running_floor_area > 0
      @total_floor_area = running_floor_area
    else
      # if the conditions floor areas are null, we look at the conditioned above and below grade areas
      if !@custom_conditioned_above_grade_floor_area.nil? && @custom_conditioned_above_grade_floor_area > 0
        running_floor_area += @custom_conditioned_above_grade_floor_area
      end
      if !@custom_conditioned_below_grade_floor_area.nil? && @custom_conditioned_below_grade_floor_area > 0
        running_floor_area += @custom_conditioned_below_grade_floor_area
      end
      if running_floor_area > 0
        @total_floor_area = running_floor_area
      end
    end
  end

  # if we did not find any area we get the parent one
  if @total_floor_area.nil? || @total_floor_area == 0
    return parent_total_floor_area
  else
    return @total_floor_area
  end
end

#set_bldg_and_system_type(occupancy_classification, total_floor_area, total_number_floors, raise_exception) ⇒ Object

set building and system type

Parameters:

  • occupancy_classification (String)
  • total_floor_area (Float)
  • total_number_floors (Integer)
  • raise_exception (Boolean)


148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/buildingsync/model_articulation/spatial_element.rb', line 148

def set_bldg_and_system_type(occupancy_classification, total_floor_area, total_number_floors, raise_exception)
  # DOE Prototype building types:from openstudio-standards/lib/openstudio-standards/prototypes/common/prototype_metaprogramming.rb
  # SmallOffice, MediumOffice, LargeOffice, RetailStandalone, RetailStripmall, PrimarySchool, SecondarySchool, Outpatient
  # Hospital, SmallHotel, LargeHotel, QuickServiceRestaurant, FullServiceRestaurant, MidriseApartment, HighriseApartment, Warehouse

  if !occupancy_classification.nil? && !total_floor_area.nil?

    building_and_system_types = eval(File.read(BUILDING_AND_SYSTEMS_FILE_PATH))

    process_bldg_and_system_type(building_and_system_types, occupancy_classification, total_floor_area, total_number_floors)

    if @standards_building_type == ''
      raise "Building type '#{occupancy_classification}' is beyond BuildingSync scope"
    end
  elsif raise_exception
    if occupancy_classification.nil? && !total_floor_area.nil?
      raise "ID: #{xget_id} occupancy classification '#{occupancy_classification}' is nil"
    elsif !occupancy_classification.nil? && total_floor_area.nil?
      raise "ID: #{xget_id} Building total floor area '#{total_floor_area}' is nil"
    end
  end
end

#sets_occupancy_bldg_system_types(occ_type) ⇒ Boolean

gets the standards occupancy type from the building type or the potential overwrite occupancy type

Parameters:

  • occ_type (Hash)

Returns:

  • (Boolean)


174
175
176
177
178
179
180
# File 'lib/buildingsync/model_articulation/spatial_element.rb', line 174

def sets_occupancy_bldg_system_types(occ_type)
  @standards_building_type = occ_type[:standards_building_type]
  @bar_division_method = occ_type[:bar_division_method]
  @system_type = occ_type[:system_type]
  OpenStudio.logFree(OpenStudio::Info, 'BuildingSync.SpatialElement.sets_occupancy_bldg_system_types', "Element ID: #{xget_id} @standards_building_type #{@standards_building_type}, @bar_division_method #{@bar_division_method} and @system_type: #{@system_type}")
  return true
end

#validate_positive_number_excluding_zero(name, value) ⇒ Object

validate positive number excluding zero

Parameters:

  • name (String)
  • value (Float)

Returns:

  • float



245
246
247
248
# File 'lib/buildingsync/model_articulation/spatial_element.rb', line 245

def validate_positive_number_excluding_zero(name, value)
  puts "Error: parameter #{name} must be positive and not zero." if value <= 0
  return value
end

#validate_positive_number_including_zero(name, value) ⇒ Object

validate positive number including zero

Parameters:

  • name (String)
  • value (Float)

Returns:

  • float



254
255
256
257
# File 'lib/buildingsync/model_articulation/spatial_element.rb', line 254

def validate_positive_number_including_zero(name, value)
  puts "Error: parameter #{name} must be positive or zero." if value < 0
  return value
end