From b19d14d36853eeee47d35e118359a979f7ac2013 Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Thu, 10 Aug 2023 18:43:26 -0600 Subject: [PATCH 01/47] updated doc string --- src/ptdf_calculations.jl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ptdf_calculations.jl b/src/ptdf_calculations.jl index 0e6b55d2..e8ef11de 100644 --- a/src/ptdf_calculations.jl +++ b/src/ptdf_calculations.jl @@ -38,7 +38,14 @@ Deserialize a PTDF from an HDF5 file. PTDF(filename::AbstractString) = from_hdf5(PTDF, filename) """ -Elements whose values are below "tol" are set to zero (dropped in case of Sparse matrix). +Sets to zero those elements whose absolute values is below the threshold +specified by the field "tol". + +# Arguments +- `mat::PTDF`: + PTDF structure +- `tol::Float64`: + tolerance """ function drop_small_entries!(mat::PTDF, tol::Float64) if tol > mat.tol[] From a45e8e96aed752a5bf8b9bef4e7cc19347f9cd75 Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Thu, 10 Aug 2023 18:46:32 -0600 Subject: [PATCH 02/47] added function for LODF sparsification --- src/lodf_calculations.jl | 19 +++++++++++++++++++ src/ptdf_calculations.jl | 4 ++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/lodf_calculations.jl b/src/lodf_calculations.jl index eeda5e05..1f6c2a44 100644 --- a/src/lodf_calculations.jl +++ b/src/lodf_calculations.jl @@ -18,6 +18,25 @@ struct LODF{Ax, L <: NTuple{2, Dict}} <: PowerNetworkMatrix{Float64} lookup::L end +""" +Sets to zero those elements of the LODF matrix whose absolute values is below +the threshold specified by the field "tol". + +# Arguments +- `mat::LODF`: + LODF structure +- `tol::Float64`: + tolerance +""" +function drop_small_entries!(mat::LODF, tol::Float64) + if tol > mat.tol[] + @info "Specified tolerance is smaller than the current tolerance." + end + make_entries_zero!(mat.data, tol) + mat.tol[] = tol + return +end + function _buildlodf( a::SparseArrays.SparseMatrixCSC{Int8, Int}, ptdf::Matrix{Float64}, diff --git a/src/ptdf_calculations.jl b/src/ptdf_calculations.jl index e8ef11de..69edb592 100644 --- a/src/ptdf_calculations.jl +++ b/src/ptdf_calculations.jl @@ -38,8 +38,8 @@ Deserialize a PTDF from an HDF5 file. PTDF(filename::AbstractString) = from_hdf5(PTDF, filename) """ -Sets to zero those elements whose absolute values is below the threshold -specified by the field "tol". +Sets to zero those elements of the PTDF matrix whose absolute values is below +the threshold specified by the field "tol". # Arguments - `mat::PTDF`: From 844a28d7a7712b6e374d7d8867dfb2e96f9b90a0 Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Thu, 10 Aug 2023 18:47:00 -0600 Subject: [PATCH 03/47] removed unused PTDF function for sparsification --- src/ptdf_calculations.jl | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/ptdf_calculations.jl b/src/ptdf_calculations.jl index 69edb592..68f0726d 100644 --- a/src/ptdf_calculations.jl +++ b/src/ptdf_calculations.jl @@ -56,14 +56,6 @@ function drop_small_entries!(mat::PTDF, tol::Float64) return end -""" -Takes and existing PTDF and makes it sparse given a certain tolerance "tol". -""" -function make_sparse_PTDF(mat::PTDF{Ax, L, Matrix{Float64}}, tol::Float64) where {Ax, L} - new_mat = sparsify(mat.data, tol) - return PTDF(new_mat, mat.axes, mat.lookup, ref_bus_positions, mat.subnetworks, Ref(tol)) -end - function _buildptdf( branches, buses::Vector{PSY.Bus}, From 38f282219c243c409f2ee74c27ef56f661733a82 Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Thu, 10 Aug 2023 19:34:42 -0600 Subject: [PATCH 04/47] fixed doc string, added get_index for BA_Matrix --- src/BA_ABA_matrices.jl | 40 +++++++++++++++++++++++++++------------- src/lodf_calculations.jl | 10 +++++++--- src/ptdf_calculations.jl | 6 +++--- 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/src/BA_ABA_matrices.jl b/src/BA_ABA_matrices.jl index af79ea35..9e23e741 100644 --- a/src/BA_ABA_matrices.jl +++ b/src/BA_ABA_matrices.jl @@ -3,16 +3,16 @@ Structure containing the BA matrix and other relevant data. # Arguments - `data::SparseArrays.SparseMatrixCSC{Float64, Int}`: - the BA matrix coming from the product between the Incidence Matrix A and - the Matrix of Susceptance B + the transposed BA matrix coming from the product between the Incidence + Matrix A and the Matrix of Susceptance B - `axes<:NTuple{2, Dict}`: Tuple containing two vectors, the first one contains the names of each - line of the network (each one related to a row of the Matrix in "data"), - the second one contains the names of each bus of the network (each one + buse of the network (each one related to a row of the Matrix in "data"), + the second one contains the names of each line of the network (each one related to a column of the Matrix in "data") - `lookup<:NTuple{2, Dict}`: Tuple containing 2 Dictionaries mapping the number of rows and columns - with the names of branches and buses + with the names of buses and branches - `ref_bus_positions::Set{Int}`: Vector containing the indexes of the columns of the BA matrix corresponding to the refence buses @@ -34,8 +34,8 @@ function BA_Matrix(sys::PSY.System) bus_lookup = make_ax_ref(buses) line_ax = [PSY.get_name(branch) for branch in branches] bus_ax = [PSY.get_number(bus) for bus in setdiff(buses, ref_bus_positions)] - axes = (line_ax, bus_ax) - lookup = (make_ax_ref(line_ax), make_ax_ref(bus_ax)) + axes = (bus_ax, line_ax) + lookup = (make_ax_ref(bus_ax), make_ax_ref(line_ax)) data = calculate_BA_matrix(branches, bus_lookup) return BA_Matrix(data, axes, lookup, ref_bus_positions) end @@ -46,15 +46,14 @@ Structure containing the ABA matrix and other relevant data. # Arguments - `data::SparseArrays.SparseMatrixCSC{Float64, Int}`: the ABA matrix coming from the product between the Incidence Matrix A and - the Matrix BA + the Matrix BA. - `axes<:NTuple{2, Dict}`: - Tuple containing two vectors, the first one contains the names of each - line of the network (each one related to a row of the Matrix in "data"), - the second one contains the names of each bus of the network (each one - related to a column of the Matrix in "data") + Tuple containing two identical vectors, both containing the number of + each bus of the network (each one related to a row/column of the Matrix + in "data"), excluding the slack buses. - `lookup<:NTuple{2, Dict}`: Tuple containing 2 Dictionaries mapping the number of rows and columns - with the names of branches and buses + with the number of the buses. - `ref_bus_positions::Set{Int}`: Vector containing the indexes of the columns of the BA matrix corresponding to the refence buses @@ -134,3 +133,18 @@ is_factorized(ABA::ABA_Matrix{Ax, L, Nothing}) where {Ax, L <: NTuple{2, Dict}} is_factorized( ABA::ABA_Matrix{Ax, L, KLU.KLUFactorization{Float64, Int}}, ) where {Ax, L <: NTuple{2, Dict}} = true + +# get_index functions: BA_Matrix stores the transposed matrix, thus get index +# must export values according to [branch, bus] indexing. +function Base.getindex(A::BA_Matrix, line, bus) + i, j = to_index(A, bus, line) + return A.data[i, j] +end + +function Base.getindex( + A::BA_Matrix, + line_number::Union{Int, Colon}, + bus_number::Union{Int, Colon}, +) + return A.data[bus_number, line_number] +end \ No newline at end of file diff --git a/src/lodf_calculations.jl b/src/lodf_calculations.jl index 1f6c2a44..1712b2e8 100644 --- a/src/lodf_calculations.jl +++ b/src/lodf_calculations.jl @@ -1,21 +1,25 @@ """ -Line Outage Distribution Factors (LODFs) are a sensitivity measure of how a change in -a line's flow affects the flows on other lines in the system. +The Line Outage Distribution Factor (LODF) matrix gathers a sensitivity coefficients +of how a change in a line's flow affects the flows on other lines in the system. # Arguments - `data<:AbstractArray{Float64, 2}`: - the actual Incidence matrix. + the transposed LODF matrix. - `axes<:NTuple{2, Dict}`: Tuple containing two vectors (the first one showing the branches names, the second showing the buses numbers). - `lookup<:NTuple{2, Dict}`: Tuple containing two dictionaries, the first mapping the branches and buses with their enumerated indexes. +- `tol::Base.RefValue{Float64}`: + tolerance used for sportifying the matrix (dropping element whose + absolute value is below this threshold). """ struct LODF{Ax, L <: NTuple{2, Dict}} <: PowerNetworkMatrix{Float64} data::Array{Float64, 2} axes::Ax lookup::L + tol::Base.RefValue{Float64} end """ diff --git a/src/ptdf_calculations.jl b/src/ptdf_calculations.jl index 68f0726d..17cbb456 100644 --- a/src/ptdf_calculations.jl +++ b/src/ptdf_calculations.jl @@ -5,10 +5,10 @@ The PTDF struct is indexed using the Bus numbers and Branch names. # Arguments - `data<:AbstractArray{Float64, 2}`: - the actual Incidence matrix. + the transposed PTDF matrix. - `axes<:NTuple{2, Dict}`: - Tuple containing two vectors (the first one showing the branches names, - the second showing the buses numbers). + Tuple containing two vectors: the first one showing the branches names, + the second showing the buses numbers. - `lookup<:NTuple{2, Dict}`: Tuple containing two dictionaries, the first mapping the branches and buses with their enumerated indexes. From a5b8d963841d4149eb5976ef2f98fcf84d5a382a Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Thu, 10 Aug 2023 19:57:47 -0600 Subject: [PATCH 05/47] Fixed get_index for ABA matrix (error if slack buses are selected) --- src/BA_ABA_matrices.jl | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/BA_ABA_matrices.jl b/src/BA_ABA_matrices.jl index 9e23e741..0ec468ac 100644 --- a/src/BA_ABA_matrices.jl +++ b/src/BA_ABA_matrices.jl @@ -94,7 +94,7 @@ function ABA_Matrix(sys::PSY.System; factorize = false) bus_ax = [PSY.get_number(bus) for bus in buses] bus_ax_ = setdiff(bus_ax, ref_bus_positions) axes = (bus_ax_, bus_ax_) - bus_ax_ref = make_ax_ref(bus_ax) + bus_ax_ref = make_ax_ref(bus_ax_) lookup = (bus_ax_ref, bus_ax_ref) if factorize K = klu(ABA) @@ -147,4 +147,20 @@ function Base.getindex( bus_number::Union{Int, Colon}, ) return A.data[bus_number, line_number] +end + +# get_index functions: ABA_Matrix stores a square matrix whose number of rows +# and column is equal to the number of the system's buses minus the slack ones, +# NOTE: bus_1, bus_2 are bus numbers not row and column indices! +function Base.getindex(A::ABA_Matrix, bus_1, bus_2) + if bus_1 in A.ref_bus_positions || bus_2 in A.ref_bus_positions + err_msg = string(" Rows and columns related to slack buses are not defined for the ABA matrix. \n", + "Indices must be referred to any bus number that is not a slack one. \n", + "For the current Systems the reference slack buses are: ", collect(A.ref_bus_positions), ". \n") + error(err_msg) + else + i, j = to_index(A, bus_1, bus_2) + return A.data[i, j] + end + return end \ No newline at end of file From d061cda0afe71ebd3cd58d06cc3cebfd578de88a Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Thu, 10 Aug 2023 21:14:32 -0600 Subject: [PATCH 06/47] added sparsification of LODF matrix (some functions need to be complete - bottom) --- src/lodf_calculations.jl | 44 +++++++++++++++++++++++----------------- src/ptdf_calculations.jl | 2 +- test/test_lodf.jl | 14 +++++++++++++ 3 files changed, 40 insertions(+), 20 deletions(-) diff --git a/src/lodf_calculations.jl b/src/lodf_calculations.jl index 1712b2e8..f4d18c40 100644 --- a/src/lodf_calculations.jl +++ b/src/lodf_calculations.jl @@ -6,17 +6,18 @@ of how a change in a line's flow affects the flows on other lines in the system. - `data<:AbstractArray{Float64, 2}`: the transposed LODF matrix. - `axes<:NTuple{2, Dict}`: - Tuple containing two vectors (the first one showing the branches names, - the second showing the buses numbers). + Tuple containing two identical vectors containing the names of the + branches related to each row/column. - `lookup<:NTuple{2, Dict}`: - Tuple containing two dictionaries, the first mapping the branches - and buses with their enumerated indexes. + Tuple containing two identical dictionaries mapping the branches + their enumerated indexes (row and column numbers). - `tol::Base.RefValue{Float64}`: - tolerance used for sportifying the matrix (dropping element whose + tolerance used for sparsifying the matrix (dropping element whose absolute value is below this threshold). """ -struct LODF{Ax, L <: NTuple{2, Dict}} <: PowerNetworkMatrix{Float64} - data::Array{Float64, 2} +struct LODF{Ax, L <: NTuple{2, Dict}, M <: AbstractArray{Float64, 2}} <: + PowerNetworkMatrix{Float64} + data::M axes::Ax lookup::L tol::Base.RefValue{Float64} @@ -189,13 +190,15 @@ Builds the LODF matrix from a group of branches and buses. The return is a LOLDF - `dist_slack::Vector{Float64}`: Vector of weights to be used as distributed slack bus. The distributed slack vector has to be the same length as the number of buses. - +- `tol::Float64`: + Tolerance to eliminate entries in the LODF matrix (default eps()) """ function LODF( branches, - buses::Vector{PSY.Bus}, + buses::Vector{PSY.Bus}; linera_solver::String = "KLU", dist_slack::Vector{Float64} = Float64[], + tol::Float64 = eps() ) # get axis names @@ -208,7 +211,11 @@ function LODF( ptdf, a = calculate_PTDF_matrix_KLU(branches, buses, bus_lookup, dist_slack) lodf = _buildlodf(a, ptdf, linera_solver) - return LODF(lodf, axes, look_up) + + if tol > eps() + return LODF(sparsify(lodf, tol), axes, look_up, Ref(tol)) + end + return LODF(lodf, axes, look_up, Ref(tol)) end """ @@ -217,25 +224,21 @@ Builds the LODF matrix from a system. The return is a LOLDF array indexed with t # Arguments - `sys::PSY.System`: Power Systems system -- `dist_slack::Vector{Float64}`: - Vector of weights to be used as distributed slack bus. - The distributed slack vector has to be the same length as the number of buses. """ function LODF( sys::PSY.System; - linear_solver::String = "KLU", - dist_slack::Vector{Float64} = Float64[], + kwargs..., ) - validate_linear_solver(linear_solver) branches = get_ac_branches(sys) buses = get_buses(sys) - return LODF(branches, buses, linear_solver, dist_slack) + return LODF(branches, buses; kwargs...) end function LODF( A::IncidenceMatrix, - PTDFm::PTDF, + PTDFm::PTDF; linear_solver::String = "KLU", + tol::Float64 = eps() ) validate_linear_solver(linear_solver) ax_ref = make_ax_ref(A.axes[1]) @@ -243,14 +246,16 @@ function LODF( _buildlodf(A.data, PTDFm.data, linear_solver), (A.axes[1], A.axes[1]), (ax_ref, ax_ref), + tol ) end function LODF( A::IncidenceMatrix, ABA::ABA_Matrix, - BA::BA_Matrix, + BA::BA_Matrix; linear_solver::String = "KLU", + tol::Float64 = eps() ) validate_linear_solver(linear_solver) ax_ref = make_ax_ref(A.axes[1]) @@ -258,5 +263,6 @@ function LODF( _buildlodf(A.data, ABA.K, BA.data, A.ref_bus_positions, linear_solver), (A.axes[1], A.axes[1]), (ax_ref, ax_ref), + tol ) end diff --git a/src/ptdf_calculations.jl b/src/ptdf_calculations.jl index 17cbb456..626aee62 100644 --- a/src/ptdf_calculations.jl +++ b/src/ptdf_calculations.jl @@ -16,7 +16,7 @@ The PTDF struct is indexed using the Bus numbers and Branch names. dictionary containing the set of bus indexes defining the subnetworks of the system. - `tol::Base.RefValue{Float64}`: - tolerance used for sportifying the matrix (dropping element whose + tolerance used for sparsifying the matrix (dropping element whose absolute value is below this threshold). """ struct PTDF{Ax, L <: NTuple{2, Dict}, M <: AbstractArray{Float64, 2}} <: diff --git a/test/test_lodf.jl b/test/test_lodf.jl index 748ebaa9..a41c3582 100644 --- a/test/test_lodf.jl +++ b/test/test_lodf.jl @@ -36,3 +36,17 @@ total_error = abs.(L5NS_from_ptdf.data' .- Lodf_5) @test isapprox(sum(total_error), 0.0, atol = 1e-3) end + +@testset "Sparse LODF matrix" begin + sys5 = PSB.build_system(PSB.PSITestSystems, "c_sys5") + L5NS = LODF(sys5, tol=0.4) + L5NS_bis = LODF(sys5) + drop_small_entries!(L5NS_bis, 0.4) + + ref_sparse_Lodf_5 = deepcopy(Lodf_5') + ref_sparse_Lodf_5[abs.(ref_sparse_Lodf_5) .< 0.4] .= 0 + + @test isapprox(Matrix(L5NS.data), ref_sparse_Lodf_5, atol=1e-3) + @test isapprox(Matrix(L5NS.data), L5NS_bis.data, atol=1e-3) + +end \ No newline at end of file From 31ae9785d34903f3161ae53f89d3bef3b8bae967 Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Fri, 11 Aug 2023 16:32:01 -0600 Subject: [PATCH 07/47] updated functions for tolerance and relative tests --- src/lodf_calculations.jl | 99 +++++++++++++++++++++++++++++++++------- src/ptdf_calculations.jl | 3 -- test/test_lodf.jl | 39 +++++++++++++--- 3 files changed, 116 insertions(+), 25 deletions(-) diff --git a/src/lodf_calculations.jl b/src/lodf_calculations.jl index f4d18c40..3544f81a 100644 --- a/src/lodf_calculations.jl +++ b/src/lodf_calculations.jl @@ -54,7 +54,6 @@ function _buildlodf( else error("MKLPardiso still to be implemented") end - return lodf_t end @@ -62,7 +61,7 @@ function _buildlodf( a::SparseArrays.SparseMatrixCSC{Int8, Int64}, k::KLU.KLUFactorization{Float64, Int64}, ba::SparseArrays.SparseMatrixCSC{Float64, Int64}, - ref_bus_positions::Vector{Int}, + ref_bus_positions::Set{Int}, linear_solver::String, ) if linear_solver == "KLU" @@ -100,9 +99,9 @@ function _calculate_LODF_matrix_KLU( end end Dem_LU = klu(SparseArrays.sparse(m_I, m_I, m_V)) - lodf = Dem_LU \ ptdf_denominator - lodf[SparseArrays.diagind(lodf)] .= -1 - return lodf + lodf_t = Dem_LU \ ptdf_denominator + lodf_t[SparseArrays.diagind(lodf_t)] .= -1 + return lodf_t end function _calculate_LODF_matrix_KLU( @@ -180,7 +179,7 @@ function _calculate_LODF_matrix_DENSE( end """ -Builds the LODF matrix from a group of branches and buses. The return is a LOLDF array indexed with the branch name. +Builds the LODF matrix given the data of branches and buses of the system. # Arguments - `branches`: @@ -208,18 +207,21 @@ function LODF( bus_ax = [PSY.get_number(bus) for bus in buses] bus_lookup = make_ax_ref(bus_ax) # get network matrices - ptdf, a = calculate_PTDF_matrix_KLU(branches, buses, bus_lookup, dist_slack) - - lodf = _buildlodf(a, ptdf, linera_solver) - + ptdf_t, a = calculate_PTDF_matrix_KLU(branches, buses, bus_lookup, dist_slack) if tol > eps() - return LODF(sparsify(lodf, tol), axes, look_up, Ref(tol)) + lodf_t = _buildlodf(a, ptdf_t, linera_solver) + return LODF(sparsify(lodf_t, tol), axes, look_up, Ref(tol)) end - return LODF(lodf, axes, look_up, Ref(tol)) + return LODF( + _buildlodf(a, ptdf_t, linera_solver), + axes, + look_up, + Ref(tol) + ) end """ -Builds the LODF matrix from a system. The return is a LOLDF array indexed with the branch name. +Builds the LODF matrix from a system. # Arguments - `sys::PSY.System`: @@ -234,6 +236,26 @@ function LODF( return LODF(branches, buses; kwargs...) end +""" +Builds the LODF matrix given the Incidence Matrix and the PTDF matrix of the system. + +NOTE (1): if the LODF with distributed slack is wanted, then a PTDF matrix computed with +distributed slack is needed. + +NOTE (2): tol is referred to the LODF sparsification, not the PTDF one. PTDF matrix +must be considered as NON sparsified ("tol" argument not specified when calling +the PTDF method). + +# Arguments +- `A::IncidenceMatrix`: + Structure containing the Incidence matrix of the system. +- `PTDFm::PTDF`: + Strucutre containing the transposed PTDF matrix of the system. +- `linear_solver::String`: + Linear solver to be used. Options are "Dense" and "KLU". +- `tol::Float64`: + Tolerance to eliminate entries in the LODF matrix (default eps()). +""" function LODF( A::IncidenceMatrix, PTDFm::PTDF; @@ -241,28 +263,73 @@ function LODF( tol::Float64 = eps() ) validate_linear_solver(linear_solver) + + if PTDFm.tol.x > 1e-15 + err_msg = string("The argument `tol` in the PTDF matrix was set to a value dirrent than the default one.\n", + "The PTDF matrix used as imput of the LODF matrix must have the default `tol` value.\n") + error(err_msg) + end + ax_ref = make_ax_ref(A.axes[1]) + if tol > eps() + lodf_t = _buildlodf(A.data, PTDFm.data, linear_solver) + return LODF( + sparsify(lodf_t, tol), + (A.axes[1], A.axes[1]), + (ax_ref, ax_ref), + Ref(tol) + ) + end return LODF( _buildlodf(A.data, PTDFm.data, linear_solver), (A.axes[1], A.axes[1]), (ax_ref, ax_ref), - tol + Ref(tol) ) end +""" +Builds the LODF matrix given the Incidence Matrix and the PTDF matrix of the system. + +# Arguments +- `A::IncidenceMatrix`: + Structure containing the Incidence matrix of the system. +- `ABA::ABA_Matrix`: + Structure containing the ABA matrix of the system. +- `BA::BA_Matrix`: + Structure containing the transposed BA matrix of the system. +- `linear_solver::String`: + Linear solver to be used. Options are "Dense" and "KLU". +- `dist_slack::Vector{Float64}`: + Vector of weights to be used as distributed slack bus. + The distributed slack vector has to be the same length as the number of buses. +- `tol::Float64`: + Tolerance to eliminate entries in the LODF matrix (default eps()). +""" function LODF( A::IncidenceMatrix, ABA::ABA_Matrix, BA::BA_Matrix; linear_solver::String = "KLU", + dist_slack::Vector{Float64} = Float64[], tol::Float64 = eps() ) validate_linear_solver(linear_solver) ax_ref = make_ax_ref(A.axes[1]) + if tol > eps() + lodf_t = _buildlodf(A.data, ABA.K, BA.data, + A.ref_bus_positions, linear_solver) + return LODF( + sparsify(lodf_t, tol), + (A.axes[1], A.axes[1]), + (ax_ref, ax_ref), + Ref(tol) + ) + end return LODF( _buildlodf(A.data, ABA.K, BA.data, A.ref_bus_positions, linear_solver), (A.axes[1], A.axes[1]), (ax_ref, ax_ref), - tol + Ref(tol) ) -end +end \ No newline at end of file diff --git a/src/ptdf_calculations.jl b/src/ptdf_calculations.jl index 626aee62..459e9c29 100644 --- a/src/ptdf_calculations.jl +++ b/src/ptdf_calculations.jl @@ -188,9 +188,6 @@ function calculate_PTDF_matrix_KLU( return PTDFm, A end -""" -!!! MISSING DOCUMENTATION !!! -""" function _binfo_check(binfo::Int) if binfo != 0 if binfo < 0 diff --git a/test/test_lodf.jl b/test/test_lodf.jl index a41c3582..95c3f221 100644 --- a/test/test_lodf.jl +++ b/test/test_lodf.jl @@ -29,24 +29,51 @@ total_error = abs.(L5NS.data' .- Lodf_5) @test isapprox(sum(total_error), 0.0, atol = 1e-3) + # A and PTDF case A = IncidenceMatrix(sys5) P5 = PTDF(sys5) L5NS_from_ptdf = LODF(A, P5) @test getindex(L5NS_from_ptdf, "6", "5") - -0.3071 <= 1e-4 total_error = abs.(L5NS_from_ptdf.data' .- Lodf_5) @test isapprox(sum(total_error), 0.0, atol = 1e-3) + + # A, ABA, and BA case + ABA = ABA_Matrix(sys5, factorize=true) + BA = BA_Matrix(sys5) + L5NS_from_ba_aba = LODF(A, ABA, BA) + @test getindex(L5NS_from_ba_aba, "6", "5") - -0.3071 <= 1e-4 + total_error = abs.(L5NS_from_ba_aba.data' .- Lodf_5) + @test isapprox(sum(total_error), 0.0, atol = 1e-3) + end @testset "Sparse LODF matrix" begin sys5 = PSB.build_system(PSB.PSITestSystems, "c_sys5") - L5NS = LODF(sys5, tol=0.4) - L5NS_bis = LODF(sys5) - drop_small_entries!(L5NS_bis, 0.4) + + # basic case: system + L5NS_1 = LODF(sys5, tol=0.4) + # basic case: system but sparsification is done afterwards + L5NS_2 = LODF(sys5) + drop_small_entries!(L5NS_2, 0.4) + + # A and PTDF case + a_matrix = IncidenceMatrix(sys5) + ptdf_matrix = PTDF(sys5) + L5NS_3 = LODF(a_matrix, ptdf_matrix, tol=0.4) + + # A, ABA, and BA case + aba_matrix = ABA_Matrix(sys5, factorize=true) + ba_matrix = BA_Matrix(sys5) + L5NS_4 = LODF(a_matrix, aba_matrix, ba_matrix, tol=0.4) + + # reference value ref_sparse_Lodf_5 = deepcopy(Lodf_5') ref_sparse_Lodf_5[abs.(ref_sparse_Lodf_5) .< 0.4] .= 0 - @test isapprox(Matrix(L5NS.data), ref_sparse_Lodf_5, atol=1e-3) - @test isapprox(Matrix(L5NS.data), L5NS_bis.data, atol=1e-3) - + # tests + @test isapprox(Matrix(L5NS_1.data), ref_sparse_Lodf_5, atol=1e-3) + @test isapprox(Matrix(L5NS_1.data), L5NS_2.data, atol=1e-3) + @test isapprox(Matrix(L5NS_1.data), L5NS_3.data, atol=1e-3) + @test isapprox(Matrix(L5NS_1.data), L5NS_4.data, atol=1e-3) end \ No newline at end of file From 8e76056c4e8b229ea622a9c09fd77b7bbcf6f8b9 Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Fri, 11 Aug 2023 16:37:10 -0600 Subject: [PATCH 08/47] formatter --- src/BA_ABA_matrices.jl | 8 +++++--- src/lodf_calculations.jl | 30 ++++++++++++++++-------------- test/test_lodf.jl | 25 ++++++++++++------------- 3 files changed, 33 insertions(+), 30 deletions(-) diff --git a/src/BA_ABA_matrices.jl b/src/BA_ABA_matrices.jl index 0ec468ac..2351dd24 100644 --- a/src/BA_ABA_matrices.jl +++ b/src/BA_ABA_matrices.jl @@ -154,13 +154,15 @@ end # NOTE: bus_1, bus_2 are bus numbers not row and column indices! function Base.getindex(A::ABA_Matrix, bus_1, bus_2) if bus_1 in A.ref_bus_positions || bus_2 in A.ref_bus_positions - err_msg = string(" Rows and columns related to slack buses are not defined for the ABA matrix. \n", + err_msg = string( + " Rows and columns related to slack buses are not defined for the ABA matrix. \n", "Indices must be referred to any bus number that is not a slack one. \n", - "For the current Systems the reference slack buses are: ", collect(A.ref_bus_positions), ". \n") + "For the current Systems the reference slack buses are: ", + collect(A.ref_bus_positions), ". \n") error(err_msg) else i, j = to_index(A, bus_1, bus_2) return A.data[i, j] end return -end \ No newline at end of file +end diff --git a/src/lodf_calculations.jl b/src/lodf_calculations.jl index 3544f81a..72f558a6 100644 --- a/src/lodf_calculations.jl +++ b/src/lodf_calculations.jl @@ -15,7 +15,7 @@ of how a change in a line's flow affects the flows on other lines in the system. tolerance used for sparsifying the matrix (dropping element whose absolute value is below this threshold). """ -struct LODF{Ax, L <: NTuple{2, Dict}, M <: AbstractArray{Float64, 2}} <: +struct LODF{Ax, L <: NTuple{2, Dict}, M <: AbstractArray{Float64, 2}} <: PowerNetworkMatrix{Float64} data::M axes::Ax @@ -197,7 +197,7 @@ function LODF( buses::Vector{PSY.Bus}; linera_solver::String = "KLU", dist_slack::Vector{Float64} = Float64[], - tol::Float64 = eps() + tol::Float64 = eps(), ) # get axis names @@ -216,8 +216,8 @@ function LODF( _buildlodf(a, ptdf_t, linera_solver), axes, look_up, - Ref(tol) - ) + Ref(tol), + ) end """ @@ -260,13 +260,15 @@ function LODF( A::IncidenceMatrix, PTDFm::PTDF; linear_solver::String = "KLU", - tol::Float64 = eps() + tol::Float64 = eps(), ) validate_linear_solver(linear_solver) if PTDFm.tol.x > 1e-15 - err_msg = string("The argument `tol` in the PTDF matrix was set to a value dirrent than the default one.\n", - "The PTDF matrix used as imput of the LODF matrix must have the default `tol` value.\n") + err_msg = string( + "The argument `tol` in the PTDF matrix was set to a value dirrent than the default one.\n", + "The PTDF matrix used as imput of the LODF matrix must have the default `tol` value.\n", + ) error(err_msg) end @@ -277,14 +279,14 @@ function LODF( sparsify(lodf_t, tol), (A.axes[1], A.axes[1]), (ax_ref, ax_ref), - Ref(tol) + Ref(tol), ) end return LODF( _buildlodf(A.data, PTDFm.data, linear_solver), (A.axes[1], A.axes[1]), (ax_ref, ax_ref), - Ref(tol) + Ref(tol), ) end @@ -312,24 +314,24 @@ function LODF( BA::BA_Matrix; linear_solver::String = "KLU", dist_slack::Vector{Float64} = Float64[], - tol::Float64 = eps() + tol::Float64 = eps(), ) validate_linear_solver(linear_solver) ax_ref = make_ax_ref(A.axes[1]) if tol > eps() lodf_t = _buildlodf(A.data, ABA.K, BA.data, - A.ref_bus_positions, linear_solver) + A.ref_bus_positions, linear_solver) return LODF( sparsify(lodf_t, tol), (A.axes[1], A.axes[1]), (ax_ref, ax_ref), - Ref(tol) + Ref(tol), ) end return LODF( _buildlodf(A.data, ABA.K, BA.data, A.ref_bus_positions, linear_solver), (A.axes[1], A.axes[1]), (ax_ref, ax_ref), - Ref(tol) + Ref(tol), ) -end \ No newline at end of file +end diff --git a/test/test_lodf.jl b/test/test_lodf.jl index 95c3f221..0f98febd 100644 --- a/test/test_lodf.jl +++ b/test/test_lodf.jl @@ -38,20 +38,19 @@ @test isapprox(sum(total_error), 0.0, atol = 1e-3) # A, ABA, and BA case - ABA = ABA_Matrix(sys5, factorize=true) + ABA = ABA_Matrix(sys5; factorize = true) BA = BA_Matrix(sys5) L5NS_from_ba_aba = LODF(A, ABA, BA) @test getindex(L5NS_from_ba_aba, "6", "5") - -0.3071 <= 1e-4 total_error = abs.(L5NS_from_ba_aba.data' .- Lodf_5) @test isapprox(sum(total_error), 0.0, atol = 1e-3) - end @testset "Sparse LODF matrix" begin sys5 = PSB.build_system(PSB.PSITestSystems, "c_sys5") - + # basic case: system - L5NS_1 = LODF(sys5, tol=0.4) + L5NS_1 = LODF(sys5; tol = 0.4) # basic case: system but sparsification is done afterwards L5NS_2 = LODF(sys5) @@ -60,20 +59,20 @@ end # A and PTDF case a_matrix = IncidenceMatrix(sys5) ptdf_matrix = PTDF(sys5) - L5NS_3 = LODF(a_matrix, ptdf_matrix, tol=0.4) + L5NS_3 = LODF(a_matrix, ptdf_matrix; tol = 0.4) # A, ABA, and BA case - aba_matrix = ABA_Matrix(sys5, factorize=true) + aba_matrix = ABA_Matrix(sys5; factorize = true) ba_matrix = BA_Matrix(sys5) - L5NS_4 = LODF(a_matrix, aba_matrix, ba_matrix, tol=0.4) + L5NS_4 = LODF(a_matrix, aba_matrix, ba_matrix; tol = 0.4) # reference value ref_sparse_Lodf_5 = deepcopy(Lodf_5') ref_sparse_Lodf_5[abs.(ref_sparse_Lodf_5) .< 0.4] .= 0 - + # tests - @test isapprox(Matrix(L5NS_1.data), ref_sparse_Lodf_5, atol=1e-3) - @test isapprox(Matrix(L5NS_1.data), L5NS_2.data, atol=1e-3) - @test isapprox(Matrix(L5NS_1.data), L5NS_3.data, atol=1e-3) - @test isapprox(Matrix(L5NS_1.data), L5NS_4.data, atol=1e-3) -end \ No newline at end of file + @test isapprox(Matrix(L5NS_1.data), ref_sparse_Lodf_5, atol = 1e-3) + @test isapprox(Matrix(L5NS_1.data), L5NS_2.data, atol = 1e-3) + @test isapprox(Matrix(L5NS_1.data), L5NS_3.data, atol = 1e-3) + @test isapprox(Matrix(L5NS_1.data), L5NS_4.data, atol = 1e-3) +end From 901b1fb68065321869e0a7ab751ab4eea9cdbbf3 Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Fri, 11 Aug 2023 17:29:04 -0600 Subject: [PATCH 09/47] added test, fixed some things, formatter --- src/lodf_calculations.jl | 21 ++++++++------------- test/test_lodf.jl | 26 ++++++++++++++++++++++++++ test/test_ptdf.jl | 5 +++++ 3 files changed, 39 insertions(+), 13 deletions(-) diff --git a/src/lodf_calculations.jl b/src/lodf_calculations.jl index 72f558a6..636fe545 100644 --- a/src/lodf_calculations.jl +++ b/src/lodf_calculations.jl @@ -80,12 +80,12 @@ function _calculate_LODF_matrix_KLU( ) linecount = size(ba, 2) # get inverse of aba - first = zeros(size(a, 2), size(a, 1)) + first_ = zeros(size(a, 2), size(a, 1)) valid_ix = setdiff(1:size(a, 2), ref_bus_positions) - copyto!(first, transpose(a)) - first[valid_ix, :] = KLU.solve!(k, first[valid_ix, :]) - first[collect(ref_bus_positions), :] .= 0.0 - ptdf_denominator = first' * ba + copyto!(first_, transpose(a)) + first_[valid_ix, :] = KLU.solve!(k, first_[valid_ix, :]) + first_[collect(ref_bus_positions), :] .= 0.0 + ptdf_denominator = first_' * ba m_I = Int[] m_V = Float64[] @@ -111,20 +111,17 @@ function _calculate_LODF_matrix_KLU( linecount = size(ptdf, 2) ptdf_denominator_t = a * ptdf m_I = Int[] - m_J = Int[] m_V = Float64[] for iline in 1:linecount if (1.0 - ptdf_denominator_t[iline, iline]) < 1.0E-06 push!(m_I, iline) - push!(m_J, iline) push!(m_V, 1.0) else push!(m_I, iline) - push!(m_J, iline) push!(m_V, 1 - ptdf_denominator_t[iline, iline]) end end - Dem_LU = klu(SparseArrays.sparse(m_I, m_J, m_V)) + Dem_LU = klu(SparseArrays.sparse(m_I, m_I, m_V)) lodf_t = Dem_LU \ ptdf_denominator_t lodf_t[SparseArrays.diagind(lodf_t)] .= -1 @@ -293,6 +290,8 @@ end """ Builds the LODF matrix given the Incidence Matrix and the PTDF matrix of the system. +NOTE: this method does not support distributed slack bus. + # Arguments - `A::IncidenceMatrix`: Structure containing the Incidence matrix of the system. @@ -302,9 +301,6 @@ Builds the LODF matrix given the Incidence Matrix and the PTDF matrix of the sys Structure containing the transposed BA matrix of the system. - `linear_solver::String`: Linear solver to be used. Options are "Dense" and "KLU". -- `dist_slack::Vector{Float64}`: - Vector of weights to be used as distributed slack bus. - The distributed slack vector has to be the same length as the number of buses. - `tol::Float64`: Tolerance to eliminate entries in the LODF matrix (default eps()). """ @@ -313,7 +309,6 @@ function LODF( ABA::ABA_Matrix, BA::BA_Matrix; linear_solver::String = "KLU", - dist_slack::Vector{Float64} = Float64[], tol::Float64 = eps(), ) validate_linear_solver(linear_solver) diff --git a/test/test_lodf.jl b/test/test_lodf.jl index 0f98febd..5780d137 100644 --- a/test/test_lodf.jl +++ b/test/test_lodf.jl @@ -76,3 +76,29 @@ end @test isapprox(Matrix(L5NS_1.data), L5NS_3.data, atol = 1e-3) @test isapprox(Matrix(L5NS_1.data), L5NS_4.data, atol = 1e-3) end + +@testset "LODF matrices with distributed slack" begin + """ + CAUTION: this test just test that all the matrices are the same, but there + are no reference values. + """ + + sys5 = PSB.build_system(PSB.PSITestSystems, "c_sys5") + + buscount = length(PNM.get_buses(sys5)) + + dist_slack = 1 / buscount * ones(buscount) + slack_array = dist_slack / sum(dist_slack) + + P5_1 = PTDF(sys5; dist_slack = slack_array, linear_solver = "KLU") + + # method with A and PTDF, PTDF already evaluated with distributed slack bus + A = IncidenceMatrix(sys5) + LODF_ref = LODF(A, P5_1) + + # base case with system data + LODF_1 = LODF(sys5; dist_slack = slack_array) + + # tests + @test isapprox(LODF_ref.data, LODF_1.data, atol = 1e-5) +end diff --git a/test/test_ptdf.jl b/test/test_ptdf.jl index e5d33a1e..dedd629b 100644 --- a/test/test_ptdf.jl +++ b/test/test_ptdf.jl @@ -194,6 +194,11 @@ end end @testset "PTDF matrices with distributed slack" begin + """ + CAUTION: this test just test that all the matrices are the same, but there + are no reference values. + """ + sys5 = PSB.build_system(PSB.PSITestSystems, "c_sys5") buscount = length(PNM.get_buses(sys5)) From 16ae71b98a0fb40b4f5f5ea65db87fb6f10e98a2 Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Mon, 14 Aug 2023 10:44:36 -0600 Subject: [PATCH 10/47] added auxiliary function for LODF matrix --- src/PowerNetworkMatrices.jl | 1 + src/lodf_calculations.jl | 25 +++++++++++++++++++++++++ test/test_lodf.jl | 25 +++++++++++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/src/PowerNetworkMatrices.jl b/src/PowerNetworkMatrices.jl index ed7d89ae..9c6a3199 100644 --- a/src/PowerNetworkMatrices.jl +++ b/src/PowerNetworkMatrices.jl @@ -8,6 +8,7 @@ export factorize export find_subnetworks export from_hdf5 export get_ptdf_data +export get_lodf_data export IncidenceMatrix export is_factorized export LODF diff --git a/src/lodf_calculations.jl b/src/lodf_calculations.jl index 636fe545..bc4f8d29 100644 --- a/src/lodf_calculations.jl +++ b/src/lodf_calculations.jl @@ -330,3 +330,28 @@ function LODF( Ref(tol), ) end + +############################################################ +# auxiliary functions for getting data from LODF structure # +############################################################ + +function Base.getindex(A::LODF, selected_line, outage_line) + i, j = to_index(A, outage_line, selected_line) + return A.data[i, j] +end + +function Base.getindex( + A::LODF, + selected_line_number::Union{Int, Colon}, + outage_line_number::Union{Int, Colon}, +) + return A.data[outage_line_number, selected_line_number] +end + +function get_lodf_data(lodf::LODF) + return transpose(lodf.data) +end + +function get_branch_ax(lodf::LODF) + return lodf.axes[1] +end diff --git a/test/test_lodf.jl b/test/test_lodf.jl index 5780d137..d53313a3 100644 --- a/test/test_lodf.jl +++ b/test/test_lodf.jl @@ -102,3 +102,28 @@ end # tests @test isapprox(LODF_ref.data, LODF_1.data, atol = 1e-5) end + +@test "LODF getindex and get_lodf_data" begin + sys = PSB.build_system(PSB.PSITestSystems, "c_sys14") + + # get the LODF matrix + lodf_ = LODF(sys) + + # test get_lodf_data + lodf_t = lodf_.data' + @test isapprox(lodf_t, get_lodf_data(lodf_)) + + # test getindex + for name1 in PNM.get_branch_ax(lodf_) # selected line + for name2 in PNM.get_branch_ax(lodf_) # outage line + i = lodf_.lookup[1][name1] + j = lodf_.lookup[2][name2] + element_1 = lodf_[name1, name2] + element_2 = lodf_[i, j] + element_3 = lodf_t[i, j] + @test isapprox(element_1, element_2, atol = 1e-5) + @test isapprox(element_1, element_3, atol = 1e-5) + @test isapprox(element_2, element_3, atol = 1e-5) + end + end +end From 058b709c041992b899380882b149e15f9a122a65 Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Mon, 14 Aug 2023 10:50:02 -0600 Subject: [PATCH 11/47] missing get_tol for PTDF struct --- src/ptdf_calculations.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ptdf_calculations.jl b/src/ptdf_calculations.jl index 459e9c29..7dd7dedc 100644 --- a/src/ptdf_calculations.jl +++ b/src/ptdf_calculations.jl @@ -507,3 +507,7 @@ end function get_bus_ax(ptdf::PTDF) return ptdf.axes[1] end + +function get_tol(ptdf::PTDF) + return ptdf.tol +end \ No newline at end of file From f07b891d94b5b12c84dae0b1c227923ae09f2f86 Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Mon, 14 Aug 2023 10:50:32 -0600 Subject: [PATCH 12/47] added get_tol for LODF matrix too --- src/lodf_calculations.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lodf_calculations.jl b/src/lodf_calculations.jl index bc4f8d29..5b26422b 100644 --- a/src/lodf_calculations.jl +++ b/src/lodf_calculations.jl @@ -355,3 +355,7 @@ end function get_branch_ax(lodf::LODF) return lodf.axes[1] end + +function get_tol(lodf::LODF) + return lodf.tol +end \ No newline at end of file From e623e5be82dcde65e00b457ec78848efbaac0347 Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Wed, 16 Aug 2023 11:12:25 -0600 Subject: [PATCH 13/47] virtual and standard LODF with distributed slack bus --- src/lodf_calculations.jl | 2 + src/ptdf_calculations.jl | 2 +- src/virtual_lodf_calculations.jl | 141 +++++++++++++++++++++---------- src/virtual_ptdf_calculations.jl | 36 +++++--- test/test_lodf.jl | 32 ++++--- test/test_virtual_lodf.jl | 38 +++++++-- test/test_virtual_ptdf.jl | 2 +- 7 files changed, 180 insertions(+), 73 deletions(-) diff --git a/src/lodf_calculations.jl b/src/lodf_calculations.jl index 5b26422b..3e79c9fb 100644 --- a/src/lodf_calculations.jl +++ b/src/lodf_calculations.jl @@ -335,6 +335,8 @@ end # auxiliary functions for getting data from LODF structure # ############################################################ +# NOTE: the LODF matrix is saved as transposed! + function Base.getindex(A::LODF, selected_line, outage_line) i, j = to_index(A, outage_line, selected_line) return A.data[i, j] diff --git a/src/ptdf_calculations.jl b/src/ptdf_calculations.jl index 7dd7dedc..a4b64fae 100644 --- a/src/ptdf_calculations.jl +++ b/src/ptdf_calculations.jl @@ -156,7 +156,7 @@ function _calculate_PTDF_matrix_KLU( PTDFm_t[collect(ref_bus_positions), :] .= 0.0 slack_array = dist_slack / sum(dist_slack) slack_array = reshape(slack_array, 1, buscount) - return PTDFm_t - ones(buscount, 1) * (slack_array * PTDFm_t) + return PTDFm_t .- (slack_array * PTDFm_t) else error("Distributed bus specification doesn't match the number of buses.") end diff --git a/src/virtual_lodf_calculations.jl b/src/virtual_lodf_calculations.jl index 314a24f5..8adf920d 100644 --- a/src/virtual_lodf_calculations.jl +++ b/src/virtual_lodf_calculations.jl @@ -1,18 +1,27 @@ """ -Power Transfer Distribution Factors (PTDF) indicate the incremental change in -real power that occurs on transmission lines due to real power injections -changes at the buses. +The Virtual Line Outage Distribution Factor (VirtualLODF) structure gathers +the rows of the LODF matrix as they are evaluated on-the-go. These rows are +evalauted independently, cached in the structure and do not require the +computation of the whole matrix (therefore significantly reducing the +computational requirements). -The PTDF struct is indexed using the Bus numbers and branch names. +The VirtualLODF is initialized with no row stored. + +The VirtualLODF struct is indexed using branch names. # Arguments - `K::KLU.KLUFactorization{Float64, Int}`: - LU factorization matrices of the ABA matrix, evaluated by means of KLU + LU factorization matrices of the ABA matrix, evaluated by means of KLU. - `BA::SparseArrays.SparseMatrixCSC{Float64, Int}`: - BA matric + BA matrix. +- `A::SparseArrays.SparseMatrixCSC{Int8, Int}`: + Incidence matrix. +- `inv_PTDF_A_diag::Vector{Float64}`: + Vector contiaining the element-wise reciprocal of the diagonal elements + coming from multuiplying the PTDF matrix with th Incidence matrix - `ref_bus_positions::Set{Int}`: - Vector containing the indexes of the columns of the BA matrix corresponding - to the refence buses + Vector containing the indexes of the rows of the transposed BA matrix + corresponding to the refence buses. - `dist_slack::Vector{Float64}`: Vector of weights to be used as distributed slack bus. The distributed slack vector has to be the same length as the number of buses. @@ -22,14 +31,17 @@ The PTDF struct is indexed using the Bus numbers and branch names. - `lookup<:NTuple{2, Dict}`: Tuple containing two dictionaries, the first mapping the branches and buses with their enumerated indexes. +- `valid_ix::Vector{Int}`: + Vector containing the row/columns indices of matrices related the buses + which are not slack ones. - `temp_data::Vector{Float64}`: - temporary vector for internal use. + Temporary vector for internal use. - `cache::RowCache`: - cache were PTDF rows are stored. + Cache were LODF rows are stored. - `subnetworks::Dict{Int, Set{Int}}`: - dictionary containing the subsets of buses defining the different subnetwork of the system. + Dictionary containing the subsets of buses defining the different subnetwork of the system. - `tol::Base.RefValue{Float64}`: - tolerance related to scarification and values to drop. + Tolerance related to scarification and values to drop. """ struct VirtualLODF{Ax, L <: NTuple{2, Dict}} <: PowerNetworkMatrix{Float64} K::KLU.KLUFactorization{Float64, Int} @@ -52,12 +64,12 @@ function _get_PTDF_A_diag( BA::SparseArrays.SparseMatrixCSC{Float64, Int}, A::SparseArrays.SparseMatrixCSC{Int8, Int64}, ref_bus_positions::Set{Int}, + valid_ix::Vector{Int}, dist_slack::Vector{Float64}, + temp_data::Vector{Float64} = [] ) buscount, linecount = size(BA) - # inizialize matrices for evaluation - valid_ix = setdiff(1:buscount, ref_bus_positions) - PTDFm_t = zeros(buscount, linecount) + if !isempty(dist_slack) && length(ref_bus_positions) != 1 error( "Distibuted slack is not supported for systems with multiple reference buses.", @@ -91,17 +103,29 @@ function _get_PTDF_A_diag( # TODO still to implement as above @info "Distributed bus" - copyto!(PTDFm_t, BA) - PTDFm_t[valid_ix, :] = KLU.solve!(K, PTDFm_t[valid_ix, :]) - PTDFm_t[ref_bus_positions, :] .= 0.0 + slack_array = dist_slack / sum(dist_slack) - slack_array = reshape(slack_array, 1, buscount) - return LinearAlgebra.diag( - A * (PTDFm_t - (PTDFm_t * slack_array) * ones(1, buscount)), - ) + slack_array = reshape(slack_array, buscount) + diag_ = zeros(size(BA, 2)) + for row in 1:linecount + # evaluate PTDF row + lin_solve = KLU.solve!(K, Vector(BA[valid_ix, row])); + for j in eachindex(valid_ix) + temp_data[valid_ix[j]] = lin_solve[j] + end + temp_data[:] .= temp_data .- dot(temp_data, slack_array) + for j in BA.rowval[BA.colptr[row]:(BA.colptr[row + 1] - 1)] + diag_[row] += temp_data[j] * A[row, j] + end + temp_data[collect(ref_bus_positions)] .= 0 + end + + return diag_ + else error("Distributed bus specification doesn't match the number of buses.") end + return end function VirtualLODF( @@ -142,17 +166,19 @@ function VirtualLODF( subnetworks = assing_reference_buses(subnetworks, ref_bus_positions) end # get diagonal of PTDF + temp_data = zeros(length(bus_ax)) + valid_ix = setdiff(1:length(bus_ax), ref_bus_positions) PTDF_diag = _get_PTDF_A_diag( K, BA, A, ref_bus_positions, + valid_ix, dist_slack, + temp_data ) PTDF_diag[PTDF_diag .> 1 - 1e-6] .= 0.0 # initialize structure - temp_data = zeros(length(bus_ax)) - valid_ix = setdiff(1:length(bus_ax), ref_bus_positions) if isempty(persistent_lines) empty_cache = RowCache(max_cache_size * MiB, Set{Int}(), length(bus_ax) * sizeof(Float64)) @@ -186,15 +212,14 @@ end # Overload Base functions """ -Checks if the any of the fields of VirtualPTDF is empty. +Checks if the any of the fields of VirtualLODF is empty. """ -# ! does not check as expected: if first line is true it does not check the others function Base.isempty(vlodf::VirtualLODF) !isempty(vlodf.K.L) && return false !isempty(vlodf.K.U) && return false !isempty(vlodf.BA) && return false !isempty(vlodf.A) && return false - !isempty(vlodf.PTDF_A_diag) && return false + !isempty(vlodf.inv_PTDF_A_diag) && return false !isempty(vlodf.ref_bus_positions) && return false !isempty(vlodf.dist_slack) && return false !isempty(vlodf.axes) && return false @@ -208,12 +233,12 @@ function Base.isempty(vlodf::VirtualLODF) end """ -Gives the size of the whole PTDF matrix, not the number of rows stored. +Shows the size of the whole LODF matrix, not the number of rows stored. """ # ! test Base.size(vlodf::VirtualLODF) = (size(vlodf.BA, 2), size(vlodf.BA, 2)) """ -Gives the cartesian indexes of the PTDF matrix (same as the BA one). +Gives the cartesian indexes of the LODF matrix. """ Base.eachindex(vlodf::VirtualLODF) = CartesianIndices(size(vlodf)) @@ -223,45 +248,73 @@ end function _getindex( vlodf::VirtualLODF, - row::Int, + row::Int, column::Union{Int, Colon}, ) # check if value is in the cache if haskey(vlodf.cache, row) return vlodf.cache[row][column] else - # evaluate the value for the PTDF column - # Needs improvement (not much found...) - lin_solve = KLU.solve!(vlodf.K, Vector(vlodf.BA[vlodf.valid_ix, row])) - # get full ptdf row - for i in eachindex(vlodf.valid_ix) - vlodf.temp_data[vlodf.valid_ix[i]] = lin_solve[i] + # evaluate the value for the LODF column + + # TODO: Needs improvement (not much found...) + + buscount, _ = size(vlodf.BA) + + if !isempty(vlodf.dist_slack) && length(vlodf.ref_bus_positions) != 1 + error( + "Distibuted slack is not supported for systems with multiple reference buses.", + ) + elseif isempty(vlodf.dist_slack) && length(vlodf.ref_bus_positions) < buscount + lin_solve = KLU.solve!(vlodf.K, Vector(vlodf.BA[vlodf.valid_ix, row])) + # get full lodf row + for i in eachindex(vlodf.valid_ix) + vlodf.temp_data[vlodf.valid_ix[i]] = lin_solve[i] + end + elseif length(vlodf.dist_slack) == buscount + lin_solve = KLU.solve!(vlodf.K, Vector(vlodf.BA[vlodf.valid_ix, row])) + # get full lodf row + for i in eachindex(vlodf.valid_ix) + vlodf.temp_data[vlodf.valid_ix[i]] = lin_solve[i] + end + vlodf.temp_data[collect(vlodf.ref_bus_positions)] .= 0 + # change vector due to distributed slack weights + slack_array = vlodf.dist_slack / sum(vlodf.dist_slack) + vlodf.temp_data[:] .= + deepcopy(vlodf.temp_data .- dot(vlodf.temp_data, slack_array)) + else + error("Distributed bus specification doesn't match the number of buses.") end + + # now get the LODF row lodf_row = (vlodf.A * vlodf.temp_data) .* vlodf.inv_PTDF_A_diag lodf_row[row] = -1.0 - # add slack bus value (zero) and make copy of temp into the cache + + # ! should be sparse if done here ! consider function "sparsify" if get_tol(vlodf) > eps() make_entries_zero!(lodf_row, get_tol(vlodf)) end + vlodf.cache[row] = deepcopy(lodf_row) return vlodf.cache[row][column] end end """ -Gets the value of the element of the PTDF matrix given the row and column indices +Gets the value of the element of the LODF matrix given the row and column indices corresponding to the branch and buses one respectively. If `column` is a Colon then the entire row is returned. # Arguments -- `vptdf::VirtualPTDF`: - VirtualPTDF struct where to evaluate and store the values. +- `vlodf::VirtualLODF`: +VirtualLODF struct where to evaluate and store the values. - `row`: Branch index. - `column`: Bus index. If Colon then get the values of the whole row. """ +# ! check if indexing is correct function Base.getindex(vlodf::VirtualLODF, row, column) row_, column_ = to_index(vlodf, row, column) return _getindex(vlodf, row_, column_) @@ -291,13 +344,13 @@ the PTDF's matrices rows and how many times they were evaluated # ! change it so to get only the non-empty values -get_data(mat::VirtualLODF) = mat.cache +get_lodf_data(mat::VirtualLODF) = mat.cache function get_branch_ax(ptdf::VirtualLODF) return ptdf.axes[1] end -""" Gets the tolerance used for sparsifying the rows of the PTDF matrix""" -function get_tol(vptdf::Union{VirtualPTDF, VirtualLODF}) - return vptdf.tol[] +""" Gets the tolerance used for sparsifying the rows of the VirtualLODF matrix""" +function get_tol(vlodf::VirtualLODF) + return vlodf.tol[] end diff --git a/src/virtual_ptdf_calculations.jl b/src/virtual_ptdf_calculations.jl index 516bc1b4..fef68420 100644 --- a/src/virtual_ptdf_calculations.jl +++ b/src/virtual_ptdf_calculations.jl @@ -1,9 +1,13 @@ """ -Power Transfer Distribution Factors (PTDF) indicate the incremental change in -real power that occurs on transmission lines due to real power injections -changes at the buses. +The Virtual Power Transfer Distribution Factor (VirtualPTDF) structure gathers +the rows of the PTDF matrix as they are evaluated on-the-go. These rows are +evalauted independently, cached in the structure and do not require the +computation of the whole matrix (therefore significantly reducing the +computational requirements). -The PTDF struct is indexed using the Bus numbers and branch names. +The VirtualPTDF is initialized with no row stored. + +The VirtualPTDF struct is indexed using the Bus numbers and branch names. # Arguments - `K::KLU.KLUFactorization{Float64, Int}`: @@ -23,13 +27,16 @@ The PTDF struct is indexed using the Bus numbers and branch names. Tuple containing two dictionaries, the first mapping the branches and buses with their enumerated indexes. - `temp_data::Vector{Float64}`: - temporary vector for internal use. + Temporary vector for internal use. +- `valid_ix::Vector{Int}`: + Vector containing the row/columns indices of matrices related the buses + which are not slack ones. - `cache::RowCache`: - cache were PTDF rows are stored. + Cache were PTDF rows are stored. - `subnetworks::Dict{Int, Set{Int}}`: - dictionary containing the subsets of buses defining the different subnetwork of the system. + Dictionary containing the subsets of buses defining the different subnetwork of the system. - `tol::Base.RefValue{Float64}`: - tolerance related to scarification and values to drop. + Tolerance related to scarification and values to drop. """ struct VirtualPTDF{Ax, L <: NTuple{2, Dict}} <: PowerNetworkMatrix{Float64} K::KLU.KLUFactorization{Float64, Int} @@ -140,11 +147,13 @@ function Base.isempty(vptdf::VirtualPTDF) !isempty(vptdf.K.L) && return false !isempty(vptdf.K.U) && return false !isempty(vptdf.BA) && return false - !isempty(vptdf.dist_slack) && return false + !isempty(vptdf.ref_bus_positions) && return false !isempty(vptdf.axes) && return false !isempty(vptdf.lookup) && return false - !isempty(vptdf.cache) && return false !isempty(vptdf.temp_data) && return false + !isempty(vptdf.valid_ix) && return false + !isempty(vptdf.cache) && return false + !isempty(vptdf.subnetworks) && return false return true end @@ -249,7 +258,7 @@ the PTDF's matrices rows and how many times they were evaluated # ! change it so to get only the non-empty values -get_data(mat::VirtualPTDF) = mat.cache +get_ptdf_data(mat::VirtualPTDF) = mat.cache function get_branch_ax(ptdf::VirtualPTDF) return ptdf.axes[1] @@ -258,3 +267,8 @@ end function get_bus_ax(ptdf::VirtualPTDF) return ptdf.axes[2] end + +""" Gets the tolerance used for sparsifying the rows of the VirtualPTDF matrix""" +function get_tol(vptdf::VirtualPTDF) + return vptdf.tol[] +end \ No newline at end of file diff --git a/test/test_lodf.jl b/test/test_lodf.jl index d53313a3..9bcb08b1 100644 --- a/test/test_lodf.jl +++ b/test/test_lodf.jl @@ -3,15 +3,15 @@ NOTE: LODF is transposed """ - # get system + # get 5 bus system sys5 = PSB.build_system(PSB.PSITestSystems, "c_sys5") buses_5 = nodes5() branches_5 = branches5(buses_5) - # get LODF + # get LODF with buses and branches L5 = LODF(branches_5, buses_5) @test isapprox(maximum(L5.data), maximum(Lodf_5), atol = 1e-3) - @test isapprox(L5[branches_5[2], branches_5[1]], 0.3447946513849093) + @test isapprox(L5[branches_5[1], branches_5[2]], 0.3447946513849093) # get LODF with second method a = IncidenceMatrix(sys5) @@ -19,13 +19,9 @@ lodf_t_3 = PNM._calculate_LODF_matrix_KLU2(a.data, ptdf.data) @test isapprox(lodf_t_3, L5.data, atol = 1e-5) - buses_14 = nodes14() - branches_14 = branches14(buses_14) - L14 = LODF(branches_14, buses_14) - @test isapprox(maximum(L14.data), maximum(Lodf_14), atol = 1e-3) - + # get LODF with system L5NS = LODF(sys5) - @test getindex(L5NS, "6", "5") - -0.3071 <= 1e-4 + @test getindex(L5NS, "5", "6") - -0.3071 <= 1e-4 total_error = abs.(L5NS.data' .- Lodf_5) @test isapprox(sum(total_error), 0.0, atol = 1e-3) @@ -33,7 +29,7 @@ A = IncidenceMatrix(sys5) P5 = PTDF(sys5) L5NS_from_ptdf = LODF(A, P5) - @test getindex(L5NS_from_ptdf, "6", "5") - -0.3071 <= 1e-4 + @test getindex(L5NS_from_ptdf, "5", "6") - -0.3071 <= 1e-4 total_error = abs.(L5NS_from_ptdf.data' .- Lodf_5) @test isapprox(sum(total_error), 0.0, atol = 1e-3) @@ -41,9 +37,23 @@ ABA = ABA_Matrix(sys5; factorize = true) BA = BA_Matrix(sys5) L5NS_from_ba_aba = LODF(A, ABA, BA) - @test getindex(L5NS_from_ba_aba, "6", "5") - -0.3071 <= 1e-4 + @test getindex(L5NS_from_ba_aba, "5", "6") - -0.3071 <= 1e-4 total_error = abs.(L5NS_from_ba_aba.data' .- Lodf_5) @test isapprox(sum(total_error), 0.0, atol = 1e-3) + + for i in axes(Lodf_5, 1) + @test isapprox(L5[i, :], Lodf_5[i, :], atol=1e-3) + @test isapprox(L5NS[i, :], Lodf_5[i, :], atol=1e-3) + @test isapprox(L5NS_from_ptdf[i, :], Lodf_5[i, :], atol=1e-3) + @test isapprox(L5NS_from_ba_aba[i, :], Lodf_5[i, :], atol=1e-3) + end + + # get 14 bus system + buses_14 = nodes14() + branches_14 = branches14(buses_14) + L14 = LODF(branches_14, buses_14) + @test isapprox(maximum(L14.data), maximum(Lodf_14), atol = 1e-3) + end @testset "Sparse LODF matrix" begin diff --git a/test/test_virtual_lodf.jl b/test/test_virtual_lodf.jl index 735a84f3..2e0a0597 100644 --- a/test/test_virtual_lodf.jl +++ b/test/test_virtual_lodf.jl @@ -6,14 +6,14 @@ # data for (idx, name) in enumerate(vlodf.axes[1]) # compare - @test isapprox(vlodf[name, :], LODF_ref.data[:, idx], atol = 1e-6) + @test isapprox(vlodf[name, :], LODF_ref[idx, :], atol = 1e-6) end # check dicionary with rows - data_dict = PNM.get_data(vlodf) + data_dict = get_lodf_data(vlodf) for i in 1:length(vlodf.axes[1]) # compare - @test isapprox(data_dict.temp_cache[i], LODF_ref.data[:, i], atol = 1e-6) + @test isapprox(data_dict.temp_cache[i], LODF_ref[i, :], atol = 1e-6) end end @@ -36,7 +36,35 @@ end sum(abs.(lodf_virtual_with_tol["ODESSA 2 0 -1001-ODESSA 3 0 -1064-i_1", :])) end -@testset "Virtual PTDF matrices for 10 bus system with 2 reference buses" begin +@testset "Virtual LODF matrix with distributed slack bus" begin + sys5 = PSB.build_system(PSB.PSITestSystems, "c_sys5") + + buscount = length(PNM.get_buses(sys5)) + + dist_slack = 1 / buscount * ones(buscount) + slack_array = dist_slack / sum(dist_slack) + + # initialize Virtual LODF + vlodf = VirtualLODF(sys5, dist_slack = slack_array) + vlodf1 = VirtualLODF(sys5) + # Standard Virtual LODF with distributed slack bus + lodf = LODF(sys5, dist_slack = slack_array) + lodf1 = LODF(sys5) + + + + for row in 1:size(lodf.data, 1) + # evaluate the column (just needs one element) + vlodf[row, 1] + vlodf1[row, 1] + @assert isapprox(vlodf.cache[row], lodf[row, :], atol = 1e-5) + @assert isapprox(vlodf.cache[row], vlodf1.cache[row], atol = 1e-5) + @assert isapprox(lodf[row, :], lodf1[row, :], atol = 1e-5) + end + +end + +@testset "Virtual LODF matrices for 10 bus system with 2 reference buses" begin # get system sys = PSB.build_system(PSISystems, "2Area 5 Bus System") # get the system composed by 2 5-bus ones connected by a DC line # get PTDF matrix with KLU as reference @@ -65,7 +93,7 @@ end @test isapprox(ptdf_first_area, ptdf_second_area, atol = 1e-6) end -@testset "Test virtual PTDF cache" begin +@testset "Test virtual LODF cache" begin RTS = build_system(PSITestSystems, "test_RTS_GMLC_sys") line_names = get_name.(PNM.get_ac_branches(RTS)) persist_lines = line_names[1:10] diff --git a/test/test_virtual_ptdf.jl b/test/test_virtual_ptdf.jl index 6ba5d0e6..a318c3c5 100644 --- a/test/test_virtual_ptdf.jl +++ b/test/test_virtual_ptdf.jl @@ -70,7 +70,7 @@ end @testset "Test virtual PTDF with distributed slack" begin # get 5 bus system - sys = PSB.build_system(PSB.PSITestSystems, "c_sys5") + sys = PSB.build_system(PSB.PSITestSystems, "c_sys14") bus_number = length(PNM.get_buses(sys)) dist_slack = 1 / bus_number * ones(bus_number) # compute full PTDF From 85187e738a8e18d25d72890c67a688264333d32e Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Wed, 16 Aug 2023 12:57:27 -0600 Subject: [PATCH 14/47] !update!: distributed slack bus produces same result for LODF matrix --- src/lodf_calculations.jl | 11 ++--------- test/test_lodf.jl | 26 -------------------------- 2 files changed, 2 insertions(+), 35 deletions(-) diff --git a/src/lodf_calculations.jl b/src/lodf_calculations.jl index 3e79c9fb..02b66574 100644 --- a/src/lodf_calculations.jl +++ b/src/lodf_calculations.jl @@ -183,9 +183,6 @@ Builds the LODF matrix given the data of branches and buses of the system. vector of the System AC branches - `buses::Vector{PSY.Bus}`: vector of the System buses -- `dist_slack::Vector{Float64}`: - Vector of weights to be used as distributed slack bus. - The distributed slack vector has to be the same length as the number of buses. - `tol::Float64`: Tolerance to eliminate entries in the LODF matrix (default eps()) """ @@ -193,7 +190,6 @@ function LODF( branches, buses::Vector{PSY.Bus}; linera_solver::String = "KLU", - dist_slack::Vector{Float64} = Float64[], tol::Float64 = eps(), ) @@ -204,7 +200,7 @@ function LODF( bus_ax = [PSY.get_number(bus) for bus in buses] bus_lookup = make_ax_ref(bus_ax) # get network matrices - ptdf_t, a = calculate_PTDF_matrix_KLU(branches, buses, bus_lookup, dist_slack) + ptdf_t, a = calculate_PTDF_matrix_KLU(branches, buses, bus_lookup, Float64[]) if tol > eps() lodf_t = _buildlodf(a, ptdf_t, linera_solver) return LODF(sparsify(lodf_t, tol), axes, look_up, Ref(tol)) @@ -236,10 +232,7 @@ end """ Builds the LODF matrix given the Incidence Matrix and the PTDF matrix of the system. -NOTE (1): if the LODF with distributed slack is wanted, then a PTDF matrix computed with -distributed slack is needed. - -NOTE (2): tol is referred to the LODF sparsification, not the PTDF one. PTDF matrix +NOTE: tol is referred to the LODF sparsification, not the PTDF one. PTDF matrix must be considered as NON sparsified ("tol" argument not specified when calling the PTDF method). diff --git a/test/test_lodf.jl b/test/test_lodf.jl index 9bcb08b1..b444fc9e 100644 --- a/test/test_lodf.jl +++ b/test/test_lodf.jl @@ -87,32 +87,6 @@ end @test isapprox(Matrix(L5NS_1.data), L5NS_4.data, atol = 1e-3) end -@testset "LODF matrices with distributed slack" begin - """ - CAUTION: this test just test that all the matrices are the same, but there - are no reference values. - """ - - sys5 = PSB.build_system(PSB.PSITestSystems, "c_sys5") - - buscount = length(PNM.get_buses(sys5)) - - dist_slack = 1 / buscount * ones(buscount) - slack_array = dist_slack / sum(dist_slack) - - P5_1 = PTDF(sys5; dist_slack = slack_array, linear_solver = "KLU") - - # method with A and PTDF, PTDF already evaluated with distributed slack bus - A = IncidenceMatrix(sys5) - LODF_ref = LODF(A, P5_1) - - # base case with system data - LODF_1 = LODF(sys5; dist_slack = slack_array) - - # tests - @test isapprox(LODF_ref.data, LODF_1.data, atol = 1e-5) -end - @test "LODF getindex and get_lodf_data" begin sys = PSB.build_system(PSB.PSITestSystems, "c_sys14") From 4e9c9e98e5cf9bb51eec9462c3ca7cbee2bfbbea Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Wed, 16 Aug 2023 13:14:13 -0600 Subject: [PATCH 15/47] !update!: distributed slack bus for the VirtualLODF produces the same reulst of the LODF and VirtualLODF methods --- src/lodf_calculations.jl | 18 ++--- src/virtual_lodf_calculations.jl | 116 ++++++------------------------- test/test_virtual_lodf.jl | 30 +------- 3 files changed, 32 insertions(+), 132 deletions(-) diff --git a/src/lodf_calculations.jl b/src/lodf_calculations.jl index 02b66574..ac8f3d67 100644 --- a/src/lodf_calculations.jl +++ b/src/lodf_calculations.jl @@ -189,7 +189,7 @@ Builds the LODF matrix given the data of branches and buses of the system. function LODF( branches, buses::Vector{PSY.Bus}; - linera_solver::String = "KLU", + linear_solver::String = "KLU", tol::Float64 = eps(), ) @@ -202,15 +202,17 @@ function LODF( # get network matrices ptdf_t, a = calculate_PTDF_matrix_KLU(branches, buses, bus_lookup, Float64[]) if tol > eps() - lodf_t = _buildlodf(a, ptdf_t, linera_solver) + lodf_t = _buildlodf(a, ptdf_t, linear_solver) return LODF(sparsify(lodf_t, tol), axes, look_up, Ref(tol)) + else + return LODF( + _buildlodf(a, ptdf_t, linear_solver), + axes, + look_up, + Ref(tol), + ) end - return LODF( - _buildlodf(a, ptdf_t, linera_solver), - axes, - look_up, - Ref(tol), - ) + return end """ diff --git a/src/virtual_lodf_calculations.jl b/src/virtual_lodf_calculations.jl index 8adf920d..31f0c4c9 100644 --- a/src/virtual_lodf_calculations.jl +++ b/src/virtual_lodf_calculations.jl @@ -22,9 +22,6 @@ The VirtualLODF struct is indexed using branch names. - `ref_bus_positions::Set{Int}`: Vector containing the indexes of the rows of the transposed BA matrix corresponding to the refence buses. -- `dist_slack::Vector{Float64}`: - Vector of weights to be used as distributed slack bus. - The distributed slack vector has to be the same length as the number of buses. - `axes<:NTuple{2, Dict}`: Tuple containing two vectors (the first one showing the branches names, the second showing the buses numbers). @@ -49,7 +46,6 @@ struct VirtualLODF{Ax, L <: NTuple{2, Dict}} <: PowerNetworkMatrix{Float64} A::SparseArrays.SparseMatrixCSC{Int8, Int} inv_PTDF_A_diag::Vector{Float64} ref_bus_positions::Set{Int} - dist_slack::Vector{Float64} axes::Ax lookup::L valid_ix::Vector{Int} @@ -64,68 +60,27 @@ function _get_PTDF_A_diag( BA::SparseArrays.SparseMatrixCSC{Float64, Int}, A::SparseArrays.SparseMatrixCSC{Int8, Int64}, ref_bus_positions::Set{Int}, - valid_ix::Vector{Int}, - dist_slack::Vector{Float64}, - temp_data::Vector{Float64} = [] ) - buscount, linecount = size(BA) - - if !isempty(dist_slack) && length(ref_bus_positions) != 1 - error( - "Distibuted slack is not supported for systems with multiple reference buses.", - ) - elseif isempty(dist_slack) && length(ref_bus_positions) < buscount - - # get inverse of ABA - Ix = SparseArrays.sparse(I, size(K, 1), size(K, 1)) - ABA_inv = zeros(Float64, size(Ix)) - ldiv!(ABA_inv, K, Ix) - # multiply the matrix just for some elements - diag_ = zeros(size(BA, 2)) - for i in 1:size(BA, 2) # per each column - for j in BA.rowval[BA.colptr[i]:(BA.colptr[i + 1] - 1)] - check_1 = sum(j .> ref_bus_positions) - for k in BA.colptr[i]:(BA.colptr[i + 1] - 1) - if BA.rowval[k] ∉ ref_bus_positions && j ∉ ref_bus_positions - check_2 = sum(BA.rowval[k] .> ref_bus_positions) - diag_[i] += - A[i, j] * - (BA.nzval[k] * ABA_inv[j - check_1, BA.rowval[k] - check_2]) - end + # get inverse of ABA + Ix = SparseArrays.sparse(I, size(K, 1), size(K, 1)) + ABA_inv = zeros(Float64, size(Ix)) + ldiv!(ABA_inv, K, Ix) + # multiply the matrix just for some elements + diag_ = zeros(size(BA, 2)) + for i in 1:size(BA, 2) # per each column + for j in BA.rowval[BA.colptr[i]:(BA.colptr[i + 1] - 1)] + check_1 = sum(j .> ref_bus_positions) + for k in BA.colptr[i]:(BA.colptr[i + 1] - 1) + if BA.rowval[k] ∉ ref_bus_positions && j ∉ ref_bus_positions + check_2 = sum(BA.rowval[k] .> ref_bus_positions) + diag_[i] += + A[i, j] * + (BA.nzval[k] * ABA_inv[j - check_1, BA.rowval[k] - check_2]) end end end - - return diag_ - - elseif length(dist_slack) == buscount - - # TODO still to implement as above - - @info "Distributed bus" - - slack_array = dist_slack / sum(dist_slack) - slack_array = reshape(slack_array, buscount) - diag_ = zeros(size(BA, 2)) - for row in 1:linecount - # evaluate PTDF row - lin_solve = KLU.solve!(K, Vector(BA[valid_ix, row])); - for j in eachindex(valid_ix) - temp_data[valid_ix[j]] = lin_solve[j] - end - temp_data[:] .= temp_data .- dot(temp_data, slack_array) - for j in BA.rowval[BA.colptr[row]:(BA.colptr[row + 1] - 1)] - diag_[row] += temp_data[j] * A[row, j] - end - temp_data[collect(ref_bus_positions)] .= 0 - end - - return diag_ - - else - error("Distributed bus specification doesn't match the number of buses.") end - return + return diag_ end function VirtualLODF( @@ -140,7 +95,6 @@ end function VirtualLODF( branches, buses::Vector{PSY.Bus}; - dist_slack::Vector{Float64} = Float64[], tol::Float64 = eps(), max_cache_size::Int = MAX_CACHE_SIZE_MiB, persistent_lines::Vector{String} = String[], @@ -173,9 +127,6 @@ function VirtualLODF( BA, A, ref_bus_positions, - valid_ix, - dist_slack, - temp_data ) PTDF_diag[PTDF_diag .> 1 - 1e-6] .= 0.0 # initialize structure @@ -198,7 +149,6 @@ function VirtualLODF( A, 1.0 ./ (1.0 .- PTDF_diag), ref_bus_positions, - dist_slack, axes, look_up, valid_ix, @@ -221,12 +171,9 @@ function Base.isempty(vlodf::VirtualLODF) !isempty(vlodf.A) && return false !isempty(vlodf.inv_PTDF_A_diag) && return false !isempty(vlodf.ref_bus_positions) && return false - !isempty(vlodf.dist_slack) && return false !isempty(vlodf.axes) && return false !isempty(vlodf.lookup) && return false - !isempty(vlodf.valid_ix) && return false !isempty(vlodf.cache) && return false - !isempty(vlodf.temp_data) && return false !isempty(subnetworks) && return false !isempty(tol) && return false return true @@ -258,33 +205,12 @@ function _getindex( # evaluate the value for the LODF column - # TODO: Needs improvement (not much found...) + # TODO: needs improvement to speed up computation (not much found...) - buscount, _ = size(vlodf.BA) - - if !isempty(vlodf.dist_slack) && length(vlodf.ref_bus_positions) != 1 - error( - "Distibuted slack is not supported for systems with multiple reference buses.", - ) - elseif isempty(vlodf.dist_slack) && length(vlodf.ref_bus_positions) < buscount - lin_solve = KLU.solve!(vlodf.K, Vector(vlodf.BA[vlodf.valid_ix, row])) - # get full lodf row - for i in eachindex(vlodf.valid_ix) - vlodf.temp_data[vlodf.valid_ix[i]] = lin_solve[i] - end - elseif length(vlodf.dist_slack) == buscount - lin_solve = KLU.solve!(vlodf.K, Vector(vlodf.BA[vlodf.valid_ix, row])) - # get full lodf row - for i in eachindex(vlodf.valid_ix) - vlodf.temp_data[vlodf.valid_ix[i]] = lin_solve[i] - end - vlodf.temp_data[collect(vlodf.ref_bus_positions)] .= 0 - # change vector due to distributed slack weights - slack_array = vlodf.dist_slack / sum(vlodf.dist_slack) - vlodf.temp_data[:] .= - deepcopy(vlodf.temp_data .- dot(vlodf.temp_data, slack_array)) - else - error("Distributed bus specification doesn't match the number of buses.") + lin_solve = KLU.solve!(vlodf.K, Vector(vlodf.BA[vlodf.valid_ix, row])) + # get full lodf row + for i in eachindex(vlodf.valid_ix) + vlodf.temp_data[vlodf.valid_ix[i]] = lin_solve[i] end # now get the LODF row diff --git a/test/test_virtual_lodf.jl b/test/test_virtual_lodf.jl index 2e0a0597..e52c93a2 100644 --- a/test/test_virtual_lodf.jl +++ b/test/test_virtual_lodf.jl @@ -36,34 +36,6 @@ end sum(abs.(lodf_virtual_with_tol["ODESSA 2 0 -1001-ODESSA 3 0 -1064-i_1", :])) end -@testset "Virtual LODF matrix with distributed slack bus" begin - sys5 = PSB.build_system(PSB.PSITestSystems, "c_sys5") - - buscount = length(PNM.get_buses(sys5)) - - dist_slack = 1 / buscount * ones(buscount) - slack_array = dist_slack / sum(dist_slack) - - # initialize Virtual LODF - vlodf = VirtualLODF(sys5, dist_slack = slack_array) - vlodf1 = VirtualLODF(sys5) - # Standard Virtual LODF with distributed slack bus - lodf = LODF(sys5, dist_slack = slack_array) - lodf1 = LODF(sys5) - - - - for row in 1:size(lodf.data, 1) - # evaluate the column (just needs one element) - vlodf[row, 1] - vlodf1[row, 1] - @assert isapprox(vlodf.cache[row], lodf[row, :], atol = 1e-5) - @assert isapprox(vlodf.cache[row], vlodf1.cache[row], atol = 1e-5) - @assert isapprox(lodf[row, :], lodf1[row, :], atol = 1e-5) - end - -end - @testset "Virtual LODF matrices for 10 bus system with 2 reference buses" begin # get system sys = PSB.build_system(PSISystems, "2Area 5 Bus System") # get the system composed by 2 5-bus ones connected by a DC line @@ -72,7 +44,7 @@ end # check VirtualPTDF rows with the ones from KLU lodf_virtual = VirtualLODF(sys) for i in axes(lodf_complete, 2) - comp = lodf_complete[:, i] + comp = lodf_complete[i, :] virtual = lodf_virtual[i, :] # check values using PTDFs axes @test isapprox(comp, virtual; atol = 1e-10) From 9a5462f808dac0c383452241b187a0be635a6b72 Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Wed, 16 Aug 2023 13:20:09 -0600 Subject: [PATCH 16/47] corrected typos --- src/lodf_calculations.jl | 2 +- src/virtual_lodf_calculations.jl | 18 ++++++------------ 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/lodf_calculations.jl b/src/lodf_calculations.jl index ac8f3d67..d8dd8bfc 100644 --- a/src/lodf_calculations.jl +++ b/src/lodf_calculations.jl @@ -259,7 +259,7 @@ function LODF( if PTDFm.tol.x > 1e-15 err_msg = string( "The argument `tol` in the PTDF matrix was set to a value dirrent than the default one.\n", - "The PTDF matrix used as imput of the LODF matrix must have the default `tol` value.\n", + "The PTDF matrix used as input of the LODF matrix must have the default `tol` value.\n", ) error(err_msg) end diff --git a/src/virtual_lodf_calculations.jl b/src/virtual_lodf_calculations.jl index 31f0c4c9..e78be538 100644 --- a/src/virtual_lodf_calculations.jl +++ b/src/virtual_lodf_calculations.jl @@ -254,29 +254,23 @@ end """ !!! STILL TO IMPLEMENT !!! """ -Base.setindex!(::VirtualLODF, _, idx...) = error("Operation not supported by VirtualPTDF") +Base.setindex!(::VirtualLODF, _, idx...) = error("Operation not supported by VirtualLODF") """ !!! STILL TO IMPLEMENT !!! """ Base.setindex!(::VirtualLODF, _, ::CartesianIndex) = - error("Operation not supported by VirtualPTDF") - -""" -PTDF data is stored in the the cache -it is a nested vector containing an array for the names of each row, -the PTDF's matrices rows and how many times they were evaluated -""" + error("Operation not supported by VirtualLODF") # ! change it so to get only the non-empty values get_lodf_data(mat::VirtualLODF) = mat.cache -function get_branch_ax(ptdf::VirtualLODF) - return ptdf.axes[1] +function get_branch_ax(mat::VirtualLODF) + return mat.axes[1] end """ Gets the tolerance used for sparsifying the rows of the VirtualLODF matrix""" -function get_tol(vlodf::VirtualLODF) - return vlodf.tol[] +function get_tol(mat::VirtualLODF) + return mat.tol[] end From a459fca388b25407a8e40827987d112c0194bda3 Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Wed, 16 Aug 2023 18:10:05 -0600 Subject: [PATCH 17/47] sparsification of VirtualLODF --- src/common.jl | 24 +++++++++++++++++++++++- src/row_cache.jl | 28 +++++++++++++++------------- src/virtual_lodf_calculations.jl | 15 +++++++-------- src/virtual_ptdf_calculations.jl | 2 +- test/test_virtual_lodf.jl | 29 +++++++++++++++++++++++++---- 5 files changed, 71 insertions(+), 27 deletions(-) diff --git a/src/common.jl b/src/common.jl index a31c392b..dbea24fe 100644 --- a/src/common.jl +++ b/src/common.jl @@ -226,7 +226,7 @@ value is above a certain tolerance. function sparsify(dense_array::Matrix{Float64}, tol::Float64) m, n = size(dense_array) sparse_array = SparseArrays.spzeros(m, n) - for i in 1:m, j in 1:n + for j in 1:n, i in 1:m if abs(dense_array[i, j]) > tol sparse_array[i, j] = dense_array[i, j] end @@ -234,6 +234,28 @@ function sparsify(dense_array::Matrix{Float64}, tol::Float64) return sparse_array end +""" +Return a sparse vector given a dense one by dropping element whose absolute +value is above a certain tolerance. + + +# Arguments +- dense_array::Vector{Float64}`: + input vector (e.g., PTDF row from VirtualPTDF). +- `tol::Float64`: + tolerance. +""" +function sparsify(dense_array::Vector{Float64}, tol::Float64) + m = length(dense_array) + sparse_array = SparseArrays.spzeros(m) + for i in 1:m + if abs(dense_array[i]) > tol + sparse_array[i] = dense_array[i] + end + end + return sparse_array +end + """ Sets to zero every element of a Sparse matrix if absolute values is below a certain tolerance. diff --git a/src/row_cache.jl b/src/row_cache.jl index 4fde964f..2e18868e 100644 --- a/src/row_cache.jl +++ b/src/row_cache.jl @@ -1,9 +1,9 @@ """ -Structure used for saving the rows of the Virtual PTDF matrix. +Structure used for saving the rows of the Virtual PTDF and LODF matrix. # Arguments -- `temp_cache::Dict{Int, Vector{Float64}}`: - Dictionay saving the row of the PTDF matrix +- `temp_cache::Dict{Int, Union{Vector{Float64}, SparseArrays.SparseVector{Float64}}}`: + Dictionay saving the row of the PTDF/LODF matrix - `persistent_cache_keys::Set{Int}`: Set listing the rows saved in `temp_cache` - `max_cache_size::Int` @@ -11,19 +11,19 @@ Structure used for saving the rows of the Virtual PTDF matrix. - `max_num_keys::Int` Defines the maximum number of keys saved (rows of the matrix) """ -struct RowCache - temp_cache::Dict{Int, Vector{Float64}} +struct RowCache{T <: Union{Vector{Float64}, SparseArrays.SparseVector{Float64}}} + temp_cache::Dict{Int, T} persistent_cache_keys::Set{Int} max_cache_size::Int max_num_keys::Int end """ -Structure used for saving the rows of the Virtual PTDF matrix. +Structure used for saving the rows of the Virtual PTDF and LODF matrix. # Arguments -- `temp_cache::Dict{Int, Vector{Float64}}`: - Dictionay saving the row of the PTDF matrix +- `temp_cache::Dict{Int, Union{Vector{Float64}, SparseArrays.SparseVector{Float64}}}`: + Dictionay saving the rows of the PTDF/LODF matrix - `persistent_cache_keys::Set{Int}`: Set listing the rows saved in `temp_cache` - `max_cache_size::Int` @@ -31,7 +31,6 @@ Structure used for saving the rows of the Virtual PTDF matrix. - `max_num_keys::Int` Defines the maximum number of keys saved (rows of the matrix) """ - function RowCache(max_cache_size::Int, persistent_rows::Set{Int}, row_size) persistent_data_size = (length(persistent_rows) + 1) * row_size if persistent_data_size > max_cache_size @@ -43,7 +42,10 @@ function RowCache(max_cache_size::Int, persistent_rows::Set{Int}, row_size) end max_num_keys = max(length(persistent_rows) + 1, floor(Int, max_cache_size / row_size)) return RowCache( - sizehint!(Dict{Int, Vector{Float64}}(), max_num_keys), + sizehint!( + Dict{Int, Union{Vector{Float64}, SparseArrays.SparseVector{Float64}}}(), + max_num_keys + ), persistent_rows, max_cache_size, max_num_keys, @@ -88,12 +90,12 @@ Allocates vector as row of the matrix saved in cache. # Arguments - `cache::RowCache`: cache where the row vector is going to be saved -- `val::Vector{Float64}`: +- `val::Union{Vector{Float64}, SparseArrays.SparseVector{Float64}}`: vector to be saved - `key::Int`: row number (corresponding to the enumerated branch index) related to the input row vector """ -function Base.setindex!(cache::RowCache, val::Vector{Float64}, key::Int) +function Base.setindex!(cache::RowCache, val::Union{Vector{Float64}, SparseArrays.SparseVector{Float64}}, key::Int) check_cache_size!(cache) cache.temp_cache[key] = val return @@ -108,7 +110,7 @@ Gets the row of the stored matrix in cache. - `key::Int`: row number (corresponding to the enumerated branch index) related to the row vector. """ -function Base.getindex(cache::RowCache, key::Int)::Vector{Float64} +function Base.getindex(cache::RowCache, key::Int)::Union{Vector{Float64}, SparseArrays.SparseVector{Float64}} return cache.temp_cache[key] end diff --git a/src/virtual_lodf_calculations.jl b/src/virtual_lodf_calculations.jl index e78be538..a1f98d7d 100644 --- a/src/virtual_lodf_calculations.jl +++ b/src/virtual_lodf_calculations.jl @@ -219,28 +219,27 @@ function _getindex( # ! should be sparse if done here ! consider function "sparsify" if get_tol(vlodf) > eps() - make_entries_zero!(lodf_row, get_tol(vlodf)) + vlodf.cache[row] = deepcopy(sparsify(lodf_row, get_tol(vlodf))) + else + vlodf.cache[row] = deepcopy(lodf_row) end - - vlodf.cache[row] = deepcopy(lodf_row) return vlodf.cache[row][column] end end """ Gets the value of the element of the LODF matrix given the row and column indices -corresponding to the branch and buses one respectively. If `column` is a Colon then +corresponding to the selected and outage branch respectively. If `column` is a Colon then the entire row is returned. # Arguments - `vlodf::VirtualLODF`: -VirtualLODF struct where to evaluate and store the values. + VirtualLODF struct where to evaluate and store the row values. - `row`: - Branch index. + selected line name - `column`: - Bus index. If Colon then get the values of the whole row. + outage line name. If `Colon` then get the values of the whole row. """ -# ! check if indexing is correct function Base.getindex(vlodf::VirtualLODF, row, column) row_, column_ = to_index(vlodf, row, column) return _getindex(vlodf, row_, column_) diff --git a/src/virtual_ptdf_calculations.jl b/src/virtual_ptdf_calculations.jl index fef68420..4743d9b8 100644 --- a/src/virtual_ptdf_calculations.jl +++ b/src/virtual_ptdf_calculations.jl @@ -223,7 +223,7 @@ the entire row is returned. # Arguments - `vptdf::VirtualPTDF`: - VirtualPTDF struct where to evaluate and store the values. + VirtualPTDF struct where to evaluate and store the row values. - `row`: Branch index. - `column`: diff --git a/test/test_virtual_lodf.jl b/test/test_virtual_lodf.jl index e52c93a2..413e25f5 100644 --- a/test/test_virtual_lodf.jl +++ b/test/test_virtual_lodf.jl @@ -15,6 +15,17 @@ # compare @test isapprox(data_dict.temp_cache[i], LODF_ref[i, :], atol = 1e-6) end + + # check the getindex function works properly + sys5 = PSB.build_system(PSB.PSITestSystems, "c_sys5") + vlodf5 = VirtualLODF(sys5) + for i in axes(Lodf_5, 1) + for j in axes(Lodf_5, 2) + # get the data + @test isapprox(vlodf5[i, j], Lodf_5[i, j], atol=1e-3) + end + end + end @testset "Virtual LODF functions" begin @@ -29,11 +40,21 @@ end end @testset "Virtual LODF matrices with tolerance" begin - sys = PSB.build_system(PSB.PSYTestSystems, "tamu_ACTIVSg2000_sys") - lodf_virtual = VirtualLODF(sys) + + sys = PSB.build_system(PSB.PSITestSystems, "c_sys14") + lodf_reference = deepcopy(Lodf_14) + lodf_reference[abs.(lodf_reference) .<= 1e-2] .= 0 lodf_virtual_with_tol = VirtualLODF(sys; tol = 1e-2) - @test sum(abs.(lodf_virtual["ODESSA 2 0 -1001-ODESSA 3 0 -1064-i_1", :])) > - sum(abs.(lodf_virtual_with_tol["ODESSA 2 0 -1001-ODESSA 3 0 -1064-i_1", :])) + for (n, i) in enumerate(axes(lodf_virtual_with_tol, 1)) + @show i + @test isapprox( + lodf_virtual_with_tol[i, :], lodf_reference[n, :], atol = 1e-3) + end + + @test isapprox( + sum(abs.(lodf_reference[lodf_virtual_with_tol.lookup[1]["Line12"], :])), + sum(abs.(lodf_virtual_with_tol["Line12", :])), + atol=1e-5) end @testset "Virtual LODF matrices for 10 bus system with 2 reference buses" begin From c0e7a6eda9bf3a84c1e026cf632eb117015fc4a0 Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Wed, 16 Aug 2023 18:48:24 -0600 Subject: [PATCH 18/47] fixed docstrings and get_tol --- src/ptdf_calculations.jl | 13 ++++++++----- src/virtual_lodf_calculations.jl | 4 +--- src/virtual_ptdf_calculations.jl | 25 +++++++++++-------------- 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/ptdf_calculations.jl b/src/ptdf_calculations.jl index a4b64fae..1c682662 100644 --- a/src/ptdf_calculations.jl +++ b/src/ptdf_calculations.jl @@ -1,5 +1,7 @@ """ -Power Transfer Distribution Factors (PTDF) indicate the incremental change in real power that occurs on transmission lines due to real power injections changes at the buses. +Power Transfer Distribution Factors (PTDF) indicate the incremental change in +real power that occurs on transmission lines due to real power injections +changes at the buses. The PTDF struct is indexed using the Bus numbers and Branch names. @@ -7,11 +9,12 @@ The PTDF struct is indexed using the Bus numbers and Branch names. - `data<:AbstractArray{Float64, 2}`: the transposed PTDF matrix. - `axes<:NTuple{2, Dict}`: - Tuple containing two vectors: the first one showing the branches names, - the second showing the buses numbers. + Tuple containing two vectors: the first one showing the bus numbers, + the second showing the branch names. The information contained in this + field matches the axes of the fiels `data`. - `lookup<:NTuple{2, Dict}`: - Tuple containing two dictionaries, the first mapping the branches - and buses with their enumerated indexes. + Tuple containing two dictionaries mapping the bus numbers and branch + names with the indices of the matrix contained in `data`. - `subnetworks::Dict{Int, Set{Int}}`: dictionary containing the set of bus indexes defining the subnetworks of the system. diff --git a/src/virtual_lodf_calculations.jl b/src/virtual_lodf_calculations.jl index a1f98d7d..677d1789 100644 --- a/src/virtual_lodf_calculations.jl +++ b/src/virtual_lodf_calculations.jl @@ -261,9 +261,7 @@ Base.setindex!(::VirtualLODF, _, idx...) = error("Operation not supported by Vir Base.setindex!(::VirtualLODF, _, ::CartesianIndex) = error("Operation not supported by VirtualLODF") -# ! change it so to get only the non-empty values - -get_lodf_data(mat::VirtualLODF) = mat.cache +get_lodf_data(mat::VirtualLODF) = mat.cache.temp_cache function get_branch_ax(mat::VirtualLODF) return mat.axes[1] diff --git a/src/virtual_ptdf_calculations.jl b/src/virtual_ptdf_calculations.jl index 4743d9b8..82232bf5 100644 --- a/src/virtual_ptdf_calculations.jl +++ b/src/virtual_ptdf_calculations.jl @@ -7,7 +7,8 @@ computational requirements). The VirtualPTDF is initialized with no row stored. -The VirtualPTDF struct is indexed using the Bus numbers and branch names. +The VirtualPTDF is indexed using branch names and bus numbers as for the PTDF +matrix. # Arguments - `K::KLU.KLUFactorization{Float64, Int}`: @@ -21,11 +22,15 @@ The VirtualPTDF struct is indexed using the Bus numbers and branch names. Vector of weights to be used as distributed slack bus. The distributed slack vector has to be the same length as the number of buses. - `axes<:NTuple{2, Dict}`: - Tuple containing two vectors (the first one showing the branches names, - the second showing the buses numbers). + Tuple containing two vectors: the first one showing the branches names, + the second showing the buses numbers. There is no link between the + order of the vector of the branche names and the way the PTDF rows are + stored in the cache. - `lookup<:NTuple{2, Dict}`: - Tuple containing two dictionaries, the first mapping the branches - and buses with their enumerated indexes. + Tuple containing two dictionaries, mapping the branches + and buses with their enumerated indexes. The branch indexes refer to + the key of the cache dictionary. The bus indexes refer to the position + of the elements in the PTDF row stored. - `temp_data::Vector{Float64}`: Temporary vector for internal use. - `valid_ix::Vector{Int}`: @@ -250,15 +255,7 @@ Base.setindex!(::VirtualPTDF, _, idx...) = error("Operation not supported by Vir Base.setindex!(::VirtualPTDF, _, ::CartesianIndex) = error("Operation not supported by VirtualPTDF") -""" -PTDF data is stored in the the cache -it is a nested vector containing an array for the names of each row, -the PTDF's matrices rows and how many times they were evaluated -""" - -# ! change it so to get only the non-empty values - -get_ptdf_data(mat::VirtualPTDF) = mat.cache +get_ptdf_data(mat::VirtualPTDF) = mat.cache.temp_cache function get_branch_ax(ptdf::VirtualPTDF) return ptdf.axes[1] From c0de7a19b35dbea644e53e5132b4741be55f6aa0 Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Wed, 16 Aug 2023 18:48:52 -0600 Subject: [PATCH 19/47] found bug in drop_small_entries! --- src/ptdf_calculations.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ptdf_calculations.jl b/src/ptdf_calculations.jl index 1c682662..932263fb 100644 --- a/src/ptdf_calculations.jl +++ b/src/ptdf_calculations.jl @@ -51,7 +51,7 @@ the threshold specified by the field "tol". tolerance """ function drop_small_entries!(mat::PTDF, tol::Float64) - if tol > mat.tol[] + if tol < mat.tol[] @info "Specified tolerance is smaller than the current tolerance." end make_entries_zero!(mat.data, tol) From bacfdf698f2f7b63e5805e18feccb73dd016b534 Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Wed, 16 Aug 2023 18:51:21 -0600 Subject: [PATCH 20/47] same problem for LODF calculation --- src/lodf_calculations.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lodf_calculations.jl b/src/lodf_calculations.jl index d8dd8bfc..2d9b7b26 100644 --- a/src/lodf_calculations.jl +++ b/src/lodf_calculations.jl @@ -34,7 +34,7 @@ the threshold specified by the field "tol". tolerance """ function drop_small_entries!(mat::LODF, tol::Float64) - if tol > mat.tol[] + if tol < mat.tol[] @info "Specified tolerance is smaller than the current tolerance." end make_entries_zero!(mat.data, tol) From 2cfb423805dd6d52a7065a4d6830f75b28790d60 Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Wed, 16 Aug 2023 19:08:55 -0600 Subject: [PATCH 21/47] corrected docstring, added drop_small_entries! for VirtualLODF --- src/lodf_calculations.jl | 2 +- src/ptdf_calculations.jl | 2 +- src/virtual_lodf_calculations.jl | 28 ++++++++++++++++++++++++---- test/test_virtual_lodf.jl | 10 +++++++++- 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/lodf_calculations.jl b/src/lodf_calculations.jl index 2d9b7b26..7bac5ef5 100644 --- a/src/lodf_calculations.jl +++ b/src/lodf_calculations.jl @@ -24,7 +24,7 @@ struct LODF{Ax, L <: NTuple{2, Dict}, M <: AbstractArray{Float64, 2}} <: end """ -Sets to zero those elements of the LODF matrix whose absolute values is below +Sets to zero those elements of the LODF matrix whose absolute values are below the threshold specified by the field "tol". # Arguments diff --git a/src/ptdf_calculations.jl b/src/ptdf_calculations.jl index 932263fb..3a802435 100644 --- a/src/ptdf_calculations.jl +++ b/src/ptdf_calculations.jl @@ -41,7 +41,7 @@ Deserialize a PTDF from an HDF5 file. PTDF(filename::AbstractString) = from_hdf5(PTDF, filename) """ -Sets to zero those elements of the PTDF matrix whose absolute values is below +Sets to zero those elements of the PTDF matrix whose absolute values are below the threshold specified by the field "tol". # Arguments diff --git a/src/virtual_lodf_calculations.jl b/src/virtual_lodf_calculations.jl index 677d1789..e6341b7b 100644 --- a/src/virtual_lodf_calculations.jl +++ b/src/virtual_lodf_calculations.jl @@ -23,11 +23,10 @@ The VirtualLODF struct is indexed using branch names. Vector containing the indexes of the rows of the transposed BA matrix corresponding to the refence buses. - `axes<:NTuple{2, Dict}`: - Tuple containing two vectors (the first one showing the branches names, - the second showing the buses numbers). + Tuple containing two vectors showing the branch names. - `lookup<:NTuple{2, Dict}`: - Tuple containing two dictionaries, the first mapping the branches - and buses with their enumerated indexes. + Tuple containing two dictionaries, mapping the branches names + the enumerated row indexes indexes. - `valid_ix::Vector{Int}`: Vector containing the row/columns indices of matrices related the buses which are not slack ones. @@ -55,6 +54,27 @@ struct VirtualLODF{Ax, L <: NTuple{2, Dict}} <: PowerNetworkMatrix{Float64} tol::Base.RefValue{Float64} end +""" +Sets to zero those elements of each LODF matrix row whose absolute values are +below the threshold specified by the field "tol". + +# Arguments +- `mat::VirtualLODF`: + VirtualLODF structure +- `tol::Float64`: + tolerance +""" +function drop_small_entries!(mat::VirtualLODF, tol::Float64) + if tol < mat.tol[] + @info "Specified tolerance is smaller than the current tolerance." + end + for i in keys(mat.cache.temp_cache) + make_entries_zero!(mat.cache[i], tol) + end + mat.tol[] = tol + return +end + function _get_PTDF_A_diag( K::KLU.KLUFactorization{Float64, Int}, BA::SparseArrays.SparseMatrixCSC{Float64, Int}, diff --git a/test/test_virtual_lodf.jl b/test/test_virtual_lodf.jl index 413e25f5..8068de7e 100644 --- a/test/test_virtual_lodf.jl +++ b/test/test_virtual_lodf.jl @@ -45,12 +45,20 @@ end lodf_reference = deepcopy(Lodf_14) lodf_reference[abs.(lodf_reference) .<= 1e-2] .= 0 lodf_virtual_with_tol = VirtualLODF(sys; tol = 1e-2) + lodf_virtual_with_tol1 = VirtualLODF(sys) for (n, i) in enumerate(axes(lodf_virtual_with_tol, 1)) - @show i + # get the row + lodf_virtual_with_tol1[i, :]; @test isapprox( lodf_virtual_with_tol[i, :], lodf_reference[n, :], atol = 1e-3) end + drop_small_entries!(lodf_virtual_with_tol1, 1e-2) + for (n, i) in enumerate(axes(lodf_virtual_with_tol, 1)) + @test isapprox( + lodf_virtual_with_tol1[i, :], lodf_reference[n, :], atol = 1e-3) + end + @test isapprox( sum(abs.(lodf_reference[lodf_virtual_with_tol.lookup[1]["Line12"], :])), sum(abs.(lodf_virtual_with_tol["Line12", :])), From 74e338de9dfd5a38d1a48eb6e2519ecfe8869a18 Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Wed, 16 Aug 2023 19:25:42 -0600 Subject: [PATCH 22/47] completed sparsification for VirtualPTDF --- src/virtual_lodf_calculations.jl | 1 - src/virtual_ptdf_calculations.jl | 27 +++++++++++++++++++++++---- test/test_ptdf.jl | 2 +- test/test_virtual_ptdf.jl | 27 +++++++++++++++++++++++---- 4 files changed, 47 insertions(+), 10 deletions(-) diff --git a/src/virtual_lodf_calculations.jl b/src/virtual_lodf_calculations.jl index e6341b7b..d5cba059 100644 --- a/src/virtual_lodf_calculations.jl +++ b/src/virtual_lodf_calculations.jl @@ -237,7 +237,6 @@ function _getindex( lodf_row = (vlodf.A * vlodf.temp_data) .* vlodf.inv_PTDF_A_diag lodf_row[row] = -1.0 - # ! should be sparse if done here ! consider function "sparsify" if get_tol(vlodf) > eps() vlodf.cache[row] = deepcopy(sparsify(lodf_row, get_tol(vlodf))) else diff --git a/src/virtual_ptdf_calculations.jl b/src/virtual_ptdf_calculations.jl index 82232bf5..9599c7be 100644 --- a/src/virtual_ptdf_calculations.jl +++ b/src/virtual_ptdf_calculations.jl @@ -143,6 +143,27 @@ function VirtualPTDF( return VirtualPTDF(branches, buses; kwargs...) end +""" +Sets to zero those elements of each PTDF matrix row whose absolute values are +below the threshold specified by the field "tol". + +# Arguments +- `mat::VirtualPTDF`: + VirtualPTDF structure +- `tol::Float64`: + tolerance +""" +function drop_small_entries!(mat::VirtualPTDF, tol::Float64) + if tol < mat.tol[] + @info "Specified tolerance is smaller than the current tolerance." + end + for i in keys(mat.cache.temp_cache) + make_entries_zero!(mat.cache[i], tol) + end + mat.tol[] = tol + return +end + # Overload Base functions """ @@ -211,12 +232,10 @@ function _getindex( error("Distributed bus specification doesn't match the number of buses.") end - # add slack bus value (zero) and make copy of temp into the cache if get_tol(vptdf) > eps() - # vptdf.cache[row] = make_entries_zero!(deepcopy(vptdf.temp_data), get_tol(vptdf)) - make_entries_zero!(vptdf.cache[row], get_tol(vptdf)) + vptdf.cache[row] = deepcopy(sparsify(vptdf.cache[row], get_tol(vptdf))) end - + return vptdf.cache[row][column] end end diff --git a/test/test_ptdf.jl b/test/test_ptdf.jl index dedd629b..e7822e26 100644 --- a/test/test_ptdf.jl +++ b/test/test_ptdf.jl @@ -1,4 +1,4 @@ -@testset "PTDF matrices" begin +@testset "PTDF matrices, w/ and w/o tolerance" begin sys5 = PSB.build_system(PSB.PSITestSystems, "c_sys5") for solver in ["KLU", "Dense", "MKLPardiso"] for approach in ["standard", "separate_matrices"] diff --git a/test/test_virtual_ptdf.jl b/test/test_virtual_ptdf.jl index a318c3c5..42673611 100644 --- a/test/test_virtual_ptdf.jl +++ b/test/test_virtual_ptdf.jl @@ -16,11 +16,30 @@ end @testset "Virtual PTDF matrices with tolerance" begin - sys = PSB.build_system(PSB.PSYTestSystems, "tamu_ACTIVSg2000_sys") - ptdf_virtual = VirtualPTDF(sys) + + sys = PSB.build_system(PSB.PSITestSystems, "c_sys14") + ptdf_reference = deepcopy(S14_slackB1) + ptdf_reference[abs.(ptdf_reference) .<= 1e-2] .= 0 ptdf_virtual_with_tol = VirtualPTDF(sys; tol = 1e-2) - @test sum(abs.(ptdf_virtual["ODESSA 2 0 -1001-ODESSA 3 0 -1064-i_1", :])) > - sum(abs.(ptdf_virtual_with_tol["ODESSA 2 0 -1001-ODESSA 3 0 -1064-i_1", :])) + ptdf_virtual_with_tol1 = VirtualPTDF(sys) + for (n, i) in enumerate(axes(ptdf_virtual_with_tol, 1)) + # get the row + @show i + ptdf_virtual_with_tol1[i, :]; + @test isapprox( + ptdf_virtual_with_tol[i, :], ptdf_reference[n, :], atol = 1e-3) + end + + drop_small_entries!(ptdf_virtual_with_tol1, 1e-2) + for (n, i) in enumerate(axes(ptdf_virtual_with_tol, 1)) + @test isapprox( + ptdf_virtual_with_tol1[i, :], ptdf_reference[n, :], atol = 1e-3) + end + + @test isapprox( + sum(abs.(ptdf_reference[ptdf_virtual_with_tol.lookup[1]["Line12"], :])), + sum(abs.(ptdf_virtual_with_tol["Line12", :])), + atol=1e-4) end @testset "Virtual PTDF matrices for 10 bus system with 2 reference buses" begin From be8e1240682eb5383945f04749bc742d3d3a631f Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Wed, 16 Aug 2023 19:32:50 -0600 Subject: [PATCH 23/47] formatter --- src/lodf_calculations.jl | 2 +- src/ptdf_calculations.jl | 2 +- src/row_cache.jl | 15 +++++++++++---- src/virtual_lodf_calculations.jl | 4 ++-- src/virtual_ptdf_calculations.jl | 4 ++-- test/test_lodf.jl | 9 ++++----- test/test_virtual_lodf.jl | 10 ++++------ test/test_virtual_ptdf.jl | 9 ++++----- 8 files changed, 29 insertions(+), 26 deletions(-) diff --git a/src/lodf_calculations.jl b/src/lodf_calculations.jl index 7bac5ef5..3617adf7 100644 --- a/src/lodf_calculations.jl +++ b/src/lodf_calculations.jl @@ -355,4 +355,4 @@ end function get_tol(lodf::LODF) return lodf.tol -end \ No newline at end of file +end diff --git a/src/ptdf_calculations.jl b/src/ptdf_calculations.jl index 3a802435..9b31219a 100644 --- a/src/ptdf_calculations.jl +++ b/src/ptdf_calculations.jl @@ -513,4 +513,4 @@ end function get_tol(ptdf::PTDF) return ptdf.tol -end \ No newline at end of file +end diff --git a/src/row_cache.jl b/src/row_cache.jl index 2e18868e..4c6dee3a 100644 --- a/src/row_cache.jl +++ b/src/row_cache.jl @@ -44,8 +44,8 @@ function RowCache(max_cache_size::Int, persistent_rows::Set{Int}, row_size) return RowCache( sizehint!( Dict{Int, Union{Vector{Float64}, SparseArrays.SparseVector{Float64}}}(), - max_num_keys - ), + max_num_keys, + ), persistent_rows, max_cache_size, max_num_keys, @@ -95,7 +95,11 @@ Allocates vector as row of the matrix saved in cache. - `key::Int`: row number (corresponding to the enumerated branch index) related to the input row vector """ -function Base.setindex!(cache::RowCache, val::Union{Vector{Float64}, SparseArrays.SparseVector{Float64}}, key::Int) +function Base.setindex!( + cache::RowCache, + val::Union{Vector{Float64}, SparseArrays.SparseVector{Float64}}, + key::Int, +) check_cache_size!(cache) cache.temp_cache[key] = val return @@ -110,7 +114,10 @@ Gets the row of the stored matrix in cache. - `key::Int`: row number (corresponding to the enumerated branch index) related to the row vector. """ -function Base.getindex(cache::RowCache, key::Int)::Union{Vector{Float64}, SparseArrays.SparseVector{Float64}} +function Base.getindex( + cache::RowCache, + key::Int, +)::Union{Vector{Float64}, SparseArrays.SparseVector{Float64}} return cache.temp_cache[key] end diff --git a/src/virtual_lodf_calculations.jl b/src/virtual_lodf_calculations.jl index d5cba059..3c4ac01b 100644 --- a/src/virtual_lodf_calculations.jl +++ b/src/virtual_lodf_calculations.jl @@ -215,7 +215,7 @@ end function _getindex( vlodf::VirtualLODF, - row::Int, + row::Int, column::Union{Int, Colon}, ) # check if value is in the cache @@ -224,7 +224,7 @@ function _getindex( else # evaluate the value for the LODF column - + # TODO: needs improvement to speed up computation (not much found...) lin_solve = KLU.solve!(vlodf.K, Vector(vlodf.BA[vlodf.valid_ix, row])) diff --git a/src/virtual_ptdf_calculations.jl b/src/virtual_ptdf_calculations.jl index 9599c7be..7de9f6e0 100644 --- a/src/virtual_ptdf_calculations.jl +++ b/src/virtual_ptdf_calculations.jl @@ -235,7 +235,7 @@ function _getindex( if get_tol(vptdf) > eps() vptdf.cache[row] = deepcopy(sparsify(vptdf.cache[row], get_tol(vptdf))) end - + return vptdf.cache[row][column] end end @@ -287,4 +287,4 @@ end """ Gets the tolerance used for sparsifying the rows of the VirtualPTDF matrix""" function get_tol(vptdf::VirtualPTDF) return vptdf.tol[] -end \ No newline at end of file +end diff --git a/test/test_lodf.jl b/test/test_lodf.jl index b444fc9e..37690d3d 100644 --- a/test/test_lodf.jl +++ b/test/test_lodf.jl @@ -42,10 +42,10 @@ @test isapprox(sum(total_error), 0.0, atol = 1e-3) for i in axes(Lodf_5, 1) - @test isapprox(L5[i, :], Lodf_5[i, :], atol=1e-3) - @test isapprox(L5NS[i, :], Lodf_5[i, :], atol=1e-3) - @test isapprox(L5NS_from_ptdf[i, :], Lodf_5[i, :], atol=1e-3) - @test isapprox(L5NS_from_ba_aba[i, :], Lodf_5[i, :], atol=1e-3) + @test isapprox(L5[i, :], Lodf_5[i, :], atol = 1e-3) + @test isapprox(L5NS[i, :], Lodf_5[i, :], atol = 1e-3) + @test isapprox(L5NS_from_ptdf[i, :], Lodf_5[i, :], atol = 1e-3) + @test isapprox(L5NS_from_ba_aba[i, :], Lodf_5[i, :], atol = 1e-3) end # get 14 bus system @@ -53,7 +53,6 @@ branches_14 = branches14(buses_14) L14 = LODF(branches_14, buses_14) @test isapprox(maximum(L14.data), maximum(Lodf_14), atol = 1e-3) - end @testset "Sparse LODF matrix" begin diff --git a/test/test_virtual_lodf.jl b/test/test_virtual_lodf.jl index 8068de7e..a262a5f6 100644 --- a/test/test_virtual_lodf.jl +++ b/test/test_virtual_lodf.jl @@ -22,10 +22,9 @@ for i in axes(Lodf_5, 1) for j in axes(Lodf_5, 2) # get the data - @test isapprox(vlodf5[i, j], Lodf_5[i, j], atol=1e-3) + @test isapprox(vlodf5[i, j], Lodf_5[i, j], atol = 1e-3) end end - end @testset "Virtual LODF functions" begin @@ -40,15 +39,14 @@ end end @testset "Virtual LODF matrices with tolerance" begin - sys = PSB.build_system(PSB.PSITestSystems, "c_sys14") lodf_reference = deepcopy(Lodf_14) lodf_reference[abs.(lodf_reference) .<= 1e-2] .= 0 lodf_virtual_with_tol = VirtualLODF(sys; tol = 1e-2) - lodf_virtual_with_tol1 = VirtualLODF(sys) + lodf_virtual_with_tol1 = VirtualLODF(sys) for (n, i) in enumerate(axes(lodf_virtual_with_tol, 1)) # get the row - lodf_virtual_with_tol1[i, :]; + lodf_virtual_with_tol1[i, :] @test isapprox( lodf_virtual_with_tol[i, :], lodf_reference[n, :], atol = 1e-3) end @@ -62,7 +60,7 @@ end @test isapprox( sum(abs.(lodf_reference[lodf_virtual_with_tol.lookup[1]["Line12"], :])), sum(abs.(lodf_virtual_with_tol["Line12", :])), - atol=1e-5) + atol = 1e-5) end @testset "Virtual LODF matrices for 10 bus system with 2 reference buses" begin diff --git a/test/test_virtual_ptdf.jl b/test/test_virtual_ptdf.jl index 42673611..e4421cb1 100644 --- a/test/test_virtual_ptdf.jl +++ b/test/test_virtual_ptdf.jl @@ -16,16 +16,15 @@ end @testset "Virtual PTDF matrices with tolerance" begin - sys = PSB.build_system(PSB.PSITestSystems, "c_sys14") ptdf_reference = deepcopy(S14_slackB1) ptdf_reference[abs.(ptdf_reference) .<= 1e-2] .= 0 ptdf_virtual_with_tol = VirtualPTDF(sys; tol = 1e-2) - ptdf_virtual_with_tol1 = VirtualPTDF(sys) + ptdf_virtual_with_tol1 = VirtualPTDF(sys) for (n, i) in enumerate(axes(ptdf_virtual_with_tol, 1)) # get the row - @show i - ptdf_virtual_with_tol1[i, :]; + @show i + ptdf_virtual_with_tol1[i, :] @test isapprox( ptdf_virtual_with_tol[i, :], ptdf_reference[n, :], atol = 1e-3) end @@ -39,7 +38,7 @@ end @test isapprox( sum(abs.(ptdf_reference[ptdf_virtual_with_tol.lookup[1]["Line12"], :])), sum(abs.(ptdf_virtual_with_tol["Line12", :])), - atol=1e-4) + atol = 1e-4) end @testset "Virtual PTDF matrices for 10 bus system with 2 reference buses" begin From 4e9f47bca74fdd7330244ba13577ca99dc4e9a36 Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Wed, 16 Aug 2023 19:47:59 -0600 Subject: [PATCH 24/47] bug in tests --- test/test_lodf.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_lodf.jl b/test/test_lodf.jl index 37690d3d..321ab3b0 100644 --- a/test/test_lodf.jl +++ b/test/test_lodf.jl @@ -86,7 +86,7 @@ end @test isapprox(Matrix(L5NS_1.data), L5NS_4.data, atol = 1e-3) end -@test "LODF getindex and get_lodf_data" begin +@testset "LODF getindex and get_lodf_data" begin sys = PSB.build_system(PSB.PSITestSystems, "c_sys14") # get the LODF matrix From 7a81e2cbee1490b4bf0c3f16b4be8d5f28dcb58e Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Thu, 17 Aug 2023 09:35:28 -0600 Subject: [PATCH 25/47] found errors in tests --- test/test_virtual_lodf.jl | 2 +- test/test_virtual_ptdf.jl | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/test/test_virtual_lodf.jl b/test/test_virtual_lodf.jl index a262a5f6..0938d526 100644 --- a/test/test_virtual_lodf.jl +++ b/test/test_virtual_lodf.jl @@ -13,7 +13,7 @@ data_dict = get_lodf_data(vlodf) for i in 1:length(vlodf.axes[1]) # compare - @test isapprox(data_dict.temp_cache[i], LODF_ref[i, :], atol = 1e-6) + @test isapprox(data_dict[i], LODF_ref[i, :], atol = 1e-6) end # check the getindex function works properly diff --git a/test/test_virtual_ptdf.jl b/test/test_virtual_ptdf.jl index e4421cb1..1f2675d2 100644 --- a/test/test_virtual_ptdf.jl +++ b/test/test_virtual_ptdf.jl @@ -23,7 +23,6 @@ end ptdf_virtual_with_tol1 = VirtualPTDF(sys) for (n, i) in enumerate(axes(ptdf_virtual_with_tol, 1)) # get the row - @show i ptdf_virtual_with_tol1[i, :] @test isapprox( ptdf_virtual_with_tol[i, :], ptdf_reference[n, :], atol = 1e-3) From 5982816a7c1180f1f7c3883a24aee88a9e7d50ea Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Fri, 18 Aug 2023 11:55:46 -0600 Subject: [PATCH 26/47] update 1 --- src/lodf_calculations.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lodf_calculations.jl b/src/lodf_calculations.jl index 3617adf7..0a627c2c 100644 --- a/src/lodf_calculations.jl +++ b/src/lodf_calculations.jl @@ -100,7 +100,7 @@ function _calculate_LODF_matrix_KLU( end Dem_LU = klu(SparseArrays.sparse(m_I, m_I, m_V)) lodf_t = Dem_LU \ ptdf_denominator - lodf_t[SparseArrays.diagind(lodf_t)] .= -1 + lodf_t[SparseArrays.diagind(lodf_t)] .= -1.0 return lodf_t end From 3270b0ff91e6f4f850526f12bc3c81a7e3f3eca9 Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Fri, 18 Aug 2023 12:00:33 -0600 Subject: [PATCH 27/47] update 2 --- src/lodf_calculations.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lodf_calculations.jl b/src/lodf_calculations.jl index 0a627c2c..dc2bcca4 100644 --- a/src/lodf_calculations.jl +++ b/src/lodf_calculations.jl @@ -123,7 +123,7 @@ function _calculate_LODF_matrix_KLU( end Dem_LU = klu(SparseArrays.sparse(m_I, m_I, m_V)) lodf_t = Dem_LU \ ptdf_denominator_t - lodf_t[SparseArrays.diagind(lodf_t)] .= -1 + lodf_t[SparseArrays.diagind(lodf_t)] .= -1.0 return lodf_t end From 5fbec8c56e81844db19485b504898e64b898f178 Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Fri, 18 Aug 2023 16:26:45 -0600 Subject: [PATCH 28/47] fixed a test, added test for RowCache, fixed docstring and fixed a function --- src/row_cache.jl | 39 ++++++++++++++++++++--------------- test/test_row_cache.jl | 43 +++++++++++++++++++++++++++++++++++++++ test/test_virtual_ptdf.jl | 2 +- 3 files changed, 67 insertions(+), 17 deletions(-) create mode 100644 test/test_row_cache.jl diff --git a/src/row_cache.jl b/src/row_cache.jl index 4c6dee3a..67dc57c4 100644 --- a/src/row_cache.jl +++ b/src/row_cache.jl @@ -5,7 +5,7 @@ Structure used for saving the rows of the Virtual PTDF and LODF matrix. - `temp_cache::Dict{Int, Union{Vector{Float64}, SparseArrays.SparseVector{Float64}}}`: Dictionay saving the row of the PTDF/LODF matrix - `persistent_cache_keys::Set{Int}`: - Set listing the rows saved in `temp_cache` + Set listing the rows to keep in `temp_cache` - `max_cache_size::Int` Defines the maximum allowed cache size (rows*row_size) - `max_num_keys::Int` @@ -22,14 +22,12 @@ end Structure used for saving the rows of the Virtual PTDF and LODF matrix. # Arguments -- `temp_cache::Dict{Int, Union{Vector{Float64}, SparseArrays.SparseVector{Float64}}}`: - Dictionay saving the rows of the PTDF/LODF matrix -- `persistent_cache_keys::Set{Int}`: - Set listing the rows saved in `temp_cache` - `max_cache_size::Int` - Defines the maximum allowed cache size (rows*row_size) -- `max_num_keys::Int` - Defines the maximum number of keys saved (rows of the matrix) + Defines the maximum allowed cache size (rows*row_size). +- `persistent_rows::Set{Int}`: + Set listing the rows to keep in `temp_cache`. +- `row_size` + Defines the size of the single row to store. """ function RowCache(max_cache_size::Int, persistent_rows::Set{Int}, row_size) persistent_data_size = (length(persistent_rows) + 1) * row_size @@ -96,11 +94,13 @@ Allocates vector as row of the matrix saved in cache. row number (corresponding to the enumerated branch index) related to the input row vector """ function Base.setindex!( - cache::RowCache, - val::Union{Vector{Float64}, SparseArrays.SparseVector{Float64}}, + cache::RowCache{T}, + val::T, key::Int, -) - check_cache_size!(cache) +) where {T <: Union{Vector{Float64}, SparseArrays.SparseVector{Float64}}} + # check size of the stored elements. If exceeding the limit, then one + # element not belonging to the `persistent_cache_keys` is removed. + check_cache_size!(cache; new_add = true) cache.temp_cache[key] = val return end @@ -129,7 +129,8 @@ function Base.length(cache::RowCache) end """ -Deletes the row of of key `key` from the stored matrix in cache. +Deletes a row from the stored matrix in cache not belonging to the +persistent_cache_keys set. """ function purge_one!(cache::RowCache) for k in keys(cache.temp_cache) @@ -142,10 +143,16 @@ function purge_one!(cache::RowCache) end """ -Check saved rows in cache and deletes those ones that are not present in "persistent_cache_keys". +Check saved rows in cache and delete one not belonging to `persistent_cache_keys`. """ -function check_cache_size!(cache::RowCache) - if length(cache.temp_cache) > cache.max_num_keys +function check_cache_size!(cache::RowCache; new_add::Bool = false) + if new_add + v = 1 + else + v = 0 + end + if length(cache.temp_cache) > cache.max_num_keys - v + @info "Maximum memory reached, removing one row from cache (not belonging to `persistent_cache_keys`)." purge_one!(cache) end return diff --git a/test/test_row_cache.jl b/test/test_row_cache.jl new file mode 100644 index 00000000..f3f2ecb9 --- /dev/null +++ b/test/test_row_cache.jl @@ -0,0 +1,43 @@ +@testset "RowCache functions" begin + + # dummy rows + key1 = 1 + row1 = ones((24,)) + + key5 = 5 + row5 = ones((24,)) + + # define cache struct + cache = PNM.RowCache( + 5 * length(row) * sizeof(Float64), + Set([1, 2, 3]), + length(row) * sizeof(Float64), + ) + + # test: Base.isempty + @test isempty(cache) == true + + # test: Base.haskey and Base.getindex + cache[key1] = row1 + @test cache[key1] == row1 + @test haskey(cache, 1) == true + + # test: Base.length (number of rows stored) + @test length(cache) == 1 + + # test: purge_one! + PNM.purge_one!(cache) + @test length(cache) == 1 + @test haskey(cache, 5) == false + + # test: Base.setindex, check_cache_size! + for i in 1:5 + cache[i] = row1 + end + cache[6] = row1 + @test length(cache) == 5 + + # test: Base.empty! + empty!(cache) + @test length(cache) == 0 +end diff --git a/test/test_virtual_ptdf.jl b/test/test_virtual_ptdf.jl index 1f2675d2..0d485874 100644 --- a/test/test_virtual_ptdf.jl +++ b/test/test_virtual_ptdf.jl @@ -97,6 +97,6 @@ end for row in 1:size(ptdf.data, 2) # evaluate the column (just needs one element) vptdf[row, 1] - @assert isapprox(vptdf.cache[row], ptdf[row, :], atol = 1e-5) + @test isapprox(vptdf.cache[row], ptdf[row, :], atol = 1e-5) end end From c46154f140686ffb46817c0f79a04265f41b0897 Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Fri, 18 Aug 2023 16:39:37 -0600 Subject: [PATCH 29/47] fixed name and bug in test --- ...st_ABA_matrix.jl => test_BA_ABA_matrix.jl} | 18 +++++++++++++++++- test/test_adjacency.jl | 4 ++-- test/test_connectivity.jl | 2 +- test/test_lodf.jl | 8 ++++---- test/test_ptdf.jl | 10 +++++----- test/test_row_cache.jl | 19 ++++++++----------- test/test_virtual_lodf.jl | 8 ++++---- test/test_virtual_ptdf.jl | 6 +++--- test/test_ybus.jl | 2 +- 9 files changed, 45 insertions(+), 32 deletions(-) rename test/{test_ABA_matrix.jl => test_BA_ABA_matrix.jl} (79%) diff --git a/test/test_ABA_matrix.jl b/test/test_BA_ABA_matrix.jl similarity index 79% rename from test/test_ABA_matrix.jl rename to test/test_BA_ABA_matrix.jl index 7921d14b..b855e938 100644 --- a/test/test_ABA_matrix.jl +++ b/test/test_BA_ABA_matrix.jl @@ -1,4 +1,4 @@ -@testset "Test ABA matrix" begin +@testset "Test A, BA, and ABA matrix creation" begin # test on 5 and 14 bus system for name in ["c_sys5", "c_sys14"] sys = PSB.build_system(PSB.PSITestSystems, name) @@ -36,3 +36,19 @@ ) end end + +@testset "Test BA and ABA matrix indexing" begin + sys = PSB.build_system(PSB.PSITestSystems, "c_sys5") + + # get the matrices + ba = BA_Matrix(sys) + aba = ABA_Matrix(sys) + + # check if indexing is correct (row and column indices) + for i in size(ba, 1) + for j in size(ba, 2) + + end + end + +end \ No newline at end of file diff --git a/test/test_adjacency.jl b/test/test_adjacency.jl index f2242401..30a4f393 100644 --- a/test/test_adjacency.jl +++ b/test/test_adjacency.jl @@ -1,4 +1,4 @@ -@testset "test connected components" begin +@testset "Test connected components" begin sys5 = PSB.build_system(PSB.PSITestSystems, "c_sys5") M = AdjacencyMatrix(sys5) subnetworks = find_subnetworks(M) @@ -12,4 +12,4 @@ subnetworks_sys = find_subnetworks(sys10) @test all([4, 9] .∈ keys(subnetworks_sys)) -end +end \ No newline at end of file diff --git a/test/test_connectivity.jl b/test/test_connectivity.jl index 5f74a637..8e43c525 100644 --- a/test/test_connectivity.jl +++ b/test/test_connectivity.jl @@ -18,4 +18,4 @@ end "Bus 1 is islanded", ) match_mode = :any validate_connectivity(sys) == false ) -end +end \ No newline at end of file diff --git a/test/test_lodf.jl b/test/test_lodf.jl index 321ab3b0..60197b93 100644 --- a/test/test_lodf.jl +++ b/test/test_lodf.jl @@ -1,4 +1,4 @@ -@testset "LODF matrices" begin +@testset "Test LODF matrices" begin """ NOTE: LODF is transposed """ @@ -55,7 +55,7 @@ @test isapprox(maximum(L14.data), maximum(Lodf_14), atol = 1e-3) end -@testset "Sparse LODF matrix" begin +@testset "Test sparse LODF matrix" begin sys5 = PSB.build_system(PSB.PSITestSystems, "c_sys5") # basic case: system @@ -86,7 +86,7 @@ end @test isapprox(Matrix(L5NS_1.data), L5NS_4.data, atol = 1e-3) end -@testset "LODF getindex and get_lodf_data" begin +@testset "Test LODF getindex and get_lodf_data" begin sys = PSB.build_system(PSB.PSITestSystems, "c_sys14") # get the LODF matrix @@ -109,4 +109,4 @@ end @test isapprox(element_2, element_3, atol = 1e-5) end end -end +end \ No newline at end of file diff --git a/test/test_ptdf.jl b/test/test_ptdf.jl index e7822e26..3abefbb9 100644 --- a/test/test_ptdf.jl +++ b/test/test_ptdf.jl @@ -1,4 +1,4 @@ -@testset "PTDF matrices, w/ and w/o tolerance" begin +@testset "Test PTDF matrices, w/ and w/o tolerance" begin sys5 = PSB.build_system(PSB.PSITestSystems, "c_sys5") for solver in ["KLU", "Dense", "MKLPardiso"] for approach in ["standard", "separate_matrices"] @@ -84,7 +84,7 @@ end end -@testset "PTDF matrices for 10 bus system with 2 reference buses" begin +@testset "Test PTDF matrices for 10 bus system with 2 reference buses" begin # get system sys = PSB.build_system(PSISystems, "2Area 5 Bus System") # get the system composed by 2 5-bus ones connected by a DC line ptdf_complete_klu = PTDF(sys; linear_solver = "KLU") @@ -124,7 +124,7 @@ end end end -@testset "System with isolated buses" begin +@testset "Test System with isolated buses" begin sys_1 = PSB.build_system(PSB.PSITestSystems, "c_sys5") PSY.add_component!( sys_1, @@ -193,7 +193,7 @@ end @test_throws IS.ConflictingInputsError ptdf_2 = PTDF(sys_2) end -@testset "PTDF matrices with distributed slack" begin +@testset "Test PTDF matrices with distributed slack" begin """ CAUTION: this test just test that all the matrices are the same, but there are no reference values. @@ -213,4 +213,4 @@ end @assert isapprox(P5_1.data, P5_2.data, atol = 1e-5) @assert isapprox(P5_1.data, P5_3.data, atol = 1e-5) @assert isapprox(P5_2.data, P5_3.data, atol = 1e-5) -end +end \ No newline at end of file diff --git a/test/test_row_cache.jl b/test/test_row_cache.jl index f3f2ecb9..d56b137b 100644 --- a/test/test_row_cache.jl +++ b/test/test_row_cache.jl @@ -1,11 +1,8 @@ -@testset "RowCache functions" begin +@testset "Test RowCache functions" begin # dummy rows - key1 = 1 - row1 = ones((24,)) - - key5 = 5 - row5 = ones((24,)) + key = 1 + row = ones((24,)) # define cache struct cache = PNM.RowCache( @@ -18,8 +15,8 @@ @test isempty(cache) == true # test: Base.haskey and Base.getindex - cache[key1] = row1 - @test cache[key1] == row1 + cache[key] = row + @test cache[key] == row @test haskey(cache, 1) == true # test: Base.length (number of rows stored) @@ -32,12 +29,12 @@ # test: Base.setindex, check_cache_size! for i in 1:5 - cache[i] = row1 + cache[i] = row end - cache[6] = row1 + cache[6] = row @test length(cache) == 5 # test: Base.empty! empty!(cache) @test length(cache) == 0 -end +end \ No newline at end of file diff --git a/test/test_virtual_lodf.jl b/test/test_virtual_lodf.jl index 0938d526..ab949d24 100644 --- a/test/test_virtual_lodf.jl +++ b/test/test_virtual_lodf.jl @@ -1,4 +1,4 @@ -@testset "Virtual LODF matrices" begin +@testset "Test Virtual LODF matrices" begin sys = PSB.build_system(PSB.PSYTestSystems, "tamu_ACTIVSg2000_sys") vlodf = VirtualLODF(sys) LODF_ref = LODF(sys) @@ -27,7 +27,7 @@ end end -@testset "Virtual LODF functions" begin +@testset "Test Virtual LODF functions" begin # get system sys5 = PSB.build_system(PSB.PSITestSystems, "c_sys5") buses_5 = nodes5() @@ -38,7 +38,7 @@ end @test length(PNM.get_branch_ax(vlodf)) == length(branches_5) end -@testset "Virtual LODF matrices with tolerance" begin +@testset "Test Virtual LODF matrices with tolerance" begin sys = PSB.build_system(PSB.PSITestSystems, "c_sys14") lodf_reference = deepcopy(Lodf_14) lodf_reference[abs.(lodf_reference) .<= 1e-2] .= 0 @@ -63,7 +63,7 @@ end atol = 1e-5) end -@testset "Virtual LODF matrices for 10 bus system with 2 reference buses" begin +@testset "Test Virtual LODF matrices for 10 bus system with 2 reference buses" begin # get system sys = PSB.build_system(PSISystems, "2Area 5 Bus System") # get the system composed by 2 5-bus ones connected by a DC line # get PTDF matrix with KLU as reference diff --git a/test/test_virtual_ptdf.jl b/test/test_virtual_ptdf.jl index 0d485874..45c3921b 100644 --- a/test/test_virtual_ptdf.jl +++ b/test/test_virtual_ptdf.jl @@ -1,4 +1,4 @@ -@testset "Virtual PTDF matrices" begin +@testset "Test Virtual PTDF matrices" begin sys = PSB.build_system(PSB.PSYTestSystems, "tamu_ACTIVSg2000_sys") ptdf_complete = PTDF(sys; linear_solver = "KLU") ptdf_virtual = VirtualPTDF(sys) @@ -15,7 +15,7 @@ @test length(ptdf_virtual.cache) == length(ptdf_virtual.axes[1]) end -@testset "Virtual PTDF matrices with tolerance" begin +@testset "Test Virtual PTDF matrices with tolerance" begin sys = PSB.build_system(PSB.PSITestSystems, "c_sys14") ptdf_reference = deepcopy(S14_slackB1) ptdf_reference[abs.(ptdf_reference) .<= 1e-2] .= 0 @@ -40,7 +40,7 @@ end atol = 1e-4) end -@testset "Virtual PTDF matrices for 10 bus system with 2 reference buses" begin +@testset "Test Virtual PTDF matrices for 10 bus system with 2 reference buses" begin # get system sys = PSB.build_system(PSISystems, "2Area 5 Bus System") # get the system composed by 2 5-bus ones connected by a DC line # get PTDF matrix with KLU as reference diff --git a/test/test_ybus.jl b/test/test_ybus.jl index 403db43c..56e321b0 100644 --- a/test/test_ybus.jl +++ b/test/test_ybus.jl @@ -1,4 +1,4 @@ -@testset "Ybus Matrix" begin +@testset "Test Ybus Matrix" begin sys = PSB.build_system(PSB.PSITestSystems, "c_sys5") buses_5 = nodes5() branches_5 = branches5(buses_5) From 09a39926b20d54c16d28500a75b703a4d7f6aad5 Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Fri, 18 Aug 2023 17:14:35 -0600 Subject: [PATCH 30/47] added test for BA and ABA getindex functions --- test/test_BA_ABA_matrix.jl | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/test/test_BA_ABA_matrix.jl b/test/test_BA_ABA_matrix.jl index b855e938..a758e90c 100644 --- a/test/test_BA_ABA_matrix.jl +++ b/test/test_BA_ABA_matrix.jl @@ -44,11 +44,40 @@ end ba = BA_Matrix(sys) aba = ABA_Matrix(sys) - # check if indexing is correct (row and column indices) - for i in size(ba, 1) - for j in size(ba, 2) + # check if indexing for the BA is correct (row and column indices) + # ba matrix is stored as transposed + for i in size(ba, 2) + for j in size(ba, 1) + @show i, j + @test isapprox(ba[i, j], ba.data[j, i]) + end + end + + # check if indexing for the BA is correct (line names and bus numbers) + lookup1 = PNM.get_lookup(ba) + for i in axes(ba, 2) + for j in axes(ba, 1) + @test isapprox(ba[i, j], ba.data[lookup1[1][j], lookup1[2][i]]) + end + end + + # check indexing for the ABA matrix + lookup2 = aba.lookup[1] + for i in axes(aba, 1) + @test aba[i, :] == aba.data[lookup2[i], :] + end + # test if error is correctly thrown when ref bus is called + rb = collect(ba.ref_bus_positions)[1] + try + aba[rb, :] + catch err + if err isa ErrorException + test_val = true + else + error("Expected an ErrorException but was not thrown.") end end + @test test_val end \ No newline at end of file From 2808d35e98d75999238a70e5ca7993204f4e6480 Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Fri, 18 Aug 2023 17:16:20 -0600 Subject: [PATCH 31/47] formatter --- test/test_BA_ABA_matrix.jl | 5 ++--- test/test_adjacency.jl | 2 +- test/test_connectivity.jl | 2 +- test/test_lodf.jl | 2 +- test/test_ptdf.jl | 2 +- test/test_row_cache.jl | 2 +- 6 files changed, 7 insertions(+), 8 deletions(-) diff --git a/test/test_BA_ABA_matrix.jl b/test/test_BA_ABA_matrix.jl index a758e90c..54c4888b 100644 --- a/test/test_BA_ABA_matrix.jl +++ b/test/test_BA_ABA_matrix.jl @@ -69,7 +69,7 @@ end # test if error is correctly thrown when ref bus is called rb = collect(ba.ref_bus_positions)[1] - try + try aba[rb, :] catch err if err isa ErrorException @@ -79,5 +79,4 @@ end end end @test test_val - -end \ No newline at end of file +end diff --git a/test/test_adjacency.jl b/test/test_adjacency.jl index 30a4f393..a7e451df 100644 --- a/test/test_adjacency.jl +++ b/test/test_adjacency.jl @@ -12,4 +12,4 @@ subnetworks_sys = find_subnetworks(sys10) @test all([4, 9] .∈ keys(subnetworks_sys)) -end \ No newline at end of file +end diff --git a/test/test_connectivity.jl b/test/test_connectivity.jl index 8e43c525..5f74a637 100644 --- a/test/test_connectivity.jl +++ b/test/test_connectivity.jl @@ -18,4 +18,4 @@ end "Bus 1 is islanded", ) match_mode = :any validate_connectivity(sys) == false ) -end \ No newline at end of file +end diff --git a/test/test_lodf.jl b/test/test_lodf.jl index 60197b93..dda34230 100644 --- a/test/test_lodf.jl +++ b/test/test_lodf.jl @@ -109,4 +109,4 @@ end @test isapprox(element_2, element_3, atol = 1e-5) end end -end \ No newline at end of file +end diff --git a/test/test_ptdf.jl b/test/test_ptdf.jl index 3abefbb9..001f0203 100644 --- a/test/test_ptdf.jl +++ b/test/test_ptdf.jl @@ -213,4 +213,4 @@ end @assert isapprox(P5_1.data, P5_2.data, atol = 1e-5) @assert isapprox(P5_1.data, P5_3.data, atol = 1e-5) @assert isapprox(P5_2.data, P5_3.data, atol = 1e-5) -end \ No newline at end of file +end diff --git a/test/test_row_cache.jl b/test/test_row_cache.jl index d56b137b..5fde4ddb 100644 --- a/test/test_row_cache.jl +++ b/test/test_row_cache.jl @@ -37,4 +37,4 @@ # test: Base.empty! empty!(cache) @test length(cache) == 0 -end \ No newline at end of file +end From 9c809ad0c9f103407cabde300a47cc817a103402 Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Fri, 18 Aug 2023 17:33:17 -0600 Subject: [PATCH 32/47] fixed test --- test/test_BA_ABA_matrix.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_BA_ABA_matrix.jl b/test/test_BA_ABA_matrix.jl index 54c4888b..8a24627f 100644 --- a/test/test_BA_ABA_matrix.jl +++ b/test/test_BA_ABA_matrix.jl @@ -69,6 +69,7 @@ end # test if error is correctly thrown when ref bus is called rb = collect(ba.ref_bus_positions)[1] + test_val = false try aba[rb, :] catch err From cb87db3e29ff2ba5d55e0013eefdf5656f5a551c Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Fri, 18 Aug 2023 17:51:17 -0600 Subject: [PATCH 33/47] removed sparsification functions --- src/PowerNetworkMatrices.jl | 1 - src/common.jl | 54 -------------------------------- src/lodf_calculations.jl | 19 ----------- src/ptdf_calculations.jl | 19 ----------- src/virtual_lodf_calculations.jl | 21 ------------- src/virtual_ptdf_calculations.jl | 21 ------------- test/test_lodf.jl | 5 --- test/test_ptdf.jl | 11 ++----- test/test_virtual_lodf.jl | 8 ----- test/test_virtual_ptdf.jl | 8 ----- 10 files changed, 3 insertions(+), 164 deletions(-) diff --git a/src/PowerNetworkMatrices.jl b/src/PowerNetworkMatrices.jl index 9c6a3199..b105c9f2 100644 --- a/src/PowerNetworkMatrices.jl +++ b/src/PowerNetworkMatrices.jl @@ -3,7 +3,6 @@ module PowerNetworkMatrices export ABA_Matrix export AdjacencyMatrix export BA_Matrix -export drop_small_entries! export factorize export find_subnetworks export from_hdf5 diff --git a/src/common.jl b/src/common.jl index dbea24fe..2d311935 100644 --- a/src/common.jl +++ b/src/common.jl @@ -256,60 +256,6 @@ function sparsify(dense_array::Vector{Float64}, tol::Float64) return sparse_array end -""" -Sets to zero every element of a Sparse matrix if absolute values is below a -certain tolerance. - -# Arguments -- `sparse_array::SparseArrays.SparseMatrixCSC{Float64, Int}`: input sparse array. -- `tol::Float64`: tolerance for removing entries in the PTDF matrix. -""" -function make_entries_zero!( - sparse_array::SparseArrays.SparseMatrixCSC{Float64, Int}, - tol::Float64, -) - for i in 1:size(sparse_array, 1) - sparse_array[i, abs.(sparse_array[i, :]) .<= tol] .= 0.0 - end - SparseArrays.dropzeros!(sparse_array) - return -end - -""" -Sets to zero every element of a Dense matrix if absolute values is below a -certain tolerance. - -# Arguments -- `dense_array::Matrix{Float64}`: input dense matrix. -- `tol::Float64`: tolerance. -""" -function make_entries_zero!( - dense_array::Matrix{Float64}, - tol::Float64, -) - for i in 1:size(dense_array, 1) - dense_array[i, abs.(dense_array[i, :]) .<= tol] .= 0.0 - end - return -end - -""" -Sets to zero every element of a Dense vector if absolute values is below a -certain tolerance. - -# Arguments -- `vector::Vector{Float64}`:input dense vector. -- `tol::Float64`: tolerance. -""" -function make_entries_zero!(vector::Vector{Float64}, tol::Float64) - for i in eachindex(vector) - if abs(vector[i]) <= tol - vector[i] = 0.0 - end - end - return -end - """ !!! MISSING DOCUMENTATION !!! """ diff --git a/src/lodf_calculations.jl b/src/lodf_calculations.jl index dc2bcca4..deed9db6 100644 --- a/src/lodf_calculations.jl +++ b/src/lodf_calculations.jl @@ -23,25 +23,6 @@ struct LODF{Ax, L <: NTuple{2, Dict}, M <: AbstractArray{Float64, 2}} <: tol::Base.RefValue{Float64} end -""" -Sets to zero those elements of the LODF matrix whose absolute values are below -the threshold specified by the field "tol". - -# Arguments -- `mat::LODF`: - LODF structure -- `tol::Float64`: - tolerance -""" -function drop_small_entries!(mat::LODF, tol::Float64) - if tol < mat.tol[] - @info "Specified tolerance is smaller than the current tolerance." - end - make_entries_zero!(mat.data, tol) - mat.tol[] = tol - return -end - function _buildlodf( a::SparseArrays.SparseMatrixCSC{Int8, Int}, ptdf::Matrix{Float64}, diff --git a/src/ptdf_calculations.jl b/src/ptdf_calculations.jl index 9b31219a..2d022741 100644 --- a/src/ptdf_calculations.jl +++ b/src/ptdf_calculations.jl @@ -40,25 +40,6 @@ Deserialize a PTDF from an HDF5 file. """ PTDF(filename::AbstractString) = from_hdf5(PTDF, filename) -""" -Sets to zero those elements of the PTDF matrix whose absolute values are below -the threshold specified by the field "tol". - -# Arguments -- `mat::PTDF`: - PTDF structure -- `tol::Float64`: - tolerance -""" -function drop_small_entries!(mat::PTDF, tol::Float64) - if tol < mat.tol[] - @info "Specified tolerance is smaller than the current tolerance." - end - make_entries_zero!(mat.data, tol) - mat.tol[] = tol - return -end - function _buildptdf( branches, buses::Vector{PSY.Bus}, diff --git a/src/virtual_lodf_calculations.jl b/src/virtual_lodf_calculations.jl index 3c4ac01b..9f71fea5 100644 --- a/src/virtual_lodf_calculations.jl +++ b/src/virtual_lodf_calculations.jl @@ -54,27 +54,6 @@ struct VirtualLODF{Ax, L <: NTuple{2, Dict}} <: PowerNetworkMatrix{Float64} tol::Base.RefValue{Float64} end -""" -Sets to zero those elements of each LODF matrix row whose absolute values are -below the threshold specified by the field "tol". - -# Arguments -- `mat::VirtualLODF`: - VirtualLODF structure -- `tol::Float64`: - tolerance -""" -function drop_small_entries!(mat::VirtualLODF, tol::Float64) - if tol < mat.tol[] - @info "Specified tolerance is smaller than the current tolerance." - end - for i in keys(mat.cache.temp_cache) - make_entries_zero!(mat.cache[i], tol) - end - mat.tol[] = tol - return -end - function _get_PTDF_A_diag( K::KLU.KLUFactorization{Float64, Int}, BA::SparseArrays.SparseMatrixCSC{Float64, Int}, diff --git a/src/virtual_ptdf_calculations.jl b/src/virtual_ptdf_calculations.jl index 7de9f6e0..99b2c9eb 100644 --- a/src/virtual_ptdf_calculations.jl +++ b/src/virtual_ptdf_calculations.jl @@ -143,27 +143,6 @@ function VirtualPTDF( return VirtualPTDF(branches, buses; kwargs...) end -""" -Sets to zero those elements of each PTDF matrix row whose absolute values are -below the threshold specified by the field "tol". - -# Arguments -- `mat::VirtualPTDF`: - VirtualPTDF structure -- `tol::Float64`: - tolerance -""" -function drop_small_entries!(mat::VirtualPTDF, tol::Float64) - if tol < mat.tol[] - @info "Specified tolerance is smaller than the current tolerance." - end - for i in keys(mat.cache.temp_cache) - make_entries_zero!(mat.cache[i], tol) - end - mat.tol[] = tol - return -end - # Overload Base functions """ diff --git a/test/test_lodf.jl b/test/test_lodf.jl index dda34230..4463c9e9 100644 --- a/test/test_lodf.jl +++ b/test/test_lodf.jl @@ -61,10 +61,6 @@ end # basic case: system L5NS_1 = LODF(sys5; tol = 0.4) - # basic case: system but sparsification is done afterwards - L5NS_2 = LODF(sys5) - drop_small_entries!(L5NS_2, 0.4) - # A and PTDF case a_matrix = IncidenceMatrix(sys5) ptdf_matrix = PTDF(sys5) @@ -81,7 +77,6 @@ end # tests @test isapprox(Matrix(L5NS_1.data), ref_sparse_Lodf_5, atol = 1e-3) - @test isapprox(Matrix(L5NS_1.data), L5NS_2.data, atol = 1e-3) @test isapprox(Matrix(L5NS_1.data), L5NS_3.data, atol = 1e-3) @test isapprox(Matrix(L5NS_1.data), L5NS_4.data, atol = 1e-3) end diff --git a/test/test_ptdf.jl b/test/test_ptdf.jl index 001f0203..4cebb782 100644 --- a/test/test_ptdf.jl +++ b/test/test_ptdf.jl @@ -62,7 +62,6 @@ @test isapprox(get_ptdf_data(P5NS), P5NS_mod, atol = 1e-3) PRTS = PTDF(RTS; linear_solver = solver) - PRTS_1 = PTDF(RTS; linear_solver = solver) PRTS_mod = zeros(size(SRTS_GMLC)) PRTS_sparse = PTDF(RTS; linear_solver = solver, tol = 1e-3) bnums = sort([PSY.get_number(b) for b in PSY.get_components(Bus, RTS)]) @@ -73,13 +72,9 @@ @test isapprox(getindex(PRTS, br, b), SRTS_GMLC[ibr, ib], atol = 1e-3) end - # check sparsification (between different methods) - drop_small_entries!(PRTS_1, 1e-1) # dense matrix - drop_small_entries!(PRTS_sparse, 1e-1) # sparse matrix - @test sum(abs.(get_ptdf_data(PRTS_1) - get_ptdf_data(PRTS_sparse)) .> 1e-3) == 0 - # check sparsification (between method and reference values) - PowerNetworkMatrices.make_entries_zero!(PRTS_mod, 1e-1) - @test sum(abs.(get_ptdf_data(PRTS_1) - PRTS_mod) .> 1e-3) == 0 + # manually sparsify the matrix + PRTS.data[abs.(PRTS.data) .< 1e-3] .= 0 + @test sum(abs.(get_ptdf_data(PRTS) - get_ptdf_data(PRTS_sparse)) .> 1e-3) == 0 end end end diff --git a/test/test_virtual_lodf.jl b/test/test_virtual_lodf.jl index ab949d24..8bc5e18f 100644 --- a/test/test_virtual_lodf.jl +++ b/test/test_virtual_lodf.jl @@ -43,20 +43,12 @@ end lodf_reference = deepcopy(Lodf_14) lodf_reference[abs.(lodf_reference) .<= 1e-2] .= 0 lodf_virtual_with_tol = VirtualLODF(sys; tol = 1e-2) - lodf_virtual_with_tol1 = VirtualLODF(sys) for (n, i) in enumerate(axes(lodf_virtual_with_tol, 1)) # get the row - lodf_virtual_with_tol1[i, :] @test isapprox( lodf_virtual_with_tol[i, :], lodf_reference[n, :], atol = 1e-3) end - drop_small_entries!(lodf_virtual_with_tol1, 1e-2) - for (n, i) in enumerate(axes(lodf_virtual_with_tol, 1)) - @test isapprox( - lodf_virtual_with_tol1[i, :], lodf_reference[n, :], atol = 1e-3) - end - @test isapprox( sum(abs.(lodf_reference[lodf_virtual_with_tol.lookup[1]["Line12"], :])), sum(abs.(lodf_virtual_with_tol["Line12", :])), diff --git a/test/test_virtual_ptdf.jl b/test/test_virtual_ptdf.jl index 45c3921b..6290d11d 100644 --- a/test/test_virtual_ptdf.jl +++ b/test/test_virtual_ptdf.jl @@ -20,20 +20,12 @@ end ptdf_reference = deepcopy(S14_slackB1) ptdf_reference[abs.(ptdf_reference) .<= 1e-2] .= 0 ptdf_virtual_with_tol = VirtualPTDF(sys; tol = 1e-2) - ptdf_virtual_with_tol1 = VirtualPTDF(sys) for (n, i) in enumerate(axes(ptdf_virtual_with_tol, 1)) # get the row - ptdf_virtual_with_tol1[i, :] @test isapprox( ptdf_virtual_with_tol[i, :], ptdf_reference[n, :], atol = 1e-3) end - drop_small_entries!(ptdf_virtual_with_tol1, 1e-2) - for (n, i) in enumerate(axes(ptdf_virtual_with_tol, 1)) - @test isapprox( - ptdf_virtual_with_tol1[i, :], ptdf_reference[n, :], atol = 1e-3) - end - @test isapprox( sum(abs.(ptdf_reference[ptdf_virtual_with_tol.lookup[1]["Line12"], :])), sum(abs.(ptdf_virtual_with_tol["Line12", :])), From 96c17243f0916635015a6ca33bbc879c5c20a10b Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Fri, 18 Aug 2023 18:30:26 -0600 Subject: [PATCH 34/47] fixed last things with Jose's suggestion --- src/row_cache.jl | 2 +- test/test_row_cache.jl | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/row_cache.jl b/src/row_cache.jl index 67dc57c4..b36e55e8 100644 --- a/src/row_cache.jl +++ b/src/row_cache.jl @@ -117,7 +117,7 @@ Gets the row of the stored matrix in cache. function Base.getindex( cache::RowCache, key::Int, -)::Union{Vector{Float64}, SparseArrays.SparseVector{Float64}} +) return cache.temp_cache[key] end diff --git a/test/test_row_cache.jl b/test/test_row_cache.jl index 5fde4ddb..0124bd0a 100644 --- a/test/test_row_cache.jl +++ b/test/test_row_cache.jl @@ -16,7 +16,6 @@ # test: Base.haskey and Base.getindex cache[key] = row - @test cache[key] == row @test haskey(cache, 1) == true # test: Base.length (number of rows stored) From 0e9600f4549c6fb974aaef4433b61100b3a29f24 Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Fri, 18 Aug 2023 19:17:00 -0600 Subject: [PATCH 35/47] added tests to increse coverage --- test/test_BA_ABA_matrix.jl | 5 ++-- test/test_ptdf.jl | 55 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/test/test_BA_ABA_matrix.jl b/test/test_BA_ABA_matrix.jl index 8a24627f..7b29912b 100644 --- a/test/test_BA_ABA_matrix.jl +++ b/test/test_BA_ABA_matrix.jl @@ -46,9 +46,8 @@ end # check if indexing for the BA is correct (row and column indices) # ba matrix is stored as transposed - for i in size(ba, 2) - for j in size(ba, 1) - @show i, j + for i in 1:size(ba, 2) + for j in 1:size(ba, 1) @test isapprox(ba[i, j], ba.data[j, i]) end end diff --git a/test/test_ptdf.jl b/test/test_ptdf.jl index 4cebb782..48d94f7e 100644 --- a/test/test_ptdf.jl +++ b/test/test_ptdf.jl @@ -77,6 +77,16 @@ @test sum(abs.(get_ptdf_data(PRTS) - get_ptdf_data(PRTS_sparse)) .> 1e-3) == 0 end end + + # check axes values + P5 = PTDF(sys5) + @test setdiff(PNM.get_branch_ax(P5), PSY.get_name.(PNM.get_ac_branches(sys5))) == + String[] + @test setdiff(PNM.get_bus_ax(P5), PSY.get_number.(PNM.get_buses(sys5))) == String[] + + # auxiliary functio1n + PRTS_sparse = PTDF(RTS; tol = 1e-3) + @test PNM.get_tol(PRTS_sparse).x == Base.RefValue(1e-3).x end @testset "Test PTDF matrices for 10 bus system with 2 reference buses" begin @@ -209,3 +219,48 @@ end @assert isapprox(P5_1.data, P5_3.data, atol = 1e-5) @assert isapprox(P5_2.data, P5_3.data, atol = 1e-5) end + +@testset "Test PTDF matrix with distributed bus and with 2 reference buses" begin + + # check if an error is correctly thrown + + # 2 bus system + sys = PSB.build_system(PSISystems, "2Area 5 Bus System") + buscount = length(PNM.get_buses(sys)) + dist_slack = 1 / buscount * ones(buscount) + slack_array = dist_slack / sum(dist_slack) + + test_val1 = false + try + ptdf_1 = PTDF(sys; dist_slack = slack_array) + catch err + if err isa ErrorException + test_val1 = true + else + error( + "Error was not thrown for PTDF with distributed slack bus and more than one reference bus.", + ) + end + end + + # incorrect dist_slack arrya length + sys5 = PSB.build_system(PSB.PSITestSystems, "c_sys5") + buscount = length(PNM.get_buses(sys5)) + 1 + dist_slack = 1 / buscount * ones(buscount) + slack_array = dist_slack / sum(dist_slack) + test_val2 = false + try + ptdf_2 = PTDF(sys5; dist_slack = slack_array) + catch err + if err isa ErrorException + test_val2 = true + else + error( + "Error was not thrown for PTDF with distributed slack array of incorrect length.", + ) + end + end + + @test test_val1 + @test test_val2 +end From 16b2b76e44222d8fb06abfaacf8e24c9d0eba06f Mon Sep 17 00:00:00 2001 From: Jose Daniel Lara Date: Sat, 19 Aug 2023 14:27:38 -0600 Subject: [PATCH 36/47] Update test/test_BA_ABA_matrix.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- test/test_BA_ABA_matrix.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/test/test_BA_ABA_matrix.jl b/test/test_BA_ABA_matrix.jl index 7b29912b..a4d9a608 100644 --- a/test/test_BA_ABA_matrix.jl +++ b/test/test_BA_ABA_matrix.jl @@ -51,7 +51,6 @@ end @test isapprox(ba[i, j], ba.data[j, i]) end end - # check if indexing for the BA is correct (line names and bus numbers) lookup1 = PNM.get_lookup(ba) for i in axes(ba, 2) From c5b29bcae3aca9f4e567fa7feec7aa9e090fe553 Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Sat, 19 Aug 2023 14:34:49 -0600 Subject: [PATCH 37/47] changes isempty for VirtualPTDF, added tests --- src/virtual_ptdf_calculations.jl | 28 +++++++++++++--------- test/test_ptdf.jl | 2 +- test/test_virtual_ptdf.jl | 41 +++++++++++++++++++++++++++++--- 3 files changed, 56 insertions(+), 15 deletions(-) diff --git a/src/virtual_ptdf_calculations.jl b/src/virtual_ptdf_calculations.jl index 99b2c9eb..f93e6dd1 100644 --- a/src/virtual_ptdf_calculations.jl +++ b/src/virtual_ptdf_calculations.jl @@ -149,17 +149,23 @@ end Checks if the any of the fields of VirtualPTDF is empty. """ function Base.isempty(vptdf::VirtualPTDF) - !isempty(vptdf.K.L) && return false - !isempty(vptdf.K.U) && return false - !isempty(vptdf.BA) && return false - !isempty(vptdf.ref_bus_positions) && return false - !isempty(vptdf.axes) && return false - !isempty(vptdf.lookup) && return false - !isempty(vptdf.temp_data) && return false - !isempty(vptdf.valid_ix) && return false - !isempty(vptdf.cache) && return false - !isempty(vptdf.subnetworks) && return false - return true + for name in fieldnames(typeof(vptdf)) + @show name + if name == :K + if isempty(vptdf.K.L) || isempty(vptdf.K.U) + return true + break + end + elseif name in [:tol, :dist_slack] + @info "Field " * string(name) * " has default value: " * + string(getfield(vptdf, name)) * "." + elseif isempty(getfield(vptdf, name)) + @info "Field " * string(name) * " not defined. Other fields might be empty." + return true + break + end + end + return false end """ diff --git a/test/test_ptdf.jl b/test/test_ptdf.jl index 48d94f7e..c7cab180 100644 --- a/test/test_ptdf.jl +++ b/test/test_ptdf.jl @@ -84,7 +84,7 @@ String[] @test setdiff(PNM.get_bus_ax(P5), PSY.get_number.(PNM.get_buses(sys5))) == String[] - # auxiliary functio1n + # auxiliary function PRTS_sparse = PTDF(RTS; tol = 1e-3) @test PNM.get_tol(PRTS_sparse).x == Base.RefValue(1e-3).x end diff --git a/test/test_virtual_ptdf.jl b/test/test_virtual_ptdf.jl index 6290d11d..9765b2ac 100644 --- a/test/test_virtual_ptdf.jl +++ b/test/test_virtual_ptdf.jl @@ -4,7 +4,6 @@ ptdf_virtual = VirtualPTDF(sys) for i in axes(ptdf_complete, 2) - comp = ptdf_complete[i, :] virtual = ptdf_virtual[i, :] for j in axes(ptdf_complete, 1) # check values using PTDFs axes @@ -61,7 +60,7 @@ end @test isapprox(ptdf_first_area, ptdf_second_area, atol = 1e-6) end -@testset "Test virtual PTDF cache" begin +@testset "Test Virtual PTDF cache" begin RTS = build_system(PSITestSystems, "test_RTS_GMLC_sys") line_names = get_name.(get_components(Line, RTS)) persist_lines = line_names[1:10] @@ -77,7 +76,7 @@ end end end -@testset "Test virtual PTDF with distributed slack" begin +@testset "Test Virtual PTDF with distributed slack" begin # get 5 bus system sys = PSB.build_system(PSB.PSITestSystems, "c_sys14") bus_number = length(PNM.get_buses(sys)) @@ -92,3 +91,39 @@ end @test isapprox(vptdf.cache[row], ptdf[row, :], atol = 1e-5) end end + +@testset "Test Virtual PTDF auxiliary functions" begin + sys = PSB.build_system(PSB.PSITestSystems, "c_sys5") + + vptdf = VirtualPTDF(sys) + @test isempty(vptdf) == true + + # check if error is correctly thrown + test_value = false + try + vptdf[1, 1] = 1 + catch err + if err isa ErrorException + test_value = true + end + end + @test test_value + + # get the rows and full PTDF matrix, test get_ptdf_data + ptdf = PTDF(sys) + for i in PNM.get_branch_ax(vptdf) + for j in PNM.get_bus_ax(vptdf) + vptdf[i, j] + end + end + dict_ = Dict() + for (n, i) in enumerate(PNM.get_branch_ax(vptdf)) + dict_[n] = vptdf.cache[n] + end + @test get_ptdf_data(vptdf) == dict_ + + # test get axes values + @test setdiff(PNM.get_branch_ax(vptdf), PSY.get_name.(PNM.get_ac_branches(sys))) == + String[] + @test setdiff(PNM.get_bus_ax(vptdf), PSY.get_number.(PNM.get_buses(sys))) == String[] +end From adbe1fa522cb78a0856fbee8b6821efc440ea50e Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Sat, 19 Aug 2023 15:01:42 -0600 Subject: [PATCH 38/47] added tests for lodf matrix --- test/test_lodf.jl | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/test/test_lodf.jl b/test/test_lodf.jl index 4463c9e9..c6bcd03a 100644 --- a/test/test_lodf.jl +++ b/test/test_lodf.jl @@ -48,6 +48,29 @@ @test isapprox(L5NS_from_ba_aba[i, :], Lodf_5[i, :], atol = 1e-3) end + # test if error is thrown in case `tol` is defined in PTDF + P5 = PTDF(sys5; tol = 1e-3) + test_value = false + try + L5NS_from_ptdf = LODF(A, P5) + catch err + if err isa ErrorException + test_value = true + end + end + @test test_value + + # test if error is thrown in case `MKLPardiso` is chosen as a linera solver + test_value = false + try + lodf = LODF(sys5; linear_solver = "MKLPardiso") + catch err + if err isa ErrorException + test_value = true + end + end + @test test_value + # get 14 bus system buses_14 = nodes14() branches_14 = branches14(buses_14) @@ -73,7 +96,7 @@ end # reference value ref_sparse_Lodf_5 = deepcopy(Lodf_5') - ref_sparse_Lodf_5[abs.(ref_sparse_Lodf_5) .< 0.4] .= 0 + ref_sparse_Lodf_5[abs.(ref_sparse_Lodf_5) .< PNM.get_tol(L5NS_1)] .= 0 # tests @test isapprox(Matrix(L5NS_1.data), ref_sparse_Lodf_5, atol = 1e-3) @@ -105,3 +128,6 @@ end end end end + +@testset "Test LODF auxiliary functions" begin +end From 467ff148d97ad9ddab0b8e42c5871facba69b8e5 Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Sat, 19 Aug 2023 15:08:20 -0600 Subject: [PATCH 39/47] formatter --- test/test_lodf.jl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/test_lodf.jl b/test/test_lodf.jl index c6bcd03a..6e78fa68 100644 --- a/test/test_lodf.jl +++ b/test/test_lodf.jl @@ -127,7 +127,4 @@ end @test isapprox(element_2, element_3, atol = 1e-5) end end -end - -@testset "Test LODF auxiliary functions" begin -end +end \ No newline at end of file From bea06582ad8e02d58c486900afd18478ff6ed490 Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Sat, 19 Aug 2023 15:15:37 -0600 Subject: [PATCH 40/47] formatter --- test/test_lodf.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_lodf.jl b/test/test_lodf.jl index 6e78fa68..ecbf64d3 100644 --- a/test/test_lodf.jl +++ b/test/test_lodf.jl @@ -127,4 +127,4 @@ end @test isapprox(element_2, element_3, atol = 1e-5) end end -end \ No newline at end of file +end From 3665b92cd86ecae984adce648588e7d0c5aa81df Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Sat, 19 Aug 2023 15:23:17 -0600 Subject: [PATCH 41/47] improved coverage for LODF tests --- src/lodf_calculations.jl | 1 - test/test_lodf.jl | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/lodf_calculations.jl b/src/lodf_calculations.jl index deed9db6..b810f189 100644 --- a/src/lodf_calculations.jl +++ b/src/lodf_calculations.jl @@ -193,7 +193,6 @@ function LODF( Ref(tol), ) end - return end """ diff --git a/test/test_lodf.jl b/test/test_lodf.jl index ecbf64d3..5a26cc2a 100644 --- a/test/test_lodf.jl +++ b/test/test_lodf.jl @@ -29,9 +29,13 @@ A = IncidenceMatrix(sys5) P5 = PTDF(sys5) L5NS_from_ptdf = LODF(A, P5) + L5NS_from_ptdf2 = LODF(A, P5; linear_solver = "Dense") @test getindex(L5NS_from_ptdf, "5", "6") - -0.3071 <= 1e-4 + @test getindex(L5NS_from_ptdf2, "5", "6") - -0.3071 <= 1e-4 total_error = abs.(L5NS_from_ptdf.data' .- Lodf_5) + total_error2 = abs.(L5NS_from_ptdf2.data' .- Lodf_5) @test isapprox(sum(total_error), 0.0, atol = 1e-3) + @test isapprox(sum(total_error2), 0.0, atol = 1e-3) # A, ABA, and BA case ABA = ABA_Matrix(sys5; factorize = true) @@ -48,6 +52,17 @@ @test isapprox(L5NS_from_ba_aba[i, :], Lodf_5[i, :], atol = 1e-3) end + # test if error is thrown in case other linear solvers are called + test_value = false + try + L5NS_from_ba_aba = LODF(A, ABA, BA; linear_solver = "Dense") + catch err + if err isa ErrorException + test_value = true + end + end + @test test_value + # test if error is thrown in case `tol` is defined in PTDF P5 = PTDF(sys5; tol = 1e-3) test_value = false From c7c97cc803fe46b2ab9db75055dab514edce39e5 Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Sat, 19 Aug 2023 15:46:56 -0600 Subject: [PATCH 42/47] extended tests on virtual lodf --- src/virtual_lodf_calculations.jl | 28 ++++++++++++++++------------ test/test_virtual_lodf.jl | 21 +++++++++++++++++++++ 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/virtual_lodf_calculations.jl b/src/virtual_lodf_calculations.jl index 9f71fea5..e62d1f72 100644 --- a/src/virtual_lodf_calculations.jl +++ b/src/virtual_lodf_calculations.jl @@ -164,18 +164,22 @@ end Checks if the any of the fields of VirtualLODF is empty. """ function Base.isempty(vlodf::VirtualLODF) - !isempty(vlodf.K.L) && return false - !isempty(vlodf.K.U) && return false - !isempty(vlodf.BA) && return false - !isempty(vlodf.A) && return false - !isempty(vlodf.inv_PTDF_A_diag) && return false - !isempty(vlodf.ref_bus_positions) && return false - !isempty(vlodf.axes) && return false - !isempty(vlodf.lookup) && return false - !isempty(vlodf.cache) && return false - !isempty(subnetworks) && return false - !isempty(tol) && return false - return true + for name in fieldnames(typeof(vlodf)) + @show name + if name == :K + if isempty(vlodf.K.L) || isempty(vlodf.K.U) + return true + break + end + elseif name == :tol + @info "Field tol has default value: " * string(getfield(vlodf, name)) * "." + elseif isempty(getfield(vlodf, name)) + @info "Field " * string(name) * " not defined. Other fields might be empty." + return true + break + end + end + return false end """ diff --git a/test/test_virtual_lodf.jl b/test/test_virtual_lodf.jl index 8bc5e18f..c53e5cd8 100644 --- a/test/test_virtual_lodf.jl +++ b/test/test_virtual_lodf.jl @@ -99,3 +99,24 @@ end @test vlodf.lookup[1][l] ∈ keys(vlodf.cache.temp_cache) end end + +@testset "Test Virtual LODF auxiliary functions" begin + sys = PSB.build_system(PSB.PSITestSystems, "c_sys5") + + vlodf = VirtualLODF(sys) + @test isempty(vlodf) == true + + @test length(eachindex(vlodf)) == + length(axes(vlodf)[1]) * length(axes(vlodf)[2]) + + # check if error is correctly thrown + test_value = false + try + vlodf[1, 1] = 1 + catch err + if err isa ErrorException + test_value = true + end + end + @test test_value +end From 678ee366561275ce620cc8724f41aed3be5319d1 Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Tue, 22 Aug 2023 18:50:09 -0600 Subject: [PATCH 43/47] implemented isempty for VirtualLODF and PTDF. Issue related to isempty called when Virtual LODF and PTDF are defined still to be solved! --- src/virtual_lodf_calculations.jl | 18 ++++-------- src/virtual_ptdf_calculations.jl | 22 +++++++------- test/test_ptdf.jl | 2 +- test/test_virtual_ptdf.jl | 50 ++++++++++++++++++++++++++++++++ 4 files changed, 67 insertions(+), 25 deletions(-) diff --git a/src/virtual_lodf_calculations.jl b/src/virtual_lodf_calculations.jl index e62d1f72..e8f07420 100644 --- a/src/virtual_lodf_calculations.jl +++ b/src/virtual_lodf_calculations.jl @@ -165,18 +165,12 @@ Checks if the any of the fields of VirtualLODF is empty. """ function Base.isempty(vlodf::VirtualLODF) for name in fieldnames(typeof(vlodf)) - @show name - if name == :K - if isempty(vlodf.K.L) || isempty(vlodf.K.U) - return true - break - end - elseif name == :tol - @info "Field tol has default value: " * string(getfield(vlodf, name)) * "." - elseif isempty(getfield(vlodf, name)) - @info "Field " * string(name) * " not defined. Other fields might be empty." + if name == :K && (isempty(vlodf.K.L) || isempty(vlodf.K.U)) + @info "Either L o U factorization matrix is empty." + return true + elseif name != :K && isempty(getfield(vlodf, name)) + @info "Field " * string(name) * " is empty." return true - break end end return false @@ -185,8 +179,8 @@ end """ Shows the size of the whole LODF matrix, not the number of rows stored. """ -# ! test Base.size(vlodf::VirtualLODF) = (size(vlodf.BA, 2), size(vlodf.BA, 2)) + """ Gives the cartesian indexes of the LODF matrix. """ diff --git a/src/virtual_ptdf_calculations.jl b/src/virtual_ptdf_calculations.jl index f93e6dd1..80019c0f 100644 --- a/src/virtual_ptdf_calculations.jl +++ b/src/virtual_ptdf_calculations.jl @@ -104,6 +104,7 @@ function VirtualPTDF( subnetworks = assing_reference_buses(subnetworks, ref_bus_positions) end temp_data = zeros(length(bus_ax)) + # if isempty(persistent_lines) if isempty(persistent_lines) empty_cache = RowCache(max_cache_size * MiB, Set{Int}(), length(bus_ax) * sizeof(Float64)) @@ -150,19 +151,15 @@ Checks if the any of the fields of VirtualPTDF is empty. """ function Base.isempty(vptdf::VirtualPTDF) for name in fieldnames(typeof(vptdf)) - @show name - if name == :K - if isempty(vptdf.K.L) || isempty(vptdf.K.U) - return true - break - end - elseif name in [:tol, :dist_slack] - @info "Field " * string(name) * " has default value: " * - string(getfield(vptdf, name)) * "." - elseif isempty(getfield(vptdf, name)) - @info "Field " * string(name) * " not defined. Other fields might be empty." + if name == :K && (isempty(vptdf.K.L) || isempty(vptdf.K.U)) + @info "Either L o U factorization matrix is empty." + return true + elseif name == :dist_slack && isempty(getfield(vptdf, name)) + @info "Field dist_slack has default value: " * + string(getfield(vptdf, name)) * "." + elseif !(name in [:K, :dist_slack]) && isempty(getfield(vptdf, name)) + @info "Field " * string(name) * " not defined." return true - break end end return false @@ -172,6 +169,7 @@ end Gives the size of the whole PTDF matrix, not the number of rows stored. """ Base.size(vptdf::VirtualPTDF) = size(vptdf.BA) + """ Gives the cartesian indexes of the PTDF matrix (same as the BA one). """ diff --git a/test/test_ptdf.jl b/test/test_ptdf.jl index c7cab180..99185b3b 100644 --- a/test/test_ptdf.jl +++ b/test/test_ptdf.jl @@ -224,7 +224,7 @@ end # check if an error is correctly thrown - # 2 bus system + # 2 reference bus system sys = PSB.build_system(PSISystems, "2Area 5 Bus System") buscount = length(PNM.get_buses(sys)) dist_slack = 1 / buscount * ones(buscount) diff --git a/test/test_virtual_ptdf.jl b/test/test_virtual_ptdf.jl index 9765b2ac..1e076cbd 100644 --- a/test/test_virtual_ptdf.jl +++ b/test/test_virtual_ptdf.jl @@ -92,12 +92,62 @@ end end end +@testset "Test Virtual PTDF matrix with distributed bus and with 2 reference buses" begin + # check if an error is correctly thrown + + # 2 reference bus system + sys = PSB.build_system(PSISystems, "2Area 5 Bus System") + buscount = length(PNM.get_buses(sys)) + dist_slack = 1 / buscount * ones(buscount) + slack_array = dist_slack / sum(dist_slack) + + test_val1 = false + try + ptdf_1 = VirtualPTDF(sys; dist_slack = slack_array) + ptdf_1[1, 1] + catch err + if err isa ErrorException + test_val1 = true + else + error( + "Error was not thrown for PTDF with distributed slack bus and more than one reference bus.", + ) + end + end + + # incorrect dist_slack array length + sys5 = PSB.build_system(PSB.PSITestSystems, "c_sys5") + buscount = length(PNM.get_buses(sys5)) + 1 + dist_slack = 1 / buscount * ones(buscount) + slack_array = dist_slack / sum(dist_slack) + test_val2 = false + try + ptdf_2 = PTDF(sys5; dist_slack = slack_array) + catch err + if err isa ErrorException + test_val2 = true + else + error( + "Error was not thrown for PTDF with distributed slack array of incorrect length.", + ) + end + end + + @test test_val1 + @test test_val2 +end + @testset "Test Virtual PTDF auxiliary functions" begin sys = PSB.build_system(PSB.PSITestSystems, "c_sys5") + # test empty cache vptdf = VirtualPTDF(sys) @test isempty(vptdf) == true + # test full cache + vptdf[2, 1] + @test isempty(vptdf) == false + # check if error is correctly thrown test_value = false try From 20f658fc154fe734ed23e583522929536e2f83ff Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Tue, 22 Aug 2023 18:52:28 -0600 Subject: [PATCH 44/47] formatter --- src/virtual_ptdf_calculations.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/virtual_ptdf_calculations.jl b/src/virtual_ptdf_calculations.jl index 80019c0f..fc19b2a3 100644 --- a/src/virtual_ptdf_calculations.jl +++ b/src/virtual_ptdf_calculations.jl @@ -156,7 +156,7 @@ function Base.isempty(vptdf::VirtualPTDF) return true elseif name == :dist_slack && isempty(getfield(vptdf, name)) @info "Field dist_slack has default value: " * - string(getfield(vptdf, name)) * "." + string(getfield(vptdf, name)) * "." elseif !(name in [:K, :dist_slack]) && isempty(getfield(vptdf, name)) @info "Field " * string(name) * " not defined." return true From bceed7c2cfc70e1b31717875c77868ebaaa5ac16 Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Wed, 23 Aug 2023 09:58:23 -0600 Subject: [PATCH 45/47] changed test to increase coverage --- src/virtual_lodf_calculations.jl | 6 ++---- src/virtual_ptdf_calculations.jl | 5 +---- test/test_virtual_ptdf.jl | 3 ++- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/virtual_lodf_calculations.jl b/src/virtual_lodf_calculations.jl index e8f07420..bacd0467 100644 --- a/src/virtual_lodf_calculations.jl +++ b/src/virtual_lodf_calculations.jl @@ -165,10 +165,8 @@ Checks if the any of the fields of VirtualLODF is empty. """ function Base.isempty(vlodf::VirtualLODF) for name in fieldnames(typeof(vlodf)) - if name == :K && (isempty(vlodf.K.L) || isempty(vlodf.K.U)) - @info "Either L o U factorization matrix is empty." - return true - elseif name != :K && isempty(getfield(vlodf, name)) + # note: impossible to define empty KLU field + if name != :K && isempty(getfield(vlodf, name)) @info "Field " * string(name) * " is empty." return true end diff --git a/src/virtual_ptdf_calculations.jl b/src/virtual_ptdf_calculations.jl index fc19b2a3..66504bf5 100644 --- a/src/virtual_ptdf_calculations.jl +++ b/src/virtual_ptdf_calculations.jl @@ -151,10 +151,7 @@ Checks if the any of the fields of VirtualPTDF is empty. """ function Base.isempty(vptdf::VirtualPTDF) for name in fieldnames(typeof(vptdf)) - if name == :K && (isempty(vptdf.K.L) || isempty(vptdf.K.U)) - @info "Either L o U factorization matrix is empty." - return true - elseif name == :dist_slack && isempty(getfield(vptdf, name)) + if name == :dist_slack && isempty(getfield(vptdf, name)) @info "Field dist_slack has default value: " * string(getfield(vptdf, name)) * "." elseif !(name in [:K, :dist_slack]) && isempty(getfield(vptdf, name)) diff --git a/test/test_virtual_ptdf.jl b/test/test_virtual_ptdf.jl index 1e076cbd..eb764f47 100644 --- a/test/test_virtual_ptdf.jl +++ b/test/test_virtual_ptdf.jl @@ -122,7 +122,8 @@ end slack_array = dist_slack / sum(dist_slack) test_val2 = false try - ptdf_2 = PTDF(sys5; dist_slack = slack_array) + ptdf_2 = VirtualPTDF(sys5; dist_slack = slack_array) + ptdf_2[1, 1] catch err if err isa ErrorException test_val2 = true From 911ce3f7f3fb88089589ee29e385087b2138df76 Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Wed, 23 Aug 2023 10:42:29 -0600 Subject: [PATCH 46/47] fixed problem with isempty (being called during variable definition) --- src/PowerNetworkMatrix.jl | 6 +++--- src/virtual_lodf_calculations.jl | 2 +- src/virtual_ptdf_calculations.jl | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/PowerNetworkMatrix.jl b/src/PowerNetworkMatrix.jl index c2733e0b..9ba24307 100644 --- a/src/PowerNetworkMatrix.jl +++ b/src/PowerNetworkMatrix.jl @@ -224,11 +224,11 @@ function Base.summary(io::IO, A::PowerNetworkMatrix) show(IOContext(io, :limit => true), ax) println(io) end - print(io, "And data, a ", size(A)) + print(io, "And data with size ", size(A)) return end -_summary(io::IO, A::PowerNetworkMatrix) = println(io, "PowerNetworkMatrix") +_summary(io::IO, ::T) where T <: PowerNetworkMatrix = println(io, "$T") function Base.summary( io::IOContext{Base.GenericIOBuffer{Array{UInt8, 1}}}, @@ -303,7 +303,7 @@ function Base.show_nd( end end -function Base.show(io::IO, array::PowerNetworkMatrix) +function Base.show(io::IO, ::MIME{Symbol("text/plain")}, array::PowerNetworkMatrix) summary(io, array) isempty(array) && return println(io, ":") diff --git a/src/virtual_lodf_calculations.jl b/src/virtual_lodf_calculations.jl index bacd0467..2e29a67c 100644 --- a/src/virtual_lodf_calculations.jl +++ b/src/virtual_lodf_calculations.jl @@ -167,7 +167,7 @@ function Base.isempty(vlodf::VirtualLODF) for name in fieldnames(typeof(vlodf)) # note: impossible to define empty KLU field if name != :K && isempty(getfield(vlodf, name)) - @info "Field " * string(name) * " is empty." + @debug "Field " * string(name) * " is empty." return true end end diff --git a/src/virtual_ptdf_calculations.jl b/src/virtual_ptdf_calculations.jl index 66504bf5..d5172cbb 100644 --- a/src/virtual_ptdf_calculations.jl +++ b/src/virtual_ptdf_calculations.jl @@ -152,10 +152,10 @@ Checks if the any of the fields of VirtualPTDF is empty. function Base.isempty(vptdf::VirtualPTDF) for name in fieldnames(typeof(vptdf)) if name == :dist_slack && isempty(getfield(vptdf, name)) - @info "Field dist_slack has default value: " * + @debug "Field dist_slack has default value: " * string(getfield(vptdf, name)) * "." elseif !(name in [:K, :dist_slack]) && isempty(getfield(vptdf, name)) - @info "Field " * string(name) * " not defined." + @debug "Field " * string(name) * " not defined." return true end end From 0ac376a6df6a0772997004742286b3e7cc4a1347 Mon Sep 17 00:00:00 2001 From: alefcastelli Date: Wed, 23 Aug 2023 10:51:30 -0600 Subject: [PATCH 47/47] formatter --- src/PowerNetworkMatrix.jl | 2 +- src/virtual_ptdf_calculations.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PowerNetworkMatrix.jl b/src/PowerNetworkMatrix.jl index 9ba24307..c3e9b7d0 100644 --- a/src/PowerNetworkMatrix.jl +++ b/src/PowerNetworkMatrix.jl @@ -228,7 +228,7 @@ function Base.summary(io::IO, A::PowerNetworkMatrix) return end -_summary(io::IO, ::T) where T <: PowerNetworkMatrix = println(io, "$T") +_summary(io::IO, ::T) where {T <: PowerNetworkMatrix} = println(io, "$T") function Base.summary( io::IOContext{Base.GenericIOBuffer{Array{UInt8, 1}}}, diff --git a/src/virtual_ptdf_calculations.jl b/src/virtual_ptdf_calculations.jl index d5172cbb..2ac60559 100644 --- a/src/virtual_ptdf_calculations.jl +++ b/src/virtual_ptdf_calculations.jl @@ -153,7 +153,7 @@ function Base.isempty(vptdf::VirtualPTDF) for name in fieldnames(typeof(vptdf)) if name == :dist_slack && isempty(getfield(vptdf, name)) @debug "Field dist_slack has default value: " * - string(getfield(vptdf, name)) * "." + string(getfield(vptdf, name)) * "." elseif !(name in [:K, :dist_slack]) && isempty(getfield(vptdf, name)) @debug "Field " * string(name) * " not defined." return true