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,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
|