MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
bilininteg_diffusion_pa.cpp
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#include "../bilininteg.hpp"
13#include "../gridfunc.hpp"
14#include "../qfunction.hpp"
15#include "../../mesh/nurbs.hpp"
19
20namespace mfem
21{
22
24{
25 if (DeviceCanUseCeed())
26 {
27 ceedOp->GetDiagonal(diag);
28 }
29 else
30 {
31 if (pa_data.Size() == 0) { AssemblePA(*fespace); }
32 MFEM_VERIFY(maps->mode != DofToQuad::RAGGED_TENSOR,
33 "AssembleDiagonalPA not implemented for ragged tensor bases");
34 const Array<real_t> &B = maps->B;
35 const Array<real_t> &G = maps->G;
36 const Vector &Dv = pa_data;
37 DiagonalPAKernels::Run(dim, dofs1D, quad1D, ne, symmetric, B, G, Dv,
38 diag, dofs1D, quad1D);
39 }
40}
41
42// PA Diffusion Apply kernel
44{
45 if (DeviceCanUseCeed())
46 {
47 ceedOp->AddMult(x, y);
48 }
49 else
50 {
51 const Array<real_t> &B = maps->B;
52 const Array<real_t> &G = maps->G;
53 const Array<real_t> &Bt = maps->Bt;
54 const Array<real_t> &Gt = maps->Gt;
55 const Vector &Dv = pa_data;
56
57#ifdef MFEM_USE_OCCA
58 if (DeviceCanUseOcca())
59 {
60 if (dim == 2)
61 {
62 internal::OccaPADiffusionApply2D(dofs1D,quad1D,ne,B,G,Bt,Gt,Dv,x,y);
63 return;
64 }
65 if (dim == 3)
66 {
67 internal::OccaPADiffusionApply3D(dofs1D,quad1D,ne,B,G,Bt,Gt,Dv,x,y);
68 return;
69 }
70 MFEM_ABORT("OCCA PADiffusionApply unknown kernel!");
71 }
72#endif // MFEM_USE_OCCA
73
74 if (fespace->UsesRaggedTensorBasis())
75 {
76 const auto *rmaps = static_cast<const RaggedDofToQuad*>(maps);
77 return ApplySimplexPAKernels::Run(dim, dofs1D, quad1D, ne, symmetric,
78 rmaps->lex_map,
79 rmaps->forward_map2d_diff,
80 rmaps->inverse_map2d_diff,
81 rmaps->forward_map3d_diff,
82 rmaps->inverse_map3d_diff,
83 rmaps->Ga1,
84 rmaps->Ga2,
85 rmaps->Ga3,
86 rmaps->Ga1t,
87 rmaps->Ga2t,
88 rmaps->Ga3t,
89 Dv, x, y, dofs1D, quad1D);
90 }
91
92 ApplyPAKernels::Run(dim, dofs1D, quad1D, ne, symmetric, B, G, Bt,
93 Gt, Dv, x, y, dofs1D, quad1D);
94 }
95}
96
98{
99 if (symmetric)
100 {
101 AddMultPA(x, y);
102 }
103 else
104 {
105 MFEM_ABORT("DiffusionIntegrator::AddMultTransposePA only implemented in "
106 "the symmetric case.")
107 }
108}
109
111{
112 const MemoryType mt = (pa_mt == MemoryType::DEFAULT) ?
114 // Assuming the same element type
115 fespace = &fes;
116 Mesh *mesh = fes.GetMesh();
117 const FiniteElement &el = *fes.GetTypicalFE();
118 const bool stroud = fes.UsesRaggedTensorBasis();
119 const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, el, stroud);
120 if (DeviceCanUseCeed())
121 {
122 delete ceedOp;
123 MFEM_VERIFY(!VQ && !MQ,
124 "Only scalar coefficient supported for DiffusionIntegrator"
125 " with libCEED");
126 const bool mixed = mesh->GetNumGeometries(mesh->Dimension()) > 1 ||
127 fes.IsVariableOrder();
128 if (mixed)
129 {
130 ceedOp = new ceed::MixedPADiffusionIntegrator(*this, fes, Q);
131 }
132 else
133 {
134 ceedOp = new ceed::PADiffusionIntegrator(fes, *ir, Q);
135 }
136 return;
137 }
138 const int dims = el.GetDim();
139 const int symmDims = (dims * (dims + 1)) / 2; // 1x1: 1, 2x2: 3, 3x3: 6
140 const int nq = ir->GetNPoints();
141 dim = mesh->Dimension();
142 ne = fes.GetNE();
144 if (stroud)
145 {
146 maps = &el.GetDofToQuad(*ir, DofToQuad::RAGGED_TENSOR);
147 }
148 else
149 {
150 maps = &el.GetDofToQuad(*ir, DofToQuad::TENSOR);
151 }
152 const int sdim = mesh->SpaceDimension();
153 dofs1D = maps->ndof;
154 quad1D = maps->nqpt;
155
156 QuadratureSpace qs(*mesh, *ir);
158 // QuadratureSpace expects ir defined in reference simplex for Bernstein
159 // elements with partial assembly
160
161 if (MQ) { coeff.ProjectTranspose(*MQ); }
162 else if (VQ) { coeff.Project(*VQ); }
163 else if (Q) { coeff.Project(*Q); }
164 else { coeff.SetConstant(1.0); }
165
166 const int coeff_dim = coeff.GetVDim();
167 symmetric = (coeff_dim != dims*dims);
168 const int pa_size = symmetric ? symmDims : dims*dims;
169
170 pa_data.SetSize(pa_size * nq * ne, mt);
171 internal::PADiffusionSetup(dim, sdim, dofs1D, quad1D, coeff_dim, ne,
172 ir->GetWeights(), geom->J, coeff, pa_data);
173}
174
176{
177 fespace = &fes;
178 Mesh *mesh = fes.GetMesh();
179 dim = mesh->Dimension();
180 MFEM_VERIFY(3 == dim, "Only 3D so far");
181
182 numPatches = mesh->NURBSext->GetNP();
183 for (int p=0; p<numPatches; ++p)
184 {
185 AssemblePatchPA(p, fes);
186 }
187}
188
190 const FiniteElementSpace &fes)
191{
192 Mesh *mesh = fes.GetMesh();
193 SetupPatchBasisData(mesh, patch);
194
195 SetupPatchPA(patch, mesh); // For full quadrature, unitWeights = false
196}
197
199{
200 if (DeviceCanUseCeed())
201 {
202 MFEM_ABORT("Ceed AbsMult not implemented yet");
203 }
204 Vector abs_pa_data(pa_data);
205 abs_pa_data.Abs();
206 auto abs_maps = maps->Abs();
207
208 ApplyPAKernels::Run(dim, dofs1D, quad1D, ne, symmetric,
209 abs_maps.B, abs_maps.G, abs_maps.Bt, abs_maps.Gt,
210 abs_pa_data, x, y, dofs1D, quad1D);
211}
212
214 Vector &y) const
215{
216 if (symmetric)
217 {
218 AddAbsMultPA(x, y);
219 }
220 else
221 {
222 MFEM_ABORT("DiffusionIntegrator::AddAbsMultTransposePA only implemented "
223 "in the symmetric case.")
224 }
225}
226
227
228// This version uses full 1D quadrature rules, taking into account the
229// minimum interaction between basis functions and integration points.
230void DiffusionIntegrator::AddMultPatchPA(const int patch, const Vector &x,
231 Vector &y) const
232{
233 MFEM_VERIFY(3 == dim, "Only 3D so far");
234
235 const Array<int>& Q1D = pQ1D[patch];
236 const Array<int>& D1D = pD1D[patch];
237
238 const std::vector<Array2D<real_t>>& B = pB[patch];
239 const std::vector<Array2D<real_t>>& G = pG[patch];
240
241 const IntArrayVar2D& minD = pminD[patch];
242 const IntArrayVar2D& maxD = pmaxD[patch];
243 const IntArrayVar2D& minQ = pminQ[patch];
244 const IntArrayVar2D& maxQ = pmaxQ[patch];
245
246 auto X = Reshape(x.Read(), D1D[0], D1D[1], D1D[2]);
247 auto Y = Reshape(y.ReadWrite(), D1D[0], D1D[1], D1D[2]);
248
249 const auto qd = Reshape(pa_data.Read(), Q1D[0]*Q1D[1]*Q1D[2],
250 (symmetric ? 6 : 9));
251
252 // NOTE: the following is adapted from AssemblePatchMatrix_fullQuadrature
253 std::vector<Array3D<real_t>> grad(dim);
254 // TODO: Can an optimal order of dimensions be determined, for each patch?
255 Array3D<real_t> gradXY(3, std::max(Q1D[0], D1D[0]), std::max(Q1D[1], D1D[1]));
256 Array2D<real_t> gradX(3, std::max(Q1D[0], D1D[0]));
257
258 for (int d=0; d<dim; ++d)
259 {
260 grad[d].SetSize(Q1D[0], Q1D[1], Q1D[2]);
261
262 for (int qz = 0; qz < Q1D[2]; ++qz)
263 {
264 for (int qy = 0; qy < Q1D[1]; ++qy)
265 {
266 for (int qx = 0; qx < Q1D[0]; ++qx)
267 {
268 grad[d](qx,qy,qz) = 0.0;
269 }
270 }
271 }
272 }
273
274 for (int dz = 0; dz < D1D[2]; ++dz)
275 {
276 for (int qy = 0; qy < Q1D[1]; ++qy)
277 {
278 for (int qx = 0; qx < Q1D[0]; ++qx)
279 {
280 for (int d=0; d<dim; ++d)
281 {
282 gradXY(d,qx,qy) = 0.0;
283 }
284 }
285 }
286 for (int dy = 0; dy < D1D[1]; ++dy)
287 {
288 for (int qx = 0; qx < Q1D[0]; ++qx)
289 {
290 gradX(0,qx) = 0.0;
291 gradX(1,qx) = 0.0;
292 }
293 for (int dx = 0; dx < D1D[0]; ++dx)
294 {
295 const real_t s = X(dx,dy,dz);
296 for (int qx = minD[0][dx]; qx <= maxD[0][dx]; ++qx)
297 {
298 gradX(0,qx) += s * B[0](qx,dx);
299 gradX(1,qx) += s * G[0](qx,dx);
300 }
301 }
302 for (int qy = minD[1][dy]; qy <= maxD[1][dy]; ++qy)
303 {
304 const real_t wy = B[1](qy,dy);
305 const real_t wDy = G[1](qy,dy);
306 // This full range of qx values is generally necessary.
307 for (int qx = 0; qx < Q1D[0]; ++qx)
308 {
309 const real_t wx = gradX(0,qx);
310 const real_t wDx = gradX(1,qx);
311 gradXY(0,qx,qy) += wDx * wy;
312 gradXY(1,qx,qy) += wx * wDy;
313 gradXY(2,qx,qy) += wx * wy;
314 }
315 }
316 }
317 for (int qz = minD[2][dz]; qz <= maxD[2][dz]; ++qz)
318 {
319 const real_t wz = B[2](qz,dz);
320 const real_t wDz = G[2](qz,dz);
321 for (int qy = 0; qy < Q1D[1]; ++qy)
322 {
323 for (int qx = 0; qx < Q1D[0]; ++qx)
324 {
325 grad[0](qx,qy,qz) += gradXY(0,qx,qy) * wz;
326 grad[1](qx,qy,qz) += gradXY(1,qx,qy) * wz;
327 grad[2](qx,qy,qz) += gradXY(2,qx,qy) * wDz;
328 }
329 }
330 }
331 }
332
333 for (int qz = 0; qz < Q1D[2]; ++qz)
334 {
335 for (int qy = 0; qy < Q1D[1]; ++qy)
336 {
337 for (int qx = 0; qx < Q1D[0]; ++qx)
338 {
339 const int q = qx + ((qy + (qz * Q1D[1])) * Q1D[0]);
340 const real_t O00 = qd(q,0);
341 const real_t O01 = qd(q,1);
342 const real_t O02 = qd(q,2);
343 const real_t O10 = symmetric ? O01 : qd(q,3);
344 const real_t O11 = symmetric ? qd(q,3) : qd(q,4);
345 const real_t O12 = symmetric ? qd(q,4) : qd(q,5);
346 const real_t O20 = symmetric ? O02 : qd(q,6);
347 const real_t O21 = symmetric ? O12 : qd(q,7);
348 const real_t O22 = symmetric ? qd(q,5) : qd(q,8);
349
350 const real_t grad0 = grad[0](qx,qy,qz);
351 const real_t grad1 = grad[1](qx,qy,qz);
352 const real_t grad2 = grad[2](qx,qy,qz);
353
354 grad[0](qx,qy,qz) = (O00*grad0)+(O01*grad1)+(O02*grad2);
355 grad[1](qx,qy,qz) = (O10*grad0)+(O11*grad1)+(O12*grad2);
356 grad[2](qx,qy,qz) = (O20*grad0)+(O21*grad1)+(O22*grad2);
357 } // qx
358 } // qy
359 } // qz
360
361 for (int qz = 0; qz < Q1D[2]; ++qz)
362 {
363 for (int dy = 0; dy < D1D[1]; ++dy)
364 {
365 for (int dx = 0; dx < D1D[0]; ++dx)
366 {
367 for (int d=0; d<3; ++d)
368 {
369 gradXY(d,dx,dy) = 0.0;
370 }
371 }
372 }
373 for (int qy = 0; qy < Q1D[1]; ++qy)
374 {
375 for (int dx = 0; dx < D1D[0]; ++dx)
376 {
377 for (int d=0; d<3; ++d)
378 {
379 gradX(d,dx) = 0.0;
380 }
381 }
382 for (int qx = 0; qx < Q1D[0]; ++qx)
383 {
384 const real_t gX = grad[0](qx,qy,qz);
385 const real_t gY = grad[1](qx,qy,qz);
386 const real_t gZ = grad[2](qx,qy,qz);
387 for (int dx = minQ[0][qx]; dx <= maxQ[0][qx]; ++dx)
388 {
389 const real_t wx = B[0](qx,dx);
390 const real_t wDx = G[0](qx,dx);
391 gradX(0,dx) += gX * wDx;
392 gradX(1,dx) += gY * wx;
393 gradX(2,dx) += gZ * wx;
394 }
395 }
396 for (int dy = minQ[1][qy]; dy <= maxQ[1][qy]; ++dy)
397 {
398 const real_t wy = B[1](qy,dy);
399 const real_t wDy = G[1](qy,dy);
400 for (int dx = 0; dx < D1D[0]; ++dx)
401 {
402 gradXY(0,dx,dy) += gradX(0,dx) * wy;
403 gradXY(1,dx,dy) += gradX(1,dx) * wDy;
404 gradXY(2,dx,dy) += gradX(2,dx) * wy;
405 }
406 }
407 }
408 for (int dz = minQ[2][qz]; dz <= maxQ[2][qz]; ++dz)
409 {
410 const real_t wz = B[2](qz,dz);
411 const real_t wDz = G[2](qz,dz);
412 for (int dy = 0; dy < D1D[1]; ++dy)
413 {
414 for (int dx = 0; dx < D1D[0]; ++dx)
415 {
416 Y(dx,dy,dz) +=
417 ((gradXY(0,dx,dy) * wz) +
418 (gradXY(1,dx,dy) * wz) +
419 (gradXY(2,dx,dy) * wDz));
420 }
421 }
422 } // dz
423 } // qz
424}
425
427{
428 Vector xp, yp;
429
430 for (int p=0; p<numPatches; ++p)
431 {
432 Array<int> vdofs;
433 fespace->GetPatchVDofs(p, vdofs);
434
435 x.GetSubVector(vdofs, xp);
436 yp.SetSize(vdofs.Size());
437 yp = 0.0;
438
439 AddMultPatchPA(p, xp, yp);
440
441 y.AddElementVector(vdofs, yp);
442 }
443}
444
445} // namespace mfem
Dynamic 2D array using row-major layout.
Definition array.hpp:459
int Size() const
Return the logical size of the array.
Definition array.hpp:192
Class to represent a coefficient evaluated at quadrature points.
void SetConstant(real_t constant)
Set this vector to the given constant.
void Project(Coefficient &coeff)
Evaluate the given Coefficient at the quadrature points defined by qs.
int GetVDim() const
Return the number of values per quadrature point.
void ProjectTranspose(MatrixCoefficient &coeff)
Project the transpose of coeff.
static MemoryType GetDeviceMemoryType()
Get the current Device MemoryType. This is the MemoryType used by most MFEM classes when allocating m...
Definition device.hpp:298
void AddMultNURBSPA(const Vector &, Vector &) const override
Method for partially assembled action on NURBS patches.
void AssemblePatchPA(const int patch, const FiniteElementSpace &fes)
void AddAbsMultTransposePA(const Vector &, Vector &) const override
static const IntegrationRule & GetRule(const FiniteElement &trial_fe, const FiniteElement &test_fe, const bool stroud=false)
MatrixCoefficient * MQ
void AddMultPA(const Vector &, Vector &) const override
Method for partially assembled action.
void AssembleDiagonalPA(Vector &diag) override
Assemble diagonal and add it to Vector diag.
VectorCoefficient * VQ
void AddAbsMultPA(const Vector &, Vector &) const override
void AddMultPatchPA(const int patch, const Vector &x, Vector &y) const
void AssembleNURBSPA(const FiniteElementSpace &fes) override
Method defining partial assembly on NURBS patches.
void AssemblePA(const FiniteElementSpace &fes) override
Method defining partial assembly.
void AddMultTransposePA(const Vector &, Vector &) const override
Method for partially assembled transposed action.
Mode mode
Describes the contents of the B, Bt, G, and Gt arrays, see Mode.
Definition fe_base.hpp:182
Array< real_t > G
Gradients/divergences/curls of basis functions evaluated at quadrature points.
Definition fe_base.hpp:222
@ RAGGED_TENSOR
Ragged tensor product representation using 1D matrices/tensors with dimensions using 1D number of qua...
Definition fe_base.hpp:178
@ 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:201
int ndof
Number of degrees of freedom = number of basis functions. When mode is TENSOR, this is the 1D number.
Definition fe_base.hpp:186
Array< real_t > Gt
Transpose of G.
Definition fe_base.hpp:229
int nqpt
Number of quadrature points. When mode is TENSOR, this is the 1D number.
Definition fe_base.hpp:190
Array< real_t > Bt
Transpose of B.
Definition fe_base.hpp:207
DofToQuad Abs() const
Returns absolute value of the maps.
Definition fe_base.cpp:23
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
Definition fespace.hpp:210
bool IsVariableOrder() const
Returns true if the space contains elements of varying polynomial orders.
Definition fespace.hpp:673
int GetNE() const
Returns number of elements in the mesh.
Definition fespace.hpp:867
bool UsesRaggedTensorBasis() const
Return true if the mesh contains only one topology, the elements are all triangles or tetrahedrons,...
Definition fespace.hpp:1595
Mesh * GetMesh() const
Returns the mesh.
Definition fespace.hpp:639
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:3896
void GetPatchVDofs(int i, Array< int > &vdofs) const
Returns indices of degrees of freedom in vdofs for NURBS patch i.
Definition fespace.cpp:320
Abstract class for all finite elements.
Definition fe_base.hpp:294
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:373
int GetDim() const
Returns the reference space dimension for the finite element.
Definition fe_base.hpp:381
Vector J
Jacobians of the element transformations at all quadrature points.
Definition mesh.hpp:3158
Class for an integration rule - an Array of IntegrationPoint.
Definition intrules.hpp:96
int GetNPoints() const
Returns the number of the points in the integration rule.
Definition intrules.hpp:255
const Array< real_t > & GetWeights() const
Return the quadrature weights in a contiguous array.
Definition intrules.cpp:98
const IntegrationRule * IntRule
Mesh data type.
Definition mesh.hpp:67
NURBSExtension * NURBSext
Optional NURBS mesh extension.
Definition mesh.hpp:317
int Dimension() const
Dimension of the reference space used within the elements.
Definition mesh.hpp:1314
int SpaceDimension() const
Dimension of the physical space containing the mesh.
Definition mesh.hpp:1317
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:958
int GetNumGeometries(int dim) const
Return the number of geometries of the given dimension present in the mesh.
Definition mesh.cpp:8014
int GetNP() const
Return the number of patches.
Definition nurbs.hpp:936
Class representing the storage layout of a QuadratureFunction.
Definition qspace.hpp:164
Structure representing the matrices/tensors needed to evaluate (in reference space) the values,...
Definition fe_base.hpp:247
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 AddElementVector(const Array< int > &dofs, const Vector &elemvect)
Add elements of the elemvect Vector to the entries listed in dofs. Negative dof values cause the -dof...
Definition vector.cpp:785
int Size() const
Returns the size of the vector.
Definition vector.hpp:234
void Abs()
(*this)(i) = abs((*this)(i))
Definition vector.cpp:392
void SetSize(int s)
Resize the vector to size s.
Definition vector.hpp:633
void GetSubVector(const Array< int > &dofs, Vector &elemvect) const
Extract entries listed in dofs to the output Vector elemvect.
Definition vector.cpp:676
void GetDiagonal(mfem::Vector &diag) const
Definition operator.cpp:104
void AddMult(const mfem::Vector &x, mfem::Vector &y, const real_t a=1.0) const override
Operator application: y+=A(x) (default) or y+=a*A(x).
Definition operator.cpp:72
Represent a DiffusionIntegrator with AssemblyLevel::Partial using libCEED.
Definition diffusion.hpp:27
int dim
Definition ex24.cpp:53
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
bool DeviceCanUseCeed()
Function that determines if a CEED kernel should be used, based on the current mfem::Device configura...
Definition util.cpp:33
@ COMPRESSED
Enable all above compressions.
bool DeviceCanUseOcca()
Function that determines if an OCCA kernel should be used, based on the current mfem::Device configur...
Definition occa.hpp:69
float real_t
Definition config.hpp:46
MemoryType
Memory types supported by MFEM.
real_t p(const Vector &x, real_t t)