How to Build Multiple Matrices Without Repeating Work
To follow along, you can download this tutorial as a Julia script (.jl) or Jupyter notebook (.ipynb).
Every matrix constructor that takes a System rebuilds the same intermediates from scratch — the Ybus, the incidence matrix A (IncidenceMatrix), and the susceptance-weighted BA (BA_Matrix). This guide shows how to compute the shared pieces once and feed them to the constructors that accept pre-built matrices.
using PowerNetworkMatrices
import PowerSystemCaseBuilderLoad an example test system with PowerSystemCaseBuilder.build_system:
sys = PowerSystemCaseBuilder.build_system(
PowerSystemCaseBuilder.PSITestSystems,
"c_sys5",
);[ Info: Loaded time series from storage file existing=/home/runner/.julia/packages/PowerSystemCaseBuilder/zuCHR/data/serialized_system/8584b9e729c8aa68ee5405660c6258cde1f67ed3b68f114823707912e9a0d16c/c_sys5_time_series_storage.h5 new=/tmp/jl_32fi7I compression=InfrastructureSystems.CompressionSettings(false, InfrastructureSystems.CompressionTypesModule.CompressionTypes.DEFLATE = 1, 3, true)Build the shared intermediates once
The construction dependency chain is
Ybus→IncidenceMatrix,BA_Matrix→ABA_Matrix/PTDF, andPTDF→LODF.
The Ybus is the expensive shared root. Build it — and the IncidenceMatrix and BA_Matrix derived from it — exactly once:
ybus = Ybus(sys)
A = IncidenceMatrix(ybus)
BA = BA_Matrix(ybus)DC_BA_Matrix
Dimension 1, [(1, 2), (1, 5), (3, 4), (2, 3), (4, 5), (1, 4)]
Dimension 2, [1, 2, 3, 4, 5]
Note!! The data shown below corresponds to the transposed matrix.
And data with size (5, 6):
35.5872 156.25 ⋅ ⋅ ⋅ 32.8947
-35.5872 ⋅ ⋅ 92.5926 ⋅ ⋅
⋅ ⋅ 33.67 -92.5926 ⋅ ⋅
⋅ ⋅ -33.67 ⋅ 33.67 -32.8947
⋅ -156.25 ⋅ ⋅ -33.67 ⋅ Reuse them across constructors
PTDF accepts the incidence and BA matrices directly, skipping its own Ybus build:
ptdf = PTDF(A, BA)DC_PTDF_Matrix
Dimension 1, [(1, 2), (1, 5), (3, 4), (2, 3), (4, 5), (1, 4)]
Dimension 2, [1, 2, 3, 4, 5]
Note!! The data shown below corresponds to the transposed matrix.
And data with size (5, 6):
0.193917 0.368495 0.193917 0.193917 -0.368495 0.437588
-0.475895 0.217552 0.524105 0.524105 -0.217552 0.258343
-0.348989 0.159538 0.651011 -0.348989 -0.159538 0.189451
0.0 0.0 0.0 0.0 0.0 0.0
0.159538 -0.519548 0.159538 0.159538 -0.480452 0.36001LODF can be built straight from a PTDF you already have, reusing that work too — no second factorization of the network:
lodf = LODF(A, ptdf)LODF{Tuple{Vector{Tuple{Int64, Int64}}, Vector{Tuple{Int64, Int64}}}, Tuple{Dict{Tuple{Int64, Int64}, Int64}, Dict{Tuple{Int64, Int64}, Int64}}, Matrix{Float64}}
Dimension 1, [(1, 2), (1, 5), (3, 4), (2, 3), (4, 5), (1, 4)]
Dimension 2, [(1, 2), (1, 5), (3, 4), (2, 3), (4, 5), (1, 4)]
Note!! The data shown below corresponds to the transposed matrix.
And data with size (6, 6):
-1.0 0.457143 -1.0 -1.0 -0.457143 0.542857
0.307071 -1.0 0.307071 0.307071 1.0 0.692929
-1.0 0.457143 -1.0 -1.0 -0.457143 0.542857
-1.0 0.457143 -1.0 -1.0 -0.457143 0.542857
-0.307071 1.0 -0.307071 -0.307071 -1.0 -0.692929
0.344795 0.655205 0.344795 0.344795 -0.655205 -1.0Alternatively, the factorized ABA_Matrix route builds LODF from the same A and BA. The factorize keyword is what pre-factorizes it; is_factorized reports the result. All three inputs must share the same network reduction — which they do here, because they all descend from one ybus:
aba = ABA_Matrix(ybus; factorize = true)
is_factorized(aba)trueThat factorized ABA_Matrix, the incidence matrix, and the BA matrix are all LODF needs:
lodf_via_aba = LODF(A, aba, BA)LODF{Tuple{Vector{Tuple{Int64, Int64}}, Vector{Tuple{Int64, Int64}}}, Tuple{Dict{Tuple{Int64, Int64}, Int64}, Dict{Tuple{Int64, Int64}, Int64}}, Matrix{Float64}}
Dimension 1, [(1, 2), (1, 5), (3, 4), (2, 3), (4, 5), (1, 4)]
Dimension 2, [(1, 2), (1, 5), (3, 4), (2, 3), (4, 5), (1, 4)]
Note!! The data shown below corresponds to the transposed matrix.
And data with size (6, 6):
-1.0 0.457143 -1.0 -1.0 -0.457143 0.542857
0.307071 -1.0 0.307071 0.307071 1.0 0.692929
-1.0 0.457143 -1.0 -1.0 -0.457143 0.542857
-1.0 0.457143 -1.0 -1.0 -0.457143 0.542857
-0.307071 1.0 -0.307071 -0.307071 -1.0 -0.692929
0.344795 0.655205 0.344795 0.344795 -0.655205 -1.0Virtual matrices likewise accept a pre-built Ybus, so VirtualPTDF and its siblings reuse the same root:
vptdf = VirtualPTDF(ybus)DC_vPTDF_Matrix{PowerNetworkMatrices.KLUWrapper.KLULinSolveCache{Float64, Int64}}
Dimension 1, [(1, 2), (1, 5), (3, 4), (2, 3), (4, 5), (1, 4)]
Dimension 2, [1, 2, 3, 4, 5]
And data with size (5, 6)Constructors that combine pre-built matrices (e.g. LODF(A, ABA, BA)) require every input to have been built with the same network_reductions. Because they all derive from a single Ybus here, they are automatically consistent. Pass network_reductions once, to the Ybus call, and everything downstream inherits it. See the NetworkReduction docstring for the keyword and its rules.
See also
- Matrix overview & indexing — every matrix type, its axes, and how the shared intermediates fit together.
- How to Choose a Linear Solver — the factorization cost that reuse avoids repeating.
NetworkReduction— supplying reductions vianetwork_reductionsto the sharedYbus.