Class: BarAspectRatioStudy

Inherits:
OpenStudio::Measure::ModelMeasure
  • Object
show all
Defined in:
lib/measures/BarAspectRatioStudy/measure.rb

Overview

start the measure

Instance Method Summary collapse

Instance Method Details

#arguments(model) ⇒ Object

define the arguments that the user will input



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/measures/BarAspectRatioStudy/measure.rb', line 24

def arguments(model)
  args = OpenStudio::Measure::OSArgumentVector.new

  # make an argument for total floor area
  total_bldg_area_ip = OpenStudio::Measure::OSArgument.makeDoubleArgument('total_bldg_area_ip', true)
  total_bldg_area_ip.setDisplayName('Total Building Floor Area')
  total_bldg_area_ip.setUnits('ft^2')
  total_bldg_area_ip.setDefaultValue(10000.0)
  args << total_bldg_area_ip

  # make an argument for aspect ratio
  ns_to_ew_ratio = OpenStudio::Measure::OSArgument.makeDoubleArgument('ns_to_ew_ratio', true)
  ns_to_ew_ratio.setDisplayName('Ratio of North/South Facade Length Relative to East/West Facade Length.')
  ns_to_ew_ratio.setDefaultValue(2.0)
  args << ns_to_ew_ratio

  # make an argument for number of floors
  num_floors = OpenStudio::Measure::OSArgument.makeIntegerArgument('num_floors', true)
  num_floors.setDisplayName('Number of Floors.')
  num_floors.setDefaultValue(2)
  args << num_floors

  # make an argument for floor height
  floor_to_floor_height_ip = OpenStudio::Measure::OSArgument.makeDoubleArgument('floor_to_floor_height_ip', true)
  floor_to_floor_height_ip.setDisplayName('Floor to Floor Height')
  floor_to_floor_height_ip.setUnits('ft')
  floor_to_floor_height_ip.setDefaultValue(10.0)
  args << floor_to_floor_height_ip

  # make an argument to surface match
  surface_matching = OpenStudio::Measure::OSArgument.makeBoolArgument('surface_matching', true)
  surface_matching.setDisplayName('Surface Matching?')
  surface_matching.setDefaultValue(true)
  args << surface_matching

  # make an argument to create zones from spaces
  make_zones = OpenStudio::Measure::OSArgument.makeBoolArgument('make_zones', true)
  make_zones.setDisplayName('Make Thermal Zones from Spaces?')
  make_zones.setDefaultValue(true)
  args << make_zones

  return args
end

#nameObject

define the name that a user will see, this method may be deprecated as the display name in PAT comes from the name field in measure.xml



19
20
21
# File 'lib/measures/BarAspectRatioStudy/measure.rb', line 19

def name
  return 'Bar Aspect Ratio Study'
end

#neat_numbers(number, roundto = 2) ⇒ Object

helper to make numbers pretty (converts 4125001.25641 to 4,125,001.26 or 4,125,001). The definition be called through this measure. round to 0 or 2)



101
102
103
104
105
106
107
108
109
# File 'lib/measures/BarAspectRatioStudy/measure.rb', line 101

def neat_numbers(number, roundto = 2)
  if roundto == 2
    number = format '%.2f', number
  else
    number = number.round
  end
  # regex to add commas
  number.to_s.reverse.gsub(/([0-9]{3}(?=([0-9])))/, '\\1,').reverse
end

#run(model, runner, user_arguments) ⇒ Object

define what happens when the measure is run



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
138
139
140
141
142
143
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
179
180
181
182
183
184
185
186
187
188
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
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
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
# File 'lib/measures/BarAspectRatioStudy/measure.rb', line 69

