urbanopt-scenario 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.github/pull_request_template.md +2 -2
  3. data/.gitignore +2 -0
  4. data/CHANGELOG.md +32 -0
  5. data/CONTRIBUTING.md +1 -1
  6. data/Gemfile +33 -17
  7. data/Jenkinsfile +1 -1
  8. data/LICENSE.md +1 -1
  9. data/RDOC_MAIN.md +1 -1
  10. data/README.md +1 -1
  11. data/Rakefile +1 -1
  12. data/docs/README.md +1 -1
  13. data/docs/package-lock.json +2499 -2322
  14. data/docs/package.json +13 -9
  15. data/lib/urbanopt-scenario.rb +1 -1
  16. data/lib/urbanopt/scenario.rb +2 -1
  17. data/lib/urbanopt/scenario/default_reports.rb +3 -8
  18. data/lib/urbanopt/scenario/extension.rb +1 -1
  19. data/lib/urbanopt/scenario/logger.rb +1 -1
  20. data/lib/urbanopt/scenario/scenario_base.rb +1 -1
  21. data/lib/urbanopt/scenario/scenario_csv.rb +22 -9
  22. data/lib/urbanopt/scenario/scenario_datapoint_base.rb +8 -1
  23. data/lib/urbanopt/scenario/scenario_post_processor_base.rb +1 -1
  24. data/lib/urbanopt/scenario/scenario_post_processor_default.rb +81 -8
  25. data/lib/urbanopt/scenario/scenario_post_processor_opendss.rb +6 -7
  26. data/lib/urbanopt/scenario/scenario_runner_base.rb +2 -2
  27. data/lib/urbanopt/scenario/scenario_runner_osw.rb +23 -9
  28. data/lib/urbanopt/scenario/scenario_visualization.rb +236 -0
  29. data/lib/urbanopt/scenario/simulation_dir_base.rb +1 -1
  30. data/lib/urbanopt/scenario/simulation_dir_osw.rb +2 -5
  31. data/lib/urbanopt/scenario/simulation_mapper_base.rb +1 -1
  32. data/lib/urbanopt/scenario/version.rb +2 -2
  33. data/package-lock.json +3 -0
  34. data/urbanopt-scenario-gem.gemspec +10 -6
  35. metadata +73 -46
  36. data/doc_templates/LICENSE.md +0 -27
  37. data/doc_templates/README.md.erb +0 -42
  38. data/doc_templates/copyright_erb.txt +0 -31
  39. data/doc_templates/copyright_js.txt +0 -4
  40. data/doc_templates/copyright_ruby.txt +0 -29
  41. data/lib/measures/.rubocop.yml +0 -5
  42. data/lib/measures/default_feature_reports/LICENSE.md +0 -27
  43. data/lib/measures/default_feature_reports/README.md +0 -26
  44. data/lib/measures/default_feature_reports/README.md.erb +0 -42
  45. data/lib/measures/default_feature_reports/measure.rb +0 -1013
  46. data/lib/measures/default_feature_reports/measure.xml +0 -160
  47. data/lib/urbanopt/scenario/default_reports/construction_cost.rb +0 -169
  48. data/lib/urbanopt/scenario/default_reports/date.rb +0 -97
  49. data/lib/urbanopt/scenario/default_reports/distributed_generation.rb +0 -379
  50. data/lib/urbanopt/scenario/default_reports/end_use.rb +0 -159
  51. data/lib/urbanopt/scenario/default_reports/end_uses.rb +0 -140
  52. data/lib/urbanopt/scenario/default_reports/feature_report.rb +0 -267
  53. data/lib/urbanopt/scenario/default_reports/generator.rb +0 -92
  54. data/lib/urbanopt/scenario/default_reports/location.rb +0 -99
  55. data/lib/urbanopt/scenario/default_reports/logger.rb +0 -44
  56. data/lib/urbanopt/scenario/default_reports/power_distribution.rb +0 -102
  57. data/lib/urbanopt/scenario/default_reports/program.rb +0 -265
  58. data/lib/urbanopt/scenario/default_reports/reporting_period.rb +0 -304
  59. data/lib/urbanopt/scenario/default_reports/scenario_report.rb +0 -317
  60. data/lib/urbanopt/scenario/default_reports/schema/README.md +0 -33
  61. data/lib/urbanopt/scenario/default_reports/schema/scenario_csv_columns.txt +0 -34
  62. data/lib/urbanopt/scenario/default_reports/schema/scenario_schema.json +0 -857
  63. data/lib/urbanopt/scenario/default_reports/solar_pv.rb +0 -93
  64. data/lib/urbanopt/scenario/default_reports/storage.rb +0 -105
  65. data/lib/urbanopt/scenario/default_reports/timeseries_csv.rb +0 -299
  66. data/lib/urbanopt/scenario/default_reports/validator.rb +0 -97
  67. data/lib/urbanopt/scenario/default_reports/wind.rb +0 -92
