84 if (!
_input->preparation().has_cached_elem_data)
87 auto replicated_mesh_ptr =
dynamic_cast<ReplicatedMesh *
>(
_input.get());
88 if (!replicated_mesh_ptr)
89 paramError(
"input",
"Input is not a replicated mesh, which is required");
90 if (*(replicated_mesh_ptr->elem_dimensions().begin()) != 3 ||
91 *(replicated_mesh_ptr->elem_dimensions().rbegin()) != 3)
94 "Only 3D meshes are supported. Use XYMeshLineCutter for 2D meshes if applicable. Mixed "
95 "dimensional meshes are not supported at the moment.");
97 ReplicatedMesh &
mesh = *replicated_mesh_ptr;
103 const boundary_id_type exist_cut_face_id =
107 "The cut face boundary name and id are both provided, but they are inconsistent "
108 "with an existing boundary name which has a different id.");
127 const auto block_id_to_remove = sid_shift_base * 3;
131 std::vector<std::pair<dof_id_type, bool>> cross_and_remained_elems_pre_convert;
134 std::set<subdomain_id_type> original_subdomain_ids;
135 for (
auto elem_it =
mesh.active_elements_begin(); elem_it !=
mesh.active_elements_end();
138 original_subdomain_ids.emplace((*elem_it)->subdomain_id());
139 const unsigned int & n_vertices = (*elem_it)->n_vertices();
140 unsigned short elem_vertices_counter(0);
141 for (
unsigned int i = 0; i < n_vertices; i++)
147 elem_vertices_counter++;
149 if (elem_vertices_counter == n_vertices)
150 (*elem_it)->subdomain_id() = block_id_to_remove;
154 if ((*elem_it)->default_order() != Order::FIRST)
155 mooseError(
"Only first order elements are supported for cutting.");
159 cross_and_remained_elems_pre_convert.push_back(std::make_pair((*elem_it)->id(),
true));
162 if ((*elem_it)->type() != TET4)
163 (*elem_it)->subdomain_id() += sid_shift_base;
166 cross_and_remained_elems_pre_convert.push_back(
167 std::make_pair((*elem_it)->id(), elem_vertices_counter > 0));
172 std::vector<dof_id_type> transition_elems_list;
173 std::vector<std::vector<unsigned int>> transition_elems_sides_list;
176 if (!
mesh.is_prepared())
177 mesh.find_neighbors();
180 for (
const auto & elem_info : cross_and_remained_elems_pre_convert)
182 for (
const auto & i_side : make_range(
mesh.elem_ptr(elem_info.first)->n_sides()))
185 if (
mesh.elem_ptr(elem_info.first)->side_ptr(i_side)->type() == QUAD4)
187 if (
mesh.elem_ptr(elem_info.first)->neighbor_ptr(i_side) !=
nullptr &&
188 mesh.elem_ptr(elem_info.first)->neighbor_ptr(i_side)->subdomain_id() !=
189 block_id_to_remove &&
190 std::find(cross_and_remained_elems_pre_convert.begin(),
191 cross_and_remained_elems_pre_convert.end(),
192 std::make_pair(
mesh.elem_ptr(elem_info.first)->neighbor_ptr(i_side)->id(),
193 true)) == cross_and_remained_elems_pre_convert.end())
195 const auto & neighbor_elem_id =
196 mesh.elem_ptr(elem_info.first)->neighbor_ptr(i_side)->id();
197 const auto & neighbor_elem_side_index =
198 mesh.elem_ptr(elem_info.first)
199 ->neighbor_ptr(i_side)
200 ->which_neighbor_am_i(
mesh.elem_ptr(elem_info.first));
201 const auto & id_found = std::find(
202 transition_elems_list.begin(), transition_elems_list.end(), neighbor_elem_id);
203 if (id_found == transition_elems_list.end())
205 transition_elems_list.push_back(neighbor_elem_id);
206 transition_elems_sides_list.push_back(
207 std::vector<unsigned int>({neighbor_elem_side_index}));
212 const auto index = std::distance(transition_elems_list.begin(), id_found);
213 transition_elems_sides_list[index].push_back(neighbor_elem_side_index);
221 std::vector<dof_id_type> converted_elems_ids_to_cut;
224 cross_and_remained_elems_pre_convert,
225 converted_elems_ids_to_cut,
230 auto & sideset_map =
mesh.get_boundary_info().get_sideset_map();
231 for (
const auto & i_elem : index_range(transition_elems_list))
233 const auto & elem_id = transition_elems_list[i_elem];
234 const auto & side_indices = transition_elems_sides_list[i_elem];
236 std::vector<std::vector<boundary_id_type>> elem_side_info(
mesh.elem_ptr(elem_id)->n_sides());
237 auto side_range = sideset_map.equal_range(
mesh.elem_ptr(elem_id));
238 for (
auto i = side_range.first; i != side_range.second; ++i)
239 elem_side_info[i->second.first].push_back(i->second.second);
242 mesh, elem_id, side_indices, elem_side_info, sid_shift_base);
245 std::vector<const Node *> new_on_plane_nodes;
248 BoundaryInfo & boundary_info =
mesh.get_boundary_info();
249 const auto bdry_side_list = boundary_info.build_side_list();
251 for (
const auto & converted_elems_id_to_cut : converted_elems_ids_to_cut)
254 mesh, bdry_side_list, converted_elems_id_to_cut, block_id_to_remove, new_on_plane_nodes);
264 original_subdomain_ids,
269 catch (
const std::exception & e)
271 if (((std::string)e.what()).compare(26, 4,
"TET4") == 0)
272 paramError(
"converted_tet_element_subdomain_name_suffix", e.what());
274 paramError(
"converted_pyramid_element_subdomain_name_suffix", e.what());
279 for (
const auto & elem_id : transition_elems_list)
280 mesh.elem_ptr(elem_id)->subdomain_id() = block_id_to_remove;
282 for (
auto elem_it =
mesh.active_subdomain_elements_begin(block_id_to_remove);
283 elem_it !=
mesh.active_subdomain_elements_end(block_id_to_remove);
285 mesh.delete_elem(*elem_it);
288 mesh.unset_is_prepared();
362 ReplicatedMesh & mesh,
363 const std::vector<libMesh::BoundaryInfo::BCTuple> & bdry_side_list,
364 const dof_id_type elem_id,
365 const subdomain_id_type & block_id_to_remove,
366 std::vector<const Node *> & new_on_plane_nodes)
369 BoundaryInfo & boundary_info =
mesh.get_boundary_info();
375 std::vector<Point> elem_side_normal_list;
376 elem_side_normal_list.resize(4);
377 for (
const auto i : make_range(4))
379 auto elem_side =
mesh.elem_ptr(elem_id)->side_ptr(i);
380 elem_side_normal_list[i] = (*elem_side->node_ptr(1) - *elem_side->node_ptr(0))
381 .cross(*elem_side->node_ptr(2) - *elem_side->node_ptr(1))
385 std::vector<std::vector<boundary_id_type>> elem_side_list;
387 bdry_side_list, elem_id, 4, elem_side_list);
389 std::vector<PointLevelSetRelationIndex> node_plane_relation(4);
390 std::vector<const Node *> tet4_nodes(4);
391 std::vector<const Node *> tet4_nodes_on_plane;
392 std::vector<const Node *> tet4_nodes_outside_plane;
393 std::vector<const Node *> tet4_nodes_inside_plane;
395 for (
unsigned int i = 0; i < 4; i++)
397 tet4_nodes[i] =
mesh.elem_ptr(elem_id)->node_ptr(i);
400 tet4_nodes_on_plane.push_back(tet4_nodes[i]);
402 tet4_nodes_outside_plane.push_back(tet4_nodes[i]);
404 tet4_nodes_inside_plane.push_back(tet4_nodes[i]);
406 std::vector<Elem *> elems_tet4;
407 bool new_elements_created(
false);
409 if (tet4_nodes_outside_plane.size() == 0)
411 if (tet4_nodes_on_plane.size() == 3)
414 elems_tet4.push_back(
mesh.elem_ptr(elem_id));
418 else if (tet4_nodes_inside_plane.size() == 0)
420 mesh.elem_ptr(elem_id)->subdomain_id() = block_id_to_remove;
421 if (tet4_nodes_on_plane.size() == 3)
430 new_elements_created =
true;
431 if (tet4_nodes_inside_plane.size() == 1 && tet4_nodes_outside_plane.size() == 3)
433 std::vector<const Node *> new_plane_nodes;
435 for (
const auto & tet4_node_outside_plane : tet4_nodes_outside_plane)
442 auto new_elem_tet4 = std::make_unique<Tet4>();
443 new_elem_tet4->set_node(0,
const_cast<Node *
>(tet4_nodes_inside_plane[0]));
444 new_elem_tet4->set_node(1,
const_cast<Node *
>(new_plane_nodes[0]));
445 new_elem_tet4->set_node(2,
const_cast<Node *
>(new_plane_nodes[1]));
446 new_elem_tet4->set_node(3,
const_cast<Node *
>(new_plane_nodes[2]));
447 new_elem_tet4->subdomain_id() =
mesh.elem_ptr(elem_id)->subdomain_id();
448 elems_tet4.push_back(
mesh.add_elem(std::move(new_elem_tet4)));
450 else if (tet4_nodes_inside_plane.size() == 2 && tet4_nodes_outside_plane.size() == 2)
452 std::vector<const Node *> new_plane_nodes;
454 for (
const auto & tet4_node_outside_plane : tet4_nodes_outside_plane)
456 for (
const auto & tet4_node_inside_plane : tet4_nodes_inside_plane)
464 std::vector<const Node *> new_elems_nodes = {tet4_nodes_inside_plane[1],
467 tet4_nodes_inside_plane[0],
470 std::vector<std::vector<unsigned int>> rotated_tet_face_indices;
471 std::vector<std::vector<const Node *>> optimized_node_list;
473 new_elems_nodes, rotated_tet_face_indices, optimized_node_list);
475 for (
unsigned int i = 0; i < optimized_node_list.size(); i++)
477 auto new_elem_tet4 = std::make_unique<Tet4>();
478 new_elem_tet4->set_node(0,
const_cast<Node *
>(optimized_node_list[i][0]));
479 new_elem_tet4->set_node(1,
const_cast<Node *
>(optimized_node_list[i][1]));
480 new_elem_tet4->set_node(2,
const_cast<Node *
>(optimized_node_list[i][2]));
481 new_elem_tet4->set_node(3,
const_cast<Node *
>(optimized_node_list[i][3]));
482 new_elem_tet4->subdomain_id() =
mesh.elem_ptr(elem_id)->subdomain_id();
483 elems_tet4.push_back(
mesh.add_elem(std::move(new_elem_tet4)));
486 else if (tet4_nodes_inside_plane.size() == 3 && tet4_nodes_outside_plane.size() == 1)
488 std::vector<const Node *> new_plane_nodes;
490 for (
const auto & tet4_node_inside_plane : tet4_nodes_inside_plane)
497 std::vector<const Node *> new_elems_nodes = {tet4_nodes_inside_plane[0],
498 tet4_nodes_inside_plane[1],
499 tet4_nodes_inside_plane[2],
503 std::vector<std::vector<unsigned int>> rotated_tet_face_indices;
504 std::vector<std::vector<const Node *>> optimized_node_list;
506 new_elems_nodes, rotated_tet_face_indices, optimized_node_list);
508 for (
unsigned int i = 0; i < optimized_node_list.size(); i++)
510 auto new_elem_tet4 = std::make_unique<Tet4>();
511 new_elem_tet4->set_node(0,
const_cast<Node *
>(optimized_node_list[i][0]));
512 new_elem_tet4->set_node(1,
const_cast<Node *
>(optimized_node_list[i][1]));
513 new_elem_tet4->set_node(2,
const_cast<Node *
>(optimized_node_list[i][2]));
514 new_elem_tet4->set_node(3,
const_cast<Node *
>(optimized_node_list[i][3]));
515 new_elem_tet4->subdomain_id() =
mesh.elem_ptr(elem_id)->subdomain_id();
516 elems_tet4.push_back(
mesh.add_elem(std::move(new_elem_tet4)));
519 else if (tet4_nodes_inside_plane.size() == 1 && tet4_nodes_outside_plane.size() == 1)
526 auto new_elem_tet4 = std::make_unique<Tet4>();
527 new_elem_tet4->set_node(0,
const_cast<Node *
>(new_plane_node));
528 new_elem_tet4->set_node(1,
const_cast<Node *
>(tet4_nodes_on_plane[0]));
529 new_elem_tet4->set_node(2,
const_cast<Node *
>(tet4_nodes_on_plane[1]));
530 new_elem_tet4->set_node(3,
const_cast<Node *
>(tet4_nodes_inside_plane[0]));
531 new_elem_tet4->subdomain_id() =
mesh.elem_ptr(elem_id)->subdomain_id();
532 elems_tet4.push_back(
mesh.add_elem(std::move(new_elem_tet4)));
534 else if (tet4_nodes_inside_plane.size() == 1 && tet4_nodes_outside_plane.size() == 2)
536 std::vector<const Node *> new_plane_nodes;
538 for (
const auto & tet4_node_outside_plane : tet4_nodes_outside_plane)
545 auto new_elem_tet4 = std::make_unique<Tet4>();
546 new_elem_tet4->set_node(0,
const_cast<Node *
>(new_plane_nodes[0]));
547 new_elem_tet4->set_node(1,
const_cast<Node *
>(new_plane_nodes[1]));
548 new_elem_tet4->set_node(2,
const_cast<Node *
>(tet4_nodes_on_plane[0]));
549 new_elem_tet4->set_node(3,
const_cast<Node *
>(tet4_nodes_inside_plane[0]));
550 new_elem_tet4->subdomain_id() =
mesh.elem_ptr(elem_id)->subdomain_id();
551 elems_tet4.push_back(
mesh.add_elem(std::move(new_elem_tet4)));
553 else if (tet4_nodes_inside_plane.size() == 2 && tet4_nodes_outside_plane.size() == 1)
555 std::vector<const Node *> new_plane_nodes;
557 for (
const auto & tet4_node_inside_plane : tet4_nodes_inside_plane)
564 std::vector<const Node *> new_elems_nodes = {tet4_nodes_inside_plane[0],
565 tet4_nodes_inside_plane[1],
568 tet4_nodes_on_plane[0]};
569 std::vector<std::vector<unsigned int>> rotated_tet_face_indices;
570 std::vector<std::vector<const Node *>> optimized_node_list;
572 new_elems_nodes, rotated_tet_face_indices, optimized_node_list);
574 for (
unsigned int i = 0; i < optimized_node_list.size(); i++)
576 auto new_elem_tet4 = std::make_unique<Tet4>();
577 new_elem_tet4->set_node(0,
const_cast<Node *
>(optimized_node_list[i][0]));
578 new_elem_tet4->set_node(1,
const_cast<Node *
>(optimized_node_list[i][1]));
579 new_elem_tet4->set_node(2,
const_cast<Node *
>(optimized_node_list[i][2]));
580 new_elem_tet4->set_node(3,
const_cast<Node *
>(optimized_node_list[i][3]));
581 new_elem_tet4->subdomain_id() =
mesh.elem_ptr(elem_id)->subdomain_id();
582 elems_tet4.push_back(
mesh.add_elem(std::move(new_elem_tet4)));
588 mesh.elem_ptr(elem_id)->subdomain_id() = block_id_to_remove;
591 for (
auto & elem_tet4 : elems_tet4)
593 if (new_elements_created)
595 if (elem_tet4->volume() < 0.0)
597 Node * temp = elem_tet4->node_ptr(0);
598 elem_tet4->set_node(0, elem_tet4->node_ptr(1));
599 elem_tet4->set_node(1, temp);
603 for (
unsigned int i = 0; i < 4; i++)
605 const Point & side_pt_0 = *elem_tet4->side_ptr(i)->node_ptr(0);
606 const Point & side_pt_1 = *elem_tet4->side_ptr(i)->node_ptr(1);
607 const Point & side_pt_2 = *elem_tet4->side_ptr(i)->node_ptr(2);
609 const Point side_normal = (side_pt_1 - side_pt_0).cross(side_pt_2 - side_pt_1).unit();
610 for (
unsigned int j = 0; j < 4; j++)
612 if (new_elements_created)
614 if (MooseUtils::absoluteFuzzyEqual(side_normal * elem_side_normal_list[j], 1.0))
616 for (
const auto & side_info : elem_side_list[j])
618 boundary_info.add_side(elem_tet4, i, side_info);