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 = nil
  @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.



341
342
343
# File 'lib/buildingsync/model_articulation/spatial_element.rb', line 341

def space_types
  @space_types
end

#standards_building_typeObject (readonly)

Returns the value of attribute standards_building_type.



341
342
343
# File 'lib/buildingsync/model_articulation/spatial_element.rb', line 341

def standards_building_type
  @standards_building_type
end

#system_typeObject (readonly)

Returns the value of attribute system_type.



341
342
343
# File 'lib/buildingsync/model_articulation/spatial_element.rb', line 341

def system_type
  @system_type
end

#total_floor_areaObject (readonly)

Returns the value of attribute total_floor_area.



341
342
343
# File 'lib/buildingsync/model_articulation/spatial_element.rb', line 341

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)


309
310
311
312
313
314
315
316
317
318
319
320
321
322
# File 'lib/buildingsync/model_articulation/spatial_element.rb', line 309

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



256
257
258
259
260
261
262
263
264
265
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
# File 'lib/buildingsync/model_articulation/spatial_element.rb', line 256

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



325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
# File 'lib/buildingsync/model_articulation/spatial_element.rb', line 325

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)


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
# File 'lib/buildingsync/model_articulation/spatial_element.rb', line 191

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}"
  min_floor_area_correct = false
  max_floor_area_correct = false
  building_and_system_types[:"#{occupancy_classification}"]&.each do |occ_type|
    if !occ_type[:standards_building_type].nil?
      if occ_type[:min_floor_area] || occ_type[:max_floor_area]
        if occ_type[:min_floor_area] && occ_type[:min_floor_area].to_f < total_floor_area
          min_floor_area_correct = true
        end
        if occ_type[:max_floor_area] && occ_type[:max_floor_area].to_f > total_floor_area
          max_floor_area_correct = true
        end
        if (min_floor_area_correct && max_floor_area_correct) || (!occ_type[:min_floor_area] && max_floor_area_correct) || (min_floor_area_correct && !occ_type[:max_floor_area])
          puts "selected the following standards_building_type: #{occ_type[:standards_building_type]}"
          return sets_occupancy_bldg_system_types(occ_type)
        end
      elsif occ_type[:min_number_floors] || occ_type[:max_number_floors]
        if occ_type[:min_number_floors] && occ_type[:min_number_floors].to_i <= total_number_floors
          puts "selected the following standards_building_type: #{occ_type[:standards_building_type]}"
          return sets_occupancy_bldg_system_types(occ_type)
        elsif occ_type[:max_number_floors] && occ_type[:max_number_floors].to_i > total_number_floors
          puts "selected the following standards_building_type: #{occ_type[:standards_building_type]}"
          return sets_occupancy_bldg_system_types(occ_type)
        end
      else
        # otherwise we assume the first one is correct and we select this
        puts "selected the following standards_building_type: #{occ_type[:standards_building_type]}"
        return sets_occupancy_bldg_system_types(occ_type)
      end
    else
      # otherwise we assume the first one is correct and we select this
      return sets_occupancy_bldg_system_types(occ_type)
    end
  end
  raise "BuildingSync Occupancy type #{occupancy_classification} is not available in the building_and_system_types.json dictionary"
  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
142
143
# 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 == 'Heated and Cooled'
      @conditioned_floor_area_heated_cooled = OpenStudio.convert(validate_positive_number_excluding_zero('@heated_and_cooled_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'
      @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)


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

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)


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

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



235
236
237
238
# File 'lib/buildingsync/model_articulation/spatial_element.rb', line 235

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



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

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