urbanopt-reopt 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
 - data/.gitignore +27 -0
 - data/.rakeTasks +7 -0
 - data/.rdoc_options +37 -0
 - data/.rspec +3 -0
 - data/.rubocop.yml +9 -0
 - data/.travis.yml +22 -0
 - data/CHANGELOG.md +5 -0
 - data/Gemfile +67 -0
 - data/Jenkinsfile +10 -0
 - data/LICENSE.md +27 -0
 - data/RDOC_MAIN.md +176 -0
 - data/README.md +177 -0
 - data/Rakefile +30 -0
 - data/deploy_docs.sh +5 -0
 - data/developer_nrel_key.rb +31 -0
 - data/doc_templates/LICENSE.md +27 -0
 - data/doc_templates/README.md.erb +42 -0
 - data/doc_templates/copyright_erb.txt +31 -0
 - data/doc_templates/copyright_js.txt +4 -0
 - data/doc_templates/copyright_ruby.txt +29 -0
 - data/docs/.gitignore +3 -0
 - data/docs/.vuepress/components/InnerJsonSchema.vue +78 -0
 - data/docs/.vuepress/components/JsonSchema.vue +12 -0
 - data/docs/.vuepress/components/ReoptInputSchema.vue +12 -0
 - data/docs/.vuepress/components/ReoptOutputSchema.vue +12 -0
 - data/docs/.vuepress/components/StaticLink.vue +8 -0
 - data/docs/.vuepress/config.js +16 -0
 - data/docs/.vuepress/highlight.js +8 -0
 - data/docs/.vuepress/public/custom_rdoc_styles.css +58 -0
 - data/docs/.vuepress/utils.js +17 -0
 - data/docs/README.md +196 -0
 - data/docs/package-lock.json +254 -0
 - data/docs/package.json +22 -0
 - data/docs/schemas/reopt-input-schema.md +57 -0
 - data/docs/schemas/reopt-output-schema.md +66 -0
 - data/index.html +1 -0
 - data/index.md +176 -0
 - data/lib/files/.gitkeep +0 -0
 - data/lib/urbanopt/reopt/extension.rb +44 -0
 - data/lib/urbanopt/reopt/feature_report_adapter.rb +364 -0
 - data/lib/urbanopt/reopt/reopt_lite_api.rb +230 -0
 - data/lib/urbanopt/reopt/reopt_logger.rb +42 -0
 - data/lib/urbanopt/reopt/reopt_post_processor.rb +245 -0
 - data/lib/urbanopt/reopt/reopt_schema/reopt_input_schema.json +1111 -0
 - data/lib/urbanopt/reopt/reopt_schema/reopt_output_schema.json +538 -0
 - data/lib/urbanopt/reopt/scenario/reopt_scenario_csv.rb +115 -0
 - data/lib/urbanopt/reopt/scenario_report_adapter.rb +404 -0
 - data/lib/urbanopt/reopt/version.rb +35 -0
 - data/lib/urbanopt/reopt.rb +36 -0
 - data/lib/urbanopt/reopt_scenario.rb +31 -0
 - data/lib/urbanopt-reopt.rb +31 -0
 - data/urbanopt-reopt.gemspec +33 -0
 - metadata +194 -0
 
| 
         @@ -0,0 +1,364 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # *********************************************************************************
         
     | 
| 
      
 2 
     | 
    
         
            +
            # URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
         
     | 
| 
      
 3 
     | 
    
         
            +
            # contributors. All rights reserved.
         
     | 
| 
      
 4 
     | 
    
         
            +
            #
         
     | 
| 
      
 5 
     | 
    
         
            +
            # Redistribution and use in source and binary forms, with or without modification,
         
     | 
| 
      
 6 
     | 
    
         
            +
            # are permitted provided that the following conditions are met:
         
     | 
| 
      
 7 
     | 
    
         
            +
            #
         
     | 
| 
      
 8 
     | 
    
         
            +
            # Redistributions of source code must retain the above copyright notice, this list
         
     | 
| 
      
 9 
     | 
    
         
            +
            # of conditions and the following disclaimer.
         
     | 
| 
      
 10 
     | 
    
         
            +
            #
         
     | 
| 
      
 11 
     | 
    
         
            +
            # Redistributions in binary form must reproduce the above copyright notice, this
         
     | 
| 
      
 12 
     | 
    
         
            +
            # list of conditions and the following disclaimer in the documentation and/or other
         
     | 
| 
      
 13 
     | 
    
         
            +
            # materials provided with the distribution.
         
     | 
| 
      
 14 
     | 
    
         
            +
            #
         
     | 
| 
      
 15 
     | 
    
         
            +
            # Neither the name of the copyright holder nor the names of its contributors may be
         
     | 
| 
      
 16 
     | 
    
         
            +
            # used to endorse or promote products derived from this software without specific
         
     | 
| 
      
 17 
     | 
    
         
            +
            # prior written permission.
         
     | 
| 
      
 18 
     | 
    
         
            +
            #
         
     | 
| 
      
 19 
     | 
    
         
            +
            # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
         
     | 
| 
      
 20 
     | 
    
         
            +
            # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
         
     | 
| 
      
 21 
     | 
    
         
            +
            # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
         
     | 
| 
      
 22 
     | 
    
         
            +
            # IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
         
     | 
| 
      
 23 
     | 
    
         
            +
            # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
         
     | 
| 
      
 24 
     | 
    
         
            +
            # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
         
     | 
| 
      
 25 
     | 
    
         
            +
            # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
         
     | 
| 
      
 26 
     | 
    
         
            +
            # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
         
     | 
| 
      
 27 
     | 
    
         
            +
            # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
         
     | 
| 
      
 28 
     | 
    
         
            +
            # OF THE POSSIBILITY OF SUCH DAMAGE.
         
     | 
| 
      
 29 
     | 
    
         
            +
            # *********************************************************************************
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
            require 'urbanopt/scenario/default_reports'
         
     | 
| 
      
 32 
     | 
    
         
            +
            require 'urbanopt/reopt/reopt_logger'
         
     | 
| 
      
 33 
     | 
    
         
            +
            require 'csv'
         
     | 
| 
      
 34 
     | 
    
         
            +
            require 'matrix'
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
            module URBANopt # :nodoc:
         
     | 
| 
      
 37 
     | 
    
         
            +
              module REopt # :nodoc:
         
     | 
| 
      
 38 
     | 
    
         
            +
                class FeatureReportAdapter
         
     | 
| 
      
 39 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 40 
     | 
    
         
            +
                  # FeatureReportAdapter can convert a URBANopt::Scenario::DefaultReports::FeatureReport into a \REopt Lite posts or update a URBANopt::Scenario::DefaultReports::FeatureReport from a \REopt Lite response.
         
     | 
| 
      
 41 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 42 
     | 
    
         
            +
                  # [*parameters:*]
         
     | 
| 
      
 43 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 44 
     | 
    
         
            +
                  def initialize
         
     | 
| 
      
 45 
     | 
    
         
            +
                    # initialize @@logger
         
     | 
| 
      
 46 
     | 
    
         
            +
                    @@logger ||= URBANopt::REopt.reopt_logger
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 50 
     | 
    
         
            +
                  # Convert a FeatureReport into a \REopt Lite post
         
     | 
| 
      
 51 
     | 
    
         
            +
                  #
         
     | 
| 
      
 52 
     | 
    
         
            +
                  # [*parameters:*]
         
     | 
| 
      
 53 
     | 
    
         
            +
                  #
         
     | 
| 
      
 54 
     | 
    
         
            +
                  # * +feature_report+ - _URBANopt::Scenario::DefaultReports::FeatureReport_ - FeatureReport to use in converting the optional +reopt_assumptions_hash+ to a \REopt Lite post. If a +reopt_assumptions_hash+ is not provided, a default post will be updated from this FeatureReport and submitted to the \REopt Lite API.
         
     | 
| 
      
 55 
     | 
    
         
            +
                  # *  +reopt_assumptions_hash+ - _Hash_ - Optional. A hash formatted for submittal to the \REopt Lite API containing default values. Values will be overwritten from the FeatureReport where available (i.e. latitude, roof_squarefeet). Missing optional parameters will be filled in with default values by the API.
         
     | 
| 
      
 56 
     | 
    
         
            +
                  #
         
     | 
| 
      
 57 
     | 
    
         
            +
                  # [*return:*] _Hash_ - Returns hash formatted for submittal to the \REopt Lite API
         
     | 
| 
      
 58 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 59 
     | 
    
         
            +
                  def reopt_json_from_feature_report(feature_report, reopt_assumptions_hash = nil)
         
     | 
| 
      
 60 
     | 
    
         
            +
                    name = feature_report.name.delete ' '
         
     | 