@@ -1,5 +0,0 @@
1
- AllCops:
2
- Exclude:
3
- - 'spec/test_measures/**/*'
4
- inherit_from:
5
- - http://s3.amazonaws.com/openstudio-resources/styles/rubocop_v3.yml
@@ -1,27 +0,0 @@
1
- URBANopt, Copyright (c) 2019-2020, 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.
@@ -1,26 +0,0 @@
1
-
2
-
3
- ###### (Automatically generated documentation)
4
-
5
- #
6
-
7
- ## Description
8
-
9
-
10
- ## Modeler Description
11
-
12
-
13
- ## Measure Type
14
- ModelMeasure
15
-
16
- ## Taxonomy
17
-
18
-
19
- ## Arguments
20
-
21
-
22
-
23
-
24
- This measure does not have any user arguments
25
-
26
-
@@ -1,42 +0,0 @@
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 %>
@@ -1,1013 +0,0 @@
1
- # *********************************************************************************
2
- # URBANopt, Copyright (c) 2019-2020, 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 and default_feature_reports.csv files 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 << 'Zone Timestep'
81
- reporting_frequency_chs << 'BillingPeriod' # match it to utility bill object
82
- ## Utility report here to report the start and end for each fueltype
83
- reporting_frequency_chs << 'Monthly'
84
- reporting_frequency_chs << 'Runperiod'
85
-
86
- reporting_frequency = OpenStudio::Measure::OSArgument.makeChoiceArgument('reporting_frequency', reporting_frequency_chs, true)
87
- reporting_frequency.setDisplayName('Reporting Frequency')
88
- reporting_frequency.setDescription('The frequency at which to report timeseries output data.')
89
- reporting_frequency.setDefaultValue('Timestep')
90
- args << reporting_frequency
91
-
92
- return args
93
- end
94
-
95
- # define fuel types
96
- def fuel_types
97
- fuel_types = [
98
- 'Electricity',
99
- 'Gas',
100
- 'AdditionalFuel',
101
- 'DistrictCooling',
102
- 'DistrictHeating',
103
- 'Water'
104
- ]
105
-
106
- return fuel_types
107
- end
108
-
109
- # define enduses
110
- def end_uses
111
- end_uses = [
112
- 'Heating',
113
- 'Cooling',
114
- 'InteriorLights',
115
- 'ExteriorLights',
116
- 'InteriorEquipment',
117
- 'ExteriorEquipment',
118
- 'Fans',
119
- 'Pumps',
120
- 'HeatRejection',
121
- 'Humidifier',
122
- 'HeatRecovery',
123
- 'WaterSystems',
124
- 'Refrigeration',
125
- 'Generators',
126
- 'Facility'
127
- ]
128
-
129
- return end_uses
130
- end
131
-
132
- # format datetime
133
- def format_datetime(date_time)
134
- date_time.tr!('-', '/')
135
- date_time.gsub!('Jan', '01')
136
- date_time.gsub!('Feb', '02')
137
- date_time.gsub!('Mar', '03')
138
- date_time.gsub!('Apr', '04')
139
- date_time.gsub!('May', '05')
140
- date_time.gsub!('Jun', '06')
141
- date_time.gsub!('Jul', '07')
142
- date_time.gsub!('Aug', '08')
143
- date_time.gsub!('Sep', '09')
144
- date_time.gsub!('Oct', '10')
145
- date_time.gsub!('Nov', '11')
146
- date_time.gsub!('Dec', '12')
147
- return date_time
148
- end
149
-
150
- # return a vector of IdfObject's to request EnergyPlus objects needed by the run method
151
- # rubocop:disable Naming/MethodName
152
- def energyPlusOutputRequests(runner, user_arguments)
153
- super(runner, user_arguments)
154
-
155
- result = OpenStudio::IdfObjectVector.new
156
-
157
- reporting_frequency = runner.getStringArgumentValue('reporting_frequency', user_arguments)
158
-
159
- # Request the output for each end use/fuel type combination
160
- end_uses.each do |end_use|
161
- fuel_types.each do |fuel_type|
162
- variable_name = if end_use == 'Facility'
163
- "#{fuel_type}:#{end_use}"
164
- else
165
- "#{end_use}:#{fuel_type}"
166
- end
167
- result << OpenStudio::IdfObject.load("Output:Meter,#{variable_name},#{reporting_frequency};").get
168
- end
169
- end
170
-
171
- # Request the output for each end use/fuel type combination
172
- result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,Electricity:Facility,#{reporting_frequency};").get
173
- result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,ElectricityProduced:Facility,#{reporting_frequency};").get
174
- result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,Gas:Facility,#{reporting_frequency};").get
175
- result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,DistrictCooling:Facility,#{reporting_frequency};").get
176
- result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,DistrictHeating:Facility,#{reporting_frequency};").get
177
- # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,Cooling:Electricity,#{reporting_frequency};").get
178
- # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,Heating:Electricity,#{reporting_frequency};").get
179
- # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,InteriorLights:Electricity,#{reporting_frequency};").get
180
- # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,ExteriorLights:Electricity,#{reporting_frequency};").get
181
- # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,InteriorEquipment:Electricity,#{reporting_frequency};").get
182
- # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,Fans:Electricity,#{reporting_frequency};").get
183
- # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,Pumps:Electricity,#{reporting_frequency};").get
184
- # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,WaterSystems:Electricity,#{reporting_frequency};").get
185
- # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,Heating:Gas,#{reporting_frequency};").get
186
- # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,WaterSystems:Gas,#{reporting_frequency};").get
187
- # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,InteriorEquipment:Gas,#{reporting_frequency};").get
188
- result << OpenStudio::IdfObject.load('Output:Variable,*,Heating Coil Heating Rate,hourly; !- HVAC Average [W];').get
189
-
190
- timeseries_data = ['District Cooling Chilled Water Rate', 'District Cooling Mass Flow Rate',
191
- 'District Cooling Inlet Temperature', 'District Cooling Outlet Temperature',
192
- 'District Heating Hot Water Rate', 'District Heating Mass Flow Rate',
193
- 'District Heating Inlet Temperature', 'District Heating Outlet Temperature', 'Cooling Coil Total Cooling Rate',
194
- 'Heating Coil Heating Rate']
195
-
196
- timeseries_data.each do |ts|
197
- result << OpenStudio::IdfObject.load("Output:Variable,*,#{ts},#{reporting_frequency};").get
198
- end
199
-
200
- # use the built-in error checking
201
- if !runner.validateUserArguments(arguments, user_arguments)
202
- return result
203
- end
204
-
205
- return result
206
- end
207
-
208
- # sql_query method
209
- def sql_query(runner, sql, report_name, query)
210
- val = nil
211
- result = sql.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='#{report_name}' AND #{query}")
212
- if result.empty?
213
- runner.registerWarning("Query failed for #{report_name} and #{query}")
214
- else
215
- begin
216
- val = result.get
217
- rescue StandardError
218
- val = nil
219
- runner.registerWarning('Query result.get failed')
220
- end
221
- end
222
-
223
- val
224
- end
225
-
226
- # unit conversion method
227
- def convert_units(value, from_units, to_units)
228
- # apply unit conversion
229
- value_converted = OpenStudio.convert(value, from_units, to_units)
230
- if value_converted.is_initialized
231
- value = value_converted.get
232
- else
233
- @runner.registerError("Was not able to convert #{value} from #{from_units} to #{to_units}.")
234
- value = nil
235
- end
236
- return value
237
- end
238
-
239
- # define what happens when the measure is run
240
- # rubocop:disable Metrics/AbcSize
241
- def run(runner, user_arguments)
242
- super(runner, user_arguments)
243
-
244
- # use the built-in error checking
245
- unless runner.validateUserArguments(arguments, user_arguments)
246
- return false
247
- end
248
-
249
- # use the built-in error checking
250
- if !runner.validateUserArguments(arguments, user_arguments)
251
- return false
252
- end
253
-
254
- feature_id = runner.getStringArgumentValue('feature_id', user_arguments)
255
- feature_name = runner.getStringArgumentValue('feature_name', user_arguments)
256
- feature_type = runner.getStringArgumentValue('feature_type', user_arguments)
257
-
258
- # Assign the user inputs to variables
259
- reporting_frequency = runner.getStringArgumentValue('reporting_frequency', user_arguments)
260
-
261
- # BilingPeriod reporting frequency not implemented yet
262
- if reporting_frequency == 'BillingPeriod'
263
- @@logger.error('BillingPeriod frequency is not implemented yet')
264
- end
265
-
266
- # cache runner for this instance of the measure
267
- @runner = runner
268
-
269
- # get the WorkflowJSON object
270
- workflow = runner.workflow
271
-
272
- # get the last model and sql file
273
- model = runner.lastOpenStudioModel
274
- if model.empty?
275
- runner.registerError('Cannot find last model.')
276
- return false
277
- end
278
- model = model.get
279
-
280
- sql_file = runner.lastEnergyPlusSqlFile
281
- if sql_file.empty?
282
- runner.registerError('Cannot find last sql file.')
283
- return false
284
- end
285
- sql_file = sql_file.get
286
- model.setSqlFile(sql_file)
287
-
288
- # get building from model
289
- building = model.getBuilding
290
-
291
- # get surfaces from model
292
- surfaces = model.getSurfaces
293
-
294
- # get epw_file
295
- epw_file = runner.lastEpwFile
296
- if epw_file.empty?
297
- runner.registerError('Cannot find last epw file.')
298
- return false
299
- end
300
- epw_file = epw_file.get
301
-
302
- # create output feature_report report object
303
- feature_report = URBANopt::Scenario::DefaultReports::FeatureReport.new
304
- feature_report.id = feature_id
305
- feature_report.name = feature_name
306
- feature_report.feature_type = feature_type
307
- feature_report.directory_name = workflow.absoluteRunDir
308
-
309
- timesteps_per_hour = model.getTimestep.numberOfTimestepsPerHour
310
- feature_report.timesteps_per_hour = timesteps_per_hour
311
-
312
- feature_report.simulation_status = 'Complete'
313
-
314
- feature_report.reporting_periods << URBANopt::Scenario::DefaultReports::ReportingPeriod.new
315
-
316
- ###########################################################################
317
- ##
318
- # Get Location information and store in the feature_report
319
- ##
320
-
321
- # latitude
322
- latitude = epw_file.latitude
323
- feature_report.location.latitude = latitude
324
-
325
- # longitude
326
- longitude = epw_file.longitude
327
- feature_report.location.longitude = longitude
328
-
329
- # surface_elevation
330
- elev = sql_query(runner, sql_file, 'InputVerificationandResultsSummary', "TableName='General' AND RowName='Elevation' AND ColumnName='Value'")
331
- feature_report.location.surface_elevation = elev
332
-
333
- ##########################################################################
334
- ##
335
- # Get program information and store in the feature_report
336
- ##
337
-
338
- # floor_area
339
- floor_area = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Building Area' AND RowName='Total Building Area' AND ColumnName='Area'")
340
- feature_report.program.floor_area = convert_units(floor_area, 'm^2', 'ft^2')
341
-
342
- # conditioned_area
343
- conditioned_area = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Building Area' AND RowName='Net Conditioned Building Area' AND ColumnName='Area'")
344
- feature_report.program.conditioned_area = convert_units(conditioned_area, 'm^2', 'ft^2')
345
-
346
- # unconditioned_area
347
- unconditioned_area = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Building Area' AND RowName='Unconditioned Building Area' AND ColumnName='Area'")
348
- feature_report.program.unconditioned_area = convert_units(unconditioned_area, 'm^2', 'ft^2')
349
-
350
- # footprint_area
351
- feature_report.program.footprint_area = convert_units(floor_area, 'm^2', 'ft^2')
352
-
353
- # maximum_number_of_stories
354
- number_of_stories = building.standardsNumberOfStories.get if building.standardsNumberOfStories.is_initialized
355
- number_of_stories ||= 1
356
- feature_report.program.maximum_number_of_stories = number_of_stories
357
-
358
- # maximum_roof_height
359
- floor_to_floor_height = building.nominalFloortoFloorHeight.to_f
360
- maximum_roof_height = number_of_stories * floor_to_floor_height
361
- feature_report.program.maximum_roof_height = maximum_roof_height
362
-
363
- # maximum_number_of_stories_above_ground
364
- number_of_stories_above_ground = building.standardsNumberOfAboveGroundStories.get if building.standardsNumberOfAboveGroundStories.is_initialized
365
- number_of_stories_above_ground ||= 1
366
- feature_report.program.maximum_number_of_stories_above_ground = number_of_stories_above_ground
367
-
368
- # number_of_residential_units
369
- number_of_living_units = building.standardsNumberOfLivingUnits.to_i.get if building.standardsNumberOfLivingUnits.is_initialized
370
- number_of_living_units ||= 1
371
- feature_report.program.number_of_residential_units = number_of_living_units
372
-
373
- ## building_types
374
-
375
- # get an array of the model spaces
376
- spaces = model.getSpaces
377
-
378
- # get array of model space types
379
- space_types = model.getSpaceTypes
380
-
381
- # create a hash for space_type_areas (spcace types as keys and their areas as values)
382
- space_type_areas = {}
383
- model.getSpaceTypes.each do |space_type|
384
- building_type = space_type.standardsBuildingType
385
- if building_type.empty?
386
- building_type = 'unknown'
387
- else
388
- building_type = building_type.get
389
- end
390
- space_type_areas[building_type] = 0 if space_type_areas[building_type].nil?
391
- space_type_areas[building_type] += convert_units(space_type.floorArea, 'm^2', 'ft^2')
392
- end
393
-
394
- # create a hash for space_type_occupancy (spcace types as keys and their occupancy as values)
395
- space_type_occupancy = {}
396
- spaces.each do |space|
397
- if space.spaceType.empty?
398
- raise 'space.spaceType is empty. Make sure spaces have a space type'
399
- else
400
- building_type = space.spaceType.get.standardsBuildingType
401
- end
402
- if building_type.empty?
403
- building_type = 'unknown'
404
- else
405
- building_type = building_type.get
406
- end
407
- space_type_occupancy[building_type] = 0 if space_type_occupancy[building_type].nil?
408
- space_type_occupancy[building_type] += space.numberOfPeople
409
- end
410
-
411
- # combine all in a building_types array
412
- building_types = []
413
- for i in 0..(space_type_areas.size - 1)
414
- building_types << { building_type: space_type_areas.keys[i], floor_area: space_type_areas.values[i], maximum_occupancy: space_type_occupancy.values[i] }
415
- end
416
- # add results to the feature report JSON
417
- feature_report.program.building_types = building_types
418
-
419
- ## window_area
420
- # north_window_area
421
- 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
422
- feature_report.program.window_area[:north_window_area] = convert_units(north_window_area, 'm^2', 'ft^2')
423
- # south_window_area
424
- 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
425
- feature_report.program.window_area[:south_window_area] = convert_units(south_window_area, 'm^2', 'ft^2')
426
- # east_window_area
427
- 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
428
- feature_report.program.window_area[:east_window_area] = convert_units(east_window_area, 'm^2', 'ft^2')
429
- # west_window_area
430
- 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
431
- feature_report.program.window_area[:west_window_area] = convert_units(west_window_area, 'm^2', 'ft^2')
432
- # total_window_area
433
- total_window_area = north_window_area + south_window_area + east_window_area + west_window_area
434
- feature_report.program.window_area[:total_window_area] = convert_units(total_window_area, 'm^2', 'ft^2')
435
-
436
- ## wall_area
437
- # north_wall_area
438
- 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
439
- feature_report.program.wall_area[:north_wall_area] = convert_units(north_wall_area, 'm^2', 'ft^2')
440
- # south_wall_area
441
- 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
442
- feature_report.program.wall_area[:south_wall_area] = convert_units(south_wall_area, 'm^2', 'ft^2')
443
- # east_wall_area
444
- 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
445
- feature_report.program.wall_area[:east_wall_area] = convert_units(east_wall_area, 'm^2', 'ft^2')
446
- # west_wall_area
447
- 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
448
- feature_report.program.wall_area[:west_wall_area] = convert_units(west_wall_area, 'm^2', 'ft^2')
449
- # total_wall_area
450
- total_wall_area = north_wall_area + south_wall_area + east_wall_area + west_wall_area
451
- feature_report.program.wall_area[:total_wall_area] = convert_units(total_wall_area, 'm^2', 'ft^2')
452
-
453
- # total_roof_area
454
- total_roof_area = 0.0
455
- surfaces.each do |surface|
456
- if (surface.outsideBoundaryCondition == 'Outdoors') && (surface.surfaceType == 'RoofCeiling')
457
- total_roof_area += surface.netArea
458
- end
459
- end
460
- feature_report.program.roof_area[:total_roof_area] = convert_units(total_roof_area, 'm^2', 'ft^2')
461
-
462
- # orientation
463
- # RK: a more robust method should be implemented to find orientation(finding main axis of the building using aspect ratio)
464
- building_rotation = model.getBuilding.northAxis
465
- feature_report.program.orientation = building_rotation
466
-
467
- # aspect_ratio
468
- 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)'")
469
- 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)'")
470
- aspect_ratio = north_wall_area / east_wall_area if north_wall_area != 0 && east_wall_area != 0
471
- aspect_ratio ||= nil
472
- feature_report.program.aspect_ratio = aspect_ratio
473
-
474
- # total_construction_cost
475
- total_construction_cost = sql_query(runner, sql_file, 'Life-Cycle Cost Report', "TableName='Present Value for Recurring, Nonrecurring and Energy Costs (Before Tax)' AND RowName='LCC_MAT - BUILDING - LIFE CYCLE COSTS' AND ColumnName='Cost'")
476
- feature_report.program.total_construction_cost = total_construction_cost
477
-
478
- ############################################################################
479
- ##
480
- # Get Reporting Periods information and store in the feature_report
481
- ##
482
-
483
- # start_date
484
- # month
485
- begin_month = model.getRunPeriod.getBeginMonth
486
- feature_report.reporting_periods[0].start_date.month = begin_month
487
- # day_of_month
488
- begin_day_of_month = model.getRunPeriod.getBeginDayOfMonth
489
- feature_report.reporting_periods[0].start_date.day_of_month = begin_day_of_month
490
- # year
491
- begin_year = model.getYearDescription.calendarYear
492
- feature_report.reporting_periods[0].start_date.year = begin_year
493
-
494
- # end_date
495
- # month
496
- end_month = model.getRunPeriod.getEndMonth
497
- feature_report.reporting_periods[0].end_date.month = end_month
498
- # day_of_month
499
- end_day_of_month = model.getRunPeriod.getEndDayOfMonth
500
- feature_report.reporting_periods[0].end_date.day_of_month = end_day_of_month
501
- # year
502
- end_year = model.getYearDescription.calendarYear
503
- feature_report.reporting_periods[0].end_date.year = end_year
504
-
505
- # total_site_energy
506
- total_site_energy = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Site and Source Energy' AND RowName='Total Site Energy' AND ColumnName='Total Energy'")
507
- feature_report.reporting_periods[0].total_site_energy = convert_units(total_site_energy, 'GJ', 'kBtu')
508
-
509
- # total_source_energy
510
- total_source_energy = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Site and Source Energy' AND RowName='Total Source Energy' AND ColumnName='Total Energy'")
511
- feature_report.reporting_periods[0].total_source_energy = convert_units(total_source_energy, 'GJ', 'kBtu')
512
-
513
- # net_site_energy
514
- net_site_energy = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Site and Source Energy' AND RowName='Net Site Energy' AND ColumnName='Total Energy'")
515
- feature_report.reporting_periods[0].net_site_energy = convert_units(net_site_energy, 'GJ', 'kBtu')
516
-
517
- # net_source_energy
518
- net_source_energy = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Site and Source Energy' AND RowName='Net Source Energy' AND ColumnName='Total Energy'")
519
- feature_report.reporting_periods[0].net_source_energy = convert_units(net_source_energy, 'GJ', 'kBtu')
520
-
521
- # electricity
522
- electricity = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='End Uses' AND RowName='Total End Uses' AND ColumnName='Electricity'")
523
- feature_report.reporting_periods[0].electricity = convert_units(electricity, 'GJ', 'kBtu')
524
-
525
- # natural_gas
526
- natural_gas = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='End Uses' AND RowName='Total End Uses' AND ColumnName='Natural Gas'")
527
- feature_report.reporting_periods[0].natural_gas = convert_units(natural_gas, 'GJ', 'kBtu')
528
-
529
- # additional_fuel
530
- additional_fuel = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='End Uses' AND RowName='Total End Uses' AND ColumnName='Additional Fuel'")
531
- feature_report.reporting_periods[0].additional_fuel = convert_units(additional_fuel, 'GJ', 'kBtu')
532
-
533
- # district_cooling
534
- district_cooling = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='End Uses' AND RowName='Total End Uses' AND ColumnName='District Cooling'")
535
- feature_report.reporting_periods[0].district_cooling = convert_units(district_cooling, 'GJ', 'kBtu')
536
-
537
- # district_heating
538
- district_heating = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='End Uses' AND RowName='Total End Uses' AND ColumnName='District Heating'")
539
- feature_report.reporting_periods[0].district_heating = convert_units(district_heating, 'GJ', 'kBtu')
540
-
541
- # water
542
- water = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='End Uses' AND RowName='Total End Uses' AND ColumnName='Water'")
543
- # feature_report.reporting_periods[0].water = convert_units(water, 'm3', 'ft3')
544
- feature_report.reporting_periods[0].water = water
545
-
546
- # electricity_produced
547
- electricity_produced = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Electric Loads Satisfied' AND RowName='Total On-Site and Utility Electric Sources' AND ColumnName='Electricity'")
548
- feature_report.reporting_periods[0].electricity_produced = convert_units(electricity_produced, 'GJ', 'kBtu')
549
-
550
- ## end_uses
551
-
552
- # get fuel type as listed in the sql file
553
- fuel_type = ['Electricity', 'Natural Gas', 'Additional Fuel', 'District Cooling', 'District Heating', 'Water']
554
-
555
- # get enduses as listed in the sql file
556
- enduses = ['Heating', 'Cooling', 'Interior Lighting', 'Exterior Lighting', 'Interior Equipment', 'Exterior Equipment', 'Fans', 'Pumps',
557
- 'Heat Rejection', 'Humidification', 'Heat Recovery', 'Water Systems', 'Refrigeration', 'Generators']
558
-
559
- # loop through fuel types and enduses to fill in sql_query method
560
- fuel_type.each do |ft|
561
- enduses.each do |eu|
562
- sql_r = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='End Uses' AND RowName='#{eu}' AND ColumnName='#{ft}'")
563
-
564
- # report each query in its corresponding feature report obeject
565
- if ft.include? ' '
566
- x = ft.tr(' ', '_').downcase
567
- m = feature_report.reporting_periods[0].end_uses.send(x)
568
- else
569
- m = feature_report.reporting_periods[0].end_uses.send(ft.downcase)
570
-
571
- end
572
-
573
- if eu.include? ' '
574
- y = eu.tr(' ', '_').downcase
575
- m.send("#{y}=", convert_units(sql_r, 'GJ', 'kBtu'))
576
- else
577
- m.send("#{eu.downcase}=", convert_units(sql_r, 'GJ', 'kBtu'))
578
- end
579
- end
580
- end
581
-
582
- ### energy_production
583
- ## electricity_produced
584
- # photovoltaic
585
- photovoltaic_power = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Electric Loads Satisfied' AND RowName='Photovoltaic Power' AND ColumnName='Electricity'")
586
- feature_report.reporting_periods[0].energy_production[:electricity_produced][:photovoltaic] = convert_units(photovoltaic_power, 'GJ', 'kBtu')
587
-
588
- ## Total utility cost
589
- total_utility_cost = sql_query(runner, sql_file, 'Economics Results Summary Report', "TableName='Annual Cost' AND RowName='Cost' AND ColumnName='Total'")
590
- feature_report.reporting_periods[0].total_utility_cost = total_utility_cost
591
-
592
- ## Utility Costs
593
- # electricity utility cost
594
- elec_utility_cost = sql_query(runner, sql_file, 'Economics Results Summary Report', "TableName='Annual Cost' AND RowName='Cost' AND ColumnName='Electric'")
595
- feature_report.reporting_periods[0].utility_costs[0][:fuel_type] = 'Electricity'
596
- feature_report.reporting_periods[0].utility_costs[0][:total_cost] = elec_utility_cost
597
- # gas utility cost
598
- gas_utility_cost = sql_query(runner, sql_file, 'Economics Results Summary Report', "TableName='Annual Cost' AND RowName='Cost' AND ColumnName='Gas'")
599
- feature_report.reporting_periods[0].utility_costs << { fuel_type: 'Natural Gas', total_cost: gas_utility_cost }
600
-
601
- ## comfort_result
602
- # time_setpoint_not_met_during_occupied_cooling
603
- 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'")
604
- feature_report.reporting_periods[0].comfort_result[:time_setpoint_not_met_during_occupied_cooling] = time_setpoint_not_met_during_occupied_cooling
605
-
606
- # time_setpoint_not_met_during_occupied_heating
607
- 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'")
608
- feature_report.reporting_periods[0].comfort_result[:time_setpoint_not_met_during_occupied_heating] = time_setpoint_not_met_during_occupied_heating
609
-
610
- # time_setpoint_not_met_during_occupied_hour
611
- time_setpoint_not_met_during_occupied_hours = time_setpoint_not_met_during_occupied_heating + time_setpoint_not_met_during_occupied_cooling
612
- feature_report.reporting_periods[0].comfort_result[:time_setpoint_not_met_during_occupied_hours] = time_setpoint_not_met_during_occupied_hours
613
-
614
- ######################################## Reporting TImeseries Results FOR CSV File ######################################
615
-
616
- # Get the weather file run period (as opposed to design day run period)
617
- ann_env_pd = nil
618
- sql_file.availableEnvPeriods.each do |env_pd|
619
- env_type = sql_file.environmentType(env_pd)
620
- if env_type.is_initialized
621
- if env_type.get == OpenStudio::EnvironmentType.new('WeatherRunPeriod')
622
- ann_env_pd = env_pd
623
- end
624
- end
625
- end
626
-
627
- if ann_env_pd == false
628
- runner.registerError("Can't find a weather runperiod, make sure you ran an annual simulation, not just the design days.")
629
- return false
630
- end
631
-
632
- # timeseries we want to report
633
- requested_timeseries_names = [
634
- 'Electricity:Facility',
635
- 'ElectricityProduced:Facility',
636
- 'Gas:Facility',
637
- 'Cooling:Electricity',
638
- 'Heating:Electricity',
639
- 'InteriorLights:Electricity',
640
- 'ExteriorLights:Electricity',
641
- 'InteriorEquipment:Electricity',
642
- 'Fans:Electricity',
643
- 'Pumps:Electricity',
644
- 'WaterSystems:Electricity',
645
- 'HeatRejection:Electricity',
646
- 'HeatRejection:Gas',
647
- 'Heating:Gas',
648
- 'WaterSystems:Gas',
649
- 'InteriorEquipment:Gas',
650
- 'DistrictCooling:Facility',
651
- 'DistrictHeating:Facility',
652
- 'District Cooling Chilled Water Rate',
653
- 'District Cooling Mass Flow Rate',
654
- 'District Cooling Inlet Temperature',
655
- 'District Cooling Outlet Temperature',
656
- 'District Heating Hot Water Rate',
657
- 'District Heating Mass Flow Rate',
658
- 'District Heating Inlet Temperature',
659
- 'District Heating Outlet Temperature',
660
- 'Cooling Coil Total Cooling Rate',
661
- 'Heating Coil Heating Rate'
662
- ]
663
-
664
- # add thermal comfort timeseries
665
- comfortTimeseries = ['Zone Thermal Comfort Fanger Model PMV', 'Zone Thermal Comfort Fanger Model PPD']
666
- requested_timeseries_names += comfortTimeseries
667
-
668
- # add additional power timeseries (for calculating transformer apparent power to compare to rating ) in VA
669
- powerTimeseries = ['Net Electric Energy', 'Electricity:Facility Power', 'ElectricityProduced:Facility Power', 'Electricity:Facility Apparent Power', 'ElectricityProduced:Facility Apparent Power', 'Net Power', 'Net Apparent Power']
670
- requested_timeseries_names += powerTimeseries
671
-
672
- # register info all timeseries
673
- runner.registerInfo("All timeseries: #{requested_timeseries_names}")
674
-
675
- # timeseries variables to keep to calculate power
676
- tsToKeep = ['Electricity:Facility', 'ElectricityProduced:Facility']
677
- tsToKeepIndexes = {}
678
-
679
- ### powerFactor ###
680
- # use power_factor default: 0.9
681
- # TODO: Set powerFactor default based on building type
682
- powerFactor = 0.9
683
-
684
- ### power_conversion ###
685
- # divide values by total_seconds to convert J to W (W = J/sec)
686
- # divide values by total_hours to convert kWh to kW (kW = kWh/hrs)
687
- total_seconds = (60 / timesteps_per_hour.to_f) * 60 # make sure timesteps_per_hour is a float in the division
688
- total_hours = 1 / timesteps_per_hour.to_f # make sure timesteps_per_hour is a float in the division
689
- # set power_conversion
690
- power_conversion = total_hours # we set the power conversio to total_hours since we want to convert lWh to kW
691
- puts "Power Converion: to convert kWh to kW values will be divided by #{power_conversion}"
692
-
693
- # number of values in each timeseries
694
- n = nil
695
- # all numeric timeseries values, transpose of CSV file (e.g. values[key_cnt] is column, values[key_cnt][i] is column and row)
696
- values = []
697
- tmpArray = []
698
- # since schedule value will have a bunch of key_values, we need to keep track of these as additional timeseries
699
- key_cnt = 0
700
- # this is recording the name of these final timeseries to write in the header of the CSV
701
- final_timeseries_names = []
702
-
703
- # loop over requested timeseries
704
- requested_timeseries_names.each_index do |i|
705
- timeseries_name = requested_timeseries_names[i]
706
- puts " *********timeseries_name = #{timeseries_name}******************"
707
- runner.registerInfo("TIMESERIES: #{timeseries_name}")
708
-
709
- # get all the key values that this timeseries can be reported for (e.g. if PMV is requested for each zone)
710
- key_values = sql_file.availableKeyValues('RUN PERIOD 1', 'Zone Timestep', timeseries_name)
711
- runner.registerInfo("KEY VALUES: #{key_values}")
712
- if key_values.empty?
713
- key_values = ['']
714
- end
715
-
716
- # sort keys
717
- sorted_keys = key_values.sort
718
- requested_keys = requested_timeseries_names
719
- final_keys = []
720
- # make sure aggregated timeseries are listed in sorted order before all individual feature timeseries
721
- sorted_keys.each do |k|
722
- if requested_keys.include? k
723
- final_keys << k
724
- end
725
- end
726
- sorted_keys.each do |k|
727
- if !requested_keys.include? k
728
- final_keys << k
729
- end
730
- end
731
-
732
- # loop over final keys
733
- final_keys.each_with_index do |key_value, key_i|
734
- new_timeseries_name = ''
735
-
736
- runner.registerInfo("!! TIMESERIES NAME: #{timeseries_name} AND key_value: #{key_value}")
737
-
738
- # check if we have to come up with a new name for the timeseries in our CSV header
739
- if key_values.size == 1
740
- # use timeseries name when only 1 keyvalue
741
- new_timeseries_name = timeseries_name
742
- else
743
- # use key_value name
744
- # special case for Zone Thermal Comfort: use both timeseries_name and key_value
745
- if timeseries_name.include? 'Zone Thermal Comfort'
746
- new_timeseries_name = timeseries_name + ' ' + key_value
747
- else
748
- new_timeseries_name = key_value
749
- end
750
- end
751
- # final_timeseries_names << new_timeseries_name
752
-
753
- # get the actual timeseries
754
- ts = sql_file.timeSeries(ann_env_pd.to_s, reporting_frequency.to_s, timeseries_name, key_value)
755
-
756
- if n.nil?
757
- # first timeseries should always be set
758
- runner.registerInfo('First timeseries')
759
- values[key_cnt] = ts.get.values
760
- n = values[key_cnt].size
761
- elsif ts.is_initialized
762
- runner.registerInfo('Is Initialized')
763
- values[key_cnt] = ts.get.values
764
- else
765
- runner.registerInfo('Is NOT Initialized')
766
- values[key_cnt] = Array.new(n, 0)
767
- end
768
-
769
- # unit conversion
770
- old_unit = ts.get.units if ts.is_initialized
771
-
772
- if timeseries_name.include? 'Gas'
773
- new_unit = 'kBtu'
774
- else
775
- new_unit = case old_unit.to_s
776
- when 'J'
777
- 'kWh'
778
- when 'kBtu'
779
- 'kWh'
780
- when 'gal'
781
- 'm3'
782
- when 'W'
783
- 'W'
784
- end
785
- end
786
-
787
- # loop through each value and apply unit conversion
788
- os_vec = values[key_cnt]
789
- if !timeseries_name.include? 'Zone Thermal Comfort'
790
- for i in 0..os_vec.length - 1
791
-
792
- unless new_unit == old_unit || old_unit.nil? || new_unit.nil? || !ts.is_initialized
793
- os_vec[i] = OpenStudio.convert(os_vec[i], old_unit, new_unit).get
794
- end
795
-
796
- end
797
- end
798
-
799
- # keep certain timeseries to calculate power
800
- if tsToKeep.include? timeseries_name
801
- tsToKeepIndexes[timeseries_name] = key_cnt
802
- end
803
-
804
- # special processing: power
805
- if powerTimeseries.include? timeseries_name
806
- # special case: net series (subtract generation from load)
807
- if timeseries_name.include? 'Net'
808
-
809
- newVals = Array.new(n, 0)
810
- # Apparent power calculation
811
-
812
- if timeseries_name.include?('Apparent')
813
- (0..n - 1).each do |j|
814
- newVals[j] = (values[tsToKeepIndexes['Electricity:Facility']][j].to_f - values[tsToKeepIndexes['ElectricityProduced:Facility']][j].to_f) / power_conversion / powerFactor
815
- j += 1
816
- end
817
- new_unit = 'kVA'
818
- elsif timeseries_name.include? 'Net Electric Energy'
819
- (0..n - 1).each do |j|
820
- newVals[j] = (values[tsToKeepIndexes['Electricity:Facility']][j].to_f - values[tsToKeepIndexes['ElectricityProduced:Facility']][j].to_f)
821
- j += 1
822
- end
823
- new_unit = 'kWh'
824
- else
825
- runner.registerInfo('Power calc')
826
- # Power calculation
827
- (0..n - 1).each do |j|
828
- newVals[j] = (values[tsToKeepIndexes['Electricity:Facility']][j].to_f - values[tsToKeepIndexes['ElectricityProduced:Facility']][j].to_f) / power_conversion
829
- j += 1
830
- end
831
- new_unit = 'kW'
832
- end
833
-
834
- values[key_cnt] = newVals
835
- else
836
- tsToKeepIndexes.each do |key, indexValue|
837
- if timeseries_name.include? key
838
- runner.registerInfo("timeseries_name: #{timeseries_name}, key: #{key}")
839
- # use this timeseries
840
- newVals = Array.new(n, 0)
841
- # Apparent power calculation
842
- if timeseries_name.include?('Apparent')
843
- (0..n - 1).each do |j|
844
- newVals[j] = values[indexValue][j].to_f / power_conversion / powerFactor
845
- j += 1
846
- end
847
- new_unit = 'kVA'
848
- else
849
- # Power calculation
850
- (0..n - 1).each do |j|
851
- newVals[j] = values[indexValue][j].to_f / power_conversion
852
- j += 1
853
- end
854
- new_unit = 'kW'
855
- end
856
- values[key_cnt] = newVals
857
- end
858
- end
859
- end
860
- end
861
-
862
- # append units to headers
863
- new_timeseries_name += "(#{new_unit})"
864
- final_timeseries_names << new_timeseries_name
865
-
866
- # TODO: DELETE PUTS
867
- # puts " *********timeseries_name = #{timeseries_name}******************"
868
- # if timeseries_name.include? 'Power'
869
- # puts "values = #{values[key_cnt]}"
870
- # puts "units = #{new_unit}"
871
- # end
872
-
873
- # comfort results usually have multiple timeseries (per zone), aggregate into a single series with consistent name and use worst value at each timestep
874
- if comfortTimeseries.include? timeseries_name
875
-
876
- # set up array if 1st key_value
877
- if key_i == 0
878
- runner.registerInfo("SETTING UP NEW ARRAY FOR: #{timeseries_name}")
879
- tmpArray = Array.new(n, 0)
880
- end
881
-
882
- # add to array (keep max value at each timestep)
883
- (0..(n - 1)).each do |ind|
884
- # process negative and positive values differently
885
- tVal = values[key_cnt][ind].to_f
886
- if tVal < 0
887
- tmpArray[ind] = [tVal, tmpArray[ind]].min
888
- else
889
- tmpArray[ind] = [tVal, tmpArray[ind]].max
890
- end
891
- end
892
-
893
- # aggregate and save when all keyvalues have been processed
894
- if key_i == final_keys.size - 1
895
-
896
- hrsOutOfBounds = 0
897
- if timeseries_name === 'Zone Thermal Comfort Fanger Model PMV'
898
- (0..(n - 1)).each do |ind|
899
- # -0.5 < x < 0.5 is within bounds
900
- if values[key_cnt][ind].to_f > 0.5 || values[key_cnt][ind].to_f < -0.5
901
- hrsOutOfBounds += 1
902
- end
903
- end
904
- hrsOutOfBounds = hrsOutOfBounds.to_f / timesteps_per_hour
905
- elsif timeseries_name === 'Zone Thermal Comfort Fanger Model PPD'
906
- (0..(n - 1)).each do |ind|
907
- # > 20 is outside bounds
908
- if values[key_cnt][ind].to_f > 20
909
- hrsOutOfBounds += 1
910
- end
911
- end
912
- hrsOutOfBounds = hrsOutOfBounds.to_f / timesteps_per_hour
913
- else
914
- # this one is already scaled by timestep, no need to divide total
915
- (0..(n - 1)).each do |ind|
916
- hrsOutOfBounds += values[key_cnt][ind].to_f if values[key_cnt][ind].to_f > 0
917
- end
918
- end
919
-
920
- # save variable to feature_reports hash
921
- runner.registerInfo("timeseries #{timeseries_name}: hours out of bounds: #{hrsOutOfBounds}")
922
- if timeseries_name === 'Zone Thermal Comfort Fanger Model PMV'
923
- feature_report.reporting_periods[0].comfort_result[:hours_out_of_comfort_bounds_PMV] = hrsOutOfBounds
924
- elsif timeseries_name == 'Zone Thermal Comfort Fanger Model PPD'
925
- feature_report.reporting_periods[0].comfort_result[:hours_out_of_comfort_bounds_PPD] = hrsOutOfBounds
926
- end
927
-
928
- end
929
-
930
- end
931
-
932
- # increment key_cnt in new_keys loop
933
- key_cnt += 1
934
- end
935
- end
936
-
937
- # Add datime column
938
- datetimes = []
939
- # check what timeseries is available
940
- available_ts = sql_file.availableTimeSeries
941
- puts "####### available_ts = #{available_ts}"
942
- # get the timeseries for any of available timeseries
943
- # RK: code enhancement needed
944
- ts_d_e = sql_file.timeSeries(ann_env_pd.to_s, reporting_frequency.to_s, 'Electricity:Facility', '')
945
- ts_d_g = sql_file.timeSeries(ann_env_pd.to_s, reporting_frequency.to_s, 'Gas:Facility', '')
946
-
947
- if ts_d_e.is_initialized
948
- timeseries_d = ts_d_e.get
949
- elsif ts_d_g.is_initialized
950
- timeseries_d = ts_d_g.get
951
- else
952
- raise 'ELECTRICITY and GAS results are not initiaized'
953
- end
954
- # get formated datetimes
955
- timeseries_d.dateTimes.each do |datetime|
956
- datetimes << format_datetime(datetime.to_s)
957
- end
958
- # insert datetimes to values
959
- values.insert(0, datetimes)
960
- # insert datetime header to names
961
- final_timeseries_names.insert(0, 'Datetime')
962
-
963
- # rubocop: enable Metrics/BlockLength
964
- runner.registerInfo("new final_timeseries_names size: #{final_timeseries_names.size}")
965
-
966
- # Save the 'default_feature_reports.csv' file
967
- File.open('default_feature_reports.csv', 'w') do |file|
968
- file.puts(final_timeseries_names.join(','))
969
- (0...n).each do |l|
970
- line = []
971
- values.each_index do |j|
972
- line << values[j][l]
973
- end
974
- file.puts(line.join(','))
975
- end
976
- end
977
-
978
- # closing the sql file
979
- sql_file.close
980
-
981
- ############################# Adding timeseries_csv info to json report and saving CSV ################################
982
- # add csv info to feature_report
983
- feature_report.timeseries_csv.path = File.join(Dir.pwd, 'default_feature_reports.csv')
984
- feature_report.timeseries_csv.first_report_datetime = '0'
985
- feature_report.timeseries_csv.column_names = final_timeseries_names
986
-
987
- ##### Save the 'default_feature_reports.json' file
988
-
989
- feature_report_hash = feature_report.to_hash
990
-
991
- File.open('default_feature_reports.json', 'w') do |f|
992
- f.puts JSON.pretty_generate(feature_report_hash)
993
- # make sure data is written to the disk one way or the other
994
- begin
995
- f.fsync
996
- rescue StandardError
997
- f.flush
998
- end
999
- end
1000
-
1001
- # reporting final condition
1002
- runner.registerFinalCondition('Default Feature Reports generated successfully.')
1003
-
1004
- true
1005
- # end the run method
1006
- end
1007
- # end the measure
1008
- end
1009
- # rubocop:enable Metrics/AbcSize
1010
- # rubocop:enable Naming/MethodName
1011
-
1012
- # register the measure to be used by the application
1013
- DefaultFeatureReports.new.registerWithApplication