urbanopt-reporting 0.3.2 → 0.3.3

This diff has not been reviewed by any users.
Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0adaa330d8958566edae263cedfd8a1bdee84769c881b98b86481ec4b0ee8bd5
4
- data.tar.gz: ceb2145a16413bd4885a1ef81fc9e534d8ed96e49ba19aaff47980dfeaa4838a
3
+ metadata.gz: e65d30b14cf3916be1251dd1092dd74f222e737a0c9d6e6278123f4c6101b503
4
+ data.tar.gz: 4fa5c62490c7dcc883c5308f8f31547090f1eef01914b0e3e54093e218f09bf9
5
5
  SHA512:
6
- metadata.gz: d8b2234582c11610f1ad932566bc60e8006930b6038dc619d307955739b21b63b3493488fea25cb2c66d3bb8c70433aebdacd66739920c944d08f53052ac105c
7
- data.tar.gz: 5be211005727f24581d0a2e2540db99b1aff5ac1003bdbaf61fafd0b6c4bbfbb2b293408afaac7cb45aa4f757cb153fb2c4aa420ca7bbcbc3e2e5ee40c1f5b3d
6
+ metadata.gz: 0e0fdd09a89d58be74b9506fc9904253b0121f0d117041799baa500f3a0b1ab07f907609e0495486943751203c1f8683cc564ffcb038cb944721e1751e79c6f3
7
+ data.tar.gz: 067ab1a1d6506c22933a1e4e9e09dac96ecc925e4d3d931bfc51bd6853faf99e658c4b578f6689d9e34cc5ff5533c77dab1d8533ccef1384963ad7a85ac627e1
@@ -1,5 +1,15 @@
1
1
  # URBANopt Reporting Gem
2
2
 