| 
      
 61 
     | 
    
         
            +
                    description = "feature_report_#{name}_#{feature_report.id}"
         
     | 
| 
      
 62 
     | 
    
         
            +
                    reopt_inputs = { Scenario: { Site: { ElectricTariff: { blended_monthly_demand_charges_us_dollars_per_kw: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 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] }, LoadProfile: {}, Wind: { max_kw: 0 } } } }
         
     | 
| 
      
 63 
     | 
    
         
            +
                    if !reopt_assumptions_hash.nil?
         
     | 
| 
      
 64 
     | 
    
         
            +
                      reopt_inputs = reopt_assumptions_hash
         
     | 
| 
      
 65 
     | 
    
         
            +
                    else
         
     | 
| 
      
 66 
     | 
    
         
            +
                      @@logger.info('Using default REopt Lite assumptions')
         
     | 
| 
      
 67 
     | 
    
         
            +
                    end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                    # Check FeatureReport has required data
         
     | 
| 
      
 70 
     | 
    
         
            +
                    requireds_names = ['latitude', 'longitude']
         
     | 
| 
      
 71 
     | 
    
         
            +
                    requireds = [feature_report.location.latitude, feature_report.location.longitude]
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                    if requireds.include?(nil) || requireds.include?(0)
         
     | 
| 
      
 74 
     | 
    
         
            +
                      requireds.each_with_index do |i, x|
         
     | 
| 
      
 75 
     | 
    
         
            +
                        if [nil, 0].include? x
         
     | 
| 
      
 76 
     | 
    
         
            +
                          n = requireds_names[i]
         
     | 
| 
      
 77 
     | 
    
         
            +
                          p 'a' # @@logger.error("Missing value for #{n} - this is a required input")
         
     | 
| 
      
 78 
     | 
    
         
            +
                          raise "Missing value for #{n} - this is a required input"
         
     | 
| 
      
 79 
     | 
    
         
            +
                        end
         
     | 
| 
      
 80 
     | 
    
         
            +
                      end
         
     | 
| 
      
 81 
     | 
    
         
            +
                    end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                    reopt_inputs[:Scenario][:description] = description
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                    # Parse Location
         
     | 
| 
      
 86 
     | 
    
         
            +
                    reopt_inputs[:Scenario][:Site][:latitude] = feature_report.location.latitude
         
     | 
| 
      
 87 
     | 
    
         
            +
                    reopt_inputs[:Scenario][:Site][:longitude] = feature_report.location.longitude
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                    # Parse Optional FeatureReport metrics
         
     | 
| 
      
 90 
     | 
    
         
            +
                    unless feature_report.program.roof_area.nil?
         
     | 
| 
      
 91 
     | 
    
         
            +
                      reopt_inputs[:Scenario][:Site][:roof_squarefeet] = feature_report.program.roof_area[:available_roof_area]
         
     | 
| 
      
 92 
     | 
    
         
            +
                    end
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                    unless feature_report.program.site_area.nil?
         
     | 
| 
      
 95 
     | 
    
         
            +
                      reopt_inputs[:Scenario][:Site][:land_acres] = feature_report.program.site_area * 1.0 / 43560 # acres/sqft
         
     | 
| 
      
 96 
     | 
    
         
            +
                    end
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                    # Parse Load Profile
         
     | 
| 
      
 99 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 100 
     | 
    
         
            +
                      col_num = feature_report.timeseries_csv.column_names.index('Electricity:Facility')
         
     | 
| 
      
 101 
     | 
    
         
            +
                      t = CSV.read(feature_report.timeseries_csv.path, headers: true, converters: :numeric)
         
     | 
| 
      
 102 
     | 
    
         
            +
                      energy_timeseries_kwh = t.by_col[col_num].map { |e| ((e || 0) * 0.293071) } # convert kBTU to KWH
         
     | 
| 
      
 103 
     | 
    
         
            +
                      if (feature_report.timesteps_per_hour || 1) > 1
         
     | 
| 
      
 104 
     | 
    
         
            +
                        energy_timeseries_kwh = energy_timeseries_kwh.each_slice(feature_report.timesteps_per_hour).to_a.map { |x| x.inject(0, :+) / x.length.to_f }
         
     | 
| 
      
 105 
     | 
    
         
            +
                      end
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
                      if energy_timeseries_kwh.length < feature_report.timesteps_per_hour * 8760
         
     | 
| 
      
 108 
     | 
    
         
            +
                        energy_timeseries_kwh += [0] * ((feature_report.timesteps_per_hour * 8760) - energy_timeseries_kwh.length)
         
     | 
| 
      
 109 
     | 
    
         
            +
                        @@logger.info("Assuming load profile for Feature Report #{feature_report.name} #{feature_report.id} starts January 1 - filling in rest  with zeros")
         
     | 
| 
      
 110 
     | 
    
         
            +
                      end
         
     | 
| 
      
 111 
     | 
    
         
            +
                      reopt_inputs[:Scenario][:Site][:LoadProfile][:loads_kw] = energy_timeseries_kwh.map { |e| e ? e : 0 }
         
     | 
| 
      
 112 
     | 
    
         
            +
                    rescue StandardError
         
     | 
| 
      
 113 
     | 
    
         
            +
                      @@logger.error("Could not parse the annual electric load from the timeseries csv - #{feature_report.timeseries_csv.path}")
         
     | 
| 
      
 114 
     | 
    
         
            +
                      raise "Could not parse the annual electric load from the timeseries csv - #{feature_report.timeseries_csv.path}"
         
     | 
| 
      
 115 
     | 
    
         
            +
                    end
         
     | 
| 
      
 116 
     | 
    
         
            +
                    return reopt_inputs
         
     | 
| 
      
 117 
     | 
    
         
            +
                  end
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 120 
     | 
    
         
            +
                  # Update a FeatureReport from a \REopt Lite response
         
     | 
| 
      
 121 
     | 
    
         
            +
                  #
         
     | 
| 
      
 122 
     | 
    
         
            +
                  # [*parameters:*]
         
     | 
| 
      
 123 
     | 
    
         
            +
                  #
         
     | 
| 
      
 124 
     | 
    
         
            +
                  # * +feature_report+ - _URBANopt::Scenario::DefaultReports::FeatureReport_ - FeatureReport to update from a \REopt Lite reponse hash.
         
     | 
| 
      
 125 
     | 
    
         
            +
                  # * +reopt_output+ - _Hash_ - A reponse hash from the \REopt Lite API to use in overwriting FeatureReport technology sizes, costs and dispatch strategies.
         
     | 
| 
      
 126 
     | 
    
         
            +
                  # * +timeseries_csv_path+ - _String_ - Optional. The path to a file at which a new timeseries CSV will be written. If not provided a file is created based on the run_uuid of the \REopt Lite optimization task.
         
     | 
| 
      
 127 
     | 
    
         
            +
                  #
         
     | 
| 
      
 128 
     | 
    
         
            +
                  # [*return:*] _URBANopt::Scenario::DefaultReports::FeatureReport_ - Returns an updated FeatureReport.
         
     | 
| 
      
 129 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 130 
     | 
    
         
            +
                  def update_feature_report(feature_report, reopt_output, timeseries_csv_path = nil)
         
     | 
| 
      
 131 
     | 
    
         
            +
                    # Check if the \REopt Lite response is valid
         
     | 
| 
      
 132 
     | 
    
         
            +
                    if reopt_output['outputs']['Scenario']['status'] != 'optimal'
         
     | 
| 
      
 133 
     | 
    
         
            +
                      @@logger.info("Warning cannot Feature Report #{feature_report.name} #{feature_report.id}  - REopt optimization was non-optimal")
         
     | 
| 
      
 134 
     | 
    
         
            +
                      return feature_report
         
     | 
| 
      
 135 
     | 
    
         
            +
                    end
         
     | 
| 
      
 136 
     | 
    
         
            +
             
     | 
| 
      
 137 
     | 
    
         
            +
                    # Update location
         
     | 
| 
      
 138 
     | 
    
         
            +
                    feature_report.location.latitude = reopt_output['inputs']['Scenario']['Site']['latitude']
         
     | 
| 
      
 139 
     | 
    
         
            +
                    feature_report.location.longitude = reopt_output['inputs']['Scenario']['Site']['longitude']
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
      
 141 
     | 
    
         
            +
                    # Update timeseries csv from \REopt Lite dispatch data
         
     | 
| 
      
 142 
     | 
    
         
            +
                    feature_report.timesteps_per_hour = reopt_output['inputs']['Scenario']['time_steps_per_hour']
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
      
 144 
     | 
    
         
            +
                    # Update distributed generation sizing and financials
         
     | 
