honeybee-openstudio 2.4.0 → 2.6.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.
@@ -100,7 +100,7 @@ module FromHoneybee
100
100
  end
101
101
 
102
102
  # assign limits to the system's heating capacity
103
- if @hash[:heating_limit] == {'type': 'NoLimit'}
103
+ if @hash[:heating_limit] == {:type => 'NoLimit'}
104
104
  os_ideal_air.setHeatingLimit('NoLimit')
105
105
  else
106
106
  os_ideal_air.setHeatingLimit('LimitCapacity')
@@ -112,7 +112,7 @@ module FromHoneybee
112
112
  end
113
113
 
114
114
  # assign limits to the system's cooling capacity
115
- if @hash[:cooling_limit] == {'type': 'NoLimit'}
115
+ if @hash[:cooling_limit] == {:type => 'NoLimit'}
116
116
  os_ideal_air.setCoolingLimit('NoLimit')
117
117
  else
118
118
  os_ideal_air.setCoolingLimit('LimitFlowRateAndCapacity')
@@ -0,0 +1,201 @@
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 'openstudio-standards'
33
+ require_relative 'Model.hvac'
34
+
35
+ require 'from_honeybee/extension'
36
+ require 'from_honeybee/model_object'
37
+
38
+ module FromHoneybee
39
+ class TemplateHVAC < ModelObject
40
+ attr_reader :errors, :warnings
41
+
42
+ @@all_air_types = ['VAV', 'PVAV', 'PSZ', 'PTAC', 'ForcedAirFurnace']
43
+ @@doas_types = ['FCUwithDOAS', 'WSHPwithDOAS', 'VRFwithDOAS']
44
+ @@heat_cool_types = ['FCU', 'WSHP', 'VRF', 'Baseboard', 'EvaporativeCooler',
45
+ 'Residential', 'WindowAC', 'GasUnitHeater']
46
+ @@types = @@all_air_types + @@doas_types + @@heat_cool_types
47
+
48
+ def initialize(hash = {})
49
+ super(hash)
50
+ end
51
+
52
+ def self.types
53
+ # array of all supported template HVAC systems
54
+ @@types
55
+ end
56
+
57
+ def self.all_air_types
58
+ # array of the All Air HVAC types
59
+ @@all_air_types
60
+ end
61
+
62
+ def self.doas_types
63
+ # array of the DOAS HVAC types
64
+ @@doas_types
65
+ end
66
+
67
+ def self.heat_cool_types
68
+ # array of the system types providing heating and cooling only
69
+ @@heat_cool_types
70
+ end
71
+
72
+ def defaults(system_type)
73
+ @@schema[:components][:schemas][system_type.to_sym][:properties]
74
+ end
75
+
76
+ def to_openstudio(openstudio_model, room_ids)
77
+ # get the defaults for the specific system type
78
+ hvac_defaults = defaults(@hash[:type])
79
+
80
+ # make the standard applier
81
+ if @hash[:vintage]
82
+ standard = Standard.build(@hash[:vintage])
83
+ else
84
+ standard = Standard.build(hvac_defaults[:vintage][:default])
85
+ end
86
+
87
+ # get the default equipment type
88
+ if @hash[:equipment_type]
89
+ equipment_type = @hash[:equipment_type]
90
+ else
91
+ equipment_type = hvac_defaults[:equipment_type][:default]
92
+ end
93
+
94
+ # get all of the thermal zones from the Model using the room identifiers
95
+ zones = []
96
+ room_ids.each do |room_id|
97
+ zone_get = openstudio_model.getThermalZoneByName(room_id)
98
+ unless zone_get.empty?
99
+ os_thermal_zone = zone_get.get
100
+ zones << os_thermal_zone
101
+ end
102
+ end
103
+
104
+ # create the HVAC system and assign the display name to the air loop name if it exists
105
+ os_hvac = openstudio_model.add_cbecs_hvac_system(standard, equipment_type, zones)
106
+ os_air_loop = nil
107
+ air_loops = openstudio_model.getAirLoopHVACs
108
+ unless air_loops.length == $air_loop_count # check if any new loops were added
109
+ $air_loop_count = air_loops.length
110
+ os_air_loop = air_loops[-1]
111
+ loop_name = os_air_loop.name
112
+ unless loop_name.empty?
113
+ if @hash[:display_name]
114
+ os_air_loop.setName(@hash[:display_name] + ' - ' + loop_name.get)
115
+ end
116
+ end
117
+ end
118
+
119
+ # TODO: consider adding the ability to decentralize the plant by changing loop names
120
+ #os_hvac.each do |hvac_loop|
121
+ # loop_name = hvac_loop.name
122
+ # unless loop_name.empty?
123
+ # hvac_loop.setName(@hash[:identifier] + ' - ' + loop_name.get)
124
+ # end
125
+ #end
126
+
127
+ # assign the economizer type if there's an air loop and the economizer is specified
128
+ if @hash[:economizer_type] && @hash[:economizer_type] != 'Inferred' && os_air_loop
129
+ oasys = os_air_loop.airLoopHVACOutdoorAirSystem
130
+ unless oasys.empty?
131
+ os_oasys = oasys.get
132
+ oactrl = os_oasys.getControllerOutdoorAir
133
+ oactrl.setEconomizerControlType(@hash[:economizer_type])
134
+ end
135
+ end
136
+
137
+ # set the sensible heat recovery if there's an air loop and the heat recovery is specified
138
+ if @hash[:sensible_heat_recovery] && @hash[:sensible_heat_recovery] != {:type => 'Autosize'} && os_air_loop
139
+ erv = get_existing_erv(os_air_loop)
140
+ unless erv
141
+ erv = create_erv(openstudio_model, os_air_loop)
142
+ end
143
+ eff_at_max = @hash[:sensible_heat_recovery] * (0.76 / 0.81) # taken from OpenStudio defaults
144
+ erv.setSensibleEffectivenessat100CoolingAirFlow(eff_at_max)
145
+ erv.setSensibleEffectivenessat100HeatingAirFlow(eff_at_max)
146
+ erv.setSensibleEffectivenessat75CoolingAirFlow(@hash[:sensible_heat_recovery])
147
+ erv.setSensibleEffectivenessat75HeatingAirFlow(@hash[:sensible_heat_recovery])
148
+ end
149
+
150
+ # set the latent heat recovery if there's an air loop and the heat recovery is specified
151
+ if @hash[:latent_heat_recovery] && @hash[:latent_heat_recovery] != {:type => 'Autosize'} && os_air_loop
152
+ erv = get_existing_erv(os_air_loop)
153
+ unless erv
154
+ erv = create_erv(openstudio_model, os_air_loop)
155
+ end
156
+ eff_at_max = @hash[:latent_heat_recovery] * (0.68 / 0.73) # taken from OpenStudio defaults
157
+ erv.setLatentEffectivenessat100CoolingAirFlow(eff_at_max)
158
+ erv.setLatentEffectivenessat100HeatingAirFlow(eff_at_max)
159
+ erv.setLatentEffectivenessat75CoolingAirFlow(@hash[:latent_heat_recovery])
160
+ erv.setLatentEffectivenessat75HeatingAirFlow(@hash[:latent_heat_recovery])
161
+ end
162
+
163
+ os_hvac
164
+ end
165
+
166
+ def get_existing_erv(os_air_loop)
167
+ # get an existing heat ecovery unit from an air loop; will be nil if there is none
168
+ os_air_loop.oaComponents.each do |supply_component|
169
+ if not supply_component.to_HeatExchangerAirToAirSensibleAndLatent.empty?
170
+ erv = supply_component.to_HeatExchangerAirToAirSensibleAndLatent.get
171
+ return erv
172
+ end
173
+ end
174
+ nil
175
+ end
176
+
177
+ def create_erv(model, os_air_loop)
178
+ # create a heat recovery unit with default zero efficiencies
179
+ heat_ex = OpenStudio::Model::HeatExchangerAirToAirSensibleAndLatent.new(model)
180
+ heat_ex.setEconomizerLockout(false)
181
+ heat_ex.setName(@hash[:identifier] + '_Heat Recovery Unit')
182
+ heat_ex.setSensibleEffectivenessat100CoolingAirFlow(0)
183
+ heat_ex.setSensibleEffectivenessat100HeatingAirFlow(0)
184
+ heat_ex.setSensibleEffectivenessat75CoolingAirFlow(0)
185
+ heat_ex.setSensibleEffectivenessat75HeatingAirFlow(0)
186
+ heat_ex.setLatentEffectivenessat100CoolingAirFlow(0)
187
+ heat_ex.setLatentEffectivenessat100HeatingAirFlow(0)
188
+ heat_ex.setLatentEffectivenessat75CoolingAirFlow(0)
189
+ heat_ex.setLatentEffectivenessat75HeatingAirFlow(0)
190
+
191
+ # add the heat exchanger to the air loop
192
+ outdoor_node = os_air_loop.reliefAirNode
193
+ unless outdoor_node.empty?
194
+ os_outdoor_node = outdoor_node.get
195
+ heat_ex.addToNode(os_outdoor_node)
196
+ end
197
+ heat_ex
198
+ end
199
+
200
+ end #TemplateHVAC
201
+ end #FromHoneybee
@@ -46,6 +46,7 @@ require 'from_honeybee/geometry/room'
46
46
 
47
47
  # import the HVAC objects
48
48
  require 'from_honeybee/hvac/ideal_air'
49
+ require 'from_honeybee/hvac/template'
49
50
 
50
51
  # import the construction objects
51
52
  require 'from_honeybee/construction/opaque'
@@ -74,6 +75,9 @@ require 'from_honeybee/schedule/ruleset'
74
75
  require 'from_honeybee/load/setpoint_thermostat'
75
76
  require 'from_honeybee/load/setpoint_humidistat'
76
77
 
78
+ # import the ventilative cooling objects
79
+ require 'from_honeybee/ventcool/simulation'
80
+
77
81
  require 'openstudio'
78
82
 
79
83
 
@@ -157,11 +161,23 @@ module FromHoneybee
157
161
  building = @openstudio_model.getBuilding
158
162
  building.setStandardsBuildingType('MediumOffice')
159
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
+
160
175
  # initialize global hashes for various model properties
161
176
  $gas_gap_hash = Hash.new # hash to track gas gaps in case they are split by shades
162
177
  $air_boundary_hash = Hash.new # hash to track any air boundary constructions
163
178
  $window_shade_hash = Hash.new # hash to track any window constructions with shade
164
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
165
181
 
166
182
  # create all of the non-geometric model elements
167
183
  if log_report
@@ -497,13 +513,14 @@ module FromHoneybee
497
513
 
498
514
  def create_hvacs
499
515
  if @hash[:properties][:energy][:hvacs]
516
+ $air_loop_count = 0 # track the total number of air loops in the model
500
517
  # gather all of the hashes of the HVACs
501
518
  hvac_hashes = Hash.new
502
519
  @hash[:properties][:energy][:hvacs].each do |hvac|
503
520
  hvac_hashes[hvac[:identifier]] = hvac
504
521
  hvac_hashes[hvac[:identifier]]['rooms'] = []
505
522
  end
506
- # loop through the rooms and trach which are assigned to each HVAC
523
+ # loop through the rooms and track which are assigned to each HVAC
507
524
  if @hash[:rooms]
508
525
  @hash[:rooms].each do |room|
509
526
  if room[:properties][:energy][:hvac]
@@ -514,8 +531,7 @@ module FromHoneybee
514
531
 
515
532
  hvac_hashes.each_value do |hvac|
516
533
  system_type = hvac[:type]
517
- case system_type
518
- when 'IdealAirSystemAbridged'
534
+ if system_type == 'IdealAirSystemAbridged'
519
535
  ideal_air_system = IdealAirSystemAbridged.new(hvac)
520
536
  os_ideal_air_system = ideal_air_system.to_openstudio(@openstudio_model)
521
537
  hvac['rooms'].each do |room_id|
@@ -525,6 +541,9 @@ module FromHoneybee
525
541
  os_ideal_air_system.addToThermalZone(os_thermal_zone)
526
542
  end
527
543
  end
544
+ elsif TemplateHVAC.types.include?(system_type)
545
+ template_system = TemplateHVAC.new(hvac)
546
+ os_template_system = template_system.to_openstudio(@openstudio_model, hvac['rooms'])
528
547
  end
529
548
  end
530
549
  end
@@ -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])
@@ -59,11 +59,11 @@ module FromHoneybee
59
59
  os_type_limit = OpenStudio::Model::ScheduleTypeLimits.new(openstudio_model)
60
60
  os_type_limit.setName(@hash[:identifier])
61
61
 
62
- if @hash[:lower_limit] != nil and @hash[:lower_limit] != {'type': 'NoLimit'}
62
+ if @hash[:lower_limit] != nil and @hash[:lower_limit] != {:type => 'NoLimit'}
63
63
  os_type_limit.setLowerLimitValue(@hash[:lower_limit])
64
64
  end
65
65
 
66
- if @hash[:upper_limit] != nil and @hash[:upper_limit] != {'type': 'NoLimit'}
66
+ if @hash[:upper_limit] != nil and @hash[:upper_limit] != {:type => 'NoLimit'}
67
67
  os_type_limit.setUpperLimitValue(@hash[:upper_limit])
68
68
  end
69
69
 
