honeybee-openstudio 2.5.1 → 2.6.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -49,6 +49,10 @@ module FromHoneybee
49
49
  @@schema[:components][:schemas][:FaceEnergyPropertiesAbridged][:properties]
50
50
  end
51
51
 
52
+ def crack_defaults
53
+ @@schema[:components][:schemas][:AFNCrack][:properties]
54
+ end
55
+
52
56
  def find_existing_openstudio_object(openstudio_model)
53
57
  model_surf = openstudio_model.getSurfaceByName(@hash[:identifier])
54
58
  return model_surf.get unless model_surf.empty?
@@ -56,16 +60,18 @@ module FromHoneybee
56
60
  end
57
61
 
58
62
  def to_openstudio(openstudio_model)
59
- # create the openstudio surface
63
+ # reorder the vertices to ensure they start from the upper-left corner
60
64
  os_vertices = OpenStudio::Point3dVector.new
61
65
  @hash[:geometry][:boundary].each do |vertex|
62
66
  os_vertices << OpenStudio::Point3d.new(vertex[0], vertex[1], vertex[2])
63
67
  end
64
68
  reordered_vertices = OpenStudio.reorderULC(os_vertices)
65
69
 
70
+ # create the openstudio surface and assign the type
66
71
  os_surface = OpenStudio::Model::Surface.new(reordered_vertices, openstudio_model)
67
72
  os_surface.setName(@hash[:identifier])
68
73
  os_surface.setSurfaceType(@hash[:face_type])
74
+
69
75
  # assign the construction if it is present
70
76
  if @hash[:properties][:energy][:construction]
71
77
  construction_identifier = @hash[:properties][:energy][:construction]
@@ -76,6 +82,32 @@ module FromHoneybee
76
82
  end
77
83
  end
78
84
 
85
+ # assign the AFN crack if it's specified and we are not using simple infiltration
86
+ if !$use_simple_vent && @hash[:properties][:energy][:vent_crack]
87
+ unless $interior_afn_srf_hash[@hash[:identifier]] # interior crack that's been accounted for
88
+ vent_crack = @hash[:properties][:energy][:vent_crack]
89
+ # create the crack object for using default values
90
+ flow_exponent = crack_defaults[:flow_exponent][:default].to_f
91
+ os_crack = OpenStudio::Model::AirflowNetworkCrack.new(
92
+ openstudio_model, vent_crack[:flow_coefficient], flow_exponent,
93
+ $afn_reference_crack)
94
+
95
+ # assign the flow exponent if it's specified
96
+ if vent_crack[:flow_exponent]
97
+ os_crack. setAirMassFlowExponent(vent_crack[:flow_exponent])
98
+ end
99
+
100
+ # if it's a Surface boundary condition ensure the neighbor is not written as a duplicate
101
+ if @hash[:boundary_condition][:type] == 'Surface'
102
+ $interior_afn_srf_hash[@hash[:boundary_condition][:boundary_condition_objects][0]] = true
103
+ end
104
+
105
+ # create the AirflowNetworkSurface
106
+ os_afn_srf = os_surface.getAirflowNetworkSurface(os_crack)
107
+
108
+ end
109
+ end
110
+
79
111
  # assign the boundary condition
80
112
  boundary_condition = (@hash[:boundary_condition][:type])
81
113
  case boundary_condition
@@ -43,6 +43,7 @@ require 'from_honeybee/load/ventilation'
43
43
  require 'from_honeybee/load/setpoint_thermostat'
44
44
  require 'from_honeybee/load/setpoint_humidistat'
45
45
  require 'from_honeybee/ventcool/opening'
46
+ require 'from_honeybee/ventcool/control'
46
47
 
47
48
  require 'openstudio'
48
49
 
@@ -139,7 +140,8 @@ module FromHoneybee
139
140
  if face[:apertures]
140
141
  face[:apertures].each do |aperture|
141
142
  if aperture[:properties][:energy][:vent_opening]
