PTDF matrix
In this tutorial the methods for computing the Power Transfer Distribution Factors (PTDF
) are presented. Before diving into this tutorial we encourage the user to load PowerNetworkMatrices
, hit the ?
key in the REPL terminal and look for the documention of the different PTDF
methods available.
Evaluation of the PTDF
matrix
The PTDF
matrix can be evaluated according to two different approaches:
Dense
: considers functions for dense matrix multiplication and inversionKLU
: considers functions for sparse matrix multiplication and inversion (default)
The evaluation of the PTDF
matrix can be easily performed starting from importing the system's data and then by simply calling the PTDF
method.
julia> using PowerNetworkMatrices
julia> using PowerSystemCaseBuilder
julia> const PNM = PowerNetworkMatrices
PowerNetworkMatrices
julia> const PSB = PowerSystemCaseBuilder
PowerSystemCaseBuilder
julia> sys = PSB.build_system(PSB.PSITestSystems, "c_sys5");
[ Info: Loaded time series from storage file existing=/home/runner/.julia/packages/PowerSystemCaseBuilder/uZO8H/data/serialized_system/614e094ea985a55912fc1321256a49b755f9fc451c0f264f24d6d04af20e84d7/c_sys5_time_series_storage.h5 new=/tmp/jl_42We6i compression=InfrastructureSystems.CompressionSettings(false, InfrastructureSystems.CompressionTypesModule.CompressionTypes.DEFLATE = 1, 3, true)
julia> ptdf_1 = PTDF(sys);
julia> get_ptdf_data(ptdf_1)
6×5 transpose(::Matrix{Float64}) with eltype Float64: 0.193917 -0.475895 -0.348989 0.0 0.159538 0.437588 0.258343 0.189451 0.0 0.36001 0.368495 0.217552 0.159538 0.0 -0.519548 0.193917 0.524105 -0.348989 0.0 0.159538 0.193917 0.524105 0.651011 0.0 0.159538 -0.368495 -0.217552 -0.159538 0.0 -0.480452
Advanced users might be interested in computing the PTDF
matrix starting from either the data contained in the IncidenceMatrix
and BA_matrix
structures, or by the information related to the branches
and buses
of the system.
julia> # evaluate the BA_matrix and Incidence_Matrix ba_matrix = BA_Matrix(sys);
julia> a_matrix = IncidenceMatrix(sys); # get the PTDF matrix starting from the values of the # previosly cumputed matrices
julia> ptdf_2 = PTDF(a_matrix, ba_matrix);
┌ Warning: PTDF creates via other matrices doesn't compute the subnetworks └ @ PowerNetworkMatrices ~/work/PowerNetworkMatrices.jl/PowerNetworkMatrices.jl/src/ptdf_calculations.jl:504
julia> get_ptdf_data(ptdf_2) # get the buses and branches of the system
6×5 transpose(::Matrix{Float64}) with eltype Float64: 0.193917 -0.475895 -0.348989 0.0 0.159538 0.437588 0.258343 0.189451 0.0 0.36001 0.368495 0.217552 0.159538 0.0 -0.519548 0.193917 0.524105 -0.348989 0.0 0.159538 0.193917 0.524105 0.651011 0.0 0.159538 -0.368495 -0.217552 -0.159538 0.0 -0.480452
julia> branches = PNM.get_ac_branches(sys);
julia> buses = PNM.get_buses(sys);
julia> ptdf_3 = PTDF(branches, buses);
julia> get_ptdf_data(ptdf_3)
6×5 transpose(::Matrix{Float64}) with eltype Float64: 0.193917 -0.475895 -0.348989 0.0 0.159538 0.437588 0.258343 0.189451 0.0 0.36001 0.368495 0.217552 0.159538 0.0 -0.519548 0.193917 0.524105 -0.348989 0.0 0.159538 0.193917 0.524105 0.651011 0.0 0.159538 -0.368495 -0.217552 -0.159538 0.0 -0.480452
NOTE: both the get_ac_branches
and get_ac_branches
functions are not exported by the PowerNetworkMatrices
package, and therefore require the package name to be called as a prefix. However, they are shown here just for the sake of making an example.
Available methods for the computation of the PTDF
matrix
As previously mentioned, the PTDF
matrix can be evaluated considering different approaches. The method can be selected by specifying the field linear_solver
in the PTDF
function.
julia> ptdf_dense = PTDF(sys, linear_solver="Dense");
julia> get_ptdf_data(ptdf_dense)
6×5 transpose(::Matrix{Float64}) with eltype Float64: 0.193917 -0.475895 -0.348989 0.0 0.159538 0.437588 0.258343 0.189451 0.0 0.36001 0.368495 0.217552 0.159538 0.0 -0.519548 0.193917 0.524105 -0.348989 0.0 0.159538 0.193917 0.524105 0.651011 0.0 0.159538 -0.368495 -0.217552 -0.159538 0.0 -0.480452
julia> ptdf_klu = PTDF(sys, linear_solver="KLU");
julia> get_ptdf_data(ptdf_klu)
6×5 transpose(::Matrix{Float64}) with eltype Float64: 0.193917 -0.475895 -0.348989 0.0 0.159538 0.437588 0.258343 0.189451 0.0 0.36001 0.368495 0.217552 0.159538 0.0 -0.519548 0.193917 0.524105 -0.348989 0.0 0.159538 0.193917 0.524105 0.651011 0.0 0.159538 -0.368495 -0.217552 -0.159538 0.0 -0.480452
By default the "KLU" method is selected, which appeared to require significant less time and memory with respect to "Dense". Please note that either the KLU
or Dense
method is used, the resulting PTDF
matrix is stored as a dense one.
Evaluating the PTDF
matrix considering distributed slack bus
Whenever needed, the PTDF
matrix can be computed with a distributed slack bus. To do so, a vector of type Vector{Float64}
needs to be defined, specifying the weights for each bus of the system. These weights identify how the load on the slakc bus is redistributed accross the system.
julia> # consider equal distribution accross each bus for this example buscount = length(PNM.get_buses(sys));
julia> dist_slack = 1 / buscount * ones(buscount);
julia> dist_slack_array = dist_slack / sum(dist_slack);
Once the vector of the weights is defined, the PTDF
matrix can be computed by defining the input argument dist_slack
(empty array Float64[]
by default):
julia> ptdf_distr = PTDF(sys, dist_slack=dist_slack_array);
[ Info: Distributed bus
The difference between a the matrix computed with and without the dist_slack
field defined can be seen as follows:
julia> # with no distributed slack bus get_ptdf_data(ptdf_klu) # with distributed slack bus
6×5 transpose(::Matrix{Float64}) with eltype Float64: 0.193917 -0.475895 -0.348989 0.0 0.159538 0.437588 0.258343 0.189451 0.0 0.36001 0.368495 0.217552 0.159538 0.0 -0.519548 0.193917 0.524105 -0.348989 0.0 0.159538 0.193917 0.524105 0.651011 0.0 0.159538 -0.368495 -0.217552 -0.159538 0.0 -0.480452
julia> get_ptdf_data(ptdf_distr)
6×5 transpose(::Matrix{Float64}) with eltype Float64: 0.288203 -0.381609 -0.254704 0.0942859 0.253824 0.18851 0.00926433 -0.0596271 -0.249079 0.110932 0.323288 0.172344 0.114331 -0.0452074 -0.564756 0.0882025 0.418391 -0.454704 -0.105714 0.0538239 -0.111797 0.218391 0.345296 -0.305714 -0.146176 -0.123288 0.0276555 0.0856694 0.245207 -0.235244
"Sparse" PTDF
matrix
The PTFD
matrix can be computed in a "sparse" fashion by defining the input argument tol
. If this argument is defined, then elements of the PTDF
matrix whose absolute values are below the set threshold are dropped. In addition, the matrix will be stored as a sparse one of type SparseArrays.SparseMatrixCSC{Float64, Int64}
instead of Matrix{Float64}
.
By considering an "extreme" value of 0.2 as tol
, the PTDF
matrix can be computed as follows:
julia> ptdf_sparse = PTDF(sys, tol=0.2);
julia> get_ptdf_data(ptdf_sparse)
6×5 LinearAlgebra.Transpose{Float64, SparseArrays.SparseMatrixCSC{Float64, Int64}} with 15 stored entries: ⋅ -0.475895 -0.348989 ⋅ ⋅ 0.437588 0.258343 ⋅ ⋅ 0.36001 0.368495 0.217552 ⋅ ⋅ -0.519548 ⋅ 0.524105 -0.348989 ⋅ ⋅ ⋅ 0.524105 0.651011 ⋅ ⋅ -0.368495 -0.217552 ⋅ ⋅ -0.480452
NOTE: 0.2 was used for the purpose of this tutorial. In practice much smaller values are used (e.g., 1e-5).