urbanopt-cli 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,4 @@
1
+
1
2
  # *********************************************************************************
2
3
  # URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
3
4
  # contributors. All rights reserved.
@@ -51,7 +52,7 @@ module URBANopt
51
52
  OpenStudio::Extension.set_measure_argument(osw, 'ReduceLightingLoadsByPercentage', '__SKIP__', false)
52
53
  OpenStudio::Extension.set_measure_argument(osw, 'ReduceLightingLoadsByPercentage', 'lighting_power_reduction_percent', 10)
53
54
 
54
- osw
55
+ return osw
55
56
  end
56
57
  end
57
58
  end
@@ -31,7 +31,7 @@
31
31
  # *********************************************************************************
32
32
 
33
33
  require 'uo_cli/version'
34
- require 'optparse'
34
+ require 'optimist'
35
35
  require 'urbanopt/geojson'
36
36
  require 'urbanopt/scenario'
37
37
  require 'urbanopt/reopt'
@@ -43,101 +43,145 @@ require_relative '../developer_nrel_key'
43
43
 
44
44
  module URBANopt
45
45
  module CLI
46
- # Set up user interface
47
- @user_input = {}
48
- the_parser = OptionParser.new do |opts|
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
46
+ class UrbanOptCLI
47
+ COMMAND_MAP = {
48
+ 'create' => 'Make new things - project directory or files',
49
+ 'run' => 'Use files in your directory to simulate district energy use',
50
+ 'process' => 'Post-process URBANopt simulations for additional insights',
51
+ 'delete' => 'Delete simulations for a specified scenario'
52
+ }.freeze
53
+
54
+ def initialize
55
+ @subopts = nil
56
+ @command = nil
57
+ @mainopts = Optimist.options do
58
+ version VERSION
59
+ banner "\nURBANopt CLI version: #{version}"
60
+ banner "\nUsage:"
61
+ banner " uo [options] [<command> [suboptions]]\n \n"
62
+ banner 'Options:'
63
+ opt :version, 'Print version and exit' ## add this here or it goes to bottom of help
64
+ opt :help, 'Show this help message' ## add this here or it goes to bottom of help
65
+ # opt :no_pager, "Disable paging"
66
+ stop_on COMMAND_MAP.keys
67
+ banner "\nCommands:"
68
+ COMMAND_MAP.each { |cmd, desc| banner format(' %-10s %s', cmd, desc) }
69
+ banner "\nFor help with a specific command: uo <command> --help"
70
+ banner "\nAdditional config options can be set with the 'runner.conf' file inside your project folder"
71
+ banner 'Fewer warnings are presented when using full paths and the user is not inside the project folder'
72
+ end
73
+ return if ARGV.empty?
74
+ @command = ARGV.shift
75
+ send("opt_#{@command}") ## dispatch to command handling method
59
76
  end
60
77
 
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
78
+ # Define creation commands
79
+ def opt_create
80
+ cmd = @command
81
+ @subopts = Optimist.options do
82
+ banner "\nURBANopt #{cmd}:\n \n"
83
+
84
+ opt :project_folder, "\nCreate project directory in your current folder. Name the directory\n" \
85
+ 'Example: uo create --project urbanopt_example_project', type: String
86
+
87
+ opt :empty, "\nUse with --project-folder argument to create an empty project folder\n" \
88
+ "Then add your own Feature file in the project directory you created,\n" \
89
+ "add Weather files in the weather folder and add OpenStudio models of Features\n" \
90
+ "in the Feature File, if any, in the osm_building folder\n" \
91
+ "Example: uo create --empty --project-folder urbanopt_example_project\n" \
92
+
93
+ opt :overwrite, "\nUse with --project-folder argument to overwrite existing project folder and replace with new project folder.\n" \
94
+ "May be combined with --empty as well to overwrite existing project folder and replace with new empty project folder.\n" \
95
+ 'Example: uo create --overwrite --empty --project-folder urbanopt_project_folder_I_want_destroyed'
96
+
97
+ opt :scenario_file, "\nAutomatically create a ScenarioFile containing the features in FeatureFile for each scenario\n" \
98
+ "Provide the FeatureFile used to create the ScenarioFile\n" \
99
+ 'Example: uo create --scenario-file example_project.json', type: String
100
+
101
+ opt :single_feature, "\nCreate a ScenarioFile with only a single feature\n" \
102
+ "Use the FeatureID from your FeatureFile\n" \
103
+ "Requires 'scenario-file' also be specified, to say which FeatureFile will create the ScenarioFile\n" \
104
+ 'Example: uo create --single-feature 2 --scenario-file example_project.json', type: String
105
+
106
+ opt :reopt_scenario_file, "\nCreate a ScenarioFile that includes a column defining the REopt assumptions file\n" \
107
+ "Specify the existing ScenarioFile that you want to extend with REopt functionality\n" \
108
+ 'Example: uo create --reopt-scenario-file baseline_scenario.csv', type: String
109
+ end
68
110
  end
