lib/urbanopt/reopt/scenario_report_adapter.rb in urbanopt-reopt-0.3.0 vs lib/urbanopt/reopt/scenario_report_adapter.rb in urbanopt-reopt-0.4.0

- old
+ new

@@ -1,7 +1,7 @@ # ********************************************************************************* -# URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other +# URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other # contributors. All rights reserved. # # Redistribution and use in source and binary forms, with or without modification, # are permitted provided that the following conditions are met: # @@ -26,11 +26,11 @@ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED # OF THE POSSIBILITY OF SUCH DAMAGE. # ********************************************************************************* -require 'urbanopt/scenario/default_reports' +require 'urbanopt/reporting/default_reports' require 'urbanopt/reopt/reopt_logger' require 'matrix' require 'csv' require 'time' @@ -49,11 +49,11 @@ ## # Convert a ScenarioReport into a \REopt Lite post # # [*parameters:*] # - # * +scenario_report+ - _URBANopt::Scenario::DefaultReports::ScenarioReport_ - ScenarioReport to use in converting the +reopt_assumptions_hash+, if provided, to a \REopt Lite post. Otherwise, if the +reopt_assumptions_hash+ is nil a default post will be updated from this ScenarioReport and submitted to the \REopt Lite API. + # * +scenario_report+ - _URBANopt::Reporting::DefaultReports::ScenarioReport_ - ScenarioReport to use in converting the +reopt_assumptions_hash+, if provided, to a \REopt Lite post. Otherwise, if the +reopt_assumptions_hash+ is nil a default post will be updated from this ScenarioReport and submitted to the \REopt Lite API. # * +reopt_assumptions_hash+ - _Hash_ - Optional. A hash formatted for submittal to the \REopt Lite API containing default values. Values will be overwritten from the ScenarioReport where available (i.e. latitude, roof_squarefeet). Missing optional parameters will be filled in with default values by the API. # # [*return:*] _Hash_ - Returns hash formatted for submittal to the \REopt Lite API ## def reopt_json_from_scenario_report(scenario_report, reopt_assumptions_json = nil) @@ -124,14 +124,14 @@ col_num = scenario_report.timeseries_csv.column_names.index('Electricity:Facility(kWh)') t = CSV.read(scenario_report.timeseries_csv.path, headers: true, converters: :numeric) energy_timeseries_kw = t.by_col[col_num].map { |e| ((e * scenario_report.timesteps_per_hour || 0) ) } if energy_timeseries_kw.length < (scenario_report.timesteps_per_hour * 8760) start_date = Time.parse(t.by_col["Datetime"][0]) - 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) / + 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) / \ (( 60 / scenario_report.timesteps_per_hour ) * 60)).to_int end_date = Time.parse(t.by_col["Datetime"][-1]) - 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) / + 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) / \ (( 60 / scenario_report.timesteps_per_hour ) * 60)).to_int energy_timeseries_kw = [0.0]*(start_ts-1) + energy_timeseries_kw + [0.0]*((scenario_report.timesteps_per_hour * 8760) - end_ts) end reopt_inputs[:Scenario][:Site][:LoadProfile][:loads_kw] = energy_timeseries_kw.map { |e| e ? e : 0 }[0,(scenario_report.timesteps_per_hour * 8760)] rescue StandardError @@ -144,11 +144,11 @@ ## # Converts a FeatureReport list from a ScenarioReport into an array of \REopt Lite posts # # [*parameters:*] # - # * +scenario_report+ - _URBANopt::Scenario::DefaultReports::ScenarioReport_ - ScenarioReport to use in converting FeatureReports and respecitive +reopt_assumptions_hashes+, if provided, to a \REopt Lite post. If no +reopt_assumptions_hashes+ are provided default posts will be updated from these FeatureReports and submitted to the \REopt Lite API. + # * +scenario_report+ - _URBANopt::Reporting::DefaultReports::ScenarioReport_ - ScenarioReport to use in converting FeatureReports and respecitive +reopt_assumptions_hashes+, if provided, to a \REopt Lite post. If no +reopt_assumptions_hashes+ are provided default posts will be updated from these FeatureReports and submitted to the \REopt Lite API. # * +reopt_assumptions_hashes+ - _Array_ - Optional. An array of hashes formatted for submittal to the \REopt Lite API containing default values. Values will be overwritten from the ScenarioReport where available (i.e. latitude, roof_squarefeet). Missing optional parameters will be filled in with default values by the API. The order should match the list in ScenarioReport.feature_reports. # # [*return:*] _Array_ - Returns an array of hashes formatted for submittal to the \REopt Lite API in the order of the FeatureReports lited in ScenarioReport.feature_reports. ## def reopt_jsons_from_scenario_feature_reports(scenario_report, reopt_assumptions_hashes = []) @@ -166,22 +166,22 @@ ## # Updates a ScenarioReport from a \REopt Lite response # # [*parameters:*] # - # * +scenario_report+ - _URBANopt::Scenario::DefaultReports::ScenarioReport_ - ScenarioReport to update from a \REopt Lite response. + # * +scenario_report+ - _URBANopt::Reporting::DefaultReports::ScenarioReport_ - ScenarioReport to update from a \REopt Lite response. # * +reopt_output+ - _Hash_ - A hash response from the \REopt Lite API. # * +timeseries_csv_path+ - _String_ - Optional. The path to a file at which new timeseries data will be written. If not provided a file is created based on the run_uuid of the \REopt Lite optimization task. # - # [*return:*] _URBANopt::Scenario::DefaultReports::ScenarioReport_ - Returns an updated ScenarioReport + # [*return:*] _URBANopt::Reporting::DefaultReports::ScenarioReport_ - Returns an updated ScenarioReport ## - def update_scenario_report(scenario_report, reopt_output, timeseries_csv_path = nil) + def update_scenario_report(scenario_report, reopt_output, timeseries_csv_path=nil, resilience_stats=nil) if reopt_output['outputs']['Scenario']['status'] != 'optimal' @@logger.info("Warning cannot Feature Report #{scenario_report.name} #{scenario_report.id} - REopt optimization was non-optimal") return scenario_report end - + $ts_per_hour = scenario_report.timesteps_per_hour def scale_timeseries(input, ts_per_hr=$ts_per_hour) if input.nil? return nil end @@ -190,11 +190,11 @@ end if input.length == (8760 * ts_per_hr) return input end result = [] - input.each do |val| + input.each do |val| (1..ts_per_hr).each do |x| result.push(val/ts_per_hr.to_f) end end return result @@ -213,39 +213,53 @@ scenario_report.distributed_generation.npv_us_dollars = reopt_output['outputs']['Scenario']['Site']['Financial']['npv_us_dollars'] || 0 scenario_report.distributed_generation.year_one_energy_cost_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_energy_cost_us_dollars'] || 0 scenario_report.distributed_generation.year_one_demand_cost_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_demand_cost_us_dollars'] || 0 scenario_report.distributed_generation.year_one_bill_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_bill_us_dollars'] || 0 scenario_report.distributed_generation.total_energy_cost_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['total_energy_cost_us_dollars'] || 0 - + scenario_report.distributed_generation.total_demand_cost_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['total_demand_cost_us_dollars'] || 0 + scenario_report.distributed_generation.year_one_energy_cost_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_energy_cost_bau_us_dollars'] || 0 + scenario_report.distributed_generation.year_one_demand_cost_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_demand_cost_bau_us_dollars'] || 0 + scenario_report.distributed_generation.year_one_bill_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_bill_bau_us_dollars'] || 0 + scenario_report.distributed_generation.total_demand_cost_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['total_demand_cost_bau_us_dollars'] || 0 + scenario_report.distributed_generation.total_energy_cost_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['total_energy_cost_bau_us_dollars'] || 0 + if !resilience_stats.nil? + scenario_report.distributed_generation.resilience_hours_min = resilience_stats['resilience_hours_min'] + scenario_report.distributed_generation.resilience_hours_max = resilience_stats['resilience_hours_max'] + scenario_report.distributed_generation.resilience_hours_avg = resilience_stats['resilience_hours_avg'] + scenario_report.distributed_generation.probs_of_surviving = resilience_stats['probs_of_surviving'] + scenario_report.distributed_generation.probs_of_surviving_by_month = resilience_stats['probs_of_surviving_by_month'] + scenario_report.distributed_generation.probs_of_surviving_by_hour_of_the_day = resilience_stats['probs_of_surviving_by_hour_of_the_day'] + end + if reopt_output['outputs']['Scenario']['Site']['PV'].class == Hash reopt_output['outputs']['Scenario']['Site']['PV'] = [reopt_output['outputs']['Scenario']['Site']['PV']] elsif reopt_output['outputs']['Scenario']['Site']['PV'].nil? reopt_output['outputs']['Scenario']['Site']['PV'] = [] end - - reopt_output['outputs']['Scenario']['Site']['PV'].each_with_index do |pv, i| - scenario_report.distributed_generation.add_tech 'solar_pv', URBANopt::Scenario::DefaultReports::SolarPV.new( {size_kw: (pv['size_kw'] || 0), id: i }) + + reopt_output['outputs']['Scenario']['Site']['PV'].each_with_index do |pv, i| + scenario_report.distributed_generation.add_tech 'solar_pv', URBANopt::Reporting::DefaultReports::SolarPV.new( {size_kw: (pv['size_kw'] || 0), id: i }) end wind = reopt_output['outputs']['Scenario']['Site']['Wind'] if !wind['size_kw'].nil? and wind['size_kw'] != 0 - scenario_report.distributed_generation.add_tech 'wind', URBANopt::Scenario::DefaultReports::Wind.new( {size_kw: (wind['size_kw'] || 0) }) + scenario_report.distributed_generation.add_tech 'wind', URBANopt::Reporting::DefaultReports::Wind.new( {size_kw: (wind['size_kw'] || 0) }) end generator = reopt_output['outputs']['Scenario']['Site']['Generator'] if !generator['size_kw'].nil? and generator['size_kw'] != 0 - scenario_report.distributed_generation.add_tech 'generator', URBANopt::Scenario::DefaultReports::Generator.new( {size_kw: (generator['size_kw'] || 0) }) + scenario_report.distributed_generation.add_tech 'generator', URBANopt::Reporting::DefaultReports::Generator.new( {size_kw: (generator['size_kw'] || 0) }) end storage = reopt_output['outputs']['Scenario']['Site']['Storage'] if !storage['size_kw'].nil? and storage['size_kw'] != 0 - scenario_report.distributed_generation.add_tech 'storage', URBANopt::Scenario::DefaultReports::Storage.new( {size_kwh: (storage['size_kwh'] || 0), size_kw: (storage['size_kw'] || 0) }) + scenario_report.distributed_generation.add_tech 'storage', URBANopt::Reporting::DefaultReports::Storage.new( {size_kwh: (storage['size_kwh'] || 0), size_kw: (storage['size_kw'] || 0) }) end - + generation_timeseries_kwh = Matrix[[0] * (8760 * scenario_report.timesteps_per_hour)] - - reopt_output['outputs']['Scenario']['Site']['PV'].each do |pv| + + reopt_output['outputs']['Scenario']['Site']['PV'].each do |pv| if (pv['size_kw'] || 0) > 0 if !pv['year_one_power_production_series_kw'].nil? generation_timeseries_kwh += Matrix[pv['year_one_power_production_series_kw']] end end