142
- window_vent[aperture[:identifier]] = aperture[:properties][:energy][:vent_opening]
143
+ window_vent[aperture[:identifier]] = \
144
+ [aperture[:properties][:energy][:vent_opening], aperture[:boundary_condition][:type]]
143
145
  end
144
146
  if aperture[:outdoor_shades]
145
147
  unless os_shd_group
@@ -156,7 +158,8 @@ module FromHoneybee
156
158
  if face[:doors]
157
159
  face[:doors].each do |door|
158
160
  if door[:properties][:energy][:vent_opening]
159
- window_vent[door[:identifier]] = door[:properties][:energy][:vent_opening]
161
+ window_vent[door[:identifier]] = \
162
+ [door[:properties][:energy][:vent_opening], door[:boundary_condition][:type]]
160
163
  end
161
164
  if door[:outdoor_shades]
162
165
  unless os_shd_group
@@ -185,17 +188,19 @@ module FromHoneybee
185
188
  os_surface.setConstruction(air_construction)
186
189
  end
187
190
  # add air mixing properties to the global list that tracks them
188
- air_hash = $air_boundary_hash[air_construction.name.to_s]
189
- if air_hash[:air_mixing_per_area]
190
- air_mix_area = air_hash[:air_mixing_per_area]
191
- else
192
- air_default = @@schema[:components][:schemas][:AirBoundaryConstructionAbridged]
193
- air_mix_area = air_default[:properties][:air_mixing_per_area][:default]
191
+ if $use_simple_vent # only use air mixing objects when simple ventilation is requested
192
+ air_hash = $air_boundary_hash[air_construction.name.to_s]
193
+ if air_hash[:air_mixing_per_area]
194
+ air_mix_area = air_hash[:air_mixing_per_area]
195
+ else
196
+ air_default = @@schema[:components][:schemas][:AirBoundaryConstructionAbridged]
197
+ air_mix_area = air_default[:properties][:air_mixing_per_area][:default]
198
+ end
199
+ flow_rate = os_surface.netArea * air_mix_area
200
+ flow_sch_id = air_hash[:air_mixing_schedule]
201
+ adj_zone_id = face[:boundary_condition][:boundary_condition_objects][-1]
202
+ $air_mxing_array << [os_thermal_zone, flow_rate, flow_sch_id, adj_zone_id]
194
203
  end
195
- flow_rate = os_surface.netArea * air_mix_area
196
- flow_sch_id = air_hash[:air_mixing_schedule]
197
- adj_zone_id = face[:boundary_condition][:boundary_condition_objects][-1]
198
- $air_mxing_array << [os_thermal_zone, flow_rate, flow_sch_id, adj_zone_id]
199
204
  end
200
205
  end
201
206
  end
@@ -265,7 +270,7 @@ module FromHoneybee
265
270
  end
266
271
 
267
272
  # assign infiltration if it exists
268
- if @hash[:properties][:energy][:infiltration]
273
+ if @hash[:properties][:energy][:infiltration] && $use_simple_vent # only use infiltration with simple ventilation
269
274
  infiltration = openstudio_model.getSpaceInfiltrationDesignFlowRateByName(
270
275
  @hash[:properties][:energy][:infiltration][:identifier])
271
276
  unless infiltration.empty?
@@ -308,17 +313,47 @@ module FromHoneybee
308
313
  end
309
314
 
310
315
  # assign window ventilation objects if they exist
