honeybee-openstudio 2.21.1 → 2.22.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,44 @@
1
+ # *******************************************************************************
2
+ # Honeybee OpenStudio Gem, Copyright (c) 2020, Alliance for Sustainable
3
+ # Energy, LLC, Ladybug Tools LLC and other contributors. All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions are met:
7
+ #
8
+ # (1) Redistributions of source code must retain the above copyright notice,
9
+ # this list of conditions and the following disclaimer.
10
+ #
11
+ # (2) Redistributions in binary form must reproduce the above copyright notice,
12
+ # this list of conditions and the following disclaimer in the documentation
13
+ # and/or other materials provided with the distribution.
14
+ #
15
+ # (3) Neither the name of the copyright holder nor the names of any contributors
16
+ # may be used to endorse or promote products derived from this software without
17
+ # specific prior written permission from the respective party.
18
+ #
19
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS
20
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE
23
+ # UNITED STATES GOVERNMENT, OR THE UNITED STATES DEPARTMENT OF ENERGY, NOR ANY OF
24
+ # THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
26
+ # OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28
+ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29
+ # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
+ # *******************************************************************************
31
+
32
+ require 'honeybee/model_object'
33
+ require 'honeybee/construction/window'
34
+
35
+ module Honeybee
36
+ class WindowConstructionDynamicAbridged < ModelObject
37
+
38
+ def defaults
39
+ @@schema[:components][:schemas][:WindowConstructionDynamicAbridged][:properties]
40
+ nil
41
+ end
42
+
43
+ end #WindowConstructionDynamicAbridged
44
+ end #Honeybee
@@ -98,9 +98,14 @@ module Honeybee
98
98
  end
99
99
  end
100
100
 
101
+ # remove illegal characters in identifier
102
+ def self.clean_display_name(str)
103
+ str.gsub(/[^[:ascii:]]/, '')
104
+ end
105
+
101
106
  # remove illegal characters in identifier
102
107
  def self.clean_identifier(str)
103
- str.gsub(/[^.A-Za-z0-9_-] /, '_').gsub(' ', '_').gsub('{', '').gsub('}', '')
108
+ str.gsub(/[^.A-Za-z0-9_-]/, '_').gsub(' ', '_')
104
109
  end
105
110
 
106
111
 
data/lib/honeybee.rb CHANGED
@@ -53,6 +53,7 @@ require 'honeybee/hvac/template'
53
53
  require 'honeybee/construction/opaque'
54
54
  require 'honeybee/construction/window'
55
55
  require 'honeybee/construction/windowshade'
56
+ require 'honeybee/construction/dynamic'
56
57
  require 'honeybee/construction/shade'
57
58
  require 'honeybee/construction/air'
58
59
 
