honeybee-openstudio 2.21.1 → 2.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
@@ -100,7 +100,7 @@ module Honeybee
100
100
 
101
101
  # remove illegal characters in identifier
102
102
  def self.clean_identifier(str)
103
- str.gsub(/[^.A-Za-z0-9_-] /, '_').gsub(' ', '_').gsub('{', '').gsub('}', '')
103
+ str.gsub(/[^.A-Za-z0-9_-]/, '_').gsub(' ', '_')
104
104
  end
105
105
 
106
106
 
data/lib/to_openstudio.rb CHANGED
@@ -53,6 +53,7 @@ require 'to_openstudio/hvac/template'
53
53
  require 'to_openstudio/construction/opaque'
54
54
  require 'to_openstudio/construction/window'
55
55
  require 'to_openstudio/construction/windowshade'
56
+ require 'to_openstudio/construction/dynamic'
56
57
  require 'to_openstudio/construction/shade'
57
58
  require 'to_openstudio/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
 
@@ -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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: honeybee-openstudio
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.21.1
4
+ version: 2.22.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tanushree Charan
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: exe
13
13
  cert_chain: []
14
- date: 2021-07-27 00:00:00.000000000 Z
14
+ date: 2021-07-30 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: bundler
@@ -195,6 +195,7 @@ files:
195
195
  - lib/honeybee/_defaults/model.json
196
196
  - lib/honeybee/_defaults/simulation-parameter.json
197
197
  - lib/honeybee/construction/air.rb
198
+ - lib/honeybee/construction/dynamic.rb
198
199
  - lib/honeybee/construction/opaque.rb
199
200
  - lib/honeybee/construction/shade.rb
200
201
  - lib/honeybee/construction/window.rb
@@ -273,6 +274,7 @@ files:
273
274
  - lib/measures/from_openstudio_model/tests/from_openstudio_model_test.rb
274
275
  - lib/to_openstudio.rb
275
276
  - lib/to_openstudio/construction/air.rb
277
+ - lib/to_openstudio/construction/dynamic.rb
276
278
  - lib/to_openstudio/construction/opaque.rb
277
279
  - lib/to_openstudio/construction/shade.rb
278
280
  - lib/to_openstudio/construction/window.rb