urbanopt-reporting 0.3.2 → 0.3.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/lib/measures/default_feature_reports/measure.rb +41 -4
- data/lib/measures/export_modelica_loads/LICENSE.md +27 -0
- data/lib/measures/export_modelica_loads/README.md +26 -0
- data/lib/measures/export_modelica_loads/README.md.erb +42 -0
- data/lib/measures/export_modelica_loads/docs/.gitkeep +0 -0
- data/lib/measures/export_modelica_loads/measure.rb +367 -0
- data/lib/measures/export_modelica_loads/measure.xml +92 -0
- data/lib/measures/export_modelica_loads/resources/report.html.in +13 -0
- data/lib/measures/export_time_series_modelica/LICENSE.md +1 -0
- data/lib/measures/export_time_series_modelica/README.md +59 -0
- data/lib/measures/export_time_series_modelica/README.md.erb +42 -0
- data/lib/measures/export_time_series_modelica/docs/.gitkeep +0 -0
- data/lib/measures/export_time_series_modelica/measure.rb +433 -0
- data/lib/measures/export_time_series_modelica/measure.xml +147 -0
- data/lib/measures/export_time_series_modelica/resources/os_lib_helper_methods.rb +399 -0
- data/lib/measures/export_time_series_modelica/resources/report.html.in +13 -0
- data/lib/urbanopt/reporting/default_reports/feature_report.rb +48 -0
- data/lib/urbanopt/reporting/default_reports/reporting_period.rb +14 -2
- data/lib/urbanopt/reporting/default_reports/scenario_report.rb +7 -4
- data/lib/urbanopt/reporting/default_reports/schema/scenario_schema.json +25 -13
- data/lib/urbanopt/reporting/version.rb +1 -1
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e65d30b14cf3916be1251dd1092dd74f222e737a0c9d6e6278123f4c6101b503
|
4
|
+
data.tar.gz: 4fa5c62490c7dcc883c5308f8f31547090f1eef01914b0e3e54093e218f09bf9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0e0fdd09a89d58be74b9506fc9904253b0121f0d117041799baa500f3a0b1ab07f907609e0495486943751203c1f8683cc564ffcb038cb944721e1751e79c6f3
|
7
|
+
data.tar.gz: 067ab1a1d6506c22933a1e4e9e09dac96ecc925e4d3d931bfc51bd6853faf99e658c4b578f6689d9e34cc5ff5533c77dab1d8533ccef1384963ad7a85ac627e1
|
data/CHANGELOG.md
CHANGED
@@ -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(',')[
|
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
|
-
|
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 %>
|
File without changes
|
@@ -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
|