urbanopt-cli 0.2.0.pre6 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/pull_request_template.md +0 -1
- data/.rubocop.yml +9 -0
- data/CHANGELOG.md +30 -1
- data/Gemfile +8 -6
- data/README.md +23 -4
- data/Rakefile +4 -2
- data/bin/console +3 -3
- data/developer_nrel_key.rb +31 -0
- data/example_files/Gemfile +4 -4
- data/example_files/mappers/Baseline.rb +360 -204
- data/example_files/mappers/HighEfficiency.rb +25 -29
- data/example_files/mappers/base_workflow.osw +2 -2
- data/example_files/reopt/base_assumptions.json +146 -0
- data/example_files/reopt/multiPV_assumptions.json +181 -0
- data/lib/change_log.rb +9 -4
- data/lib/uo_cli/version.rb +1 -1
- data/lib/uo_cli.rb +345 -295
- data/uo_cli.gemspec +26 -26
- metadata +31 -13
data/lib/uo_cli.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
#!/usr/bin/ ruby
|
2
2
|
|
3
|
-
|
3
|
+
# *********************************************************************************
|
4
4
|
# URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
5
5
|
# contributors. All rights reserved.
|
6
6
|
#
|
@@ -28,359 +28,409 @@
|
|
28
28
|
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
29
29
|
# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
30
30
|
# OF THE POSSIBILITY OF SUCH DAMAGE.
|
31
|
-
|
32
|
-
|
33
|
-
require
|
34
|
-
require
|
35
|
-
require
|
36
|
-
require
|
37
|
-
require
|
38
|
-
require
|
39
|
-
require
|
40
|
-
|
31
|
+
# *********************************************************************************
|
32
|
+
|
33
|
+
require 'uo_cli/version'
|
34
|
+
require 'optparse'
|
35
|
+
require 'urbanopt/geojson'
|
36
|
+
require 'urbanopt/scenario'
|
37
|
+
require 'urbanopt/reopt'
|
38
|
+
require 'urbanopt/reopt_scenario'
|
39
|
+
require 'csv'
|
40
|
+
require 'json'
|
41
|
+
require 'openssl'
|
42
|
+
require_relative '../developer_nrel_key'
|
41
43
|
|
42
44
|
module URBANopt
|
43
45
|
module CLI
|
44
|
-
|
45
46
|
# Set up user interface
|
46
47
|
@user_input = {}
|
47
48
|
the_parser = OptionParser.new do |opts|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
49
|
+
opts.banner = "Usage: uo [-peomrgdsfitv]\n" \
|
50
|
+
"\n" \
|
51
|
+
"URBANopt CLI\n" \
|
52
|
+
"First create a project folder with -p, then run additional commands as desired\n" \
|
53
|
+
"Additional config options can be set with the 'runner.conf' file inside your new project folder"
|
54
|
+
opts.separator ''
|
55
|
+
|
56
|
+
opts.on('-p', '--project_folder <DIR>', String, "Create project directory named <DIR> in your current folder\n" \
|
57
|
+
' You must be inside the project directory you just created for all following commands to work') do |folder|
|
58
|
+
@user_input[:project_folder] = folder
|
59
|
+
end
|
60
|
+
|
61
|
+
opts.on('-e', '--empty_project_folder', String, "Use with -p argument to create an empty project folder\n" \
|
62
|
+
" Example: uo -e -p <DIR>\n" \
|
63
|
+
" Then add your own Feature file in the project directory you created,\n" \
|
64
|
+
" add Weather files in the weather folder and add OpenStudio models of Features \n" \
|
65
|
+
" in the Feature File, if any in the osm_building folder \n" \
|
66
|
+
' You must be inside the project directory you just created for all following commands to work') do
|
67
|
+
@user_input[:empty_project_folder] = 'Create empty project folder' # This text does not get displayed to the user
|
68
|
+
end
|
69
|
+
|
70
|
+
opts.on('-o', '--overwrite_project_folder', String, "Use with -p argument to overwrite existing project folder and replace with new project folder.\n" \
|
71
|
+
" Or, use with -e and -p argument to overwrite existing project folder and replace with new empty project folder.\n" \
|
72
|
+
" Usage: uo -o -p <DIR>\n" \
|
73
|
+
" or, uo -o -e -p <DIR>\n" \
|
74
|
+
' Where, <DIR> is the existing project folder') do
|
75
|
+
@user_input[:overwrite_project_folder] = 'Overwriting existing project folder' # This text does not get displayed to the user
|
76
|
+
end
|
77
|
+
|
78
|
+
opts.on('-m', '--make_scenario', String, "Create ScenarioCSV files for each MapperFile using the Feature file path. Must specify -f argument\n" \
|
79
|
+
" Example: uo -m -f example_project.json\n" \
|
80
|
+
" Or, Create Scenario CSV for each MapperFile for a single Feature from Feature File. Must specify -f and -i argument\n" \
|
81
|
+
' Example: uo -m -f example_project.json -i 1') do
|
82
|
+
@user_input[:make_scenario_from] = "Create scenario files from FeatureFiles or for single Feature according to the MapperFiles in the 'mappers' directory" # This text does not get displayed to the user
|
83
|
+
end
|
84
|
+
|
85
|
+
opts.on('-r', '--run', String, "Run simulations. Must specify -s & -f arguments\n" \
|
86
|
+
' Example: uo -r -s baseline_scenario.csv -f example_project.json') do
|
87
|
+
@user_input[:run_scenario] = 'Run simulations' # This text does not get displayed to the user
|
88
|
+
end
|
89
|
+
|
90
|
+
opts.on('-g', '--gather', String, "group individual feature results to scenario-level results. Must specify -t, -s, & -f arguments\n" \
|
91
|
+
' Example: uo -g -t default -s baseline_scenario.csv -f example_project.json') do
|
92
|
+
@user_input[:gather] = 'Aggregate all features to a whole Scenario' # This text does not get displayed to the user
|
93
|
+
end
|
94
|
+
|
95
|
+
opts.on('-d', '--delete_scenario', String, "Delete results from scenario. Must specify -s argument\n" \
|
96
|
+
' Example: uo -d -s baseline_scenario.csv') do
|
97
|
+
@user_input[:delete_scenario] = 'Delete scenario results that were created from <SFP>' # This text does not get displayed to the user
|
98
|
+
end
|
99
|
+
|
100
|
+
opts.on('-s', '--scenario_file <SFP>', String, 'Specify <SFP> (ScenarioCSV file path). Used as input for other commands') do |scenario|
|
101
|
+
@user_input[:scenario] = scenario
|
102
|
+
@root_dir, @scenario_file_name = File.split(File.absolute_path(@user_input[:scenario]))
|
103
|
+
end
|
104
|
+
|
105
|
+
opts.on('-f', '--feature_file <FFP>', String, 'Specify <FFP> (Feature file path). Used as input for other commands') do |feature|
|
106
|
+
@user_input[:feature] = feature
|
107
|
+
@feature_path, @feature_name = File.split(File.absolute_path(@user_input[:feature]))
|
108
|
+
end
|
109
|
+
|
110
|
+
opts.on('-i', '--feature_id <FID>', Integer, 'Specify <FID> (Feature ID). Used as input for other commands') do |feature_id|
|
111
|
+
@user_input[:feature_id] = feature_id
|
112
|
+
end
|
113
|
+
|
114
|
+
opts.on('-t', '--type <TYPE>', String, "Specify <TYPE> of post-processor to run:\n" \
|
115
|
+
" default\n" \
|
116
|
+
" reopt-scenario\n" \
|
117
|
+
" reopt-feature\n" \
|
118
|
+
" opendss\n") do |type|
|
119
|
+
@user_input[:type] = type
|
120
|
+
end
|
121
|
+
|
122
|
+
opts.on('-v', '--version', 'Show CLI version and exit') do
|
123
|
+
@user_input[:version_request] = VERSION
|
124
|
+
end
|
114
125
|
end
|
115
126
|
|
116
127
|
begin
|
117
|
-
|
128
|
+
the_parser.parse!
|
118
129
|
rescue OptionParser::InvalidOption => e
|
119
130
|
puts e
|
120
131
|
end
|
121
132
|
|
122
|
-
# Simulate energy usage
|
133
|
+
# Simulate energy usage as defined by ScenarioCSV\
|
123
134
|
# params\
|
124
135
|
# +scenario+:: _string_ Path to csv file that defines the scenario\
|
125
136
|
# +feature_file_path+:: _string_ Path to Feature File used to describe set of features in the district
|
126
|
-
#
|
137
|
+
#
|
127
138
|
# FIXME: This only works when scenario_file and feature_file are in the project root directory
|
128
139
|
# This works when called with filename (from inside project directory) and with absolute filepaths
|
129
140
|
# Also, feels a little weird that now I'm only using instance variables and not passing anything to this function. I guess it's ok?
|
130
|
-
def self.run_func
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
return scenario_output
|
141
|
+
def self.run_func
|
142
|
+
name = File.basename(@scenario_file_name, File.extname(@scenario_file_name))
|
143
|
+
run_dir = File.join(@root_dir, 'run', name.downcase)
|
144
|
+
csv_file = File.join(@root_dir, @scenario_file_name)
|
145
|
+
featurefile = File.join(@root_dir, @feature_name)
|
146
|
+
mapper_files_dir = File.join(@root_dir, 'mappers')
|
147
|
+
reopt_files_dir = File.join(@root_dir, 'reopt/')
|
148
|
+
num_header_rows = 1
|
149
|
+
# FIXME: This can be cleaned up in Ruby 2.5 with Dir.children(<"foldername">)
|
150
|
+
reopt_files_dir_contents_list = Dir["#{reopt_files_dir}/*"]
|
151
|
+
reopt_folder_path, reopt_assumptions_filename = File.split(reopt_files_dir_contents_list[0])
|
152
|
+
|
153
|
+
if @feature_id
|
154
|
+
feature_run_dir = File.join(run_dir, @feature_id)
|
155
|
+
# If run folder for feature exists, remove it
|
156
|
+
FileUtils.rm_rf(feature_run_dir) if File.exist?(feature_run_dir)
|
157
|
+
end
|
158
|
+
|
159
|
+
feature_file = URBANopt::GeoJSON::GeoFile.from_file(featurefile)
|
160
|
+
scenario_output = URBANopt::Scenario::REoptScenarioCSV.new(name, @root_dir, run_dir, feature_file, mapper_files_dir, csv_file, num_header_rows, reopt_files_dir, reopt_assumptions_filename)
|
161
|
+
scenario_output
|
152
162
|
end
|
153
163
|
|
154
164
|
# Create a scenario csv file from a FeatureFile
|
155
165
|
# params\
|
156
166
|
# +feature_file_path+:: _string_ Path to a FeatureFile
|
157
|
-
def self.create_scenario_csv_file(
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
end
|
175
|
-
elsif feature_id == feature[:properties][:id].to_i
|
176
|
-
csv << [feature[:properties][:id], feature[:properties][:name], "URBANopt::Scenario::#{mapper_name}Mapper"]
|
177
|
-
elsif
|
178
|
-
# If Feature ID specified does not exist in the Feature File raise error
|
179
|
-
unless feature_file_json[:features].any? {|hash| hash[:properties][:id].include?(feature_id.to_s)}
|
180
|
-
abort("\nYou must provide Feature ID from FeatureFile!\n---\n\n")
|
181
|
-
end
|
182
|
-
end
|
167
|
+
def self.create_scenario_csv_file(feature_id)
|
168
|
+
feature_file_json = JSON.parse(File.read(File.absolute_path(@user_input[:feature])), symbolize_names: true)
|
169
|
+
Dir["#{@feature_path}/mappers/*.rb"].each do |mapper_file|
|
170
|
+
mapper_path, mapper_name = File.split(mapper_file)
|
171
|
+
mapper_name = mapper_name.split('.')[0]
|
172
|
+
scenario_file_name = if feature_id == 'SKIP'
|
173
|
+
"#{mapper_name.downcase}_scenario.csv"
|
174
|
+
else
|
175
|
+
"#{mapper_name.downcase}_scenario-#{feature_id}.csv"
|
176
|
+
end
|
177
|
+
CSV.open(File.join(@feature_path, scenario_file_name), 'wb', write_headers: true,
|
178
|
+
headers: ['Feature Id', 'Feature Name', 'Mapper Class', 'REopt Assumptions']) do |csv|
|
179
|
+
feature_file_json[:features].each do |feature|
|
180
|
+
if feature_id == 'SKIP'
|
181
|
+
# ensure that feature is a building
|
182
|
+
if feature[:properties][:type] == 'Building'
|
183
|
+
csv << [feature[:properties][:id], feature[:properties][:name], "URBANopt::Scenario::#{mapper_name}Mapper", 'base_assumptions.json']
|
183
184
|
end
|
185
|
+
elsif feature_id == feature[:properties][:id].to_i
|
186
|
+
csv << [feature[:properties][:id], feature[:properties][:name], "URBANopt::Scenario::#{mapper_name}Mapper", 'base_assumptions.json']
|
187
|
+
elsif
|
188
|
+
# If Feature ID specified does not exist in the Feature File raise error
|
189
|
+
unless feature_file_json[:features].any? { |hash| hash[:properties][:id].include?(feature_id.to_s) }
|
190
|
+
abort("\nYou must provide Feature ID from FeatureFile!\n---\n\n")
|
191
|
+
end
|
184
192
|
end
|
193
|
+
end
|
185
194
|
end
|
195
|
+
end
|
186
196
|
end
|
187
197
|
|
188
|
-
|
189
198
|
# Create project folder
|
190
199
|
# params\
|
191
200
|
# +dir_name+:: _string_ Name of new project folder
|
192
|
-
#
|
201
|
+
#
|
193
202
|
# Folder gets created in the current working directory
|
194
203
|
# Includes weather for UO's example location, a base workflow file, and mapper files to show a baseline and a high-efficiency option.
|
195
204
|
def self.create_project_folder(dir_name, empty_folder = false, overwrite_project = false)
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
+
if overwrite_project == true
|
206
|
+
if Dir.exist?(dir_name)
|
207
|
+
FileUtils.rm_rf(dir_name)
|
208
|
+
puts "Overwriting project directory: #{dir_name}\n"
|
209
|
+
end
|
210
|
+
elsif overwrite_project == false
|
211
|
+
if Dir.exist?(dir_name)
|
212
|
+
abort("\nERROR: there is already a directory here named #{dir_name}... aborting\n---\n\n")
|
213
|
+
end
|
214
|
+
end
|
215
|
+
puts "CREATING NEW URBANopt project directory: #{dir_name}\n"
|
216
|
+
Dir.mkdir dir_name
|
217
|
+
Dir.mkdir File.join(dir_name, 'mappers')
|
218
|
+
Dir.mkdir File.join(dir_name, 'weather')
|
219
|
+
Dir.mkdir File.join(dir_name, 'reopt')
|
220
|
+
Dir.mkdir File.join(dir_name, 'osm_building')
|
221
|
+
mappers_dir_abs_path = File.absolute_path(File.join(dir_name, 'mappers/'))
|
222
|
+
weather_dir_abs_path = File.absolute_path(File.join(dir_name, 'weather/'))
|
223
|
+
reopt_dir_abs_path = File.absolute_path(File.join(dir_name, 'reopt/'))
|
224
|
+
osm_dir_abs_path = File.absolute_path(File.join(dir_name, 'osm_building/'))
|
225
|
+
|
226
|
+
config_file = 'https://raw.githubusercontent.com/urbanopt/urbanopt-cli/master/example_files/runner.conf'
|
227
|
+
example_feature_file = 'https://raw.githubusercontent.com/urbanopt/urbanopt-cli/master/example_files/example_project.json'
|
228
|
+
example_gem_file = 'https://raw.githubusercontent.com/urbanopt/urbanopt-cli/master/example_files/Gemfile'
|
229
|
+
remote_weather_files = [
|
230
|
+
'https://raw.githubusercontent.com/urbanopt/urbanopt-cli/master/example_files/weather/USA_NY_Buffalo-Greater.Buffalo.Intl.AP.725280_TMY3.epw',
|
231
|
+
'https://raw.githubusercontent.com/urbanopt/urbanopt-cli/master/example_files/weather/USA_NY_Buffalo-Greater.Buffalo.Intl.AP.725280_TMY3.ddy',
|
232
|
+
'https://raw.githubusercontent.com/urbanopt/urbanopt-cli/master/example_files/weather/USA_NY_Buffalo-Greater.Buffalo.Intl.AP.725280_TMY3.stat'
|
233
|
+
]
|
234
|
+
osm_files = [
|
235
|
+
'https://raw.githubusercontent.com/urbanopt/urbanopt-cli/master/example_files/osm_building/7.osm',
|
236
|
+
'https://raw.githubusercontent.com/urbanopt/urbanopt-cli/master/example_files/osm_building/8.osm',
|
237
|
+
'https://raw.githubusercontent.com/urbanopt/urbanopt-cli/master/example_files/osm_building/9.osm'
|
238
|
+
]
|
239
|
+
|
240
|
+
reopt_files = [
|
241
|
+
'https://raw.githubusercontent.com/urbanopt/urbanopt-cli/master/example_files/reopt/base_assumptions.json',
|
242
|
+
'https://raw.githubusercontent.com/urbanopt/urbanopt-cli/master/example_files/reopt/multiPV_assumptions.json'
|
243
|
+
]
|
244
|
+
|
245
|
+
# FIXME: When residential hpxml flow is implemented
|
246
|
+
# (https://github.com/urbanopt/urbanopt-example-geojson-project/pull/24 gets merged)
|
247
|
+
# these files will change
|
248
|
+
remote_mapper_files = [
|
249
|
+
'https://raw.githubusercontent.com/urbanopt/urbanopt-cli/master/example_files/mappers/base_workflow.osw',
|
250
|
+
'https://raw.githubusercontent.com/urbanopt/urbanopt-cli/master/example_files/mappers/Baseline.rb',
|
251
|
+
'https://raw.githubusercontent.com/urbanopt/urbanopt-cli/master/example_files/mappers/HighEfficiency.rb'
|
252
|
+
]
|
253
|
+
|
254
|
+
# Download mapper files to user's local machine
|
255
|
+
remote_mapper_files.each do |mapper_file|
|
256
|
+
mapper_path, mapper_name = File.split(mapper_file)
|
257
|
+
mapper_download = open(mapper_file, ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE)
|
258
|
+
IO.copy_stream(mapper_download, File.join(mappers_dir_abs_path, mapper_name))
|
259
|
+
end
|
260
|
+
|
261
|
+
# Download gemfile to user's local machine
|
262
|
+
gem_path, gem_name = File.split(example_gem_file)
|
263
|
+
example_gem_download = open(example_gem_file, ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE)
|
264
|
+
IO.copy_stream(example_gem_download, File.join(dir_name, gem_name))
|
265
|
+
|
266
|
+
# if argument for creating an empty folder is not added
|
267
|
+
if empty_folder == false
|
268
|
+
|
269
|
+
# Download reopt files to user's local machine
|
270
|
+
reopt_files.each do |reopt_remote_file|
|
271
|
+
reopt_file = File.basename(reopt_remote_file)
|
272
|
+
reopt_file_download = open(reopt_remote_file, ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE)
|
273
|
+
IO.copy_stream(reopt_file_download, File.join(reopt_dir_abs_path, reopt_file))
|
205
274
|
end
|
206
|
-
puts "CREATING NEW URBANopt project directory: #{dir_name}"
|
207
|
-
Dir.mkdir dir_name
|
208
|
-
Dir.mkdir File.join(dir_name, 'mappers')
|
209
|
-
Dir.mkdir File.join(dir_name, 'weather')
|
210
|
-
Dir.mkdir File.join(dir_name, 'osm_building')
|
211
|
-
mappers_dir_abs_path = File.absolute_path(File.join(dir_name, 'mappers/'))
|
212
|
-
weather_dir_abs_path = File.absolute_path(File.join(dir_name, 'weather/'))
|
213
|
-
osm_dir_abs_path = File.absolute_path(File.join(dir_name, 'osm_building/'))
|
214
|
-
|
215
|
-
# FIXME: When residential hpxml flow is implemented
|
216
|
-
# (https://github.com/urbanopt/urbanopt-example-geojson-project/pull/24 gets merged)
|
217
|
-
# these files will change
|
218
|
-
|
219
|
-
|
220
|
-
remote_mapper_files = [
|
221
|
-
"https://raw.githubusercontent.com/urbanopt/urbanopt-cli/master/example_files/mappers/base_workflow.osw",
|
222
|
-
"https://raw.githubusercontent.com/urbanopt/urbanopt-cli/master/example_files/mappers/Baseline.rb",
|
223
|
-
"https://raw.githubusercontent.com/urbanopt/urbanopt-cli/master/example_files/mappers/HighEfficiency.rb",
|
224
|
-
]
|
225
|
-
|
226
|
-
example_gem_file = "https://raw.githubusercontent.com/urbanopt/urbanopt-cli/master/example_files/Gemfile"
|
227
|
-
|
228
|
-
# Download mapper files to user's local machine
|
229
|
-
remote_mapper_files.each do |mapper_file|
|
230
|
-
mapper_path, mapper_name = File.split(mapper_file)
|
231
|
-
mapper_download = open(mapper_file, {ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE})
|
232
|
-
IO.copy_stream(mapper_download, File.join(mappers_dir_abs_path, mapper_name))
|
233
|
-
end
|
234
275
|
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
#if argument for creating an empty folder is not added
|
241
|
-
if empty_folder == false
|
242
|
-
|
243
|
-
example_feature_file = "https://raw.githubusercontent.com/urbanopt/urbanopt-cli/master/example_files/example_project.json"
|
244
|
-
|
245
|
-
remote_weather_files = [
|
246
|
-
"https://raw.githubusercontent.com/urbanopt/urbanopt-cli/master/example_files/weather/USA_NY_Buffalo-Greater.Buffalo.Intl.AP.725280_TMY3.epw",
|
247
|
-
"https://raw.githubusercontent.com/urbanopt/urbanopt-cli/master/example_files/weather/USA_NY_Buffalo-Greater.Buffalo.Intl.AP.725280_TMY3.ddy",
|
248
|
-
"https://raw.githubusercontent.com/urbanopt/urbanopt-cli/master/example_files/weather/USA_NY_Buffalo-Greater.Buffalo.Intl.AP.725280_TMY3.stat",
|
249
|
-
]
|
250
|
-
|
251
|
-
osm_files = [
|
252
|
-
"https://raw.githubusercontent.com/urbanopt/urbanopt-cli/master/example_files/osm_building/7.osm",
|
253
|
-
"https://raw.githubusercontent.com/urbanopt/urbanopt-cli/master/example_files/osm_building/8.osm",
|
254
|
-
"https://raw.githubusercontent.com/urbanopt/urbanopt-cli/master/example_files/osm_building/9.osm"
|
255
|
-
]
|
256
|
-
|
257
|
-
config_file = "https://raw.githubusercontent.com/urbanopt/urbanopt-cli/master/example_files/runner.conf"
|
258
|
-
config_path, config_name = File.split(config_file)
|
259
|
-
config_download = open(config_file, {ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE})
|
260
|
-
IO.copy_stream(config_download, File.join(dir_name, config_name))
|
261
|
-
|
262
|
-
|
263
|
-
# Download weather file to user's local machine
|
264
|
-
remote_weather_files.each do |weather_file|
|
265
|
-
weather_path, weather_name = File.split(weather_file)
|
266
|
-
weather_download = open(weather_file, {ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE})
|
267
|
-
IO.copy_stream(weather_download, File.join(weather_dir_abs_path, weather_name))
|
268
|
-
end
|
276
|
+
# Download config file to user's local machine
|
277
|
+
config_path, config_name = File.split(config_file)
|
278
|
+
config_download = open(config_file, ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE)
|
279
|
+
IO.copy_stream(config_download, File.join(dir_name, config_name))
|
269
280
|
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
281
|
+
# Download weather file to user's local machine
|
282
|
+
remote_weather_files.each do |weather_file|
|
283
|
+
weather_path, weather_name = File.split(weather_file)
|
284
|
+
weather_download = open(weather_file, ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE)
|
285
|
+
IO.copy_stream(weather_download, File.join(weather_dir_abs_path, weather_name))
|
286
|
+
end
|
275
287
|
|
276
|
-
|
277
|
-
|
278
|
-
|
288
|
+
# Download osm files to user's local machine
|
289
|
+
osm_files.each do |osm_file|
|
290
|
+
osm_path, osm_name = File.split(osm_file)
|
291
|
+
osm_download = open(osm_file, ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE)
|
292
|
+
IO.copy_stream(osm_download, File.join(osm_dir_abs_path, osm_name))
|
293
|
+
end
|
279
294
|
|
280
|
-
|
295
|
+
# Download feature file to user's local machine
|
296
|
+
feature_path, feature_name = File.split(example_feature_file)
|
297
|
+
example_feature_download = open(example_feature_file, ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE)
|
298
|
+
IO.copy_stream(example_feature_download, File.join(dir_name, feature_name))
|
299
|
+
end
|
281
300
|
end
|
282
301
|
|
283
|
-
|
284
302
|
# Perform CLI actions
|
285
303
|
if @user_input[:project_folder] && @user_input[:empty_project_folder].nil?
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
304
|
+
if @user_input[:overwrite_project_folder]
|
305
|
+
create_project_folder(@user_input[:project_folder], empty_folder = false, overwrite_project = true)
|
306
|
+
puts "\nOverwriting exiting project folder #{@user_input[:project_folder]}."
|
307
|
+
puts "Creating a new project folder.\n"
|
308
|
+
elsif @user_input[:overwrite_project_folder].nil?
|
309
|
+
create_project_folder(@user_input[:project_folder], empty_folder = false, overwrite_project = false)
|
310
|
+
end
|
311
|
+
puts "\nAn example FeatureFile is included: 'example_project.json'. You may place your own FeatureFile alongside the example."
|
312
|
+
puts 'Weather data is provided for the example FeatureFile. Additional weather data files may be downloaded from energyplus.net/weather for free'
|
313
|
+
puts "If you use additional weather files, ensure they are added to the 'weather' directory. You will need to configure your mapper file and your osw file to use the desired weather file"
|
314
|
+
puts "Next, move inside your new folder ('cd <FolderYouJustCreated>') and create ScenarioFiles using this CLI call: 'uo -m -f <FFP>'\n"
|
297
315
|
elsif @user_input[:project_folder] && @user_input[:empty_project_folder]
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
316
|
+
if @user_input[:overwrite_project_folder]
|
317
|
+
create_project_folder(@user_input[:project_folder], empty_folder = true, overwrite_project = true)
|
318
|
+
puts "\nOverwriting exiting project folder #{@user_input[:project_folder]}."
|
319
|
+
puts "Creating a new project folder.\n"
|
320
|
+
elsif @user_input[:overwrite_project].nil?
|
321
|
+
create_project_folder(@user_input[:project_folder], empty_folder = true, overwrite_project = false)
|
322
|
+
end
|
323
|
+
puts 'Add your FeatureFile in the Project directory you just created.'
|
324
|
+
puts 'Add your weather data files in the Weather folder. They may be downloaded from energyplus.net/weather for free'
|
325
|
+
puts 'Add your OpenStudio models for Features in your Feature file, if any in the osm_building folder'
|
326
|
+
puts "Next, move inside your new folder ('cd <FolderYouJustCreated>') and create ScenarioFiles using this CLI call: 'uo -m -f <FFP>'\n"
|
309
327
|
end
|
310
328
|
|
311
329
|
if @user_input[:make_scenario_from]
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
end
|
330
|
+
if @user_input[:feature].nil?
|
331
|
+
abort("\nYou must provide the '-f' flag and a valid path to a FeatureFile!\n---\n\n")
|
332
|
+
end
|
333
|
+
|
334
|
+
if @user_input[:feature_id]
|
335
|
+
puts "\nBuilding sample ScenarioFiles, assigning mapper classes to Feature ID #{@user_input[:feature_id]}..."
|
336
|
+
create_scenario_csv_file(@user_input[:feature_id])
|
337
|
+
puts "\nDone\n"
|
338
|
+
else
|
339
|
+
puts "\nBuilding sample ScenarioFiles, assigning mapper classes to each feature from #{@feature_name}..."
|
340
|
+
# Skip Feature ID argument if not present
|
341
|
+
create_scenario_csv_file('SKIP')
|
342
|
+
puts "\nDone\n"
|
343
|
+
end
|
327
344
|
end
|
328
345
|
|
329
|
-
|
330
|
-
|
331
346
|
if @user_input[:run_scenario]
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
puts "Done"
|
347
|
+
if @user_input[:scenario].nil?
|
348
|
+
abort("\nYou must provide '-s' flag and a valid path to a ScenarioFile!\n---\n\n")
|
349
|
+
end
|
350
|
+
if @user_input[:feature].nil?
|
351
|
+
abort("\nYou must provide '-f' flag and a valid path to a FeatureFile!\n---\n\n")
|
352
|
+
end
|
353
|
+
if @user_input[:scenario].to_s.include? '-'
|
354
|
+
@scenario_folder = scenario_file_name.split(/\W+/)[0].capitalize.to_s
|
355
|
+
@feature_id = (@feature_name.split(/\W+/)[1]).to_s
|
356
|
+
else
|
357
|
+
@scenario_folder = @scenario_file_name.split('.')[0].capitalize.to_s
|
358
|
+
end
|
359
|
+
puts "\nSimulating features of '#{@feature_name}' as directed by '#{@user_input[:scenario]}'...\n\n"
|
360
|
+
scenario_runner = URBANopt::Scenario::ScenarioRunnerOSW.new
|
361
|
+
scenario_runner.run(run_func)
|
362
|
+
puts "\nDone\n"
|
349
363
|
end
|
350
364
|
|
351
|
-
if @user_input[:
|
352
|
-
|
353
|
-
|
365
|
+
if @user_input[:gather]
|
366
|
+
if @user_input[:scenario].nil?
|
367
|
+
abort("\nYou must provide '-s' flag and a valid path to a ScenarioFile!\n---\n\n")
|
368
|
+
end
|
369
|
+
if @user_input[:feature].nil?
|
370
|
+
abort("\nYou must provide '-f' flag and a valid path to a FeatureFile!\n---\n\n")
|
371
|
+
end
|
372
|
+
if @user_input[:type].nil?
|
373
|
+
abort("\nYou must provide '-t' flag and a valid Gather type!\n" \
|
374
|
+
"Valid types include: 'default', 'reopt-scenario', 'reopt-feature', or 'opendss'\n---\n\n")
|
375
|
+
end
|
376
|
+
@scenario_folder = @scenario_file_name.split('.')[0].capitalize.to_s
|
377
|
+
|
378
|
+
default_post_processor = URBANopt::Scenario::ScenarioDefaultPostProcessor.new(run_func)
|
379
|
+
scenario_report = default_post_processor.run
|
380
|
+
scenario_report.save
|
381
|
+
# FIXME: Remove this feature_reports block once urbanopt/urbanopt-scenario-gem#104 works as expected.
|
382
|
+
# save feature reports
|
383
|
+
scenario_report.feature_reports.each(&:save_feature_report)
|
384
|
+
|
385
|
+
if @user_input[:type].to_s.casecmp('default').zero?
|
386
|
+
puts "\nDone\n"
|
387
|
+
elsif @user_input[:type].to_s.casecmp('opendss').zero?
|
388
|
+
puts "\nPost-processing OpenDSS results\n"
|
389
|
+
opendss_folder = File.join(@root_dir, 'run', @scenario_name.split('.')[0], 'opendss')
|
390
|
+
if File.directory?(opendss_folder)
|
391
|
+
opendss_folder_path, opendss_folder_name = File.split(opendss_folder)
|
392
|
+
opendss_post_processor = URBANopt::Scenario::OpenDSSPostProcessor.new(scenario_report, opendss_results_dir_name = opendss_folder_name)
|
393
|
+
opendss_post_processor.run
|
394
|
+
puts "\nDone\n"
|
395
|
+
else
|
396
|
+
abort("\nNo OpenDSS results available in folder '#{opendss_folder}'\n")
|
354
397
|
end
|
355
|
-
|
356
|
-
|
398
|
+
elsif @user_input[:type].to_s.downcase.include?('reopt')
|
399
|
+
scenario_base = default_post_processor.scenario_base
|
400
|
+
reopt_post_processor = URBANopt::REopt::REoptPostProcessor.new(scenario_report, scenario_base.scenario_reopt_assumptions_file, scenario_base.reopt_feature_assumptions, DEVELOPER_NREL_KEY)
|
401
|
+
|
402
|
+
# Optimize REopt outputs for the whole Scenario
|
403
|
+
if @user_input[:type].to_s.casecmp('reopt-scenario').zero?
|
404
|
+
puts "\nOptimizing renewable energy for the scenario\n"
|
405
|
+
scenario_report_scenario = reopt_post_processor.run_scenario_report(scenario_report: scenario_report, save_name: 'scenario_optimization')
|
406
|
+
puts "\nDone\n"
|
407
|
+
# Optimize REopt outputs for each feature individually
|
408
|
+
elsif @user_input[:type].to_s.casecmp('reopt-feature').zero?
|
409
|
+
puts "\nOptimizing renewable energy for each feature\n"
|
410
|
+
scenario_report_features = reopt_post_processor.run_scenario_report_features(scenario_report: scenario_report, save_names_feature_reports: ['feature_optimization'] * scenario_report.feature_reports.length, save_name_scenario_report: 'feature_optimization')
|
411
|
+
puts "\nDone\n"
|
412
|
+
else
|
413
|
+
abort("\nError: did not use type 'reopt-scenario', 'reopt-feature'. Aborting...\n---\n\n")
|
357
414
|
end
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
puts "\nAggregating results across all features of #{@feature_name} according to '#{@scenario_name}'...\n"
|
362
|
-
scenario_result = URBANopt::Scenario::ScenarioDefaultPostProcessor.new(run_func()).run
|
363
|
-
scenario_result.save
|
364
|
-
puts "Done"
|
415
|
+
else
|
416
|
+
abort("\nError: did not use type 'default', 'reopt-scenario', 'reopt-feature', or 'opendss'. Aborting...\n---\n\n")
|
417
|
+
end
|
365
418
|
end
|
366
419
|
|
367
420
|
if @user_input[:delete_scenario]
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
puts "Done"
|
421
|
+
if @user_input[:scenario].nil?
|
422
|
+
abort("\nYou must provide '-s' flag and a valid path to a ScenarioFile!\n---\n\n")
|
423
|
+
end
|
424
|
+
scenario_name = @scenario_file_name.split('.')[0]
|
425
|
+
scenario_path = File.absolute_path(@root_dir)
|
426
|
+
scenario_results_dir = File.join(scenario_path, 'run', scenario_name)
|
427
|
+
puts "\nDeleting previous results from '#{@scenario_file_name}'...\n"
|
428
|
+
FileUtils.rm_rf(scenario_results_dir)
|
429
|
+
puts "\nDone\n"
|
378
430
|
end
|
379
431
|
|
380
432
|
if @user_input[:version_request]
|
381
|
-
|
433
|
+
puts "\nURBANopt CLI version: #{@user_input[:version_request]}\n---\n\n"
|
382
434
|
end
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
end # End module Urbanopt
|
435
|
+
end
|
436
|
+
end
|