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
Jacobian.h
Go to the documentation of this file.
1
11
12#ifndef PBAT_FEM_JACOBIAN_H
13#define PBAT_FEM_JACOBIAN_H
14
15#include "Concepts.h"
17
18#include <Eigen/Cholesky>
19#include <Eigen/LU>
20#include <Eigen/SVD>
21#include <fmt/core.h>
22#include <pbat/Aliases.h>
23#include <pbat/common/Eigen.h>
25#include <string>
26#include <tbb/parallel_for.h>
27
28namespace pbat::fem {
29
40template <
41 CElement TElement,
42 class TDerivedX,
43 class TDerivedx,
44 common::CFloatingPoint TScalar = typename TDerivedX::Scalar>
45[[maybe_unused]] auto
46Jacobian(Eigen::MatrixBase<TDerivedX> const& X, Eigen::MatrixBase<TDerivedx> const& x)
47 -> Eigen::Matrix<TScalar, TDerivedx::RowsAtCompileTime, TElement::kDims>
48{
49 static_assert(
50 TDerivedx::ColsAtCompileTime == TElement::kNodes,
51 "x must have the same number of columns as the number of nodes in the element.");
52 auto constexpr kDimsOut = TDerivedx::RowsAtCompileTime;
53 Eigen::Matrix<TScalar, kDimsOut, TElement::kDims> const J = x * TElement::GradN(X);
54 return J;
55}
56
67template <class TDerived>
68[[maybe_unused]] auto DeterminantOfJacobian(Eigen::MatrixBase<TDerived> const& J) ->
69 typename TDerived::Scalar
70{
71 using ScalarType = typename TDerived::Scalar;
72 bool const bIsSquare = J.rows() == J.cols();
73 auto const detJ = bIsSquare ? J.determinant() : J.jacobiSvd().singularValues().prod();
74 ScalarType constexpr eps = ScalarType(1e-10);
75 if (detJ <= eps)
76 {
77 throw std::invalid_argument("Inverted or singular jacobian");
78 }
79 return detJ;
80}
81
93template <CElement TElement, int QuadratureOrder, class TDerivedE, class TDerivedX>
94[[maybe_unused]] auto
95DeterminantOfJacobian(Eigen::DenseBase<TDerivedE> const& E, Eigen::MatrixBase<TDerivedX> const& X)
96 -> Eigen::Matrix<
97 typename TDerivedX::Scalar,
98 TElement::template QuadratureType<QuadratureOrder, typename TDerivedX::Scalar>::kPoints,
99 Eigen::Dynamic>
100{
101 PBAT_PROFILE_NAMED_SCOPE("pbat.fem.DeterminantOfJacobian");
102 using ScalarType = typename TDerivedX::Scalar;
103 using ElementType = TElement;
104 using AffineElementType = typename ElementType::AffineBaseType;
105 using QuadratureRuleType =
106 typename ElementType::template QuadratureType<QuadratureOrder, ScalarType>;
107 using MatrixType = Eigen::Matrix<ScalarType, QuadratureRuleType::kPoints, Eigen::Dynamic>;
108 if (X.rows() < ElementType::kDims)
109 {
110 throw std::invalid_argument(
111 fmt::format(
112 "Invalid number of dimensions, expected at least {}, but got {}",
113 ElementType::kDims,
114 X.rows()));
115 }
116 auto const Xg = common::ToEigen(QuadratureRuleType::points)
117 .reshaped(QuadratureRuleType::kDims + 1, QuadratureRuleType::kPoints)
118 .template bottomRows<ElementType::kDims>();
119 auto const numberOfElements = E.cols();
120 MatrixType detJe(QuadratureRuleType::kPoints, numberOfElements);
121 tbb::parallel_for(Index{0}, Index{numberOfElements}, [&](Index e) {
122 auto const nodes = E.col(e);
123 auto const vertices = nodes(ElementType::Vertices);
124 auto constexpr kRowsJ = TDerivedX::RowsAtCompileTime;
125 auto constexpr kColsJ = AffineElementType::kNodes;
126 Eigen::Matrix<ScalarType, kRowsJ, kColsJ> const Ve = X(Eigen::placeholders::all, vertices);
127 if constexpr (AffineElementType::bHasConstantJacobian)
128 {
129 ScalarType const detJ = DeterminantOfJacobian(
130 Jacobian<AffineElementType>(Xg.col(0) /*arbitrary eval.pt.*/, Ve));
131 detJe.col(e).setConstant(detJ);
132 }
133 else
134 {
135 for (auto g = 0; g < QuadratureRuleType::kPoints; ++g)
136 {
137 auto const detJ = DeterminantOfJacobian(Jacobian<AffineElementType>(Xg.col(g), Ve));
138 detJe(g, e) = detJ;
139 }
140 }
141 });
142 return detJe;
143}
144
154template <int QuadratureOrder, CMesh TMesh>
155[[maybe_unused]] auto DeterminantOfJacobian(TMesh const& mesh) -> Eigen::Matrix<
156 typename TMesh::ScalarType,
157 TMesh::ElementType::template QuadratureType<QuadratureOrder, typename TMesh::ScalarType>::
158 kPoints,
159 Eigen::Dynamic>
160{
162}
163
176template <CElement TElement, class TDerivedEg, class TDerivedX, class TDerivedXi>
177[[maybe_unused]] auto DeterminantOfJacobianAt(
178 Eigen::DenseBase<TDerivedEg> const& Eg,
179 Eigen::MatrixBase<TDerivedX> const& X,
180 Eigen::MatrixBase<TDerivedXi> const& Xi)
181 -> Eigen::Vector<typename TDerivedX::Scalar, Eigen::Dynamic>
182{
183 PBAT_PROFILE_NAMED_SCOPE("pbat.fem.DeterminantOfJacobian");
184 using ScalarType = typename TDerivedX::Scalar;
185 using ElementType = TElement;
186 using AffineElementType = typename ElementType::AffineBaseType;
187 Eigen::Vector<ScalarType, Eigen::Dynamic> detJe(Eg.cols());
188 tbb::parallel_for(Index{0}, Index{Eg.cols()}, [&](Index g) {
189 auto const nodes = Eg.col(g);
190 auto const vertices = nodes(ElementType::Vertices);
191 auto constexpr kRowsJ = TDerivedX::RowsAtCompileTime;
192 auto constexpr kColsJ = AffineElementType::kNodes;
193 Eigen::Matrix<ScalarType, kRowsJ, kColsJ> const Ve = X(Eigen::placeholders::all, vertices);
194 ScalarType const detJ = DeterminantOfJacobian(Jacobian<AffineElementType>(Xi.col(g), Ve));
195 detJe(g) = detJ;
196 });
197 return detJe;
198}
199
214template <CElement TElement, class TDerivedE, class TDerivedX, class TDerivedeg, class TDerivedXi>
215[[maybe_unused]] auto DeterminantOfJacobianAt(
216 Eigen::DenseBase<TDerivedE> const& E,
217 Eigen::MatrixBase<TDerivedX> const& X,
218 Eigen::MatrixBase<TDerivedeg> const& eg,
219 Eigen::MatrixBase<TDerivedXi> const& Xi)
220 -> Eigen::Vector<typename TDerivedX::Scalar, Eigen::Dynamic>
221{
223 E(Eigen::placeholders::all, eg.derived()),
224 X.derived(),
225 Xi.derived());
226}
227
255template <
256 CElement TElement,
257 class TDerivedX,
258 class TDerivedx,
259 common::CFloatingPoint TScalar = typename TDerivedX::Scalar>
261 Eigen::MatrixBase<TDerivedX> const& X,
262 Eigen::MatrixBase<TDerivedx> const& x,
263 int const maxIterations = 5,
264 TScalar const eps = 1e-10) -> Eigen::Vector<TScalar, TElement::kDims>
265{
266 using ScalarType = TScalar;
267 static_assert(
268 std::is_same_v<ScalarType, typename TDerivedX::Scalar>,
269 "X and x must have given scalar type");
270 static_assert(
271 std::is_same_v<ScalarType, typename TDerivedx::Scalar>,
272 "X and x must have given scalar type");
273 using ElementType = TElement;
274 assert(x.cols() == ElementType::kNodes);
275 auto constexpr kDims = ElementType::kDims;
276 auto constexpr kNodes = ElementType::kNodes;
277 auto constexpr kOutDims = Eigen::MatrixBase<TDerivedx>::RowsAtCompileTime;
278 // Initial guess is element's barycenter.
279 auto const coords = common::ToEigen(ElementType::Coordinates).reshaped(kDims, kNodes);
280 auto const vertexLagrangePositions =
281 (coords(Eigen::placeholders::all, ElementType::Vertices).template cast<ScalarType>() /
282 static_cast<ScalarType>(ElementType::kOrder));
283 Eigen::Vector<ScalarType, kDims> Xik = vertexLagrangePositions.rowwise().sum() /
284 static_cast<ScalarType>(ElementType::Vertices.size());
285
286 Eigen::Vector<ScalarType, kOutDims> rk = x * ElementType::N(Xik) - X;
287 Eigen::Matrix<ScalarType, kOutDims, kDims> J;
288 Eigen::LDLT<Eigen::Matrix<ScalarType, kDims, kDims>> LDLT;
289
290 // If Jacobian is constant, let's precompute it
291 if constexpr (ElementType::bHasConstantJacobian)
292 {
293 J = Jacobian<ElementType>(Xik, x);
294 LDLT.compute(J.transpose() * J);
295 }
296 // Do up to maxIterations Gauss Newton iterations
297 for (auto k = 0; k < maxIterations; ++k)
298 {
299 if (rk.template lpNorm<1>() <= eps)
300 break;
301
302 // Non-constant jacobians need to be updated
303 if constexpr (not ElementType::bHasConstantJacobian)
304 {
305 J = Jacobian<ElementType>(Xik, x);
306 LDLT.compute(J.transpose() * J);
307 }
308
309 Xik -= LDLT.solve(J.transpose() * rk);
310 rk = x * ElementType::N(Xik) - X;
311 }
312 return Xik;
313}
314
331template <
332 CElement TElement,
333 class TDerivedEg,
334 class TDerivedX,
335 class TDerivedXg,
336 common::CFloatingPoint TScalar = typename TDerivedXg::Scalar>
338 Eigen::DenseBase<TDerivedEg> const& Eg,
339 Eigen::MatrixBase<TDerivedX> const& X,
340 Eigen::MatrixBase<TDerivedXg> const& Xg,
341 int maxIterations = 5,
342 TScalar eps = 1e-10) -> Eigen::Matrix<TScalar, TElement::kDims, Eigen::Dynamic>
343{
344 PBAT_PROFILE_NAMED_SCOPE("pbat.fem.ReferencePositions");
345 static_assert(
346 std::is_same_v<TScalar, typename TDerivedX::Scalar>,
347 "X must have given scalar type");
348 static_assert(
349 std::is_same_v<TScalar, typename TDerivedXg::Scalar>,
350 "Xg must have given scalar type");
351 using ElementType = TElement;
352 using ScalarType = TScalar;
353 using MatrixType = Eigen::Matrix<ScalarType, ElementType::kDims, Eigen::Dynamic>;
354 MatrixType Xi(ElementType::kDims, Eg.cols());
355 tbb::parallel_for(Index{0}, Index{Eg.cols()}, [&](Index g) {
356 auto const nodes = Eg.col(g);
357 auto constexpr kOutDims = TDerivedX::RowsAtCompileTime;
358 auto constexpr kNodes = ElementType::kNodes;
359 Eigen::Matrix<ScalarType, kOutDims, kNodes> const Xe = X(Eigen::placeholders::all, nodes);
360 Eigen::Vector<ScalarType, kOutDims> const Xk = Xg.col(g);
361 Xi.col(g) = ReferencePosition<ElementType>(Xk, Xe, maxIterations, eps);
362 });
363 return Xi;
364}
365
381template <CElement TElement, class TDerivedE, class TDerivedX, class TDerivedEg, class TDerivedXg>
383 Eigen::DenseBase<TDerivedE> const& E,
384 Eigen::MatrixBase<TDerivedX> const& X,
385 Eigen::DenseBase<TDerivedEg> const& eg,
386 Eigen::MatrixBase<TDerivedXg> const& Xg,
387 int maxIterations = 5,
388 typename TDerivedXg::Scalar eps = 1e-10)
389 -> Eigen::Matrix<typename TDerivedXg::Scalar, TElement::kDims, Eigen::Dynamic>
390{
392 E(Eigen::placeholders::all, eg.derived()),
393 X.derived(),
394 Xg.derived(),
395 maxIterations,
396 eps);
397}
398
415template <CMesh TMesh, class TDerivedEg, class TDerivedXg>
417 TMesh const& mesh,
418 Eigen::DenseBase<TDerivedEg> const& eg,
419 Eigen::MatrixBase<TDerivedXg> const& Xg,
420 int maxIterations = 5,
421 typename TMesh::ScalarType eps = 1e-10)
422 -> Eigen::Matrix<typename TMesh::ScalarType, TMesh::ElementType::kDims, Eigen::Dynamic>
423{
425 mesh.E(Eigen::placeholders::all, eg.derived()),
426 mesh.X,
427 Xg.derived(),
428 maxIterations,
429 eps);
430}
431
432} // namespace pbat::fem
433
434#endif // PBAT_FEM_JACOBIAN_H
Concepts for common types.
Eigen adaptors for ranges.
Concept for floating-point types.
Definition Concepts.h:56
Reference finite element.
Definition Concepts.h:63
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 Jacobian(Eigen::MatrixBase< TDerivedX > const &X, Eigen::MatrixBase< TDerivedx > const &x) -> Eigen::Matrix< TScalar, TDerivedx::RowsAtCompileTime, TElement::kDims >
Given a map , where is in reference space coordinates, computes .
Definition Jacobian.h:46
auto ReferencePosition(Eigen::MatrixBase< TDerivedX > const &X, Eigen::MatrixBase< TDerivedx > const &x, int const maxIterations=5, TScalar const eps=1e-10) -> Eigen::Vector< TScalar, TElement::kDims >
Computes the reference position such that . This inverse problem is solved using Gauss-Newton iterat...
Definition Jacobian.h:260
auto DeterminantOfJacobian(Eigen::MatrixBase< TDerived > const &J) -> typename TDerived::Scalar
Computes the determinant of a (potentially non-square) Jacobian matrix.
Definition Jacobian.h:68
auto DeterminantOfJacobianAt(Eigen::DenseBase< TDerivedEg > const &Eg, Eigen::MatrixBase< TDerivedX > const &X, Eigen::MatrixBase< TDerivedXi > const &Xi) -> Eigen::Vector< typename TDerivedX::Scalar, Eigen::Dynamic >
Computes the determinant of the Jacobian matrix at element quadrature points.
Definition Jacobian.h:177
auto ReferencePositions(Eigen::DenseBase< TDerivedEg > const &Eg, Eigen::MatrixBase< TDerivedX > const &X, Eigen::MatrixBase< TDerivedXg > const &Xg, int maxIterations=5, TScalar eps=1e-10) -> Eigen::Matrix< TScalar, TElement::kDims, Eigen::Dynamic >
Computes reference positions such that for every point in .
Definition Jacobian.h:337
std::ptrdiff_t Index
Index type.
Definition Aliases.h:17
Profiling utilities for the Physics-Based Animation Toolkit (PBAT)