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
67 // TODO: use existing PolyFEM code instead of hard coding these gmappings
68 VectorNd P1_2D_gmapping(
69 const Eigen::MatrixXd &nodes, const Eigen::Vector2d &uv)
70 {
71 assert(nodes.rows() == 3 && nodes.cols() == 2);
72 return (1 - uv[0] - uv[1]) * nodes.row(0) + uv[0] * nodes.row(1) + uv[1] * nodes.row(2);
73 }
74
75 VectorNd P1_3D_gmapping(
76 const Eigen::MatrixXd &nodes, const Eigen::Vector3d &uvw)
77 {
78 assert(nodes.rows() == 4 && nodes.cols() == 3);
79 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);
80 }
81 } // namespace
82
84 const bool is_volume,
85 const int size,
86 const int n_from_basis,
87 const std::vector<basis::ElementBases> &from_bases,
88 const std::vector<basis::ElementBases> &from_gbases,
89 const int n_to_basis,
90 const std::vector<basis::ElementBases> &to_bases,
91 const std::vector<basis::ElementBases> &to_gbases,
93 StiffnessMatrix &mass) const
94 {
95 const int buffer_size = std::min(long(1e8), long(std::max(n_from_basis, n_to_basis)) * size);
96 // logger().debug("buffer_size {}", buffer_size);
97
98 mass.resize(n_to_basis * size, n_from_basis * size);
99 mass.setZero();
100
101 // auto storage = create_thread_storage(LocalThreadMatStorage(buffer_size, mass.rows()));
102
104 if (is_volume)
106 else
108
109 // Use a AABB tree to find all intersecting elements then loop over only those pairs
110 std::vector<std::array<Eigen::Vector3d, 2>> boxes(from_bases.size());
111 for (int i = 0; i < from_bases.size(); i++)
112 {
113 const Eigen::MatrixXd from_nodes = from_bases[i].nodes();
114 boxes[i][0].setZero();
115 boxes[i][0].head(size) = from_nodes.colwise().minCoeff();
116 boxes[i][1].setZero();
117 boxes[i][1].head(size) = from_nodes.colwise().maxCoeff();
118 }
119
120 SimpleBVH::BVH bvh;
121 bvh.init(boxes);
122
123 // maybe_parallel_for(n_to_basis, [&](int start, int end, int thread_id) {
124 // LocalThreadMatStorage &local_storage = get_local_thread_storage(storage, thread_id);
125
126 std::vector<Eigen::Triplet<double>> triplets;
127
128 for (const ElementBases &to_element : to_bases)
129 {
130 const Eigen::MatrixXd to_nodes = to_element.nodes();
131
132 std::vector<unsigned int> candidates;
133 {
134 Eigen::Vector3d bbox_min = Eigen::Vector3d::Zero();
135 bbox_min.head(size) = to_nodes.colwise().minCoeff();
136 Eigen::Vector3d bbox_max = Eigen::Vector3d::Zero();
137 bbox_max.head(size) = to_nodes.colwise().maxCoeff();
138 bvh.intersect_box(bbox_min, bbox_max, candidates);
139 }
140
141 // for (const ElementBases &from_element : from_bases)
142 for (const unsigned int from_element_i : candidates)
143 {
144 const ElementBases &from_element = from_bases[from_element_i];
145 const Eigen::MatrixXd from_nodes = from_element.nodes();
146
147 // Compute the overlap between the two elements as a list of simplices.
148 const std::vector<Eigen::MatrixXd> overlap =
149 is_volume
150 ? TetrahedronClipping::clip(to_nodes, from_nodes)
151 : TriangleClipping::clip(to_nodes, from_nodes);
152
153 for (const Eigen::MatrixXd &simplex : overlap)
154 {
155 const double volume = abs(is_volume ? tetrahedron_volume(simplex) : triangle_area(simplex));
156 if (abs(volume) == 0.0)
157 continue;
158 assert(volume > 0);
159
160 for (int qi = 0; qi < quadrature.size(); qi++)
161 {
162 // NOTE: the 2/6 is neccesary here because the mass matrix assembly use the
163 // determinant of the Jacobian (i.e., area of the parallelogram/volume of the hexahedron)
164 const double w = (is_volume ? 6 : 2) * volume * quadrature.weights[qi];
165 const VectorNd q = quadrature.points.row(qi);
166
167 const VectorNd p = is_volume ? P1_3D_gmapping(simplex, q) : P1_2D_gmapping(simplex, q);
168
169 // NOTE: Row vector because evaluate_bases expects a rows of a matrix.
170 const RowVectorNd from_bc = barycentric_coordinates(p, from_nodes).tail(size).transpose();
171 const RowVectorNd to_bc = barycentric_coordinates(p, to_nodes).tail(size).transpose();
172
173 std::vector<AssemblyValues> from_phi, to_phi;
174 from_element.evaluate_bases(from_bc, from_phi);
175 to_element.evaluate_bases(to_bc, to_phi);
176
177#ifndef NDEBUG
178 Eigen::MatrixXd debug;
179 from_element.eval_geom_mapping(from_bc, debug);
180 assert((debug.transpose() - p).norm() < 1e-12);
181 to_element.eval_geom_mapping(to_bc, debug);
182 assert((debug.transpose() - p).norm() < 1e-12);
183#endif
184
185 for (int n = 0; n < size; ++n)
186 {
187 // local matrix is diagonal
188 const int m = n;
189 {
190 for (int to_local_i = 0; to_local_i < to_phi.size(); ++to_local_i)
191 {
192 const int to_global_i = to_element.bases[to_local_i].global()[0].index * size + m;
193 for (int from_local_i = 0; from_local_i < from_phi.size(); ++from_local_i)
194 {
195 const auto from_global_i = from_element.bases[from_local_i].global()[0].index * size + n;
196 triplets.emplace_back(
197 to_global_i, from_global_i,
198 w * from_phi[from_local_i].val(0) * to_phi[to_local_i].val(0));
199 }
200 }
201 }
202 }
203 }
204 }
205 }
206 }
207
208 mass.setFromTriplets(triplets.begin(), triplets.end());
209 mass.makeCompressed();
210 }
211
212 } // namespace assembler
213} // 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.
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.
str nodes
Definition p_bases.py:372
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.
double triangle_area(const Eigen::MatrixXd V)
Compute the signed area of a triangle defined by three points.
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:42
Eigen::Matrix< double, Eigen::Dynamic, 1, 0, 3, 1 > VectorNd
Definition Types.hpp:11
Eigen::Matrix< double, Eigen::Dynamic, 1, 0, MAX_QUAD_POINTS, 1 > QuadratureVector
Definition Types.hpp:17
Eigen::Matrix< double, 1, Eigen::Dynamic, Eigen::RowMajor, 1, 3 > RowVectorNd
Definition Types.hpp:13
Eigen::SparseMatrix< double, Eigen::ColMajor > StiffnessMatrix
Definition Types.hpp:22