diff --git a/.github/workflows/nightly_ci_build.yml b/.github/workflows/nightly_ci_build.yml index f7b06461..af1bb49d 100644 --- a/.github/workflows/nightly_ci_build.yml +++ b/.github/workflows/nightly_ci_build.yml @@ -1,8 +1,9 @@ name: CLI CI on: - # Run on weeknights, and also whenever a push is made - push: + workflow_dispatch: + # Run on demand and weeknights + # push: schedule: # # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule # # 5:24 am UTC (11:24pm MDT the day before) every weekday night in MDT @@ -16,6 +17,7 @@ env: # If CI is then successful, we have a breaking change in a dependency somewhere. FAVOR_LOCAL_GEMS: true GEM_DEVELOPER_KEY: ${{ secrets.GEM_DEVELOPER_KEY }} + # RUBYLIB: /usr/local/openstudio-3.7.0/Ruby UO_NUM_PARALLEL: 4 # GHA machines have 4 cores. Trying to run more concurrently will slow everything down. # https://github.blog/2024-01-17-github-hosted-runners-double-the-power-for-open-source/ @@ -32,10 +34,12 @@ jobs: steps: - uses: actions/checkout@v4 - name: Change Owner of Container Working Directory - # working dir permissions workaround from https://github.com/actions/runner-images/issues/6775#issuecomment-1377299658 - run: chown root:root . + # working dir permissions workaround from https://github.com/actions/runner-images/issues/6775s#issuecomment-1377299658 + run: | + chown root:root . - name: Install Ruby dependencies run: | + which ruby ruby --version bundle update bundle exec certified-update @@ -60,6 +64,5 @@ jobs: if: failure() with: name: rspec_results - path: | - spec/test_directory**/ + path: artifact.zip retention-days: 7 # save for 1 week then delete diff --git a/CHANGELOG.md b/CHANGELOG.md index 884de0df..69f237d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## Version 0.12.0 +Date Range: 02/08/2024 - 05/15/2024 + +### Exciting New Features 🎉 +* Update CLI sub-commands to be consistent across commands by @vtnate in https://github.com/urbanopt/urbanopt-cli/pull/455 +* Update commands, paths, and example files for Reopt v3 by @vtnate in https://github.com/urbanopt/urbanopt-cli/pull/463 +### Other Changes +* Update installer 0.11.1 by @tijcolem in https://github.com/urbanopt/urbanopt-cli/pull/452 +* adding rescue blocks to catch errors by @kflemin in https://github.com/urbanopt/urbanopt-cli/pull/454 +* update error handling for installing python and dependencies by @kflemin in https://github.com/urbanopt/urbanopt-cli/pull/458 + +**Full Changelog**: https://github.com/urbanopt/urbanopt-cli/compare/v0.11.1...v0.12.0 + ## Version 0.11.1 Date Range: 01/26/2024 - 02/08/2024 diff --git a/CMakeLists.txt b/CMakeLists.txt index bb3af474..983deda3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.10.2) cmake_policy(SET CMP0048 NEW) -project(URBANoptCLI VERSION 0.11.0) +project(URBANoptCLI VERSION 0.11.1) include(FindOpenStudioSDK.cmake) @@ -89,16 +89,21 @@ option(BUILD_PACKAGE "Build package" OFF) # need to update the MD5sum for each platform and url below if(UNIX) if(APPLE) - set(URBANOPT_CLI_GEMS_ZIP_FILENAME "urbanopt-cli-gems-20240202-darwin.tar.gz") - set(URBANOPT_CLI_GEMS_ZIP_EXPECTED_MD5 "27dad609451f5b0e7835efbcb8e362b7") + set(URBANOPT_CLI_GEMS_ZIP_FILENAME "urbanopt-cli-gems-20240220-darwin.tar.gz") + set(URBANOPT_CLI_GEMS_ZIP_EXPECTED_MD5 "1f735d71eab98df9be7b72f9d5d8a940") else() - set(URBANOPT_CLI_GEMS_ZIP_FILENAME "urbanopt-cli-gems-20240202-linux.tar.gz") - set(URBANOPT_CLI_GEMS_ZIP_EXPECTED_MD5 "2182c1ca7bcd85505db4700f4937d181") + if (ARCH MATCHES "arm64") + set(URBANOPT_CLI_GEMS_ZIP_FILENAME "urbanopt-cli-gems-20240220-linux-arm.tar.gz") + set(URBANOPT_CLI_GEMS_ZIP_EXPECTED_MD5 "ceb41bdd108a77bc43c6d260725a399f") + else() + set(URBANOPT_CLI_GEMS_ZIP_FILENAME "urbanopt-cli-gems-20240220-linux.tar.gz") + set(URBANOPT_CLI_GEMS_ZIP_EXPECTED_MD5 "715c40a3bf59d539a18a03ad3932ca67") + endif() endif() elseif(WIN32) if(CMAKE_CL_64) - set(URBANOPT_CLI_GEMS_ZIP_FILENAME "urbanopt-cli-gems-20240202-windows.tar.gz") - set(URBANOPT_CLI_GEMS_ZIP_EXPECTED_MD5 "7a7cc4f928646363bd57b41e10caebca") + set(URBANOPT_CLI_GEMS_ZIP_FILENAME "urbanopt-cli-gems-20240220-windows.tar.gz") + set(URBANOPT_CLI_GEMS_ZIP_EXPECTED_MD5 "944c39e178651173a03efae1d99713e3") endif() endif() diff --git a/FindOpenStudioSDK.cmake b/FindOpenStudioSDK.cmake index 537e845f..1ef76711 100644 --- a/FindOpenStudioSDK.cmake +++ b/FindOpenStudioSDK.cmake @@ -24,6 +24,11 @@ else() set(OPENSTUDIO_EXT "tar.gz") elseif(UNIX) if(LSB_RELEASE_VERSION_SHORT MATCHES "20.04") + if (ARCH MATCHES "arm64") + set(OPENSTUDIO_EXPECTED_HASH d98b3d3d133a7c5bc1b9581553d23811) + else() + set(OPENSTUDIO_EXPECTED_HASH 1e48f36417d3d118d6a10f1a7d61f46e) + endif() set(OPENSTUDIO_EXPECTED_HASH 5c006e5e66e5e859206a63626ea2966f) set(OPENSTUDIO_PLATFORM "Ubuntu-20.04-x86_64") else() # Assumes 20.04 diff --git a/Gemfile b/Gemfile index b904ca83..de0d0f4d 100644 --- a/Gemfile +++ b/Gemfile @@ -40,16 +40,16 @@ gem 'parser', '3.2.2.2' # if allow_local && File.exist?('../urbanopt-reopt-gem') # gem 'urbanopt-reopt', path: '../urbanopt-reopt-gem' # elsif allow_local -# gem 'urbanopt-reopt', github: 'URBANopt/urbanopt-reopt-gem', branch: 'develop' +# gem 'urbanopt-reopt', github: 'URBANopt/urbanopt-reopt-gem', branch: 'develop' # end # if allow_local && File.exist?('../urbanopt-reporting-gem') # gem 'urbanopt-reporting', path: '../urbanopt-reporting-gem' # elsif allow_local -# gem 'urbanopt-reporting', github: 'URBANopt/urbanopt-reporting-gem', branch: 'develop' +# gem 'urbanopt-reporting', github: 'URBANopt/urbanopt-reporting-gem', branch: 'develop' # end -#if allow_local && File.exist?('../urbanopt-rnm-us-gem') +# if allow_local && File.exist?('../urbanopt-rnm-us-gem') # gem 'urbanopt-rnm-us', path: '../urbanopt-rnm-us-gem' # elsif allow_local # gem 'urbanopt-rnm-us', github: 'URBANopt/urbanopt-rnm-us-gem', branch: 'develop' diff --git a/bin/console b/bin/console index f16b5f7c..76bd44a1 100755 --- a/bin/console +++ b/bin/console @@ -3,12 +3,5 @@ require 'bundler/setup' require 'uo_cli' -# You can add fixtures and/or initialization code here to make experimenting -# with your gem easier. You can also use a different console, if you like. - -# (If you use this, don't forget to add pry to your Gemfile!) -# require "pry" -# Pry.start - require 'irb' IRB.start(__FILE__) diff --git a/example_files/Gemfile b/example_files/Gemfile index 93e63e65..6f25cbec 100644 --- a/example_files/Gemfile +++ b/example_files/Gemfile @@ -51,7 +51,7 @@ if allow_local && File.exist?('../openstudio-load-flexibility-measures-gem') elsif allow_local gem 'openstudio-load-flexibility-measures', github: 'NREL/openstudio-load-flexibility-measures-gem', branch: 'develop' else - gem 'openstudio-load-flexibility-measures', '~> 0.8.0' + gem 'openstudio-load-flexibility-measures', '~> 0.8.0' end if allow_local && File.exist?('../openstudio-ee-gem') @@ -78,7 +78,7 @@ else gem 'openstudio-geb', '~> 0.4.0' end -if allow_local && File.exists?('../urbanopt-geojson-gem') +if allow_local && File.exist?('../urbanopt-geojson-gem') gem 'urbanopt-geojson', path: '../../urbanopt-geojson-gem' elsif allow_local gem 'urbanopt-geojson', github: 'URBANopt/urbanopt-geojson-gem', branch: 'develop' @@ -94,5 +94,5 @@ if allow_local && File.exist?('../urbanopt-reporting-gem') elsif allow_local gem 'urbanopt-reporting', github: 'URBANopt/urbanopt-reporting-gem', branch: 'develop' else - gem 'urbanopt-reporting', '~> 0.9.1' + gem 'urbanopt-reporting', '~> 0.10.0' end diff --git a/example_files/mappers/Baseline.rb b/example_files/mappers/Baseline.rb index 5ce00973..72fe9f79 100644 --- a/example_files/mappers/Baseline.rb +++ b/example_files/mappers/Baseline.rb @@ -1105,6 +1105,6 @@ def time_mapping(time) return osw end - end # end class + end end end diff --git a/example_files/mappers/residential/template/util.rb b/example_files/mappers/residential/template/util.rb index 44addb19..d40007f3 100644 --- a/example_files/mappers/residential/template/util.rb +++ b/example_files/mappers/residential/template/util.rb @@ -3,7 +3,6 @@ # See also https://github.com/urbanopt/urbanopt-cli/blob/develop/LICENSE.md # ********************************************************************************* - def residential_template(args, template, climate_zone) '''Assign arguments from tsv files.''' @@ -44,7 +43,6 @@ def residential_template(args, template, climate_zone) { args[:heating_system_type] => 'iecc/heating_system.tsv', args[:cooling_system_type] => 'iecc/cooling_system.tsv', args[:heat_pump_type] => 'iecc/heat_pump.tsv' }.each do |type, path| - if type != 'none' filepath = File.join(File.dirname(__FILE__), path) lookup_tsv = get_lookup_tsv(args, filepath) diff --git a/example_files/mappers/residential/util.rb b/example_files/mappers/residential/util.rb index 31aa7317..110595ab 100644 --- a/example_files/mappers/residential/util.rb +++ b/example_files/mappers/residential/util.rb @@ -3,7 +3,6 @@ # See also https://github.com/urbanopt/urbanopt-cli/blob/develop/LICENSE.md # ********************************************************************************* - def residential(scenario, feature, args, building_type) '''Assign arguments from geojson file.''' diff --git a/example_files/measures/BuildResidentialModel/measure.rb b/example_files/measures/BuildResidentialModel/measure.rb index ac6b0965..3edfd6f2 100644 --- a/example_files/measures/BuildResidentialModel/measure.rb +++ b/example_files/measures/BuildResidentialModel/measure.rb @@ -125,7 +125,7 @@ def run(model, runner, user_arguments) standards_number_of_living_units = units.size else - xml_building_folder = "xml_building" + xml_building_folder = 'xml_building' hpxml_dir = File.join(File.dirname(__FILE__), "../../#{xml_building_folder}/#{args[:hpxml_dir]}") if !File.exist?(hpxml_dir) @@ -158,7 +158,6 @@ def run(model, runner, user_arguments) hpxml_path = File.expand_path(args[:hpxml_path]) units.each_with_index do |unit, unit_num| - measures = {} if !unit.key?('hpxml_path') # create a single new HPXML file describing all dwelling units of the feature @@ -167,7 +166,7 @@ def run(model, runner, user_arguments) full_measure_path = File.join(measures_dir, measure_subdir, 'measure.rb') check_file_exists(full_measure_path, runner) - measure_args = args.clone.collect { |k, v| [k.to_s, v] }.to_h + measure_args = args.clone.transform_keys(&:to_s) measure_args['hpxml_path'] = hpxml_path if unit_num > 0 measure_args['existing_hpxml_path'] = hpxml_path @@ -208,7 +207,7 @@ def run(model, runner, user_arguments) FileUtils.cp(File.expand_path(unit['hpxml_path']), hpxml_path) end - end # end units.each_with_index do |unit, unit_num| + end # call BuildResidentialScheduleFile / HPXMLtoOpenStudio after HPXML file is created measures = {} diff --git a/example_files/measures/BuildResidentialModel/tests/test_build_residential_model.rb b/example_files/measures/BuildResidentialModel/tests/test_build_residential_model.rb index 09a836ea..79ac1973 100644 --- a/example_files/measures/BuildResidentialModel/tests/test_build_residential_model.rb +++ b/example_files/measures/BuildResidentialModel/tests/test_build_residential_model.rb @@ -10,7 +10,7 @@ require_relative '../../../mappers/residential/template//util' require 'openstudio' require 'openstudio/measure/ShowRunnerOutput' -require_relative '../measure.rb' +require_relative '../measure' require 'csv' class BuildResidentialModelTest < Minitest::Test @@ -21,14 +21,14 @@ def setup FileUtils.mkdir_p(@run_path) @args = {} - _initialize_arguments() + _initialize_arguments end def teardown FileUtils.rm_rf(@run_path) end - def _initialize_arguments() + def _initialize_arguments # BuildResidentialModel arguments @args[:hpxml_path] = @hpxml_path @args[:output_dir] = @run_path @@ -77,7 +77,7 @@ def test_hpxml_dir @args[:geometry_building_num_units] = 4 - _test_measure() + _test_measure end def test_schedules_type @@ -88,8 +88,8 @@ def test_schedules_type schedules_types.each do |schedules_type| @args[:schedules_type] = schedules_type - _apply_residential() - _test_measure() + _apply_residential + _test_measure end end @@ -118,7 +118,7 @@ def test_feature_building_types_num_units_and_stories end end - _apply_residential() + _apply_residential _test_measure(expected_errors: expected_errors) end end @@ -157,7 +157,7 @@ def test_feature_building_foundation_and_attic_types_and_num_stories end end - _apply_residential() + _apply_residential _test_measure(expected_errors: expected_errors) end end @@ -182,8 +182,8 @@ def test_feature_building_types_num_units_and_bedrooms @args[:geometry_building_num_units] = feature_number_of_residential_units @number_of_bedrooms = feature_number_of_bedrooms - _apply_residential() - _test_measure() + _apply_residential + _test_measure end end end @@ -210,8 +210,8 @@ def test_feature_building_occ_calc_types_num_occupants_and_units @args[:geometry_building_num_units] = feature_number_of_residential_units @number_of_occupants = feature_number_of_occupants - _apply_residential() - _test_measure() + _apply_residential + _test_measure end end end @@ -244,7 +244,7 @@ def test_feature_building_foundation_types_and_garages expected_errors = ['Conditioned basement/crawlspace foundation type for apartment units is not currently supported.'] end - _apply_residential() + _apply_residential _test_measure(expected_errors: expected_errors) end end @@ -264,8 +264,8 @@ def test_hvac_system_and_fuel_types @system_type = feature_system_type @heating_system_fuel_type = feature_heating_system_fuel_type - _apply_residential() - _test_measure() + _apply_residential + _test_measure end end end @@ -278,17 +278,17 @@ def test_residential_template_types feature_templates.each do |feature_template| @args = {} - _initialize_arguments() + _initialize_arguments @template = feature_template - _apply_residential() - _apply_residential_template() - _test_measure() + _apply_residential + _apply_residential_template + _test_measure end end - def _apply_residential() + def _apply_residential residential_simulation(@args, @timestep, @run_period, @calendar_year, @weather_filename) residential_geometry_unit(@args, @building_type, @floor_area, @number_of_bedrooms, @geometry_unit_orientation, @geometry_unit_aspect_ratio, @occupancy_calculation_type, @number_of_occupants, @maximum_roof_height) residential_geometry_foundation(@args, @foundation_type) @@ -299,7 +299,7 @@ def _apply_residential() residential_appliances(@args) end - def _apply_residential_template() + def _apply_residential_template residential_template(@args, @template, @climate_zone) end @@ -317,7 +317,7 @@ def _test_measure(expected_errors: []) # populate argument with specified hash value if specified arguments.each do |arg| temp_arg_var = arg.clone - if @args.has_key?(arg.name.to_sym) + if @args.key?(arg.name.to_sym) assert(temp_arg_var.setValue(@args[arg.name.to_sym])) end argument_map[arg.name] = temp_arg_var @@ -332,7 +332,7 @@ def _test_measure(expected_errors: []) # show_output(result) unless result.value.valueName == 'Fail' assert_equal('Fail', result.value.valueName) - error_msgs = result.errors.map { |x| x.logMessage } + error_msgs = result.errors.map(&:logMessage) expected_errors.each do |expected_error| assert_includes(error_msgs, expected_error) end diff --git a/example_files/python_deps/dependencies.json b/example_files/python_deps/dependencies.json index 0899e1d9..cfe2595d 100644 --- a/example_files/python_deps/dependencies.json +++ b/example_files/python_deps/dependencies.json @@ -1,6 +1,6 @@ [ - { "name": "ThermalNetwork", "version": "0.2.3"}, - { "name": "urbanopt-ditto-reader", "version": "0.6.3"}, - { "name": "NREL-disco", "version": "0.5.0"}, - { "name": "geojson-modelica-translator", "version": "0.6.0"} + { "name": "ThermalNetwork", "version": "0.2.5"}, + { "name": "urbanopt-ditto-reader", "version": "0.6.4"}, + { "name": "NREL-disco", "version": "0.5.1"}, + { "name": "geojson-modelica-translator", "version": "0.7.0"} ] diff --git a/example_files/python_deps/install_python.ps1 b/example_files/python_deps/install_python.ps1 index 0e325f9c..c648b103 100644 --- a/example_files/python_deps/install_python.ps1 +++ b/example_files/python_deps/install_python.ps1 @@ -39,7 +39,7 @@ function Get-Python { Remove-Item $path } if ($result.ExitCode -ne 0) { - $msg = "Failed to run Python installer: ExitCode=${result.ExitCode}" + $msg = "Failed to run Python installer: ExitCode=${result.ExitCode}" Write-Error $msg exit $result.ExitCode } diff --git a/example_files/reopt/base_assumptions.json b/example_files/reopt/base_assumptions.json index bf7ead14..7a2dc430 100644 --- a/example_files/reopt/base_assumptions.json +++ b/example_files/reopt/base_assumptions.json @@ -1,148 +1,125 @@ { - "Scenario": { - "off_grid_flag": false, - "Site": { - "renewable_electricity_min_pct": 0, - "renewable_electricity_max_pct": 1, - "Financial": { - "om_cost_escalation_pct": 0.025, - "escalation_pct": 0.023, - "offtaker_tax_pct": 0.26, - "offtaker_discount_pct": 0.083, - "analysis_years": 25, - "value_of_lost_load_us_dollars_per_kwh": 100, - "microgrid_upgrade_cost_pct": 0.3 - }, - "LoadProfile": { - "year": 2018, - "loads_kw_is_net": true, - "critical_loads_kw_is_net": false, - "critical_load_pct": 0.5, - "outage_is_major_event": true - }, - "ElectricTariff": { - "add_blended_rates_to_urdb_rate": false, - "net_metering_limit_kw": 0, - "interconnection_limit_kw": 100000000.0, - "blended_monthly_demand_charges_us_dollars_per_kw": [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10], - "blended_monthly_rates_us_dollars_per_kwh": [0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13] - }, - "Wind": { - "min_kw": 0, - "max_kw": 0, - "installed_cost_us_dollars_per_kw": 3013, - "om_cost_us_dollars_per_kw": 40, - "macrs_option_years": 5, - "macrs_bonus_pct": 1, - "macrs_itc_reduction": 0.5, - "federal_itc_pct": 0.26, - "state_ibi_pct": 0, - "state_ibi_max_us_dollars": 10000000000.0, - "utility_ibi_pct": 0, - "utility_ibi_max_us_dollars": 10000000000.0, - "federal_rebate_us_dollars_per_kw": 0, - "state_rebate_us_dollars_per_kw": 0, - "state_rebate_max_us_dollars": 10000000000.0, - "utility_rebate_us_dollars_per_kw": 0, - "utility_rebate_max_us_dollars": 10000000000.0, - "pbi_us_dollars_per_kwh": 0, - "pbi_max_us_dollars": 1000000000.0, - "pbi_years": 1, - "pbi_system_max_kw": 1000000000.0 - }, - "PV": { - "pv_name": "Roof - South Face", - "location":"roof", - "existing_kw": 0, - "min_kw": 0, - "max_kw": 1000000000.0, - "installed_cost_us_dollars_per_kw": 1600, - "om_cost_us_dollars_per_kw": 16, - "macrs_option_years": 5, - "macrs_bonus_pct": 1, - "macrs_itc_reduction": 0.5, - "federal_itc_pct": 0.26, - "state_ibi_pct": 0, - "state_ibi_max_us_dollars": 10000000000.0, - "utility_ibi_pct": 0, - "utility_ibi_max_us_dollars": 10000000000.0, - "federal_rebate_us_dollars_per_kw": 0, - "state_rebate_us_dollars_per_kw": 0, - "state_rebate_max_us_dollars": 10000000000.0, - "utility_rebate_us_dollars_per_kw": 0, - "utility_rebate_max_us_dollars": 10000000000.0, - "pbi_us_dollars_per_kwh": 0, - "pbi_max_us_dollars": 1000000000.0, - "pbi_years": 1, - "pbi_system_max_kw": 1000000000.0, - "degradation_pct": 0.005, - "azimuth": 180, - "losses": 0.14, - "array_type": 1, - "module_type": 0, - "gcr": 0.99, - "dc_ac_ratio": 1.2, - "inv_eff": 0.96, - "radius": 0, - "tilt": 0.537 - }, - "Storage": { - "min_kw": 0, - "max_kw": 1000000, - "min_kwh": 0, - "max_kwh": 1000000, - "internal_efficiency_pct": 0.975, - "inverter_efficiency_pct": 0.96, - "rectifier_efficiency_pct": 0.96, - "soc_min_pct": 0.2, - "soc_init_pct": 0.5, - "canGridCharge": true, - "installed_cost_us_dollars_per_kw": 840, - "installed_cost_us_dollars_per_kwh": 420, - "replace_cost_us_dollars_per_kw": 410, - "replace_cost_us_dollars_per_kwh": 200, - "inverter_replacement_year": 10, - "battery_replacement_year": 10, - "macrs_option_years": 7, - "macrs_bonus_pct": 1, - "macrs_itc_reduction": 0.5, - "total_itc_pct": 0.0, - "total_rebate_us_dollars_per_kw": 0 - }, - "Generator": { - "existing_kw": 0, - "min_kw": 0, - "max_kw": 1000000000.0, - "installed_cost_us_dollars_per_kw": 500, - "om_cost_us_dollars_per_kw": 10, - "om_cost_us_dollars_per_kwh": 0.0, - "diesel_fuel_cost_us_dollars_per_gallon": 3, - "fuel_slope_gal_per_kwh": 0.076, - "fuel_intercept_gal_per_hr": 0, - "fuel_avail_gal": 660, - "min_turn_down_pct": 0, - "generator_only_runs_during_grid_outage": true, - "generator_sells_energy_back_to_grid": false, - "macrs_option_years": 0, - "macrs_bonus_pct": 1, - "macrs_itc_reduction": 0, - "federal_itc_pct": 0, - "state_ibi_pct": 0, - "state_ibi_max_us_dollars": 0, - "utility_ibi_pct": 0, - "utility_ibi_max_us_dollars": 0, - "federal_rebate_us_dollars_per_kw": 0, - "state_rebate_us_dollars_per_kw": 0, - "state_rebate_max_us_dollars": 0, - "utility_rebate_us_dollars_per_kw": 0, - "utility_rebate_max_us_dollars": 0, - "pbi_us_dollars_per_kwh": 0, - "pbi_max_us_dollars": 0, - "pbi_years": 0, - "pbi_system_max_kw": 0 - } - }, + "Settings": { "timeout_seconds": 295, - "time_steps_per_hour": 1 + "time_steps_per_hour": 1, + "off_grid_flag": false + }, + "Site": { + "roof_squarefeet": null, + "renewable_electricity_min_fraction": 0, + "renewable_electricity_max_fraction": 1 + }, + "Financial": { + "elec_cost_escalation_rate_fraction": 0.026, + "offtaker_discount_rate_fraction": 0.081, + "value_of_lost_load_per_kwh": 100.0, + "analysis_years": 20, + "microgrid_upgrade_cost_fraction": 0.3, + "offtaker_tax_rate_fraction": 0.26, + "om_cost_escalation_rate_fraction": 0.025 + }, + "ElectricTariff": { + "add_monthly_rates_to_urdb_rate": false, + "urdb_label": "63d2f36655296095a0092a6e" + }, + "ElectricUtility": { + "interconnection_limit_kw": 100000000.0, + "net_metering_limit_kw": 0.0 + }, + "ElectricLoad": { + "critical_loads_kw_is_net": false, + "critical_load_fraction": 0, + "loads_kw_is_net": true + }, + "PV": { + "name": "Roof - South Face", + "production_incentive_years": 1.0, + "location":"roof", + "macrs_bonus_fraction": 0.0, + "max_kw": 1000000000.0, + "production_incentive_max_benefit": 1000000000.0, + "radius": 0.0, + "state_ibi_fraction": 0.0, + "utility_rebate_max": 10000000000.0, + "installed_cost_per_kw": 2000.0, + "utility_ibi_max": 10000000000.0, + "tilt": 10.0, + "federal_rebate_per_kw": 0.0, + "gcr": 0.4, + "production_incentive_max_kw": 1000000000.0, + "utility_ibi_fraction": 0.0, + "state_ibi_max": 10000000000.0, + "state_rebate_per_kw": 0.0, + "macrs_option_years": 5, + "state_rebate_max": 10000000000.0, + "dc_ac_ratio": 1.1, + "federal_itc_fraction": 0.3, + "production_incentive_per_kwh": 0.0, + "module_type": 0, + "array_type": 1, + "existing_kw": 0.0, + "om_cost_per_kw": 16.0, + "utility_rebate_per_kw": 0.0, + "min_kw": 0.0, + "losses": 0.14, + "macrs_itc_reduction": 0.5, + "degradation_fraction": 0.005, + "inv_eff": 0.96, + "azimuth": 180.0 + }, + "Generator": { + "production_incentive_years": 0.0, + "macrs_bonus_fraction": 0.0, + "om_cost_per_kwh": 20.0, + "max_kw": 1000000000.0, + "production_incentive_max_benefit": 0.0, + "state_ibi_fraction": 0.0, + "fuel_intercept_gal_per_hr": 0.0125, + "generator_only_runs_during_grid_outage": true, + "state_rebate_per_kw": 0.0, + "installed_cost_per_kw": 2500.0, + "utility_ibi_max": 0.0, + "fuel_avail_gal": 0.0, + "min_turn_down_fraction": 0.3, + "production_incentive_max_kw": 0.0, + "utility_ibi_fraction": 0.0, + "state_ibi_max": 0.0, + "fuel_cost_per_gallon": 20.0, + "fuel_slope_gal_per_kwh": 0.068, + "utility_rebate_max": 0.0, + "macrs_option_years": 0, + "state_rebate_max": 0.0, + "federal_itc_fraction": 0.0, + "existing_kw": 0.0, + "production_incentive_per_kwh": 0.0, + "om_cost_per_kw": 50.0, + "utility_rebate_per_kw": 0.0, + "min_kw": 0.0, + "macrs_itc_reduction": 0.0, + "federal_rebate_per_kw": 0.0, + "generator_sells_energy_back_to_grid": false + }, + "Storage": { + "max_kwh": 1000000.0, + "rectifier_efficiency_fraction": 0.96, + "total_itc_fraction": 0.0, + "min_kw": 0.0, + "max_kw": 1000000.0, + "replace_cost_per_kw": 460.0, + "replace_cost_per_kwh": 230.0, + "min_kwh": 0.0, + "installed_cost_per_kw": 1000.0, + "total_rebate_per_kw": 0, + "installed_cost_per_kwh": 500.0, + "inverter_efficiency_fraction": 0.96, + "macrs_itc_reduction": 0.5, + "canGridCharge": true, + "macrs_bonus_fraction": 0.0, + "battery_replacement_year": 10, + "macrs_option_years": 7, + "internal_efficiency_fraction": 0.975, + "soc_min_fraction": 0.2, + "soc_init_fraction": 0.5, + "inverter_replacement_year": 10 } } diff --git a/example_files/reopt/multiPV_assumptions.json b/example_files/reopt/multiPV_assumptions.json index 7dc9058f..3b19c67e 100644 --- a/example_files/reopt/multiPV_assumptions.json +++ b/example_files/reopt/multiPV_assumptions.json @@ -1,184 +1,164 @@ { - "Scenario": { - "off_grid_flag": false, - "Site": { - "renewable_electricity_min_pct": 0, - "renewable_electricity_max_pct": 1, - "Financial": { - "om_cost_escalation_pct": 0.025, - "escalation_pct": 0.023, - "offtaker_tax_pct": 0.26, - "offtaker_discount_pct": 0.083, - "analysis_years": 25, - "value_of_lost_load_us_dollars_per_kwh": 100, - "microgrid_upgrade_cost_pct": 0.3 - }, - "LoadProfile": { - "year": 2018, - "loads_kw_is_net": true, - "critical_loads_kw_is_net": false, - "critical_load_pct": 0.5, - "outage_is_major_event": true - }, - "ElectricTariff": { - "add_blended_rates_to_urdb_rate": false, - "net_metering_limit_kw": 0, - "interconnection_limit_kw": 100000000.0, - "blended_monthly_demand_charges_us_dollars_per_kw": [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10], - "blended_monthly_rates_us_dollars_per_kwh": [0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13] - }, - "Wind": { - "min_kw": 0, - "max_kw": 0, - "installed_cost_us_dollars_per_kw": 3013, - "om_cost_us_dollars_per_kw": 40, - "macrs_option_years": 5, - "macrs_bonus_pct": 1, - "macrs_itc_reduction": 0.5, - "federal_itc_pct": 0.26, - "state_ibi_pct": 0, - "state_ibi_max_us_dollars": 10000000000.0, - "utility_ibi_pct": 0, - "utility_ibi_max_us_dollars": 10000000000.0, - "federal_rebate_us_dollars_per_kw": 0, - "state_rebate_us_dollars_per_kw": 0, - "state_rebate_max_us_dollars": 10000000000.0, - "utility_rebate_us_dollars_per_kw": 0, - "utility_rebate_max_us_dollars": 10000000000.0, - "pbi_us_dollars_per_kwh": 0, - "pbi_max_us_dollars": 1000000000.0, - "pbi_years": 1, - "pbi_system_max_kw": 1000000000.0 - }, - "PV": [{ - "pv_name": "Roof - South Face", - "location":"roof", - "existing_kw": 0, - "min_kw": 0, - "max_kw": 1000000000.0, - "installed_cost_us_dollars_per_kw": 1600, - "om_cost_us_dollars_per_kw": 16, - "macrs_option_years": 5, - "macrs_bonus_pct": 1, - "macrs_itc_reduction": 0.5, - "federal_itc_pct": 0.26, - "state_ibi_pct": 0, - "state_ibi_max_us_dollars": 10000000000.0, - "utility_ibi_pct": 0, - "utility_ibi_max_us_dollars": 10000000000.0, - "federal_rebate_us_dollars_per_kw": 0, - "state_rebate_us_dollars_per_kw": 0, - "state_rebate_max_us_dollars": 10000000000.0, - "utility_rebate_us_dollars_per_kw": 0, - "utility_rebate_max_us_dollars": 10000000000.0, - "pbi_us_dollars_per_kwh": 0, - "pbi_max_us_dollars": 1000000000.0, - "pbi_years": 1, - "pbi_system_max_kw": 1000000000.0, - "degradation_pct": 0.005, - "azimuth": 180, - "losses": 0.14, - "array_type": 1, - "module_type": 0, - "gcr": 0.99, - "dc_ac_ratio": 1.2, - "inv_eff": 0.96, - "radius": 0, - "tilt": 0.537 - }, - { - "pv_name": "Groundmount", - "location":"ground", - "existing_kw": 0, - "min_kw": 0, - "max_kw": 1000000000.0, - "installed_cost_us_dollars_per_kw": 2200, - "om_cost_us_dollars_per_kw": 16, - "macrs_option_years": 5, - "macrs_bonus_pct": 1, - "macrs_itc_reduction": 0.5, - "federal_itc_pct": 0.26, - "state_ibi_pct": 0, - "state_ibi_max_us_dollars": 10000000000.0, - "utility_ibi_pct": 0, - "utility_ibi_max_us_dollars": 10000000000.0, - "federal_rebate_us_dollars_per_kw": 0, - "state_rebate_us_dollars_per_kw": 0, - "state_rebate_max_us_dollars": 10000000000.0, - "utility_rebate_us_dollars_per_kw": 0, - "utility_rebate_max_us_dollars": 10000000000.0, - "pbi_us_dollars_per_kwh": 0, - "pbi_max_us_dollars": 1000000000.0, - "pbi_years": 1, - "pbi_system_max_kw": 1000000000.0, - "degradation_pct": 0.005, - "azimuth": 180, - "losses": 0.14, - "array_type": 0, - "module_type": 0, - "gcr": 0.99, - "dc_ac_ratio": 1.2, - "inv_eff": 0.96, - "radius": 0, - "tilt": 0.537 - }], - "Storage": { - "min_kw": 0, - "max_kw": 1000000, - "min_kwh": 0, - "max_kwh": 1000000, - "internal_efficiency_pct": 0.975, - "inverter_efficiency_pct": 0.96, - "rectifier_efficiency_pct": 0.96, - "soc_min_pct": 0.2, - "soc_init_pct": 0.5, - "canGridCharge": true, - "installed_cost_us_dollars_per_kw": 840, - "installed_cost_us_dollars_per_kwh": 420, - "replace_cost_us_dollars_per_kw": 410, - "replace_cost_us_dollars_per_kwh": 200, - "inverter_replacement_year": 10, - "battery_replacement_year": 10, - "macrs_option_years": 7, - "macrs_bonus_pct": 1, - "macrs_itc_reduction": 0.5, - "total_itc_pct": 0.0, - "total_rebate_us_dollars_per_kw": 0 - }, - "Generator": { - "existing_kw": 0, - "min_kw": 0, - "max_kw": 1000000000.0, - "installed_cost_us_dollars_per_kw": 500, - "om_cost_us_dollars_per_kw": 10, - "om_cost_us_dollars_per_kwh": 0.0, - "diesel_fuel_cost_us_dollars_per_gallon": 3, - "fuel_slope_gal_per_kwh": 0.076, - "fuel_intercept_gal_per_hr": 0, - "fuel_avail_gal": 660, - "min_turn_down_pct": 0, - "generator_only_runs_during_grid_outage": true, - "generator_sells_energy_back_to_grid": false, - "macrs_option_years": 0, - "macrs_bonus_pct": 1, - "macrs_itc_reduction": 0, - "federal_itc_pct": 0, - "state_ibi_pct": 0, - "state_ibi_max_us_dollars": 0, - "utility_ibi_pct": 0, - "utility_ibi_max_us_dollars": 0, - "federal_rebate_us_dollars_per_kw": 0, - "state_rebate_us_dollars_per_kw": 0, - "state_rebate_max_us_dollars": 0, - "utility_rebate_us_dollars_per_kw": 0, - "utility_rebate_max_us_dollars": 0, - "pbi_us_dollars_per_kwh": 0, - "pbi_max_us_dollars": 0, - "pbi_years": 0, - "pbi_system_max_kw": 0 - } - }, + "Settings": { "timeout_seconds": 295, - "time_steps_per_hour": 1 + "time_steps_per_hour": 1, + "off_grid_flag": false + }, + "Site": { + "roof_squarefeet": null, + "renewable_electricity_min_fraction": 0, + "renewable_electricity_max_fraction": 1 + }, + "Financial": { + "elec_cost_escalation_rate_fraction": 0.026, + "offtaker_discount_rate_fraction": 0.081, + "value_of_lost_load_per_kwh": 100.0, + "analysis_years": 20, + "microgrid_upgrade_cost_fraction": 0.3, + "offtaker_tax_rate_fraction": 0.26, + "om_cost_escalation_rate_fraction": 0.025 + }, + "ElectricTariff": { + "add_monthly_rates_to_urdb_rate": false, + "urdb_label": "63d2f36655296095a0092a6e" + }, + "ElectricUtility": { + "interconnection_limit_kw": 100000000.0, + "net_metering_limit_kw": 0.0 + }, + "ElectricLoad": { + "year": 2018, + "critical_loads_kw_is_net": false, + "critical_load_fraction": 0, + "loads_kw_is_net": true + }, + "PV": [ + { + "name": "Roof - South Face", + "location":"roof", + "production_incentive_years": 1.0, + "macrs_bonus_fraction": 0.0, + "max_kw": 1000000000.0, + "production_incentive_max_benefit": 1000000000.0, + "radius": 0.0, + "state_ibi_fraction": 0.0, + "utility_rebate_max": 10000000000.0, + "installed_cost_per_kw": 2000.0, + "utility_ibi_max": 10000000000.0, + "tilt": 10.0, + "federal_rebate_per_kw": 0.0, + "gcr": 0.4, + "production_incentive_max_kw": 1000000000.0, + "utility_ibi_fraction": 0.0, + "state_ibi_max": 10000000000.0, + "state_rebate_per_kw": 0.0, + "macrs_option_years": 5, + "state_rebate_max": 10000000000.0, + "dc_ac_ratio": 1.1, + "federal_itc_fraction": 0.3, + "production_incentive_per_kwh": 0.0, + "module_type": 0, + "array_type": 1, + "existing_kw": 0.0, + "om_cost_per_kw": 16.0, + "utility_rebate_per_kw": 0.0, + "min_kw": 0.0, + "losses": 0.14, + "macrs_itc_reduction": 0.5, + "degradation_fraction": 0.005, + "inv_eff": 0.96, + "azimuth": 180.0 + }, + { + "name": "Groundmount", + "location":"ground", + "production_incentive_years": 1.0, + "macrs_bonus_fraction": 0.0, + "max_kw": 1000000000.0, + "production_incentive_max_benefit": 1000000000.0, + "radius": 0.0, + "state_ibi_fraction": 0.0, + "utility_rebate_max": 10000000000.0, + "installed_cost_per_kw": 2000.0, + "utility_ibi_max": 10000000000.0, + "tilt": 10.0, + "federal_rebate_per_kw": 0.0, + "gcr": 0.4, + "production_incentive_max_kw": 1000000000.0, + "utility_ibi_fraction": 0.0, + "state_ibi_max": 10000000000.0, + "state_rebate_per_kw": 0.0, + "macrs_option_years": 5, + "state_rebate_max": 10000000000.0, + "dc_ac_ratio": 1.1, + "federal_itc_fraction": 0.3, + "production_incentive_per_kwh": 0.0, + "module_type": 0, + "array_type": 1, + "existing_kw": 0.0, + "om_cost_per_kw": 16.0, + "utility_rebate_per_kw": 0.0, + "min_kw": 0.0, + "losses": 0.14, + "macrs_itc_reduction": 0.5, + "degradation_fraction": 0.005, + "inv_eff": 0.96, + "azimuth": 180.0 + } + ], + "Generator": { + "production_incentive_years": 0.0, + "macrs_bonus_fraction": 0.0, + "om_cost_per_kwh": 20.0, + "max_kw": 1000000000.0, + "production_incentive_max_benefit": 0.0, + "state_ibi_fraction": 0.0, + "fuel_intercept_gal_per_hr": 0.0125, + "generator_only_runs_during_grid_outage": true, + "state_rebate_per_kw": 0.0, + "installed_cost_per_kw": 2500.0, + "utility_ibi_max": 0.0, + "fuel_avail_gal": 0.0, + "min_turn_down_fraction": 0.3, + "production_incentive_max_kw": 0.0, + "utility_ibi_fraction": 0.0, + "state_ibi_max": 0.0, + "fuel_cost_per_gallon": 20.0, + "fuel_slope_gal_per_kwh": 0.068, + "utility_rebate_max": 0.0, + "macrs_option_years": 0, + "state_rebate_max": 0.0, + "federal_itc_fraction": 0.0, + "existing_kw": 0.0, + "production_incentive_per_kwh": 0.0, + "om_cost_per_kw": 50.0, + "utility_rebate_per_kw": 0.0, + "min_kw": 0.0, + "macrs_itc_reduction": 0.0, + "federal_rebate_per_kw": 0.0, + "generator_sells_energy_back_to_grid": false + }, + "Storage": { + "max_kwh": 1000000.0, + "rectifier_efficiency_fraction": 0.96, + "total_itc_fraction": 0.0, + "min_kw": 0.0, + "max_kw": 1000000.0, + "replace_cost_per_kw": 460.0, + "replace_cost_per_kwh": 230.0, + "min_kwh": 0.0, + "installed_cost_per_kw": 1000.0, + "total_rebate_per_kw": 0, + "installed_cost_per_kwh": 500.0, + "inverter_efficiency_fraction": 0.96, + "macrs_itc_reduction": 0.5, + "canGridCharge": true, + "macrs_bonus_fraction": 0.0, + "battery_replacement_year": 10, + "macrs_option_years": 7, + "internal_efficiency_fraction": 0.975, + "soc_min_fraction": 0.2, + "soc_init_fraction": 0.5, + "inverter_replacement_year": 10 } } diff --git a/lib/uo_cli.rb b/lib/uo_cli.rb index 54ef24b6..c16f8801 100755 --- a/lib/uo_cli.rb +++ b/lib/uo_cli.rb @@ -66,6 +66,8 @@ def initialize send("opt_#{@command}") ## dispatch to command handling method rescue NoMethodError abort('Invalid command, please run uo --help for a list of available commands') + rescue StandardError => e + puts "\nERROR: #{e.message}" end end @@ -288,26 +290,26 @@ def opt_process @subopts = Optimist.options do banner "\nURBANopt #{@command}:\n \n" - opt :default, "\nStandard post-processing for your scenario" + opt :default, "\nStandard post-processing for your scenario", short: :d - opt :opendss, "\nPost-process with OpenDSS" + opt :opendss, "\nPost-process with OpenDSS", short: :o - opt :disco, "\nPost-process with DISCO" + opt :disco, "\nPost-process with DISCO", short: :i opt :reopt_scenario, "\nOptimize for entire scenario with REopt. Used with the --reopt-scenario-assumptions-file to specify the assumptions to use.\n" \ - 'Example: uo process --reopt-scenario' + 'Example: uo process --reopt-scenario', short: :r opt :reopt_feature, "\nOptimize for each building individually with REopt\n" \ - 'Example: uo process --reopt-feature' + 'Example: uo process --reopt-feature', short: :e opt :reopt_resilience, "\nInclude resilience reporting in REopt optimization\n" \ - 'Example: uo process --reopt-scenario --reopt-resilience' + 'Example: uo process --reopt-scenario --reopt-resilience', short: :p opt :reopt_keep_existing, "\nKeep existing reopt feature optimizations instead of rerunning them to avoid rate limit issues.\n" \ 'Example: uo process --reopt-feature --reopt-keep-existing', short: :k opt :with_database, "\nInclude a sql database output of post-processed results\n" \ - 'Example: uo process --default --with-database' + 'Example: uo process --default --with-database', short: :w opt :reopt_scenario_assumptions_file, "\nPath to the scenario REopt assumptions JSON file you want to use. Use with the --reopt-scenario post-processor.\n" \ 'If not specified, the reopt/base_assumptions.json file will be used', type: String, short: :a @@ -362,23 +364,25 @@ def opt_des_params @subopts = Optimist.options do banner "\nURBANopt #{@command}:\n \n" - opt :sys_param_file, "\nBuild a system parameters JSON config file for Modelica District Energy System or Ground Heat Exchanger simulation using URBANopt SDK outputs\n" \ + opt :sys_param, "\nBuild a system parameters JSON config file for Modelica District Energy System or Ground Heat Exchanger simulation using URBANopt SDK outputs\n" \ "Provide path/name of json file to be created\n" \ - 'Example: uo des_params --sys-param-file path/to/sys_params.json', type: String, required: true, short: :y + 'Example: uo des_params --sys-param path/to/sys_params.json', type: String, required: true, short: :y opt :scenario, "\nPath to the scenario CSV file\n" \ - "Example: uo des_params --sys-param-file path/to/sys_params.json --scenario path/to/baseline_scenario.csv\n", type: String, required: true, short: :s + "Example: uo des_params --sys-param path/to/sys_params.json --scenario path/to/baseline_scenario.csv\n", type: String, required: true, short: :s opt :feature, "\nPath to the feature JSON file\n" \ - "Example: uo des_params --sys-param-file path/to/sys_params.json --feature path/to/example_project.json\n", type: String, required: true, short: :f + "Example: uo des_params --sys-param path/to/sys_params.json --feature path/to/example_project.json\n", type: String, required: true, short: :f opt :model_type, "\nSelection for which kind of DES simulation to perform\n" \ "Valid choices: 'time_series'", type: String, default: 'time_series' - opt :ghe, "\nUse this argument to add Ground Heat Exchanger properties to the System Parameter File.\n", short: :g + opt :district_type, "\nSelection for which kind of district system parameters to generate\n" \ + "Example: uo des_params --sys-param path/to/sys_params.json --feature path/to/example_project.json --district-type 5G_ghe\n" \ + 'If not specified, the default 4G district type will be used', type: String, required: false, short: :t - opt :overwrite, "\n Delete and rebuild existing sys-param file\n", short: :o - 'Example: uo des_params --sys-param-file path/to/sys_params.json --feature path/to/example_project.json --overwrite' + opt :overwrite, "\nDelete and rebuild existing sys-param file\n", short: :o + 'Example: uo des_params --sys-param path/to/sys_params.json --feature path/to/example_project.json --overwrite' end end @@ -386,17 +390,17 @@ def opt_des_create @subopts = Optimist.options do banner "\nURBANopt #{@command}:\n" - opt :sys_param, "Path to system parameters config file, possibly created with 'des_params' command in this CLI\n" \ + opt :sys_param, "\nPath to system parameters config file, possibly created with 'des_params' command in this CLI\n" \ "Example: uo des_create --sys-param system_parameters.json\n", type: String, required: true, short: :y - opt :feature, "Path to the feature JSON file\n" \ + opt :feature, "\nPath to the feature JSON file\n" \ 'Example: uo des_create --feature path/to/example_project.json', type: String, required: true, short: :f opt :des_name, "\nPath to Modelica project dir to be created\n" \ 'Example: uo des_create --des-name path/to/example_modelica_project', type: String, required: true, short: :n opt :overwrite, "\nDelete and rebuild existing model directory\n", short: :o - 'Example: uo des_create --des-name path/to/example_modelica_project --overwrite' + 'Example: uo des_create --des-name path/to/example_modelica_project --overwrite' end end @@ -438,6 +442,8 @@ def opt_ghe_size end rescue NoMethodError abort('Invalid command, please run uo --help for a list of available commands') + rescue StandardError => e + puts "\nERROR: #{e.message}" end # FIXME: Can this be combined with the above block? This isn't very DRY @@ -468,9 +474,11 @@ def self.run_func feature_file = URBANopt::GeoJSON::GeoFile.from_file(featurefile) if @opthash.subopts[:reopt] == true || @opthash.subopts[:reopt_scenario] == true || @opthash.subopts[:reopt_feature] == true - # TODO: Better way of grabbing assumptions file than the first file in the folder - reopt_files_dir_contents_list = Dir.children(reopt_files_dir.to_s) - reopt_assumptions_filename = File.basename(reopt_files_dir_contents_list[0]) + parsed_scenario_file = CSV.read(csv_file, headers: true, col_sep: ',') + # TODO: determine what to do if multiple assumptions are provided + # num_unique_reopt_assumptions = parsed_scenario_file['REopt Assumptions'].tally.size + # Use the first assumption as the default + reopt_assumptions_filename = parsed_scenario_file['REopt Assumptions'][0] scenario_output = URBANopt::Scenario::REoptScenarioCSV.new( @scenario_name.downcase, @root_dir, @@ -509,6 +517,8 @@ def self.create_scenario_csv_file(feature_id) # Rescue if file isn't json rescue JSON::ParserError => e abort("\nOops! You didn't provide a json file. Please provide path to the geojson feature_file") + rescue StandardError => e + puts "\nERROR: #{e.message}" end Dir["#{@feature_path}/mappers/*.rb"].each do |mapper_file| mapper_name = File.basename(mapper_file, File.extname(mapper_file)) @@ -536,6 +546,8 @@ def self.create_scenario_csv_file(feature_id) # Rescue if json isn't a geojson feature_file rescue NoMethodError abort("\nOops! You didn't provde a valid feature_file. Please provide path to the geojson feature_file") + rescue StandardError => e + puts "\nERROR: #{e.message}" end end end @@ -936,7 +948,7 @@ def self.get_python_deps # Check Python def self.check_python(python_only: false) - results = { python: false, pvars: [], message: '', python_deps: false, result: false } + results = { python: false, pvars: [], message: [], python_deps: false, result: false } puts 'Checking system.....' pvars = setup_python_variables results[:pvars] = pvars @@ -944,7 +956,7 @@ def self.check_python(python_only: false) # check vars if pvars[:python_path].nil? || pvars[:pip_path].nil? # need to install - results[:message] = 'Python paths have not yet been initialized with URBANopt.' + results[:message] << 'Python paths have not yet been initialized with URBANopt.' puts results[:message] return results end @@ -954,7 +966,7 @@ def self.check_python(python_only: false) if stderr.empty? puts "...python found at #{pvars[:python_path]}" else - results[:message] = "ERROR installing python: #{stderr}" + results[:message] << "ERROR installing python: #{stderr}" puts results[:message] return results end @@ -964,7 +976,7 @@ def self.check_python(python_only: false) if stderr.empty? puts "...pip found at #{pvars[:pip_path]}" else - results[:message] = "ERROR finding pip: #{stderr}" + results[:message] << "ERROR finding pip: #{stderr}" puts results[:message] return results end @@ -984,6 +996,12 @@ def self.check_python(python_only: false) else stdout, stderr, status = Open3.capture3("#{pvars[:pip_path]} show #{dep[:name]}") end + if @opthash.subopts[:verbose] + puts dep[:name] + puts "stdout: #{stdout}" + puts "status: #{status}" + end + if stderr.empty? # check versions m = stdout.match(/^Version: (\S{3,}$)/) @@ -998,14 +1016,17 @@ def self.check_python(python_only: false) end end if err - results[:message] = "incorrect version found for #{dep[:name]}...expecting version #{dep[:version]}" + results[:message] << "incorrect version found for #{dep[:name]}...expecting version #{dep[:version]}" puts results[:message] errors << stderr end else - results[:message] = stderr - puts results[:message] - errors << stderr + # ignore warnings + unless stderr.include? 'WARNING:' + results[:message] << stderr + puts results[:message] + errors << stderr + end end end if errors.empty? @@ -1013,8 +1034,11 @@ def self.check_python(python_only: false) end end - # all is good - results[:result] = true + # all is good if messages are empty + if results[:message].empty? + results[:result] = true + end + return results end @@ -1113,7 +1137,7 @@ def self.install_python_dependencies if dep[:version].nil? the_command = "#{pvars[:pip_path]} install #{dep[:name]}" else - the_command = "#{pvars[:pip_path]} install #{dep[:name]}~=#{dep[:version]}" + the_command = "#{pvars[:pip_path]} install #{dep[:name]}==#{dep[:version]}" end if @opthash.subopts[:verbose] @@ -1124,7 +1148,7 @@ def self.install_python_dependencies puts "status: #{status}" puts "stdout: #{stdout}" end - if stderr && !stderr == '' + if !stderr.empty? puts "Error installing: #{stderr}" end end @@ -1293,6 +1317,8 @@ def self.install_python_dependencies end rescue Errno::ENOENT # Same abort message if there is no run_dir abort("ERROR: URBANopt simulations are required before using opendss. Please run and process simulations, then try again.\n") + rescue StandardError => e + puts "\nERROR: #{e.message}" end ditto_cli_root = "#{res[:pvars][:ditto_path]} run-opendss " @@ -1336,6 +1362,8 @@ def self.install_python_dependencies rescue FileNotFoundError abort("\nMust post-process results before running OpenDSS. We recommend 'process --default'." \ "Once OpenDSS is run, you may then 'process --opendss'") + rescue StandardError => e + puts "\nERROR: #{e.message}" end end @@ -1441,6 +1469,8 @@ def self.install_python_dependencies runner.post_process rescue StandardError => e abort("\nError: #{e.message}") + rescue StandardError => e + puts "\nERROR: #{e.message}" end # TODO: aggregate back into scenario reports and geojson file @@ -1510,6 +1540,12 @@ def self.install_python_dependencies # Ensure reopt default files are prepared # create_reopt_files(@opthash.subopts[:scenario]) + if @opthash.subopts[:reopt_resilience] == true + abort('The REopt API is now using open-source optimization solvers; you may experience longer solve times and' \ + ' timeout errors, especially for evaluations with net metering, resilience, and/or 3+ technologies. ' \ + 'We will support resilience calculations with the REopt API in a future release.') + end + scenario_base = default_post_processor.scenario_base # see if reopt-scenario-assumptions-file was passed in, otherwise use the default @@ -1526,7 +1562,8 @@ def self.install_python_dependencies if feature[:properties][:district_system_type] && (feature[:properties][:district_system_type] == 'Community Photovoltaic') community_photovoltaic << feature end - rescue StandardError + rescue StandardError => e + puts "\nERROR: #{e.message}" end reopt_post_processor = URBANopt::REopt::REoptPostProcessor.new( scenario_report, @@ -1553,7 +1590,8 @@ def self.install_python_dependencies if feature[:properties][:district_system_type] && (feature[:properties][:district_system_type] == 'Ground Mount Photovoltaic') groundmount_photovoltaic[feature[:properties][:associated_building_id]] = feature[:properties][:footprint_area] end - rescue StandardError + rescue StandardError => e + puts "\nERROR: #{e.message}" end scenario_report_features = reopt_post_processor.run_scenario_report_features( scenario_report: scenario_report, @@ -1739,18 +1777,15 @@ def self.install_python_dependencies end des_cli_root = "#{res[:pvars][:gmt_path]} build-sys-param" - if @opthash.subopts[:sys_param_file] - des_cli_addition = " #{@opthash.subopts[:sys_param_file]}" + if @opthash.subopts[:sys_param] + des_cli_addition = " #{@opthash.subopts[:sys_param]}" if @opthash.subopts[:scenario] des_cli_addition += " #{@opthash.subopts[:scenario]}" end if @opthash.subopts[:feature] des_cli_addition += " #{@opthash.subopts[:feature]}" end - if @opthash.subopts[:model_type] - des_cli_addition += " #{@opthash.subopts[:model_type]}" - end - if @opthash.subopts[:ghe] + if @opthash.subopts[:district_type] run_dir = @root_dir / 'run' / @scenario_name.downcase ghe_run_dir = run_dir / 'ghe_dir' # make ghe run dir @@ -1758,11 +1793,14 @@ def self.install_python_dependencies Dir.mkdir ghe_run_dir puts "Creating GHE results folder #{ghe_run_dir}" end - des_cli_addition += " --ghe" + des_cli_addition += " #{@opthash.subopts[:district_type]}" + end + if @opthash.subopts[:model_type] + des_cli_addition += " #{@opthash.subopts[:model_type]}" end if @opthash.subopts[:overwrite] puts "\nDeleting and rebuilding existing sys-param file" - des_cli_addition += " --overwrite" + des_cli_addition += ' --overwrite' end else abort("\nCommand must include new system parameter file name, ScenarioFile, & FeatureFile. Please try again") @@ -1771,6 +1809,8 @@ def self.install_python_dependencies system(des_cli_root + des_cli_addition) rescue FileNotFoundError abort("\nMust simulate using 'uo run' before preparing Modelica models.") + rescue StandardError => e + puts "\nERROR: #{e.message}" end end @@ -1794,7 +1834,7 @@ def self.install_python_dependencies end if @opthash.subopts[:overwrite] puts "\nDeleting and rebuilding existing Modelica dir" - des_cli_addition += " --overwrite" + des_cli_addition += ' --overwrite' end else abort("\nCommand must include system parameter file name, FeatureFile, and model name. Please try again") @@ -1803,6 +1843,8 @@ def self.install_python_dependencies system(des_cli_root + des_cli_addition) rescue FileNotFoundError abort("\nMust simulate using 'uo run' before preparing Modelica models.") + rescue StandardError => e + puts "\nERROR: #{e.message}" end end @@ -1825,6 +1867,8 @@ def self.install_python_dependencies system(des_cli_root + des_cli_addition) rescue FileNotFoundError abort("\nMust simulate using 'uo run' before preparing Modelica models.") + rescue StandardError => e + puts "\nERROR: #{e.message}" end end @@ -1872,6 +1916,8 @@ def self.install_python_dependencies system(ghe_cli_root + ghe_cli_addition) rescue FileNotFoundError abort("\nFile Not Found Error Holder.") + rescue StandardError => e + puts "\nERROR: #{e.message}" end end diff --git a/lib/uo_cli/version.rb b/lib/uo_cli/version.rb index 6f5f3942..cf199d5c 100644 --- a/lib/uo_cli/version.rb +++ b/lib/uo_cli/version.rb @@ -5,6 +5,6 @@ module URBANopt module CLI - VERSION = '0.11.1'.freeze + VERSION = '0.12.0'.freeze end end diff --git a/scripts/setup-env-gitbash.sh b/scripts/setup-env-gitbash.sh index 61bd2395..e7ab2beb 100755 --- a/scripts/setup-env-gitbash.sh +++ b/scripts/setup-env-gitbash.sh @@ -10,7 +10,7 @@ BASE_DIR_NAME=$(dirname `which $0`) GEM_HOME=${BASE_DIR_NAME}/gems/ruby/2.7.0 GEM_PATH=${BASE_DIR_NAME}/gems/ruby/2.7.0 -PATH=${BASE_DIR_NAME}/ruby/bin:${BASE_DIR_NAME}/gems/ruby/2.7.0/bin:$PATH +PATH=${BASE_DIR_NAME}/ruby/bin:${BASE_DIR_NAME}/gems/ruby/2.7.0/bin:${BASE_DIR_NAME}/gems/ruby/2.7.0/gems/urbanopt-cli-0.11.1/example_files/python_deps/Miniconda-4.12.0/bin:$PATH RUBYLIB=${BASE_DIR_NAME}/OpenStudio/Ruby RUBY_DLL_PATH=${BASE_DIR_NAME}/OpenStudio/Ruby @@ -24,4 +24,3 @@ echo "export GEM_PATH=\"${GEM_PATH}\"" >> ~/.env_uo.sh echo "export PATH=\"${PATH}\"" >> ~/.env_uo.sh echo "export RUBYLIB=\"${RUBYLIB}\"" >> ~/.env_uo.sh echo "export RUBY_DLL_PATH=\"${RUBY_DLL_PATH}\"" >> ~/.env_uo.sh - diff --git a/scripts/setup-env-unix.sh b/scripts/setup-env-unix.sh index 72653ba8..1ef877c3 100755 --- a/scripts/setup-env-unix.sh +++ b/scripts/setup-env-unix.sh @@ -9,7 +9,7 @@ BASE_DIR_NAME="$(cd "$(dirname "$0")" && pwd)" GEM_HOME=${BASE_DIR_NAME}/gems/ruby/2.7.0 GEM_PATH=${BASE_DIR_NAME}/gems/ruby/2.7.0 -PATH=${BASE_DIR_NAME}/ruby/bin:${BASE_DIR_NAME}/gems/ruby/2.7.0/bin:$PATH +PATH=${BASE_DIR_NAME}/ruby/bin:${BASE_DIR_NAME}/gems/ruby/2.7.0/bin:${BASE_DIR_NAME}/gems/ruby/2.7.0/gems/urbanopt-cli-0.11.1/example_files/python_deps/Miniconda-4.12.0/bin:$PATH RUBYLIB=${BASE_DIR_NAME}/OpenStudio/Ruby RUBY_DLL_PATH=${BASE_DIR_NAME}/OpenStudio/Ruby diff --git a/scripts/setup-env.bat b/scripts/setup-env.bat index 072a6263..a5ab691e 100644 --- a/scripts/setup-env.bat +++ b/scripts/setup-env.bat @@ -1,21 +1,20 @@ -IF "%HOMEPATH%"=="" ECHO HOMEPATH is NOT defined. Please set this env value to your home directory before running this script. +IF "%HOMEPATH%"=="" ECHO HOMEPATH is NOT defined. Please set this env value to your home directory before running this script. IF "%HOMEPATH%"=="" exit /B SET BASE_DIR_NAME=%~d0%~p0 SET GEM_HOME=%BASE_DIR_NAME%\gems\ruby\2.7.0 SET GEM_PATH=%BASE_DIR_NAME%\gems\ruby\2.7.0 -SET PATH=%BASE_DIR_NAME%\ruby\bin;%BASE_DIR_NAME%\gems\ruby\2.7.0\bin;%PATH% +SET PATH=%BASE_DIR_NAME%\ruby\bin;%BASE_DIR_NAME%\gems\ruby\2.7.0\bin;%BASE_DIR_NAME%\gems\ruby\2.7.0\gems\urbanopt-cli-0.11.1\example_files\python_deps\python-3.10;%PATH% SET RUBYLIB=%BASE_DIR_NAME%\OpenStudio\Ruby SET RUBY_DLL_PATH=%BASE_DIR_NAME%\OpenStudio\Ruby IF EXIST %HOMEPATH%\.env_uo.bat ( del "%HOMEPATH%\.env_uo.bat" -) +) echo SET "GEM_HOME=%GEM_HOME%">> "%HOMEPATH%\.env_uo.bat" echo SET "GEM_PATH=%GEM_PATH%">> "%HOMEPATH%\.env_uo.bat" echo SET "PATH=%PATH%">> "%HOMEPATH%\.env_uo.bat" echo SET "RUBYLIB=%RUBYLIB%">> "%HOMEPATH%\.env_uo.bat" echo SET "RUBY_DLL_PATH=%RUBY_DLL_PATH%">> "%HOMEPATH%\.env_uo.bat" - diff --git a/scripts/setup-env.ps1 b/scripts/setup-env.ps1 index c824b757..1cc87d21 100644 --- a/scripts/setup-env.ps1 +++ b/scripts/setup-env.ps1 @@ -1,8 +1,8 @@ -# This is a simple setup script that generates an enviroment file that -# is used to setup the ruby enviroment to run the urbanopt-cli tool. -# To use just run this script in powershell (e.g. ./setup-env.ps1) -# Then you can use this env.ps1 to setup the enviroment. -# (e.g. . env.ps1) +# This is a simple setup script that generates an enviroment file that +# is used to setup the ruby enviroment to run the urbanopt-cli tool. +# To use just run this script in powershell (e.g. ./setup-env.ps1) +# Then you can use this env.ps1 to setup the enviroment. +# (e.g. . env.ps1) if (-not (Test-Path $HOME)) { echo "env HOME needs to be set before running this script" } if (-not (Test-Path $HOME)) { exit } @@ -11,7 +11,7 @@ $BASE_DIR_NAME = $PSScriptRoot $env:GEM_HOME = "$BASE_DIR_NAME\gems\ruby\2.7.0" $env:GEM_PATH = "$BASE_DIR_NAME\gems\ruby\2.7.0" -$env:PATH += ";$BASE_DIR_NAME\ruby\bin;$BASE_DIR_NAME\gems\ruby\2.7.0\bin" +$env:PATH += ";$BASE_DIR_NAME\ruby\bin;$BASE_DIR_NAME\gems\ruby\2.7.0\bin;$BASE_DIR_NAME\gems\ruby\2.7.0\gems\urbanopt-cli-0.11.1\example_files\python_deps\python-3.10" $env:RUBYLIB = "$BASE_DIR_NAME\OpenStudio\Ruby" $env:RUBY_DLL_PATH = "$BASE_DIR_NAME\OpenStudio\Ruby" @@ -23,4 +23,3 @@ Remove-Item $HOME/.env_uo.ps1 -ErrorAction Ignore '$env:PATH = "' + $env:PATH + '"' >> $HOME/.env_uo.ps1 '$env:RUBYLIB = "' + $env:RUBYLIB + '"' >> $HOME/.env_uo.ps1 '$env:RUBY_DLL_PATH = "' + $env:RUBY_DLL_PATH + '"' >> $HOME/.env_uo.ps1 - diff --git a/spec/spec_files/reopt/base_assumptions.json b/spec/spec_files/reopt/base_assumptions.json index 901941be..42fed7f5 100644 --- a/spec/spec_files/reopt/base_assumptions.json +++ b/spec/spec_files/reopt/base_assumptions.json @@ -1,146 +1,142 @@ { - "Scenario": { - "Site": { - "Financial": { - "om_cost_escalation_pct": 0.025, - "escalation_pct": 0.023, - "offtaker_tax_pct": 0.26, - "offtaker_discount_pct": 0.083, - "analysis_years": 25, - "value_of_lost_load_us_dollars_per_kwh": 100, - "microgrid_upgrade_cost_pct": 0.3 - }, - "LoadProfile": { - "year": 2018, - "loads_kw_is_net": true, - "critical_loads_kw_is_net": false, - "critical_load_pct": 0.5, - "outage_is_major_event": true - }, - "ElectricTariff": { - "add_blended_rates_to_urdb_rate": false, - "net_metering_limit_kw": 0, - "interconnection_limit_kw": 100000000.0, - "urdb_label": "5e162e2a5457a3d50873e3af" - }, - "Wind": { - "min_kw": 0, - "max_kw": 0, - "installed_cost_us_dollars_per_kw": 3013, - "om_cost_us_dollars_per_kw": 40, - "macrs_option_years": 5, - "macrs_bonus_pct": 1, - "macrs_itc_reduction": 0.5, - "federal_itc_pct": 0.26, - "state_ibi_pct": 0, - "state_ibi_max_us_dollars": 10000000000.0, - "utility_ibi_pct": 0, - "utility_ibi_max_us_dollars": 10000000000.0, - "federal_rebate_us_dollars_per_kw": 0, - "state_rebate_us_dollars_per_kw": 0, - "state_rebate_max_us_dollars": 10000000000.0, - "utility_rebate_us_dollars_per_kw": 0, - "utility_rebate_max_us_dollars": 10000000000.0, - "pbi_us_dollars_per_kwh": 0, - "pbi_max_us_dollars": 1000000000.0, - "pbi_years": 1, - "pbi_system_max_kw": 1000000000.0 - }, - "PV": { - "pv_name": "Roof - South Face", - "location":"roof", - "existing_kw": 0, - "existing_kw": 0, - "min_kw": 0, - "max_kw": 1000000000.0, - "installed_cost_us_dollars_per_kw": 1600, - "om_cost_us_dollars_per_kw": 16, - "macrs_option_years": 5, - "macrs_bonus_pct": 1, - "macrs_itc_reduction": 0.5, - "federal_itc_pct": 0.26, - "state_ibi_pct": 0, - "state_ibi_max_us_dollars": 10000000000.0, - "utility_ibi_pct": 0, - "utility_ibi_max_us_dollars": 10000000000.0, - "federal_rebate_us_dollars_per_kw": 0, - "state_rebate_us_dollars_per_kw": 0, - "state_rebate_max_us_dollars": 10000000000.0, - "utility_rebate_us_dollars_per_kw": 0, - "utility_rebate_max_us_dollars": 10000000000.0, - "pbi_us_dollars_per_kwh": 0, - "pbi_max_us_dollars": 1000000000.0, - "pbi_years": 1, - "pbi_system_max_kw": 1000000000.0, - "degradation_pct": 0.005, - "azimuth": 180, - "losses": 0.14, - "array_type": 1, - "module_type": 0, - "gcr": 0.4, - "dc_ac_ratio": 1.2, - "inv_eff": 0.96, - "radius": 0, - "tilt": 0.537 - }, - "Storage": { - "min_kw": 0, - "max_kw": 1000000, - "min_kwh": 0, - "max_kwh": 1000000, - "internal_efficiency_pct": 0.975, - "inverter_efficiency_pct": 0.96, - "rectifier_efficiency_pct": 0.96, - "soc_min_pct": 0.2, - "soc_init_pct": 0.5, - "canGridCharge": true, - "installed_cost_us_dollars_per_kw": 840, - "installed_cost_us_dollars_per_kwh": 420, - "replace_cost_us_dollars_per_kw": 410, - "replace_cost_us_dollars_per_kwh": 200, - "inverter_replacement_year": 10, - "battery_replacement_year": 10, - "macrs_option_years": 7, - "macrs_bonus_pct": 1, - "macrs_itc_reduction": 0.5, - "total_itc_pct": 0.0, - "total_rebate_us_dollars_per_kw": 0 - }, - "Generator": { - "existing_kw": 0, - "min_kw": 0, - "max_kw": 1000000000.0, - "installed_cost_us_dollars_per_kw": 500, - "om_cost_us_dollars_per_kw": 10, - "om_cost_us_dollars_per_kwh": 0.0, - "diesel_fuel_cost_us_dollars_per_gallon": 3, - "fuel_slope_gal_per_kwh": 0.076, - "fuel_intercept_gal_per_hr": 0, - "fuel_avail_gal": 660, - "min_turn_down_pct": 0, - "generator_only_runs_during_grid_outage": true, - "generator_sells_energy_back_to_grid": false, - "macrs_option_years": 0, - "macrs_bonus_pct": 1, - "macrs_itc_reduction": 0, - "federal_itc_pct": 0, - "state_ibi_pct": 0, - "state_ibi_max_us_dollars": 0, - "utility_ibi_pct": 0, - "utility_ibi_max_us_dollars": 0, - "federal_rebate_us_dollars_per_kw": 0, - "state_rebate_us_dollars_per_kw": 0, - "state_rebate_max_us_dollars": 0, - "utility_rebate_us_dollars_per_kw": 0, - "utility_rebate_max_us_dollars": 0, - "pbi_us_dollars_per_kwh": 0, - "pbi_max_us_dollars": 0, - "pbi_years": 0, - "pbi_system_max_kw": 0 - } - }, + "Settings": { "timeout_seconds": 295, - "time_steps_per_hour": 1 + "time_steps_per_hour": 1, + "off_grid_flag": false + }, + "Site": { + "roof_squarefeet": null, + "renewable_electricity_min_fraction": 0, + "renewable_electricity_max_fraction": 1 + }, + "Financial": { + "elec_cost_escalation_rate_fraction": 0.026, + "offtaker_discount_rate_fraction": 0.081, + "value_of_lost_load_per_kwh": 100.0, + "analysis_years": 20, + "microgrid_upgrade_cost_fraction": 0.3, + "offtaker_tax_rate_fraction": 0.26, + "om_cost_escalation_rate_fraction": 0.025 + }, + "ElectricTariff": { + "add_monthly_rates_to_urdb_rate": false, + "urdb_label": "63d2f36655296095a0092a6e" + }, + "ElectricUtility": { + "interconnection_limit_kw": 100000000.0, + "net_metering_limit_kw": 0.0 + }, + "ElectricLoad": { + "critical_loads_kw_is_net": false, + "critical_load_fraction": 0, + "loads_kw_is_net": true + }, + "PV": { + "name": "Roof - South Face", + "production_incentive_years": 1.0, + "location":"roof", + "macrs_bonus_fraction": 0.0, + "max_kw": 1000000000.0, + "production_incentive_max_benefit": 1000000000.0, + "radius": 0.0, + "state_ibi_fraction": 0.0, + "utility_rebate_max": 10000000000.0, + "installed_cost_per_kw": 2000.0, + "utility_ibi_max": 10000000000.0, + "tilt": 10.0, + "federal_rebate_per_kw": 0.0, + "gcr": 0.4, + "production_incentive_max_kw": 1000000000.0, + "utility_ibi_fraction": 0.0, + "state_ibi_max": 10000000000.0, + "state_rebate_per_kw": 0.0, + "macrs_option_years": 5, + "state_rebate_max": 10000000000.0, + "dc_ac_ratio": 1.1, + "federal_itc_fraction": 0.3, + "production_incentive_per_kwh": 0.0, + "module_type": 0, + "array_type": 1, + "existing_kw": 0.0, + "om_cost_per_kw": 16.0, + "utility_rebate_per_kw": 0.0, + "min_kw": 0.0, + "losses": 0.14, + "macrs_itc_reduction": 0.5, + "degradation_fraction": 0.005, + "inv_eff": 0.96, + "azimuth": 180.0 + }, + "Generator": { + "production_incentive_years": 0.0, + "macrs_bonus_fraction": 0.0, + "om_cost_per_kwh": 20.0, + "max_kw": 1000000000.0, + "production_incentive_max_benefit": 0.0, + "state_ibi_fraction": 0.0, + "fuel_intercept_gal_per_hr": 0.0125, + "generator_only_runs_during_grid_outage": true, + "state_rebate_per_kw": 0.0, + "installed_cost_per_kw": 2500.0, + "utility_ibi_max": 0.0, + "fuel_avail_gal": 0.0, + "min_turn_down_fraction": 0.3, + "production_incentive_max_kw": 0.0, + "utility_ibi_fraction": 0.0, + "state_ibi_max": 0.0, + "fuel_cost_per_gallon": 20.0, + "fuel_slope_gal_per_kwh": 0.068, + "utility_rebate_max": 0.0, + "macrs_option_years": 0, + "state_rebate_max": 0.0, + "federal_itc_fraction": 0.0, + "existing_kw": 0.0, + "production_incentive_per_kwh": 0.0, + "om_cost_per_kw": 50.0, + "utility_rebate_per_kw": 0.0, + "min_kw": 0.0, + "macrs_itc_reduction": 0.0, + "federal_rebate_per_kw": 0.0, + "generator_sells_energy_back_to_grid": false + }, + "Storage": { + "max_kwh": 1000000.0, + "rectifier_efficiency_fraction": 0.96, + "total_itc_fraction": 0.0, + "min_kw": 0.0, + "max_kw": 1000000.0, + "replace_cost_per_kw": 460.0, + "replace_cost_per_kwh": 230.0, + "min_kwh": 0.0, + "installed_cost_per_kw": 1000.0, + "total_rebate_per_kw": 0, + "installed_cost_per_kwh": 500.0, + "inverter_efficiency_fraction": 0.96, + "macrs_itc_reduction": 0.5, + "canGridCharge": true, + "macrs_bonus_fraction": 0.0, + "battery_replacement_year": 10, + "macrs_option_years": 7, + "internal_efficiency_fraction": 0.975, + "soc_min_fraction": 0.2, + "soc_init_fraction": 0.5, + "inverter_replacement_year": 10 + }, + "Wind": { + "max_kw": 1000000000.0, + "installed_cost_per_kw": 100000, + "om_cost_per_kw": 50.0, + "macrs_option_years": 7, + "macrs_bonus_fraction": 0.0, + "macrs_itc_reduction": 0.5, + "state_ibi_fraction": 0.0, + "state_ibi_max": 10000000000, + "utility_ibi_fraction": 0.0, + "utility_ibi_max": 10000000000, + "federal_rebate_per_kw": 0.0, + "production_incentive_years": 1.0, + "production_incentive_max_benefit": 1000000000.0, + "production_incentive_max_kw": 1000000000.0, + "production_incentive_per_kwh": 0.0 } } - diff --git a/spec/spec_files/reopt/multiPV_assumptions.json b/spec/spec_files/reopt/multiPV_assumptions.json index 314ce5c8..ea92b829 100644 --- a/spec/spec_files/reopt/multiPV_assumptions.json +++ b/spec/spec_files/reopt/multiPV_assumptions.json @@ -1,181 +1,181 @@ { - "Scenario": { - "Site": { - "Financial": { - "om_cost_escalation_pct": 0.025, - "escalation_pct": 0.023, - "offtaker_tax_pct": 0.26, - "offtaker_discount_pct": 0.083, - "analysis_years": 25, - "value_of_lost_load_us_dollars_per_kwh": 100, - "microgrid_upgrade_cost_pct": 0.3 - }, - "LoadProfile": { - "year": 2018, - "loads_kw_is_net": true, - "critical_loads_kw_is_net": false, - "critical_load_pct": 0.5, - "outage_is_major_event": true - }, - "ElectricTariff": { - "add_blended_rates_to_urdb_rate": false, - "net_metering_limit_kw": 0, - "interconnection_limit_kw": 100000000.0, - "urdb_label": "5e162e2a5457a3d50873e3af" - }, - "Wind": { - "min_kw": 0, - "max_kw": 0, - "installed_cost_us_dollars_per_kw": 3013, - "om_cost_us_dollars_per_kw": 40, - "macrs_option_years": 5, - "macrs_bonus_pct": 1, - "macrs_itc_reduction": 0.5, - "federal_itc_pct": 0.26, - "state_ibi_pct": 0, - "state_ibi_max_us_dollars": 10000000000.0, - "utility_ibi_pct": 0, - "utility_ibi_max_us_dollars": 10000000000.0, - "federal_rebate_us_dollars_per_kw": 0, - "state_rebate_us_dollars_per_kw": 0, - "state_rebate_max_us_dollars": 10000000000.0, - "utility_rebate_us_dollars_per_kw": 0, - "utility_rebate_max_us_dollars": 10000000000.0, - "pbi_us_dollars_per_kwh": 0, - "pbi_max_us_dollars": 1000000000.0, - "pbi_years": 1, - "pbi_system_max_kw": 1000000000.0 - }, - "PV": [{ - "pv_name": "Roof - South Face", - "location":"roof", - "existing_kw": 0, - "min_kw": 1.6, - "max_kw": 1000000000.0, - "installed_cost_us_dollars_per_kw": 1600, - "om_cost_us_dollars_per_kw": 16, - "macrs_option_years": 5, - "macrs_bonus_pct": 1, - "macrs_itc_reduction": 0.5, - "federal_itc_pct": 0.26, - "state_ibi_pct": 0, - "state_ibi_max_us_dollars": 10000000000.0, - "utility_ibi_pct": 0, - "utility_ibi_max_us_dollars": 10000000000.0, - "federal_rebate_us_dollars_per_kw": 0, - "state_rebate_us_dollars_per_kw": 0, - "state_rebate_max_us_dollars": 10000000000.0, - "utility_rebate_us_dollars_per_kw": 0, - "utility_rebate_max_us_dollars": 10000000000.0, - "pbi_us_dollars_per_kwh": 0, - "pbi_max_us_dollars": 1000000000.0, - "pbi_years": 1, - "pbi_system_max_kw": 1000000000.0, - "degradation_pct": 0.005, - "azimuth": 180, - "losses": 0.14, - "array_type": 1, - "module_type": 0, - "gcr": 0.4, - "dc_ac_ratio": 1.2, - "inv_eff": 0.96, - "radius": 0, - "tilt": 0.537 - }, - { - "pv_name": "Groundmount", - "location":"ground", - "existing_kw": 5, - "min_kw": 0, - "max_kw": 1000000000.0, - "installed_cost_us_dollars_per_kw": 2200, - "om_cost_us_dollars_per_kw": 16, - "macrs_option_years": 5, - "macrs_bonus_pct": 1, - "macrs_itc_reduction": 0.5, - "federal_itc_pct": 0.26, - "state_ibi_pct": 0, - "state_ibi_max_us_dollars": 10000000000.0, - "utility_ibi_pct": 0, - "utility_ibi_max_us_dollars": 10000000000.0, - "federal_rebate_us_dollars_per_kw": 0, - "state_rebate_us_dollars_per_kw": 0, - "state_rebate_max_us_dollars": 10000000000.0, - "utility_rebate_us_dollars_per_kw": 0, - "utility_rebate_max_us_dollars": 10000000000.0, - "pbi_us_dollars_per_kwh": 0, - "pbi_max_us_dollars": 1000000000.0, - "pbi_years": 1, - "pbi_system_max_kw": 1000000000.0, - "degradation_pct": 0.005, - "azimuth": 180, - "losses": 0.14, - "array_type": 0, - "module_type": 0, - "gcr": 0.4, - "dc_ac_ratio": 1.2, - "inv_eff": 0.96, - "radius": 0, - "tilt": 0.537 - }], - "Storage": { - "min_kw": 0, - "max_kw": 1000000, - "min_kwh": 0, - "max_kwh": 1000000, - "internal_efficiency_pct": 0.975, - "inverter_efficiency_pct": 0.96, - "rectifier_efficiency_pct": 0.96, - "soc_min_pct": 0.2, - "soc_init_pct": 0.5, - "canGridCharge": true, - "installed_cost_us_dollars_per_kw": 840, - "installed_cost_us_dollars_per_kwh": 420, - "replace_cost_us_dollars_per_kw": 410, - "replace_cost_us_dollars_per_kwh": 200, - "inverter_replacement_year": 10, - "battery_replacement_year": 10, - "macrs_option_years": 7, - "macrs_bonus_pct": 1, - "macrs_itc_reduction": 0.5, - "total_itc_pct": 0.0, - "total_rebate_us_dollars_per_kw": 0 - }, - "Generator": { - "existing_kw": 0, - "min_kw": 0, - "max_kw": 1000000000.0, - "installed_cost_us_dollars_per_kw": 500, - "om_cost_us_dollars_per_kw": 10, - "om_cost_us_dollars_per_kwh": 0.0, - "diesel_fuel_cost_us_dollars_per_gallon": 3, - "fuel_slope_gal_per_kwh": 0.076, - "fuel_intercept_gal_per_hr": 0, - "fuel_avail_gal": 660, - "min_turn_down_pct": 0, - "generator_only_runs_during_grid_outage": true, - "generator_sells_energy_back_to_grid": false, - "macrs_option_years": 0, - "macrs_bonus_pct": 1, - "macrs_itc_reduction": 0, - "federal_itc_pct": 0, - "state_ibi_pct": 0, - "state_ibi_max_us_dollars": 0, - "utility_ibi_pct": 0, - "utility_ibi_max_us_dollars": 0, - "federal_rebate_us_dollars_per_kw": 0, - "state_rebate_us_dollars_per_kw": 0, - "state_rebate_max_us_dollars": 0, - "utility_rebate_us_dollars_per_kw": 0, - "utility_rebate_max_us_dollars": 0, - "pbi_us_dollars_per_kwh": 0, - "pbi_max_us_dollars": 0, - "pbi_years": 0, - "pbi_system_max_kw": 0 - } - }, + "Settings": { "timeout_seconds": 295, - "time_steps_per_hour": 1 + "time_steps_per_hour": 1, + "off_grid_flag": false + }, + "Site": { + "roof_squarefeet": null, + "renewable_electricity_min_fraction": 0, + "renewable_electricity_max_fraction": 1 + }, + "Financial": { + "elec_cost_escalation_rate_fraction": 0.026, + "offtaker_discount_rate_fraction": 0.081, + "value_of_lost_load_per_kwh": 100.0, + "analysis_years": 20, + "microgrid_upgrade_cost_fraction": 0.3, + "offtaker_tax_rate_fraction": 0.26, + "om_cost_escalation_rate_fraction": 0.025 + }, + "ElectricTariff": { + "add_monthly_rates_to_urdb_rate": false, + "urdb_label": "63d2f36655296095a0092a6e" + }, + "ElectricUtility": { + "interconnection_limit_kw": 100000000.0, + "net_metering_limit_kw": 0.0 + }, + "ElectricLoad": { + "year": 2018, + "critical_loads_kw_is_net": false, + "critical_load_fraction": 0, + "loads_kw_is_net": true + }, + "PV": [ + { + "name": "Roof - South Face", + "location":"roof", + "production_incentive_years": 1.0, + "macrs_bonus_fraction": 0.0, + "max_kw": 1000000000.0, + "production_incentive_max_benefit": 1000000000.0, + "radius": 0.0, + "state_ibi_fraction": 0.0, + "utility_rebate_max": 10000000000.0, + "installed_cost_per_kw": 2000.0, + "utility_ibi_max": 10000000000.0, + "tilt": 10.0, + "federal_rebate_per_kw": 0.0, + "gcr": 0.4, + "production_incentive_max_kw": 1000000000.0, + "utility_ibi_fraction": 0.0, + "state_ibi_max": 10000000000.0, + "state_rebate_per_kw": 0.0, + "macrs_option_years": 5, + "state_rebate_max": 10000000000.0, + "dc_ac_ratio": 1.1, + "federal_itc_fraction": 0.3, + "production_incentive_per_kwh": 0.0, + "module_type": 0, + "array_type": 1, + "existing_kw": 0.0, + "om_cost_per_kw": 16.0, + "utility_rebate_per_kw": 0.0, + "min_kw": 0.0, + "losses": 0.14, + "macrs_itc_reduction": 0.5, + "degradation_fraction": 0.005, + "inv_eff": 0.96, + "azimuth": 180.0 + }, + { + "name": "Groundmount", + "location":"ground", + "production_incentive_years": 1.0, + "macrs_bonus_fraction": 0.0, + "max_kw": 1000000000.0, + "production_incentive_max_benefit": 1000000000.0, + "radius": 0.0, + "state_ibi_fraction": 0.0, + "utility_rebate_max": 10000000000.0, + "installed_cost_per_kw": 2000.0, + "utility_ibi_max": 10000000000.0, + "tilt": 10.0, + "federal_rebate_per_kw": 0.0, + "gcr": 0.4, + "production_incentive_max_kw": 1000000000.0, + "utility_ibi_fraction": 0.0, + "state_ibi_max": 10000000000.0, + "state_rebate_per_kw": 0.0, + "macrs_option_years": 5, + "state_rebate_max": 10000000000.0, + "dc_ac_ratio": 1.1, + "federal_itc_fraction": 0.3, + "production_incentive_per_kwh": 0.0, + "module_type": 0, + "array_type": 1, + "existing_kw": 0.0, + "om_cost_per_kw": 16.0, + "utility_rebate_per_kw": 0.0, + "min_kw": 0.0, + "losses": 0.14, + "macrs_itc_reduction": 0.5, + "degradation_fraction": 0.005, + "inv_eff": 0.96, + "azimuth": 180.0 + } + ], + "Wind": { + "max_kw": 1000000000.0, + "installed_cost_per_kw": 100000, + "om_cost_per_kw": 50.0, + "macrs_option_years": 7, + "macrs_bonus_fraction": 0.0, + "macrs_itc_reduction": 0.5, + "state_ibi_fraction": 0.0, + "state_ibi_max": 10000000000, + "utility_ibi_fraction": 0.0, + "utility_ibi_max": 10000000000, + "federal_rebate_per_kw": 0.0, + "production_incentive_years": 1.0, + "production_incentive_max_benefit": 1000000000.0, + "production_incentive_max_kw": 1000000000.0, + "production_incentive_per_kwh": 0.0 + }, + "Generator": { + "production_incentive_years": 0.0, + "macrs_bonus_fraction": 0.0, + "om_cost_per_kwh": 20.0, + "max_kw": 1000000000.0, + "production_incentive_max_benefit": 0.0, + "state_ibi_fraction": 0.0, + "fuel_intercept_gal_per_hr": 0.0125, + "generator_only_runs_during_grid_outage": true, + "state_rebate_per_kw": 0.0, + "installed_cost_per_kw": 2500.0, + "utility_ibi_max": 0.0, + "fuel_avail_gal": 0.0, + "min_turn_down_fraction": 0.3, + "production_incentive_max_kw": 0.0, + "utility_ibi_fraction": 0.0, + "state_ibi_max": 0.0, + "fuel_cost_per_gallon": 20.0, + "fuel_slope_gal_per_kwh": 0.068, + "utility_rebate_max": 0.0, + "macrs_option_years": 0, + "state_rebate_max": 0.0, + "federal_itc_fraction": 0.0, + "existing_kw": 0.0, + "production_incentive_per_kwh": 0.0, + "om_cost_per_kw": 50.0, + "utility_rebate_per_kw": 0.0, + "min_kw": 0.0, + "macrs_itc_reduction": 0.0, + "federal_rebate_per_kw": 0.0, + "generator_sells_energy_back_to_grid": false + }, + "Storage": { + "max_kwh": 1000000.0, + "rectifier_efficiency_fraction": 0.96, + "total_itc_fraction": 0.0, + "min_kw": 0.0, + "max_kw": 1000000.0, + "replace_cost_per_kw": 460.0, + "replace_cost_per_kwh": 230.0, + "min_kwh": 0.0, + "installed_cost_per_kw": 1000.0, + "total_rebate_per_kw": 0, + "installed_cost_per_kwh": 500.0, + "inverter_efficiency_fraction": 0.96, + "macrs_itc_reduction": 0.5, + "canGridCharge": true, + "macrs_bonus_fraction": 0.0, + "battery_replacement_year": 10, + "macrs_option_years": 7, + "internal_efficiency_fraction": 0.975, + "soc_min_fraction": 0.2, + "soc_init_fraction": 0.5, + "inverter_replacement_year": 10 } } - diff --git a/spec/uo_cli_spec.rb b/spec/uo_cli_spec.rb index b639701b..497d194f 100644 --- a/spec/uo_cli_spec.rb +++ b/spec/uo_cli_spec.rb @@ -50,6 +50,17 @@ def delete_directory_or_file(dir_or_file) if File.exist?(dir_or_file) FileUtils.rm_rf(dir_or_file) + puts "Deleted #{dir_or_file} during test preparation" + end + end + + # Find Python version + # Returns Python version as a list of strings for major, minor, and patch + def find_python_version + version_output, status = Open3.capture2e('python3 --version') + if status.success? + version = version_output.split(' ')[1] + return version.split('.') end end @@ -146,6 +157,7 @@ def select_measures(test_dir, measure_name_list, workflow = 'base_workflow.osw', delete_directory_or_file(test_directory_elec) delete_directory_or_file(test_directory_disco) delete_directory_or_file(test_directory_pv) + delete_directory_or_file(test_directory_ghe) end it 'creates an example project directory' do @@ -323,12 +335,6 @@ def select_measures(test_dir, measure_name_list, workflow = 'base_workflow.osw', expect((test_directory / 'run' / 'two_building_scenario' / '3' / 'finished.job').exist?).to be false end - it 'creates a system parameter file', :basic do - skip('Requires Python 3.10') unless system('python3 --version') =~ /3\.10/ - system("#{call_cli} des_params --scenario #{test_scenario} --feature #{test_feature} --sys-param-file #{system_parameters_file}") - expect(system_parameters_file.exist?).to be true - end - it 'runs a 2 building scenario using create bar geometry method', :basic do # Copy create bar specific files system("cp #{example_dir / 'mappers' / 'CreateBar.rb'} #{test_directory / 'mappers' / 'CreateBar.rb'}") @@ -472,19 +478,19 @@ def select_measures(test_dir, measure_name_list, workflow = 'base_workflow.osw', # This test requires the 'run ghe project' be run first test_scenario_report = test_directory_ghe / 'run' / 'baseline_scenario_ghe' / 'default_scenario_report.csv' system("#{call_cli} process --default --scenario #{test_scenario_ghe} --feature #{test_feature_ghe}") - #expect(`wc -l < #{test_scenario_report}`.to_i).to be > 2 + # expect(`wc -l < #{test_scenario_report}`.to_i).to be > 2 expect((test_directory_ghe / 'run' / 'baseline_scenario_ghe' / 'process_status.json').exist?).to be true end it 'creates a system parameter file with GHE properties', :ghe do - system("#{call_cli} des_params --scenario #{test_scenario_ghe} --feature #{test_feature_ghe} --sys-param-file #{ghe_system_parameters_file} --ghe") + system("#{call_cli} des_params --scenario #{test_scenario_ghe} --feature #{test_feature_ghe} --sys-param #{ghe_system_parameters_file} --district-type 5G_ghe") expect(ghe_system_parameters_file.exist?).to be true expect((test_directory_ghe / 'run' / 'baseline_scenario_ghe' / 'ghe_dir').exist?).to be true end it 'overwrites a system parameter file', :ghe do expect(ghe_system_parameters_file.exist?).to be true - system("#{call_cli} des_params --scenario #{test_scenario_ghe} --feature #{test_feature_ghe} --sys-param-file #{ghe_system_parameters_file} --ghe --overwrite") + system("#{call_cli} des_params --scenario #{test_scenario_ghe} --feature #{test_feature_ghe} --sys-param #{ghe_system_parameters_file} --district-type 5G_ghe --overwrite") expect(ghe_system_parameters_file.exist?).to be true expect((test_directory_ghe / 'run' / 'baseline_scenario_ghe' / 'ghe_dir').exist?).to be true end @@ -495,20 +501,20 @@ def select_measures(test_dir, measure_name_list, workflow = 'base_workflow.osw', expect((test_directory_ghe / 'run' / 'baseline_scenario_ghe' / 'ghe_dir').empty?).to be false end - it 'creates a Modelica model with the GMT', :ghe do + it 'creates a 5G Modelica model with the GMT', :ghe do system("#{call_cli} des_create --feature #{test_feature_ghe} --sys-param #{ghe_system_parameters_file} --des-name #{test_directory_ghe / 'modelica_ghe'}") - expect((test_directory_ghe / 'modelica_ghe'/ 'Districts' / 'DistrictEnergySystem.mo').exist?).to be true + expect((test_directory_ghe / 'modelica_ghe' / 'Districts' / 'DistrictEnergySystem.mo').exist?).to be true end - it 'overwrites an existing Modelica model', :ghe do + it 'overwrites an existing 5G Modelica model', :ghe do system("#{call_cli} des_create --feature #{test_feature_ghe} --sys-param #{ghe_system_parameters_file} --des-name #{test_directory_ghe / 'modelica_ghe'} --overwrite") - expect((test_directory_ghe / 'modelica_ghe'/ 'Districts' / 'DistrictEnergySystem.mo').exist?).to be true + expect((test_directory_ghe / 'modelica_ghe' / 'Districts' / 'DistrictEnergySystem.mo').exist?).to be true end it 'runs a Modelica simulation with the GMT', :ghe do skip('Requires Docker to be installed') unless system('which docker > /dev/null 2>&1') system("#{call_cli} des_run --model #{test_directory_ghe / 'modelica_ghe'}") - expect((test_directory_ghe / 'modelica_ghe'/ 'modelica_ghe.Districts.DistrictEnergySystem_results' / 'modelica_ghe.Districts.DistrictEnergySystem_res.mat').exist?).to be true + expect((test_directory_ghe / 'modelica_ghe' / 'modelica_ghe.Districts.DistrictEnergySystem_results' / 'modelica_ghe.Districts.DistrictEnergySystem_res.mat').exist?).to be true end end @@ -706,8 +712,6 @@ def select_measures(test_dir, measure_name_list, workflow = 'base_workflow.osw', it 'successfully gets results from the opendss cli', :electric do # This test requires the 'runs an electrical network scenario' be run first system("#{call_cli} process --default --scenario #{test_scenario_elec} --feature #{test_feature_elec}") - system("#{call_cli} opendss --scenario #{test_scenario_elec} --feature #{test_feature_elec} --start-date 2017/01/15 --start-time 01:00:00 --end-date 2017/01/16 --end-time 00:00:00") - expect((test_directory_elec / 'run' / 'electrical_scenario' / 'opendss' / 'profiles' / 'load_1.csv').exist?).to be true expect { system("#{call_cli} opendss --scenario #{test_scenario_elec} --feature #{test_feature_elec} --start-date 2017/01/15 --start-time 01:00:00 --end-date 2017/01/16 --end-time 00:00:00 --upgrade") } .to output(a_string_including('Upgrading undersized transformers:')) .to_stdout_from_any_process @@ -722,7 +726,6 @@ def select_measures(test_dir, measure_name_list, workflow = 'base_workflow.osw', it 'reopt post-processes a scenario and visualize', :electric do # This test requires the 'runs a PV scenario when called with reopt' be run first - system("#{call_cli} process --default --scenario #{test_scenario_reopt} --feature #{test_feature_pv}") system("#{call_cli} process --reopt-scenario --scenario #{test_scenario_reopt} --feature #{test_feature_pv}") expect((test_directory_pv / 'run' / 'reopt_scenario' / 'scenario_optimization.json').exist?).to be true expect((test_directory_pv / 'run' / 'reopt_scenario' / 'process_status.json').exist?).to be true @@ -733,7 +736,6 @@ def select_measures(test_dir, measure_name_list, workflow = 'base_workflow.osw', it 'reopt post-processes a scenario with specified scenario assumptions file', :electric do # This test requires the 'runs a PV scenario when called with reopt' be run first - system("#{call_cli} process --default --scenario #{test_scenario_reopt} --feature #{test_feature_pv}") expect { system("#{call_cli} process --reopt-scenario -a #{test_reopt_scenario_assumptions_file} --scenario #{test_scenario_reopt} --feature #{test_feature_pv}") } .to output(a_string_including('multiPV_assumptions.json')) .to_stdout_from_any_process @@ -742,8 +744,8 @@ def select_measures(test_dir, measure_name_list, workflow = 'base_workflow.osw', end it 'reopt post-processes a scenario with resilience reporting', :electric do + skip('Resilience processing is not yet implemented with REopt v3') # This test requires the 'runs a PV scenario when called with reopt' be run first - system("#{call_cli} process --default --scenario #{test_scenario_reopt} --feature #{test_feature_pv}") system("#{call_cli} process --reopt-scenario --reopt-resilience --scenario #{test_scenario_reopt} --feature #{test_feature_pv}") expect((test_directory_pv / 'run' / 'reopt_scenario' / 'scenario_optimization.json').exist?).to be true expect((test_directory_pv / 'run' / 'reopt_scenario' / 'process_status.json').exist?).to be true @@ -752,7 +754,6 @@ def select_measures(test_dir, measure_name_list, workflow = 'base_workflow.osw', it 'reopt post-processes each feature and visualize', :electric do # This test requires the 'runs a PV scenario when called with reopt' be run first - system("#{call_cli} process --default --scenario #{test_scenario_reopt} --feature #{test_feature_pv}") system("#{call_cli} process --reopt-feature --scenario #{test_scenario_reopt} --feature #{test_feature_pv}") expect((test_directory_pv / 'run' / 'reopt_scenario' / 'feature_optimization.csv').exist?).to be true # and visualize diff --git a/uo_cli.gemspec b/uo_cli.gemspec index 12590b6b..21748f5f 100644 --- a/uo_cli.gemspec +++ b/uo_cli.gemspec @@ -35,10 +35,10 @@ Gem::Specification.new do |spec| # use specific versions of urbanopt and openstudio dependencies while under heavy development spec.add_runtime_dependency 'optimist', '~> 3' spec.add_runtime_dependency 'urbanopt-geojson', '~> 0.11.1' - spec.add_runtime_dependency 'urbanopt-reopt', '~> 0.11.0' - spec.add_runtime_dependency 'urbanopt-reporting', '~> 0.9.1' + spec.add_runtime_dependency 'urbanopt-reopt', '~> 0.12.0' + spec.add_runtime_dependency 'urbanopt-reporting', '~> 0.10.0' spec.add_runtime_dependency 'urbanopt-rnm-us', '~> 0.7.0' - spec.add_runtime_dependency 'urbanopt-scenario', '~> 0.11.0' + spec.add_runtime_dependency 'urbanopt-scenario', '~> 0.12.0' spec.add_development_dependency 'bundler', '~> 2.1' spec.add_development_dependency 'rake', '~> 13.0'