Class: MergeFloorspaceJsWithModel

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

Overview

start the measure

Instance Method Summary collapse

Instance Method Details

#arguments(model) ⇒ Object

define the arguments that the user will input



32
33
34
35
36
37
38
39
40
41
42
# File 'lib/measures/merge_floorspace_js_with_model/measure.rb', line 32

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

  # path to the floorplan JSON file to load
  floorplan_path = OpenStudio::Measure::OSArgument.makeStringArgument('floorplan_path', true)
  floorplan_path.setDisplayName('Floorplan Path')
  floorplan_path.setDescription('Path to the floorplan JSON.')
  args << floorplan_path

  return args
end

#descriptionObject

human readable description



22
23
24
# File 'lib/measures/merge_floorspace_js_with_model/measure.rb', line 22

def description
  return 'This measure will import a FloorspacJS JSON file into an OpenStudio model. This is meant to function in similar way to the merge function in the geometry editor of the OpenStudio Applicaiton.'
end

#modeler_descriptionObject

human readable description of modeling approach



27
28
29
# File 'lib/measures/merge_floorspace_js_with_model/measure.rb', line 27

def modeler_description
  return 'This measure is based off of the ResidentialGeometryCreateFromFloorspaceJS measure on the OpenStudio-Buildstock repository'
end

#nameObject

human readable name



16
17
18
19
# File 'lib/measures/merge_floorspace_js_with_model/measure.rb', line 16

def name
  # Measure name should be the title case of the class name.
  return 'Merge FloorspaceJs with Model'
end

#run(model, runner, user_arguments) ⇒ Object

