diff --git a/.gitignore b/.gitignore index edda4228..49011c3f 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,6 @@ spec/test_directory_* Gemfile.lock .rubocop* + +# python dependencies +example_files/python_deps \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index def25a29..c4c14c08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## Version 0.8.1 +Date Range: 05/14/22 - 06/28/22: + +- Fixed [#343]( https://github.com/urbanopt/urbanopt-cli/issues/343 ), Carbon Emission Reporting +- Fixed [#329]( https://github.com/urbanopt/urbanopt-cli/issues/329 ), Better error messages for missing modelica files +- Fixed [#349]( https://github.com/urbanopt/urbanopt-cli/issues/349 ), num_parallel bug + ## Version 0.8.0 Date Range: 12/22/21 - 05/13/22: diff --git a/CMakeLists.txt b/CMakeLists.txt index 9869bbd1..c783defe 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.7.1) +project(URBANoptCLI VERSION 0.8.0) include(FindOpenStudioSDK.cmake) @@ -89,16 +89,16 @@ 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-20211227-darwin.tar.gz") - set(URBANOPT_CLI_GEMS_ZIP_EXPECTED_MD5 "9c09ef89a66acff72377fa052542f0df") + set(URBANOPT_CLI_GEMS_ZIP_FILENAME "urbanopt-cli-gems-20220520-darwin.tar.gz") + set(URBANOPT_CLI_GEMS_ZIP_EXPECTED_MD5 "9dea01c3a9664818a15ad34a5d37a155") else() - set(URBANOPT_CLI_GEMS_ZIP_FILENAME "urbanopt-cli-gems-20211227-linux.tar.gz") - set(URBANOPT_CLI_GEMS_ZIP_EXPECTED_MD5 "6094f724a143023c19ab28b65d26094d") + set(URBANOPT_CLI_GEMS_ZIP_FILENAME "urbanopt-cli-gems-20220520-linux.tar.gz") + set(URBANOPT_CLI_GEMS_ZIP_EXPECTED_MD5 "84678cac20979c63913c8e4f99f30e0e") endif() elseif(WIN32) if(CMAKE_CL_64) - set(URBANOPT_CLI_GEMS_ZIP_FILENAME "urbanopt-cli-gems-20211227-windows.tar.gz") - set(URBANOPT_CLI_GEMS_ZIP_EXPECTED_MD5 "e1e0b44997dfd188996ef3bc5aa31388") + set(URBANOPT_CLI_GEMS_ZIP_FILENAME "urbanopt-cli-gems-20220520-windows.tar.gz") + set(URBANOPT_CLI_GEMS_ZIP_EXPECTED_MD5 "e31a7f7c12bde79046e18f9d2a23f105") endif() endif() diff --git a/FindOpenStudioSDK.cmake b/FindOpenStudioSDK.cmake index 60bf5231..4fd44313 100644 --- a/FindOpenStudioSDK.cmake +++ b/FindOpenStudioSDK.cmake @@ -1,5 +1,5 @@ set(OPENSTUDIO_VERSION_MAJOR 3) -set(OPENSTUDIO_VERSION_MINOR 3) +set(OPENSTUDIO_VERSION_MINOR 4) set(OPENSTUDIO_VERSION_PATCH 0) set(OPENSTUDIO_VERSION "${OPENSTUDIO_VERSION_MAJOR}.${OPENSTUDIO_VERSION_MINOR}.${OPENSTUDIO_VERSION_PATCH}") @@ -16,23 +16,23 @@ else() set(OPENSTUDIO_BASELINK "https://openstudio-builds.s3.amazonaws.com/${OPENSTUDIO_VERSION}" CACHE STRING "Base link to where the openstudio archives are hosted" FORCE) - set(OPENSTUDIO_VERSION_SHA "+ad235ff36e") + set(OPENSTUDIO_VERSION_SHA "+4bd816f785") if(APPLE) - set(OPENSTUDIO_EXPECTED_HASH 77c2bf77f07dc2e2af7658df55940c49) + set(OPENSTUDIO_EXPECTED_HASH 5a1e5fdfc61a879a9d72dcf625a83e65) set(OPENSTUDIO_PLATFORM "Darwin") set(OPENSTUDIO_EXT "tar.gz") elseif(UNIX) if(LSB_RELEASE_VERSION_SHORT MATCHES "20.04") - set(OPENSTUDIO_EXPECTED_HASH 7fca6ef73399fdb21cde01aca4223d7c) + set(OPENSTUDIO_EXPECTED_HASH 1922de95bb3e196f1c719400ce58871c) set(OPENSTUDIO_PLATFORM "Ubuntu-20.04") else() # Assumes 18.04 - set(OPENSTUDIO_EXPECTED_HASH 24858ac666a3d2fc40d0cff7b4f444b9) + set(OPENSTUDIO_EXPECTED_HASH 44a837fa96fe2ce1a883492a3a1cae09) set(OPENSTUDIO_PLATFORM "Ubuntu-18.04") endif() set(OPENSTUDIO_EXT "tar.gz") elseif(WIN32) - set(OPENSTUDIO_EXPECTED_HASH f01ddb50a7ce1f4461e1919350e7c629) + set(OPENSTUDIO_EXPECTED_HASH 9adffb37a62721ec51a33ce97533e956) set(OPENSTUDIO_PLATFORM "Windows") set(OPENSTUDIO_EXT "tar.gz") endif() diff --git a/README.md b/README.md index d45839e0..6e389555 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Below are installation instructions for each platform. ### Linux (Ubuntu 18.04) -Download the .deb package. +Download the [.deb package](https://docs.urbanopt.net/installation/linux.html#install-with-the-urbanopt-installer). ```terminal sudo apt update @@ -52,7 +52,7 @@ When launching new shell terminals run `. ~/.env_uo.sh` to setup the environment ### Mac OSX (>= 10.12) -Download the .dmg package. +Download the [.dmg package](https://docs.urbanopt.net/installation/mac.html#install-with-the-urbanopt-installer). Use the GUI installer and choose a directory to install. Once installed, open a terminal and run the provided setup script. The `setup-env.sh` generates env variables and stores them in a file `.env_uo.sh` in your home directory. @@ -66,7 +66,7 @@ When launching new shell terminals run `. ~/.env_uo.s` to setup the environment. ### Windows (64-bit Windows 7 – 10) -Download the .exe installer. +Download the [.exe installer](https://docs.urbanopt.net/installation/windows.html#install-with-the-urbanopt-installer). Use the GUI installer and choose a directory to install. Once installed, open a terminal (Powershell, Windows CMD and GitBash are supported) and run the provided setup script for that shell (below are the setup scripts for each respective shell environment). diff --git a/example_files/Gemfile b/example_files/Gemfile index 15709069..5871183c 100644 --- a/example_files/Gemfile +++ b/example_files/Gemfile @@ -31,7 +31,7 @@ if allow_local && File.exist?('../openstudio-common-measures-gem') elsif allow_local gem 'openstudio-common-measures', github: 'NREL/openstudio-common-measures-gem', branch: 'develop' else - gem 'openstudio-common-measures', '~> 0.6.0' + gem 'openstudio-common-measures', '~> 0.6.1' end if allow_local && File.exist?('../openstudio-model-articulation-gem') @@ -67,22 +67,21 @@ else end -# TODO : Uncomment to revert changes once gem is released -if allow_local && File.exists?('../urbanopt-geojson-gem') - gem 'urbanopt-geojson', path: '../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' else - gem 'urbanopt-geojson', '~> 0.8.0' + gem 'urbanopt-geojson', '~> 0.8.1' end # NEVER put SCENARIO-GEM in this file...it will make all simulations fail due to the sqlite dependency # gem 'urbanopt-scenario', github: 'URBANopt/urbanopt-scenario-gem', branch: 'develop' if allow_local && File.exist?('../urbanopt-reporting-gem') - gem 'urbanopt-reporting', path: '../urbanopt-reporting-gem' + gem 'urbanopt-reporting', path: '../urbanopt-reporting-gem' elsif allow_local gem 'urbanopt-reporting', github: 'URBANopt/urbanopt-reporting-gem', branch: 'develop' else - gem 'urbanopt-reporting', '~> 0.6.0' + gem 'urbanopt-reporting', '~> 0.6.1' end diff --git a/example_files/example_project.json b/example_files/example_project.json index 6b82625b..e3746a52 100644 --- a/example_files/example_project.json +++ b/example_files/example_project.json @@ -13,7 +13,14 @@ "surface_elevation": null, "tariff_filename": null, "timesteps_per_hour": 1, - "weather_filename": "USA_NY_Buffalo-Greater.Buffalo.Intl.AP.725280_TMY3.epw" + "weather_filename": "USA_NY_Buffalo-Greater.Buffalo.Intl.AP.725280_TMY3.epw", + "emissions": true, + "electricity_emissions_future_subregion": "NYSTc", + "electricity_emissions_hourly_historical_subregion": "New York", + "electricity_emissions_annual_historical_subregion": "NYCW", + "electricity_emissions_future_year": "2030", + "electricity_emissions_hourly_historical_year": "2019", + "electricity_emissions_annual_historical_year": "2019" }, "scenarios": [ { @@ -164,10 +171,6 @@ "floor_area": 125631, "footprint_area": 41877, "number_of_stories": 3, - "emissions" : true, - "emissions_future_year": "2020", - "emissions_hourly_historical_year": "2019", - "emissions_annual_historical_year": "2019", "ev_charging": true, "ev_charging_station_type": "Typical Public" }, @@ -253,7 +256,6 @@ "floor_area": 8804, "footprint_area": 8804, "number_of_stories": 1, - "emissions" : false, "ev_charging": true, "ev_charging_station_type": "Typical Work" }, @@ -337,14 +339,7 @@ "building_type": "Office", "system_type": "VAV district chilled water with district hot water reheat", "number_of_stories": 6, - "detailed_model_filename": "7.osm", - "emissions" : true, - "emissions_future_subregion": "RMPAc", - "emissions_hourly_historical_subregion": "Rocky Mountains", - "emissions_annual_historical_subregion": "RMPA", - "emissions_future_year": "2020", - "emissions_hourly_historical_year": "2019", - "emissions_annual_historical_year": "2019" + "detailed_model_filename": "7.osm" }, "geometry": { "type": "Polygon", diff --git a/example_files/example_project_combined.json b/example_files/example_project_combined.json index bfd8444c..88e4e6c6 100644 --- a/example_files/example_project_combined.json +++ b/example_files/example_project_combined.json @@ -12,7 +12,14 @@ "begin_date": "2017-01-01T07:00:00.000Z", "end_date": "2017-12-31T07:00:00.000Z", "timesteps_per_hour": 1, - "default_template": "90.1-2013" + "default_template": "90.1-2013", + "emissions": true, + "electricity_emissions_future_subregion": "NYSTc", + "electricity_emissions_hourly_historical_subregion": "New York", + "electricity_emissions_annual_historical_subregion": "NYCW", + "electricity_emissions_future_year": "2030", + "electricity_emissions_hourly_historical_year": "2019", + "electricity_emissions_annual_historical_year": "2019" }, "features": [ { @@ -157,10 +164,6 @@ "weekday_duration": "11:30", "weekend_start_time": "11:00", "weekend_duration": "11:30", - "emissions" : true, - "emissions_future_year": "2020", - "emissions_hourly_historical_year": "2019", - "emissions_annual_historical_year": "2019", "ev_charging": true, "ev_charging_station_type": "Typical Public" }, @@ -249,7 +252,6 @@ "number_of_stories": 1, "weekday_start_time": "06:00", "weekday_duration": "16:00", - "emissions" : false, "ev_charging": true, "ev_charging_station_type": "Typical Work" }, @@ -329,14 +331,7 @@ "type": "Building", "building_type": "Office", "number_of_stories": 6, - "detailed_model_filename": "7.osm", - "emissions" : true, - "emissions_future_subregion": "RMPAc", - "emissions_hourly_historical_subregion": "Rocky Mountains", - "emissions_annual_historical_subregion": "RMPA", - "emissions_future_year": "2020", - "emissions_hourly_historical_year": "2019", - "emissions_annual_historical_year": "2019" + "detailed_model_filename": "7.osm" }, "geometry": { "type": "Polygon", @@ -655,14 +650,7 @@ "system_type": "Residential - furnace and central air conditioner", "heating_system_fuel_type": "natural gas", "onsite_parking_fraction": 1, - "template": "Residential IECC 2015 - Customizable Template Sep 2020", - "emissions" : true, - "emissions_future_subregion": "RMPAc", - "emissions_hourly_historical_subregion": "Rocky Mountains", - "emissions_annual_historical_subregion": "RMPA", - "emissions_future_year": "2020", - "emissions_hourly_historical_year": "2019", - "emissions_annual_historical_year": "2019" + "template": "Residential IECC 2015 - Customizable Template Sep 2020" }, "geometry": { "type": "Polygon", diff --git a/example_files/example_project_with_PV.json b/example_files/example_project_with_PV.json index 87875ff3..935615c7 100644 --- a/example_files/example_project_with_PV.json +++ b/example_files/example_project_with_PV.json @@ -13,7 +13,14 @@ "end_date": "2017-12-31T07:00:00.000Z", "timesteps_per_hour": 1, "default_template": "90.1-2013", - "tariff_filename": null + "tariff_filename": null, + "emissions": true, + "electricity_emissions_future_subregion": "NYSTc", + "electricity_emissions_hourly_historical_subregion": "New York", + "electricity_emissions_annual_historical_subregion": "NYCW", + "electricity_emissions_future_year": "2030", + "electricity_emissions_hourly_historical_year": "2019", + "electricity_emissions_annual_historical_year": "2019" }, "features": [ { diff --git a/example_files/example_project_with_electric_network.json b/example_files/example_project_with_electric_network.json index 7d3cec38..2299e6b1 100644 --- a/example_files/example_project_with_electric_network.json +++ b/example_files/example_project_with_electric_network.json @@ -13,7 +13,14 @@ "surface_elevation": null, "tariff_filename": null, "timesteps_per_hour": 1, - "weather_filename": "USA_NY_Buffalo-Greater.Buffalo.Intl.AP.725280_TMY3.epw" + "weather_filename": "USA_NY_Buffalo-Greater.Buffalo.Intl.AP.725280_TMY3.epw", + "emissions": true, + "electricity_emissions_future_subregion": "NYSTc", + "electricity_emissions_hourly_historical_subregion": "New York", + "electricity_emissions_annual_historical_subregion": "NYCW", + "electricity_emissions_future_year": "2030", + "electricity_emissions_hourly_historical_year": "2019", + "electricity_emissions_annual_historical_year": "2019" }, "scenarios": [ { diff --git a/example_files/example_project_with_streets.json b/example_files/example_project_with_streets.json index 3b5d66f0..6636c773 100644 --- a/example_files/example_project_with_streets.json +++ b/example_files/example_project_with_streets.json @@ -13,9 +13,16 @@ "end_date": "2017-12-31T07:00:00.000Z", "timesteps_per_hour": 1, "default_template": "90.1-2013", - "underground_cables_ratio": "0.9", + "underground_cables_ratio": 0.9, "only_lv_consumers": false, - "max_number_of_lv_nodes_per_building": 1 + "max_number_of_lv_nodes_per_building": 1, + "emissions": true, + "electricity_emissions_future_subregion": "NYSTc", + "electricity_emissions_hourly_historical_subregion": "New York", + "electricity_emissions_annual_historical_subregion": "NYCW", + "electricity_emissions_future_year": "2030", + "electricity_emissions_hourly_historical_year": "2019", + "electricity_emissions_annual_historical_year": "2019" }, "features": [ { diff --git a/example_files/mappers/Baseline.rb b/example_files/mappers/Baseline.rb index 0b345de8..8275d59f 100644 --- a/example_files/mappers/Baseline.rb +++ b/example_files/mappers/Baseline.rb @@ -37,11 +37,13 @@ require 'json' require 'rexml/document' +require 'logger' module URBANopt module Scenario class BaselineMapper < SimulationMapperBase # class level variables + @@logger = Logger.new($stdout) @@instance_lock = Mutex.new @@osw = nil @@geometry = nil @@ -383,222 +385,215 @@ def get_climate_zone_iecc(epw) end # epw_state to subregions mapping methods - #REK: Maybe we can move these method to the geojson gem + # REK: Maybe we can move these method to the geojson gem def get_future_emissions_region(feature) # Options are: AZNMc, CAMXc, ERCTc, FRCCc, MROEc, MROWc, NEWEc, NWPPc, NYSTc, RFCEc, RFCMc, RFCWc, RMPAc, SPNOc, SPSOc, SRMVc, SRMWc, SRSOc, SRTVc, and SRVCc # egrid subregions can map directly to zipcodes but not to states. Some state might include multiple egrid subregions. the default mapper prioritize the egrid subregion that is most common in the state (covers the biggest number of zipcodes) - future_emissions_mapping_hash = - {'FL': 'FRCCc', #['FRCCc', 'SRSOc'] - 'MS': 'SRMVc', #['SRMVc', 'SRTVc'] - 'NE': 'MROWc', #['MROWc', 'RMPAc'] - 'OR': 'NWPPc', - 'CA': 'CAMXc', #['CAMXc', 'NWPPc'] - 'VA': 'SRVCc', #['SRVCc', 'RFCWc', 'RFCEc'], - 'AR': 'SRMVc', #['SRMVc', 'SPSOc'] - 'TX': 'ERCTc', #['ERCTc', 'SRMVc', 'SPSOc', 'AZNMc'] - 'OH': 'RFCWc', - 'UT': 'NWPPc', - 'MT': 'NWPPc', #['NWPPc', 'MROWc'] - 'TN': 'SRTVc', - 'ID': 'NWPPc', - 'WI': 'MROEc', #['RFCWc', 'MROEc', 'MROWc'] - 'WV': 'RFCWc', - 'NC': 'SRVCc', - 'LA': 'SRMVc', - 'IL': 'SRMWc', #['RFCWc', 'SRMWc'] - 'OK': 'SPSOc', - 'IA': 'MROWc', - 'WA': 'NWPPc', - 'SD': 'MROWc', #['MROWc', 'RMPAc'] - 'MN': 'MROWc', - 'KY': 'SRTVc', #['SRTVc', 'RFCWc'] - 'MI': 'RFCMc', #['RFCMc', 'MROEc'] - 'KS': 'SPNOc', - 'NJ': 'RFCEc', - 'NY': 'NYSTc', - 'IN': 'RFCWc', - 'VT': 'NEWEc', - 'NM': 'AZNMc', #['AZNMc', 'SPSOc'] - 'WY': 'RMPAc', #['RMPAc', 'NWPPc'] - 'GA': 'SRSOc', - 'MO': 'SRMWc', #['SRMWc', 'SPNOc'] - 'DC': 'RFCEc', - 'SC': 'SRVCc', - 'PA': 'RFCEc', #['RFCEc', 'RFCWc'] - 'CO': 'RMPAc', - 'AZ': 'AZNMc', - 'ME': 'NEWEc', - 'AL': 'SRSOc', - 'MD': 'RFCEc', #['RFCEc', 'RFCWc'] - 'NH': 'NEWEc', - 'MA': 'NEWEc', - 'ND': 'MROWc', - 'NV': 'NWPPc', #['NWPPc', 'AZNMc'] - 'CT': 'NEWEc', - 'DE': 'RFCEc', - 'RI': 'NEWEc'} - - #get the state from weather file + future_emissions_mapping_hash = + { 'FL': 'FRCCc', # ['FRCCc', 'SRSOc'] + 'MS': 'SRMVc', # ['SRMVc', 'SRTVc'] + 'NE': 'MROWc', # ['MROWc', 'RMPAc'] + 'OR': 'NWPPc', + 'CA': 'CAMXc', # ['CAMXc', 'NWPPc'] + 'VA': 'SRVCc', # ['SRVCc', 'RFCWc', 'RFCEc'], + 'AR': 'SRMVc', # ['SRMVc', 'SPSOc'] + 'TX': 'ERCTc', # ['ERCTc', 'SRMVc', 'SPSOc', 'AZNMc'] + 'OH': 'RFCWc', + 'UT': 'NWPPc', + 'MT': 'NWPPc', # ['NWPPc', 'MROWc'] + 'TN': 'SRTVc', + 'ID': 'NWPPc', + 'WI': 'MROEc', # ['RFCWc', 'MROEc', 'MROWc'] + 'WV': 'RFCWc', + 'NC': 'SRVCc', + 'LA': 'SRMVc', + 'IL': 'SRMWc', # ['RFCWc', 'SRMWc'] + 'OK': 'SPSOc', + 'IA': 'MROWc', + 'WA': 'NWPPc', + 'SD': 'MROWc', # ['MROWc', 'RMPAc'] + 'MN': 'MROWc', + 'KY': 'SRTVc', # ['SRTVc', 'RFCWc'] + 'MI': 'RFCMc', # ['RFCMc', 'MROEc'] + 'KS': 'SPNOc', + 'NJ': 'RFCEc', + 'NY': 'NYSTc', + 'IN': 'RFCWc', + 'VT': 'NEWEc', + 'NM': 'AZNMc', # ['AZNMc', 'SPSOc'] + 'WY': 'RMPAc', # ['RMPAc', 'NWPPc'] + 'GA': 'SRSOc', + 'MO': 'SRMWc', # ['SRMWc', 'SPNOc'] + 'DC': 'RFCEc', + 'SC': 'SRVCc', + 'PA': 'RFCEc', # ['RFCEc', 'RFCWc'] + 'CO': 'RMPAc', + 'AZ': 'AZNMc', + 'ME': 'NEWEc', + 'AL': 'SRSOc', + 'MD': 'RFCEc', # ['RFCEc', 'RFCWc'] + 'NH': 'NEWEc', + 'MA': 'NEWEc', + 'ND': 'MROWc', + 'NV': 'NWPPc', # ['NWPPc', 'AZNMc'] + 'CT': 'NEWEc', + 'DE': 'RFCEc', + 'RI': 'NEWEc' } + + # get the state from weather file state = feature.weather_filename.split('_', -1)[1] - - #find region input based on the state + + # find region input based on the state region = future_emissions_mapping_hash[state.to_sym] - puts "emissions_future_subregion for #{state} is assigned to: #{region}" - puts "You can overwrite this assigned input by specifiying the emissions_future_subregion input in the FeatureFile" + @@logger.warn("emissions_future_subregion for #{state} is assigned to: #{region}. Note: Not all states have a 1 to 1 mapping with a subregion. Some states('ND','IN', 'MN', 'SD', 'IA', 'WV', 'OH', 'NE' ) include 2 subregions. + The default mapper maps to the subregion that includes the most zipcodes in the corresponding state. You can overwrite this assigned input by specifiying the emissions_future_subregion input in the FeatureFile.") return region - end def get_hourly_historical_emissions_region(feature) - # Options are: California, Carolinas, Central, Florida, Mid-Atlantic, Midwest, New England, New York, Northwest, Rocky Mountains, Southeast, Southwest, Tennessee, and Texas - # There is no "correct" mapping of eGrid to AVERT regions as they are both large geographical areas that partially overlap. - # Mapping is done using mapping tools from eGrid and AVERT (ZipCode for eGrid and fraction of state for AVERT). + # There is no "correct" mapping of eGrid to AVERT regions as they are both large geographical areas that partially overlap. + # Mapping is done using mapping tools from eGrid and AVERT (ZipCode for eGrid and fraction of state for AVERT). # Mapped based on the maps of each set of regions: - hourly_historical_mapping_hash = - {'FL': 'Florida', - 'MS': 'Midwest', - 'NE': 'Midwest',#MRWO could be Midwest / Central - 'OR': 'Northwest', - 'CA': 'California', - 'VA': 'Carolinas', - 'AR': 'Midwest', - 'TX': 'Texas', - 'OH': 'Midwest',#RFCW could be Midwest / Mid Atlantic - 'UT': 'Northwest', - 'MT': 'Northwest', - 'TN': 'Tennessee', - 'ID': 'Northwest', - 'WI': 'Midwest', - 'WV': 'Midwest', #RFCW could be Midwest / Mid Atlantic - 'NC': 'Carolinas', - 'LA': 'Midwest', - 'IL': 'Midwest', - 'OK': 'Central', - 'IA': 'Midwest', #MRWO could be Midwest / Central - 'WA': 'Northwest', - 'SD': 'Midwest',#MRWO could be Midwest / Central - 'MN': 'Midwest',#MRWO could be Midwest / Central - 'KY': 'Tennessee', - 'MI': 'Midwest', - 'KS': 'Central', - 'NJ': 'Mid-Atlantic', - 'NY': 'New York', - 'IN': 'Midwest', #RFCW could be Midwest / Mid Atlantic - 'VT': 'New England', - 'NM': 'Southwest', - 'WY': 'Rocky Mountains', - 'GA': 'SRSO', - 'MO': 'Midwest', - 'DC': 'Mid-Atlantic', - 'SC': 'Carolinas', - 'PA': 'Mid-Atlantic', - 'CO': 'Rocky Mountains', - 'AZ': 'Southwest', - 'ME': 'New England', - 'AL': 'Southeast', - 'MD': 'Mid-Atlantic', - 'NH': 'New England', - 'MA': 'New England', - 'ND': 'Midwest',#MRWO could be Midwest / Central - 'NV': 'Northwest', - 'CT': 'New England', - 'DE': 'Mid-Atlantic', - 'RI': 'New England'} - - #get the state from weather file + hourly_historical_mapping_hash = + { 'FL': 'Florida', + 'MS': 'Midwest', + 'NE': 'Midwest', # MRWO could be Midwest / Central + 'OR': 'Northwest', + 'CA': 'California', + 'VA': 'Carolinas', + 'AR': 'Midwest', + 'TX': 'Texas', + 'OH': 'Midwest', # RFCW could be Midwest / Mid Atlantic + 'UT': 'Northwest', + 'MT': 'Northwest', + 'TN': 'Tennessee', + 'ID': 'Northwest', + 'WI': 'Midwest', + 'WV': 'Midwest', # RFCW could be Midwest / Mid Atlantic + 'NC': 'Carolinas', + 'LA': 'Midwest', + 'IL': 'Midwest', + 'OK': 'Central', + 'IA': 'Midwest', # MRWO could be Midwest / Central + 'WA': 'Northwest', + 'SD': 'Midwest', # MRWO could be Midwest / Central + 'MN': 'Midwest', # MRWO could be Midwest / Central + 'KY': 'Tennessee', + 'MI': 'Midwest', + 'KS': 'Central', + 'NJ': 'Mid-Atlantic', + 'NY': 'New York', + 'IN': 'Midwest', # RFCW could be Midwest / Mid Atlantic + 'VT': 'New England', + 'NM': 'Southwest', + 'WY': 'Rocky Mountains', + 'GA': 'SRSO', + 'MO': 'Midwest', + 'DC': 'Mid-Atlantic', + 'SC': 'Carolinas', + 'PA': 'Mid-Atlantic', + 'CO': 'Rocky Mountains', + 'AZ': 'Southwest', + 'ME': 'New England', + 'AL': 'Southeast', + 'MD': 'Mid-Atlantic', + 'NH': 'New England', + 'MA': 'New England', + 'ND': 'Midwest', # MRWO could be Midwest / Central + 'NV': 'Northwest', + 'CT': 'New England', + 'DE': 'Mid-Atlantic', + 'RI': 'New England' } + + # get the state from weather file state = feature.weather_filename.split('_', -1)[1] - #find region input based on the state - region = hourly_historical_mapping_hash[state.to_sym] - puts "emissions_hourly_historical_subregion for #{state} is assigned to: #{region}" - puts "You can overwrite this assigned input by specifiying the emissions_hourly_historical_subregion input in the FeatureFile" - - return region + # find region input based on the state + region = hourly_historical_mapping_hash[state.to_sym] + @@logger.warn("emissions_hourly_historical_subregion for #{state} is assigned to: #{region}. Note: Not all states have a 1 to 1 mapping with a subregion. Some states('ND','IN', 'MN', 'SD', 'IA', 'WV', 'OH', 'NE' ) include 2 subregions. + The default mapper maps to the subregion that includes the most zipcodes in the corresponding state. You can overwrite this assigned input by specifiying the emissions_hourly_historical_subregion input in the FeatureFile.") + return region end - def get_annual_historical_emissions_region(feature) - # Options are: AKGD, AKMS, AZNM, CAMX, ERCT, FRCC, HIMS, HIOA, MROE, MROW, NEWE, NWPP, NYCW, NYLI, NYUP, RFCE, RFCM, RFCW, RMPA, SPNO, SPSO, SRMV, SRMW, SRSO, SRTV, and SRVC # egrid subregions can map directly to zipcodes but not to states. Some state might include multiple egrid subregions. the default mapper prioritize the egrid subregion that is most common in the state (covers the biggest number of zipcodes) - annual_historical_mapping_hash = - {'FL': 'FRCC', - 'MS': 'SRMV', - 'NE': 'MROW', - 'OR': 'NWPP', - 'CA': 'CAMX', - 'VA': 'SRVC', - 'AR': 'SRMV', - 'TX': 'ERCT', - 'OH': 'RFCW', - 'UT': 'NWPP', - 'MT': 'NWPP', - 'TN': 'SRTV', - 'ID': 'NWPP', - 'WI': 'MROE', - 'WV': 'RFCW', - 'NC': 'SRVC', - 'LA': 'SRMV', - 'IL': 'SRMW', - 'OK': 'SPSO', - 'IA': 'MROW', - 'WA': 'NWPP', - 'SD': 'MROW', - 'MN': 'MROW', - 'KY': 'SRTV', - 'MI': 'RFCM', - 'KS': 'SPNO', - 'NJ': 'RFCE', - 'NY': 'NYCW', - 'IN': 'RFCW', - 'VT': 'NEWE', - 'NM': 'AZNM', - 'WY': 'RMPA', - 'GA': 'SRSO', - 'MO': 'SRMW', - 'DC': 'RFCE', - 'SC': 'SRVC', - 'PA': 'RFCE', - 'CO': 'RMPA', - 'AZ': 'AZNM', - 'ME': 'NEWE', - 'AL': 'SRSO', - 'MD': 'RFCE', - 'NH': 'NEWE', - 'MA': 'NEWE', - 'ND': 'MROW', - 'NV': 'NWPP', - 'CT': 'NEWE', - 'DE': 'RFCE', - 'RI': 'NEWE'} - #get the state from weather file + annual_historical_mapping_hash = + { 'FL': 'FRCC', + 'MS': 'SRMV', + 'NE': 'MROW', + 'OR': 'NWPP', + 'CA': 'CAMX', + 'VA': 'SRVC', + 'AR': 'SRMV', + 'TX': 'ERCT', + 'OH': 'RFCW', + 'UT': 'NWPP', + 'MT': 'NWPP', + 'TN': 'SRTV', + 'ID': 'NWPP', + 'WI': 'MROE', + 'WV': 'RFCW', + 'NC': 'SRVC', + 'LA': 'SRMV', + 'IL': 'SRMW', + 'OK': 'SPSO', + 'IA': 'MROW', + 'WA': 'NWPP', + 'SD': 'MROW', + 'MN': 'MROW', + 'KY': 'SRTV', + 'MI': 'RFCM', + 'KS': 'SPNO', + 'NJ': 'RFCE', + 'NY': 'NYCW', + 'IN': 'RFCW', + 'VT': 'NEWE', + 'NM': 'AZNM', + 'WY': 'RMPA', + 'GA': 'SRSO', + 'MO': 'SRMW', + 'DC': 'RFCE', + 'SC': 'SRVC', + 'PA': 'RFCE', + 'CO': 'RMPA', + 'AZ': 'AZNM', + 'ME': 'NEWE', + 'AL': 'SRSO', + 'MD': 'RFCE', + 'NH': 'NEWE', + 'MA': 'NEWE', + 'ND': 'MROW', + 'NV': 'NWPP', + 'CT': 'NEWE', + 'DE': 'RFCE', + 'RI': 'NEWE' } + # get the state from weather file state = feature.weather_filename.split('_', -1)[1] - - #finf region input based on the state + + # finf region input based on the state region = annual_historical_mapping_hash[state.to_sym] - puts "emissions_annual_historical_subregion for #{state} is assigned to: #{region}" - puts "You can overwrite this assigned input by specifiying the emissions_annual_historical_subregion input in the FeatureFile" - + @@logger.warn("electricity_emissions_annual_historical_subregion for #{state} is assigned to: #{region}. Note: Not all states have a 1 to 1 mapping with a subregion. Some states('ND','IN', 'MN', 'SD', 'IA', 'WV', 'OH', 'NE' ) include 2 subregions. + The default mapper maps to the subregion that includes the most zipcodes in the corresponding state. You can overwrite this assigned input by specifiying the electricity_emissions_annual_historical_subregion input in the FeatureFile.") + return region + end - end - - def is_defined(feature, method_name, raise_error=true) - begin - if feature.method_missing(method_name) - return true - end - rescue NoMethodError - if raise_error - raise "*** ERROR *** #{method_name} is not set on this feature" - end - return false + def is_defined(feature, method_name, raise_error = true) + if feature.method_missing(method_name) + return true end + rescue NoMethodError + if raise_error + raise "*** ERROR *** #{method_name} is not set on this feature" + end + + return false end def create_osw(scenario, features, feature_names) @@ -641,8 +636,8 @@ def create_osw(scenario, features, feature_names) # Check for required residential fields is_defined(feature, :number_of_stories_above_ground) is_defined(feature, :foundation_type) - - if not is_defined(feature, :hpxml_directory, false) + + if !is_defined(feature, :hpxml_directory, false) # check additional fields when HPXML dir is not given is_defined(feature, :attic_type) is_defined(feature, :number_of_bedrooms) @@ -782,7 +777,7 @@ def create_osw(scenario, features, feature_names) args[:geometry_unit_num_occupants] = 'auto' begin - args[:geometry_unit_num_occupants] = "#{feature.number_of_occupants / args[:geometry_building_num_units]}" + args[:geometry_unit_num_occupants] = (feature.number_of_occupants / args[:geometry_building_num_units]).to_s rescue StandardError end @@ -1018,11 +1013,10 @@ def create_osw(scenario, features, feature_names) elsif commercial_building_types.include? building_type # set_run_period OpenStudio::Extension.set_measure_argument(osw, 'set_run_period', '__SKIP__', false) - # can enable reporting (commercial building types only for now) - #OpenStudio::Extension.set_measure_argument(osw, 'openstudio_results', '__SKIP__', false) - #OpenStudio::Extension.set_measure_argument(osw, 'envelope_and_internal_load_breakdown', '__SKIP__', false) - #OpenStudio::Extension.set_measure_argument(osw, 'generic_qaqc', '__SKIP__', false) + # OpenStudio::Extension.set_measure_argument(osw, 'openstudio_results', '__SKIP__', false) + # OpenStudio::Extension.set_measure_argument(osw, 'envelope_and_internal_load_breakdown', '__SKIP__', false) + # OpenStudio::Extension.set_measure_argument(osw, 'generic_qaqc', '__SKIP__', false) begin timesteps_per_hour = feature.timesteps_per_hour @@ -1353,111 +1347,144 @@ def time_mapping(time) else raise "Building type #{building_type} not currently supported." end + end - - ####### Emissions Adition + + ######## Emissions Adition from add_ems_emissions_reporting if feature_type == 'Building' + # emissions options + future_regions = ['AZNMc', 'CAMXc', 'ERCTc', 'FRCCc', 'MROEc', 'MROWc', 'NEWEc', 'NWPPc', 'NYSTc', 'RFCEc', 'RFCMc', 'RFCWc', 'RMPAc', 'SPNOc', 'SPSOc', 'SRMVc', 'SRMWc', 'SRSOc', 'SRTVc', 'SRVCc'] + hourly_historical_regions = ['California', 'Carolinas', 'Central', 'Florida', 'Mid-Atlantic', 'Midwest', 'New England', 'New York', 'Northwest', 'Rocky Mountains', 'Southeast', 'Southwest', 'Tennessee', 'Texas'] + annual_historical_regions = ['AKGD', 'AKMS', 'AZNM', 'CAMX', 'ERCT', 'FRCC', 'HIMS', 'HIOA', 'MROE', 'MROW', 'NEWE', 'NWPP', 'NYCW', 'NYLI', 'NYUP', 'RFCE', 'RFCM', 'RFCW', 'RMPA', 'SPNO', 'SPSO', 'SRMV', 'SRMW', 'SRSO', 'SRTV', 'SRVC'] + annual_historical_years = ['2007', '2009', '2010', '2012', '2014', '2016', '2018', '2019'] + future_years = ['2020', '2022', '2024', '2026', '2028', '2030', '2032', '2034', '2036', '2038', '2040', '2042', '2044', '2046', '2048', '2050'] + hourly_historical_years = ['2019'] + # add Emissions emissions = nil begin emissions = feature.emissions - rescue + rescue StandardError end - + if emissions != true - puts "Emissions is not activated for this feature. Please set emissions to true in the the Feature properties in the GeoJSON file to add emissions results." + @@logger.info('Emissions is not activated for this feature. Please set emissions to true in the the Feature properties in the GeoJSON file to add emissions results.') elsif emissions == true - - #activate emissions measure + + # activate emissions measure OpenStudio::Extension.set_measure_argument(osw, 'add_ems_emissions_reporting', '__SKIP__', false) - #get emissions inputs if they are available or get them from the mapping methods if the are not + # get emissions inputs if they are available or get them from the mapping methods if the are not begin - emissions_future_subregion = feature.emissions_future_subregion - rescue - puts "\nemission_future_subregion is not assigned for feature #{feature_id}. Defining subregion based on the State...." - emissions_future_subregion = get_future_emissions_region(feature) + electricity_emissions_future_subregion = feature.electricity_emissions_future_subregion + rescue StandardError + @@logger.info("\nelectricity_emission_future_subregion is not assigned for feature #{feature_id}. Defining subregion based on the State....") + electricity_emissions_future_subregion = get_future_emissions_region(feature) end begin - emissions_hourly_historical_subregion = feature.emissions_hourly_historical_subregion - rescue - puts "\nemissions_hourly_historical_subregion is not assigned for feature #{feature_id}. Defining subregion based on the State...." - emissions_hourly_historical_subregion = get_hourly_historical_emissions_region(feature) + electricity_emissions_hourly_historical_subregion = feature.electricity_emissions_hourly_historical_subregion + rescue StandardError + @@logger.info("\nelectricity_emissions_hourly_historical_subregion is not assigned for feature #{feature_id}. Defining subregion based on the State....") + electricity_emissions_hourly_historical_subregion = get_hourly_historical_emissions_region(feature) end begin - emissions_annual_historical_subregion = feature.emissions_annual_historical_subregion - rescue - puts "\nemissions_annual_historical_subregion is not assigned for feature #{feature_id}. Defining subregion based on the State...." - emissions_annual_historical_subregion = get_annual_historical_emissions_region(feature) + electricity_emissions_annual_historical_subregion = feature.electricity_emissions_annual_historical_subregion + rescue StandardError + @@logger.info("\nelectricity_emissions_annual_historical_subregion is not assigned for feature #{feature_id}. Defining subregion based on the State....") + electricity_emissions_annual_historical_subregion = get_annual_historical_emissions_region(feature) end begin - emissions_future_year = feature.emissions_future_year - rescue - puts "emissions_future_year should be assigned !" + electricity_emissions_future_year = feature.electricity_emissions_future_year + rescue StandardError + @@logger.info("\nelectricity_emissions_future_year was not assigned by the user. The assigned default value is 2030") + electricity_emissions_future_year = '2030' end begin - emissions_hourly_historical_year = feature.emissions_hourly_historical_year - rescue - puts "emissions_hourly_historical_year should be assigned !" + electricity_emissions_hourly_historical_year = feature.electricity_emissions_hourly_historical_year + rescue StandardError + @@logger.info("\nelectricity_emissions_hourly_historical_year was not assigned by the user. The assigned default value is 2019") + electricity_emissions_hourly_historical_year = '2019' end begin - emissions_annual_historical_year = feature.emissions_annual_historical_year - rescue - puts "emissions_annual_historical_year should be assigned !" + electricity_emissions_annual_historical_year = feature.electricity_emissions_annual_historical_year + rescue StandardError + @@logger.info("\nelectricity_emissions_annual_historical_year was not assigned by the user. The assigned default value is 2019") + electricity_emissions_annual_historical_year = '2019' end - puts "\n building #{feature_id} emission inputs summarry: - emissions_future_subregion = #{emissions_future_subregion}; - emissions_hourly_historical_subregion = #{emissions_hourly_historical_subregion}; - emissions_annual_historical_subregion = #{emissions_annual_historical_subregion}; - emissions_future_year = #{emissions_future_year}; - emissions_hourly_historical_year = #{emissions_hourly_historical_year}; - emissions_annual_historical_year = #{emissions_annual_historical_year} \n " + # puts "\n building #{feature_id} emission inputs summarry: + # electricity_emissions_future_subregion = #{electricity_emissions_future_subregion}; + # electricity_emissions_hourly_historical_subregion = #{electricity_emissions_hourly_historical_subregion}; + # electricity_emissions_annual_historical_subregion = #{electricity_emissions_annual_historical_subregion}; + # electricity_emissions_future_year = #{electricity_emissions_future_year}; + # electricity_emissions_hourly_historical_year = #{electricity_emissions_hourly_historical_year}; + # electricity_emissions_annual_historical_year = #{electricity_emissions_annual_historical_year}\n " ## Assign the OS measure arguments begin - - #emissions_future_subregion - if !emissions_future_subregion.nil? && !emissions_future_subregion.empty? - OpenStudio::Extension.set_measure_argument(osw, 'add_ems_emissions_reporting', 'future_subregion', emissions_future_subregion) + # emissions_future_subregion + if !electricity_emissions_future_subregion.nil? && !electricity_emissions_future_subregion.empty? + if future_regions.include? electricity_emissions_future_subregion + OpenStudio::Extension.set_measure_argument(osw, 'add_ems_emissions_reporting', 'future_subregion', electricity_emissions_future_subregion) + else + @@logger.error(" '#{electricity_emissions_future_subregion}' is not valid option for electricity_emissions_future_subregion. Please choose an input from #{future_regions}") + end end - #hourly_historical_subregion - if !emissions_hourly_historical_subregion.nil? && !emissions_hourly_historical_subregion.empty? - OpenStudio::Extension.set_measure_argument(osw, 'add_ems_emissions_reporting', 'hourly_historical_subregion', emissions_hourly_historical_subregion) + # hourly_historical_subregion + if !electricity_emissions_hourly_historical_subregion.nil? && !electricity_emissions_hourly_historical_subregion.empty? + if hourly_historical_regions.include? electricity_emissions_hourly_historical_subregion + OpenStudio::Extension.set_measure_argument(osw, 'add_ems_emissions_reporting', 'hourly_historical_subregion', electricity_emissions_hourly_historical_subregion) + else + @@logger.error(" '#{electricity_emissions_hourly_historical_subregion}' is not valid option for electricity_emissions_hourly_historical_subregion. Please choose an input from #{hourly_historical_regions}") + end end - #annual_historical_subregion - if !emissions_annual_historical_subregion.nil? && !emissions_annual_historical_subregion.empty? - OpenStudio::Extension.set_measure_argument(osw, 'add_ems_emissions_reporting', 'annual_historical_subregion', emissions_annual_historical_subregion) + # annual_historical_subregion + if !electricity_emissions_annual_historical_subregion.nil? && !electricity_emissions_annual_historical_subregion.empty? + if annual_historical_regions.include? electricity_emissions_annual_historical_subregion + OpenStudio::Extension.set_measure_argument(osw, 'add_ems_emissions_reporting', 'annual_historical_subregion', electricity_emissions_annual_historical_subregion) + else + @@logger.error(" '#{electricity_emissions_annual_historical_subregion}' is not valid option for electricity_emissions_annual_historical_subregion. Please choose an input from #{annual_historical_regions}") + end end - #future_year - if !emissions_future_year.nil? && !emissions_future_year.empty? - OpenStudio::Extension.set_measure_argument(osw, 'add_ems_emissions_reporting', 'future_year', emissions_future_year) + # future_year + if !electricity_emissions_future_year.nil? && !electricity_emissions_future_year.empty? + + if future_years.include? electricity_emissions_future_year + OpenStudio::Extension.set_measure_argument(osw, 'add_ems_emissions_reporting', 'future_year', electricity_emissions_future_year) + else + @@logger.error(" '#{electricity_emissions_future_year}' is not valid option for electricity_emissions_future_year. Please choose an input from #{future_years}") + end end - #hourly_historical_year - if !emissions_hourly_historical_year.nil? && !emissions_hourly_historical_year.empty? - OpenStudio::Extension.set_measure_argument(osw, 'add_ems_emissions_reporting', 'hourly_historical_year', emissions_hourly_historical_year) - else - + # hourly_historical_year + if !electricity_emissions_hourly_historical_year.nil? && !electricity_emissions_hourly_historical_year.empty? + if hourly_historical_years.include? electricity_emissions_hourly_historical_year + OpenStudio::Extension.set_measure_argument(osw, 'add_ems_emissions_reporting', 'hourly_historical_year', electricity_emissions_hourly_historical_year) + else + @@logger.error(" '#{electricity_emissions_hourly_historical_year}' is not valid option for electricity_emissions_hourly_historical_year. Please choose an input from #{hourly_historical_years}") + end end - - #annual_historical_year' - if !emissions_annual_historical_year.nil? && !emissions_annual_historical_year.empty? - OpenStudio::Extension.set_measure_argument(osw, 'add_ems_emissions_reporting', 'annual_historical_year', emissions_annual_historical_year) - end - rescue + # annual_historical_year + if !electricity_emissions_annual_historical_year.nil? && !electricity_emissions_annual_historical_year.empty? + if annual_historical_years.include? electricity_emissions_annual_historical_year + OpenStudio::Extension.set_measure_argument(osw, 'add_ems_emissions_reporting', 'annual_historical_year', electricity_emissions_annual_historical_year) + else + @@logger.error("'#{electricity_emissions_annual_historical_year}' is not valid option for electricity_emissions_annual_historical_year. Please choose an input from #{annual_historical_years}") + end + end + rescue StandardError end end diff --git a/example_files/validation_schema.yaml b/example_files/validation_schema.yaml index 2f4daf48..8f13b06c 100644 --- a/example_files/validation_schema.yaml +++ b/example_files/validation_schema.yaml @@ -19,7 +19,7 @@ EUI: min: 10 max: 60 Office: - min: 40 + min: 30 max: 75 Laboratory: min: 100 @@ -35,7 +35,7 @@ EUI: max: 75 Outpatient health care: min: 50 - max: 100 + max: 130 Refrigerated warehouse: min: 75 max: 100 @@ -52,7 +52,7 @@ EUI: min: 200 max: 500 Inpatient health care: - min: 200 + min: 100 max: 300 Nursing: min: 50 @@ -61,7 +61,7 @@ EUI: min: 50 max: 100 Strip shopping mall: - min: 80 + min: 50 max: 120 Enclosed mall: min: 50 @@ -73,8 +73,8 @@ EUI: min: 30 max: 75 Mixed use: - min: 10 - max: 75 + min: 30 + max: 250 SI: Units: "kWh/m2/yr" @@ -91,7 +91,7 @@ EUI: min: 30 max: 200 Office: - min: 126 + min: 100 max: 237 Laboratory: min: 315 @@ -107,7 +107,7 @@ EUI: max: 237 Outpatient health care: min: 157 - max: 315 + max: 410 Refrigerated warehouse: min: 236 max: 315 @@ -124,7 +124,7 @@ EUI: min: 630 max: 1577 Inpatient health care: - min: 630 + min: 315 max: 946 Nursing: min: 157 @@ -133,7 +133,7 @@ EUI: min: 157 max: 315 Strip shopping mall: - min: 252 + min: 157 max: 379 Enclosed mall: min: 157 @@ -145,5 +145,5 @@ EUI: min: 94 max: 237 Mixed use: - min: 31 + min: 100 max: 789 diff --git a/example_files/visualization/input_visualization_feature.html b/example_files/visualization/input_visualization_feature.html index 80136484..0d430ca7 100644 --- a/example_files/visualization/input_visualization_feature.html +++ b/example_files/visualization/input_visualization_feature.html @@ -217,6 +217,45 @@

