MFEM v4.8.0
Finite element discretization library
Loading...
Searching...
No Matches
dgmassinv.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 "dgmassinv.hpp"
13#include "bilinearform.hpp"
14#include "dgmassinv_kernels.hpp"
15#include "../general/forall.hpp"
16
17namespace mfem
18{
19
21 const IntegrationRule *ir,
22 int btype)
23 : Solver(fes_orig.GetTrueVSize()),
24 fec(fes_orig.GetMaxElementOrder(),
25 fes_orig.GetMesh()->Dimension(),
26 btype,
27 fes_orig.GetTypicalFE()->GetMapType()),
28 fes(fes_orig.GetMesh(), &fec)
29{
30 MFEM_VERIFY(fes.IsDGSpace(), "Space must be DG.");
31 MFEM_VERIFY(!fes.IsVariableOrder(), "Variable orders not supported.");
32
33 const int btype_orig =
34 static_cast<const L2_FECollection*>(fes_orig.FEColl())->GetBasisType();
35
36 if (btype_orig == btype)
37 {
38 // No change of basis required
39 d2q = nullptr;
40 }
41 else
42 {
43 // original basis to solver basis
44 const auto mode = DofToQuad::TENSOR;
45 const FiniteElement &fe_orig = *fes_orig.GetTypicalFE();
46 const FiniteElement &fe = *fes.GetTypicalFE();
47 d2q = &fe_orig.GetDofToQuad(fe.GetNodes(), mode);
48
49 int n = d2q->ndof;
50 Array<real_t> B_inv = d2q->B; // deep copy
51 Array<int> ipiv(n);
52 // solver basis to original
53 LUFactors lu(B_inv.HostReadWrite(), ipiv.HostWrite());
54 lu.Factor(n);
55 B_.SetSize(n*n);
57 Bt_.SetSize(n*n);
58 DenseMatrix B_matrix(B_.HostReadWrite(), n, n);
59 DenseMatrix Bt_matrix(Bt_.HostWrite(), n, n);
60 Bt_matrix.Transpose(B_matrix);
61 }
62
63 if (coeff) { m = new MassIntegrator(*coeff, ir); }
64 else { m = new MassIntegrator(ir); }
65
67 // Workspace vectors used for CG
71 // Only need transformed RHS if basis is different
72 if (btype_orig != btype) { b2_.SetSize(height); }
73
74 M = new BilinearForm(&fes);
75 M->AddDomainIntegrator(m); // M assumes ownership of m
77
78 // Assemble the bilinear form and its diagonal (for preconditioning).
79 Update();
80}
81
83 int btype)
84 : DGMassInverse(fes_, &coeff, nullptr, btype) { }
85
87 const IntegrationRule &ir, int btype)
88 : DGMassInverse(fes_, &coeff, &ir, btype) { }
89
91 const IntegrationRule &ir, int btype)
92 : DGMassInverse(fes_, nullptr, &ir, btype) { }
93
95 : DGMassInverse(fes_, nullptr, nullptr, btype) { }
96
98{
99 MFEM_ABORT("SetOperator not supported with DGMassInverse.")
100}
101
102void DGMassInverse::SetRelTol(const real_t rel_tol_) { rel_tol = rel_tol_; }
103
104void DGMassInverse::SetAbsTol(const real_t abs_tol_) { abs_tol = abs_tol_; }
105
106void DGMassInverse::SetMaxIter(const int max_iter_) { max_iter = max_iter_; }
107
114
116{
117 delete M;
118}
119
120template<int DIM, int D1D, int Q1D>
122{
123 using namespace internal; // host/device kernel functions
124
125 const int NE = fes.GetNE();
126 const int d1d = m->dofs1D;
127 const int q1d = m->quad1D;
128
129 const int ND = static_cast<int>(pow(d1d, DIM));
130
131 const auto B = m->maps->B.Read();
132 const auto Bt = m->maps->Bt.Read();
133 const auto pa_data = m->pa_data.Read();
134 const auto dinv = diag_inv.Read();
135 auto r = r_.Write();
136 auto d = d_.Write();
137 auto z = z_.Write();
138 auto u = u_.ReadWrite();
139
140 const real_t RELTOL = rel_tol;
141 const real_t ABSTOL = abs_tol;
142 const int MAXIT = max_iter;
143 const bool IT_MODE = iterative_mode;
144 const bool CHANGE_BASIS = (d2q != nullptr);
145
146 // b is the right-hand side (if no change of basis, this just points to the
147 // incoming RHS vector, if we have to change basis, this points to the
148 // internal b2 vector where we put the transformed RHS)
149 const real_t *b;
150 // the following are non-null if we have to change basis
151 real_t *b2 = nullptr; // non-const access to b2
152 const real_t *b_orig = nullptr; // RHS vector in "original" basis
153 const real_t *d2q_B = nullptr; // matrix to transform initial guess
154 const real_t *q2d_B = nullptr; // matrix to transform solution
155 const real_t *q2d_Bt = nullptr; // matrix to transform RHS
156 if (CHANGE_BASIS)
157 {
158 d2q_B = d2q->B.Read();
159 q2d_B = B_.Read();
160 q2d_Bt = Bt_.Read();
161
162 b2 = b2_.Write();
163 b_orig = b_.Read();
164 b = b2;
165 }
166 else
167 {
168 b = b_.Read();
169 }
170
171 static constexpr int NB = Q1D ? Q1D : 1; // block size
172
173 mfem::forall_2D(NE, NB, NB, [=] MFEM_HOST_DEVICE (int e)
174 {
175 // Perform change of basis if needed
176 if (CHANGE_BASIS)
177 {
178 // Transform RHS
179 DGMassBasis<DIM,D1D>(e, NE, q2d_Bt, b_orig, b2, d1d);
180 if (IT_MODE)
181 {
182 // Transform initial guess
183 DGMassBasis<DIM,D1D>(e, NE, d2q_B, u, u, d1d);
184 }
185 }
186
187 const int tid = MFEM_THREAD_ID(x) + NB*MFEM_THREAD_ID(y);
188
189 // Compute first residual
190 if (IT_MODE)
191 {
192 DGMassApply<DIM,D1D,Q1D>(e, NE, B, Bt, pa_data, u, r, d1d, q1d);
193 DGMassAxpy(e, NE, ND, 1.0, b, -1.0, r, r); // r = b - r
194 }
195 else
196 {
197 // if not in iterative mode, use zero initial guess
198 const int BX = MFEM_THREAD_SIZE(x);
199 const int BY = MFEM_THREAD_SIZE(y);
200 const int bxy = BX*BY;
201 const auto B = ConstDeviceMatrix(b, ND, NE);
202 auto U = DeviceMatrix(u, ND, NE);
203 auto R = DeviceMatrix(r, ND, NE);
204 for (int i = tid; i < ND; i += bxy)
205 {
206 U(i, e) = 0.0;
207 R(i, e) = B(i, e);
208 }
209 MFEM_SYNC_THREAD;
210 }
211
212 DGMassPreconditioner(e, NE, ND, dinv, r, z);
213 DGMassAxpy(e, NE, ND, 1.0, z, 0.0, z, d); // d = z
214
215 real_t nom = DGMassDot<NB>(e, NE, ND, d, r);
216 if (nom < 0.0) { return; /* Not positive definite */ }
217 real_t r0 = fmax(nom*RELTOL*RELTOL, ABSTOL*ABSTOL);
218 if (nom <= r0) { return; /* Converged */ }
219
220 DGMassApply<DIM,D1D,Q1D>(e, NE, B, Bt, pa_data, d, z, d1d, q1d);
221 real_t den = DGMassDot<NB>(e, NE, ND, z, d);
222 if (den <= 0.0)
223 {
224 DGMassDot<NB>(e, NE, ND, d, d);
225 // d2 > 0 => not positive definite
226 if (den == 0.0) { return; }
227 }
228
229 // start iteration
230 int i = 1;
231 while (true)
232 {
233 const real_t alpha = nom/den;
234 DGMassAxpy(e, NE, ND, 1.0, u, alpha, d, u); // u = u + alpha*d
235 DGMassAxpy(e, NE, ND, 1.0, r, -alpha, z, r); // r = r - alpha*A*d
236
237 DGMassPreconditioner(e, NE, ND, dinv, r, z);
238
239 real_t betanom = DGMassDot<NB>(e, NE, ND, r, z);
240 if (betanom < 0.0) { return; /* Not positive definite */ }
241 if (betanom <= r0) { break; /* Converged */ }
242
243 if (++i > MAXIT) { break; }
244
245 const real_t beta = betanom/nom;
246 DGMassAxpy(e, NE, ND, 1.0, z, beta, d, d); // d = z + beta*d
247 DGMassApply<DIM,D1D,Q1D>(e, NE, B, Bt, pa_data, d, z, d1d, q1d); // z = A d
248 den = DGMassDot<NB>(e, NE, ND, d, z);
249 if (den <= 0.0)
250 {
251 DGMassDot<NB>(e, NE, ND, d, d);
252 // d2 > 0 => not positive definite
253 if (den == 0.0) { break; }
254 }
255 nom = betanom;
256 }
257
258 if (CHANGE_BASIS)
259 {
260 DGMassBasis<DIM,D1D>(e, NE, q2d_B, u, u, d1d);
261 }
262 });
263}
264
265void DGMassInverse::Mult(const Vector &Mu, Vector &u) const
266{
267 // Dispatch to templated version based on dim, d1d, and q1d.
268 const int dim = fes.GetMesh()->Dimension();
269 const int d1d = m->dofs1D;
270 const int q1d = m->quad1D;
271
272 const int id = (d1d << 4) | q1d;
273
274 if (dim == 2)
275 {
276 switch (id)
277 {
278 case 0x11: return DGMassCGIteration<2,1,1>(Mu, u);
279 case 0x22: return DGMassCGIteration<2,2,2>(Mu, u);
280 case 0x33: return DGMassCGIteration<2,3,3>(Mu, u);
281 case 0x35: return DGMassCGIteration<2,3,5>(Mu, u);
282 case 0x44: return DGMassCGIteration<2,4,4>(Mu, u);
283 case 0x46: return DGMassCGIteration<2,4,6>(Mu, u);
284 case 0x55: return DGMassCGIteration<2,5,5>(Mu, u);
285 case 0x57: return DGMassCGIteration<2,5,7>(Mu, u);
286 case 0x66: return DGMassCGIteration<2,6,6>(Mu, u);
287 case 0x68: return DGMassCGIteration<2,6,8>(Mu, u);
288 default: return DGMassCGIteration<2>(Mu, u); // Fallback
289 }
290 }
291 else if (dim == 3)
292 {
293 switch (id)
294 {
295 case 0x22: return DGMassCGIteration<3,2,2>(Mu, u);
296 case 0x23: return DGMassCGIteration<3,2,3>(Mu, u);
297 case 0x33: return DGMassCGIteration<3,3,3>(Mu, u);
298 case 0x34: return DGMassCGIteration<3,3,4>(Mu, u);
299 case 0x35: return DGMassCGIteration<3,3,5>(Mu, u);
300 case 0x44: return DGMassCGIteration<3,4,4>(Mu, u);
301 case 0x45: return DGMassCGIteration<3,4,5>(Mu, u);
302 case 0x46: return DGMassCGIteration<3,4,6>(Mu, u);
303 case 0x48: return DGMassCGIteration<3,4,8>(Mu, u);
304 case 0x55: return DGMassCGIteration<3,5,5>(Mu, u);
305 case 0x56: return DGMassCGIteration<3,5,6>(Mu, u);
306 case 0x57: return DGMassCGIteration<3,5,7>(Mu, u);
307 case 0x58: return DGMassCGIteration<3,5,8>(Mu, u);
308 case 0x66: return DGMassCGIteration<3,6,6>(Mu, u);
309 case 0x67: return DGMassCGIteration<3,6,7>(Mu, u);
310 default: return DGMassCGIteration<3>(Mu, u); // Fallback
311 }
312 }
313}
314
315} // namespace mfem
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition array.hpp:758
const T * Read(bool on_dev=true) const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), on_dev).
Definition array.hpp:337
T * HostReadWrite()
Shortcut for mfem::ReadWrite(a.GetMemory(), a.Size(), false).
Definition array.hpp:357
T * HostWrite()
Shortcut for mfem::Write(a.GetMemory(), a.Size(), false).
Definition array.hpp:349
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.
Base class Coefficients that optionally depend on space and time. These are used by the BilinearFormI...
Solver for the discontinuous Galerkin mass matrix.
Definition dgmassinv.hpp:28
Vector diag_inv
Jacobi preconditioner.
Definition dgmassinv.hpp:37
class BilinearForm * M
Mass bilinear form, owned.
Definition dgmassinv.hpp:35
class MassIntegrator * m
Mass integrator, owned by the form M.
Definition dgmassinv.hpp:36
real_t rel_tol
Relative CG tolerance.
Definition dgmassinv.hpp:38
int max_iter
Maximum number of CG iterations;.
Definition dgmassinv.hpp:40
void SetOperator(const Operator &op)
Not implemented. Aborts.
Definition dgmassinv.cpp:97
Array< real_t > B_
Inverse of change of basis.
Definition dgmassinv.hpp:33
real_t abs_tol
Absolute CG tolerance.
Definition dgmassinv.hpp:39
void Mult(const Vector &b, Vector &u) const
Solve the system M b = u.
void SetMaxIter(const int max_iter_)
Set the maximum number of iterations.
DGMassInverse(FiniteElementSpace &fes_, Coefficient *coeff, const IntegrationRule *ir, int btype)
Protected constructor, used internally.
Definition dgmassinv.cpp:20
Array< real_t > Bt_
Inverse of change of basis, transposed.
Definition dgmassinv.hpp:34
void Update()
Recompute operator and preconditioner (when coefficient or mesh changes).
void DGMassCGIteration(const Vector &b_, Vector &u_) const
Solve the system M b = u. Not part of the public interface.
void SetRelTol(const real_t rel_tol_)
Set the relative tolerance.
FiniteElementSpace fes
FE space in requested basis.
Definition dgmassinv.hpp:31
const DofToQuad * d2q
Change of basis. Not owned.
Definition dgmassinv.hpp:32
void SetAbsTol(const real_t abs_tol_)
Set the absolute tolerance.
Data type dense matrix using column-major storage.
Definition densemat.hpp:24
void Transpose()
(*this) = (*this)^t
@ TENSOR
Tensor product representation using 1D matrices/tensors with dimensions using 1D number of quadrature...
Definition fe_base.hpp:165
Array< real_t > B
Basis functions evaluated at quadrature points.
Definition fe_base.hpp:193
int ndof
Number of degrees of freedom = number of basis functions. When mode is TENSOR, this is the 1D number.
Definition fe_base.hpp:178
Array< real_t > Bt
Transpose of B.
Definition fe_base.hpp:199
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
Definition fespace.hpp:244
bool IsVariableOrder() const
Returns true if the space contains elements of varying polynomial orders.
Definition fespace.hpp:709
int GetNE() const
Returns number of elements in the mesh.
Definition fespace.hpp:891
const FiniteElementCollection * FEColl() const
Definition fespace.hpp:878
Mesh * GetMesh() const
Returns the mesh.
Definition fespace.hpp:679
bool IsDGSpace() const
Return whether or not the space is discontinuous (L2)
Definition fespace.hpp:1510
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
virtual const DofToQuad & GetDofToQuad(const IntegrationRule &ir, DofToQuad::Mode mode) const
Return a DofToQuad structure corresponding to the given IntegrationRule using the given DofToQuad::Mo...
Definition fe_base.cpp:365
const IntegrationRule & GetNodes() const
Get a const reference to the nodes of the element.
Definition fe_base.hpp:400
Class for an integration rule - an Array of IntegrationPoint.
Definition intrules.hpp:100
Arbitrary order "L2-conforming" discontinuous finite elements.
Definition fe_coll.hpp:346
bool Factor(int m, real_t TOL=0.0) override
Compute the LU factorization of the current matrix.
void GetInverseMatrix(int m, real_t *X) const override
Assuming L.U = P.A factored data of size (m x m), compute X <- A^{-1}.
const DofToQuad * maps
Not owned.
int Dimension() const
Dimension of the reference space used within the elements.
Definition mesh.hpp:1216
Abstract operator.
Definition operator.hpp:25
int height
Dimension of the output / number of rows in the matrix.
Definition operator.hpp:27
Base class for solvers.
Definition operator.hpp:780
bool iterative_mode
If true, use the second argument of Mult() as an initial guess.
Definition operator.hpp:783
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 SetSize(int s)
Resize the vector to size s.
Definition vector.hpp:558
void Reciprocal()
(*this)(i) = 1.0 / (*this)(i)
Definition vector.cpp:383
virtual real_t * Write(bool on_dev=true)
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:502
Vector beta
const real_t alpha
Definition ex15.cpp:369
int dim
Definition ex24.cpp:53
Mesh * GetMesh(int type)
Definition ex29.cpp:218
real_t b
Definition lissajous.cpp:42
constexpr int DIM
real_t u(const Vector &xvec)
Definition lor_mms.hpp:22
DeviceTensor< 2, real_t > DeviceMatrix
Definition dtensor.hpp:143
DeviceTensor< 2, const real_t > ConstDeviceMatrix
Definition dtensor.hpp:144
void forall_2D(int N, int X, int Y, lambda &&body)
Definition forall.hpp:762
float real_t
Definition config.hpp:43