urbanopt-reporting 0.3.2 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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