MFEM  v4.5.1
Finite element discretization library
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
dgmassinv.cpp
Go to the documentation of this file.
1 // Copyright (c) 2010-2022, 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 
17 namespace 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.GetFE(0)->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  d2q = &fes_orig.GetFE(0)->GetDofToQuad(fes.GetFE(0)->GetNodes(), mode);
46 
47  int n = d2q->ndof;
48  Array<double> B_inv = d2q->B; // deep copy
49  Array<int> ipiv(n);
50  // solver basis to original
51  LUFactors lu(B_inv.HostReadWrite(), ipiv.HostWrite());
52  lu.Factor(n);
53  B_.SetSize(n*n);
54  lu.GetInverseMatrix(n, B_.HostWrite());
55  Bt_.SetSize(n*n);
56  DenseMatrix B_matrix(B_.HostReadWrite(), n, n);
57  DenseMatrix Bt_matrix(Bt_.HostWrite(), n, n);
58  Bt_matrix.Transpose(B_matrix);
59  }
60 
61  if (coeff) { m = new MassIntegrator(*coeff, ir); }
62  else { m = new MassIntegrator(ir); }
63 
65  // Workspace vectors used for CG
66  r_.SetSize(height);
67  d_.SetSize(height);
68  z_.SetSize(height);
69  // Only need transformed RHS if basis is different
70  if (btype_orig != btype) { b2_.SetSize(height); }
71 
72  M = new BilinearForm(&fes);
73  M->AddDomainIntegrator(m); // M assumes ownership of m
75 
76  // Assemble the bilinear form and its diagonal (for preconditioning).
77  Update();
78 }
79 
81  int btype)
82  : DGMassInverse(fes_, &coeff, nullptr, btype) { }
83 
85  const IntegrationRule &ir, int btype)
86  : DGMassInverse(fes_, &coeff, &ir, btype) { }
87 
89  const IntegrationRule &ir, int btype)
90  : DGMassInverse(fes_, nullptr, &ir, btype) { }
91 
93  : DGMassInverse(fes_, nullptr, nullptr, btype) { }
94 
96 {
97  MFEM_ABORT("SetOperator not supported with DGMassInverse.")
98 }
99 
100 void DGMassInverse::SetRelTol(const double rel_tol_) { rel_tol = rel_tol_; }
101 
102 void DGMassInverse::SetAbsTol(const double abs_tol_) { abs_tol = abs_tol_; }
103 
104 void DGMassInverse::SetMaxIter(const double max_iter_) { max_iter = max_iter_; }
105 
107 {
108  M->Assemble();
110  internal::MakeReciprocal(diag_inv.Size(), diag_inv.ReadWrite());
111 }
112 
114 {
115  delete M;
116 }
117 
118 template<int DIM, int D1D, int Q1D>
120 {
121  using namespace internal; // host/device kernel functions
122 
123  const int NE = fes.GetNE();
124  const int d1d = m->dofs1D;
125  const int q1d = m->quad1D;
126 
127  const int ND = static_cast<int>(pow(d1d, DIM));
128 
129  const auto B = m->maps->B.Read();
130  const auto Bt = m->maps->Bt.Read();
131  const auto pa_data = m->pa_data.Read();
132  const auto dinv = diag_inv.Read();
133  auto r = r_.Write();
134  auto d = d_.Write();
135  auto z = z_.Write();
136  auto u = u_.ReadWrite();
137 
138  const double RELTOL = rel_tol;
139  const double ABSTOL = abs_tol;
140  const double MAXIT = max_iter;
141  const bool IT_MODE = iterative_mode;
142  const bool CHANGE_BASIS = (d2q != nullptr);
143 
144  // b is the right-hand side (if no change of basis, this just points to the
145  // incoming RHS vector, if we have to change basis, this points to the
146  // internal b2 vector where we put the transformed RHS)
147  const double *b;
148  // the following are non-null if we have to change basis
149  double *b2 = nullptr; // non-const access to b2
150  const double *b_orig = nullptr; // RHS vector in "original" basis
151  const double *d2q_B = nullptr; // matrix to transform initial guess
152  const double *q2d_B = nullptr; // matrix to transform solution
153  const double *q2d_Bt = nullptr; // matrix to transform RHS
154  if (CHANGE_BASIS)
155  {
156  d2q_B = d2q->B.Read();
157  q2d_B = B_.Read();
158  q2d_Bt = Bt_.Read();
159 
160  b2 = b2_.Write();
161  b_orig = b_.Read();
162  b = b2;
163  }
164  else
165  {
166  b = b_.Read();
167  }
168 
169  constexpr int NB = Q1D ? Q1D : 1; // block size
170 
171  MFEM_FORALL_2D(e, NE, NB, NB, 1,
172  {
173  constexpr int NB = Q1D ? Q1D : 1; // redefine here for some compilers
174 
175  // Perform change of basis if needed
176  if (CHANGE_BASIS)
177  {
178  // Transform RHS
179  DGMassBasis<DIM,D1D,MAX_D1D>(e, NE, q2d_Bt, b_orig, b2, d1d);
180  if (IT_MODE)
181  {
182  // Transform initial guess
183  DGMassBasis<DIM,D1D,MAX_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  double nom = DGMassDot<NB>(e, NE, ND, d, r);
216  if (nom < 0.0) { return; /* Not positive definite */ }
217  double 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  double 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 double 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  double 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 double 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,MAX_D1D>(e, NE, q2d_B, u, u, d1d);
261  }
262  });
263 }
264 
265 void 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
T * HostReadWrite()
Shortcut for mfem::ReadWrite(a.GetMemory(), a.Size(), false).
Definition: array.hpp:324
Class for an integration rule - an Array of IntegrationPoint.
Definition: intrules.hpp:90
DeviceTensor< 2, const double > ConstDeviceMatrix
Definition: dtensor.hpp:144
bool IsVariableOrder() const
Returns true if the space contains elements of varying polynomial orders.
Definition: fespace.hpp:463
void Assemble(int skip_zeros=1)
Assembles the form i.e. sums over all domain/bdr integrators.
Tensor product representation using 1D matrices/tensors with dimensions using 1D number of quadrature...
Definition: fe_base.hpp:162
void SetSize(int s)
Resize the vector to size s.
Definition: vector.hpp:513
void SetOperator(const Operator &op)
Not implemented. Aborts.
Definition: dgmassinv.cpp:95
Data type dense matrix using column-major storage.
Definition: densemat.hpp:23
int Size() const
Returns the size of the vector.
Definition: vector.hpp:200
int ndof
Number of degrees of freedom = number of basis functions. When mode is TENSOR, this is the 1D number...
Definition: fe_base.hpp:170
DGMassInverse(FiniteElementSpace &fes_, Coefficient *coeff, const IntegrationRule *ir, int btype)
Protected constructor, used internally.
Definition: dgmassinv.cpp:20
virtual void AssembleDiagonal(Vector &diag) const
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.
constexpr int DIM
bool iterative_mode
If true, use the second argument of Mult() as an initial guess.
Definition: operator.hpp:659
void SetAbsTol(const double abs_tol_)
Set the absolute tolerance.
Definition: dgmassinv.cpp:102
const DofToQuad * maps
Not owned.
DeviceTensor< 2, double > DeviceMatrix
Definition: dtensor.hpp:143
Array< double > Bt_
Inverse of change of basis, transposed.
Definition: dgmassinv.hpp:34
int GetNE() const
Returns number of elements in the mesh.
Definition: fespace.hpp:614
void SetMaxIter(const double max_iter_)
Set the maximum number of iterations.
Definition: dgmassinv.cpp:104
double abs_tol
Absolute CG tolerance.
Definition: dgmassinv.hpp:39
Mesh * GetMesh() const
Returns the mesh.
Definition: fespace.hpp:441
virtual double * Write(bool on_dev=true)
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), on_dev).
Definition: vector.hpp:457
double b
Definition: lissajous.cpp:42
double rel_tol
Relative CG tolerance.
Definition: dgmassinv.hpp:38
const IntegrationRule & GetNodes() const
Get a const reference to the nodes of the element.
Definition: fe_base.hpp:390
const T * Read(bool on_dev=true) const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), on_dev).
Definition: array.hpp:304
bool IsDGSpace() const
Return whether or not the space is discontinuous (L2)
Definition: fespace.hpp:925
int Dimension() const
Definition: mesh.hpp:1006
Array< double > B_
Inverse of change of basis.
Definition: dgmassinv.hpp:33
Array< double > Bt
Transpose of B.
Definition: fe_base.hpp:191
void Transpose()
(*this) = (*this)^t
Definition: densemat.cpp:1381
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
Definition: fespace.hpp:96
Base class Coefficients that optionally depend on space and time. These are used by the BilinearFormI...
Definition: coefficient.hpp:41
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition: array.hpp:679
void Mult(const Vector &b, Vector &u) const
Solve the system M b = u.
Definition: dgmassinv.cpp:265
class BilinearForm * M
Mass bilinear form, owned.
Definition: dgmassinv.hpp:35
int max_iter
Maximum number of CG iterations;.
Definition: dgmassinv.hpp:40
int height
Dimension of the output / number of rows in the matrix.
Definition: operator.hpp:27
Array< double > B
Basis functions evaluated at quadrature points.
Definition: fe_base.hpp:185
void DGMassCGIteration(const Vector &b_, Vector &u_) const
Solve the system M b = u. Not part of the public interface.
Definition: dgmassinv.cpp:119
T * HostWrite()
Shortcut for mfem::Write(a.GetMemory(), a.Size(), false).
Definition: array.hpp:316
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:366
A &quot;square matrix&quot; operator for the associated FE space and BLFIntegrators The sum of all the BLFInteg...
virtual double * ReadWrite(bool on_dev=true)
Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), on_dev).
Definition: vector.hpp:465
class MassIntegrator * m
Mass integrator, owned by the form M.
Definition: dgmassinv.hpp:36
Mesh * GetMesh(int type)
Definition: ex29.cpp:218
int dim
Definition: ex24.cpp:53
virtual bool Factor(int m, double TOL=0.0)
Compute the LU factorization of the current matrix.
Definition: densemat.cpp:3230
void AddDomainIntegrator(BilinearFormIntegrator *bfi)
Adds new Domain Integrator. Assumes ownership of bfi.
virtual const FiniteElement * GetFE(int i) const
Returns pointer to the FiniteElement in the FiniteElementCollection associated with i&#39;th element in t...
Definition: fespace.cpp:2781
const DofToQuad * d2q
Change of basis. Not owned.
Definition: dgmassinv.hpp:32
Vector diag_inv
Jacobi preconditioner.
Definition: dgmassinv.hpp:37
const double alpha
Definition: ex15.cpp:369
Vector data type.
Definition: vector.hpp:60
const FiniteElementCollection * FEColl() const
Definition: fespace.hpp:601
double u(const Vector &xvec)
Definition: lor_mms.hpp:24
Base class for solvers.
Definition: operator.hpp:655
Abstract operator.
Definition: operator.hpp:24
FiniteElementSpace fes
FE space in requested basis.
Definition: dgmassinv.hpp:31
virtual const double * Read(bool on_dev=true) const
Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), on_dev).
Definition: vector.hpp:449
void SetRelTol(const double rel_tol_)
Set the relative tolerance.
Definition: dgmassinv.cpp:100
Solver for the discontinuous Galerkin mass matrix.
Definition: dgmassinv.hpp:27
void Update()
Recompute operator and preconditioner (when coefficient or mesh changes).
Definition: dgmassinv.cpp:106
Arbitrary order &quot;L2-conforming&quot; discontinuous finite elements.
Definition: fe_coll.hpp:288