MFEM v4.9.0
Finite element discretization library
Loading...
Searching...
No Matches
bramble_pasciak.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
2// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3// LICENSE and NOTICE for details. LLNL-CODE-806117.
4//
5// This file is part of the MFEM library. For more information and source code
6// availability visit https://mfem.org.
7//
8// MFEM is free software; you can redistribute it and/or modify it under the
9// terms of the BSD-3 license. We welcome feedback and contributions, see file
10// CONTRIBUTING.md for details.
11
12#include "bramble_pasciak.hpp"
13
14using namespace std;
15
17{
18
19/// Bramble-Pasciak Solver
22 const BPSParameters &param)
23 : DarcySolver(mVarf.ParFESpace()->GetTrueVSize(),
24 bVarf.TestFESpace()->GetTrueVSize())
25{
26 M_.reset(mVarf.ParallelAssemble());
27 B_.reset(bVarf.ParallelAssemble());
28 Q_.reset(ConstructMassPreconditioner(mVarf, param.q_scaling));
29
30 Vector diagM;
31 M_->GetDiag(diagM);
32 std::unique_ptr<HypreParMatrix> invDBt(B_->Transpose());
33 invDBt->InvScaleRows(diagM);
34 S_.reset(ParMult(B_.get(), invDBt.get(), true));
35 M0_.Reset(new HypreDiagScale(*M_));
36 M1_.Reset(new HypreBoomerAMG(*S_));
37 M1_.As<HypreBoomerAMG>()->SetPrintLevel(0);
38
39 Init(*M_, *B_, *Q_, *M0_.As<Solver>(), *M1_.As<Solver>(), param);
40}
41
45 Solver &M0, Solver &M1,
46 const BPSParameters &param)
47 : DarcySolver(M.NumRows(), B.NumRows())
48{
49 Init(M, B, Q, M0, M1, param);
50}
51
52void BramblePasciakSolver::Init(HypreParMatrix &M,
55 Solver &M0, Solver &M1,
56 const BPSParameters &param)
57{
58 Bt_ = std::make_unique<TransposeOperator>(&B);
59 auto invQ = new HypreDiagScale(Q);
60
61 use_bpcg = param.use_bpcg;
62
63 if (use_bpcg)
64 {
65 oop_ = std::make_unique<BlockOperator>(offsets_);
66 oop_->SetBlock(0, 0, &M);
67 oop_->SetBlock(0, 1, Bt_.get());
68 oop_->SetBlock(1, 0, &B);
69 // cpc_ unused in bpcg
70 auto temp_cpc = new BlockDiagonalPreconditioner(offsets_);
71 temp_cpc->SetDiagonalBlock(0, invQ);
72 temp_cpc->SetDiagonalBlock(1, &M1);
73 // tri(1,0) = B M0 = B invQ
74 auto id_m = new IdentityOperator(M.NumRows());
75 auto id_b = new IdentityOperator(B.NumRows());
76 auto BinvQ = new ProductOperator(&B, invQ, false, false);
77 // tri
78 auto temp_tri = new BlockOperator(offsets_);
79 temp_tri->SetBlock(0, 0, id_m);
80 temp_tri->SetBlock(1, 1, id_b, -1.0);
81 temp_tri->SetBlock(1, 0, BinvQ);
82 temp_tri->owns_blocks = 1;
83
84 ppc_ = std::make_unique<ProductOperator>(temp_cpc, temp_tri, true, true);
85
86 ipc_ = std::make_unique<BlockOperator>(offsets_);
87 ipc_->SetDiagonalBlock(0, invQ);
88 ipc_->owns_blocks = 1;
89
90 // bpcg
91 solver_ = std::make_unique<BPCGSolver>(M.GetComm(), ipc_.get(), ppc_.get());
92 solver_->SetOperator(*oop_);
93 }
94 else
95 {
96 // oop_ unused in cg
97 auto temp_oop = new BlockOperator(offsets_);
98 temp_oop->SetBlock(0, 0, &M);
99 temp_oop->SetBlock(0, 1, Bt_.get());
100 temp_oop->SetBlock(1, 0, &B);
101
102 // ipc_ unused in cg
103 auto temp_ipc = new BlockOperator(offsets_);
104 temp_ipc->SetDiagonalBlock(0, invQ);
105 temp_ipc->owns_blocks = 1;
106
107 // temp_AN = temp_oop * temp_ipc
108 auto temp_AN = new ProductOperator(temp_oop, temp_ipc, true, true);
109
110 // Required for updating the RHS
111 auto id = new IdentityOperator(M.NumRows()+B.NumRows());
112 map_ = std::make_unique<SumOperator>(temp_AN, 1.0, id, -1.0, true, true);
113 mop_ = std::make_unique<ProductOperator>(map_.get(), temp_oop, false, false);
114
115 cpc_ = std::make_unique<BlockDiagonalPreconditioner>(offsets_);
116 cpc_->SetDiagonalBlock(0, &M0);
117 cpc_->SetDiagonalBlock(1, &M1);
118
119 // (P)CG
120 solver_ = std::make_unique<CGSolver>(M.GetComm());
121 solver_->SetOperator(*mop_);
122 solver_->SetPreconditioner(*cpc_);
123 }
124 SetOptions(*solver_, param);
125}
126
128 const ParBilinearForm &mVarf, real_t q_scaling)
129{
130 MFEM_ASSERT((q_scaling > 0.0) && (q_scaling < 1.0),
131 "Invalid Q-scaling factor: q_scaling = " << q_scaling );
132 ParBilinearForm qVarf(mVarf.ParFESpace());
133 qVarf.AllocateMatrix();
134#ifndef MFEM_USE_LAPACK
135 if (Mpi::Root())
136 {
137 mfem::out << "Warning: Using inverse power method to compute the minimum "
138 << "eigenvalue of the small eigenvalue problem.\n";
139 mfem::out << " Consider compiling MFEM with LAPACK support.\n";
140 }
141#endif
142 for (int i = 0; i < mVarf.ParFESpace()->GetNE(); ++i)
143 {
144 DenseMatrix M_i, Q_i;
145 Vector diag_i;
146 real_t scaling = 0.0, eval_i = 0.0;
147 mVarf.ComputeElementMatrix(i, M_i);
148 M_i.GetDiag(diag_i);
149 // M_i <- D^{-1/2} M_i D^{-1/2}, where D = diag(M_i)
150 M_i.InvSymmetricScaling(diag_i);
151 // M_i x = ev diag(M_i) x
152#ifdef MFEM_USE_LAPACK
153 DenseMatrix evec;
154 Vector eval;
155 M_i.Eigenvalues(eval, evec);
156 eval_i = eval.Min();
157#else
158 // Inverse power method
159 Vector x(M_i.Height()), Mx(M_i.Height()), diff(M_i.Height());
160 real_t eval_prev = 0.0;
161 int iter = 0;
162 x.Randomize(static_cast<int>(696383552LL+779345LL*i));
163#if defined(MFEM_USE_DOUBLE)
164 const real_t rel_tol = 1e-12;
165#elif defined(MFEM_USE_SINGLE)
166 const real_t rel_tol = 1e-6;
167#else
168#error "Only single and double precision are supported!"
169 const real_t rel_tol = 1e-12;
170#endif
171 DenseMatrixInverse M_i_inv(M_i);
172 do
173 {
174 eval_prev = eval_i;
175 M_i_inv.Mult(x, Mx);
176 eval_i = Mx.Norml2();
177 x.Set(1.0/eval_i, Mx);
178 ++iter;
179 }
180 while ((iter < 1000) && (fabs(eval_i - eval_prev)/fabs(eval_i) > rel_tol));
181 MFEM_VERIFY(fabs(eval_i - eval_prev)/fabs(eval_i) <= rel_tol,
182 "Inverse power method did not converge."
183 << "\n\t iter = " << iter
184 << "\n\t eval_i = " << eval_i
185 << "\n\t eval_prev = " << eval_prev
186 << "\n\t fabs(eval_i - eval_prev)/fabs(eval_i) = "
187 << fabs(eval_i - eval_prev)/fabs(eval_i));
188 eval_i = 1.0/eval_i;
189#endif
190 scaling = q_scaling*eval_i;
191 diag_i.Set(scaling, diag_i);
192 Q_i.Diag(diag_i.GetData(), diag_i.Size());
193 qVarf.AssembleElementMatrix(i, Q_i, 1);
194 }
195 qVarf.Finalize();
196 return qVarf.ParallelAssemble();
197}
198
199void BramblePasciakSolver::Mult(const Vector & x, Vector & y) const
200{
201 if (!use_bpcg)
202 {
203 Vector transformed_rhs(x.Size());
204 map_->Mult(x, transformed_rhs);
205 solver_->Mult(transformed_rhs, y);
206 }
207 else
208 {
209 solver_->Mult(x, y);
210 }
211 for (int dof : ess_zero_dofs_) { y[dof] = 0.0; }
212}
213
214/// Bramble-Pasciak CG
216{
218
219 r.SetSize(width, mt); r.UseDevice(true);
220 p.SetSize(width, mt); p.UseDevice(true);
221 g.SetSize(width, mt); g.UseDevice(true);
222 t.SetSize(width, mt); t.UseDevice(true);
223 r_bar.SetSize(width, mt); r_bar.UseDevice(true);
224 r_red.SetSize(width, mt); r_red.UseDevice(true);
225 g_red.SetSize(width, mt); g_red.UseDevice(true);
226}
227
228void BPCGSolver::Mult(const Vector &b, Vector &x) const
229{
230 int i;
231 real_t delta, delta0, del0;
232 real_t alpha, beta, gamma;
233
234 // Initialization
235 x.UseDevice(true);
236 if (iterative_mode)
237 {
238 oper->Mult(x, r);
239 subtract(b, r, r); // r = b - A x
240 }
241 else
242 {
243 r = b;
244 x = 0.0;
245 }
246
247 pprec->Mult(r,r_bar); // r_bar = P r
248 p = r_bar;
249 oper->Mult(p, g); // g = A p
250 oper->Mult(r_bar, t); // t = A r_bar
251 iprec->Mult(r, r_red); // r_red = N r
252
253 delta = delta0 = Dot(t, r_red) - Dot(r_bar, r); // Dot(Pr, r)
254 if (delta0 >= 0.0) { initial_norm = sqrt(delta0); }
255 MFEM_ASSERT(IsFinite(delta), "norm = " << delta);
257 {
258 mfem::out << " Iteration : " << setw(3) << 0 << " (P r, r) = "
259 << delta << (print_options.first_and_last ? " ...\n" : "\n");
260 }
261 Monitor(0, delta, r, x);
262
263 if (delta < 0.0)
264 {
266 {
267 mfem::out << "BPCG: The preconditioner is not positive definite. (Pr, r) = "
268 << delta << '\n';
269 }
270 converged = false;
271 final_iter = 0;
274 return;
275 }
276 del0 = std::max(delta*rel_tol*rel_tol, abs_tol*abs_tol);
277 if (delta <= del0)
278 {
279 converged = true;
280 final_iter = 0;
281 final_norm = sqrt(delta);
282 return;
283 }
284
285 iprec->Mult(g, g_red);
286 gamma = Dot(g, g_red) - Dot(g,p); // Dot(Ap, p)
287 MFEM_ASSERT(IsFinite(gamma), "den (gamma) = " << gamma);
288 if (gamma <= 0.0)
289 {
290 if (Dot(r_bar, r_bar) > 0.0 && print_options.warnings)
291 {
292 mfem::out << "BPCG: The operator is not positive definite. (Ar, r) = "
293 << gamma << '\n';
294 }
295 if (gamma == 0.0)
296 {
297 converged = false;
298 final_iter = 0;
299 final_norm = sqrt(delta);
300 return;
301 }
302 }
303
304 // Start iteration
305 converged = false;
307 for (i = 1; true; )
308 {
309 alpha = delta0/gamma;
310 add(x, alpha, p, x); // x = x + alpha p
311 add(r, -alpha, g, r); // r = r - alpha g
312
313 pprec->Mult(r, r_bar); // r_bar = P r
314 iprec->Mult(r, r_red); // r_red = N r
315 oper->Mult(r_bar, t); // t = A r_bar
316 delta = Dot(t, r_red) - Dot(r_bar,r);
317
318 // Check
319 MFEM_ASSERT(IsFinite(delta), "norm = " << delta);
320 if (delta < 0.0)
321 {
323 {
324 mfem::out << "BPCG: The preconditioner is not positive definite. (Pr, r) = "
325 << delta << '\n';
326 }
327 converged = false;
328 final_iter = i;
329 break;
330 }
332 {
333 mfem::out << " Iteration : " << setw(3) << i << " (Pr, r) = "
334 << delta << std::endl;
335 }
336 Monitor(i, delta, r, x);
337 if (delta <= del0)
338 {
339 converged = true;
340 final_iter = i;
341 break;
342 }
343 if (++i > max_iter)
344 {
345 break;
346 }
347 // End check
348
349 beta = delta/delta0;
350 add(r_bar, beta, p, p); // p = r_bar + beta p
351 add(t, beta, g, g); // g = t + beta g
352
353 delta0 = delta;
354 iprec->Mult(g, g_red);
355 gamma = Dot(g, g_red) - Dot(g,p); // Dot(Ap, p)
356 MFEM_ASSERT(IsFinite(gamma), "den (gamma) = " << gamma);
357 if (gamma <= 0.0)
358 {
359 if (Dot(r_bar, r_bar) > 0.0 && print_options.warnings)
360 {
361 mfem::out << "BPCG: The operator is not positive definite. (Ar, r) = "
362 << gamma << '\n';
363 }
364 if (gamma == 0.0)
365 {
366 final_iter = i;
367 break;
368 }
369 }
370 }
371
373 {
374 mfem::out << " Iteration : " << setw(3) << final_iter << " (Pr, r) = "
375 << delta << '\n';
376 }
378 {
379 mfem::out << "BPCG: Number of iterations: " << final_iter << '\n';
380 }
383 {
384 const auto arf = pow (gamma/delta0, 0.5/final_iter);
385 mfem::out << "Average reduction factor = " << arf << '\n';
386 }
388 {
389 mfem::out << "BPCG: No convergence!" << '\n';
390 }
391
392 final_norm = sqrt(delta);
393 Monitor(final_iter, final_norm, r, x, true);
394}
395
396} // namespace mfem::blocksolvers
void AllocateMatrix()
Pre-allocate the internal SparseMatrix before assembly. If the internal flag precompute_sparsity is s...
void Finalize(int skip_zeros=1) override
Finalizes the matrix initialization if the AssemblyLevel is AssemblyLevel::LEGACY....
void AssembleElementMatrix(int i, const DenseMatrix &elmat, int skip_zeros=1)
Assemble the given element matrix.
void ComputeElementMatrix(int i, DenseMatrix &elmat) const
Compute the element matrix of the given element.
A class to handle Block diagonal preconditioners in a matrix-free implementation.
A class to handle Block systems in a matrix-free implementation.
void Mult(const real_t *x, real_t *y) const
Matrix vector multiplication with the inverse of dense matrix.
Data type dense matrix using column-major storage.
Definition densemat.hpp:24
void GetDiag(Vector &d) const
Returns the diagonal of the matrix.
void Eigenvalues(Vector &ev)
Compute eigenvalues of A x = ev x where A = *this.
Definition densemat.hpp:291
void Diag(real_t c, int n)
Creates n x n diagonal matrix with diagonal elements c.
void InvSymmetricScaling(const Vector &s)
InvSymmetricScaling this = diag(sqrt(1./s)) * this * diag(sqrt(1./s))
Definition densemat.cpp:382
The BoomerAMG solver in hypre.
Definition hypre.hpp:1737
Jacobi preconditioner in hypre.
Definition hypre.hpp:1527
Wrapper for hypre's ParCSR matrix class.
Definition hypre.hpp:419
MPI_Comm GetComm() const
MPI communicator.
Definition hypre.hpp:609
Identity Operator I: x -> x.
Definition operator.hpp:815
A class to initialize the size of a Tensor.
Definition dtensor.hpp:57
real_t abs_tol
Absolute tolerance.
Definition solvers.hpp:183
real_t rel_tol
Relative tolerance.
Definition solvers.hpp:180
PrintLevel print_options
Output behavior for the iterative solver.
Definition solvers.hpp:163
virtual real_t Dot(const Vector &x, const Vector &y) const
Return the standard (l2, i.e., Euclidean) inner product of x and y.
Definition solvers.cpp:58
const Operator * oper
Definition solvers.hpp:145
int max_iter
Limit for the number of iterations the solver is allowed to do.
Definition solvers.hpp:177
bool Monitor(int it, real_t norm, const Vector &r, const Vector &x, bool final=false) const
Monitor both the residual r and the solution x.
Definition solvers.cpp:195
static bool Root()
Return true if the rank in MPI_COMM_WORLD is zero.
OpType * As() const
Return the Operator pointer statically cast to a specified OpType. Similar to the method Get().
Definition handle.hpp:104
void Reset(OpType *A, bool own_A=true)
Reset the OperatorHandle to the given OpType pointer, A.
Definition handle.hpp:145
virtual MemoryClass GetMemoryClass() const
Return the MemoryClass preferred by the Operator.
Definition operator.hpp:86
int width
Dimension of the input / number of columns in the matrix.
Definition operator.hpp:28
int Height() const
Get the height (size of output) of the Operator. Synonym with NumRows().
Definition operator.hpp:66
virtual void Mult(const Vector &x, Vector &y) const =0
Operator application: y=A(x).
int NumRows() const
Get the number of rows (size of output) of the Operator. Synonym with Height().
Definition operator.hpp:69
Class for parallel bilinear form.
HypreParMatrix * ParallelAssemble()
Returns the matrix assembled on the true dofs, i.e. P^t A P.
ParFiniteElementSpace * ParFESpace() const
Return the parallel FE space associated with the ParBilinearForm.
Class for parallel bilinear form using different test and trial FE spaces.
HypreParMatrix * ParallelAssemble()
Returns the matrix assembled on the true dofs, i.e. P_test^t A P_trial.
General product operator: x -> (A*B)(x) = A(B(x)).
Definition operator.hpp:906
Base class for solvers.
Definition operator.hpp:792
bool iterative_mode
If true, use the second argument of Mult() as an initial guess.
Definition operator.hpp:795
Vector data type.
Definition vector.hpp:82
Vector & Set(const real_t a, const Vector &x)
(*this) = a * x
Definition vector.cpp:341
int Size() const
Returns the size of the vector.
Definition vector.hpp:234
virtual void UseDevice(bool use_dev) const
Enable execution of Vector operations using the mfem::Device.
Definition vector.hpp:145
void SetSize(int s)
Resize the vector to size s.
Definition vector.hpp:584
real_t * GetData() const
Return a pointer to the beginning of the Vector data.
Definition vector.hpp:243
real_t Min() const
Returns the minimal element of the vector.
Definition vector.cpp:1154
void Mult(const Vector &b, Vector &x) const override
Operator application: y=A(x).
void UpdateVectors()
Bramble-Pasciak CG.
BramblePasciakSolver(ParBilinearForm &mVarf, ParMixedBilinearForm &bVarf, const BPSParameters &param)
System and mass preconditioner are constructed from bilinear forms.
void Mult(const Vector &x, Vector &y) const override
Operator application: y=A(x).
static HypreParMatrix * ConstructMassPreconditioner(const ParBilinearForm &mVarf, const real_t alpha=0.5)
Assemble a preconditioner for the mass matrix.
Abstract solver class for Darcy's flow.
const real_t alpha
Definition ex15.cpp:369
real_t b
Definition lissajous.cpp:42
real_t delta
Definition lissajous.cpp:43
void SetOptions(IterativeSolver &solver, const IterSolveParameters &param)
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
Definition globals.hpp:66
void add(const Vector &v1, const Vector &v2, Vector &v)
Definition vector.cpp:414
bool IsFinite(const real_t &val)
Definition vector.hpp:553
MemoryType GetMemoryType(MemoryClass mc)
Return a suitable MemoryType for a given MemoryClass.
HypreParMatrix * ParMult(const HypreParMatrix *A, const HypreParMatrix *B, bool own_matrix)
Definition hypre.cpp:3021
void subtract(const Vector &x, const Vector &y, Vector &z)
Definition vector.cpp:570
float real_t
Definition config.hpp:46
MemoryType
Memory types supported by MFEM.
bool iterations
Detailed information about each iteration will be reported to mfem::out.
Definition solvers.hpp:112
bool warnings
If a non-fatal problem has been detected some context-specific information will be reported to mfem::...
Definition solvers.hpp:109
bool first_and_last
Information about the first and last iteration will be printed to mfem::out.
Definition solvers.hpp:118
bool summary
A summary of the solver process will be reported after the last iteration to mfem::out.
Definition solvers.hpp:115
Parameters for the BramblePasciakSolver method.