@@ -0,0 +1,161 @@
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
+
42
+ def initialize(hash = {})
43
+ super(hash)
44
+ end
45
+
46
+ def defaults
47
+ @@schema[:components][:schemas][:VentilationControlAbridged][:properties]
48
+ end
49
+
50
+ def get_outdoor_node(openstudio_model)
51
+ # get the EMS sensor for the outdoor node if it exists or generate it if it doesn't
52
+ if @@outdoor_node.nil?
53
+ out_var = OpenStudio::Model::OutputVariable.new(
54
+ 'Site Outdoor Air Drybulb Temperature', openstudio_model)
55
+ out_var.setReportingFrequency('Timestep')
56
+ out_var.setKeyValue('Environment')
57
+ @@outdoor_node = OpenStudio::Model::EnergyManagementSystemSensor.new(
58
+ openstudio_model, out_var)
59
+ @@outdoor_node.setName('Outdoor_Sensor')
60
+ end
61
+ @@outdoor_node
62
+ end
63
+
64
+ def get_program_manager(openstudio_model)
65
+ # get the EMS Program Manager for all window opening if it exists or generate it if it doesn't
66
+ if @@program_manager.nil?
67
+ @@program_manager = OpenStudio::Model::EnergyManagementSystemProgramCallingManager.new(
68
+ openstudio_model)
69
+ @@program_manager.setName('Temperature_Controlled_Window_Opening')
70
+ @@program_manager.setCallingPoint('BeginTimestepBeforePredictor')
71
+ end
72
+ @@program_manager
73
+ end
74
+
75
+ def to_openstudio(openstudio_model, parent_zone, vent_opening_surfaces, vent_opening_factors)
76
+ # Get the outdoor temperature sensor and the room air temperature sensor
77
+ out_air_temp = get_outdoor_node(openstudio_model)
78
+ in_var = OpenStudio::Model::OutputVariable.new('Zone Air Temperature', openstudio_model)
79
+ in_var.setReportingFrequency('Timestep')
80
+ zone_name = parent_zone.name
81
+ os_zone_name = 'Indoor'
82
+ unless zone_name.empty?
83
+ os_zone_name = zone_name.get
84
+ in_var.setKeyValue(os_zone_name)
85
+ end
86
+ in_air_temp = OpenStudio::Model::EnergyManagementSystemSensor.new(openstudio_model, in_var)
87
+ in_sensor_name = os_zone_name.delete(' ').delete('.') + '_Sensor'
88
+ in_air_temp.setName(in_sensor_name)
89
+
90
+ # create the actuators for each of the operaable windows
91
+ actuator_names = []
92
+ vent_opening_surfaces.each do |vent_srf|
93
+ window_act = OpenStudio::Model::EnergyManagementSystemActuator.new(
94
+ vent_srf, 'AirFlow Network Window/Door Opening', 'Venting Opening Factor')
95
+ vent_srf_name = vent_srf.name
96
+ unless vent_srf_name.empty?
97
+ act_name = vent_srf_name.get.delete(' ').delete('.') + '_OpenFactor'
98
+ window_act.setName(act_name)
99
+ actuator_names << act_name
100
+ end
101
+ end
102
+
103
+ # create the first line of the EMS Program to open each window according to the control logic
104
+ logic_statements = []
105
+ # check the minimum indoor tempertaure for ventilation
106
+ min_in = @hash[:min_indoor_temperature]
107
+ if min_in && min_in != defaults[:min_indoor_temperature][:default]
108
+ logic_statements << '(' + in_sensor_name + ' > ' + min_in.to_s + ')'
109
+ end
110
+ # check the maximum indoor tempertaure for ventilation
111
+ max_in = @hash[:max_indoor_temperature]
112
+ if max_in && max_in != defaults[:max_indoor_temperature][:default]
113
+ logic_statements << '(' + in_sensor_name + ' < ' + max_in.to_s + ')'
114
+ end
115
+ # check the minimum outdoor tempertaure for ventilation
116
+ min_out = @hash[:min_outdoor_temperature]
117
+ if min_out && min_out != defaults[:min_outdoor_temperature][:default]
118
+ logic_statements << '(Outdoor_Sensor > ' + min_out.to_s + ')'
119
+ end
120
+ # check the maximum outdoor tempertaure for ventilation
121
+ max_out = @hash[:max_outdoor_temperature]
122
+ if max_out && max_out != defaults[:max_outdoor_temperature][:default]
123
+ logic_statements << '(Outdoor_Sensor < ' + max_out.to_s + ')'
124
+ end
125
+ # check the delta tempertaure for ventilation
126
+ delta_in_out = @hash[:delta_temperature]
127
+ if delta_in_out && delta_in_out != defaults[:delta_temperature][:default]
128
+ logic_statements << '((' + in_sensor_name + ' - Outdoor_Sensor) > ' + delta_in_out.to_s + ')'
129
+ end
130
+ # create the complete logic statement for opening windows
131
+ if logic_statements.empty?
132
+ complete_logic = 'IF (Outdoor_Sensor < 100)' # no logic has been provided; always open windows
133
+ else
134
+ complete_logic = 'IF ' + logic_statements.join(' && ')
135
+ end
136
+
137
+ # initialize the program and add the complete logic
138
+ ems_program = OpenStudio::Model::EnergyManagementSystemProgram.new(openstudio_model)
139
+ ems_program.setName(os_zone_name.delete(' ').delete('.') + '_WindowOpening')
140
+ ems_program.addLine(complete_logic)
141
+
142
+ # loop through each of the actuators and open each window
143
+ actuator_names.zip(vent_opening_factors).each do |act_name, open_factor|
144
+ ems_program.addLine('SET ' + act_name + ' = ' + open_factor.to_s)
145
+ end
146
+ # loop through each of the actuators and close each window
147
+ ems_program.addLine('ELSE')
148
+ actuator_names.each do |act_name|
149
+ ems_program.addLine('SET ' + act_name + ' = 0')
150
+ end
151
+ ems_program.addLine('ENDIF')
152
+
153
+ # add the program object the the global program manager for all window opening
154
+ prog_manager = get_program_manager(openstudio_model)
155
+ prog_manager.addProgram(ems_program)
156
+
157
+ ems_program
158
+ end
159
+
160
+ end #VentilationControl
161
+ end #FromHoneybee