13#include <igl/boundary_facets.h>
23 const std::vector<json> &obstacle_displacements,
24 const std::vector<json> &dirichlet_conditions,
25 const bool non_conforming)
const
32 geometry, obstacle_displacements, dirichlet_conditions,
33 result.
fem->dimension(), non_conforming);
39 const json &geometry_selection)
const
41 if (geometry_selection.is_object() && geometry_selection.contains(
"same_as_volume"))
43 if (!geometry_selection[
"same_as_volume"].get<bool>())
46 std::vector<int> geometry_ids(mesh.
n_elements());
57 Eigen::MatrixXd barycenters;
59 std::vector<int> geometry_ids(mesh.
n_elements(), 0);
63 for (
const auto &selection : selections)
65 if (selection->inside(e, vertices, barycenters.row(e)))
67 geometry_ids[e] = selection->id(e, vertices, barycenters.row(e));
77 const bool non_conforming)
const
82 if (j_mesh[
"extract"].get<std::string>() !=
"volume")
89 if (j_mesh[
"advanced"][
"normalize_mesh"])
95 mesh->bounding_box(bbox[0], bbox[1]);
97 const std::string unit = j_mesh[
"unit"];
98 double unit_scale = 1;
107 j_mesh[
"transformation"],
108 (bbox[1] - bbox[0]).cwiseAbs().transpose(),
110 mesh->apply_affine_transformation(A, b);
113 mesh->bounding_box(bbox[0], bbox[1]);
116 std::vector<std::shared_ptr<Selection>> surface_selections =
121 const int n_refs = j_mesh[
"n_refs"];
122 const double refinement_location = j_mesh[
"advanced"][
"refinement_location"];
133 if (mesh->has_boundary_ids() && surface_selections.empty())
134 log_and_throw_error(
"Unable to refine a mesh with stored surface selections; provide an explicit surface_selection to recompute them after refinement.");
137 assert(mesh->n_elements() > 0);
138 const int uniform_value = mesh->get_body_id(0);
139 for (
int i = 1; i < mesh->n_elements(); ++i)
140 if (mesh->get_body_id(i) != uniform_value)
141 log_and_throw_error(
"Unable to apply stored nonuniform volume_selection because n_refs={} > 0!", n_refs);
143 logger().info(
"Performing global h-refinement with {} refinements", n_refs);
144 mesh->refine(n_refs, refinement_location);
145 mesh->set_body_ids(std::vector<int>(mesh->n_elements(), uniform_value));
150 if (j_mesh[
"advanced"][
"min_component"].get<int>() != -1)
182 if (j_mesh[
"advanced"][
"force_linear_geometry"].get<bool>())
196 const std::vector<std::shared_ptr<Selection>> node_selections =
199 if (!node_selections.empty())
201 bool boundary_only =
true;
202 for (
const auto &selection : node_selections)
204 if (!selection->boundary_only())
206 boundary_only =
false;
211 mesh->compute_node_ids([&](
const size_t n_id,
const RowVectorNd &p,
bool is_boundary) {
212 if (boundary_only && !is_boundary)
215 const std::vector<int> tmp = {int(n_id)};
216 for (
const auto &selection : node_selections)
218 if (selection->boundary_only() && !is_boundary)
221 if (selection->inside(n_id, tmp, p))
222 return selection->id(n_id, tmp, p);
224 return std::numeric_limits<int>::max();
228 if (!j_mesh[
"curve_selection"].is_null())
233 if (!surface_selections.empty())
235 bool boundary_only =
true;
236 for (
const auto &selection : surface_selections)
238 if (!selection->boundary_only())
240 boundary_only =
false;
245 mesh->compute_boundary_ids([&](
const size_t p_id,
const std::vector<int> &vs,
const RowVectorNd &p,
bool is_boundary) {
246 if (boundary_only && !is_boundary)
249 for (
const auto &selection : surface_selections)
251 if (selection->boundary_only() && !is_boundary)
254 if (selection->inside(p_id, vs, p))
255 return selection->id(p_id, vs, p);
257 return std::numeric_limits<int>::max();
264 const json volume_selection = j_mesh[
"volume_selection"];
265 if (volume_selection.is_object()
266 && volume_selection.size() == 1
267 && volume_selection.contains(
"id_offset"))
269 const int id_offset = volume_selection[
"id_offset"].get<
int>();
272 const int n_body_ids = mesh->n_elements();
273 std::vector<int> body_ids(n_body_ids);
274 for (
int i = 0; i < n_body_ids; ++i)
275 body_ids[i] = mesh->get_body_id(i) + id_offset;
276 mesh->set_body_ids(body_ids);
282 std::vector<std::shared_ptr<Selection>> volume_selections =
286 if (mesh->has_body_ids())
287 volume_selections.push_back(std::make_shared<SpecifiedSelection>(mesh->get_body_ids()));
289 mesh->compute_body_ids([&](
const size_t cell_id,
const std::vector<int> &vs,
const RowVectorNd &p) ->
int {
290 for (
const auto &selection : volume_selections)
293 if (selection->inside(cell_id, vs, p))
294 return selection->id(cell_id, vs, p);
313 const json &geometry,
314 const bool non_conforming)
const
318 if (geometry.empty())
325 std::unique_ptr<Mesh> mesh =
nullptr;
327 for (
const json &geometry : geometries)
329 if (!geometry[
"enabled"].get<bool>() || geometry[
"is_obstacle"].get<
bool>())
332 if (geometry[
"type"] !=
"mesh" && geometry[
"type"] !=
"mesh_array")
335 const std::unique_ptr<Mesh> tmp_mesh =
load_fem_entry(geometry, non_conforming);
338 mesh = tmp_mesh->copy();
340 mesh->append(tmp_mesh);
342 if (geometry[
"type"] ==
"mesh_array")
345 tmp_mesh->bounding_box(bbox[0], bbox[1]);
347 const long dim = tmp_mesh->dimension();
348 const bool is_offset_relative = geometry[
"array"][
"relative"];
349 const double offset = geometry[
"array"][
"offset"];
350 const VectorNd dimensions = (bbox[1] - bbox[0]);
351 const VectorNi size = geometry[
"array"][
"size"];
353 for (
int i = 0; i < size[0]; ++i)
355 for (
int j = 0; j < size[1]; ++j)
357 for (
int k = 0; k < (size.size() > 2 ? size[2] : 1); ++k)
359 if (i == 0 && j == 0 && k == 0)
362 RowVectorNd translation = offset * Eigen::RowVector3d(i, j, k).head(dim);
363 if (is_offset_relative)
364 translation.array() *= dimensions.array();
366 const std::unique_ptr<Mesh> copy_mesh = tmp_mesh->copy();
367 copy_mesh->apply_affine_transformation(MatrixNd::Identity(dim, dim), translation);
368 mesh->append(copy_mesh);
391 auto &codim_vertices = result.
points;
392 auto &codim_edges = result.
edges;
394 auto &tets = result.
tets;
396 const int prev_dim = vertices.cols();
397 vertices.conservativeResize(vertices.rows(), dim);
399 vertices.rightCols(dim - prev_dim).setZero();
404 const std::string unit = j_mesh[
"unit"];
405 double unit_scale = 1;
409 const VectorNd mesh_dimensions = (vertices.colwise().maxCoeff() - vertices.colwise().minCoeff()).cwiseAbs();
413 vertices = vertices * A.transpose();
414 vertices.rowwise() += b.transpose();
419 if (dim == 2 && tets.rows() == 0 &&
faces.rows() > 0 &&
faces.cols() == 3)
422 std::string extract = j_mesh[
"extract"];
424 if (extract ==
"volume")
427 if (extract ==
"points")
430 codim_edges.resize(0, 0);
432 codim_vertices.resize(vertices.rows());
433 for (
int i = 0; i < codim_vertices.size(); ++i)
434 codim_vertices[i] = i;
436 else if (extract ==
"edges" &&
faces.size() != 0)
439 Eigen::MatrixXi edges;
440 igl::edges(
faces, edges);
442 codim_edges.conservativeResize(codim_edges.rows() + edges.rows(), 2);
443 codim_edges.bottomRows(edges.rows()) = edges;
445 else if (extract ==
"surface" && dim == 2 &&
faces.size() != 0)
448 Eigen::MatrixXi boundary_edges;
449 igl::boundary_facets(
faces, boundary_edges);
450 codim_edges.conservativeResize(codim_edges.rows() + boundary_edges.rows(), 2);
451 codim_edges.bottomRows(boundary_edges.rows()) = boundary_edges;
456 else if (extract ==
"volume")
462 if (j_mesh[
"n_refs"].get<int>() != 0)
464 if (
faces.size() != 0)
467 const int n_refs = j_mesh[
"n_refs"];
468 const double refinement_location = j_mesh[
"advanced"][
"refinement_location"];
469 for (
int i = 0; i < n_refs; i++)
471 const size_t n_vertices = vertices.rows();
472 const size_t n_edges = codim_edges.rows();
473 vertices.conservativeResize(n_vertices + n_edges, vertices.cols());
474 codim_edges.conservativeResize(2 * n_edges, codim_edges.cols());
475 for (
size_t ei = 0; ei < n_edges; ei++)
477 const int v0i = codim_edges(ei, 0);
478 const int v1i = codim_edges(ei, 1);
479 const int v2i = n_vertices + ei;
480 vertices.row(v2i) = (vertices.row(v1i) - vertices.row(v0i)) * refinement_location + vertices.row(v0i);
481 codim_edges.row(ei) << v0i, v2i;
482 codim_edges.row(n_edges + ei) << v2i, v1i;
492 const json &geometry,
493 const std::vector<json> &displacements,
494 const std::vector<json> &dirichlets,
500 if (geometry.empty())
505 for (
const json &geometry : geometries)
508 if (!geometry[
"is_obstacle"].get<bool>())
511 if (!geometry[
"enabled"].get<bool>())
514 if (geometry[
"type"] ==
"mesh" || geometry[
"type"] ==
"mesh_array")
517 Eigen::MatrixXd &vertices = loaded.
vertices;
518 Eigen::VectorXi &codim_vertices = loaded.
points;
519 Eigen::MatrixXi &codim_edges = loaded.
edges;
521 Eigen::MatrixXi &tets = loaded.
tets;
523 if (geometry[
"type"] ==
"mesh_array")
525 const Selection::BBox bbox{{vertices.colwise().minCoeff(), vertices.colwise().maxCoeff()}};
527 const bool is_offset_relative = geometry[
"array"][
"relative"];
528 const double offset = geometry[
"array"][
"offset"];
529 const VectorNd dimensions = (bbox[1] - bbox[0]);
530 const VectorNi size = geometry[
"array"][
"size"];
532 const int N = size.head(dim).prod();
533 const int nV = vertices.rows(), nCV = codim_vertices.rows(), nCE = codim_edges.rows(), nF =
faces.rows(), nT = tets.rows();
535 vertices.conservativeResize(N * nV, Eigen::NoChange);
536 codim_vertices.conservativeResize(N * nCV, Eigen::NoChange);
537 codim_edges.conservativeResize(N * nCE, Eigen::NoChange);
538 faces.conservativeResize(N * nF, Eigen::NoChange);
539 tets.conservativeResize(N * nT, tets.cols());
541 for (
int i = 0; i < size[0]; ++i)
543 for (
int j = 0; j < size[1]; ++j)
545 for (
int k = 0; k < (size.size() > 2 ? size[2] : 1); ++k)
547 RowVectorNd translation = offset * Eigen::RowVector3d(i, j, k).head(vertices.cols());
548 if (is_offset_relative)
549 translation.array() *= dimensions.array();
551 int n = i * size[1] + j;
557 vertices.middleRows(n * nV, nV) = vertices.topRows(nV).rowwise() + translation;
559 codim_vertices.segment(n * nV, nV) = codim_vertices.head(nV).array() + n * nV;
561 codim_edges.middleRows(n * nCE, nCE) = codim_edges.topRows(nCE).array() + n * nV;
563 faces.middleRows(n * nF, nF) =
faces.topRows(nF).array() + n * nV;
565 tets.middleRows(n * nT, nT) = tets.topRows(nT).array() + n * nV;
571 json displacement =
"{\"value\":[0, 0, 0]}"_json;
574 if (!geometry[
"surface_selection"].is_number())
577 const int id = geometry[
"surface_selection"];
578 for (
const json &disp : dirichlets)
580 if ((disp[
"id"].is_string() && disp[
"id"].get<std::string>() ==
"all")
581 || (disp[
"id"].is_number_integer() && disp[
"id"].get<
int>() ==
id))
586 else if (disp[
"id"].is_array())
588 for (
const json &disp_id : disp[
"id"])
590 assert(disp_id.is_number_integer());
591 if (disp_id.get<
int>() ==
id)
599 for (
const json &disp : displacements)
601 if ((disp[
"id"].is_string() && disp[
"id"].get<std::string>() ==
"all")
602 || (disp[
"id"].is_number_integer() && disp[
"id"].get<
int>() ==
id))
607 else if (disp[
"id"].is_array())
609 for (
const json &disp_id : disp[
"id"])
611 assert(disp_id.is_number_integer());
612 if (disp_id.get<
int>() ==
id)
623 vertices, codim_vertices, codim_edges,
faces, displacement,
resources_, tets);
625 else if (geometry[
"type"] ==
"plane")
627 obstacle.
append_plane(geometry[
"point"], geometry[
"normal"]);
629 else if (geometry[
"type"] ==
"ground")
631 VectorNd gravity = VectorNd::Zero(dim);
633 const double height = geometry[
"height"];
634 assert(gravity.norm() != 0);
635 const VectorNd normal = -gravity.normalized();
639 else if (geometry[
"type"] ==
"mesh_sequence")
641 std::vector<std::string> mesh_files;
642 if (geometry[
"mesh_sequence"].is_array())
644 mesh_files = geometry[
"mesh_sequence"].get<std::vector<std::string>>();
648 assert(geometry[
"mesh_sequence"].is_string());
649 const std::string sequence = geometry[
"mesh_sequence"].get<std::string>();
652 std::sort(mesh_files.begin(), mesh_files.end(), [](
const std::string &p1,
const std::string &p2) {
653 return strnatcmp(p1.c_str(), p2.c_str()) < 0;
657 std::vector<Eigen::MatrixXd> vertices(mesh_files.size());
658 Eigen::VectorXi codim_vertices;
659 Eigen::MatrixXi codim_edges;
660 Eigen::MatrixXi
faces;
661 Eigen::MatrixXi tets;
663 for (
int i = 0; i < mesh_files.size(); ++i)
665 json jmesh = geometry;
666 jmesh[
"mesh"] = mesh_files[i];
670 vertices[i] = std::move(loaded.
vertices);
671 Eigen::VectorXi &tmp_codim_vertices = loaded.
points;
672 Eigen::MatrixXi &tmp_codim_edges = loaded.
edges;
673 Eigen::MatrixXi &tmp_faces = loaded.
faces;
674 Eigen::MatrixXi &tmp_tets = loaded.
tets;
677 codim_vertices = tmp_codim_vertices;
678 codim_edges = tmp_codim_edges;
684 assert((codim_vertices.array() == tmp_codim_vertices.array()).all());
685 assert((codim_edges.array() == tmp_codim_edges.array()).all());
686 assert((
faces.array() == tmp_faces.array()).all());
687 assert((tets.array() == tmp_tets.array()).all());
692 vertices, codim_vertices, codim_edges,
faces, geometry[
"fps"], tets);
707 const double unit_scale,
708 const json &transform,
713 const int dim = mesh_dimensions.size();
720 if (transform[
"dimensions"].is_array())
723 (mesh_dimensions.array() == 0).select(1, mesh_dimensions);
725 scale = transform[
"dimensions"];
726 const int scale_size = scale.size();
727 scale.conservativeResize(dim);
728 if (scale_size < dim)
729 scale.tail(dim - scale_size).setZero();
731 scale.array() /= modified_dimensions.array();
733 else if (transform[
"scale"].is_number())
735 scale.setConstant(dim, transform[
"scale"].get<double>());
739 assert(transform[
"scale"].is_array());
740 scale = transform[
"scale"];
741 const int scale_size = scale.size();
742 scale.conservativeResize(dim);
743 if (scale_size < dim)
744 scale.tail(dim - scale_size).setZero();
750 A = (unit_scale * scale).asDiagonal();
758 MatrixNd R = MatrixNd::Identity(dim, dim);
759 if (!transform[
"rotation"].is_null())
763 if (transform[
"rotation"].is_number())
764 R = Eigen::Rotation2Dd(
deg2rad(transform[
"rotation"].get<double>()))
766 else if (!transform[
"rotation"].is_array() || !transform[
"rotation"].empty())
781 b = transform[
"translation"];
782 const int translation_size = b.size();
783 b.conservativeResize(dim);
784 if (translation_size < dim)
785 b.tail(dim - translation_size).setZero();
std::vector< Eigen::VectorXi > faces
static double convert(const json &val, const std::string &unit_type)
const std::string & length() const
virtual std::vector< std::string > list(const std::string &path) const =0
std::vector< std::string > glob(const std::string &pattern) const
Glob normalized logical paths using POSIX '*' and '?' wildcards.
virtual bool is_group(const std::string &path) const =0
std::unique_ptr< Mesh > load_fem(const json &geometry, bool non_conforming=false) const
Load and combine the enabled FEM entries in a geometry configuration.
Obstacle load_obstacles(const json &geometry, const std::vector< json > &obstacle_displacements, const std::vector< json > &dirichlet_conditions, int dimension, bool non_conforming=false) const
const io::ResourceIO & resources_
LoadedGeometry load(const json &geometry, const std::vector< json > &obstacle_displacements, const std::vector< json > &dirichlet_conditions, bool non_conforming=false) const
static void construct_affine_transformation(double unit_scale, const json &transform, const VectorNd &mesh_dimensions, MatrixNd &A, VectorNd &b)
Construct the affine map used by geometry and collision-proxy inputs.
void apply_geometry_selection(Mesh &mesh, const json &geometry_selection) const
Apply configured geometry IDs to an already constructed mesh.
SurfaceMesh load_surface(const json &geometry, int dimension) const
Load and prepare one configured surface/codimensional geometry entry.
std::unique_ptr< Mesh > load_fem_entry(const json &geometry, bool non_conforming=false) const
std::unique_ptr< Mesh > fem
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 get_body_id(const int primitive) const
Get the volume selection of an element (cell in 3d, face in 2d)
virtual void compute_element_barycenters(Eigen::MatrixXd &barycenters) const =0
utility for 2d/3d.
virtual void bounding_box(RowVectorNd &min, RowVectorNd &max) const =0
computes the bbox of the mesh
void set_geometry_ids(const std::vector< int > &geometry_ids)
Set the geometry selection, one ID per element.
std::vector< int > element_vertices(const int el_id) const
list of vids of an element
The only non-legacy decoder from a logical resource path to mesh data.
SurfaceMesh load_surface(const std::string &path, bool keep_volume=false) const
std::unique_ptr< Mesh > load_fem(const std::string &path, bool non_conforming=false) const
void append_mesh(const Eigen::MatrixXd &vertices, const Eigen::VectorXi &codim_vertices, const Eigen::MatrixXi &codim_edges, const Eigen::MatrixXi &faces, const json &displacement, const io::ResourceIO &resources, const Eigen::MatrixXi &tets=Eigen::MatrixXi())
void append_mesh_sequence(const std::vector< Eigen::MatrixXd > &vertices, const Eigen::VectorXi &codim_vertices, const Eigen::MatrixXi &codim_edges, const Eigen::MatrixXi &faces, const int fps, const Eigen::MatrixXi &tets=Eigen::MatrixXi())
void append_plane(const VectorNd &point, const VectorNd &normal)
void set_units(const Units &units)
Eigen::MatrixXi tets
Volumetric cells indexing vertices; only filled when a 3D tet mesh is read with keep_volume.
static std::vector< std::shared_ptr< utils::Selection > > build_selections(const json &j_selections, const BBox &mesh_bbox, const io::ResourceIO &resources)
Build a vector of selection objects from a JSON selection(s).
std::array< RowVectorNd, 2 > BBox
Eigen::Matrix3d to_rotation_matrix(const json &jr, std::string mode)
std::vector< T > json_as_array(const json &j)
Return the value of a json object as an array.
bool is_param_valid(const json ¶ms, const std::string &key)
Determine if a key exists and is non-null in a json object.
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< int, Eigen::Dynamic, 1, 0, 3, 1 > VectorNi
Eigen::Matrix< double, 1, Eigen::Dynamic, Eigen::RowMajor, 1, 3 > RowVectorNd
void log_and_throw_error(const std::string &msg)