buildingsync 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/continuous_integration.yml +146 -0
- data/.gitignore +33 -0
- data/.rspec +3 -0
- data/.rubocop.yml +10 -0
- data/CHANGELOG.md +50 -0
- data/Gemfile +31 -0
- data/Jenkinsfile +10 -0
- data/LICENSE.md +29 -0
- data/README.md +105 -0
- data/Rakefile +77 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/buildingsync.gemspec +37 -0
- data/config.rb.in +26 -0
- data/doc_templates/LICENSE.md +29 -0
- data/doc_templates/README.md.erb +42 -0
- data/doc_templates/copyright_erb.txt +38 -0
- data/doc_templates/copyright_js.txt +5 -0
- data/doc_templates/copyright_ruby.txt +36 -0
- data/lib/buildingsync.rb +43 -0
- data/lib/buildingsync/all_resource_total.rb +54 -0
- data/lib/buildingsync/audit_date.rb +54 -0
- data/lib/buildingsync/constants.rb +49 -0
- data/lib/buildingsync/contact.rb +54 -0
- data/lib/buildingsync/extension.rb +57 -0
- data/lib/buildingsync/generator.rb +584 -0
- data/lib/buildingsync/get_bcl_weather_file.rb +326 -0
- data/lib/buildingsync/helpers/Model.hvac.rb +216 -0
- data/lib/buildingsync/helpers/helper.rb +494 -0
- data/lib/buildingsync/helpers/xml_get_set.rb +215 -0
- data/lib/buildingsync/makers/phase_zero_base.osw +178 -0
- data/lib/buildingsync/makers/workflow_maker.json +811 -0
- data/lib/buildingsync/makers/workflow_maker.rb +581 -0
- data/lib/buildingsync/makers/workflow_maker_base.rb +167 -0
- data/lib/buildingsync/model_articulation/building.rb +1119 -0
- data/lib/buildingsync/model_articulation/building_and_system_types.json +121 -0
- data/lib/buildingsync/model_articulation/building_section.rb +190 -0
- data/lib/buildingsync/model_articulation/building_system.rb +49 -0
- data/lib/buildingsync/model_articulation/envelope_system.rb +102 -0
- data/lib/buildingsync/model_articulation/exterior_floor_system_type.rb +64 -0
- data/lib/buildingsync/model_articulation/facility.rb +439 -0
- data/lib/buildingsync/model_articulation/foundation_system_type.rb +64 -0
- data/lib/buildingsync/model_articulation/hvac_system.rb +395 -0
- data/lib/buildingsync/model_articulation/lighting_system.rb +102 -0
- data/lib/buildingsync/model_articulation/loads_system.rb +287 -0
- data/lib/buildingsync/model_articulation/location_element.rb +129 -0
- data/lib/buildingsync/model_articulation/measure.rb +57 -0
- data/lib/buildingsync/model_articulation/roof_system_type.rb +64 -0
- data/lib/buildingsync/model_articulation/service_hot_water_system.rb +87 -0
- data/lib/buildingsync/model_articulation/site.rb +242 -0
- data/lib/buildingsync/model_articulation/spatial_element.rb +343 -0
- data/lib/buildingsync/model_articulation/wall_system_type.rb +64 -0
- data/lib/buildingsync/report.rb +217 -0
- data/lib/buildingsync/resource_use.rb +55 -0
- data/lib/buildingsync/scenario.rb +622 -0
- data/lib/buildingsync/selection_tool.rb +98 -0
- data/lib/buildingsync/time_series.rb +85 -0
- data/lib/buildingsync/translator.rb +167 -0
- data/lib/buildingsync/utility.rb +67 -0
- data/lib/buildingsync/version.rb +45 -0
- metadata +223 -0
@@ -0,0 +1,581 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# *******************************************************************************
|
4
|
+
# OpenStudio(R), Copyright (c) 2008-2020, Alliance for Sustainable Energy, LLC.
|
5
|
+
# BuildingSync(R), Copyright (c) 2015-2020, Alliance for Sustainable Energy, LLC.
|
6
|
+
# All rights reserved.
|
7
|
+
#
|
8
|
+
# Redistribution and use in source and binary forms, with or without
|
9
|
+
# modification, are permitted provided that the following conditions are met:
|
10
|
+
#
|
11
|
+
# (1) Redistributions of source code must retain the above copyright notice,
|
12
|
+
# this list of conditions and the following disclaimer.
|
13
|
+
#
|
14
|
+
# (2) Redistributions in binary form must reproduce the above copyright notice,
|
15
|
+
# this list of conditions and the following disclaimer in the documentation
|
16
|
+
# and/or other materials provided with the distribution.
|
17
|
+
#
|
18
|
+
# (3) Neither the name of the copyright holder nor the names of any contributors
|
19
|
+
# may be used to endorse or promote products derived from this software without
|
20
|
+
# specific prior written permission from the respective party.
|
21
|
+
#
|
22
|
+
# (4) Other than as required in clauses (1) and (2), distributions in any form
|
23
|
+
# of modifications or other derivative works may not use the "OpenStudio"
|
24
|
+
# trademark, "OS", "os", or any other confusingly similar designation without
|
25
|
+
# specific prior written permission from Alliance for Sustainable Energy, LLC.
|
26
|
+
#
|
27
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS
|
28
|
+
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
29
|
+
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
30
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE
|
31
|
+
# UNITED STATES GOVERNMENT, OR THE UNITED STATES DEPARTMENT OF ENERGY, NOR ANY OF
|
32
|
+
# THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
33
|
+
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
34
|
+
# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
35
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
36
|
+
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
37
|
+
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
38
|
+
# *******************************************************************************
|
39
|
+
require 'rexml/document'
|
40
|
+
|
41
|
+
require 'openstudio/common_measures'
|
42
|
+
require 'openstudio/model_articulation'
|
43
|
+
require 'openstudio/ee_measures'
|
44
|
+
|
45
|
+
require 'buildingsync/extension'
|
46
|
+
require 'buildingsync/constants'
|
47
|
+
require 'buildingsync/scenario'
|
48
|
+
require 'buildingsync/makers/workflow_maker_base'
|
49
|
+
require 'buildingsync/model_articulation/facility'
|
50
|
+
|
51
|
+
module BuildingSync
|
52
|
+
# base class for objects that will configure workflows based on building sync files
|
53
|
+
class WorkflowMaker < WorkflowMakerBase
|
54
|
+
# initialize - load workflow json file and add necessary measure paths
|
55
|
+
# @param doc [REXML::Document]
|
56
|
+
# @param ns [String]
|
57
|
+
def initialize(doc, ns)
|
58
|
+
super(doc, ns)
|
59
|
+
|
60
|
+
@facility_xml = nil
|
61
|
+
@facility = nil
|
62
|
+
|
63
|
+
# TODO: Be consistent in symbolizing names in hashes or not
|
64
|
+
File.open(PHASE_0_BASE_OSW_FILE_PATH, 'r') do |file|
|
65
|
+
@workflow = JSON.parse(file.read)
|
66
|
+
end
|
67
|
+
|
68
|
+
File.open(WORKFLOW_MAKER_JSON_FILE_PATH, 'r') do |file|
|
69
|
+
@workflow_maker_json = JSON.parse(file.read, symbolize_names: true)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Add all of the measure directories from the extension gems
|
73
|
+
# into the @workflow, then check they exist
|
74
|
+
set_measure_paths(get_measure_directories_array)
|
75
|
+
measures_exist?
|
76
|
+
read_xml
|
77
|
+
end
|
78
|
+
|
79
|
+
def read_xml
|
80
|
+
facility_xml_temp = @doc.get_elements("#{get_prefix}BuildingSync/#{get_prefix}Facilities/#{get_prefix}Facility")
|
81
|
+
|
82
|
+
# Raise errors for zero or multiple Facilities. Not supported at this time.
|
83
|
+
if facility_xml_temp.nil? || facility_xml_temp.empty?
|
84
|
+
OpenStudio.logFree(OpenStudio::Error, 'BuildingSync.WorkflowMaker.populate_facility_report_and_scenarios', 'There are no Facility elements in your BuildingSync file.')
|
85
|
+
raise StandardError, 'There are no Facility elements in your BuildingSync file.'
|
86
|
+
elsif facility_xml_temp.size > 1
|
87
|
+
@facility_xml = facility_xml_temp.first
|
88
|
+
OpenStudio.logFree(OpenStudio::Warn, 'BuildingSync.WorkflowMaker.populate_facility_report_and_scenarios', "There are more than one (#{facility_xml_temp.size}) Facility elements in your BuildingSync file. Only the first Facility will be considered (ID: #{@facility_xml.attributes['ID']}")
|
89
|
+
else
|
90
|
+
@facility_xml = facility_xml_temp.first
|
91
|
+
end
|
92
|
+
|
93
|
+
OpenStudio.logFree(OpenStudio::Info, 'BuildingSync.WorkflowMaker.read_xml', "Setting up workflow for Facility ID: #{@facility_xml.attributes['ID']}")
|
94
|
+
|
95
|
+
# Initialize Facility object
|
96
|
+
@facility = BuildingSync::Facility.new(@facility_xml, @ns)
|
97
|
+
end
|
98
|
+
|
99
|
+
# get the facility object from this workflow
|
100
|
+
# @return [BuildingSync::Facility] facility
|
101
|
+
def get_facility
|
102
|
+
return @facility
|
103
|
+
end
|
104
|
+
|
105
|
+
# get the space types of the facility
|
106
|
+
# @return [Vector<OpenStudio::Model::SpaceType>] vector of space types
|
107
|
+
def get_space_types
|
108
|
+
return @facility.get_space_types
|
109
|
+
end
|
110
|
+
|
111
|
+
# get model
|
112
|
+
# @return [OpenStudio::Model] model
|
113
|
+
def get_model
|
114
|
+
return @facility.get_model
|
115
|
+
end
|
116
|
+
|
117
|
+
# get the current workflow
|
118
|
+
# @return [Hash]
|
119
|
+
def get_workflow
|
120
|
+
return @workflow
|
121
|
+
end
|
122
|
+
|
123
|
+
# get scenario elements
|
124
|
+
# @return [Array<BuildingSync::Scenario>]
|
125
|
+
def get_scenarios
|
126
|
+
return @facility.report.scenarios
|
127
|
+
end
|
128
|
+
|
129
|
+
# generate the baseline model as osm model
|
130
|
+
# @param dir [String]
|
131
|
+
# @param epw_file_path [String]
|
132
|
+
# @param standard_to_be_used [String] 'ASHRAE90.1' or 'CaliforniaTitle24' are supported options
|
133
|
+
# @param ddy_file [String] path to the ddy file
|
134
|
+
# @return @see BuildingSync::Facility.write_osm
|
135
|
+
def setup_and_sizing_run(dir, epw_file_path, standard_to_be_used, ddy_file = nil)
|
136
|
+
@facility.set_all
|
137
|
+
@facility.determine_open_studio_standard(standard_to_be_used)
|
138
|
+
@facility.generate_baseline_osm(epw_file_path, dir, standard_to_be_used, ddy_file)
|
139
|
+
@facility.write_osm(dir)
|
140
|
+
end
|
141
|
+
|
142
|
+
# writes the parameters determined during processing back to the BldgSync XML file
|
143
|
+
def prepare_final_xml
|
144
|
+
@facility.prepare_final_xml
|
145
|
+
end
|
146
|
+
|
147
|
+
# # write osm
|
148
|
+
# # @param dir [String]
|
149
|
+
# def write_osm(dir)
|
150
|
+
# @scenario_types = @facility.write_osm(dir)
|
151
|
+
# end
|
152
|
+
|
153
|
+
# iterate over the current measure list in the workflow and check if they are available at the referenced measure directories
|
154
|
+
# @return [Boolean]
|
155
|
+
def measures_exist?
|
156
|
+
all_measures_found = true
|
157
|
+
number_measures_found = 0
|
158
|
+
@workflow['steps'].each do |step|
|
159
|
+
measure_is_valid = false
|
160
|
+
measure_dir_name = step['measure_dir_name']
|
161
|
+
get_measure_directories_array.each do |potential_measure_path|
|
162
|
+
measure_dir_full_path = "#{potential_measure_path}/#{measure_dir_name}"
|
163
|
+
if Dir.exist?(measure_dir_full_path)
|
164
|
+
measure_is_valid = true
|
165
|
+
OpenStudio.logFree(OpenStudio::Info, 'BuildingSync.WorkflowMaker.measures_exist?', "Measure: #{measure_dir_name} found at: #{measure_dir_full_path}")
|
166
|
+
number_measures_found += 1
|
167
|
+
break
|
168
|
+
end
|
169
|
+
end
|
170
|
+
if !measure_is_valid
|
171
|
+
all_measures_found = false
|
172
|
+
OpenStudio.logFree(OpenStudio::Error, 'BuildingSync.WorkflowMaker.measures_exist?', "CANNOT find measure with name (#{measure_dir_name}) in any of the measure paths ")
|
173
|
+
end
|
174
|
+
end
|
175
|
+
if all_measures_found
|
176
|
+
OpenStudio.logFree(OpenStudio::Info, 'BuildingSync.WorkflowMaker.measures_exist?', "Total measures found: #{number_measures_found}. All measures defined by @workflow found.")
|
177
|
+
puts "Total measures found: #{number_measures_found}. All measures defined by @workflow found."
|
178
|
+
end
|
179
|
+
return all_measures_found
|
180
|
+
end
|
181
|
+
|
182
|
+
# gets all available measures across all measure directories
|
183
|
+
# @return [hash] Looks as follows {path_to_measure_dir: [measure_name1, mn2, etc.], path_to_measure_dir_2: [...]}
|
184
|
+
def get_available_measures_hash
|
185
|
+
measures_hash = {}
|
186
|
+
get_measure_directories_array.each do |potential_measure_path|
|
187
|
+
Dir.chdir(potential_measure_path) do
|
188
|
+
measures_hash[potential_measure_path] = Dir.glob('*').select { |f| File.directory? f }
|
189
|
+
end
|
190
|
+
end
|
191
|
+
return measures_hash
|
192
|
+
end
|
193
|
+
|
194
|
+
# collect all measure directories that contain measures needed for BldgSync
|
195
|
+
# @return [array] of measure dirs
|
196
|
+
def get_measure_directories_array
|
197
|
+
common_measures_instance = OpenStudio::CommonMeasures::Extension.new
|
198
|
+
model_articulation_instance = OpenStudio::ModelArticulation::Extension.new
|
199
|
+
ee_measures_instance = OpenStudio::EeMeasures::Extension.new
|
200
|
+
bldg_sync_instance = BuildingSync::Extension.new
|
201
|
+
return [common_measures_instance.measures_dir, model_articulation_instance.measures_dir, bldg_sync_instance.measures_dir, ee_measures_instance.measures_dir]
|
202
|
+
end
|
203
|
+
|
204
|
+
# inserts any measure. traverses through the measures available in the included extensions
|
205
|
+
# (common measures, model articulation, etc.) to find the lib/measures/[measure_dir] specified.
|
206
|
+
# It is inserted at the relative position according to its type
|
207
|
+
# @param measure_goal_type [String] one of: 'EnergyPlusMeasure', 'ReportingMeasure', or 'ModelMeasure'
|
208
|
+
# @param measure_dir_name [String] the directory name for the measure, as it appears
|
209
|
+
# in any of the gems, i.e. openstudio-common-measures-gem/lib/measures/[measure_dir_name]
|
210
|
+
# @param relative_position [Integer] the position where the measure should be inserted with respect to the measure_goal_type
|
211
|
+
# @param args_hash [hash]
|
212
|
+
def insert_measure_into_workflow(measure_goal_type, measure_dir_name, relative_position = 0, args_hash = {})
|
213
|
+
successfully_added = false
|
214
|
+
count = 0 # count for all of the measures, regardless of the type
|
215
|
+
measure_type_count = 0 # count of measures specific to the measure_goal_type
|
216
|
+
measure_type_found = false
|
217
|
+
new_step = {}
|
218
|
+
new_step['measure_dir_name'] = measure_dir_name
|
219
|
+
new_step['arguments'] = args_hash
|
220
|
+
if @workflow['steps'].empty?
|
221
|
+
@workflow['steps'].insert(count, new_step)
|
222
|
+
successfully_added = true
|
223
|
+
else
|
224
|
+
@workflow['steps'].each do |step|
|
225
|
+
measure_dir_name = step['measure_dir_name']
|
226
|
+
measure_type = get_measure_type(measure_dir_name)
|
227
|
+
OpenStudio.logFree(OpenStudio::Info, 'BuildingSync.WorkflowMaker.insert_measure_into_workflow', "measure: #{measure_dir_name} with type: #{measure_type} found")
|
228
|
+
if measure_type == measure_goal_type
|
229
|
+
measure_type_found = true
|
230
|
+
if measure_type_count == relative_position
|
231
|
+
# insert measure here
|
232
|
+
OpenStudio.logFree(OpenStudio::Info, 'BuildingSync.WorkflowMaker.insert_measure_into_workflow', "inserting measure with type (#{measure_goal_type}) at position #{count} and dir: #{measure_dir_name} and type: #{get_measure_type(measure_dir_name)}")
|
233
|
+
puts "inserting measure with type (#{measure_goal_type}) at position #{count} and dir: #{measure_dir_name} and type: #{get_measure_type(measure_dir_name)}"
|
234
|
+
@workflow['steps'].insert(count, new_step)
|
235
|
+
successfully_added = true
|
236
|
+
break
|
237
|
+
end
|
238
|
+
measure_type_count += 1
|
239
|
+
elsif measure_type_found
|
240
|
+
OpenStudio.logFree(OpenStudio::Info, 'BuildingSync.WorkflowMaker.insert_measure_into_workflow', "inserting measure with type (#{measure_goal_type})at position #{count} and dir: #{measure_dir_name} and type: #{get_measure_type(measure_dir_name)}")
|
241
|
+
puts "inserting measure with type (#{measure_goal_type}) at position #{count} and dir: #{measure_dir_name} and type: #{get_measure_type(measure_dir_name)}"
|
242
|
+
@workflow['steps'].insert(count - 1, new_step)
|
243
|
+
successfully_added = true
|
244
|
+
break
|
245
|
+
end
|
246
|
+
count += 1
|
247
|
+
end
|
248
|
+
end
|
249
|
+
if !successfully_added
|
250
|
+
OpenStudio.logFree(OpenStudio::Error, 'BuildingSync.WorkflowMakerPhaseZero.insert_measure_into_workflow', "CANNOT insert measure with type (#{measure_goal_type}) at position #{count} and dir: #{measure_dir_name} and type: #{get_measure_type(measure_dir_name)}")
|
251
|
+
end
|
252
|
+
return successfully_added
|
253
|
+
end
|
254
|
+
|
255
|
+
# gets the measure type of a measure given its directory - looking up the measure type in the measure.xml file
|
256
|
+
# @param measure_dir_name [String] the directory name for the measure, as it appears
|
257
|
+
# in any of the gems, i.e. openstudio-common-measures-gem/lib/measures/[measure_dir_name]
|
258
|
+
# @return [String]
|
259
|
+
def get_measure_type(measure_dir_name)
|
260
|
+
measure_type = nil
|
261
|
+
get_measure_directories_array.each do |potential_measure_path|
|
262
|
+
measure_dir_full_path = "#{potential_measure_path}/#{measure_dir_name}"
|
263
|
+
if Dir.exist?(measure_dir_full_path)
|
264
|
+
measure_xml_doc = nil
|
265
|
+
File.open(measure_dir_full_path + '/measure.xml', 'r') do |file|
|
266
|
+
measure_xml_doc = REXML::Document.new(file)
|
267
|
+
end
|
268
|
+
measure_xml_doc.elements.each('/measure/attributes/attribute') do |attribute|
|
269
|
+
attribute_name = attribute.elements['name'].text
|
270
|
+
if attribute_name == 'Measure Type'
|
271
|
+
measure_type = attribute.elements['value'].text
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
return measure_type
|
277
|
+
end
|
278
|
+
|
279
|
+
# Based on the MeasureIDs defined by the Scenario, configure the workflow provided
|
280
|
+
# using the default measure arguments defined by the lib/buildingsync/makers/workflow_maker.json
|
281
|
+
# @param base_workflow [Hash] a Hash map of the @workflow. DO NOT use @workflow directly, should be a deep clone
|
282
|
+
# @param scenario [BuildingSync::Scenario] a Scenario object
|
283
|
+
def configure_workflow_for_scenario(base_workflow, scenario)
|
284
|
+
successful = true
|
285
|
+
|
286
|
+
num_measures = 0
|
287
|
+
scenario.get_measure_ids.each do |measure_id|
|
288
|
+
measure = @facility.measures.find { |m| m.xget_id == measure_id }
|
289
|
+
current_num_measure = num_measures
|
290
|
+
|
291
|
+
sym_to_find = measure.xget_text('SystemCategoryAffected')
|
292
|
+
if sym_to_find.nil? || sym_to_find.empty?
|
293
|
+
OpenStudio.logFree(OpenStudio::Warn, 'BuildingSync.WorkflowMaker.configure_workflow_for_scenario', "Measure ID: #{measure.xget_id} does not define a SystemCategoryAffected.")
|
294
|
+
successful = false
|
295
|
+
else
|
296
|
+
sym_to_find = sym_to_find.to_s.to_sym
|
297
|
+
end
|
298
|
+
|
299
|
+
# 'Other HVAC' or 'Cooling System' as examples
|
300
|
+
categories_found = @workflow_maker_json.key?(sym_to_find)
|
301
|
+
if categories_found
|
302
|
+
m_name = measure.xget_name
|
303
|
+
|
304
|
+
if m_name.nil? || m_name.empty?
|
305
|
+
OpenStudio.logFree(OpenStudio::Warn, 'BuildingSync.WorkflowMaker.configure_workflow_for_scenario', "Measure ID: #{measure.xget_id} does not have a MeasureName defined.")
|
306
|
+
successful = false
|
307
|
+
else
|
308
|
+
m_name = m_name.to_sym
|
309
|
+
end
|
310
|
+
|
311
|
+
# Where standardized measure names have not been adopted as enumerations
|
312
|
+
# in the BuildingSync Schema, a <MeasureName>Other</MeasureName> is used
|
313
|
+
# and the actual measure name added
|
314
|
+
if m_name == :Other
|
315
|
+
m_name = measure.xget_text('CustomMeasureName')
|
316
|
+
if m_name.nil? || m_name.empty?
|
317
|
+
OpenStudio.logFree(OpenStudio::Warn, 'BuildingSync.WorkflowMaker.configure_workflow_for_scenario', "Measure ID: #{measure.xget_id} has a MeasureName of 'Other' but does not have a CustomMeasureName defined.")
|
318
|
+
successful = false
|
319
|
+
else
|
320
|
+
m_name = m_name.to_sym
|
321
|
+
end
|
322
|
+
end
|
323
|
+
measure_found = false
|
324
|
+
@workflow_maker_json[sym_to_find].each do |category|
|
325
|
+
# m_name is, for example: 'Replace HVAC system type to VRF'
|
326
|
+
|
327
|
+
if !category[m_name].nil?
|
328
|
+
measure_found = true
|
329
|
+
measure_dir_name = category[m_name][:measure_dir_name]
|
330
|
+
num_measures += 1
|
331
|
+
category[m_name][:arguments].each do |argument|
|
332
|
+
# Certain arguments are only applied under specific conditions
|
333
|
+
#
|
334
|
+
if !argument[:condition].nil? && !argument[:condition].empty?
|
335
|
+
set_argument_detail(base_workflow, argument, measure_dir_name, m_name.to_s)
|
336
|
+
else
|
337
|
+
set_measure_argument(base_workflow, measure_dir_name, argument[:name], argument[:value])
|
338
|
+
end
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end
|
342
|
+
if !measure_found
|
343
|
+
OpenStudio.logFree(OpenStudio::Warn, 'BuildingSync.WorkflowMaker.configure_workflow_for_scenario', "Could not find measure '#{m_name}' under category #{sym_to_find} in workflow_maker.json.")
|
344
|
+
end
|
345
|
+
else
|
346
|
+
OpenStudio.logFree(OpenStudio::Warn, 'BuildingSync.WorkflowMaker.configure_workflow_for_scenario', "Category: #{measure.xget_text('SystemCategoryAffected')} not found in workflow_maker.json.")
|
347
|
+
end
|
348
|
+
|
349
|
+
if current_num_measure == num_measures
|
350
|
+
OpenStudio.logFree(OpenStudio::Warn, 'BuildingSync.WorkflowMaker.configure_workflow_for_scenario', "Measure ID: #{measure.xget_id} could not be processed!")
|
351
|
+
successful = false
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
# ensure that we didn't miss any measures by accident
|
356
|
+
OpenStudio.logFree(OpenStudio::Error, 'BuildingSync.WorkflowMaker.configure_workflow_for_scenario', "#{scenario.get_measure_ids.size} measures expected, #{num_measures} resolved, expected measure_ids = #{scenario.get_measure_ids}") if num_measures != scenario.get_measure_ids.size
|
357
|
+
return successful
|
358
|
+
end
|
359
|
+
|
360
|
+
# TODO: Update this as I believe no longer will work as expected, keys being searched for
|
361
|
+
# by the @facility_xml['key'] don't make sense.
|
362
|
+
# set argument details, used when the condition
|
363
|
+
# @param workflow [Hash] a hash of the openstudio workflow
|
364
|
+
# @param argument [Hash]
|
365
|
+
# @param measure_dir_name [String] the directory name for the measure, as it appears
|
366
|
+
# in any of the gems, i.e. openstudio-common-measures-gem/lib/measures/[measure_dir_name]
|
367
|
+
# @param measure_name [String]
|
368
|
+
def set_argument_detail(workflow, argument, measure_dir_name, measure_name)
|
369
|
+
argument_name = ''
|
370
|
+
argument_value = ''
|
371
|
+
|
372
|
+
if measure_name == 'Add daylight controls' || measure_name == 'Replace HVAC system type to PZHP'
|
373
|
+
# For these measures, the condition is based on the standards building type determined
|
374
|
+
if argument[:condition] == @facility.site.get_building_type
|
375
|
+
argument_name = argument[:name]
|
376
|
+
|
377
|
+
# This is a really terrible way to do this. It fails
|
378
|
+
# in many scenarios
|
379
|
+
argument_value = "#{argument[:value]} #{@facility.site.get_standard_template}"
|
380
|
+
end
|
381
|
+
elsif measure_name == 'Replace burner'
|
382
|
+
if argument[:condition] == @facility.site.get_system_type
|
383
|
+
argument_name = argument[:name]
|
384
|
+
argument_value = argument[:value]
|
385
|
+
end
|
386
|
+
elsif measure_name == 'Replace boiler'
|
387
|
+
if argument[:condition] == @facility.site.get_system_type
|
388
|
+
argument_name = argument[:name]
|
389
|
+
argument_value = argument[:value]
|
390
|
+
end
|
391
|
+
elsif measure_name == 'Replace package units'
|
392
|
+
if argument[:condition] == @facility.site.get_system_type
|
393
|
+
argument_name = argument[:name]
|
394
|
+
argument_value = argument[:value]
|
395
|
+
end
|
396
|
+
elsif measure_name == 'Replace HVAC system type to VRF' || measure_name == 'Replace HVAC with GSHP and DOAS' || measure_name == 'Replace AC and heating units with ground coupled heat pump systems'
|
397
|
+
if argument[:condition] == @facility.site.get_building_type
|
398
|
+
argument_name = (argument[:name]).to_s
|
399
|
+
argument_value = argument[:value]
|
400
|
+
end
|
401
|
+
else
|
402
|
+
OpenStudio.logFree(OpenStudio::Error, 'BuildingSync.WorkflowMaker.set_argument_detail', "measure dir name not found #{measure_name}.")
|
403
|
+
puts "BuildingSync.WorkflowMaker.set_argument_detail: Measure dir name not found #{measure_name}."
|
404
|
+
end
|
405
|
+
|
406
|
+
set_measure_argument(workflow, measure_dir_name, argument_name, argument_value) if !argument_name.nil? && !argument_name.empty?
|
407
|
+
end
|
408
|
+
|
409
|
+
# write workflows for scenarios into osw files. This includes:
|
410
|
+
# - Package of Measure Scenarios
|
411
|
+
# - Current Building Modeled (Baseline) Scenario
|
412
|
+
# @param main_output_dir [String] main output path, not scenario specific. i.e. SR should be a subdirectory
|
413
|
+
# @return [Boolean] whether writing of all the new workflows was successful
|
414
|
+
def write_osws(main_output_dir, only_cb_modeled = false)
|
415
|
+
# make sure paths exist
|
416
|
+
FileUtils.mkdir_p(main_output_dir)
|
417
|
+
|
418
|
+
if @facility.report.cb_modeled.nil?
|
419
|
+
OpenStudio.logFree(OpenStudio::Error, 'BuildingSync.WorkflowMaker.write_osws', 'OSW cannot be written since no current building modeled scenario is defined. One can be added after file import using the add_cb_modeled method')
|
420
|
+
raise StandardError, 'BuildingSync.WorkflowMaker.write_osws: OSW cannot be written since no current building modeled scenario is defined. One can be added after file import using the add_cb_modeled method'
|
421
|
+
end
|
422
|
+
|
423
|
+
# Write a workflow for the current building modeled scenario
|
424
|
+
cb_modeled_success = write_osw(main_output_dir, @facility.report.cb_modeled)
|
425
|
+
|
426
|
+
if !cb_modeled_success
|
427
|
+
OpenStudio.logFree(OpenStudio::Error, 'BuildingSync.WorkflowMaker.write_osws', 'A workflow was not successfully written for the cb_modeled (Current Building Modeled) Scenario.')
|
428
|
+
raise StandardError, 'BuildingSync.WorkflowMaker.write_osws: A workflow was not successfully written for the cb_modeled (Current Building Modeled) Scenario.'
|
429
|
+
end
|
430
|
+
|
431
|
+
number_successful = cb_modeled_success ? 1 : 0
|
432
|
+
|
433
|
+
if !only_cb_modeled
|
434
|
+
# write an osw for each Package Of Measures scenario
|
435
|
+
@facility.report.poms.each do |scenario|
|
436
|
+
successful = write_osw(main_output_dir, scenario)
|
437
|
+
if successful
|
438
|
+
number_successful += 1
|
439
|
+
else
|
440
|
+
OpenStudio.logFree(OpenStudio::Error, 'BuildingSync.WorkflowMaker.write_osws', "Scenario ID: #{scenario.xget_id}. Unsuccessful write_osw")
|
441
|
+
end
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
# Compare the total number of potential successes to the number of actual successes
|
446
|
+
if only_cb_modeled
|
447
|
+
# In this case we should have only 1 success
|
448
|
+
expected_successes = 1
|
449
|
+
really_successful = number_successful == expected_successes
|
450
|
+
else
|
451
|
+
# In this case, all pom scenarios should be run + the cb_modeled scenario
|
452
|
+
expected_successes = @facility.report.poms.size + 1
|
453
|
+
really_successful = number_successful == expected_successes
|
454
|
+
end
|
455
|
+
|
456
|
+
if !really_successful
|
457
|
+
OpenStudio.logFree(OpenStudio::Error, 'BuildingSync.WorkflowMaker.write_osws', "Facility ID: #{@facility.xget_id}. Expected #{expected_successes}, Got #{number_successful} OSWs")
|
458
|
+
end
|
459
|
+
|
460
|
+
return really_successful
|
461
|
+
end
|
462
|
+
|
463
|
+
# Write an OSW for the provided scenario
|
464
|
+
# @param main_output_dir [String] main output path, not scenario specific. i.e. SR should be a subdirectory
|
465
|
+
# @param [BuildingSync::Scenario]
|
466
|
+
# @return [Boolean] whether the writing was successful
|
467
|
+
def write_osw(main_output_dir, scenario)
|
468
|
+
successful = true
|
469
|
+
# deep clone
|
470
|
+
base_workflow = deep_copy_workflow
|
471
|
+
|
472
|
+
# configure the workflow based on measures in this scenario
|
473
|
+
begin
|
474
|
+
# The workflow is updated by configure_workflow, put with pass by reference
|
475
|
+
# we are ok to use it later without returning
|
476
|
+
if !configure_workflow_for_scenario(base_workflow, scenario)
|
477
|
+
successful = false
|
478
|
+
OpenStudio.logFree(OpenStudio::Error, 'BuildingSync.WorkflowMaker.write_osw', "Could not configure workflow for scenario #{scenario.xget_name}")
|
479
|
+
else
|
480
|
+
purge_skipped_from_workflow(base_workflow)
|
481
|
+
scenario.set_workflow(base_workflow)
|
482
|
+
scenario.write_osw(main_output_dir)
|
483
|
+
end
|
484
|
+
rescue StandardError => e
|
485
|
+
OpenStudio.logFree(OpenStudio::Error, 'BuildingSync.WorkflowMaker.write_osw', "Could not configure for scenario #{scenario.xget_name}. Error: #{e}")
|
486
|
+
puts "Could not configure for scenario #{scenario.xget_name}"
|
487
|
+
puts e.backtrace.join("\n\t")
|
488
|
+
successful = false
|
489
|
+
end
|
490
|
+
return successful
|
491
|
+
end
|
492
|
+
|
493
|
+
# run osws - running all scenario simulations
|
494
|
+
# @param only_cb_modeled [Boolean] used to only run the simulations for the cb_modeled (baseline) scenario
|
495
|
+
# @param runner_options [hash]
|
496
|
+
def run_osws(output_dir, only_cb_modeled = false, runner_options = { run_simulations: true, verbose: false, num_parallel: 7, max_to_run: Float::INFINITY })
|
497
|
+
osw_files = []
|
498
|
+
osw_sr_files = []
|
499
|
+
if only_cb_modeled
|
500
|
+
osw_files << "#{@facility.report.cb_modeled.get_osw_dir}/in.osw"
|
501
|
+
else
|
502
|
+
Dir.glob("#{output_dir}/**/in.osw") { |osw| osw_files << osw }
|
503
|
+
end
|
504
|
+
Dir.glob("#{output_dir}/SR/in.osw") { |osw| osw_sr_files << osw }
|
505
|
+
|
506
|
+
runner = OpenStudio::Extension::Runner.new(dirname = Dir.pwd, bundle_without = [], options = runner_options)
|
507
|
+
|
508
|
+
# This doesn't run the workflow defined by the Sizing Run
|
509
|
+
return runner.run_osws(osw_files - osw_sr_files)
|
510
|
+
end
|
511
|
+
|
512
|
+
# Creates a deep copy of the @workflow be serializing and reloading with JSON
|
513
|
+
# @return [Hash] a new workflow object
|
514
|
+
def deep_copy_workflow
|
515
|
+
return JSON.load(JSON.generate(@workflow))
|
516
|
+
end
|
517
|
+
|
518
|
+
# Removes unused measures from a workflow, where __SKIP__ == true
|
519
|
+
# @param workflow [Hash] a hash of the openstudio workflow, typically after a deep
|
520
|
+
# copy is made and the measures are configured for the specific scenario
|
521
|
+
def purge_skipped_from_workflow(workflow)
|
522
|
+
non_skipped = []
|
523
|
+
if !workflow.nil? && !workflow['steps'].nil? && workflow.key?('steps')
|
524
|
+
workflow['steps'].each do |step|
|
525
|
+
if !step.nil? && step.key?('arguments') && !step['arguments'].nil?
|
526
|
+
if step['arguments'].key?('__SKIP__') && step['arguments']['__SKIP__'] == false
|
527
|
+
non_skipped << step
|
528
|
+
end
|
529
|
+
end
|
530
|
+
end
|
531
|
+
workflow['steps'] = non_skipped
|
532
|
+
end
|
533
|
+
end
|
534
|
+
|
535
|
+
# get failed scenarios
|
536
|
+
# @return [Array<BuildingSync::Scenario>]
|
537
|
+
def get_failed_scenarios
|
538
|
+
failed = []
|
539
|
+
@facility.report.scenarios.each do |scenario|
|
540
|
+
failed << scenario if !scenario.simulation_success?
|
541
|
+
end
|
542
|
+
return failed
|
543
|
+
end
|
544
|
+
|
545
|
+
# cleanup larger files
|
546
|
+
# @param osw_dir [String]
|
547
|
+
def cleanup_larger_files(osw_dir)
|
548
|
+
path = File.join(osw_dir, 'eplusout.sql')
|
549
|
+
FileUtils.rm_f(path) if File.exist?(path)
|
550
|
+
path = File.join(osw_dir, 'data_point.zip')
|
551
|
+
FileUtils.rm_f(path) if File.exist?(path)
|
552
|
+
path = File.join(osw_dir, 'eplusout.eso')
|
553
|
+
FileUtils.rm_f(path) if File.exist?(path)
|
554
|
+
Dir.glob(File.join(osw_dir, '*create_typical_building_from_model*')).each do |path|
|
555
|
+
FileUtils.rm_rf(path) if File.exist?(path)
|
556
|
+
end
|
557
|
+
Dir.glob(File.join(osw_dir, '*create_typical_building_from_model*')).each do |path|
|
558
|
+
FileUtils.rm_rf(path) if File.exist?(path)
|
559
|
+
end
|
560
|
+
end
|
561
|
+
|
562
|
+
# gather results for all CB Modeled and POM Scenarios, including both annual and monthly results
|
563
|
+
# - ResourceUse and AllResourceTotal elements are added to the Scenario as part of this process
|
564
|
+
# - ResourceUse - holds consumption information about a specific resource / fuel (Electricity, Natural gas, etc.)
|
565
|
+
# - AllResourceTotal - holds total site and source energy consumption information
|
566
|
+
# @param year_val [Integer]
|
567
|
+
# @param baseline_only [Boolean]
|
568
|
+
# @return [Boolean]
|
569
|
+
def gather_results(year_val = Date.today.year, baseline_only = false)
|
570
|
+
# Gather results for the Current Building Modeled (Baseline) Scenario
|
571
|
+
@facility.report.cb_modeled.os_gather_results(year_val)
|
572
|
+
|
573
|
+
if !baseline_only
|
574
|
+
# Gather results for the Package of Measures scenarios
|
575
|
+
@facility.report.poms.each do |scenario|
|
576
|
+
scenario.os_gather_results(year_val)
|
577
|
+
end
|
578
|
+
end
|
579
|
+
end
|
580
|
+
end
|
581
|
+
end
|