urbanopt-scenario 0.1.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/.gitignore +25 -0
- data/.rdoc_options +36 -0
- data/.rspec +3 -0
- data/.rubocop.yml +10 -0
- data/.travis.yml +23 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +42 -0
- data/Jenkinsfile +10 -0
- data/LICENSE.md +27 -0
- data/RDOC_MAIN.md +39 -0
- data/README.md +39 -0
- data/Rakefile +51 -0
- data/deploy_docs.sh +5 -0
- data/doc_templates/LICENSE.md +27 -0
- data/doc_templates/README.md.erb +42 -0
- data/doc_templates/copyright_erb.txt +31 -0
- data/doc_templates/copyright_js.txt +4 -0
- data/doc_templates/copyright_ruby.txt +29 -0
- data/docs/.gitignore +3 -0
- data/docs/.vuepress/components/InnerJsonSchema.vue +84 -0
- data/docs/.vuepress/components/JsonSchema.vue +12 -0
- data/docs/.vuepress/components/ScenarioSchema.vue +12 -0
- data/docs/.vuepress/components/StaticLink.vue +8 -0
- data/docs/.vuepress/config.js +15 -0
- data/docs/.vuepress/highlight.js +8 -0
- data/docs/.vuepress/public/custom_rdoc_styles.css +74 -0
- data/docs/.vuepress/utils.js +17 -0
- data/docs/README.md +39 -0
- data/docs/package-lock.json +11791 -0
- data/docs/package.json +22 -0
- data/docs/schemas/scenario-schema.md +3 -0
- data/lib/measures/.rubocop.yml +5 -0
- data/lib/measures/default_feature_reports/LICENSE.md +27 -0
- data/lib/measures/default_feature_reports/README.md +56 -0
- data/lib/measures/default_feature_reports/README.md.erb +42 -0
- data/lib/measures/default_feature_reports/measure.rb +731 -0
- data/lib/measures/default_feature_reports/measure.xml +139 -0
- data/lib/measures/default_feature_reports/tests/USA_CO_Golden-NREL.724666_TMY3.epw +8768 -0
- data/lib/measures/default_feature_reports/tests/default_feature_reports_test.rb +238 -0
- data/lib/measures/default_feature_reports/tests/example_model.osm +4378 -0
- data/lib/urbanopt-scenario.rb +31 -0
- data/lib/urbanopt/scenario.rb +45 -0
- data/lib/urbanopt/scenario/default_reports.rb +40 -0
- data/lib/urbanopt/scenario/default_reports/construction_cost.rb +169 -0
- data/lib/urbanopt/scenario/default_reports/date.rb +97 -0
- data/lib/urbanopt/scenario/default_reports/end_use.rb +159 -0
- data/lib/urbanopt/scenario/default_reports/end_uses.rb +140 -0
- data/lib/urbanopt/scenario/default_reports/feature_report.rb +207 -0
- data/lib/urbanopt/scenario/default_reports/location.rb +99 -0
- data/lib/urbanopt/scenario/default_reports/logger.rb +44 -0
- data/lib/urbanopt/scenario/default_reports/program.rb +261 -0
- data/lib/urbanopt/scenario/default_reports/reporting_period.rb +298 -0
- data/lib/urbanopt/scenario/default_reports/scenario_report.rb +276 -0
- data/lib/urbanopt/scenario/default_reports/schema/README.md +33 -0
- data/lib/urbanopt/scenario/default_reports/schema/scenario_csv_columns.txt +13 -0
- data/lib/urbanopt/scenario/default_reports/schema/scenario_schema.json +742 -0
- data/lib/urbanopt/scenario/default_reports/timeseries_csv.rb +231 -0
- data/lib/urbanopt/scenario/default_reports/validator.rb +97 -0
- data/lib/urbanopt/scenario/extension.rb +63 -0
- data/lib/urbanopt/scenario/logger.rb +42 -0
- data/lib/urbanopt/scenario/scenario_base.rb +79 -0
- data/lib/urbanopt/scenario/scenario_csv.rb +122 -0
- data/lib/urbanopt/scenario/scenario_datapoint_base.rb +162 -0
- data/lib/urbanopt/scenario/scenario_post_processor_base.rb +69 -0
- data/lib/urbanopt/scenario/scenario_post_processor_default.rb +97 -0
- data/lib/urbanopt/scenario/scenario_runner_base.rb +63 -0
- data/lib/urbanopt/scenario/scenario_runner_osw.rb +158 -0
- data/lib/urbanopt/scenario/simulation_dir_base.rb +90 -0
- data/lib/urbanopt/scenario/simulation_dir_osw.rb +261 -0
- data/lib/urbanopt/scenario/simulation_mapper_base.rb +47 -0
- data/lib/urbanopt/scenario/version.rb +35 -0
- data/urbanopt-scenario-gem.gemspec +36 -0
- metadata +227 -0
data/docs/package.json
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
{
|
2
|
+
"name": "urbanopt-scenario-gem-docs",
|
3
|
+
"version": "0.0.1",
|
4
|
+
"description": "",
|
5
|
+
"main": "index.js",
|
6
|
+
"scripts": {
|
7
|
+
"dev": "vuepress dev",
|
8
|
+
"build": "vuepress build",
|
9
|
+
"deploy": "gh-pages -d .vuepress/dist"
|
10
|
+
},
|
11
|
+
"author": "NREL",
|
12
|
+
"dependencies": {
|
13
|
+
"highlight.js": "^9.15.6",
|
14
|
+
"json-schema-ref-parser": "^6.1.0",
|
15
|
+
"json-schema-view-js": "git+https://git@github.com/bgschiller/json-schema-view-js.git",
|
16
|
+
"vuepress": "^0.14.10",
|
17
|
+
"webpack-dev-middleware": "^3.6.0"
|
18
|
+
},
|
19
|
+
"devDependencies": {
|
20
|
+
"gh-pages": "^2.0.1"
|
21
|
+
}
|
22
|
+
}
|
@@ -0,0 +1,27 @@
|
|
1
|
+
URBANopt, Copyright (c) 2019, Alliance for Sustainable Energy, LLC, and other
|
2
|
+
contributors. All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without modification,
|
5
|
+
are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
Redistributions of source code must retain the above copyright notice, this list
|
8
|
+
of conditions and the following disclaimer.
|
9
|
+
|
10
|
+
Redistributions in binary form must reproduce the above copyright notice, this
|
11
|
+
list of conditions and the following disclaimer in the documentation and/or other
|
12
|
+
materials provided with the distribution.
|
13
|
+
|
14
|
+
Neither the name of the copyright holder nor the names of its contributors may be
|
15
|
+
used to endorse or promote products derived from this software without specific
|
16
|
+
prior written permission.
|
17
|
+
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
19
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
20
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
21
|
+
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
22
|
+
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
23
|
+
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
24
|
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
25
|
+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
26
|
+
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
27
|
+
OF THE POSSIBILITY OF SUCH DAMAGE.
|
@@ -0,0 +1,56 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
###### (Automatically generated documentation)
|
4
|
+
|
5
|
+
# DefaultFeatureReports
|
6
|
+
|
7
|
+
## Description
|
8
|
+
Writes default_feature_reports.json file used by URBANopt Scenario Default Post Processor
|
9
|
+
|
10
|
+
## Modeler Description
|
11
|
+
This measure only allows for one feature_report per simulation. If multiple features are simulated in a single simulation, a new measure must be written to disaggregate simulation results to multiple features.
|
12
|
+
|
13
|
+
## Measure Type
|
14
|
+
ReportingMeasure
|
15
|
+
|
16
|
+
## Taxonomy
|
17
|
+
|
18
|
+
|
19
|
+
## Arguments
|
20
|
+
|
21
|
+
|
22
|
+
### Feature unique identifier
|
23
|
+
|
24
|
+
**Name:** feature_id,
|
25
|
+
**Type:** String,
|
26
|
+
**Units:** ,
|
27
|
+
**Required:** false,
|
28
|
+
**Model Dependent:** false
|
29
|
+
|
30
|
+
### Feature scenario specific name
|
31
|
+
|
32
|
+
**Name:** feature_name,
|
33
|
+
**Type:** String,
|
34
|
+
**Units:** ,
|
35
|
+
**Required:** false,
|
36
|
+
**Model Dependent:** false
|
37
|
+
|
38
|
+
### URBANopt Feature Type
|
39
|
+
|
40
|
+
**Name:** feature_type,
|
41
|
+
**Type:** String,
|
42
|
+
**Units:** ,
|
43
|
+
**Required:** false,
|
44
|
+
**Model Dependent:** false
|
45
|
+
|
46
|
+
### Reporting Frequency
|
47
|
+
The frequency at which to report timeseries output data.
|
48
|
+
**Name:** reporting_frequency,
|
49
|
+
**Type:** Choice,
|
50
|
+
**Units:** ,
|
51
|
+
**Required:** true,
|
52
|
+
**Model Dependent:** false
|
53
|
+
|
54
|
+
|
55
|
+
|
56
|
+
|
@@ -0,0 +1,42 @@
|
|
1
|
+
<%#= README.md.erb is used to auto-generate README.md. %>
|
2
|
+
<%#= To manually maintain README.md throw away README.md.erb and manually edit README.md %>
|
3
|
+
###### (Automatically generated documentation)
|
4
|
+
|
5
|
+
# <%= name %>
|
6
|
+
|
7
|
+
## Description
|
8
|
+
<%= description %>
|
9
|
+
|
10
|
+
## Modeler Description
|
11
|
+
<%= modelerDescription %>
|
12
|
+
|
13
|
+
## Measure Type
|
14
|
+
<%= measureType %>
|
15
|
+
|
16
|
+
## Taxonomy
|
17
|
+
<%= taxonomy %>
|
18
|
+
|
19
|
+
## Arguments
|
20
|
+
|
21
|
+
<% arguments.each do |argument| %>
|
22
|
+
### <%= argument[:display_name] %>
|
23
|
+
<%= argument[:description] %>
|
24
|
+
**Name:** <%= argument[:name] %>,
|
25
|
+
**Type:** <%= argument[:type] %>,
|
26
|
+
**Units:** <%= argument[:units] %>,
|
27
|
+
**Required:** <%= argument[:required] %>,
|
28
|
+
**Model Dependent:** <%= argument[:model_dependent] %>
|
29
|
+
<% end %>
|
30
|
+
|
31
|
+
<% if arguments.size == 0 %>
|
32
|
+
<%= "This measure does not have any user arguments" %>
|
33
|
+
<% end %>
|
34
|
+
|
35
|
+
<% if outputs.size > 0 %>
|
36
|
+
## Outputs
|
37
|
+
<% output_names = [] %>
|
38
|
+
<% outputs.each do |output| %>
|
39
|
+
<% output_names << output[:display_name] %>
|
40
|
+
<% end %>
|
41
|
+
<%= output_names.join(", ") %>
|
42
|
+
<% end %>
|
@@ -0,0 +1,731 @@
|
|
1
|
+
# *********************************************************************************
|
2
|
+
# URBANopt, Copyright (c) 2019, Alliance for Sustainable Energy, LLC, and other
|
3
|
+
# contributors. All rights reserved.
|
4
|
+
#
|
5
|
+
# Redistribution and use in source and binary forms, with or without modification,
|
6
|
+
# are permitted provided that the following conditions are met:
|
7
|
+
#
|
8
|
+
# Redistributions of source code must retain the above copyright notice, this list
|
9
|
+
# of conditions and the following disclaimer.
|
10
|
+
#
|
11
|
+
# Redistributions in binary form must reproduce the above copyright notice, this
|
12
|
+
# list of conditions and the following disclaimer in the documentation and/or other
|
13
|
+
# materials provided with the distribution.
|
14
|
+
#
|
15
|
+
# Neither the name of the copyright holder nor the names of its contributors may be
|
16
|
+
# used to endorse or promote products derived from this software without specific
|
17
|
+
# prior written permission.
|
18
|
+
#
|
19
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
20
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
21
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
22
|
+
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
23
|
+
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
24
|
+
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
25
|
+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
26
|
+
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
27
|
+
# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
28
|
+
# OF THE POSSIBILITY OF SUCH DAMAGE.
|
29
|
+
# *********************************************************************************
|
30
|
+
|
31
|
+
require 'urbanopt/scenario/default_reports/feature_report'
|
32
|
+
require 'csv'
|
33
|
+
require 'benchmark'
|
34
|
+
require 'logger'
|
35
|
+
|
36
|
+
@@logger = Logger.new(STDOUT)
|
37
|
+
|
38
|
+
# start the measure
|
39
|
+
class DefaultFeatureReports < OpenStudio::Measure::ReportingMeasure
|
40
|
+
# human readable name
|
41
|
+
def name
|
42
|
+
return 'DefaultFeatureReports'
|
43
|
+
end
|
44
|
+
|
45
|
+
# human readable description
|
46
|
+
def description
|
47
|
+
return 'Writes default_feature_reports.json file used by URBANopt Scenario Default Post Processor'
|
48
|
+
end
|
49
|
+
|
50
|
+
# human readable description of modeling approach
|
51
|
+
def modeler_description
|
52
|
+
return 'This measure only allows for one feature_report per simulation. If multiple features are simulated in a single simulation, a new measure must be written to disaggregate simulation results to multiple features.'
|
53
|
+
end
|
54
|
+
|
55
|
+
# define the arguments that the user will input
|
56
|
+
def arguments
|
57
|
+
args = OpenStudio::Measure::OSArgumentVector.new
|
58
|
+
|
59
|
+
id = OpenStudio::Measure::OSArgument.makeStringArgument('feature_id', false)
|
60
|
+
id.setDisplayName('Feature unique identifier')
|
61
|
+
id.setDefaultValue('1')
|
62
|
+
args << id
|
63
|
+
|
64
|
+
name = OpenStudio::Measure::OSArgument.makeStringArgument('feature_name', false)
|
65
|
+
name.setDisplayName('Feature scenario specific name')
|
66
|
+
name.setDefaultValue('name')
|
67
|
+
args << name
|
68
|
+
|
69
|
+
feature_type = OpenStudio::Measure::OSArgument.makeStringArgument('feature_type', false)
|
70
|
+
feature_type.setDisplayName('URBANopt Feature Type')
|
71
|
+
feature_type.setDefaultValue('Building')
|
72
|
+
args << feature_type
|
73
|
+
|
74
|
+
# make an argument for the frequency
|
75
|
+
reporting_frequency_chs = OpenStudio::StringVector.new
|
76
|
+
reporting_frequency_chs << 'Detailed'
|
77
|
+
reporting_frequency_chs << 'Timestep'
|
78
|
+
reporting_frequency_chs << 'Hourly'
|
79
|
+
reporting_frequency_chs << 'Daily'
|
80
|
+
# reporting_frequency_chs << "BillingPeriod" # match it to utility bill object
|
81
|
+
## Utility report here to report the start and end for each fueltype
|
82
|
+
reporting_frequency_chs << 'Monthly'
|
83
|
+
reporting_frequency_chs << 'Runperiod'
|
84
|
+
|
85
|
+
reporting_frequency = OpenStudio::Measure::OSArgument.makeChoiceArgument('reporting_frequency', reporting_frequency_chs, true)
|
86
|
+
reporting_frequency.setDisplayName('Reporting Frequency')
|
87
|
+
reporting_frequency.setDescription('The frequency at which to report timeseries output data.')
|
88
|
+
reporting_frequency.setDefaultValue('Hourly')
|
89
|
+
args << reporting_frequency
|
90
|
+
|
91
|
+
# move this in the run method
|
92
|
+
if reporting_frequency.defaultValueDisplayName == 'BillingPeriod'
|
93
|
+
@@logger.error('BillingPeriod frequency is not implemented yet')
|
94
|
+
end
|
95
|
+
|
96
|
+
return args
|
97
|
+
end
|
98
|
+
|
99
|
+
def fuel_types
|
100
|
+
fuel_types = [
|
101
|
+
'Electricity',
|
102
|
+
'Gas',
|
103
|
+
'AdditionalFuel',
|
104
|
+
'DistrictCooling',
|
105
|
+
'DistrictHeating',
|
106
|
+
'Water'
|
107
|
+
]
|
108
|
+
|
109
|
+
return fuel_types
|
110
|
+
end
|
111
|
+
|
112
|
+
def end_uses
|
113
|
+
end_uses = [
|
114
|
+
'Heating',
|
115
|
+
'Cooling',
|
116
|
+
'InteriorLights',
|
117
|
+
'ExteriorLights',
|
118
|
+
'InteriorEquipment',
|
119
|
+
'ExteriorEquipment',
|
120
|
+
'Fans',
|
121
|
+
'Pumps',
|
122
|
+
'HeatRejection',
|
123
|
+
'Humidifier',
|
124
|
+
'HeatRecovery',
|
125
|
+
'WaterSystems',
|
126
|
+
'Refrigeration',
|
127
|
+
'Generators',
|
128
|
+
'Facility'
|
129
|
+
]
|
130
|
+
|
131
|
+
return end_uses
|
132
|
+
end
|
133
|
+
|
134
|
+
# return a vector of IdfObject's to request EnergyPlus objects needed by the run method
|
135
|
+
# rubocop:disable Naming/MethodName
|
136
|
+
def energyPlusOutputRequests(runner, user_arguments)
|
137
|
+
super(runner, user_arguments)
|
138
|
+
|
139
|
+
result = OpenStudio::IdfObjectVector.new
|
140
|
+
|
141
|
+
reporting_frequency = runner.getStringArgumentValue('reporting_frequency', user_arguments)
|
142
|
+
|
143
|
+
# Request the output for each end use/fuel type combination
|
144
|
+
end_uses.each do |end_use|
|
145
|
+
fuel_types.each do |fuel_type|
|
146
|
+
variable_name = if end_use == 'Facility'
|
147
|
+
"#{fuel_type}:#{end_use}"
|
148
|
+
else
|
149
|
+
"#{end_use}:#{fuel_type}"
|
150
|
+
end
|
151
|
+
result << OpenStudio::IdfObject.load("Output:Meter,#{variable_name},#{reporting_frequency};").get
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
### Request the output for each end use/fuel type combination
|
156
|
+
result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,Electricity:Facility,#{reporting_frequency};").get
|
157
|
+
result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,ElectricityProduced:Facility,#{reporting_frequency};").get
|
158
|
+
result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,Gas:Facility,#{reporting_frequency};").get
|
159
|
+
result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,DistrictCooling:Facility,#{reporting_frequency};").get
|
160
|
+
result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,DistrictHeating:Facility,#{reporting_frequency};").get
|
161
|
+
|
162
|
+
timeseries_data = ['District Cooling Chilled Water Rate', 'District Cooling Mass Flow Rate',
|
163
|
+
'District Cooling Inlet Temperature', 'District Cooling Outlet Temperature',
|
164
|
+
'District Heating Hot Water Rate', 'District Heating Mass Flow Rate',
|
165
|
+
'District Heating Inlet Temperature', 'District Heating Outlet Temperature']
|
166
|
+
|
167
|
+
timeseries_data.each do |ts|
|
168
|
+
result << OpenStudio::IdfObject.load("Output:Variable,*,#{ts},#{reporting_frequency};").get
|
169
|
+
end
|
170
|
+
|
171
|
+
# use the built-in error checking
|
172
|
+
if !runner.validateUserArguments(arguments, user_arguments)
|
173
|
+
return result
|
174
|
+
end
|
175
|
+
|
176
|
+
return result
|
177
|
+
end
|
178
|
+
|
179
|
+
# sql_query method
|
180
|
+
def sql_query(runner, sql, report_name, query)
|
181
|
+
val = nil
|
182
|
+
result = sql.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='#{report_name}' AND #{query}")
|
183
|
+
if result.empty?
|
184
|
+
runner.registerWarning("Query failed for #{report_name} and #{query}")
|
185
|
+
else
|
186
|
+
begin
|
187
|
+
val = result.get
|
188
|
+
rescue StandardError
|
189
|
+
val = nil
|
190
|
+
runner.registerWarning('Query result.get failed')
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
val
|
195
|
+
end
|
196
|
+
|
197
|
+
# unit conversion method
|
198
|
+
def convert_units(value, from_units, to_units)
|
199
|
+
# apply unit conversion
|
200
|
+
value_converted = OpenStudio.convert(value, from_units, to_units)
|
201
|
+
if value_converted.is_initialized
|
202
|
+
value = value_converted.get
|
203
|
+
else
|
204
|
+
@runner.registerError("Was not able to convert #{value} from #{from_units} to #{to_units}.")
|
205
|
+
value = nil
|
206
|
+
end
|
207
|
+
return value
|
208
|
+
end
|
209
|
+
|
210
|
+
# define what happens when the measure is run
|
211
|
+
# rubocop:disable Metrics/AbcSize
|
212
|
+
def run(runner, user_arguments)
|
213
|
+
super(runner, user_arguments)
|
214
|
+
|
215
|
+
# use the built-in error checking
|
216
|
+
unless runner.validateUserArguments(arguments, user_arguments)
|
217
|
+
return false
|
218
|
+
end
|
219
|
+
|
220
|
+
# use the built-in error checking
|
221
|
+
if !runner.validateUserArguments(arguments, user_arguments)
|
222
|
+
return false
|
223
|
+
end
|
224
|
+
|
225
|
+
feature_id = runner.getStringArgumentValue('feature_id', user_arguments)
|
226
|
+
feature_name = runner.getStringArgumentValue('feature_name', user_arguments)
|
227
|
+
feature_type = runner.getStringArgumentValue('feature_type', user_arguments)
|
228
|
+
|
229
|
+
# Assign the user inputs to variables
|
230
|
+
reporting_frequency = runner.getStringArgumentValue('reporting_frequency', user_arguments)
|
231
|
+
|
232
|
+
# cache runner for this instance of the measure
|
233
|
+
@runner = runner
|
234
|
+
|
235
|
+
# get the WorkflowJSON object
|
236
|
+
workflow = runner.workflow
|
237
|
+
|
238
|
+
# get the last model and sql file
|
239
|
+
model = runner.lastOpenStudioModel
|
240
|
+
if model.empty?
|
241
|
+
runner.registerError('Cannot find last model.')
|
242
|
+
return false
|
243
|
+
end
|
244
|
+
model = model.get
|
245
|
+
|
246
|
+
sql_file = runner.lastEnergyPlusSqlFile
|
247
|
+
if sql_file.empty?
|
248
|
+
runner.registerError('Cannot find last sql file.')
|
249
|
+
return false
|
250
|
+
end
|
251
|
+
sql_file = sql_file.get
|
252
|
+
model.setSqlFile(sql_file)
|
253
|
+
|
254
|
+
# get building from model
|
255
|
+
building = model.getBuilding
|
256
|
+
|
257
|
+
# get surfaces from model
|
258
|
+
surfaces = model.getSurfaces
|
259
|
+
|
260
|
+
# get epw_file
|
261
|
+
epw_file = runner.lastEpwFile
|
262
|
+
if epw_file.empty?
|
263
|
+
runner.registerError('Cannot find last epw file.')
|
264
|
+
return false
|
265
|
+
end
|
266
|
+
epw_file = epw_file.get
|
267
|
+
|
268
|
+
# create output feature_report report object
|
269
|
+
feature_report = URBANopt::Scenario::DefaultReports::FeatureReport.new
|
270
|
+
feature_report.id = feature_id
|
271
|
+
feature_report.name = feature_name
|
272
|
+
feature_report.feature_type = feature_type
|
273
|
+
feature_report.directory_name = workflow.absoluteRunDir
|
274
|
+
feature_report.timesteps_per_hour = model.getTimestep.numberOfTimestepsPerHour
|
275
|
+
feature_report.simulation_status = 'Complete'
|
276
|
+
|
277
|
+
feature_report.reporting_periods << URBANopt::Scenario::DefaultReports::ReportingPeriod.new
|
278
|
+
|
279
|
+
###########################################################################
|
280
|
+
##
|
281
|
+
# Get Location information and store in the feature_report
|
282
|
+
##
|
283
|
+
|
284
|
+
# latitude
|
285
|
+
latitude = epw_file.latitude
|
286
|
+
feature_report.location.latitude = latitude
|
287
|
+
|
288
|
+
# longitude
|
289
|
+
longitude = epw_file.longitude
|
290
|
+
feature_report.location.longitude = longitude
|
291
|
+
|
292
|
+
# surface_elevation
|
293
|
+
elev = sql_query(runner, sql_file, 'InputVerificationandResultsSummary', "TableName='General' AND RowName='Elevation' AND ColumnName='Value'")
|
294
|
+
feature_report.location.surface_elevation = elev
|
295
|
+
|
296
|
+
##########################################################################
|
297
|
+
##
|
298
|
+
# Get program information and store in the feature_report
|
299
|
+
##
|
300
|
+
|
301
|
+
# floor_area
|
302
|
+
floor_area = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Building Area' AND RowName='Total Building Area' AND ColumnName='Area'")
|
303
|
+
feature_report.program.floor_area = convert_units(floor_area, 'm^2', 'ft^2')
|
304
|
+
|
305
|
+
# conditioned_area
|
306
|
+
conditioned_area = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Building Area' AND RowName='Net Conditioned Building Area' AND ColumnName='Area'")
|
307
|
+
feature_report.program.conditioned_area = convert_units(conditioned_area, 'm^2', 'ft^2')
|
308
|
+
|
309
|
+
# unconditioned_area
|
310
|
+
unconditioned_area = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Building Area' AND RowName='Unconditioned Building Area' AND ColumnName='Area'")
|
311
|
+
feature_report.program.unconditioned_area = convert_units(unconditioned_area, 'm^2', 'ft^2')
|
312
|
+
|
313
|
+
# footprint_area
|
314
|
+
feature_report.program.footprint_area = convert_units(floor_area, 'm^2', 'ft^2')
|
315
|
+
|
316
|
+
# maximum_number_of_stories
|
317
|
+
number_of_stories = building.standardsNumberOfStories.get if building.standardsNumberOfStories.is_initialized
|
318
|
+
number_of_stories ||= 1
|
319
|
+
feature_report.program.maximum_number_of_stories = number_of_stories
|
320
|
+
|
321
|
+
# maximum_roof_height
|
322
|
+
floor_to_floor_height = building.nominalFloortoFloorHeight.to_f
|
323
|
+
maximum_roof_height = number_of_stories * floor_to_floor_height
|
324
|
+
feature_report.program.maximum_roof_height = maximum_roof_height
|
325
|
+
|
326
|
+
# maximum_number_of_stories_above_ground
|
327
|
+
number_of_stories_above_ground = building.standardsNumberOfAboveGroundStories.get if building.standardsNumberOfAboveGroundStories.is_initialized
|
328
|
+
number_of_stories_above_ground ||= 1
|
329
|
+
feature_report.program.maximum_number_of_stories_above_ground = number_of_stories_above_ground
|
330
|
+
|
331
|
+
# number_of_residential_units
|
332
|
+
number_of_living_units = building.standardsNumberOfLivingUnits.to_i.get if building.standardsNumberOfLivingUnits.is_initialized
|
333
|
+
number_of_living_units ||= 1
|
334
|
+
feature_report.program.number_of_residential_units = number_of_living_units
|
335
|
+
|
336
|
+
## building_types
|
337
|
+
|
338
|
+
# get an array of the model spaces
|
339
|
+
spaces = model.getSpaces
|
340
|
+
|
341
|
+
# get array of model space types
|
342
|
+
space_types = model.getSpaceTypes
|
343
|
+
|
344
|
+
# create a hash for space_type_areas (spcace types as keys and their areas as values)
|
345
|
+
space_type_areas = {}
|
346
|
+
model.getSpaceTypes.each do |space_type|
|
347
|
+
building_type = space_type.standardsBuildingType
|
348
|
+
if building_type.empty?
|
349
|
+
building_type = 'unknown'
|
350
|
+
else
|
351
|
+
building_type = building_type.get
|
352
|
+
end
|
353
|
+
space_type_areas[building_type] = 0 if space_type_areas[building_type].nil?
|
354
|
+
space_type_areas[building_type] += convert_units(space_type.floorArea, 'm^2', 'ft^2')
|
355
|
+
end
|
356
|
+
|
357
|
+
# create a hash for space_type_occupancy (spcace types as keys and their occupancy as values)
|
358
|
+
space_type_occupancy = {}
|
359
|
+
spaces.each do |space|
|
360
|
+
if space.spaceType.empty?
|
361
|
+
raise 'space.spaceType is empty. Make sure spaces have a space type'
|
362
|
+
else
|
363
|
+
building_type = space.spaceType.get.standardsBuildingType
|
364
|
+
end
|
365
|
+
if building_type.empty?
|
366
|
+
building_type = 'unknown'
|
367
|
+
else
|
368
|
+
building_type = building_type.get
|
369
|
+
end
|
370
|
+
space_type_occupancy[building_type] = 0 if space_type_occupancy[building_type].nil?
|
371
|
+
space_type_occupancy[building_type] += space.numberOfPeople
|
372
|
+
end
|
373
|
+
|
374
|
+
# combine all in a building_types array
|
375
|
+
building_types = []
|
376
|
+
for i in 0..(space_type_areas.size - 1)
|
377
|
+
building_types << { building_type: space_type_areas.keys[i], floor_area: space_type_areas.values[i], maximum_occupancy: space_type_occupancy.values[i] }
|
378
|
+
end
|
379
|
+
# add results to the feature report JSON
|
380
|
+
feature_report.program.building_types = building_types
|
381
|
+
|
382
|
+
## window_area
|
383
|
+
# north_window_area
|
384
|
+
north_window_area = sql_query(runner, sql_file, 'InputVerificationandResultsSummary', "TableName='Window-Wall Ratio' AND RowName='Window Opening Area' AND ColumnName='North (315 to 45 deg)'").to_f
|
385
|
+
feature_report.program.window_area[:north_window_area] = convert_units(north_window_area, 'm^2', 'ft^2')
|
386
|
+
# south_window_area
|
387
|
+
south_window_area = sql_query(runner, sql_file, 'InputVerificationandResultsSummary', "TableName='Window-Wall Ratio' AND RowName='Window Opening Area' AND ColumnName='South (135 to 225 deg)'").to_f
|
388
|
+
feature_report.program.window_area[:south_window_area] = convert_units(south_window_area, 'm^2', 'ft^2')
|
389
|
+
# east_window_area
|
390
|
+
east_window_area = sql_query(runner, sql_file, 'InputVerificationandResultsSummary', "TableName='Window-Wall Ratio' AND RowName='Window Opening Area' AND ColumnName='East (45 to 135 deg)'").to_f
|
391
|
+
feature_report.program.window_area[:east_window_area] = convert_units(east_window_area, 'm^2', 'ft^2')
|
392
|
+
# west_window_area
|
393
|
+
west_window_area = sql_query(runner, sql_file, 'InputVerificationandResultsSummary', "TableName='Window-Wall Ratio' AND RowName='Window Opening Area' AND ColumnName='West (225 to 315 deg)'").to_f
|
394
|
+
feature_report.program.window_area[:west_window_area] = convert_units(west_window_area, 'm^2', 'ft^2')
|
395
|
+
# total_window_area
|
396
|
+
total_window_area = north_window_area + south_window_area + east_window_area + west_window_area
|
397
|
+
feature_report.program.window_area[:total_window_area] = convert_units(total_window_area, 'm^2', 'ft^2')
|
398
|
+
|
399
|
+
## wall_area
|
400
|
+
# north_wall_area
|
401
|
+
north_wall_area = sql_query(runner, sql_file, 'InputVerificationandResultsSummary', "TableName='Window-Wall Ratio' AND RowName='Gross Wall Area' AND ColumnName='North (315 to 45 deg)'").to_f
|
402
|
+
feature_report.program.wall_area[:north_wall_area] = convert_units(north_wall_area, 'm^2', 'ft^2')
|
403
|
+
# south_wall_area
|
404
|
+
south_wall_area = sql_query(runner, sql_file, 'InputVerificationandResultsSummary', "TableName='Window-Wall Ratio' AND RowName='Gross Wall Area' AND ColumnName='South (135 to 225 deg)'").to_f
|
405
|
+
feature_report.program.wall_area[:south_wall_area] = convert_units(south_wall_area, 'm^2', 'ft^2')
|
406
|
+
# east_wall_area
|
407
|
+
east_wall_area = sql_query(runner, sql_file, 'InputVerificationandResultsSummary', "TableName='Window-Wall Ratio' AND RowName='Gross Wall Area' AND ColumnName='East (45 to 135 deg)'").to_f
|
408
|
+
feature_report.program.wall_area[:east_wall_area] = convert_units(east_wall_area, 'm^2', 'ft^2')
|
409
|
+
# west_wall_area
|
410
|
+
west_wall_area = sql_query(runner, sql_file, 'InputVerificationandResultsSummary', "TableName='Window-Wall Ratio' AND RowName='Gross Wall Area' AND ColumnName='West (225 to 315 deg)'").to_f
|
411
|
+
feature_report.program.wall_area[:west_wall_area] = convert_units(west_wall_area, 'm^2', 'ft^2')
|
412
|
+
# total_wall_area
|
413
|
+
total_wall_area = north_wall_area + south_wall_area + east_wall_area + west_wall_area
|
414
|
+
feature_report.program.wall_area[:total_wall_area] = convert_units(total_wall_area, 'm^2', 'ft^2')
|
415
|
+
|
416
|
+
# total_roof_area
|
417
|
+
total_roof_area = 0.0
|
418
|
+
surfaces.each do |surface|
|
419
|
+
if (surface.outsideBoundaryCondition == 'Outdoors') && (surface.surfaceType == 'RoofCeiling')
|
420
|
+
total_roof_area += surface.netArea
|
421
|
+
end
|
422
|
+
end
|
423
|
+
feature_report.program.roof_area[:total_roof_area] = convert_units(total_roof_area, 'm^2', 'ft^2')
|
424
|
+
|
425
|
+
# orientation
|
426
|
+
# RK: a more robust method should be implemented to find orientation(finding main axis of the building using aspect ratio)
|
427
|
+
building_rotation = model.getBuilding.northAxis
|
428
|
+
feature_report.program.orientation = building_rotation
|
429
|
+
|
430
|
+
# aspect_ratio
|
431
|
+
north_wall_area = sql_query(runner, sql_file, 'InputVerificationandResultsSummary', "TableName='Window-Wall Ratio' AND RowName='Gross Wall Area' AND ColumnName='North (315 to 45 deg)'")
|
432
|
+
east_wall_area = sql_query(runner, sql_file, 'InputVerificationandResultsSummary', "TableName='Window-Wall Ratio' AND RowName='Gross Wall Area' AND ColumnName='East (45 to 135 deg)'")
|
433
|
+
aspect_ratio = north_wall_area / east_wall_area if north_wall_area != 0 && east_wall_area != 0
|
434
|
+
aspect_ratio ||= nil
|
435
|
+
feature_report.program.aspect_ratio = aspect_ratio
|
436
|
+
|
437
|
+
############################################################################
|
438
|
+
##
|
439
|
+
# Get Reporting Periods information and store in the feature_report
|
440
|
+
##
|
441
|
+
|
442
|
+
# start_date
|
443
|
+
# month
|
444
|
+
begin_month = model.getRunPeriod.getBeginMonth
|
445
|
+
feature_report.reporting_periods[0].start_date.month = begin_month
|
446
|
+
# day_of_month
|
447
|
+
begin_day_of_month = model.getRunPeriod.getBeginDayOfMonth
|
448
|
+
feature_report.reporting_periods[0].start_date.day_of_month = begin_day_of_month
|
449
|
+
# year
|
450
|
+
begin_year = model.getYearDescription.calendarYear
|
451
|
+
feature_report.reporting_periods[0].start_date.year = begin_year
|
452
|
+
|
453
|
+
# end_date
|
454
|
+
# month
|
455
|
+
end_month = model.getRunPeriod.getEndMonth
|
456
|
+
feature_report.reporting_periods[0].end_date.month = end_month
|
457
|
+
# day_of_month
|
458
|
+
end_day_of_month = model.getRunPeriod.getEndDayOfMonth
|
459
|
+
feature_report.reporting_periods[0].end_date.day_of_month = end_day_of_month
|
460
|
+
# year
|
461
|
+
end_year = model.getYearDescription.calendarYear
|
462
|
+
feature_report.reporting_periods[0].end_date.year = end_year
|
463
|
+
|
464
|
+
# total_site_energy
|
465
|
+
total_site_energy = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Site and Source Energy' AND RowName='Total Site Energy' AND ColumnName='Total Energy'")
|
466
|
+
feature_report.reporting_periods[0].total_site_energy = convert_units(total_site_energy, 'GJ', 'kBtu')
|
467
|
+
|
468
|
+
# total_source_energy
|
469
|
+
total_source_energy = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Site and Source Energy' AND RowName='Total Source Energy' AND ColumnName='Total Energy'")
|
470
|
+
feature_report.reporting_periods[0].total_source_energy = convert_units(total_source_energy, 'GJ', 'kBtu')
|
471
|
+
|
472
|
+
# net_site_energy
|
473
|
+
net_site_energy = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Site and Source Energy' AND RowName='Net Site Energy' AND ColumnName='Total Energy'")
|
474
|
+
feature_report.reporting_periods[0].net_site_energy = convert_units(net_site_energy, 'GJ', 'kBtu')
|
475
|
+
|
476
|
+
# net_source_energy
|
477
|
+
net_source_energy = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Site and Source Energy' AND RowName='Net Source Energy' AND ColumnName='Total Energy'")
|
478
|
+
feature_report.reporting_periods[0].net_source_energy = convert_units(net_source_energy, 'GJ', 'kBtu')
|
479
|
+
|
480
|
+
# electricity
|
481
|
+
electricity = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='End Uses' AND RowName='Total End Uses' AND ColumnName='Electricity'")
|
482
|
+
feature_report.reporting_periods[0].electricity = convert_units(electricity, 'GJ', 'kBtu')
|
483
|
+
|
484
|
+
# natural_gas
|
485
|
+
natural_gas = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='End Uses' AND RowName='Total End Uses' AND ColumnName='Natural Gas'")
|
486
|
+
feature_report.reporting_periods[0].natural_gas = convert_units(natural_gas, 'GJ', 'kBtu')
|
487
|
+
|
488
|
+
# additional_fuel
|
489
|
+
additional_fuel = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='End Uses' AND RowName='Total End Uses' AND ColumnName='Additional Fuel'")
|
490
|
+
feature_report.reporting_periods[0].additional_fuel = convert_units(additional_fuel, 'GJ', 'kBtu')
|
491
|
+
|
492
|
+
# district_cooling
|
493
|
+
district_cooling = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='End Uses' AND RowName='Total End Uses' AND ColumnName='District Cooling'")
|
494
|
+
feature_report.reporting_periods[0].district_cooling = convert_units(district_cooling, 'GJ', 'kBtu')
|
495
|
+
|
496
|
+
# district_heating
|
497
|
+
district_heating = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='End Uses' AND RowName='Total End Uses' AND ColumnName='District Heating'")
|
498
|
+
feature_report.reporting_periods[0].district_heating = convert_units(district_heating, 'GJ', 'kBtu')
|
499
|
+
|
500
|
+
# water
|
501
|
+
water = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='End Uses' AND RowName='Total End Uses' AND ColumnName='Water'")
|
502
|
+
# feature_report.reporting_periods[0].water = convert_units(water, 'm3', 'ft3')
|
503
|
+
feature_report.reporting_periods[0].water = water
|
504
|
+
|
505
|
+
# electricity_produced
|
506
|
+
electricity_produced = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Electric Loads Satisfied' AND RowName='Total On-Site and Utility Electric Sources' AND ColumnName='Electricity'")
|
507
|
+
feature_report.reporting_periods[0].electricity_produced = convert_units(electricity_produced, 'GJ', 'kBtu')
|
508
|
+
|
509
|
+
## end_uses
|
510
|
+
|
511
|
+
# get fuel type as listed in the sql file
|
512
|
+
fuel_type = ['Electricity', 'Natural Gas', 'Additional Fuel', 'District Cooling', 'District Heating', 'Water']
|
513
|
+
|
514
|
+
# get enduses as listed in the sql file
|
515
|
+
enduses = ['Heating', 'Cooling', 'Interior Lighting', 'Exterior Lighting', 'Interior Equipment', 'Exterior Equipment', 'Fans', 'Pumps',
|
516
|
+
'Heat Rejection', 'Humidification', 'Heat Recovery', 'Water Systems', 'Refrigeration', 'Generators']
|
517
|
+
|
518
|
+
# loop through fuel types and enduses to fill in sql_query method
|
519
|
+
fuel_type.each do |ft|
|
520
|
+
enduses.each do |eu|
|
521
|
+
sql_r = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='End Uses' AND RowName='#{eu}' AND ColumnName='#{ft}'")
|
522
|
+
|
523
|
+
# report each query in its corresponding feature report obeject
|
524
|
+
if ft.include? ' '
|
525
|
+
x = ft.tr(' ', '_').downcase
|
526
|
+
m = feature_report.reporting_periods[0].end_uses.send(x)
|
527
|
+
else
|
528
|
+
m = feature_report.reporting_periods[0].end_uses.send(ft.downcase)
|
529
|
+
|
530
|
+
end
|
531
|
+
|
532
|
+
if eu.include? ' '
|
533
|
+
y = eu.tr(' ', '_').downcase
|
534
|
+
m.send("#{y}=", convert_units(sql_r, 'GJ', 'kBtu'))
|
535
|
+
else
|
536
|
+
m.send("#{eu.downcase}=", convert_units(sql_r, 'GJ', 'kBtu'))
|
537
|
+
end
|
538
|
+
end
|
539
|
+
end
|
540
|
+
|
541
|
+
### energy_production
|
542
|
+
## electricity_produced
|
543
|
+
# photovoltaic
|
544
|
+
photovoltaic_power = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Electric Loads Satisfied' AND RowName='Photovoltaic Power' AND ColumnName='Electricity'")
|
545
|
+
feature_report.reporting_periods[0].energy_production[:electricity_produced][:photovoltaic] = convert_units(photovoltaic_power, 'GJ', 'kBtu')
|
546
|
+
|
547
|
+
## comfort_result
|
548
|
+
# time_setpoint_not_met_during_occupied_cooling
|
549
|
+
time_setpoint_not_met_during_occupied_cooling = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Comfort and Setpoint Not Met Summary' AND RowName='Time Setpoint Not Met During Occupied Cooling' AND ColumnName='Facility'")
|
550
|
+
feature_report.reporting_periods[0].comfort_result[:time_setpoint_not_met_during_occupied_cooling] = time_setpoint_not_met_during_occupied_cooling
|
551
|
+
|
552
|
+
# time_setpoint_not_met_during_occupied_heating
|
553
|
+
time_setpoint_not_met_during_occupied_heating = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Comfort and Setpoint Not Met Summary' AND RowName='Time Setpoint Not Met During Occupied Heating' AND ColumnName='Facility'")
|
554
|
+
feature_report.reporting_periods[0].comfort_result[:time_setpoint_not_met_during_occupied_heating] = time_setpoint_not_met_during_occupied_heating
|
555
|
+
|
556
|
+
# time_setpoint_not_met_during_occupied_hour
|
557
|
+
time_setpoint_not_met_during_occupied_hours = time_setpoint_not_met_during_occupied_heating + time_setpoint_not_met_during_occupied_cooling
|
558
|
+
feature_report.reporting_periods[0].comfort_result[:time_setpoint_not_met_during_occupied_hours] = time_setpoint_not_met_during_occupied_hours
|
559
|
+
|
560
|
+
######################################## Reporting TImeseries Results FOR CSV File ######################################
|
561
|
+
|
562
|
+
# Get the weather file run period (as opposed to design day run period)
|
563
|
+
ann_env_pd = nil
|
564
|
+
sql_file.availableEnvPeriods.each do |env_pd|
|
565
|
+
env_type = sql_file.environmentType(env_pd)
|
566
|
+
if env_type.is_initialized
|
567
|
+
if env_type.get == OpenStudio::EnvironmentType.new('WeatherRunPeriod')
|
568
|
+
ann_env_pd = env_pd
|
569
|
+
end
|
570
|
+
end
|
571
|
+
end
|
572
|
+
|
573
|
+
if ann_env_pd == false
|
574
|
+
runner.registerError("Can't find a weather runperiod, make sure you ran an annual simulation, not just the design days.")
|
575
|
+
return false
|
576
|
+
end
|
577
|
+
|
578
|
+
# timeseries we want to report
|
579
|
+
requested_timeseries_names = ['Electricity:Facility', 'ElectricityProduced:Facility', 'Gas:Facility',
|
580
|
+
'DistrictCooling:Facility', 'DistrictHeating:Facility', 'District Cooling Chilled Water Rate',
|
581
|
+
'District Cooling Mass Flow Rate', 'District Cooling Inlet Temperature', 'District Cooling Outlet Temperature',
|
582
|
+
'District Heating Hot Water Rate', 'District Heating Mass Flow Rate', 'District Heating Inlet Temperature', 'District Heating Outlet Temperature']
|
583
|
+
|
584
|
+
# number of values in each timeseries
|
585
|
+
n = nil
|
586
|
+
|
587
|
+
# all numeric timeseries values, transpose of CSV file (e.g. values[j] is column, values[j][i] is column and row)
|
588
|
+
values = []
|
589
|
+
|
590
|
+
# Since schedule value will have a bunch of key_values, we need to keep track of these as additional timeseries
|
591
|
+
# this is recording the name of these final timeseries to write in the header of the CSV
|
592
|
+
final_timeseries_names = []
|
593
|
+
|
594
|
+
# loop over requested timeseries
|
595
|
+
# rubocop: disable Metrics/BlockLength
|
596
|
+
requested_timeseries_names.each_with_index do |timeseries_name, j|
|
597
|
+
runner.registerInfo("TIMESERIES: #{timeseries_name}")
|
598
|
+
|
599
|
+
# get all the key values that this timeseries can be reported for (e.g. if power is requested for each zone)
|
600
|
+
key_values = sql_file.availableKeyValues(ann_env_pd.to_s, reporting_frequency.to_s, timeseries_name)
|
601
|
+
runner.registerInfo("KEY VALUES: #{key_values}")
|
602
|
+
if key_values.empty?
|
603
|
+
key_values = ['']
|
604
|
+
end
|
605
|
+
|
606
|
+
# sort keys
|
607
|
+
sorted_keys = key_values.sort
|
608
|
+
requested_keys = ['SUMMED ELECTRICITY:FACILITY', 'SUMMED ELECTRICITY:FACILITY POWER', 'SUMMED ELECTRICITYPRODUCED:FACILITY', 'SUMMED ELECTRICITYPRODUCED:FACILITY POWER', 'SUMMED NET APPARENT POWER', 'SUMMED NET ELECTRIC ENERGY', 'SUMMED NET POWER', 'TRANSFORMER OUTPUT ELECTRIC ENERGY SCHEDULE']
|
609
|
+
final_keys = []
|
610
|
+
# make sure aggregated timeseries are listed in sorted order before all individual feature timeseries
|
611
|
+
sorted_keys.each do |k|
|
612
|
+
if requested_keys.include? k
|
613
|
+
final_keys << k
|
614
|
+
end
|
615
|
+
end
|
616
|
+
sorted_keys.each do |k|
|
617
|
+
if !requested_keys.include? k
|
618
|
+
final_keys << k
|
619
|
+
end
|
620
|
+
end
|
621
|
+
|
622
|
+
# loop over final keys
|
623
|
+
final_keys.each_with_index do |key_value, key_i|
|
624
|
+
new_timeseries_name = ''
|
625
|
+
|
626
|
+
runner.registerInfo("!! TIMESERIES NAME: #{timeseries_name} AND key_value: #{key_value}")
|
627
|
+
|
628
|
+
# check if we have to come up with a new name for the timeseries in our CSV header
|
629
|
+
if key_values.size == 1
|
630
|
+
# use timeseries name when only 1 keyvalue
|
631
|
+
new_timeseries_name = timeseries_name
|
632
|
+
else
|
633
|
+
# use key_value name
|
634
|
+
# special case for Zone Thermal Comfort: use both timeseries_name and key_value
|
635
|
+
if timeseries_name.include? 'Zone Thermal Comfort'
|
636
|
+
new_timeseries_name = timeseries_name + ' ' + key_value
|
637
|
+
else
|
638
|
+
new_timeseries_name = key_value
|
639
|
+
end
|
640
|
+
end
|
641
|
+
final_timeseries_names << new_timeseries_name
|
642
|
+
|
643
|
+
# get the actual timeseries
|
644
|
+
ts = sql_file.timeSeries(ann_env_pd.to_s, reporting_frequency.to_s, timeseries_name, key_value)
|
645
|
+
|
646
|
+
if n.nil?
|
647
|
+
# first timeseries should always be set
|
648
|
+
runner.registerInfo('First timeseries')
|
649
|
+
values[j] = ts.get.values
|
650
|
+
n = values[j].size
|
651
|
+
elsif ts.is_initialized
|
652
|
+
runner.registerInfo('Is Initialized')
|
653
|
+
values[j] = ts.get.values
|
654
|
+
else
|
655
|
+
runner.registerInfo('Is NOT Initialized')
|
656
|
+
values[j] = Array.new(n, 0)
|
657
|
+
end
|
658
|
+
|
659
|
+
# ##Unit conversion
|
660
|
+
old_units = ts.get.units if ts.is_initialized
|
661
|
+
new_units = case old_units.to_s
|
662
|
+
when 'J'
|
663
|
+
'kBtu'
|
664
|
+
when 'kWh'
|
665
|
+
'kBtu'
|
666
|
+
when 'm3'
|
667
|
+
'gal'
|
668
|
+
end
|
669
|
+
|
670
|
+
# Unit conversion here
|
671
|
+
os_vec = values[j]
|
672
|
+
|
673
|
+
# loop through each value to retrieve it
|
674
|
+
for i in 0..os_vec.length - 1
|
675
|
+
unless new_units == old_units
|
676
|
+
os_vec[i] = OpenStudio.convert(os_vec[i], old_units, new_units).get
|
677
|
+
end
|
678
|
+
end
|
679
|
+
end
|
680
|
+
end
|
681
|
+
# rubocop: enable Metrics/BlockLength
|
682
|
+
runner.registerInfo("new final_timeseries_names size: #{final_timeseries_names.size}")
|
683
|
+
|
684
|
+
# Save the 'default_feature_reports.csv' file
|
685
|
+
File.open('default_feature_reports.csv', 'w') do |file|
|
686
|
+
file.puts(final_timeseries_names.join(','))
|
687
|
+
(0...n).each do |l|
|
688
|
+
line = []
|
689
|
+
values.each_index do |j|
|
690
|
+
line << values[j][l]
|
691
|
+
end
|
692
|
+
file.puts(line.join(','))
|
693
|
+
end
|
694
|
+
end
|
695
|
+
|
696
|
+
# closing the sql file
|
697
|
+
sql_file.close
|
698
|
+
|
699
|
+
############################# Adding timeseries_csv info to json report and saving CSV ################################
|
700
|
+
# add csv info to feature_report
|
701
|
+
feature_report.timeseries_csv.path = File.join(Dir.pwd, 'default_feature_reports.csv')
|
702
|
+
feature_report.timeseries_csv.first_report_datetime = '0'
|
703
|
+
feature_report.timeseries_csv.column_names = final_timeseries_names
|
704
|
+
|
705
|
+
##### Save the 'default_feature_reports.json' file
|
706
|
+
|
707
|
+
feature_report_hash = feature_report.to_hash
|
708
|
+
|
709
|
+
File.open('default_feature_reports.json', 'w') do |f|
|
710
|
+
f.puts JSON.pretty_generate(feature_report_hash)
|
711
|
+
# make sure data is written to the disk one way or the other
|
712
|
+
begin
|
713
|
+
f.fsync
|
714
|
+
rescue StandardError
|
715
|
+
f.flush
|
716
|
+
end
|
717
|
+
end
|
718
|
+
|
719
|
+
# reporting final condition
|
720
|
+
runner.registerFinalCondition('Default Feature Reports generated successfully.')
|
721
|
+
|
722
|
+
true
|
723
|
+
# end the run method
|
724
|
+
end
|
725
|
+
# end the measure
|
726
|
+
end
|
727
|
+
# rubocop:enable Metrics/AbcSize
|
728
|
+
# rubocop:enable Naming/MethodName
|
729
|
+
|
730
|
+
# register the measure to be used by the application
|
731
|
+
DefaultFeatureReports.new.registerWithApplication
|