urbanopt-reopt 0.6.0 → 0.6.1
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 +4 -4
 - data/.rubocop.yml +2 -2
 - data/CHANGELOG.md +7 -0
 - data/Gemfile +1 -1
 - data/Rakefile +2 -2
 - data/lib/urbanopt/reopt/feature_report_adapter.rb +38 -55
 - data/lib/urbanopt/reopt/reopt_lite_api.rb +56 -42
 - data/lib/urbanopt/reopt/reopt_logger.rb +1 -1
 - data/lib/urbanopt/reopt/reopt_post_processor.rb +72 -47
 - data/lib/urbanopt/reopt/scenario/reopt_scenario_csv.rb +7 -7
 - data/lib/urbanopt/reopt/scenario_report_adapter.rb +46 -63
 - data/lib/urbanopt/reopt/utilities.rb +106 -106
 - data/lib/urbanopt/reopt/version.rb +1 -1
 - data/urbanopt-reopt.gemspec +2 -5
 - metadata +10 -24
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 219ca91a5d19e9bb7e556b82795b1c6d8585ff114a315e684d90877a955f0d77
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: d3176067f1c7621d242285404274e932ee03cf4cef4520565321f23ca21d866b
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: b535f5094280f785bc86172528931011f1dba89cbc8c5b3673e6452aea093f7add85bf9b2ef94ab8db960d39c26b71c2edd7d0742f8f6ef3c405c24bcb67a745
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 4052393e76b6b15077dc7b24c5654aa7dc74d69482d31fdf472c6a7ddf1c98dd1f9202b17adb3e964879a6c193450bd88843047a557ce936e4c78d1bc2c21dda
         
     | 
    
        data/.rubocop.yml
    CHANGED
    
    
    
        data/CHANGELOG.md
    CHANGED
    
    | 
         @@ -1,5 +1,12 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # URBANopt REopt Gem
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
      
 3 
     | 
    
         
            +
            ## Version 0.6.1
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            Date Range: 04/30/21 - 06/30/21:
         
     | 