311
- unless window_vent.empty?
312
- window_vent.each do |sub_f_id, opening|
316
+ if $use_simple_vent && !window_vent.empty? # write simple WindAndStack ventilation
317
+ window_vent.each do |sub_f_id, open_prop|
318
+ opening = open_prop[0]
319
+ bc = open_prop[1]
320
+ if bc == 'Outdoors'
321
+ opt_sub_f = openstudio_model.getSubSurfaceByName(sub_f_id)
322
+ unless opt_sub_f.empty?
323
+ sub_f = opt_sub_f.get
324
+ vent_open = VentilationOpening.new(opening)
325
+ os_vent_open = vent_open.to_openstudio(
326
+ openstudio_model, sub_f, @hash[:properties][:energy][:window_vent_control])
327
+ os_vent_open.addToThermalZone(os_thermal_zone)
328
+ end
329
+ end
330
+ end
331
+ elsif !$use_simple_vent # we're using the AFN!
332
+ # write an AirflowNetworkZone object in for the Room
333
+ os_afn_room_node = os_thermal_zone.getAirflowNetworkZone
334
+ os_afn_room_node.setVentilationControlMode('NoVent')
335
+ # write the opening objects for each Aperture / Door
336
+ operable_subfs = [] # collect the sub-face objects for the EMS
337
+ opening_factors = [] # collect the maximum opening factors for the EMS
338
+ window_vent.each do |sub_f_id, open_prop|
339
+ opening = open_prop[0]
313
340
  opt_sub_f = openstudio_model.getSubSurfaceByName(sub_f_id)
314
341
  unless opt_sub_f.empty?
315
342
  sub_f = opt_sub_f.get
316
- window_vent = VentilationOpening.new(opening)
317
- os_window_vent = window_vent.to_openstudio(
318
- openstudio_model, sub_f, @hash[:properties][:energy][:window_vent_control])
319
- os_window_vent.addToThermalZone(os_thermal_zone)
343
+ if sub_f.adjacentSubSurface.empty? # not an interior window that's already in the AFN
344
+ vent_open = VentilationOpening.new(opening)
345
+ open_fac = vent_open.to_openstudio_afn(openstudio_model, sub_f)
346
+ operable_subfs << sub_f
347
+ opening_factors << open_fac
348
+ end
320
349
  end
321
350
  end
351
+ # add the control startegy of the ventilation openings using the EMS
352
+ if @hash[:properties][:energy][:window_vent_control]
353
+ vent_control = VentilationControl.new(@hash[:properties][:energy][:window_vent_control])
354
+ vent_control.to_openstudio(
355
+ openstudio_model, os_thermal_zone, operable_subfs, opening_factors)
356
+ end
322
357
  end
323
358
 
324
359
  os_space
@@ -60,7 +60,7 @@ class OpenStudio::Model::Model
60
60
  standard.model_add_hvac_system(self, 'Baseboards', ht = 'NaturalGas', znht = nil, cl = nil, heated_zones)
61
61
 
62
62
  when 'Baseboard central air source heat pump'
63
- standard.model_add_hvac_system(self, 'Baseboards', ht = 'AirSourceHeatPump', znht = nil, cl = nil, heated_only_zones)
63
+ standard.model_add_hvac_system(self, 'Baseboards', ht = 'AirSourceHeatPump', znht = nil, cl = nil, heated_zones)
64
64
 
65
65
  when 'Baseboard district hot water'
66
66
  standard.model_add_hvac_system(self, 'Baseboards', ht = 'DistrictHeating', znht = nil, cl = nil, heated_zones)
@@ -75,6 +75,9 @@ require 'from_honeybee/schedule/ruleset'
75
75
  require 'from_honeybee/load/setpoint_thermostat'
76
76
  require 'from_honeybee/load/setpoint_humidistat'
77
77
 
78
+ # import the ventilative cooling objects
79
+ require 'from_honeybee/ventcool/simulation'
80
+
78
81
  require 'openstudio'
79
82
 
80
83
 
@@ -158,11 +161,23 @@ module FromHoneybee
158
161
  building = @openstudio_model.getBuilding
159
162
  building.setStandardsBuildingType('MediumOffice')
160
163
 
164
+ # initialize a global variable for whether the AFN is used instead of simple ventilation
165
+ $use_simple_vent = true
166
+ if @hash[:properties][:energy][:ventilation_simulation_control]
167
+ vent_sim_control = @hash[:properties][:energy][:ventilation_simulation_control]
168
+ if vent_sim_control[:vent_control_type] && vent_sim_control[:vent_control_type] != 'SingleZone'
169
+ $use_simple_vent = false
170
+ vsim_cntrl = VentilationSimulationControl.new(vent_sim_control)
171
+ $afn_reference_crack = vsim_cntrl.to_openstudio(@openstudio_model)
172
+ end
173
+ end
174
+
161
175
  # initialize global hashes for various model properties