@@ -0,0 +1,198 @@
1
+ # *******************************************************************************
2
+ # Honeybee OpenStudio Gem, Copyright (c) 2020, Alliance for Sustainable
3
+ # Energy, LLC, Ladybug Tools LLC and other contributors. All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions are met:
7
+ #
8
+ # (1) Redistributions of source code must retain the above copyright notice,
9
+ # this list of conditions and the following disclaimer.
10
+ #
11
+ # (2) Redistributions in binary form must reproduce the above copyright notice,
12
+ # this list of conditions and the following disclaimer in the documentation
13
+ # and/or other materials provided with the distribution.
14
+ #
15
+ # (3) Neither the name of the copyright holder nor the names of any contributors
16
+ # may be used to endorse or promote products derived from this software without
17
+ # specific prior written permission from the respective party.
18
+ #
19
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS
20
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE
23
+ # UNITED STATES GOVERNMENT, OR THE UNITED STATES DEPARTMENT OF ENERGY, NOR ANY OF
24
+ # THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
26
+ # OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28
+ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29
+ # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
+ # *******************************************************************************
31
+
32
+ require 'honeybee/construction/dynamic'
33
+
34
+ require 'to_openstudio/model_object'
35
+
36
+ module Honeybee
37
+ class WindowConstructionDynamicAbridged
38
+
39
+ attr_reader :constructions, :schedule
40
+
41
+ @@program_manager = nil
42
+ @@sensor_count = 1
43
+ @@actuator_count = 1
44
+ @@program_count = 1
45
+ @@state_count = 1
46
+
47
+ def constructions
48
+ # wind constructions representing the dynamic states
49
+ @constructions
50
+ end
51
+
52
+ def sub_faces
53
+ # sub faces that have the construction assigned
54
+ @sub_faces
55
+ end
56
+
57
+ def replace_ems_special_characters(ems_variable_name)
58
+ # remove special characters from an name to be used as an EMS variable
59
+ new_name = ems_variable_name.to_s
60
+ new_name = new_name.dup # avoid the case of frozen strings
61
+ new_name.gsub!(/[^A-Za-z]/, '')
62
+ new_name
63
+ end
64
+
65
+ def get_program_manager(openstudio_model)
66
+ # get the EMS Program Manager for all window opening if it exists or generate it if it doesn't
67
+ if @@program_manager.nil?
68
+ @@program_manager = OpenStudio::Model::EnergyManagementSystemProgramCallingManager.new(
69
+ openstudio_model)
70
+ @@program_manager.setName('Dynamic_Window_Constructions')
71
+ @@program_manager.setCallingPoint('BeginTimestepBeforePredictor')
72
+ end
73
+ @@program_manager
74
+ end
75
+
76
+ def to_openstudio(openstudio_model)
77
+ # perform the initial translation of the constituient constructions to the opensutido model
78
+
79
+ # create an empty list that will collect the objects with the construciton assinged
80
+ @sub_faces = []
81
+
82
+ # write all versions of the window constructions into the model
83
+ @constructions = []
84
+ @hash[:constructions].each do |win_con|
85
+ constr_obj = WindowConstructionAbridged.new(win_con)
86
+ @constructions << constr_obj.to_openstudio(openstudio_model)
87
+ end
88
+
89
+ # set up the EMS sensor for the schedule value
90
+ state_sch = openstudio_model.getScheduleByName(@hash[:schedule])
91
+ @sch_sensor_name = replace_ems_special_characters(@hash[:identifier]) + '_Sensor' + @@sensor_count.to_s
92
+ @@sensor_count = @@sensor_count + 1
93
+ unless state_sch.empty? # schedule not specified
94
+ sch_var = OpenStudio::Model::OutputVariable.new('Schedule Value', openstudio_model)
95
+ sch_var.setReportingFrequency('Timestep')
96
+ sch_var.setKeyValue(@hash[:schedule])
97
+ sch_sens = OpenStudio::Model::EnergyManagementSystemSensor.new(openstudio_model, sch_var)
98
+ sch_sens.setName(@sch_sensor_name)
99
+ end
100
+ end
101
+
102
+ def ems_program_to_openstudio(openstudio_model)
103
+ # after adding sub-faces to the hash, write the EMS program that controls the sub-faces
104
+
105
+ # create the actuators for each of the dynamic windows
106
+ actuator_names = []
107
+ @sub_faces.each do |dyn_window|
108
+ window_act = OpenStudio::Model::EnergyManagementSystemActuator.new(
109
+ dyn_window, 'Surface', 'Construction State')
110
+ dyn_window_name = dyn_window.name
111
+ unless dyn_window_name.empty?
112
+ act_name = replace_ems_special_characters(dyn_window_name.get) + '_Actuator' + @@actuator_count.to_s
113
+ @@actuator_count = @@actuator_count + 1
114
+ window_act.setName(act_name)
115
+ actuator_names << act_name
116
+ end
117
+ end
118
+
119
+ # create the EMS Program to accout for each state according to the control logic
120
+ ems_program = OpenStudio::Model::EnergyManagementSystemProgram.new(openstudio_model)
121
+ prog_name = replace_ems_special_characters(@hash[:identifier]) + '_StateChange' + @@program_count.to_s
122
+ @@program_count = @@program_count + 1
123
+ ems_program.setName(prog_name)
124
+
125
+ # add each construction state to the program
126
+ max_state_count = @constructions.length() - 1
127
+ @constructions.each_with_index do |construction, i|
128
+ # determine which conditional operator to use
129
+ cond_op = 'IF'
130
+ if i != 0
131
+ cond_op = 'ELSEIF'
132
+ end
133
+
134
+ # add the conditional statement
135
+ state_count = i + 1
136
+ if i == max_state_count
137
+ cond_stmt = 'ELSE'
138
+ else
139
+ cond_stmt = cond_op + ' (' + @sch_sensor_name + ' < ' + state_count.to_s + ')'
140
+ end
141
+ ems_program.addLine(cond_stmt)
142
+
143
+ # create the construction index variable
144
+ constr_i = OpenStudio::Model::EnergyManagementSystemConstructionIndexVariable.new(
145
+ openstudio_model, construction)
146
+ constr_name = construction.name
147
+ unless constr_name.empty?
148
+ constr_i_name = replace_ems_special_characters(constr_name.get) + '_State' + @@state_count.to_s
149
+ @@state_count = @@state_count + 1
150
+ constr_i.setName(constr_i_name)
151
+ end
152
+
153
+ # loop through the actuators and set the appropriate window state
154
+ actuator_names.each do |act_name|
155
+ ems_program.addLine('SET ' + act_name + ' = ' + constr_i_name)
156
+ end
157
+ end
158
+ ems_program.addLine('ENDIF')
159
+
160
+ # add the program object the the global program manager for all window opening
161
+ prog_manager = get_program_manager(openstudio_model)
162
+ prog_manager.addProgram(ems_program)
163
+ end
164
+
165
+ def self.add_sub_faces_to_window_dynamic_hash(openstudio_model)
166
+ # loop through the model and add relevant sub_faces to the $window_dynamic_hash
167
+
168
+ # get the names of the constructions that would be assigned to the geometry
169
+ constr_names = Hash.new
170
+ $window_dynamic_hash.each do |constr_id, constr_obj|
171
+ first_constr = constr_obj.constructions[0]
172
+ first_constr_name_ref = first_constr.name
173
+ unless first_constr_name_ref.empty?
174
+ first_constr_name = first_constr_name_ref.get
175
+ constr_names[first_constr_name] = constr_id
176
+ end
177
+ end
178
+
179
+ # loop through the sub-faces and find any that have the construction assigned
180
+ sub_faces = openstudio_model.getSubSurfaces()
181
+ sub_faces.each do |sub_face|
182
+ constr_ref = sub_face.construction
183
+ unless constr_ref.empty?
184
+ constr = constr_ref.get
185
+ constr_name_ref = constr.name
186
+ unless constr_name_ref.empty?
187
+ constr_name = constr_name_ref.get
188
+ unless constr_names[constr_name].nil?
189
+ dyn_constr_name = constr_names[constr_name]
190
+ $window_dynamic_hash[dyn_constr_name].sub_faces << sub_face
191
+ end
192
+ end
193
+ end
194
+ end
195
+ end
196
+
197
+ end #WindowConstructionDynamicAbridged
198
+ end #Honeybee
@@ -127,37 +127,27 @@ module Honeybee
127
127
  # assign any constructions in the aperture set
