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
Laplacian.h
1
15
16#ifndef PBAT_FEM_LAPLACIAN_H
17#define PBAT_FEM_LAPLACIAN_H
18
19#include "Concepts.h"
20#include "pbat/Aliases.h"
21#include "pbat/common/Eigen.h"
23
24#include <exception>
25#include <fmt/core.h>
26#include <tbb/parallel_for.h>
27
28namespace pbat::fem {
29
51template <
52 CElement TElement,
53 int Dims,
54 class TDerivedE,
55 class TDerivedeg,
56 class TDerivedwg,
57 class TDerivedGNeg,
58 class TDerivedIn,
59 class TDerivedOut>
60void GemmLaplacian(
61 Eigen::DenseBase<TDerivedE> const& E,
62 Eigen::Index nNodes,
63 Eigen::DenseBase<TDerivedeg> const& eg,
64 Eigen::DenseBase<TDerivedwg> const& wg,
65 Eigen::MatrixBase<TDerivedGNeg> const& GNeg,
66 int dims,
67 Eigen::MatrixBase<TDerivedIn> const& X,
68 Eigen::DenseBase<TDerivedOut>& Y);
69
87template <
88 CMesh TMesh,
89 class TDerivedeg,
90 class TDerivedwg,
91 class TDerivedGNeg,
92 class TDerivedIn,
93 class TDerivedOut>
94inline void GemmLaplacian(
95 TMesh const& mesh,
96 Eigen::DenseBase<TDerivedeg> const& eg,
97 Eigen::DenseBase<TDerivedwg> const& wg,
98 Eigen::MatrixBase<TDerivedGNeg> const& GNeg,
99 int dims,
100 Eigen::MatrixBase<TDerivedIn> const& X,
101 Eigen::DenseBase<TDerivedOut>& Y)
102{
104 mesh.E,
105 static_cast<typename TMesh::IndexType>(mesh.X.cols()),
106 eg.derived(),
107 wg.derived(),
108 GNeg.derived(),
109 dims,
110 X,
111 Y);
112}
113
132template <
133 CElement TElement,
134 int Dims,
135 Eigen::StorageOptions Options,
136 class TDerivedE,
137 class TDerivedeg,
138 class TDerivedwg,
139 class TDerivedGNeg>
140auto LaplacianMatrix(
141 Eigen::DenseBase<TDerivedE> const& E,
142 Eigen::Index nNodes,
143 Eigen::DenseBase<TDerivedeg> const& eg,
144 Eigen::DenseBase<TDerivedwg> const& wg,
145 Eigen::MatrixBase<TDerivedGNeg> const& GNeg,
146 int dims = 1)
147 -> Eigen::SparseMatrix<typename TDerivedGNeg::Scalar, Options, typename TDerivedE::Scalar>;
148
164template <
165 Eigen::StorageOptions Options,
166 CMesh TMesh,
167 class TDerivedeg,
168 class TDerivedwg,
169 class TDerivedGNeg>
171 TMesh const& mesh,
172 Eigen::DenseBase<TDerivedeg> const& eg,
173 Eigen::DenseBase<TDerivedwg> const& wg,
174 Eigen::MatrixBase<TDerivedGNeg> const& GNeg,
175 int dims = 1)
176 -> Eigen::SparseMatrix<typename TDerivedGNeg::Scalar, Options, typename TMesh::IndexType>
177{
179 mesh.E,
180 static_cast<typename TMesh::IndexType>(mesh.X.cols()),
181 eg.derived(),
182 wg.derived(),
183 GNeg.derived(),
184 dims);
185}
186
187template <
188 CElement TElement,
189 int Dims,
190 class TDerivedE,
191 class TDerivedeg,
192 class TDerivedwg,
193 class TDerivedGNeg,
194 class TDerivedIn,
195 class TDerivedOut>
196inline void GemmLaplacian(
197 Eigen::DenseBase<TDerivedE> const& E,
198 Eigen::Index nNodes,
199 Eigen::DenseBase<TDerivedeg> const& eg,
200 Eigen::DenseBase<TDerivedwg> const& wg,
201 Eigen::MatrixBase<TDerivedGNeg> const& GNeg,
202 int dims,
203 Eigen::MatrixBase<TDerivedIn> const& X,
204 Eigen::DenseBase<TDerivedOut>& Y)
205{
206 PBAT_PROFILE_NAMED_SCOPE("pbat.fem.GemmLaplacian");
207
208 // Check inputs
209 auto const numberOfDofs = dims * nNodes;
210 bool const bDimensionsMatch =
211 (X.cols() == Y.cols()) and (X.rows() == numberOfDofs) and (Y.rows() == numberOfDofs);
212 if (not bDimensionsMatch)
213 {
214 std::string const what = fmt::format(
215 "Expected input and output to have {} rows and same number of columns, but got "
216 "dimensions X,Y=({} x {}), ({} x {})",
217 numberOfDofs,
218 X.rows(),
219 X.cols(),
220 Y.rows(),
221 Y.cols());
222 throw std::invalid_argument(what);
223 }
224
225 if (dims < 1)
226 {
227 std::string const what = fmt::format("Expected dims >= 1, got {} instead", dims);
228 throw std::invalid_argument(what);
229 }
230
231 // Compute element Laplacians and apply
232 auto constexpr kNodesPerElement = TElement::kNodes;
233 auto constexpr kDims = Dims;
234 auto const nQuadPts = eg.size();
235 for (auto c = 0; c < X.cols(); ++c)
236 {
237 for (auto g = 0; g < nQuadPts; ++g)
238 {
239 auto const e = eg(g);
240 auto const nodes = E.col(e);
241 auto const w = wg(g);
242 // Get shape function gradients at this quadrature point
243 auto const GP = GNeg.template block<kNodesPerElement, kDims>(0, g * kDims);
244 // Apply to each dimension
245 auto ye = Y.col(c).reshaped(dims, nNodes)(Eigen::placeholders::all, nodes);
246 auto const xe = X.col(c).reshaped(dims, nNodes)(Eigen::placeholders::all, nodes);
247 ye -= ((w * xe) * GP) * GP.transpose(); // Laplacian is -w GP GP^T
248 }
249 }
250}
251
252template <
253 CElement TElement,
254 int Dims,
255 Eigen::StorageOptions Options,
256 class TDerivedE,
257 class TDerivedeg,
258 class TDerivedwg,
259 class TDerivedGNeg>
261 Eigen::DenseBase<TDerivedE> const& E,
262 Eigen::Index nNodes,
263 Eigen::DenseBase<TDerivedeg> const& eg,
264 Eigen::DenseBase<TDerivedwg> const& wg,
265 Eigen::MatrixBase<TDerivedGNeg> const& GNeg,
266 int dims)
267 -> Eigen::SparseMatrix<typename TDerivedGNeg::Scalar, Options, typename TDerivedE::Scalar>
268{
269 PBAT_PROFILE_NAMED_SCOPE("pbat.fem.LaplacianMatrix");
270 using ScalarType = typename TDerivedGNeg::Scalar;
271 using IndexType = typename TDerivedE::Scalar;
272 using SparseMatrixType = Eigen::SparseMatrix<ScalarType, Options, IndexType>;
273 using Triplet = Eigen::Triplet<ScalarType, IndexType>;
274
275 auto constexpr kNodesPerElement = TElement::kNodes;
276 auto constexpr kDims = Dims;
277
278 auto const nQuadPts = eg.size();
279 std::vector<Triplet> triplets{};
280 triplets.reserve(
281 static_cast<std::size_t>(kNodesPerElement * kNodesPerElement * nQuadPts * dims));
282
283 auto const numberOfDofs = dims * nNodes;
284 SparseMatrixType L(numberOfDofs, numberOfDofs);
285
286 for (auto g = 0; g < nQuadPts; ++g)
287 {
288 auto const e = eg(g);
289 auto const nodes = E.col(e);
290 auto const w = wg(g);
291
292 // Get shape function gradients at this quadrature point
293 auto const GP = GNeg.template block<kNodesPerElement, kDims>(0, g * kDims);
294
295 // Compute element Laplacian: -w * GP * GP^T
296 auto const Leg = -w * GP * GP.transpose();
297
298 // Add contributions for each dimension
299 for (auto i = 0; i < kNodesPerElement; ++i)
300 {
301 for (auto j = 0; j < kNodesPerElement; ++j)
302 {
303 for (auto d = 0; d < dims; ++d)
304 {
305 auto const ni = static_cast<IndexType>(dims * nodes(i) + d);
306 auto const nj = static_cast<IndexType>(dims * nodes(j) + d);
307 triplets.emplace_back(ni, nj, Leg(i, j));
308 }
309 }
310 }
311 }
312
313 L.setFromTriplets(triplets.begin(), triplets.end());
314 return L;
315}
316
317} // namespace pbat::fem
318
319#endif // PBAT_FEM_LAPLACIAN_H
Eigen adaptors for ranges.
Reference finite element.
Definition Concepts.h:63
Finite element mesh.
Definition Concepts.h:84
Finite Element Method (FEM)
Definition Concepts.h:19
auto LaplacianMatrix(Eigen::DenseBase< TDerivedE > const &E, Eigen::Index nNodes, Eigen::DenseBase< TDerivedeg > const &eg, Eigen::DenseBase< TDerivedwg > const &wg, Eigen::MatrixBase< TDerivedGNeg > const &GNeg, int dims=1) -> Eigen::SparseMatrix< typename TDerivedGNeg::Scalar, Options, typename TDerivedE::Scalar >
Construct the Laplacian operator's sparse matrix representation.
Definition Laplacian.h:260
void GemmLaplacian(Eigen::DenseBase< TDerivedE > const &E, Eigen::Index nNodes, Eigen::DenseBase< TDerivedeg > const &eg, Eigen::DenseBase< TDerivedwg > const &wg, Eigen::MatrixBase< TDerivedGNeg > const &GNeg, int dims, Eigen::MatrixBase< TDerivedIn > const &X, Eigen::DenseBase< TDerivedOut > &Y)
Compute Laplacian matrix-vector multiply .
Definition Laplacian.h:196
Profiling utilities for the Physics-Based Animation Toolkit (PBAT)