162
176
  $gas_gap_hash = Hash.new # hash to track gas gaps in case they are split by shades
163
177
  $air_boundary_hash = Hash.new # hash to track any air boundary constructions
164
178
  $window_shade_hash = Hash.new # hash to track any window constructions with shade
165
179
  $programtype_setpoint_hash = Hash.new # hash to track Setpoint objects
180
+ $interior_afn_srf_hash = Hash.new # track whether an adjacent surface is already in the AFN
166
181
 
167
182
  # create all of the non-geometric model elements
168
183
  if log_report
@@ -94,14 +94,14 @@ module FromHoneybee
94
94
  os_gas_equipment = gas_equipment.to_openstudio(openstudio_model)
95
95
  os_gas_equipment.setSpaceType(os_space_type)
96
96
  end
97
-
97
+
98
98
  # assign infiltration
99
- if @hash[:infiltration]
99
+ if @hash[:infiltration] && $use_simple_vent # only use infiltration with simple ventilation
100
100
  infiltration = InfiltrationAbridged.new(@hash[:infiltration])
101
101
  os_infiltration = infiltration.to_openstudio(openstudio_model)
102
102
  os_infiltration.setSpaceType(os_space_type)
103
103
  end
104
-
104
+
105
105
  # assign ventilation
106
106
  if @hash[:ventilation]
107
107
  ventilation = VentilationAbridged.new(@hash[:ventilation])
