MFEM v4.8.0
Finite element discretization library
Loading...
Searching...
No Matches
hdiv_linear_solver.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
15
16namespace mfem
17{
18
19/// Replace x[i] with 1.0/x[i] for all i.
21{
22 const int n = x.Size();
23 real_t *d_x = x.ReadWrite();
24 MFEM_FORALL(i, n, d_x[i] = 1.0/d_x[i]; );
25}
26
27/// Return a new HypreParMatrix with given diagonal entries
29 const ParFiniteElementSpace &fes)
30{
31 const int n = diag.Size();
32
33 SparseMatrix diag_spmat;
34 diag_spmat.OverrideSize(n, n);
35 diag_spmat.GetMemoryI().New(n+1, Device::GetDeviceMemoryType());
38
39 {
40 int *I = diag_spmat.WriteI();
41 int *J = diag_spmat.WriteJ();
42 real_t *A = diag_spmat.WriteData();
43 const real_t *d_diag = diag.Read();
44 MFEM_FORALL(i, n+1, I[i] = i;);
45 MFEM_FORALL(i, n,
46 {
47 J[i] = i;
48 A[i] = d_diag[i];
49 });
50 }
51
52 HYPRE_BigInt global_size = fes.GlobalTrueVSize();
53 HYPRE_BigInt *row_starts = fes.GetTrueDofOffsets();
54 HypreParMatrix D(fes.GetComm(), global_size, row_starts, &diag_spmat);
55 return new HypreParMatrix(D); // make a deep copy
56}
57
59{
60 Mesh *mesh = fes_l2.GetMesh();
61 const FiniteElement *fe = fes_l2.GetTypicalFE();
63 *fe, *fe, *mesh->GetTypicalElementTransformation());
64}
65
67 ParMesh &mesh, ParFiniteElementSpace &fes_rt_, ParFiniteElementSpace &fes_l2_,
68 Coefficient &L_coeff_, Coefficient &R_coeff_, const Array<int> &ess_rt_dofs_,
69 Mode mode_)
70 : minres(mesh.GetComm()),
71 order(fes_rt_.GetMaxElementOrder()),
72 fec_l2(order - 1, mesh.Dimension(), b2, mt),
73 fes_l2(&mesh, &fec_l2),
74 fec_rt(order - 1, mesh.Dimension(), b1, b2),
75 fes_rt(&mesh, &fec_rt),
76 ess_rt_dofs(ess_rt_dofs_),
77 basis_l2(fes_l2_),
78 basis_rt(fes_rt_),
79 convert_map_type(fes_l2_.GetTypicalFE()->GetMapType() == FiniteElement::VALUE),
80 mass_l2(&fes_l2),
81 mass_rt(&fes_rt),
82 L_coeff(L_coeff_),
83 R_coeff(R_coeff_),
84 mode(mode_),
85 qs(mesh, GetMassIntRule(fes_l2)),
86 W_coeff_qf(qs),
87 W_mix_coeff_qf(qs),
88 W_coeff(W_coeff_qf),
89 W_mix_coeff(W_mix_coeff_qf)
90{
91 // If the user gives zero L coefficient, switch mode to DARCY_ZERO
92 auto *L_const_coeff = dynamic_cast<ConstantCoefficient*>(&L_coeff);
93 zero_l2_block = (L_const_coeff && L_const_coeff->constant == 0.0);
94
95 if (mode == Mode::GRAD_DIV)
96 {
97 MFEM_VERIFY(!zero_l2_block,
98 "Mode::GRAD_DIV incompatible with zero coefficient.");
99 }
100
101 mass_l2.AddDomainIntegrator(new MassIntegrator(W_coeff));
103
104 mass_rt.AddDomainIntegrator(new VectorFEMassIntegrator(&R_coeff));
106
107 D.reset(FormDiscreteDivergenceMatrix(fes_rt, fes_l2, ess_rt_dofs));
108 Dt.reset(D->Transpose());
109
110 // Versions without BCs needed for elimination
111 D_e.reset(FormDiscreteDivergenceMatrix(fes_rt, fes_l2, empty));
112 mass_rt.FormSystemMatrix(empty, R_e);
113
114 offsets.SetSize(3);
115 offsets[0] = 0;
116 offsets[1] = fes_l2.GetTrueVSize();
117 offsets[2] = offsets[1] + fes_rt.GetTrueVSize();
118
119 minres.SetAbsTol(0.0);
120 minres.SetRelTol(1e-12);
121 minres.SetMaxIter(500);
123 minres.iterative_mode = false;
124
125 R_diag.SetSize(fes_rt.GetTrueVSize());
126 L_diag.SetSize(fes_l2.GetTrueVSize());
127
128 S_inv.SetPrintLevel(0);
129
130 if (mode == Mode::DARCY && !zero_l2_block)
131 {
132 ParBilinearForm mass_l2_unweighted(&fes_l2);
133 QuadratureFunction det_J_qf(qs);
134 QuadratureFunctionCoefficient det_J_coeff(det_J_qf);
135 if (convert_map_type)
136 {
137 const auto flags = GeometricFactors::DETERMINANTS;
138 auto *geom = fes_l2.GetMesh()->GetGeometricFactors(qs.GetIntRule(0), flags);
139 det_J_qf = geom->detJ;
140 mass_l2_unweighted.AddDomainIntegrator(new MassIntegrator(det_J_coeff));
141 }
142 else
143 {
144 mass_l2_unweighted.AddDomainIntegrator(new MassIntegrator);
145 }
146 mass_l2_unweighted.SetAssemblyLevel(AssemblyLevel::PARTIAL);
147 mass_l2_unweighted.Assemble();
148 const int n_l2 = fes_l2.GetTrueVSize();
149 L_diag_unweighted.SetSize(n_l2);
150 mass_l2_unweighted.AssembleDiagonal(L_diag_unweighted);
151 }
152
153 Setup();
154}
155
157 ParMesh &mesh, ParFiniteElementSpace &fes_rt_, ParFiniteElementSpace &fes_l2_,
158 Coefficient &R_coeff_, const Array<int> &ess_rt_dofs_)
159 : HdivSaddlePointSolver(mesh, fes_rt_, fes_l2_, zero, R_coeff_,
160 ess_rt_dofs_, Mode::DARCY)
161{ }
162
164{
165 const auto flags = GeometricFactors::DETERMINANTS;
166 auto *geom = fes_l2.GetMesh()->GetGeometricFactors(qs.GetIntRule(0), flags);
167
168 if (!zero_l2_block) { L_coeff.Project(W_coeff_qf); }
169 // In "grad-div mode", the transformation matrix is scaled by the coefficient
170 // of the mass and divergence matrices.
171 // In "Darcy mode", the transformation matrix is unweighted.
172 if (mode == Mode::GRAD_DIV) { W_mix_coeff_qf = W_coeff_qf; }
173 else { W_mix_coeff_qf = 1.0; }
174
175 // The transformation matrix has to be "mixed" value and integral map type,
176 // which means that the coefficient has to be scaled like the Jacobian
177 // determinant.
178 if (convert_map_type)
179 {
180 const int n = W_mix_coeff_qf.Size();
181 const real_t *d_detJ = geom->detJ.Read();
182 real_t *d_w_mix = W_mix_coeff_qf.ReadWrite();
183 real_t *d_w = W_coeff_qf.ReadWrite();
184 const bool zero_l2 = zero_l2_block;
185 MFEM_FORALL(i, n,
186 {
187 const real_t detJ = d_detJ[i];
188 if (!zero_l2) { d_w[i] *= detJ*detJ; }
189 d_w_mix[i] *= detJ;
190 });
191 }
192
193 L_inv.reset(new DGMassInverse(fes_l2, W_mix_coeff));
194
195 if (zero_l2_block)
196 {
197 A_11.reset();
198 }
199 else
200 {
201 mass_l2.Assemble();
202 mass_l2.AssembleDiagonal(L_diag);
203 mass_l2.FormSystemMatrix(empty, L);
204
205 A_11.reset(new RAPOperator(*L_inv, *L, *L_inv));
206
207 if (mode == GRAD_DIV)
208 {
209 L_diag_unweighted.SetSize(L_diag.Size());
210
211 BilinearForm mass_l2_mix(&fes_l2);
212 mass_l2_mix.AddDomainIntegrator(new MassIntegrator(W_mix_coeff));
214 mass_l2_mix.Assemble();
215 mass_l2_mix.AssembleDiagonal(L_diag_unweighted);
216 }
217
218 const real_t *d_L_diag_unweighted = L_diag_unweighted.Read();
219 real_t *d_L_diag = L_diag.ReadWrite();
220 MFEM_FORALL(i, L_diag.Size(),
221 {
222 const real_t d = d_L_diag_unweighted[i];
223 d_L_diag[i] /= d*d;
224 });
225 }
226
227 // Reassemble the RT mass operator with the new coefficient
228 mass_rt.Update();
229 mass_rt.Assemble();
230 mass_rt.FormSystemMatrix(ess_rt_dofs, R);
231
232 // Form the updated approximate Schur complement
233 mass_rt.AssembleDiagonal(R_diag);
234
235 // Update the mass RT diagonal for essential DOFs
236 {
237 const int *d_I = ess_rt_dofs.Read();
238 real_t *d_R_diag = R_diag.ReadWrite();
239 MFEM_FORALL(i, ess_rt_dofs.Size(), d_R_diag[d_I[i]] = 1.0;);
240 }
241
242 // Form the approximate Schur complement
243 {
244 Reciprocal(R_diag);
245 std::unique_ptr<HypreParMatrix> R_diag_inv(MakeDiagonalMatrix(R_diag, fes_rt));
246 if (zero_l2_block)
247 {
248 S.reset(RAP(R_diag_inv.get(), Dt.get()));
249 }
250 else
251 {
252 std::unique_ptr<HypreParMatrix> D_Minv_Dt(RAP(R_diag_inv.get(), Dt.get()));
253 std::unique_ptr<HypreParMatrix> L_diag_inv(MakeDiagonalMatrix(L_diag, fes_l2));
254 S.reset(ParAdd(D_Minv_Dt.get(), L_diag_inv.get()));
255 }
256 }
257
258 // Reassemble the preconditioners
259 R_inv.reset(new OperatorJacobiSmoother(mass_rt, ess_rt_dofs));
260 S_inv.SetOperator(*S);
261
262 // Set up the block operators
263 A_block.reset(new BlockOperator(offsets));
264 // Omit the (1,1)-block when the L coefficient is identically zero.
265 if (A_11) { A_block->SetBlock(0, 0, A_11.get()); }
266 A_block->SetBlock(0, 1, D.get());
267 A_block->SetBlock(1, 0, Dt.get());
268 A_block->SetBlock(1, 1, R.Ptr(), -1.0);
269
270 D_prec.reset(new BlockDiagonalPreconditioner(offsets));
271 D_prec->SetDiagonalBlock(0, &S_inv);
272 D_prec->SetDiagonalBlock(1, R_inv.get());
273
274 minres.SetPreconditioner(*D_prec);
275 minres.SetOperator(*A_block);
276}
277
279{
280 const int n_ess_dofs = ess_rt_dofs.Size();
281 if (fes_l2.GetParMesh()->ReduceInt(n_ess_dofs) == 0) { return; }
282
283 const int n_l2 = offsets[1];
284 const int n_rt = offsets[2]-offsets[1];
285 Vector bE(b, 0, n_l2);
286 Vector bF(b, n_l2, n_rt);
287
288 // SetBC must be called first
289 MFEM_VERIFY(x_bc.Size() == n_rt || n_ess_dofs == 0, "BCs not set");
290
291 // Create a vector z that has the BC values at essential DOFs, zero elsewhere
292 z.SetSize(n_rt);
293 z.UseDevice(true);
294 z = 0.0;
295 const int *d_I = ess_rt_dofs.Read();
296 const real_t *d_x_bc = x_bc.Read();
297 real_t *d_z = z.ReadWrite();
298 MFEM_FORALL(i, n_ess_dofs,
299 {
300 const int j = d_I[i];
301 d_z[j] = d_x_bc[j];
302 });
303
304 // Convert to the IntegratedGLL basis used internally
305 w.SetSize(n_rt);
306 basis_rt.MultInverse(z, w);
307
308 // Eliminate the BCs in the L2 RHS
309 D_e->Mult(-1.0, w, 1.0, bE);
310
311 // Eliminate the BCs in the RT RHS
312 // Flip the sign because the R block appears with multiplier -1
313 z.SetSize(n_rt);
314 R_e->Mult(w, z);
315 bF += z;
316
317 // Insert the RT BCs into the RHS at the essential DOFs.
318 const real_t *d_w = w.Read();
319 real_t *d_bF = bF.ReadWrite(); // Need read-write access to set subvector
320 MFEM_FORALL(i, n_ess_dofs,
321 {
322 const int j = d_I[i];
323 d_bF[j] = -d_w[j];
324 });
325
326 // Make sure the monolithic RHS is updated
327 bE.SyncAliasMemory(b);
328 bF.SyncAliasMemory(b);
329}
330
332{
333 w.SetSize(fes_l2.GetTrueVSize());
334 b_prime.SetSize(b.Size());
335 x_prime.SetSize(x.Size());
336
337 // Transform RHS to the IntegratedGLL basis
338 Vector bE_prime(b_prime, offsets[0], offsets[1]-offsets[0]);
339 Vector bF_prime(b_prime, offsets[1], offsets[2]-offsets[1]);
340
341 const Vector bE(const_cast<Vector&>(b), offsets[0], offsets[1]-offsets[0]);
342 const Vector bF(const_cast<Vector&>(b), offsets[1], offsets[2]-offsets[1]);
343
344 z.SetSize(bE.Size());
345 basis_l2.MultTranspose(bE, z);
346 basis_rt.MultTranspose(bF, bF_prime);
347 // Transform by the inverse of the L2 mass matrix
348 L_inv->Mult(z, bE_prime);
349
350 // Update the monolithic transformed RHS
351 bE_prime.SyncAliasMemory(b_prime);
352 bF_prime.SyncAliasMemory(b_prime);
353
354 // Eliminate the RT essential BCs
355 EliminateBC(b_prime);
356
357 // Solve the transformed system
358 minres.Mult(b_prime, x_prime);
359
360 // Transform the solution back to the user's basis
361 Vector xE_prime(x_prime, offsets[0], offsets[1]-offsets[0]);
362 Vector xF_prime(x_prime, offsets[1], offsets[2]-offsets[1]);
363
364 Vector xE(x, offsets[0], offsets[1]-offsets[0]);
365 Vector xF(x, offsets[1], offsets[2]-offsets[1]);
366
367 z.SetSize(bE.Size()); // Size of z may have changed in EliminateBC
368 L_inv->Mult(xE_prime, z);
369
370 basis_l2.Mult(z, xE);
371 basis_rt.Mult(xF_prime, xF);
372
373 // Update the monolithic solution vector
374 xE.SyncAliasMemory(x);
375 xF.SyncAliasMemory(x);
376}
377
378} // namespace mfem
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition array.hpp:758
int Size() const
Return the logical size of the array.
Definition array.hpp:147
const T * Read(bool on_dev=true) const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), on_dev).
Definition array.hpp:337
A "square matrix" operator for the associated FE space and BLFIntegrators The sum of all the BLFInteg...
void AssembleDiagonal(Vector &diag) const override
Assemble the diagonal of the bilinear form into diag. Note that diag is a tdof Vector.
void SetAssemblyLevel(AssemblyLevel assembly_level)
Set the desired assembly level.
void AddDomainIntegrator(BilinearFormIntegrator *bfi)
Adds new Domain Integrator. Assumes ownership of bfi.
void Assemble(int skip_zeros=1)
Assembles the form i.e. sums over all domain/bdr integrators.
A class to handle Block diagonal preconditioners in a matrix-free implementation.
A class to handle Block systems in a matrix-free implementation.
void MultTranspose(const Vector &x, Vector &y) const override
Action of the transpose operator: y=A^t(x). The default behavior in class Operator is to generate an ...
void Mult(const Vector &x, Vector &y) const override
Operator application: y=A(x).
void MultTranspose(const Vector &x, Vector &y) const override
Action of the transpose operator: y=A^t(x). The default behavior in class Operator is to generate an ...
void MultInverse(const Vector &x, Vector &y) const
Base class Coefficients that optionally depend on space and time. These are used by the BilinearFormI...
virtual void Project(QuadratureFunction &qf)
Fill the QuadratureFunction qf by evaluating the coefficient at the quadrature points.
A coefficient that is constant across space and time.
Solver for the discontinuous Galerkin mass matrix.
Definition dgmassinv.hpp:28
static MemoryType GetDeviceMemoryType()
Get the current Device MemoryType. This is the MemoryType used by most MFEM classes when allocating m...
Definition device.hpp:274
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
Definition fespace.hpp:244
Mesh * GetMesh() const
Returns the mesh.
Definition fespace.hpp:679
const FiniteElement * GetTypicalFE() const
Return GetFE(0) if the local mesh is not empty; otherwise return a typical FE based on the Geometry t...
Definition fespace.cpp:3871
Abstract class for all finite elements.
Definition fe_base.hpp:244
Solve the H(div) saddle-point system using MINRES with matrix-free block-diagonal preconditioning.
HdivSaddlePointSolver(ParMesh &mesh_, ParFiniteElementSpace &fes_rt_, ParFiniteElementSpace &fes_l2_, Coefficient &L_coeff_, Coefficient &R_coeff_, const Array< int > &ess_rt_dofs_, Mode mode_)
Creates a solver for the H(div) saddle-point system.
void Setup()
Build the linear operator and solver. Must be called when the coefficients change.
void EliminateBC(Vector &) const
Eliminates the BCs (called internally, not public interface).
Mode
Which type of saddle-point problem is being solved?
@ DARCY
Darcy/mixed Poisson problem.
void Mult(const Vector &b, Vector &x) const override
Solve the linear system for L2 (scalar) and RT (flux) unknowns.
void SetPrintLevel(int print_level)
Definition hypre.hpp:1797
void SetOperator(const Operator &op) override
Set/update the solver for the given operator.
Definition hypre.cpp:5150
Wrapper for hypre's ParCSR matrix class.
Definition hypre.hpp:408
Class for an integration rule - an Array of IntegrationPoint.
Definition intrules.hpp:100
void SetRelTol(real_t rtol)
Definition solvers.hpp:229
virtual void SetPrintLevel(int print_lvl)
Legacy method to set the level of verbosity of the solver output.
Definition solvers.cpp:72
void SetMaxIter(int max_it)
Definition solvers.hpp:231
void SetAbsTol(real_t atol)
Definition solvers.hpp:230
void Mult(const Vector &b, Vector &x) const override
Iterative solution of the linear system using the MINRES method.
Definition solvers.cpp:1705
void SetOperator(const Operator &op) override
Also calls SetOperator for the preconditioner if there is one.
Definition solvers.cpp:1680
void SetPreconditioner(Solver &pr) override
This should be called before SetOperator.
Definition solvers.hpp:665
static const IntegrationRule & GetRule(const FiniteElement &trial_fe, const FiniteElement &test_fe, const ElementTransformation &Trans)
void New(int size)
Allocate host memory for size entries with the current host memory type returned by MemoryManager::Ge...
Mesh data type.
Definition mesh.hpp:64
ElementTransformation * GetTypicalElementTransformation()
If the local mesh is not empty return GetElementTransformation(0); otherwise, return the identity tra...
Definition mesh.cpp:390
const GeometricFactors * GetGeometricFactors(const IntegrationRule &ir, const int flags, MemoryType d_mt=MemoryType::DEFAULT)
Return the mesh geometric factors corresponding to the given integration rule.
Definition mesh.cpp:880
Operator * Ptr() const
Access the underlying Operator pointer.
Definition handle.hpp:87
Jacobi smoothing for a given bilinear form (no matrix necessary).
Definition solvers.hpp:333
virtual void Mult(const Vector &x, Vector &y) const =0
Operator application: y=A(x).
Class for parallel bilinear form.
void Assemble(int skip_zeros=1)
Assemble the local matrix.
void FormSystemMatrix(const Array< int > &ess_tdof_list, OperatorHandle &A) override
Form the linear system matrix A, see FormLinearSystem() for details.
void Update(FiniteElementSpace *nfes=NULL) override
Update the FiniteElementSpace and delete all data associated with the old one.
void AssembleDiagonal(Vector &diag) const override
Assemble the diagonal of the bilinear form into diag. Note that diag is a true-dof Vector.
Abstract parallel finite element space.
Definition pfespace.hpp:29
MPI_Comm GetComm() const
Definition pfespace.hpp:334
HYPRE_BigInt * GetTrueDofOffsets() const
Definition pfespace.hpp:343
HYPRE_BigInt GlobalTrueVSize() const
Definition pfespace.hpp:346
int GetTrueVSize() const override
Return the number of local vector true dofs.
Definition pfespace.hpp:350
ParMesh * GetParMesh() const
Definition pfespace.hpp:338
Class for parallel meshes.
Definition pmesh.hpp:34
long long ReduceInt(int value) const override
Utility function: sum integers from all processors (Allreduce).
Definition pmesh.cpp:6306
Quadrature function coefficient which requires that the quadrature rules used for this coefficient be...
Represents values or vectors of values at quadrature points on a mesh.
Definition qfunction.hpp:24
const IntegrationRule & GetIntRule(int idx) const
Return the IntegrationRule associated with entity idx.
Definition qspace.hpp:81
The operator x -> R*A*P*x constructed through the actions of R^T, A and P.
Definition operator.hpp:914
bool iterative_mode
If true, use the second argument of Mult() as an initial guess.
Definition operator.hpp:783
Data type sparse matrix.
Definition sparsemat.hpp:51
int * WriteJ(bool on_dev=true)
Memory< int > & GetMemoryI()
int * WriteI(bool on_dev=true)
void OverrideSize(int height_, int width_)
Sets the height and width of the matrix.
Memory< int > & GetMemoryJ()
Memory< real_t > & GetMemoryData()
real_t * WriteData(bool on_dev=true)
Vector data type.
Definition vector.hpp:82
virtual const real_t * Read(bool on_dev=true) const
Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:494
virtual real_t * ReadWrite(bool on_dev=true)
Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:510
void SyncAliasMemory(const Vector &v) const
Update the alias memory location of the vector to match v.
Definition vector.hpp:267
int Size() const
Returns the size of the vector.
Definition vector.hpp:226
virtual void UseDevice(bool use_dev) const
Enable execution of Vector operations using the mfem::Device.
Definition vector.hpp:144
void SetSize(int s)
Resize the vector to size s.
Definition vector.hpp:558
HYPRE_Int HYPRE_BigInt
real_t b
Definition lissajous.cpp:42
HypreParMatrix * MakeDiagonalMatrix(Vector &diag, const ParFiniteElementSpace &fes)
Return a new HypreParMatrix with given diagonal entries.
HypreParMatrix * FormDiscreteDivergenceMatrix(ParFiniteElementSpace &fes_rt, ParFiniteElementSpace &fes_l2, const Array< int > &ess_dofs)
void RAP(const DenseMatrix &A, const DenseMatrix &P, DenseMatrix &RAP)
HypreParMatrix * ParAdd(const HypreParMatrix *A, const HypreParMatrix *B)
Returns the matrix A + B.
Definition hypre.cpp:2967
void Reciprocal(Vector &x)
Replace x[i] with 1.0/x[i] for all i.
float real_t
Definition config.hpp:43
const IntegrationRule & GetMassIntRule(FiniteElementSpace &fes_l2)
Settings for the output behavior of the IterativeSolver.
Definition solvers.hpp:98