urbanopt-scenario 0.2.0.pre2 → 0.4.1
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 +5 -5
- data/.github/pull_request_template.md +2 -2
- data/.gitignore +2 -0
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +82 -1
- data/CONTRIBUTING.md +1 -1
- data/Gemfile +28 -18
- data/Jenkinsfile +1 -1
- data/LICENSE.md +1 -1
- data/RDOC_MAIN.md +1 -1
- data/README.md +1 -1
- data/Rakefile +2 -2
- data/docs/README.md +1 -1
- data/docs/package-lock.json +2499 -2322
- data/docs/package.json +13 -9
- data/lib/urbanopt-scenario.rb +1 -1
- data/lib/urbanopt/scenario.rb +3 -1
- data/lib/urbanopt/scenario/default_reports.rb +3 -8
- data/lib/urbanopt/scenario/extension.rb +1 -1
- data/lib/urbanopt/scenario/logger.rb +1 -1
- data/lib/urbanopt/scenario/scenario_base.rb +1 -1
- data/lib/urbanopt/scenario/scenario_csv.rb +22 -9
- data/lib/urbanopt/scenario/scenario_datapoint_base.rb +8 -1
- data/lib/urbanopt/scenario/scenario_post_processor_base.rb +1 -1
- data/lib/urbanopt/scenario/scenario_post_processor_default.rb +99 -8
- data/lib/urbanopt/scenario/scenario_post_processor_opendss.rb +275 -0
- data/lib/urbanopt/scenario/scenario_runner_base.rb +2 -2
- data/lib/urbanopt/scenario/scenario_runner_osw.rb +23 -9
- data/lib/urbanopt/scenario/scenario_visualization.rb +236 -0
- data/lib/urbanopt/scenario/simulation_dir_base.rb +1 -1
- data/lib/urbanopt/scenario/simulation_dir_osw.rb +2 -9
- data/lib/urbanopt/scenario/simulation_mapper_base.rb +1 -1
- data/lib/urbanopt/scenario/version.rb +2 -2
- data/package-lock.json +3 -0
- data/urbanopt-scenario-gem.gemspec +15 -15
- metadata +78 -82
- data/doc_templates/LICENSE.md +0 -27
- data/doc_templates/README.md.erb +0 -42
- data/doc_templates/copyright_erb.txt +0 -31
- data/doc_templates/copyright_js.txt +0 -4
- data/doc_templates/copyright_ruby.txt +0 -29
- data/lib/change_log.rb +0 -147
- data/lib/measures/.rubocop.yml +0 -5
- data/lib/measures/default_feature_reports/LICENSE.md +0 -27
- data/lib/measures/default_feature_reports/README.md +0 -56
- data/lib/measures/default_feature_reports/README.md.erb +0 -42
- data/lib/measures/default_feature_reports/measure.rb +0 -957
- data/lib/measures/default_feature_reports/measure.xml +0 -143
- data/lib/measures/default_feature_reports/tests/USA_CO_Golden-NREL.724666_TMY3.epw +0 -8768
- data/lib/measures/default_feature_reports/tests/default_feature_reports_test.rb +0 -238
- data/lib/measures/default_feature_reports/tests/example_model.osm +0 -4378
- data/lib/urbanopt/scenario/default_reports/construction_cost.rb +0 -169
- data/lib/urbanopt/scenario/default_reports/date.rb +0 -97
- data/lib/urbanopt/scenario/default_reports/distributed_generation.rb +0 -374
- data/lib/urbanopt/scenario/default_reports/end_use.rb +0 -159
- data/lib/urbanopt/scenario/default_reports/end_uses.rb +0 -140
- data/lib/urbanopt/scenario/default_reports/feature_report.rb +0 -260
- data/lib/urbanopt/scenario/default_reports/generator.rb +0 -92
- data/lib/urbanopt/scenario/default_reports/location.rb +0 -99
- data/lib/urbanopt/scenario/default_reports/logger.rb +0 -44
- data/lib/urbanopt/scenario/default_reports/program.rb +0 -266
- data/lib/urbanopt/scenario/default_reports/reporting_period.rb +0 -301
- data/lib/urbanopt/scenario/default_reports/scenario_report.rb +0 -311
- data/lib/urbanopt/scenario/default_reports/schema/README.md +0 -33
- data/lib/urbanopt/scenario/default_reports/schema/scenario_csv_columns.txt +0 -21
- data/lib/urbanopt/scenario/default_reports/schema/scenario_schema.json +0 -839
- data/lib/urbanopt/scenario/default_reports/solar_pv.rb +0 -93
- data/lib/urbanopt/scenario/default_reports/storage.rb +0 -105
- data/lib/urbanopt/scenario/default_reports/timeseries_csv.rb +0 -284
- data/lib/urbanopt/scenario/default_reports/validator.rb +0 -97
- data/lib/urbanopt/scenario/default_reports/wind.rb +0 -92
data/docs/package.json
CHANGED
@@ -10,17 +10,21 @@
|
|
10
10
|
},
|
11
11
|
"author": "NREL",
|
12
12
|
"dependencies": {
|
13
|
-
"highlight.js": "^
|
14
|
-
"json-schema-ref-parser": "^
|
13
|
+
"highlight.js": "^10.2.0",
|
14
|
+
"json-schema-ref-parser": "^9.0.6",
|
15
15
|
"json-schema-view-js": "git+https://git@github.com/bgschiller/json-schema-view-js.git",
|
16
|
-
"vuepress": "^1.
|
17
|
-
"webpack-dev-middleware": "^3.
|
16
|
+
"vuepress": "^1.5.4",
|
17
|
+
"webpack-dev-middleware": "^3.7.2"
|
18
18
|
},
|
19
19
|
"devDependencies": {
|
20
|
-
"braces": "
|
21
|
-
"
|
22
|
-
"
|
23
|
-
"
|
24
|
-
"
|
20
|
+
"braces": "^3.0.2",
|
21
|
+
"dot-prop": "^5.3.0",
|
22
|
+
"gh-pages": "^3.1.0",
|
23
|
+
"js-yaml": "^3.14.0",
|
24
|
+
"minimist": ">=1.2.3",
|
25
|
+
"node-forge": ">=0.10.0",
|
26
|
+
"serialize-javascript": "^5.0.1",
|
27
|
+
"set-value": "^3.0.2",
|
28
|
+
"yargs-parser": ">=18.1.1"
|
25
29
|
}
|
26
30
|
}
|
data/lib/urbanopt-scenario.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# *********************************************************************************
|
2
|
-
# URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
2
|
+
# URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
3
3
|
# contributors. All rights reserved.
|
4
4
|
#
|
5
5
|
# Redistribution and use in source and binary forms, with or without modification,
|
data/lib/urbanopt/scenario.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# *********************************************************************************
|
2
|
-
# URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
2
|
+
# URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
3
3
|
# contributors. All rights reserved.
|
4
4
|
#
|
5
5
|
# Redistribution and use in source and binary forms, with or without modification,
|
@@ -36,10 +36,12 @@ require 'urbanopt/scenario/scenario_base'
|
|
36
36
|
require 'urbanopt/scenario/scenario_csv'
|
37
37
|
require 'urbanopt/scenario/scenario_post_processor_base'
|
38
38
|
require 'urbanopt/scenario/scenario_post_processor_default'
|
39
|
+
require 'urbanopt/scenario/scenario_post_processor_opendss'
|
39
40
|
require 'urbanopt/scenario/scenario_runner_base'
|
40
41
|
require 'urbanopt/scenario/scenario_runner_osw'
|
41
42
|
require 'urbanopt/scenario/simulation_dir_base'
|
42
43
|
require 'urbanopt/scenario/simulation_dir_osw'
|
43
44
|
require 'urbanopt/scenario/simulation_mapper_base'
|
45
|
+
require 'urbanopt/scenario/scenario_visualization'
|
44
46
|
|
45
47
|
require 'urbanopt/scenario/default_reports'
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# *********************************************************************************
|
2
|
-
# URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
2
|
+
# URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
3
3
|
# contributors. All rights reserved.
|
4
4
|
#
|
5
5
|
# Redistribution and use in source and binary forms, with or without modification,
|
@@ -31,10 +31,5 @@
|
|
31
31
|
##
|
32
32
|
# Retrieve all default_reports classes.
|
33
33
|
##
|
34
|
-
|
35
|
-
require 'urbanopt/
|
36
|
-
require 'urbanopt/scenario/default_reports/logger'
|
37
|
-
require 'urbanopt/scenario/default_reports/program'
|
38
|
-
require 'urbanopt/scenario/default_reports/reporting_period'
|
39
|
-
require 'urbanopt/scenario/default_reports/scenario_report'
|
40
|
-
require 'urbanopt/scenario/default_reports/timeseries_csv'
|
34
|
+
|
35
|
+
require 'urbanopt/reporting/default_reports'
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# *********************************************************************************
|
2
|
-
# URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
2
|
+
# URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
3
3
|
# contributors. All rights reserved.
|
4
4
|
#
|
5
5
|
# Redistribution and use in source and binary forms, with or without modification,
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# *********************************************************************************
|
2
|
-
# URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
2
|
+
# URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
3
3
|
# contributors. All rights reserved.
|
4
4
|
#
|
5
5
|
# Redistribution and use in source and binary forms, with or without modification,
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# *********************************************************************************
|
2
|
-
# URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
2
|
+
# URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
3
3
|
# contributors. All rights reserved.
|
4
4
|
#
|
5
5
|
# Redistribution and use in source and binary forms, with or without modification,
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# *********************************************************************************
|
2
|
-
# URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
2
|
+
# URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
3
3
|
# contributors. All rights reserved.
|
4
4
|
#
|
5
5
|
# Redistribution and use in source and binary forms, with or without modification,
|
@@ -52,7 +52,6 @@ module URBANopt
|
|
52
52
|
|
53
53
|
def initialize(name, root_dir, run_dir, feature_file, mapper_files_dir, csv_file, num_header_rows)
|
54
54
|
super(name, root_dir, run_dir, feature_file)
|
55
|
-
|
56
55
|
@mapper_files_dir = mapper_files_dir
|
57
56
|
@csv_file = csv_file
|
58
57
|
@num_header_rows = num_header_rows
|
@@ -73,13 +72,27 @@ module URBANopt
|
|
73
72
|
|
74
73
|
# Require all simulation mappers in mapper_files_dir
|
75
74
|
def load_mapper_files
|
76
|
-
Dir.glob(File.join(@mapper_files_dir, '/*.rb'))
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
75
|
+
dirs = Dir.glob(File.join(@mapper_files_dir, '/*.rb'))
|
76
|
+
# order is not guaranteed...attempt to add Baseline first, then High Efficiency
|
77
|
+
ordered_dirs = []
|
78
|
+
bindex = dirs.find_index { |i| i.include? 'Baseline.rb' }
|
79
|
+
if bindex
|
80
|
+
ordered_dirs << dirs[bindex]
|
81
|
+
dirs.delete_at(bindex)
|
82
|
+
end
|
83
|
+
hindex = dirs.find_index { |i| i.include? 'HighEfficiency.rb' }
|
84
|
+
if hindex
|
85
|
+
ordered_dirs << dirs[hindex] if hindex
|
86
|
+
dirs.delete_at(hindex)
|
87
|
+
end
|
88
|
+
# then the rest
|
89
|
+
ordered_dirs += dirs
|
90
|
+
|
91
|
+
ordered_dirs.each do |f|
|
92
|
+
require(f)
|
93
|
+
rescue LoadError => e
|
94
|
+
@@logger.error(e.message)
|
95
|
+
raise
|
83
96
|
end
|
84
97
|
end
|
85
98
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# *********************************************************************************
|
2
|
-
# URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
2
|
+
# URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
3
3
|
# contributors. All rights reserved.
|
4
4
|
#
|
5
5
|
# Redistribution and use in source and binary forms, with or without modification,
|
@@ -59,6 +59,13 @@ module URBANopt
|
|
59
59
|
@feature.feature_type
|
60
60
|
end
|
61
61
|
|
62
|
+
##
|
63
|
+
# Gets the type of a feature
|
64
|
+
##
|
65
|
+
def feature_location
|
66
|
+
@feature.feature_location
|
67
|
+
end
|
68
|
+
|
62
69
|
##
|
63
70
|
# Return the directory that this datapoint will run in.
|
64
71
|
##
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# *********************************************************************************
|
2
|
-
# URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
2
|
+
# URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
3
3
|
# contributors. All rights reserved.
|
4
4
|
#
|
5
5
|
# Redistribution and use in source and binary forms, with or without modification,
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# *********************************************************************************
|
2
|
-
# URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
2
|
+
# URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
3
3
|
# contributors. All rights reserved.
|
4
4
|
#
|
5
5
|
# Redistribution and use in source and binary forms, with or without modification,
|
@@ -29,12 +29,12 @@
|
|
29
29
|
# *********************************************************************************
|
30
30
|
|
31
31
|
require 'urbanopt/scenario/scenario_post_processor_base'
|
32
|
-
require 'urbanopt/
|
33
|
-
require 'urbanopt/scenario/default_reports/logger'
|
32
|
+
require 'urbanopt/reporting/default_reports'
|
34
33
|
|
35
34
|
require 'csv'
|
36
35
|
require 'json'
|
37
36
|
require 'fileutils'
|
37
|
+
require 'sqlite3'
|
38
38
|
|
39
39
|
module URBANopt
|
40
40
|
module Scenario
|
@@ -47,10 +47,11 @@ module URBANopt
|
|
47
47
|
def initialize(scenario_base)
|
48
48
|
super(scenario_base)
|
49
49
|
|
50
|
-
initialization_hash = { directory_name: scenario_base.run_dir, name: scenario_base.name, id: scenario_base.name }
|
51
|
-
@scenario_result = URBANopt::
|
50
|
+
@initialization_hash = { directory_name: scenario_base.run_dir, name: scenario_base.name, id: scenario_base.name, root_dir: scenario_base.root_dir }
|
51
|
+
@scenario_result = URBANopt::Reporting::DefaultReports::ScenarioReport.new(@initialization_hash)
|
52
|
+
@default_save_name = 'default_scenario_report'
|
52
53
|
|
53
|
-
@@logger ||= URBANopt::
|
54
|
+
@@logger ||= URBANopt::Reporting::DefaultReports.logger
|
54
55
|
end
|
55
56
|
|
56
57
|
##
|
@@ -70,7 +71,7 @@ module URBANopt
|
|
70
71
|
# [parameters:]
|
71
72
|
# +simulation_dir+ - _SimulationDirOSW_ - An object on SimulationDirOSW class.
|
72
73
|
def add_simulation_dir(simulation_dir)
|
73
|
-
feature_reports = URBANopt::
|
74
|
+
feature_reports = URBANopt::Reporting::DefaultReports::FeatureReport.from_simulation_dir(simulation_dir)
|
74
75
|
|
75
76
|
feature_reports.each do |feature_report|
|
76
77
|
if feature_report.to_hash[:simulation_status] == 'Complete'
|
@@ -83,12 +84,102 @@ module URBANopt
|
|
83
84
|
return feature_reports
|
84
85
|
end
|
85
86
|
|
87
|
+
# Create database file with scenario-level results
|
88
|
+
# Sum values for each timestep across all features. Save to new table in a new database
|
89
|
+
def create_scenario_db_file(file_name = @default_save_name)
|
90
|
+
new_db_file = File.join(@initialization_hash[:directory_name], "#{file_name}.db")
|
91
|
+
scenario_db = SQLite3::Database.open new_db_file
|
92
|
+
scenario_db.execute "CREATE TABLE IF NOT EXISTS ReportData(
|
93
|
+
TimeIndex INTEGER,
|
94
|
+
ReportDataDictionaryIndex INTEGER,
|
95
|
+
Value INTEGER
|
96
|
+
)"
|
97
|
+
|
98
|
+
values_arr = []
|
99
|
+
feature_list = Pathname.new(@initialization_hash[:directory_name]).children.select(&:directory?) # Folders in the run/scenario directory
|
100
|
+
|
101
|
+
# get scenario CSV
|
102
|
+
scenario_csv = File.join(@initialization_hash[:root_dir], @initialization_hash[:name] + '.csv')
|
103
|
+
if File.exist?(scenario_csv)
|
104
|
+
# csv found
|
105
|
+
feature_ids = CSV.read(scenario_csv, :headers => true)
|
106
|
+
feature_list = []
|
107
|
+
# loop through building feature ids from scenario csv
|
108
|
+
feature_ids["Feature Id"].each do |feature|
|
109
|
+
if Dir.exist?(File.join(@initialization_hash[:directory_name], feature))
|
110
|
+
feature_list << File.join(@initialization_hash[:directory_name], feature)
|
111
|
+
else
|
112
|
+
puts "warning: did not find a directory for datapoint #{feature}...skipping"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
else
|
116
|
+
raise "Couldn't find scenario CSV: #{scenario_csv}"
|
117
|
+
end
|
118
|
+
feature_1_name = File.basename(feature_list[0]) # Get name of first feature, so we can read eplusout.sql from there
|
119
|
+
uo_output_sql_file = File.join(@initialization_hash[:directory_name], feature_1_name, 'eplusout.sql')
|
120
|
+
feature_list.each do |feature| # Loop through each feature in the scenario
|
121
|
+
feature_db = SQLite3::Database.open uo_output_sql_file
|
122
|
+
# Doing "db.results_as_hash = true" is prettier, but in this case significantly slower.
|
123
|
+
|
124
|
+
# RDDI == 10 is the timestep value for facility electricity
|
125
|
+
elec_query = feature_db.query "SELECT TimeIndex, Value
|
126
|
+
FROM ReportData
|
127
|
+
WHERE (TimeIndex % 2) != 0
|
128
|
+
AND ReportDataDictionaryIndex=10 order by TimeIndex"
|
129
|
+
|
130
|
+
elec_query.each do |row| # Add up all the values for electricity usage across all Features at this timestep
|
131
|
+
# row[0] == TimeIndex, row[1] == Value
|
132
|
+
arr_match = values_arr.find { |v| v[:time_index] == row[0] }
|
133
|
+
if arr_match.nil?
|
134
|
+
# add new row to value_arr
|
135
|
+
values_arr << { time_index: row[0], elec_val: Float(row[1]), gas_val: 0 }
|
136
|
+
else
|
137
|
+
# running sum
|
138
|
+
arr_match[:elec_val] += Float(row[1])
|
139
|
+
end
|
140
|
+
end # End elec_query
|
141
|
+
elec_query.close
|
142
|
+
|
143
|
+
# RDDI == 255 is the timestep value for facility gas
|
144
|
+
gas_query = feature_db.query "SELECT TimeIndex, Value
|
145
|
+
FROM ReportData
|
146
|
+
WHERE (TimeIndex % 2) != 0
|
147
|
+
AND ReportDataDictionaryIndex=255 order by TimeIndex"
|
148
|
+
|
149
|
+
gas_query.each do |row|
|
150
|
+
# row[0] == TimeIndex, row[1] == Value
|
151
|
+
arr_match = values_arr.find { |v| v[:time_index] == row[0] }
|
152
|
+
if arr_match.nil?
|
153
|
+
# add new row to value_arr
|
154
|
+
values_arr << { time_index: row[0], gas_val: Float(row[1]), elec_val: 0 }
|
155
|
+
else
|
156
|
+
# running sum
|
157
|
+
arr_match[:gas_val] += Float(row[1])
|
158
|
+
end
|
159
|
+
end # End gas_query
|
160
|
+
gas_query.close
|
161
|
+
feature_db.close
|
162
|
+
end # End feature_list loop
|
163
|
+
|
164
|
+
elec_sql = []
|
165
|
+
gas_sql = []
|
166
|
+
values_arr.each do |i|
|
167
|
+
elec_sql << "(#{i[:time_index]}, 10, #{i[:elec_val]})"
|
168
|
+
gas_sql << "(#{i[:time_index]}, 255, #{i[:gas_val]})"
|
169
|
+
end
|
170
|
+
|
171
|
+
# Put summed Values into the database
|
172
|
+
scenario_db.execute("INSERT INTO ReportData (TimeIndex, ReportDataDictionaryIndex, Value) VALUES #{elec_sql.join(', ')}")
|
173
|
+
scenario_db.execute("INSERT INTO ReportData (TimeIndex, ReportDataDictionaryIndex, Value) VALUES #{gas_sql.join(', ')}")
|
174
|
+
scenario_db.close
|
175
|
+
end
|
176
|
+
|
86
177
|
##
|
87
178
|
# Save scenario result
|
88
179
|
##
|
89
180
|
# [parameters:]
|
90
181
|
# +file_name+ - _String_ - Assign a name to the saved scenario results file
|
91
|
-
def save(file_name =
|
182
|
+
def save(file_name = @default_save_name)
|
92
183
|
@scenario_result.save
|
93
184
|
|
94
185
|
return @scenario_result
|
@@ -0,0 +1,275 @@
|
|
1
|
+
# *********************************************************************************
|
2
|
+
# URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
3
|
+
# contributors. All rights reserved.
|
4
|
+
#
|
5
|
+
# Redistribution and use in source and binary forms, with or without modification,
|
6
|
+
# are permitted provided that the following conditions are met:
|
7
|
+
#
|
8
|
+
# Redistributions of source code must retain the above copyright notice, this list
|
9
|
+
# of conditions and the following disclaimer.
|
10
|
+
#
|
11
|
+
# Redistributions in binary form must reproduce the above copyright notice, this
|
12
|
+
# list of conditions and the following disclaimer in the documentation and/or other
|
13
|
+
# materials provided with the distribution.
|
14
|
+
#
|
15
|
+
# Neither the name of the copyright holder nor the names of its contributors may be
|
16
|
+
# used to endorse or promote products derived from this software without specific
|
17
|
+
# prior written permission.
|
18
|
+
#
|
19
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
20
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
21
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
22
|
+
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
23
|
+
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
24
|
+
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
25
|
+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
26
|
+
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
27
|
+
# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
28
|
+
# OF THE POSSIBILITY OF SUCH DAMAGE.
|
29
|
+
# *********************************************************************************
|
30
|
+
|
31
|
+
# require 'urbanopt/scenario/scenario_post_processor_base'
|
32
|
+
require 'urbanopt/reporting/default_reports'
|
33
|
+
|
34
|
+
require 'csv'
|
35
|
+
require 'json'
|
36
|
+
require 'fileutils'
|
37
|
+
require 'pathname'
|
38
|
+
|
39
|
+
module URBANopt
|
40
|
+
module Scenario
|
41
|
+
class OpenDSSPostProcessor
|
42
|
+
##
|
43
|
+
# OpenDSSPostProcessor post-processes OpenDSS results to selected OpenDSS results and integrate them in scenario and feature reports.
|
44
|
+
##
|
45
|
+
# [parameters:]
|
46
|
+
# +scenario_report+ - _ScenarioBase_ - An object of Scenario_report class.
|
47
|
+
# +opendss_results_dir_name+ - _directory name of opendss results
|
48
|
+
def initialize(scenario_report, opendss_results_dir_name = 'opendss')
|
49
|
+
if !scenario_report.nil?
|
50
|
+
@scenario_report = scenario_report
|
51
|
+
@opendss_results_dir = File.join(@scenario_report.directory_name, opendss_results_dir_name)
|
52
|
+
else
|
53
|
+
raise 'scenario_report is not valid'
|
54
|
+
end
|
55
|
+
|
56
|
+
# hash of column_name to array of values, does not get serialized to hash
|
57
|
+
@mutex = Mutex.new
|
58
|
+
|
59
|
+
# initialize opendss data
|
60
|
+
@opendss_data = {}
|
61
|
+
|
62
|
+
# initialize feature_reports data
|
63
|
+
@feature_reports_data = {}
|
64
|
+
|
65
|
+
# initialize logger
|
66
|
+
@@logger ||= URBANopt::Reporting::DefaultReports.logger
|
67
|
+
end
|
68
|
+
|
69
|
+
# load opendss data
|
70
|
+
def load_opendss_data
|
71
|
+
# load building features data
|
72
|
+
@scenario_report.feature_reports.each do |feature_report|
|
73
|
+
# read results from opendss
|
74
|
+
opendss_csv = CSV.read(File.join(@opendss_results_dir, 'results', 'Features', feature_report.id + '.csv'))
|
75
|
+
# add results to data
|
76
|
+
@opendss_data[feature_report.id] = opendss_csv
|
77
|
+
end
|
78
|
+
|
79
|
+
## load transformers data
|
80
|
+
|
81
|
+
# transformers results directory path
|
82
|
+
tf_results_path = File.join(@opendss_results_dir, 'results', 'Transformers')
|
83
|
+
|
84
|
+
# get transformer ids
|
85
|
+
transformer_ids = []
|
86
|
+
Dir.entries(tf_results_path.to_s).select do |f|
|
87
|
+
if !File.directory? f
|
88
|
+
fn = File.basename(f, '.csv')
|
89
|
+
transformer_ids << fn
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# add transformer results to @opendss_data
|
94
|
+
transformer_ids.each do |id|
|
95
|
+
# read results from transformers
|
96
|
+
transformer_csv = CSV.read(File.join(tf_results_path, id + '.csv'))
|
97
|
+
# add results to data
|
98
|
+
@opendss_data[id] = transformer_csv
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# load feature report data
|
103
|
+
def load_feature_report_data
|
104
|
+
@scenario_report.feature_reports.each do |feature_report|
|
105
|
+
# read feature results
|
106
|
+
feature_csv = CSV.read(File.join(feature_report.timeseries_csv.path))
|
107
|
+
# add results to data
|
108
|
+
@feature_reports_data[feature_report.id] = feature_csv
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# load feature report data and opendss data
|
113
|
+
def load_data
|
114
|
+
# load selected opendss data
|
115
|
+
load_opendss_data
|
116
|
+
# load selected feature reports data
|
117
|
+
load_feature_report_data
|
118
|
+
end
|
119
|
+
|
120
|
+
# merge data
|
121
|
+
def merge_data(feature_report_data, opendss_data)
|
122
|
+
output = CSV.generate do |csv|
|
123
|
+
opendss_data.each_with_index do |row, i|
|
124
|
+
if row.include? 'Datetime'
|
125
|
+
row.map { |header| header.prepend('opendss_') }
|
126
|
+
end
|
127
|
+
csv << (feature_report_data[i] + row[1..-1])
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
return output
|
132
|
+
end
|
133
|
+
|
134
|
+
# add feature reports for transformers
|
135
|
+
def save_transformers_reports
|
136
|
+
@opendss_data.keys.each do |k|
|
137
|
+
if k.include? 'Transformer'
|
138
|
+
|
139
|
+
# create transformer directory
|
140
|
+
transformer_dir = File.join(@scenario_report.directory_name, k)
|
141
|
+
FileUtils.mkdir_p(File.join(transformer_dir, 'feature_reports'))
|
142
|
+
|
143
|
+
# write data to csv
|
144
|
+
# store under voltages and over voltages
|
145
|
+
under_voltage_hrs = 0
|
146
|
+
over_voltage_hrs = 0
|
147
|
+
|
148
|
+
transformer_csv = CSV.generate do |csv|
|
149
|
+
@opendss_data[k].each_with_index do |row, i|
|
150
|
+
csv << row
|
151
|
+
|
152
|
+
if !row[1].include? 'loading'
|
153
|
+
if row[1].to_f > 1.05
|
154
|
+
over_voltage_hrs += 1
|
155
|
+
end
|
156
|
+
|
157
|
+
if row[1].to_f < 0.95
|
158
|
+
under_voltage_hrs += 1
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# save transformer CSV report
|
165
|
+
File.write(File.join(transformer_dir, 'feature_reports', 'default_feature_report_opendss' + '.csv'), transformer_csv)
|
166
|
+
|
167
|
+
# create transformer report
|
168
|
+
transformer_report = URBANopt::Reporting::DefaultReports::FeatureReport.new(id: k, name: k, directory_name: transformer_dir, feature_type: 'Transformer',
|
169
|
+
timesteps_per_hour: @scenario_report.timesteps_per_hour,
|
170
|
+
simulation_status: 'complete')
|
171
|
+
|
172
|
+
# assign results to transfomrer report
|
173
|
+
transformer_report.power_distribution.over_voltage_hours = over_voltage_hrs
|
174
|
+
transformer_report.power_distribution.under_voltage_hours = under_voltage_hrs
|
175
|
+
|
176
|
+
## save transformer JSON file
|
177
|
+
# transformer_hash
|
178
|
+
transformer_hash = transformer_report.to_hash
|
179
|
+
# transformer_hash.delete_if { |k, v| v.nil? }
|
180
|
+
|
181
|
+
json_name_path = File.join(transformer_dir, 'feature_reports', 'default_feature_report_opendss' + '.json')
|
182
|
+
|
183
|
+
# save the json file
|
184
|
+
File.open(json_name_path, 'w') do |f|
|
185
|
+
f.puts JSON.pretty_generate(transformer_hash)
|
186
|
+
# make sure data is written to the disk one way or the other
|
187
|
+
begin
|
188
|
+
f.fsync
|
189
|
+
rescue StandardError
|
190
|
+
f.flush
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# add transformers reports to scenario_report
|
195
|
+
@scenario_report.feature_reports << transformer_report
|
196
|
+
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
##
|
202
|
+
# Save csv report method
|
203
|
+
##
|
204
|
+
# [parameters:]
|
205
|
+
# +feature_report+ - _feature report object_ - An onject of the feature report
|
206
|
+
# +updated_feature_report_csv+ - _CSV_ - An updated feature report csv
|
207
|
+
# +file_name+ - _String_ - Assigned name to save the file with no extension
|
208
|
+
def save_csv(feature_report, updated_feature_report_csv, file_name = 'default_feature_report')
|
209
|
+
File.write(File.join(feature_report.directory_name, 'feature_reports', "#{file_name}.csv"), updated_feature_report_csv)
|
210
|
+
end
|
211
|
+
|
212
|
+
##
|
213
|
+
# create opendss json report results
|
214
|
+
##
|
215
|
+
# [parameters:]
|
216
|
+
# +feature_report+ - _feature report object_ - An onject of the feature report
|
217
|
+
def add_summary_results(feature_report)
|
218
|
+
under_voltage_hrs = 0
|
219
|
+
over_voltage_hrs = 0
|
220
|
+
|
221
|
+
id = feature_report.id
|
222
|
+
@opendss_data[id].each_with_index do |row, i|
|
223
|
+
if !row[1].include? 'voltage'
|
224
|
+
|
225
|
+
if row[1].to_f > 1.05
|
226
|
+
over_voltage_hrs += 1
|
227
|
+
end
|
228
|
+
|
229
|
+
if row[1].to_f < 0.95
|
230
|
+
under_voltage_hrs += 1
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
# assign results to feature report
|
237
|
+
feature_report.power_distribution.over_voltage_hours = over_voltage_hrs
|
238
|
+
feature_report.power_distribution.under_voltage_hours = under_voltage_hrs
|
239
|
+
|
240
|
+
return feature_report
|
241
|
+
end
|
242
|
+
|
243
|
+
##
|
244
|
+
# run opendss post_processor
|
245
|
+
##
|
246
|
+
def run
|
247
|
+
@scenario_report.feature_reports.each do |feature_report|
|
248
|
+
# load data
|
249
|
+
load_data
|
250
|
+
|
251
|
+
# puts " @opendss data = #{@opendss_data}"
|
252
|
+
|
253
|
+
# get summary results
|
254
|
+
add_summary_results(feature_report)
|
255
|
+
|
256
|
+
# merge csv data
|
257
|
+
id = feature_report.id
|
258
|
+
updated_feature_csv = merge_data(@feature_reports_data[id], @opendss_data[id])
|
259
|
+
|
260
|
+
# save fetaure reports
|
261
|
+
feature_report.save_feature_report('default_feature_report_opendss')
|
262
|
+
|
263
|
+
# resave updated csv report
|
264
|
+
save_csv(feature_report, updated_feature_csv, 'default_feature_report_opendss')
|
265
|
+
end
|
266
|
+
|
267
|
+
# add transformer reports
|
268
|
+
save_transformers_reports
|
269
|
+
|
270
|
+
# save the updated scenario reports
|
271
|
+
@scenario_report.save(file_name = 'scenario_report_opendss')
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|