From 3bcd167ca999355ee7382c66a80dba0e60b7e2ae Mon Sep 17 00:00:00 2001 From: gvidigal-psr Date: Tue, 18 Aug 2026 16:05:45 -0300 Subject: [PATCH 1/6] Add renewable case --- test/case_23/case_summary.md | 15 + test/case_23/renewables_case/modify_case.jl | 292 +++++++++++++++++ test/case_23/renewables_case/test_case.jl | 339 ++++++++++++++++++++ 3 files changed, 646 insertions(+) create mode 100644 test/case_23/case_summary.md create mode 100644 test/case_23/renewables_case/modify_case.jl create mode 100644 test/case_23/renewables_case/test_case.jl diff --git a/test/case_23/case_summary.md b/test/case_23/case_summary.md new file mode 100644 index 0000000000..cbc6d1d1d8 --- /dev/null +++ b/test/case_23/case_summary.md @@ -0,0 +1,15 @@ +# Case 23: +- 8 agents, thermal only, 2 agent types. +- IARA-UI case. +- No expected outputs. + +## Variations: +- virtual_reservoir_case: + - from base_case. + - 1 subscenario, adds one virtual reservoir to each agent. +- multiple_subscenarios_case: + - from virtual_reservoir_case. + - modifies the case to have 3 subscenarios. +- renewables_case: + - from base_case. + - removes two of each thermal agent type, replaces with two pairs of renewables. \ No newline at end of file diff --git a/test/case_23/renewables_case/modify_case.jl b/test/case_23/renewables_case/modify_case.jl new file mode 100644 index 0000000000..73994ba398 --- /dev/null +++ b/test/case_23/renewables_case/modify_case.jl @@ -0,0 +1,292 @@ +# Copyright (c) 2024: PSR, CCEE (Câmara de Comercialização de Energia +# Elétrica), and contributors +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +############################################################################# +# IARA +# See https://github.com/psrenergy/IARA.jl +############################################################################# + +db = IARA.load_study(PATH; read_only = false) + +number_of_renewable_units = 4 +number_of_subscenarios = 6 +wind_max_generation = 80.0 +solar_max_generation = 50.0 + +# The base case supplies only ex-post demand scenarios; do the same for the +# renewable generation scenarios added below +IARA.update_configuration!( + db; + renewable_scenarios_files = IARA.Configurations_UncertaintyScenariosFiles.ONLY_EX_POST, + number_of_subscenarios = number_of_subscenarios, +) + +# Remove the third and fourth agent of each thermal type, along with their assets +# ------------------------------------------------------------------------------ +for i in 3:4 + IARA.delete_element!(db, "ThermalUnit", "Termica $i") + IARA.delete_element!(db, "BiddingGroup", "Termico $i") + IARA.delete_asset_owner!(db, "Agente Termico $i") + + IARA.delete_element!(db, "ThermalUnit", "Peaker $i") + IARA.delete_element!(db, "BiddingGroup", "Peaker $i") + IARA.delete_asset_owner!(db, "Agente Peaker $i") +end + +# Add two wind agents and two solar agents, with a bidding group and a +# renewable unit each +# -------------------------------------------------------------------- +for i in 1:2 + IARA.add_asset_owner!(db; label = "Agente Eolico $i", purchase_discount_rate = [0.1]) + IARA.add_bidding_group!( + db; + label = "Eolico $i", + assetowner_id = "Agente Eolico $i", + risk_factor = [0.0], + segment_fraction = [1.0], + ex_post_adjust_mode = IARA.BiddingGroup_ExPostAdjustMode.PROPORTIONAL_TO_EX_POST_GENERATION_OVER_EX_ANTE_BID, + ) + IARA.add_renewable_unit!( + db; + label = "Eolica $i", + parameters = DataFrame(; + date_time = [DateTime(0)], + existing = [Int(IARA.RenewableUnit_Existence.EXISTS)], + max_generation = [wind_max_generation], + om_cost = [10.0], + curtailment_cost = [0.0], + ), + biddinggroup_id = "Eolico $i", + bus_id = "Sistema", + ) + + IARA.add_asset_owner!(db; label = "Agente Solar $i", purchase_discount_rate = [0.1]) + IARA.add_bidding_group!( + db; + label = "Solar $i", + assetowner_id = "Agente Solar $i", + risk_factor = [0.0], + segment_fraction = [1.0], + ex_post_adjust_mode = IARA.BiddingGroup_ExPostAdjustMode.PROPORTIONAL_TO_EX_POST_GENERATION_OVER_EX_ANTE_BID, + ) + IARA.add_renewable_unit!( + db; + label = "Solar $i", + parameters = DataFrame(; + date_time = [DateTime(0)], + existing = [Int(IARA.RenewableUnit_Existence.EXISTS)], + max_generation = [solar_max_generation], + om_cost = [3.0], + curtailment_cost = [0.0], + ), + biddinggroup_id = "Solar $i", + bus_id = "Sistema", + ) +end + +# The bidding group labels changed, so the bid files written by the base case +# must be rewritten for the new set of bidding groups +# -------------------------------------------------------------------------- +bidding_group_labels = [ + "Termico 1", + "Termico 2", + "Peaker 1", + "Peaker 2", + "Eolico 1", + "Solar 1", + "Eolico 2", + "Solar 2", +] + +bg_quantity_bid = fill( + 999.0, + number_of_bidding_groups, + number_of_buses, + number_of_bg_segments, + number_of_subperiods, + number_of_scenarios, + number_of_periods, +) + +IARA.write_bids_time_series_file( + joinpath(PATH, "bidding_group_energy_bid"), + bg_quantity_bid; + dimensions = ["period", "scenario", "subperiod", "bid_segment"], + labels_bidding_groups = bidding_group_labels, + labels_buses = ["Sistema"], + time_dimension = "period", + dimension_size = [ + number_of_periods, + number_of_scenarios, + number_of_subperiods, + number_of_bg_segments, + ], + initial_date = "2025-01-01", + unit = "MWh", +) + +bg_price_bid = fill( + 999.0, + number_of_bidding_groups, + number_of_buses, + number_of_bg_segments, + number_of_subperiods, + number_of_scenarios, + number_of_periods, +) + +IARA.write_bids_time_series_file( + joinpath(PATH, "bidding_group_price_bid"), + bg_price_bid; + dimensions = ["period", "scenario", "subperiod", "bid_segment"], + labels_bidding_groups = bidding_group_labels, + labels_buses = ["Sistema"], + time_dimension = "period", + dimension_size = [ + number_of_periods, + number_of_scenarios, + number_of_subperiods, + number_of_bg_segments, + ], + initial_date = "2025-01-01", + unit = "\$/MWh", +) + +justifications = [] +for period in 1:number_of_periods + period_justification = Dict( + "period" => period, + "justifications" => Dict(label => "foo bar baz" for label in bidding_group_labels), + ) + push!(justifications, period_justification) +end + +open(joinpath(PATH, "bid_justifications.json"), "w") do file + return write(file, IARA.JSON.json(justifications)) +end + +# Renewable generation time series +# -------------------------------- +renewable_generation_ex_post = zeros( + number_of_renewable_units, + number_of_subperiods, + number_of_subscenarios, + number_of_scenarios, + number_of_periods, +) + +wind_indexes = [1, 3] +solar_indexes = [2, 4] +low_values = 0.2 +medium_values = 0.6 +high_values = 1.0 +wind_scenarios = [ + low_values, + low_values, + medium_values, + medium_values, + high_values, + high_values, +] +solar_scenarios = [ + medium_values, + high_values, + low_values, + high_values, + low_values, + medium_values, +] +for subscenario in 1:number_of_subscenarios + renewable_generation_ex_post[wind_indexes, :, subscenario, :, :] .= wind_scenarios[subscenario] + renewable_generation_ex_post[solar_indexes, :, subscenario, :, :] .= solar_scenarios[subscenario] +end + +IARA.write_timeseries_file( + joinpath(PATH, "renewable_generation_ex_post"), + renewable_generation_ex_post; + dimensions = ["period", "scenario", "subscenario", "subperiod"], + labels = ["Eolica 1", "Solar 1", "Eolica 2", "Solar 2"], + time_dimension = "period", + dimension_size = [ + number_of_periods, + number_of_scenarios, + number_of_subscenarios, + number_of_subperiods, + ], + initial_date = "2025-01-01T00:00:00", + unit = "p.u.", +) + +IARA.link_time_series_to_file( + db, + "RenewableUnit"; + generation_ex_post = "renewable_generation_ex_post", +) + +# Demand time series +# ------------------ +# The base case wrote the ex-post demand for 4 subscenarios, so it must be +# rewritten for the new subscenario count +max_demand = 400.0 +demand_ex_post = [300.0, 200.0, 400.0, 300.0, 400.0, 200.0] + +demand_factor_ex_post = zeros( + number_of_buses, + number_of_subperiods, + number_of_subscenarios, + number_of_scenarios, + number_of_periods, +) +for subscenario in 1:number_of_subscenarios + demand_factor_ex_post[:, :, subscenario, :, :] .= demand_ex_post[subscenario] / max_demand +end + +IARA.write_timeseries_file( + joinpath(PATH, "demand_ex_post"), + demand_factor_ex_post; + dimensions = ["period", "scenario", "subscenario", "subperiod"], + labels = ["Demanda"], + time_dimension = "period", + dimension_size = [ + number_of_periods, + number_of_scenarios, + number_of_subscenarios, + number_of_subperiods, + ], + initial_date = "2025-01-01T00:00:00", + unit = "p.u.", +) + +IARA.link_time_series_to_file( + db, + "DemandUnit"; + demand_ex_post = "demand_ex_post", +) + +# Total renewable generation per subscenario +# ----------------------------------------- +# The generation time series is in p.u., so each unit's factor is scaled by its +# installed capacity to get MW +installed_capacity = zeros(number_of_renewable_units) +installed_capacity[wind_indexes] .= wind_max_generation +installed_capacity[solar_indexes] .= solar_max_generation + +println("Total renewable generation per subscenario:") +renewable_generation_ex_ante = 0.0 +for idx in 1:4 + global renewable_generation_ex_ante += + sum(renewable_generation_ex_post[idx, 1, :, 1, 1] * installed_capacity[idx]) / number_of_subscenarios +end +for subscenario in 1:number_of_subscenarios + generation_per_unit = + renewable_generation_ex_post[:, 1, subscenario, 1, 1] .* installed_capacity + net_demand = demand_ex_post[subscenario] + renewable_generation_ex_ante - sum(generation_per_unit) + println(" Subscenario $subscenario:") + println(" Renewable generation: $(sum(generation_per_unit)) MW") + println(" Net demand: $net_demand MW") +end +println(" Ex-ante : $renewable_generation_ex_ante MW") + +IARA.close_study!(db) diff --git a/test/case_23/renewables_case/test_case.jl b/test/case_23/renewables_case/test_case.jl new file mode 100644 index 0000000000..3710576b41 --- /dev/null +++ b/test/case_23/renewables_case/test_case.jl @@ -0,0 +1,339 @@ +############################################################################# +# Copyright (c) 2024: PSR, CCEE (Câmara de Comercialização de Energia +# Elétrica), and contributors +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +############################################################################# +# IARA +# See https://github.com/psrenergy/IARA.jl +############################################################################# + +module TestCase23RenewablesCase + +using Test +using IARA + +const PATH = @__DIR__ + +db = nothing +try + include("../base_case/build_case.jl") + include("modify_case.jl") +finally + if db !== nothing + IARA.close_study!(db) + end +end + +# Setup +IARA.train_min_cost( + PATH; + plot_outputs = false, + delete_output_folder_before_execution = true, + output_path = "outputs/mincost", +) +cp(joinpath(PATH, "outputs/mincost", "cuts.json"), joinpath(PATH, "cuts.json"); force = true) + +IARA.InterfaceCalls.interface_call( + PATH; + delete_output_folder_before_execution = true, + output_path = "outputs/interface_call", +) + +# Period 1 +IARA.single_period_heuristic_bid( + PATH; + plot_outputs = false, + delete_output_folder_before_execution = true, + period = 1, + plot_ui_outputs = true, + output_path = "outputs/heuristic_bid_1", +) + +cp( + joinpath(PATH, "outputs/heuristic_bid_1", "bidding_group_energy_bid_period_1.csv"), + joinpath(PATH, "bidding_group_energy_bid.csv"); + force = true, +) + +cp( + joinpath(PATH, "outputs/heuristic_bid_1", "bidding_group_energy_bid_period_1.toml"), + joinpath(PATH, "bidding_group_energy_bid.toml"); + force = true, +) + +cp( + joinpath(PATH, "bidding_group_energy_bid.csv"), + joinpath(PATH, "bidding_group_no_markup_energy_bid_period_1.csv"); + force = true, +) + +cp( + joinpath(PATH, "bidding_group_energy_bid.toml"), + joinpath(PATH, "bidding_group_no_markup_energy_bid_period_1.toml"); + force = true, +) + +cp( + joinpath(PATH, "outputs/heuristic_bid_1", "bidding_group_price_bid_period_1.csv"), + joinpath(PATH, "bidding_group_price_bid.csv"); + force = true, +) + +cp( + joinpath(PATH, "outputs/heuristic_bid_1", "bidding_group_price_bid_period_1.toml"), + joinpath(PATH, "bidding_group_price_bid.toml"); + force = true, +) + +cp( + joinpath(PATH, "outputs/heuristic_bid_1", "bidding_group_no_markup_price_bid_period_1.csv"), + joinpath(PATH, "bidding_group_no_markup_price_bid_period_1.csv"); + force = true, +) + +cp( + joinpath(PATH, "outputs/heuristic_bid_1", "bidding_group_no_markup_price_bid_period_1.toml"), + joinpath(PATH, "bidding_group_no_markup_price_bid_period_1.toml"); + force = true, +) + +IARA.single_period_market_clearing( + PATH; + plot_outputs = false, + delete_output_folder_before_execution = true, + period = 1, + plot_ui_outputs = true, + output_path = "outputs/market_clearing_1", +) + +# Inter-period +cp( + joinpath(PATH, "outputs/market_clearing_1", "EX_POST_PHYSICAL_period_1_scenario_1.json"), + joinpath(PATH, "EX_POST_PHYSICAL_period_1_scenario_1.json"); + force = true, +) + +# Period 2 +IARA.single_period_heuristic_bid( + PATH; + plot_outputs = false, + delete_output_folder_before_execution = true, + period = 2, + plot_ui_outputs = true, + output_path = "outputs/heuristic_bid_2", +) + +cp( + joinpath(PATH, "outputs/heuristic_bid_2", "bidding_group_energy_bid_period_2.csv"), + joinpath(PATH, "bidding_group_energy_bid.csv"); + force = true, +) + +cp( + joinpath(PATH, "outputs/heuristic_bid_2", "bidding_group_energy_bid_period_2.toml"), + joinpath(PATH, "bidding_group_energy_bid.toml"); + force = true, +) + +cp( + joinpath(PATH, "bidding_group_energy_bid.csv"), + joinpath(PATH, "bidding_group_no_markup_energy_bid_period_2.csv"); + force = true, +) + +cp( + joinpath(PATH, "bidding_group_energy_bid.toml"), + joinpath(PATH, "bidding_group_no_markup_energy_bid_period_2.toml"); + force = true, +) + +cp( + joinpath(PATH, "outputs/heuristic_bid_2", "bidding_group_price_bid_period_2.csv"), + joinpath(PATH, "bidding_group_price_bid.csv"); + force = true, +) + +cp( + joinpath(PATH, "outputs/heuristic_bid_2", "bidding_group_price_bid_period_2.toml"), + joinpath(PATH, "bidding_group_price_bid.toml"); + force = true, +) + +cp( + joinpath(PATH, "outputs/heuristic_bid_2", "bidding_group_no_markup_price_bid_period_2.csv"), + joinpath(PATH, "bidding_group_no_markup_price_bid_period_2.csv"); + force = true, +) + +cp( + joinpath(PATH, "outputs/heuristic_bid_2", "bidding_group_no_markup_price_bid_period_2.toml"), + joinpath(PATH, "bidding_group_no_markup_price_bid_period_2.toml"); + force = true, +) + +IARA.single_period_market_clearing( + PATH; + plot_outputs = false, + delete_output_folder_before_execution = true, + period = 2, + plot_ui_outputs = true, + output_path = "outputs/market_clearing_2", +) + +# Inter-period +cp( + joinpath(PATH, "outputs/market_clearing_2", "EX_POST_PHYSICAL_period_2_scenario_1.json"), + joinpath(PATH, "EX_POST_PHYSICAL_period_2_scenario_1.json"); + force = true, +) + +# Period 3 +IARA.single_period_heuristic_bid( + PATH; + plot_outputs = false, + delete_output_folder_before_execution = true, + period = 3, + plot_ui_outputs = true, + output_path = "outputs/heuristic_bid_3", +) + +cp( + joinpath(PATH, "outputs/heuristic_bid_3", "bidding_group_energy_bid_period_3.csv"), + joinpath(PATH, "bidding_group_energy_bid.csv"); + force = true, +) + +cp( + joinpath(PATH, "outputs/heuristic_bid_3", "bidding_group_energy_bid_period_3.toml"), + joinpath(PATH, "bidding_group_energy_bid.toml"); + force = true, +) + +cp( + joinpath(PATH, "bidding_group_energy_bid.csv"), + joinpath(PATH, "bidding_group_no_markup_energy_bid_period_3.csv"); + force = true, +) + +cp( + joinpath(PATH, "bidding_group_energy_bid.toml"), + joinpath(PATH, "bidding_group_no_markup_energy_bid_period_3.toml"); + force = true, +) + +cp( + joinpath(PATH, "outputs/heuristic_bid_3", "bidding_group_price_bid_period_3.csv"), + joinpath(PATH, "bidding_group_price_bid.csv"); + force = true, +) + +cp( + joinpath(PATH, "outputs/heuristic_bid_3", "bidding_group_price_bid_period_3.toml"), + joinpath(PATH, "bidding_group_price_bid.toml"); + force = true, +) + +cp( + joinpath(PATH, "outputs/heuristic_bid_3", "bidding_group_no_markup_price_bid_period_3.csv"), + joinpath(PATH, "bidding_group_no_markup_price_bid_period_3.csv"); + force = true, +) + +cp( + joinpath(PATH, "outputs/heuristic_bid_3", "bidding_group_no_markup_price_bid_period_3.toml"), + joinpath(PATH, "bidding_group_no_markup_price_bid_period_3.toml"); + force = true, +) + +IARA.single_period_market_clearing( + PATH; + plot_outputs = false, + delete_output_folder_before_execution = true, + period = 3, + plot_ui_outputs = true, + output_path = "outputs/market_clearing_3", +) + +# Inter-period +cp( + joinpath(PATH, "outputs/market_clearing_3", "EX_POST_PHYSICAL_period_3_scenario_1.json"), + joinpath(PATH, "EX_POST_PHYSICAL_period_3_scenario_1.json"); + force = true, +) + +# Period 4 +IARA.single_period_heuristic_bid( + PATH; + plot_outputs = false, + delete_output_folder_before_execution = true, + period = 4, + plot_ui_outputs = true, + output_path = "outputs/heuristic_bid_4", +) + +cp( + joinpath(PATH, "outputs/heuristic_bid_4", "bidding_group_energy_bid_period_4.csv"), + joinpath(PATH, "bidding_group_energy_bid.csv"); + force = true, +) + +cp( + joinpath(PATH, "outputs/heuristic_bid_4", "bidding_group_energy_bid_period_4.toml"), + joinpath(PATH, "bidding_group_energy_bid.toml"); + force = true, +) + +cp( + joinpath(PATH, "bidding_group_energy_bid.csv"), + joinpath(PATH, "bidding_group_no_markup_energy_bid_period_4.csv"); + force = true, +) + +cp( + joinpath(PATH, "bidding_group_energy_bid.toml"), + joinpath(PATH, "bidding_group_no_markup_energy_bid_period_4.toml"); + force = true, +) + +cp( + joinpath(PATH, "outputs/heuristic_bid_4", "bidding_group_price_bid_period_4.csv"), + joinpath(PATH, "bidding_group_price_bid.csv"); + force = true, +) + +cp( + joinpath(PATH, "outputs/heuristic_bid_4", "bidding_group_price_bid_period_4.toml"), + joinpath(PATH, "bidding_group_price_bid.toml"); + force = true, +) + +cp( + joinpath(PATH, "outputs/heuristic_bid_4", "bidding_group_no_markup_price_bid_period_4.csv"), + joinpath(PATH, "bidding_group_no_markup_price_bid_period_4.csv"); + force = true, +) + +cp( + joinpath(PATH, "outputs/heuristic_bid_4", "bidding_group_no_markup_price_bid_period_4.toml"), + joinpath(PATH, "bidding_group_no_markup_price_bid_period_4.toml"); + force = true, +) + +IARA.single_period_market_clearing( + PATH; + plot_outputs = false, + delete_output_folder_before_execution = true, + period = 4, + plot_ui_outputs = true, + output_path = "outputs/market_clearing_4", +) + +if Main.UPDATE_RESULTS + Main.update_outputs!(PATH) +else + Main.compare_outputs(PATH) +end + +end From d0e62ef8e9fbe78c77876122d9ebb2dfe9d77889 Mon Sep 17 00:00:00 2001 From: gvidigal-psr Date: Tue, 18 Aug 2026 19:32:33 -0300 Subject: [PATCH 2/6] Update how renewable generation is considered in EX_ANTE HYBRID problems --- src/clearing_utils.jl | 17 +++++++++++++++++ src/model_constraints/renewable_balance.jl | 12 +++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/clearing_utils.jl b/src/clearing_utils.jl index 6e216cb3b1..02650b44aa 100644 --- a/src/clearing_utils.jl +++ b/src/clearing_utils.jl @@ -376,6 +376,23 @@ function is_ex_ante_problem(run_time_options::RunTimeOptions) run_time_options.clearing_model_subproblem == RunTime_ClearingSubproblem.EX_ANTE_COMMERCIAL end +""" + ignore_renewable_generation_scenario(inputs::Inputs, run_time_options::RunTimeOptions) + +Check if the renewable generation scenario should be ignored, i.e. treated as full availability. + +This happens in ex-ante hybrid problems whenever at least one ex-post problem is not skipped, +because in that case the renewable availability is settled in the ex-post problem. +""" +function ignore_renewable_generation_scenario(inputs::Inputs, run_time_options::RunTimeOptions) + return is_ex_ante_problem(run_time_options) && + construction_type(inputs, run_time_options) == Configurations_ConstructionType.HYBRID && + ( + construction_type_ex_post_physical(inputs) != Configurations_ConstructionType.SKIP || + construction_type_ex_post_commercial(inputs) != Configurations_ConstructionType.SKIP + ) +end + """ is_commercial_problem(run_time_options::RunTimeOptions) diff --git a/src/model_constraints/renewable_balance.jl b/src/model_constraints/renewable_balance.jl index 36af5c0753..92344df362 100644 --- a/src/model_constraints/renewable_balance.jl +++ b/src/model_constraints/renewable_balance.jl @@ -29,13 +29,23 @@ function renewable_balance!( # Model parameters renewable_generation_scenario = get_model_object(model, :renewable_generation_scenario) + # In ex-ante hybrid problems with at least one ex-post problem, the renewable availability is + # settled in the ex-post problem, so full availability is assumed here. + generation_scenario = + if ignore_renewable_generation_scenario(inputs, run_time_options) + # (b, r) -> 1.0 + (b, r) -> renewable_generation_scenario[b, r] + else + (b, r) -> renewable_generation_scenario[b, r] + end + # Constraints @constraint( model.jump_model, renewable_balance[b in subperiods(inputs), r in renewable_units], renewable_generation[b, r] + renewable_curtailment[b, r] == renewable_unit_max_generation(inputs, r) * subperiod_duration_in_hours(inputs, b) * - renewable_generation_scenario[b, r] + generation_scenario(b, r) ) return nothing From b336c21c544b970aa70bde02c0323c9551b26cf7 Mon Sep 17 00:00:00 2001 From: gvidigal-psr Date: Tue, 18 Aug 2026 19:39:31 -0300 Subject: [PATCH 3/6] fix --- src/model_constraints/renewable_balance.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/model_constraints/renewable_balance.jl b/src/model_constraints/renewable_balance.jl index 92344df362..4891152f7c 100644 --- a/src/model_constraints/renewable_balance.jl +++ b/src/model_constraints/renewable_balance.jl @@ -33,8 +33,7 @@ function renewable_balance!( # settled in the ex-post problem, so full availability is assumed here. generation_scenario = if ignore_renewable_generation_scenario(inputs, run_time_options) - # (b, r) -> 1.0 - (b, r) -> renewable_generation_scenario[b, r] + (b, r) -> 1.0 else (b, r) -> renewable_generation_scenario[b, r] end From 8d0e24683a89bd5082c8d4320ec6473783121fd6 Mon Sep 17 00:00:00 2001 From: gvidigal-psr Date: Wed, 19 Aug 2026 10:40:07 -0300 Subject: [PATCH 4/6] [no ci] update agent order --- test/case_23/renewables_case/modify_case.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/case_23/renewables_case/modify_case.jl b/test/case_23/renewables_case/modify_case.jl index 73994ba398..1d8a79b797 100644 --- a/test/case_23/renewables_case/modify_case.jl +++ b/test/case_23/renewables_case/modify_case.jl @@ -61,7 +61,9 @@ for i in 1:2 biddinggroup_id = "Eolico $i", bus_id = "Sistema", ) +end +for i in 1:2 IARA.add_asset_owner!(db; label = "Agente Solar $i", purchase_discount_rate = [0.1]) IARA.add_bidding_group!( db; From 3922a3899800a4b874ff5609c2bbe14f8ba9b724 Mon Sep 17 00:00:00 2001 From: gvidigal-psr Date: Mon, 24 Aug 2026 19:15:28 -0300 Subject: [PATCH 5/6] Add renewable bid price limit and increase number of periods --- test/case_23/renewables_case/modify_case.jl | 35 +- test/case_23/renewables_case/test_case.jl | 349 ++++---------------- 2 files changed, 88 insertions(+), 296 deletions(-) diff --git a/test/case_23/renewables_case/modify_case.jl b/test/case_23/renewables_case/modify_case.jl index 1d8a79b797..05f57342fb 100644 --- a/test/case_23/renewables_case/modify_case.jl +++ b/test/case_23/renewables_case/modify_case.jl @@ -10,6 +10,7 @@ db = IARA.load_study(PATH; read_only = false) +number_of_periods = 6 number_of_renewable_units = 4 number_of_subscenarios = 6 wind_max_generation = 80.0 @@ -19,8 +20,10 @@ solar_max_generation = 50.0 # renewable generation scenarios added below IARA.update_configuration!( db; + number_of_periods = number_of_periods, renewable_scenarios_files = IARA.Configurations_UncertaintyScenariosFiles.ONLY_EX_POST, number_of_subscenarios = number_of_subscenarios, + bid_price_limit_low_reference = 100.0, ) # Remove the third and fourth agent of each thermal type, along with their assets @@ -200,6 +203,29 @@ solar_scenarios = [ low_values, medium_values, ] + +# The generation time series is in p.u., so each unit's factor is scaled by its +# installed capacity to get MW +installed_capacity = zeros(number_of_renewable_units) +installed_capacity[wind_indexes] .= wind_max_generation +installed_capacity[solar_indexes] .= solar_max_generation + +max_demand = 400.0 +demand_ex_post = [300.0, 200.0, 400.0, 300.0, 400.0, 200.0] + +# Sort the subscenarios by increasing net demand, so that all time series are +# written in the same, monotonic order. The ex-ante renewable generation is the +# average over subscenarios, hence constant, so it does not affect the ordering +renewable_generation_per_subscenario = [ + wind_scenarios[subscenario] * sum(installed_capacity[wind_indexes]) + + solar_scenarios[subscenario] * sum(installed_capacity[solar_indexes]) + for subscenario in 1:number_of_subscenarios +] +subscenario_order = sortperm(demand_ex_post .- renewable_generation_per_subscenario) +wind_scenarios = wind_scenarios[subscenario_order] +solar_scenarios = solar_scenarios[subscenario_order] +demand_ex_post = demand_ex_post[subscenario_order] + for subscenario in 1:number_of_subscenarios renewable_generation_ex_post[wind_indexes, :, subscenario, :, :] .= wind_scenarios[subscenario] renewable_generation_ex_post[solar_indexes, :, subscenario, :, :] .= solar_scenarios[subscenario] @@ -231,9 +257,6 @@ IARA.link_time_series_to_file( # ------------------ # The base case wrote the ex-post demand for 4 subscenarios, so it must be # rewritten for the new subscenario count -max_demand = 400.0 -demand_ex_post = [300.0, 200.0, 400.0, 300.0, 400.0, 200.0] - demand_factor_ex_post = zeros( number_of_buses, number_of_subperiods, @@ -269,12 +292,6 @@ IARA.link_time_series_to_file( # Total renewable generation per subscenario # ----------------------------------------- -# The generation time series is in p.u., so each unit's factor is scaled by its -# installed capacity to get MW -installed_capacity = zeros(number_of_renewable_units) -installed_capacity[wind_indexes] .= wind_max_generation -installed_capacity[solar_indexes] .= solar_max_generation - println("Total renewable generation per subscenario:") renewable_generation_ex_ante = 0.0 for idx in 1:4 diff --git a/test/case_23/renewables_case/test_case.jl b/test/case_23/renewables_case/test_case.jl index 3710576b41..b4fb1da90a 100644 --- a/test/case_23/renewables_case/test_case.jl +++ b/test/case_23/renewables_case/test_case.jl @@ -41,294 +41,69 @@ IARA.InterfaceCalls.interface_call( output_path = "outputs/interface_call", ) -# Period 1 -IARA.single_period_heuristic_bid( - PATH; - plot_outputs = false, - delete_output_folder_before_execution = true, - period = 1, - plot_ui_outputs = true, - output_path = "outputs/heuristic_bid_1", -) - -cp( - joinpath(PATH, "outputs/heuristic_bid_1", "bidding_group_energy_bid_period_1.csv"), - joinpath(PATH, "bidding_group_energy_bid.csv"); - force = true, -) - -cp( - joinpath(PATH, "outputs/heuristic_bid_1", "bidding_group_energy_bid_period_1.toml"), - joinpath(PATH, "bidding_group_energy_bid.toml"); - force = true, -) - -cp( - joinpath(PATH, "bidding_group_energy_bid.csv"), - joinpath(PATH, "bidding_group_no_markup_energy_bid_period_1.csv"); - force = true, -) - -cp( - joinpath(PATH, "bidding_group_energy_bid.toml"), - joinpath(PATH, "bidding_group_no_markup_energy_bid_period_1.toml"); - force = true, -) - -cp( - joinpath(PATH, "outputs/heuristic_bid_1", "bidding_group_price_bid_period_1.csv"), - joinpath(PATH, "bidding_group_price_bid.csv"); - force = true, -) - -cp( - joinpath(PATH, "outputs/heuristic_bid_1", "bidding_group_price_bid_period_1.toml"), - joinpath(PATH, "bidding_group_price_bid.toml"); - force = true, -) - -cp( - joinpath(PATH, "outputs/heuristic_bid_1", "bidding_group_no_markup_price_bid_period_1.csv"), - joinpath(PATH, "bidding_group_no_markup_price_bid_period_1.csv"); - force = true, -) - -cp( - joinpath(PATH, "outputs/heuristic_bid_1", "bidding_group_no_markup_price_bid_period_1.toml"), - joinpath(PATH, "bidding_group_no_markup_price_bid_period_1.toml"); - force = true, -) - -IARA.single_period_market_clearing( - PATH; - plot_outputs = false, - delete_output_folder_before_execution = true, - period = 1, - plot_ui_outputs = true, - output_path = "outputs/market_clearing_1", -) - -# Inter-period -cp( - joinpath(PATH, "outputs/market_clearing_1", "EX_POST_PHYSICAL_period_1_scenario_1.json"), - joinpath(PATH, "EX_POST_PHYSICAL_period_1_scenario_1.json"); - force = true, -) - -# Period 2 -IARA.single_period_heuristic_bid( - PATH; - plot_outputs = false, - delete_output_folder_before_execution = true, - period = 2, - plot_ui_outputs = true, - output_path = "outputs/heuristic_bid_2", -) - -cp( - joinpath(PATH, "outputs/heuristic_bid_2", "bidding_group_energy_bid_period_2.csv"), - joinpath(PATH, "bidding_group_energy_bid.csv"); - force = true, -) - -cp( - joinpath(PATH, "outputs/heuristic_bid_2", "bidding_group_energy_bid_period_2.toml"), - joinpath(PATH, "bidding_group_energy_bid.toml"); - force = true, -) - -cp( - joinpath(PATH, "bidding_group_energy_bid.csv"), - joinpath(PATH, "bidding_group_no_markup_energy_bid_period_2.csv"); - force = true, -) - -cp( - joinpath(PATH, "bidding_group_energy_bid.toml"), - joinpath(PATH, "bidding_group_no_markup_energy_bid_period_2.toml"); - force = true, -) - -cp( - joinpath(PATH, "outputs/heuristic_bid_2", "bidding_group_price_bid_period_2.csv"), - joinpath(PATH, "bidding_group_price_bid.csv"); - force = true, -) - -cp( - joinpath(PATH, "outputs/heuristic_bid_2", "bidding_group_price_bid_period_2.toml"), - joinpath(PATH, "bidding_group_price_bid.toml"); - force = true, -) - -cp( - joinpath(PATH, "outputs/heuristic_bid_2", "bidding_group_no_markup_price_bid_period_2.csv"), - joinpath(PATH, "bidding_group_no_markup_price_bid_period_2.csv"); - force = true, -) - -cp( - joinpath(PATH, "outputs/heuristic_bid_2", "bidding_group_no_markup_price_bid_period_2.toml"), - joinpath(PATH, "bidding_group_no_markup_price_bid_period_2.toml"); - force = true, -) - -IARA.single_period_market_clearing( - PATH; - plot_outputs = false, - delete_output_folder_before_execution = true, - period = 2, - plot_ui_outputs = true, - output_path = "outputs/market_clearing_2", -) - -# Inter-period -cp( - joinpath(PATH, "outputs/market_clearing_2", "EX_POST_PHYSICAL_period_2_scenario_1.json"), - joinpath(PATH, "EX_POST_PHYSICAL_period_2_scenario_1.json"); - force = true, -) - -# Period 3 -IARA.single_period_heuristic_bid( - PATH; - plot_outputs = false, - delete_output_folder_before_execution = true, - period = 3, - plot_ui_outputs = true, - output_path = "outputs/heuristic_bid_3", -) - -cp( - joinpath(PATH, "outputs/heuristic_bid_3", "bidding_group_energy_bid_period_3.csv"), - joinpath(PATH, "bidding_group_energy_bid.csv"); - force = true, -) - -cp( - joinpath(PATH, "outputs/heuristic_bid_3", "bidding_group_energy_bid_period_3.toml"), - joinpath(PATH, "bidding_group_energy_bid.toml"); - force = true, -) - -cp( - joinpath(PATH, "bidding_group_energy_bid.csv"), - joinpath(PATH, "bidding_group_no_markup_energy_bid_period_3.csv"); - force = true, -) - -cp( - joinpath(PATH, "bidding_group_energy_bid.toml"), - joinpath(PATH, "bidding_group_no_markup_energy_bid_period_3.toml"); - force = true, -) - -cp( - joinpath(PATH, "outputs/heuristic_bid_3", "bidding_group_price_bid_period_3.csv"), - joinpath(PATH, "bidding_group_price_bid.csv"); - force = true, -) - -cp( - joinpath(PATH, "outputs/heuristic_bid_3", "bidding_group_price_bid_period_3.toml"), - joinpath(PATH, "bidding_group_price_bid.toml"); - force = true, -) - -cp( - joinpath(PATH, "outputs/heuristic_bid_3", "bidding_group_no_markup_price_bid_period_3.csv"), - joinpath(PATH, "bidding_group_no_markup_price_bid_period_3.csv"); - force = true, -) - -cp( - joinpath(PATH, "outputs/heuristic_bid_3", "bidding_group_no_markup_price_bid_period_3.toml"), - joinpath(PATH, "bidding_group_no_markup_price_bid_period_3.toml"); - force = true, -) - -IARA.single_period_market_clearing( - PATH; - plot_outputs = false, - delete_output_folder_before_execution = true, - period = 3, - plot_ui_outputs = true, - output_path = "outputs/market_clearing_3", -) - -# Inter-period -cp( - joinpath(PATH, "outputs/market_clearing_3", "EX_POST_PHYSICAL_period_3_scenario_1.json"), - joinpath(PATH, "EX_POST_PHYSICAL_period_3_scenario_1.json"); - force = true, -) - -# Period 4 -IARA.single_period_heuristic_bid( - PATH; - plot_outputs = false, - delete_output_folder_before_execution = true, - period = 4, - plot_ui_outputs = true, - output_path = "outputs/heuristic_bid_4", -) - -cp( - joinpath(PATH, "outputs/heuristic_bid_4", "bidding_group_energy_bid_period_4.csv"), - joinpath(PATH, "bidding_group_energy_bid.csv"); - force = true, -) - -cp( - joinpath(PATH, "outputs/heuristic_bid_4", "bidding_group_energy_bid_period_4.toml"), - joinpath(PATH, "bidding_group_energy_bid.toml"); - force = true, -) - -cp( - joinpath(PATH, "bidding_group_energy_bid.csv"), - joinpath(PATH, "bidding_group_no_markup_energy_bid_period_4.csv"); - force = true, -) - -cp( - joinpath(PATH, "bidding_group_energy_bid.toml"), - joinpath(PATH, "bidding_group_no_markup_energy_bid_period_4.toml"); - force = true, -) - -cp( - joinpath(PATH, "outputs/heuristic_bid_4", "bidding_group_price_bid_period_4.csv"), - joinpath(PATH, "bidding_group_price_bid.csv"); - force = true, -) - -cp( - joinpath(PATH, "outputs/heuristic_bid_4", "bidding_group_price_bid_period_4.toml"), - joinpath(PATH, "bidding_group_price_bid.toml"); - force = true, -) - -cp( - joinpath(PATH, "outputs/heuristic_bid_4", "bidding_group_no_markup_price_bid_period_4.csv"), - joinpath(PATH, "bidding_group_no_markup_price_bid_period_4.csv"); - force = true, -) - -cp( - joinpath(PATH, "outputs/heuristic_bid_4", "bidding_group_no_markup_price_bid_period_4.toml"), - joinpath(PATH, "bidding_group_no_markup_price_bid_period_4.toml"); - force = true, -) +number_of_periods = 6 + +for period in 1:number_of_periods + IARA.single_period_heuristic_bid( + PATH; + plot_outputs = false, + delete_output_folder_before_execution = true, + period = period, + plot_ui_outputs = true, + output_path = "outputs/heuristic_bid_$period", + ) + + heuristic_bid_path = joinpath(PATH, "outputs/heuristic_bid_$period") + + for extension in ["csv", "toml"] + cp( + joinpath(heuristic_bid_path, "bidding_group_energy_bid_period_$period.$extension"), + joinpath(PATH, "bidding_group_energy_bid.$extension"); + force = true, + ) + + cp( + joinpath(PATH, "bidding_group_energy_bid.$extension"), + joinpath(PATH, "bidding_group_no_markup_energy_bid_period_$period.$extension"); + force = true, + ) + + cp( + joinpath(heuristic_bid_path, "bidding_group_price_bid_period_$period.$extension"), + joinpath(PATH, "bidding_group_price_bid.$extension"); + force = true, + ) + + cp( + joinpath(heuristic_bid_path, "bidding_group_no_markup_price_bid_period_$period.$extension"), + joinpath(PATH, "bidding_group_no_markup_price_bid_period_$period.$extension"); + force = true, + ) + end -IARA.single_period_market_clearing( - PATH; - plot_outputs = false, - delete_output_folder_before_execution = true, - period = 4, - plot_ui_outputs = true, - output_path = "outputs/market_clearing_4", -) + IARA.single_period_market_clearing( + PATH; + plot_outputs = false, + delete_output_folder_before_execution = true, + period = period, + plot_ui_outputs = true, + output_path = "outputs/market_clearing_$period", + ) + + # Carry the physical state over to the next period. The last period has no + # successor, so nothing needs to be copied + if period < number_of_periods + cp( + joinpath( + PATH, + "outputs/market_clearing_$period", + "EX_POST_PHYSICAL_period_$(period)_scenario_1.json", + ), + joinpath(PATH, "EX_POST_PHYSICAL_period_$(period)_scenario_1.json"); + force = true, + ) + end +end if Main.UPDATE_RESULTS Main.update_outputs!(PATH) From 514c8fb404d6e12bd2c413bb60f5593a3076c170 Mon Sep 17 00:00:00 2001 From: gvidigal-psr Date: Mon, 24 Aug 2026 19:32:25 -0300 Subject: [PATCH 6/6] Shuffle correlation between scenarios across periods in the renewable case --- test/case_23/renewables_case/modify_case.jl | 185 ++++++++++++++------ 1 file changed, 134 insertions(+), 51 deletions(-) diff --git a/test/case_23/renewables_case/modify_case.jl b/test/case_23/renewables_case/modify_case.jl index 05f57342fb..ce936351fd 100644 --- a/test/case_23/renewables_case/modify_case.jl +++ b/test/case_23/renewables_case/modify_case.jl @@ -187,22 +187,6 @@ solar_indexes = [2, 4] low_values = 0.2 medium_values = 0.6 high_values = 1.0 -wind_scenarios = [ - low_values, - low_values, - medium_values, - medium_values, - high_values, - high_values, -] -solar_scenarios = [ - medium_values, - high_values, - low_values, - high_values, - low_values, - medium_values, -] # The generation time series is in p.u., so each unit's factor is scaled by its # installed capacity to get MW @@ -211,24 +195,91 @@ installed_capacity[wind_indexes] .= wind_max_generation installed_capacity[solar_indexes] .= solar_max_generation max_demand = 400.0 -demand_ex_post = [300.0, 200.0, 400.0, 300.0, 400.0, 200.0] - -# Sort the subscenarios by increasing net demand, so that all time series are -# written in the same, monotonic order. The ex-ante renewable generation is the -# average over subscenarios, hence constant, so it does not affect the ordering -renewable_generation_per_subscenario = [ - wind_scenarios[subscenario] * sum(installed_capacity[wind_indexes]) + - solar_scenarios[subscenario] * sum(installed_capacity[solar_indexes]) - for subscenario in 1:number_of_subscenarios +low_demand = 200.0 +medium_demand = 300.0 +high_demand = 400.0 + +# Correlation patterns between wind, solar and demand +# --------------------------------------------------- +# Each period pairs the six subscenarios differently, so that the case covers a +# range of correlation structures. Every series always takes each level (low, +# medium, high) exactly twice; only the pairing between them changes. +# +# Periods 1 and 2 keep the original pattern of the case, and periods 3 to 6 use +# pseudo-random pairings, chosen so that no two series are strongly correlated +# and no obvious rule relates them. The combination of high demand with both low +# solar and low wind is not allowed, and none of the patterns below contains it. +const LOW = 1 +const MEDIUM = 2 +const HIGH = 3 + +renewable_levels = [low_values, medium_values, high_values] +demand_levels = [low_demand, medium_demand, high_demand] + +# Each entry is (wind, solar, demand) levels over the six subscenarios +correlation_patterns = [ + # Original pattern of the case + ( + [LOW, LOW, MEDIUM, MEDIUM, HIGH, HIGH], + [MEDIUM, HIGH, LOW, HIGH, LOW, MEDIUM], + [MEDIUM, LOW, HIGH, MEDIUM, HIGH, LOW], + ), + ( + [HIGH, MEDIUM, HIGH, LOW, LOW, MEDIUM], + [LOW, HIGH, MEDIUM, HIGH, MEDIUM, LOW], + [MEDIUM, LOW, HIGH, MEDIUM, HIGH, LOW], + ), + ( + [HIGH, MEDIUM, HIGH, LOW, LOW, MEDIUM], + [LOW, HIGH, MEDIUM, LOW, MEDIUM, HIGH], + [MEDIUM, LOW, HIGH, MEDIUM, HIGH, LOW], + ), + ( + [HIGH, LOW, HIGH, LOW, MEDIUM, MEDIUM], + [HIGH, MEDIUM, MEDIUM, LOW, HIGH, LOW], + [MEDIUM, LOW, HIGH, MEDIUM, HIGH, LOW], + ), + ( + [HIGH, HIGH, MEDIUM, LOW, MEDIUM, LOW], + [LOW, MEDIUM, HIGH, HIGH, MEDIUM, LOW], + [MEDIUM, LOW, HIGH, MEDIUM, HIGH, LOW], + ), ] -subscenario_order = sortperm(demand_ex_post .- renewable_generation_per_subscenario) -wind_scenarios = wind_scenarios[subscenario_order] -solar_scenarios = solar_scenarios[subscenario_order] -demand_ex_post = demand_ex_post[subscenario_order] - -for subscenario in 1:number_of_subscenarios - renewable_generation_ex_post[wind_indexes, :, subscenario, :, :] .= wind_scenarios[subscenario] - renewable_generation_ex_post[solar_indexes, :, subscenario, :, :] .= solar_scenarios[subscenario] + +# Periods 1 and 2 share the original pattern; the remaining periods take one each +period_to_pattern = [1, 1, 2, 3, 4, 5] + +wind_scenarios = zeros(number_of_subscenarios, number_of_periods) +solar_scenarios = zeros(number_of_subscenarios, number_of_periods) +demand_ex_post = zeros(number_of_subscenarios, number_of_periods) + +for period in 1:number_of_periods + wind_levels, solar_levels, demand_pattern = correlation_patterns[period_to_pattern[period]] + + period_wind = renewable_levels[wind_levels] + period_solar = renewable_levels[solar_levels] + period_demand = demand_levels[demand_pattern] + + # Sort the subscenarios by increasing net demand, so that every period is + # written in the same, monotonic order. This only permutes the subscenario + # slots, so the correlation defined by the pattern is preserved. The ex-ante + # renewable generation is an average over subscenarios, hence constant, so + # it does not affect the ordering + renewable_generation_per_subscenario = + period_wind * sum(installed_capacity[wind_indexes]) + + period_solar * sum(installed_capacity[solar_indexes]) + subscenario_order = sortperm(period_demand .- renewable_generation_per_subscenario) + + wind_scenarios[:, period] = period_wind[subscenario_order] + solar_scenarios[:, period] = period_solar[subscenario_order] + demand_ex_post[:, period] = period_demand[subscenario_order] +end + +for period in 1:number_of_periods, subscenario in 1:number_of_subscenarios + renewable_generation_ex_post[wind_indexes, :, subscenario, :, period] .= + wind_scenarios[subscenario, period] + renewable_generation_ex_post[solar_indexes, :, subscenario, :, period] .= + solar_scenarios[subscenario, period] end IARA.write_timeseries_file( @@ -264,8 +315,9 @@ demand_factor_ex_post = zeros( number_of_scenarios, number_of_periods, ) -for subscenario in 1:number_of_subscenarios - demand_factor_ex_post[:, :, subscenario, :, :] .= demand_ex_post[subscenario] / max_demand +for period in 1:number_of_periods, subscenario in 1:number_of_subscenarios + demand_factor_ex_post[:, :, subscenario, :, period] .= + demand_ex_post[subscenario, period] / max_demand end IARA.write_timeseries_file( @@ -290,22 +342,53 @@ IARA.link_time_series_to_file( demand_ex_post = "demand_ex_post", ) -# Total renewable generation per subscenario -# ----------------------------------------- -println("Total renewable generation per subscenario:") -renewable_generation_ex_ante = 0.0 -for idx in 1:4 - global renewable_generation_ex_ante += - sum(renewable_generation_ex_post[idx, 1, :, 1, 1] * installed_capacity[idx]) / number_of_subscenarios -end -for subscenario in 1:number_of_subscenarios - generation_per_unit = - renewable_generation_ex_post[:, 1, subscenario, 1, 1] .* installed_capacity - net_demand = demand_ex_post[subscenario] + renewable_generation_ex_ante - sum(generation_per_unit) - println(" Subscenario $subscenario:") - println(" Renewable generation: $(sum(generation_per_unit)) MW") - println(" Net demand: $net_demand MW") +# Correlation table +# ----------------- +# For each period and subscenario, show the level taken by each series along +# with the resulting renewable generation and net demand +level_name(value, levels) = ["low", "medium", "high"][findfirst(==(value), levels)] + +wind_capacity = sum(installed_capacity[wind_indexes]) +solar_capacity = sum(installed_capacity[solar_indexes]) + +println("Correlation between wind, solar and demand per period:") +println( + rpad("Period", 8), + rpad("Subscen.", 10), + rpad("Wind", 9), + rpad("Solar", 9), + rpad("Demand", 9), + lpad("Renew. (MW)", 13), + lpad("Net dem. (MW)", 15), +) +for period in 1:number_of_periods + # The ex-ante generation is the average over the subscenarios of the period + renewable_generation_ex_ante = + sum( + wind_scenarios[subscenario, period] * wind_capacity + + solar_scenarios[subscenario, period] * solar_capacity for + subscenario in 1:number_of_subscenarios + ) / number_of_subscenarios + + for subscenario in 1:number_of_subscenarios + renewable_generation = + wind_scenarios[subscenario, period] * wind_capacity + + solar_scenarios[subscenario, period] * solar_capacity + net_demand = + demand_ex_post[subscenario, period] + renewable_generation_ex_ante - + renewable_generation + + println( + rpad(period, 8), + rpad(subscenario, 10), + rpad(level_name(wind_scenarios[subscenario, period], renewable_levels), 9), + rpad(level_name(solar_scenarios[subscenario, period], renewable_levels), 9), + rpad(level_name(demand_ex_post[subscenario, period], demand_levels), 9), + lpad(renewable_generation, 13), + lpad(net_demand, 15), + ) + end + println(" Ex-ante renewable generation: $renewable_generation_ex_ante MW") end -println(" Ex-ante : $renewable_generation_ex_ante MW") IARA.close_study!(db)