128
128
  if @hash[:aperture_set]
129
129
  if @hash[:aperture_set][:interior_construction]
130
- int_ap_ref = openstudio_model.getConstructionByName(
131
- @hash[:aperture_set][:interior_construction])
132
- unless int_ap_ref.empty?
133
- interior_aperture = int_ap_ref.get
130
+ interior_aperture = get_window_construction(openstudio_model, @hash[:aperture_set][:interior_construction])
131
+ unless interior_aperture.nil?
134
132
  int_subsurf_const.setFixedWindowConstruction(interior_aperture)
135
133
  int_subsurf_const.setOperableWindowConstruction(interior_aperture)
136
134
  end
137
135
  end
138
136
  if @hash[:aperture_set][:window_construction]
139
- window_ref = openstudio_model.getConstructionByName(
140
- @hash[:aperture_set][:window_construction])
141
- unless window_ref.empty?
142
- window_aperture = window_ref.get
143
- #TODO: This looks wrong, it should be set to interior subsurface construction since
144
- #window_construction apertures have a surface boundary condition.
137
+ window_aperture = get_window_construction(openstudio_model, @hash[:aperture_set][:window_construction])
138
+ unless window_aperture.nil?
145
139
  ext_subsurf_const.setFixedWindowConstruction(window_aperture)