69
111
 
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
112
+ # Define running commands
113
+ def opt_run
114
+ cmd = @command
115
+ @subopts = Optimist.options do
116
+ banner "\nURBANopt #{cmd}:\n \n"
77
117
 
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
118
+ opt :reopt, "\nSimulate with additional REopt functionality. Must do this before post-processing with REopt"
84
119
 
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
120
+ opt :scenario, "\nRun URBANopt simulations for <scenario>\n" \
121
+ "Requires --feature also be specified\n" \
122
+ 'Example: uo run --scenario baseline_scenario-2.csv --feature example_project.jsonn', default: 'baseline_scenario.csv', required: true
89
123
 
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
124
+ opt :feature, "\nRun URBANopt simulations according to <featurefile>\n" \
125
+ "Requires --scenario also be specified\n" \
126
+ 'Example: uo run --scenario baseline_scenario.csv --feature example_project.json', default: 'example_project.json', required: true
127
+ end
93
128
  end
94
129
 
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
130
+ # Define post-processing commands
131
+ def opt_process
132
+ cmd = @command
133
+ @subopts = Optimist.options do
134
+ banner "\nURBANopt #{cmd}:\n \n"
99
135
 
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
136
+ opt :default, "\nStandard post-processing for your scenario"
104
137
 
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
138
+ opt :opendss, "\nPost-process with OpenDSS"
109
139
 
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
140
+ opt :reopt_scenario, "\nOptimize for entire scenario with REopt\n" \
141
+ 'Example: uo process --reopt-scenario'
142
+
143
+ opt :reopt_feature, "\nOptimize for each building individually with REopt\n" \
144
+ 'Example: uo process --reopt-feature'
113
145
 
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
146
+ opt :scenario, "\nSelect which scenario to optimize", default: 'baseline_scenario.csv', required: true
147
+
148
+ opt :feature, "\nSelect which FeatureFile to use", default: 'example_project.json', required: true
149
+ end
120
150
  end
121
151
 
122
- opts.on('-v', '--version', 'Show CLI version and exit') do
123
- @user_input[:version_request] = VERSION
152
+ def opt_delete
153
+ cmd = @command
154
+ @subopts = Optimist.options do
155
+ banner "\nURBANopt #{cmd}:\n \n"
156
+
157
+ opt :scenario, "\nDelete simulation files for this scenario", default: 'baseline_scenario.csv', required: true
158
+ end
124
159
  end
160
+
161
+ attr_reader :mainopts, :command, :subopts
125
162
  end
126
163
 
127
- begin
128
- the_parser.parse!
129
- rescue OptionParser::InvalidOption => e
130
- puts e
164
+ # Initialize the CLI class
165
+ @opthash = UrbanOptCLI.new
166
+
167
+ # Pull out feature and scenario filenames and paths
168
+ if @opthash.subopts[:scenario_file]
169
+ @feature_path, @feature_name = File.split(File.absolute_path(@opthash.subopts[:scenario_file]))
170
+ end
171
+ # FIXME: Can this be combined with the above block? This isn't very DRY
172
+ # One solution would be changing scenario_file to feature.
173
+ # Would that be confusing when creating a ScenarioFile from the FeatureFile?
174
+ if @opthash.subopts[:feature]
175
+ @feature_path, @feature_name = File.split(File.absolute_path(@opthash.subopts[:feature]))
176
+ end
177
+ if @opthash.subopts[:scenario]
178
+ @root_dir, @scenario_file_name = File.split(File.absolute_path(@opthash.subopts[:scenario]))
131
179
  end
