urbanopt-reporting 0.3.1 → 0.3.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +42 -2
  3. data/docs/package-lock.json +3 -3
  4. data/docs/package.json +1 -1
  5. data/lib/measures/default_feature_reports/measure.rb +78 -32
  6. data/lib/measures/default_feature_reports/measure.xml +3 -3
  7. data/lib/measures/export_modelica_loads/LICENSE.md +27 -0
  8. data/lib/measures/export_modelica_loads/README.md +26 -0
  9. data/lib/measures/export_modelica_loads/README.md.erb +42 -0
  10. data/lib/measures/export_modelica_loads/docs/.gitkeep +0 -0
  11. data/lib/measures/export_modelica_loads/measure.rb +374 -0
  12. data/lib/measures/export_modelica_loads/measure.xml +92 -0
  13. data/lib/measures/export_modelica_loads/resources/report.html.in +13 -0
  14. data/lib/measures/export_time_series_modelica/LICENSE.md +1 -0
  15. data/lib/measures/export_time_series_modelica/README.md +59 -0
  16. data/lib/measures/export_time_series_modelica/README.md.erb +42 -0
  17. data/lib/measures/export_time_series_modelica/docs/.gitkeep +0 -0
  18. data/lib/measures/export_time_series_modelica/measure.rb +430 -0
  19. data/lib/measures/export_time_series_modelica/measure.xml +147 -0
  20. data/lib/measures/export_time_series_modelica/resources/os_lib_helper_methods.rb +399 -0
  21. data/lib/measures/export_time_series_modelica/resources/report.html.in +13 -0
  22. data/lib/urbanopt/reporting/default_reports/end_use.rb +38 -17
  23. data/lib/urbanopt/reporting/default_reports/feature_report.rb +52 -4
  24. data/lib/urbanopt/reporting/default_reports/reporting_period.rb +14 -2
  25. data/lib/urbanopt/reporting/default_reports/scenario_report.rb +10 -5
  26. data/lib/urbanopt/reporting/default_reports/schema/scenario_csv_columns.txt +11 -10
  27. data/lib/urbanopt/reporting/default_reports/schema/scenario_schema.json +27 -12
  28. data/lib/urbanopt/reporting/version.rb +1 -1
  29. data/urbanopt-reporting-gem.gemspec +1 -1
  30. metadata +19 -4
