@@ -294,3 +294,97 @@ LinearAlgebra.ldiv!(fact::JacobiPreconditioner, v) = ldiv!(fact.factorization, v
294294
295295allow_views (:: JacobiPreconditioner ) = true
296296allow_views (:: Type{JacobiPreconditioner} ) = true
297+
298+
299+ """
300+ SchurComplementPreconditioner{AT, ST}
301+
302+ Preconditioner for saddle-point problems of the form:
303+ ```
304+ [ A B ]
305+ [ Bᵀ 0 ]
306+ ```
307+
308+ # Fields
309+ - `partitions`: Vector of two ranges defining matrix partitioning
310+ - `A_fac::AT`: Factorization of the A-block (top-left)
311+ - `S_fac::ST`: Factorization of the Schur complement (approximation of -BᵀA⁻¹B)
312+ """
313+ struct SchurComplementPreconditioner{AT, ST}
314+ partitions
315+ A_fac:: AT
316+ S_fac:: ST
317+ end
318+
319+
320+ """
321+ SchurComplementPreconBuilder(dofs_first_block, A_factorization, S_factorization = lu)
322+
323+ Factory function creating preconditioners for saddle-point problems.
324+
325+ # Arguments
326+ - `dofs_first_block`: Number of degrees of freedom in the first block (size of A matrix)
327+ - `A_factorization`: Factorization method for the A-block (e.g., `ilu0, Diagonal, lu`)
328+ - `S_factorization`: Factorization method for the Schur complement (defaults to `lu`)
329+
330+ Returns a function `prec(M, p)` that creates a `SchurComplementPreconditioner`.
331+ """
332+ function SchurComplementPreconBuilder (dofs_first_block, A_factorization, S_factorization = lu)
333+
334+ # this is the resulting preconditioner
335+ function prec (M, p)
336+
337+ # We have
338+ # M = [ A B ] → n1 dofs
339+ # [ Bᵀ 0 ] → n2 dofs
340+
341+ n1 = dofs_first_block
342+ n2 = size (M, 1 ) - n1
343+
344+ # I see no other way than creating this expensive copy
345+ A = M[1 : n1, 1 : n1]
346+ B = M[1 : n1, (n1 + 1 ): end ]
347+
348+ # first factorization
349+ A_fac = A_factorization (A)
350+
351+ # compute the (dense!) Schur Matrix
352+ # S ≈ - Bᵀ A⁻¹ B
353+ S = zeros (n2, n2)
354+
355+ @info " Computing the Schur complement..."
356+ ldiv_buffer = zeros (n1)
357+ for col in 1 : n2
358+ # TODO : the following is not thread parallel?
359+ @views ldiv! (ldiv_buffer, A_fac, B[:, col])
360+ @views mul! (S[:, col], B' , ldiv_buffer)
361+ end
362+ S .*= - 1.0
363+ @info " ...done"
364+
365+ S_fac = S_factorization (sparse (S))
366+
367+ return SchurComplementPreconditioner ([1 : n1, (n1 + 1 ): (n1 + n2)], A_fac, S_fac)
368+ end
369+
370+ return prec
371+ end
372+
373+
374+ function LinearAlgebra. ldiv! (u, p:: SchurComplementPreconditioner , v)
375+
376+ (part1, part2) = p. partitions
377+ @views ldiv! (u[part1], p. A_fac, v[part1])
378+ @views ldiv! (u[part2], p. S_fac, v[part2])
379+
380+ return u
381+ end
382+
383+ function LinearAlgebra. ldiv! (p:: SchurComplementPreconditioner , v)
384+
385+ (part1, part2) = p. partitions
386+ @views ldiv! (p. A_fac, v[part1])
387+ @views ldiv! (p. S_fac, v[part2])
388+
389+ return v
390+ end
0 commit comments