honeybee-openstudio 2.16.1 → 2.16.6

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.
@@ -36,7 +36,7 @@ module Honeybee
36
36
  attr_reader :errors, :warnings
37
37
 
38
38
  @@all_air_types = ['VAV', 'PVAV', 'PSZ', 'PTAC', 'ForcedAirFurnace']
39
- @@doas_types = ['FCUwithDOAS', 'WSHPwithDOAS', 'VRFwithDOAS']
39
+ @@doas_types = ['FCUwithDOASAbridged', 'WSHPwithDOASAbridged', 'VRFwithDOASAbridged']
40
40
  @@heat_cool_types = ['FCU', 'WSHP', 'VRF', 'Baseboard', 'EvaporativeCooler',
41
41
  'Residential', 'WindowAC', 'GasUnitHeater']
42
42
  @@types = @@all_air_types + @@doas_types + @@heat_cool_types
@@ -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_-] /, '_')
103
+ str.gsub(/[^.A-Za-z0-9_-] /, '_').gsub(' ', '_')
104
104
  end
105
105
 
106
106
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  ###### (Automatically generated documentation)
4
4
 
5
- # From Energy Model
5
+ # From Honeybee Model
6
6
 
7
7
  ## Description
8
8
  Translate a JSON file of a Honeybee Model into an OpenStudio Model.
@@ -0,0 +1,23 @@
1
+ Honeybee OpenStudio Gem, Copyright (c) 2020, Alliance for Sustainable Energy, LLC,
2
+ Ladybug Tools LLC and other contributors. All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
5
+ following conditions are met:
6
+
7
+ (1) Redistributions of source code must retain the above copyright notice, this list of conditions and the following
8
+ disclaimer.
9
+
10
+ (2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
11
+ disclaimer in the documentation and/or other materials provided with the distribution.
12
+
13
+ (3) Neither the name of the copyright holder nor the names of any contributors may be used to endorse or promote products
14
+ derived from this software without specific prior written permission from the respective party.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
17
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE UNITED STATES GOVERNMENT, OR THE UNITED
19
+ STATES DEPARTMENT OF ENERGY, NOR ANY OF THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
21
+ USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
23
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,32 @@
1
+
2
+
3
+ ###### (Automatically generated documentation)
4
+
5
+ # From Honeybee Model To gbXML
6
+
7
+ ## Description
8
+ Translate a JSON file of a Honeybee Model into a gbXML Model.
9
+
10
+ ## Modeler Description
11
+ Translate a JSON file of a Honeybee Model into a gbXML Model.
12
+
13
+ ## Measure Type
14
+ ModelMeasure
15
+
16
+ ## Taxonomy
17
+
18
+
19
+ ## Arguments
20
+
21
+
22
+ ### Path to the Honeybee Model JSON file
23
+
24
+ **Name:** model_json,
25
+ **Type:** String,
26
+ **Units:** ,
27
+ **Required:** true,
28
+ **Model Dependent:** false
29
+
30
+
31
+
32
+
@@ -0,0 +1,113 @@
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
+ # see the URL below for information on how to write OpenStudio measures
33
+ # http://nrel.github.io/OpenStudio-user-documentation/reference/measure_writing_guide/
34
+
35
+ require 'to_openstudio'
36
+ require 'openstudio'
37
+
38
+ # start the measure
39
+ class FromHoneybeeModelToGbxml < OpenStudio::Measure::ModelMeasure
40
+ # human readable name
41
+ def name
42
+ return 'From Honeybee Model to gbXML'
43
+ end
44
+
45
+ # human readable description
46
+ def description
47
+ return 'Translate a JSON file of a Honeybee Model into a gbXML Model.'
48
+ end
49
+
50
+ # human readable description of modeling approach
51
+ def modeler_description
52
+ return 'Translate a JSON file of a Honeybee Model into a gbXML Model.'
53
+ end
54
+
55
+ # define the arguments that the user will input
56
+ def arguments(model)
57
+ args = OpenStudio::Measure::OSArgumentVector.new
58
+
59
+ # Make an argument for the honyebee model json
60
+ model_json = OpenStudio::Measure::OSArgument.makeStringArgument('model_json', true)
61
+ model_json.setDisplayName('Path to the Honeybee Model JSON file')
62
+ args << model_json
63
+
64
+ # Make an argument for the output file path
65
+ output_file_path = OpenStudio::Measure::OSArgument.makeStringArgument('output_file_path', false)
66
+ output_file_path.setDisplayName('Output file path')
67
+ output_file_path.setDescription('If set, the output gbXML file will be exported to this path. Othervise The file will be exported to the same path as the input model.')
68
+ output_file_path.setDefaultValue('')
69
+ args << output_file_path
70
+
71
+ return args
72
+ end
73
+
74
+ # define what happens when the measure is run
75
+ def run(model, runner, user_arguments)
76
+ super(model, runner, user_arguments)
77
+ STDOUT.flush
78
+ if !runner.validateUserArguments(arguments(model), user_arguments)
79
+ return false
80
+ end
81
+
82
+ # convert the Honeybee model into an OpenStudio Model
83
+ model_json = runner.getStringArgumentValue('model_json', user_arguments)
84
+ if !File.exist?(model_json)
85
+ runner.registerError("Cannot find file '#{model_json}'")
86
+ return false
87
+ end
88
+ honeybee_model = Honeybee::Model.read_from_disk(model_json)
89
+ STDOUT.flush
90
+ os_model = honeybee_model.to_openstudio_model(model)
91
+ STDOUT.flush
92
+
93
+ # convert the OpenStudio model into a gbXML Model
94
+ output_file_path = runner.getStringArgumentValue('output_file_path', user_arguments)
95
+ if output_file_path && !output_file_path.empty?
96
+ unless File.exist?(output_file_path)
97
+ output_folder = File.split(output_file_path)[0]
98
+ FileUtils.mkdir_p(output_folder)
99
+ end
100
+ else
101
+ model_path, model_name = File.split(model_json)
102
+ gbxml_model_name = model_name.split('.')[0] + '.gbxml'
103
+ output_file_path = File.join(model_path, gbxml_model_name)
104
+ end
105
+ translator = OpenStudio::GbXML::GbXMLForwardTranslator.new
106
+ translator.modelToGbXML(os_model, output_file_path)
107
+
108
+ return true
109
+ end
110
+ end
111
+
112
+ # register the measure to be used by the application
113
+ FromHoneybeeModelToGbxml.new.registerWithApplication
@@ -0,0 +1,88 @@
1
+ <?xml version="1.0"?>
2
+ <measure>
3
+ <schema_version>3.0</schema_version>
4
+ <name>from_honeybee_model</name>
5
+ <uid>a5f500d5-97ff-4048-a6f4-6b0b0a92d0f0</uid>
6
+ <version_id>1171d409-32be-4b94-baae-56d59a7e3b1d</version_id>
7
+ <version_modified>20201124T031956Z</version_modified>
8
+ <xml_checksum>D8922A73</xml_checksum>
9
+ <class_name>FromHoneybeeModelToGbxml</class_name>
10
+ <display_name>From Honeybee Model to gbXML</display_name>
11
+ <description>Translate a JSON file of a Honeybee Model into a gbXML Model.</description>
12
+ <modeler_description>Translate a JSON file of a Honeybee Model into a gbXML Model.</modeler_description>
13
+ <arguments>
14
+ <argument>
15
+ <name>model_json</name>
16
+ <display_name>Path to the Honeybee Model JSON file</display_name>
17
+ <type>String</type>
18
+ <required>true</required>
19
+ <model_dependent>false</model_dependent>
20
+ </argument>
21
+ <argument>
22
+ <name>output_file_path</name>
23
+ <display_name>Output file path</display_name>
24
+ <type>String</type>
25
+ <required>false</required>
26
+ <model_dependent>false</model_dependent>
27
+ <default_value></default_value>
28
+ </argument>
29
+ </arguments>
30
+ <outputs />
31
+ <provenances />
32
+ <tags>
33
+ <tag>Whole Building.Space Types</tag>
34
+ </tags>
35
+ <attributes>
36
+ <attribute>
37
+ <name>Measure Type</name>
38
+ <value>ModelMeasure</value>
39
+ <datatype>string</datatype>
40
+ </attribute>
41
+ <attribute>
42
+ <name>Intended Software Tool</name>
43
+ <value>Apply Measure Now</value>
44
+ <datatype>string</datatype>
45
+ </attribute>
46
+ <attribute>
47
+ <name>Intended Software Tool</name>
48
+ <value>OpenStudio Application</value>
49
+ <datatype>string</datatype>
50
+ </attribute>
51
+ <attribute>
52
+ <name>Intended Software Tool</name>
53
+ <value>Parametric Analysis Tool</value>
54
+ <datatype>string</datatype>
55
+ </attribute>
56
+ </attributes>
57
+ <files>
58
+ <file>
59
+ <filename>LICENSE.md</filename>
60
+ <filetype>md</filetype>
61
+ <usage_type>license</usage_type>
62
+ <checksum>1E0EDDB0</checksum>
63
+ </file>
64
+ <file>
65
+ <filename>README.md</filename>
66
+ <filetype>md</filetype>
67
+ <usage_type>readme</usage_type>
68
+ <checksum>782C496D</checksum>
69
+ </file>
70
+ <file>
71
+ <version>
72
+ <software_program>OpenStudio</software_program>
73
+ <identifier>2.0.0</identifier>
74
+ <min_compatible>2.0.0</min_compatible>
75
+ </version>
76
+ <filename>measure.rb</filename>
77
+ <filetype>rb</filetype>
78
+ <usage_type>script</usage_type>
79
+ <checksum>8B48C43C</checksum>
80
+ </file>
81
+ <file>
82
+ <filename>from_honeybee_model_to_gbxml_test.rb</filename>
83
+ <filetype>rb</filetype>
84
+ <usage_type>test</usage_type>
85
+ <checksum>A800695A</checksum>
86
+ </file>
87
+ </files>
88
+ </measure>
@@ -0,0 +1,105 @@
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'
33
+ require 'openstudio/ruleset/ShowRunnerOutput'
34
+ require 'minitest/autorun'
35
+ require_relative '../measure.rb'
36
+ require 'fileutils'
37
+
38
+ class FromHoneybeeModelToGbxml_Test < Minitest::Test
39
+ # method to apply arguments, run measure, and assert results (only populate args hash with non-default argument values)
40
+ def apply_measure_to_model(test_name, args, model_name = nil, result_value = 'Success', warnings_count = 0, info_count = nil)
41
+ # create an instance of the measure
42
+ measure = FromHoneybeeModelToGbxml.new
43
+
44
+ # create an instance of a runner
45
+ runner = OpenStudio::Measure::OSRunner.new(OpenStudio::WorkflowJSON.new)
46
+
47
+ if model_name.nil?
48
+ # make an empty model
49
+ model = OpenStudio::Model::Model.new
50
+ else
51
+ # load the test model
52
+ translator = OpenStudio::OSVersion::VersionTranslator.new
53
+ path = OpenStudio::Path.new(File.dirname(__FILE__) + '/' + model_name)
54
+ model = translator.loadModel(path)
55
+ assert(!model.empty?)
56
+ model = model.get
57
+ end
58
+
59
+ # get arguments
60
+ arguments = measure.arguments(model)
61
+ argument_map = OpenStudio::Measure.convertOSArgumentVectorToMap(arguments)
62
+
63
+ # populate argument with specified hash value if specified
64
+ arguments.each do |arg|
65
+ temp_arg_var = arg.clone
66
+ if args.key?(arg.name)
67
+ assert(temp_arg_var.setValue(args[arg.name]), "could not set #{arg.name} to #{args[arg.name]}.")
68
+ end
69
+ argument_map[arg.name] = temp_arg_var
70
+ end
71
+
72
+ # run the measure
73
+ measure.run(model, runner, argument_map)
74
+ result = runner.result
75
+
76
+ # show the output
77
+ puts "measure results for #{test_name}"
78
+ show_output(result)
79
+
80
+ # assert that it ran correctly
81
+ if result_value.nil? then result_value = 'Success' end
82
+ assert_equal(result_value, result.value.valueName)
83
+
84
+ # check count of warning and info messages
85
+ unless info_count.nil? then assert(result.info.size == info_count) end
86
+ unless warnings_count.nil? then assert(result.warnings.size == warnings_count, "warning count (#{result.warnings.size}) did not match expectation (#{warnings_count})") end
87
+
88
+ # if 'Fail' passed in make sure at least one error message (while not typical there may be more than one message)
89
+ if result_value == 'Fail' then assert(result.errors.size >= 1) end
90
+
91
+ end
92
+
93
+ def test_shoe_box
94
+ args = {}
95
+ args['model_json'] = File.join(File.dirname(__FILE__) + "/../../../../spec/samples/model/model_energy_shoe_box.json")
96
+ apply_measure_to_model(__method__.to_s.gsub('test_',''),args, nil)
97
+ end
98
+
99
+ def test_office_floor
100
+ args = {}
101
+ args['model_json'] = File.join(File.dirname(__FILE__) + "/../../../../spec/samples/model/model_complete_office_floor.json")
102
+ apply_measure_to_model(__method__.to_s.gsub('test_',''),args, nil)
103
+ end
104
+
105
+ end
@@ -50,8 +50,12 @@ module Honeybee
50
50
  # create material vector
51
51
  os_materials = OpenStudio::Model::MaterialVector.new
52
52
  # loop through each layer and add to material vector
53
- @hash[:layers].each do |layer|
54
- material_identifier = layer
53
+ if @hash.key?(:layers)
54
+ mat_key = :layers
55
+ else
56
+ mat_key = :materials
57
+ end
58
+ @hash[mat_key].each do |material_identifier|
55
59
  material = openstudio_model.getMaterialByName(material_identifier)
56
60
  unless material.empty?
57
61
  os_material = material.get
@@ -50,8 +50,12 @@ module Honeybee
50
50
  # create material vector
51
51
  os_materials = OpenStudio::Model::MaterialVector.new
52
52
  # loop through each layer and add to material vector
53
- @hash[:layers].each do |layer|
54
- material_identifier = layer
53
+ if @hash.key?(:layers)
54
+ mat_key = :layers
55
+ else
56
+ mat_key = :materials
57
+ end
58
+ @hash[mat_key].each do |material_identifier|
55
59
  material = openstudio_model.getMaterialByName(material_identifier)
56
60
  unless material.empty?
57
61
  os_material = material.get
@@ -62,8 +62,12 @@ module Honeybee
62
62
 
63
63
  # create the layers of the unshaded construction into which we will insert the shade
64
64
  os_materials = []
65
- @hash[:window_construction][:layers].each do |layer|
66
- material_identifier = layer
65
+ if @hash.key?(:layers)
66
+ mat_key = :layers
67
+ else
68
+ mat_key = :materials
69
+ end
70
+ @hash[:window_construction][mat_key].each do |material_identifier|
67
71
  material = openstudio_model.getMaterialByName(material_identifier)
68
72
  unless material.empty?
69
73
  os_material = material.get
@@ -91,6 +91,7 @@ module Honeybee
91
91
  os_air_loop = os_air_loop_opt.get
92
92
  loop_name = os_air_loop.name
93
93
  unless loop_name.empty?
94
+ # set the name of the air loop to align with the HVAC name
94
95
  if @hash[:display_name]
95
96
  clean_name = @hash[:display_name].to_s.gsub(/[^.A-Za-z0-9_-] /, " ")
96
97
  os_air_loop.setName(clean_name + ' - ' + loop_name.get)
@@ -101,17 +102,17 @@ module Honeybee
101
102
  end
102
103
 
103
104
  # assign the economizer type if there's an air loop and the economizer is specified
104
- if @hash[:economizer_type] && @hash[:economizer_type] != 'Inferred' && os_air_loop
105
+ if @hash[:economizer_type] && os_air_loop
105
106
  oasys = os_air_loop.airLoopHVACOutdoorAirSystem
106
107
  unless oasys.empty?
107
- os_oasys = oasys.get
108
- oactrl = os_oasys.getControllerOutdoorAir
109
- oactrl.setEconomizerControlType(@hash[:economizer_type])
108
+ os_oasys = oasys.get
109
+ oactrl = os_oasys.getControllerOutdoorAir
110
+ oactrl.setEconomizerControlType(@hash[:economizer_type])
110
111
  end
111
112
  end
112
113
 
113
114
  # set the sensible heat recovery if there's an air loop and the heat recovery is specified
114
- if @hash[:sensible_heat_recovery] && @hash[:sensible_heat_recovery] != {:type => 'Autosize'} && os_air_loop
115
+ if @hash[:sensible_heat_recovery] && @hash[:sensible_heat_recovery] != 0 && os_air_loop
115
116
  erv = get_existing_erv(os_air_loop)
116
117
  unless erv
117
118
  erv = create_erv(openstudio_model, os_air_loop)
@@ -124,7 +125,7 @@ module Honeybee
124
125
  end
125
126
 
126
127
  # set the latent heat recovery if there's an air loop and the heat recovery is specified
127
- if @hash[:latent_heat_recovery] && @hash[:latent_heat_recovery] != {:type => 'Autosize'} && os_air_loop
128
+ if @hash[:latent_heat_recovery] && @hash[:latent_heat_recovery] != 0 && os_air_loop
128
129
  erv = get_existing_erv(os_air_loop)
129
130
  unless erv
130
131
  erv = create_erv(openstudio_model, os_air_loop)
@@ -136,6 +137,60 @@ module Honeybee
136
137
  erv.setLatentEffectivenessat75HeatingAirFlow(@hash[:latent_heat_recovery])
137
138
  end
138
139
 
140
+ # assign demand controlled ventilation if there's an air loop
141
+ if @hash[:demand_controlled_ventilation] && os_air_loop
142
+ oasys = os_air_loop.airLoopHVACOutdoorAirSystem
143
+ unless oasys.empty?
144
+ os_oasys = oasys.get
145
+ oactrl = os_oasys.getControllerOutdoorAir
146
+ vent_ctrl = oactrl.controllerMechanicalVentilation
147
+ vent_ctrl.setDemandControlledVentilationNoFail(true)
148
+ oactrl.resetMinimumFractionofOutdoorAirSchedule
149
+ end
150
+ end
151
+
152
+ # assign the DOAS availability schedule if there's an air loop and it is specified
153
+ if @hash[:doas_availability_schedule] && os_air_loop
154
+ schedule = openstudio_model.getScheduleByName(@hash[:doas_availability_schedule])
155
+ unless schedule.empty?
156
+ avail_sch = schedule.get
157
+ os_air_loop.setAvailabilitySchedule(avail_sch)
158
+ end
159
+ end
160
+
161
+ # set the outdoor air controller to respect room-level ventilation schedules if they exist
162
+ if os_air_loop
163
+ oasys = os_air_loop.airLoopHVACOutdoorAirSystem
164
+ unless oasys.empty?
165
+ os_oasys = oasys.get
166
+ oactrl = os_oasys.getControllerOutdoorAir
167
+ oa_sch, oa_sch_name = nil, nil
168
+ zones.each do |zone|
169
+ oa_spec = zone.spaces[0].designSpecificationOutdoorAir
170
+ unless oa_spec.empty?
171
+ oa_spec = oa_spec.get
172
+ space_oa_sch = oa_spec.outdoorAirFlowRateFractionSchedule
173
+ unless space_oa_sch.empty?
174
+ space_oa_sch = space_oa_sch.get
175
+ space_oa_sch_name = space_oa_sch.name
176
+ unless space_oa_sch_name.empty?
177
+ space_oa_sch_name = space_oa_sch_name.get
178
+ if oa_sch_name.nil? || space_oa_sch_name == oa_sch_name
179
+ oa_sch, oa_sch_name = space_oa_sch, space_oa_sch_name
180
+ else
181
+ oa_sch = nil
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end
187
+ if oa_sch
188
+ oactrl.resetMinimumFractionofOutdoorAirSchedule
189
+ oactrl.setMinimumOutdoorAirSchedule(oa_sch)
190
+ end
191
+ end
192
+ end
193
+
139
194
  # if the systems are PTAC and there is ventilation, ensure the system includes it
140
195
  if equipment_type.include?('PTAC') || equipment_type.include?('PTHP')
141
196
  always_on = openstudio_model.getScheduleByName('Always On').get
@@ -172,6 +227,20 @@ module Honeybee
172
227
  end
173
228
  end
174
229
 
230
+ # assign an electric humidifier if there's an air loop and the zones have a humidistat
231
+ if os_air_loop
232
+ humidistat_exists = false
233
+ zones.each do |zone|
234
+ h_stat = zone.zoneControlHumidistat
235
+ unless h_stat.empty?
236
+ humidistat_exists = true
237
+ end
238
+ end
239
+ if humidistat_exists
240
+ humidifier = create_humidifier(openstudio_model, os_air_loop)
241
+ end
242
+ end
243
+
175
244
  # set all plants to non-coincident sizing to avoid simualtion control issues on design days
176
245
  openstudio_model.getPlantLoops.each do |loop|
177
246
  sizing = loop.sizingPlant
@@ -217,5 +286,20 @@ module Honeybee
217
286
  heat_ex
218
287
  end
219
288
 
289
+ def create_humidifier(model, os_air_loop)
290
+ # create an electric humidifier
291
+ humidifier = OpenStudio::Model::HumidifierSteamElectric.new(model)
292
+ humidifier.setName(@hash[:identifier] + '_Humidifier Unit')
293
+ humid_controller = OpenStudio::Model::SetpointManagerMultiZoneHumidityMinimum.new(model)
294
+ humid_controller.setName(@hash[:identifier] + '_Humidifier Controller')
295
+
296
+ # add the humidifier to the air loop
297
+ supply_node = os_air_loop.supplyOutletNode
298
+ humidifier.addToNode(supply_node)
299
+ humid_controller.addToNode(supply_node)
300
+
301
+ humidifier
302
+ end
303
+
220
304
  end #TemplateHVAC
221
305
  end #Honeybee