Annual End Uses

+
+

Emissions

+

Multiple emissions associated with electricity may be included in the graphs below and represent different calculation approaches (e.g., future hourly and historical average). Electricity emissions values from different calculation approaches should NOT be summed together.

+

Emissions Per Feature

+ +
+ + + + {{ feature.name }} + Emissions per Fuel (mt CO2e) + {{errorText}} + + + + + + +
+
+
+
+

Emissions Per Fuel Type

+ +
+ + + + {{ chart.name | strReplace:'_':' ' | strReplace:'Emissions':'' | strReplace:'Intensity':''}} + Emissions Intensity per Feature (kg CO2e / ft2) + + + + + + +
+
+
@@ -376,10 +415,113 @@

Annual End Uses

} }; + //'#AFECE7' + $scope.defaultAnnualEmissionsChartOptions = { + chart: { + type: 'multiBarChart', + color: ['#156666', '#187C7C', '#20a0a0', '#27c6c6', '#8CC025', '#CE2828', '#F19953', '#1F77B4', '#9467BD'], + height: 320, + margin: { + top: 45, + right: 2, + bottom: 32, + left: 64 + }, + clipEdge: true, + groupSpacing: 0.3, + reduceXTicks: false, + stacked: false, + duration: 250, + x: function (d) { + return d.x; + }, + y: function (d) { + return d.y; + }, + yAxis: { + tickFormat: function (d) { + return d; + } + }, + showControls: false, + legend: { + margin: { + top: 8, + bottom: 32, + left: 250, + right: 10 + }, + maxKeyLength: 100 + }, + tooltip: { + gravity: 's', + classes: 'gravity-s' + } + } + }; + + $scope.defaultAnnualIntensityChartOptions = { + chart: { + type: 'multiBarChart', + color: ['#187C7C', '#8CC025', '#CE2828', '#AFECE7', '#F19953', '#1F77B4', '#9467BD'], + height: 320, + margin: { + top: 45, + right: 2, + bottom: 32, + left: 64 + }, + clipEdge: true, + groupSpacing: 0.3, + reduceXTicks: false, + stacked: false, + duration: 250, + x: function (d) { + return d.x; + }, + y: function (d) { + return d.y; + }, + yAxis: { + tickFormat: function (d) { + return d; + } + }, + showControls: false, + showLegend: false, + tooltip: { + gravity: 's', + classes: 'gravity-s' + } + } + }; + // use different options for each feature $scope.monthlyFuelChartOptions = {}; $scope.monthlyNetChartOptions = {}; $scope.annualEndUseChartOptions = {}; + $scope.annualEmissionsChartOptions = {}; + $scope.annualIntensityChartOptions = {}; + + $scope.emissionCharts = [ + {'name': 'Future_Annual_Electricity_Emissions', 'units': 'mt CO2e', 'short': 'FA Elec', 'display': 'Future Annual Elec' }, + {'name': 'Future_Hourly_Electricity_Emissions','units': 'mt CO2e', 'short': 'FH Elec', 'display': 'Future Hourly Elec' }, + {'name': 'Historical_Annual_Electricity_Emissions','units': 'mt CO2e', 'short': 'HA Elec' , 'display': 'Historical Annual Elec'}, + {'name': 'Historical_Hourly_Electricity_Emissions','units': 'mt CO2e', 'short': 'HH Elec', 'display': 'Historical Hourly Elec' }, + {'name': 'Natural_Gas_Emissions', 'units': 'mt CO2e', 'short': 'Gas', 'display': 'Natural Gas' }, + {'name': 'Propane_Emissions', 'units': 'mt CO2e', 'short': 'Propane', 'display': 'Propane'}, + {'name': 'FuelOilNo2_Emissions', 'units': 'mt CO2e', 'short': 'FuelOil2', 'display': 'Fuel Oil No2' } + ]; + + $scope.intensityCharts = [ + {'name': 'Future_Annual_Electricity_Emissions_Intensity', 'units': 'kg CO2e / ft2' }, + {'name': 'Future_Hourly_Electricity_Emissions_Intensity', 'units': 'kg CO2e / ft2' }, + {'name': 'Historical_Annual_Electricity_Emissions_Intensity', 'units': 'kg CO2e / ft2' }, + {'name': 'Historical_Hourly_Electricity_Emissions_Intensity', 'units': 'kg CO2e / ft2' }, + {'name': 'Natural_Gas_Emissions_Intensity', 'units': 'kg CO2e / ft2' }, + {'name': 'Propane_Emissions_Intensity', 'units': 'kg CO2e / ft2' }, + {'name': 'FuelOilNo2_Emissions_Intensity', 'units': 'kg CO2e / ft2' } + ]; var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; @@ -388,33 +530,47 @@