def run(model, runner, user_arguments)
  super(model, runner, user_arguments)

  # use the built-in error checking
  if !runner.validateUserArguments(arguments(model), user_arguments)
    return false
  end

  # assign the user inputs to variables
  total_bldg_area_ip = runner.getDoubleArgumentValue('total_bldg_area_ip', user_arguments)
  ns_to_ew_ratio = runner.getDoubleArgumentValue('ns_to_ew_ratio', user_arguments)
  num_floors = runner.getIntegerArgumentValue('num_floors', user_arguments)
  floor_to_floor_height_ip = runner.getDoubleArgumentValue('floor_to_floor_height_ip', user_arguments)
  surface_matching = runner.getBoolArgumentValue('surface_matching', user_arguments)
  make_zones = runner.getBoolArgumentValue('make_zones', user_arguments)

  # test for positive inputs
  if total_bldg_area_ip <= 0
    runner.registerError('Enter a total building area greater than 0.')
  end
  if ns_to_ew_ratio <= 0
    runner.registerError('Enter ratio grater than 0.')
  end
  if num_floors <= 0
    runner.registerError('Enter a number of stories 1 or greater.')
  end
  if floor_to_floor_height_ip <= 0
    runner.registerError('Enter a positive floor height.')
  end

  # helper to make numbers pretty (converts 4125001.25641 to 4,125,001.26 or 4,125,001). The definition be called through this measure.
  # round to 0 or 2)
  def neat_numbers(number, roundto = 2)
    if roundto == 2
      number = format '%.2f', number
    else
      number = number.round
    end
    # regex to add commas
    number.to_s.reverse.gsub(/([0-9]{3}(?=([0-9])))/, '\\1,').reverse
  end

  # helper to make it easier to do unit conversions on the fly.  The definition be called through this measure.
  def unit_helper(number, from_unit_string, to_unit_string)
    converted_number = OpenStudio.convert(OpenStudio::Quantity.new(number, OpenStudio.createUnit(from_unit_string).get), OpenStudio.createUnit(to_unit_string).get).get.value
  end

  # calculate needed variables
  footprint_ip = total_bldg_area_ip / num_floors
  footprint_si = unit_helper(footprint_ip, 'ft^2', 'm^2')
  floor_to_floor_height = unit_helper(floor_to_floor_height_ip, 'ft', 'm')

  # variables from original rectangle script not exposed in this measure
  width = Math.sqrt(footprint_si / ns_to_ew_ratio)
  length = footprint_si / width
  plenum_height = 0 # this doesn't look like it is used anywhere

  # determine if core and perimeter zoning can be used
  if (length > 10) && (width > 10)
    perimeter_zone_depth = 4.57 # hard coded in meters
  else
    perimeter_zone_depth = 0 # if any size is to small then just model floor as single zone, issue warning
    runner.registerWarning('Due to the size of the building modeling each floor as a single zone.')
  end

  # reporting initial condition of model
  starting_spaces = model.getSpaces
  runner.registerInitialCondition("The building started with #{starting_spaces.size} spaces.")

  # Loop through the number of floors
  for floor in (0..num_floors - 1)

    z = floor_to_floor_height * floor

    # Create a new story within the building
    story = OpenStudio::Model::BuildingStory.new(model)
    story.setNominalFloortoFloorHeight(floor_to_floor_height)
    story.setName("Story #{floor + 1}")

    nw_point = OpenStudio::Point3d.new(0, width, z)
    ne_point = OpenStudio::Point3d.new(length, width, z)
    se_point = OpenStudio::Point3d.new(length, 0, z)
    sw_point = OpenStudio::Point3d.new(0, 0, z)

    # Identity matrix for setting space origins
    m = OpenStudio::Matrix.new(4, 4, 0)
    m[0, 0] = 1
    m[1, 1] = 1
    m[2, 2] = 1
    m[3, 3] = 1

    # Define polygons for a rectangular building
    if perimeter_zone_depth > 0
      perimeter_nw_point = nw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, -perimeter_zone_depth, 0)
      perimeter_ne_point = ne_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, -perimeter_zone_depth, 0)
      perimeter_se_point = se_point + OpenStudio::Vector3d.new(-perimeter_zone_depth, perimeter_zone_depth, 0)
      perimeter_sw_point = sw_point + OpenStudio::Vector3d.new(perimeter_zone_depth, perimeter_zone_depth, 0)

      west_polygon = OpenStudio::Point3dVector.new
      west_polygon << sw_point
      west_polygon << nw_point
      west_polygon << perimeter_nw_point
      west_polygon << perimeter_sw_point
      west_space = OpenStudio::Model::Space.fromFloorPrint(west_polygon, floor_to_floor_height, model)
      west_space = west_space.get
      m[0, 3] = sw_point.x
      m[1, 3] = sw_point.y
      m[2, 3] = sw_point.z
      west_space.changeTransformation(OpenStudio::Transformation.new(m))
      west_space.setBuildingStory(story)
      west_space.setName("Story #{floor + 1} West Perimeter Space")

      north_polygon = OpenStudio::Point3dVector.new
      north_polygon << nw_point
      north_polygon << ne_point
      north_polygon << perimeter_ne_point
      north_polygon << perimeter_nw_point
      north_space = OpenStudio::Model::Space.fromFloorPrint(north_polygon, floor_to_floor_height, model)
      north_space = north_space.get
      m[0, 3] = perimeter_nw_point.x
      m[1, 3] = perimeter_nw_point.y
      m[2, 3] = perimeter_nw_point.z
      north_space.changeTransformation(OpenStudio::Transformation.new(m))
      north_space.setBuildingStory(story)
      north_space.setName("Story #{floor + 1} North Perimeter Space")

      east_polygon = OpenStudio::Point3dVector.new
      east_polygon << ne_point
      east_polygon << se_point
      east_polygon << perimeter_se_point
      east_polygon << perimeter_ne_point
      east_space = OpenStudio::Model::Space.fromFloorPrint(east_polygon, floor_to_floor_height, model)
      east_space = east_space.get
      m[0, 3] = perimeter_se_point.x
      m[1, 3] = perimeter_se_point.y
      m[2, 3] = perimeter_se_point.z
      east_space.changeTransformation(OpenStudio::Transformation.new(m))
      east_space.setBuildingStory(story)
      east_space.setName("Story #{floor + 1} East Perimeter Space")

      south_polygon = OpenStudio::Point3dVector.new
      south_polygon << se_point
      south_polygon << sw_point
      south_polygon << perimeter_sw_point
      south_polygon << perimeter_se_point
      south_space = OpenStudio::Model::Space.fromFloorPrint(south_polygon, floor_to_floor_height, model)
      south_space = south_space.get
      m[0, 3] = sw_point.x
      m[1, 3] = sw_point.y
      m[2, 3] = sw_point.z
      south_space.changeTransformation(OpenStudio::Transformation.new(m))
      south_space.setBuildingStory(story)
      south_space.setName("Story #{floor + 1} South Perimeter Space")

      core_polygon = OpenStudio::Point3dVector.new
      core_polygon << perimeter_sw_point
      core_polygon << perimeter_nw_point
      core_polygon << perimeter_ne_point
      core_polygon << perimeter_se_point
      core_space = OpenStudio::Model::Space.fromFloorPrint(core_polygon, floor_to_floor_height, model)
      core_space = core_space.get
      m[0, 3] = perimeter_sw_point.x
      m[1, 3] = perimeter_sw_point.y
      m[2, 3] = perimeter_sw_point.z
      core_space.changeTransformation(OpenStudio::Transformation.new(m))
      core_space.setBuildingStory(story)
      core_space.setName("Story #{floor + 1} Core Space")

      # Minimal zones
    else
      core_polygon = OpenStudio::Point3dVector.new
      core_polygon << sw_point
      core_polygon << nw_point
      core_polygon << ne_point
      core_polygon << se_point
      core_space = OpenStudio::Model::Space.fromFloorPrint(core_polygon, floor_to_floor_height, model)
      core_space = core_space.get
      m[0, 3] = sw_point.x
      m[1, 3] = sw_point.y
      m[2, 3] = sw_point.z
      core_space.changeTransformation(OpenStudio::Transformation.new(m))
      core_space.setBuildingStory(story)
      core_space.setName("Story #{floor + 1} Core Space")

    end

    # Set vertical story position
    story.setNominalZCoordinate(z)

  end

  # put all of the spaces in the model into a vector
  spaces = OpenStudio::Model::SpaceVector.new
  model.getSpaces.each do |space|
    spaces << space
    if make_zones
      # create zones
      new_zone = OpenStudio::Model::ThermalZone.new(model)
      space.setThermalZone(new_zone)
      zone_name = space.name.get.gsub('Space', 'Zone')
      new_zone.setName(zone_name)
    end
  end

  if surface_matching
    # match surfaces for each space in the vector
    OpenStudio::Model.matchSurfaces(spaces)
  end

  # reporting final condition of model
  finishing_spaces = model.getSpaces
  runner.registerFinalCondition("The building finished with #{finishing_spaces.size} spaces.")

  return true
end

#unit_helper(number, from_unit_string, to_unit_string) ⇒ Object

helper to make it easier to do unit conversions on the fly. The definition be called through this measure.



112
113
114
# File 'lib/measures/BarAspectRatioStudy/measure.rb', line 112

def unit_helper(number, from_unit_string, to_unit_string)
  converted_number = OpenStudio.convert(OpenStudio::Quantity.new(number, OpenStudio.createUnit(from_unit_string).get), OpenStudio.createUnit(to_unit_string).get).get.value
end