132
180
 
133
181
  # Simulate energy usage as defined by ScenarioCSV\
134
182
  # params\
135
183
  # +scenario+:: _string_ Path to csv file that defines the scenario\
136
184
  # +feature_file_path+:: _string_ Path to Feature File used to describe set of features in the district
137
- #
138
- # FIXME: This only works when scenario_file and feature_file are in the project root directory
139
- # This works when called with filename (from inside project directory) and with absolute filepaths
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?
141
185
  def self.run_func
142
186
  name = File.basename(@scenario_file_name, File.extname(@scenario_file_name))
143
187
  run_dir = File.join(@root_dir, 'run', name.downcase)
@@ -147,8 +191,9 @@ module URBANopt
147
191
  reopt_files_dir = File.join(@root_dir, 'reopt/')
148
192
  num_header_rows = 1
149
193
  # FIXME: This can be cleaned up in Ruby 2.5 with Dir.children(<"foldername">)
194
+ # TODO: Better way of grabbing assumptions file than the first file in the folder
150
195
  reopt_files_dir_contents_list = Dir["#{reopt_files_dir}/*"]
151
- reopt_folder_path, reopt_assumptions_filename = File.split(reopt_files_dir_contents_list[0])
196
+ reopt_assumptions_filename = File.basename(reopt_files_dir_contents_list[0])
152
197
 
153
198
  if @feature_id
154
199
  feature_run_dir = File.join(run_dir, @feature_id)
@@ -157,7 +202,11 @@ module URBANopt
157
202
  end
158
203
 
159
204
  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)
205
+ if @opthash.subopts[:reopt] == true || @opthash.subopts[:reopt_scenario] == true || @opthash.subopts[:reopt_feature] == true
206
+ 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)
207
+ else
208
+ scenario_output = URBANopt::Scenario::ScenarioCSV.new(name, @root_dir, run_dir, feature_file, mapper_files_dir, csv_file, num_header_rows)
209
+ end
161
210
  scenario_output
162
211
  end
163
212
 
@@ -165,25 +214,24 @@ module URBANopt
165
214
  # params\
166
215
  # +feature_file_path+:: _string_ Path to a FeatureFile
167
216
  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)
217
+ feature_file_json = JSON.parse(File.read(File.absolute_path(@opthash.subopts[:scenario_file])), symbolize_names: true)
169
218
  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]
219
+ mapper_name = File.basename(mapper_file, File.extname(mapper_file))
172
220
  scenario_file_name = if feature_id == 'SKIP'
173
221
  "#{mapper_name.downcase}_scenario.csv"
174
222
  else
175
223
  "#{mapper_name.downcase}_scenario-#{feature_id}.csv"
176
224
  end
177
225
  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|
226
+ headers: ['Feature Id', 'Feature Name', 'Mapper Class']) do |csv|
179
227
  feature_file_json[:features].each do |feature|
180
228
  if feature_id == 'SKIP'
181
229
  # ensure that feature is a building
182
230
  if feature[:properties][:type] == 'Building'
183
- csv << [feature[:properties][:id], feature[:properties][:name], "URBANopt::Scenario::#{mapper_name}Mapper", 'base_assumptions.json']
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']
231
+ csv << [feature[:properties][:id], feature[:properties][:name], "URBANopt::Scenario::#{mapper_name}Mapper"]
232
+ end
233
+ elsif feature_id == feature[:properties][:id]
234
+ csv << [feature[:properties][:id], feature[:properties][:name], "URBANopt::Scenario::#{mapper_name}Mapper"]
187
235
  elsif
188
236
  # If Feature ID specified does not exist in the Feature File raise error
189
237
  unless feature_file_json[:features].any? { |hash| hash[:properties][:id].include?(feature_id.to_s) }
@@ -195,24 +243,38 @@ module URBANopt
195
243
  end
196
244
  end
197
245
 