146
140
  end
147
141
  end
148
142
  if @hash[:aperture_set][:skylight_construction]
149
- skylight_ref = openstudio_model.getConstructionByName(
150
- @hash[:aperture_set][:skylight_construction])
151
- unless skylight_ref.empty?
152
- skylight_aperture = skylight_ref.get
143
+ skylight_aperture = get_window_construction(openstudio_model, @hash[:aperture_set][:skylight_construction])
144
+ unless skylight_aperture.nil?
153
145
  ext_subsurf_const.setSkylightConstruction(skylight_aperture)
154
146
  end
155
147
  end
156
148
  if @hash[:aperture_set][:operable_construction]
157
- operable_ref = openstudio_model.getConstructionByName(
158
- @hash[:aperture_set][:operable_construction])
159
- unless operable_ref.empty?
160
- operable_aperture = operable_ref.get
149
+ operable_aperture = get_window_construction(openstudio_model, @hash[:aperture_set][:operable_construction])
150
+ unless operable_aperture.nil?
161
151
  ext_subsurf_const.setOperableWindowConstruction(operable_aperture)
162
152
  end
163
153
  end
@@ -190,18 +180,14 @@ module Honeybee
190
180
  end
191
181
  end
192
182
  if @hash[:door_set][:exterior_glass_construction]
193
- ext_glz_door_ref = openstudio_model.getConstructionByName(
194
- @hash[:door_set][:exterior_glass_construction])
195
- unless ext_glz_door_ref.empty?
196
- exterior_glass_door = ext_glz_door_ref.get
183
+ exterior_glass_door = get_window_construction(openstudio_model, @hash[:door_set][:exterior_glass_construction])
184
+ unless exterior_glass_door.nil?
197
185
  ext_subsurf_const.setGlassDoorConstruction(exterior_glass_door)
198
186
  end
199
187
  end
200
188
  if @hash[:door_set][:interior_glass_construction]
201
- int_glz_door_ref = openstudio_model.getConstructionByName(
202
- @hash[:door_set][:interior_glass_construction])
203
- unless int_glz_door_ref.empty?
204
- interior_glass_door = int_glz_door_ref.get
189
+ interior_glass_door = get_window_construction(openstudio_model, @hash[:door_set][:interior_glass_construction])
190
+ unless interior_glass_door.nil?
205
191
  int_subsurf_const.setGlassDoorConstruction(interior_glass_door)
206
192
  end
207
193
  end
@@ -263,6 +249,19 @@ module Honeybee
263
249
  constr_subset.setRoofCeilingConstruction(constr_id)
264
250
  end
265
251
  end
252
+
253
+ # assign window construction with a check for dynamic constructions
254
+ def get_window_construction(openstudio_model, construction_identifier)
255
+ os_construction = nil
256
+ constr_ref = openstudio_model.getConstructionByName(construction_identifier)
257
+ if !constr_ref.empty?
258
+ os_construction = constr_ref.get
259
+ elsif !$window_dynamic_hash.nil? && !$window_dynamic_hash[construction_identifier].nil?
260
+ os_construction = $window_dynamic_hash[construction_identifier].constructions[0]
261
+ end
262
+
263
+ os_construction
264
+ end
266
265
 
267
266
  end #ConstructionSetAbridged
268
267
  end #Honeybee
@@ -118,9 +118,12 @@ module Honeybee
118
118
  if @hash[:properties][:energy][:construction]
119
119
  construction_identifier = @hash[:properties][:energy][:construction]
120
120
  construction = openstudio_model.getConstructionByName(construction_identifier)
121
- unless construction.empty?
121
+ if !construction.empty?
122
122
  os_construction = construction.get
123
123
  os_subsurface.setConstruction(os_construction)
124
+ elsif $window_dynamic_hash[construction_identifier] != nil
125
+ os_construction = $window_dynamic_hash[construction_identifier].constructions[0]
126
+ os_subsurface.setConstruction(os_construction)
124
127
  end
125
128
  end
126
129
 
@@ -118,9 +118,12 @@ module Honeybee
118
118
  if @hash[:properties][:energy][:construction]
