MFEM v4.9.0
Finite element discretization library
Loading...
Searching...
No Matches
bilininteg_diffusion_pa.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 "../bilininteg.hpp"
13#include "../gridfunc.hpp"
14#include "../qfunction.hpp"
15#include "../../mesh/nurbs.hpp"
18
19namespace mfem
20{
21
23{
24 if (DeviceCanUseCeed())
25 {
26 ceedOp->GetDiagonal(diag);
27 }
28 else
29 {
30 if (pa_data.Size() == 0) { AssemblePA(*fespace); }
31 const Array<real_t> &B = maps->B;
32 const Array<real_t> &G = maps->G;
33 const Vector &Dv = pa_data;
34 DiagonalPAKernels::Run(dim, dofs1D, quad1D, ne, symmetric, B, G, Dv,
35 diag, dofs1D, quad1D);
36 }
37}
38
39// PA Diffusion Apply kernel
41{
42 if (DeviceCanUseCeed())
43 {
44 ceedOp->AddMult(x, y);
45 }
46 else
47 {
48 const Array<real_t> &B = maps->B;
49 const Array<real_t> &G = maps->G;
50 const Array<real_t> &Bt = maps->Bt;
51 const Array<real_t> &Gt = maps->Gt;
52 const Vector &Dv = pa_data;
53
54#ifdef MFEM_USE_OCCA
55 if (DeviceCanUseOcca())
56 {
57 if (dim == 2)
58 {
59 internal::OccaPADiffusionApply2D(dofs1D,quad1D,ne,B,G,Bt,Gt,Dv,x,y);
60 return;
61 }
62 if (dim == 3)
63 {
64 internal::OccaPADiffusionApply3D(dofs1D,quad1D,ne,B,G,Bt,Gt,Dv,x,y);
65 return;
66 }
67 MFEM_ABORT("OCCA PADiffusionApply unknown kernel!");
68 }
69#endif // MFEM_USE_OCCA
70
71 ApplyPAKernels::Run(dim, dofs1D, quad1D, ne, symmetric, B, G, Bt,
72 Gt, Dv, x, y, dofs1D, quad1D);
73 }
74}
75
77{
78 if (symmetric)
79 {
80 AddMultPA(x, y);
81 }
82 else
83 {
84 MFEM_ABORT("DiffusionIntegrator::AddMultTransposePA only implemented in "
85 "the symmetric case.")
86 }
87}
88
90{
91 const MemoryType mt = (pa_mt == MemoryType::DEFAULT) ?
93 // Assuming the same element type
94 fespace = &fes;
95 Mesh *mesh = fes.GetMesh();
96 const FiniteElement &el = *fes.GetTypicalFE();
97 const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, el);
98 if (DeviceCanUseCeed())
99 {
100 delete ceedOp;
101 MFEM_VERIFY(!VQ && !MQ,
102 "Only scalar coefficient supported for DiffusionIntegrator"
103 " with libCEED");
104 const bool mixed = mesh->GetNumGeometries(mesh->Dimension()) > 1 ||
105 fes.IsVariableOrder();
106 if (mixed)
107 {
108 ceedOp = new ceed::MixedPADiffusionIntegrator(*this, fes, Q);
109 }
110 else
111 {
112 ceedOp = new ceed::PADiffusionIntegrator(fes, *ir, Q);
113 }
114 return;
115 }
116 const int dims = el.GetDim();
117 const int symmDims = (dims * (dims + 1)) / 2; // 1x1: 1, 2x2: 3, 3x3: 6
118 const int nq = ir->GetNPoints();
119 dim = mesh->Dimension();
120 ne = fes.GetNE();
122 const int sdim = mesh->SpaceDimension();
123 maps = &el.GetDofToQuad(*ir, DofToQuad::TENSOR);
124 dofs1D = maps->ndof;
125 quad1D = maps->nqpt;
126
127 QuadratureSpace qs(*mesh, *ir);
129
130 if (MQ) { coeff.ProjectTranspose(*MQ); }
131 else if (VQ) { coeff.Project(*VQ); }
132 else if (Q) { coeff.Project(*Q); }
133 else { coeff.SetConstant(1.0); }
134
135 const int coeff_dim = coeff.GetVDim();
136 symmetric = (coeff_dim != dims*dims);
137 const int pa_size = symmetric ? symmDims : dims*dims;
138
139 pa_data.SetSize(pa_size * nq * ne, mt);
140 internal::PADiffusionSetup(dim, sdim, dofs1D, quad1D, coeff_dim, ne,
141 ir->GetWeights(), geom->J, coeff, pa_data);
142}
143
145{
146 fespace = &fes;
147 Mesh *mesh = fes.GetMesh();
148 dim = mesh->Dimension();
149 MFEM_VERIFY(3 == dim, "Only 3D so far");
150
151 numPatches = mesh->NURBSext->GetNP();
152 for (int p=0; p<numPatches; ++p)
153 {
154 AssemblePatchPA(p, fes);
155 }
156}
157
159 const FiniteElementSpace &fes)
160{
161 Mesh *mesh = fes.GetMesh();
162 SetupPatchBasisData(mesh, patch);
163
164 SetupPatchPA(patch, mesh); // For full quadrature, unitWeights = false
165}
166
168{
169 if (DeviceCanUseCeed())
170 {
171 MFEM_ABORT("Ceed AbsMult not implemented yet");
172 }
173 Vector abs_pa_data(pa_data);
174 abs_pa_data.Abs();
175 auto abs_maps = maps->Abs();
176
177 ApplyPAKernels::Run(dim, dofs1D, quad1D, ne, symmetric,
178 abs_maps.B, abs_maps.G, abs_maps.Bt, abs_maps.Gt,
179 abs_pa_data, x, y, dofs1D, quad1D);
180}
181
183 Vector &y) const
184{
185 if (symmetric)
186 {
187 AddAbsMultPA(x, y);
188 }
189 else
190 {
191 MFEM_ABORT("DiffusionIntegrator::AddAbsMultTransposePA only implemented "
192 "in the symmetric case.")
193 }
194}
195
196
197// This version uses full 1D quadrature rules, taking into account the
198// minimum interaction between basis functions and integration points.
199void DiffusionIntegrator::AddMultPatchPA(const int patch, const Vector &x,
200 Vector &y) const
201{
202 MFEM_VERIFY(3 == dim, "Only 3D so far");
203
204 const Array<int>& Q1D = pQ1D[patch];
205 const Array<int>& D1D = pD1D[patch];
206
207 const std::vector<Array2D<real_t>>& B = pB[patch];
208 const std::vector<Array2D<real_t>>& G = pG[patch];
209
210 const IntArrayVar2D& minD = pminD[patch];
211 const IntArrayVar2D& maxD = pmaxD[patch];
212 const IntArrayVar2D& minQ = pminQ[patch];
213 const IntArrayVar2D& maxQ = pmaxQ[patch];
214
215 auto X = Reshape(x.Read(), D1D[0], D1D[1], D1D[2]);
216 auto Y = Reshape(y.ReadWrite(), D1D[0], D1D[1], D1D[2]);
217
218 const auto qd = Reshape(pa_data.Read(), Q1D[0]*Q1D[1]*Q1D[2],
219 (symmetric ? 6 : 9));
220
221 // NOTE: the following is adapted from AssemblePatchMatrix_fullQuadrature
222 std::vector<Array3D<real_t>> grad(dim);
223 // TODO: Can an optimal order of dimensions be determined, for each patch?
224 Array3D<real_t> gradXY(3, std::max(Q1D[0], D1D[0]), std::max(Q1D[1], D1D[1]));
225 Array2D<real_t> gradX(3, std::max(Q1D[0], D1D[0]));
226
227 for (int d=0; d<dim; ++d)
228 {
229 grad[d].SetSize(Q1D[0], Q1D[1], Q1D[2]);
230
231 for (int qz = 0; qz < Q1D[2]; ++qz)
232 {
233 for (int qy = 0; qy < Q1D[1]; ++qy)
234 {
235 for (int qx = 0; qx < Q1D[0]; ++qx)
236 {
237 grad[d](qx,qy,qz) = 0.0;
238 }
239 }
240 }
241 }
242
243 for (int dz = 0; dz < D1D[2]; ++dz)
244 {
245 for (int qy = 0; qy < Q1D[1]; ++qy)
246 {
247 for (int qx = 0; qx < Q1D[0]; ++qx)
248 {
249 for (int d=0; d<dim; ++d)
250 {
251 gradXY(d,qx,qy) = 0.0;
252 }
253 }
254 }
255 for (int dy = 0; dy < D1D[1]; ++dy)
256 {
257 for (int qx = 0; qx < Q1D[0]; ++qx)
258 {
259 gradX(0,qx) = 0.0;
260 gradX(1,qx) = 0.0;
261 }
262 for (int dx = 0; dx < D1D[0]; ++dx)
263 {
264 const real_t s = X(dx,dy,dz);
265 for (int qx = minD[0][dx]; qx <= maxD[0][dx]; ++qx)
266 {
267 gradX(0,qx) += s * B[0](qx,dx);
268 gradX(1,qx) += s * G[0](qx,dx);
269 }
270 }
271 for (int qy = minD[1][dy]; qy <= maxD[1][dy]; ++qy)
272 {
273 const real_t wy = B[1](qy,dy);
274 const real_t wDy = G[1](qy,dy);
275 // This full range of qx values is generally necessary.
276 for (int qx = 0; qx < Q1D[0]; ++qx)
277 {
278 const real_t wx = gradX(0,qx);
279 const real_t wDx = gradX(1,qx);
280 gradXY(0,qx,qy) += wDx * wy;
281 gradXY(1,qx,qy) += wx * wDy;
282 gradXY(2,qx,qy) += wx * wy;
283 }
284 }
285 }
286 for (int qz = minD[2][dz]; qz <= maxD[2][dz]; ++qz)
287 {
288 const real_t wz = B[2](qz,dz);
289 const real_t wDz = G[2](qz,dz);
290 for (int qy = 0; qy < Q1D[1]; ++qy)
291 {
292 for (int qx = 0; qx < Q1D[0]; ++qx)
293 {
294 grad[0](qx,qy,qz) += gradXY(0,qx,qy) * wz;
295 grad[1](qx,qy,qz) += gradXY(1,qx,qy) * wz;
296 grad[2](qx,qy,qz) += gradXY(2,qx,qy) * wDz;
297 }
298 }
299 }
300 }
301
302 for (int qz = 0; qz < Q1D[2]; ++qz)
303 {
304 for (int qy = 0; qy < Q1D[1]; ++qy)
305 {
306 for (int qx = 0; qx < Q1D[0]; ++qx)
307 {
308 const int q = qx + ((qy + (qz * Q1D[1])) * Q1D[0]);
309 const real_t O00 = qd(q,0);
310 const real_t O01 = qd(q,1);
311 const real_t O02 = qd(q,2);
312 const real_t O10 = symmetric ? O01 : qd(q,3);
313 const real_t O11 = symmetric ? qd(q,3) : qd(q,4);
314 const real_t O12 = symmetric ? qd(q,4) : qd(q,5);
315 const real_t O20 = symmetric ? O02 : qd(q,6);
316 const real_t O21 = symmetric ? O12 : qd(q,7);
317 const real_t O22 = symmetric ? qd(q,5) : qd(q,8);
318
319 const real_t grad0 = grad[0](qx,qy,qz);
320 const real_t grad1 = grad[1](qx,qy,qz);
321 const real_t grad2 = grad[2](qx,qy,qz);
322
323 grad[0](qx,qy,qz) = (O00*grad0)+(O01*grad1)+(O02*grad2);
324 grad[1](qx,qy,qz) = (O10*grad0)+(O11*grad1)+(O12*grad2);
325 grad[2](qx,qy,qz) = (O20*grad0)+(O21*grad1)+(O22*grad2);
326 } // qx
327 } // qy
328 } // qz
329
330 for (int qz = 0; qz < Q1D[2]; ++qz)
331 {
332 for (int dy = 0; dy < D1D[1]; ++dy)
333 {
334 for (int dx = 0; dx < D1D[0]; ++dx)
335 {
336 for (int d=0; d<3; ++d)
337 {
338 gradXY(d,dx,dy) = 0.0;
339 }
340 }
341 }
342 for (int qy = 0; qy < Q1D[1]; ++qy)
343 {
344 for (int dx = 0; dx < D1D[0]; ++dx)
345 {
346 for (int d=0; d<3; ++d)
347 {
348 gradX(d,dx) = 0.0;
349 }
350 }
351 for (int qx = 0; qx < Q1D[0]; ++qx)
352 {
353 const real_t gX = grad[0](qx,qy,qz);
354 const real_t gY = grad[1](qx,qy,qz);
355 const real_t gZ = grad[2](qx,qy,qz);
356 for (int dx = minQ[0][qx]; dx <= maxQ[0][qx]; ++dx)
357 {
358 const real_t wx = B[0](qx,dx);
359 const real_t wDx = G[0](qx,dx);
360 gradX(0,dx) += gX * wDx;
361 gradX(1,dx) += gY * wx;
362 gradX(2,dx) += gZ * wx;
363 }
364 }
365 for (int dy = minQ[1][qy]; dy <= maxQ[1][qy]; ++dy)
366 {
367 const real_t wy = B[1](qy,dy);
368 const real_t wDy = G[1](qy,dy);
369 for (int dx = 0; dx < D1D[0]; ++dx)
370 {
371 gradXY(0,dx,dy) += gradX(0,dx) * wy;
372 gradXY(1,dx,dy) += gradX(1,dx) * wDy;
373 gradXY(2,dx,dy) += gradX(2,dx) * wy;
374 }
375 }
376 }
377 for (int dz = minQ[2][qz]; dz <= maxQ[2][qz]; ++dz)
378 {
379 const real_t wz = B[2](qz,dz);
380 const real_t wDz = G[2](qz,dz);
381 for (int dy = 0; dy < D1D[1]; ++dy)
382 {
383 for (int dx = 0; dx < D1D[0]; ++dx)
384 {
385 Y(dx,dy,dz) +=
386 ((gradXY(0,dx,dy) * wz) +
387 (gradXY(1,dx,dy) * wz) +
388 (gradXY(2,dx,dy) * wDz));
389 }
390 }
391 } // dz
392 } // qz
393}
394
396{
397 Vector xp, yp;
398
399 for (int p=0; p<numPatches; ++p)
400 {
401 Array<int> vdofs;
402 fespace->GetPatchVDofs(p, vdofs);
403
404 x.GetSubVector(vdofs, xp);
405 yp.SetSize(vdofs.Size());
406 yp = 0.0;
407
408 AddMultPatchPA(p, xp, yp);
409
410 y.AddElementVector(vdofs, yp);
411 }
412}
413
414} // namespace mfem
Dynamic 2D array using row-major layout.
Definition array.hpp:430
int Size() const
Return the logical size of the array.
Definition array.hpp:166
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:277
static const IntegrationRule & GetRule(const FiniteElement &trial_fe, const FiniteElement &test_fe)
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
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.
Array< real_t > G
Gradients/divergences/curls of basis functions evaluated at quadrature points.
Definition fe_base.hpp:214
@ 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 > Gt
Transpose of G.
Definition fe_base.hpp:221
int nqpt
Number of quadrature points. When mode is TENSOR, this is the 1D number.
Definition fe_base.hpp:182
Array< real_t > Bt
Transpose of B.
Definition fe_base.hpp:199
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: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
Mesh * GetMesh() const
Returns the mesh.
Definition fespace.hpp:644
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
void GetPatchVDofs(int i, Array< int > &vdofs) const
Returns indices of degrees of freedom in vdofs for NURBS patch i.
Definition fespace.cpp:325
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
int GetDim() const
Returns the reference space dimension for the finite element.
Definition fe_base.hpp:324
Vector J
Jacobians of the element transformations at all quadrature points.
Definition mesh.hpp:3115
Class for an integration rule - an Array of IntegrationPoint.
Definition intrules.hpp:100
int GetNPoints() const
Returns the number of the points in the integration rule.
Definition intrules.hpp:256
const Array< real_t > & GetWeights() const
Return the quadrature weights in a contiguous array.
Definition intrules.cpp:86
const IntegrationRule * IntRule
Mesh data type.
Definition mesh.hpp:65
NURBSExtension * NURBSext
Optional NURBS mesh extension.
Definition mesh.hpp:312
int Dimension() const
Dimension of the reference space used within the elements.
Definition mesh.hpp:1306
int SpaceDimension() const
Dimension of the physical space containing the mesh.
Definition mesh.hpp:1309
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:883
int GetNumGeometries(int dim) const
Return the number of geometries of the given dimension present in the mesh.
Definition mesh.cpp:7558
int GetNP() const
Return the number of patches.
Definition nurbs.hpp:824
Class representing the storage layout of a QuadratureFunction.
Definition qspace.hpp:164
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:584
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)