246
+ # Write new ScenarioFile with REopt column
247
+ # params \
248
+ # +existing_scenario_file+:: _string_ - Name of existing ScenarioFile
249
+ def self.create_reopt_scenario_file(existing_scenario_file)
250
+ existing_path, existing_name = File.split(File.absolute_path(existing_scenario_file))
251
+ table = CSV.read(existing_scenario_file, headers: true, col_sep: ',')
252
+ # Add another column, row by row:
253
+ table.each do |row|
254
+ row['REopt Assumptions'] = 'multiPV_assumptions.json'
255
+ end
256
+ # write new file
257
+ CSV.open(File.join(existing_path, 'REopt_scenario.csv'), 'w') do |f|
258
+ f << table.headers
259
+ table.each { |row| f << row }
260
+ end
261
+ end
262
+
198
263
  # Create project folder
199
264
  # params\
200
265
  # +dir_name+:: _string_ Name of new project folder
201
266
  #
202
- # Folder gets created in the current working directory
203
- # Includes weather for UO's example location, a base workflow file, and mapper files to show a baseline and a high-efficiency option.
267
+ # Includes weather for example location, a base workflow file, and mapper files to show a baseline and a high-efficiency option.
204
268
  def self.create_project_folder(dir_name, empty_folder = false, overwrite_project = false)
205
269
  if overwrite_project == true
206
270
  if Dir.exist?(dir_name)
207
271
  FileUtils.rm_rf(dir_name)
208
- puts "Overwriting project directory: #{dir_name}\n"
209
272
  end
210
273
  elsif overwrite_project == false
211
274
  if Dir.exist?(dir_name)
212
275
  abort("\nERROR: there is already a directory here named #{dir_name}... aborting\n---\n\n")
213
276
  end
214
277
  end
215
- puts "CREATING NEW URBANopt project directory: #{dir_name}\n"
216
278
  Dir.mkdir dir_name
217
279
  Dir.mkdir File.join(dir_name, 'mappers')
218
280
  Dir.mkdir File.join(dir_name, 'weather')
@@ -253,13 +315,13 @@ module URBANopt
253
315
 
254
316
  # Download mapper files to user's local machine
255
317
  remote_mapper_files.each do |mapper_file|
256
- mapper_path, mapper_name = File.split(mapper_file)
318
+ mapper_name = File.basename(mapper_file)
257
319
  mapper_download = open(mapper_file, ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE)
258
320
  IO.copy_stream(mapper_download, File.join(mappers_dir_abs_path, mapper_name))
259
321
  end
260
322
 
261
323
  # Download gemfile to user's local machine
262
- gem_path, gem_name = File.split(example_gem_file)
324
+ gem_name = File.basename(example_gem_file)
263
325
  example_gem_download = open(example_gem_file, ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE)
264
326
  IO.copy_stream(example_gem_download, File.join(dir_name, gem_name))
265
327
 
@@ -274,153 +336,135 @@ module URBANopt
274
336
  end
275
337
 
276
338
  # Download config file to user's local machine
277
- config_path, config_name = File.split(config_file)
339
+ config_name = File.basename(config_file)
278
340
  config_download = open(config_file, ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE)
279
341
  IO.copy_stream(config_download, File.join(dir_name, config_name))
280
342
 
281
343
  # Download weather file to user's local machine
282
344
  remote_weather_files.each do |weather_file|
283
- weather_path, weather_name = File.split(weather_file)
345
+ weather_name = File.basename(weather_file)
284
346
  weather_download = open(weather_file, ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE)
285
347
  IO.copy_stream(weather_download, File.join(weather_dir_abs_path, weather_name))
286
348
  end
287
349
 
288
350
  # Download osm files to user's local machine
289
351
  osm_files.each do |osm_file|
290
- osm_path, osm_name = File.split(osm_file)
352
+ osm_name = File.basename(osm_file)
291
353
  osm_download = open(osm_file, ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE)
292
354
  IO.copy_stream(osm_download, File.join(osm_dir_abs_path, osm_name))
293
355
  end
294
356
 
295
357
  # Download feature file to user's local machine
296
- feature_path, feature_name = File.split(example_feature_file)
358
+ feature_name = File.basename(example_feature_file)
297
359
  example_feature_download = open(example_feature_file, ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE)
298
360
  IO.copy_stream(example_feature_download, File.join(dir_name, feature_name))
299
361
  end
300
362
  end
301
363
 
302
364
  # Perform CLI actions
303
- if @user_input[:project_folder] && @user_input[:empty_project_folder].nil?
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)
365
+
366
+ # Create new project folder
367
+ if @opthash.command == 'create' && @opthash.subopts[:project_folder] && @opthash.subopts[:empty] == false
368
+ if @opthash.subopts[:overwrite] == true
369
+ puts "\nOverwriting existing project folder: #{@opthash.subopts[:project_folder]}...\n\n"
370
+ create_project_folder(@opthash.subopts[:project_folder], empty_folder = false, overwrite_project = true)
371
+ elsif @opthash.subopts[:overwrite] == false
372
+ puts "\nCreating a new project folder...\n"
373
+ create_project_folder(@opthash.subopts[:project_folder], empty_folder = false, overwrite_project = false)
310
374
  end
311
375
  puts "\nAn example FeatureFile is included: 'example_project.json'. You may place your own FeatureFile alongside the example."
312
376
  puts 'Weather data is provided for the example FeatureFile. Additional weather data files may be downloaded from energyplus.net/weather for free'
313
377
  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"
315
- elsif @user_input[:project_folder] && @user_input[:empty_project_folder]
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)
378
+ puts "We recommend using absolute paths for all commands, for cleaner output\n"
379
+ elsif @opthash.command == 'create' && @opthash.subopts[:project_folder] && @opthash.subopts[:empty] == true
380
+ if @opthash.subopts[:overwrite] == true
381
+ puts "\nOverwriting existing project folder: #{@opthash.subopts[:project_folder]} with an empty folder...\n\n"
382
+ create_project_folder(@opthash.subopts[:project_folder], empty_folder = true, overwrite_project = true)
383
+ elsif @opthash.subopts[:overwrite] == false
384
+ puts "\nCreating a new empty project folder...\n"
385
+ create_project_folder(@opthash.subopts[:project_folder], empty_folder = true, overwrite_project = false)
322
386
  end
323
- puts 'Add your FeatureFile in the Project directory you just created.'
387
+ puts "\nAdd your FeatureFile in the Project directory you just created."
324
388
  puts 'Add your weather data files in the Weather folder. They may be downloaded from energyplus.net/weather for free'
325
389
  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"
390
+ puts "We recommend using absolute paths for all commands, for cleaner output\n"
327
391
  end
328
392
 
329
- if @user_input[:make_scenario_from]
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])
393
+ # Create ScenarioFile from FeatureFile
394
+ if @opthash.command == 'create' && @opthash.subopts[:scenario_file]
395
+ if @opthash.subopts[:single_feature]
396
+ puts "\nBuilding sample ScenarioFiles, assigning mapper classes to Feature ID #{@opthash.subopts[:single_feature]}"
397
+ create_scenario_csv_file(@opthash.subopts[:single_feature])
337
398
  puts "\nDone\n"
338
399
  else
339
- puts "\nBuilding sample ScenarioFiles, assigning mapper classes to each feature from #{@feature_name}..."
400
+ puts "\nBuilding sample ScenarioFiles, assigning mapper classes to each feature from #{@feature_name}"
340
401
  # Skip Feature ID argument if not present
341
402
  create_scenario_csv_file('SKIP')
342
403
  puts "\nDone\n"
343
404
  end
344
405
  end
345
406
 
346
- if @user_input[:run_scenario]
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
407
+ # Create REopt ScenarioFile from existing
408
+ if @opthash.command == 'create' && @opthash.subopts[:reopt_scenario_file]
409
+ puts "\nCreating ScenarioFile with REopt functionality, extending from #{@opthash.subopts[:reopt_scenario_file]}..."
410
+ create_reopt_scenario_file(@opthash.subopts[:reopt_scenario_file])
411
+ puts "\nDone"
412
+ end
413
+
414
+ # Run simulations
415
+ if @opthash.command == 'run' && @opthash.subopts[:scenario] && @opthash.subopts[:feature]
416
+ if @opthash.subopts[:scenario].to_s.include? '-'
417
+ @scenario_folder = @scenario_file_name.split(/\W+/)[0].capitalize.to_s
355
418
  @feature_id = (@feature_name.split(/\W+/)[1]).to_s
356
419
  else