| 
      
 6 
     | 
    
         
            +
            * Fixed [#83]( https://github.com/urbanopt/urbanopt-reopt-gem/issues/83 ), reopt rate-limit error is hard to decipher
         
     | 
| 
      
 7 
     | 
    
         
            +
            * Fixed [#84]( https://github.com/urbanopt/urbanopt-reopt-gem/pull/84 ), Api error codes
         
     | 
| 
      
 8 
     | 
    
         
            +
            * Fixed [#86]( https://github.com/urbanopt/urbanopt-reopt-gem/pull/86 ), update rubocop configs to v4
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
       3 
10 
     | 
    
         
             
            ## Version 0.6.0
         
     | 
| 
       4 
11 
     | 
    
         
             
            Date Range: 4/16/21 - 4/29/21
         
     | 
| 
       5 
12 
     | 
    
         | 
    
        data/Gemfile
    CHANGED
    
    | 
         @@ -53,4 +53,4 @@ allow_local = ENV['FAVOR_LOCAL_GEMS'] 
     | 
|
| 
       53 
53 
     | 
    
         
             
            #   gem 'urbanopt-geojson', github: 'URBANopt/urbanopt-geojson-gem', branch: 'develop'
         
     | 
| 
       54 
54 
     | 
    
         
             
            # end
         
     | 
| 
       55 
55 
     | 
    
         | 
| 
       56 
     | 
    
         
            -
            # gem 'urbanopt-reporting', github: 'URBANopt/urbanopt-reporting-gem', branch: 'develop'
         
     | 
| 
      
 56 
     | 
    
         
            +
            # gem 'urbanopt-reporting', github: 'URBANopt/urbanopt-reporting-gem', branch: 'develop'
         
     | 
    
        data/Rakefile
    CHANGED
    
    
| 
         @@ -99,16 +99,12 @@ module URBANopt # :nodoc: 
     | 
|
| 
       99 
99 
     | 
    
         
             
                    reopt_inputs[:Scenario][:Site][:longitude] = feature_report.location.longitude_deg
         
     | 
| 
       100 
100 
     | 
    
         | 
| 
       101 
101 
     | 
    
         
             
                    # Parse Optional FeatureReport metrics - do not overwrite from assumptions file
         
     | 
| 
       102 
     | 
    
         
            -
                    if reopt_inputs[:Scenario][:Site][:roof_squarefeet].nil?
         
     | 
| 
       103 
     | 
    
         
            -
                       
     | 
| 
       104 
     | 
    
         
            -
                        reopt_inputs[:Scenario][:Site][:roof_squarefeet] = feature_report.program.roof_area_sqft[:available_roof_area_sqft]
         
     | 
| 
       105 
     | 
    
         
            -
                      end
         
     | 
| 
      
 102 
     | 
    
         
            +
                    if reopt_inputs[:Scenario][:Site][:roof_squarefeet].nil? && !feature_report.program.roof_area_sqft.nil?
         
     | 
| 
      
 103 
     | 
    
         
            +
                      reopt_inputs[:Scenario][:Site][:roof_squarefeet] = feature_report.program.roof_area_sqft[:available_roof_area_sqft]
         
     | 
| 
       106 
104 
     | 
    
         
             
                    end
         
     | 
| 
       107 
105 
     | 
    
         | 
| 
       108 
     | 
    
         
            -
                    if reopt_inputs[:Scenario][:Site][:land_acres].nil?
         
     | 
| 
       109 
     | 
    
         
            -
                       
     | 
| 
       110 
     | 
    
         
            -
                        reopt_inputs[:Scenario][:Site][:land_acres] = feature_report.program.site_area_sqft * 1.0 / 43560 # acres/sqft
         
     | 
| 
       111 
     | 
    
         
            -
                      end
         
     | 
| 
      
 106 
     | 
    
         
            +
                    if reopt_inputs[:Scenario][:Site][:land_acres].nil? && !feature_report.program.site_area_sqft.nil?
         
     | 
| 
      
 107 
     | 
    
         
            +
                      reopt_inputs[:Scenario][:Site][:land_acres] = feature_report.program.site_area_sqft * 1.0 / 43560 # acres/sqft
         
     | 
| 
       112 
108 
     | 
    
         
             
                    end
         
     | 
| 
       113 
109 
     | 
    
         | 
| 
       114 
110 
     | 
    
         
             
                    if reopt_inputs[:Scenario][:time_steps_per_hour].nil?
         
     | 
| 
         @@ -117,23 +113,23 @@ module URBANopt # :nodoc: 
     | 
|
| 
       117 
113 
     | 
    
         | 
| 
       118 
114 
     | 
    
         
             
                    # Parse Load Profile
         
     | 
| 
       119 
115 
     | 
    
         
             
                    begin
         
     | 
| 
       120 
     | 
    
         
            -
                      #Convert kWh values in the timeseries CSV to kW
         
     | 
| 
      
 116 
     | 
    
         
            +
                      # Convert kWh values in the timeseries CSV to kW
         
     | 
| 
       121 
117 
     | 
    
         
             
                      col_num = feature_report.timeseries_csv.column_names.index('Electricity:Facility(kWh)')
         
     | 
| 
       122 
118 
     | 
    
         
             
                      t = CSV.read(feature_report.timeseries_csv.path, headers: true, converters: :numeric)
         
     | 
| 
       123 
     | 
    
         
            -
                      energy_timeseries_kw = t.by_col[col_num].map { |e| ((e * feature_report.timesteps_per_hour || 0) 
     | 
| 
       124 
     | 
    
         
            -
                      #Fill in missing timestep values with 0 if a full year is not provided
         
     | 
| 
      
 119 
     | 
    
         
            +
                      energy_timeseries_kw = t.by_col[col_num].map { |e| ((e * feature_report.timesteps_per_hour || 0)) }
         
     | 
| 
      
 120 
     | 
    
         
            +
                      # Fill in missing timestep values with 0 if a full year is not provided
         
     | 
| 
       125 
121 
     | 
    
         
             
                      if energy_timeseries_kw.length < (feature_report.timesteps_per_hour * 8760)
         
     | 
| 
       126 
     | 
    
         
            -
                        start_date = Time.parse(t.by_col[ 
     | 
| 
      
 122 
     | 
    
         
            +
                        start_date = Time.parse(t.by_col['Datetime'][0])
         
     | 
| 
       127 
123 
     | 
    
         
             
                        start_ts = (((start_date.yday * 60.0 * 60.0 * 24) + (start_date.hour * 60.0 * 60.0) + (start_date.min * 60.0) + start_date.sec) /
         
     | 
| 
       128 
     | 
    
         
            -
                                    (( 
     | 
| 
       129 
     | 
    
         
            -
                        end_date = Time.parse(t.by_col[ 
     | 
| 
      
 124 
     | 
    
         
            +
                                    ((60 / feature_report.timesteps_per_hour) * 60)).to_int
         
     | 
| 
      
 125 
     | 
    
         
            +
                        end_date = Time.parse(t.by_col['Datetime'][-1])
         
     | 
| 
       130 
126 
     | 
    
         
             
                        end_ts = (((end_date.yday * 60.0 * 60.0 * 24) + (end_date.hour * 60.0 * 60.0) + (end_date.min * 60.0) + end_date.sec) /
         
     | 
| 
       131 
     | 
    
         
            -
                                    (( 
     | 
| 
       132 
     | 
    
         
            -
                        energy_timeseries_kw = [0.0]*(start_ts-1) + energy_timeseries_kw + [0.0]*((feature_report.timesteps_per_hour * 8760) - end_ts)
         
     | 
| 
      
 127 
     | 
    
         
            +
                                    ((60 / feature_report.timesteps_per_hour) * 60)).to_int
         
     | 
| 
      
 128 
     | 
    
         
            +
                        energy_timeseries_kw = [0.0] * (start_ts - 1) + energy_timeseries_kw + [0.0] * ((feature_report.timesteps_per_hour * 8760) - end_ts)
         
     | 
| 
       133 
129 
     | 
    
         
             
                      end
         
     | 
| 
       134 
     | 
    
         
            -
                      #Clip to one non-leap year's worth of data
         
     | 
| 
       135 
     | 
    
         
            -
                      energy_timeseries_kw = energy_timeseries_kw.map { |e| e  
     | 
| 
       136 
     | 
    
         
            -
                      #Convert from the OpenDSS resolution to the REopt Lite resolution, if necessary
         
     | 
| 
      
 130 
     | 
    
         
            +
                      # Clip to one non-leap year's worth of data
         
     | 
| 
      
 131 
     | 
    
         
            +
                      energy_timeseries_kw = energy_timeseries_kw.map { |e| e || 0 }[0, (feature_report.timesteps_per_hour * 8760)]
         
     | 
| 
      
 132 
     | 
    
         
            +
                      # Convert from the OpenDSS resolution to the REopt Lite resolution, if necessary
         
     | 
| 
       137 
133 
     | 
    
         
             
                    rescue StandardError
         
     | 
| 
       138 
134 
     | 
    
         
             
                      @@logger.error("Could not parse the annual electric load from the timeseries csv - #{feature_report.timeseries_csv.path}")
         
     | 
| 
       139 
135 
     | 
    
         
             
                      raise "Could not parse the annual electric load from the timeseries csv - #{feature_report.timeseries_csv.path}"
         
     | 
| 
         @@ -142,8 +138,7 @@ module URBANopt # :nodoc: 
     | 
|
| 
       142 
138 
     | 
    
         
             
                    # Convert load to REopt Resolution
         
     | 
| 
       143 
139 
     | 
    
         
             
                    begin
         
     | 
| 
       144 
140 
     | 
    
         
             
                      reopt_inputs[:Scenario][:Site][:LoadProfile][:loads_kw] = convert_powerflow_resolution(energy_timeseries_kw, feature_report.timesteps_per_hour, reopt_inputs[:Scenario][:time_steps_per_hour])
         
     | 
| 
       145 
     | 
    
         
            -
             
     | 
| 
       146 
     | 
    
         
            -
                    rescue
         
     | 
| 
      
 141 
     | 
    
         
            +
                    rescue StandardError
         
     | 
| 
       147 
142 
     | 
    
         
             
                      @@logger.error("Could not convert the annual electric load from a resolution of #{feature_report.timesteps_per_hour} to #{reopt_inputs[:Scenario][:time_steps_per_hour]}")
         
     | 
| 
       148 
143 
     | 
    
         
             
                      raise "Could not convert the annual electric load from a resolution of #{feature_report.timesteps_per_hour} to #{reopt_inputs[:Scenario][:time_steps_per_hour]}"
         
     | 
| 
       149 
144 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -151,9 +146,9 @@ module URBANopt # :nodoc: 
     | 
|
| 
       151 
146 
     | 
    
         
             
                    if reopt_inputs[:Scenario][:Site][:ElectricTariff][:coincident_peak_load_active_timesteps].nil?
         
     | 
| 
       152 
147 
     | 
    
         
             
                      n_top_values = 100
         
     | 
| 
       153 
148 
     | 
    
         
             
                      tmp1 = reopt_inputs[:Scenario][:Site][:LoadProfile][:loads_kw]
         
     | 
| 
       154 
     | 
    
         
            -
                      tmp2 = tmp1.each_index.max_by(n_top_values*reopt_inputs[:Scenario][:time_steps_per_hour]){|i| tmp1[i]}
         
     | 
| 
      
 149 
     | 
    
         
            +
                      tmp2 = tmp1.each_index.max_by(n_top_values * reopt_inputs[:Scenario][:time_steps_per_hour]) { |i| tmp1[i] }
         
     | 
| 
       155 
150 
     | 
    
         
             
                      for i in (0...tmp2.count)
         
     | 
| 
       156 
     | 
    
         
            -
             
     | 
| 
      
 151 
     | 
    
         
            +
                        tmp2[i] += 1
         
     | 
| 
       157 
152 
     | 
    
         
             
                      end
         
     | 
| 
       158 
153 
     | 
    
         
             
                      reopt_inputs[:Scenario][:Site][:ElectricTariff][:coincident_peak_load_active_timesteps] = tmp2
         
     | 
| 
       159 
154 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -165,7 +160,6 @@ module URBANopt # :nodoc: 
     | 
|
| 
       165 
160 
     | 
    
         
             
                    return reopt_inputs
         
     | 
| 
       166 
161 
     | 
    
         
             
                  end
         
     | 
| 
       167 
162 
     | 
    
         | 
| 
       168 
     | 
    
         
            -
             
     | 
| 
       169 
163 
     | 
    
         
             
                  ##
         
     | 
| 
       170 
164 
     | 
    
         
             
                  # Update a FeatureReport from a \REopt Lite response
         
     | 
| 
       171 
165 
     | 
    
         
             
                  #
         
     | 
| 
         @@ -177,7 +171,7 @@ module URBANopt # :nodoc: 
     | 
|
| 
       177 
171 
     | 
    
         
             
                  #
         
     | 
| 
       178 
172 
     | 
    
         
             
                  # [*return:*] _URBANopt::Reporting::DefaultReports::FeatureReport_ - Returns an updated FeatureReport.
         
     | 
| 
       179 
173 
     | 
    
         
             
                  ##
         
     | 
| 
       180 
     | 
    
         
            -
                  def update_feature_report(feature_report, reopt_output, timeseries_csv_path=nil, resilience_stats=nil)
         
     | 
| 
      
 174 
     | 
    
         
            +
                  def update_feature_report(feature_report, reopt_output, timeseries_csv_path = nil, resilience_stats = nil)
         
     | 
| 
       181 
175 
     | 
    
         
             
                    # Check if the \REopt Lite response is valid
         
     | 
| 
       182 
176 
     | 
    
         
             
                    if reopt_output['outputs']['Scenario']['status'] != 'optimal'
         
     | 
| 
       183 
177 
     | 
    
         
             
                      @@logger.info("Warning cannot Feature Report #{feature_report.name} #{feature_report.id}  - REopt optimization was non-optimal")
         
     | 
| 
         @@ -210,29 +204,29 @@ module URBANopt # :nodoc: 
     | 
|
| 
       210 
204 
     | 
    
         
             
                      feature_report.distributed_generation.probs_of_surviving_by_hour_of_the_day = resilience_stats['probs_of_surviving_by_hour_of_the_day']
         
     | 
| 
       211 
205 
     | 
    
         
             
                    end
         
     | 
| 
       212 
206 
     | 
    
         | 
| 
       213 
     | 
    
         
            -
                    if reopt_output['outputs']['Scenario']['Site']['PV']. 
     | 
| 
      
 207 
     | 
    
         
            +
                    if reopt_output['outputs']['Scenario']['Site']['PV'].instance_of?(Hash)
         
     | 
| 
       214 
208 
     | 
    
         
             
                      reopt_output['outputs']['Scenario']['Site']['PV'] = [reopt_output['outputs']['Scenario']['Site']['PV']]
         
     | 
| 
       215 
209 
     | 
    
         
             
                    elsif reopt_output['outputs']['Scenario']['Site']['PV'].nil?
         
     | 
| 
       216 
210 
     | 
    
         
             
                      reopt_output['outputs']['Scenario']['Site']['PV'] = []
         
     | 
| 
       217 
211 
     | 
    
         
             
                    end
         
     | 
| 
       218 
212 
     | 
    
         | 
| 
       219 
213 
     | 
    
         
             
                    reopt_output['outputs']['Scenario']['Site']['PV'].each_with_index do |pv, i|
         
     | 
| 
       220 
     | 
    
         
            -
                      feature_report.distributed_generation.add_tech 'solar_pv', 
     | 
| 
      
 214 
     | 
    
         
            +
                      feature_report.distributed_generation.add_tech 'solar_pv', URBANopt::Reporting::DefaultReports::SolarPV.new({ size_kw: (pv['size_kw'] || 0), id: i })
         
     | 
| 
       221 
215 
     | 
    
         
             
                    end
         
     | 
| 
       222 
216 
     | 
    
         | 
| 
       223 
217 
     | 
    
         
             
                    wind = reopt_output['outputs']['Scenario']['Site']['Wind']
         
     | 
| 
       224 
     | 
    
         
            -
                    if !wind['size_kw'].nil?  
     | 
| 
       225 
     | 
    
         
            -
                      feature_report.distributed_generation.add_tech 'wind', 
     | 
| 
      
 218 
     | 
    
         
            +
                    if !wind['size_kw'].nil? && (wind['size_kw'] != 0)
         
     | 
| 
      
 219 
     | 
    
         
            +
                      feature_report.distributed_generation.add_tech 'wind', URBANopt::Reporting::DefaultReports::Wind.new({ size_kw: (wind['size_kw'] || 0) })
         
     | 
| 
       226 
220 
     | 
    
         
             
                    end
         
     | 
| 
       227 
221 
     | 
    
         | 
| 
       228 
222 
     | 
    
         
             
                    generator = reopt_output['outputs']['Scenario']['Site']['Generator']
         
     | 
| 
       229 
     | 
    
         
            -
                    if !generator['size_kw'].nil?  
     | 
| 
       230 
     | 
    
         
            -
                      feature_report.distributed_generation.add_tech 'generator', 
     | 
| 
      
 223 
     | 
    
         
            +
                    if !generator['size_kw'].nil? && (generator['size_kw'] != 0)
         
     | 
| 
      
 224 
     | 
    
         
            +
                      feature_report.distributed_generation.add_tech 'generator', URBANopt::Reporting::DefaultReports::Generator.new({ size_kw: (generator['size_kw'] || 0) })
         
     | 
| 
       231 
225 
     | 
    
         
             
                    end
         
     | 
| 
       232 
226 
     | 
    
         | 
| 
       233 
227 
     | 
    
         
             
                    storage = reopt_output['outputs']['Scenario']['Site']['Storage']
         
     | 
| 
       234 
     | 
    
         
            -
                    if !storage['size_kw'].nil? 
     | 
| 
       235 
     | 
    
         
            -
                      feature_report.distributed_generation.add_tech 'storage', 
     | 
| 
      
 228 
     | 
    
         
            +
                    if !storage['size_kw'].nil? && (storage['size_kw'] != 0)
         
     | 
| 
      
 229 
     | 
    
         
            +
                      feature_report.distributed_generation.add_tech 'storage', URBANopt::Reporting::DefaultReports::Storage.new({ size_kwh: (storage['size_kwh'] || 0), size_kw: (storage['size_kw'] || 0) })
         
     | 
| 
       236 
230 
     | 
    
         
             
                    end
         
     | 
| 
       237 
231 
     | 
    
         | 
| 
       238 
232 
     | 
    
         
             
                    generation_timeseries_kwh = Matrix[[0] * (8760 * feature_report.timesteps_per_hour)]
         
     | 
| 
         @@ -240,28 +234,18 @@ module URBANopt # :nodoc: 
     | 
|
| 
       240 
234 
     | 
    
         | 
| 
       241 
235 
     | 
    
         
             
                    unless reopt_output['outputs']['Scenario']['Site']['PV'].nil?
         
     | 
| 
       242 
236 
     | 
    
         
             
                      reopt_output['outputs']['Scenario']['Site']['PV'].each do |pv|
         
     | 
| 
       243 
     | 
    
         
            -
                        if (pv['size_kw'] || 0) > 0
         
     | 
| 
       244 
     | 
    
         
            -
                           
     | 
| 
       245 
     | 
    
         
            -
                            generation_timeseries_kwh += Matrix[convert_powerflow_resolution(pv['year_one_power_production_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)]
         
     | 
| 
       246 
     | 
    
         
            -
                          end
         
     | 
| 
      
 237 
     | 
    
         
            +
                        if (pv['size_kw'] || 0) > 0 && !pv['year_one_power_production_series_kw'].nil?
         
     | 
| 
      
 238 
     | 
    
         
            +
                          generation_timeseries_kwh += Matrix[convert_powerflow_resolution(pv['year_one_power_production_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)]
         
     | 
| 
       247 
239 
     | 
    
         
             
                        end
         
     | 
| 
       248 
     | 
    
         
            -
             
     | 
| 
      
 240 
     | 
    
         
            +
                      end
         
     | 
| 
       249 
241 
     | 
    
         
             
                    end
         
     | 
| 
       250 
242 
     | 
    
         | 
| 
       251 
     | 
    
         
            -
                     
     | 
| 
       252 
     | 
    
         
            -
                       
     | 
| 
       253 
     | 
    
         
            -
                        if !reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_power_production_series_kw'].nil?
         
     | 
| 
       254 
     | 
    
         
            -
                          generation_timeseries_kwh += Matrix[convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_power_production_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)]
         
     | 
| 
       255 
     | 
    
         
            -
                        end
         
     | 
| 
       256 
     | 
    
         
            -
                      end
         
     | 
| 
      
 243 
     | 
    
         
            +
                    if !reopt_output['outputs']['Scenario']['Site']['Wind'].nil? && ((reopt_output['outputs']['Scenario']['Site']['Wind']['size_kw'] || 0) > 0) && !reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_power_production_series_kw'].nil?
         
     | 
| 
      
 244 
     | 
    
         
            +
                      generation_timeseries_kwh += Matrix[convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_power_production_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)]
         
     | 
| 
       257 
245 
     | 
    
         
             
                    end
         
     | 
| 
       258 
246 
     | 
    
         | 
| 
       259 
     | 
    
         
            -
                     
     | 
| 
       260 
     | 
    
         
            -
                       
     | 
| 
       261 
     | 
    
         
            -
                        if !reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_power_production_series_kw'].nil?
         
     | 
| 
       262 
     | 
    
         
            -
                          generation_timeseries_kwh += Matrix[convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_power_production_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)]
         
     | 
| 
       263 
     | 
    
         
            -
                        end
         
     | 
| 
       264 
     | 
    
         
            -
                      end
         
     | 
| 
      
 247 
     | 
    
         
            +
                    if !reopt_output['outputs']['Scenario']['Site']['Generator'].nil? && ((reopt_output['outputs']['Scenario']['Site']['Generator']['size_kw'] || 0) > 0) && !reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_power_production_series_kw'].nil?
         
     | 
| 
      
 248 
     | 
    
         
            +
                      generation_timeseries_kwh += Matrix[convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_power_production_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)]
         
     | 
| 
       265 
249 
     | 
    
         
             
                    end
         
     | 
| 
       266 
250 
     | 
    
         | 
| 
       267 
251 
     | 
    
         
             
                    $generation_timeseries_kwh = generation_timeseries_kwh.to_a[0] || [0] * (8760 * feature_report.timesteps_per_hour)
         
     | 
| 
         @@ -359,7 +343,6 @@ module URBANopt # :nodoc: 
     | 
|
| 
       359 
343 
     | 
    
         
             
                      feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:PV:ToLoad(kw)')
         
     | 
| 
       360 
344 
     | 
    
         
             
                    end
         
     | 
| 
       361 
345 
     | 
    
         | 
| 
       362 
     | 
    
         
            -
             
     | 
| 
       363 
346 
     | 
    
         
             
                    $pv_to_grid_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:PV:ToGrid(kw)')
         
     | 
| 
       364 
347 
     | 
    
         
             
                    if $pv_to_grid_col.nil?
         
     | 
| 
       365 
348 
     | 
    
         
             
                      $pv_to_grid_col = feature_report.timeseries_csv.column_names.length
         
     | 
| 
         @@ -441,14 +424,14 @@ module URBANopt # :nodoc: 
     | 
|
| 
       441 
424 
     | 
    
         
             
                    start_ts = (
         
     | 
| 
       442 
425 
     | 
    
         
             
                                  (
         
     | 
| 
       443 
426 
     | 
    
         
             
                                    ((start_date.yday - 1) * 60.0 * 60.0 * 24) +
         
     | 
| 
       444 
     | 
    
         
            -
                                    (( 
     | 
| 
       445 
     | 
    
         
            -
                                    (start_date.min * 60.0) + start_date.sec 
     | 
| 
       446 
     | 
    
         
            -
                                  (( 
     | 
| 
      
 427 
     | 
    
         
            +
                                    ((start_date.hour - 1) * 60.0 * 60.0) +
         
     | 
| 
      
 428 
     | 
    
         
            +
                                    (start_date.min * 60.0) + start_date.sec) /
         
     | 
| 
      
 429 
     | 
    
         
            +
                                  ((60 / feature_report.timesteps_per_hour) * 60)
         
     | 
| 
       447 
430 
     | 
    
         
             
                                ).to_int
         
     | 
| 
       448 
431 
     | 
    
         | 
| 
       449 
432 
     | 
    
         
             
                    mod_data = old_data.map.with_index do |x, i|
         
     | 
| 
       450 
433 
     | 
    
         
             
                      if i > 0
         
     | 
| 
       451 
     | 
    
         
            -
                        modrow(x, start_ts + i -1)
         
     | 
| 
      
 434 
     | 
    
         
            +
                        modrow(x, start_ts + i - 1)
         
     | 
| 
       452 
435 
     | 
    
         
             
                      else
         
     | 
| 
       453 
436 
     | 
    
         
             
                        x
         
     | 
| 
       454 
437 
     | 
    
         
             
                      end
         
     | 
| 
         @@ -97,6 +97,7 @@ module URBANopt # :nodoc: 
     | 
|
| 
       97 
97 
     | 
    
         
             
                    if @use_localhost
         
     | 
| 
       98 
98 
     | 
    
         
             
                      return URI.parse("http://127.0.0.1:8000/v1/job/#{run_uuid}/results")
         
     | 
| 
       99 
99 
     | 
    
         
             
                    end
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
       100 
101 
     | 
    
         
             
                    return URI.parse("https://developer.nrel.gov/api/reopt/v1/job/#{run_uuid}/results?api_key=#{@nrel_developer_key}")
         
     | 
| 
       101 
102 
     | 
    
         
             
                  end
         
     | 
| 
       102 
103 
     | 
    
         | 
| 
         @@ -114,6 +115,7 @@ module URBANopt # :nodoc: 
     | 
|
| 
       114 
115 
     | 
    
         
             
                    if @use_localhost
         
     | 
| 
       115 
116 
     | 
    
         
             
                      return URI.parse("http://127.0.0.1:8000/v1/job/#{run_uuid}/resilience_stats")
         
     | 
| 
       116 
117 
     | 
    
         
             
                    end
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
       117 
119 
     | 
    
         
             
                    return URI.parse("https://developer.nrel.gov/api/reopt/v1/job/#{run_uuid}/resilience_stats?api_key=#{@nrel_developer_key}")
         
     | 
| 
       118 
120 
     | 
    
         
             
                  end
         
     | 
| 
       119 
121 
     | 
    
         | 
| 
         @@ -122,11 +124,20 @@ module URBANopt # :nodoc: 
     | 
|
| 
       122 
124 
     | 
    
         
             
                    tries = 0
         
     | 
| 
       123 
125 
     | 
    
         
             
                    while tries < max_tries
         
     | 
| 
       124 
126 
     | 
    
         
             
                      begin
         
     | 
| 
       125 
     | 
    
         
            -
             
     | 
| 
       126 
     | 
    
         
            -
             
     | 
| 
       127 
     | 
    
         
            -
             
     | 
| 
       128 
     | 
    
         
            -
             
     | 
| 
       129 
     | 
    
         
            -
             
     | 
| 
      
 127 
     | 
    
         
            +
                        result = http.request(r)
         
     | 
| 
      
 128 
     | 
    
         
            +
                        # Result codes sourced from https://developer.nrel.gov/docs/errors/
         
     | 
| 
      
 129 
     | 
    
         
            +
                        if result.code == '429'
         
     | 
| 
      
 130 
     | 
    
         
            +
                          @@logger.fatal('Exceeded the REopt-Lite API limit of 300 requests per hour')
         
     | 
| 
      
 131 
     | 
    
         
            +
                          puts 'Using the URBANopt CLI to submit a Scenario optimization counts as one request per scenario'
         
     | 
| 
      
 132 
     | 
    
         
            +
                          puts 'Using the URBANopt CLI to submit a Feature optimization counts as one request per feature'
         
     | 
| 
      
 133 
     | 
    
         
            +
                          abort('Please wait and try again once the time period has elapsed')
         
     | 
| 
      
 134 
     | 
    
         
            +
                        elsif (result.code != '201') && (result.code != '200') # Anything in the 200s is success
         
     | 
| 
      
 135 
     | 
    
         
            +
                          @@logger.debug("REopt-Lite has returned a '#{result.code}' status code. Visit https://developer.nrel.gov/docs/errors/ for more status code information")
         
     | 
| 
      
 136 
     | 
    
         
            +
                        end
         
     | 
| 
      
 137 
     | 
    
         
            +
                        tries = 4
         
     | 
| 
      
 138 
     | 
    
         
            +
                      rescue StandardError
         
     | 
| 
      
 139 
     | 
    
         
            +
                        tries += 1
         
     | 
| 
      
 140 
     | 
    
         
            +
                      end
         
     | 
| 
       130 
141 
     | 
    
         
             
                    end
         
     | 
| 
       131 
142 
     | 
    
         
             
                    return result
         
     | 
| 
       132 
143 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -148,11 +159,11 @@ module URBANopt # :nodoc: 
     | 
|
| 
       148 
159 
     | 
    
         
             
                      http.use_ssl = true
         
     | 
| 
       149 
160 
     | 
    
         
             
                    end
         
     | 
| 
       150 
161 
     | 
    
         | 
| 
       151 
     | 
    
         
            -
                     
     | 
| 
       152 
     | 
    
         
            -
                     
     | 
| 
      
 162 
     | 
    
         
            +
                    post_request = Net::HTTP::Post.new(@uri_submit, header)
         
     | 
| 
      
 163 
     | 
    
         
            +
                    post_request.body = ::JSON.generate(data, allow_nan: true)
         
     | 
| 
       153 
164 
     | 
    
         | 
| 
       154 
165 
     | 
    
         
             
                    # Send the request
         
     | 
| 
       155 
     | 
    
         
            -
                    response = make_request(http,  
     | 
| 
      
 166 
     | 
    
         
            +
                    response = make_request(http, post_request)
         
     | 
| 
       156 
167 
     | 
    
         | 
| 
       157 
168 
     | 
    
         
             
                    if !response.is_a?(Net::HTTPSuccess)
         
     | 
| 
       158 
169 
     | 
    
         
             
                      @@logger.error('Check_connection Failed')
         
     | 
| 
         @@ -175,7 +186,6 @@ module URBANopt # :nodoc: 
     | 
|
| 
       175 
186 
     | 
    
         
             
                  # [*return:*] _Bool_ - Returns true if the post succeeeds. Otherwise returns false.
         
     | 
| 
       176 
187 
     | 
    
         
             
                  ##
         
     | 
| 
       177 
188 
     | 
    
         
             
                  def resilience_request(run_uuid, filename)
         
     | 
| 
       178 
     | 
    
         
            -
             
     | 
| 
       179 
189 
     | 
    
         
             
                    if File.directory? filename
         
     | 
| 
       180 
190 
     | 
    
         
             
                      if run_uuid.nil?
         
     | 
| 
       181 
191 
     | 
    
         
             
                        run_uuid = 'error'
         
     | 
| 
         @@ -187,33 +197,38 @@ module URBANopt # :nodoc: 
     | 
|
| 
       187 
197 
     | 
    
         
             
                      @@logger.info("REopt results saved to #{filename}")
         
     | 
| 
       188 
198 
     | 
    
         
             
                    end
         
     | 
| 
       189 
199 
     | 
    
         | 
| 
       190 
     | 
    
         
            -
                    #Submit Job
         
     | 
| 
      
 200 
     | 
    
         
            +
                    # Submit Job
         
     | 
| 
       191 
201 
     | 
    
         
             
                    @@logger.info("Submitting Resilience Statistics job for #{run_uuid}")
         
     | 
| 
       192 
202 
     | 
    
         
             
                    header = { 'Content-Type' => 'application/json' }
         
     | 
| 
       193 
203 
     | 
    
         
             
                    http = Net::HTTP.new(@uri_submit_outagesimjob.host, @uri_submit_outagesimjob.port)
         
     | 
| 
       194 
204 
     | 
    
         
             
                    if !@use_localhost
         
     | 
| 
       195 
205 
     | 
    
         
             
                      http.use_ssl = true
         
     | 
| 
       196 
206 
     | 
    
         
             
                    end
         
     | 
| 
       197 
     | 
    
         
            -
                     
     | 
| 
       198 
     | 
    
         
            -
                     
     | 
| 
       199 
     | 
    
         
            -
                    submit_response = make_request(http,  
     | 
| 
       200 
     | 
    
         
            -
                    @@logger. 
     | 
| 
      
 207 
     | 
    
         
            +
                    post_request = Net::HTTP::Post.new(@uri_submit_outagesimjob, header)
         
     | 
| 
      
 208 
     | 
    
         
            +
                    post_request.body = ::JSON.generate({ 'run_uuid' => run_uuid, 'bau' => false }, allow_nan: true)
         
     | 
| 
      
 209 
     | 
    
         
            +
                    submit_response = make_request(http, post_request)
         
     | 
| 
      
 210 
     | 
    
         
            +
                    @@logger.debug(submit_response.body)
         
     | 
| 
       201 
211 
     | 
    
         | 
| 
       202 
     | 
    
         
            -
                    #Fetch Results
         
     | 
| 
      
 212 
     | 
    
         
            +
                    # Fetch Results
         
     | 
| 
       203 
213 
     | 
    
         
             
                    uri = uri_resilience(run_uuid)
         
     | 
| 
       204 
214 
     | 
    
         
             
                    http = Net::HTTP.new(uri.host, uri.port)
         
     | 
| 
       205 
215 
     | 
    
         
             
                    if !@use_localhost
         
     | 
| 
       206 
216 
     | 
    
         
             
                      http.use_ssl = true
         
     | 
| 
       207 
217 
     | 
    
         
             
                    end
         
     | 
| 
       208 
218 
     | 
    
         | 
| 
      
 219 
     | 
    
         
            +
                    # Wait a few seconds for the REopt database to update before GETing results
         
     | 
| 
      
 220 
     | 
    
         
            +
                    sleep 5
         
     | 
| 
      
 221 
     | 
    
         
            +
                    get_request = Net::HTTP::Get.new(uri.request_uri)
         
     | 
| 
      
 222 
     | 
    
         
            +
                    response = make_request(http, get_request)
         
     | 
| 
      
 223 
     | 
    
         
            +
             
     | 
| 
      
 224 
     | 
    
         
            +
                    # Set a limit on retries when 404s are returned from REopt API
         
     | 
| 
       209 
225 
     | 
    
         
             
                    elapsed_time = 0
         
     | 
| 
       210 
226 
     | 
    
         
             
                    max_elapsed_time = 60 * 5
         
     | 
| 
       211 
227 
     | 
    
         | 
| 
       212 
     | 
    
         
            -
                     
     | 
| 
       213 
     | 
    
         
            -
                     
     | 
| 
       214 
     | 
    
         
            -
             
     | 
| 
       215 
     | 
    
         
            -
             
     | 
| 
       216 
     | 
    
         
            -
                      response = make_request(http, request)
         
     | 
| 
      
 228 
     | 
    
         
            +
                    # If database still hasn't updated, wait a little longer and try again
         
     | 
| 
      
 229 
     | 
    
         
            +
                    while (elapsed_time < max_elapsed_time) & (response.code == '404')
         
     | 
| 
      
 230 
     | 
    
         
            +
                      response = make_request(http, get_request)
         
     | 
| 
      
 231 
     | 
    
         
            +
                      @@logger.warn('GET request was too fast for REOpt-Lite API. Retrying...')
         
     | 
| 
       217 
232 
     | 
    
         
             
                      elapsed_time += 5
         
     | 
| 
       218 
233 
     | 
    
         
             
                      sleep 5
         
     | 
| 
       219 
234 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -224,17 +239,16 @@ module URBANopt # :nodoc: 
     | 
|
| 
       224 
239 
     | 
    
         
             
                      File.open(filename, 'w+') do |f|
         
     | 
| 
       225 
240 
     | 
    
         
             
                        f.puts(text)
         
     | 
| 
       226 
241 
     | 
    
         
             
                      end
         
     | 
| 
       227 
     | 
    
         
            -
                    rescue
         
     | 
| 
       228 
     | 
    
         
            -
                      @@logger. 
     | 
| 
      
 242 
     | 
    
         
            +
                    rescue StandardError
         
     | 
| 
      
 243 
     | 
    
         
            +
                      @@logger.error("Cannot write - #{filename}")
         
     | 
| 
       229 
244 
     | 
    
         
             
                    end
         
     | 
| 
       230 
245 
     | 
    
         | 
| 
       231 
     | 
    
         
            -
                    if response.code ==  
     | 
| 
      
 246 
     | 
    
         
            +
                    if response.code == '200'
         
     | 
| 
       232 
247 
     | 
    
         
             
                      return data
         
     | 
| 
       233 
248 
     | 
    
         
             
                    end
         
     | 
| 
       234 
249 
     | 
    
         | 
| 
       235 
     | 
    
         
            -
                    @@logger. 
     | 
| 
      
 250 
     | 
    
         
            +
                    @@logger.error("Error from REopt API - #{data['Error']}")
         
     | 
| 
       236 
251 
     | 
    
         
             
                    return {}
         
     | 
| 
       237 
     | 
    
         
            -
             
     | 
| 
       238 
252 
     | 
    
         
             
                  end
         
     | 
| 
       239 
253 
     | 
    
         | 
| 
       240 
254 
     | 
    
         
             
                  ##
         
     | 
| 
         @@ -261,14 +275,14 @@ module URBANopt # :nodoc: 
     | 
|
| 
       261 
275 
     | 
    
         
             
                    if !@use_localhost
         
     | 
| 
       262 
276 
     | 
    
         
             
                      http.use_ssl = true
         
     | 
| 
       263 
277 
     | 
    
         
             
                    end
         
     | 
| 
       264 
     | 
    
         
            -
                     
     | 
| 
       265 
     | 
    
         
            -
                     
     | 
| 
      
 278 
     | 
    
         
            +
                    post_request = Net::HTTP::Post.new(@uri_submit, header)
         
     | 
| 
      
 279 
     | 
    
         
            +
                    post_request.body = ::JSON.generate(reopt_input, allow_nan: true)
         
     | 
| 
       266 
280 
     | 
    
         | 
| 
       267 
281 
     | 
    
         
             
                    # Send the request
         
     | 
| 
       268 
     | 
    
         
            -
                    response = make_request(http,  
     | 
| 
      
 282 
     | 
    
         
            +
                    response = make_request(http, post_request)
         
     | 
| 
       269 
283 
     | 
    
         | 
| 
       270 
284 
     | 
    
         
             
                    # Get UUID
         
     | 
| 
       271 
     | 
    
         
            -
                    run_uuid = JSON.parse(response.body, allow_nan:true)['run_uuid']
         
     | 
| 
      
 285 
     | 
    
         
            +
                    run_uuid = JSON.parse(response.body, allow_nan: true)['run_uuid']
         
     | 
| 
       272 
286 
     | 
    
         | 
| 
       273 
287 
     | 
    
         
             
                    if File.directory? filename
         
     | 
| 
       274 
288 
     | 
    
         
             
                      if run_uuid.nil?
         
     | 
| 
         @@ -286,7 +300,7 @@ module URBANopt # :nodoc: 
     | 
|
| 
       286 
300 
     | 
    
         
             
                      File.open(filename, 'w+') do |f|
         
     | 
| 
       287 
301 
     | 
    
         
             
                        f.puts(text)
         
     | 
| 
       288 
302 
     | 
    
         
             
                      end
         
     | 
| 
       289 
     | 
    
         
            -
                      @@logger. 
     | 
| 
      
 303 
     | 
    
         
            +
                      @@logger.error("Cannot write - #{filename}")
         
     | 
| 
       290 
304 
     | 
    
         
             
                      raise "Error in REopt optimization post - see #{filename}"
         
     | 
| 
       291 
305 
     | 
    
         
             
                    end
         
     | 
| 
       292 
306 
     | 
    
         | 
| 
         @@ -298,17 +312,17 @@ module URBANopt # :nodoc: 
     | 
|
| 
       298 
312 
     | 
    
         
             
                      http.use_ssl = true
         
     | 
| 
       299 
313 
     | 
    
         
             
                    end
         
     | 
| 
       300 
314 
     | 
    
         | 
| 
       301 
     | 
    
         
            -
                     
     | 
| 
      
 315 
     | 
    
         
            +
                    get_request = Net::HTTP::Get.new(uri.request_uri)
         
     | 
| 
       302 
316 
     | 
    
         | 
| 
       303 
317 
     | 
    
         
             
                    while status == 'Optimizing...'
         
     | 
| 
       304 
     | 
    
         
            -
                      response = make_request(http,  
     | 
| 
      
 318 
     | 
    
         
            +
                      response = make_request(http, get_request)
         
     | 
| 
       305 
319 
     | 
    
         | 
| 
       306 
     | 
    
         
            -
                      data = JSON.parse(response.body, allow_nan:true)
         
     | 
| 
      
 320 
     | 
    
         
            +
                      data = JSON.parse(response.body, allow_nan: true)
         
     | 
| 
       307 
321 
     | 
    
         | 
| 
       308 
     | 
    
         
            -
                      if data['outputs']['Scenario']['Site']['PV']. 
     | 
| 
      
 322 
     | 
    
         
            +
                      if data['outputs']['Scenario']['Site']['PV'].is_a?(Array)
         
     | 
| 
       309 
323 
     | 
    
         
             
                        pv_sizes = 0
         
     | 
| 
       310 
324 
     | 
    
         
             
                        data['outputs']['Scenario']['Site']['PV'].each do |x|
         
     | 
| 
       311 
     | 
    
         
            -
                          pv_sizes  
     | 
| 
      
 325 
     | 
    
         
            +
                          pv_sizes += x['size_kw'].to_f
         
     | 
| 
       312 
326 
     | 
    
         
             
                        end
         
     | 
| 
       313 
327 
     | 
    
         
             
                      else
         
     | 
| 
       314 
328 
     | 
    
         
             
                        pv_sizes = data['outputs']['Scenario']['Site']['PV']['size_kw'] || 0
         
     | 
| 
         @@ -323,13 +337,13 @@ module URBANopt # :nodoc: 
     | 
|
| 
       323 
337 
     | 
    
         
             
                    _tries = 0
         
     | 
| 
       324 
338 
     | 
    
         
             
                    (check_complete = sizes == 0) && ((data['outputs']['Scenario']['Site']['Financial']['npv_us_dollars'] || 0) > 0)
         
     | 
| 
       325 
339 
     | 
    
         
             
                    while (_tries < _max_retry) && check_complete
         
     | 
| 
       326 
     | 
    
         
            -
                      sleep  
     | 
| 
       327 
     | 
    
         
            -
                      response = make_request(http,  
     | 
| 
       328 
     | 
    
         
            -
                      data = JSON.parse(response.body, allow_nan:true)
         
     | 
| 
       329 
     | 
    
         
            -
                      if data['outputs']['Scenario']['Site']['PV']. 
     | 
| 
      
 340 
     | 
    
         
            +
                      sleep 3
         
     | 
| 
      
 341 
     | 
    
         
            +
                      response = make_request(http, get_request)
         
     | 
| 
      
 342 
     | 
    
         
            +
                      data = JSON.parse(response.body, allow_nan: true)
         
     | 
| 
      
 343 
     | 
    
         
            +
                      if data['outputs']['Scenario']['Site']['PV'].is_a?(Array)
         
     | 
| 
       330 
344 
     | 
    
         
             
                        pv_sizes = 0
         
     | 
| 
       331 
345 
     | 
    
         
             
                        data['outputs']['Scenario']['Site']['PV'].each do |x|
         
     | 
| 
       332 
     | 
    
         
            -
                          pv_sizes  
     | 
| 
      
 346 
     | 
    
         
            +
                          pv_sizes += x['size_kw'].to_f
         
     | 
| 
       333 
347 
     | 
    
         
             
                        end
         
     | 
| 
       334 
348 
     | 
    
         
             
                      else
         
     | 
| 
       335 
349 
     | 
    
         
             
                        pv_sizes = data['outputs']['Scenario']['Site']['PV']['size_kw'] || 0
         
     | 
| 
         @@ -345,8 +359,8 @@ module URBANopt # :nodoc: 
     | 
|
| 
       345 
359 
     | 
    
         
             
                      File.open(filename, 'w+') do |f|
         
     | 
| 
       346 
360 
     | 
    
         
             
                        f.puts(text)
         
     | 
| 
       347 
361 
     | 
    
         
             
                      end
         
     | 
| 
       348 
     | 
    
         
            -
                    rescue
         
     | 
| 
       349 
     | 
    
         
            -
                      @@logger. 
     | 
| 
      
 362 
     | 
    
         
            +
                    rescue StandardError
         
     | 
| 
      
 363 
     | 
    
         
            +
                      @@logger.error("Cannot write - #{filename}")
         
     | 
| 
       350 
364 
     | 
    
         
             
                    end
         
     | 
| 
       351 
365 
     | 
    
         | 
| 
       352 
366 
     | 
    
         
             
                    if status == 'optimal'
         
     |