Created
February 20, 2025 16:38
-
-
Save jwscook/2b38863004f277a15e1a4703bcd54e67 to your computer and use it in GitHub Desktop.
Static Condensation of structured array
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using LinearAlgebra, SparseArrays | |
struct StaticCondensationMatrix{T, M<:AbstractMatrix{T}} <: AbstractMatrix{T} | |
A::M | |
indices::Vector{UnitRange{Int}} | |
nlocalblocks::Int | |
ncouplingblocks::Int | |
reducedlocalindices::Vector{UnitRange{Int}} | |
reducedcoupledindices::Vector{UnitRange{Int}} | |
reducedlhs::M | |
end | |
function StaticCondensationMatrix(A::AbstractMatrix{T}, blockindices) where T | |
n = size(A, 1) | |
@assert isodd(length(blockindices)) | |
ncouplingblocks = (length(blockindices) - 1) ÷ 2 | |
nlocalblocks = ncouplingblocks + 1 | |
reducedlocalindices = Vector{UnitRange{Int}}() | |
push!(reducedlocalindices, blockindices[1]) | |
for (c, i) in enumerate(3:2:length(blockindices)) | |
inds = (blockindices[i] .- blockindices[i][1] .+ 1) .+ reducedlocalindices[c][end] | |
push!(reducedlocalindices, inds) | |
end | |
reducedcoupledindices = Vector{UnitRange{Int}}() | |
push!(reducedcoupledindices, blockindices[2] .- blockindices[2][1] .+ 1) | |
for (c, i) in enumerate(4:2:length(blockindices)) | |
inds = (blockindices[i] .- blockindices[i][1] .+ 1) .+ reducedcoupledindices[c][end] | |
push!(reducedcoupledindices, inds) | |
end | |
totalcouplingblocksize = sum(length(i) for i in reducedcoupledindices) | |
# allocate reduced lhs | |
reducedlhs = similar(A, totalcouplingblocksize, totalcouplingblocksize) | |
fill!(reducedlhs, 0) | |
return StaticCondensationMatrix(A, blockindices, nlocalblocks, ncouplingblocks, reducedlocalindices, reducedcoupledindices, reducedlhs) | |
end | |
function StaticCondensationMatrix(A::AbstractMatrix{T}, localblocksize, couplingblocksize) where T | |
n = size(A, 1) | |
ncouplingblocks = (n - localblocksize) ÷ (localblocksize + couplingblocksize) | |
nlocalblocks = ncouplingblocks + 1 | |
indices = Vector{UnitRange{Int}}() | |
a = 1 | |
for i = 1:nlocalblocks-1 | |
inds = a:a + localblocksize - 1 | |
push!(indices, inds) | |
a = a + localblocksize | |
inds = a:a + couplingblocksize - 1 | |
push!(indices, inds) | |
a = a + couplingblocksize | |
end | |
push!(indices, a:a + localblocksize - 1) | |
@assert indices[end][end] == size(A, 1) == size(A, 2) | |
return StaticCondensationMatrix(A, indices) | |
end | |
Base.size(A::StaticCondensationMatrix) = (size(A.A, 1), size(A.A, 2)) | |
Base.size(A::StaticCondensationMatrix, i) = size(A.A, i) | |
islocalblock(i) = isodd(i) | |
iscouplingblock(i) = !islocalblock(i) | |
function enumeratelocalindices(A::StaticCondensationMatrix) | |
return zip(1:A.nlocalblocks, 1:2:length(A.indices), A.indices[1:2:end]) | |
end | |
function enumeratecouplingindices(A::StaticCondensationMatrix) | |
return zip(1:A.ncouplingblocks, 2:2:length(A.indices), A.indices[2:2:end-1]) | |
end | |
function factoriselocals(A::StaticCondensationMatrix{T}) where T | |
lis = A.indices | |
localfact = lu!(view(A.A, lis[1], lis[1])) # can't use a view | |
d = Dict{Int, typeof(localfact)}(1=>localfact) | |
for (i_1, li) in enumerate(lis[2:end]) # parallelisable | |
i = i_1 + 1 | |
iscouplingblock(i) && continue | |
d[i] = lu!(view(A.A, li, li)) # can't use a view | |
end | |
return d | |
end | |
function calculatecouplings(A::StaticCondensationMatrix{T,M}, localfactors) where {T,M} | |
d = Dict{Tuple{Int, Int}, M}() | |
for (c, i, li) in enumeratecouplingindices(A) # parallelisable | |
if i - 1 >= 1 | |
lim = A.indices[i-1] | |
d[(i-1, i)] = localfactors[i-1] \ A.A[lim, li] | |
end | |
if i + 1 <= length(A.indices) | |
lip = A.indices[i+1] | |
d[(i+1, i)] = localfactors[i+1] \ A.A[lip, li] | |
end | |
end | |
return d | |
end | |
function solvelocalparts(A::StaticCondensationMatrix{T,M}, b, localfactors) where {T,M} | |
d = Dict{Int, M}() | |
for (c, i, li) in enumeratelocalindices(A) # parallelisable | |
d[i] = localfactors[i] \ b[li, :] | |
end | |
return d | |
end | |
function couplingblockindices(A::StaticCondensationMatrix, i) | |
@assert i > 1 | |
return A.indices[i] .- A.indices[i-1][1] .+ 1 | |
end | |
function localblockindices(A::StaticCondensationMatrix, i) | |
@assert i > 1 | |
return A.indices[i] .- A.indices[i-1][1] .+ 1 | |
end | |
function assemblecoupledrhs(A::StaticCondensationMatrix, B, localsolutions, couplings) | |
b = similar(A.reducedlhs, size(A.reducedlhs, 1), size(B, 2)) | |
@views for (c, i, li) in enumeratecouplingindices(A) # parallelisable | |
rows = A.reducedcoupledindices[c] | |
b[rows, :] .= B[li, :] | |
b[rows, :] .-= A.A[li, A.indices[i-1]] * localsolutions[i-1] | |
b[rows, :] .-= A.A[li, A.indices[i+1]] * localsolutions[i+1] | |
end | |
return b | |
end | |
function assemblecoupledlhs!(A::StaticCondensationMatrix, couplings; assignblocks=false, applycouplings=false) | |
M = A.reducedlhs | |
@views for (c, i, li) in enumeratecouplingindices(A) # parallelisable | |
rows = A.reducedcoupledindices[c] | |
assignblocks && (M[rows, rows] .= A.A[li, li]) | |
aim = A.A[li, A.indices[i-1]] | |
aip = A.A[li, A.indices[i+1]] | |
applycouplings && (M[rows, rows] .-= aim * couplings[(i - 1, i)]) | |
applycouplings && (M[rows, rows] .-= aip * couplings[(i + 1, i)]) | |
if c + 1 <= A.ncouplingblocks | |
right = A.reducedcoupledindices[c + 1] | |
assignblocks && (M[rows, right] .= A.A[li, A.indices[i + 2]]) | |
applycouplings && (M[rows, right] .-= aip * couplings[(i + 1, i + 2)]) | |
end | |
if c - 1 >= 1 | |
left = A.reducedcoupledindices[c - 1] | |
assignblocks && (M[rows, left] .= A.A[li, A.indices[i - 2]]) | |
applycouplings && (M[rows, left] .-= aim * couplings[(i - 1, i - 2)]) | |
end | |
end | |
return M | |
end | |
function coupledx!(x, A::StaticCondensationMatrix{T}, b, localsolutions, couplings) where {T} | |
Ac = assemblecoupledlhs!(A, couplings; applycouplings=true) | |
bc = assemblecoupledrhs(A, b, localsolutions, couplings) | |
xc = Ac \ bc | |
for (c, i, ind) in enumeratecouplingindices(A) | |
x[ind, :] .= xc[A.reducedcoupledindices[c], :] | |
end | |
return x | |
end | |
function localx!(x, A::StaticCondensationMatrix{T}, b, localsolutions, couplings) where T | |
for (c, i, li) in enumeratelocalindices(A) # parallelisable | |
rows = A.reducedlocalindices[c] | |
x[li, :] .= localsolutions[i] | |
for j in (i + 1, i - 1) | |
0 < j <= length(A.indices) || continue | |
x[li, :] .-= couplings[(i, j)] * x[A.indices[j], :] | |
end | |
end | |
return x | |
end | |
function solve(A::StaticCondensationMatrix{T}, b) where T | |
assemblecoupledlhs!(A, nothing; assignblocks=true) | |
localfactors = factoriselocals(A) | |
localsolutions = solvelocalparts(A, b, localfactors) | |
couplings = calculatecouplings(A, localfactors) | |
x = zeros(T, size(b)) | |
x = coupledx!(x, A, b, localsolutions, couplings) | |
x = localx!(x, A, b, localsolutions, couplings) | |
return x | |
end | |
using Random, Test | |
Random.seed!(0) | |
@testset "StaticCondensationCondensation" begin | |
for (L, C) in ((2, 1), (3, 2), (4, 2), (16, 4), (5, 7), (128, 64), (256, 128), (1024, 512)) | |
A11, A33, A55, A77 = (rand(L, L) for _ in 1:4) | |
t12, t32, t34, t54, t56, t76 = (rand(L, C) for _ in 1:6) | |
s21, s23, s43, s45, s65, s67 = (rand(C, L) for _ in 1:6) | |
a22, a44, a66, a24, a42, a46, a64 = (rand(C, C) for _ in 1:7) | |
zLL = zeros(L, L) | |
zCL = zeros(C, L) | |
zLC = zeros(L, C) | |
zCC = zeros(C, C) | |
A = [A11 t12 zLL; | |
s21 a22 s23; | |
zLL t32 A33] | |
b = rand(size(A, 1)) | |
x = A \ b | |
SCM = StaticCondensationMatrix(A, L, C) | |
@test solve(SCM, b) ≈ x | |
A = [A11 t12 zLL zLC zLL; | |
s21 a22 s23 a24 zCL; | |
zLL t32 A33 t34 zLL; | |
zCL a42 s43 a44 s45; | |
zLL zLC zLL t54 A55] | |
b = rand(size(A, 1)) | |
x = A \ b | |
SCM = StaticCondensationMatrix(A, L, C) | |
@test solve(SCM, b) ≈ x | |
A = [A11 t12 zLL zLC zLL zLC zLL; | |
s21 a22 s23 a24 zCL zCC zCL; | |
zLL t32 A33 t34 zLL zLC zLL; | |
zCL a42 s43 a44 s45 a46 zCL; | |
zLL zLC zLL t54 A55 t56 zLL; | |
zCL zCC zCL a64 s65 a66 s67; | |
zLL zLC zLL zLC zLL t76 A77] | |
b = rand(size(A, 1)) | |
x = A \ b | |
SCM = StaticCondensationMatrix(A, L, C) | |
@test solve(SCM, b) ≈ x | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment