From b4ddc9c15742be3385e69a88eb67eaa18a922221 Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Tue, 2 Apr 2024 11:48:01 -0600 Subject: [PATCH 1/8] formatter --- src/multi_optimization_container.jl | 44 ++++++++++++++--------------- src/multiproblem_template.jl | 4 +-- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/multi_optimization_container.jl b/src/multi_optimization_container.jl index f321432..befcebe 100644 --- a/src/multi_optimization_container.jl +++ b/src/multi_optimization_container.jl @@ -42,28 +42,28 @@ function MultiOptimizationContainer( ) return MultiOptimizationContainer{T}(; - main_problem = PSI.OptimizationContainer(sys, settings, nothing, U), - subproblems = subproblems, - time_steps = 1:1, - resolution = IS.time_period_conversion(resolution), - settings = settings, - settings_copy = PSI.copy_for_serialization(settings), - variables = Dict{PSI.VariableKey, AbstractArray}(), - aux_variables = Dict{PSI.AuxVarKey, AbstractArray}(), - duals = Dict{PSI.ConstraintKey, AbstractArray}(), - constraints = Dict{PSI.ConstraintKey, AbstractArray}(), - objective_function = PSI.ObjectiveFunction(), - expressions = Dict{PSI.ExpressionKey, AbstractArray}(), - parameters = Dict{PSI.ParameterKey, PSI.ParameterContainer}(), - primal_values_cache = PSI.PrimalValuesCache(), - initial_conditions = Dict{PSI.ICKey, Vector{PSI.InitialCondition}}(), - initial_conditions_data = PSI.InitialConditionsData(), - base_power = PSY.get_base_power(sys), - optimizer_stats = PSI.OptimizerStats(), - built_for_recurrent_solves = false, - metadata = PSI.OptimizationContainerMetadata(), - default_time_series_type = U, - mpi_info = nothing, + main_problem=PSI.OptimizationContainer(sys, settings, nothing, U), + subproblems=subproblems, + time_steps=1:1, + resolution=IS.time_period_conversion(resolution), + settings=settings, + settings_copy=PSI.copy_for_serialization(settings), + variables=Dict{PSI.VariableKey, AbstractArray}(), + aux_variables=Dict{PSI.AuxVarKey, AbstractArray}(), + duals=Dict{PSI.ConstraintKey, AbstractArray}(), + constraints=Dict{PSI.ConstraintKey, AbstractArray}(), + objective_function=PSI.ObjectiveFunction(), + expressions=Dict{PSI.ExpressionKey, AbstractArray}(), + parameters=Dict{PSI.ParameterKey, PSI.ParameterContainer}(), + primal_values_cache=PSI.PrimalValuesCache(), + initial_conditions=Dict{PSI.ICKey, Vector{PSI.InitialCondition}}(), + initial_conditions_data=PSI.InitialConditionsData(), + base_power=PSY.get_base_power(sys), + optimizer_stats=PSI.OptimizerStats(), + built_for_recurrent_solves=false, + metadata=PSI.OptimizationContainerMetadata(), + default_time_series_type=U, + mpi_info=nothing, ) end diff --git a/src/multiproblem_template.jl b/src/multiproblem_template.jl index e4b287b..57d48de 100644 --- a/src/multiproblem_template.jl +++ b/src/multiproblem_template.jl @@ -123,10 +123,10 @@ function PSI.set_service_model!( PSI.set_service_model!( template.base_template, service_name, - ServiceModel(service_type, formulation; use_service_name = true), + ServiceModel(service_type, formulation; use_service_name=true), ) for (id, sub_template) in get_sub_templates(template) - service_model = ServiceModel(service_type, formulation; use_service_name = true) + service_model = ServiceModel(service_type, formulation; use_service_name=true) PSI.set_subsystem!(service_model, id) PSI.set_service_model!(sub_template, service_name, service_model) end From cf2c1a9ed64f8c09213af087d4bbe1a47751f997 Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Tue, 2 Apr 2024 11:48:07 -0600 Subject: [PATCH 2/8] add ff methods --- src/problems/multi_region_problem.jl | 65 +++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/src/problems/multi_region_problem.jl b/src/problems/multi_region_problem.jl index 22f62ae..3234850 100644 --- a/src/problems/multi_region_problem.jl +++ b/src/problems/multi_region_problem.jl @@ -3,7 +3,7 @@ struct MultiRegionProblem <: PSI.DecisionProblem end function PSI.DecisionModel{MultiRegionProblem}( template::MultiProblemTemplate, sys::PSY.System, - ::Union{Nothing, JuMP.Model} = nothing; + ::Union{Nothing, JuMP.Model}=nothing; kwargs..., ) name = Symbol(get(kwargs, :name, nameof(MultiRegionProblem))) @@ -261,3 +261,66 @@ function PSI.solve_impl!(model::PSI.DecisionModel{MultiRegionProblem}) end function PSI._check_numerical_bounds(model::PSI.DecisionModel{MultiRegionProblem}) end + +### Simulation Related methods ### +# These code blocks are duplicative from PSI, refactoring might be required on the PSI side to +# avoid duplication. + +function PSI._add_feedforward_to_model( + sim_model::PSI.DecisionModel{MultiRegionProblem}, + ff::T, + ::Type{U}, +) where {T <: PSI.AbstractAffectFeedforward, U <: PSY.Device} + template = PSI.get_template(sim_model) + for (id, sub_template) in get_sub_templates(template) + device_model = PSI.get_model(sub_template, PSI.get_component_type(ff)) + if device_model === nothing + model_name = PSI.get_name(sim_model) + throw( + IS.ConflictingInputsError( + "Device model $(PSI.get_component_type(ff)) not found in model $model_name", + ), + ) + end + @info "attaching $T to $(PSI.get_component_type(ff)) to Template $id" + PSI.attach_feedforward!(device_model, ff) + end + return +end + +function PSI._add_feedforward_to_model( + sim_model::PSI.DecisionModel{MultiRegionProblem}, + ff::T, + ::Type{U}, +) where {T <: PSI.AbstractAffectFeedforward, U <: PSY.Service} + template = PSI.get_template(sim_model) + name_provided = PSI.get_feedforward_meta(ff) != PSI.NO_SERVICE_NAME_PROVIDED + for (id, sub_template) in get_sub_templates(template) + if name_provided + service_model = PSI.get_model( + sub_template, + PSI.get_component_type(ff), + PSI.get_feedforward_meta(ff), + ) + if service_model === nothing + throw( + IS.ConflictingInputsError( + "Service model $(get_component_type(ff)) not found in model $(get_name(sim_model))", + ), + ) + end + @info "attaching $T to $(PSI.get_component_type(ff)) $(PSI.get_feedforward_meta(ff)) to Template $id" + PSI.attach_feedforward!(service_model, ff) + else + service_found = false + for (key, model) in PSI.get_service_models(sub_template) + if key[2] == Symbol(PSI.get_component_type(ff)) + service_found = true + @info "attaching $T to $(PSI.get_component_type(ff))" + PSI.attach_feedforward!(model, ff) + end + end + end + end + return +end From f8d18de7f39de1c6dd0bbeb1d443d89879964ea0 Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Tue, 2 Apr 2024 11:48:13 -0600 Subject: [PATCH 3/8] add new deps --- test/Project.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/Project.toml b/test/Project.toml index 8cbe0c2..9a225ef 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,8 +1,10 @@ [deps] +HydroPowerSimulations = "fc1677e0-6ad7-4515-bf3a-bd6bf20a0b1b" InfrastructureSystems = "2cd47ed4-ca9b-11e9-27f2-ab636a7671f1" PowerSimulations = "e690365d-45e2-57bb-ac84-44ba829e73c4" PowerSimulationsDecomposition = "bed98974-b02a-5e2f-9ee0-a103f5c450dd" PowerSystemCaseBuilder = "f00506e0-b84f-492a-93c2-c0a9afc4364e" PowerSystems = "bcd98974-b02a-5e2f-9ee0-a103f5c450dd" Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" +StorageSystemsSimulations = "e2f1a126-19d0-4674-9252-42b2384f8e3c" Xpress = "9e70acf3-d6c9-5be6-b5bd-4e2c73e3e054" From 776fb01b7ed7c0d287f393da6e1fcc2d6cfc01dd Mon Sep 17 00:00:00 2001 From: Daniel Thom Date: Tue, 2 Apr 2024 12:05:12 -0600 Subject: [PATCH 4/8] Fix merge_attributes --- src/problems/multi_region_problem.jl | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/problems/multi_region_problem.jl b/src/problems/multi_region_problem.jl index 3234850..7f8cdea 100644 --- a/src/problems/multi_region_problem.jl +++ b/src/problems/multi_region_problem.jl @@ -126,11 +126,7 @@ function _make_parameter_attributes(subproblem_parameters) if !haskey(data, key) data[key] = deepcopy(val.attributes) else - existing = data[key] - if val.attributes.name != existing.name - error("Mismatch in attributes name: $key $val $(existing.name)") - end - _merge_attributes!(existing, val.attributes) + _merge_attributes!(data[key], val.attributes) end end end @@ -138,7 +134,10 @@ function _make_parameter_attributes(subproblem_parameters) return data end -function _merge_attributes(attributes::T, other::T) where {T <: PSI.ParameterAttributes} +function _merge_attributes!(attributes::T, other::T) where {T <: PSI.ParameterAttributes} + if attributes.attributes.name != other.name + error("Mismatch in attributes name: $(attributes.name) $(other.name)") + end for field in fieldnames(T) val1 = getproperty(attributes, field) val2 = getproperty(other, field) From fb6df7a548459064f692f073f317ed4f0902b73a Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Tue, 2 Apr 2024 12:10:55 -0600 Subject: [PATCH 5/8] fix double dot access --- src/problems/multi_region_problem.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/problems/multi_region_problem.jl b/src/problems/multi_region_problem.jl index 7f8cdea..7730987 100644 --- a/src/problems/multi_region_problem.jl +++ b/src/problems/multi_region_problem.jl @@ -135,7 +135,7 @@ function _make_parameter_attributes(subproblem_parameters) end function _merge_attributes!(attributes::T, other::T) where {T <: PSI.ParameterAttributes} - if attributes.attributes.name != other.name + if attributes.name != other.name error("Mismatch in attributes name: $(attributes.name) $(other.name)") end for field in fieldnames(T) From 44970aaa5b259c60d378eee838790479aa801e17 Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Tue, 2 Apr 2024 12:11:45 -0600 Subject: [PATCH 6/8] add check in the correct place --- src/problems/multi_region_problem.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/problems/multi_region_problem.jl b/src/problems/multi_region_problem.jl index 7730987..6af01ac 100644 --- a/src/problems/multi_region_problem.jl +++ b/src/problems/multi_region_problem.jl @@ -135,9 +135,6 @@ function _make_parameter_attributes(subproblem_parameters) end function _merge_attributes!(attributes::T, other::T) where {T <: PSI.ParameterAttributes} - if attributes.name != other.name - error("Mismatch in attributes name: $(attributes.name) $(other.name)") - end for field in fieldnames(T) val1 = getproperty(attributes, field) val2 = getproperty(other, field) @@ -150,6 +147,9 @@ function _merge_attributes!(attributes::T, other::T) where {T <: PSI.ParameterAt end function _merge_attributes!(attributes::T, other::T) where {T <: PSI.TimeSeriesAttributes} + if attributes.name != other.name + error("Mismatch in attributes name: $(attributes.name) $(other.name)") + end intersection = intersect( keys(attributes.component_name_to_ts_uuid), keys(other.component_name_to_ts_uuid), From c21acf3c6212e370318ee73ff5f6570e5645f4ef Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Thu, 4 Apr 2024 11:30:03 -0600 Subject: [PATCH 7/8] changes to support simulation --- src/algorithms/sequential_algorithm.jl | 5 ++- src/multi_optimization_container.jl | 1 + src/multiproblem_template.jl | 30 +++++++++++++--- src/problems/multi_region_problem.jl | 47 ++++++++++++++++++++++++-- 4 files changed, 73 insertions(+), 10 deletions(-) diff --git a/src/algorithms/sequential_algorithm.jl b/src/algorithms/sequential_algorithm.jl index ffdb173..fe473a3 100644 --- a/src/algorithms/sequential_algorithm.jl +++ b/src/algorithms/sequential_algorithm.jl @@ -35,7 +35,7 @@ function write_results_to_main_container(container::MultiOptimizationContainer) main_container_data_field = getproperty(container, field) for (key, src) in subproblem_data_field if src isa JuMP.Containers.SparseAxisArray - @warn "Skip SparseAxisArray" field key + # @warn "Skip SparseAxisArray" field key continue end num_dims = ndims(src) @@ -45,7 +45,7 @@ function write_results_to_main_container(container::MultiOptimizationContainer) data = PSI.jump_value.(src) catch e if e isa UndefRefError - @warn "Skip UndefRefError for" field key + #@warn "Skip UndefRefError for" field key continue end rethrow() @@ -68,7 +68,6 @@ function write_results_to_main_container(container::MultiOptimizationContainer) end _write_parameter_results_to_main_container(container, subproblem) end - # Parameters need a separate approach due to the way the containers work return end diff --git a/src/multi_optimization_container.jl b/src/multi_optimization_container.jl index befcebe..8331eef 100644 --- a/src/multi_optimization_container.jl +++ b/src/multi_optimization_container.jl @@ -163,6 +163,7 @@ function init_optimization_container!( PSI.LOG_GROUP_OPTIMIZATION_CONTAINER subproblem.settings = deepcopy(settings) PSI.init_optimization_container!(subproblem, network_model, sys) + subproblem.built_for_recurrent_solves = true end _finalize_jump_model!(container, settings) return diff --git a/src/multiproblem_template.jl b/src/multiproblem_template.jl index 57d48de..06e8603 100644 --- a/src/multiproblem_template.jl +++ b/src/multiproblem_template.jl @@ -47,6 +47,26 @@ function get_sub_problem_keys(template::MultiProblemTemplate) return sort!(collect(keys(get_sub_templates(template)))) end +function PSI.get_component_types(template::MultiProblemTemplate)::Vector{DataType} + base_template = template.base_template + return vcat( + get_component_type.(values(get_device_models(base_template))), + get_component_type.(values(get_branch_models(base_template))), + get_component_type.(values(get_service_models(base_template))), + ) +end + +function PSI.get_model(template::MultiProblemTemplate, ::Type{T}) where {T <: PSY.Device} + base_template = template.base_template + if T <: PSY.Branch + return get(base_template.branches, Symbol(T), nothing) + elseif T <: PSY.Device + return get(base_template.devices, Symbol(T), nothing) + else + error("Component $T not present in the template") + end +end + """ Sets the network model in a template. """ @@ -92,8 +112,9 @@ function PSI.set_device_model!( ) PSI.set_device_model!(template.base_template, model) for (id, sub_template) in get_sub_templates(template) - PSI.set_subsystem!(model, id) - PSI.set_device_model!(sub_template, model) + new_model = deepcopy(model) + PSI.set_subsystem!(new_model, id) + PSI.set_device_model!(sub_template, new_model) end return end @@ -104,8 +125,9 @@ function PSI.set_device_model!( ) PSI.set_device_model!(template.base_template, model) for (id, sub_template) in get_sub_templates(template) - PSI.set_subsystem!(model, id) - PSI.set_device_model!(sub_template, PSI.DeviceModel(component_type, formulation)) + new_model = deepcopy(model) + PSI.set_subsystem!(new_model, id) + PSI.set_device_model!(sub_template, new_model) end return end diff --git a/src/problems/multi_region_problem.jl b/src/problems/multi_region_problem.jl index 6af01ac..9502afc 100644 --- a/src/problems/multi_region_problem.jl +++ b/src/problems/multi_region_problem.jl @@ -184,9 +184,9 @@ function _make_parameter_arrays(subproblem_parameters, field_name) end function _make_array_joined_by_axes( - a1::JuMP.Containers.DenseAxisArray{Float64, 2}, - a2::JuMP.Containers.DenseAxisArray{Float64, 2}, -) + a1::JuMP.Containers.DenseAxisArray{T, 2}, + a2::JuMP.Containers.DenseAxisArray{U, 2}, +) where {T <: Union{Float64, JuMP.VariableRef}, U <: Union{Float64, JuMP.VariableRef}} ax1 = axes(a1) ax2 = axes(a2) if ax1[2] != ax2[2] @@ -208,6 +208,8 @@ function PSI.build_impl!(model::PSI.DecisionModel{MultiRegionProblem}) handle_initial_conditions!(model) PSI.build_model!(model) _map_containers(model) + container = PSI.get_optimization_container(model) + container.built_for_recurrent_solves = true # Might need custom implementation for this container type # serialize_metadata!(get_optimization_container(model), get_output_dir(model)) PSI.log_values(PSI.get_settings(model)) @@ -323,3 +325,42 @@ function PSI._add_feedforward_to_model( end return end + +function PSI.update_parameters!( + model::PSI.DecisionModel{MultiRegionProblem}, + decision_states::PSI.DatasetContainer{PSI.InMemoryDataset}, +) + container = PSI.get_optimization_container(model) + for (ix, subproblem) in container.subproblems + @info "Updating subproblem $ix" + PSI.cost_function_unsynch(subproblem) + for key in keys(PSI.get_parameters(subproblem)) + PSI.update_container_parameter_values!(subproblem, model, key, decision_states) + end + end + + + if !PSI.is_synchronized(model) + for subproblem in values(container.subproblems) + PSI.update_objective_function!(subproblem) + obj_func = PSI.get_objective_expression(subproblem) + PSI.set_synchronized_status(obj_func, true) + end + end + return +end + +""" +Default problem update function for most problems with no customization +""" +function PSI.update_model!(model::PSI.DecisionModel{MultiRegionProblem}, sim::PSI.Simulation) + PSI.update_model!(model, PSI.get_simulation_state(sim), PSI.get_ini_cond_chronology(sim)) + #= + if get_rebuild_model(model) + container = get_optimization_container(model) + reset_optimization_model!(container) + build_impl!(container, get_template(model), get_system(model)) + end + =# + return +end From 1d36fb5e72ac5463949d582a76693e4bbed022ee Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Thu, 4 Apr 2024 11:42:30 -0600 Subject: [PATCH 8/8] formatter --- src/problems/multi_region_problem.jl | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/problems/multi_region_problem.jl b/src/problems/multi_region_problem.jl index 9502afc..867c4e5 100644 --- a/src/problems/multi_region_problem.jl +++ b/src/problems/multi_region_problem.jl @@ -339,7 +339,6 @@ function PSI.update_parameters!( end end - if !PSI.is_synchronized(model) for subproblem in values(container.subproblems) PSI.update_objective_function!(subproblem) @@ -353,8 +352,15 @@ end """ Default problem update function for most problems with no customization """ -function PSI.update_model!(model::PSI.DecisionModel{MultiRegionProblem}, sim::PSI.Simulation) - PSI.update_model!(model, PSI.get_simulation_state(sim), PSI.get_ini_cond_chronology(sim)) +function PSI.update_model!( + model::PSI.DecisionModel{MultiRegionProblem}, + sim::PSI.Simulation, +) + PSI.update_model!( + model, + PSI.get_simulation_state(sim), + PSI.get_ini_cond_chronology(sim), + ) #= if get_rebuild_model(model) container = get_optimization_container(model)