| 
      
 145 
     | 
    
         
            +
                    (feature_report.distributed_generation.lcc_us_dollars = reopt_output['outputs']['Scenario']['Site']['Financial']['lcc_us_dollars']) || 0
         
     | 
| 
      
 146 
     | 
    
         
            +
                    (feature_report.distributed_generation.npv_us_dollars = reopt_output['outputs']['Scenario']['Site']['Financial']['npv_us_dollars']) || 0
         
     | 
| 
      
 147 
     | 
    
         
            +
                    (feature_report.distributed_generation.year_one_energy_cost_us_dollars =  reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_energy_cost_us_dollars']) || 0
         
     | 
| 
      
 148 
     | 
    
         
            +
                    (feature_report.distributed_generation.year_one_demand_cost_us_dollars =  reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_demand_cost_us_dollars']) || 0
         
     | 
| 
      
 149 
     | 
    
         
            +
                    (feature_report.distributed_generation.year_one_bill_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_bill_us_dollars']) || 0
         
     | 
| 
      
 150 
     | 
    
         
            +
                    (feature_report.distributed_generation.total_energy_cost_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['total_energy_cost_us_dollars']) || 0
         
     | 
| 
      
 151 
     | 
    
         
            +
             
     | 
| 
      
 152 
     | 
    
         
            +
                    (feature_report.distributed_generation.solar_pv.size_kw = reopt_output['outputs']['Scenario']['Site']['PV']['size_kw']) || 0
         
     | 
| 
      
 153 
     | 
    
         
            +
                    (feature_report.distributed_generation.wind.size_kw = reopt_output['outputs']['Scenario']['Site']['Wind']['size_kw']) || 0
         
     | 
| 
      
 154 
     | 
    
         
            +
                    (feature_report.distributed_generation.generator.size_kw = reopt_output['outputs']['Scenario']['Site']['Generator']['size_kw']) || 0
         
     | 
| 
      
 155 
     | 
    
         
            +
                    (feature_report.distributed_generation.storage.size_kw = reopt_output['outputs']['Scenario']['Site']['Storage']['size_kw']) || 0
         
     | 
| 
      
 156 
     | 
    
         
            +
                    (feature_report.distributed_generation.storage.size_kwh = reopt_output['outputs']['Scenario']['Site']['Storage']['size_kwh']) || 0
         
     | 
| 
      
 157 
     | 
    
         
            +
             
     | 
| 
      
 158 
     | 
    
         
            +
                    generation_timeseries_kwh = Matrix[[0] * 8760]
         
     | 
| 
      
 159 
     | 
    
         
            +
             
     | 
| 
      
 160 
     | 
    
         
            +
                    unless reopt_output['outputs']['Scenario']['Site']['PV'].nil?
         
     | 
| 
      
 161 
     | 
    
         
            +
                      if (reopt_output['outputs']['Scenario']['Site']['PV']['size_kw'] || 0) > 0
         
     | 
| 
      
 162 
     | 
    
         
            +
                        if !reopt_output['outputs']['Scenario']['Site']['PV']['year_one_power_production_series_kw'].nil?
         
     | 
| 
      
 163 
     | 
    
         
            +
                          generation_timeseries_kwh += Matrix[reopt_output['outputs']['Scenario']['Site']['PV']['year_one_power_production_series_kw']]
         
     | 
| 
      
 164 
     | 
    
         
            +
                        end
         
     | 
| 
      
 165 
     | 
    
         
            +
                      end
         
     | 
| 
      
 166 
     | 
    
         
            +
                    end
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
                    # unless reopt_output['outputs']['Scenario']['Site']['Storage'].nil?
         
     | 
| 
      
 169 
     | 
    
         
            +
                    #   if (reopt_output['outputs']['Scenario']['Site']['Storage']['size_kw'] or 0) > 0
         
     | 
| 
      
 170 
     | 
    
         
            +
                    #     if !reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_to_grid_series_kw'].nil?
         
     | 
| 
      
 171 
     | 
    
         
            +
                    #       generation_timeseries_kwh = generation_timeseries_kwh + Matrix[reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_to_grid_series_kw']]
         
     | 
| 
      
 172 
     | 
    
         
            +
                    #     end
         
     | 
| 
      
 173 
     | 
    
         
            +
                    #   end
         
     | 
| 
      
 174 
     | 
    
         
            +
                    # end
         
     | 
| 
      
 175 
     | 
    
         
            +
             
     | 
| 
      
 176 
     | 
    
         
            +
                    unless reopt_output['outputs']['Scenario']['Site']['Wind'].nil?
         
     | 
| 
      
 177 
     | 
    
         
            +
                      if (reopt_output['outputs']['Scenario']['Site']['Wind']['size_kw'] || 0) > 0
         
     | 
| 
      
 178 
     | 
    
         
            +
                        if !reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_power_production_series_kw'].nil?
         
     | 
| 
      
 179 
     | 
    
         
            +
                          generation_timeseries_kwh += Matrix[reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_power_production_series_kw']]
         
     | 
| 
      
 180 
     | 
    
         
            +
                        end
         
     | 
| 
      
 181 
     | 
    
         
            +
                      end
         
     | 
| 
      
 182 
     | 
    
         
            +
                    end
         
     | 
| 
      
 183 
     | 
    
         
            +
             
     | 
| 
      
 184 
     | 
    
         
            +
                    unless reopt_output['outputs']['Scenario']['Site']['Generator'].nil?
         
     | 
| 
      
 185 
     | 
    
         
            +
                      if (reopt_output['outputs']['Scenario']['Site']['Generator']['size_kw'] || 0) > 0
         
     | 
| 
      
 186 
     | 
    
         
            +
                        if !reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_power_production_series_kw'].nil?
         
     | 
| 
      
 187 
     | 
    
         
            +
                          generation_timeseries_kwh += Matrix[reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_power_production_series_kw']]
         
     | 
| 
      
 188 
     | 
    
         
            +
                        end
         
     | 
| 
      
 189 
     | 
    
         
            +
                      end
         
     | 
| 
      
 190 
     | 
    
         
            +
                    end
         
     | 
| 
      
 191 
     | 
    
         
            +
             
     | 
| 
      
 192 
     | 
    
         
            +
                    $generation_timeseries_kwh = generation_timeseries_kwh.to_a[0] || [0] * 8760
         
     | 
| 
      
 193 
     | 
    
         
            +
                    $generation_timeseries_kwh_col = feature_report.timeseries_csv.column_names.index('ElectricityProduced:Total')
         
     | 
| 
      
 194 
     | 
    
         
            +
                    if $generation_timeseries_kwh_col.nil?
         
     | 
| 
      
 195 
     | 
    
         
            +
                      $generation_timeseries_kwh_col = feature_report.timeseries_csv.column_names.length
         
     | 
| 
      
 196 
     | 
    
         
            +
                      feature_report.timeseries_csv.column_names.push('ElectricityProduced:Total')
         
     | 
| 
      
 197 
     | 
    
         
            +
                    end
         
     | 
| 
      
 198 
     | 
    
         
            +
             
     | 
| 
      
 199 
     | 
    
         
            +
                    $load = reopt_output['outputs']['Scenario']['Site']['LoadProfile']['year_one_electric_load_series_kw'] || [0] * 8760
         
     | 
| 
      
 200 
     | 
    
         
            +
                    $load_col = feature_report.timeseries_csv.column_names.index('Electricity:Load:Total')
         
     | 
| 
      
 201 
     | 
    
         
            +
                    if $load_col.nil?
         
     | 
| 
      
 202 
     | 
    
         
            +
                      $load_col = feature_report.timeseries_csv.column_names.length
         
     | 
| 
      
 203 
     | 
    
         
            +
                      feature_report.timeseries_csv.column_names.push('Electricity:Load:Total')
         
     | 
| 
      
 204 
     | 
    
         
            +
                    end
         
     | 
| 
      
 205 
     | 
    
         
            +
             
     | 
| 
      
 206 
     | 
    
         
            +
                    $utility_to_load = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_to_load_series_kw'] || [0] * 8760
         
     | 
| 
      
 207 
     | 
    
         
            +
                    $utility_to_load_col = feature_report.timeseries_csv.column_names.index('Electricity:Grid:ToLoad')
         
     | 
| 
      
 208 
     | 
    
         
            +
                    if $utility_to_load_col.nil?
         
     | 
| 
      
 209 
     | 
    
         
            +
                      $utility_to_load_col = feature_report.timeseries_csv.column_names.length
         
     | 
| 
      
 210 
     | 
    
         
            +
                      feature_report.timeseries_csv.column_names.push('Electricity:Grid:ToLoad')
         
     | 
| 
      
 211 
     | 
    
         
            +
                    end
         
     | 
| 
      
 212 
     | 
    
         
            +
             
     | 
| 
      
 213 
     | 
    
         
            +
                    $utility_to_battery = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_to_battery_series_kw'] || [0] * 8760
         
     | 
| 
      
 214 
     | 
    
         
            +
                    $utility_to_battery_col = feature_report.timeseries_csv.column_names.index('Electricity:Grid:ToBattery')
         
     | 
| 
      
 215 
     | 
    
         
            +
                    if $utility_to_battery_col.nil?
         
     | 
| 
      
 216 
     | 
    
         
            +
                      $utility_to_battery_col = feature_report.timeseries_csv.column_names.length
         
     | 
| 
      
 217 
     | 
    
         
            +
                      feature_report.timeseries_csv.column_names.push('Electricity:Grid:ToBattery')
         
     | 
| 
      
 218 
     | 
    
         
            +
                    end
         
     | 
| 
      
 219 
     | 
    
         
            +
             
     | 
| 
      
 220 
     | 
    
         
            +
                    $storage_to_load = reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_to_load_series_kw'] || [0] * 8760
         
     | 
| 
      
 221 
     | 
    
         
            +
                    $storage_to_load_col = feature_report.timeseries_csv.column_names.index('Electricity:Storage:ToLoad')
         
     | 
| 
      
 222 
     | 
    
         
            +
                    if $storage_to_load_col.nil?
         
     | 
| 
      
 223 
     | 
    
         
            +
                      $storage_to_load_col = feature_report.timeseries_csv.column_names.length
         
     | 
| 
      
 224 
     | 
    
         
            +
                      feature_report.timeseries_csv.column_names.push('Electricity:Storage:ToLoad')
         
     | 
| 
      
 225 
     | 
    
         
            +
                    end
         
     | 
| 
      
 226 
     | 
    
         
            +
             
     | 
| 
      
 227 
     | 
    
         
            +
                    $storage_to_grid = reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_to_grid_series_kw'] || [0] * 8760
         
     | 
| 
      
 228 
     | 
    
         
            +
                    $storage_to_grid_col = feature_report.timeseries_csv.column_names.index('Electricity:Storage:ToGrid')
         
     | 
| 
      
 229 
     | 
    
         
            +
                    if $storage_to_grid_col.nil?
         
     | 
| 
      
 230 
     | 
    
         
            +
                      $storage_to_grid_col = feature_report.timeseries_csv.column_names.length
         
     | 
| 
      
 231 
     | 
    
         
            +
                      feature_report.timeseries_csv.column_names.push('Electricity:Storage:ToGrid')
         
     | 
| 
      
 232 
     | 
    
         
            +
                    end
         
     | 
| 
      
 233 
     | 
    
         
            +
             
     | 
| 
      
 234 
     | 
    
         
            +
                    $storage_soc = reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_soc_series_pct'] || [0] * 8760
         
     | 
| 
      
 235 
     | 
    
         
            +
                    $storage_soc_col = feature_report.timeseries_csv.column_names.index('Electricity:Storage:StateOfCharge')
         
     | 
| 
      
 236 
     | 
    
         
            +
                    if $storage_soc_col.nil?
         
     | 
| 
      
 237 
     | 
    
         
            +
                      $storage_soc_col = feature_report.timeseries_csv.column_names.length
         
     | 
| 
      
 238 
     | 
    
         
            +
                      feature_report.timeseries_csv.column_names.push('Electricity:Storage:StateOfCharge')
         
     | 
| 
      
 239 
     | 
    
         
            +
                    end
         
     | 
| 
      
 240 
     | 
    
         
            +
             
     | 
| 
      
 241 
     | 
    
         
            +
                    $generator_total = reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_power_production_series_kw'] || [0] * 8760
         
     | 
| 
      
 242 
     | 
    
         
            +
                    $generator_total_col = feature_report.timeseries_csv.column_names.index('ElectricityProduced:Generator:Total')
         
     | 
| 
      
 243 
     | 
    
         
            +
                    if $generator_total_col.nil?
         
     | 
| 
      
 244 
     | 
    
         
            +
                      $generator_total_col = feature_report.timeseries_csv.column_names.length
         
     | 
| 
      
 245 
     | 
    
         
            +
                      feature_report.timeseries_csv.column_names.push('ElectricityProduced:Generator:Total')
         
     | 
| 
      
 246 
     | 
    
         
            +
                    end
         
     | 
| 
      
 247 
     | 
    
         
            +
             
     | 
| 
      
 248 
     | 
    
         
            +
                    $generator_to_battery = reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_to_battery_series_kw'] || [0] * 8760
         
     | 
| 
      
 249 
     | 
    
         
            +
                    $generator_to_battery_col = feature_report.timeseries_csv.column_names.index('ElectricityProduced:Generator:ToBattery')
         
     | 
| 
      
 250 
     | 
    
         
            +
                    if $generator_to_battery_col.nil?
         
     | 
| 
      
 251 
     | 
    
         
            +
                      $generator_to_battery_col = feature_report.timeseries_csv.column_names.length
         
     | 
| 
      
 252 
     | 
    
         
            +
                      feature_report.timeseries_csv.column_names.push('ElectricityProduced:Generator:ToBattery')
         
     | 
| 
      
 253 
     | 
    
         
            +
                    end
         
     | 
| 
      
 254 
     | 
    
         
            +
             
     | 
| 
      
 255 
     | 
    
         
            +
                    $generator_to_load = reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_to_load_series_kw'] || [0] * 8760
         
     | 
| 
      
 256 
     | 
    
         
            +
                    $generator_to_load_col = feature_report.timeseries_csv.column_names.index('ElectricityProduced:Generator:ToLoad')
         
     | 
| 
      
 257 
     | 
    
         
            +
                    if $generator_to_load_col.nil?
         
     | 
| 
      
 258 
     | 
    
         
            +
                      $generator_to_load_col = feature_report.timeseries_csv.column_names.length
         
     | 
| 
      
 259 
     | 
    
         
            +
                      feature_report.timeseries_csv.column_names.push('ElectricityProduced:Generator:ToLoad')
         
     | 
| 
      
 260 
     | 
    
         
            +
                    end
         
     | 
| 
      
 261 
     | 
    
         
            +
             
     | 
| 
      
 262 
     | 
    
         
            +
                    $generator_to_grid = reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_to_grid_series_kw'] || [0] * 8760
         
     | 
| 
      
 263 
     | 
    
         
            +
                    $generator_to_grid_col = feature_report.timeseries_csv.column_names.index('ElectricityProduced:Generator:ToGrid')
         
     | 
| 
      
 264 
     | 
    
         
            +
                    if $generator_to_grid_col.nil?
         
     | 
| 
      
 265 
     | 
    
         
            +
                      $generator_to_grid_col = feature_report.timeseries_csv.column_names.length
         
     | 
| 
      
 266 
     | 
    
         
            +
                      feature_report.timeseries_csv.column_names.push('ElectricityProduced:Generator:ToGrid')
         
     | 
| 
      
 267 
     | 
    
         
            +
                    end
         
     | 
| 
      
 268 
     | 
    
         
            +
             
     | 
| 
      
 269 
     | 
    
         
            +
                    $pv_total = reopt_output['outputs']['Scenario']['Site']['PV']['year_one_power_production_series_kw'] || [0] * 8760
         
     | 
| 
      
 270 
     | 
    
         
            +
                    $pv_total_col = feature_report.timeseries_csv.column_names.index('ElectricityProduced:PV:Total')
         
     | 
| 
      
 271 
     | 
    
         
            +
                    if $pv_total_col.nil?
         
     | 
| 
      
 272 
     | 
    
         
            +
                      $pv_total_col = feature_report.timeseries_csv.column_names.length
         
     | 
| 
      
 273 
     | 
    
         
            +
                      feature_report.timeseries_csv.column_names.push('ElectricityProduced:PV:Total')
         
     | 
| 
      
 274 
     | 
    
         
            +
                    end
         
     | 
| 
      
 275 
     | 
    
         
            +
             
     | 
| 
      
 276 
     | 
    
         
            +
                    $pv_to_battery = reopt_output['outputs']['Scenario']['Site']['PV']['year_one_to_battery_series_kw'] || [0] * 8760
         
     | 
| 
      
 277 
     | 
    
         
            +
                    $pv_to_battery_col = feature_report.timeseries_csv.column_names.index('ElectricityProduced:PV:ToBattery')
         
     | 
| 
      
 278 
     | 
    
         
            +
                    if $pv_to_battery_col.nil?
         
     | 
| 
      
 279 
     | 
    
         
            +
                      $pv_to_battery_col = feature_report.timeseries_csv.column_names.length
         
     | 
| 
      
 280 
     | 
    
         
            +
                      feature_report.timeseries_csv.column_names.push('ElectricityProduced:PV:ToBattery')
         
     | 
| 
      
 281 
     | 
    
         
            +
                    end
         
     | 
| 
      
 282 
     | 
    
         
            +
             
     | 
| 
      
 283 
     | 
    
         
            +
                    $pv_to_load = reopt_output['outputs']['Scenario']['Site']['PV']['year_one_to_load_series_kw'] || [0] * 8760
         
     | 
| 
      
 284 
     | 
    
         
            +
                    $pv_to_load_col = feature_report.timeseries_csv.column_names.index('ElectricityProduced:PV:ToLoad')
         
     | 
| 
      
 285 
     | 
    
         
            +
                    if $pv_to_load_col.nil?
         
     | 
| 
      
 286 
     | 
    
         
            +
                      $pv_to_load_col = feature_report.timeseries_csv.column_names.length
         
     | 
| 
      
 287 
     | 
    
         
            +
                      feature_report.timeseries_csv.column_names.push('ElectricityProduced:PV:ToLoad')
         
     | 
| 
      
 288 
     | 
    
         
            +
                    end
         
     | 
| 
      
 289 
     | 
    
         
            +
             
     | 
| 
      
 290 
     | 
    
         
            +
                    $pv_to_grid = reopt_output['outputs']['Scenario']['Site']['PV']['year_one_to_grid_series_kw'] || [0] * 8760
         
     | 
| 
      
 291 
     | 
    
         
            +
                    $pv_to_grid_col = feature_report.timeseries_csv.column_names.index('ElectricityProduced:PV:ToGrid')
         
     | 
| 
      
 292 
     | 
    
         
            +
                    if $pv_to_grid_col.nil?
         
     | 
| 
      
 293 
     | 
    
         
            +
                      $pv_to_grid_col = feature_report.timeseries_csv.column_names.length
         
     | 
| 
      
 294 
     | 
    
         
            +
                      feature_report.timeseries_csv.column_names.push('ElectricityProduced:PV:ToGrid')
         
     | 
| 
      
 295 
     | 
    
         
            +
                    end
         
     | 
| 
      
 296 
     | 
    
         
            +
             
     | 
| 
      
 297 
     | 
    
         
            +
                    $wind_total = reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_power_production_series_kw'] || [0] * 8760
         
     | 
| 
      
 298 
     | 
    
         
            +
                    $wind_total_col = feature_report.timeseries_csv.column_names.index('ElectricityProduced:Wind:Total')
         
     | 
| 
      
 299 
     | 
    
         
            +
                    if $wind_total_col.nil?
         
     | 
| 
      
 300 
     | 
    
         
            +
                      $wind_total_col = feature_report.timeseries_csv.column_names.length
         
     | 
| 
      
 301 
     | 
    
         
            +
                      feature_report.timeseries_csv.column_names.push('ElectricityProduced:Wind:Total')
         
     | 
| 
      
 302 
     | 
    
         
            +
                    end
         
     | 
| 
      
 303 
     | 
    
         
            +
             
     | 
| 
      
 304 
     | 
    
         
            +
                    $wind_to_battery = reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_to_battery_series_kw'] || [0] * 8760
         
     | 
| 
      
 305 
     | 
    
         
            +
                    $wind_to_battery_col = feature_report.timeseries_csv.column_names.index('ElectricityProduced:Wind:ToBattery')
         
     | 
| 
      
 306 
     | 
    
         
            +
                    if $wind_to_battery_col.nil?
         
     | 
| 
      
 307 
     | 
    
         
            +
                      $wind_to_battery_col = feature_report.timeseries_csv.column_names.length
         
     | 
| 
      
 308 
     | 
    
         
            +
                      feature_report.timeseries_csv.column_names.push('ElectricityProduced:Wind:ToBattery')
         
     | 
| 
      
 309 
     | 
    
         
            +
                    end
         
     | 
| 
      
 310 
     | 
    
         
            +
             
     | 
| 
      
 311 
     | 
    
         
            +
                    $wind_to_load = reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_to_load_series_kw'] || [0] * 8760
         
     | 
| 
      
 312 
     | 
    
         
            +
                    $wind_to_load_col = feature_report.timeseries_csv.column_names.index('ElectricityProduced:Wind:ToLoad')
         
     | 
| 
      
 313 
     | 
    
         
            +
                    if $wind_to_load_col.nil?
         
     | 
| 
      
 314 
     | 
    
         
            +
                      $wind_to_load_col = feature_report.timeseries_csv.column_names.length
         
     | 
| 
      
 315 
     | 
    
         
            +
                      feature_report.timeseries_csv.column_names.push('ElectricityProduced:Wind:ToLoad')
         
     | 
| 
      
 316 
     | 
    
         
            +
                    end
         
     | 
| 
      
 317 
     | 
    
         
            +
             
     | 
| 
      
 318 
     | 
    
         
            +
                    $wind_to_grid = reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_to_grid_series_kw'] || [0] * 8760
         
     | 
| 
      
 319 
     | 
    
         
            +
                    $wind_to_grid_col = feature_report.timeseries_csv.column_names.index('ElectricityProduced:Wind:ToGrid')
         
     | 
| 
      
 320 
     | 
    
         
            +
                    if $wind_to_grid_col.nil?
         
     | 
| 
      
 321 
     | 
    
         
            +
                      $wind_to_grid_col = feature_report.timeseries_csv.column_names.length
         
     | 
| 
      
 322 
     | 
    
         
            +
                      feature_report.timeseries_csv.column_names.push('ElectricityProduced:Wind:ToGrid')
         
     | 
| 
      
 323 
     | 
    
         
            +
                    end
         
     | 
| 
      
 324 
     | 
    
         
            +
             
     | 
| 
      
 325 
     | 
    
         
            +
                    def modrow(x, i) # :nodoc:
         
     | 
| 
      
 326 
     | 
    
         
            +
                      x[$generation_timeseries_kwh_col] = $generation_timeseries_kwh[i] || 0
         
     | 
| 
      
 327 
     | 
    
         
            +
                      x[$load_col] = $load[i] || 0
         
     | 
| 
      
 328 
     | 
    
         
            +
                      x[$utility_to_load_col] = $utility_to_load[i] || 0
         
     | 
| 
      
 329 
     | 
    
         
            +
                      x[$utility_to_battery_col] = $utility_to_battery[i] || 0
         
     | 
| 
      
 330 
     | 
    
         
            +
                      x[$storage_to_load_col] = $storage_to_load[i] || 0
         
     | 
| 
      
 331 
     | 
    
         
            +
                      x[$storage_to_grid_col] = $storage_to_grid[i] || 0
         
     | 
| 
      
 332 
     | 
    
         
            +
                      x[$storage_soc_col] = $storage_soc[i] || 0
         
     | 
| 
      
 333 
     | 
    
         
            +
                      x[$generator_total_col] = $generator_total[i] || 0
         
     | 
| 
      
 334 
     | 
    
         
            +
                      x[$generator_to_battery_col] = $generator_to_battery[i] || 0
         
     | 
| 
      
 335 
     | 
    
         
            +
                      x[$generator_to_load_col] = $generator_to_load[i] || 0
         
     | 
| 
      
 336 
     | 
    
         
            +
                      x[$generator_to_grid_col] = $generator_to_grid[i] || 0
         
     | 
| 
      
 337 
     | 
    
         
            +
                      x[$pv_total_col] = $pv_total[i] || 0
         
     | 
| 
      
 338 
     | 
    
         
            +
                      x[$pv_to_battery_col] = $pv_to_battery[i] || 0
         
     | 
| 
      
 339 
     | 
    
         
            +
                      x[$pv_to_load_col] = $pv_to_load[i] || 0
         
     | 
| 
      
 340 
     | 
    
         
            +
                      x[$pv_to_grid_col] = $pv_to_grid[i] || 0
         
     | 
| 
      
 341 
     | 
    
         
            +
                      x[$wind_total_col] = $wind_total[i] || 0
         
     | 
| 
      
 342 
     | 
    
         
            +
                      x[$wind_to_battery_col] = $wind_to_battery[i] || 0
         
     | 
| 
      
 343 
     | 
    
         
            +
                      x[$wind_to_load_col] = $wind_to_load[i] || 0
         
     | 
| 
      
 344 
     | 
    
         
            +
                      x[$wind_to_grid_col] = $wind_to_grid[i] || 0
         
     | 
| 
      
 345 
     | 
    
         
            +
                      return x
         
     | 
| 
      
 346 
     | 
    
         
            +
                    end
         
     | 
| 
      
 347 
     | 
    
         
            +
             
     | 
| 
      
 348 
     | 
    
         
            +
                    old_data = CSV.open(feature_report.timeseries_csv.path).read
         
     | 
| 
      
 349 
     | 
    
         
            +
                    mod_data = old_data.map.with_index do |x, i|
         
     | 
| 
      
 350 
     | 
    
         
            +
                      if i > 0
         
     | 
| 
      
 351 
     | 
    
         
            +
                        modrow(x, i)
         
     | 
| 
      
 352 
     | 
    
         
            +
                      else
         
     | 
| 
      
 353 
     | 
    
         
            +
                        x
         
     | 
| 
      
 354 
     | 
    
         
            +
                      end
         
     | 
| 
      
 355 
     | 
    
         
            +
                    end
         
     | 
| 
      
 356 
     | 
    
         
            +
             
     | 
| 
      
 357 
     | 
    
         
            +
                    mod_data[0] = feature_report.timeseries_csv.column_names
         
     | 
| 
      
 358 
     | 
    
         
            +
             
     | 
| 
      
 359 
     | 
    
         
            +
                    feature_report.timeseries_csv.reload_data(mod_data)
         
     | 
| 
      
 360 
     | 
    
         
            +
                    return feature_report
         
     | 
| 
      
 361 
     | 
    
         
            +
                  end
         
     | 
| 
      
 362 
     | 
    
         
            +
                end
         
     | 
| 
      
 363 
     | 
    
         
            +
              end
         
     | 
| 
      
 364 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,230 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # *********************************************************************************
         
     | 
| 
      
 2 
     | 
    
         
            +
            # URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
         
     | 
| 
      
 3 
     | 
    
         
            +
            # contributors. All rights reserved.
         
     | 
| 
      
 4 
     | 
    
         
            +
            #
         
     | 
| 
      
 5 
     | 
    
         
            +
            # Redistribution and use in source and binary forms, with or without modification,
         
     | 
| 
      
 6 
     | 
    
         
            +
            # are permitted provided that the following conditions are met:
         
     | 
| 
      
 7 
     | 
    
         
            +
            #
         
     | 
| 
      
 8 
     | 
    
         
            +
            # Redistributions of source code must retain the above copyright notice, this list
         
     | 
| 
      
 9 
     | 
    
         
            +
            # of conditions and the following disclaimer.
         
     | 
| 
      
 10 
     | 
    
         
            +
            #
         
     | 
| 
      
 11 
     | 
    
         
            +
            # Redistributions in binary form must reproduce the above copyright notice, this
         
     | 
| 
      
 12 
     | 
    
         
            +
            # list of conditions and the following disclaimer in the documentation and/or other
         
     | 
| 
      
 13 
     | 
    
         
            +
            # materials provided with the distribution.
         
     | 
| 
      
 14 
     | 
    
         
            +
            #
         
     | 
| 
      
 15 
     | 
    
         
            +
            # Neither the name of the copyright holder nor the names of its contributors may be
         
     | 
| 
      
 16 
     | 
    
         
            +
            # used to endorse or promote products derived from this software without specific
         
     | 
| 
      
 17 
     | 
    
         
            +
            # prior written permission.
         
     | 
| 
      
 18 
     | 
    
         
            +
            #
         
     | 
| 
      
 19 
     | 
    
         
            +
            # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
         
     | 
| 
      
 20 
     | 
    
         
            +
            # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
         
     | 
| 
      
 21 
     | 
    
         
            +
            # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
         
     | 
| 
      
 22 
     | 
    
         
            +
            # IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
         
     | 
| 
      
 23 
     | 
    
         
            +
            # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
         
     | 
| 
      
 24 
     | 
    
         
            +
            # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
         
     | 
| 
      
 25 
     | 
    
         
            +
            # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
         
     | 
| 
      
 26 
     | 
    
         
            +
            # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
         
     | 
| 
      
 27 
     | 
    
         
            +
            # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
         
     | 
| 
      
 28 
     | 
    
         
            +
            # OF THE POSSIBILITY OF SUCH DAMAGE.
         
     | 
| 
      
 29 
     | 
    
         
            +
            # *********************************************************************************
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
            require 'net/https'
         
     | 
| 
      
 32 
     | 
    
         
            +
            require 'openssl'
         
     | 
| 
      
 33 
     | 
    
         
            +
            require 'uri'
         
     | 
| 
      
 34 
     | 
    
         
            +
            require 'uri'
         
     | 
| 
      
 35 
     | 
    
         
            +
            require 'json'
         
     | 
| 
      
 36 
     | 
    
         
            +
            require 'securerandom'
         
     | 
| 
      
 37 
     | 
    
         
            +
            require 'certified'
         
     | 
| 
      
 38 
     | 
    
         
            +
            require_relative '../../../developer_nrel_key'
         
     | 
| 
      
 39 
     | 
    
         
            +
            require 'urbanopt/reopt/reopt_logger'
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
            module URBANopt # :nodoc:
         
     | 
| 
      
 42 
     | 
    
         
            +
              module REopt  # :nodoc:
         
     | 
| 
      
 43 
     | 
    
         
            +
                class REoptLiteAPI
         
     | 
| 
      
 44 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 45 
     | 
    
         
            +
                  # \REoptLiteAPI manages submitting optimization tasks to the \REopt Lite API  and recieving results.
         
     | 
| 
      
 46 
     | 
    
         
            +
                  # Results can either be sourced from the production \REopt Lite API with an API key from developer.nrel.gov, or from
         
     | 
| 
      
 47 
     | 
    
         
            +
                  # a version running at localhost.
         
     | 
| 
      
 48 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 49 
     | 
    
         
            +
                  #
         
     | 
| 
      
 50 
     | 
    
         
            +
                  # [*parameters:*]
         
     | 
| 
      
 51 
     | 
    
         
            +
                  #
         
     | 
| 
      
 52 
     | 
    
         
            +
                  # * +use_localhost+ - _Bool_ - If this is true, requests will be sent to a version of the \REopt Lite API running on localhost. Default is false, such that the production version of \REopt Lite is accessed.
         
     | 
| 
      
 53 
     | 
    
         
            +
                  # * +nrel_developer_key+ - _String_ - API key used to access the \REopt Lite APi. Required only if localhost is false. Obtain from https://developer.nrel.gov/signup/
         
     | 
| 
      
 54 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 55 
     | 
    
         
            +
                  def initialize(nrel_developer_key = nil, use_localhost = false)
         
     | 
| 
      
 56 
     | 
    
         
            +
                    @use_localhost = use_localhost
         
     | 
| 
      
 57 
     | 
    
         
            +
                    if @use_localhost
         
     | 
| 
      
 58 
     | 
    
         
            +
                      @uri_submit = URI.parse('http//:127.0.0.1:8000/v1/job/')
         
     | 
| 
      
 59 
     | 
    
         
            +
                    else
         
     | 
| 
      
 60 
     | 
    
         
            +
                      if [nil, '', '<insert your key here>'].include? nrel_developer_key
         
     | 
| 
      
 61 
     | 
    
         
            +
                        if [nil, '', '<insert your key here>'].include? DEVELOPER_NREL_KEY
         
     | 
| 
      
 62 
     | 
    
         
            +
                          raise 'A developer.nrel.gov API key is required. Please see https://developer.nrel.gov/signup/ then update the file urbanopt-reopt-gem/developer_nrel_key.rb'
         
     | 
| 
      
 63 
     | 
    
         
            +
                        else
         
     | 
| 
      
 64 
     | 
    
         
            +
                          nrel_developer_key = DEVELOPER_NREL_KEY
         
     | 
| 
      
 65 
     | 
    
         
            +
                        end
         
     | 
| 
      
 66 
     | 
    
         
            +
                      end
         
     | 
| 
      
 67 
     | 
    
         
            +
                      @nrel_developer_key = nrel_developer_key
         
     | 
| 
      
 68 
     | 
    
         
            +
                      @uri_submit = URI.parse("https://developer.nrel.gov/api/reopt/v1/job/?api_key=#{@nrel_developer_key}")
         
     | 
| 
      
 69 
     | 
    
         
            +
                      # initialize @@logger
         
     | 
| 
      
 70 
     | 
    
         
            +
                      @@logger ||= URBANopt::REopt.reopt_logger
         
     | 
| 
      
 71 
     | 
    
         
            +
                    end
         
     | 
| 
      
 72 
     | 
    
         
            +
                  end
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 75 
     | 
    
         
            +
                  # URL of the results end point for a specific optimization task
         
     | 
| 
      
 76 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 77 
     | 
    
         
            +
                  #
         
     | 
| 
      
 78 
     | 
    
         
            +
                  # [*parameters:*]
         
     | 
| 
      
 79 
     | 
    
         
            +
                  #
         
     | 
| 
      
 80 
     | 
    
         
            +
                  # * +run_uuid+ - _String_ - Unique run_uuid obtained from the \REopt Lite job submittal URL for a specific optimization task.
         
     | 
| 
      
 81 
     | 
    
         
            +
                  #
         
     | 
| 
      
 82 
     | 
    
         
            +
                  # [*return:*] _URI_ - Returns URI object for use in calling the \REopt Lite results endpoint for a specifc optimization task.
         
     | 
| 
      
 83 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 84 
     | 
    
         
            +
                  def uri_results(run_uuid) # :nodoc:
         
     | 
| 
      
 85 
     | 
    
         
            +
                    if @use_localhost
         
     | 
| 
      
 86 
     | 
    
         
            +
                      return URI.parse("http://127.0.0.1:8000/v1/job/#{run_uuid}/results")
         
     | 
| 
      
 87 
     | 
    
         
            +
                    end
         
     | 
| 
      
 88 
     | 
    
         
            +
                    return URI.parse("https://developer.nrel.gov/api/reopt/v1/job/#{run_uuid}/results?api_key=#{@nrel_developer_key}")
         
     | 
| 
      
 89 
     | 
    
         
            +
                  end
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                  def make_request(http, r, max_tries = 3)
         
     | 
| 
      
 92 
     | 
    
         
            +
                    result = nil
         
     | 
| 
      
 93 
     | 
    
         
            +
                    tries = 0
         
     | 
| 
      
 94 
     | 
    
         
            +
                    while tries < max_tries
         
     | 
| 
      
 95 
     | 
    
         
            +
                      begin
         
     | 
| 
      
 96 
     | 
    
         
            +
                           result = http.request(r)
         
     | 
| 
      
 97 
     | 
    
         
            +
                           tries = 4
         
     | 
| 
      
 98 
     | 
    
         
            +
                         rescue StandardError
         
     | 
| 
      
 99 
     | 
    
         
            +
                           tries += 1
         
     | 
| 
      
 100 
     | 
    
         
            +
                         end
         
     | 
| 
      
 101 
     | 
    
         
            +
                    end
         
     | 
| 
      
 102 
     | 
    
         
            +
                    return result
         
     | 
| 
      
 103 
     | 
    
         
            +
                  end
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 106 
     | 
    
         
            +
                  # Checks if a optimization task can be submitted to the \REopt Lite API
         
     | 
| 
      
 107 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 108 
     | 
    
         
            +
                  #
         
     | 
| 
      
 109 
     | 
    
         
            +
                  # [*parameters:*]
         
     | 
| 
      
 110 
     | 
    
         
            +
                  #
         
     | 
| 
      
 111 
     | 
    
         
            +
                  # * +data+ - _Hash_ - Default \REopt Lite formatted post containing at least all the required parameters.
         
     | 
| 
      
 112 
     | 
    
         
            +
                  #
         
     | 
| 
      
 113 
     | 
    
         
            +
                  # [*return:*] _Bool_ - Returns true if the post succeeeds. Otherwise returns false.
         
     | 
| 
      
 114 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 115 
     | 
    
         
            +
                  def check_connection(data)
         
     | 
| 
      
 116 
     | 
    
         
            +
                    header = { 'Content-Type' => 'application/json' }
         
     | 
| 
      
 117 
     | 
    
         
            +
                    http = Net::HTTP.new(@uri_submit.host, @uri_submit.port)
         
     | 
| 
      
 118 
     | 
    
         
            +
                    if !@use_localhost
         
     | 
| 
      
 119 
     | 
    
         
            +
                      http.use_ssl = true
         
     | 
| 
      
 120 
     | 
    
         
            +
                end
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
                    request = Net::HTTP::Post.new(@uri_submit, header)
         
     | 
| 
      
 123 
     | 
    
         
            +
                    request.body = data.to_json
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                    # Send the request
         
     | 
| 
      
 126 
     | 
    
         
            +
                    response = make_request(http, request)
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
                    if !response.is_a?(Net::HTTPSuccess)
         
     | 
| 
      
 129 
     | 
    
         
            +
                      @@logger.error('Check_connection Failed')
         
     | 
| 
      
 130 
     | 
    
         
            +
                      raise 'Check_connection Failed'
         
     | 
| 
      
 131 
     | 
    
         
            +
                    end
         
     | 
| 
      
 132 
     | 
    
         
            +
                    return true
         
     | 
| 
      
 133 
     | 
    
         
            +
                  end
         
     | 
| 
      
 134 
     | 
    
         
            +
             
     | 
| 
      
 135 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 136 
     | 
    
         
            +
                  # Completes a \REopt Lite optimization. From a formatted hash, an optimization task is submitted to the API.
         
     | 
| 
      
 137 
     | 
    
         
            +
                  # Results are polled at 5 second interval until they are ready or an error is returned from the API. Results
         
     | 
| 
      
 138 
     | 
    
         
            +
                  # are written to disk.
         
     | 
| 
      
 139 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 140 
     | 
    
         
            +
                  #
         
     | 
| 
      
 141 
     | 
    
         
            +
                  # [*parameters:*]
         
     | 
| 
      
 142 
     | 
    
         
            +
                  #
         
     | 
| 
      
 143 
     | 
    
         
            +
                  # * +reopt_input+ - _Hash_ - \REopt Lite formatted post containing at least required parameters.
         
     | 
| 
      
 144 
     | 
    
         
            +
                  # * +filename+ - _String_ - Path to file that will be created containing the full \REopt Lite response.
         
     | 
| 
      
 145 
     | 
    
         
            +
                  #
         
     | 
| 
      
 146 
     | 
    
         
            +
                  # [*return:*] _Bool_ - Returns true if the post succeeeds. Otherwise returns false.
         
     | 
| 
      
 147 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 148 
     | 
    
         
            +
                  def reopt_request(reopt_input, filename)
         
     | 
| 
      
 149 
     | 
    
         
            +
                    description = reopt_input[:Scenario][:description]
         
     | 
| 
      
 150 
     | 
    
         
            +
             
     | 
| 
      
 151 
     | 
    
         
            +
                    @@logger.info("Submitting #{description} to REopt Lite API")
         
     | 
| 
      
 152 
     | 
    
         
            +
             
     | 
| 
      
 153 
     | 
    
         
            +
                    # Format the request
         
     | 
| 
      
 154 
     | 
    
         
            +
                    header = { 'Content-Type' => 'application/json' }
         
     | 
| 
      
 155 
     | 
    
         
            +
                    http = Net::HTTP.new(@uri_submit.host, @uri_submit.port)
         
     | 
| 
      
 156 
     | 
    
         
            +
                    if !@use_localhost
         
     | 
| 
      
 157 
     | 
    
         
            +
                      http.use_ssl = true
         
     | 
| 
      
 158 
     | 
    
         
            +
                    end
         
     | 
| 
      
 159 
     | 
    
         
            +
                    request = Net::HTTP::Post.new(@uri_submit, header)
         
     | 
| 
      
 160 
     | 
    
         
            +
                    request.body = reopt_input.to_json
         
     | 
| 
      
 161 
     | 
    
         
            +
             
     | 
| 
      
 162 
     | 
    
         
            +
                    # Send the request
         
     | 
| 
      
 163 
     | 
    
         
            +
                    response = make_request(http, request)
         
     | 
| 
      
 164 
     | 
    
         
            +
             
     | 
| 
      
 165 
     | 
    
         
            +
                    # Get UUID
         
     | 
| 
      
 166 
     | 
    
         
            +
                    run_uuid = JSON.parse(response.body)['run_uuid']
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
                    if File.directory? filename
         
     | 
| 
      
 169 
     | 
    
         
            +
                      if run_uuid.nil?
         
     | 
| 
      
 170 
     | 
    
         
            +
                        run_uuid = 'error'
         
     | 
| 
      
 171 
     | 
    
         
            +
                      end
         
     | 
| 
      
 172 
     | 
    
         
            +
                      if run_uuid.downcase.include? 'error'
         
     | 
| 
      
 173 
     | 
    
         
            +
                        run_uuid = "error#{SecureRandom.uuid}"
         
     | 
| 
      
 174 
     | 
    
         
            +
                      end
         
     | 
| 
      
 175 
     | 
    
         
            +
                      filename = File.join(filename, "#{description}_#{run_uuid}.json")
         
     | 
| 
      
 176 
     | 
    
         
            +
                      @@logger.info("REopt results saved to #{filename}")
         
     | 
| 
      
 177 
     | 
    
         
            +
                    end
         
     | 
| 
      
 178 
     | 
    
         
            +
             
     | 
| 
      
 179 
     | 
    
         
            +
                    if response.code != '201'
         
     | 
| 
      
 180 
     | 
    
         
            +
                      File.open(filename, 'w') do |f|
         
     | 
| 
      
 181 
     | 
    
         
            +
                        f.write(response.body)
         
     | 
| 
      
 182 
     | 
    
         
            +
                      end
         
     | 
| 
      
 183 
     | 
    
         
            +
                      raise "Error in REopt optimization post - see #{filename}"
         
     | 
| 
      
 184 
     | 
    
         
            +
                    end
         
     | 
| 
      
 185 
     | 
    
         
            +
             
     | 
| 
      
 186 
     | 
    
         
            +
                    # Poll results until ready or error occurs
         
     | 
| 
      
 187 
     | 
    
         
            +
                    status = 'Optimizing...'
         
     | 
| 
      
 188 
     | 
    
         
            +
                    uri = uri_results(run_uuid)
         
     | 
| 
      
 189 
     | 
    
         
            +
                    http = Net::HTTP.new(uri.host, uri.port)
         
     | 
| 
      
 190 
     | 
    
         
            +
                    if !@use_localhost
         
     | 
| 
      
 191 
     | 
    
         
            +
                      http.use_ssl = true
         
     | 
| 
      
 192 
     | 
    
         
            +
                    end
         
     | 
| 
      
 193 
     | 
    
         
            +
             
     | 
| 
      
 194 
     | 
    
         
            +
                    request = Net::HTTP::Get.new(uri.request_uri)
         
     | 
| 
      
 195 
     | 
    
         
            +
             
     | 
| 
      
 196 
     | 
    
         
            +
                    while status == 'Optimizing...'
         
     | 
| 
      
 197 
     | 
    
         
            +
                      response = make_request(http, request)
         
     | 
| 
      
 198 
     | 
    
         
            +
                      data = JSON.parse(response.body)
         
     | 
| 
      
 199 
     | 
    
         
            +
                      sizes = (data['outputs']['Scenario']['Site']['PV']['size_kw'] || 0) + (data['outputs']['Scenario']['Site']['Storage']['size_kw'] || 0) + (data['outputs']['Scenario']['Site']['Wind']['size_kw'] || 0) + (data['outputs']['Scenario']['Site']['Generator']['size_kw'] || 0)
         
     | 
| 
      
 200 
     | 
    
         
            +
                      status = data['outputs']['Scenario']['status']
         
     | 
| 
      
 201 
     | 
    
         
            +
             
     | 
| 
      
 202 
     | 
    
         
            +
                      sleep 5
         
     | 
| 
      
 203 
     | 
    
         
            +
                    end
         
     | 
| 
      
 204 
     | 
    
         
            +
             
     | 
| 
      
 205 
     | 
    
         
            +
                    _max_retry = 5
         
     | 
| 
      
 206 
     | 
    
         
            +
                    _tries = 0
         
     | 
| 
      
 207 
     | 
    
         
            +
                    (check_complete = sizes == 0) && ((data['outputs']['Scenario']['Site']['Financial']['npv_us_dollars'] || 0) > 0)
         
     | 
| 
      
 208 
     | 
    
         
            +
                    while (_tries < _max_retry) && check_complete
         
     | 
| 
      
 209 
     | 
    
         
            +
                      sleep 1
         
     | 
| 
      
 210 
     | 
    
         
            +
                      response = make_request(http, request)
         
     | 
| 
      
 211 
     | 
    
         
            +
                      data = JSON.parse(response.body)
         
     | 
| 
      
 212 
     | 
    
         
            +
                      sizes = (data['outputs']['Scenario']['Site']['PV']['size_kw'] || 0) + (data['outputs']['Scenario']['Site']['Storage']['size_kw'] || 0) + (data['outputs']['Scenario']['Site']['Wind']['size_kw'] || 0) + (data['outputs']['Scenario']['Site']['Generator']['size_kw'] || 0)
         
     | 
| 
      
 213 
     | 
    
         
            +
                      (check_complete = sizes == 0) && ((data['outputs']['Scenario']['Site']['Financial']['npv_us_dollars'] || 0) > 0)
         
     | 
| 
      
 214 
     | 
    
         
            +
                      _tries += 1
         
     | 
| 
      
 215 
     | 
    
         
            +
                    end
         
     | 
| 
      
 216 
     | 
    
         
            +
             
     | 
| 
      
 217 
     | 
    
         
            +
                    File.open(filename, 'w') do |f|
         
     | 
| 
      
 218 
     | 
    
         
            +
                      f.write(data.to_json)
         
     | 
| 
      
 219 
     | 
    
         
            +
                    end
         
     | 
| 
      
 220 
     | 
    
         
            +
             
     | 
| 
      
 221 
     | 
    
         
            +
                    if status == 'optimal'
         
     | 
| 
      
 222 
     | 
    
         
            +
                      return data
         
     | 
| 
      
 223 
     | 
    
         
            +
                    end
         
     | 
| 
      
 224 
     | 
    
         
            +
             
     | 
| 
      
 225 
     | 
    
         
            +
                    error_message = data['messages']['error']
         
     | 
| 
      
 226 
     | 
    
         
            +
                    raise "Error from REopt API - #{error_message}"
         
     | 
| 
      
 227 
     | 
    
         
            +
                  end
         
     | 
| 
      
 228 
     | 
    
         
            +
                end
         
     | 
| 
      
 229 
     | 
    
         
            +
              end
         
     | 
| 
      
 230 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,42 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # *********************************************************************************
         
     | 
| 
      
 2 
     | 
    
         
            +
            # URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
         
     | 
| 
      
 3 
     | 
    
         
            +
            # contributors. All rights reserved.
         
     | 
| 
      
 4 
     | 
    
         
            +
            #
         
     | 
| 
      
 5 
     | 
    
         
            +
            # Redistribution and use in source and binary forms, with or without modification,
         
     | 
| 
      
 6 
     | 
    
         
            +
            # are permitted provided that the following conditions are met:
         
     | 
| 
      
 7 
     | 
    
         
            +
            #
         
     | 
| 
      
 8 
     | 
    
         
            +
            # Redistributions of source code must retain the above copyright notice, this list
         
     | 
| 
      
 9 
     | 
    
         
            +
            # of conditions and the following disclaimer.
         
     | 
| 
      
 10 
     | 
    
         
            +
            #
         
     | 
| 
      
 11 
     | 
    
         
            +
            # Redistributions in binary form must reproduce the above copyright notice, this
         
     | 
| 
      
 12 
     | 
    
         
            +
            # list of conditions and the following disclaimer in the documentation and/or other
         
     | 
| 
      
 13 
     | 
    
         
            +
            # materials provided with the distribution.
         
     | 
| 
      
 14 
     | 
    
         
            +
            #
         
     | 
| 
      
 15 
     | 
    
         
            +
            # Neither the name of the copyright holder nor the names of its contributors may be
         
     | 
| 
      
 16 
     | 
    
         
            +
            # used to endorse or promote products derived from this software without specific
         
     | 
| 
      
 17 
     | 
    
         
            +
            # prior written permission.
         
     | 
| 
      
 18 
     | 
    
         
            +
            #
         
     | 
| 
      
 19 
     | 
    
         
            +
            # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
         
     | 
| 
      
 20 
     | 
    
         
            +
            # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
         
     | 
| 
      
 21 
     | 
    
         
            +
            # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
         
     | 
| 
      
 22 
     | 
    
         
            +
            # IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
         
     | 
| 
      
 23 
     | 
    
         
            +
            # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
         
     | 
| 
      
 24 
     | 
    
         
            +
            # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
         
     | 
| 
      
 25 
     | 
    
         
            +
            # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
         
     | 
| 
      
 26 
     | 
    
         
            +
            # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
         
     | 
| 
      
 27 
     | 
    
         
            +
            # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
         
     | 
| 
      
 28 
     | 
    
         
            +
            # OF THE POSSIBILITY OF SUCH DAMAGE.
         
     | 
| 
      
 29 
     | 
    
         
            +
            # *********************************************************************************
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
            require 'logger'
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
            module URBANopt
         
     | 
| 
      
 34 
     | 
    
         
            +
              module REopt
         
     | 
| 
      
 35 
     | 
    
         
            +
                @@reopt_logger = Logger.new(STDOUT)
         
     | 
| 
      
 36 
     | 
    
         
            +
                ##
         
     | 
| 
      
 37 
     | 
    
         
            +
                # Definining class variable "@@logger" to log errors, info and warning messages.
         
     | 
| 
      
 38 
     | 
    
         
            +
                def self.reopt_logger
         
     | 
| 
      
 39 
     | 
    
         
            +
                  @@reopt_logger
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
              end
         
     | 
| 
      
 42 
     | 
    
         
            +
            end
         
     |