define what happens when the measure is run



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
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
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
# File 'lib/measures/merge_floorspace_js_with_model/measure.rb', line 45

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
  floorplan_path = runner.getStringArgumentValue('floorplan_path', user_arguments)

  # check the floorplan_path for reasonableness
  if floorplan_path.empty?
    runner.registerError('Empty floorplan path was entered.')
    return false
  end

  path = runner.workflow.findFile(floorplan_path)
  if path.empty?
    runner.registerError("Cannot find floorplan path '#{floorplan_path}'.")
    return false
  end

  json = nil
  File.open(path.get.to_s, 'r') do |file|
    json = file.read
  end

  floorplan = OpenStudio::FloorplanJS.load(json)
  if floorplan.empty?
    runner.registerError("Cannot load floorplan from '#{floorplan_path}'.")
    return false
  end

  # scene = floorplan.get.toThreeScene(true)
  # rt = OpenStudio::Model::ThreeJSReverseTranslator.new
  # new_model = rt.modelFromThreeJS(scene

  rt = OpenStudio::Model::FloorspaceReverseTranslator.new
  new_model = rt.modelFromFloorspace(json)

  unless new_model.is_initialized
    runner.registerError('Cannot convert floorplan to model.')
    return false
  end
  new_model = new_model.get
  runner.registerInfo("Model from FloorSpaceJS has #{new_model.getPlanarSurfaceGroups.size} planar surface groups.")
  puts "hello"
  puts new_model

  runner.registerInitialCondition("Initial model has #{model.getPlanarSurfaceGroups.size} planar surface groups.")

  mm = OpenStudio::Model::ModelMerger.new
  mm.mergeModels(model, new_model, rt.handleMapping)

  mm.warnings.each do |warnings|
    runner.registerWarning(warnings.logMessage)
  end

  # put all of the spaces in the model into a vector
  spaces = OpenStudio::Model::SpaceVector.new
  model.getSpaces.each do |space|
    spaces << space
  end

  # intersect and match surfaces for each space in the vector
  # todo - add in diagnostic intersect as option
  # OpenStudio::Model.intersectSurfaces(spaces)
  # OpenStudio::Model.matchSurfaces(spaces)

  # removing duplicate points in a surface
  model.getPlanarSurfaces.each do |surface|
    array = []
    vertices = surface.vertices
    fixed = false
    vertices.each do |vertex|
      next if fixed

      if array.include?(vertex)
        # create a new set of vertices
        new_vertices = OpenStudio::Point3dVector.new
        array_b = []
        surface.vertices.each do |vertex_b|
          next if array_b.include?(vertex_b)

          new_vertices << vertex_b
          array_b << vertex_b
        end
        surface.setVertices(new_vertices)
        num_removed = vertices.size - surface.vertices.size
        runner.registerWarning("#{surface.name} has duplicate vertices. Started with #{vertices.size} vertices, removed #{num_removed}.")
        fixed = true
      else
        array << vertex
      end
    end
  end

  # remove collinear points in a surface
  model.getPlanarSurfaces.each do |surface|
    new_vertices = OpenStudio.removeCollinear(surface.vertices)
    starting_count = surface.vertices.size
    final_count = new_vertices.size
    if final_count < starting_count
      runner.registerWarning("Removing #{starting_count - final_count} collinear vertices from #{surface.name}.")
      surface.setVertices(new_vertices)
    end
  end

  # remove duplicate surfaces in a space (should be done after remove duplicate and collinear points)
  model.getSpaces.each do |space|
    # secondary array to compare against
    surfaces_b = space.surfaces.sort

    space.surfaces.sort.each do |surface_a|
      # delete from secondary array
      surfaces_b.delete(surface_a)

      surfaces_b.each do |surface_b|
        next if surface_a == surface_b # dont' test against same surface

        if surface_a.equalVertices(surface_b)
          runner.registerWarning("#{surface_a.name} and #{surface_b.name} in #{space.name} have duplicate geometry, removing #{surface_b.name}.")
          surface_b.remove
        elsif surface_a.reverseEqualVertices(surface_b)
          # TODO: - add logic to determine which face naormal is reversed and which is correct
          runner.registerWarning("#{surface_a.name} and #{surface_b.name} in #{space.name} have reversed geometry, removing #{surface_b.name}.")
          surface_b.remove
        end
      end
    end
  end

  # secondary array of spaces that we can remove items from once they have gone through in primary loop
  spaces_b = model.getSpaces.sort

  # looping through vector of each space
  model.getSpaces.sort.each do |space_a|
    runner.registerInfo("Intersecting and matching surfaces for #{space_a.name}.")

    # delete from secondary array
    spaces_b.delete(space_a)

    spaces_b.each do |space_b|
      # runner.registerInfo("Intersecting and matching surfaces between #{space_a.name} and #{space.name}")
      spaces = OpenStudio::Model::SpaceVector.new
      spaces << space_a
      spaces << space_b

      # intersect and match surfaces in pair of spaces
      OpenStudio::Model.intersectSurfaces(spaces)
      OpenStudio::Model.matchSurfaces(spaces)
    end
  end

  json = JSON.parse(File.read(path.get.to_s))

  # error checking
  if json['space_types'].empty?
    runner.registerInfo('No space types were created.')
  end

  # set the space type standards fields based on what user wrote in the editor
  json['space_types'].each do |st|
    model.getSpaceTypes.each do |space_type|
      next unless space_type.name.to_s.include? st['name']
      next if space_type.standardsSpaceType.is_initialized

      space_type.setStandardsSpaceType(st['name'])
    end
  end

  # remove any unused space types
  model.getSpaceTypes.each do |space_type|
    if space_type.spaces.empty?
      space_type.remove
    end
  end

  # for any spaces with no assigned zone, create (unless another space of the same space type has an assigned zone) a thermal zone based on the space type
  # todo - add argument to enable disable zone creation
  model.getSpaceTypes.each do |space_type|
    space_type.spaces.each do |space|
      unless space.thermalZone.is_initialized
        thermal_zone = OpenStudio::Model::ThermalZone.new(model)
        thermal_zone.setName(space.name.to_s)
        space.setThermalZone(thermal_zone)
      end
    end
  end

  # report final condition of model
  runner.registerFinalCondition("Final model has #{model.getPlanarSurfaceGroups.size} planar surface groups.")

  return true
end