@@ -0,0 +1,176 @@
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 'from_honeybee/model_object'
33
+
34
+ require 'openstudio'
35
+
36
+ module FromHoneybee
37
+ class VentilationControl < ModelObject
38
+ attr_reader :errors, :warnings
39
+ @@outdoor_node = nil
40
+ @@program_manager = nil
41
+ @@sensor_count = 1
42
+ @@actuator_count = 1
43
+ @@program_count = 1
44
+
45
+ def initialize(hash = {})
46
+ super(hash)
47
+ end
48
+
49
+ def defaults
50
+ @@schema[:components][:schemas][:VentilationControlAbridged][:properties]
51
+ end
52
+
53
+ def get_outdoor_node(openstudio_model)
54
+ # get the EMS sensor for the outdoor node if it exists or generate it if it doesn't
55
+ if @@outdoor_node.nil?
56
+ out_var = OpenStudio::Model::OutputVariable.new(
57
+ 'Site Outdoor Air Drybulb Temperature', openstudio_model)
58
+ out_var.setReportingFrequency('Timestep')
59
+ out_var.setKeyValue('Environment')
60
+ @@outdoor_node = OpenStudio::Model::EnergyManagementSystemSensor.new(
61
+ openstudio_model, out_var)
62
+ @@outdoor_node.setName('Outdoor_Sensor')
63
+ end
64
+ @@outdoor_node
65
+ end
66
+
67
+ def get_program_manager(openstudio_model)
68
+ # get the EMS Program Manager for all window opening if it exists or generate it if it doesn't
69
+ if @@program_manager.nil?
70
+ @@program_manager = OpenStudio::Model::EnergyManagementSystemProgramCallingManager.new(
71
+ openstudio_model)
72
+ @@program_manager.setName('Temperature_Controlled_Window_Opening')
73
+ @@program_manager.setCallingPoint('BeginTimestepBeforePredictor')
74
+ end
75
+ @@program_manager
76
+ end
77
+
78
+ def replace_ems_special_characters(ems_variable_name)
79
+ # remove special characters from an name to be used as an EMS variable
80
+ new_name = ems_variable_name.to_s
81
+ new_name.gsub!(/[^A-Za-z]/, '')
82
+ new_name
83
+ end
84
+
85
+ def to_openstudio(openstudio_model, parent_zone, vent_opening_surfaces, vent_opening_factors)
86
+ # Get the outdoor temperature sensor and the room air temperature sensor
87
+ out_air_temp = get_outdoor_node(openstudio_model)
88
+ in_var = OpenStudio::Model::OutputVariable.new('Zone Air Temperature', openstudio_model)
89
+ in_var.setReportingFrequency('Timestep')
90
+ zone_name = parent_zone.name
91
+ os_zone_name = 'Indoor'
92
+ unless zone_name.empty?
93
+ os_zone_name = zone_name.get
94
+ in_var.setKeyValue(os_zone_name)
95
+ end
96
+ in_air_temp = OpenStudio::Model::EnergyManagementSystemSensor.new(openstudio_model, in_var)
97
+ in_sensor_name = replace_ems_special_characters(os_zone_name) + '_Sensor' + @@sensor_count.to_s
98
+ @@sensor_count = @@sensor_count + 1
99
+ in_air_temp.setName(in_sensor_name)
100
+
101
+ # create the actuators for each of the operaable windows
102
+ actuator_names = []
103
+ vent_opening_surfaces.each do |vent_srf|
104
+ window_act = OpenStudio::Model::EnergyManagementSystemActuator.new(
105
+ vent_srf, 'AirFlow Network Window/Door Opening', 'Venting Opening Factor')
106
+ vent_srf_name = vent_srf.name
107
+ unless vent_srf_name.empty?
108
+ act_name = replace_ems_special_characters(vent_srf_name.get) + \
109
+ '_OpenFactor' + @@actuator_count.to_s
110
+ @@actuator_count = @@actuator_count + 1
111
+ window_act.setName(act_name)
112
+ actuator_names << act_name
113
+ end
114
+ end
115
+
116
+ # create the first line of the EMS Program to open each window according to the control logic
117
+ logic_statements = []
118
+ # check the minimum indoor tempertaure for ventilation
119
+ min_in = @hash[:min_indoor_temperature]
120
+ if min_in && min_in != defaults[:min_indoor_temperature][:default]
121
+ logic_statements << '(' + in_sensor_name + ' > ' + min_in.to_s + ')'
122
+ end
123
+ # check the maximum indoor tempertaure for ventilation
124
+ max_in = @hash[:max_indoor_temperature]
125
+ if max_in && max_in != defaults[:max_indoor_temperature][:default]
126
+ logic_statements << '(' + in_sensor_name + ' < ' + max_in.to_s + ')'
127
+ end
128
+ # check the minimum outdoor tempertaure for ventilation
129
+ min_out = @hash[:min_outdoor_temperature]
130
+ if min_out && min_out != defaults[:min_outdoor_temperature][:default]
131
+ logic_statements << '(Outdoor_Sensor > ' + min_out.to_s + ')'
132
+ end
133
+ # check the maximum outdoor tempertaure for ventilation
134
+ max_out = @hash[:max_outdoor_temperature]
135
+ if max_out && max_out != defaults[:max_outdoor_temperature][:default]
136
+ logic_statements << '(Outdoor_Sensor < ' + max_out.to_s + ')'
137
+ end
138
+ # check the delta tempertaure for ventilation
139
+ delta_in_out = @hash[:delta_temperature]
140
+ if delta_in_out && delta_in_out != defaults[:delta_temperature][:default]
141
+ logic_statements << '((' + in_sensor_name + ' - Outdoor_Sensor) > ' + delta_in_out.to_s + ')'
142
+ end
143
+ # create the complete logic statement for opening windows
144
+ if logic_statements.empty?
145
+ complete_logic = 'IF (Outdoor_Sensor < 100)' # no logic has been provided; always open windows
146
+ else
147
+ complete_logic = 'IF ' + logic_statements.join(' && ')
148
+ end
149
+
150
+ # initialize the program and add the complete logic
151
+ ems_program = OpenStudio::Model::EnergyManagementSystemProgram.new(openstudio_model)
152
+ prog_name = replace_ems_special_characters(os_zone_name) + '_WindowOpening' + @@program_count.to_s
153
+ @@program_count = @@program_count + 1
154
+ ems_program.setName(prog_name)
155
+ ems_program.addLine(complete_logic)
156
+
157
+ # loop through each of the actuators and open each window
158
+ actuator_names.zip(vent_opening_factors).each do |act_name, open_factor|
159
+ ems_program.addLine('SET ' + act_name + ' = ' + open_factor.to_s)
160
+ end
161
+ # loop through each of the actuators and close each window
162
+ ems_program.addLine('ELSE')
163
+ actuator_names.each do |act_name|
164
+ ems_program.addLine('SET ' + act_name + ' = 0')
165
+ end
166
+ ems_program.addLine('ENDIF')
167
+
168
+ # add the program object the the global program manager for all window opening
169
+ prog_manager = get_program_manager(openstudio_model)
170
+ prog_manager.addProgram(ems_program)
171
+
172
+ ems_program
173
+ end
174
+
175
+ end #VentilationControl
176
+ end #FromHoneybee
@@ -46,8 +46,8 @@ module FromHoneybee
46
46
  end
