OGS
MeshSurfaceExtraction.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright (c) OpenGeoSys Community (opengeosys.org)
2// SPDX-License-Identifier: BSD-3-Clause
3
5
6#include <memory>
7#include <numbers>
8#include <range/v3/view/join.hpp>
9#include <range/v3/view/transform.hpp>
10#include <span>
11
13#include "BaseLib/Logging.h"
18#include "MeshLib/Mesh.h"
24
25namespace MeshToolsLib
26{
27template <typename T>
29 std::vector<std::size_t> const& id_map,
30 MeshLib::Mesh& sfc_mesh)
31{
32 auto const number_of_components = property.getNumberOfGlobalComponents();
33
35 sfc_mesh, property.getPropertyName(), property.getMeshItemType(),
36 number_of_components);
37
38 auto get_components = [&](std::size_t const bulk_id)
39 {
40 return std::span(&property.getComponent(bulk_id, 0 /*component_id*/),
41 number_of_components);
42 };
43 sfc_prop->assign(id_map | ranges::views::transform(get_components) |
44 ranges::views::join);
45}
46
48 MeshLib::Properties const& properties,
49 std::vector<std::size_t> const& node_ids_map,
50 std::vector<std::size_t> const& element_ids_map)
51{
52 std::size_t const n_elems(sfc_mesh.getNumberOfElements());
53 std::size_t const n_nodes(sfc_mesh.getNumberOfNodes());
54 if (node_ids_map.size() != n_nodes)
55 {
56 ERR("createSfcMeshProperties() - Incorrect number of node IDs ({:d}) "
57 "compared to actual number of surface nodes ({:d}).",
58 node_ids_map.size(), n_nodes);
59 return false;
60 }
61
62 if (element_ids_map.size() != n_elems)
63 {
64 ERR("createSfcMeshProperties() - Incorrect number of element IDs "
65 "({:d}) compared to actual number of surface elements ({:d}).",
66 element_ids_map.size(), n_elems);
67 return false;
68 }
69 std::map<MeshLib::MeshItemType, std::vector<std::size_t> const*> const
70 id_maps = {{MeshLib::MeshItemType::Cell, &element_ids_map},
71 {MeshLib::MeshItemType::Node, &node_ids_map}};
72
73 std::size_t vectors_copied(0);
74 std::size_t vectors_skipped(0);
75 for (auto [name, property] : properties)
76 {
77 if (property->getMeshItemType() != MeshLib::MeshItemType::Cell &&
78 property->getMeshItemType() != MeshLib::MeshItemType::Node)
79 {
80 WARN(
81 "Skipping property vector '{:s}' - not defined on cells or "
82 "nodes.",
83 name);
84 vectors_skipped++;
85 continue;
86 }
87
88 auto const& id_map = *id_maps.at(property->getMeshItemType());
89 if (auto const* p =
90 dynamic_cast<MeshLib::PropertyVector<double>*>(property))
91 {
92 processPropertyVector(*p, id_map, sfc_mesh);
93 vectors_copied++;
94 }
95 else if (auto const* p =
96 dynamic_cast<MeshLib::PropertyVector<float>*>(property))
97 {
98 processPropertyVector(*p, id_map, sfc_mesh);
99 vectors_copied++;
100 }
101 else if (auto const* p =
102 dynamic_cast<MeshLib::PropertyVector<int>*>(property))
103 {
104 processPropertyVector(*p, id_map, sfc_mesh);
105 vectors_copied++;
106 }
107 else if (auto const* p =
108 dynamic_cast<MeshLib::PropertyVector<unsigned>*>(property))
109 {
110 processPropertyVector(*p, id_map, sfc_mesh);
111 vectors_copied++;
112 }
113 else if (auto const* p =
114 dynamic_cast<MeshLib::PropertyVector<long>*>(property))
115 {
116 processPropertyVector(*p, id_map, sfc_mesh);
117 vectors_copied++;
118 }
119 else if (auto const* p =
121 property))
122 {
123 processPropertyVector(*p, id_map, sfc_mesh);
124 vectors_copied++;
125 }
126 else if (auto const* p =
128 property))
129 {
130 processPropertyVector(*p, id_map, sfc_mesh);
131 vectors_copied++;
132 }
133 else if (auto const* p =
135 property))
136 {
137 processPropertyVector(*p, id_map, sfc_mesh);
138 vectors_copied++;
139 }
140 else if (auto const* p =
142 property))
143 {
144 processPropertyVector(*p, id_map, sfc_mesh);
145 vectors_copied++;
146 }
147 else if (auto const* p =
148 dynamic_cast<MeshLib::PropertyVector<char>*>(property))
149 {
150 processPropertyVector(*p, id_map, sfc_mesh);
151 vectors_copied++;
152 }
153 else
154 {
155 WARN(
156 "Skipping property vector '{:s}' - no matching data type "
157 "'{:s}' found.",
158 name, BaseLib::typeToString(*property));
159 vectors_skipped++;
160 }
161 }
162 INFO("{:d} property vectors copied, {:d} vectors skipped.", vectors_copied,
163 vectors_skipped);
164 return true;
165}
166
167std::tuple<std::vector<MeshLib::Node*>, std::vector<std::size_t>>
168createNodesAndIDMapFromElements(std::vector<MeshLib::Element*> const& elements,
169 std::size_t const n_all_nodes)
170{
171 std::vector<MeshLib::Node const*> tmp_nodes(n_all_nodes, nullptr);
172 for (auto const* elem : elements)
173 {
174 auto const n_nodes = elem->getNumberOfNodes();
175 for (unsigned j = 0; j < n_nodes; ++j)
176 {
177 const MeshLib::Node* node(elem->getNode(j));
178 tmp_nodes[node->getID()] = node;
179 }
180 }
181
182 std::vector<MeshLib::Node*> nodes;
183 std::vector<std::size_t> node_id_map(n_all_nodes);
184 for (unsigned i = 0; i < n_all_nodes; ++i)
185 {
186 if (tmp_nodes[i])
187 {
188 node_id_map[i] = nodes.size();
189 nodes.push_back(new MeshLib::Node(*tmp_nodes[i]));
190 }
191 }
192 return {nodes, node_id_map};
193}
194
196 const MeshLib::Mesh& mesh)
197{
198 std::vector<double> node_area_vec;
199 if (mesh.getDimension() != 2)
200 {
201 ERR("Error in MeshSurfaceExtraction::getSurfaceAreaForNodes() - Given "
202 "mesh is no surface mesh (dimension != 2).");
203 return node_area_vec;
204 }
205
206 double total_area(0);
207
208 // for each node, a vector containing all the element idget every element
209 const std::vector<MeshLib::Node*>& nodes = mesh.getNodes();
210 const std::size_t nNodes(mesh.getNumberOfNodes());
211 for (std::size_t n = 0; n < nNodes; ++n)
212 {
213 double node_area(0);
214
215 auto const conn_elems = mesh.getElementsConnectedToNode(*nodes[n]);
216 const std::size_t nConnElems(conn_elems.size());
217
218 for (std::size_t i = 0; i < nConnElems; ++i)
219 {
220 const MeshLib::Element* elem(conn_elems[i]);
221 const unsigned nElemParts =
223 : 4;
224 const double area = conn_elems[i]->getContent() / nElemParts;
225 node_area += area;
226 total_area += area;
227 }
228
229 node_area_vec.push_back(node_area);
230 }
231
232 INFO("Total surface Area: {:f}", total_area);
233
234 return node_area_vec;
235}
236
238 const MeshLib::Mesh& subsfc_mesh, Eigen::Vector3d const& dir, double angle,
239 std::string_view subsfc_node_id_prop_name,
240 std::string_view subsfc_element_id_prop_name,
241 std::string_view face_id_prop_name)
242{
243 // allow slightly greater angles than 90 degrees for numerical errors
244 if (angle < 0 || angle > 91)
245 {
246 ERR("Supported angle between 0 and 90 degrees only.");
247 return nullptr;
248 }
249
250 INFO("Extracting mesh surface...");
251 std::vector<MeshLib::Element*> sfc_elements;
252 std::vector<std::size_t> element_ids_map;
253 std::vector<std::size_t> face_ids_map;
254 get2DSurfaceElements(subsfc_mesh.getElements(), sfc_elements,
255 element_ids_map, face_ids_map, dir, angle,
256 subsfc_mesh.getDimension());
257
258 if (sfc_elements.empty())
259 {
260 return nullptr;
261 }
262
263 auto [sfc_nodes, node_id_map] = createNodesAndIDMapFromElements(
264 sfc_elements, subsfc_mesh.getNumberOfNodes());
265
266 // create new elements vector with newly created nodes (and delete
267 // temp-elements)
268 auto new_elements =
269 MeshLib::copyElementVector(sfc_elements, sfc_nodes, &node_id_map);
270 std::for_each(sfc_elements.begin(), sfc_elements.end(),
271 [](MeshLib::Element* e) { delete e; });
272
273 auto sfc_node_ids = sfc_nodes | MeshLib::views::ids;
274 std::vector<std::size_t> const id_map(sfc_node_ids.begin(),
275 sfc_node_ids.end());
276
277 MeshLib::Mesh* result(
278 new MeshLib::Mesh(subsfc_mesh.getName() + "-Surface", sfc_nodes,
279 new_elements, true /* compute_element_neighbors */));
280
281 addBulkIDPropertiesToMesh(*result, subsfc_node_id_prop_name, id_map,
282 subsfc_element_id_prop_name, element_ids_map,
283 face_id_prop_name, face_ids_map);
284
285 if (!createSfcMeshProperties(*result, subsfc_mesh.getProperties(), id_map,
286 element_ids_map))
287 {
288 ERR("Transferring subsurface properties failed.");
289 }
290 return result;
291}
292
294 const std::vector<MeshLib::Element*>& all_elements,
295 std::vector<MeshLib::Element*>& sfc_elements,
296 std::vector<std::size_t>& element_to_bulk_element_id_map,
297 std::vector<std::size_t>& element_to_bulk_face_id_map,
298 Eigen::Vector3d const& dir, double angle, unsigned mesh_dimension)
299{
300 if (mesh_dimension < 2 || mesh_dimension > 3)
301 {
302 ERR("Cannot handle meshes of dimension {}", mesh_dimension);
303 }
304
305 bool const complete_surface = (dir.dot(dir) == 0);
306
307 double const cos_theta(std::cos(angle * std::numbers::pi / 180.0));
308 Eigen::Vector3d const norm_dir(dir.normalized());
309
310 for (auto const* elem : all_elements)
311 {
312 const unsigned element_dimension(elem->getDimension());
313 if (element_dimension < mesh_dimension)
314 {
315 continue;
316 }
317
318 if (element_dimension == 2)
319 {
320 if (!complete_surface)
321 {
322 if (MeshLib::FaceRule::getSurfaceNormal(*elem).normalized().dot(
323 norm_dir) > cos_theta)
324 {
325 continue;
326 }
327 }
328 sfc_elements.push_back(elem->clone());
329 element_to_bulk_element_id_map.push_back(elem->getID());
330 element_to_bulk_face_id_map.push_back(0);
331 }
332 else
333 {
334 if (!elem->isBoundaryElement())
335 {
336 continue;
337 }
338 const unsigned nFaces(elem->getNumberOfFaces());
339 for (unsigned j = 0; j < nFaces; ++j)
340 {
341 if (elem->getNeighbor(j) != nullptr)
342 {
343 continue;
344 }
345
346 auto const face =
347 std::unique_ptr<MeshLib::Element const>{elem->getFace(j)};
348 if (!complete_surface)
349 {
351 .normalized()
352 .dot(norm_dir) < cos_theta)
353 {
354 continue;
355 }
356 }
357 switch (face->getCellType())
358 {
360 sfc_elements.push_back(new MeshLib::Tri(
361 *static_cast<const MeshLib::Tri*>(face.get())));
362 break;
364 sfc_elements.push_back(new MeshLib::Tri6(
365 *static_cast<const MeshLib::Tri6*>(face.get())));
366 break;
368 sfc_elements.push_back(new MeshLib::Quad(
369 *static_cast<const MeshLib::Quad*>(face.get())));
370 break;
372 sfc_elements.push_back(new MeshLib::Quad8(
373 *static_cast<const MeshLib::Quad8*>(face.get())));
374 break;
376 sfc_elements.push_back(new MeshLib::Quad9(
377 *static_cast<const MeshLib::Quad9*>(face.get())));
378 break;
379 default:
380 DBUG("unknown cell type");
381 }
382 element_to_bulk_element_id_map.push_back(elem->getID());
383 element_to_bulk_face_id_map.push_back(j);
384 }
385 }
386 }
387}
388
389std::vector<MeshLib::Node*> MeshSurfaceExtraction::getSurfaceNodes(
390 const MeshLib::Mesh& mesh, Eigen::Vector3d const& dir, double angle)
391{
392 INFO("Extracting surface nodes...");
393 std::vector<MeshLib::Element*> sfc_elements;
394 std::vector<std::size_t> element_to_bulk_element_id_map;
395 std::vector<std::size_t> element_to_bulk_face_id_map;
396
398 mesh.getElements(), sfc_elements, element_to_bulk_element_id_map,
399 element_to_bulk_face_id_map, dir, angle, mesh.getDimension());
400
401 std::vector<MeshLib::Node*> surface_nodes;
402 std::tie(surface_nodes, std::ignore) =
404
405 for (auto e : sfc_elements)
406 {
407 delete e;
408 }
409
410 return surface_nodes;
411}
412
414 MeshLib::Element const& surface_element,
415 std::vector<MeshLib::Element*>& surface_elements,
416 std::vector<std::size_t>& element_to_bulk_element_id_map,
417 std::vector<std::size_t>& element_to_bulk_face_id_map)
418{
419 const unsigned n_faces(surface_element.getNumberOfBoundaries());
420 for (unsigned j = 0; j < n_faces; ++j)
421 {
422 if (surface_element.getNeighbor(j) != nullptr)
423 {
424 continue;
425 }
426
427 surface_elements.push_back(
428 const_cast<MeshLib::Element*>(surface_element.getBoundary(j)));
429 element_to_bulk_face_id_map.push_back(j);
430 element_to_bulk_element_id_map.push_back(surface_element.getID());
431 }
432}
433
434std::tuple<std::vector<MeshLib::Element*>, std::vector<std::size_t>,
435 std::vector<std::size_t>>
437{
438 std::vector<std::size_t> element_to_bulk_element_id_map;
439 std::vector<std::size_t> element_to_bulk_face_id_map;
440 std::vector<MeshLib::Element*> surface_elements;
441
442 auto const& bulk_elements = bulk_mesh.getElements();
443 auto const mesh_dimension = bulk_mesh.getDimension();
444
445 for (auto const* elem : bulk_elements)
446 {
447 const unsigned element_dimension(elem->getDimension());
448 if (element_dimension < mesh_dimension)
449 {
450 continue;
451 }
452
453 if (!elem->isBoundaryElement())
454 {
455 continue;
456 }
457 createSurfaceElementsFromElement(*elem, surface_elements,
458 element_to_bulk_element_id_map,
459 element_to_bulk_face_id_map);
460 }
461 return {surface_elements, element_to_bulk_element_id_map,
462 element_to_bulk_face_id_map};
463}
464
466{
467std::unique_ptr<MeshLib::Mesh> getBoundaryElementsAsMesh(
468 MeshLib::Mesh const& bulk_mesh,
469 std::string_view subsfc_node_id_prop_name,
470 std::string_view subsfc_element_id_prop_name,
471 std::string_view face_id_prop_name)
472{
473 auto const mesh_dimension = bulk_mesh.getDimension();
474 if (mesh_dimension < 2 || mesh_dimension > 3)
475 {
476 ERR("Cannot handle meshes of dimension {}", mesh_dimension);
477 }
478
479 // create boundary elements based on the subsurface nodes
480 auto [boundary_elements, element_to_bulk_element_id_map,
481 element_to_bulk_face_id_map] = createBoundaryElements(bulk_mesh);
482
483 // create new nodes needed for the new surface elements
484 auto [boundary_nodes, node_id_map] = createNodesAndIDMapFromElements(
485 boundary_elements, bulk_mesh.getNumberOfNodes());
486
487 // create new elements using newly created nodes and delete temp-elements
488 auto new_elements = MeshLib::copyElementVector(
489 boundary_elements, boundary_nodes, &node_id_map);
490 for (auto* e : boundary_elements)
491 {
492 delete e;
493 }
494
495 auto boundary_node_ids = boundary_nodes | MeshLib::views::ids;
496
497 std::vector<std::size_t> const nodes_to_bulk_nodes_id_map(
498 boundary_node_ids.begin(), boundary_node_ids.end());
499
500 std::unique_ptr<MeshLib::Mesh> boundary_mesh(
501 new MeshLib::Mesh(bulk_mesh.getName() + "-boundary", boundary_nodes,
502 new_elements, true /* compute_element_neighbors */));
503
505 *boundary_mesh, subsfc_node_id_prop_name, nodes_to_bulk_nodes_id_map,
506 subsfc_element_id_prop_name, element_to_bulk_element_id_map,
507 face_id_prop_name, element_to_bulk_face_id_map);
508
510 if (!createSfcMeshProperties(*boundary_mesh, bulk_mesh.getProperties(),
511 nodes_to_bulk_nodes_id_map,
512 element_to_bulk_element_id_map))
513 {
514 ERR("Transferring subsurface properties failed.");
515 }
516 return boundary_mesh;
517}
518
519} // namespace BoundaryExtraction
520
522 MeshLib::Mesh& surface_mesh,
523 std::string_view node_to_bulk_node_id_map_name,
524 std::vector<std::size_t> const& node_to_bulk_node_id_map,
525 std::string_view element_to_bulk_element_id_map_name,
526 std::vector<std::size_t> const& element_to_bulk_element_id_map,
527 std::string_view element_to_bulk_face_id_map_name,
528 std::vector<std::size_t> const& element_to_bulk_face_id_map)
529{
530 // transmit the original node ids of the bulk mesh as a property
531 if (!node_to_bulk_node_id_map_name.empty())
532 {
534 surface_mesh, node_to_bulk_node_id_map_name,
535 MeshLib::MeshItemType::Node, 1, {node_to_bulk_node_id_map});
536 }
537
538 // transmit the original bulk element ids as a property
539 if (!element_to_bulk_element_id_map_name.empty())
540 {
542 surface_mesh, element_to_bulk_element_id_map_name,
543 MeshLib::MeshItemType::Cell, 1, {element_to_bulk_element_id_map});
544 }
545
546 // transmit the face id of the original bulk element as a property
547 if (!element_to_bulk_face_id_map_name.empty())
548 {
550 surface_mesh, element_to_bulk_face_id_map_name,
551 MeshLib::MeshItemType::Cell, 1, {element_to_bulk_face_id_map});
552 }
553}
554
555} // namespace MeshToolsLib
void INFO(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:28
void DBUG(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:22
void ERR(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:40
void WARN(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:34
std::size_t getID() const
virtual MeshElemType getGeomType() const =0
virtual unsigned getNumberOfBoundaries() const =0
std::size_t getID() const
Returns the ID of the element.
Definition Element.h:80
virtual const Element * getNeighbor(unsigned i) const =0
Get the specified neighbor.
virtual const Element * getBoundary(unsigned i) const =0
static Eigen::Vector3d getSurfaceNormal(Element const &e)
Returns the surface normal of a 2D element.
Definition FaceRule.cpp:33
std::vector< Node * > const & getNodes() const
Get the nodes-vector for the mesh.
Definition Mesh.h:97
std::vector< Element * > const & getElements() const
Get the element-vector for the mesh.
Definition Mesh.h:100
unsigned getDimension() const
Returns the dimension of the mesh (determined by the maximum dimension over all elements).
Definition Mesh.h:79
Properties & getProperties()
Definition Mesh.h:125
const std::string getName() const
Get name of the mesh.
Definition Mesh.h:94
std::size_t getNumberOfNodes() const
Get the number of nodes.
Definition Mesh.h:91
std::vector< Element const * > const & getElementsConnectedToNode(std::size_t node_id) const
Definition Mesh.cpp:246
std::size_t getNumberOfElements() const
Get the number of elements.
Definition Mesh.h:88
Property manager on mesh items. Class Properties manages scalar, vector or matrix properties....
MeshItemType getMeshItemType() const
std::string const & getPropertyName() const
PROP_VAL_TYPE & getComponent(std::size_t tuple_index, int component)
Returns the value for the given component stored in the given tuple.
static std::vector< double > getSurfaceAreaForNodes(const MeshLib::Mesh &mesh)
Returns a vector of the areas assigned to each node on a surface mesh.
static void get2DSurfaceElements(const std::vector< MeshLib::Element * > &all_elements, std::vector< MeshLib::Element * > &sfc_elements, std::vector< std::size_t > &element_to_bulk_element_id_map, std::vector< std::size_t > &element_to_bulk_face_id_map, Eigen::Vector3d const &dir, double angle, unsigned mesh_dimension)
Functionality needed for getSurfaceNodes() and getMeshSurface()
static MeshLib::Mesh * getMeshSurface(const MeshLib::Mesh &subsfc_mesh, Eigen::Vector3d const &dir, double angle, std::string_view subsfc_node_id_prop_name="", std::string_view subsfc_element_id_prop_name="", std::string_view face_id_prop_name="")
static std::vector< MeshLib::Node * > getSurfaceNodes(const MeshLib::Mesh &mesh, Eigen::Vector3d const &dir, double angle)
Returns the surface nodes of a mesh.
std::string typeToString()
constexpr ranges::views::view_closure ids
For an element of a range view return its id.
Definition Mesh.h:216
TemplateElement< MeshLib::QuadRule9 > Quad9
Definition Quad.h:19
PropertyVector< T > * getOrCreateMeshProperty(Mesh &mesh, std::string const &property_name, MeshItemType const item_type, int const number_of_components)
void addPropertyToMesh(Mesh &mesh, std::string_view name, MeshItemType item_type, std::size_t number_of_components, std::span< T const > values)
TemplateElement< MeshLib::QuadRule8 > Quad8
Definition Quad.h:18
TemplateElement< MeshLib::QuadRule4 > Quad
Definition Quad.h:17
TemplateElement< MeshLib::TriRule3 > Tri
Definition Tri.h:15
TemplateElement< MeshLib::TriRule6 > Tri6
Definition Tri.h:16
std::vector< Element * > copyElementVector(std::vector< Element * > const &elements, std::vector< Node * > const &new_nodes, std::vector< std::size_t > const *const node_id_map)
std::unique_ptr< MeshLib::Mesh > getBoundaryElementsAsMesh(MeshLib::Mesh const &bulk_mesh, std::string_view subsfc_node_id_prop_name, std::string_view subsfc_element_id_prop_name, std::string_view face_id_prop_name)
std::tuple< std::vector< MeshLib::Node * >, std::vector< std::size_t > > createNodesAndIDMapFromElements(std::vector< MeshLib::Element * > const &elements, std::size_t const n_all_nodes)
void processPropertyVector(MeshLib::PropertyVector< T > const &property, std::vector< std::size_t > const &id_map, MeshLib::Mesh &sfc_mesh)
std::tuple< std::vector< MeshLib::Element * >, std::vector< std::size_t >, std::vector< std::size_t > > createBoundaryElements(MeshLib::Mesh const &bulk_mesh)
void createSurfaceElementsFromElement(MeshLib::Element const &surface_element, std::vector< MeshLib::Element * > &surface_elements, std::vector< std::size_t > &element_to_bulk_element_id_map, std::vector< std::size_t > &element_to_bulk_face_id_map)
bool createSfcMeshProperties(MeshLib::Mesh &sfc_mesh, MeshLib::Properties const &properties, std::vector< std::size_t > const &node_ids_map, std::vector< std::size_t > const &element_ids_map)
void addBulkIDPropertiesToMesh(MeshLib::Mesh &surface_mesh, std::string_view node_to_bulk_node_id_map_name, std::vector< std::size_t > const &node_to_bulk_node_id_map, std::string_view element_to_bulk_element_id_map_name, std::vector< std::size_t > const &element_to_bulk_element_id_map, std::string_view element_to_bulk_face_id_map_name, std::vector< std::size_t > const &element_to_bulk_face_id_map)