357
420
  @scenario_folder = @scenario_file_name.split('.')[0].capitalize.to_s
358
421
  end
359
- puts "\nSimulating features of '#{@feature_name}' as directed by '#{@user_input[:scenario]}'...\n\n"
422
+ puts "\nSimulating features of '#{@feature_name}' as directed by '#{@scenario_file_name}'...\n\n"
360
423
  scenario_runner = URBANopt::Scenario::ScenarioRunnerOSW.new
361
424
  scenario_runner.run(run_func)
362
425
  puts "\nDone\n"
363
426
  end
364
427
 
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")
428
+ # Post-process the scenario
429
+ if @opthash.command == 'process'
430
+ if @opthash.subopts[:default] == false && @opthash.subopts[:opendss] == false && @opthash.subopts[:reopt_scenario] == false && @opthash.subopts[:reopt_feature] == false
431
+ abort("\nERROR: No valid process type entered. Must enter a valid process type\n")
375
432
  end
376
433
  @scenario_folder = @scenario_file_name.split('.')[0].capitalize.to_s
377
-
378
434
  default_post_processor = URBANopt::Scenario::ScenarioDefaultPostProcessor.new(run_func)
379
435
  scenario_report = default_post_processor.run
380
436
  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?
437
+ if @opthash.subopts[:default] == true
438
+ puts 'Post-processing URBANopt results'
386
439
  puts "\nDone\n"
387
- elsif @user_input[:type].to_s.casecmp('opendss').zero?
440
+ elsif @opthash.subopts[:opendss] == true
388
441
  puts "\nPost-processing OpenDSS results\n"
389
- opendss_folder = File.join(@root_dir, 'run', @scenario_name.split('.')[0], 'opendss')
442
+ opendss_folder = File.join(@root_dir, 'run', @scenario_file_name.split('.')[0], 'opendss')
390
443
  if File.directory?(opendss_folder)
391
- opendss_folder_path, opendss_folder_name = File.split(opendss_folder)
444
+ opendss_folder_name = File.basename(opendss_folder)
392
445
  opendss_post_processor = URBANopt::Scenario::OpenDSSPostProcessor.new(scenario_report, opendss_results_dir_name = opendss_folder_name)
393
446
  opendss_post_processor.run
394
447
  puts "\nDone\n"
395
448
  else
396
449
  abort("\nNo OpenDSS results available in folder '#{opendss_folder}'\n")
397
450
  end
398
- elsif @user_input[:type].to_s.downcase.include?('reopt')
451
+ elsif @opthash.subopts.to_s.include?('reopt')
399
452
  scenario_base = default_post_processor.scenario_base
400
453
  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"
454
+ if @opthash.subopts[:reopt_scenario] == true
455
+ puts "\nPost-processing entire scenario with REopt\n"
405
456
  scenario_report_scenario = reopt_post_processor.run_scenario_report(scenario_report: scenario_report, save_name: 'scenario_optimization')
406
457
  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"
458
+ elsif @opthash.subopts[:reopt_feature] == true
459
+ puts "\nPost-processing each building individually with REopt\n"
410
460
  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
461
  puts "\nDone\n"
412
- else
413
- abort("\nError: did not use type 'reopt-scenario', 'reopt-feature'. Aborting...\n---\n\n")
414
462
  end
415
- else
416
- abort("\nError: did not use type 'default', 'reopt-scenario', 'reopt-feature', or 'opendss'. Aborting...\n---\n\n")
417
463
  end
418
464
  end
419
465
 
420
- if @user_input[:delete_scenario]
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
466
+ # Delete simulations from a scenario
467
+ if @opthash.command == 'delete'
424
468
  scenario_name = @scenario_file_name.split('.')[0]
425
469
  scenario_path = File.absolute_path(@root_dir)
426
470
  scenario_results_dir = File.join(scenario_path, 'run', scenario_name)
@@ -428,9 +472,5 @@ module URBANopt
428
472
  FileUtils.rm_rf(scenario_results_dir)
429
473
  puts "\nDone\n"
430
474
  end
431
-
432
- if @user_input[:version_request]
433
- puts "\nURBANopt CLI version: #{@user_input[:version_request]}\n---\n\n"
434
- end
435
475
  end
436
476
  end