buildingsync 0.2.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.
- 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,55 @@
|
|
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
|
+
|
40
|
+
module BuildingSync
|
41
|
+
# ResourceUse class
|
42
|
+
class ResourceUse
|
43
|
+
include BuildingSync::Helper
|
44
|
+
include BuildingSync::XmlGetSet
|
45
|
+
# initialize
|
46
|
+
# @param @base_xml [REXML::Element]
|
47
|
+
# @param ns [String]
|
48
|
+
def initialize(base_xml, ns)
|
49
|
+
@base_xml = base_xml
|
50
|
+
@ns = ns
|
51
|
+
|
52
|
+
help_element_class_type_check(base_xml, 'ResourceUse')
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,622 @@
|
|
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/element'
|
40
|
+
require 'securerandom'
|
41
|
+
|
42
|
+
require 'buildingsync/helpers/helper'
|
43
|
+
require 'buildingsync/helpers/xml_get_set'
|
44
|
+
require 'buildingsync/resource_use'
|
45
|
+
require 'buildingsync/all_resource_total'
|
46
|
+
require 'buildingsync/time_series'
|
47
|
+
|
48
|
+
module BuildingSync
|
49
|
+
# Scenario class
|
50
|
+
class Scenario
|
51
|
+
include BuildingSync::Helper
|
52
|
+
include BuildingSync::XmlGetSet
|
53
|
+
|
54
|
+
def initialize(base_xml, ns)
|
55
|
+
@base_xml = base_xml
|
56
|
+
@ns = ns
|
57
|
+
|
58
|
+
help_element_class_type_check(base_xml, 'Scenario')
|
59
|
+
|
60
|
+
# Helpful
|
61
|
+
@site_eui_xpath = "#{@ns}:AllResourceTotals/#{@ns}:AllResourceTotal/#{@ns}:SiteEnergyUseIntensity"
|
62
|
+
@g = BuildingSync::Generator.new(@ns)
|
63
|
+
|
64
|
+
# linked fields
|
65
|
+
@resource_uses = [] # Array[<BuildingSync::ResourceUse>]
|
66
|
+
@time_series_data = [] # Array[<BuildingSync::TimeSeries]
|
67
|
+
@all_resource_totals = [] # Array[<REXML::Element>] of AllResourceTotal
|
68
|
+
|
69
|
+
# Simulation relevant fields
|
70
|
+
@main_output_dir = nil
|
71
|
+
@osw_dir = nil
|
72
|
+
@workflow = {} # Hash to hold the workflow, see set_workflow
|
73
|
+
@results_file_name = 'results.json' # holds annual and monthly results
|
74
|
+
@eplustbl_file_name = 'eplustbl.htm' # holds source energy results
|
75
|
+
@out_osw_file_name = 'out.osw' # holds the completion status of the simulation
|
76
|
+
@out_osw_json = nil # Hash to hold the read in of out.osw
|
77
|
+
@results_json = nil # Hash to hold the read in of results.json
|
78
|
+
|
79
|
+
# Define mappings for native units by Resource Use
|
80
|
+
@native_units_map = {
|
81
|
+
'Electricity' => 'kWh',
|
82
|
+
'Natural gas' => 'kBtu'
|
83
|
+
}
|
84
|
+
|
85
|
+
# Define a mapping between BuildingSync concepts to openstudio concepts
|
86
|
+
# available in the results.json file
|
87
|
+
@bsync_openstudio_resources_map = {
|
88
|
+
'IP' => {
|
89
|
+
'ResourceUse' => [
|
90
|
+
{
|
91
|
+
'EnergyResource' => 'Electricity',
|
92
|
+
'EndUse' => 'All end uses',
|
93
|
+
'fields' => [
|
94
|
+
{
|
95
|
+
# AnnualFuelUseConsistentUnits is in MMBtu/yr
|
96
|
+
'bsync_element_name' => 'AnnualFuelUseConsistentUnits',
|
97
|
+
'bsync_element_units' => 'MMBtu',
|
98
|
+
'os_results_key' => 'fuel_electricity',
|
99
|
+
'os_results_unit' => 'kBtu'
|
100
|
+
},
|
101
|
+
{
|
102
|
+
'bsync_element_name' => 'AnnualPeakConsistentUnits',
|
103
|
+
'bsync_element_units' => 'kW',
|
104
|
+
'os_results_key' => 'annual_peak_electric_demand',
|
105
|
+
'os_results_unit' => 'kW'
|
106
|
+
}
|
107
|
+
],
|
108
|
+
'monthly' => {
|
109
|
+
# [bracket text] is replaced when processed
|
110
|
+
'text' => 'electricity_ip_[month]',
|
111
|
+
'os_results_unit' => 'kWh'
|
112
|
+
}
|
113
|
+
},
|
114
|
+
{
|
115
|
+
'EnergyResource' => 'Natural gas',
|
116
|
+
'EndUse' => 'All end uses',
|
117
|
+
'fields' => [
|
118
|
+
{
|
119
|
+
# AnnualFuelUseConsistentUnits is in MMBtu/yr
|
120
|
+
'bsync_element_name' => 'AnnualFuelUseConsistentUnits',
|
121
|
+
'bsync_element_units' => 'MMBtu',
|
122
|
+
'os_results_key' => 'fuel_natural_gas',
|
123
|
+
'os_results_unit' => 'kBtu'
|
124
|
+
}
|
125
|
+
],
|
126
|
+
'monthly' => {
|
127
|
+
# [bracket text] is replaced when processed
|
128
|
+
'text' => 'natural_gas_ip_[month]',
|
129
|
+
'os_results_unit' => 'MMBtu'
|
130
|
+
}
|
131
|
+
}
|
132
|
+
],
|
133
|
+
'AllResourceTotal' => [
|
134
|
+
{
|
135
|
+
'EndUse' => 'All end uses',
|
136
|
+
'fields' => [
|
137
|
+
{
|
138
|
+
'bsync_element_name' => 'SiteEnergyUse',
|
139
|
+
'bsync_element_units' => 'kBtu',
|
140
|
+
'os_results_key' => 'total_site_energy',
|
141
|
+
'os_results_unit' => 'kBtu'
|
142
|
+
},
|
143
|
+
{
|
144
|
+
'bsync_element_name' => 'SiteEnergyUseIntensity',
|
145
|
+
'bsync_element_units' => 'kBtu/ft^2',
|
146
|
+
'os_results_key' => 'total_site_eui',
|
147
|
+
'os_results_unit' => 'kBtu/ft^2'
|
148
|
+
}
|
149
|
+
]
|
150
|
+
}
|
151
|
+
]
|
152
|
+
}
|
153
|
+
}
|
154
|
+
|
155
|
+
read_xml
|
156
|
+
|
157
|
+
# Removes data from POM and CB Modeled on import
|
158
|
+
if !get_scenario_type_child_element.nil? && (pom? || cb_modeled?)
|
159
|
+
delete_previous_results
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def read_xml
|
164
|
+
# Read in data about ResourceUses, AllResourceTotals, and TimeSeriesData
|
165
|
+
read_resource_uses
|
166
|
+
read_all_resource_totals
|
167
|
+
read_time_series_data
|
168
|
+
end
|
169
|
+
|
170
|
+
# @return [REXML::Element]
|
171
|
+
def get_scenario_type_child_element
|
172
|
+
scenario_type = xget_element('ScenarioType')
|
173
|
+
if !scenario_type.nil?
|
174
|
+
scenario_type.get_elements('*')[0]
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# @return [Array<String>]
|
179
|
+
def get_measure_ids
|
180
|
+
return xget_idrefs('MeasureID')
|
181
|
+
end
|
182
|
+
|
183
|
+
# @return [Array<BuildingSync::ResourceUse>]
|
184
|
+
def get_resource_uses
|
185
|
+
return @resource_uses
|
186
|
+
end
|
187
|
+
|
188
|
+
def get_all_end_use_resource_uses
|
189
|
+
return @resource_uses.each.select { |ru| ru.xget_text('EndUse') == 'All end uses' }
|
190
|
+
end
|
191
|
+
|
192
|
+
# @return [Array<REXML::Element>]
|
193
|
+
def get_all_resource_totals
|
194
|
+
return @all_resource_totals
|
195
|
+
end
|
196
|
+
|
197
|
+
# @return [Array<BuildingSync::TimeSeries>]
|
198
|
+
def get_time_series_data
|
199
|
+
return @time_series_data
|
200
|
+
end
|
201
|
+
|
202
|
+
# @return [Hash]
|
203
|
+
def get_workflow
|
204
|
+
return @workflow
|
205
|
+
end
|
206
|
+
|
207
|
+
# @return [String]
|
208
|
+
def get_main_output_dir
|
209
|
+
return @main_output_dir
|
210
|
+
end
|
211
|
+
|
212
|
+
# get osw dir
|
213
|
+
# @return [String] directory to the new osw_dir
|
214
|
+
def get_osw_dir
|
215
|
+
return @osw_dir
|
216
|
+
end
|
217
|
+
|
218
|
+
def get_benchmark_tool
|
219
|
+
child = get_scenario_type_child_element
|
220
|
+
return help_get_text_value(child.elements["#{@ns}:BenchmarkTool"])
|
221
|
+
end
|
222
|
+
|
223
|
+
# @param workflow [Hash] a hash of the openstudio workflow
|
224
|
+
def set_workflow(workflow)
|
225
|
+
if !workflow.is_a?(Hash)
|
226
|
+
OpenStudio.logFree(OpenStudio::Error, 'BuildingSync.Scenario.set_workflow', "Scenario ID: #{xget_id}. Cannot set_workflow, argument must be a Hash.")
|
227
|
+
raise StandardError, "BuildingSync.Scenario.set_workflow Scenario ID: #{xget_id}. Cannot set_workflow, argument must be a Hash, not a #{workflow.class}"
|
228
|
+
else
|
229
|
+
@workflow = workflow
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def set_main_output_dir(main_output_dir)
|
234
|
+
@main_output_dir = main_output_dir
|
235
|
+
return @main_output_dir
|
236
|
+
end
|
237
|
+
|
238
|
+
def set_osw_dir(main_output_dir = @main_output_dir)
|
239
|
+
if !xget_name.nil?
|
240
|
+
to_use = xget_name
|
241
|
+
elsif !xget_id.nil?
|
242
|
+
to_use = xget_id
|
243
|
+
end
|
244
|
+
@osw_dir = File.join(main_output_dir, to_use)
|
245
|
+
return @osw_dir
|
246
|
+
end
|
247
|
+
|
248
|
+
# Create the @osw_dir
|
249
|
+
def osw_mkdir_p
|
250
|
+
if !@osw_dir.nil?
|
251
|
+
FileUtils.mkdir_p(@osw_dir)
|
252
|
+
else
|
253
|
+
OpenStudio.logFree(OpenStudio::Error, 'BuildingSync.Scenario.osw_mkdir_p', "Scenario ID: #{xget_id}. @osw_dir must be set first")
|
254
|
+
raise StandardError, "BuildingSync.Scenario.osw_mkdir_p Scenario ID: #{xget_id}. @osw_dir must be set first"
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
# Use the @workflow definition to write a new ' in.osw ' file.
|
259
|
+
# The main_output_dir and osw_dir are set and created if not existing.
|
260
|
+
# @param main_output_dir [String] path to the main output directory to use
|
261
|
+
def write_osw(main_output_dir = @main_output_dir)
|
262
|
+
set_main_output_dir(main_output_dir)
|
263
|
+
set_osw_dir(main_output_dir)
|
264
|
+
osw_mkdir_p
|
265
|
+
# write the osw
|
266
|
+
path = File.join(@osw_dir, 'in.osw')
|
267
|
+
File.open(path, 'w') do |file|
|
268
|
+
file << JSON.pretty_generate(@workflow)
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
# delete previous results from the Scenario. This only affects POM or cb_modeled scenarios,
|
273
|
+
# unless all = true is passed
|
274
|
+
def delete_previous_results(all = false)
|
275
|
+
if pom?
|
276
|
+
get_scenario_type_child_element.elements.delete("#{@ns}:AnnualSavingsSiteEnergy")
|
277
|
+
get_scenario_type_child_element.elements.delete("#{@ns}:AnnualSavingsCost")
|
278
|
+
get_scenario_type_child_element.elements.delete("#{@ns}:CalculationMethod")
|
279
|
+
get_scenario_type_child_element.elements.delete("#{@ns}AnnualSavingsByFuels")
|
280
|
+
end
|
281
|
+
|
282
|
+
# Delete elements from the xml and reset the attributes to empty
|
283
|
+
if pom? || cb_modeled? || all
|
284
|
+
get_scenario_type_child_element.elements.delete("#{@ns}AllResourceTotals")
|
285
|
+
get_scenario_type_child_element.elements.delete("#{@ns}ResourceUses")
|
286
|
+
@resource_uses = []
|
287
|
+
@all_resource_totals = []
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
# Check that the simulation was completed successfully. We check:
|
292
|
+
# - out.osw completed_status == 'Success'
|
293
|
+
# - finished.job file exists
|
294
|
+
# - failed.job file doesn't exist
|
295
|
+
# - eplusout.end and eplusout.err files
|
296
|
+
def simulation_success?
|
297
|
+
success = true
|
298
|
+
|
299
|
+
# Check out.osw
|
300
|
+
out_osw_file = File.join(get_osw_dir, @out_osw_file_name)
|
301
|
+
if !File.exist?(out_osw_file)
|
302
|
+
OpenStudio.logFree(OpenStudio::Error, 'BuildingSync.Scenario.simulation_success?', "Scenario ID: #{xget_id}. #{out_osw_file} does not exist.")
|
303
|
+
else
|
304
|
+
File.open(out_osw_file, 'r') do |file|
|
305
|
+
@out_osw_json = JSON.parse(file.read)
|
306
|
+
end
|
307
|
+
if @out_osw_json['completed_status'] == 'Success'
|
308
|
+
OpenStudio.logFree(OpenStudio::Info, 'BuildingSync.Scenario.simulation_success?', "Scenario ID: #{xget_id} successfully completed.")
|
309
|
+
else
|
310
|
+
success = false
|
311
|
+
OpenStudio.logFree(OpenStudio::Error, 'BuildingSync.Scenario.simulation_success?', "Scenario ID: #{xget_id} unsuccessful.")
|
312
|
+
end
|
313
|
+
|
314
|
+
end
|
315
|
+
|
316
|
+
# Check for finished.job
|
317
|
+
finished_job = File.join(get_osw_dir, 'finished.job')
|
318
|
+
if !File.exist?(finished_job)
|
319
|
+
OpenStudio.logFree(OpenStudio::Error, 'BuildingSync.Scenario.simulation_success?', "Scenario ID: #{xget_id}: finished.job does not exist, simulation unsuccessful.")
|
320
|
+
success = false
|
321
|
+
end
|
322
|
+
|
323
|
+
# Check for failed.job
|
324
|
+
failed_job = File.join(get_osw_dir, 'failed.job')
|
325
|
+
if File.exist?(failed_job)
|
326
|
+
OpenStudio.logFree(OpenStudio::Error, 'BuildingSync.Scenario.simulation_success?', "Scenario ID: #{xget_id}: failed.job exists, simulation unsuccessful.")
|
327
|
+
success = false
|
328
|
+
end
|
329
|
+
|
330
|
+
# Check eplusout.end and eplusout.err files
|
331
|
+
end_file = File.join(get_osw_dir, 'eplusout.end')
|
332
|
+
if File.exist?(end_file)
|
333
|
+
# we open the .end file to determine if EnergyPlus was successful or not
|
334
|
+
energy_plus_string = File.open(end_file, &:readline)
|
335
|
+
if energy_plus_string.include? 'Fatal Error Detected'
|
336
|
+
OpenStudio.logFree(OpenStudio::Error, 'BuildingSync.Scenario.simulation_success?', "Scenario ID: #{xget_id}: eplusout.end detected error, simulation unsuccessful: #{energy_plus_string}")
|
337
|
+
success = false
|
338
|
+
# if we found out that there was a fatal error we search the err file for the first error.
|
339
|
+
File.open(File.join(scenario.get_osw_dir, 'eplusout.err')).each do |line|
|
340
|
+
if line.include? '** Severe **'
|
341
|
+
OpenStudio.logFree(OpenStudio::Error, 'BuildingSync.Scenario.simulation_success?', "Scenario ID: #{xget_id}: Severe error occurred! #{line}")
|
342
|
+
elsif line.include? '** Fatal **'
|
343
|
+
OpenStudio.logFree(OpenStudio::Error, 'BuildingSync.Scenario.simulation_success?', "Scenario ID: #{xget_id}: Fatal error occurred! #{line}")
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
return success
|
350
|
+
end
|
351
|
+
|
352
|
+
def results_available_and_correct_units?(results = @results_json)
|
353
|
+
results_available = true
|
354
|
+
|
355
|
+
if !results.nil?
|
356
|
+
if @results_json['units'] == 'SI'
|
357
|
+
OpenStudio.logFree(OpenStudio::Error, 'BuildingSync.Scenario.results_available_and_correct_units?', "Scenario ID: #{xget_id}. Only able to process IP results.")
|
358
|
+
results_available = false
|
359
|
+
end
|
360
|
+
elsif !File.exist?(File.join(get_osw_dir, @results_file_name))
|
361
|
+
results_available = false
|
362
|
+
OpenStudio.logFree(OpenStudio::Error, 'BuildingSync.Scenario.results_available_and_correct_units?', "Scenario ID: #{xget_id}. Unable to gather results: #{results_file} does not exist.")
|
363
|
+
else
|
364
|
+
results_file = File.join(get_osw_dir, @results_file_name)
|
365
|
+
File.open(results_file, 'r') do |file|
|
366
|
+
@results_json = JSON.parse(file.read)
|
367
|
+
end
|
368
|
+
if @results_json['units'] == 'SI'
|
369
|
+
OpenStudio.logFree(OpenStudio::Error, 'BuildingSync.Scenario.results_available_and_correct_units?', "Scenario ID: #{xget_id}. Only able to process IP results.")
|
370
|
+
results_available = false
|
371
|
+
end
|
372
|
+
end
|
373
|
+
return results_available
|
374
|
+
end
|
375
|
+
|
376
|
+
def os_gather_results(year_val)
|
377
|
+
if simulation_success? && results_available_and_correct_units?
|
378
|
+
os_parse_annual_results
|
379
|
+
os_parse_monthly_all_end_uses_results(year_val)
|
380
|
+
elsif !simulation_success?
|
381
|
+
OpenStudio.logFree(OpenStudio::Error, 'BuildingSync.Scenario.os_gather_results', "Scenario ID: #{xget_id}. Unable to gather results as simulation was unsuccessful.")
|
382
|
+
elsif !results_available_and_correct_units?
|
383
|
+
OpenStudio.logFree(OpenStudio::Error, 'BuildingSync.Scenario.os_gather_results', "Scenario ID: #{xget_id}. Unable to gather results as results are not available.")
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
def os_parse_annual_results(results = @results_json)
|
388
|
+
os_add_resource_uses(results)
|
389
|
+
os_add_all_resource_totals(results)
|
390
|
+
end
|
391
|
+
|
392
|
+
def os_parse_monthly_all_end_uses_results(year_val = Date.today.year, results = @results_json)
|
393
|
+
if results_available_and_correct_units?(results)
|
394
|
+
time_series_data_xml = xget_or_create('TimeSeriesData')
|
395
|
+
resource_use_map = @bsync_openstudio_resources_map['IP']['ResourceUse']
|
396
|
+
os_results = results['OpenStudioResults']
|
397
|
+
get_all_end_use_resource_uses.each do |resource_use|
|
398
|
+
resource_use_hash = resource_use_map.each.find { |h| h['EnergyResource'] == resource_use.xget_text('EnergyResource') && h['EndUse'] == 'All end uses' }
|
399
|
+
if resource_use_hash.nil?
|
400
|
+
OpenStudio.logFree(OpenStudio::Error, 'BuildingSync.Scenario.os_parse_monthly_all_end_uses_results', "Scenario ID: #{xget_id}: Unable to find mapping for ResourceUse: #{resource_use.xget_id} and 'All end uses'. Cannot parse monthly results")
|
401
|
+
else
|
402
|
+
monthly_text = resource_use_hash['monthly']['text']
|
403
|
+
monthly_units = resource_use_hash['monthly']['os_results_unit']
|
404
|
+
native_units = @native_units_map[resource_use.xget_text('EnergyResource')]
|
405
|
+
(1..12).each do |month|
|
406
|
+
start_date_time = DateTime.new(year_val, month, 1)
|
407
|
+
|
408
|
+
# substitues [month] with oct, for example, so we get electricity_ip_oct
|
409
|
+
key_to_find = monthly_text.gsub('[month]', start_date_time.strftime('%b').downcase)
|
410
|
+
if os_results.key?(key_to_find)
|
411
|
+
# We always use the first day of the month as the start day
|
412
|
+
time_series_xml = REXML::Element.new("#{@ns}:TimeSeries", time_series_data_xml)
|
413
|
+
time_series_xml.add_attribute('ID', "TS-#{start_date_time.strftime('%b').upcase}-#{resource_use.xget_id}")
|
414
|
+
|
415
|
+
# Convert value to correct units
|
416
|
+
interval_reading_value = help_convert(os_results[key_to_find], monthly_units, native_units)
|
417
|
+
|
418
|
+
# Create new TimeSeries element
|
419
|
+
ts = BuildingSync::TimeSeries.new(time_series_xml, @ns)
|
420
|
+
ts.set_monthly_energy_reading(start_date_time.dup, interval_reading_value, resource_use.xget_id)
|
421
|
+
|
422
|
+
else
|
423
|
+
OpenStudio.logFree(OpenStudio::Error, 'BuildingSync.Scenario.os_parse_monthly_all_end_uses_results', "Scenario ID: #{xget_id}: Key #{key_to_find} not found in results['OpenStudioResults']. Make sure monthly data is being output by os_results measure")
|
424
|
+
end
|
425
|
+
end
|
426
|
+
end
|
427
|
+
end
|
428
|
+
else
|
429
|
+
OpenStudio.logFree(OpenStudio::Error, 'BuildingSync.WorkflowMaker.get_timeseries_element', 'Cannot add monthly report values to the BldgSync file since it is missing.')
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
# Use the bsync to openstudio resources map to add results from the openstudio
|
434
|
+
# simulations as new ResourceUse elements and objects
|
435
|
+
# @param results [Hash] a hash of the results as directly read in from a results.json file
|
436
|
+
def os_add_resource_uses(results)
|
437
|
+
@results_json = results
|
438
|
+
ip_map = @bsync_openstudio_resources_map['IP']
|
439
|
+
os_results = @results_json['OpenStudioResults']
|
440
|
+
|
441
|
+
# Loop through ResourceUses in the resource_use_map
|
442
|
+
ip_map['ResourceUse'].each do |resource_use_map|
|
443
|
+
ru_type = resource_use_map['EnergyResource']
|
444
|
+
end_use = resource_use_map['EndUse']
|
445
|
+
native_units = @native_units_map[ru_type]
|
446
|
+
|
447
|
+
# Check if a ResourceUse of the desired type already exists
|
448
|
+
resource_use_element = @base_xml.get_elements("./#{@ns}:ResourceUses/#{@ns}:ResourceUse[#{@ns}:EnergyResource/text() = '#{ru_type}' and #{@ns}:EndUse/text() = '#{end_use}']")
|
449
|
+
|
450
|
+
# Add a new ResourceUse xml to the Scenario. This also adds ResourceUses if not defined
|
451
|
+
if resource_use_element.nil? || resource_use_element.empty?
|
452
|
+
ru_id = "#{xget_id}-ResourceUse-#{ru_type.split.map(&:capitalize).join('')}-#{end_use.split.map(&:capitalize).join('')}"
|
453
|
+
resource_use_xml = @g.add_energy_resource_use_to_scenario(@base_xml, ru_type, end_use, ru_id, native_units)
|
454
|
+
else
|
455
|
+
OpenStudio.logFree(OpenStudio::Warn, 'BuildingSync.Scenario.parse_annual_results', "Scenario ID: #{xget_id}. Resource Use of type: #{ru_type} and end use: #{end_use} already exists")
|
456
|
+
resource_use_xml = resource_use_element.first
|
457
|
+
end
|
458
|
+
|
459
|
+
# Map in the fields for each ResourceUse element into the xml
|
460
|
+
add_fields_from_map(resource_use_map['fields'], os_results, resource_use_xml)
|
461
|
+
|
462
|
+
# Add ResourceUse to array
|
463
|
+
@resource_uses << BuildingSync::ResourceUse.new(resource_use_xml, @ns)
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
def os_add_all_resource_totals(results)
|
468
|
+
ip_map = @bsync_openstudio_resources_map['IP']
|
469
|
+
os_results = results['OpenStudioResults']
|
470
|
+
|
471
|
+
# Loop through ResourceUses in the resource_use_map
|
472
|
+
ip_map['AllResourceTotal'].each do |map|
|
473
|
+
end_use = map['EndUse']
|
474
|
+
|
475
|
+
# Check if an AllResourceTotal of the desired type already exists
|
476
|
+
element = @base_xml.get_elements("./#{@ns}:AllResourceTotals/#{@ns}:AllResourceTotal[#{@ns}:EndUse/text() = '#{end_use}']")
|
477
|
+
|
478
|
+
# Add a new ResourceUse xml to the Scenario
|
479
|
+
if element.nil? || element.empty?
|
480
|
+
art_id = "#{xget_id}-AllResourceTotal-#{end_use.split.map(&:capitalize).join('')}"
|
481
|
+
all_resource_total_xml = @g.add_all_resource_total_to_scenario(@base_xml, end_use, art_id)
|
482
|
+
else
|
483
|
+
OpenStudio.logFree(OpenStudio::Warn, 'BuildingSync.Scenario.parse_annual_results', "Scenario ID: #{xget_id}. Resource Use of type: #{ru_type} and end use: #{end_use} already exists")
|
484
|
+
all_resource_total_xml = element.first
|
485
|
+
end
|
486
|
+
|
487
|
+
add_fields_from_map(map['fields'], os_results, all_resource_total_xml)
|
488
|
+
# add_source_energy(all_resource_total_xml)
|
489
|
+
|
490
|
+
@all_resource_totals << BuildingSync::AllResourceTotal.new(all_resource_total_xml, @ns)
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
def add_source_energy(all_resource_total_xml)
|
495
|
+
eplustbl_file = File.join(get_osw_dir, @eplustbl_file_name)
|
496
|
+
if !File.exist?(eplustbl_file)
|
497
|
+
OpenStudio.logFree(OpenStudio::Warn, 'BuildingSync.Scenario.add_source_energy', "Scenario ID: #{xget_id}. #{@eplustbl_file_name} does not exist, cannot add source energy results")
|
498
|
+
else
|
499
|
+
source_energy, source_eui = get_source_energy_array(eplustbl_file)
|
500
|
+
source_energy_xml = REXML::Element.new("#{@ns}:SourceEnergyUse", all_resource_total_xml)
|
501
|
+
source_energy_xml.text = source_energy
|
502
|
+
source_eui_xml = REXML::Element.new("#{@ns}:SourceEnergyUseIntensity", all_resource_total_xml)
|
503
|
+
source_eui_xml.text = source_eui
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
# Get source energy and source EUI from
|
508
|
+
# @param eplustbl_path [String]
|
509
|
+
# @return [Array] [total_source_energy_kbtu, total_source_eui_kbtu_ft2]
|
510
|
+
def get_source_energy_array(eplustbl_path)
|
511
|
+
# DLM: total hack because these are not reported in the out.osw
|
512
|
+
# output is array of [source_energy, source_eui] in kBtu and kBtu/ft2
|
513
|
+
result = []
|
514
|
+
File.open(eplustbl_path, 'r') do |f|
|
515
|
+
while line = f.gets
|
516
|
+
if /\<td align=\"right\"\>Total Source Energy\<\/td\>/.match?(line)
|
517
|
+
result << /\<td align=\"right\"\>(.*?)<\/td\>/.match(f.gets)[1].to_f
|
518
|
+
result << /\<td align=\"right\"\>(.*?)<\/td\>/.match(f.gets)[1].to_f
|
519
|
+
break
|
520
|
+
end
|
521
|
+
end
|
522
|
+
end
|
523
|
+
|
524
|
+
result[0] = result[0] * 947.8171203133 # GJ to kBtu
|
525
|
+
result[1] = result[1] * 0.947817120313 * 0.092903 # MJ/m2 to kBtu/ft2
|
526
|
+
|
527
|
+
return result[0], result[1]
|
528
|
+
end
|
529
|
+
|
530
|
+
def add_fields_from_map(fields, os_results, parent_xml)
|
531
|
+
fields.each do |field|
|
532
|
+
os_results_val = os_results[field['os_results_key']]
|
533
|
+
if os_results_val.nil?
|
534
|
+
OpenStudio.logFree(OpenStudio::Error, 'BuildingSync.Scenario.parse_annual_results', "Scenario ID: #{xget_id}. Unable to find result for #{field['os_results_key']}")
|
535
|
+
else
|
536
|
+
if field['os_results_unit'] == field['bsync_element_units']
|
537
|
+
# Parent element
|
538
|
+
new_element = REXML::Element.new("#{@ns}:#{field['bsync_element_name']}", parent_xml)
|
539
|
+
new_element.text = os_results_val
|
540
|
+
else
|
541
|
+
converted = help_convert(os_results_val, field['os_results_unit'], field['bsync_element_units'])
|
542
|
+
if !converted.nil?
|
543
|
+
new_element = REXML::Element.new("#{@ns}:#{field['bsync_element_name']}", parent_xml)
|
544
|
+
new_element.text = converted
|
545
|
+
end
|
546
|
+
end
|
547
|
+
end
|
548
|
+
end
|
549
|
+
end
|
550
|
+
|
551
|
+
def read_resource_uses
|
552
|
+
resource_use = @base_xml.get_elements("./#{@ns}:ResourceUses/#{@ns}:ResourceUse")
|
553
|
+
if !resource_use.nil? && !resource_use.empty?
|
554
|
+
resource_use.each do |ru|
|
555
|
+
@resource_uses << BuildingSync::ResourceUse.new(ru, @ns)
|
556
|
+
end
|
557
|
+
end
|
558
|
+
end
|
559
|
+
|
560
|
+
def read_all_resource_totals
|
561
|
+
all_resource_total = @base_xml.get_elements("./#{@ns}:AllResourceTotals/#{@ns}:AllResourceTotal")
|
562
|
+
if !all_resource_total.nil? && !all_resource_total.empty?
|
563
|
+
all_resource_total.each do |art|
|
564
|
+
@all_resource_totals << BuildingSync::AllResourceTotal.new(art, @ns)
|
565
|
+
end
|
566
|
+
end
|
567
|
+
end
|
568
|
+
|
569
|
+
def read_time_series_data
|
570
|
+
time_series = @base_xml.get_elements("./#{@ns}:TimeSeriesData/#{@ns}:TimeSeries")
|
571
|
+
if !time_series.nil? && !time_series.empty?
|
572
|
+
time_series.each do |ts|
|
573
|
+
@time_series_data << BuildingSync::TimeSeries.new(ts, @ns)
|
574
|
+
end
|
575
|
+
end
|
576
|
+
end
|
577
|
+
|
578
|
+
def check_scenario_type(path)
|
579
|
+
to_check = xget_element('ScenarioType').get_elements(path)
|
580
|
+
if !to_check.nil? && !to_check.empty?
|
581
|
+
return true
|
582
|
+
else
|
583
|
+
return false
|
584
|
+
end
|
585
|
+
end
|
586
|
+
|
587
|
+
def cb_measured?
|
588
|
+
if xget_element('ScenarioType').nil?
|
589
|
+
return false
|
590
|
+
end
|
591
|
+
return check_scenario_type("./#{@ns}:CurrentBuilding/#{@ns}:CalculationMethod/#{@ns}:Measured")
|
592
|
+
end
|
593
|
+
|
594
|
+
def cb_modeled?
|
595
|
+
if xget_element('ScenarioType').nil?
|
596
|
+
return false
|
597
|
+
end
|
598
|
+
return check_scenario_type("./#{@ns}:CurrentBuilding/#{@ns}:CalculationMethod/#{@ns}:Modeled")
|
599
|
+
end
|
600
|
+
|
601
|
+
def pom?
|
602
|
+
if xget_element('ScenarioType').nil?
|
603
|
+
return false
|
604
|
+
end
|
605
|
+
return check_scenario_type("./#{@ns}:PackageOfMeasures")
|
606
|
+
end
|
607
|
+
|
608
|
+
def target?
|
609
|
+
if xget_element('ScenarioType').nil?
|
610
|
+
return false
|
611
|
+
end
|
612
|
+
return check_scenario_type("./#{@ns}:Target")
|
613
|
+
end
|
614
|
+
|
615
|
+
def benchmark?
|
616
|
+
if xget_element('ScenarioType').nil?
|
617
|
+
return false
|
618
|
+
end
|
619
|
+
return check_scenario_type("./#{@ns}:Benchmark")
|
620
|
+
end
|
621
|
+
end
|
622
|
+
end
|