@@ -0,0 +1,13 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>ReportingMeasure</title>
6
+ <link href="http://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.3.2/css/bootstrap.min.css" rel="stylesheet">
7
+ <script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
8
+ <script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/d3/3.3.9/d3.min.js"></script>
9
+ </head>
10
+ <body>
11
+ <%= output %>
12
+ </body>
13
+ </html>
@@ -0,0 +1 @@
1
+ Insert your license here
@@ -0,0 +1,59 @@
1
+
2
+
3
+ ###### (Automatically generated documentation)
4
+
5
+ # ExportTimeSeriesLoadsCSV
6
+
7
+ ## Description
8
+ This measure will add the required output variables and create a CSV file with plant loop level mass flow rates and temperatures for use in a Modelica simulation. Note that this measure has certain
9
+ requirements for naming of hydronic loops (discussed in the modeler description section).
10
+
11
+ ## Modeler Description
12
+ This measure is currently configured to report the temperatures and mass flow rates at the demand outlet and inlet nodes of hot water and chilled water loops, after adding the required output variables to the model. These values can be used to calculate the sum of the demand-side loads, and could thus represent the load on a connection to a district thermal energy system, or on
13
+ building-level primary equipment. This measure assumes that the model includes hydronic HVAC loops, and that the hot water and chilled water loop names can each be uniquely identified by a user-provided string. This measure also assumes that there is a single heating hot water loop
14
+ and a single chilled-water loop per building.
15
+
16
+ ## Measure Type
17
+ ReportingMeasure
18
+
19
+ ## Taxonomy
20
+
21
+
22
+ ## Arguments
23
+
24
+
25
+ ### Name or Partial Name of Heating Hot Water Loop, non-case-sensitive
26
+
27
+ **Name:** hhw_loop_name,
28
+ **Type:** String,
29
+ **Units:** ,
30
+ **Required:** true,
31
+ **Model Dependent:** false
32
+
33
+ ### Name or Partial Name of Chilled Water Loop, non-case-sensitive
34
+
35
+ **Name:** chw_loop_name,
36
+ **Type:** String,
37
+ **Units:** ,
38
+ **Required:** true,
39
+ **Model Dependent:** false
40
+
41
+ ### Number of Decimal Places to Round Mass Flow Rate
42
+ Number of decimal places to which mass flow rate will be rounded
43
+ **Name:** dec_places_mass_flow,
44
+ **Type:** Integer,
45
+ **Units:** ,
46
+ **Required:** true,
47
+ **Model Dependent:** false
48
+
49
+ ### Number of Decimal Places to Round Temperature
50
+ Number of decimal places to which temperature will be rounded
51
+ **Name:** dec_places_temp,
52
+ **Type:** Integer,
53
+ **Units:** ,
54
+ **Required:** true,
55
+ **Model Dependent:** false
56
+
57
+
58
+
59
+
@@ -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,430 @@
1
+ # *******************************************************************************
2
+ # OpenStudio(R), Copyright (c) 2008-2020, Alliance for Sustainable Energy, LLC.
3
+ # All rights reserved.
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are met:
6
+ #
7
+ # (1) Redistributions of source code must retain the above copyright notice,
8
+ # this list of conditions and the following disclaimer.
9
+ #
10
+ # (2) Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ #
14
+ # (3) Neither the name of the copyright holder nor the names of any contributors
15
+ # may be used to endorse or promote products derived from this software without
16
+ # specific prior written permission from the respective party.
17
+ #
18
+ # (4) Other than as required in clauses (1) and (2), distributions in any form
19
+ # of modifications or other derivative works may not use the "OpenStudio"
20
+ # trademark, "OS", "os", or any other confusingly similar designation without
21
+ # specific prior written permission from Alliance for Sustainable Energy, LLC.
22
+ #
23
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS
24
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
25
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE
27
+ # UNITED STATES GOVERNMENT, OR THE UNITED STATES DEPARTMENT OF ENERGY, NOR ANY OF
28
+ # THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
30
+ # OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32
+ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33
+ # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
+ # *******************************************************************************
35
+
36
+ require 'erb'
37
+
38
+
39
+ # This measure is originally from https://github.com/urbanopt/DES_HVAC
40
+ # start the measure
41
+ class ExportTimeSeriesLoadsCSV < OpenStudio::Measure::ReportingMeasure
42
+ Dir[File.dirname(__FILE__) + '/resources/*.rb'].each { |file| require file }
43
+ include OsLib_HelperMethods
44
+ # human readable name
45
+ def name
46
+ # Measure name should be the title case of the class name.
47
+ 'ExportTimeSeriesLoadsCSV'
48
+ end
49
+
50
+ def description
51
+ 'This measure will add the required output variables and create a CSV file with plant loop level mass flow rates and temperatures for use in a Modelica simulation. Note that this measure has certain
52
+ requirements for naming of hydronic loops (discussed in the modeler description section).'
53
+ end
54
+
55
+ def modeler_description
56
+ 'This measure is currently configured to report the temperatures and mass flow rates at the demand outlet and inlet nodes of hot water and chilled water loops, after adding the required output variables to the model. These values can be used to calculate the sum of the demand-side loads, and could thus represent the load on a connection to a district thermal energy system, or on
57
+ building-level primary equipment. This measure assumes that the model includes hydronic HVAC loops, and that the hot water and chilled water loop names can each be uniquely identified by a user-provided string. This measure also assumes that there is a single heating hot water loop
58
+ and a single chilled-water loop per building.'
59
+ end
60
+
61
+ def log(str)
62
+ puts "#{Time.now}: #{str}"
63
+ end
64
+
65
+ def arguments(_model)
66
+ args = OpenStudio::Measure::OSArgumentVector.new
67
+
68
+ hhw_loop_name = OpenStudio::Measure::OSArgument.makeStringArgument('hhw_loop_name', true)
69
+ hhw_loop_name.setDisplayName('Name or Partial Name of Heating Hot Water Loop, non-case-sensitive')
70
+ hhw_loop_name.setDefaultValue('hot')
71
+ args << hhw_loop_name
72
+
73
+ chw_loop_name = OpenStudio::Measure::OSArgument.makeStringArgument('chw_loop_name', true)
74
+ chw_loop_name.setDisplayName('Name or Partial Name of Chilled Water Loop, non-case-sensitive')
75
+ chw_loop_name.setDefaultValue('chilled')
76
+ args << chw_loop_name
77
+
78
+ dec_places_mass_flow = OpenStudio::Measure::OSArgument.makeIntegerArgument('dec_places_mass_flow', true)
79
+ dec_places_mass_flow.setDisplayName('Number of Decimal Places to Round Mass Flow Rate')
80
+ dec_places_mass_flow.setDescription('Number of decimal places to which mass flow rate will be rounded')
81
+ dec_places_mass_flow.setDefaultValue(3)
82
+ args << dec_places_mass_flow
83
+
84
+ dec_places_temp = OpenStudio::Measure::OSArgument.makeIntegerArgument('dec_places_temp', true)
85
+ dec_places_temp.setDisplayName('Number of Decimal Places to Round Temperature')
86
+ dec_places_temp.setDescription('Number of decimal places to which temperature will be rounded')
87
+ dec_places_temp.setDefaultValue(1)
88
+ args << dec_places_temp
89
+
90
+ return args
91
+ end
92
+
93
+ # return a vector of IdfObject's to request EnergyPlus objects needed by the run method
94
+ def energyPlusOutputRequests(runner, user_arguments)
95
+ super(runner, user_arguments)
96
+
97
+ result = OpenStudio::IdfObjectVector.new
98
+
99
+ # To use the built-in error checking we need the model...
100
+ # get the last model and sql file
101
+ model = runner.lastOpenStudioModel
102
+ if model.empty?
103
+ runner.registerError('Cannot find last model.')
104
+ return false
105
+ end
106
+ model = model.get
107
+
108
+ # use the built-in error checking
109
+ return false unless runner.validateUserArguments(arguments(model), user_arguments)
110
+
111
+ ##Read in argumetns related to variables for output requests
112
+ hhw_loop_name = runner.getStringArgumentValue('hhw_loop_name', user_arguments)
113
+ chw_loop_name = runner.getStringArgumentValue('chw_loop_name', user_arguments)
114
+
115
+ #Identify key names for output variables.
116
+ plantloops = model.getPlantLoops
117
+
118
+ selected_plant_loops = []
119
+ i = 0
120
+
121
+ variable_name1 = 'System Node Mass Flow Rate'
122
+ variable_name2 = 'System Node Temperature'
123
+ reporting_frequency = 'timestep'
124
+
125
+ plantloops.each do |plantLoop|
126
+ log "plant loop name #{plantLoop.name.get.to_s}"
127
+ if plantLoop.name.get.to_s.downcase.include? chw_loop_name.to_s
128
+ #Extract plant loop information
129
+ selected_plant_loops[0]=plantLoop
130
+ key_value_chw_outlet = selected_plant_loops[0].demandOutletNode.name.to_s
131
+ key_value_chw_inlet = selected_plant_loops[0].demandInletNode.name.to_s
132
+ result << OpenStudio::IdfObject.load("Output:Variable,#{key_value_chw_outlet},#{variable_name2},timestep;").get
133
+ result << OpenStudio::IdfObject.load("Output:Variable,#{key_value_chw_inlet},#{variable_name2},timestep;").get
134
+ result << OpenStudio::IdfObject.load("Output:Variable,#{key_value_chw_outlet},#{variable_name1},timestep;").get
135
+ end
136
+ if plantLoop.name.get.to_s.downcase.include? hhw_loop_name.to_s and !plantLoop.name.get.to_s.downcase.include? "service" and !plantLoop.name.get.to_s.downcase.include? "domestic"
137
+ #Extract plant loop information
138
+ selected_plant_loops[1]=plantLoop
139
+ key_value_hhw_outlet = selected_plant_loops[1].demandOutletNode.name.to_s
140
+ key_value_hhw_inlet = selected_plant_loops[1].demandInletNode.name.to_s
141
+ result << OpenStudio::IdfObject.load("Output:Variable,#{key_value_hhw_outlet},#{variable_name2},timestep;").get
142
+ result << OpenStudio::IdfObject.load("Output:Variable,#{key_value_hhw_inlet},#{variable_name2},timestep;").get
143
+ result << OpenStudio::IdfObject.load("Output:Variable,#{key_value_hhw_outlet},#{variable_name1},timestep;").get
144
+ end
145
+ end
146
+
147
+ result << OpenStudio::IdfObject.load('Output:Variable,,Site Mains Water Temperature,hourly;').get
148
+ result << OpenStudio::IdfObject.load('Output:Variable,,Site Outdoor Air Drybulb Temperature,hourly;').get
149
+ result << OpenStudio::IdfObject.load('Output:Variable,,Site Outdoor Air Relative Humidity,hourly;').get
150
+ result << OpenStudio::IdfObject.load('Output:Meter,Cooling:Electricity,hourly;').get
151
+ result << OpenStudio::IdfObject.load('Output:Meter,Electricity:Facility,timestep;').get ##Using this for data at timestep interval
152
+ result << OpenStudio::IdfObject.load('Output:Meter,Heating:Electricity,hourly;').get
153
+ result << OpenStudio::IdfObject.load('Output:Meter,Heating:NaturalGas,hourly;').get
154
+ result << OpenStudio::IdfObject.load('Output:Meter,InteriorLights:Electricity,hourly;').get
155
+ result << OpenStudio::IdfObject.load('Output:Meter,Fans:Electricity,hourly;').get
156
+ result << OpenStudio::IdfObject.load('Output:Meter,InteriorEquipment:Electricity,hourly;').get # Joules
157
+ result << OpenStudio::IdfObject.load('Output:Meter,ExteriorLighting:Electricity,hourly;').get # Joules
158
+ result << OpenStudio::IdfObject.load('Output:Meter,Electricity:Facility,hourly;').get # Joules
159
+ result << OpenStudio::IdfObject.load('Output:Meter,Gas:Facility,hourly;').get # Joules
160
+ result << OpenStudio::IdfObject.load('Output:Meter,Heating:EnergyTransfer,hourly;').get # Joules
161
+ result << OpenStudio::IdfObject.load('Output:Meter,WaterSystems:EnergyTransfer,hourly;').get # Joules
162
+ # these variables are used for the modelica export.
163
+ result << OpenStudio::IdfObject.load('Output:Variable,*,Zone Predicted Sensible Load to Setpoint Heat Transfer Rate,hourly;').get # watts according to e+
164
+ result << OpenStudio::IdfObject.load('Output:Variable,*,Water Heater Total Demand Heat Transfer Rate,hourly;').get # Watts
165
+
166
+ result
167
+ end
168
+
169
+ def extract_timeseries_into_matrix(sqlfile, data, variable_name, str, key_value = nil, default_if_empty = 0,dec_places, timestep)
170
+ log "Executing query for #{variable_name}"
171
+ #column_name = variable_name
172
+ if key_value
173
+ ts = sqlfile.timeSeries('RUN PERIOD 1', 'Zone Timestep', variable_name, key_value)
174
+ #column_name += "_#{key_value}"
175
+ column_name=str
176
+ else
177
+ #ts = sqlfile.timeSeries('RUN PERIOD 1', 'Hourly', variable_name)
178
+ ts = sqlfile.timeSeries('RUN PERIOD 1', 'Zone Timestep', variable_name)
179
+ end
180
+ log 'Iterating over timeseries'
181
+ column = [column_name.delete(':').delete(' ')] # Set the header of the data to the variable name, removing : and spaces
182
+
183
+ if ts.empty?
184
+ log "No time series for #{variable_name}:#{key_value}... defaulting to #{default_if_empty}"
185
+ # needs to be data.size-1 since the column name is already stored above (+=)
186
+ column += [default_if_empty] * (data.size - 1)
187
+ else
188
+ ts = ts.get if ts.respond_to?(:get)
189
+ ts = ts.first if ts.respond_to?(:first)
190
+
191
+ start = Time.now
192
+ # Iterating in OpenStudio can take up to 60 seconds with 10min data. The quick_proc takes 0.03 seconds.
193
+ # for i in 0..ts.values.size - 1
194
+ # log "... at #{i}" if i % 10000 == 0
195
+ # column << ts.values[i]
196
+ # end
197
+
198
+ quick_proc = ts.values.to_s.split(',')
199
+ quick_proc[0]=quick_proc[0].split('(', 2).last #cleanup necessary to remove opening paren
200
+ quick_proc=quick_proc.map(&:to_f)
201
+ x = 0
202
+ len = quick_proc.length
203
+ log "quick proc #{quick_proc}"
204
+ while(x < len) #Round to the # of decimal places specified
205
+ quick_proc[x]=(quick_proc[x]).round(dec_places)
206
+ x=x+1
207
+ end
208
+ quick_proc=quick_proc.map(&:to_s)
209
+
210
+ # the first and last have some cleanup items because of the Vector method
211
+ quick_proc[0] = quick_proc[0].gsub(/^.*\(/, '')
212
+ quick_proc[-1] = quick_proc[-1].delete(')')
213
+ column += quick_proc
214
+
215
+ log "Took #{Time.now - start} to iterate"
216
+ end
217
+
218
+ log 'Appending column to data'
219
+
220
+ # append the data to the end of the rows
221
+ if column.size == data.size
222
+ data.each_index do |index|
223
+ data[index] << column[index]
224
+ end
225
+ end
226
+
227
+ log "Finished extracting #{variable_name}"
228
+ end
229
+
230
+ def create_new_variable_sum(data, new_var_name, include_str, options=nil)
231
+ var_info = {
232
+ name: new_var_name,
233
+ var_indexes: []
234
+ }
235
+ data.each_with_index do |row, index|
236
+ if index.zero?
237
+ # Get the index of the columns to add
238
+ row.each do |c|
239
+ var_info[:var_indexes] << row.index(c) if c.include? include_str
240
+ end
241
+
242
+ # add the new var to the header row
243
+ data[0] << var_info[:name]
244
+ else
245
+ # sum the values
246
+ sum = 0
247
+ var_info[:var_indexes].each do |var|
248
+ temp_v = row[var].to_f
249
+ if options.nil?
250
+ sum += temp_v
251
+ elsif options[:positive_only] && temp_v > 0
252
+ sum += temp_v
253
+ elsif options[:negative_only] && temp_v < 0
254
+ sum += temp_v
255
+ end
256
+ end
257
+ data[index] << sum
258
+ end
259
+ end
260
+ end
261
+
262
+ def run(runner, user_arguments)
263
+ super(runner, user_arguments)
264
+
265
+ # get the last model and sql file
266
+ model = runner.lastOpenStudioModel
267
+ if model.empty?
268
+ runner.registerError('Cannot find last model.')
269
+ return false
270
+ end
271
+ model = model.get
272
+
273
+ # use the built-in error checking
274
+ return false unless runner.validateUserArguments(arguments(model), user_arguments)
275
+
276
+ args = OsLib_HelperMethods.createRunVariables(runner, model, user_arguments, arguments(model))
277
+ if !args
278
+ return false
279
+ end
280
+
281
+ # lookup and replace argument values from upstream measures
282
+ if args['use_upstream_args'] == true
283
+ args.each do |arg,value|
284
+ next if arg == 'use_upstream_args' # this argument should not be changed
285
+ value_from_osw = OsLib_HelperMethods.check_upstream_measure_for_arg(runner, arg)
286
+ if !value_from_osw.empty?
287
+ runner.registerInfo("Replacing argument named #{arg} from current measure with a value of #{value_from_osw[:value]} from #{value_from_osw[:measure_name]}.")
288
+ new_val = value_from_osw[:value]
289
+ # TODO: make code to handle non strings more robust. check_upstream_measure_for_arg could pass back the argument type
290
+ if arg == 'hhw_loop_name'
291
+ args[arg] = new_val.to_s
292
+ elsif arg == 'chw_loop_name'
293
+ args[arg] = new_val.to_s
294
+ else
295
+ args[arg] = new_val
296
+ end
297
+ end
298
+ end
299
+ end
300
+ hhw_loop_name = args['hhw_loop_name']
301
+ chw_loop_name = args['chw_loop_name']
302
+ dec_places_temp = args['dec_places_temp']
303
+ dec_places_mass_flow = args['dec_places_mass_flow']
304
+ # get the last model and sql file
305
+ model = runner.lastOpenStudioModel
306
+ if model.empty?
307
+ runner.registerError('Cannot find last model.')
308
+ return false
309
+ end
310
+ model = model.get
311
+
312
+ timesteps_per_hour=model.getTimestep.numberOfTimestepsPerHour.to_i
313
+ timestep=60/timesteps_per_hour #timestep in minutes
314
+
315
+ sqlFile = runner.lastEnergyPlusSqlFile
316
+ if sqlFile.empty?
317
+ runner.registerError('Cannot find last sql file.')
318
+ return false
319
+ end
320
+ sqlFile = sqlFile.get
321
+ model.setSqlFile(sqlFile)
322
+
323
+ # create a new csv with the values and save to the reports directory.
324
+ # assumptions:
325
+ # - all the variables exist
326
+ # - data are the same length
327
+
328
+ # initialize the rows with the header
329
+ log 'Starting to process Timeseries data'
330
+ # Initial header row
331
+ rows = [
332
+ ['Date Time', 'Month', 'Day', 'Day of Week', 'Hour', 'Minute', 'SecondsFromStart']
333
+ ]
334
+
335
+ # just grab one of the variables to get the date/time stamps
336
+ attribute_name = 'Electricity:Facility'
337
+ ts = sqlFile.timeSeries('RUN PERIOD 1', 'Zone Timestep', attribute_name)
338
+ if ts.empty?
339
+ runner.registerError("This feature does not have the attribute '#{attribute_name}' to enable this measure to work." \
340
+ "To resolve, simulate a building with electricity or remove this measure from your workflow.")
341
+ else
342
+ ts = ts.first
343
+ dt_base = nil
344
+ # Save off the date time values
345
+ ts.dateTimes.each_with_index do |dt, index|
346
+ dt_base = DateTime.parse(dt.to_s) if dt_base.nil?
347
+ dt_current = DateTime.parse(dt.to_s)
348
+ rows << [
349
+ DateTime.parse(dt.to_s).strftime('%m/%d/%Y %H:%M'),
350
+ dt.date.monthOfYear.value,
351
+ dt.date.dayOfMonth,
352
+ dt.date.dayOfWeek.value,
353
+ dt.time.hours,
354
+ dt.time.minutes,
355
+ dt_current.to_time.to_i - dt_base.to_time.to_i + timestep*60
356
+ ]
357
+ end
358
+ end
359
+
360
+ plantloops = model.getPlantLoops
361
+
362
+ selected_plant_loops = []
363
+ i = 0
364
+
365
+ key_var={}
366
+
367
+ plantloops.each do |plantLoop|
368
+ if plantLoop.name.get.to_s.downcase.include? chw_loop_name.to_str
369
+ #Extract plant loop information
370
+ selected_plant_loops[0]=plantLoop
371
+ end
372
+ if plantLoop.name.get.to_s.downcase.include? hhw_loop_name.to_str
373
+ #Get plant loop information
374
+ selected_plant_loops[1]=plantLoop
375
+ end
376
+ end
377
+
378
+ if !selected_plant_loops[1].nil?
379
+ #Set up variables for output
380
+ key_value_hhw_outlet = selected_plant_loops[1].demandOutletNode.name.to_s
381
+ key_value_hhw_inlet = selected_plant_loops[1].demandInletNode.name.to_s
382
+ key_var['hhw_outlet_massflow']='massFlowRateHeating'
383
+ key_var['hhw_outlet_temp']='heatingReturnTemperature[C]'
384
+ key_var['hhw_inlet_temp']='heatingSupplyTemperature[C]'
385
+ #Extract time series
386
+ extract_timeseries_into_matrix(sqlFile, rows, 'System Node Temperature', key_var['hhw_outlet_temp'], key_value_hhw_outlet, 0, dec_places_temp, timestep)
387
+ extract_timeseries_into_matrix(sqlFile, rows, 'System Node Temperature', key_var['hhw_inlet_temp'], key_value_hhw_inlet, 0, dec_places_temp, timestep)
388
+ extract_timeseries_into_matrix(sqlFile, rows, 'System Node Mass Flow Rate', key_var['hhw_outlet_massflow'], key_value_hhw_outlet, 0, dec_places_mass_flow, timestep)
389
+ else
390
+ runner.registerWarning("No hot water loop found. If one is expected, make sure the hot water loop name argument provides a string present in its name.")
391
+ end
392
+
393
+ if !selected_plant_loops[0].nil?
394
+ #Set up variables for outputs
395
+ key_value_chw_outlet = selected_plant_loops[0].demandOutletNode.name.to_s
396
+ key_value_chw_inlet = selected_plant_loops[0].demandInletNode.name.to_s
397
+ key_var['chw_outlet_massflow']='massFlowRateCooling'
398
+ key_var['chw_outlet_temp']='ChilledWaterReturnTemperature[C]'
399
+ key_var['chw_inlet_temp']='ChilledWaterSupplyTemperature[C]'
400
+ #Extract time series
401
+ extract_timeseries_into_matrix(sqlFile, rows, 'System Node Temperature', key_var['chw_outlet_temp'], key_value_chw_outlet, 0, dec_places_temp,timestep)
402
+ extract_timeseries_into_matrix(sqlFile, rows, 'System Node Temperature', key_var['chw_inlet_temp'], key_value_chw_inlet, 0, dec_places_temp,timestep)
403
+ extract_timeseries_into_matrix(sqlFile, rows, 'System Node Mass Flow Rate', key_var['chw_outlet_massflow'], key_value_chw_outlet, 0, dec_places_mass_flow,timestep)
404
+ else
405
+ runner.registerWarning("No chilled water loop found. If one is expected, make sure the chilled water loop name argument provides a string present in its name.")
406
+ end
407
+
408
+
409
+ if selected_plant_loops[0].nil? and selected_plant_loops[1].nil?
410
+ runner.registerWarning("No HVAC plant loops found. If one or more plant loops are expected, make sure they follow the naming conventions mentioned in the previous warnings.")
411
+ end
412
+
413
+ if !selected_plant_loops.nil?
414
+ # convert this to CSV object
415
+ File.open('./building_loads.csv', 'w') do |f|
416
+ rows.each do |row|
417
+ f << row.join(',') << "\n"
418
+ end
419
+ end
420
+ end
421
+
422
+ true
423
+ ensure
424
+ sqlFile&.close
425
+ end
426
+ end
427
+
428
+
429
+ # register the measure to be used by the application
430
+ ExportTimeSeriesLoadsCSV.new.registerWithApplication