Annual End Uses

var endUseKeys = ['Heating:Electricity', 'Cooling:Electricity', 'InteriorLights:Electricity', 'ExteriorLights:Electricity', 'ExteriorEquipment:Electricity', 'InteriorEquipment:Electricity', 'Fans:Electricity', 'Pumps:Electricity', 'HeatRejection:Electricity', 'WaterSystems:Electricity']; var kbtu_datasets = ['NaturalGas:Facility', 'Propane:Facility', 'FuelOilNo2:Facility', 'OtherFuels:Facility']; + var changeToKbtu = false; - var applicableEndUseKeys = []; $scope.monthlyFuelChartData = {}; $scope.monthlyNetChartData = {}; $scope.annualNetChartData = {}; - $scope.annualEndUseChartData = {}; + $scope.annualEmissionsChartData = {}; + $scope.annualIntensityChartData = {}; // calculate global maximums across features var monthlyFuelYMax = 0; var monthlyNetYMin = 0; var monthlyNetYMax = 0; var annualTotalYMax = 0; + var annualEmissionsTotalYMax = 0; + var annualIntensityTotalYMax = 0; // also record totals per feature $scope.annualEndUseTotals = {}; $scope.monthlyFuelTotals = {}; $scope.monthlyNetTotals = {}; + $scope.annualEmissionsTotals = {}; + $scope.annualIntensityTotals = {}; + + // setup emissions intensity charts + // var intensityYMin = {}; + // var intensityYMax = {}; + _.forEach($scope.intensityCharts, function(chart) { + $scope.annualIntensityChartData[chart.name] = [{key: chart.name, values: []}]; + // intensityYMin[chart.name] = 0; + // intensityYMax[chart.name] = 0; + }); $scope.errorText = "No data. This feature either did not complete successfully or did not include an annual simulation ranging from January 1 to December 31."; // figure out max total energy use and applicable end uses + _.forEach($scope.features, function (feature) { - console.log("Feature: ", feature.name); var total_value = 0; _.forEach(endUseKeys, function (endUseKey) { @@ -432,15 +588,44 @@

