PhysicsBasedAnimationToolkit 0.0.10
Cross-platform C++20 library of algorithms and data structures commonly used in computer graphics research on physically-based simulation.
Loading...
Searching...
No Matches
ShapeFunctions.h
Go to the documentation of this file.
1
10
11#ifndef PBAT_FEM_SHAPEFUNCTIONS_H
12#define PBAT_FEM_SHAPEFUNCTIONS_H
13
14#include "Concepts.h"
15#include "Jacobian.h"
16#include "pbat/Aliases.h"
18#include "pbat/common/Eigen.h"
20
21#include <Eigen/LU>
22#include <Eigen/SVD>
23#include <exception>
24#include <fmt/core.h>
25#include <string>
26#include <tbb/parallel_for.h>
27
28namespace pbat::fem {
29
39template <CElement TElement, int QuadratureOrder, common::CFloatingPoint TScalar = Scalar>
40auto ElementShapeFunctions() -> Eigen::Matrix<
41 TScalar,
42 TElement::kNodes,
43 TElement::template QuadratureType<QuadratureOrder, TScalar>::kPoints>
44{
45 using QuadratureRuleType = typename TElement::template QuadratureType<QuadratureOrder, TScalar>;
46 using ElementType = TElement;
47 auto const Xg = common::ToEigen(QuadratureRuleType::points)
48 .reshaped(QuadratureRuleType::kDims + 1, QuadratureRuleType::kPoints)
49 .template bottomRows<QuadratureRuleType::kDims>();
50 Eigen::Matrix<TScalar, ElementType::kNodes, QuadratureRuleType::kPoints> Ng{};
51 for (auto g = 0; g < QuadratureRuleType::kPoints; ++g)
52 {
53 Ng.col(g) = ElementType::N(Xg.col(g));
54 }
55 return Ng;
56}
57
69template <CElement TElement, int QuadratureOrder, common::CFloatingPoint TScalar, class TDerivedE>
70auto ShapeFunctionMatrix(Eigen::DenseBase<TDerivedE> const& E, Eigen::Index nNodes)
71 -> Eigen::SparseMatrix<TScalar, Eigen::RowMajor, typename TDerivedE::Scalar>
72{
73 PBAT_PROFILE_NAMED_SCOPE("pbat.fem.ShapeFunctionMatrix");
74 using ElementType = TElement;
75 using ScalarType = TScalar;
76 using IndexType = typename TDerivedE::Scalar;
77 using IndexVectorType = Eigen::Vector<IndexType, Eigen::Dynamic>;
79 auto const nElements = E.cols();
80 auto const nQuadPointsPerElement = Ng.cols();
81 auto const m = nQuadPointsPerElement * nElements;
82 auto const n = nNodes;
83 auto constexpr kNodesPerElement = ElementType::kNodes;
84 Eigen::SparseMatrix<ScalarType, Eigen::RowMajor, IndexType> N(m, n);
85 N.reserve(IndexVectorType::Constant(m, kNodesPerElement));
86 for (auto e = 0; e < nElements; ++e)
87 {
88 auto const nodes = E.col(e);
89 for (auto g = 0; g < nQuadPointsPerElement; ++g)
90 {
91 auto const row = e * nQuadPointsPerElement + g;
92 for (auto i = 0; i < Ng.rows(); ++i)
93 {
94 auto const col = nodes(i);
95 N.insert(row, col) = Ng(i, g);
96 }
97 }
98 }
99 return N;
100}
101
110template <int QuadratureOrder, CMesh TMesh>
111auto ShapeFunctionMatrix(TMesh const& mesh)
112 -> Eigen::SparseMatrix<typename TMesh::ScalarType, Eigen::RowMajor, typename TMesh::IndexType>
113{
114 return ShapeFunctionMatrix<
115 typename TMesh::ElementType,
116 QuadratureOrder,
117 typename TMesh::ScalarType>(mesh.E, mesh.X.cols());
118}
119
132template <
133 CElement TElement,
134 class TDerivedE,
135 class TDerivedXi,
136 common::CIndex TIndex = typename TDerivedE::Scalar>
138 Eigen::DenseBase<TDerivedE> const& E,
139 TIndex nNodes,
140 Eigen::MatrixBase<TDerivedXi> const& Xi)
141 -> Eigen::SparseMatrix<typename TDerivedXi::Scalar, Eigen::RowMajor, TIndex>
142{
143 PBAT_PROFILE_NAMED_SCOPE("pbat.fem.ShapeFunctionMatrix");
144 using ElementType = TElement;
145 using ScalarType = typename TDerivedXi::Scalar;
146 using IndexType = TIndex;
147 using IndexVectorType = Eigen::Vector<IndexType, Eigen::Dynamic>;
148 using SparseMatrixType = Eigen::SparseMatrix<ScalarType, Eigen::RowMajor, IndexType>;
149 auto const numberOfQuadPoints = Xi.cols();
150 auto constexpr kNodesPerElement = ElementType::kNodes;
151 SparseMatrixType N(numberOfQuadPoints, nNodes);
152 N.reserve(IndexVectorType::Constant(numberOfQuadPoints, kNodesPerElement));
153 for (auto g = 0; g < numberOfQuadPoints; ++g)
154 {
155 auto const nodes = E.col(g);
156 auto Ng = ElementType::N(Xi.col(g));
157 for (auto i = 0; i < kNodesPerElement; ++i)
158 {
159 N.insert(g, nodes(i)) = Ng(i);
160 }
161 }
162 return N;
163}
164
176template <CMesh TMesh, class TDerivedEg, class TDerivedXi>
178 TMesh const& mesh,
179 Eigen::DenseBase<TDerivedEg> const& eg,
180 Eigen::MatrixBase<TDerivedXi> const& Xi)
181 -> Eigen::SparseMatrix<typename TMesh::ScalarType, Eigen::RowMajor, typename TMesh::IndexType>
182{
183 using ScalarType = typename TMesh::ScalarType;
184 using IndexType = typename TMesh::IndexType;
185 static_assert(
186 std::is_same_v<ScalarType, typename TDerivedXi::Scalar>,
187 "Scalar type of Xi must match mesh scalar type");
189 mesh.E(Eigen::placeholders::all, eg.derived()),
190 static_cast<IndexType>(mesh.X.cols()),
191 Xi.derived());
192}
193
202template <CElement TElement, class TDerivedXi>
203auto ShapeFunctionsAt(Eigen::DenseBase<TDerivedXi> const& Xi)
204 -> Eigen::Matrix<typename TDerivedXi::Scalar, TElement::kNodes, Eigen::Dynamic>
205{
206 PBAT_PROFILE_NAMED_SCOPE("pbat.fem.ShapeFunctionsAt");
207 using ElementType = TElement;
208 using ScalarType = typename TDerivedXi::Scalar;
209 using MatrixType = Eigen::Matrix<ScalarType, ElementType::kNodes, Eigen::Dynamic>;
210 if (Xi.rows() != ElementType::kDims)
211 {
212 std::string const what = fmt::format(
213 "Expected evaluation points in d={} dimensions, but got Xi.rows()={}",
214 ElementType::kDims,
215 Xi.rows());
216 throw std::invalid_argument(what);
217 }
218 MatrixType N(ElementType::kNodes, Xi.cols());
219 tbb::parallel_for(Index{0}, Index{Xi.cols()}, [&](Index i) {
220 N.col(i) = ElementType::N(Xi.col(i));
221 });
222 return N;
223}
224
266template <CElement TElement, class TDerivedXi, class TDerivedX>
268 Eigen::MatrixBase<TDerivedXi> const& Xi,
269 Eigen::MatrixBase<TDerivedX> const& X)
270 -> Eigen::Matrix<typename TDerivedX::Scalar, TElement::kNodes, TDerivedX::RowsAtCompileTime>
271{
272 auto constexpr kInputDims = TElement::kDims;
273 auto constexpr kOutputDims = TDerivedX::RowsAtCompileTime;
274 auto constexpr kNodes = TElement::kNodes;
275 using ScalarType = typename TDerivedX::Scalar;
276 static_assert(
277 std::is_same_v<ScalarType, typename TDerivedXi::Scalar>,
278 "Scalar type of Xi must match X's scalar type");
279 static_assert(kOutputDims != Eigen::Dynamic, "Rows of X must be fixed at compile time");
280
281 Eigen::Matrix<ScalarType, kNodes, kInputDims> const GN = TElement::GradN(Xi);
282 Eigen::Matrix<ScalarType, kNodes, kOutputDims> GP;
283 Eigen::Matrix<ScalarType, kOutputDims, kInputDims> J = X * GN;
284 // Compute \nabla_\xi N (X \nabla_\xi N)^{-1}
285 bool constexpr bIsJacobianSquare = kInputDims == kOutputDims;
286 if constexpr (bIsJacobianSquare)
287 {
288 GP.transpose() = J.transpose().fullPivLu().solve(GN.transpose());
289 }
290 else
291 {
292 GP.transpose() =
293 J.transpose().template jacobiSvd<Eigen::ComputeThinU | Eigen::ComputeThinV>().solve(
294 GN.transpose());
295 }
296 return GP;
297}
298
313template <
314 CElement TElement,
315 int Dims,
316 int QuadratureOrder,
317 class TDerivedE,
318 class TDerivedX,
319 common::CFloatingPoint TScalar = typename TDerivedX::Scalar,
320 common::CIndex TIndex = typename TDerivedE::Scalar>
322 Eigen::MatrixBase<TDerivedE> const& E,
323 Eigen::MatrixBase<TDerivedX> const& X)
324 -> Eigen::Matrix<TScalar, TElement::kNodes, Eigen::Dynamic>
325{
326 PBAT_PROFILE_NAMED_SCOPE("pbat.fem.ShapeFunctionGradients");
327 using ScalarType = TScalar;
328 using QuadratureRuleType =
329 typename TElement::template QuadratureType<QuadratureOrder, ScalarType>;
330 using ElementType = TElement;
331 using MatrixType = Eigen::Matrix<ScalarType, ElementType::kNodes, Eigen::Dynamic>;
332 auto const Xg = common::ToEigen(QuadratureRuleType::points)
333 .reshaped(QuadratureRuleType::kDims + 1, QuadratureRuleType::kPoints)
334 .template bottomRows<QuadratureRuleType::kDims>();
335 auto const numberOfElements = E.cols();
336 auto constexpr kNodesPerElement = ElementType::kNodes;
337 MatrixType GNe(kNodesPerElement, numberOfElements * Dims * QuadratureRuleType::kPoints);
338 tbb::parallel_for(Index{0}, Index{numberOfElements}, [&](Index e) {
339 auto const nodes = E.col(e);
340 for (auto g = 0; g < QuadratureRuleType::kPoints; ++g)
341 {
343 Xg.col(g),
344 X(Eigen::placeholders::all, nodes)
345 .template topLeftCorner<Dims, kNodesPerElement>());
346 auto constexpr kStride = Dims * QuadratureRuleType::kPoints;
347 GNe.template block<kNodesPerElement, Dims>(0, e * kStride + g * Dims) = GP;
348 }
349 });
350 return GNe;
351}
352
361template <int QuadratureOrder, CMesh TMesh>
362auto ShapeFunctionGradients(TMesh const& mesh)
363 -> Eigen::Matrix<typename TMesh::ScalarType, TMesh::ElementType::kNodes, Eigen::Dynamic>
364{
365 using MeshType = TMesh;
366 using ElementType = typename MeshType::ElementType;
368}
369
382template <CElement TElement, int Dims, class TDerivedEg, class TDerivedX, class TDerivedXi>
384 Eigen::DenseBase<TDerivedEg> const& Eg,
385 Eigen::MatrixBase<TDerivedX> const& X,
386 Eigen::MatrixBase<TDerivedXi> const& Xi)
387 -> Eigen::Matrix<typename TDerivedXi::Scalar, TElement::kNodes, Eigen::Dynamic>
388{
389 PBAT_PROFILE_NAMED_SCOPE("pbat.fem.ShapeFunctionGradientsAt");
390 using ElementType = TElement;
391 using ScalarType = typename TDerivedXi::Scalar;
392 using MatrixType = Eigen::Matrix<ScalarType, ElementType::kNodes, Eigen::Dynamic>;
393 if (Eg.cols() != Xi.cols())
394 {
395 std::string const what = fmt::format(
396 "Expected Eg.cols() == Xi.cols(), but got Eg.cols()={} and Xi.cols()={}",
397 Eg.cols(),
398 Xi.cols());
399 throw std::invalid_argument(what);
400 }
401 auto const numberOfEvaluationPoints = Xi.cols();
402 MatrixType GNe(ElementType::kNodes, numberOfEvaluationPoints * Dims);
403 tbb::parallel_for(Index{0}, Index{numberOfEvaluationPoints}, [&](Index g) {
404 auto const nodes = Eg.col(g);
406 Xi.col(g),
407 X(Eigen::placeholders::all, nodes).template topLeftCorner<Dims, ElementType::kNodes>());
408 GNe.template block<ElementType::kNodes, Dims>(0, g * Dims) = GP;
409 });
410 return GNe;
411}
412
424template <CMesh TMesh, class TDerivedEg, class TDerivedXi>
426 TMesh const& mesh,
427 Eigen::DenseBase<TDerivedEg> const& eg,
428 Eigen::MatrixBase<TDerivedXi> const& Xi)
429 -> Eigen::Matrix<typename TMesh::ScalarType, TMesh::ElementType::kNodes, Eigen::Dynamic>
430{
431 PBAT_PROFILE_NAMED_SCOPE("pbat.fem.ShapeFunctionGradientsAt");
432 using ScalarType = typename TMesh::ScalarType;
433 using MeshType = TMesh;
434 using ElementType = typename MeshType::ElementType;
435 static_assert(
436 std::is_same_v<ScalarType, typename TDerivedXi::Scalar>,
437 "Scalar type of Xi must match mesh scalar type");
439 mesh.E(Eigen::placeholders::all, eg.derived()),
440 mesh.X,
441 Xi.derived());
442}
443
444} // namespace pbat::fem
445
446#endif // PBAT_FEM_SHAPEFUNCTIONS_H
Functions to compute jacobians, their determinants, domain quadrature weights and mapping domain to r...
Concepts for common types.
Eigen adaptors for ranges.
Concept for floating-point types.
Definition Concepts.h:56
Concept for integral types.
Definition Concepts.h:45
auto ToEigen(R &&r) -> Eigen::Map< Eigen::Vector< std::ranges::range_value_t< R >, Eigen::Dynamic > const >
Map a range of scalars to an eigen vector of such scalars.
Definition Eigen.h:28
Finite Element Method (FEM)
Definition Concepts.h:19
auto ElementShapeFunctions() -> Eigen::Matrix< TScalar, TElement::kNodes, TElement::template QuadratureType< QuadratureOrder, TScalar >::kPoints >
Computes shape functions at element quadrature points for a polynomial quadrature rule of order Quadr...
Definition ShapeFunctions.h:40
auto ShapeFunctionMatrixAt(Eigen::DenseBase< TDerivedE > const &E, TIndex nNodes, Eigen::MatrixBase< TDerivedXi > const &Xi) -> Eigen::SparseMatrix< typename TDerivedXi::Scalar, Eigen::RowMajor, TIndex >
Constructs a shape function matrix for a given mesh, i.e. at the element quadrature points.
Definition ShapeFunctions.h:137
auto ShapeFunctionGradients(Eigen::MatrixBase< TDerivedE > const &E, Eigen::MatrixBase< TDerivedX > const &X) -> Eigen::Matrix< TScalar, TElement::kNodes, Eigen::Dynamic >
Computes nodal shape function gradients at each element quadrature points.
Definition ShapeFunctions.h:321
auto ShapeFunctionMatrix(Eigen::DenseBase< TDerivedE > const &E, Eigen::Index nNodes) -> Eigen::SparseMatrix< TScalar, Eigen::RowMajor, typename TDerivedE::Scalar >
Constructs a shape function matrix for a given mesh, i.e. at the element quadrature points.
Definition ShapeFunctions.h:70
auto ShapeFunctionsAt(Eigen::DenseBase< TDerivedXi > const &Xi) -> Eigen::Matrix< typename TDerivedXi::Scalar, TElement::kNodes, Eigen::Dynamic >
Compute shape functions at the given reference positions.
Definition ShapeFunctions.h:203
auto ShapeFunctionGradientsAt(Eigen::DenseBase< TDerivedEg > const &Eg, Eigen::MatrixBase< TDerivedX > const &X, Eigen::MatrixBase< TDerivedXi > const &Xi) -> Eigen::Matrix< typename TDerivedXi::Scalar, TElement::kNodes, Eigen::Dynamic >
Computes nodal shape function gradients at evaluation points Xi.
Definition ShapeFunctions.h:383
auto ElementShapeFunctionGradients(Eigen::MatrixBase< TDerivedXi > const &Xi, Eigen::MatrixBase< TDerivedX > const &X) -> Eigen::Matrix< typename TDerivedX::Scalar, TElement::kNodes, TDerivedX::RowsAtCompileTime >
Computes gradients of FEM basis functions. Given some FEM function.
Definition ShapeFunctions.h:267
std::ptrdiff_t Index
Index type.
Definition Aliases.h:17
Profiling utilities for the Physics-Based Animation Toolkit (PBAT)