From e6aebc6b5c8a7c71b73a7f436a4a56886f89a765 Mon Sep 17 00:00:00 2001 From: Manuel Date: Mon, 30 Oct 2023 14:03:14 +0100 Subject: [PATCH] Report relative_optimality_gap Re #580 --- src/run_spineopt_standard.jl | 7 ++ templates/spineopt_template.json | 109 ++++++++++++++++--------------- test/run_spineopt.jl | 38 +++++++++++ 3 files changed, 100 insertions(+), 54 deletions(-) diff --git a/src/run_spineopt_standard.jl b/src/run_spineopt_standard.jl index 62a49a3128..e89ce99618 100644 --- a/src/run_spineopt_standard.jl +++ b/src/run_spineopt_standard.jl @@ -385,6 +385,7 @@ function _save_model_results!(m; iterations=nothing) _save_variable_values!(m) _save_constraint_values!(m) _save_objective_values!(m) + _save_other_values!(m) end """ @@ -396,6 +397,12 @@ function _save_variable_values!(m::Model) end end +function _save_other_values!(m::Model) + m.ext[:spineopt].values[:relative_optimality_gap] = Dict( + (model=m.ext[:spineopt].instance, t=current_window(m),) => JuMP.MOI.get(m, JuMP.MOI.RelativeGap()) + ) +end + """ Save the value of all constraints if the user wants to report it. """ diff --git a/templates/spineopt_template.json b/templates/spineopt_template.json index 83ecdf84d8..425a5f359b 100644 --- a/templates/spineopt_template.json +++ b/templates/spineopt_template.json @@ -460,60 +460,61 @@ ["units_on__temporal_block", "is_active", true, "boolean_value_list", "If false, the object is excluded from the model if the tool filter object activity control is specified"] ], "objects": [ - ["output","binary_gas_connection_flow", null], - ["output","connection_flow", null], - ["output","connection_intact_flow", null], - ["output","connections_decommissioned", null], - ["output","connections_invested_available", null], - ["output","connections_invested", null], - ["output","contingency_is_binding", null], - ["output","mp_objective_lowerbound", null], - ["output","mga_objective", null], - ["output","node_injection", null], - ["output","node_pressure", null], - ["output","node_slack_neg", null], - ["output","node_slack_pos", null], - ["output","node_state", null], - ["output","node_voltage_angle", null], - ["output","nonspin_ramp_down_unit_flow", null], - ["output","nonspin_ramp_up_unit_flow", null], - ["output","nonspin_units_shut_down", null], - ["output","nonspin_units_started_up", null], - ["output","ramp_down_unit_flow", null], - ["output","ramp_up_unit_flow", null], - ["output","shut_down_unit_flow", null], - ["output","start_up_unit_flow", null], - ["output","storages_decommissioned", null], - ["output","storages_invested_available", null], - ["output","storages_invested", null], - ["output","unit_flow_op_active", null], - ["output","unit_flow_op", null], - ["output","unit_flow", null], - ["output","units_available", null], - ["output","units_invested_available", null], - ["output","units_invested", null], - ["output","units_mothballed", null], - ["output","units_on", null], - ["output","units_shut_down", null], - ["output","units_started_up", null], - ["output","connection_avg_throughflow", null], - ["output","connection_avg_intact_throughflow", null], - ["output","variable_om_costs", null], - ["output","fixed_om_costs", null], - ["output","taxes", null], - ["output","fuel_costs", null], - ["output","unit_investment_costs", null], - ["output","connection_investment_costs", null], - ["output","storage_investment_costs", null], - ["output","start_up_costs", null], - ["output","shut_down_costs", null], - ["output","objective_penalties", null], - ["output","connection_flow_costs", null], - ["output","renewable_curtailment_costs", null], - ["output","res_proc_costs", null], - ["output","ramp_costs", null], - ["output","units_on_costs", null], - ["output","total_costs", null] + ["output", "binary_gas_connection_flow", null], + ["output", "connection_flow", null], + ["output", "connection_intact_flow", null], + ["output", "connections_decommissioned", null], + ["output", "connections_invested_available", null], + ["output", "connections_invested", null], + ["output", "contingency_is_binding", null], + ["output", "mp_objective_lowerbound", null], + ["output", "mga_objective", null], + ["output", "node_injection", null], + ["output", "node_pressure", null], + ["output", "node_slack_neg", null], + ["output", "node_slack_pos", null], + ["output", "node_state", null], + ["output", "node_voltage_angle", null], + ["output", "nonspin_ramp_down_unit_flow", null], + ["output", "nonspin_ramp_up_unit_flow", null], + ["output", "nonspin_units_shut_down", null], + ["output", "nonspin_units_started_up", null], + ["output", "ramp_down_unit_flow", null], + ["output", "ramp_up_unit_flow", null], + ["output", "shut_down_unit_flow", null], + ["output", "start_up_unit_flow", null], + ["output", "storages_decommissioned", null], + ["output", "storages_invested_available", null], + ["output", "storages_invested", null], + ["output", "unit_flow_op_active", null], + ["output", "unit_flow_op", null], + ["output", "unit_flow", null], + ["output", "units_available", null], + ["output", "units_invested_available", null], + ["output", "units_invested", null], + ["output", "units_mothballed", null], + ["output", "units_on", null], + ["output", "units_shut_down", null], + ["output", "units_started_up", null], + ["output", "connection_avg_throughflow", null], + ["output", "connection_avg_intact_throughflow", null], + ["output", "variable_om_costs", null], + ["output", "fixed_om_costs", null], + ["output", "taxes", null], + ["output", "fuel_costs", null], + ["output", "unit_investment_costs", null], + ["output", "connection_investment_costs", null], + ["output", "storage_investment_costs", null], + ["output", "start_up_costs", null], + ["output", "shut_down_costs", null], + ["output", "objective_penalties", null], + ["output", "connection_flow_costs", null], + ["output", "renewable_curtailment_costs", null], + ["output", "res_proc_costs", null], + ["output", "ramp_costs", null], + ["output", "units_on_costs", null], + ["output", "total_costs", null], + ["output", "relative_optimality_gap", null] ], "tools": [ ["object_activity_control", ""] diff --git a/test/run_spineopt.jl b/test/run_spineopt.jl index f12864bc58..43a339270b 100644 --- a/test/run_spineopt.jl +++ b/test/run_spineopt.jl @@ -72,6 +72,43 @@ function _test_run_spineopt_setup() url_in, url_out, file_path_out end +function _test_report_relative_optimality_gap() + @testset "report_relative_optimality_gap" begin + url_in, url_out, file_path_out = _test_run_spineopt_setup() + index = Dict("start" => "2000-01-01T00:00:00", "resolution" => "1 hour") + vom_cost_data = [100 * k for k in 1:24] + vom_cost = Dict("type" => "time_series", "data" => vom_cost_data, "index" => index) + demand_data = [2 * k for k in 1:24] + demand = Dict("type" => "time_series", "data" => demand_data, "index" => index) + unit_capacity = demand + object_parameter_values = [ + ["node", "node_b", "demand", demand], + ["model", "instance", "roll_forward", Dict("type" => "duration", "data" => "1h")], + ] + relationship_parameter_values = [ + ["unit__to_node", ["unit_ab", "node_b"], "unit_capacity", unit_capacity], + ["unit__to_node", ["unit_ab", "node_b"], "vom_cost", vom_cost], + ] + objects = [["output", "relative_optimality_gap"]] + relationships = [["report__output", ["report_x", "relative_optimality_gap"]]] + SpineInterface.import_data( + url_in; + object_parameter_values=object_parameter_values, + relationship_parameter_values=relationship_parameter_values, + relationships=relationships, + objects=objects + ) + rm(file_path_out; force=true) + m = run_spineopt(url_in, url_out; log_level=0) + using_spinedb(url_out, Y) + @testset for k in 1:24 + t1 = DateTime(2000, 1, 1, k - 1) + t = TimeSlice(t1, t1 + Hour(1)) + @test Y.relative_optimality_gap(model=first(Y.model()), report=Y.report(:report_x), t=t) !== nothing + end + end +end + function _test_rolling() @testset "rolling" begin url_in, url_out, file_path_out = _test_run_spineopt_setup() @@ -884,4 +921,5 @@ end _test_fix_node_state_using_map_with_rolling() _test_time_limit() _test_only_linear_model_has_duals() + _test_report_relative_optimality_gap() end \ No newline at end of file