Annual End Uses

// global maximum annualTotalYMax = _.max([annualTotalYMax, total_value]); + // emissions + $scope.annualEmissionsTotals[feature.name] = 0; + _.forEach($scope.emissionCharts, function(chart){ + var eVal = feature.annual_values[chart.name]; + + if (eVal == 0 || !eVal) { + console.log("no data found for: ", chart.name); + } + else { + annualEmissionsTotalYMax = _.max([annualEmissionsTotalYMax, eVal]); + $scope.annualEmissionsTotals[feature.name] = _.max([$scope.annualEmissionsTotals[feature.name], _.round(eVal)]); + } + }); + + // emissions intensity + _.forEach($scope.intensityCharts, function(chart){ + var iVal = feature.annual_values[chart.name]; + if (iVal == 0 || !iVal) { + console.log("no data found for: ", chart.name); + } else { + // global intensity maximum + annualIntensityTotalYMax = _.max([annualIntensityTotalYMax, iVal]); + $scope.annualIntensityTotals[chart.name] = _.max([$scope.annualIntensityTotals[chart.name], _.round(iVal)]); + } + }); + }); + // applicable end uses applicableEndUseKeys = _.uniq(applicableEndUseKeys).sort(); // store global default $scope.defaultAnnualEndUseChartOptions.chart['yDomain'] = [0, _.round(annualTotalYMax)]; + $scope.defaultAnnualEmissionsChartOptions.chart['yDomain'] = [0, _.round(annualEmissionsTotalYMax)]; + $scope.defaultAnnualIntensityChartOptions.chart['yDomain'] = [0, _.round(annualIntensityTotalYMax)]; // gather data for each feature - _.forEach($scope.features, function (feature) { + _.forEach($scope.features, function (feature, feature_index) { // calculate local maximums per feature var localFuelYMax = 0; @@ -448,6 +633,7 @@