47
47
 
48
48
  def defaults_control
49
- @@schema[:components][:schemas][:VentilationControlAbridged][:properties]
50
- end
49
+ @@schema[:components][:schemas][:VentilationControlAbridged][:properties]
50
+ end
51
51
 
52
52
  def to_openstudio(openstudio_model, parent, vent_control_hash)
53
53
  # create wind and stack object and set identifier
@@ -140,6 +140,48 @@ module FromHoneybee
140
140
  os_opening
141
141
  end
142
142
 
143
+ def to_openstudio_afn(openstudio_model, parent)
144
+ # process the flow_coefficient_closed and set it to a very small number if it's 0
145
+ if @hash[:flow_coefficient_closed] and @hash[:flow_coefficient_closed] != 0
146
+ flow_coefficient = @hash[:flow_coefficient_closed]
147
+ else
148
+ flow_coefficient = 1.0e-09 # set it to a very small number
149
+ end
150
+
151
+ # create the simple opening object for the Aperture or Door using default values
152
+ flow_exponent = defaults[:flow_exponent_closed][:default].to_f
153
+ two_way_thresh = defaults[:two_way_threshold][:default].to_f
154
+ discharge_coeff = defaults[:discharge_coefficient][:default].to_f
155
+ os_opening = OpenStudio::Model::AirflowNetworkSimpleOpening.new(
156
+ openstudio_model, flow_coefficient, flow_exponent, two_way_thresh, discharge_coeff)
157
+
158
+ # assign the flow exponent when the opening is closed
159
+ if @hash[:flow_exponent_closed]
160
+ os_opening.setAirMassFlowExponentWhenOpeningisClosed(@hash[:flow_exponent_closed])
161
+ end
162
+ # assign the minimum difference for two-way flow
163
+ if @hash[:two_way_threshold]
164
+ os_opening.setMinimumDensityDifferenceforTwoWayFlow(@hash[:two_way_threshold])
165
+ end
166
+ # assign the discharge coefficient
167
+ if @hash[:discharge_coefficient]
168
+ os_opening.setDischargeCoefficient(@hash[:discharge_coefficient])
169
+ end
170
+
171
+ # create the AirflowNetworkSurface
172
+ os_afn_srf = parent.getAirflowNetworkSurface(os_opening)
173
+
174
+ # assign the opening area
175
+ if @hash[:fraction_area_operable]
176
+ open_fac = @hash[:fraction_area_operable]
177
+ else
178
+ open_fac = defaults[:fraction_area_operable][:default]
179
+ end
180
+ os_afn_srf.setWindowDoorOpeningFactorOrCrackFactor(open_fac)
181
+
182
+ open_fac
183
+ end
184
+
143
185
  def compute_height(surface)
144
186
  # derive the height (difference in z values) of a surface
145
187
  verts = surface.vertices
@@ -152,7 +194,8 @@ module FromHoneybee
152
194
  max_pt = v.z
153
195
  end
154
196
  end
155
- max_pt - min_pt
197
+ # quarter the window height to get the height from midpoint of lower opening to neutral pressure level
198
+ (max_pt - min_pt) / 4
156
199
  end
157
200
 
158
201
  end #VentilationOpening