MFEM v4.9.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
20struct DGMassInvKernels { DGMassInvKernels(); };
21
23 Coefficient *coeff,
24 const IntegrationRule *ir,
25 int btype)
26 : Solver(fes_orig.GetTrueVSize()),
27 fec(fes_orig.GetMaxElementOrder(),
28 fes_orig.GetMesh()->Dimension(),
29 btype,
30 fes_orig.GetTypicalFE()->GetMapType()),
31 fes(fes_orig.GetMesh(), &fec)
32{
33 static DGMassInvKernels kernels;
34
35 MFEM_VERIFY(fes.IsDGSpace(), "Space must be DG.");
36 MFEM_VERIFY(!fes.IsVariableOrder(), "Variable orders not supported.");
37
38 const int btype_orig =
39 static_cast<const L2_FECollection*>(fes_orig.FEColl())->GetBasisType();
40
41 if (btype_orig == btype)
42 {
43 // No change of basis required
44 d2q = nullptr;
45 }
46 else
47 {
48 // original basis to solver basis
49 const auto mode = DofToQuad::TENSOR;
50 const FiniteElement &fe_orig = *fes_orig.GetTypicalFE();
51 const FiniteElement &fe = *fes.GetTypicalFE();
52 d2q = &fe_orig.GetDofToQuad(fe.GetNodes(), mode);
53
54 const int n = d2q->ndof;
55 Array<real_t> B_inv = d2q->B; // deep copy
56 Array<int> ipiv(n);
57 // solver basis to original
58 LUFactors lu(B_inv.HostReadWrite(), ipiv.HostWrite());
59 lu.Factor(n);
60 B_.SetSize(n*n);
62 Bt_.SetSize(n*n);
63 DenseMatrix B_matrix(B_.HostReadWrite(), n, n);
64 DenseMatrix Bt_matrix(Bt_.HostWrite(), n, n);
65 Bt_matrix.Transpose(B_matrix);
66 }
67
68 if (coeff) { m = new MassIntegrator(*coeff, ir); }
69 else { m = new MassIntegrator(ir); }
70
72 // Workspace vectors used for CG
76 // Only need transformed RHS if basis is different
77 if (btype_orig != btype) { b2_.SetSize(height); }
78
79 M.reset(new BilinearForm(&fes));
80 M->AddDomainIntegrator(m); // M assumes ownership of m
81 M->SetAssemblyLevel(AssemblyLevel::PARTIAL);
82
83 // Assemble the bilinear form and its diagonal (for preconditioning).
84 Update();
85}
86
88 int btype)
89 : DGMassInverse(fes_, &coeff, nullptr, btype) { }
90
92 const IntegrationRule &ir, int btype)
93 : DGMassInverse(fes_, &coeff, &ir, btype) { }
94
96 const IntegrationRule &ir, int btype)
97 : DGMassInverse(fes_, nullptr, &ir, btype) { }
98
100 : DGMassInverse(fes_, nullptr, nullptr, btype) { }
101
103{
104 MFEM_ABORT("SetOperator not supported with DGMassInverse.")
105}
106
107void DGMassInverse::SetRelTol(const real_t rel_tol_) { rel_tol = rel_tol_; }
108
109void DGMassInverse::SetAbsTol(const real_t abs_tol_) { abs_tol = abs_tol_; }
110
111void DGMassInverse::SetMaxIter(const int max_iter_) { max_iter = max_iter_; }
112
114{
115 M->Assemble();
118}
119
121
122template<int DIM, int D1D, int Q1D>
124{
125 using namespace internal; // host/device kernel functions
126
127 const int NE = fes.GetNE();
128 const int d1d = m->dofs1D;
129 const int q1d = m->quad1D;
130
131 const int ND = static_cast<int>(pow(d1d, DIM));
132
133 const auto B = m->maps->B.Read();
134 const auto Bt = m->maps->Bt.Read();
135 const auto pa_data = m->pa_data.Read();
136 const auto dinv = diag_inv.Read();
137 auto r = r_.Write();
138 auto d = d_.Write();
139 auto z = z_.Write();
140 auto u = u_.ReadWrite();
141
142 const real_t RELTOL = rel_tol;
143 const real_t ABSTOL = abs_tol;
144 const int MAXIT = max_iter;
145 const bool IT_MODE = iterative_mode;
146 const bool CHANGE_BASIS = (d2q != nullptr);
147
148 // b is the right-hand side (if no change of basis, this just points to the
149 // incoming RHS vector, if we have to change basis, this points to the
150 // internal b2 vector where we put the transformed RHS)
151 const real_t *b;
152 // the following are non-null if we have to change basis
153 real_t *b2 = nullptr; // non-const access to b2
154 const real_t *b_orig = nullptr; // RHS vector in "original" basis
155 const real_t *d2q_B = nullptr; // matrix to transform initial guess
156 const real_t *q2d_B = nullptr; // matrix to transform solution
157 const real_t *q2d_Bt = nullptr; // matrix to transform RHS
158 if (CHANGE_BASIS)
159 {
160 d2q_B = d2q->B.Read();
161 q2d_B = B_.Read();
162 q2d_Bt = Bt_.Read();
163
164 b2 = b2_.Write();
165 b_orig = b_.Read();
166 b = b2;
167 }
168 else
169 {
170 b = b_.Read();
171 }
172
173 static constexpr int NB = Q1D ? Q1D : 1; // block size
174
175 mfem::forall_2D(NE, NB, NB, [=] MFEM_HOST_DEVICE (int e)
176 {
177 // Perform change of basis if needed
178 if (CHANGE_BASIS)
179 {
180 // Transform RHS
181 DGMassBasis<DIM,D1D>(e, NE, q2d_Bt, b_orig, b2, d1d);
182 if (IT_MODE)
183 {
184 // Transform initial guess
185 DGMassBasis<DIM,D1D>(e, NE, d2q_B, u, u, d1d);
186 }
187 }
188
189 const int tid = MFEM_THREAD_ID(x) + NB*MFEM_THREAD_ID(y);
190
191 // Compute first residual
192 if (IT_MODE)
193 {
194 DGMassApply<DIM,D1D,Q1D>(e, NE, B, Bt, pa_data, u, r, d1d, q1d);
195 DGMassAxpy(e, NE, ND, 1.0, b, -1.0, r, r); // r = b - r
196 }
197 else
198 {
199 // if not in iterative mode, use zero initial guess
200 const int BX = MFEM_THREAD_SIZE(x);
201 const int BY = MFEM_THREAD_SIZE(y);
202 const int bxy = BX*BY;
203 const auto B = ConstDeviceMatrix(b, ND, NE);
204 auto U = DeviceMatrix(u, ND, NE);
205 auto R = DeviceMatrix(r, ND, NE);
206 for (int i = tid; i < ND; i += bxy)
207 {
208 U(i, e) = 0.0;
209 R(i, e) = B(i, e);
210 }
211 MFEM_SYNC_THREAD;
212 }
213
214 DGMassPreconditioner(e, NE, ND, dinv, r, z);
215 DGMassAxpy(e, NE, ND, 1.0, z, 0.0, z, d); // d = z
216
217 real_t nom = DGMassDot<NB>(e, NE, ND, d, r);
218 if (nom < 0.0) { return; /* Not positive definite */ }
219 real_t r0 = fmax(nom*RELTOL*RELTOL, ABSTOL*ABSTOL);
220 if (nom <= r0) { return; /* Converged */ }
221
222 DGMassApply<DIM,D1D,Q1D>(e, NE, B, Bt, pa_data, d, z, d1d, q1d);
223 real_t den = DGMassDot<NB>(e, NE, ND, z, d);
224 if (den <= 0.0)
225 {
226 DGMassDot<NB>(e, NE, ND, d, d);
227 // d2 > 0 => not positive definite
228 if (den == 0.0) { return; }
229 }
230
231 // start iteration
232 int i = 1;
233 while (true)
234 {
235 const real_t alpha = nom/den;
236 DGMassAxpy(e, NE, ND, 1.0, u, alpha, d, u); // u = u + alpha*d
237 DGMassAxpy(e, NE, ND, 1.0, r, -alpha, z, r); // r = r - alpha*A*d
238
239 DGMassPreconditioner(e, NE, ND, dinv, r, z);
240
241 real_t betanom = DGMassDot<NB>(e, NE, ND, r, z);
242 if (betanom < 0.0) { return; /* Not positive definite */ }
243 if (betanom <= r0) { break; /* Converged */ }
244
245 if (++i > MAXIT) { break; }
246
247 const real_t beta = betanom/nom;
248 DGMassAxpy(e, NE, ND, 1.0, z, beta, d, d); // d = z + beta*d
249 DGMassApply<DIM,D1D,Q1D>(e, NE, B, Bt, pa_data, d, z, d1d, q1d); // z = A d
250 den = DGMassDot<NB>(e, NE, ND, d, z);
251 if (den <= 0.0)
252 {
253 DGMassDot<NB>(e, NE, ND, d, d);
254 // d2 > 0 => not positive definite
255 if (den == 0.0) { break; }
256 }
257 nom = betanom;
258 }
259
260 if (CHANGE_BASIS)
261 {
262 DGMassBasis<DIM,D1D>(e, NE, q2d_B, u, u, d1d);
263 }
264 });
265}
266
267void DGMassInverse::Mult(const Vector &Mu, Vector &u) const
268{
269 // Dispatch to templated version based on dim, d1d, and q1d.
270 const int dim = fes.GetMesh()->Dimension();
271 const int d1d = m->dofs1D;
272 const int q1d = m->quad1D;
273
274 CGKernels::Run(dim, d1d, q1d, *this, Mu, u);
275}
276
277DGMassInvKernels::DGMassInvKernels()
278{
279 using k = DGMassInverse::CGKernels;
280 // 2D
281 k::Specialization<2,1,1>::Add();
282 k::Specialization<2,2,2>::Add();
283 k::Specialization<2,3,3>::Add();
284 k::Specialization<2,3,5>::Add();
285 k::Specialization<2,4,4>::Add();
286 k::Specialization<2,4,6>::Add();
287 k::Specialization<2,5,5>::Add();
288 k::Specialization<2,5,7>::Add();
289 k::Specialization<2,6,6>::Add();
290 k::Specialization<2,6,8>::Add();
291 // 3D
292 k::Specialization<3,2,2>::Add();
293 k::Specialization<3,2,3>::Add();
294 k::Specialization<3,3,3>::Add();
295 k::Specialization<3,3,4>::Add();
296 k::Specialization<3,3,5>::Add();
297 k::Specialization<3,4,4>::Add();
298 k::Specialization<3,4,5>::Add();
299 k::Specialization<3,4,6>::Add();
300 k::Specialization<3,4,8>::Add();
301 k::Specialization<3,5,5>::Add();
302 k::Specialization<3,5,6>::Add();
303 k::Specialization<3,5,7>::Add();
304 k::Specialization<3,5,8>::Add();
305 k::Specialization<3,6,6>::Add();
306 k::Specialization<3,6,7>::Add();
307}
308
309/// @cond Suppress_Doxygen_warnings
310
311template <int DIM, int D1D, int Q1D>
312DGMassInverse::CGKernelType DGMassInverse::CGKernels::Kernel()
313{
315}
316
317DGMassInverse::CGKernelType DGMassInverse::CGKernels::Fallback(
318 int dim, int, int)
319{
320 if (dim == 1) { return &DGMassInverse::DGMassCGIteration<1>; }
321 else if (dim == 2) { return &DGMassInverse::DGMassCGIteration<2>; }
322 else if (dim == 3) { return &DGMassInverse::DGMassCGIteration<3>; }
323 else { MFEM_ABORT("Unsupported dimension."); }
324}
325
326/// @endcond
327
328} // namespace mfem
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition array.hpp:840
const T * Read(bool on_dev=true) const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), on_dev).
Definition array.hpp:381
T * HostReadWrite()
Shortcut for mfem::ReadWrite(a.GetMemory(), a.Size(), false).
Definition array.hpp:401
T * HostWrite()
Shortcut for mfem::Write(a.GetMemory(), a.Size(), false).
Definition array.hpp:393
A "square matrix" operator for the associated FE space and BLFIntegrators The sum of all the BLFInteg...
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:30
Vector diag_inv
Jacobi preconditioner.
Definition dgmassinv.hpp:39
class MassIntegrator * m
Mass integrator, owned by the form M.
Definition dgmassinv.hpp:38
void(DGMassInverse::*)(const Vector &b_, Vector &u) const CGKernelType
real_t rel_tol
Relative CG tolerance.
Definition dgmassinv.hpp:40
int max_iter
Maximum number of CG iterations;.
Definition dgmassinv.hpp:42
Array< real_t > B_
Inverse of change of basis.
Definition dgmassinv.hpp:35
std::unique_ptr< class BilinearForm > M
Mass bilinear form.
Definition dgmassinv.hpp:37
real_t abs_tol
Absolute CG tolerance.
Definition dgmassinv.hpp:41
DGMassInverse(const FiniteElementSpace &fes_, Coefficient *coeff, const IntegrationRule *ir, int btype)
Protected constructor, used internally.
Definition dgmassinv.cpp:22
void SetMaxIter(const int max_iter_)
Set the maximum number of iterations.
Array< real_t > Bt_
Inverse of change of basis, transposed.
Definition dgmassinv.hpp:36
void SetOperator(const Operator &op) override
Not implemented. Aborts.
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 Mult(const Vector &b, Vector &u) const override
Solve the system M b = u.
void SetRelTol(const real_t rel_tol_)
Set the relative tolerance.
FiniteElementSpace fes
FE space in requested basis.
Definition dgmassinv.hpp:33
const DofToQuad * d2q
Change of basis. Not owned.
Definition dgmassinv.hpp:34
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:208
bool IsVariableOrder() const
Returns true if the space contains elements of varying polynomial orders.
Definition fespace.hpp:678
int GetNE() const
Returns number of elements in the mesh.
Definition fespace.hpp:869
const FiniteElementCollection * FEColl() const
Definition fespace.hpp:856
Mesh * GetMesh() const
Returns the mesh.
Definition fespace.hpp:644
bool IsDGSpace() const
Return whether or not the space is discontinuous (L2)
Definition fespace.hpp:1503
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:3860
Abstract class for all finite elements.
Definition fe_base.hpp:247
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:375
const IntegrationRule & GetNodes() const
Get a const reference to the nodes of the element.
Definition fe_base.hpp:403
Class for an integration rule - an Array of IntegrationPoint.
Definition intrules.hpp:100
Arbitrary order "L2-conforming" discontinuous finite elements.
Definition fe_coll.hpp:350
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:1306
Abstract operator.
Definition operator.hpp:25
int height
Dimension of the output / number of rows in the matrix.
Definition operator.hpp:27
virtual void AssembleDiagonal(Vector &diag) const
Computes the diagonal entries into diag. Typically, this operation only makes sense for linear Operat...
Definition operator.hpp:143
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
virtual const real_t * Read(bool on_dev=true) const
Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:520
virtual real_t * ReadWrite(bool on_dev=true)
Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:536
void SetSize(int s)
Resize the vector to size s.
Definition vector.hpp:584
void Reciprocal()
(*this)(i) = 1.0 / (*this)(i)
Definition vector.cpp:384
virtual real_t * Write(bool on_dev=true)
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:528
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
void forall_2D(int N, int X, int Y, lambda &&body)
Definition forall.hpp:925
float real_t
Definition config.hpp:46
DeviceTensor< 2, const real_t > ConstDeviceMatrix
Definition dtensor.hpp:151
DeviceTensor< 2, real_t > DeviceMatrix
Definition dtensor.hpp:150