Annual End Uses

var localNetYMin = 100000000; var localEndUseMax = 0; + // annual if(feature['complete_simulation'] == true){ // monthly fuel use @@ -456,7 +642,7 @@

Annual End Uses

// first iterate through all kbtu datasets to see if you'll need to change to kBtu units _.forEach(kbtu_datasets, function (kbtu_dataset) { var values = feature.monthly_values[kbtu_dataset]; - if (!(values.every(item => item === 0))) { + if ((typeof values !== 'undefined') && (!(values.every(item => item === 0)))) { changeToKbtu = true; } }); @@ -468,8 +654,8 @@

Annual End Uses

if (dataset == 'Electricity:Facility') { // first check if there is data to include - if (!(values.every(item => item === 0))) { - console.log(changeToKbtu); + if ((typeof values !== 'undefined') && (!(values.every(item => item === 0)))) { + console.log("change to Kbtu: ", changeToKbtu); if (changeToKbtu) { var kBtuValues = []; var kwhValues = feature.monthly_values[dataset] @@ -503,7 +689,7 @@

Annual End Uses

} } else { // everything else gets kBtu for now - if (!(values.every(item => item === 0))) { + if ((typeof values !== 'undefined') && (!(values.every(item => item === 0)))) { datasetUnit = dataset + '(kBtu)'; } }; @@ -604,6 +790,36 @@

Annual End Uses

$scope.annualEndUseTotals[feature.name] = _.round(localEndUseMax); } + + // emissions per feature + if(feature['complete_simulation'] == true) { + $scope.annualEmissionsChartData[feature.name] = []; + _.forEach($scope.emissionCharts, function (chart){ + var eValue = feature.annual_values[chart.name]; + + $scope.annualEmissionsChartData[feature.name].push({ + key: chart.display, + values: [{ + x: chart.short, + y: eValue + }] + }); + }); + } + + // emission intensity per fuel + var the_name = _.truncate(feature.name, {'length': 4, 'omission': ''}); + _.forEach($scope.intensityCharts, function(chart) { + var val = feature.annual_values[chart.name]; + $scope.annualIntensityChartData[chart.name][0]['values'].push({ + //x: feature_index + 1, + x: the_name, + y: val, + }); + // intensityYMin[chart.name] = _.min([intensityYMin[chart.name], val]); + // intensityYMax[chart.name] = _.max([intensityYMax[chart.name], val]); + }); + }); // assign chart options for each feature. assign global maximum to start @@ -611,7 +827,11 @@