3
+ ## Version 0.3.3
4
+
5
+ Date Range: 12/09/20 - 01/13/21
6
+
7
+ - Fixed [#36]( https://github.com/urbanopt/urbanopt-reporting-gem/issues/36 ), Add reporting measure for district heating/cooling system mass flow rates
8
+ - Fixed [#37]( https://github.com/urbanopt/urbanopt-reporting-gem/issues/37 ), Add EUI to default report
9
+ - Fixed [#38]( https://github.com/urbanopt/urbanopt-reporting-gem/issues/38 ), Add better error handling around convert_units
10
+ - Fixed [#43]( https://github.com/urbanopt/urbanopt-reporting-gem/issues/43 ), Add available_roof_area calculation
11
+ - Fixed [#44]( https://github.com/urbanopt/urbanopt-reporting-gem/issues/44 ), Fix coordinates order
12
+
3
13
  ## Version 0.3.2
4
14
 
5
15
  Date Range: 12/07/20 - 12/08/20
@@ -258,6 +258,11 @@ class DefaultFeatureReports < OpenStudio::Measure::ReportingMeasure
258
258
  if value.nil?
259
259
  return nil
260
260
  end
261
+ if from_units.nil? || to_units.nil?
262
+ @runner.registerError("Cannot convert units...from_units: #{from_units} or to_units: #{to_units} left blank.")
263
+ return nil
264
+ end
265
+
261
266
  # apply unit conversion
262
267
  value_converted = OpenStudio.convert(value, from_units, to_units)
263
268
  if value_converted.is_initialized
@@ -353,10 +358,10 @@ class DefaultFeatureReports < OpenStudio::Measure::ReportingMeasure
353
358
  ##
354
359
 
355
360
  if feature_location.include? '['
356
- # get latitude from feature_location
357
- latitude = (feature_location.split(',')[0].delete! '[]').to_f
358
361
  # get longitude from feature_location
359
- longitude = (feature_location.split(',')[1].delete! '[]').to_f
362
+ longitude = (feature_location.split(',')[0].delete! '[]').to_f
363
+ # get latitude from feature_location
364
+ latitude = (feature_location.split(',')[1].delete! '[]').to_f
360
365
  # latitude
361
366
  feature_report.location.latitude_deg = latitude
362
367
  # longitude
@@ -495,7 +500,23 @@ class DefaultFeatureReports < OpenStudio::Measure::ReportingMeasure
495
500
  total_roof_area += surface.netArea
496
501
  end
497
502
  end
498
- feature_report.program.roof_area_sqft[:total_roof_area_sqft] = convert_units(total_roof_area, 'm^2', 'ft^2')
503
+
504
+ total_roof_area_sqft = convert_units(total_roof_area, 'm^2', 'ft^2')
505
+ feature_report.program.roof_area_sqft[:total_roof_area_sqft] = total_roof_area_sqft
506
+
507
+ # available_roof_area_sqft
508
+ # RK: a more robust method should be implemented to find the available_roof_area
509
+ # assign available roof area to be a percentage of the total roof area
510
+
511
+ if building_types[0][:building_type].include? 'Single-Family Detached'
512
+ feature_report.program.roof_area_sqft[:available_roof_area_sqft] = 0.45 * total_roof_area_sqft
513
+ else
514
+ feature_report.program.roof_area_sqft[:available_roof_area_sqft] = 0.75 * total_roof_area_sqft
515
+ end
516
+
517
+ # RK: Temporary solution: assign available roof area to be equal to total roof area
518
+ #feature_report.program.roof_area_sqft[:available_roof_area_sqft] = total_roof_area_sqft
519
+
499
520
 
500
521
  # orientation
501
522
  # RK: a more robust method should be implemented to find orientation(finding main axis of the building using aspect ratio)
@@ -581,6 +602,22 @@ class DefaultFeatureReports < OpenStudio::Measure::ReportingMeasure
581
602
  total_source_energy = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Site and Source Energy' AND RowName='Total Source Energy' AND ColumnName='Total Energy'")
582
603
  feature_report.reporting_periods[0].total_source_energy_kwh = convert_units(total_source_energy, 'GJ', 'kWh')
583
604
 
605
+ # EUI is only valid with a full year of energy data
606
+ if begin_month == 1 && begin_day_of_month == 1 && end_month == 12 && end_day_of_month == 31
607
+ # calculate site EUI
608
+ site_EUI_kwh_per_m2 = feature_report.reporting_periods[0].total_site_energy_kwh / floor_area
609
+ site_EUI_kbtu_per_ft2 = convert_units(total_site_energy, 'GJ', 'kBtu') / feature_report.program.floor_area_sqft
610
+ # add site EUI to feature report
611
+ feature_report.reporting_periods[0].site_EUI_kwh_per_m2 = site_EUI_kwh_per_m2
612
+ feature_report.reporting_periods[0].site_EUI_kbtu_per_ft2 = site_EUI_kbtu_per_ft2
613
+ # calculate source EUI
614
+ source_EUI_kwh_per_m2 = feature_report.reporting_periods[0].total_source_energy_kwh / floor_area
615
+ source_EUI_kbtu_per_ft2 = convert_units(total_source_energy, 'GJ', 'kBtu') / feature_report.program.floor_area_sqft
616
+ # add source EUI to feature report
617
+ feature_report.reporting_periods[0].source_EUI_kwh_per_m2 = source_EUI_kwh_per_m2
618
+ feature_report.reporting_periods[0].source_EUI_kbtu_per_ft2 = source_EUI_kbtu_per_ft2
619
+ end
620
+
584
621
  # net_site_energy
585
622
  net_site_energy = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Site and Source Energy' AND RowName='Net Site Energy' AND ColumnName='Total Energy'")
586
623
  feature_report.reporting_periods[0].net_site_energy_kwh = convert_units(net_site_energy, 'GJ', 'kWh')
@@ -0,0 +1,27 @@
1
+ OpenStudio(R), Copyright (c) 2008-2020, Alliance for Sustainable Energy, LLC. All rights reserved.
2
+
3
+ Redistribution and use in source and binary forms, with or without modification, are permitted
4
+ provided that the following conditions are met:
5
+
6
+ (1) Redistributions of source code must retain the above copyright notice, this list of conditions
7
+ and the following disclaimer.
8
+
9
+ (2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions
10
+ and the following disclaimer in the documentation and/or other materials provided with the distribution.
11
+
12
+ (3) Neither the name of the copyright holder nor the names of any contributors may be used to endorse
13
+ or promote products derived from this software without specific prior written permission from the
14
+ respective party.
15
+
16
+ (4) Other than as required in clauses (1) and (2), distributions in any form of modifications or other
17
+ derivative works may not use the "OpenStudio" trademark, "OS", "os", or any other confusingly similar
18
+ designation without specific prior written permission from Alliance for Sustainable Energy, LLC.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
21
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER, THE UNITED STATES GOVERNMENT,
23
+ OR ANY CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
25
+ OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,26 @@
1
+
2
+
3
+ ###### (Automatically generated documentation)
4
+
5
+ # Export Modelica Loads
6
+
7
+ ## Description
8
+ Use the results from the EnergyPlus simulation to generate a load file for use in Modelica. This will create a MOS and a CSV file of the heating, cooling, and hot water loads.
9
+
10
+ ## Modeler Description
11
+
12
+
13
+ ## Measure Type
14
+ ReportingMeasure
15
+
16
+ ## Taxonomy
17
+
18
+
19
+ ## Arguments
20
+
21
+
22
+
23
+
24
+ This measure does not have any user arguments
25
+
26
+
@@ -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,367 @@
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
+ # start the measure
39
+ class ExportModelicaLoads < OpenStudio::Measure::ReportingMeasure
40
+ # human readable name
41
+ def name
42
+ # Measure name should be the title case of the class name.
43
+ return 'Export Modelica Loads'
44
+ end
45
+
46
+ def description
47
+ return 'Use the results from the EnergyPlus simulation to generate a load file for use in Modelica. This will create a MOS and a CSV file of the heating, cooling, and hot water loads.'
48
+ end
49
+
50
+ def modeler_description
51
+ return ''
52
+ end
53
+
54
+ def log(str)
55
+ puts "#{Time.now}: #{str}"
56
+ end
57
+
58
+ def arguments(_model)
59
+ args = OpenStudio::Measure::OSArgumentVector.new
60
+
61
+ # this measure does not require any user arguments, return an empty list
62
+ return args
63
+ end
64
+
65
+ # return a vector of IdfObject's to request EnergyPlus objects needed by the run method
66
+ def energyPlusOutputRequests(runner, user_arguments)
67
+ super(runner, user_arguments)
68
+
69
+ result = OpenStudio::IdfObjectVector.new
70
+
71
+ # To use the built-in error checking we need the model...
72
+ # get the last model and sql file
73
+ model = runner.lastOpenStudioModel
74
+ if model.empty?
75
+ runner.registerError('Cannot find last model.')
76
+ return false
77
+ end
78
+ model = model.get
79
+
80
+ # use the built-in error checking
81
+ if !runner.validateUserArguments(arguments(model), user_arguments)
82
+ return false
83
+ end
84
+
85
+ result << OpenStudio::IdfObject.load('Output:Variable,,Site Mains Water Temperature,hourly;').get
86
+ result << OpenStudio::IdfObject.load('Output:Variable,,Site Outdoor Air Drybulb Temperature,hourly;').get
87
+ result << OpenStudio::IdfObject.load('Output:Variable,,Site Outdoor Air Relative Humidity,hourly;').get
88
+ result << OpenStudio::IdfObject.load('Output:Meter,Cooling:Electricity,hourly;').get
89
+ result << OpenStudio::IdfObject.load('Output:Meter,Heating:Electricity,hourly;').get
90
+ result << OpenStudio::IdfObject.load('Output:Meter,Heating:Gas,hourly;').get
91
+ result << OpenStudio::IdfObject.load('Output:Meter,InteriorLights:Electricity,hourly;').get
92
+ result << OpenStudio::IdfObject.load('Output:Meter,Fans:Electricity,hourly;').get
93
+ result << OpenStudio::IdfObject.load('Output:Meter,InteriorEquipment:Electricity,hourly;').get # Joules
94
+ result << OpenStudio::IdfObject.load('Output:Meter,ExteriorLighting:Electricity,hourly;').get # Joules
95
+ result << OpenStudio::IdfObject.load('Output:Meter,Electricity:Facility,hourly;').get # Joules
96
+ result << OpenStudio::IdfObject.load('Output:Meter,Gas:Facility,hourly;').get # Joules
97
+ result << OpenStudio::IdfObject.load('Output:Meter,Heating:EnergyTransfer,hourly;').get # Joules
98
+ result << OpenStudio::IdfObject.load('Output:Meter,WaterSystems:EnergyTransfer,hourly;').get # Joules
99
+ # these variables are used for the modelica export.
100
+ result << OpenStudio::IdfObject.load('Output:Variable,*,Zone Predicted Sensible Load to Setpoint Heat Transfer Rate,hourly;').get # watts according to e+
101
+ result << OpenStudio::IdfObject.load('Output:Variable,*,Water Heater Total Demand Heat Transfer Rate,hourly;').get # Watts
102
+
103
+ return result
104
+ end
105
+
106
+ def extract_timeseries_into_matrix(sqlfile, data, variable_name, key_value = nil, default_if_empty=0)
107
+ log "Executing query for #{variable_name}"
108
+ column_name = variable_name
109
+ if key_value
110
+ ts = sqlfile.timeSeries('RUN PERIOD 1', 'Hourly', variable_name, key_value)
111
+ # ts = sqlfile.timeSeries('RUN PERIOD 1', 'Zone Timestep', variable_name, key_value)
112
+ column_name += "_#{key_value}"
113
+ else
114
+ ts = sqlfile.timeSeries('RUN PERIOD 1', 'Hourly', variable_name)
115
+ # ts = sqlfile.timeSeries('RUN PERIOD 1', 'Zone Timestep', variable_name)
116
+ end
117
+ log 'Iterating over timeseries'
118
+ column = [column_name.delete(':').delete(' ')] # Set the header of the data to the variable name, removing : and spaces
119
+
120
+ if ts.empty?
121
+ log "No time series for #{variable_name}:#{key_value}... defaulting to #{default_if_empty}"
122
+ # needs to be data.size-1 since the column name is already stored above (+=)
123
+ column += [default_if_empty] * (data.size-1)
124
+ else
125
+ ts = ts.get if ts.respond_to?(:get)
126
+ ts = ts.first if ts.respond_to?(:first)
127
+
128
+ start = Time.now
129
+ # Iterating in OpenStudio can take up to 60 seconds with 10min data. The quick_proc takes 0.03 seconds.
130
+ # for i in 0..ts.values.size - 1
131
+ # log "... at #{i}" if i % 10000 == 0
132
+ # column << ts.values[i]
133
+ # end
134
+
135
+ quick_proc = ts.values.to_s.split(',')
136
+
137
+ # the first and last have some cleanup items because of the Vector method
138
+ quick_proc[0] = quick_proc[0].gsub(/^.*\(/, '')
139
+ quick_proc[-1] = quick_proc[-1].delete(')')
140
+ column += quick_proc
141
+
142
+ log "Took #{Time.now - start} to iterate"
143
+ end
144
+
145
+ log 'Appending column to data'
146
+
147
+ # append the data to the end of the rows
148
+ if column.size == data.size
149
+ data.each_index do |index|
150
+ data[index] << column[index]
151
+ end
152
+ end
153
+
154
+ log "Finished extracting #{variable_name}"
155
+ end
156
+
157
+ def create_new_variable_sum(data, new_var_name, include_str, options=nil)
158
+ var_info = {
159
+ name: new_var_name,
160
+ var_indexes: []
161
+ }
162
+ data.each_with_index do |row, index|
163
+ if index.zero?
164
+ # Get the index of the columns to add
165
+ row.each do |c|
166
+ if c.include? include_str
167
+ var_info[:var_indexes] << row.index(c)
168
+ end
169
+ end
170
+
171
+ # add the new var to the header row
172
+ data[0] << var_info[:name]
173
+ else
174
+ # sum the values
175
+ sum = 0
176
+ var_info[:var_indexes].each do |var|
177
+ temp_v = row[var].to_f
178
+ if options.nil?
179
+ sum += temp_v
180
+ elsif options[:positive_only] && temp_v > 0
181
+ sum += temp_v
182
+ elsif options[:negative_only] && temp_v < 0
183
+ sum += temp_v
184
+ end
185
+ end
186
+
187
+ # Also round the data here, because we don't need 10 decimals
188
+ data[index] << sum.round(1)
189
+ end
190
+ end
191
+ end
192
+
193
+ def run(runner, user_arguments)
194
+ super(runner, user_arguments)
195
+
196
+ # get the last model and sql file
197
+ model = runner.lastOpenStudioModel
198
+ if model.empty?
199
+ runner.registerError('Cannot find last model.')
200
+ return false
201
+ end
202
+ model = model.get
203
+
204
+ # use the built-in error checking
205
+ return false unless runner.validateUserArguments(arguments(model), user_arguments)
206
+
207
+ # get the last model and sql file
208
+ model = runner.lastOpenStudioModel
209
+ if model.empty?
210
+ runner.registerError('Cannot find last model.')
211
+ return false
212
+ end
213
+ model = model.get
214
+
215
+ sqlFile = runner.lastEnergyPlusSqlFile
216
+ if sqlFile.empty?
217
+ runner.registerError('Cannot find last sql file.')
218
+ return false
219
+ end
220
+ sqlFile = sqlFile.get
221
+ model.setSqlFile(sqlFile)
222
+
223
+ # create a new csv with the values and save to the reports directory.
224
+ # assumptions:
225
+ # - all the variables exist
226
+ # - data are the same length
227
+
228
+ # initialize the rows with the header
229
+ log 'Starting to process Timeseries data'
230
+ # Initial header row
231
+ rows = [
232
+ ['Date Time', 'Month', 'Day', 'Day of Week', 'Hour', 'Minute', 'SecondsFromStart']
233
+ ]
234
+
235
+ # just grab one of the variables to get the date/time stamps
236
+ # ts = sqlFile.timeSeries('RUN PERIOD 1', 'Zone Timestep', 'Cooling:Electricity')
237
+ ts = sqlFile.timeSeries('RUN PERIOD 1', 'Hourly', 'Cooling:Electricity')
238
+ unless ts.empty?
239
+ ts = ts.first
240
+ dt_base = nil
241
+ # Save off the date time values
242
+ ts.dateTimes.each_with_index do |dt, index|
243
+ runner.registerInfo("My index is #{index}")
244
+ dt_base = DateTime.parse(dt.to_s) if dt_base.nil?
245
+ dt_current = DateTime.parse(dt.to_s)
246
+ rows << [
247
+ DateTime.parse(dt.to_s).strftime('%m/%d/%Y %H:%M'),
248
+ dt.date.monthOfYear.value,
249
+ dt.date.dayOfMonth,
250
+ dt.date.dayOfWeek.value,
251
+ dt.time.hours,
252
+ dt.time.minutes,
253
+ dt_current.to_time.to_i - dt_base.to_time.to_i
254
+ ]
255
+ end
256
+ end
257
+
258
+ # add in the other variables by columns -- should really pull this from the report variables defined above
259
+ extract_timeseries_into_matrix(sqlFile, rows, 'Site Outdoor Air Drybulb Temperature', 'Environment', 0)
260
+ extract_timeseries_into_matrix(sqlFile, rows, 'Site Outdoor Air Relative Humidity', 'Environment', 0)
261
+ extract_timeseries_into_matrix(sqlFile, rows, 'Heating:Electricity', nil, 0)
262
+ extract_timeseries_into_matrix(sqlFile, rows, 'Heating:Gas', nil, 0)
263
+ extract_timeseries_into_matrix(sqlFile, rows, 'Cooling:Electricity', nil, 0)
264
+ extract_timeseries_into_matrix(sqlFile, rows, 'Electricity:Facility', nil, 0)
265
+ extract_timeseries_into_matrix(sqlFile, rows, 'Gas:Facility', nil, 0)
266
+ extract_timeseries_into_matrix(sqlFile, rows, 'Heating:EnergyTransfer', nil, 0)
267
+ extract_timeseries_into_matrix(sqlFile, rows, 'WaterSystems:EnergyTransfer', nil, 0)
268
+
269
+ # get all zones and save the names for later use in aggregation.
270
+ tz_names = []
271
+ model.getThermalZones.each do |tz|
272
+ tz_names << tz.name.get if tz.name.is_initialized
273
+ extract_timeseries_into_matrix(sqlFile, rows, 'Zone Predicted Sensible Load to Setpoint Heat Transfer Rate', tz_names.last, 0)
274
+ extract_timeseries_into_matrix(sqlFile, rows, 'Water Heater Heating Rate', tz_names.last, 0)
275
+ end
276
+
277
+ # sum up a couple of the columns and create a new columns
278
+ create_new_variable_sum(rows, 'TotalSensibleLoad', 'ZonePredictedSensibleLoadtoSetpointHeatTransferRate')
279
+ create_new_variable_sum(rows, 'TotalCoolingSensibleLoad', 'ZonePredictedSensibleLoadtoSetpointHeatTransferRate', negative_only: true)
280
+ create_new_variable_sum(rows, 'TotalHeatingSensibleLoad', 'ZonePredictedSensibleLoadtoSetpointHeatTransferRate', positive_only: true)
281
+ create_new_variable_sum(rows, 'TotalWaterHeating', 'WaterHeaterHeatingRate')
282
+
283
+ # convert this to CSV object
284
+ File.open('./building_loads.csv', 'w') do |f|
285
+ rows.each do |row|
286
+ f << row.join(',') << "\n"
287
+ end
288
+ end
289
+
290
+ # covert the row data into the format needed by modelica
291
+ modelica_data = [['seconds', 'cooling', 'heating', 'waterheating']]
292
+ seconds_index = nil
293
+ total_water_heating_index = nil
294
+ total_cooling_sensible_index = nil
295
+ total_heating_sensible_index = nil
296
+ peak_cooling = 0
297
+ peak_heating = 0
298
+ peak_water_heating = 0
299
+ rows.each_with_index do |row, index|
300
+ if index.zero?
301
+ seconds_index = row.index('SecondsFromStart')
302
+ total_cooling_sensible_index = row.index('TotalCoolingSensibleLoad')
303
+ total_heating_sensible_index = row.index('TotalHeatingSensibleLoad')
304
+ total_water_heating_index = row.index('TotalWaterHeating')
305
+ else
306
+ new_data = [
307
+ row[seconds_index],
308
+ row[total_cooling_sensible_index],
309
+ row[total_heating_sensible_index],
310
+ row[total_water_heating_index]
311
+ ]
312
+
313
+ modelica_data << new_data
314
+
315
+ # store the peaks
316
+ peak_cooling = row[total_cooling_sensible_index] if row[total_cooling_sensible_index] < peak_cooling
317
+ peak_heating = row[total_heating_sensible_index] if row[total_heating_sensible_index] > peak_heating
318
+ peak_water_heating = row[total_water_heating_index] if row[total_water_heating_index] > peak_water_heating
319
+ end
320
+ end
321
+
322
+ File.open('./modelica.mos', 'w') do |f|
323
+ f << "#1\n"
324
+ f << "#Heating and Cooling Model loads from OpenStudio Prototype Buildings\n"
325
+ f << "# Building Type: {{BUILDINGTYPE}}\n"
326
+ f << "# Climate Zone: {{CLIMATEZONE}}\n"
327
+ f << "# Vintage: {{VINTAGE}}\n"
328
+ f << "# Simulation ID (for debugging): {{SIMID}}\n"
329
+ f << "# URL: https://github.com/urbanopt/openstudio-prototype-loads\n"
330
+ f << "\n"
331
+ f << "#First column: Seconds in the year (loads are hourly)\n"
332
+ f << "#Second column: cooling loads in Watts (as negative numbers).\n"
333
+ f << "#Third column: space heating loads in Watts\n"
334
+ f << "#Fourth column: water heating in Watts\n"
335
+ f << "\n"
336
+ f << "#Peak space cooling load = #{peak_cooling} Watts\n"
337
+ f << "#Peak space heating load = #{peak_heating} Watts\n"
338
+ f << "#Peak water heating load = #{peak_water_heating} Watts\n"
339
+ f << "double tab1(8760,4)\n"
340
+ modelica_data.each_with_index do |row, index|
341
+ next if index.zero?
342
+ f << row.join(';') << "\n"
343
+ end
344
+ end
345
+
346
+ # Find the total runtime for energyplus and save it into a registerValue
347
+ total_time = -999
348
+ location_of_file = ['../eplusout.end', './run/eplusout.end']
349
+ first_index = location_of_file.map {|f| File.exist?(f)}.index(true)
350
+ if first_index
351
+ match = File.read(location_of_file[first_index]).to_s.match(/Elapsed.Time=(.*)hr(.*)min(.*)sec/)
352
+ total_time = match[1].to_i * 3600 + match[2].to_i * 60 + match[3].to_f
353
+ end
354
+
355
+ runner.registerValue('energyplus_runtime', total_time, 'sec')
356
+ runner.registerValue('peak_cooling_load', peak_cooling, 'W')
357
+ runner.registerValue('peak_heating_load', peak_heating, 'W')
358
+ runner.registerValue('peak_water_heating', peak_water_heating, 'W')
359
+
360
+ return true
361
+ ensure
362
+ sqlFile.close if sqlFile
363
+ end
364
+ end
365
+
366
+ # register the measure to be used by the application
367
+ ExportModelicaLoads.new.registerWithApplication