15#include <geogram/mesh/mesh_io.h>
16#include <geogram/mesh/mesh_geometry.h>
18#include <Eigen/Geometry>
20#include <igl/boundary_facets.h>
21#include <igl/oriented_facets.h>
25#include <unordered_set>
41 std::vector<MeshWithID> result;
42 result.reserve(ids.size());
43 for (
const int id : ids)
49 child->remove_elements(keep);
50 result.push_back({id, std::move(child)});
57 const int kept = std::count(keep.begin(), keep.end(),
true);
59 auto filter_vector = [&keep, kept](
auto &values) {
62 assert(values.size() == keep.size());
63 using Value =
typename std::decay_t<
decltype(values)>::value_type;
64 std::vector<Value> filtered;
65 filtered.reserve(kept);
66 for (
int i = 0; i < keep.size(); ++i)
68 filtered.push_back(values[i]);
69 values = std::move(filtered);
79 assert(
orders_.rows() == keep.size());
80 Eigen::MatrixXi filtered(kept,
orders_.cols());
81 for (
int i = 0, j = 0; i < keep.size(); ++i)
83 filtered.row(j++) =
orders_.row(i);
90 std::vector<int> sort_face(
const Eigen::RowVectorXi f)
92 std::vector<int> sorted_face(f.data(), f.data() + f.size());
93 std::sort(sorted_face.begin(), sorted_face.end());
103 template <
typename DerivedT,
typename DerivedF>
105 const Eigen::MatrixBase<DerivedT> &T,
106 Eigen::PlainObjectBase<DerivedF> &
F)
108 assert(T.cols() == 4);
109 assert(T.rows() >= 1);
111 Eigen::MatrixXi BF, OF;
112 igl::boundary_facets(T, BF);
113 igl::oriented_facets(T, OF);
114 assert((OF.rows() + BF.rows()) % 2 == 0);
115 const int num_faces = (OF.rows() + BF.rows()) / 2;
116 F.resize(num_faces, 3);
117 F.topRows(BF.rows()) = BF;
118 std::unordered_set<std::vector<int>,
HashVector> processed_faces;
119 for (
int fi = 0; fi < BF.rows(); fi++)
121 processed_faces.insert(sort_face(BF.row(fi)));
124 for (
int fi = 0; fi < OF.rows(); fi++)
126 std::vector<int> sorted_face = sort_face(OF.row(fi));
127 const auto iter = processed_faces.find(sorted_face);
128 if (iter == processed_faces.end())
130 F.row(processed_faces.size()) = OF.row(fi);
131 processed_faces.insert(sorted_face);
135 assert(
F.rows() == processed_faces.size());
139 std::unique_ptr<Mesh>
Mesh::create(
const int dim,
const bool non_conforming)
141 assert(dim == 2 || dim == 3);
142 if (dim == 2 && non_conforming)
143 return std::make_unique<NCMesh2D>();
144 else if (dim == 2 && !non_conforming)
145 return std::make_unique<CMesh2D>();
146 else if (dim == 3 && non_conforming)
147 return std::make_unique<NCMesh3D>();
148 else if (dim == 3 && !non_conforming)
149 return std::make_unique<CMesh3D>();
150 throw std::runtime_error(
"Invalid dimension");
153 std::unique_ptr<Mesh>
Mesh::create(GEO::Mesh &meshin,
const bool non_conforming)
158 std::unique_ptr<Mesh> mesh =
create(2, non_conforming);
159 if (mesh->load(meshin))
161 mesh->in_ordered_vertices_ = Eigen::VectorXi::LinSpaced(meshin.vertices.nb(), 0, meshin.vertices.nb() - 1);
162 assert(mesh->in_ordered_vertices_[0] == 0);
163 assert(mesh->in_ordered_vertices_[1] == 1);
164 assert(mesh->in_ordered_vertices_[2] == 2);
165 assert(mesh->in_ordered_vertices_[mesh->in_ordered_vertices_.size() - 1] == meshin.vertices.nb() - 1);
167 mesh->in_ordered_edges_.resize(meshin.edges.nb(), 2);
169 for (
int e = 0; e < (int)meshin.edges.nb(); ++e)
171 for (
int lv = 0; lv < 2; ++lv)
173 mesh->in_ordered_edges_(e, lv) = meshin.edges.vertex(e, lv);
175 assert(mesh->in_ordered_edges_(e, 0) != mesh->in_ordered_edges_(e, 1));
177 assert(mesh->in_ordered_edges_.size() > 0);
179 mesh->in_ordered_faces_.resize(0, 0);
186 std::unique_ptr<Mesh> mesh =
create(3, non_conforming);
187 meshin.cells.connect();
188 if (mesh->load(meshin))
190 mesh->in_ordered_vertices_ = Eigen::VectorXi::LinSpaced(meshin.vertices.nb(), 0, meshin.vertices.nb() - 1);
191 assert(mesh->in_ordered_vertices_[0] == 0);
192 assert(mesh->in_ordered_vertices_[1] == 1);
193 assert(mesh->in_ordered_vertices_[2] == 2);
194 assert(mesh->in_ordered_vertices_[mesh->in_ordered_vertices_.size() - 1] == meshin.vertices.nb() - 1);
196 mesh->in_ordered_edges_.resize(meshin.edges.nb(), 2);
198 for (
int e = 0; e < (int)meshin.edges.nb(); ++e)
200 for (
int lv = 0; lv < 2; ++lv)
202 mesh->in_ordered_edges_(e, lv) = meshin.edges.vertex(e, lv);
205 assert(mesh->in_ordered_edges_.size() > 0);
207 mesh->in_ordered_faces_.resize(meshin.facets.nb(), meshin.facets.nb_vertices(0));
209 for (
int f = 0; f < (int)meshin.edges.nb(); ++f)
211 assert(mesh->in_ordered_faces_.cols() == meshin.facets.nb_vertices(f));
213 for (
int lv = 0; lv < mesh->in_ordered_faces_.cols(); ++lv)
215 mesh->in_ordered_faces_(f, lv) = meshin.facets.vertex(f, lv);
218 assert(mesh->in_ordered_faces_.size() > 0);
224 logger().error(
"Failed to load mesh");
228 std::unique_ptr<Mesh>
Mesh::create(
const std::string &path,
const bool non_conforming)
230 if (!std::filesystem::exists(path))
232 logger().error(path.empty() ?
"No mesh provided!" :
"Mesh file does not exist: {}", path);
236 std::string lowername = path;
237 std::transform(lowername.begin(), lowername.end(), lowername.begin(), ::tolower);
241 std::unique_ptr<Mesh> mesh =
create(3, non_conforming);
242 if (mesh->load(path))
250 Eigen::MatrixXd vertices;
251 Eigen::MatrixXi cells;
252 std::vector<std::vector<int>> elements;
253 std::vector<std::vector<double>>
weights;
254 std::vector<int> body_ids;
255 std::vector<std::vector<int>> boundary_elements;
256 std::vector<int> boundary_ids;
258 if (!
MshReader::load(path, vertices, cells, elements,
weights, body_ids, boundary_elements, boundary_ids))
260 logger().error(
"Failed to load MSH mesh: {}", path);
264 const int dim = vertices.cols();
265 std::unique_ptr<Mesh> mesh =
create(vertices, cells, non_conforming);
268 if ((dim == 2 && cells.cols() == 3) || (dim == 3 && cells.cols() == 4))
270 mesh->attach_higher_order_nodes(vertices, elements);
271 mesh->set_cell_weights(
weights);
279 mesh->set_is_rational(
true);
284 mesh->set_body_ids(body_ids);
286 if (!boundary_ids.empty())
288 std::unordered_map<std::vector<int>, int,
HashVector> boundary_element_to_id;
289 for (
int i = 0; i < boundary_elements.size(); ++i)
291 std::sort(boundary_elements[i].begin(), boundary_elements[i].end());
292 const auto [it, inserted] = boundary_element_to_id.emplace(boundary_elements[i], boundary_ids[i]);
293 if (!inserted && it->second != boundary_ids[i])
294 logger().warn(
"Gmsh side has multiple physical tags; using tag {}.", it->second);
297 int matched_boundaries = 0;
298 mesh->compute_boundary_ids([&](
const size_t primitive_id,
const std::vector<int> &vertices,
const RowVectorNd &,
const bool) {
299 std::vector<int> sorted_vertices = vertices;
300 std::sort(sorted_vertices.begin(), sorted_vertices.end());
301 const auto it = boundary_element_to_id.find(sorted_vertices);
302 if (it == boundary_element_to_id.end())
303 return mesh->get_default_boundary_id(primitive_id);
304 ++matched_boundaries;
308 if (matched_boundaries != boundary_element_to_id.size())
310 "Unable to match {} of {} tagged Gmsh sides to mesh primitives.",
311 boundary_element_to_id.size() - matched_boundaries, boundary_element_to_id.size());
319 if (GEO::mesh_load(path, tmp))
321 return create(tmp, non_conforming);
324 logger().error(
"Failed to load mesh: {}", path);
329 const Eigen::MatrixXd &vertices,
const Eigen::MatrixXi &cells,
const bool non_conforming)
331 const int dim = vertices.cols();
333 std::unique_ptr<Mesh> mesh =
create(dim, non_conforming);
335 mesh->build_from_matrices(vertices, cells);
337 std::vector<int> tmp(cells.data(), cells.data() + cells.size());
338 std::sort(tmp.begin(), tmp.end());
339 tmp.erase(std::unique(tmp.begin(), tmp.end()), tmp.end());
341 mesh->in_ordered_vertices_ = Eigen::Map<Eigen::VectorXi, Eigen::Unaligned>(tmp.data(), tmp.size());
350 for (
int f = 0; f < cells.rows(); ++f)
352 for (
int lv = 0; lv < cells.cols(); ++lv)
354 const int v0 = cells(f, lv);
355 const int v1 = cells(f, (lv + 1) % cells.cols());
356 edges.emplace(std::pair<int, int>(std::min(v0, v1), std::max(v0, v1)));
359 mesh->in_ordered_edges_.resize(
edges.size(), 2);
361 for (
auto it =
edges.begin(); it !=
edges.end(); ++it)
363 mesh->in_ordered_edges_(index, 0) = it->first;
364 mesh->in_ordered_edges_(index, 1) = it->second;
368 assert(mesh->in_ordered_edges_.size() > 0);
370 mesh->in_ordered_faces_.resize(0, 0);
374 if (cells.cols() == 4)
376 get_faces(cells, mesh->in_ordered_faces_);
377 igl::edges(mesh->in_ordered_faces_, mesh->in_ordered_edges_);
390 for (
int e = 0; e <
n_edges(); ++e)
399 for (
int f = 0; f <
n_faces(); ++f)
408 for (
int c = 0; c <
n_cells(); ++c)
458 if (in_node_to_node.size() <= 0 ||
node_ids_.empty())
479 const auto p =
point(n);
480 node_ids_[n] = marker(n, p, is_boundary);
488 std::ifstream file(path);
492 while (std::getline(file, line))
494 std::istringstream iss(line);
524 std::vector<std::pair<int, int>> res;
527 for (
int e_id = 0; e_id <
n_edges(); ++e_id)
532 res.emplace_back(std::min(e0, e1), std::max(e0, e1));
540 std::vector<std::vector<int>> res(
n_faces());
542 for (
int f_id = 0; f_id <
n_faces(); ++f_id)
544 auto &tmp = res[f_id];
548 std::sort(tmp.begin(), tmp.end());
556 std::unordered_map<std::pair<int, int>, size_t,
HashPair> res;
559 for (
int e_id = 0; e_id <
n_edges(); ++e_id)
564 res[std::pair<int, int>(std::min(e0, e1), std::max(e0, e1))] = e_id;
572 std::unordered_map<std::vector<int>, size_t,
HashVector> res;
575 for (
int f_id = 0; f_id <
n_faces(); ++f_id)
581 std::sort(f.begin(), f.end());
601 for (
int i = 0; i <
node_ids_.size(); ++i)
664 Eigen::MatrixXi mesh_orders = mesh.
orders_;
665 if (mesh_orders.size() == 0)
667 assert(
orders_.cols() == mesh_orders.cols());
669 orders_.bottomRows(mesh_orders.rows()) = mesh_orders;
724 template <
typename T>
725 void transform_high_order_nodes(std::vector<T> &nodes,
const MatrixNd &A,
const VectorNd &b)
731 n.nodes = (n.nodes * A.transpose()).rowwise() + b.transpose();
static bool load(const std::string &path, Eigen::MatrixXd &vertices, Eigen::MatrixXi &cells, std::vector< std::vector< int > > &elements, std::vector< std::vector< double > > &weights, std::vector< int > &body_ids)
Abstract mesh class to capture 2d/3d conforming and non-conforming meshes.
int n_elements() const
utitlity to return the number of elements, cells or faces in 3d and 2d
virtual int n_vertices() const =0
number of vertices
bool is_polytope(const int el_id) const
checks if element is polygon compatible
Eigen::MatrixXi orders_
list of geometry orders, one per cell
std::unordered_map< std::pair< int, int >, size_t, polyfem::utils::HashPair > edges_to_ids() const
map from edge (pair of v id) to the id of the edge
std::vector< ElementType > elements_tag_
list of element types
virtual RowVectorNd edge_barycenter(const int e) const =0
edge barycenter
bool is_rational_
stores if the mesh is rational
bool has_boundary_ids() const
checks if surface selections are available
void cell_barycenters(Eigen::MatrixXd &barycenters) const
all cells barycenters
virtual RowVectorNd face_barycenter(const int f) const =0
face barycenter
int get_geometry_id(const int element) const
Get the geometry ID of an element. The default geometry is 0.
virtual void set_point(const int global_index, const RowVectorNd &p)=0
Set the point.
bool is_cube(const int el_id) const
checks if element is cube compatible
bool has_node_ids() const
checks if points selections are available
Eigen::MatrixXi in_ordered_faces_
Order of the input faces, TODO: change to std::vector of Eigen::Vector.
void compute_node_ids(const std::function< int(const size_t, const RowVectorNd &, bool)> &marker)
computes boundary selections based on a function
virtual int get_boundary_id(const int primitive) const
Get the boundary selection of an element (face in 3d, edge in 2d)
virtual bool is_boundary_vertex(const int vertex_global_id) const =0
is vertex boundary
bool is_simplex(const int el_id) const
checks if element is simplex
void face_barycenters(Eigen::MatrixXd &barycenters) const
all face barycenters
virtual bool has_body_ids() const
checks if volumes selections are available
bool is_spline_compatible(const int el_id) const
checks if element is spline compatible
virtual void load_boundary_ids(const std::string &path)
loads the boundary selections for a file
std::vector< int > geometry_ids_
list of geometry labels, one per top-dimensional element
std::vector< int > boundary_ids_
list of surface labels
bool is_prism(const int el_id) const
checks if element is a prism
void apply_affine_transformation(const MatrixNd &A, const VectorNd &b)
Apply an affine transformation to the vertex positions .
std::vector< int > node_ids_
list of node labels
std::unordered_map< std::vector< int >, size_t, polyfem::utils::HashVector > faces_to_ids() const
map from face (tuple of v id) to the id of the face
std::vector< CellNodes > cell_nodes_
high-order nodes associates to cells
std::vector< std::vector< double > > cell_weights_
weights associates to cells for rational polynomail meshes
virtual bool is_volume() const =0
checks if mesh is volume
std::vector< std::pair< int, int > > edges() const
list of sorted edges.
void update_nodes(const Eigen::VectorXi &in_node_to_node)
Update the node ids to reorder them.
virtual int edge_vertex(const int e_id, const int lv_id) const =0
id of the edge vertex
virtual std::unique_ptr< Mesh > copy() const =0
Create a copy of the mesh.
std::vector< MeshWithID > split() const
Split the mesh according to its per-element geometry IDs.
std::vector< int > body_ids_
list of volume labels
static std::unique_ptr< Mesh > create(const std::string &path, const bool non_conforming=false)
factory to build the proper mesh
virtual int get_default_boundary_id(const int primitive) const
Get the default boundary selection of an element (face in 3d, edge in 2d)
int dimension() const
utily for dimension
virtual int n_cells() const =0
number of cells
std::vector< FaceNodes > face_nodes_
high-order nodes associates to faces
virtual int n_faces() const =0
number of faces
std::vector< EdgeNodes > edge_nodes_
high-order nodes associates to edges
std::vector< std::vector< int > > faces() const
list of sorted faces.
int n_boundary_elements() const
utitlity to return the number of boundary elements, faces or edges in 3d and 2d
void filter_element_data(const std::vector< bool > &keep)
Eigen::MatrixXi in_ordered_edges_
Order of the input edges.
virtual void append(const Mesh &mesh)
appends a new mesh to the end of this
bool is_pyramid(const int el_id) const
checks if element is a pyramid
virtual RowVectorNd cell_barycenter(const int c) const =0
cell barycenter
virtual int n_edges() const =0
number of edges
void edge_barycenters(Eigen::MatrixXd &barycenters) const
all edges barycenters
virtual int n_face_vertices(const int f_id) const =0
number of vertices of a face
Eigen::VectorXi in_ordered_vertices_
Order of the input vertices.
bool has_geometry_ids() const
virtual int get_node_id(const int node_id) const
Get the boundary selection of a node.
virtual int face_vertex(const int f_id, const int lv_id) const =0
id of the face vertex
bool is_planar(const GEO::Mesh &M, const double tol=1e-5)
Determine if the given mesh is planar (2D or tiny z-range).
@ REGULAR_INTERIOR_CUBE
Triangle/tet element.
@ REGULAR_BOUNDARY_CUBE
Quad/Hex incident to more than 1 singular vertices (should not happen in 2D)
@ MULTI_SINGULAR_BOUNDARY_CUBE
Quad incident to exactly 1 singular vertex (in 2D); hex incident to exactly 1 singular interior edge,...
@ PRISM
Boundary polytope.
@ MULTI_SINGULAR_INTERIOR_CUBE
Quad/hex incident to exactly 1 singular vertex (in 2D) or edge (in 3D)
@ SIMPLE_SINGULAR_INTERIOR_CUBE
Regular quad/hex inside a 3^n patch.
@ INTERFACE_CUBE
Boundary hex that is not regular nor SimpleSingularBoundaryCube.
@ INTERIOR_POLYTOPE
Quad/hex that is at the interface with a polytope (if a cube has both external boundary and and inter...
@ SIMPLE_SINGULAR_BOUNDARY_CUBE
Boundary quad/hex, where all boundary vertices/edges are incident to at most 2 quads/hexes.
@ BOUNDARY_POLYTOPE
Interior polytope.
void generate_edges(GEO::Mesh &M)
assing edges to M
bool endswith(const std::string &str, const std::string &suffix)
void append_rows(DstMat &dst, const SrcMat &src)
spdlog::logger & logger()
Retrieves the current logger.
Eigen::Matrix< double, Eigen::Dynamic, 1, 0, 3, 1 > VectorNd
Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor, 3, 3 > MatrixNd
Eigen::Matrix< double, 1, Eigen::Dynamic, Eigen::RowMajor, 1, 3 > RowVectorNd