Annual End Uses

$scope.monthlyFuelChartOptions[feature.name] = _.cloneDeep($scope.defaultMonthlyFuelChartOptions); $scope.monthlyNetChartOptions[feature.name] = _.cloneDeep($scope.defaultMonthlyNetChartOptions); $scope.annualEndUseChartOptions[feature.name] = _.cloneDeep($scope.defaultAnnualEndUseChartOptions); - + $scope.annualEmissionsChartOptions[feature.name] = _.cloneDeep($scope.defaultAnnualEmissionsChartOptions); + }); + // intensity default charts + _.forEach($scope.intensityCharts, function(chart){ + $scope.annualIntensityChartOptions[chart.name] = _.cloneDeep($scope.defaultAnnualIntensityChartOptions); }); // console.log("LOCAL MAXES:"); @@ -639,6 +859,10 @@

Annual End Uses

$scope.monthlyFuelChartOptions[feature.name].chart['yDomain'] = [0, $scope.monthlyFuelTotals[feature.name]]; $scope.monthlyNetChartOptions[feature.name].chart['yDomain'] = $scope.monthlyNetTotals[feature.name]; $scope.annualEndUseChartOptions[feature.name].chart['yDomain'] = [0, $scope.annualEndUseTotals[feature.name]]; + $scope.annualEmissionsChartOptions[feature.name].chart['yDomain'] = [0, $scope.annualEmissionsTotals[feature.name]]; + }); + _.forEach($scope.intensityCharts, function(chart){ + $scope.annualIntensityChartOptions[chart.name].chart['yDomain'] = [0, $scope.annualIntensityTotals[chart.name]]; }); $scope.axes = 1; @@ -649,6 +873,10 @@

