MFEM v4.9.0
Finite element discretization library
Loading...
Searching...
No Matches
bilininteg_elasticity_kernels.hpp
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/**
13 * @file
14 * @brief Header for small strain, isotropic, linear elasticity kernels.
15 *
16 * Strong form: -div(sigma(u))
17 *
18 * The constitutive model is given in terms of Lame parameters,
19 * sigma(u) = lambda*div(u)I + 2*mu*sym(grad(u)).
20 * The weak form implemented is (suppressing integral)
21 *
22 * Weak form : lambda*div(u)*div(v) + 2*mu*sym(grad(u))*sym(grad(v))
23 *
24 * DATA LAYOUT ASSUMPTIONS :
25 * Finite element space - Ordering::byNODES
26 * Finite element basis - ElementDofOrdering::NATIVE
27 * Quadrature functions - QVectorLayout::byNODES
28 * All elements in "fespace" are the same.
29 */
30
31#ifndef MFEM_BILININTEG_ELASTICITY_KERNELS_HPP
32#define MFEM_BILININTEG_ELASTICITY_KERNELS_HPP
33
41#include "../bilininteg.hpp"
42#include "../coefficient.hpp"
43#include "../qfunction.hpp"
44
45namespace mfem
46{
47
48namespace internal
49{
50
51/// @brief Elasticity kernel for AddMultPA.
52///
53/// Performs y += Ax. Implemented for byNODES ordering only, and does not use
54/// tensor basis, so it should work for any H1 element.
55///
56/// @param[in] dim 2 or 3
57/// @param[in] nDofs Number of scalar dofs per element.
58/// @param[in] fespace Vector-valued finite element space.
59/// @param[in] lambda Quadrature function for first Lame param.
60/// @param[in] mu Quadrature function for second Lame param.
61/// @param[in] geom Geometric factors corresponding to fespace.
62/// @param[in] maps DofToQuad maps for one element (assume elements all same).
63/// @param[in] x Input vector. nDofs x dim x numEls.
64/// @param Q Scratch Q-Vector. nQuad x dim x dim x numEls.
65/// @param[in,out] y Ax gets added to this. nDofs x dim x numEls.
66void ElasticityAddMultPA(const int dim, const int nDofs,
67 const FiniteElementSpace &fespace, const CoefficientVector &lambda,
68 const CoefficientVector &mu, const GeometricFactors &geom,
69 const DofToQuad &maps, const Vector &x, QuadratureFunction &QVec, Vector &y);
70
71/// @brief Elasticity component kernel for AddMultPA.
72///
73/// Performs y += Ax. Implemented for byNODES ordering only, and does not use
74/// tensor basis, so it should work for any H1 element. i_block and j_block are
75/// the dimensional component that is integrated. They must both be
76/// non-negative.
77///
78/// Example: In 2D, A = [A_00 A_01], x = [x_0], y = [y_0]
79/// [A_10 A_11] [x_1] [y_1].
80/// So i_block = 0, j_block = 1 implies only y_0 += A_01*x_1 is evaluated.
81///
82/// @param[in] dim 2 or 3
83/// @param[in] nDofs Number of scalar dofs per element.
84/// @param[in] fespace Scalar-valued finite element space.
85/// @param[in] lambda Quadrature function for first Lame param.
86/// @param[in] mu Quadrature function for second Lame param.
87/// @param[in] geom Geometric factors corresponding to fespace.
88/// @param[in] maps DofToQuad maps for one element (assume elements all same).
89/// @param[in] x Input vector. nDofs x numEls.
90/// @param Q Scratch Q-Vector. nQuad x dim x numEls.
91/// @param[in,out] y Ax gets added to this. nDofs x numEls.
92/// @param[in] i_block The row dimensional component. <= dim - 1
93/// @param[in] j_block The column dimensional component. <= dim -1
94void ElasticityComponentAddMultPA(
95 const int dim, const int nDofs, const FiniteElementSpace &fespace,
96 const CoefficientVector &lambda, const CoefficientVector &mu,
97 const GeometricFactors &geom, const DofToQuad &maps, const Vector &x,
98 QuadratureFunction &QVec, Vector &y, const int i_block, const int j_block);
99
100/// @brief Elasticity kernel for AssembleEA.
101///
102/// Assembles the E-Matrix for a single dimensional component. Does not require
103/// tensor product elements.
104///
105/// Example: In 2D, A = [A_00 A_01]
106/// [A_10 A_11].
107/// So i_block = 0, j_block = 1 implies only A_01 is assembled.
108///
109/// Mainly intended to be used for order 1 elements on gpus to enable
110/// preconditioning with a LOR-AMG operator. It's expected behavior that higher
111/// orders may request too many resources.
112///
113/// @param[in] dim 2 or 3
114/// @param[in] i_block The row dimensional component. 0 <= i_block <= dim - 1
115/// @param[in] j_block The column dimensional component. 0 <= j_block<= dim -1
116/// @param[in] nDofs Number of scalar dofs per element.
117/// @param[in] lambda Quadrature function for first Lame param.
118/// @param[in] mu Quadrature function for second Lame param.
119/// @param[in] geom Geometric factors corresponding to fespace.
120/// @param[in] maps DofToQuad maps for one element (assume elements all same).
121/// @param[out] emat Resulting E-Matrix Vector. nDofs x nDofs x numEls.
122void ElasticityAssembleEA(const int dim, const int i_block, const int j_block,
123 const int nDofs, const IntegrationRule &ir,
124 const CoefficientVector &lambda,
125 const CoefficientVector &mu, const GeometricFactors &geom,
126 const DofToQuad &maps, Vector &emat);
127
128/// @brief Elasticity kernel for AssembleDiagonalPA.
129///
130/// @param[in] dim 2 or 3
131/// @param[in] nDofs Number of scalar dofs per element.
132/// @param[in] lambda Quadrature function for first Lame param.
133/// @param[in] mu Quadrature function for second Lame param.
134/// @param[in] geom Geometric factors corresponding to fespace.
135/// @param[in] maps DofToQuad maps for one element (assume elements all same).
136/// @param QVec Scratch Q-Vector. nQuad x dim x dim x dim x dim x numEls.
137/// @param[out] diag diagonal of A. nDofs x dim x numEls.
138void ElasticityAssembleDiagonalPA(const int dim, const int nDofs,
139 const CoefficientVector &lambda,
140 const CoefficientVector &mu, const GeometricFactors &geom,
141 const DofToQuad &maps, QuadratureFunction &QVec, Vector &diag);
142
143/// Templated implementation of ElasticityAddMultPA.
144template<int dim, int i_block = -1, int j_block = -1>
145void ElasticityAddMultPA_(const int nDofs, const FiniteElementSpace &fespace,
146 const CoefficientVector &lambda, const CoefficientVector &mu,
147 const GeometricFactors &geom, const DofToQuad &maps, const Vector &x,
148 QuadratureFunction &QVec, Vector &y)
149{
150 using future::tensor;
152 using future::det;
153 using future::inv;
154
155 static_assert((i_block < 0) == (j_block < 0),
156 "i_block and j_block must both be non-negative or strictly negative.");
157 static constexpr int d = dim;
158 static constexpr int qLower = (i_block < 0) ? 0 : i_block;
159 static constexpr int qUpper = (i_block < 0) ? d : i_block+1;
160 static constexpr int qSize = qUpper-qLower;
161 static constexpr int aLower = (j_block < 0) ? 0 : j_block;
162 static constexpr int aUpper = (j_block < 0) ? d : j_block+1;
163 static constexpr int aSize = aUpper-aLower;
164 static constexpr bool isComponent = (i_block >= 0);
165
166 // Assuming all elements are the same
167 const auto &ir = QVec.GetIntRule(0);
168 const QuadratureInterpolator *E_To_Q_Map = fespace.GetQuadratureInterpolator(
169 ir);
170 E_To_Q_Map->SetOutputLayout(QVectorLayout::byNODES);
171 // interpolate physical derivatives to quadrature points.
172 E_To_Q_Map->PhysDerivatives(x, QVec);
173
174 const int numPoints = ir.GetNPoints();
175 const int numEls = fespace.GetNE();
176 const auto lamDev = Reshape(lambda.Read(), numPoints, numEls);
177 const auto muDev = Reshape(mu.Read(), numPoints, numEls);
178 const auto J = Reshape(geom.J.Read(), numPoints, d, d, numEls);
179 auto Q = Reshape(QVec.ReadWrite(), numPoints, d, qSize, numEls);
180 const real_t *ipWeights = ir.GetWeights().Read();
181 mfem::forall_2D(numEls, numPoints, 1, [=] MFEM_HOST_DEVICE (int e)
182 {
183 // for(int p = 0; p < numPoints, )
184 MFEM_FOREACH_THREAD(p, x,numPoints)
185 {
186 auto invJ = inv(make_tensor<d, d>(
187 [&](int i, int j) { return J(p, i, j, e); }));
188 tensor<real_t, aSize, d> gradx;
189 // load grad(x) into gradx
190 if (isComponent)
191 {
192 for (int i = 0; i < d; i++)
193 {
194 gradx(0,i) = Q(p, i, 0, e);
195 }
196 }
197 else
198 {
199 for (int j = 0; j < d; j++)
200 {
201 for (int i = 0; i < d; i++)
202 {
203 gradx(i,j) = Q(p, i, j, e);
204 }
205 }
206 }
207 // compute divergence
208 real_t div = 0.;
209 for (int i = aLower; i < aUpper; i++)
210 {
211 // take size of gradx into account
212 const int iIndex = isComponent ? 0 : i;
213 div += gradx(iIndex,i);
214 }
215 const real_t w = ipWeights[p]/det(invJ);
216 for (int m = 0; m < d; m++)
217 {
218 for (int q = qLower; q < qUpper; q++)
219 {
220 // compute contraction of 4*sym(grad(u))sym(grad(v)) term.
221 // this contraction could be made slightly cheaper using Voigt
222 // notation, but repeated entries are summed for simplicity.
223 real_t contraction = 0.;
224 // not sure how to combine cases
225 if (isComponent)
226 {
227 for (int a = 0; a < d; a++)
228 {
229 contraction += 2*((a == q)*invJ(m,j_block)
230 + (j_block==q)*invJ(m,a))*(gradx(0, a));
231 }
232 }
233 else
234 {
235 for (int a = 0; a < d; a++)
236 {
237 for (int b = 0; b < d; b++)
238 {
239 contraction += ((a == q)*invJ(m,b) + (b == q)*invJ(m,a))
240 *(gradx(a,b) + gradx(b, a));
241 }
242 }
243 }
244 // lambda*div(u)*div(v) + 2*mu*sym(grad(u))*sym(grad(v))
245 // contraction = 4*sym(grad(u))sym(grad(v))
246 const int qIndex = isComponent ? 0 : q;
247 Q(p,m,qIndex,e) = w*(lamDev(p, e)*invJ(m,q)*div
248 + 0.5*muDev(p, e)*contraction);
249 }
250 }
251 }
252 });
253
254 // Reduce quadrature function to an E-Vector
255 const auto QRead = Reshape(QVec.Read(), numPoints, d, qSize, numEls);
256 const auto G = Reshape(maps.G.Read(), numPoints, d, nDofs);
257 auto yDev = Reshape(y.ReadWrite(), nDofs, qSize, numEls);
258 mfem::forall_2D(numEls, qSize, nDofs, [=] MFEM_HOST_DEVICE (int e)
259 {
260 MFEM_FOREACH_THREAD(i, y, nDofs)
261 {
262 MFEM_FOREACH_THREAD(q, x, qSize)
263 {
264 const int qIndex = isComponent ? 0 : q;
265 real_t sum = 0.;
266 for (int m = 0; m < d; m++ )
267 {
268 for (int p = 0; p < numPoints; p++ )
269 {
270 sum += QRead(p,m,qIndex,e)*G(p,m,i);
271 }
272 }
273 yDev(i, qIndex, e) += sum;
274 }
275 }
276 });
277}
278
279/// Templated implementation of ElasticityAssembleDiagonalPA.
280template<int dim>
281void ElasticityAssembleDiagonalPA_(const int nDofs,
282 const CoefficientVector &lambda,
283 const CoefficientVector &mu, const GeometricFactors &geom,
284 const DofToQuad &maps, QuadratureFunction &QVec, Vector &diag)
285{
286 using future::tensor;
288 using future::det;
289 using future::inv;
290
291 // Assuming all elements are the same
292 const auto &ir = QVec.GetIntRule(0);
293 static constexpr int d = dim;
294 const int numPoints = ir.GetNPoints();
295 const int numEls = lambda.Size()/numPoints;
296 const auto lamDev = Reshape(lambda.Read(), numPoints, numEls);
297 const auto muDev = Reshape(mu.Read(), numPoints, numEls);
298 const auto J = Reshape(geom.J.Read(), numPoints, d, d, numEls);
299 auto Q = Reshape(QVec.ReadWrite(), numPoints, d,d, d, numEls);
300 const real_t *ipWeights = ir.GetWeights().Read();
301 mfem::forall_2D(numEls, numPoints,1, [=] MFEM_HOST_DEVICE (int e)
302 {
303 MFEM_FOREACH_THREAD(p, x,numPoints)
304 {
305 auto invJ = inv(make_tensor<d, d>(
306 [&](int i, int j) { return J(p, i, j, e); }));
307 const real_t w = ipWeights[p] /det(invJ);
308 for (int n = 0; n < d; n++)
309 {
310 for (int m = 0; m < d; m++)
311 {
312 for (int q = 0; q < d; q++)
313 {
314 // compute contraction of 4*sym(grad(u))sym(grad(v)) term.
315 // this contraction could be made slightly cheaper using Voigt
316 // notation, but repeated entries are summed for simplicity.
317 real_t contraction = 0.;
318 for (int a = 0; a < d; a++)
319 {
320 for (int b = 0; b < d; b++)
321 {
322 contraction += ((a == q)*invJ(m,b) + (b==q)*invJ(m,a))*((a == q)
323 *invJ(n, b) + (b==q)*invJ(n,a));
324 }
325 }
326 // lambda*div(u)*div(v) + 2*mu*sym(grad(u))*sym(grad(v))
327 // contraction = 4*sym(grad(u))sym(grad(v))
328 Q(p,m,n,q,e) = w*(lamDev(p, e)*invJ(m,q)*invJ(n,q)
329 + 0.5*muDev(p, e)*contraction);
330 }
331 }
332 }
333 }
334 });
335
336 // Reduce quadrature function to an E-Vector
337 const auto QRead = Reshape(QVec.Read(), numPoints, d, d, d, numEls);
338 auto diagDev = Reshape(diag.Write(), nDofs, d, numEls);
339 const auto G = Reshape(maps.G.Read(), numPoints, d, nDofs);
340 mfem::forall_2D(numEls, d, nDofs, [=] MFEM_HOST_DEVICE (int e)
341 {
342 MFEM_FOREACH_THREAD(i, y, nDofs)
343 {
344 MFEM_FOREACH_THREAD(q, x, d)
345 {
346 real_t sum = 0.;
347 for (int n = 0; n < d; n++)
348 {
349 for (int m = 0; m < d; m++)
350 {
351 for (int p = 0; p < numPoints; p++ )
352 {
353 sum += QRead(p,m,n,q,e)*G(p,m,i)*G(p,n,i);
354 }
355 }
356 }
357 diagDev(i, q, e) = sum;
358 }
359 }
360 });
361}
362
363// Templated implementation of ElasticityAssembleEA.
364template<int dim>
365void ElasticityAssembleEA_(const int i_block,
366 const int j_block,
367 const int nDofs,
368 const IntegrationRule &ir,
369 const CoefficientVector &lambda,
370 const CoefficientVector &mu,
371 const GeometricFactors &geom,
372 const DofToQuad &maps,
373 Vector &emat)
374{
375 using future::tensor;
377 using future::det;
378 using future::inv;
379
380 // Assuming all elements are the same
381 static constexpr int d = dim;
382 const int numPoints = ir.GetNPoints();
383 const int numEls = lambda.Size()/numPoints;
384 const auto lamDev = Reshape(lambda.Read(), numPoints, numEls);
385 const auto muDev = Reshape(mu.Read(), numPoints, numEls);
386 const auto J = Reshape(geom.J.Read(), numPoints, d, d, numEls);
387 const auto G = Reshape(maps.G.Read(), numPoints, d, nDofs);
388 auto ematDev = Reshape(emat.Write(), nDofs, nDofs, numEls);
389 const real_t *ipWeights = ir.GetWeights().Read();
390 mfem::forall_2D(numEls, nDofs, nDofs, [=] MFEM_HOST_DEVICE (int e)
391 {
392 MFEM_FOREACH_THREAD(JDof, y, nDofs)
393 {
394 MFEM_FOREACH_THREAD(IDof, x, nDofs)
395 {
396 real_t sum = 0;
397 for (int p = 0 ; p < numPoints; p++)
398 {
399 auto invJ = inv(make_tensor<d, d>(
400 [&](int i, int j) { return J(p, i, j, e); }));
401 const real_t w = ipWeights[p] /det(invJ);
402 for (int n = 0; n < d; n++)
403 {
404 for (int m = 0; m < d; m++)
405 {
406 // compute contraction of 4*sym(grad(u))sym(grad(v)) term.
407 real_t contraction = 0.;
408 for (int a = 0; a < d; a++)
409 {
410 for (int b = 0; b < d; b++)
411 {
412 contraction += ((a == i_block)*invJ(m,b) + (b==i_block)*invJ(m,
413 a))*((a == j_block)*invJ(n,
414 b) + (b==j_block)*invJ(n,a));
415 }
416 }
417 // lambda*div(u)*div(v) + 2*mu*sym(grad(u))*sym(grad(v))
418 // contraction = 4*sym(grad(u))sym(grad(v))
419 sum += w*(lamDev(p, e)*invJ(m,i_block)*invJ(n,j_block)
420 + 0.5*muDev(p, e)*contraction)*G(p,m,IDof)*G(p,n,JDof);
421 }
422 }
423 }
424 ematDev(IDof, JDof, e) = sum;
425 }
426 }
427 });
428}
429
430} // namespace internal
431
432} // namespace mfem
433
434#endif
int dim
Definition ex24.cpp:53
real_t mu
Definition ex25.cpp:140
real_t b
Definition lissajous.cpp:42
real_t a
Definition lissajous.cpp:41
mfem::real_t real_t
MFEM_HOST_DEVICE T det(const tensor< T, 1, 1 > &A)
Returns the determinant of a matrix.
Definition tensor.hpp:1406
MFEM_HOST_DEVICE constexpr auto make_tensor(lambda_type f) -> tensor< decltype(f())>
Creates a tensor of requested dimension by subsequent calls to a functor Can be thought of as analogo...
Definition tensor.hpp:327
MFEM_HOST_DEVICE tensor< T, 1, 1 > inv(const tensor< T, 1, 1 > &A)
Inverts a matrix.
Definition tensor.hpp:1707
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:925
real_t p(const Vector &x, real_t t)
Implementation of the tensor class.