MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
dgmassinv_kernels.hpp
Go to the documentation of this file.
1// Copyright (c) 2010-2026, 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#ifndef MFEM_DGMASSINV_KERNELS_HPP
13#define MFEM_DGMASSINV_KERNELS_HPP
14
15#include "../linalg/kernels.hpp"
16#include "kernels.hpp"
18#include "dgmassinv.hpp"
19
20namespace mfem
21{
22
23namespace internal
24{
25
26template <int DIM, int D1D, int Q1D>
27MFEM_HOST_DEVICE inline
28void DGMassApply(const int e,
29 const int NE,
30 const real_t *B,
31 const real_t *Bt,
32 const real_t *pa_data,
33 const real_t *x,
34 real_t *y,
35 const int d1d = 0,
36 const int q1d = 0)
37{
38 constexpr bool use_smem = (D1D > 0 && Q1D > 0);
39 constexpr bool ACCUM = false;
40 constexpr int NBZ = 1;
41
42 if (DIM == 1)
43 {
44 PAMassApply1D_Element<ACCUM>(e, NE, B, Bt, pa_data, x, y, d1d, q1d);
45 return;
46 }
47
48 if (use_smem)
49 {
50 // cannot specialize functions below with D1D or Q1D equal to zero
51 // (this branch only runs with D1D and Q1D are both positive)
52 constexpr int TD1D = D1D ? D1D : 1;
53 constexpr int TQ1D = Q1D ? Q1D : 1;
54 if (DIM == 2)
55 {
56 SmemPAMassApply2D_Element<TD1D,TQ1D,NBZ,ACCUM>(e, NE, B, pa_data, x, y);
57 }
58 else if (DIM == 3)
59 {
60 SmemPAMassApply3D_Element<TD1D,TQ1D,NBZ,ACCUM>(e, NE, B, pa_data, x, y);
61 }
62 else
63 {
64 MFEM_ABORT_KERNEL("Unsupported dimension.");
65 }
66 }
67 else
68 {
69 if (DIM == 2)
70 {
71 PAMassApply2D_Element<ACCUM>(e, NE, B, Bt, pa_data, x, y, d1d, q1d);
72 }
73 else if (DIM == 3)
74 {
75 PAMassApply3D_Element<ACCUM>(e, NE, B, Bt, pa_data, x, y, d1d, q1d);
76 }
77 else
78 {
79 MFEM_ABORT_KERNEL("Unsupported dimension.");
80 }
81 }
82}
83
84MFEM_HOST_DEVICE inline
85void DGMassPreconditioner(const int e,
86 const int NE,
87 const int ND,
88 const real_t *dinv,
89 const real_t *x,
90 real_t *y)
91{
92 const auto X = ConstDeviceMatrix(x, ND, NE);
93 const auto D = ConstDeviceMatrix(dinv, ND, NE);
94 auto Y = DeviceMatrix(y, ND, NE);
95
96 const int tid = MFEM_THREAD_ID(x) + MFEM_THREAD_SIZE(x)*MFEM_THREAD_ID(y);
97 const int bxy = MFEM_THREAD_SIZE(x)*MFEM_THREAD_SIZE(y);
98
99 for (int i = tid; i < ND; i += bxy)
100 {
101 Y(i, e) = D(i, e)*X(i, e);
102 }
103 MFEM_SYNC_THREAD;
104}
105
106MFEM_HOST_DEVICE inline
107void DGMassAxpy(const int e,
108 const int NE,
109 const int ND,
110 const real_t a,
111 const real_t *x,
112 const real_t b,
113 const real_t *y,
114 real_t *z)
115{
116 const auto X = ConstDeviceMatrix(x, ND, NE);
117 const auto Y = ConstDeviceMatrix(y, ND, NE);
118 auto Z = DeviceMatrix(z, ND, NE);
119
120 const int tid = MFEM_THREAD_ID(x) + MFEM_THREAD_SIZE(x)*MFEM_THREAD_ID(y);
121 const int bxy = MFEM_THREAD_SIZE(x)*MFEM_THREAD_SIZE(y);
122
123 for (int i = tid; i < ND; i += bxy)
124 {
125 Z(i, e) = a*X(i, e) + b*Y(i, e);
126 }
127 MFEM_SYNC_THREAD;
128}
129
130template <int NB>
131MFEM_HOST_DEVICE inline
132real_t DGMassDot(const int e,
133 const int NE,
134 const int ND,
135 const real_t *x,
136 const real_t *y)
137{
138 const auto X = ConstDeviceMatrix(x, ND, NE);
139 const auto Y = ConstDeviceMatrix(y, ND, NE);
140
141 const int tid = MFEM_THREAD_ID(x) + MFEM_THREAD_SIZE(x)*MFEM_THREAD_ID(y);
142 const int bxy = MFEM_THREAD_SIZE(x)*MFEM_THREAD_SIZE(y);
143
144 MFEM_SHARED real_t s_dot[NB*NB];
145 s_dot[tid] = 0.0;
146
147 for (int i = tid; i < ND; i += bxy) { s_dot[tid] += X(i,e)*Y(i,e); }
148 MFEM_SYNC_THREAD;
149
150 if (bxy > 512 && tid + 512 < bxy) { s_dot[tid] += s_dot[tid + 512]; }
151 MFEM_SYNC_THREAD;
152
153 if (bxy > 256 && tid < 256 && tid + 256 < bxy) { s_dot[tid] += s_dot[tid + 256]; }
154 MFEM_SYNC_THREAD;
155
156 if (bxy > 128 && tid < 128 && tid + 128 < bxy) { s_dot[tid] += s_dot[tid + 128]; }
157 MFEM_SYNC_THREAD;
158
159 if (bxy > 64 && tid < 64 && tid + 64 < bxy) { s_dot[tid] += s_dot[tid + 64]; }
160 MFEM_SYNC_THREAD;
161
162 if (bxy > 32 && tid < 32 && tid + 32 < bxy) { s_dot[tid] += s_dot[tid + 32]; }
163 MFEM_SYNC_THREAD;
164
165 if (bxy > 16 && tid < 16 && tid + 16 < bxy) { s_dot[tid] += s_dot[tid + 16]; }
166 MFEM_SYNC_THREAD;
167
168 if (bxy > 8 && tid < 8 && tid + 8 < bxy) { s_dot[tid] += s_dot[tid + 8]; }
169 MFEM_SYNC_THREAD;
170
171 if (bxy > 4 && tid < 4 && tid + 4 < bxy) { s_dot[tid] += s_dot[tid + 4]; }
172 MFEM_SYNC_THREAD;
173
174 if (bxy > 2 && tid < 2 && tid + 2 < bxy) { s_dot[tid] += s_dot[tid + 2]; }
175 MFEM_SYNC_THREAD;
176
177 if (bxy > 1 && tid < 1 && tid + 1 < bxy) { s_dot[tid] += s_dot[tid + 1]; }
178 MFEM_SYNC_THREAD;
179
180 return s_dot[0];
181}
182
183template<int T_D1D = 0>
184MFEM_HOST_DEVICE inline
185void DGMassBasis1D(const int e,
186 const int NE,
187 const real_t *b_,
188 const real_t *x_,
189 real_t *y_,
190 const int d1d = 0)
191{
192 const int D1D = T_D1D ? T_D1D : d1d;
193
194 const auto b = Reshape(b_, D1D, D1D);
195 const auto x = Reshape(x_, D1D, NE);
196 auto y = Reshape(y_, D1D, NE);
197
198 constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
199 real_t Y[MD1];
200
201 MFEM_FOREACH_THREAD(i,x,D1D)
202 {
203 real_t val = 0.0;
204 for (int j = 0; j < D1D; ++j)
205 {
206 val += b(i,j)*x(j,e);
207 }
208 Y[i] = val;
209 }
210 MFEM_SYNC_THREAD;
211 if (MFEM_THREAD_ID(y) == 0)
212 {
213 MFEM_FOREACH_THREAD(i,x,D1D)
214 {
215 y(i,e) = Y[i];
216 }
217 }
218}
219
220template<int T_D1D = 0>
221MFEM_HOST_DEVICE inline
222void DGMassBasis2D(const int e,
223 const int NE,
224 const real_t *b_,
225 const real_t *x_,
226 real_t *y_,
227 const int d1d = 0)
228{
229 constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
230 const int D1D = T_D1D ? T_D1D : d1d;
231
232 const auto b = Reshape(b_, D1D, D1D);
233 const auto x = Reshape(x_, D1D, D1D, NE);
234 auto y = Reshape(y_, D1D, D1D, NE);
235
236 MFEM_SHARED real_t sB[MD1*MD1];
237 MFEM_SHARED real_t sm0[MD1*MD1];
238 MFEM_SHARED real_t sm1[MD1*MD1];
239
240 kernels::internal::LoadB<MD1,MD1>(D1D,D1D,b,sB);
241
242 ConstDeviceMatrix B(sB, D1D,D1D);
243 DeviceMatrix DD(sm0, MD1, MD1);
244 DeviceMatrix DQ(sm1, MD1, MD1);
245 DeviceMatrix QQ(sm0, MD1, MD1);
246
247 kernels::internal::LoadX(e,D1D,x,DD);
248 kernels::internal::EvalX(D1D,D1D,B,DD,DQ);
249 kernels::internal::EvalY(D1D,D1D,B,DQ,QQ);
250 MFEM_SYNC_THREAD; // sync here to allow in-place evaluations
251 MFEM_FOREACH_THREAD(qy,y,D1D)
252 {
253 MFEM_FOREACH_THREAD(qx,x,D1D)
254 {
255 y(qx,qy,e) = QQ(qx,qy);
256 }
257 }
258 MFEM_SYNC_THREAD;
259}
260
261template<int T_D1D = 0>
262MFEM_HOST_DEVICE inline
263void DGMassBasis3D(const int e,
264 const int NE,
265 const real_t *b_,
266 const real_t *x_,
267 real_t *y_,
268 const int d1d = 0)
269{
270 const int D1D = T_D1D ? T_D1D : d1d;
271
272 const auto b = Reshape(b_, D1D, D1D);
273 const auto x = Reshape(x_, D1D, D1D, D1D, NE);
274 auto y = Reshape(y_, D1D, D1D, D1D, NE);
275
276 constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
277
278 MFEM_SHARED real_t sB[MD1*MD1];
279 MFEM_SHARED real_t sm0[MD1*MD1*MD1];
280 MFEM_SHARED real_t sm1[MD1*MD1*MD1];
281
282 kernels::internal::LoadB<MD1,MD1>(D1D,D1D,b,sB);
283
284 ConstDeviceMatrix B(sB, D1D,D1D);
285 DeviceCube DDD(sm0, MD1,MD1,MD1);
286 DeviceCube DDQ(sm1, MD1,MD1,MD1);
287 DeviceCube DQQ(sm0, MD1,MD1,MD1);
288 DeviceCube QQQ(sm1, MD1,MD1,MD1);
289
290 kernels::internal::LoadX(e,D1D,x,DDD);
291 kernels::internal::EvalX(D1D,D1D,B,DDD,DDQ);
292 kernels::internal::EvalY(D1D,D1D,B,DDQ,DQQ);
293 kernels::internal::EvalZ(D1D,D1D,B,DQQ,QQQ);
294 MFEM_SYNC_THREAD; // sync here to allow in-place evaluation
295 MFEM_FOREACH_THREAD(qz,z,D1D)
296 {
297 MFEM_FOREACH_THREAD(qy,y,D1D)
298 {
299 for (int qx = 0; qx < D1D; ++qx)
300 {
301 y(qx,qy,qz,e) = QQQ(qz,qy,qx);
302 }
303 }
304 }
305 MFEM_SYNC_THREAD;
306}
307
308template<int DIM, int T_D1D = 0>
309MFEM_HOST_DEVICE inline
310void DGMassBasis(const int e,
311 const int NE,
312 const real_t *b_,
313 const real_t *x_,
314 real_t *y_,
315 const int d1d = 0)
316{
317 if (DIM == 1)
318 {
319 DGMassBasis1D<T_D1D>(e, NE, b_, x_, y_, d1d);
320 }
321 else if (DIM == 2)
322 {
323 DGMassBasis2D<T_D1D>(e, NE, b_, x_, y_, d1d);
324 }
325 else if (DIM == 3)
326 {
327 DGMassBasis3D<T_D1D>(e, NE, b_, x_, y_, d1d);
328 }
329 else
330 {
331 MFEM_ABORT_KERNEL("Dimension not supported.");
332 }
333}
334
335} // namespace internal
336
337template<int DIM, int D1D, int Q1D>
339{
340 using namespace internal; // host/device kernel functions
341
342 const int NE = fes.GetNE();
343 const int d1d = m->dofs1D;
344 const int q1d = m->quad1D;
345
346 const int ND = static_cast<int>(pow(d1d, DIM));
347
348 const auto B = m->maps->B.Read();
349 const auto Bt = m->maps->Bt.Read();
350 const auto pa_data = m->pa_data.Read();
351 const auto dinv = diag_inv.Read();
352 auto r = r_.Write();
353 auto d = d_.Write();
354 auto z = z_.Write();
355 auto u = u_.ReadWrite();
356
357 const real_t RELTOL = rel_tol;
358 const real_t ABSTOL = abs_tol;
359 const int MAXIT = max_iter;
360 const bool IT_MODE = iterative_mode;
361 const bool CHANGE_BASIS = (d2q != nullptr);
362
363 // b is the right-hand side (if no change of basis, this just points to the
364 // incoming RHS vector, if we have to change basis, this points to the
365 // internal b2 vector where we put the transformed RHS)
366 const real_t *b;
367 // the following are non-null if we have to change basis
368 real_t *b2 = nullptr; // non-const access to b2
369 const real_t *b_orig = nullptr; // RHS vector in "original" basis
370 const real_t *d2q_B = nullptr; // matrix to transform initial guess
371 const real_t *q2d_B = nullptr; // matrix to transform solution
372 const real_t *q2d_Bt = nullptr; // matrix to transform RHS
373 if (CHANGE_BASIS)
374 {
375 d2q_B = d2q->B.Read();
376 q2d_B = B_.Read();
377 q2d_Bt = Bt_.Read();
378
379 b2 = b2_.Write();
380 b_orig = b_.Read();
381 b = b2;
382 }
383 else
384 {
385 b = b_.Read();
386 }
387
388 static constexpr int NB = Q1D ? Q1D : 1; // block size
389
390 mfem::forall_2D<NB*NB>(NE, NB, NB, [=] MFEM_HOST_DEVICE (int e)
391 {
392 // Perform change of basis if needed
393 if (CHANGE_BASIS)
394 {
395 // Transform RHS
396 DGMassBasis<DIM,D1D>(e, NE, q2d_Bt, b_orig, b2, d1d);
397 if (IT_MODE)
398 {
399 // Transform initial guess
400 DGMassBasis<DIM,D1D>(e, NE, d2q_B, u, u, d1d);
401 }
402 }
403
404 const int tid = MFEM_THREAD_ID(x) + NB*MFEM_THREAD_ID(y);
405
406 // Compute first residual
407 if (IT_MODE)
408 {
409 DGMassApply<DIM,D1D,Q1D>(e, NE, B, Bt, pa_data, u, r, d1d, q1d);
410 DGMassAxpy(e, NE, ND, 1.0, b, -1.0, r, r); // r = b - r
411 }
412 else
413 {
414 // if not in iterative mode, use zero initial guess
415 const int BX = MFEM_THREAD_SIZE(x);
416 const int BY = MFEM_THREAD_SIZE(y);
417 const int bxy = BX*BY;
418 const auto B = ConstDeviceMatrix(b, ND, NE);
419 auto U = DeviceMatrix(u, ND, NE);
420 auto R = DeviceMatrix(r, ND, NE);
421 for (int i = tid; i < ND; i += bxy)
422 {
423 U(i, e) = 0.0;
424 R(i, e) = B(i, e);
425 }
426 MFEM_SYNC_THREAD;
427 }
428
429 DGMassPreconditioner(e, NE, ND, dinv, r, z);
430 DGMassAxpy(e, NE, ND, 1.0, z, 0.0, z, d); // d = z
431
432 real_t nom = DGMassDot<NB>(e, NE, ND, d, r);
433 if (nom < 0.0) { return; /* Not positive definite */ }
434 real_t r0 = fmax(nom*RELTOL*RELTOL, ABSTOL*ABSTOL);
435 if (nom <= r0) { return; /* Converged */ }
436
437 DGMassApply<DIM,D1D,Q1D>(e, NE, B, Bt, pa_data, d, z, d1d, q1d);
438 real_t den = DGMassDot<NB>(e, NE, ND, z, d);
439 if (den <= 0.0)
440 {
441 DGMassDot<NB>(e, NE, ND, d, d);
442 // d2 > 0 => not positive definite
443 if (den == 0.0) { return; }
444 }
445
446 // start iteration
447 int i = 1;
448 while (true)
449 {
450 const real_t alpha = nom/den;
451 DGMassAxpy(e, NE, ND, 1.0, u, alpha, d, u); // u = u + alpha*d
452 DGMassAxpy(e, NE, ND, 1.0, r, -alpha, z, r); // r = r - alpha*A*d
453
454 DGMassPreconditioner(e, NE, ND, dinv, r, z);
455
456 real_t betanom = DGMassDot<NB>(e, NE, ND, r, z);
457 if (betanom < 0.0) { return; /* Not positive definite */ }
458 if (betanom <= r0) { break; /* Converged */ }
459
460 if (++i > MAXIT) { break; }
461
462 const real_t beta = betanom/nom;
463 DGMassAxpy(e, NE, ND, 1.0, z, beta, d, d); // d = z + beta*d
464 DGMassApply<DIM,D1D,Q1D>(e, NE, B, Bt, pa_data, d, z, d1d, q1d); // z = A d
465 den = DGMassDot<NB>(e, NE, ND, d, z);
466 if (den <= 0.0)
467 {
468 DGMassDot<NB>(e, NE, ND, d, d);
469 // d2 > 0 => not positive definite
470 if (den == 0.0) { break; }
471 }
472 nom = betanom;
473 }
474
475 if (CHANGE_BASIS)
476 {
477 DGMassBasis<DIM,D1D>(e, NE, q2d_B, u, u, d1d);
478 }
479 });
480}
481
482/// @cond Suppress_Doxygen_warnings
483
484template <int DIM, int D1D, int Q1D>
485inline DGMassInverse::CGKernelType DGMassInverse::CGKernels::Kernel()
486{
488}
489
490inline DGMassInverse::CGKernelType DGMassInverse::CGKernels::Fallback(
491 int dim, int, int)
492{
493 if (dim == 1) { return &DGMassInverse::DGMassCGIteration<1>; }
494 else if (dim == 2) { return &DGMassInverse::DGMassCGIteration<2>; }
495 else if (dim == 3) { return &DGMassInverse::DGMassCGIteration<3>; }
496 else { MFEM_ABORT("Unsupported dimension."); }
497}
498
499/// @endcond
500
501} // namespace mfem
502
503#endif
const T * Read(bool on_dev=true) const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), on_dev).
Definition array.hpp:410
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
real_t abs_tol
Absolute CG tolerance.
Definition dgmassinv.hpp:41
Array< real_t > Bt_
Inverse of change of basis, transposed.
Definition dgmassinv.hpp:36
void DGMassCGIteration(const Vector &b_, Vector &u_) const
Solve the system M b = u. Not part of the public interface.
FiniteElementSpace fes
FE space in requested basis.
Definition dgmassinv.hpp:33
const DofToQuad * d2q
Change of basis. Not owned.
Definition dgmassinv.hpp:34
Array< real_t > B
Basis functions evaluated at quadrature points.
Definition fe_base.hpp:201
Array< real_t > Bt
Transpose of B.
Definition fe_base.hpp:207
int GetNE() const
Returns number of elements in the mesh.
Definition fespace.hpp:867
const DofToQuad * maps
Not owned.
bool iterative_mode
If true, use the second argument of Mult() as an initial guess.
Definition operator.hpp:858
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
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
real_t b
Definition lissajous.cpp:42
real_t a
Definition lissajous.cpp:41
constexpr int DIM
mfem::real_t real_t
DeviceTensor< 3, real_t > DeviceCube
Definition dtensor.hpp:153
real_t u(const Vector &xvec)
Definition lor_mms.hpp:22
MFEM_HOST_DEVICE DeviceTensor< sizeof...(Dims), T > Reshape(T *ptr, Dims... dims)
Wrap a pointer as a DeviceTensor with automatically deduced template parameters.
Definition dtensor.hpp:138
void forall_2D(int N, int X, int Y, lambda &&body)
Definition forall.hpp:1220
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