PolyFEM
Loading...
Searching...
No Matches
MassMatrixAssembler.cpp
Go to the documentation of this file.
2
9
10#include <SimpleBVH/BVH.hpp>
11
12namespace polyfem
13{
14 using namespace basis;
15 using namespace quadrature;
16 using namespace utils;
17
18 namespace assembler
19 {
20 namespace
21 {
22 class LocalThreadMatStorage
23 {
24 public:
25 std::vector<Eigen::Triplet<double>> entries;
28 ElementAssemblyValues vals;
30 Quadrature quadrature;
31
32 LocalThreadMatStorage()
33 {
34 }
35
36 LocalThreadMatStorage(const int buffer_size, const int mat_size)
37 {
38 init(buffer_size, mat_size);
39 }
40
41 void init(const int buffer_size, const int mat_size)
42 {
43 entries.reserve(buffer_size);
44 tmp_mat.resize(mat_size, mat_size);
45 mass_mat.resize(mat_size, mat_size);
46 }
47
48 void condense()
49 {
50 if (entries.size() >= 1e8)
51 {
52 tmp_mat.setFromTriplets(entries.begin(), entries.end());
54 mass_mat.makeCompressed();
55
56 tmp_mat.setZero();
57 tmp_mat.data().squeeze();
58
59 mass_mat.makeCompressed();
60
61 entries.clear();
62 logger().debug("cleaning memory...");
63 }
64 }
65 };
66 } // namespace
67
69 const bool is_volume,
70 const int size,
71 const int n_basis,
72 const Density &density,
73 const std::vector<ElementBases> &bases,
74 const std::vector<ElementBases> &gbases,
76 StiffnessMatrix &mass) const
77 {
78 const int buffer_size = std::min(long(1e8), long(n_basis) * size);
79 // logger().debug("buffer_size {}", buffer_size);
80
81 mass.resize(n_basis * size, n_basis * size);
82 mass.setZero();
83
84 auto storage = create_thread_storage(LocalThreadMatStorage(buffer_size, mass.rows()));
85
86 const int n_bases = int(bases.size());
87
88 maybe_parallel_for(n_bases, [&](int start, int end, int thread_id) {
89 LocalThreadMatStorage &local_storage = get_local_thread_storage(storage, thread_id);
90
91 for (int e = start; e < end; ++e)
92 {
93 ElementAssemblyValues &vals = local_storage.vals;
94 bases[e].compute_mass_quadrature(vals.quadrature);
95 const auto &quadrature = vals.quadrature;
96 vals.compute(e, is_volume, quadrature.points, bases[e], gbases[e]);
97
98 assert(MAX_QUAD_POINTS == -1 || quadrature.weights.size() < MAX_QUAD_POINTS);
99 local_storage.da = vals.det.array() * quadrature.weights.array();
100 const int n_loc_bases = int(vals.basis_values.size());
101
102 for (int i = 0; i < n_loc_bases; ++i)
103 {
104 const auto &global_i = vals.basis_values[i].global;
105
106 for (int j = 0; j <= i; ++j)
107 {
108 const auto &global_j = vals.basis_values[j].global;
109
110 double tmp = 0; //(vals.basis_values[i].val.array() * vals.basis_values[j].val.array() * da.array()).sum();
111 for (int q = 0; q < local_storage.da.size(); ++q)
112 {
113 // TODO t
114 const double rho = density(vals.quadrature.points.row(q), vals.val.row(q), 0, vals.element_id);
115 tmp += rho * vals.basis_values[i].val(q) * vals.basis_values[j].val(q) * local_storage.da(q);
116 }
117
118 for (int n = 0; n < size; ++n)
119 {
120 // local matrix is diagonal
121 const int m = n;
122 // for(int m = 0; m < size; ++m)
123 {
124 const double local_value = tmp; // val(n*size+m);
125 for (size_t ii = 0; ii < global_i.size(); ++ii)
126 {
127 const auto gi = global_i[ii].index * size + m;
128 const auto wi = global_i[ii].val;
129
130 for (size_t jj = 0; jj < global_j.size(); ++jj)
131 {
132 const auto gj = global_j[jj].index * size + n;
133 const auto wj = global_j[jj].val;
134
135 local_storage.entries.emplace_back(gi, gj, local_value * wi * wj);
136 if (j < i)
137 {
138 local_storage.entries.emplace_back(gj, gi, local_value * wj * wi);
139 }
140
141 local_storage.condense();
142 }
143 }
144 }
145 }
146
147 // t1.stop();
148 // if (!vals.has_parameterization) { std::cout << "-- t1: " << t1.getElapsedTime() << std::endl; }
149 }
150 }
151
152 // timer.stop();
153 // if (!vals.has_parameterization) { std::cout << "-- Timer: " << timer.getElapsedTime() << std::endl; }
154 }
155 });
156
157 // Serially merge local storages
158 for (LocalThreadMatStorage &local_storage : storage)
159 {
160 mass += local_storage.mass_mat;
161 local_storage.tmp_mat.setFromTriplets(local_storage.entries.begin(), local_storage.entries.end());
162 mass += local_storage.tmp_mat;
163 }
164 mass.makeCompressed();
165 }
166
167 namespace
168 {
169 // TODO: use existing PolyFEM code instead of hard coding these gmappings
170
171 VectorNd P1_2D_gmapping(
172 const Eigen::MatrixXd &nodes, const Eigen::Vector2d &uv)
173 {
174 assert(nodes.rows() == 3 && nodes.cols() == 2);
175 return (1 - uv[0] - uv[1]) * nodes.row(0) + uv[0] * nodes.row(1) + uv[1] * nodes.row(2);
176 }
177
178 VectorNd P1_3D_gmapping(
179 const Eigen::MatrixXd &nodes, const Eigen::Vector3d &uvw)
180 {
181 assert(nodes.rows() == 4 && nodes.cols() == 3);
182 return (1 - uvw[0] - uvw[1] - uvw[2]) * nodes.row(0) + uvw[0] * nodes.row(1) + uvw[1] * nodes.row(2) + uvw[2] * nodes.row(3);
183 }
184 } // namespace
185
187 const bool is_volume,
188 const int size,
189 const int n_from_basis,
190 const std::vector<basis::ElementBases> &from_bases,
191 const std::vector<basis::ElementBases> &from_gbases,
192 const int n_to_basis,
193 const std::vector<basis::ElementBases> &to_bases,
194 const std::vector<basis::ElementBases> &to_gbases,
196 StiffnessMatrix &mass) const
197 {
198 const int buffer_size = std::min(long(1e8), long(std::max(n_from_basis, n_to_basis)) * size);
199 // logger().debug("buffer_size {}", buffer_size);
200
201 mass.resize(n_to_basis * size, n_from_basis * size);
202 mass.setZero();
203
204 // auto storage = create_thread_storage(LocalThreadMatStorage(buffer_size, mass.rows()));
205
207 if (is_volume)
209 else
211
212 // Use a AABB tree to find all intersecting elements then loop over only those pairs
213 std::vector<std::array<Eigen::Vector3d, 2>> boxes(from_bases.size());
214 for (int i = 0; i < from_bases.size(); i++)
215 {
216 const Eigen::MatrixXd from_nodes = from_bases[i].nodes();
217 boxes[i][0].setZero();
218 boxes[i][0].head(size) = from_nodes.colwise().minCoeff();
219 boxes[i][1].setZero();
220 boxes[i][1].head(size) = from_nodes.colwise().maxCoeff();
221 }
222
223 SimpleBVH::BVH bvh;
224 bvh.init(boxes);
225
226 // maybe_parallel_for(n_to_basis, [&](int start, int end, int thread_id) {
227 // LocalThreadMatStorage &local_storage = get_local_thread_storage(storage, thread_id);
228
229 std::vector<Eigen::Triplet<double>> triplets;
230
231 for (const ElementBases &to_element : to_bases)
232 {
233 const Eigen::MatrixXd to_nodes = to_element.nodes();
234
235 std::vector<unsigned int> candidates;
236 {
237 Eigen::Vector3d bbox_min = Eigen::Vector3d::Zero();
238 bbox_min.head(size) = to_nodes.colwise().minCoeff();
239 Eigen::Vector3d bbox_max = Eigen::Vector3d::Zero();
240 bbox_max.head(size) = to_nodes.colwise().maxCoeff();
241 bvh.intersect_box(bbox_min, bbox_max, candidates);
242 }
243
244 // for (const ElementBases &from_element : from_bases)
245 for (const unsigned int from_element_i : candidates)
246 {
247 const ElementBases &from_element = from_bases[from_element_i];
248 const Eigen::MatrixXd from_nodes = from_element.nodes();
249
250 // Compute the overlap between the two elements as a list of simplices.
251 const std::vector<Eigen::MatrixXd> overlap =
252 is_volume
253 ? TetrahedronClipping::clip(to_nodes, from_nodes)
254 : TriangleClipping::clip(to_nodes, from_nodes);
255
256 for (const Eigen::MatrixXd &simplex : overlap)
257 {
258 const double volume = abs(is_volume ? tetrahedron_volume(simplex) : triangle_area(simplex));
259 if (abs(volume) == 0.0)
260 continue;
261 assert(volume > 0);
262
263 for (int qi = 0; qi < quadrature.size(); qi++)
264 {
265 // NOTE: the 2/6 is neccesary here because the mass matrix assembly use the
266 // determinant of the Jacobian (i.e., area of the parallelogram/volume of the hexahedron)
267 const double w = (is_volume ? 6 : 2) * volume * quadrature.weights[qi];
268 const VectorNd q = quadrature.points.row(qi);
269
270 const VectorNd p = is_volume ? P1_3D_gmapping(simplex, q) : P1_2D_gmapping(simplex, q);
271
272 // NOTE: Row vector because evaluate_bases expects a rows of a matrix.
273 const RowVectorNd from_bc = barycentric_coordinates(p, from_nodes).tail(size).transpose();
274 const RowVectorNd to_bc = barycentric_coordinates(p, to_nodes).tail(size).transpose();
275
276 std::vector<AssemblyValues> from_phi, to_phi;
277 from_element.evaluate_bases(from_bc, from_phi);
278 to_element.evaluate_bases(to_bc, to_phi);
279
280#ifndef NDEBUG
281 Eigen::MatrixXd debug;
282 from_element.eval_geom_mapping(from_bc, debug);
283 assert((debug.transpose() - p).norm() < 1e-12);
284 to_element.eval_geom_mapping(to_bc, debug);
285 assert((debug.transpose() - p).norm() < 1e-12);
286#endif
287
288 for (int n = 0; n < size; ++n)
289 {
290 // local matrix is diagonal
291 const int m = n;
292 {
293 for (int to_local_i = 0; to_local_i < to_phi.size(); ++to_local_i)
294 {
295 const int to_global_i = to_element.bases[to_local_i].global()[0].index * size + m;
296 for (int from_local_i = 0; from_local_i < from_phi.size(); ++from_local_i)
297 {
298 const auto from_global_i = from_element.bases[from_local_i].global()[0].index * size + n;
299 triplets.emplace_back(
300 to_global_i, from_global_i,
301 w * from_phi[from_local_i].val(0) * to_phi[to_local_i].val(0));
302 }
303 }
304 }
305 }
306 }
307 }
308 }
309 }
310
311 mass.setFromTriplets(triplets.begin(), triplets.end());
312 mass.makeCompressed();
313 }
314
315 } // namespace assembler
316} // namespace polyfem
double val
Definition Assembler.cpp:86
QuadratureVector da
Definition Assembler.cpp:23
std::unique_ptr< MatrixCache > cache
Definition Assembler.cpp:21
ElementAssemblyValues vals
Definition Assembler.cpp:22
Quadrature quadrature
std::vector< Eigen::Triplet< double > > entries
StiffnessMatrix tmp_mat
StiffnessMatrix mass_mat
Caches basis evaluation and geometric mapping at every element.
stores per element basis values at given quadrature points and geometric mapping
void assemble(const bool is_volume, const int size, const int n_basis, const Density &density, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const AssemblyValsCache &cache, StiffnessMatrix &mass) const
Assembles the mass matrix.
void assemble_cross(const bool is_volume, const int size, const int n_from_basis, const std::vector< basis::ElementBases > &from_bases, const std::vector< basis::ElementBases > &from_gbases, const int n_to_basis, const std::vector< basis::ElementBases > &to_bases, const std::vector< basis::ElementBases > &to_gbases, const AssemblyValsCache &cache, StiffnessMatrix &mass) const
Assembles the cross mass matrix between to function spaces.
Stores the basis functions for a given element in a mesh (facet in 2d, cell in 3d).
void eval_geom_mapping(const Eigen::MatrixXd &samples, Eigen::MatrixXd &mapped) const
Map the sample positions in the parametric domain to the object domain (if the element has no paramet...
void evaluate_bases(const Eigen::MatrixXd &uv, std::vector< assembler::AssemblyValues > &basis_values) const
evaluate stored bases at given points on the reference element saves results to basis_values
Eigen::MatrixXd nodes() const
Assemble the global nodal positions of the bases.
std::vector< Basis > bases
one basis function per node in the element
void get_quadrature(const int order, Quadrature &quad)
void get_quadrature(const int order, Quadrature &quad)
static std::vector< Eigen::MatrixXd > clip(const Eigen::MatrixXd &subject_tet, const Eigen::MatrixXd &clipping_tet)
Clip a tetrahedron using tetrahedron.
static std::vector< Eigen::MatrixXd > clip(const Eigen::MatrixXd &subject_triangle, const Eigen::MatrixXd &clipping_triangle)
Clip a triangle using triangle.
auto & get_local_thread_storage(Storages &storage, int thread_id)
double tetrahedron_volume(const Eigen::Vector3d &a, const Eigen::Vector3d &b, const Eigen::Vector3d &c, const Eigen::Vector3d &d)
Compute the signed volume of a tetrahedron defined by four points.
Eigen::VectorXd barycentric_coordinates(const Eigen::VectorXd &p, const Eigen::MatrixXd &V)
Compute barycentric coordinates for point p with respect to a simplex.
auto create_thread_storage(const LocalStorage &initial_local_storage)
double triangle_area(const Eigen::MatrixXd V)
Compute the signed area of a triangle defined by three points.
void maybe_parallel_for(int size, const std::function< void(int, int, int)> &partial_for)
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:42
Eigen::Matrix< double, Eigen::Dynamic, 1, 0, 3, 1 > VectorNd
Definition Types.hpp:9
Eigen::Matrix< double, Eigen::Dynamic, 1, 0, MAX_QUAD_POINTS, 1 > QuadratureVector
Definition Types.hpp:15
Eigen::Matrix< double, 1, Eigen::Dynamic, Eigen::RowMajor, 1, 3 > RowVectorNd
Definition Types.hpp:11
Eigen::SparseMatrix< double, Eigen::ColMajor > StiffnessMatrix
Definition Types.hpp:20