18#include <igl/per_vertex_normals.h>
25 using namespace assembler;
28 using namespace utils;
35 const int max_num_kernels = 300;
39 std::vector<int> compute_nonzero_bases_ids(
const Mesh3D &mesh,
const int c,
40 const std::vector<ElementBases> &bases,
41 const std::map<int, InterfaceData> &poly_face_to_data)
43 std::vector<int> local_to_global;
45 for (
int lf = 0; lf < mesh.n_cell_faces(c); ++lf)
47 auto index = mesh.get_index_from_element(c, lf, 0);
48 const int c2 = mesh.switch_element(index).element;
50 assert(poly_face_to_data.count(index.face) > 0);
51 const InterfaceData &bdata = poly_face_to_data.at(index.face);
52 const ElementBases &b = bases[
c2];
53 for (
int other_local_basis_id : bdata.local_indices)
55 for (
const auto &
x : b.bases[other_local_basis_id].global())
57 const int global_node_id =
x.index;
58 local_to_global.push_back(global_node_id);
63 std::sort(local_to_global.begin(), local_to_global.end());
64 auto it = std::unique(local_to_global.begin(), local_to_global.end());
65 local_to_global.resize(std::distance(local_to_global.begin(), it));
67 return local_to_global;
73 void compute_canonical_pattern(
int n_samples_per_edge, Eigen::MatrixXd &
V, Eigen::MatrixXi &
F)
85 constexpr int lv0 = 3;
89 const Mesh3D &mesh,
int c, Eigen::MatrixXd &
V, Eigen::MatrixXi &
F)
91 std::vector<std::array<int, 4>> quads(mesh.n_cell_faces(c));
92 typedef std::tuple<int, int, bool> QuadLocalEdge;
93 std::vector<std::array<QuadLocalEdge, 4>>
adj(mesh.n_cell_faces(c));
95 std::map<int, int> vertex_g2l;
96 std::map<int, int> face_g2l;
97 for (
int lf = 0; lf < mesh.n_cell_faces(c); ++lf)
99 face_g2l.emplace(mesh.get_index_from_element(c, lf, lv0).face, lf);
101 for (
int lf = 0; lf < mesh.n_cell_faces(c); ++lf)
103 auto index = mesh.get_index_from_element(c, lf, lv0);
104 assert(mesh.n_face_vertices(index.face) == 4);
105 for (
int lv = 0; lv < 4; ++lv)
107 if (!vertex_g2l.count(index.vertex))
109 vertex_g2l.emplace(index.vertex, num_vertices++);
111 quads[lf][lv] = vertex_g2l.at(index.vertex);
114 auto index2 = mesh.switch_face(index);
115 int lf2 = face_g2l.at(index2.face);
116 std::get<0>(
adj[lf][lv]) = lf2;
117 auto index3 = mesh.get_index_from_element(c, lf2, lv0);
118 for (
int lv2 = 0; lv2 < 4; ++lv2)
120 if (index3.edge == index2.edge)
122 std::get<1>(
adj[lf][lv]) = lv2;
123 if (index2.vertex != index3.vertex)
125 assert(mesh.switch_vertex(index3).vertex == index2.vertex);
126 std::get<2>(
adj[lf][lv]) =
true;
130 std::get<2>(
adj[lf][lv]) =
false;
133 index3 = mesh.next_around_face(index3);
136 index = mesh.next_around_face(index);
139 V.resize(num_vertices, 3);
140 for (
const auto &kv : vertex_g2l)
142 V.row(kv.second) = mesh.point(kv.first);
144 F.resize(quads.size(), 4);
148 F.row(f++) << q[0], q[1], q[2], q[3];
151 return [
adj](
int q,
int lv) {
158 void compute_offset_kernels(
const Eigen::MatrixXd &QV,
const Eigen::MatrixXi &QF,
159 int n_kernels_per_edge,
double eps, Eigen::MatrixXd &kernel_centers,
160 Eigen::MatrixXd &KV, Eigen::MatrixXi &KF,
163 Eigen::MatrixXd PV, KN;
166 compute_canonical_pattern(n_kernels_per_edge, PV, PF);
171 if (
true || KV.rows() < max_num_kernels)
173 igl::per_vertex_normals(KV, KF, KN);
184 kernel_centers += eps * volume * KN;
191 assert(kernel_centers.cols() == 3);
193 std::vector<Eigen::RowVector3d> remap;
194 std::vector<Eigen::RowVector3d> rejected;
195 for (
int v = 0; v < kernel_centers.rows(); ++v)
197 if (std::sqrt(D(v)) > 0.8 * eps * volume)
199 remap.push_back(kernel_centers.row(v));
206 kernel_centers.resize(remap.size(), 3);
207 for (
int v = 0; v < kernel_centers.rows(); ++v)
209 kernel_centers.row(v) = remap[v];
229 void sample_polyhedra(
230 const int element_index,
231 const int n_quadrature_vertices_per_edge,
232 const int n_kernels_per_edge,
233 int n_samples_per_edge,
234 const int quadrature_order,
235 const int mass_quadrature_order,
237 const std::map<int, InterfaceData> &poly_face_to_data,
238 const std::vector<ElementBases> &bases,
239 const std::vector<ElementBases> &gbases,
241 std::vector<int> &local_to_global,
242 Eigen::MatrixXd &collocation_points,
243 Eigen::MatrixXd &kernel_centers,
244 Eigen::MatrixXd &rhs,
245 Eigen::MatrixXd &triangulated_vertices,
246 Eigen::MatrixXi &triangulated_faces,
247 Quadrature &quadrature,
248 Quadrature &mass_quadrature,
250 Eigen::RowVector3d &translation)
253 local_to_global = compute_nonzero_bases_ids(mesh, element_index, bases, poly_face_to_data);
257 auto evalFunc = [&](
const Eigen::MatrixXd &uv, Eigen::MatrixXd &mapped,
int lf) {
258 const auto &u = uv.col(0).array();
259 const auto &v = uv.col(1).array();
260 auto index = mesh.get_index_from_element(element_index, lf, lv0);
261 index = mesh.switch_element(index);
265 Eigen::MatrixXd abcd;
267 Eigen::RowVector3d a = abcd.row(
indices(0));
268 Eigen::RowVector3d b = abcd.row(
indices(1));
269 Eigen::RowVector3d c = abcd.row(
indices(2));
270 Eigen::RowVector3d d = abcd.row(
indices(3));
271 mapped = ((1 - u) * (1 - v)).matrix() * a + (u * (1 - v)).matrix() * b + (u * v).matrix() * c + ((1 - u) * v).matrix() * d;
272 mapped = mapped.array().max(0.0).min(1.0);
273 assert(mapped.maxCoeff() >= 0.0);
274 assert(mapped.maxCoeff() <= 1.0);
276 auto evalFuncGeom = [&](
const Eigen::MatrixXd &uv, Eigen::MatrixXd &mapped,
int lf) {
277 Eigen::MatrixXd samples;
278 evalFunc(uv, samples, lf);
279 auto index = mesh.get_index_from_element(element_index, lf, lv0);
280 index = mesh.switch_element(index);
281 const ElementBases &gb = gbases[index.element];
282 gb.eval_geom_mapping(samples, mapped);
285 Eigen::MatrixXd QV, KV;
286 Eigen::MatrixXi QF, KF;
287 auto getAdjLocalEdge = compute_quad_mesh_from_cell(mesh, element_index, QV, QF);
290 compute_offset_kernels(QV, QF, n_kernels_per_edge, eps, kernel_centers, KV, KF,
291 evalFuncGeom, getAdjLocalEdge);
295 Eigen::MatrixXd PV, UV;
296 Eigen::MatrixXi PF, CF, UF;
297 Eigen::VectorXi uv_sources, uv_ranges;
298 compute_canonical_pattern(n_samples_per_edge, PV, PF);
301 instantiate_pattern(QV, QF, PV, PF, collocation_points, CF,
nullptr, evalFuncGeom, getAdjLocalEdge);
303 reorder_mesh(collocation_points, CF, uv_sources, uv_ranges);
305 assert(uv_ranges.size() == mesh.n_cell_faces(element_index) + 1);
308 compute_canonical_pattern(n_quadrature_vertices_per_edge, PV, PF);
310 nullptr, evalFuncGeom, getAdjLocalEdge);
340 Eigen::MatrixXd samples;
341 std::vector<AssemblyValues> basis_val;
342 rhs.resize(UV.rows(), local_to_global.size());
344 for (
int lf = 0; lf < mesh.n_cell_faces(element_index); ++lf)
346 auto index = mesh.get_index_from_element(element_index, lf, 0);
347 const int c2 = mesh.switch_element(index).element;
350 const InterfaceData &bdata = poly_face_to_data.at(index.face);
351 const ElementBases &b = bases[
c2];
353 samples = UV.middleRows(uv_ranges(lf), uv_ranges(lf + 1) - uv_ranges(lf));
354 b.evaluate_bases(samples, basis_val);
357 for (
int other_local_basis_id : bdata.local_indices)
361 for (
const auto &
x : b.bases[other_local_basis_id].global())
363 const int global_node_id =
x.index;
364 const double weight =
x.val;
366 const int poly_local_basis_id = std::distance(local_to_global.begin(),
367 std::find(local_to_global.begin(), local_to_global.end(), global_node_id));
368 rhs.block(uv_ranges(lf), poly_local_basis_id, basis_val[other_local_basis_id].
val.size(), 1) += basis_val[other_local_basis_id].val * weight;
374 Eigen::MatrixXd NV = triangulated_vertices;
378 translation.setZero();
381 quadrature_order, quadrature);
383 mass_quadrature_order, mass_quadrature);
390 triangulated_vertices = KV;
391 triangulated_faces = KF;
405 const std::vector<ElementBases> &bases,
406 const std::vector<ElementBases> &gbases,
407 Eigen::MatrixXd &basis_integrals)
411 basis_integrals.resize(n_bases, 9);
412 basis_integrals.setZero();
413 Eigen::MatrixXd rhs(n_bases, 9);
417 for (
int e = 0; e < n_elements; ++e)
429 const int n_local_bases = int(
vals.basis_values.size());
430 for (
int j = 0; j < n_local_bases; ++j)
433 const double integral_100 = (v.
grad_t_m.col(0).array() *
vals.det.array() *
vals.quadrature.weights.array()).sum();
434 const double integral_010 = (v.
grad_t_m.col(1).array() *
vals.det.array() *
vals.quadrature.weights.array()).sum();
435 const double integral_001 = (v.
grad_t_m.col(2).array() *
vals.det.array() *
vals.quadrature.weights.array()).sum();
437 const double integral_110 = ((
vals.val.col(1).array() * v.
grad_t_m.col(0).array() +
vals.val.col(0).array() * v.
grad_t_m.col(1).array()) *
vals.det.array() *
vals.quadrature.weights.array()).sum();
438 const double integral_011 = ((
vals.val.col(2).array() * v.
grad_t_m.col(1).array() +
vals.val.col(1).array() * v.
grad_t_m.col(2).array()) *
vals.det.array() *
vals.quadrature.weights.array()).sum();
439 const double integral_101 = ((
vals.val.col(0).array() * v.
grad_t_m.col(2).array() +
vals.val.col(2).array() * v.
grad_t_m.col(0).array()) *
vals.det.array() *
vals.quadrature.weights.array()).sum();
441 const double integral_200 = 2 * (
vals.val.col(0).array() * v.
grad_t_m.col(0).array() *
vals.det.array() *
vals.quadrature.weights.array()).sum();
442 const double integral_020 = 2 * (
vals.val.col(1).array() * v.
grad_t_m.col(1).array() *
vals.det.array() *
vals.quadrature.weights.array()).sum();
443 const double integral_002 = 2 * (
vals.val.col(2).array() * v.
grad_t_m.col(2).array() *
vals.det.array() *
vals.quadrature.weights.array()).sum();
445 const double area = (v.
val.array() *
vals.det.array() *
vals.quadrature.weights.array()).sum();
447 for (
size_t ii = 0; ii < v.
global.size(); ++ii)
449 basis_integrals(v.
global[ii].index, 0) += integral_100 * v.
global[ii].val;
450 basis_integrals(v.
global[ii].index, 1) += integral_010 * v.
global[ii].val;
451 basis_integrals(v.
global[ii].index, 2) += integral_001 * v.
global[ii].val;
453 basis_integrals(v.
global[ii].index, 3) += integral_110 * v.
global[ii].val;
454 basis_integrals(v.
global[ii].index, 4) += integral_011 * v.
global[ii].val;
455 basis_integrals(v.
global[ii].index, 5) += integral_101 * v.
global[ii].val;
457 basis_integrals(v.
global[ii].index, 6) += integral_200 * v.
global[ii].val;
458 basis_integrals(v.
global[ii].index, 7) += integral_020 * v.
global[ii].val;
459 basis_integrals(v.
global[ii].index, 8) += integral_002 * v.
global[ii].val;
461 rhs(v.
global[ii].index, 6) += -2.0 * area * v.
global[ii].val;
462 rhs(v.
global[ii].index, 7) += -2.0 * area * v.
global[ii].val;
463 rhs(v.
global[ii].index, 8) += -2.0 * area * v.
global[ii].val;
468 basis_integrals -= rhs;
577 const int nn_samples_per_edge,
580 const int quadrature_order,
581 const int mass_quadrature_order,
582 const int integral_constraints,
583 std::vector<ElementBases> &bases,
584 const std::vector<ElementBases> &gbases,
585 const std::map<int, InterfaceData> &poly_face_to_data,
586 std::map<
int, std::pair<Eigen::MatrixXd, Eigen::MatrixXi>> &mapped_boundary)
589 if (poly_face_to_data.empty())
593 int n_kernels_per_edge = 4;
594 int n_samples_per_edge = 3 * n_kernels_per_edge;
597 Eigen::MatrixXd basis_integrals;
613 std::vector<int> local_to_global;
614 Eigen::MatrixXd collocation_points, kernel_centers, triangulated_vertices;
615 Eigen::MatrixXi triangulated_faces;
621 Quadrature tmp_quadrature, tmp_mass_quadrature;
623 Eigen::RowVector3d translation;
624 sample_polyhedra(e, 2, n_kernels_per_edge, n_samples_per_edge,
627 mesh, poly_face_to_data, bases, gbases, eps, local_to_global,
628 collocation_points, kernel_centers, rhs, triangulated_vertices,
629 triangulated_faces, tmp_quadrature, tmp_mass_quadrature, scaling, translation);
668 Eigen::MatrixXd local_basis_integrals(rhs.cols(), basis_integrals.cols());
669 for (
long k = 0; k < rhs.cols(); ++k)
671 local_basis_integrals.row(k) = -basis_integrals.row(local_to_global[k]);
673 auto set_rbf = [&b](
auto rbf) {
674 b.
set_bases_func([rbf](
const Eigen::MatrixXd &uv, std::vector<AssemblyValues> &
val) {
676 rbf->bases_values(uv, tmp);
677 val.resize(tmp.cols());
678 assert(tmp.rows() == uv.rows());
680 for (
size_t i = 0; i < tmp.cols(); ++i)
682 val[i].val = tmp.col(i);
685 b.
set_grads_func([rbf](
const Eigen::MatrixXd &uv, std::vector<AssemblyValues> &
val) {
686 Eigen::MatrixXd tmpx, tmpy, tmpz;
688 rbf->bases_grads(0, uv, tmpx);
689 rbf->bases_grads(1, uv, tmpy);
690 rbf->bases_grads(2, uv, tmpz);
692 val.resize(tmpx.cols());
693 assert(tmpx.cols() == tmpy.cols());
694 assert(tmpx.cols() == tmpz.cols());
695 assert(tmpx.rows() == uv.rows());
696 for (
size_t i = 0; i < tmpx.cols(); ++i)
698 val[i].grad.resize(uv.rows(), uv.cols());
699 val[i].grad.col(0) = tmpx.col(i);
700 val[i].grad.col(1) = tmpy.col(i);
701 val[i].grad.col(2) = tmpz.col(i);
705 if (integral_constraints == 0)
707 set_rbf(std::make_shared<RBFWithLinear>(
708 kernel_centers, collocation_points, local_basis_integrals, tmp_quadrature, rhs,
false));
710 else if (integral_constraints == 1)
712 set_rbf(std::make_shared<RBFWithLinear>(
713 kernel_centers, collocation_points, local_basis_integrals, tmp_quadrature, rhs));
715 else if (integral_constraints == 2)
717 set_rbf(std::make_shared<RBFWithQuadratic>(
719 assembler, kernel_centers, collocation_points, local_basis_integrals, tmp_quadrature, rhs));
723 throw std::runtime_error(fmt::format(
"Unsupported constraint order: {:d}", integral_constraints));
727 const int n_poly_bases = int(local_to_global.size());
728 b.
bases.resize(n_poly_bases);
729 for (
int i = 0; i < n_poly_bases; ++i)
731 b.
bases[i].init(-2, local_to_global[i], i, Eigen::MatrixXd::Constant(1, 3, std::nan(
"")));
736 mapped_boundary[e].first = triangulated_vertices;
737 mapped_boundary[e].second = triangulated_faces;
vector< list< int > > adj
ElementAssemblyValues vals
virtual std::string name() const =0
static int quadrature_order(const std::string &assembler, const int basis_degree, const BasisType &b_type, const int dim)
utility for retrieving the needed quadrature order to precisely integrate the given form on the given...
stores per local bases evaluations
std::vector< basis::Local2Global > global
stores per element basis values at given quadrature points and geometric mapping
void compute(const int el_index, const bool is_volume, const Eigen::MatrixXd &pts, const basis::ElementBases &basis, const basis::ElementBases &gbasis)
computes the per element values at the local (ref el) points (pts) sets basis_values,...
assemble matrix based on the local assembler local assembler is eg Laplace, LinearElasticity etc
Stores the basis functions for a given element in a mesh (facet in 2d, cell in 3d).
void set_bases_func(EvalBasesFunc fun)
void set_quadrature(const QuadratureFunction &fun)
void set_grads_func(EvalBasesFunc fun)
std::vector< Basis > bases
one basis function per node in the element
bool has_parameterization
void set_mass_quadrature(const QuadratureFunction &fun)
static Eigen::VectorXi hex_face_local_nodes(const bool serendipity, const int q, const mesh::Mesh3D &mesh, mesh::Navigation3D::Index index)
static int build_bases(const assembler::LinearAssembler &assembler, const int n_samples_per_edge, const mesh::Mesh3D &mesh, const int n_bases, const int quadrature_order, const int mass_quadrature_order, const int integral_constraints, std::vector< ElementBases > &bases, const std::vector< ElementBases > &gbases, const std::map< int, InterfaceData > &poly_face_to_data, std::map< int, std::pair< Eigen::MatrixXd, Eigen::MatrixXi > > &mapped_boundary)
Build bases over the remaining polygons of a mesh.
static void compute_integral_constraints(const assembler::Assembler &assembler, const mesh::Mesh3D &mesh, const int n_bases, const std::vector< ElementBases > &bases, const std::vector< ElementBases > &gbases, Eigen::MatrixXd &basis_integrals)
bool is_volume() const override
checks if mesh is volume
int n_elements() const
utitlity to return the number of elements, cells or faces in 3d and 2d
bool is_polytope(const int el_id) const
checks if element is polygon compatible
static void get_quadrature(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F, const Eigen::RowVector3d &kernel, const int order, Quadrature &quadr)
Gets the quadrature points & weights for a polyhedron.
void q_nodes_3d(const int q, Eigen::MatrixXd &val)
double compute_epsilon(const Mesh2D &mesh, int e)
void sample_surface(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F, int num_samples, Eigen::MatrixXd &P, Eigen::MatrixXd *N=nullptr, int num_lloyd=10, int num_newton=10)
Samples points on a surface.
void orient_closed_surface(const Eigen::MatrixXd &V, Eigen::MatrixXi &F, bool positive=true)
Orient a triangulated surface to have positive volume.
double signed_volume(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F)
Compute the signed volume of a surface mesh.
void reorder_mesh(Eigen::MatrixXd &V, Eigen::MatrixXi &F, const Eigen::VectorXi &C, Eigen::VectorXi &R)
Reorder vertices of a mesh using color tags, so that vertices are ordered by increasing colors.
void signed_squared_distances(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F, const Eigen::MatrixXd &P, Eigen::VectorXd &D)
Computes the signed squared distance from a list of points to a triangle mesh.
bool instantiate_pattern(const Eigen::MatrixXd &IV, const Eigen::MatrixXi &IF, const Eigen::MatrixXd &PV, const Eigen::MatrixXi &PF, Eigen::MatrixXd &OV, Eigen::MatrixXi &OF, Eigen::VectorXi *SF=nullptr, EvalParametersFunc evalFunc=nullptr, GetAdjacentLocalEdge getAdjLocalEdge=nullptr)
std::function< void(const Eigen::MatrixXd &, Eigen::MatrixXd &, int)> EvalParametersFunc
std::function< std::tuple< int, int, bool >(int, int)> GetAdjacentLocalEdge
void regular_2d_grid(const int n, bool tri, Eigen::MatrixXd &V, Eigen::MatrixXi &F)
Generate a canonical triangle/quad subdivided from a regular grid.