Annual End Uses

$scope.monthlyFuelChartOptions[feature.name].chart['yDomain'] = _.cloneDeep($scope.defaultMonthlyFuelChartOptions.chart['yDomain']); $scope.monthlyNetChartOptions[feature.name].chart['yDomain'] = _.cloneDeep($scope.defaultMonthlyNetChartOptions.chart['yDomain']); $scope.annualEndUseChartOptions[feature.name].chart['yDomain'] = _.cloneDeep($scope.defaultAnnualEndUseChartOptions.chart['yDomain']); + $scope.annualEmissionsChartOptions[feature.name].chart['yDomain'] = _.cloneDeep($scope.defaultAnnualEmissionsChartOptions.chart['yDomain']); + }); + _.forEach($scope.intensityCharts, function(chart){ + $scope.annualIntensityChartOptions[chart.name].chart['yDomain'] = _.cloneDeep($scope.defaultAnnualIntensityChartOptions.chart['yDomain']); }); $scope.axes = 0; } @@ -660,6 +888,15 @@

Annual End Uses

window.dispatchEvent(new Event('resize')); }, 1000); + myApp.filter('strReplace', function () { + return function (input, from, to) { + input = input || ''; + from = from || ''; + to = to || ''; + return input.replace(new RegExp(from, 'g'), to); + }; + }); + diff --git a/example_files/visualization/input_visualization_scenario.html b/example_files/visualization/input_visualization_scenario.html index 9df7b77a..c17f9066 100644 --- a/example_files/visualization/input_visualization_scenario.html +++ b/example_files/visualization/input_visualization_scenario.html @@ -212,6 +212,30 @@

Annual End Uses

+
+

Emissions

+

Multiple emissions associated with electricity may be included in the graphs below and represent different calculation approaches (e.g., future hourly and historical average). Electricity emissions values from different calculation approaches should NOT be summed together.

+ +
+ + + + {{ chart.name | strReplace:'_':' ' | strReplace:'Emissions':'' | strReplace:'Intensity':''}} + + Emissions + Intensity + ({{ chart.units}}) + + + + + + + + +
+
+
@@ -371,6 +395,59 @@

