88 using namespace nglib;
90 LOG_SCOPE(
"triangulate()",
"NetGenMeshInterface");
95 libmesh_not_implemented();
105 std::map<std::pair<Point,Point>,
Point> edge_midpoints;
111 std::map<std::array<Point,3>,
Point> face_centroids;
113 auto record_and_strip =
116 bool has_quadratic =
false;
117 for (
const auto & elem : m.element_ptr_range())
119 if (elem->type() !=
TRI6 && elem->type() !=
TRI7)
continue;
120 has_quadratic =
true;
124 const Point & pa = elem->point(e);
125 const Point & pb = elem->point((e+1)%3);
127 ? std::make_pair(pa,pb) : std::make_pair(pb,pa);
128 edge_midpoints[key] = elem->point(e+3);
131 if (elem->type() ==
TRI7)
133 std::array<Point,3> corners =
134 {elem->point(0), elem->point(1), elem->point(2)};
135 std::sort(corners.begin(), corners.end());
136 face_centroids[corners] = elem->point(6);
147 record_and_strip(this->
_mesh);
149 std::vector<MeshSerializer> hole_serializers;
151 for (std::unique_ptr<UnstructuredMesh> & hole : *
_holes)
157 record_and_strip(*hole);
161 "Found hole with bounding box " << hole_bb <<
162 "\nextending outside of mesh bounding box " << mesh_bb);
164 hole_serializers.emplace_back
179 auto increase_order_and_restore_midpoints =
180 [
this, &edge_midpoints, &face_centroids]()
204 if (!edge_midpoints.empty() || !face_centroids.empty())
205 for (
Elem * elem :
_mesh.element_ptr_range())
206 for (
auto s : elem->side_index_range())
208 if (elem->neighbor_ptr(s))
continue;
213 auto side = elem->build_side_ptr(s);
216 const Point & pa = side->point(e);
217 const Point & pb = side->point((e+1)%3);
219 ? std::make_pair(pa,pb) : std::make_pair(pb,pa);
220 if (
auto it = edge_midpoints.find(key);
221 it != edge_midpoints.end())
222 side->point(e+3) = it->second;
227 if (side->type() ==
TRI7)
229 std::array<Point,3> corners =
230 {side->point(0), side->point(1), side->point(2)};
231 std::sort(corners.begin(), corners.end());
232 if (
auto it = face_centroids.find(corners);
233 it != face_centroids.end())
234 side->point(6) = it->second;
251 hole_serializers.clear();
261 "NetGen failed to generate any tetrahedra");
264 increase_order_and_restore_midpoints();
270 Ng_Meshing_Parameters params;
276 params.uselocalh =
false;
278 params.elementsperedge = 1;
279 params.elementspercurve = 1;
280 params.closeedgeenable =
false;
281 params.closeedgefact = 0;
282 params.minedgelenenable =
false;
283 params.minedgelen = 0;
293 params.maxh = std::numeric_limits<double>::max();
299 params.optsteps_3d = 0;
306 std::unordered_map<int, dof_id_type> ng_to_libmesh_id;
308 auto handle_ng_result = [](Ng_Result result) {
309 static const std::vector<std::string> result_types =
310 {
"Netgen error",
"Netgen success",
"Netgen surface input error",
311 "Netgen volume failure",
"Netgen STL input error",
312 "Netgen surface failure",
"Netgen file not found"};
315 std::size_t(result+1) < result_types.size())
317 (result,
"Ng_GenerateVolumeMesh failed: " <<
318 result_types[result+1]);
321 (
"Ng_GenerateVolumeMesh failed with an unknown error code");
328 std::unordered_map<std::array<dof_id_type,3>,
332 (std::array<dof_id_type,3> & array,
337 while (array[i] < n_id)
340 std::swap(array[i++], n_id);
343 WrappedNgMesh ngmesh;
352 auto create_surface_component =
353 [
this, &ng_id, &ng_to_libmesh_id, &ngmesh, &side_boundary_id, &insert_id]
357 LOG_SCOPE(
"create_surface_component()",
"NetGenMeshInterface");
364 std::unordered_map<dof_id_type, int> libmesh_to_ng_id;
368 std::unordered_map<dof_id_type, dof_id_type> hole_to_main_mesh_id;
372 std::array<double, 3> point_val;
375 std::array<int, 3> elem_nodes;
377 for (
const auto * elem : srcmesh.element_ptr_range())
381 if (elem->type() ==
TRI6 ||
382 elem->type() ==
TRI7)
383 libmesh_not_implemented_msg
384 (
"Netgen tetrahedralization currently only supports TRI3 boundaries");
387 if (elem->type() !=
TRI3)
390 std::array<dof_id_type,3> sorted_ids =
399 auto & elem_node = hole_mesh ? elem_nodes[2-ni] : elem_nodes[ni];
401 const Node & n = elem->node_ref(ni);
405 if (
auto it = hole_to_main_mesh_id.find(n_id);
406 it != hole_to_main_mesh_id.end())
414 hole_to_main_mesh_id.emplace(n_id, n_new_id);
419 if (
auto it = libmesh_to_ng_id.find(n_id);
420 it != libmesh_to_ng_id.end())
422 const int existing_ng_id = it->second;
423 elem_node = existing_ng_id;
428 point_val[i] =
double(n(i));
430 Ng_AddPoint(ngmesh, point_val.data());
432 ng_to_libmesh_id[ng_id] = n_id;
433 libmesh_to_ng_id[n_id] = ng_id;
438 insert_id(sorted_ids, n_id);
441 side_boundary_id[sorted_ids] = bcid;
443 Ng_AddSurfaceElement(ngmesh, NG_TRIG, elem_nodes.data());
450 create_surface_component(this->
_mesh,
false, bcid);
453 for (
const std::unique_ptr<UnstructuredMesh> & h : *
_holes)
454 create_surface_component(*h,
true, ++bcid);
458 LOG_SCOPE(
"Ng_GenerateVolumeMesh()",
"NetGenMeshInterface");
460 auto result = Ng_GenerateVolumeMesh(ngmesh, ¶ms);
461 handle_ng_result(result);
464 const int n_elem = Ng_GetNE(ngmesh);
475 libmesh_error_msg (
"NetGen failed to generate any tetrahedra");
482 if (n_points != old_nodes)
484 std::array<double, 3> point_val;
490 "NetGen output " << n_points <<
491 " points when we gave it " <<
492 old_nodes <<
" and disabled refinement\n" <<
493 "If new interior points are acceptable in your mesh, please set\n" <<
494 "a non-zero desired_volume to indicate that. If new interior\n" <<
495 "points are not acceptable in your mesh, you may need a different\n" <<
496 "(non-advancing-front?) mesh generator." << std::endl;
500 for (
auto i :
make_range(old_nodes, n_points))
503 Ng_GetPoint (ngmesh, i+1, point_val.data());
504 const Point p(point_val[0], point_val[1], point_val[2]);
507 ng_to_libmesh_id[i+1] = n_new_id;
511 for (
auto * elem : this->
_mesh.element_ptr_range())
523 Ng_Volume_Element_Type ngtype =
524 Ng_GetVolumeElement(ngmesh, i+1, ngnodes);
534 libmesh_map_find(ng_to_libmesh_id, ngnodes[n]);
543 std::array<dof_id_type,3> sorted_ids =
547 std::vector<unsigned int> nos = elem->nodes_on_side(s);
549 insert_id(sorted_ids, elem->node_id(n));
551 if (
auto it = side_boundary_id.find(sorted_ids);
552 it != side_boundary_id.end())
559 hole_serializers.clear();
568 increase_order_and_restore_midpoints();