119
119
  construction_identifier = @hash[:properties][:energy][:construction]
120
120
  construction = openstudio_model.getConstructionByName(construction_identifier)
121
- unless construction.empty?
121
+ if !construction.empty?
122
122
  os_construction = construction.get
123
123
  os_subsurface.setConstruction(os_construction)
124
+ elsif $window_dynamic_hash[construction_identifier] != nil
125
+ os_construction = $window_dynamic_hash[construction_identifier].constructions[0]
126
+ os_subsurface.setConstruction(os_construction)
124
127
  end
125
128
  end
126
129
 
@@ -77,7 +77,7 @@ module Honeybee
77
77
 
78
78
  # assign the flow exponent if it's specified
79
79
  if vent_crack[:flow_exponent]
80
- os_crack. setAirMassFlowExponent(vent_crack[:flow_exponent])
80
+ os_crack.setAirMassFlowExponent(vent_crack[:flow_exponent])
81
81
  end
82
82
 
83
83
  # if it's a Surface boundary condition ensure the neighbor is not written as a duplicate
@@ -349,8 +349,10 @@ module Honeybee
349
349
  if sub_f.adjacentSubSurface.empty? # not an interior window that's already in the AFN
350
350
  vent_open = VentilationOpening.new(opening)
351
351
  open_fac = vent_open.to_openstudio_afn(openstudio_model, sub_f)
352
- operable_subfs << sub_f
353
- opening_factors << open_fac
352
+ unless open_fac.nil? # nil is used for horizontal exterior skylights
353
+ operable_subfs << sub_f
354
+ opening_factors << open_fac
355
+ end
354
356
  end
355
357
  end
356
358
  end
@@ -122,6 +122,7 @@ module Honeybee
122
122
  $gas_gap_hash = Hash.new # hash to track gas gaps in case they are split by shades
123
123
  $air_boundary_hash = Hash.new # hash to track any air boundary constructions
124
124
  $window_shade_hash = Hash.new # hash to track any window constructions with shade
125
+ $window_dynamic_hash = Hash.new # hash to track any dynamic window constructions
125
126
  $programtype_shw_hash = Hash.new # hash to track ServiceHotWater objects
126
127
  $programtype_setpoint_hash = Hash.new # hash to track Setpoint objects
127
128
  $interior_afn_srf_hash = Hash.new # track whether an adjacent surface is already in the AFN
@@ -185,6 +186,13 @@ module Honeybee
185
186
  create_shading_control
186
187
  end
187
188
 
189
+ unless $window_dynamic_hash.empty?
190
+ if log_report
191
+ puts 'Translating Dynamic Windows'
192
+ end
193
+ create_dynamic_windows
194
+ end
195
+
188
196
  if log_report
189
197
  puts 'Translating HVAC Systems'
190
198
  end
@@ -266,6 +274,9 @@ module Honeybee
266
274
  when 'WindowConstructionShadeAbridged'
267
275
  construction_object = WindowConstructionShadeAbridged.new(construction)
268
276
  $window_shade_hash[construction[:identifier]] = construction_object
277
+ when 'WindowConstructionDynamicAbridged'
278
+ construction_object = WindowConstructionDynamicAbridged.new(construction)
279
+ $window_dynamic_hash[construction[:identifier]] = construction_object
269
280
  when 'ShadeConstruction'
270
281
  construction_object = ShadeConstruction.new(construction)
271
282
  when 'AirBoundaryConstructionAbridged'
@@ -502,6 +513,14 @@ module Honeybee
502
513
  end
503
514
  end
504
515
 
516
+ def create_dynamic_windows
517
+ # create the actuators and EMS program for any dynamic windows
518
+ WindowConstructionDynamicAbridged.add_sub_faces_to_window_dynamic_hash(@openstudio_model)
519
+ $window_dynamic_hash.each do |constr_id, constr_obj|
520
+ constr_obj.ems_program_to_openstudio(@openstudio_model)
521
+ end
522
+ end
523
+
505
524
  def create_hvacs
506
525
  if @hash[:properties][:energy][:hvacs]
507
526
  $air_loop_count = 0 # track the total number of air loops in the model