Annual End Uses

} }; + $scope.emissionCharts = [ + {'name': 'Future_Annual_Electricity_Emissions', 'units': 'mt CO2e' }, + {'name': 'Future_Hourly_Electricity_Emissions','units': 'mt CO2e' }, + {'name': 'Historical_Annual_Electricity_Emissions','units': 'mt CO2e' }, + {'name': 'Historical_Hourly_Electricity_Emissions','units': 'mt CO2e' }, + {'name': 'Natural_Gas_Emissions', 'units': 'mt CO2e' }, + {'name': 'Propane_Emissions', 'units': 'mt CO2e' }, + {'name': 'FuelOilNo2_Emissions', 'units': 'mt CO2e' } + ]; + + $scope.annualEmissionsChartOptions = []; + _.forEach($scope.emissionCharts, function(chart) { + $scope.annualEmissionsChartOptions[chart.name] = { + chart: { + type: 'multiBarChart', + color: ['#014D4D'], + height: 320, + margin: { + top: 45, + right: 2, + bottom: 32, + left: 64 + }, + clipEdge: true, + groupSpacing: 0.3, + reduceXTicks: false, + stacked: false, + duration: 250, + x: function (d) { + return d.x; + }, + y: function (d) { + return d.y; + }, + yAxis: { + tickFormat: function (d) { + return d; + } + }, + showControls: false, + showLegend: false, + tooltip: { + gravity: 's', + classes: 'gravity-s' + } + } + }; + // update color for Intensity charts + if (chart.name.includes('Intensity')) { + $scope.annualEmissionsChartOptions[chart.name]['chart']['color'] = ['#8CC025']; + } + }); + var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; var datasets = ['Electricity:Facility', 'REopt:ElectricityProduced:Total', 'NaturalGas:Facility', 'Propane:Facility', 'FuelOilNo2:Facility', 'OtherFuels:Facility']; @@ -385,9 +462,18 @@

Annual End Uses

$scope.monthlyFuelChartData = {}; $scope.monthlyNetChartData = {}; $scope.annualNetChartData = {}; - $scope.annualEndUseChartData = {}; + $scope.annualEmissionsChartData = {}; + // setup emissions + var emissionsYMin = {}; + var emissionsYMax = {}; + _.forEach($scope.emissionCharts, function(chart) { + $scope.annualEmissionsChartData[chart.name] = [{key: chart.name, values: []}]; + emissionsYMin[chart.name] = 0; + emissionsYMax[chart.name] = 0; + }); + var monthlyFuelYMax = 0; var monthlyNetYMin = 0; var monthlyNetYMax = 0; @@ -397,7 +483,6 @@

Annual End Uses

// figure out max total energy use and applicable end uses _.forEach($scope.scenarios, function (scenario) { - console.log("SCENARIO: ", scenario.name); var total_value = 0; _.forEach(endUseKeys, function (endUseKey) { @@ -417,19 +502,29 @@

Annual End Uses

applicableEndUseKeys = _.uniq(applicableEndUseKeys).sort(); - // gather data for each scenario _.forEach($scope.scenarios, function (scenario) { if(scenario['complete_simulation'] == true){ + // emissions chart + _.forEach($scope.emissionCharts, function(chart) { + var val = scenario.annual_values[chart.name]; + $scope.annualEmissionsChartData[chart.name][0]['values'].push({ + x: scenario.name, + y: val + }); + emissionsYMin[chart.name] = _.min([emissionsYMin[chart.name], val]); + emissionsYMax[chart.name] = _.max([emissionsYMax[chart.name], val]); + }); + // monthly fuel use $scope.monthlyFuelChartData[scenario.name] = []; // first iterate through all kbtu datasets to see if you'll need to change to kBtu units _.forEach(kbtu_datasets, function (kbtu_dataset) { var values = scenario.monthly_values[kbtu_dataset]; - if (!(values.every(item => item === 0))) { + if ((typeof values !== 'undefined') && (!(values.every(item => item === 0)))) { changeToKbtu = true; } }); @@ -441,8 +536,8 @@

Annual End Uses

if (dataset == 'Electricity:Facility') { // first check if there is data to include - if (!(values.every(item => item === 0))) { - console.log(changeToKbtu); + if ((typeof values !== 'undefined') && (!(values.every(item => item === 0)))) { + console.log("change to kbtu?", changeToKbtu); if (changeToKbtu) { var kBtuValues = []; var kwhValues = scenario.monthly_values[dataset] @@ -501,7 +596,7 @@

Annual End Uses

$scope.monthlyNetChartData[scenario.name] = []; var values = scenario.monthly_values['NaturalGas:Facility']; var changeToKbtu = false; - if (!(values.every(item => item === 0))) { + if ((typeof values !== 'undefined') && (!(values.every(item => item === 0)))) { changeToKbtu = true } var unit = ' (kWh)' @@ -516,6 +611,7 @@

Annual End Uses

var value = 0; if (changeToKbtu){ value = scenario.monthly_values['Electricity:Facility'][i]*3.41 + scenario.monthly_values['NaturalGas:Facility'][i] + scenario.monthly_values['Propane:Facility'][i] + scenario.monthly_values['FuelOilNo2:Facility'][i] + scenario.monthly_values['OtherFuels:Facility'][i]; + // check that we have ElectricityProduced monthly data if(scenario.monthly_values['REopt:ElectricityProduced:Total']) { value = value - scenario.monthly_values['REopt:ElectricityProduced:Total'][i]*3.41 @@ -565,13 +661,25 @@

Annual End Uses

} }); - }); - - + + // fix the emissions charts Y range + _.forEach($scope.emissionCharts, function(chart) { + $scope.annualEmissionsChartOptions[chart.name].chart['yDomain'] = [_.round(emissionsYMin[chart.name]), _.round(emissionsYMax[chart.name])]; + }); + }); + _.delay(function () { window.dispatchEvent(new Event('resize')); }, 1000); - + + myApp.filter('strReplace', function () { + return function (input, from, to) { + input = input || ''; + from = from || ''; + to = to || ''; + return input.replace(new RegExp(from, 'g'), to); + }; + }); diff --git a/lib/uo_cli.rb b/lib/uo_cli.rb index 4e93aba1..22ed72b1 100755 --- a/lib/uo_cli.rb +++ b/lib/uo_cli.rb @@ -151,7 +151,7 @@ def opt_create "Use the FeatureID from your FeatureFile\n" \ "Requires 'scenario-file' also be specified, to say which FeatureFile will create the ScenarioFile\n" \ "Example: uo create --single-feature 2 --scenario-file example_project.json\n", type: String, short: :i - + opt :reopt_scenario_file, "\nCreate a ScenarioFile that includes a column defining the REopt assumptions file\n" \ "Specify the existing ScenarioFile that you want to extend with REopt functionality\n" \ "Example: uo create --reopt-scenario-file baseline_scenario.csv\n", type: String, short: :r @@ -173,7 +173,7 @@ def opt_run opt :num_parallel, "\nOPTIONAL: Run URBANopt simulations in parallel using cores\n" \ "Adjusts value of 'num_parallel' in the 'runner.conf' file\n" \ - "Example: uo run --num-parallel 2\n", default: 2, short: :n + "Example: uo run --num-parallel 2\n", short: :n end end @@ -414,31 +414,31 @@ def self.run_func end 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]) + 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]) scenario_output = URBANopt::Scenario::REoptScenarioCSV.new( - @scenario_name.downcase, - @root_dir, - run_dir, - feature_file, - mapper_files_dir, - csv_file, - num_header_rows, - reopt_files_dir, + @scenario_name.downcase, + @root_dir, + run_dir, + feature_file, + mapper_files_dir, + csv_file, + num_header_rows, + reopt_files_dir, reopt_assumptions_filename - ) - else + ) + else scenario_output = URBANopt::Scenario::ScenarioCSV.new( - @scenario_name.downcase, - @root_dir, - run_dir, - feature_file, - mapper_files_dir, - csv_file, + @scenario_name.downcase, + @root_dir, + run_dir, + feature_file, + mapper_files_dir, + csv_file, num_header_rows - ) + ) end scenario_output end @@ -487,12 +487,12 @@ def self.create_scenario_csv_file(feature_id) end end - # Write new ScenarioFile with REopt column - # params \ - # +existing_scenario_file+:: _string_ - Name of existing ScenarioFile - def self.create_reopt_scenario_file(existing_scenario_file) - existing_path, existing_name = File.split(File.expand_path(existing_scenario_file)) - # make reopt folder (if it does not exist) + # Write new ScenarioFile with REopt column + # params \ + # +existing_scenario_file+:: _string_ - Name of existing ScenarioFile + def self.create_reopt_scenario_file(existing_scenario_file) + existing_path, existing_name = File.split(File.expand_path(existing_scenario_file)) + # make reopt folder (if it does not exist) unless Dir.exist?(File.join(existing_path, 'reopt')) Dir.mkdir File.join(existing_path, 'reopt') # copy reopt files from cli examples @@ -502,18 +502,18 @@ def self.create_reopt_scenario_file(existing_scenario_file) Pathname.new(reopt_files).children.each { |reopt_file| FileUtils.cp(reopt_file, File.join(existing_path, 'reopt')) } end end - end - - table = CSV.read(existing_scenario_file, headers: true, col_sep: ',') - # Add another column, row by row: - table.each do |row| - row['REopt Assumptions'] = 'multiPV_assumptions.json' - end + end + + table = CSV.read(existing_scenario_file, headers: true, col_sep: ',') + # Add another column, row by row: + table.each do |row| + row['REopt Assumptions'] = 'multiPV_assumptions.json' + end # write new file (name it REopt + existing scenario name) - CSV.open(File.join(existing_path, 'REopt_' + existing_name), 'w') do |f| - f << table.headers - table.each { |row| f << row } - end + CSV.open(File.join(existing_path, "REopt_#{existing_name}"), 'w') do |f| + f << table.headers + table.each { |row| f << row } + end end # Create project folder @@ -550,6 +550,15 @@ def self.create_project_folder(dir_name, empty_folder = false, overwrite_project # copy config file FileUtils.cp(File.join(path_item, 'runner.conf'), dir_name) + # If the env var is set, change the num_parallel value to be what the env var is set to + if ENV['UO_NUM_PARALLEL'] + runner_file_path = File.join(dir_name, 'runner.conf') + runner_conf_hash = JSON.parse(File.read(runner_file_path)) + runner_conf_hash['num_parallel'] = ENV['UO_NUM_PARALLEL'].to_i + File.open(runner_file_path, 'w+') do |f| + f << runner_conf_hash.to_json + end + end # copy gemfile FileUtils.cp(File.join(path_item, 'Gemfile'), dir_name) @@ -798,11 +807,11 @@ def self.check_reader end end - # Create REopt ScenarioFile from existing - if @opthash.command == 'create' && @opthash.subopts[:reopt_scenario_file] - puts "\nCreating ScenarioFile with REopt functionality, extending from #{@opthash.subopts[:reopt_scenario_file]}..." - create_reopt_scenario_file(@opthash.subopts[:reopt_scenario_file]) - puts "\nDone" + # Create REopt ScenarioFile from existing + if @opthash.command == 'create' && @opthash.subopts[:reopt_scenario_file] + puts "\nCreating ScenarioFile with REopt functionality, extending from #{@opthash.subopts[:reopt_scenario_file]}..." + create_reopt_scenario_file(@opthash.subopts[:reopt_scenario_file]) + puts "\nDone" end # Run simulations @@ -868,7 +877,7 @@ def self.check_reader puts "Scenario path: #{scenario_path}" - #config_root_dir = File.dirname(File.expand_path(config_scenario_file)) + # config_root_dir = File.dirname(File.expand_path(config_scenario_file)) config_root_dir = config_path run_dir = File.join(config_root_dir, 'run', config_scenario_name.downcase) featurefile = Pathname.new(opendss_config[:urbanopt_geojson_file]) @@ -1025,7 +1034,7 @@ def self.check_reader end elsif (@opthash.subopts[:reopt_scenario] == true) || (@opthash.subopts[:reopt_feature] == true) # Ensure reopt default files are prepared - #create_reopt_files(@opthash.subopts[:scenario]) + # create_reopt_files(@opthash.subopts[:scenario]) scenario_base = default_post_processor.scenario_base @@ -1232,9 +1241,9 @@ def self.check_reader if feature_eui_value > validation_params['EUI'][@opthash.subopts[:units]][building_type]['max'] puts "\nFeature #{File.basename(feature_path)} EUI of #{feature_eui_value.round(2)} #{unit_value} is greater than the validation maximum." elsif feature_eui_value < validation_params['EUI'][@opthash.subopts[:units]][building_type]['min'] - puts "\nFeature #{File.basename(feature_path)} EUI of #{feature_eui_value.round(2)} #{unit_value} is less than the validation minimum." + puts "\nFeature #{File.basename(feature_path)} (#{building_type}) EUI of #{feature_eui_value.round(2)} #{unit_value} is less than the validation minimum." else - puts "\nFeature #{File.basename(feature_path)} EUI of #{feature_eui_value.round(2)} #{unit_value} is within bounds set by #{validation_file_name}." + puts "\nFeature #{File.basename(feature_path)} (#{building_type}) EUI of #{feature_eui_value.round(2)} #{unit_value} is within bounds set by #{validation_file_name}." end end end diff --git a/lib/uo_cli/version.rb b/lib/uo_cli/version.rb index a43d9660..caa9333c 100644 --- a/lib/uo_cli/version.rb +++ b/lib/uo_cli/version.rb @@ -40,6 +40,6 @@ module URBANopt module CLI - VERSION = '0.8.0'.freeze + VERSION = '0.8.1'.freeze end end diff --git a/spec/uo_cli_spec.rb b/spec/uo_cli_spec.rb index f074cceb..c659df60 100644 --- a/spec/uo_cli_spec.rb +++ b/spec/uo_cli_spec.rb @@ -206,6 +206,15 @@ def delete_directory_or_file(dir_or_file) expect(File.exist?(test_feature)).to be false expect(File.exist?(File.join(test_directory, 'mappers', 'Baseline.rb'))).to be true end + + it 'sets num_parallel on project creation with env var' do + ENV['UO_NUM_PARALLEL'] = '3' + expect(ENV['UO_NUM_PARALLEL']).to eq('3') + system("#{call_cli} create --project-folder #{test_directory}") + runner_file_path = File.join(test_directory, 'runner.conf') + runner_conf_hash = JSON.parse(File.read(runner_file_path)) + expect(runner_conf_hash['num_parallel']).to eq(3) + end end context 'Make and manipulate ScenarioFiles' do @@ -389,7 +398,7 @@ def delete_directory_or_file(dir_or_file) expect(File.exist?(File.join(test_directory_pv, 'run', 'reopt_scenario', 'process_status.json'))).to be true path_to_resilience_report_file = File.join(test_directory_pv, 'run', 'reopt_scenario', 'reopt', 'scenario_report_reopt_scenario_reopt_run_resilience.json') end - + it 'reopt post-processes each feature and visualize' do system("#{call_cli} process --default --scenario #{test_reopt_scenario} --feature #{test_feature_pv}") system("#{call_cli} process --reopt-feature --scenario #{test_reopt_scenario} --feature #{test_feature_pv}") diff --git a/uo_cli.gemspec b/uo_cli.gemspec index a6ef1474..539229b1 100644 --- a/uo_cli.gemspec +++ b/uo_cli.gemspec @@ -34,9 +34,9 @@ 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.8.0' + spec.add_runtime_dependency 'urbanopt-geojson', '~> 0.8.1' spec.add_runtime_dependency 'urbanopt-reopt', '~> 0.8.0' - spec.add_runtime_dependency 'urbanopt-reporting', '~> 0.6.0' + spec.add_runtime_dependency 'urbanopt-reporting', '~> 0.6.1' spec.add_runtime_dependency 'urbanopt-rnm-us', '~> 0.3.0' spec.add_runtime_dependency 'urbanopt-scenario', '~> 0.8.0'