From 1c306015973009d3c8663fd752c9eb9549f63a1f Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Tue, 9 Jan 2024 12:31:30 -0700 Subject: [PATCH 01/39] add PFS deps --- Project.toml | 2 ++ test/Project.toml | 1 + 2 files changed, 3 insertions(+) diff --git a/Project.toml b/Project.toml index 92630cf604..6077b2ca0e 100644 --- a/Project.toml +++ b/Project.toml @@ -19,6 +19,7 @@ JuMP = "4076af6c-e467-56ae-b986-b466b2749572" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" +PowerFlows = "94fada2c-fd9a-4e89-8d82-81405f5cb4f6" PowerModels = "c36e90e8-916a-50a6-bd94-075b64ef4655" PowerNetworkMatrices = "bed98974-b02a-5e2f-9fe0-a103f5c450dd" PowerSystems = "bcd98974-b02a-5e2f-9ee0-a103f5c450dd" @@ -47,6 +48,7 @@ Logging = "1" MathOptInterface = "1" PowerModels = "~0.19" PowerNetworkMatrices = "^0.9" +PowerFlows = "0.6" PowerSystems = "^3" PrettyTables = "2" ProgressMeter = "^1.5" diff --git a/test/Project.toml b/test/Project.toml index 5ac2b5fe45..99e3482775 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -15,6 +15,7 @@ JuMP = "4076af6c-e467-56ae-b986-b466b2749572" Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" Memento = "f28f55f0-a522-5efc-85c2-fe41dfb9b2d9" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" +PowerFlows = "94fada2c-fd9a-4e89-8d82-81405f5cb4f6" PowerModels = "c36e90e8-916a-50a6-bd94-075b64ef4655" PowerNetworkMatrices = "bed98974-b02a-5e2f-9fe0-a103f5c450dd" PowerSimulations = "e690365d-45e2-57bb-ac84-44ba829e73c4" From 0bcb68e284fa523e97968813dae8cae76b1a4f89 Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Tue, 9 Jan 2024 12:32:10 -0700 Subject: [PATCH 02/39] make time_steps consistent --- src/utils/jump_utils.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils/jump_utils.jl b/src/utils/jump_utils.jl index c3360c5da5..6744a8a7df 100644 --- a/src/utils/jump_utils.jl +++ b/src/utils/jump_utils.jl @@ -106,9 +106,9 @@ function _to_matrix( array::SparseAxisArray{T, N, K}, columns, ) where {T, N, K <: NTuple{N, Any}} - timesteps = Set{Int}(k[N] for k in keys(array.data)) - data = Matrix{Float64}(undef, length(timesteps), length(columns)) - for (ix, col) in enumerate(columns), t in timesteps + time_steps = Set{Int}(k[N] for k in keys(array.data)) + data = Matrix{Float64}(undef, length(time_steps), length(columns)) + for (ix, col) in enumerate(columns), t in time_steps data[t, ix] = array.data[(col..., t)] end return data From cac31f229c18c29ec3f450fac05ec9beee81c75f Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Tue, 9 Jan 2024 12:32:24 -0700 Subject: [PATCH 03/39] add PowerFlows --- src/PowerSimulations.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/PowerSimulations.jl b/src/PowerSimulations.jl index e640b788a0..739ed18b1f 100644 --- a/src/PowerSimulations.jl +++ b/src/PowerSimulations.jl @@ -340,6 +340,7 @@ import LinearAlgebra import JSON3 import PowerSystems import InfrastructureSystems +import PowerFlows import PowerNetworkMatrices import PowerNetworkMatrices: PTDF, VirtualPTDF export PTDF @@ -400,6 +401,7 @@ const MOI = MathOptInterface const MOIU = MathOptInterface.Utilities const MOPFM = MOI.FileFormats.Model const PNM = PowerNetworkMatrices +const PFS = PowerFlows const TS = TimeSeries ################################################################################ From 1b7d4152e1b8b9a1742d14977a4bf3ad719e2743 Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Tue, 9 Jan 2024 12:33:19 -0700 Subject: [PATCH 04/39] add new aux variables for power flow --- src/PowerSimulations.jl | 4 ++++ src/core/auxiliary_variables.jl | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/PowerSimulations.jl b/src/PowerSimulations.jl index 739ed18b1f..c5d183ab9d 100644 --- a/src/PowerSimulations.jl +++ b/src/PowerSimulations.jl @@ -239,6 +239,10 @@ export LowerBoundFeedForwardSlack export TimeDurationOn export TimeDurationOff export PowerOutput +export PowerFlowAngle +export PowerFlowVoltage +export PowerFlowLineReactivePower +export PowerFlowLineActivePower # Constraints export AbsoluteValueConstraint diff --git a/src/core/auxiliary_variables.jl b/src/core/auxiliary_variables.jl index 263615b30b..9845cad015 100644 --- a/src/core/auxiliary_variables.jl +++ b/src/core/auxiliary_variables.jl @@ -31,7 +31,30 @@ Auxiliary Variable for Thermal Generation Models that solve for power above min """ struct PowerOutput <: AuxVariableType end +""" +Auxiliary Variable for the bus angle results from power flow evaluation +""" +struct PowerFlowAngle <: AuxVariableType end + +""" +Auxiliary Variable for the bus voltage magnitued results from power flow evaluation +""" +struct PowerFlowVoltage <: AuxVariableType end + +""" +Auxiliary Variable for the line reactive flow from power flow evaluation +""" +struct PowerFlowLineReactivePower <: AuxVariableType end + +""" +Auxiliary Variable for the line active flow from power flow evaluation +""" +struct PowerFlowLineActivePower <: AuxVariableType end + + should_write_resulting_value(::Type{<:AuxVariableType}) = true convert_result_to_natural_units(::Type{<:AuxVariableType}) = false convert_result_to_natural_units(::Type{PowerOutput}) = true +convert_result_to_natural_units(::Type{PowerFlowLineReactivePower}) = true +convert_result_to_natural_units(::Type{PowerFlowLineActivePower}) = true From 02c52ed04ae7befc7c52718cb466306c90ac606a Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Tue, 9 Jan 2024 12:33:35 -0700 Subject: [PATCH 05/39] make time steps consistent --- src/services_models/transmission_interface.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/services_models/transmission_interface.jl b/src/services_models/transmission_interface.jl index 25c2e550eb..62cc9a5edb 100644 --- a/src/services_models/transmission_interface.jl +++ b/src/services_models/transmission_interface.jl @@ -33,13 +33,13 @@ function add_constraints!(container::OptimizationContainer, model::ServiceModel{T, ConstantMaxInterfaceFlow}, ) where {T <: PSY.TransmissionInterface} expr = get_expression(container, InterfaceTotalFlow(), T) - interfaces, timesteps = axes(expr) + interfaces, time_steps = axes(expr) constraint_container_ub = lazy_container_addition!( container, InterfaceFlowLimit(), T, interfaces, - timesteps; + time_steps; meta = "ub", ) constraint_container_lb = lazy_container_addition!( @@ -47,12 +47,12 @@ function add_constraints!(container::OptimizationContainer, InterfaceFlowLimit(), T, interfaces, - timesteps; + time_steps; meta = "lb", ) int_name = PSY.get_name(interface) min_flow, max_flow = PSY.get_active_power_flow_limits(interface) - for t in timesteps + for t in time_steps constraint_container_ub[int_name, t] = JuMP.@constraint(get_jump_model(container), expr[int_name, t] <= max_flow) constraint_container_lb[int_name, t] = From 1e71c061ba01c50a7471723d9076bfc550ced7ba Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Tue, 9 Jan 2024 12:33:43 -0700 Subject: [PATCH 06/39] fix failing test --- src/network_models/network_constructor.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/network_models/network_constructor.jl b/src/network_models/network_constructor.jl index e2d4a5d850..a44690d6d0 100644 --- a/src/network_models/network_constructor.jl +++ b/src/network_models/network_constructor.jl @@ -202,8 +202,8 @@ function construct_network!( end if get_use_slacks(model) - add_variables!(container, SystemBalanceSlackUp, sys, T) - add_variables!(container, SystemBalanceSlackDown, sys, T) + add_variables!(container, SystemBalanceSlackUp, sys, model) + add_variables!(container, SystemBalanceSlackDown, sys, model) add_to_expression!( container, ActivePowerBalance, From a94defb22b32eb7500d38f038cc5fd9cb8c280bf Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Tue, 9 Jan 2024 12:33:57 -0700 Subject: [PATCH 07/39] add power flow evaluator to model and container --- src/core/network_model.jl | 4 ++++ src/core/optimization_container.jl | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/src/core/network_model.jl b/src/core/network_model.jl index 15c0edbee6..75f5a63e95 100644 --- a/src/core/network_model.jl +++ b/src/core/network_model.jl @@ -34,6 +34,7 @@ mutable struct NetworkModel{T <: PM.AbstractPowerModel} duals::Vector{DataType} radial_branches::PNM.RadialBranches reduce_radial_branches::Bool + powerflow_evaluation::Union{Nothing, PFS.PowerFlowEvaluationModel} function NetworkModel( ::Type{T}; @@ -42,6 +43,7 @@ mutable struct NetworkModel{T <: PM.AbstractPowerModel} reduce_radial_branches = false, subnetworks = Dict{Int, Set{Int}}(), duals = Vector{DataType}(), + powerflow_evaluation = nothing ) where {T <: PM.AbstractPowerModel} _check_pm_formulation(T) new{T}( @@ -52,6 +54,7 @@ mutable struct NetworkModel{T <: PM.AbstractPowerModel} duals, PNM.RadialBranches(), reduce_radial_branches, + powerflow_evaluation ) end end @@ -66,6 +69,7 @@ get_reference_buses(m::NetworkModel{T}) where {T <: PM.AbstractPowerModel} = collect(keys(m.subnetworks)) get_subnetworks(m::NetworkModel) = m.subnetworks get_bus_area_map(m::NetworkModel) = m.bus_area_map +get_powerflow_evaluation(m::NetworkModel) = m.powerflow_evaluation has_subnetworks(m::NetworkModel) = !isempty(m.bus_area_map) function add_dual!(model::NetworkModel, dual) diff --git a/src/core/optimization_container.jl b/src/core/optimization_container.jl index 5e68f6e067..4cad1a4eb8 100644 --- a/src/core/optimization_container.jl +++ b/src/core/optimization_container.jl @@ -110,6 +110,7 @@ mutable struct OptimizationContainer <: AbstractModelContainer built_for_recurrent_solves::Bool metadata::OptimizationContainerMetadata default_time_series_type::Type{<:PSY.TimeSeriesData} + power_flow_data::Union{PFS.PowerFlowData, Nothing} end function OptimizationContainer( @@ -154,6 +155,7 @@ function OptimizationContainer( false, OptimizationContainerMetadata(), T, + nothing ) end @@ -650,6 +652,11 @@ function build_impl!( check_optimization_container(container) + powerflow_evaluation_model = get_powerflow_evaluation(transmission_model) + if !isnothing(powerflow_evaluation_model) + @info "Building PowerFlow evaluator using $(powerflow_evaluation_model)" + container.power_flow_data = PFS.PowerFlowData(powerflow_evaluation_model, sys; time_steps = length(get_time_steps(container))) + end return end From d1336c0c9d11235277748e5523ed20f0993d2630 Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Tue, 9 Jan 2024 12:48:04 -0700 Subject: [PATCH 08/39] add evaluation in initialization --- src/initial_conditions/initialization.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/initial_conditions/initialization.jl b/src/initial_conditions/initialization.jl index d866993118..fb8d6a1c1e 100644 --- a/src/initial_conditions/initialization.jl +++ b/src/initial_conditions/initialization.jl @@ -11,6 +11,8 @@ function get_initial_conditions_template(model::OperationModel) ) network_model.radial_branches = get_radial_branches(get_network_model(model.template)) network_model.subnetworks = get_subnetworks(get_network_model(model.template)) + # Initialization does not support PowerFlow evaluation + network_model.powerflow_evaluation = Nothing bus_area_map = get_bus_area_map(get_network_model(model.template)) if !isempty(bus_area_map) network_model.bus_area_map = get_bus_area_map(get_network_model(model.template)) From 7d1b0d2b8e9a1b3f9d7e7459e0294ba8fa916c44 Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Tue, 9 Jan 2024 12:48:20 -0700 Subject: [PATCH 09/39] add code for evaluation --- src/PowerSimulations.jl | 1 + src/core/optimization_container.jl | 7 +----- src/network_models/powerflow_evaluation.jl | 27 ++++++++++++++++++++++ 3 files changed, 29 insertions(+), 6 deletions(-) create mode 100644 src/network_models/powerflow_evaluation.jl diff --git a/src/PowerSimulations.jl b/src/PowerSimulations.jl index c5d183ab9d..f4fb316863 100644 --- a/src/PowerSimulations.jl +++ b/src/PowerSimulations.jl @@ -537,6 +537,7 @@ include("network_models/pm_translator.jl") include("network_models/network_slack_variables.jl") include("network_models/area_balance_model.jl") include("network_models/hvdc_networks.jl") +include("network_models/powerflow_evaluation.jl") include("initial_conditions/initialization.jl") diff --git a/src/core/optimization_container.jl b/src/core/optimization_container.jl index 4cad1a4eb8..ea5f00e5e1 100644 --- a/src/core/optimization_container.jl +++ b/src/core/optimization_container.jl @@ -650,13 +650,8 @@ function build_impl!( @debug "Total operation count $(PSI.get_jump_model(container).operator_counter)" _group = LOG_GROUP_OPTIMIZATION_CONTAINER + add_power_flow_data!(container, get_powerflow_evaluation(transmission_model)) check_optimization_container(container) - - powerflow_evaluation_model = get_powerflow_evaluation(transmission_model) - if !isnothing(powerflow_evaluation_model) - @info "Building PowerFlow evaluator using $(powerflow_evaluation_model)" - container.power_flow_data = PFS.PowerFlowData(powerflow_evaluation_model, sys; time_steps = length(get_time_steps(container))) - end return end diff --git a/src/network_models/powerflow_evaluation.jl b/src/network_models/powerflow_evaluation.jl new file mode 100644 index 0000000000..99b105dda1 --- /dev/null +++ b/src/network_models/powerflow_evaluation.jl @@ -0,0 +1,27 @@ +function add_power_flow_data!(container::OptimizationContainer, evaluator::Nothing) +# NO OP function +end + +add_aux_variable_container!( + container, + var_type, + D, + [PSY.get_name(d) for d in devices], + time_steps, +) + +function add_power_flow_data!(container::OptimizationContainer, evaluator::T) where T <: Union{PFS.PTDFDCPowerFlow, PFS.vPTDFDCPowerFlow} + @info "Building PowerFlow evaluator using $(evaluator)" + container.power_flow_data = PFS.PowerFlowData(evaluator, sys; time_steps = length(get_time_steps(container))) +end + +function add_power_flow_data!(container::OptimizationContainer, evaluator::PFS.DCPowerFlow()) + @info "Building PowerFlow evaluator using $(evaluator)" + container.power_flow_data = PFS.PowerFlowData(evaluator, sys; time_steps = length(get_time_steps(container))) +end + + +function add_power_flow_data!(container::OptimizationContainer, evaluator::PFS.ACPowerFlow()) + @info "Building PowerFlow evaluator using $(evaluator)" + container.power_flow_data = PFS.PowerFlowData(evaluator, sys; time_steps = length(get_time_steps(container))) +end From c4071f46a17be53f592fac7d3b45ecd86b05394a Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Tue, 9 Jan 2024 12:48:25 -0700 Subject: [PATCH 10/39] add missing kwarg --- test/test_network_constructors.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_network_constructors.jl b/test/test_network_constructors.jl index 0d30eb4456..7036e54d45 100644 --- a/test/test_network_constructors.jl +++ b/test/test_network_constructors.jl @@ -939,7 +939,7 @@ end NetworkModel(network; PTDF_matrix = PTDF(c_sys5), reduce_radial_branches = true, - use_slacks), + use_slacks =true), ) ps_model = DecisionModel(template, c_sys5; optimizer = solver) @test build!(ps_model; output_dir = mktempdir(; cleanup = true)) == From 61ee4b5841f2a7dd9974732715f8a705dd6a9a84 Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Tue, 9 Jan 2024 14:24:22 -0700 Subject: [PATCH 11/39] fix incorrect var type --- src/initial_conditions/initialization.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/initial_conditions/initialization.jl b/src/initial_conditions/initialization.jl index fb8d6a1c1e..a2158881f3 100644 --- a/src/initial_conditions/initialization.jl +++ b/src/initial_conditions/initialization.jl @@ -12,7 +12,7 @@ function get_initial_conditions_template(model::OperationModel) network_model.radial_branches = get_radial_branches(get_network_model(model.template)) network_model.subnetworks = get_subnetworks(get_network_model(model.template)) # Initialization does not support PowerFlow evaluation - network_model.powerflow_evaluation = Nothing + network_model.powerflow_evaluation = nothing bus_area_map = get_bus_area_map(get_network_model(model.template)) if !isempty(bus_area_map) network_model.bus_area_map = get_bus_area_map(get_network_model(model.template)) From 3b31d021ec7bd3d0dd4cbfd61fdcdb34de9cacea Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Tue, 9 Jan 2024 14:24:42 -0700 Subject: [PATCH 12/39] fix old bug --- src/network_models/network_constructor.jl | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/network_models/network_constructor.jl b/src/network_models/network_constructor.jl index a44690d6d0..7f53454439 100644 --- a/src/network_models/network_constructor.jl +++ b/src/network_models/network_constructor.jl @@ -210,7 +210,6 @@ function construct_network!( SystemBalanceSlackUp, sys, model, - T, ) add_to_expression!( container, @@ -218,7 +217,6 @@ function construct_network!( SystemBalanceSlackDown, sys, model, - T, ) add_to_expression!( container, @@ -226,7 +224,6 @@ function construct_network!( SystemBalanceSlackUp, sys, model, - T, ) add_to_expression!( container, @@ -234,7 +231,6 @@ function construct_network!( SystemBalanceSlackDown, sys, model, - T, ) objective_function!(container, PSY.ACBus, model) end From 347b67c2d9c4abf87d8a1aa3abff10e4bed81b67 Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Tue, 9 Jan 2024 14:25:02 -0700 Subject: [PATCH 13/39] update aux vars for power flow evaluation --- src/core/auxiliary_variables.jl | 1 - src/core/network_model.jl | 4 ++-- src/core/optimization_container.jl | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/core/auxiliary_variables.jl b/src/core/auxiliary_variables.jl index 9845cad015..a5b5619d14 100644 --- a/src/core/auxiliary_variables.jl +++ b/src/core/auxiliary_variables.jl @@ -51,7 +51,6 @@ Auxiliary Variable for the line active flow from power flow evaluation """ struct PowerFlowLineActivePower <: AuxVariableType end - should_write_resulting_value(::Type{<:AuxVariableType}) = true convert_result_to_natural_units(::Type{<:AuxVariableType}) = false diff --git a/src/core/network_model.jl b/src/core/network_model.jl index 75f5a63e95..bd165dc63f 100644 --- a/src/core/network_model.jl +++ b/src/core/network_model.jl @@ -43,7 +43,7 @@ mutable struct NetworkModel{T <: PM.AbstractPowerModel} reduce_radial_branches = false, subnetworks = Dict{Int, Set{Int}}(), duals = Vector{DataType}(), - powerflow_evaluation = nothing + powerflow_evaluation = nothing, ) where {T <: PM.AbstractPowerModel} _check_pm_formulation(T) new{T}( @@ -54,7 +54,7 @@ mutable struct NetworkModel{T <: PM.AbstractPowerModel} duals, PNM.RadialBranches(), reduce_radial_branches, - powerflow_evaluation + powerflow_evaluation, ) end end diff --git a/src/core/optimization_container.jl b/src/core/optimization_container.jl index ea5f00e5e1..8cb11f374b 100644 --- a/src/core/optimization_container.jl +++ b/src/core/optimization_container.jl @@ -155,7 +155,7 @@ function OptimizationContainer( false, OptimizationContainerMetadata(), T, - nothing + nothing, ) end @@ -650,7 +650,7 @@ function build_impl!( @debug "Total operation count $(PSI.get_jump_model(container).operator_counter)" _group = LOG_GROUP_OPTIMIZATION_CONTAINER - add_power_flow_data!(container, get_powerflow_evaluation(transmission_model)) + add_power_flow_data!(container, get_powerflow_evaluation(transmission_model), sys) check_optimization_container(container) return end From 5606f659d7cc877d5ec6ad4aba81d0b78afdd1ba Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Tue, 9 Jan 2024 14:25:18 -0700 Subject: [PATCH 14/39] WIP: function for the power flow evaluation --- src/network_models/powerflow_evaluation.jl | 75 +++++++++++++++++----- 1 file changed, 60 insertions(+), 15 deletions(-) diff --git a/src/network_models/powerflow_evaluation.jl b/src/network_models/powerflow_evaluation.jl index 99b105dda1..be9f45b5a7 100644 --- a/src/network_models/powerflow_evaluation.jl +++ b/src/network_models/powerflow_evaluation.jl @@ -1,27 +1,72 @@ -function add_power_flow_data!(container::OptimizationContainer, evaluator::Nothing) -# NO OP function +function add_power_flow_data!(::OptimizationContainer, ::Nothing, ::PSY.System) + # NO OP function end -add_aux_variable_container!( - container, - var_type, - D, - [PSY.get_name(d) for d in devices], - time_steps, +function _add_branches_aux_variables!( + container::OptimizationContainer, + vars::Vector{DataType}, + branch_types::Vector{DataType}, + branch_names::Vector{String}, ) + time_steps = get_time_steps(container) + for var_type in vars + for D in Set(branch_types) + add_aux_variable_container!( + container, + var_type, + D, + branch_names[branch_types == D], + time_steps, + ) + end + end + return +end -function add_power_flow_data!(container::OptimizationContainer, evaluator::T) where T <: Union{PFS.PTDFDCPowerFlow, PFS.vPTDFDCPowerFlow} - @info "Building PowerFlow evaluator using $(evaluator)" - container.power_flow_data = PFS.PowerFlowData(evaluator, sys; time_steps = length(get_time_steps(container))) +function _add_buses_aux_variables!( + container::OptimizationContainer, + vars::Vector{DataType}, + bus_names::Vector{String}, +) + time_steps = get_time_steps(container) + for var_type in vars + add_aux_variable_container!( + container, + var_type, + D, + bus_names, + time_steps, + ) + end + return end -function add_power_flow_data!(container::OptimizationContainer, evaluator::PFS.DCPowerFlow()) +function add_power_flow_data!( + container::OptimizationContainer, + evaluator::T, + sys::PSY.System, +) where {T <: Union{PFS.PTDFDCPowerFlow, PFS.vPTDFDCPowerFlow}} @info "Building PowerFlow evaluator using $(evaluator)" - container.power_flow_data = PFS.PowerFlowData(evaluator, sys; time_steps = length(get_time_steps(container))) + container.power_flow_data = + PFS.PowerFlowData(evaluator, sys; time_steps = length(get_time_steps(container))) end +function add_power_flow_data!( + container::OptimizationContainer, + evaluator::PFS.DCPowerFlow, + sys::PSY.System, +) + @info "Building PowerFlow evaluator using $(evaluator)" + container.power_flow_data = + PFS.PowerFlowData(evaluator, sys; time_steps = length(get_time_steps(container))) +end -function add_power_flow_data!(container::OptimizationContainer, evaluator::PFS.ACPowerFlow()) +function add_power_flow_data!( + container::OptimizationContainer, + evaluator::PFS.ACPowerFlow, + sys::PSY.System, +) @info "Building PowerFlow evaluator using $(evaluator)" - container.power_flow_data = PFS.PowerFlowData(evaluator, sys; time_steps = length(get_time_steps(container))) + container.power_flow_data = + PFS.PowerFlowData(evaluator, sys; time_steps = length(get_time_steps(container))) end From e8caaca0ebfaa56980e747fff46cd630fb81a24f Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Tue, 9 Jan 2024 14:25:24 -0700 Subject: [PATCH 15/39] whitespace --- test/test_network_constructors.jl | 50 +++++++++++++++---------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/test/test_network_constructors.jl b/test/test_network_constructors.jl index 7036e54d45..3f44b92848 100644 --- a/test/test_network_constructors.jl +++ b/test/test_network_constructors.jl @@ -1,26 +1,27 @@ # Note to devs. Use GLPK or Cbc for models with linear constraints and linear cost functions # Use OSQP for models with quadratic cost function and linear constraints and ipopt otherwise -const networks_for_testing = networks = [ - (PM.ACPPowerModel, fast_ipopt_optimizer), - (PM.ACRPowerModel, fast_ipopt_optimizer), - (PM.ACTPowerModel, fast_ipopt_optimizer), - #(PM.IVRPowerModel, fast_ipopt_optimizer), #instantiate_ivp_expr_model not implemented - (PM.DCPPowerModel, fast_ipopt_optimizer), - (PM.DCMPPowerModel, fast_ipopt_optimizer), - (PM.NFAPowerModel, fast_ipopt_optimizer), - (PM.DCPLLPowerModel, fast_ipopt_optimizer), - (PM.LPACCPowerModel, fast_ipopt_optimizer), - (PM.SOCWRPowerModel, fast_ipopt_optimizer), - (PM.SOCWRConicPowerModel, scs_solver), - (PM.QCRMPowerModel, fast_ipopt_optimizer), - (PM.QCLSPowerModel, fast_ipopt_optimizer), - #(PM.SOCBFPowerModel, fast_ipopt_optimizer), # not implemented - (PM.BFAPowerModel, fast_ipopt_optimizer), - #(PM.SOCBFConicPowerModel, fast_ipopt_optimizer), # not implemented - (PM.SDPWRMPowerModel, scs_solver), - (PM.SparseSDPWRMPowerModel, scs_solver), - (PTDFPowerModel, fast_ipopt_optimizer), -] +const networks_for_testing = + networks = [ + (PM.ACPPowerModel, fast_ipopt_optimizer), + (PM.ACRPowerModel, fast_ipopt_optimizer), + (PM.ACTPowerModel, fast_ipopt_optimizer), + #(PM.IVRPowerModel, fast_ipopt_optimizer), #instantiate_ivp_expr_model not implemented + (PM.DCPPowerModel, fast_ipopt_optimizer), + (PM.DCMPPowerModel, fast_ipopt_optimizer), + (PM.NFAPowerModel, fast_ipopt_optimizer), + (PM.DCPLLPowerModel, fast_ipopt_optimizer), + (PM.LPACCPowerModel, fast_ipopt_optimizer), + (PM.SOCWRPowerModel, fast_ipopt_optimizer), + (PM.SOCWRConicPowerModel, scs_solver), + (PM.QCRMPowerModel, fast_ipopt_optimizer), + (PM.QCLSPowerModel, fast_ipopt_optimizer), + #(PM.SOCBFPowerModel, fast_ipopt_optimizer), # not implemented + (PM.BFAPowerModel, fast_ipopt_optimizer), + #(PM.SOCBFConicPowerModel, fast_ipopt_optimizer), # not implemented + (PM.SDPWRMPowerModel, scs_solver), + (PM.SparseSDPWRMPowerModel, scs_solver), + (PTDFPowerModel, fast_ipopt_optimizer), + ] @testset "All PowerModels models construction" begin c_sys5 = PSB.build_system(PSITestSystems, "c_sys5") @@ -871,7 +872,6 @@ end end @testset "DCPPowerModel Radial Branches Test" begin - net_model = DCPPowerModel template_uc = template_unit_commitment(; @@ -937,9 +937,9 @@ end for (network, solver) in networks_for_testing template = get_thermal_dispatch_template_network( NetworkModel(network; - PTDF_matrix = PTDF(c_sys5), - reduce_radial_branches = true, - use_slacks =true), + PTDF_matrix = PTDF(c_sys5), + reduce_radial_branches = true, + use_slacks = true), ) ps_model = DecisionModel(template, c_sys5; optimizer = solver) @test build!(ps_model; output_dir = mktempdir(; cleanup = true)) == From bf872cad2b74a9284bb98ad4549666d606179457 Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Tue, 23 Jan 2024 12:28:36 -0700 Subject: [PATCH 16/39] bump deps --- Project.toml | 4 ++-- test/Project.toml | 4 ---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/Project.toml b/Project.toml index 54cbbc0e2a..14de3d3406 100644 --- a/Project.toml +++ b/Project.toml @@ -46,9 +46,9 @@ JuMP = "1" LinearAlgebra = "1" Logging = "1" MathOptInterface = "1" -PowerModels = "^0.20" +PowerModels = "^0.21" PowerNetworkMatrices = "^0.10" -PowerFlows = "0.6" +PowerFlows = "0.7" PowerSystems = "^3" PrettyTables = "2" ProgressMeter = "^1.5" diff --git a/test/Project.toml b/test/Project.toml index 99e3482775..25e3ad5c2a 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -7,7 +7,6 @@ DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" GLPK = "60bf3e95-4087-53dc-ae20-288a0d20c6a6" HiGHS = "87dc4568-4c63-4d18-b0c0-bb2238e4078b" -HydroPowerSimulations = "fc1677e0-6ad7-4515-bf3a-bd6bf20a0b1b" InfrastructureSystems = "2cd47ed4-ca9b-11e9-27f2-ab636a7671f1" Ipopt = "b6b21f68-93f8-5de0-b562-5493be1d77c9" JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" @@ -19,13 +18,11 @@ PowerFlows = "94fada2c-fd9a-4e89-8d82-81405f5cb4f6" PowerModels = "c36e90e8-916a-50a6-bd94-075b64ef4655" PowerNetworkMatrices = "bed98974-b02a-5e2f-9fe0-a103f5c450dd" PowerSimulations = "e690365d-45e2-57bb-ac84-44ba829e73c4" -PowerSystemCaseBuilder = "f00506e0-b84f-492a-93c2-c0a9afc4364e" PowerSystems = "bcd98974-b02a-5e2f-9ee0-a103f5c450dd" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" SCS = "c946c3f1-0d1f-5ce8-9dea-7daa1f7e2d13" Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b" -StorageSystemsSimulations = "e2f1a126-19d0-4674-9252-42b2384f8e3c" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" TestSetExtensions = "98d24dd4-01ad-11ea-1b02-c9a08f80db04" TimeSeries = "9e3dc215-6440-5c97-bce1-76c03772f85e" @@ -35,5 +32,4 @@ UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" [compat] HiGHS = "=1.1.2" Ipopt = "=1.4.0" -PowerSystemCaseBuilder = "^1.2.0" julia = "^1.6" From 4e0f8eee9548fd6f450523b291cd0c38be58dc87 Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Tue, 23 Jan 2024 15:39:05 -0700 Subject: [PATCH 17/39] change to varnames --- src/PowerSimulations.jl | 4 ++-- src/core/auxiliary_variables.jl | 4 ++-- test/Project.toml | 3 +++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/PowerSimulations.jl b/src/PowerSimulations.jl index 36787698c3..23aba5e140 100644 --- a/src/PowerSimulations.jl +++ b/src/PowerSimulations.jl @@ -238,8 +238,8 @@ export LowerBoundFeedForwardSlack export TimeDurationOn export TimeDurationOff export PowerOutput -export PowerFlowAngle -export PowerFlowVoltage +export PowerFlowVoltageAngle +export PowerFlowVoltageMagnitude export PowerFlowLineReactivePower export PowerFlowLineActivePower diff --git a/src/core/auxiliary_variables.jl b/src/core/auxiliary_variables.jl index a5b5619d14..c8c37345dc 100644 --- a/src/core/auxiliary_variables.jl +++ b/src/core/auxiliary_variables.jl @@ -34,12 +34,12 @@ struct PowerOutput <: AuxVariableType end """ Auxiliary Variable for the bus angle results from power flow evaluation """ -struct PowerFlowAngle <: AuxVariableType end +struct PowerFlowVoltageAngle <: AuxVariableType end """ Auxiliary Variable for the bus voltage magnitued results from power flow evaluation """ -struct PowerFlowVoltage <: AuxVariableType end +struct PowerFlowVoltageMagnitude <: AuxVariableType end """ Auxiliary Variable for the line reactive flow from power flow evaluation diff --git a/test/Project.toml b/test/Project.toml index 25e3ad5c2a..61512a1a39 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -7,6 +7,7 @@ DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" GLPK = "60bf3e95-4087-53dc-ae20-288a0d20c6a6" HiGHS = "87dc4568-4c63-4d18-b0c0-bb2238e4078b" +HydroPowerSimulations = "fc1677e0-6ad7-4515-bf3a-bd6bf20a0b1b" InfrastructureSystems = "2cd47ed4-ca9b-11e9-27f2-ab636a7671f1" Ipopt = "b6b21f68-93f8-5de0-b562-5493be1d77c9" JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" @@ -18,11 +19,13 @@ PowerFlows = "94fada2c-fd9a-4e89-8d82-81405f5cb4f6" PowerModels = "c36e90e8-916a-50a6-bd94-075b64ef4655" PowerNetworkMatrices = "bed98974-b02a-5e2f-9fe0-a103f5c450dd" PowerSimulations = "e690365d-45e2-57bb-ac84-44ba829e73c4" +PowerSystemCaseBuilder = "f00506e0-b84f-492a-93c2-c0a9afc4364e" PowerSystems = "bcd98974-b02a-5e2f-9ee0-a103f5c450dd" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" SCS = "c946c3f1-0d1f-5ce8-9dea-7daa1f7e2d13" Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b" +StorageSystemsSimulations = "e2f1a126-19d0-4674-9252-42b2384f8e3c" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" TestSetExtensions = "98d24dd4-01ad-11ea-1b02-c9a08f80db04" TimeSeries = "9e3dc215-6440-5c97-bce1-76c03772f85e" From 7a2555a65b1d08901a1e745892f4af1e92621e2e Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Tue, 23 Jan 2024 15:39:20 -0700 Subject: [PATCH 18/39] add auxiliary variables --- src/core/optimization_container.jl | 5 ++ src/network_models/powerflow_evaluation.jl | 76 ++++++++++++++++++---- 2 files changed, 68 insertions(+), 13 deletions(-) diff --git a/src/core/optimization_container.jl b/src/core/optimization_container.jl index a307ed4ab1..bedeb47176 100644 --- a/src/core/optimization_container.jl +++ b/src/core/optimization_container.jl @@ -193,6 +193,7 @@ get_jump_model(container::OptimizationContainer) = container.JuMPmodel get_metadata(container::OptimizationContainer) = container.metadata get_optimizer_stats(container::OptimizationContainer) = container.optimizer_stats get_parameters(container::OptimizationContainer) = container.parameters +get_power_flow_data(container::OptimizationContainer) = container.power_flow_data get_resolution(container::OptimizationContainer) = container.resolution get_settings(container::OptimizationContainer) = container.settings get_time_steps(container::OptimizationContainer) = container.time_steps @@ -1505,6 +1506,10 @@ function deserialize_key(container::OptimizationContainer, name::AbstractString) end function calculate_aux_variables!(container::OptimizationContainer, system::PSY.System) + if !isnothing(get_power_flow_data(container)) + solve_powerflow!(container, system) + end + aux_vars = get_aux_variables(container) for key in keys(aux_vars) calculate_aux_variable_value!(container, key, system) diff --git a/src/network_models/powerflow_evaluation.jl b/src/network_models/powerflow_evaluation.jl index be9f45b5a7..28cbd10f37 100644 --- a/src/network_models/powerflow_evaluation.jl +++ b/src/network_models/powerflow_evaluation.jl @@ -1,21 +1,24 @@ function add_power_flow_data!(::OptimizationContainer, ::Nothing, ::PSY.System) # NO OP function + return end function _add_branches_aux_variables!( container::OptimizationContainer, vars::Vector{DataType}, branch_types::Vector{DataType}, - branch_names::Vector{String}, + branch_lookup::Dict{String, Int}, ) + branch_type_map = Dict{String, DataType}(k => branch_types[v] for (k, v) in branch_lookup) time_steps = get_time_steps(container) for var_type in vars for D in Set(branch_types) + branch_names = [k for (k, v) in branch_type_map if v == D] add_aux_variable_container!( container, - var_type, + var_type(), D, - branch_names[branch_types == D], + branch_names, time_steps, ) end @@ -26,15 +29,15 @@ end function _add_buses_aux_variables!( container::OptimizationContainer, vars::Vector{DataType}, - bus_names::Vector{String}, + bus_lookup::Dict{Int, Int}, ) time_steps = get_time_steps(container) for var_type in vars add_aux_variable_container!( container, - var_type, - D, - bus_names, + var_type(), + PSY.ACBus, + sort!(collect(keys(bus_lookup))), time_steps, ) end @@ -47,8 +50,11 @@ function add_power_flow_data!( sys::PSY.System, ) where {T <: Union{PFS.PTDFDCPowerFlow, PFS.vPTDFDCPowerFlow}} @info "Building PowerFlow evaluator using $(evaluator)" - container.power_flow_data = - PFS.PowerFlowData(evaluator, sys; time_steps = length(get_time_steps(container))) + pf_data = PFS.PowerFlowData(evaluator, sys; time_steps = length(get_time_steps(container))) + container.power_flow_data = pf_data + branch_aux_vars = [PowerFlowLineActivePower] + _add_branches_aux_variables!(container, branch_aux_vars, PFS.get_branch_type(pf_data), PFS.get_branch_lookup(pf_data)) + return end function add_power_flow_data!( @@ -57,8 +63,13 @@ function add_power_flow_data!( sys::PSY.System, ) @info "Building PowerFlow evaluator using $(evaluator)" - container.power_flow_data = - PFS.PowerFlowData(evaluator, sys; time_steps = length(get_time_steps(container))) + pf_data = PFS.PowerFlowData(evaluator, sys; time_steps = length(get_time_steps(container))) + container.power_flow_data = pf_data + branch_aux_vars = [PowerFlowLineActivePower] + _add_branches_aux_variables!(container, branch_aux_vars, PFS.get_branch_type(pf_data), PFS.get_branch_lookup(pf_data)) + bus_aux_vars = [PowerFlowVoltageAngle] + _add_buses_aux_variables!(container, bus_aux_vars, PFS.get_bus_lookup(pf_data)) + return end function add_power_flow_data!( @@ -67,6 +78,45 @@ function add_power_flow_data!( sys::PSY.System, ) @info "Building PowerFlow evaluator using $(evaluator)" - container.power_flow_data = - PFS.PowerFlowData(evaluator, sys; time_steps = length(get_time_steps(container))) + pf_data = PFS.PowerFlowData(evaluator, sys; time_steps = length(get_time_steps(container))) + container.power_flow_data = pf_data + branch_aux_vars = [PowerFlowLineActivePower, PowerFlowLineReactivePower] + _add_branches_aux_variables!(container, branch_aux_vars, PFS.get_branch_type(pf_data), PFS.get_branch_lookup(pf_data)) + bus_aux_vars = [PowerFlowVoltageAngle, PowerFlowVoltageMagnitude] + _add_buses_aux_variables!(container, bus_aux_vars, PFS.get_bus_lookup(pf_data)) + return +end + +function make_injection_map(pf_data, container, sys) + + for var in get_variables(container) + + end + + for param in get_parameters(container) + + end + + for comp in PSY.get_componets(PSY.get_available, StaticInjection, sys) + + end +end + + +function update_pf_data!(pf_data, container::OptimizationContainer) + +end + +function solve_powerflow!( + container::OptimizationContainer, + system::PSY.System) + pf_data = get_pf_data(container) + update_pf_data!(pf_data, container) + PFS.solve_powerflow!(pf_data) +end + +function calculate_aux_variable_value!(container::OptimizationContainer, + key, + system::PSY.System) + return end From d95ef9ec2b3bc6c73d31877fd670d41ab37e20b0 Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Tue, 23 Jan 2024 19:46:57 -0700 Subject: [PATCH 19/39] add wrapper for power flow data --- src/PowerSimulations.jl | 1 + src/core/optimization_container.jl | 2 +- src/core/powerflow_data_wrapper.jl | 4 ++++ 3 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 src/core/powerflow_data_wrapper.jl diff --git a/src/PowerSimulations.jl b/src/PowerSimulations.jl index 23aba5e140..a4175a847a 100644 --- a/src/PowerSimulations.jl +++ b/src/PowerSimulations.jl @@ -450,6 +450,7 @@ include("core/results_by_time.jl") # Order Required include("operation/problem_template.jl") +include("core/powerflow_data_wrapper.jl") include("core/optimization_container.jl") include("core/store_common.jl") include("initial_conditions/initial_condition_chronologies.jl") diff --git a/src/core/optimization_container.jl b/src/core/optimization_container.jl index bedeb47176..21ef224242 100644 --- a/src/core/optimization_container.jl +++ b/src/core/optimization_container.jl @@ -110,7 +110,7 @@ mutable struct OptimizationContainer <: AbstractModelContainer built_for_recurrent_solves::Bool metadata::OptimizationContainerMetadata default_time_series_type::Type{<:PSY.TimeSeriesData} - power_flow_data::Union{PFS.PowerFlowData, Nothing} + power_flow_data::Union{<:PowerFlowEvaluationData, Nothing} end function OptimizationContainer( diff --git a/src/core/powerflow_data_wrapper.jl b/src/core/powerflow_data_wrapper.jl new file mode 100644 index 0000000000..7da5b44969 --- /dev/null +++ b/src/core/powerflow_data_wrapper.jl @@ -0,0 +1,4 @@ +mutable struct PowerFlowEvaluationData{T <: PFS.PowerFlowData} + power_flow_data::T + injection_key_map::Dict{<:OptimizationContainerKey, Dict{String, Int}} +end From d2b26acfced9766cf0f9f4992d3c85962f95fc95 Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Tue, 23 Jan 2024 22:43:09 -0700 Subject: [PATCH 20/39] use wrapper --- src/core/parameters.jl | 1 + src/core/powerflow_data_wrapper.jl | 4 ++ src/network_models/powerflow_evaluation.jl | 76 ++++++++++++++++++++-- 3 files changed, 75 insertions(+), 6 deletions(-) diff --git a/src/core/parameters.jl b/src/core/parameters.jl index d14c113c26..163546f853 100644 --- a/src/core/parameters.jl +++ b/src/core/parameters.jl @@ -64,6 +64,7 @@ function add_component_name!(attr::TimeSeriesAttributes, name::String, uuid::Str return end +get_component_names(attr::TimeSeriesAttributes) = keys(attr.component_name_to_ts_uuid) _get_ts_uuid(attr::TimeSeriesAttributes, name) = attr.component_name_to_ts_uuid[name] struct VariableValueAttributes{T <: OptimizationContainerKey} <: ParameterAttributes diff --git a/src/core/powerflow_data_wrapper.jl b/src/core/powerflow_data_wrapper.jl index 7da5b44969..cd01153d17 100644 --- a/src/core/powerflow_data_wrapper.jl +++ b/src/core/powerflow_data_wrapper.jl @@ -2,3 +2,7 @@ mutable struct PowerFlowEvaluationData{T <: PFS.PowerFlowData} power_flow_data::T injection_key_map::Dict{<:OptimizationContainerKey, Dict{String, Int}} end + +function PowerFlowEvaluationData(power_flow_data::T) where {T <: PFS.PowerFlowData} + return PowerFlowEvaluationData{T}(power_flow_data, Dict{OptimizationContainerKey, Dict{String, Int}}()) +end diff --git a/src/network_models/powerflow_evaluation.jl b/src/network_models/powerflow_evaluation.jl index 28cbd10f37..8231eecb90 100644 --- a/src/network_models/powerflow_evaluation.jl +++ b/src/network_models/powerflow_evaluation.jl @@ -64,7 +64,7 @@ function add_power_flow_data!( ) @info "Building PowerFlow evaluator using $(evaluator)" pf_data = PFS.PowerFlowData(evaluator, sys; time_steps = length(get_time_steps(container))) - container.power_flow_data = pf_data + container.power_flow_data = PowerFlowEvaluationData(pf_data) branch_aux_vars = [PowerFlowLineActivePower] _add_branches_aux_variables!(container, branch_aux_vars, PFS.get_branch_type(pf_data), PFS.get_branch_lookup(pf_data)) bus_aux_vars = [PowerFlowVoltageAngle] @@ -87,19 +87,83 @@ function add_power_flow_data!( return end -function make_injection_map(pf_data, container, sys) +injection_keys =[ + ActivePowerVariable + PowerOutput + ActivePowerTimeSeriesParameter +] + +function make_injection_map(container::OptimizationContainer, sys::PSY.System) + pf_data = get_power_flow_data(container) + # Maps the StaticInjection component type by name to the + # index in the PowerFlow data arrays going from Bus number to bus index + temp_component_bus_map = Dict{DataType, Dict{String, Int}}() + available_injectors = PSY.get_components(PSY.get_available, PSY.StaticInjection, sys) + sizehint!(temp_component_bus_map, length(available_injectors)) + bus_lookup = PFS.get_bus_lookup(pf_data) + for comp ∈ available_injectors + comp_type = typeof(comp) + bus_dict = get!(temp_component_bus_map, comp_type, Dict{String, Int}()) + bus_number = PSY.get_number(PSY.get_bus(comp)) + bus_dict[get_name(comp)] = bus_lookup[bus_number] + end - for var in get_variables(container) + # Second map that persists to store the bus index that the variable + # has to be added/substracted to in the power flow data dictionary + pf_data_opt_container_map = Dict{<:OptimizationContainerKey, Dict{String, Int}}() + added_injection_types = DataType[] + for (key, array) in get_variables(container) + if get_entry_type(key) ∉ injection_keys + continue + end + name_bus_ix_map = Dict{String, Int}() + comp_type = get_component_type(key) + push!(added_injection_types, comp_type) + for n in axes(array)[1] + name_bus_ix_map[n] = temp_component_bus_map[comp_type][n] + end + + pf_data_opt_container_map[key] = name_bus_ix_map end - for param in get_parameters(container) + for (key, array) in get_aux_variables(container) + if get_entry_type(key) ∉ injection_keys + continue + end + # Skip aux variable if the device was added as a variable + if comp_type ∈ added_injection_types + continue + end + name_bus_ix_map = Dict{String, Int}() + comp_type = get_component_type(key) + push!(added_injection_types, comp_type) + for n in axes(array)[1] + name_bus_ix_map[n] = temp_component_bus_map[comp_type][n] + end + + pf_data_opt_container_map[key] = name_bus_ix_map + end + for (key, param_container) in get_parameters(container) + if get_entry_type(key) ∉ injection_keys + continue + end + comp_type = get_component_type(key) + # Skip parameter if the device was added as a variable + if comp_type ∈ added_injection_types + continue + end + param_attributes = get_attributes(param_container) + name_bus_ix_map = Dict{String, Int}() + for n in get_component_names(param_attributes) + name_bus_ix_map[n] = temp_component_bus_map[comp_type][n] + end + pf_data_opt_container_map[key] = name_bus_ix_map end - for comp in PSY.get_componets(PSY.get_available, StaticInjection, sys) - end + return pf_data_opt_container_map end From ddb92e3f8af79c604537976ee67ec50c953e9f79 Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Tue, 23 Jan 2024 22:57:13 -0700 Subject: [PATCH 21/39] add powerflow wrapper --- src/core/optimization_container.jl | 5 +- src/core/powerflow_data_wrapper.jl | 7 +- src/network_models/powerflow_evaluation.jl | 133 ++++++++++++--------- test/test_network_constructors.jl | 1 - 4 files changed, 86 insertions(+), 60 deletions(-) diff --git a/src/core/optimization_container.jl b/src/core/optimization_container.jl index 21ef224242..99f18a723f 100644 --- a/src/core/optimization_container.jl +++ b/src/core/optimization_container.jl @@ -110,7 +110,7 @@ mutable struct OptimizationContainer <: AbstractModelContainer built_for_recurrent_solves::Bool metadata::OptimizationContainerMetadata default_time_series_type::Type{<:PSY.TimeSeriesData} - power_flow_data::Union{<:PowerFlowEvaluationData, Nothing} + power_flow_evaluation_data::Union{<:PowerFlowEvaluationData, Nothing} end function OptimizationContainer( @@ -193,7 +193,8 @@ get_jump_model(container::OptimizationContainer) = container.JuMPmodel get_metadata(container::OptimizationContainer) = container.metadata get_optimizer_stats(container::OptimizationContainer) = container.optimizer_stats get_parameters(container::OptimizationContainer) = container.parameters -get_power_flow_data(container::OptimizationContainer) = container.power_flow_data +get_power_flow_evaluation_data(container::OptimizationContainer) = + container.power_flow_evaluation_data get_resolution(container::OptimizationContainer) = container.resolution get_settings(container::OptimizationContainer) = container.settings get_time_steps(container::OptimizationContainer) = container.time_steps diff --git a/src/core/powerflow_data_wrapper.jl b/src/core/powerflow_data_wrapper.jl index cd01153d17..254c6a125c 100644 --- a/src/core/powerflow_data_wrapper.jl +++ b/src/core/powerflow_data_wrapper.jl @@ -4,5 +4,10 @@ mutable struct PowerFlowEvaluationData{T <: PFS.PowerFlowData} end function PowerFlowEvaluationData(power_flow_data::T) where {T <: PFS.PowerFlowData} - return PowerFlowEvaluationData{T}(power_flow_data, Dict{OptimizationContainerKey, Dict{String, Int}}()) + return PowerFlowEvaluationData{T}( + power_flow_data, + Dict{OptimizationContainerKey, Dict{String, Int}}(), + ) end + +get_power_flow_data(ped::PowerFlowEvaluationData) = ped.power_flow_data diff --git a/src/network_models/powerflow_evaluation.jl b/src/network_models/powerflow_evaluation.jl index 8231eecb90..44a5a01641 100644 --- a/src/network_models/powerflow_evaluation.jl +++ b/src/network_models/powerflow_evaluation.jl @@ -9,7 +9,8 @@ function _add_branches_aux_variables!( branch_types::Vector{DataType}, branch_lookup::Dict{String, Int}, ) - branch_type_map = Dict{String, DataType}(k => branch_types[v] for (k, v) in branch_lookup) + branch_type_map = + Dict{String, DataType}(k => branch_types[v] for (k, v) in branch_lookup) time_steps = get_time_steps(container) for var_type in vars for D in Set(branch_types) @@ -44,64 +45,22 @@ function _add_buses_aux_variables!( return end -function add_power_flow_data!( - container::OptimizationContainer, - evaluator::T, - sys::PSY.System, -) where {T <: Union{PFS.PTDFDCPowerFlow, PFS.vPTDFDCPowerFlow}} - @info "Building PowerFlow evaluator using $(evaluator)" - pf_data = PFS.PowerFlowData(evaluator, sys; time_steps = length(get_time_steps(container))) - container.power_flow_data = pf_data - branch_aux_vars = [PowerFlowLineActivePower] - _add_branches_aux_variables!(container, branch_aux_vars, PFS.get_branch_type(pf_data), PFS.get_branch_lookup(pf_data)) - return -end - -function add_power_flow_data!( - container::OptimizationContainer, - evaluator::PFS.DCPowerFlow, - sys::PSY.System, -) - @info "Building PowerFlow evaluator using $(evaluator)" - pf_data = PFS.PowerFlowData(evaluator, sys; time_steps = length(get_time_steps(container))) - container.power_flow_data = PowerFlowEvaluationData(pf_data) - branch_aux_vars = [PowerFlowLineActivePower] - _add_branches_aux_variables!(container, branch_aux_vars, PFS.get_branch_type(pf_data), PFS.get_branch_lookup(pf_data)) - bus_aux_vars = [PowerFlowVoltageAngle] - _add_buses_aux_variables!(container, bus_aux_vars, PFS.get_bus_lookup(pf_data)) - return -end - -function add_power_flow_data!( - container::OptimizationContainer, - evaluator::PFS.ACPowerFlow, - sys::PSY.System, -) - @info "Building PowerFlow evaluator using $(evaluator)" - pf_data = PFS.PowerFlowData(evaluator, sys; time_steps = length(get_time_steps(container))) - container.power_flow_data = pf_data - branch_aux_vars = [PowerFlowLineActivePower, PowerFlowLineReactivePower] - _add_branches_aux_variables!(container, branch_aux_vars, PFS.get_branch_type(pf_data), PFS.get_branch_lookup(pf_data)) - bus_aux_vars = [PowerFlowVoltageAngle, PowerFlowVoltageMagnitude] - _add_buses_aux_variables!(container, bus_aux_vars, PFS.get_bus_lookup(pf_data)) - return -end - -injection_keys =[ +const ACTIVE_POWER_INJECTION_KEYS = [ ActivePowerVariable PowerOutput ActivePowerTimeSeriesParameter ] -function make_injection_map(container::OptimizationContainer, sys::PSY.System) - pf_data = get_power_flow_data(container) +function _make_injection_map!(container::OptimizationContainer, sys::PSY.System) + pf_e_data = get_power_flow_evaluation_data(container) + pf_data = get_power_flow_data(pf_e_data) # Maps the StaticInjection component type by name to the # index in the PowerFlow data arrays going from Bus number to bus index temp_component_bus_map = Dict{DataType, Dict{String, Int}}() available_injectors = PSY.get_components(PSY.get_available, PSY.StaticInjection, sys) sizehint!(temp_component_bus_map, length(available_injectors)) bus_lookup = PFS.get_bus_lookup(pf_data) - for comp ∈ available_injectors + for comp in available_injectors comp_type = typeof(comp) bus_dict = get!(temp_component_bus_map, comp_type, Dict{String, Int}()) bus_number = PSY.get_number(PSY.get_bus(comp)) @@ -110,11 +69,11 @@ function make_injection_map(container::OptimizationContainer, sys::PSY.System) # Second map that persists to store the bus index that the variable # has to be added/substracted to in the power flow data dictionary - pf_data_opt_container_map = Dict{<:OptimizationContainerKey, Dict{String, Int}}() + pf_data_opt_container_map = Dict{OptimizationContainerKey, Dict{String, Int}}() added_injection_types = DataType[] for (key, array) in get_variables(container) - if get_entry_type(key) ∉ injection_keys - continue + if get_entry_type(key) ∉ ACTIVE_POWER_INJECTION_KEYS + continue end name_bus_ix_map = Dict{String, Int}() @@ -128,8 +87,8 @@ function make_injection_map(container::OptimizationContainer, sys::PSY.System) end for (key, array) in get_aux_variables(container) - if get_entry_type(key) ∉ injection_keys - continue + if get_entry_type(key) ∉ ACTIVE_POWER_INJECTION_KEYS + continue end # Skip aux variable if the device was added as a variable if comp_type ∈ added_injection_types @@ -146,7 +105,7 @@ function make_injection_map(container::OptimizationContainer, sys::PSY.System) end for (key, param_container) in get_parameters(container) - if get_entry_type(key) ∉ injection_keys + if get_entry_type(key) ∉ ACTIVE_POWER_INJECTION_KEYS continue end comp_type = get_component_type(key) @@ -162,13 +121,75 @@ function make_injection_map(container::OptimizationContainer, sys::PSY.System) pf_data_opt_container_map[key] = name_bus_ix_map end + pf_e_data.injection_key_map = pf_data_opt_container_map + return +end - return pf_data_opt_container_map +function add_power_flow_data!( + container::OptimizationContainer, + evaluator::T, + sys::PSY.System, +) where {T <: Union{PFS.PTDFDCPowerFlow, PFS.vPTDFDCPowerFlow}} + @info "Building PowerFlow evaluator using $(evaluator)" + pf_data = + PFS.PowerFlowData(evaluator, sys; time_steps = length(get_time_steps(container))) + container.power_flow_evaluation_data = PowerFlowEvaluationData(pf_data) + branch_aux_vars = [PowerFlowLineActivePower] + _add_branches_aux_variables!( + container, + branch_aux_vars, + PFS.get_branch_type(pf_data), + PFS.get_branch_lookup(pf_data), + ) + _make_injection_map!(container, sys) + return end +function add_power_flow_data!( + container::OptimizationContainer, + evaluator::PFS.DCPowerFlow, + sys::PSY.System, +) + @info "Building PowerFlow evaluator using $(evaluator)" + pf_data = + PFS.PowerFlowData(evaluator, sys; time_steps = length(get_time_steps(container))) + container.power_flow_evaluation_data = PowerFlowEvaluationData(pf_data) + branch_aux_vars = [PowerFlowLineActivePower] + _add_branches_aux_variables!( + container, + branch_aux_vars, + PFS.get_branch_type(pf_data), + PFS.get_branch_lookup(pf_data), + ) + bus_aux_vars = [PowerFlowVoltageAngle] + _add_buses_aux_variables!(container, bus_aux_vars, PFS.get_bus_lookup(pf_data)) + _make_injection_map!(container, sys) + return +end -function update_pf_data!(pf_data, container::OptimizationContainer) +function add_power_flow_data!( + container::OptimizationContainer, + evaluator::PFS.ACPowerFlow, + sys::PSY.System, +) + @info "Building PowerFlow evaluator using $(evaluator)" + pf_data = + PFS.PowerFlowData(evaluator, sys; time_steps = length(get_time_steps(container))) + container.power_flow_evaluation_data = PowerFlowEvaluationData(pf_data) + branch_aux_vars = [PowerFlowLineActivePower, PowerFlowLineReactivePower] + _add_branches_aux_variables!( + container, + branch_aux_vars, + PFS.get_branch_type(pf_data), + PFS.get_branch_lookup(pf_data), + ) + bus_aux_vars = [PowerFlowVoltageAngle, PowerFlowVoltageMagnitude] + _add_buses_aux_variables!(container, bus_aux_vars, PFS.get_bus_lookup(pf_data)) + _make_injection_map!(container, sys) + return +end +function update_pf_data!(pf_data, container::OptimizationContainer) end function solve_powerflow!( diff --git a/test/test_network_constructors.jl b/test/test_network_constructors.jl index 817fe1381f..b82858a178 100644 --- a/test/test_network_constructors.jl +++ b/test/test_network_constructors.jl @@ -21,7 +21,6 @@ const NETWORKS_FOR_TESTING = [ (PM.SparseSDPWRMPowerModel, scs_solver), ] - @testset "All PowerModels models construction" begin c_sys5 = PSB.build_system(PSITestSystems, "c_sys5") for (network, solver) in NETWORKS_FOR_TESTING From 33bd9773c1531d1fd635bf482df980ad3d4c83ff Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Wed, 24 Jan 2024 08:42:37 -0700 Subject: [PATCH 22/39] power flow rename --- src/PowerSimulations.jl | 4 ++-- src/core/network_model.jl | 8 ++++---- src/core/optimization_container.jl | 4 ++-- src/initial_conditions/initialization.jl | 2 +- .../{powerflow_evaluation.jl => power_flow_evaluation.jl} | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) rename src/network_models/{powerflow_evaluation.jl => power_flow_evaluation.jl} (99%) diff --git a/src/PowerSimulations.jl b/src/PowerSimulations.jl index a4175a847a..21a2f361c4 100644 --- a/src/PowerSimulations.jl +++ b/src/PowerSimulations.jl @@ -450,7 +450,7 @@ include("core/results_by_time.jl") # Order Required include("operation/problem_template.jl") -include("core/powerflow_data_wrapper.jl") +include("core/power_flow_data_wrapper.jl") include("core/optimization_container.jl") include("core/store_common.jl") include("initial_conditions/initial_condition_chronologies.jl") @@ -537,7 +537,7 @@ include("network_models/pm_translator.jl") include("network_models/network_slack_variables.jl") include("network_models/area_balance_model.jl") include("network_models/hvdc_networks.jl") -include("network_models/powerflow_evaluation.jl") +include("network_models/power_flow_evaluation.jl") include("initial_conditions/initialization.jl") diff --git a/src/core/network_model.jl b/src/core/network_model.jl index 6dddb1281e..d301b4e4c2 100644 --- a/src/core/network_model.jl +++ b/src/core/network_model.jl @@ -34,7 +34,7 @@ mutable struct NetworkModel{T <: PM.AbstractPowerModel} duals::Vector{DataType} radial_network_reduction::PNM.RadialNetworkReduction reduce_radial_branches::Bool - powerflow_evaluation::Union{Nothing, PFS.PowerFlowEvaluationModel} + power_flow_evaluation::Union{Nothing, PFS.PowerFlowEvaluationModel} function NetworkModel( ::Type{T}; @@ -43,7 +43,7 @@ mutable struct NetworkModel{T <: PM.AbstractPowerModel} reduce_radial_branches = false, subnetworks = Dict{Int, Set{Int}}(), duals = Vector{DataType}(), - powerflow_evaluation = nothing, + power_flow_evaluation = nothing, ) where {T <: PM.AbstractPowerModel} _check_pm_formulation(T) new{T}( @@ -54,7 +54,7 @@ mutable struct NetworkModel{T <: PM.AbstractPowerModel} duals, PNM.RadialNetworkReduction(), reduce_radial_branches, - powerflow_evaluation, + power_flow_evaluation, ) end end @@ -69,7 +69,7 @@ get_reference_buses(m::NetworkModel{T}) where {T <: PM.AbstractPowerModel} = collect(keys(m.subnetworks)) get_subnetworks(m::NetworkModel) = m.subnetworks get_bus_area_map(m::NetworkModel) = m.bus_area_map -get_powerflow_evaluation(m::NetworkModel) = m.powerflow_evaluation +get_power_flow_evaluation(m::NetworkModel) = m.power_flow_evaluation has_subnetworks(m::NetworkModel) = !isempty(m.bus_area_map) function add_dual!(model::NetworkModel, dual) diff --git a/src/core/optimization_container.jl b/src/core/optimization_container.jl index 99f18a723f..e748e6d14f 100644 --- a/src/core/optimization_container.jl +++ b/src/core/optimization_container.jl @@ -654,7 +654,7 @@ function build_impl!( @debug "Total operation count $(PSI.get_jump_model(container).operator_counter)" _group = LOG_GROUP_OPTIMIZATION_CONTAINER - add_power_flow_data!(container, get_powerflow_evaluation(transmission_model), sys) + add_power_flow_data!(container, get_power_flow_evaluation(transmission_model), sys) check_optimization_container(container) return end @@ -1508,7 +1508,7 @@ end function calculate_aux_variables!(container::OptimizationContainer, system::PSY.System) if !isnothing(get_power_flow_data(container)) - solve_powerflow!(container, system) + solve_power_flow!(container, system) end aux_vars = get_aux_variables(container) diff --git a/src/initial_conditions/initialization.jl b/src/initial_conditions/initialization.jl index 01ec41fbec..4498eafb54 100644 --- a/src/initial_conditions/initialization.jl +++ b/src/initial_conditions/initialization.jl @@ -13,7 +13,7 @@ function get_initial_conditions_template(model::OperationModel) get_radial_network_reduction(get_network_model(model.template)) network_model.subnetworks = get_subnetworks(get_network_model(model.template)) # Initialization does not support PowerFlow evaluation - network_model.powerflow_evaluation = nothing + network_model.power_flow_evaluation = nothing bus_area_map = get_bus_area_map(get_network_model(model.template)) if !isempty(bus_area_map) network_model.bus_area_map = get_bus_area_map(get_network_model(model.template)) diff --git a/src/network_models/powerflow_evaluation.jl b/src/network_models/power_flow_evaluation.jl similarity index 99% rename from src/network_models/powerflow_evaluation.jl rename to src/network_models/power_flow_evaluation.jl index 44a5a01641..6563c79ec7 100644 --- a/src/network_models/powerflow_evaluation.jl +++ b/src/network_models/power_flow_evaluation.jl @@ -192,12 +192,12 @@ end function update_pf_data!(pf_data, container::OptimizationContainer) end -function solve_powerflow!( +function solve_power_flow!( container::OptimizationContainer, system::PSY.System) pf_data = get_pf_data(container) update_pf_data!(pf_data, container) - PFS.solve_powerflow!(pf_data) + PFS.solve_power_flow!(pf_data) end function calculate_aux_variable_value!(container::OptimizationContainer, From 2645ced0c4962ac442539578e4a54da2c793e637 Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Wed, 24 Jan 2024 08:53:28 -0700 Subject: [PATCH 23/39] file rename --- .../{powerflow_data_wrapper.jl => power_flow_data_wrapper.jl} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/core/{powerflow_data_wrapper.jl => power_flow_data_wrapper.jl} (100%) diff --git a/src/core/powerflow_data_wrapper.jl b/src/core/power_flow_data_wrapper.jl similarity index 100% rename from src/core/powerflow_data_wrapper.jl rename to src/core/power_flow_data_wrapper.jl From c997e2f262bb2349aff28d9c36462ff3be661aae Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Thu, 25 Jan 2024 09:34:32 -0700 Subject: [PATCH 24/39] add evaluation to the container --- src/core/optimization_container.jl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/core/optimization_container.jl b/src/core/optimization_container.jl index e748e6d14f..5ddeba796b 100644 --- a/src/core/optimization_container.jl +++ b/src/core/optimization_container.jl @@ -688,8 +688,8 @@ function solve_impl!(container::OptimizationContainer, system::PSY.System) return RunStatus.FAILED end - status = RunStatus.SUCCESSFUL - + # Order is important because if a dual is needed then it could move the results to the + # temporary primal container _, optimizer_stats.timed_calculate_aux_variables = @timed calculate_aux_variables!(container, system) @@ -698,7 +698,8 @@ function solve_impl!(container::OptimizationContainer, system::PSY.System) _, optimizer_stats.timed_calculate_dual_variables = @timed calculate_dual_variables!(container, system, is_milp(container)) - return status + + return RunStatus.SUCCESSFUL end function compute_conflict!(container::OptimizationContainer) From 1ba76cbf03992963ca2bdbb96805d53aeab9359d Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Thu, 25 Jan 2024 09:34:43 -0700 Subject: [PATCH 25/39] WIP: add functions to map results to pf --- src/core/power_flow_data_wrapper.jl | 1 + src/network_models/power_flow_evaluation.jl | 57 ++++++++++++++++++++- 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/src/core/power_flow_data_wrapper.jl b/src/core/power_flow_data_wrapper.jl index 254c6a125c..f5d61d843f 100644 --- a/src/core/power_flow_data_wrapper.jl +++ b/src/core/power_flow_data_wrapper.jl @@ -11,3 +11,4 @@ function PowerFlowEvaluationData(power_flow_data::T) where {T <: PFS.PowerFlowDa end get_power_flow_data(ped::PowerFlowEvaluationData) = ped.power_flow_data +get_injection_key_map(ped::PowerFlowEvaluationData) = ped.injection_key_map diff --git a/src/network_models/power_flow_evaluation.jl b/src/network_models/power_flow_evaluation.jl index 6563c79ec7..fc6b15d0b7 100644 --- a/src/network_models/power_flow_evaluation.jl +++ b/src/network_models/power_flow_evaluation.jl @@ -189,7 +189,62 @@ function add_power_flow_data!( return end -function update_pf_data!(pf_data, container::OptimizationContainer) +function _write_value_to_pf_data!( + pf_data::PFS.PowerFlowData, + container::OptimizationContainer, + key::AuxVariableKey{PowerOutput, T}, + bus_index_map) where {T <: PSY.ThermalGen} + + result = get_variable(container, key) + for (device_name, index) in bus_index_map + injection_values = result[device_name, :] + for t in axes(result)[2] + pf_data[index, t] = jump_value(injection_values[t]) + end + end + return +end + +function _write_value_to_pf_data!( + pf_data::PFS.PowerFlowData, + container::OptimizationContainer, + key::VariableKey{ParameterKey, T}, + bus_index_map) where {T <: PSY.Generator} + + result = get_variable(container, key) + for (device_name, index) in bus_index_map + injection_values = result[device_name, :] + for t in axes(result)[2] + pf_data[index, t] = jump_value(injection_values[t]) + end + end + return +end + +function _write_value_to_pf_data!( + pf_data::PFS.PowerFlowData, + container::OptimizationContainer, + key::VariableKey{ActivePowerVariable, T}, + bus_index_map) where {T <: PSY.StaticInjection} + + result = get_variable(container, key) + for (device_name, index) in bus_index_map + injection_values = result[device_name, :] + for t in axes(result)[2] + pf_data[index, t] = jump_value(injection_values[t]) + end + end + return +end + +function update_pf_data!(pf_e_data, container::OptimizationContainer) + pf_data = get_power_flow_data(pf_e_data) + PFS.clear_injection_data!(pf_data) + key_map = get_injection_key_map(container) + for (key, bus_index_map) in key_map + _write_value_to_pf_data!(pf_data, container, key, bus_index_map) + end + return end function solve_power_flow!( From b26e2b0db51f550cfb5ee4d4c689fdd998c2771a Mon Sep 17 00:00:00 2001 From: GabrielKS <23368820+GabrielKS@users.noreply.github.com> Date: Tue, 15 Oct 2024 15:34:48 -0600 Subject: [PATCH 26/39] Remove redundant code --- src/core/auxiliary_variables.jl | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/core/auxiliary_variables.jl b/src/core/auxiliary_variables.jl index a595311368..4c6a9efd05 100644 --- a/src/core/auxiliary_variables.jl +++ b/src/core/auxiliary_variables.jl @@ -33,10 +33,6 @@ Auxiliary Variable for the line active flow from power flow evaluation """ struct PowerFlowLineActivePower <: AuxVariableType end -should_write_resulting_value(::Type{<:AuxVariableType}) = true - -convert_result_to_natural_units(::Type{<:AuxVariableType}) = false - convert_result_to_natural_units(::Type{PowerOutput}) = true convert_result_to_natural_units(::Type{PowerFlowLineReactivePower}) = true convert_result_to_natural_units(::Type{PowerFlowLineActivePower}) = true From 54e485927ca84c301127f4387ef60e51754893be Mon Sep 17 00:00:00 2001 From: GabrielKS <23368820+GabrielKS@users.noreply.github.com> Date: Tue, 15 Oct 2024 15:51:11 -0600 Subject: [PATCH 27/39] Rewrite power flow evaluation to support more types of power flow --- src/core/optimization_container.jl | 22 +- src/core/power_flow_data_wrapper.jl | 12 +- src/network_models/power_flow_evaluation.jl | 272 +++++++++---------- src/simulation/simulation_problem_results.jl | 9 +- 4 files changed, 161 insertions(+), 154 deletions(-) diff --git a/src/core/optimization_container.jl b/src/core/optimization_container.jl index 692a69a3cc..2d4af512fe 100644 --- a/src/core/optimization_container.jl +++ b/src/core/optimization_container.jl @@ -1635,12 +1635,11 @@ function deserialize_key(container::OptimizationContainer, name::AbstractString) end function calculate_aux_variables!(container::OptimizationContainer, system::PSY.System) - if !isnothing(get_power_flow_data(container)) - solve_power_flow!(container, system) - end - - aux_vars = get_aux_variables(container) - for key in keys(aux_vars) + pf_e_data = get_power_flow_evaluation_data(container) + update_pf_data!(pf_e_data, container) + PFS.solve_powerflow!(get_power_flow_data(pf_e_data)) + pf_e_data.is_solved = true + for key in keys(get_aux_variables(container)) calculate_aux_variable_value!(container, key, system) end return RunStatus.SUCCESSFULLY_FINALIZED @@ -1911,3 +1910,14 @@ function get_time_series_initial_values!( ) return ts_values end + +lookup_value(container::OptimizationContainer, key::VariableKey) = + get_variable(container, key) +lookup_value(container::OptimizationContainer, key::ParameterKey) = + calculate_parameter_values(get_parameter(container, key)) +lookup_value(container::OptimizationContainer, key::AuxVarKey) = + get_aux_variable(container, key) +lookup_value(container::OptimizationContainer, key::ExpressionKey) = + get_expression(container, key) +lookup_value(container::OptimizationContainer, key::ConstraintKey) = + get_constraint(container, key) diff --git a/src/core/power_flow_data_wrapper.jl b/src/core/power_flow_data_wrapper.jl index f5d61d843f..b7772d11f2 100644 --- a/src/core/power_flow_data_wrapper.jl +++ b/src/core/power_flow_data_wrapper.jl @@ -1,12 +1,18 @@ -mutable struct PowerFlowEvaluationData{T <: PFS.PowerFlowData} +mutable struct PowerFlowEvaluationData{T <: PFS.PowerFlowContainer} power_flow_data::T - injection_key_map::Dict{<:OptimizationContainerKey, Dict{String, Int}} + """ + Records which PSI keys get used to update the data and how they are mapped to it. For + `PowerFlowData`, values are `Dict{String, Int}` specifying component name, matrix index; + for `SystemPowerFlowContainer`, values are Set{String} merely specifying component + names. + """ + injection_key_map::Dict{<:OptimizationContainerKey, <:Any} end function PowerFlowEvaluationData(power_flow_data::T) where {T <: PFS.PowerFlowData} return PowerFlowEvaluationData{T}( power_flow_data, - Dict{OptimizationContainerKey, Dict{String, Int}}(), + Dict{OptimizationContainerKey, Nothing}(), ) end diff --git a/src/network_models/power_flow_evaluation.jl b/src/network_models/power_flow_evaluation.jl index fc6b15d0b7..7d933200fe 100644 --- a/src/network_models/power_flow_evaluation.jl +++ b/src/network_models/power_flow_evaluation.jl @@ -3,62 +3,36 @@ function add_power_flow_data!(::OptimizationContainer, ::Nothing, ::PSY.System) return end -function _add_branches_aux_variables!( +function _add_aux_variables!( container::OptimizationContainer, - vars::Vector{DataType}, - branch_types::Vector{DataType}, - branch_lookup::Dict{String, Int}, + component_map::Dict{Type{<:AuxVariableType}, <:Set{<:Tuple{DataType, Any}}}, ) - branch_type_map = - Dict{String, DataType}(k => branch_types[v] for (k, v) in branch_lookup) - time_steps = get_time_steps(container) - for var_type in vars - for D in Set(branch_types) - branch_names = [k for (k, v) in branch_type_map if v == D] + for (var_type, components) in pairs(component_map) + component_types = unique(first.(components)) + for component_type in component_types + component_names = [v for (k, v) in components if k <: component_type] + sort!(component_names) add_aux_variable_container!( container, var_type(), - D, - branch_names, - time_steps, + component_type, + component_names, + get_time_steps(container), ) end end - return end -function _add_buses_aux_variables!( - container::OptimizationContainer, - vars::Vector{DataType}, - bus_lookup::Dict{Int, Int}, -) - time_steps = get_time_steps(container) - for var_type in vars - add_aux_variable_container!( - container, - var_type(), - PSY.ACBus, - sort!(collect(keys(bus_lookup))), - time_steps, - ) - end - return -end - -const ACTIVE_POWER_INJECTION_KEYS = [ - ActivePowerVariable - PowerOutput - ActivePowerTimeSeriesParameter -] +# Trait that determines what keys serve as input to each type of power flow +# Currently there is only the default; written this way so as to be easily overridable +pf_input_keys(::PFS.PowerFlowContainer) = + [ActivePowerVariable, PowerOutput, ActivePowerTimeSeriesParameter] -function _make_injection_map!(container::OptimizationContainer, sys::PSY.System) - pf_e_data = get_power_flow_evaluation_data(container) - pf_data = get_power_flow_data(pf_e_data) - # Maps the StaticInjection component type by name to the - # index in the PowerFlow data arrays going from Bus number to bus index +# Maps the StaticInjection component type by name to the +# index in the PowerFlow data arrays going from Bus number to bus index +function _make_temp_component_bus_map(pf_data::PFS.PowerFlowData, sys::PSY.System) temp_component_bus_map = Dict{DataType, Dict{String, Int}}() available_injectors = PSY.get_components(PSY.get_available, PSY.StaticInjection, sys) - sizehint!(temp_component_bus_map, length(available_injectors)) bus_lookup = PFS.get_bus_lookup(pf_data) for comp in available_injectors comp_type = typeof(comp) @@ -66,13 +40,36 @@ function _make_injection_map!(container::OptimizationContainer, sys::PSY.System) bus_number = PSY.get_number(PSY.get_bus(comp)) bus_dict[get_name(comp)] = bus_lookup[bus_number] end + return temp_component_bus_map +end + +# Creates Sets of components by type +function _make_temp_component_bus_map(::PFS.SystemPowerFlowContainer, sys::PSY.System) + temp_component_bus_map = Dict{DataType, Set{String}}() + # TODO `ComponentSelector` use case + available_injectors = PSY.get_components(PSY.get_available, PSY.StaticInjection, sys) + for comp_type in unique(typeof.(available_injectors)) + temp_component_bus_map[comp_type] = + Set(filter(x -> typeof(x) == comp_type, available_injectors)) + end + return temp_component_bus_map +end + +function _make_injection_map!( + pf_e_data::PowerFlowEvaluationData, + container::OptimizationContainer, + sys::PSY.System, +) + pf_data = get_power_flow_data(pf_e_data) + temp_component_bus_map = _make_temp_component_bus_map(pf_data, sys) + injection_keys = pf_input_keys(pf_data) # Second map that persists to store the bus index that the variable # has to be added/substracted to in the power flow data dictionary pf_data_opt_container_map = Dict{OptimizationContainerKey, Dict{String, Int}}() added_injection_types = DataType[] for (key, array) in get_variables(container) - if get_entry_type(key) ∉ ACTIVE_POWER_INJECTION_KEYS + if get_entry_type(key) ∉ injection_keys continue end @@ -87,7 +84,7 @@ function _make_injection_map!(container::OptimizationContainer, sys::PSY.System) end for (key, array) in get_aux_variables(container) - if get_entry_type(key) ∉ ACTIVE_POWER_INJECTION_KEYS + if get_entry_type(key) ∉ injection_keys continue end # Skip aux variable if the device was added as a variable @@ -105,7 +102,7 @@ function _make_injection_map!(container::OptimizationContainer, sys::PSY.System) end for (key, param_container) in get_parameters(container) - if get_entry_type(key) ∉ ACTIVE_POWER_INJECTION_KEYS + if get_entry_type(key) ∉ injection_keys continue end comp_type = get_component_type(key) @@ -125,138 +122,129 @@ function _make_injection_map!(container::OptimizationContainer, sys::PSY.System) return end -function add_power_flow_data!( - container::OptimizationContainer, - evaluator::T, - sys::PSY.System, -) where {T <: Union{PFS.PTDFDCPowerFlow, PFS.vPTDFDCPowerFlow}} - @info "Building PowerFlow evaluator using $(evaluator)" - pf_data = - PFS.PowerFlowData(evaluator, sys; time_steps = length(get_time_steps(container))) - container.power_flow_evaluation_data = PowerFlowEvaluationData(pf_data) - branch_aux_vars = [PowerFlowLineActivePower] - _add_branches_aux_variables!( - container, - branch_aux_vars, - PFS.get_branch_type(pf_data), - PFS.get_branch_lookup(pf_data), - ) - _make_injection_map!(container, sys) - return -end +# Trait that determines what branch aux vars we can get from each type of power flow +branch_aux_vars(::PFS.ACPowerFlow) = [PowerFlowLineActivePower, PowerFlowLineReactivePower] +branch_aux_vars(::PFS.DCPowerFlow) = [PowerFlowLineActivePower, PowerFlowLineReactivePower] +branch_aux_vars(::PFS.PTDFDCPowerFlow) = [PowerFlowLineActivePower] +branch_aux_vars(::PFS.vPTDFDCPowerFlow) = [PowerFlowLineActivePower] +branch_aux_vars(::PFS.PSSEExportPowerFlow) = + [PowerFlowLineActivePower, PowerFlowLineReactivePower] -function add_power_flow_data!( - container::OptimizationContainer, - evaluator::PFS.DCPowerFlow, - sys::PSY.System, -) - @info "Building PowerFlow evaluator using $(evaluator)" - pf_data = - PFS.PowerFlowData(evaluator, sys; time_steps = length(get_time_steps(container))) - container.power_flow_evaluation_data = PowerFlowEvaluationData(pf_data) - branch_aux_vars = [PowerFlowLineActivePower] - _add_branches_aux_variables!( - container, - branch_aux_vars, - PFS.get_branch_type(pf_data), - PFS.get_branch_lookup(pf_data), - ) - bus_aux_vars = [PowerFlowVoltageAngle] - _add_buses_aux_variables!(container, bus_aux_vars, PFS.get_bus_lookup(pf_data)) - _make_injection_map!(container, sys) - return -end +# Same for bus aux vars +bus_aux_vars(::PFS.ACPowerFlow) = [PowerFlowVoltageAngle, PowerFlowVoltageMagnitude] +bus_aux_vars(::PFS.DCPowerFlow) = [PowerFlowVoltageAngle] +bus_aux_vars(::PFS.PTDFDCPowerFlow) = Vector{DataType}[] +bus_aux_vars(::PFS.vPTDFDCPowerFlow) = Vector{DataType}[] +bus_aux_vars(::PFS.PSSEExportPowerFlow) = [PowerFlowVoltageAngle, PowerFlowVoltageMagnitude] + +_get_branch_component_tuples(pfd::PFS.PowerFlowData) = + zip(PFS.get_branch_type(pfd), keys(PFS.get_branch_lookup(pfd))) + +_get_branch_component_tuples(pfd::PFS.SystemPowerFlowContainer) = + [(typeof(c), get_name(c)) for c in get_components(Branch, get_system(pfd))] + +_get_bus_component_tuples(pfd::PFS.PowerFlowData) = + tuple.(PSY.ACBus, keys(PFS.get_bus_lookup(pfd))) # get_bus_type returns a ACBusTypes, not the DataType we need here + +_get_bus_component_tuples(pfd::PFS.SystemPowerFlowContainer) = + [(typeof(c), PSY.get_number(c)) for c in get_components(Bus, get_system(pfd))] function add_power_flow_data!( container::OptimizationContainer, - evaluator::PFS.ACPowerFlow, + evaluators::Vector{PFS.PowerFlowEvaluationModel}, sys::PSY.System, ) - @info "Building PowerFlow evaluator using $(evaluator)" - pf_data = - PFS.PowerFlowData(evaluator, sys; time_steps = length(get_time_steps(container))) - container.power_flow_evaluation_data = PowerFlowEvaluationData(pf_data) - branch_aux_vars = [PowerFlowLineActivePower, PowerFlowLineReactivePower] - _add_branches_aux_variables!( - container, - branch_aux_vars, - PFS.get_branch_type(pf_data), - PFS.get_branch_lookup(pf_data), - ) - bus_aux_vars = [PowerFlowVoltageAngle, PowerFlowVoltageMagnitude] - _add_buses_aux_variables!(container, bus_aux_vars, PFS.get_bus_lookup(pf_data)) - _make_injection_map!(container, sys) - return -end - -function _write_value_to_pf_data!( - pf_data::PFS.PowerFlowData, - container::OptimizationContainer, - key::AuxVariableKey{PowerOutput, T}, - bus_index_map) where {T <: PSY.ThermalGen} - - result = get_variable(container, key) - for (device_name, index) in bus_index_map - injection_values = result[device_name, :] - for t in axes(result)[2] - pf_data[index, t] = jump_value(injection_values[t]) + container.power_flow_evaluation_data = Vector{PowerFlowEvaluationData}() + sizehint!(container.power_flow_evaluation_data, length(evaluators)) + # For each output key, what components are we working with? + branch_aux_var_components = + Dict{Type{<:AuxVariableType}, Set{Tuple{<:DataType, String}}}() + bus_aux_var_components = Dict{Type{<:AuxVariableType}, Set{Tuple{<:DataType, <:Int}}}() + for evaluator in evaluators + @info "Building PowerFlow evaluator using $(evaluator)" + pf_data = PFS.make_power_flow_container(evaluator, sys; + time_steps = length(get_time_steps(container))) + pf_e_data = PowerFlowEvaluationData(pf_data) + my_branch_aux_vars = branch_aux_vars(evaluator) + my_bus_aux_vars = bus_aux_vars(evaluator) + + my_branch_components = _get_branch_component_tuples(pf_data) + for branch_aux_var in my_branch_aux_vars + to_add_to = get!( + branch_aux_var_components, + branch_aux_var, + Set{Tuple{<:DataType, String}}(), + ) + push!.(Ref(to_add_to), my_branch_components) end - end - return -end - -function _write_value_to_pf_data!( - pf_data::PFS.PowerFlowData, - container::OptimizationContainer, - key::VariableKey{ParameterKey, T}, - bus_index_map) where {T <: PSY.Generator} - result = get_variable(container, key) - for (device_name, index) in bus_index_map - injection_values = result[device_name, :] - for t in axes(result)[2] - pf_data[index, t] = jump_value(injection_values[t]) + my_bus_components = _get_bus_component_tuples(pf_data) + for bus_aux_var in my_bus_aux_vars + to_add_to = + get!(bus_aux_var_components, bus_aux_var, Set{Tuple{<:DataType, <:Int}}()) + push!.(Ref(to_add_to), my_bus_components) end + _make_injection_map!(pf_e_data, container, sys) + push!(container.power_flow_evaluation_data, pf_e_data) end - return + + _add_aux_variables!(container, branch_aux_var_components) + _add_aux_variables!(container, bus_aux_var_components) end function _write_value_to_pf_data!( pf_data::PFS.PowerFlowData, container::OptimizationContainer, - key::VariableKey{ActivePowerVariable, T}, - bus_index_map) where {T <: PSY.StaticInjection} - - result = get_variable(container, key) + key::OptimizationContainerKey, + bus_index_map) + result = lookup_value(container, key) for (device_name, index) in bus_index_map injection_values = result[device_name, :] for t in axes(result)[2] - pf_data[index, t] = jump_value(injection_values[t]) + pf_data.bus_activepower_injection[index, t] += jump_value(injection_values[t]) end end return end -function update_pf_data!(pf_e_data, container::OptimizationContainer) +function update_pf_data!( + pf_e_data::PowerFlowEvaluationData{<:PFS.PowerFlowData}, + container::OptimizationContainer, +) pf_data = get_power_flow_data(pf_e_data) PFS.clear_injection_data!(pf_data) - key_map = get_injection_key_map(container) + key_map = get_injection_key_map(pf_e_data) for (key, bus_index_map) in key_map _write_value_to_pf_data!(pf_data, container, key, bus_index_map) end return end -function solve_power_flow!( +function update_pf_system!(sys::PSY.System, container::OptimizationContainer, key_map) + TIME = 1 # TODO figure out how to handle multiple time periods here + for (key, bus_index_map) in key_map + result = lookup_value(container, key) + for (device_name, _) in bus_index_map + injection_values = result[device_name, :] + comp = PSY.get_component(get_component_type(key), sys, device_name) + comp.active_power = jump_value(injection_values[TIME]) + end + end +end + +function update_pf_data!( + pf_e_data::PowerFlowEvaluationData{<:PFS.SystemPowerFlowContainer}, container::OptimizationContainer, - system::PSY.System) - pf_data = get_pf_data(container) - update_pf_data!(pf_data, container) - PFS.solve_power_flow!(pf_data) +) + pf_data = get_power_flow_data(pf_e_data) + key_map = get_injection_key_map(pf_e_data) + update_pf_system!(PFS.get_system(pf_data), container, key_map) + return end function calculate_aux_variable_value!(container::OptimizationContainer, key, system::PSY.System) + # TODO read data back from the power flow return end diff --git a/src/simulation/simulation_problem_results.jl b/src/simulation/simulation_problem_results.jl index faa327d540..f5c1607c46 100644 --- a/src/simulation/simulation_problem_results.jl +++ b/src/simulation/simulation_problem_results.jl @@ -79,13 +79,16 @@ function set_results_timestamps!( result.results_timestamps = results_timestamps end -list_result_keys(res::SimulationProblemResults, ::AuxVarKey) = list_aux_variable_keys(res) -list_result_keys(res::SimulationProblemResults, ::ConstraintKey) = list_dual_keys(res) +list_result_keys(res::SimulationProblemResults, ::AuxVarKey) = + list_aux_variable_keys(res) +list_result_keys(res::SimulationProblemResults, ::ConstraintKey) = + list_dual_keys(res) list_result_keys(res::SimulationProblemResults, ::ExpressionKey) = list_expression_keys(res) list_result_keys(res::SimulationProblemResults, ::ParameterKey) = list_parameter_keys(res) -list_result_keys(res::SimulationProblemResults, ::VariableKey) = list_variable_keys(res) +list_result_keys(res::SimulationProblemResults, ::VariableKey) = + list_variable_keys(res) get_cached_results(res::SimulationProblemResults, ::Type{<:AuxVarKey}) = get_cached_aux_variables(res) From 1e714ac10a0116d0d5d00620827deae39a9ad845 Mon Sep 17 00:00:00 2001 From: GabrielKS <23368820+GabrielKS@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:06:25 -0600 Subject: [PATCH 28/39] Support evaluation of multiple power flows in the loop --- src/core/network_model.jl | 4 +-- src/core/optimization_container.jl | 33 ++++++++++++++++----- src/core/power_flow_data_wrapper.jl | 2 ++ src/initial_conditions/initialization.jl | 2 +- src/network_models/power_flow_evaluation.jl | 15 ++++++++++ 5 files changed, 45 insertions(+), 11 deletions(-) diff --git a/src/core/network_model.jl b/src/core/network_model.jl index 3172547d62..e017a2af11 100644 --- a/src/core/network_model.jl +++ b/src/core/network_model.jl @@ -34,7 +34,7 @@ mutable struct NetworkModel{T <: PM.AbstractPowerModel} duals::Vector{DataType} radial_network_reduction::PNM.RadialNetworkReduction reduce_radial_branches::Bool - power_flow_evaluation::Union{Nothing, PFS.PowerFlowEvaluationModel} + power_flow_evaluation::Vector{PFS.PowerFlowEvaluationModel} subsystem::Union{Nothing, String} modeled_branch_types::Vector{DataType} @@ -45,7 +45,7 @@ mutable struct NetworkModel{T <: PM.AbstractPowerModel} reduce_radial_branches = false, subnetworks = Dict{Int, Set{Int}}(), duals = Vector{DataType}(), - power_flow_evaluation = nothing, + power_flow_evaluation = Vector{PFS.PowerFlowEvaluationModel}[], ) where {T <: PM.AbstractPowerModel} _check_pm_formulation(T) new{T}( diff --git a/src/core/optimization_container.jl b/src/core/optimization_container.jl index 2d4af512fe..478272414a 100644 --- a/src/core/optimization_container.jl +++ b/src/core/optimization_container.jl @@ -73,7 +73,7 @@ mutable struct OptimizationContainer <: IS.Optimization.AbstractOptimizationCont built_for_recurrent_solves::Bool metadata::IS.Optimization.OptimizationContainerMetadata default_time_series_type::Type{<:PSY.TimeSeriesData} - power_flow_evaluation_data::Union{<:PowerFlowEvaluationData, Nothing} + power_flow_evaluation_data::Vector{PowerFlowEvaluationData} end function OptimizationContainer( @@ -116,7 +116,7 @@ function OptimizationContainer( false, IS.Optimization.OptimizationContainerMetadata(), T, - nothing, + Vector{PowerFlowEvaluationData}[], ) end @@ -169,6 +169,12 @@ is_synchronized(container::OptimizationContainer) = set_time_steps!(container::OptimizationContainer, time_steps::UnitRange{Int64}) = container.time_steps = time_steps +function reset_power_flow_is_solved!(container::OptimizationContainer) + for pf_e_data in get_power_flow_evaluation_data(container) + pf_e_data.is_solved = false + end +end + function has_container_key( container::OptimizationContainer, ::Type{T}, @@ -1635,12 +1641,23 @@ function deserialize_key(container::OptimizationContainer, name::AbstractString) end function calculate_aux_variables!(container::OptimizationContainer, system::PSY.System) - pf_e_data = get_power_flow_evaluation_data(container) - update_pf_data!(pf_e_data, container) - PFS.solve_powerflow!(get_power_flow_data(pf_e_data)) - pf_e_data.is_solved = true - for key in keys(get_aux_variables(container)) - calculate_aux_variable_value!(container, key, system) + reset_power_flow_is_solved!(container) + # PERF currently we update the aux vars after each power flow because aux vars are how + # power flows pass information to each other. This shouldn't be a huge problem because + # there shouldn't ever be more than a few power flows, but we could record which aux + # vars are power flow-related and calculate the rest only once at the end. + for (i, pf_e_data) in enumerate(get_power_flow_evaluation_data(container)) + @debug "Processing power flow $i" + solve_powerflow!(pf_e_data, container) + for key in keys(get_aux_variables(container)) + calculate_aux_variable_value!(container, key, system) + end + end + + if isempty(get_power_flow_evaluation_data(container)) + for key in keys(get_aux_variables(container)) + calculate_aux_variable_value!(container, key, system) + end end return RunStatus.SUCCESSFULLY_FINALIZED end diff --git a/src/core/power_flow_data_wrapper.jl b/src/core/power_flow_data_wrapper.jl index b7772d11f2..820e583ac0 100644 --- a/src/core/power_flow_data_wrapper.jl +++ b/src/core/power_flow_data_wrapper.jl @@ -7,12 +7,14 @@ mutable struct PowerFlowEvaluationData{T <: PFS.PowerFlowContainer} names. """ injection_key_map::Dict{<:OptimizationContainerKey, <:Any} + is_solved::Bool end function PowerFlowEvaluationData(power_flow_data::T) where {T <: PFS.PowerFlowData} return PowerFlowEvaluationData{T}( power_flow_data, Dict{OptimizationContainerKey, Nothing}(), + false, ) end diff --git a/src/initial_conditions/initialization.jl b/src/initial_conditions/initialization.jl index 66535811d8..606f1e8a3e 100644 --- a/src/initial_conditions/initialization.jl +++ b/src/initial_conditions/initialization.jl @@ -13,7 +13,7 @@ function get_initial_conditions_template(model::OperationModel) get_radial_network_reduction(get_network_model(model.template)) network_model.subnetworks = get_subnetworks(get_network_model(model.template)) # Initialization does not support PowerFlow evaluation - network_model.power_flow_evaluation = nothing + network_model.power_flow_evaluation = Vector{PFS.PowerFlowEvaluationModel}[] bus_area_map = get_bus_area_map(get_network_model(model.template)) if !isempty(bus_area_map) network_model.bus_area_map = get_bus_area_map(get_network_model(model.template)) diff --git a/src/network_models/power_flow_evaluation.jl b/src/network_models/power_flow_evaluation.jl index 7d933200fe..2f51be4ac8 100644 --- a/src/network_models/power_flow_evaluation.jl +++ b/src/network_models/power_flow_evaluation.jl @@ -242,6 +242,21 @@ function update_pf_data!( return end +"Fetch the most recently solved `PowerFlowEvaluationData`" +function latest_solved_power_flow_evaluation_data(container::OptimizationContainer) + datas = get_power_flow_evaluation_data(container) + return datas[findlast(x -> x.is_solved, datas)] +end + +function solve_powerflow!( + pf_e_data::PowerFlowEvaluationData, + container::OptimizationContainer) + update_pf_data!(pf_e_data, container) + PFS.solve_powerflow!(get_power_flow_data(pf_e_data)) + pf_e_data.is_solved = true + return +end + function calculate_aux_variable_value!(container::OptimizationContainer, key, system::PSY.System) From 508db25a31408d81939a408791bd10d39c0ca96f Mon Sep 17 00:00:00 2001 From: GabrielKS <23368820+GabrielKS@users.noreply.github.com> Date: Wed, 16 Oct 2024 17:30:17 -0600 Subject: [PATCH 29/39] Better accommodate `PSSEExporter` in the loop, reduce duplication --- src/core/power_flow_data_wrapper.jl | 2 +- src/network_models/power_flow_evaluation.jl | 81 ++++++++------------- 2 files changed, 33 insertions(+), 50 deletions(-) diff --git a/src/core/power_flow_data_wrapper.jl b/src/core/power_flow_data_wrapper.jl index 820e583ac0..743c51a1c9 100644 --- a/src/core/power_flow_data_wrapper.jl +++ b/src/core/power_flow_data_wrapper.jl @@ -10,7 +10,7 @@ mutable struct PowerFlowEvaluationData{T <: PFS.PowerFlowContainer} is_solved::Bool end -function PowerFlowEvaluationData(power_flow_data::T) where {T <: PFS.PowerFlowData} +function PowerFlowEvaluationData(power_flow_data::T) where {T <: PFS.PowerFlowContainer} return PowerFlowEvaluationData{T}( power_flow_data, Dict{OptimizationContainerKey, Nothing}(), diff --git a/src/network_models/power_flow_evaluation.jl b/src/network_models/power_flow_evaluation.jl index 2f51be4ac8..5aeb75610c 100644 --- a/src/network_models/power_flow_evaluation.jl +++ b/src/network_models/power_flow_evaluation.jl @@ -45,12 +45,16 @@ end # Creates Sets of components by type function _make_temp_component_bus_map(::PFS.SystemPowerFlowContainer, sys::PSY.System) - temp_component_bus_map = Dict{DataType, Set{String}}() + temp_component_bus_map = Dict{DataType, Dict{String, String}}() # TODO `ComponentSelector` use case - available_injectors = PSY.get_components(PSY.get_available, PSY.StaticInjection, sys) + available_injectors = + collect(PSY.get_components(PSY.get_available, PSY.StaticInjection, sys)) for comp_type in unique(typeof.(available_injectors)) temp_component_bus_map[comp_type] = - Set(filter(x -> typeof(x) == comp_type, available_injectors)) + Dict( + PSY.get_name(c) => PSY.get_name(c) for + c in available_injectors if c isa comp_type + ) end return temp_component_bus_map end @@ -62,60 +66,33 @@ function _make_injection_map!( ) pf_data = get_power_flow_data(pf_e_data) temp_component_bus_map = _make_temp_component_bus_map(pf_data, sys) + map_type = valtype(temp_component_bus_map) # Dict{String, Int} for PowerFlowData, Dict{String, String} for SystemPowerFlowContainer injection_keys = pf_input_keys(pf_data) # Second map that persists to store the bus index that the variable # has to be added/substracted to in the power flow data dictionary - pf_data_opt_container_map = Dict{OptimizationContainerKey, Dict{String, Int}}() + pf_data_opt_container_map = Dict{OptimizationContainerKey, map_type}() added_injection_types = DataType[] - for (key, array) in get_variables(container) - if get_entry_type(key) ∉ injection_keys - continue - end + for (key, val) in Iterators.flatten([ + get_variables(container), + get_aux_variables(container), + get_parameters(container), + ]) + # Skip irrelevant keys + (get_entry_type(key) in injection_keys) || continue - name_bus_ix_map = Dict{String, Int}() comp_type = get_component_type(key) + # Skip types that have already been handled (prefer variable over aux variable, aux variable over parameter) + (comp_type in added_injection_types) && continue push!(added_injection_types, comp_type) - for n in axes(array)[1] - name_bus_ix_map[n] = temp_component_bus_map[comp_type][n] - end - pf_data_opt_container_map[key] = name_bus_ix_map - end - - for (key, array) in get_aux_variables(container) - if get_entry_type(key) ∉ injection_keys - continue - end - # Skip aux variable if the device was added as a variable - if comp_type ∈ added_injection_types - continue - end - name_bus_ix_map = Dict{String, Int}() - comp_type = get_component_type(key) - push!(added_injection_types, comp_type) - for n in axes(array)[1] - name_bus_ix_map[n] = temp_component_bus_map[comp_type][n] - end - - pf_data_opt_container_map[key] = name_bus_ix_map - end - - for (key, param_container) in get_parameters(container) - if get_entry_type(key) ∉ injection_keys - continue - end - comp_type = get_component_type(key) - # Skip parameter if the device was added as a variable - if comp_type ∈ added_injection_types - continue - end - param_attributes = get_attributes(param_container) - name_bus_ix_map = Dict{String, Int}() - for n in get_component_names(param_attributes) - name_bus_ix_map[n] = temp_component_bus_map[comp_type][n] + name_bus_ix_map = map_type() + # Maybe this should be rewritten as multiple dispatch but it should not be rewritten as a copypasted loop + comp_names = + (key isa ParameterKey) ? get_component_names(get_attributes(val)) : axes(val)[1] + for comp_name in comp_names + name_bus_ix_map[comp_name] = temp_component_bus_map[comp_type][comp_name] end - pf_data_opt_container_map[key] = name_bus_ix_map end pf_e_data.injection_key_map = pf_data_opt_container_map @@ -141,13 +118,16 @@ _get_branch_component_tuples(pfd::PFS.PowerFlowData) = zip(PFS.get_branch_type(pfd), keys(PFS.get_branch_lookup(pfd))) _get_branch_component_tuples(pfd::PFS.SystemPowerFlowContainer) = - [(typeof(c), get_name(c)) for c in get_components(Branch, get_system(pfd))] + [(typeof(c), get_name(c)) for c in PSY.get_components(PSY.Branch, PFS.get_system(pfd))] _get_bus_component_tuples(pfd::PFS.PowerFlowData) = tuple.(PSY.ACBus, keys(PFS.get_bus_lookup(pfd))) # get_bus_type returns a ACBusTypes, not the DataType we need here _get_bus_component_tuples(pfd::PFS.SystemPowerFlowContainer) = - [(typeof(c), PSY.get_number(c)) for c in get_components(Bus, get_system(pfd))] + [ + (typeof(c), PSY.get_number(c)) for + c in PSY.get_components(PSY.Bus, PFS.get_system(pfd)) + ] function add_power_flow_data!( container::OptimizationContainer, @@ -261,5 +241,8 @@ function calculate_aux_variable_value!(container::OptimizationContainer, key, system::PSY.System) # TODO read data back from the power flow + pf_e_data = latest_solved_power_flow_evaluation_data(container) + pf_type = typeof(get_power_flow_data(pf_e_data)) + @warn "Placeholder for power flow write back to optimization container, $pf_type, $key" return end From b6fc453fd06473e33e3fe069c1ec4447f739e322 Mon Sep 17 00:00:00 2001 From: GabrielKS <23368820+GabrielKS@users.noreply.github.com> Date: Wed, 16 Oct 2024 18:22:53 -0600 Subject: [PATCH 30/39] Trait-based special behavior for power flow aux vars --- src/core/auxiliary_variables.jl | 7 +++++++ src/core/optimization_container.jl | 20 +++++++++++--------- src/network_models/power_flow_evaluation.jl | 6 +++++- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/core/auxiliary_variables.jl b/src/core/auxiliary_variables.jl index 4c6a9efd05..0e4ac6cf42 100644 --- a/src/core/auxiliary_variables.jl +++ b/src/core/auxiliary_variables.jl @@ -36,3 +36,10 @@ struct PowerFlowLineActivePower <: AuxVariableType end convert_result_to_natural_units(::Type{PowerOutput}) = true convert_result_to_natural_units(::Type{PowerFlowLineReactivePower}) = true convert_result_to_natural_units(::Type{PowerFlowLineActivePower}) = true + +"Whether the auxiliary variable is calculated using a `PowerFlowEvaluationModel`" +is_from_power_flow(::Type{<:AuxVariableType}) = false +is_from_power_flow(::Type{PowerFlowVoltageAngle}) = true +is_from_power_flow(::Type{PowerFlowVoltageMagnitude}) = true +is_from_power_flow(::Type{PowerFlowLineReactivePower}) = true +is_from_power_flow(::Type{PowerFlowLineActivePower}) = true diff --git a/src/core/optimization_container.jl b/src/core/optimization_container.jl index 478272414a..75d1227e0a 100644 --- a/src/core/optimization_container.jl +++ b/src/core/optimization_container.jl @@ -1641,23 +1641,25 @@ function deserialize_key(container::OptimizationContainer, name::AbstractString) end function calculate_aux_variables!(container::OptimizationContainer, system::PSY.System) + aux_var_keys = keys(get_aux_variables(container)) + pf_aux_var_keys = filter(is_from_power_flow ∘ get_entry_type, aux_var_keys) + non_pf_aux_var_keys = setdiff(aux_var_keys, pf_aux_var_keys) + # We should only have power flow aux vars if we have power flow evaluators + @assert isempty(pf_aux_var_keys) || !isempty(get_power_flow_evaluation_data(container)) + reset_power_flow_is_solved!(container) - # PERF currently we update the aux vars after each power flow because aux vars are how - # power flows pass information to each other. This shouldn't be a huge problem because - # there shouldn't ever be more than a few power flows, but we could record which aux - # vars are power flow-related and calculate the rest only once at the end. + # Power flow-related aux vars get calculated once per power flow for (i, pf_e_data) in enumerate(get_power_flow_evaluation_data(container)) @debug "Processing power flow $i" solve_powerflow!(pf_e_data, container) - for key in keys(get_aux_variables(container)) + for key in pf_aux_var_keys calculate_aux_variable_value!(container, key, system) end end - if isempty(get_power_flow_evaluation_data(container)) - for key in keys(get_aux_variables(container)) - calculate_aux_variable_value!(container, key, system) - end + # Other aux vars get calculated once at the end + for key in non_pf_aux_var_keys + calculate_aux_variable_value!(container, key, system) end return RunStatus.SUCCESSFULLY_FINALIZED end diff --git a/src/network_models/power_flow_evaluation.jl b/src/network_models/power_flow_evaluation.jl index 5aeb75610c..0722f0c400 100644 --- a/src/network_models/power_flow_evaluation.jl +++ b/src/network_models/power_flow_evaluation.jl @@ -93,6 +93,7 @@ function _make_injection_map!( for comp_name in comp_names name_bus_ix_map[comp_name] = temp_component_bus_map[comp_type][comp_name] end + pf_data_opt_container_map[key] = name_bus_ix_map end pf_e_data.injection_key_map = pf_data_opt_container_map @@ -172,11 +173,14 @@ function add_power_flow_data!( _add_aux_variables!(container, bus_aux_var_components) end +asdf = 1 function _write_value_to_pf_data!( pf_data::PFS.PowerFlowData, container::OptimizationContainer, key::OptimizationContainerKey, bus_index_map) + @show key + PowerSimulations.asdf = container result = lookup_value(container, key) for (device_name, index) in bus_index_map injection_values = result[device_name, :] @@ -243,6 +247,6 @@ function calculate_aux_variable_value!(container::OptimizationContainer, # TODO read data back from the power flow pf_e_data = latest_solved_power_flow_evaluation_data(container) pf_type = typeof(get_power_flow_data(pf_e_data)) - @warn "Placeholder for power flow write back to optimization container, $pf_type, $key" + # @warn "Placeholder for power flow write back to optimization container, $pf_type, $key" return end From eb51cfb3961668df34474f9a492e52da58ea770c Mon Sep 17 00:00:00 2001 From: GabrielKS <23368820+GabrielKS@users.noreply.github.com> Date: Wed, 16 Oct 2024 18:50:06 -0600 Subject: [PATCH 31/39] Scaffold subtype-based updating of power flow aux vars --- src/core/auxiliary_variables.jl | 18 +++---- src/network_models/power_flow_evaluation.jl | 53 ++++++++++++--------- 2 files changed, 40 insertions(+), 31 deletions(-) diff --git a/src/core/auxiliary_variables.jl b/src/core/auxiliary_variables.jl index 0e4ac6cf42..50adb7ba91 100644 --- a/src/core/auxiliary_variables.jl +++ b/src/core/auxiliary_variables.jl @@ -13,25 +13,30 @@ Auxiliary Variable for Thermal Generation Models that solve for power above min """ struct PowerOutput <: AuxVariableType end +""" +Auxiliary Variables that are calculated using a `PowerFlowEvaluationModel` +""" +abstract type PowerFlowAuxVariableType <: AuxVariableType end + """ Auxiliary Variable for the bus angle results from power flow evaluation """ -struct PowerFlowVoltageAngle <: AuxVariableType end +struct PowerFlowVoltageAngle <: PowerFlowAuxVariableType end """ Auxiliary Variable for the bus voltage magnitued results from power flow evaluation """ -struct PowerFlowVoltageMagnitude <: AuxVariableType end +struct PowerFlowVoltageMagnitude <: PowerFlowAuxVariableType end """ Auxiliary Variable for the line reactive flow from power flow evaluation """ -struct PowerFlowLineReactivePower <: AuxVariableType end +struct PowerFlowLineReactivePower <: PowerFlowAuxVariableType end """ Auxiliary Variable for the line active flow from power flow evaluation """ -struct PowerFlowLineActivePower <: AuxVariableType end +struct PowerFlowLineActivePower <: PowerFlowAuxVariableType end convert_result_to_natural_units(::Type{PowerOutput}) = true convert_result_to_natural_units(::Type{PowerFlowLineReactivePower}) = true @@ -39,7 +44,4 @@ convert_result_to_natural_units(::Type{PowerFlowLineActivePower}) = true "Whether the auxiliary variable is calculated using a `PowerFlowEvaluationModel`" is_from_power_flow(::Type{<:AuxVariableType}) = false -is_from_power_flow(::Type{PowerFlowVoltageAngle}) = true -is_from_power_flow(::Type{PowerFlowVoltageMagnitude}) = true -is_from_power_flow(::Type{PowerFlowLineReactivePower}) = true -is_from_power_flow(::Type{PowerFlowLineActivePower}) = true +is_from_power_flow(::Type{<:PowerFlowAuxVariableType}) = true diff --git a/src/network_models/power_flow_evaluation.jl b/src/network_models/power_flow_evaluation.jl index 0722f0c400..fcb8420206 100644 --- a/src/network_models/power_flow_evaluation.jl +++ b/src/network_models/power_flow_evaluation.jl @@ -1,8 +1,3 @@ -function add_power_flow_data!(::OptimizationContainer, ::Nothing, ::PSY.System) - # NO OP function - return -end - function _add_aux_variables!( container::OptimizationContainer, component_map::Dict{Type{<:AuxVariableType}, <:Set{<:Tuple{DataType, Any}}}, @@ -100,20 +95,21 @@ function _make_injection_map!( return end -# Trait that determines what branch aux vars we can get from each type of power flow -branch_aux_vars(::PFS.ACPowerFlow) = [PowerFlowLineActivePower, PowerFlowLineReactivePower] -branch_aux_vars(::PFS.DCPowerFlow) = [PowerFlowLineActivePower, PowerFlowLineReactivePower] -branch_aux_vars(::PFS.PTDFDCPowerFlow) = [PowerFlowLineActivePower] -branch_aux_vars(::PFS.vPTDFDCPowerFlow) = [PowerFlowLineActivePower] -branch_aux_vars(::PFS.PSSEExportPowerFlow) = +# Trait that determines what branch aux vars we can get from each PowerFlowContainer +branch_aux_vars(::PFS.ACPowerFlowData) = [PowerFlowLineActivePower, PowerFlowLineReactivePower] +branch_aux_vars(::PFS.ABAPowerFlowData) = + [PowerFlowLineActivePower, PowerFlowLineReactivePower] +branch_aux_vars(::PFS.PTDFPowerFlowData) = [PowerFlowLineActivePower] +branch_aux_vars(::PFS.vPTDFPowerFlowData) = [PowerFlowLineActivePower] +branch_aux_vars(::PFS.PSSEExporter) = DataType[] # Same for bus aux vars -bus_aux_vars(::PFS.ACPowerFlow) = [PowerFlowVoltageAngle, PowerFlowVoltageMagnitude] -bus_aux_vars(::PFS.DCPowerFlow) = [PowerFlowVoltageAngle] -bus_aux_vars(::PFS.PTDFDCPowerFlow) = Vector{DataType}[] -bus_aux_vars(::PFS.vPTDFDCPowerFlow) = Vector{DataType}[] -bus_aux_vars(::PFS.PSSEExportPowerFlow) = [PowerFlowVoltageAngle, PowerFlowVoltageMagnitude] +bus_aux_vars(::PFS.ACPowerFlowData) = [PowerFlowVoltageAngle, PowerFlowVoltageMagnitude] +bus_aux_vars(::PFS.ABAPowerFlowData) = [PowerFlowVoltageAngle] +bus_aux_vars(::PFS.PTDFPowerFlowData) = DataType[] +bus_aux_vars(::PFS.vPTDFPowerFlowData) = DataType[] +bus_aux_vars(::PFS.PSSEExporter) = DataType[] _get_branch_component_tuples(pfd::PFS.PowerFlowData) = zip(PFS.get_branch_type(pfd), keys(PFS.get_branch_lookup(pfd))) @@ -146,8 +142,8 @@ function add_power_flow_data!( pf_data = PFS.make_power_flow_container(evaluator, sys; time_steps = length(get_time_steps(container))) pf_e_data = PowerFlowEvaluationData(pf_data) - my_branch_aux_vars = branch_aux_vars(evaluator) - my_bus_aux_vars = bus_aux_vars(evaluator) + my_branch_aux_vars = branch_aux_vars(pf_data) + my_bus_aux_vars = bus_aux_vars(pf_data) my_branch_components = _get_branch_component_tuples(pf_data) for branch_aux_var in my_branch_aux_vars @@ -241,12 +237,23 @@ function solve_powerflow!( return end +# Currently nothing to write back to the optimization container from a PSSEExporter +calculate_aux_variable_value!(::OptimizationContainer, + ::AuxVarKey{T, <:Any} where {T <: PowerFlowAuxVariableType}, + ::PSY.System, ::PowerFlowEvaluationData{PFS.PSSEExporter}) = nothing + +function calculate_aux_variable_value!(container::OptimizationContainer, + key::AuxVarKey{T, <:Any} where {T <: PowerFlowAuxVariableType}, + system::PSY.System, pf_e_data::PowerFlowEvaluationData{<:PFS.PowerFlowData}) + @warn "TODO" # TODO +end + function calculate_aux_variable_value!(container::OptimizationContainer, - key, + key::AuxVarKey{T, <:Any} where {T <: PowerFlowAuxVariableType}, system::PSY.System) - # TODO read data back from the power flow pf_e_data = latest_solved_power_flow_evaluation_data(container) - pf_type = typeof(get_power_flow_data(pf_e_data)) - # @warn "Placeholder for power flow write back to optimization container, $pf_type, $key" - return + pf_data = get_power_flow_data(pf_e_data) + # Skip the aux vars that the current power flow isn't meant to update + (key in branch_aux_vars(pf_data) || key in bus_aux_vars(pf_data)) && return + calculate_aux_variable_value!(container, key, system, pf_e_data) end From dfd74059d7d8a128a2c09084cdd5049c74d98b1f Mon Sep 17 00:00:00 2001 From: GabrielKS <23368820+GabrielKS@users.noreply.github.com> Date: Wed, 16 Oct 2024 23:30:49 -0600 Subject: [PATCH 32/39] Update power flow aux vars from `PowerFlowData` --- src/core/power_flow_data_wrapper.jl | 12 +++--- src/network_models/power_flow_evaluation.jl | 42 +++++++++++++++------ 2 files changed, 37 insertions(+), 17 deletions(-) diff --git a/src/core/power_flow_data_wrapper.jl b/src/core/power_flow_data_wrapper.jl index 743c51a1c9..b88adaa68e 100644 --- a/src/core/power_flow_data_wrapper.jl +++ b/src/core/power_flow_data_wrapper.jl @@ -1,12 +1,12 @@ mutable struct PowerFlowEvaluationData{T <: PFS.PowerFlowContainer} power_flow_data::T """ - Records which PSI keys get used to update the data and how they are mapped to it. For - `PowerFlowData`, values are `Dict{String, Int}` specifying component name, matrix index; - for `SystemPowerFlowContainer`, values are Set{String} merely specifying component - names. + Records which PSI keys are read as input to the power flow and how the data are mapped. + For `PowerFlowData`, values are `Dict{String, Int}` mapping component name to matrix + index; for `SystemPowerFlowContainer`, values are Dict{String, String} mapping component + name to component name. """ - injection_key_map::Dict{<:OptimizationContainerKey, <:Any} + input_key_map::Dict{<:OptimizationContainerKey, <:Any} is_solved::Bool end @@ -19,4 +19,4 @@ function PowerFlowEvaluationData(power_flow_data::T) where {T <: PFS.PowerFlowCo end get_power_flow_data(ped::PowerFlowEvaluationData) = ped.power_flow_data -get_injection_key_map(ped::PowerFlowEvaluationData) = ped.injection_key_map +get_input_key_map(ped::PowerFlowEvaluationData) = ped.input_key_map diff --git a/src/network_models/power_flow_evaluation.jl b/src/network_models/power_flow_evaluation.jl index fcb8420206..aec981e837 100644 --- a/src/network_models/power_flow_evaluation.jl +++ b/src/network_models/power_flow_evaluation.jl @@ -54,7 +54,7 @@ function _make_temp_component_bus_map(::PFS.SystemPowerFlowContainer, sys::PSY.S return temp_component_bus_map end -function _make_injection_map!( +function _make_pf_input_map!( pf_e_data::PowerFlowEvaluationData, container::OptimizationContainer, sys::PSY.System, @@ -91,15 +91,14 @@ function _make_injection_map!( pf_data_opt_container_map[key] = name_bus_ix_map end - pf_e_data.injection_key_map = pf_data_opt_container_map + pf_e_data.input_key_map = pf_data_opt_container_map return end # Trait that determines what branch aux vars we can get from each PowerFlowContainer branch_aux_vars(::PFS.ACPowerFlowData) = [PowerFlowLineActivePower, PowerFlowLineReactivePower] -branch_aux_vars(::PFS.ABAPowerFlowData) = - [PowerFlowLineActivePower, PowerFlowLineReactivePower] +branch_aux_vars(::PFS.ABAPowerFlowData) = [PowerFlowLineActivePower] branch_aux_vars(::PFS.PTDFPowerFlowData) = [PowerFlowLineActivePower] branch_aux_vars(::PFS.vPTDFPowerFlowData) = [PowerFlowLineActivePower] branch_aux_vars(::PFS.PSSEExporter) = DataType[] @@ -161,7 +160,7 @@ function add_power_flow_data!( get!(bus_aux_var_components, bus_aux_var, Set{Tuple{<:DataType, <:Int}}()) push!.(Ref(to_add_to), my_bus_components) end - _make_injection_map!(pf_e_data, container, sys) + _make_pf_input_map!(pf_e_data, container, sys) push!(container.power_flow_evaluation_data, pf_e_data) end @@ -175,7 +174,6 @@ function _write_value_to_pf_data!( container::OptimizationContainer, key::OptimizationContainerKey, bus_index_map) - @show key PowerSimulations.asdf = container result = lookup_value(container, key) for (device_name, index) in bus_index_map @@ -193,7 +191,7 @@ function update_pf_data!( ) pf_data = get_power_flow_data(pf_e_data) PFS.clear_injection_data!(pf_data) - key_map = get_injection_key_map(pf_e_data) + key_map = get_input_key_map(pf_e_data) for (key, bus_index_map) in key_map _write_value_to_pf_data!(pf_data, container, key, bus_index_map) end @@ -217,7 +215,7 @@ function update_pf_data!( container::OptimizationContainer, ) pf_data = get_power_flow_data(pf_e_data) - key_map = get_injection_key_map(pf_e_data) + key_map = get_input_key_map(pf_e_data) update_pf_system!(PFS.get_system(pf_data), container, key_map) return end @@ -242,10 +240,32 @@ calculate_aux_variable_value!(::OptimizationContainer, ::AuxVarKey{T, <:Any} where {T <: PowerFlowAuxVariableType}, ::PSY.System, ::PowerFlowEvaluationData{PFS.PSSEExporter}) = nothing +_get_pf_result(::Type{PowerFlowVoltageAngle}, pf_data::PFS.PowerFlowData) = + PFS.get_bus_angles(pf_data) +_get_pf_result(::Type{PowerFlowVoltageMagnitude}, pf_data::PFS.PowerFlowData) = + PFS.get_bus_magnitude(pf_data) +_get_pf_result(::Type{PowerFlowLineActivePower}, pf_data::PFS.PowerFlowData) = + PFS.get_branch_flow_values(pf_data) +# TODO implement method for PowerFlowLineReactivePower -- I don't think we have a PowerFlowData field for this? +# _fetch_pf_result(pf_data::PFS.PowerFlowData, ::Type{PowerFlowLineActivePower}) = ... + +_get_pf_lookup(::Type{<:PSY.Bus}, pf_data::PFS.PowerFlowData) = PFS.get_bus_lookup(pf_data) +_get_pf_lookup(::Type{<:PSY.Branch}, pf_data::PFS.PowerFlowData) = + PFS.get_branch_lookup(pf_data) + function calculate_aux_variable_value!(container::OptimizationContainer, - key::AuxVarKey{T, <:Any} where {T <: PowerFlowAuxVariableType}, - system::PSY.System, pf_e_data::PowerFlowEvaluationData{<:PFS.PowerFlowData}) - @warn "TODO" # TODO + key::AuxVarKey{T, U}, + system::PSY.System, pf_e_data::PowerFlowEvaluationData{<:PFS.PowerFlowData}, +) where {T <: PowerFlowAuxVariableType, U} + @debug "Updating $key from PowerFlowData" + pf_data = get_power_flow_data(pf_e_data) + src = _get_pf_result(T, pf_data) + lookup = _get_pf_lookup(U, pf_data) + dest = get_aux_variable(container, key) + for component_id in axes(dest, 1) # these are bus numbers or branch names + dest[component_id, :] = src[lookup[component_id], :] + end + return end function calculate_aux_variable_value!(container::OptimizationContainer, From c0ac0be56cb6744fd5a236db6bef79cb70ca6448 Mon Sep 17 00:00:00 2001 From: GabrielKS <23368820+GabrielKS@users.noreply.github.com> Date: Thu, 17 Oct 2024 02:13:36 -0600 Subject: [PATCH 33/39] Complete prototype power flow in the loop -> export implementation It works! --- src/core/power_flow_data_wrapper.jl | 6 +- src/network_models/power_flow_evaluation.jl | 109 +++++++++++++------- 2 files changed, 74 insertions(+), 41 deletions(-) diff --git a/src/core/power_flow_data_wrapper.jl b/src/core/power_flow_data_wrapper.jl index b88adaa68e..08da670cd4 100644 --- a/src/core/power_flow_data_wrapper.jl +++ b/src/core/power_flow_data_wrapper.jl @@ -2,9 +2,9 @@ mutable struct PowerFlowEvaluationData{T <: PFS.PowerFlowContainer} power_flow_data::T """ Records which PSI keys are read as input to the power flow and how the data are mapped. - For `PowerFlowData`, values are `Dict{String, Int}` mapping component name to matrix - index; for `SystemPowerFlowContainer`, values are Dict{String, String} mapping component - name to component name. + For `PowerFlowData`, values are `Dict{String, Int64}` mapping component name to matrix + index of bus; for `SystemPowerFlowContainer`, values are Dict{Union{String, Int64}, + Union{String, Int64}} mapping component name/bus number to component name/bus number. """ input_key_map::Dict{<:OptimizationContainerKey, <:Any} is_solved::Bool diff --git a/src/network_models/power_flow_evaluation.jl b/src/network_models/power_flow_evaluation.jl index aec981e837..1cac22498d 100644 --- a/src/network_models/power_flow_evaluation.jl +++ b/src/network_models/power_flow_evaluation.jl @@ -18,40 +18,50 @@ function _add_aux_variables!( end end -# Trait that determines what keys serve as input to each type of power flow -# Currently there is only the default; written this way so as to be easily overridable -pf_input_keys(::PFS.PowerFlowContainer) = +# Trait that determines what keys serve as input to each type of power flow, if they exist +pf_input_keys(::PFS.PowerFlowData) = [ActivePowerVariable, PowerOutput, ActivePowerTimeSeriesParameter] +pf_input_keys(::PFS.PSSEExporter) = + [ActivePowerVariable, PowerOutput, ActivePowerTimeSeriesParameter, + PowerFlowVoltageAngle, PowerFlowVoltageMagnitude] # Maps the StaticInjection component type by name to the # index in the PowerFlow data arrays going from Bus number to bus index -function _make_temp_component_bus_map(pf_data::PFS.PowerFlowData, sys::PSY.System) - temp_component_bus_map = Dict{DataType, Dict{String, Int}}() +function _make_temp_component_map(pf_data::PFS.PowerFlowData, sys::PSY.System) + temp_component_map = Dict{DataType, Dict{String, Int}}() available_injectors = PSY.get_components(PSY.get_available, PSY.StaticInjection, sys) bus_lookup = PFS.get_bus_lookup(pf_data) for comp in available_injectors comp_type = typeof(comp) - bus_dict = get!(temp_component_bus_map, comp_type, Dict{String, Int}()) + bus_dict = get!(temp_component_map, comp_type, Dict{String, Int}()) bus_number = PSY.get_number(PSY.get_bus(comp)) bus_dict[get_name(comp)] = bus_lookup[bus_number] end - return temp_component_bus_map + return temp_component_map end -# Creates Sets of components by type -function _make_temp_component_bus_map(::PFS.SystemPowerFlowContainer, sys::PSY.System) - temp_component_bus_map = Dict{DataType, Dict{String, String}}() - # TODO `ComponentSelector` use case - available_injectors = - collect(PSY.get_components(PSY.get_available, PSY.StaticInjection, sys)) - for comp_type in unique(typeof.(available_injectors)) - temp_component_bus_map[comp_type] = +_get_temp_component_map_id(comp::PSY.Component) = PSY.get_name(comp) +_get_temp_component_map_id(comp::PSY.Bus) = PSY.get_number(comp) + +# Creates dicts of components by type +function _make_temp_component_map(::PFS.SystemPowerFlowContainer, sys::PSY.System) + temp_component_map = + Dict{DataType, Dict{Union{String, Int64}, Union{String, Int64}}}() + # TODO don't hardcode the types here, handle get_available more elegantly, likely `ComponentSelector` use case + relevant_components = vcat( + collect.([ + PSY.get_components(PSY.get_available, PSY.StaticInjection, sys), + PSY.get_components(Union{PSY.Bus, PSY.Branch}, sys)], + )..., + ) + for comp_type in unique(typeof.(relevant_components)) + temp_component_map[comp_type] = Dict( - PSY.get_name(c) => PSY.get_name(c) for - c in available_injectors if c isa comp_type + _get_temp_component_map_id(c) => _get_temp_component_map_id(c) for + c in relevant_components if c isa comp_type ) end - return temp_component_bus_map + return temp_component_map end function _make_pf_input_map!( @@ -60,9 +70,9 @@ function _make_pf_input_map!( sys::PSY.System, ) pf_data = get_power_flow_data(pf_e_data) - temp_component_bus_map = _make_temp_component_bus_map(pf_data, sys) - map_type = valtype(temp_component_bus_map) # Dict{String, Int} for PowerFlowData, Dict{String, String} for SystemPowerFlowContainer - injection_keys = pf_input_keys(pf_data) + temp_component_map = _make_temp_component_map(pf_data, sys) + map_type = valtype(temp_component_map) # Dict{String, Int} for PowerFlowData, Dict{Union{String, Int64}, Union{String, Int64}} for SystemPowerFlowContainer + input_keys = pf_input_keys(pf_data) # Second map that persists to store the bus index that the variable # has to be added/substracted to in the power flow data dictionary @@ -74,7 +84,7 @@ function _make_pf_input_map!( get_parameters(container), ]) # Skip irrelevant keys - (get_entry_type(key) in injection_keys) || continue + (get_entry_type(key) in input_keys) || continue comp_type = get_component_type(key) # Skip types that have already been handled (prefer variable over aux variable, aux variable over parameter) @@ -86,7 +96,7 @@ function _make_pf_input_map!( comp_names = (key isa ParameterKey) ? get_component_names(get_attributes(val)) : axes(val)[1] for comp_name in comp_names - name_bus_ix_map[comp_name] = temp_component_bus_map[comp_type][comp_name] + name_bus_ix_map[comp_name] = temp_component_map[comp_type][comp_name] end pf_data_opt_container_map[key] = name_bus_ix_map end @@ -160,12 +170,16 @@ function add_power_flow_data!( get!(bus_aux_var_components, bus_aux_var, Set{Tuple{<:DataType, <:Int}}()) push!.(Ref(to_add_to), my_bus_components) end - _make_pf_input_map!(pf_e_data, container, sys) push!(container.power_flow_evaluation_data, pf_e_data) end _add_aux_variables!(container, branch_aux_var_components) _add_aux_variables!(container, bus_aux_var_components) + + # Make the input maps after adding aux vars so output of one power flow can be input of another + for pf_e_data in get_power_flow_evaluation_data(container) + _make_pf_input_map!(pf_e_data, container, sys) + end end asdf = 1 @@ -173,12 +187,14 @@ function _write_value_to_pf_data!( pf_data::PFS.PowerFlowData, container::OptimizationContainer, key::OptimizationContainerKey, - bus_index_map) + component_map) PowerSimulations.asdf = container result = lookup_value(container, key) - for (device_name, index) in bus_index_map + for (device_name, index) in component_map injection_values = result[device_name, :] for t in axes(result)[2] + # NOTE in the future we may want to update more than just + # bus_activepower_injection; see update_pf_system! for a design for this pf_data.bus_activepower_injection[index, t] += jump_value(injection_values[t]) end end @@ -191,32 +207,49 @@ function update_pf_data!( ) pf_data = get_power_flow_data(pf_e_data) PFS.clear_injection_data!(pf_data) - key_map = get_input_key_map(pf_e_data) - for (key, bus_index_map) in key_map - _write_value_to_pf_data!(pf_data, container, key, bus_index_map) + input_map = get_input_key_map(pf_e_data) + for (key, component_map) in input_map + _write_value_to_pf_data!(pf_data, container, key, component_map) end return end -function update_pf_system!(sys::PSY.System, container::OptimizationContainer, key_map) +_lookup_component(type::Type{<:PSY.Component}, sys::PSY.System, id::AbstractString) = + PSY.get_component(type, sys, id) +_lookup_component(::Type{<:PSY.Bus}, sys::PSY.System, id::Int) = + PSY.get_bus(sys, id) + +_update_component( + ::Type{<:Union{ActivePowerVariable, PowerOutput, ActivePowerTimeSeriesParameter}}, + comp::PSY.Component, + value, +) = + comp.active_power = value +_update_component(::Type{PowerFlowVoltageAngle}, comp::PSY.Component, value) = + comp.angle = value +_update_component(::Type{PowerFlowVoltageMagnitude}, comp::PSY.Component, value) = + comp.magnitude = value + +function update_pf_system!(sys::PSY.System, container::OptimizationContainer, input_map) TIME = 1 # TODO figure out how to handle multiple time periods here - for (key, bus_index_map) in key_map + for (key, component_map) in input_map result = lookup_value(container, key) - for (device_name, _) in bus_index_map - injection_values = result[device_name, :] - comp = PSY.get_component(get_component_type(key), sys, device_name) - comp.active_power = jump_value(injection_values[TIME]) + for (device_id, _) in component_map + injection_values = result[device_id, :] + comp = _lookup_component(get_component_type(key), sys, device_id) + _update_component(get_entry_type(key), comp, jump_value(injection_values[TIME])) end end end function update_pf_data!( - pf_e_data::PowerFlowEvaluationData{<:PFS.SystemPowerFlowContainer}, + pf_e_data::PowerFlowEvaluationData{PFS.PSSEExporter}, container::OptimizationContainer, ) pf_data = get_power_flow_data(pf_e_data) - key_map = get_input_key_map(pf_e_data) - update_pf_system!(PFS.get_system(pf_data), container, key_map) + input_map = get_input_key_map(pf_e_data) + update_pf_system!(PFS.get_system(pf_data), container, input_map) + isnothing(pf_data.step) || (pf_data.step += 1) return end From 9e1f30a56402e96613aa9c4f762f47b35b723905 Mon Sep 17 00:00:00 2001 From: GabrielKS <23368820+GabrielKS@users.noreply.github.com> Date: Mon, 28 Oct 2024 11:56:21 -0600 Subject: [PATCH 34/39] Power flow in the loop: handle load special cases --- src/network_models/power_flow_evaluation.jl | 37 ++++++++++++++++----- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/src/network_models/power_flow_evaluation.jl b/src/network_models/power_flow_evaluation.jl index 1cac22498d..1954b83f39 100644 --- a/src/network_models/power_flow_evaluation.jl +++ b/src/network_models/power_flow_evaluation.jl @@ -182,20 +182,33 @@ function add_power_flow_data!( end end -asdf = 1 +# How to update the PowerFlowData given a component type. A bit duplicative of code in PowerFlows.jl. +_update_pf_data_component!( + pf_data::PFS.PowerFlowData, + ::Type{<:PSY.StaticInjection}, + index, + t, + value, +) = (pf_data.bus_activepower_injection[index, t] += value) +_update_pf_data_component!( + pf_data::PFS.PowerFlowData, + ::Type{<:PSY.ElectricLoad}, + index, + t, + value, +) = (pf_data.bus_activepower_withdrawals[index, t] += value) + function _write_value_to_pf_data!( pf_data::PFS.PowerFlowData, container::OptimizationContainer, key::OptimizationContainerKey, component_map) - PowerSimulations.asdf = container result = lookup_value(container, key) for (device_name, index) in component_map injection_values = result[device_name, :] - for t in axes(result)[2] - # NOTE in the future we may want to update more than just - # bus_activepower_injection; see update_pf_system! for a design for this - pf_data.bus_activepower_injection[index, t] += jump_value(injection_values[t]) + for t in get_time_steps(container) + value = jump_value(injection_values[t]) + _update_pf_data_component!(pf_data, get_component_type(key), index, t, value) end end return @@ -223,8 +236,13 @@ _update_component( ::Type{<:Union{ActivePowerVariable, PowerOutput, ActivePowerTimeSeriesParameter}}, comp::PSY.Component, value, -) = - comp.active_power = value +) = (comp.active_power = value) +# Sign is flipped for loads (TODO can we rely on some existing function that encodes this information?) +_update_component( + ::Type{<:Union{ActivePowerVariable, PowerOutput, ActivePowerTimeSeriesParameter}}, + comp::PSY.ElectricLoad, + value, +) = (comp.active_power = -value) _update_component(::Type{PowerFlowVoltageAngle}, comp::PSY.Component, value) = comp.angle = value _update_component(::Type{PowerFlowVoltageMagnitude}, comp::PSY.Component, value) = @@ -237,7 +255,8 @@ function update_pf_system!(sys::PSY.System, container::OptimizationContainer, in for (device_id, _) in component_map injection_values = result[device_id, :] comp = _lookup_component(get_component_type(key), sys, device_id) - _update_component(get_entry_type(key), comp, jump_value(injection_values[TIME])) + val = jump_value(injection_values[TIME]) + _update_component(get_entry_type(key), comp, val) end end end From 1507b14720fd43370421e8f0931412acd7e3603c Mon Sep 17 00:00:00 2001 From: GabrielKS <23368820+GabrielKS@users.noreply.github.com> Date: Mon, 28 Oct 2024 13:00:32 -0600 Subject: [PATCH 35/39] Power flow in the loop: support multi period export --- src/network_models/power_flow_evaluation.jl | 39 +++++++++++++++++---- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/src/network_models/power_flow_evaluation.jl b/src/network_models/power_flow_evaluation.jl index 1954b83f39..c0a9022bfd 100644 --- a/src/network_models/power_flow_evaluation.jl +++ b/src/network_models/power_flow_evaluation.jl @@ -248,27 +248,44 @@ _update_component(::Type{PowerFlowVoltageAngle}, comp::PSY.Component, value) = _update_component(::Type{PowerFlowVoltageMagnitude}, comp::PSY.Component, value) = comp.magnitude = value -function update_pf_system!(sys::PSY.System, container::OptimizationContainer, input_map) - TIME = 1 # TODO figure out how to handle multiple time periods here +function update_pf_system!( + sys::PSY.System, + container::OptimizationContainer, + input_map::Dict{<:OptimizationContainerKey, <:Any}, + time_step::Int, +) for (key, component_map) in input_map result = lookup_value(container, key) for (device_id, _) in component_map injection_values = result[device_id, :] comp = _lookup_component(get_component_type(key), sys, device_id) - val = jump_value(injection_values[TIME]) + val = jump_value(injection_values[time_step]) _update_component(get_entry_type(key), comp, val) end end end +""" +Update a `PowerFlowEvaluationData` containing a `PowerFlowContainer` that does not +`supports_multi_period` using a single `time_step` of the `OptimizationContainer`. To +properly keep track of outer step number, time steps must be passed in sequentially, +starting with 1. +""" function update_pf_data!( pf_e_data::PowerFlowEvaluationData{PFS.PSSEExporter}, container::OptimizationContainer, + time_step::Int, ) pf_data = get_power_flow_data(pf_e_data) input_map = get_input_key_map(pf_e_data) - update_pf_system!(PFS.get_system(pf_data), container, input_map) - isnothing(pf_data.step) || (pf_data.step += 1) + update_pf_system!(PFS.get_system(pf_data), container, input_map, time_step) + if !isnothing(pf_data.step) + outer_step, _ = pf_data.step + # time_step == 1 means we have rolled over to a new outer step + # (TODO it works but seems a little brittle, consider redesigning) + (time_step == 1) && (outer_step += 1) + pf_data.step = (outer_step, time_step) + end return end @@ -281,8 +298,16 @@ end function solve_powerflow!( pf_e_data::PowerFlowEvaluationData, container::OptimizationContainer) - update_pf_data!(pf_e_data, container) - PFS.solve_powerflow!(get_power_flow_data(pf_e_data)) + pf_data = get_power_flow_data(pf_e_data) + if PFS.supports_multi_period(pf_data) + update_pf_data!(pf_e_data, container) + PFS.solve_powerflow!(pf_data) + else + for t in get_time_steps(container) + update_pf_data!(pf_e_data, container, t) + PFS.solve_powerflow!(pf_data) + end + end pf_e_data.is_solved = true return end From 9ec135601c0f8e017a3417f1b06da530e8b2d54e Mon Sep 17 00:00:00 2001 From: GabrielKS <23368820+GabrielKS@users.noreply.github.com> Date: Mon, 28 Oct 2024 14:38:52 -0600 Subject: [PATCH 36/39] Misc code cleanup following self-review --- src/network_models/power_flow_evaluation.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network_models/power_flow_evaluation.jl b/src/network_models/power_flow_evaluation.jl index c0a9022bfd..6858d6338e 100644 --- a/src/network_models/power_flow_evaluation.jl +++ b/src/network_models/power_flow_evaluation.jl @@ -92,7 +92,6 @@ function _make_pf_input_map!( push!(added_injection_types, comp_type) name_bus_ix_map = map_type() - # Maybe this should be rewritten as multiple dispatch but it should not be rewritten as a copypasted loop comp_names = (key isa ParameterKey) ? get_component_names(get_attributes(val)) : axes(val)[1] for comp_name in comp_names @@ -180,6 +179,7 @@ function add_power_flow_data!( for pf_e_data in get_power_flow_evaluation_data(container) _make_pf_input_map!(pf_e_data, container, sys) end + return end # How to update the PowerFlowData given a component type. A bit duplicative of code in PowerFlows.jl. From ad25918f57e2f42ff8e5ea3a65180ab65220c3be Mon Sep 17 00:00:00 2001 From: GabrielKS <23368820+GabrielKS@users.noreply.github.com> Date: Mon, 4 Nov 2024 12:16:27 -0700 Subject: [PATCH 37/39] Add basic tests of power flow in the loop --- test/includes.jl | 1 + test/test_basic_model_structs.jl | 5 ++ test/test_simulation_results.jl | 78 +++++++++++++++++++++++++++++--- 3 files changed, 78 insertions(+), 6 deletions(-) diff --git a/test/includes.jl b/test/includes.jl index 647e724e94..ba8c7baeb4 100644 --- a/test/includes.jl +++ b/test/includes.jl @@ -8,6 +8,7 @@ using HydroPowerSimulations import PowerSystemCaseBuilder: PSITestSystems using PowerNetworkMatrices using StorageSystemsSimulations +using PowerFlows # Test Packages using Test diff --git a/test/test_basic_model_structs.jl b/test/test_basic_model_structs.jl index a5c37eb5c3..02cd696a8d 100644 --- a/test/test_basic_model_structs.jl +++ b/test/test_basic_model_structs.jl @@ -6,6 +6,11 @@ end @testset "NetworkModel Tests" begin @test_throws ArgumentError NetworkModel(PM.AbstractPowerModel) + @test NetworkModel( + PTDFPowerModel; + use_slacks = true, + power_flow_evaluation = [DCPowerFlow(), PSSEExportPowerFlow(:v33, "exports", true)], + ) isa NetworkModel end #= diff --git a/test/test_simulation_results.jl b/test/test_simulation_results.jl index d3ec744777..5ab275dc8f 100644 --- a/test/test_simulation_results.jl +++ b/test/test_simulation_results.jl @@ -155,21 +155,31 @@ function run_simulation( export_path; in_memory = false, system_to_file = true, + uc_network_model = nothing, + ed_network_model = nothing, ) template_uc = get_template_basic_uc_simulation() template_ed = get_template_nomin_ed_simulation() + isnothing(uc_network_model) && ( + uc_network_model = + NetworkModel(CopperPlatePowerModel; duals = [CopperPlateBalanceConstraint]) + ) + isnothing(ed_network_model) && ( + ed_network_model = + NetworkModel( + CopperPlatePowerModel; + duals = [CopperPlateBalanceConstraint], + use_slacks = true, + ) + ) set_device_model!(template_ed, InterruptiblePowerLoad, StaticPowerLoad) set_network_model!( template_uc, - NetworkModel(CopperPlatePowerModel; duals = [CopperPlateBalanceConstraint]), + uc_network_model, ) set_network_model!( template_ed, - NetworkModel( - CopperPlatePowerModel; - duals = [CopperPlateBalanceConstraint], - use_slacks = true, - ), + ed_network_model, ) models = SimulationModels(; decision_models = [ @@ -977,3 +987,59 @@ end test_decision_problem_results(results, sys_ed, sys_uc, in_memory) test_emulation_problem_results(results, in_memory) end + +function load_pf_export(root, export_subdir) + raw_path, md_path = get_psse_export_paths(export_subdir) + sys = System(joinpath(root, raw_path), JSON3.read(joinpath(root, md_path), Dict)) + # TODO I think the necessity of this might speak to a unit misinterpretation somewhere + set_units_base_system!(sys, "NATURAL_UNITS") + return sys +end + +@testset "Test power flow in the loop" begin + file_path = mktempdir(; cleanup = true) + export_path = mktempdir(; cleanup = true) + pf_path = mktempdir(; cleanup = true) + c_sys5_hy_uc = PSB.build_system(PSITestSystems, "c_sys5_hy_uc") + c_sys5_hy_ed = PSB.build_system(PSITestSystems, "c_sys5_hy_ed") + sim = run_simulation( + c_sys5_hy_uc, + c_sys5_hy_ed, + file_path, + export_path; + ed_network_model = NetworkModel( + CopperPlatePowerModel; + duals = [CopperPlateBalanceConstraint], + use_slacks = true, + power_flow_evaluation = + [DCPowerFlow(), PSSEExportPowerFlow(:v33, pf_path, true)], + ), + ) + results = SimulationResults(sim) + results_ed = get_decision_problem_results(results, "ED") + thermal_results = first( + values( + PSI.read_results_with_keys(results_ed, + [PSI.VariableKey(ActivePowerVariable, ThermalStandard)]), + ), + ) + first_result = first(thermal_results) + last_result = last(thermal_results) + + @test length(filter(x -> isdir(joinpath(pf_path, x)), readdir(pf_path))) == 48 * 12 + first_export = load_pf_export(pf_path, "export_1_1") + last_export = load_pf_export(pf_path, "export_48_12") + + # Test that the active powers written to the first and last exports line up with the real simulation results + for gen_name in get_name.(get_components(ThermalStandard, c_sys5_hy_ed)) + this_first_result = first_result[gen_name] + this_first_exported = + get_active_power(get_component(ThermalStandard, first_export, gen_name)) + @test isapprox(this_first_result, this_first_exported) + + this_last_result = last_result[gen_name] + this_last_exported = + get_active_power(get_component(ThermalStandard, last_export, gen_name)) + @test isapprox(this_last_result, this_last_exported) + end +end From 958e68db64debc7186c01171255b7bd9ffa5b990 Mon Sep 17 00:00:00 2001 From: GabrielKS <23368820+GabrielKS@users.noreply.github.com> Date: Mon, 4 Nov 2024 12:16:46 -0700 Subject: [PATCH 38/39] Update `DISABLED_TEST_FILES` --- test/runtests.jl | 47 +++++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index f6eaa26fe9..3b6797d8d1 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -9,28 +9,31 @@ Aqua.test_ambiguities(PowerSimulations) const LOG_FILE = "power-simulations-test.log" const DISABLED_TEST_FILES = [ -# "test_basic_model_structs.jl", -# "test_device_branch_constructors.jl", -# "test_device_hydro_generation_constructors.jl", -# "test_device_load_constructors.jl", -# "test_device_hybrid_generation_constructors.jl", -# "test_device_renewable_generation_constructors.jl", -# "test_device_storage_constructors.jl", -# "test_device_thermal_generation_constructors.jl", -# "test_jump_model_utils.jl", -# "test_model_decision.jl", -# "test_problem_template.jl", -# "test_model_emulation.jl", -# "test_network_constructors.jl", -# "test_services_constructor.jl", -# "test_simulation_models.jl", -# "test_simulation_sequence.jl", -# "test_simulation_build.jl", -# "test_initialization_problem.jl", -# "test_simulation_execute.jl", -# "test_simulation_results.jl", -# "test_simulation_results_export.jl", -# "test_simulation_store.jl", +# "test_basic_model_structs.jl" +# "test_device_branch_constructors.jl" +# "test_device_hvdc.jl" +# "test_device_load_constructors.jl" +# "test_device_renewable_generation_constructors.jl" +# "test_device_thermal_generation_constructors.jl" +# "test_formulation_combinations.jl" +# "test_ic_reconciliation.jl" +# "test_initialization_problem.jl" +# "test_model_decision.jl" +# "test_model_emulation.jl" +# "test_network_constructors.jl" +# "test_print.jl" +# "test_problem_template.jl" +# "test_recorder_events.jl" +# "test_services_constructor.jl" +# "test_simulation_build.jl" +# "test_simulation_execute.jl" +# "test_simulation_models.jl" +# "test_simulation_partitions.jl" +# "test_simulation_results.jl" +# "test_simulation_results_export.jl" +# "test_simulation_sequence.jl" +# "test_simulation_store.jl" +# "test_utils.jl" ] LOG_LEVELS = Dict( From e016521b08807294ed323ee7d97abf038074becc Mon Sep 17 00:00:00 2001 From: GabrielKS <23368820+GabrielKS@users.noreply.github.com> Date: Tue, 19 Nov 2024 13:49:03 -0700 Subject: [PATCH 39/39] PERF: don't use `get_bus(sys, number)`, it's O(n) --- src/network_models/power_flow_evaluation.jl | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/network_models/power_flow_evaluation.jl b/src/network_models/power_flow_evaluation.jl index 6858d6338e..5768b12415 100644 --- a/src/network_models/power_flow_evaluation.jl +++ b/src/network_models/power_flow_evaluation.jl @@ -40,13 +40,13 @@ function _make_temp_component_map(pf_data::PFS.PowerFlowData, sys::PSY.System) return temp_component_map end -_get_temp_component_map_id(comp::PSY.Component) = PSY.get_name(comp) -_get_temp_component_map_id(comp::PSY.Bus) = PSY.get_number(comp) +_get_temp_component_map_lhs(comp::PSY.Component) = PSY.get_name(comp) +_get_temp_component_map_lhs(comp::PSY.Bus) = PSY.get_number(comp) # Creates dicts of components by type function _make_temp_component_map(::PFS.SystemPowerFlowContainer, sys::PSY.System) temp_component_map = - Dict{DataType, Dict{Union{String, Int64}, Union{String, Int64}}}() + Dict{DataType, Dict{Union{String, Int64}, String}}() # TODO don't hardcode the types here, handle get_available more elegantly, likely `ComponentSelector` use case relevant_components = vcat( collect.([ @@ -55,9 +55,10 @@ function _make_temp_component_map(::PFS.SystemPowerFlowContainer, sys::PSY.Syste )..., ) for comp_type in unique(typeof.(relevant_components)) + # NOTE we avoid using bus numbers here because PSY.get_bus(system, number) is O(n) temp_component_map[comp_type] = Dict( - _get_temp_component_map_id(c) => _get_temp_component_map_id(c) for + _get_temp_component_map_lhs(c) => PSY.get_name(c) for c in relevant_components if c isa comp_type ) end @@ -71,7 +72,7 @@ function _make_pf_input_map!( ) pf_data = get_power_flow_data(pf_e_data) temp_component_map = _make_temp_component_map(pf_data, sys) - map_type = valtype(temp_component_map) # Dict{String, Int} for PowerFlowData, Dict{Union{String, Int64}, Union{String, Int64}} for SystemPowerFlowContainer + map_type = valtype(temp_component_map) # Dict{String, Int} for PowerFlowData, Dict{Union{String, Int64}, String} for SystemPowerFlowContainer input_keys = pf_input_keys(pf_data) # Second map that persists to store the bus index that the variable @@ -227,11 +228,6 @@ function update_pf_data!( return end -_lookup_component(type::Type{<:PSY.Component}, sys::PSY.System, id::AbstractString) = - PSY.get_component(type, sys, id) -_lookup_component(::Type{<:PSY.Bus}, sys::PSY.System, id::Int) = - PSY.get_bus(sys, id) - _update_component( ::Type{<:Union{ActivePowerVariable, PowerOutput, ActivePowerTimeSeriesParameter}}, comp::PSY.Component, @@ -256,9 +252,9 @@ function update_pf_system!( ) for (key, component_map) in input_map result = lookup_value(container, key) - for (device_id, _) in component_map + for (device_id, device_name) in component_map injection_values = result[device_id, :] - comp = _lookup_component(get_component_type(key), sys, device_id) + comp = PSY.get_component(get_component_type(key), sys, device_name) val = jump_value(injection_values[time_step]) _update_component(get_entry_type(key), comp, val) end