https://mooseframework.inl.gov
MooseMeshElementConversionUtils.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://mooseframework.inl.gov
3 //*
4 //* All rights reserved, see COPYRIGHT for full restrictions
5 //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 //*
7 //* Licensed under LGPL 2.1, please see LICENSE for details
8 //* https://www.gnu.org/licenses/lgpl-2.1.html
9 
10 // MOOSE includes
12 #include "MooseError.h"
13 
14 #include "libmesh/elem.h"
15 #include "libmesh/enum_order.h"
16 #include "libmesh/boundary_info.h"
17 #include "libmesh/mesh_base.h"
18 #include "libmesh/parallel.h"
19 #include "libmesh/parallel_algebra.h"
20 #include "libmesh/utility.h"
21 #include "libmesh/cell_tet4.h"
22 #include "libmesh/face_tri3.h"
23 
24 using namespace libMesh;
25 
27 {
28 void
30  const std::vector<libMesh::BoundaryInfo::BCTuple> & bdry_side_list,
31  const dof_id_type elem_id,
32  std::vector<dof_id_type> & converted_elems_ids)
33 {
34  // Build boundary information of the mesh
35  BoundaryInfo & boundary_info = mesh.get_boundary_info();
36  // Create a list of sidesets involving the element to be split
37  std::vector<std::vector<boundary_id_type>> elem_side_list;
38  elem_side_list.resize(6);
39  elementBoundaryInfoCollector(bdry_side_list, elem_id, 6, elem_side_list);
40 
41  const unsigned int n_elem_extra_ids = mesh.n_elem_integers();
42  std::vector<dof_id_type> exist_extra_ids(n_elem_extra_ids);
43  // Record all the element extra integers of the original quad element
44  for (unsigned int j = 0; j < n_elem_extra_ids; j++)
45  exist_extra_ids[j] = mesh.elem_ptr(elem_id)->get_extra_integer(j);
46 
47  std::vector<std::vector<unsigned int>> opt_option;
48  std::vector<const Node *> elem_node_list = {mesh.elem_ptr(elem_id)->node_ptr(0),
49  mesh.elem_ptr(elem_id)->node_ptr(1),
50  mesh.elem_ptr(elem_id)->node_ptr(2),
51  mesh.elem_ptr(elem_id)->node_ptr(3),
52  mesh.elem_ptr(elem_id)->node_ptr(4),
53  mesh.elem_ptr(elem_id)->node_ptr(5),
54  mesh.elem_ptr(elem_id)->node_ptr(6),
55  mesh.elem_ptr(elem_id)->node_ptr(7)};
56  std::vector<std::vector<unsigned int>> rotated_tet_face_indices;
57 
58  std::vector<std::vector<const Node *>> optimized_node_list;
59  hexNodesToTetNodesDeterminer(elem_node_list, rotated_tet_face_indices, optimized_node_list);
60 
61  std::vector<Elem *> elems_Tet4;
62  for (const auto i : index_range(optimized_node_list))
63  {
64  auto new_elem = std::make_unique<Tet4>();
65  new_elem->set_node(0, const_cast<Node *>(optimized_node_list[i][0]));
66  new_elem->set_node(1, const_cast<Node *>(optimized_node_list[i][1]));
67  new_elem->set_node(2, const_cast<Node *>(optimized_node_list[i][2]));
68  new_elem->set_node(3, const_cast<Node *>(optimized_node_list[i][3]));
69  new_elem->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id();
70  elems_Tet4.push_back(mesh.add_elem(std::move(new_elem)));
71  converted_elems_ids.push_back(elems_Tet4.back()->id());
72 
73  for (unsigned int j = 0; j < 4; j++)
74  {
75  // A hex element has 6 faces indexed from 0 to 5
76  // a <6 value indicates that the face of the tet element corresponds to the face of the
77  // original hex; a =6 value means the face of the tet is an interior face of the hex
78  if (rotated_tet_face_indices[i][j] < 6)
79  {
80  for (const auto & side_info : elem_side_list[rotated_tet_face_indices[i][j]])
81  boundary_info.add_side(elems_Tet4.back(), j, side_info);
82  }
83  }
84  }
85 
86  // Retain element extra integers
87  for (unsigned int i = 0; i < 6; i++)
88  for (unsigned int j = 0; j < n_elem_extra_ids; j++)
89  {
90  elems_Tet4[i]->set_extra_integer(j, exist_extra_ids[j]);
91  }
92 }
93 
94 void
96  const std::vector<libMesh::BoundaryInfo::BCTuple> & bdry_side_list,
97  const dof_id_type elem_id,
98  std::vector<dof_id_type> & converted_elems_ids)
99 {
100  // Build boundary information of the mesh
101  BoundaryInfo & boundary_info = mesh.get_boundary_info();
102  // Create a list of sidesets involving the element to be split
103  std::vector<std::vector<boundary_id_type>> elem_side_list;
104  elementBoundaryInfoCollector(bdry_side_list, elem_id, 5, elem_side_list);
105 
106  const unsigned int n_elem_extra_ids = mesh.n_elem_integers();
107  std::vector<dof_id_type> exist_extra_ids(n_elem_extra_ids);
108 
109  // Record all the element extra integers of the original quad element
110  for (unsigned int j = 0; j < n_elem_extra_ids; j++)
111  exist_extra_ids[j] = mesh.elem_ptr(elem_id)->get_extra_integer(j);
112 
113  std::vector<const Node *> elem_node_list = {mesh.elem_ptr(elem_id)->node_ptr(0),
114  mesh.elem_ptr(elem_id)->node_ptr(1),
115  mesh.elem_ptr(elem_id)->node_ptr(2),
116  mesh.elem_ptr(elem_id)->node_ptr(3),
117  mesh.elem_ptr(elem_id)->node_ptr(4),
118  mesh.elem_ptr(elem_id)->node_ptr(5)};
119  std::vector<std::vector<unsigned int>> rotated_tet_face_indices;
120  std::vector<std::vector<const Node *>> optimized_node_list;
121  prismNodesToTetNodesDeterminer(elem_node_list, rotated_tet_face_indices, optimized_node_list);
122 
123  std::vector<Elem *> elems_Tet4;
124  for (const auto i : index_range(optimized_node_list))
125  {
126  auto new_elem = std::make_unique<Tet4>();
127  new_elem->set_node(0, const_cast<Node *>(optimized_node_list[i][0]));
128  new_elem->set_node(1, const_cast<Node *>(optimized_node_list[i][1]));
129  new_elem->set_node(2, const_cast<Node *>(optimized_node_list[i][2]));
130  new_elem->set_node(3, const_cast<Node *>(optimized_node_list[i][3]));
131  new_elem->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id();
132  elems_Tet4.push_back(mesh.add_elem(std::move(new_elem)));
133  converted_elems_ids.push_back(elems_Tet4.back()->id());
134 
135  for (unsigned int j = 0; j < 4; j++)
136  {
137  // A prism element has 5 faces indexed from 0 to 4
138  // a <4 value indicates that the face of the tet element corresponds to the face of the
139  // original prism; a =5 value means the face of the tet is an interior face of the prism
140  if (rotated_tet_face_indices[i][j] < 5)
141  {
142  for (const auto & side_info : elem_side_list[rotated_tet_face_indices[i][j]])
143  boundary_info.add_side(elems_Tet4.back(), j, side_info);
144  }
145  }
146  }
147 
148  // Retain element extra integers
149  for (unsigned int i = 0; i < 3; i++)
150  for (unsigned int j = 0; j < n_elem_extra_ids; j++)
151  {
152  elems_Tet4[i]->set_extra_integer(j, exist_extra_ids[j]);
153  }
154 }
155 
156 void
158  const std::vector<libMesh::BoundaryInfo::BCTuple> & bdry_side_list,
159  const dof_id_type elem_id,
160  std::vector<dof_id_type> & converted_elems_ids)
161 {
162  // Build boundary information of the mesh
163  BoundaryInfo & boundary_info = mesh.get_boundary_info();
164  // Create a list of sidesets involving the element to be split
165  std::vector<std::vector<boundary_id_type>> elem_side_list;
166  elementBoundaryInfoCollector(bdry_side_list, elem_id, 5, elem_side_list);
167 
168  const unsigned int n_elem_extra_ids = mesh.n_elem_integers();
169  std::vector<dof_id_type> exist_extra_ids(n_elem_extra_ids);
170  // Record all the element extra integers of the original quad element
171  for (unsigned int j = 0; j < n_elem_extra_ids; j++)
172  exist_extra_ids[j] = mesh.elem_ptr(elem_id)->get_extra_integer(j);
173 
174  std::vector<const Node *> elem_node_list = {mesh.elem_ptr(elem_id)->node_ptr(0),
175  mesh.elem_ptr(elem_id)->node_ptr(1),
176  mesh.elem_ptr(elem_id)->node_ptr(2),
177  mesh.elem_ptr(elem_id)->node_ptr(3),
178  mesh.elem_ptr(elem_id)->node_ptr(4)};
179  std::vector<std::vector<unsigned int>> rotated_tet_face_indices;
180  std::vector<std::vector<const Node *>> optimized_node_list;
181  pyramidNodesToTetNodesDeterminer(elem_node_list, rotated_tet_face_indices, optimized_node_list);
182 
183  std::vector<Elem *> elems_Tet4;
184  for (const auto i : index_range(optimized_node_list))
185  {
186  auto new_elem = std::make_unique<Tet4>();
187  new_elem->set_node(0, const_cast<Node *>(optimized_node_list[i][0]));
188  new_elem->set_node(1, const_cast<Node *>(optimized_node_list[i][1]));
189  new_elem->set_node(2, const_cast<Node *>(optimized_node_list[i][2]));
190  new_elem->set_node(3, const_cast<Node *>(optimized_node_list[i][3]));
191  new_elem->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id();
192  elems_Tet4.push_back(mesh.add_elem(std::move(new_elem)));
193  converted_elems_ids.push_back(elems_Tet4.back()->id());
194 
195  for (unsigned int j = 0; j < 4; j++)
196  {
197  // A pyramid element has 5 faces indexed from 0 to 4
198  // a <4 value indicates that the face of the tet element corresponds to the face of the
199  // original pyramid; a =5 value means the face of the tet is an interior face of the pyramid
200  if (rotated_tet_face_indices[i][j] < 5)
201  {
202  for (const auto & side_info : elem_side_list[rotated_tet_face_indices[i][j]])
203  boundary_info.add_side(elems_Tet4.back(), j, side_info);
204  }
205  }
206  }
207 
208  // Retain element extra integers
209  for (unsigned int i = 0; i < 2; i++)
210  for (unsigned int j = 0; j < n_elem_extra_ids; j++)
211  {
212  elems_Tet4[i]->set_extra_integer(j, exist_extra_ids[j]);
213  }
214 }
215 
216 std::vector<unsigned int>
217 neighborNodeIndicesHEX8(unsigned int min_id_index)
218 {
219  const std::vector<std::vector<unsigned int>> preset_indices = {
220  {1, 3, 4}, {0, 2, 5}, {3, 1, 6}, {2, 0, 7}, {5, 7, 0}, {4, 6, 1}, {7, 5, 2}, {6, 4, 3}};
221  if (min_id_index > 7)
222  mooseError("The input node index is out of range.");
223  else
224  return preset_indices[min_id_index];
225 }
226 
227 void
228 hexNodesToTetNodesDeterminer(std::vector<const Node *> & hex_nodes,
229  std::vector<std::vector<unsigned int>> & rotated_tet_face_indices,
230  std::vector<std::vector<const Node *>> & tet_nodes_list)
231 {
232  // Find the node with the minimum id
233  std::vector<dof_id_type> node_ids(8);
234  for (unsigned int i = 0; i < 8; i++)
235  node_ids[i] = hex_nodes[i]->id();
236 
237  const unsigned int min_node_id_index = std::distance(
238  std::begin(node_ids), std::min_element(std::begin(node_ids), std::end(node_ids)));
239  // Get the index of the three neighbor nodes of the minimum node
240  // The order is consistent with the description in nodeRotationHEX8()
241  // Then determine the index of the second minimum node
242  const auto neighbor_node_indices = neighborNodeIndicesHEX8(min_node_id_index);
243 
244  const auto neighbor_node_ids = {node_ids[neighbor_node_indices[0]],
245  node_ids[neighbor_node_indices[1]],
246  node_ids[neighbor_node_indices[2]]};
247  const unsigned int sec_min_pos =
248  std::distance(std::begin(neighbor_node_ids),
249  std::min_element(std::begin(neighbor_node_ids), std::end(neighbor_node_ids)));
250 
251  // Rotate the node and face indices based on the identified minimum and second minimum nodes
252  // After the rotation, we guarantee that the minimum node is the first node (Node 0)
253  // And the second node (Node 1) has the minium global id among the three neighbor nodes of Node 0
254  // This makes the splitting process simpler
255  std::vector<unsigned int> face_rotation;
256  std::vector<unsigned int> rotated_indices;
257  nodeRotationHEX8(min_node_id_index, sec_min_pos, face_rotation, rotated_indices);
258  std::vector<const Node *> rotated_hex_nodes;
259  for (unsigned int i = 0; i < 8; i++)
260  rotated_hex_nodes.push_back(hex_nodes[rotated_indices[i]]);
261 
262  // Find the selection of each face's cutting direction
263  const auto diagonal_directions = quadFaceDiagonalDirectionsHex(rotated_hex_nodes);
264 
265  // Based on the determined splitting directions of all the faces, determine the nodes of each
266  // resulting TET4 elements after the splitting.
267  std::vector<std::vector<unsigned int>> tet_face_indices;
268  const auto tet_nodes_set = tetNodesForHex(diagonal_directions, tet_face_indices);
269  for (const auto & tet_face_index : tet_face_indices)
270  {
271  rotated_tet_face_indices.push_back(std::vector<unsigned int>());
272  for (const auto & face_index : tet_face_index)
273  {
274  if (face_index < 6)
275  rotated_tet_face_indices.back().push_back(face_rotation[face_index]);
276  else
277  rotated_tet_face_indices.back().push_back(6);
278  }
279  }
280 
281  for (const auto & tet_nodes : tet_nodes_set)
282  {
283  tet_nodes_list.push_back(std::vector<const Node *>());
284  for (const auto & tet_node : tet_nodes)
285  tet_nodes_list.back().push_back(rotated_hex_nodes[tet_node]);
286  }
287 }
288 
289 std::vector<bool>
290 quadFaceDiagonalDirectionsHex(const std::vector<const Node *> & hex_nodes)
291 {
292  // Bottom/Top; Front/Back; Right/Left
293  const std::vector<std::vector<unsigned int>> face_indices = {
294  {0, 1, 2, 3}, {4, 5, 6, 7}, {0, 1, 5, 4}, {2, 3, 7, 6}, {1, 2, 6, 5}, {3, 0, 4, 7}};
295  std::vector<bool> diagonal_directions;
296  for (const auto & face_index : face_indices)
297  {
298  std::vector<const Node *> quad_nodes = {hex_nodes[face_index[0]],
299  hex_nodes[face_index[1]],
300  hex_nodes[face_index[2]],
301  hex_nodes[face_index[3]]};
302  diagonal_directions.push_back(quadFaceDiagonalDirection(quad_nodes));
303  }
304  return diagonal_directions;
305 }
306 
307 bool
308 quadFaceDiagonalDirection(const std::vector<const Node *> & quad_nodes)
309 {
310  const std::vector<dof_id_type> node_ids = {
311  quad_nodes[0]->id(), quad_nodes[1]->id(), quad_nodes[2]->id(), quad_nodes[3]->id()};
312  const unsigned int min_id_index = std::distance(
313  std::begin(node_ids), std::min_element(std::begin(node_ids), std::end(node_ids)));
314  if (min_id_index == 0 || min_id_index == 2)
315  return true;
316  else
317  return false;
318 }
319 
320 std::vector<std::vector<unsigned int>>
321 tetNodesForHex(const std::vector<bool> diagonal_directions,
322  std::vector<std::vector<unsigned int>> & tet_face_indices)
323 {
324  const std::vector<std::vector<bool>> possible_inputs = {{true, true, true, true, true, false},
325  {true, true, true, true, false, false},
326  {true, true, true, false, true, false},
327  {true, false, true, true, true, false},
328  {true, false, true, true, false, false},
329  {true, false, true, false, true, false},
330  {true, false, true, false, false, false}};
331 
332  const unsigned int input_index = std::distance(
333  std::begin(possible_inputs),
334  std::find(std::begin(possible_inputs), std::end(possible_inputs), diagonal_directions));
335 
336  switch (input_index)
337  {
338  case 0:
339  tet_face_indices = {
340  {0, 6, 2, 6}, {1, 6, 2, 6}, {1, 6, 5, 6}, {0, 6, 3, 4}, {6, 6, 3, 6}, {6, 4, 5, 6}};
341  return {{0, 1, 2, 6}, {0, 5, 1, 6}, {0, 4, 5, 6}, {0, 2, 3, 7}, {0, 6, 2, 7}, {0, 4, 6, 7}};
342  case 1:
343  tet_face_indices = {
344  {0, 1, 2, 6}, {6, 6, 2, 6}, {6, 6, 5, 1}, {0, 6, 3, 4}, {6, 6, 3, 6}, {6, 4, 5, 6}};
345  return {{0, 1, 2, 5}, {0, 2, 6, 5}, {0, 6, 4, 5}, {0, 2, 3, 7}, {0, 6, 2, 7}, {0, 4, 6, 7}};
346  case 2:
347  tet_face_indices = {
348  {0, 6, 2, 6}, {1, 6, 2, 6}, {1, 6, 5, 6}, {4, 6, 5, 6}, {4, 6, 3, 6}, {0, 6, 3, 6}};
349  return {{0, 1, 2, 6}, {0, 5, 1, 6}, {0, 4, 5, 6}, {0, 7, 4, 6}, {0, 3, 7, 6}, {0, 2, 3, 6}};
350  case 3:
351  tet_face_indices = {
352  {4, 6, 5, 1}, {6, 6, 5, 6}, {6, 1, 2, 6}, {4, 0, 3, 6}, {6, 6, 3, 6}, {6, 6, 2, 0}};
353  return {{0, 7, 4, 5}, {0, 6, 7, 5}, {0, 1, 6, 5}, {0, 3, 7, 2}, {0, 7, 6, 2}, {0, 6, 1, 2}};
354  case 4:
355  tet_face_indices = {{0, 1, 2, 6}, {0, 6, 3, 4}, {5, 4, 6, 1}, {5, 6, 3, 2}, {6, 6, 6, 6}};
356  return {{0, 1, 2, 5}, {0, 2, 3, 7}, {4, 7, 5, 0}, {5, 7, 6, 2}, {0, 2, 7, 5}};
357  case 5:
358  tet_face_indices = {
359  {4, 6, 5, 1}, {6, 6, 5, 6}, {6, 1, 2, 6}, {2, 6, 6, 0}, {3, 6, 6, 0}, {3, 6, 6, 4}};
360  return {{0, 7, 4, 5}, {0, 6, 7, 5}, {0, 1, 6, 5}, {1, 6, 2, 0}, {2, 6, 3, 0}, {3, 6, 7, 0}};
361  case 6:
362  tet_face_indices = {
363  {1, 4, 5, 6}, {6, 6, 5, 6}, {6, 6, 3, 4}, {1, 6, 2, 0}, {6, 6, 2, 6}, {6, 0, 3, 6}};
364  return {{0, 4, 5, 7}, {0, 5, 6, 7}, {0, 6, 3, 7}, {0, 5, 1, 2}, {0, 6, 5, 2}, {0, 3, 6, 2}};
365  default:
366  mooseError("Unexpected input.");
367  }
368 }
369 
370 void
371 nodeRotationHEX8(const unsigned int min_id_index,
372  const unsigned int sec_min_pos,
373  std::vector<unsigned int> & face_rotation,
374  std::vector<unsigned int> & node_rotation)
375 {
376  // Assuming the original hex element is a cube, the vectors formed by nodes 0-1, 0-3, and 0-4 are
377  // overlapped with the x, y, and z axes, respectively. sec_min_pos = 0 means the second minimum
378  // node is in the x direction, sec_min_pos = 1 means the second minimum node is in the y
379  // direction, and sec_min_pos = 2 means the second minimum node is in the z direction.
380  const std::vector<std::vector<std::vector<unsigned int>>> preset_indices = {
381  {{0, 1, 2, 3, 4, 5, 6, 7}, {0, 3, 7, 4, 1, 2, 6, 5}, {0, 4, 5, 1, 3, 7, 6, 2}},
382  {{1, 0, 4, 5, 2, 3, 7, 6}, {1, 2, 3, 0, 5, 6, 7, 4}, {1, 5, 6, 2, 0, 4, 7, 3}},
383  {{2, 3, 0, 1, 6, 7, 4, 5}, {2, 1, 5, 6, 3, 0, 4, 7}, {2, 6, 7, 3, 1, 5, 4, 0}},
384  {{3, 2, 6, 7, 0, 1, 5, 4}, {3, 0, 1, 2, 7, 4, 5, 6}, {3, 7, 4, 0, 2, 6, 5, 1}},
385  {{4, 5, 1, 0, 7, 6, 2, 3}, {4, 7, 6, 5, 0, 3, 2, 1}, {4, 0, 3, 7, 5, 1, 2, 6}},
386  {{5, 4, 7, 6, 1, 0, 3, 2}, {5, 6, 2, 1, 4, 7, 3, 0}, {5, 1, 0, 4, 6, 2, 3, 7}},
387  {{6, 7, 3, 2, 5, 4, 0, 1}, {6, 5, 4, 7, 2, 1, 0, 3}, {6, 2, 1, 5, 7, 3, 0, 4}},
388  {{7, 6, 5, 4, 3, 2, 1, 0}, {7, 4, 0, 3, 6, 5, 1, 2}, {7, 3, 2, 6, 4, 0, 1, 5}}};
389 
390  const std::vector<std::vector<std::vector<unsigned int>>> preset_face_indices = {
391  {{0, 1, 2, 3, 4, 5}, {4, 0, 3, 5, 1, 2}, {1, 4, 5, 2, 0, 3}},
392  {{1, 0, 4, 5, 2, 3}, {0, 2, 3, 4, 1, 5}, {2, 1, 5, 3, 0, 4}},
393  {{0, 3, 4, 1, 2, 5}, {2, 0, 1, 5, 3, 4}, {3, 2, 5, 4, 0, 1}},
394  {{3, 0, 2, 5, 4, 1}, {0, 4, 1, 2, 3, 5}, {4, 3, 5, 1, 0, 2}},
395  {{1, 5, 2, 0, 4, 3}, {5, 4, 3, 2, 1, 0}, {4, 1, 0, 3, 5, 2}},
396  {{5, 1, 4, 3, 2, 0}, {2, 5, 3, 0, 1, 4}, {1, 2, 0, 4, 5, 3}},
397  {{3, 5, 4, 0, 2, 1}, {5, 2, 1, 4, 3, 0}, {2, 3, 0, 1, 5, 4}},
398  {{5, 3, 2, 1, 4, 0}, {4, 5, 1, 0, 3, 2}, {3, 4, 0, 2, 5, 1}}};
399 
400  if (min_id_index > 7 || sec_min_pos > 2)
401  mooseError("The input node index is out of range.");
402  else
403  {
404  // index: new face index; value: old face index
405  face_rotation = preset_face_indices[min_id_index][sec_min_pos];
406  node_rotation = preset_indices[min_id_index][sec_min_pos];
407  }
408 }
409 
410 void
411 nodeRotationPRISM6(unsigned int min_id_index,
412  std::vector<unsigned int> & face_rotation,
413  std::vector<unsigned int> & node_rotation)
414 {
415  const std::vector<std::vector<unsigned int>> preset_indices = {{0, 1, 2, 3, 4, 5},
416  {1, 2, 0, 4, 5, 3},
417  {2, 0, 1, 5, 3, 4},
418  {3, 5, 4, 0, 2, 1},
419  {4, 3, 5, 1, 0, 2},
420  {5, 4, 3, 2, 1, 0}};
421 
422  const std::vector<std::vector<unsigned int>> preset_face_indices = {{0, 1, 2, 3, 4},
423  {0, 2, 3, 1, 4},
424  {0, 3, 1, 2, 4},
425  {4, 3, 2, 1, 0},
426  {4, 1, 3, 2, 0},
427  {4, 2, 1, 3, 0}};
428 
429  if (min_id_index > 5)
430  mooseError("The input node index is out of range.");
431  else
432  {
433  // index: new face index; value: old face index
434  face_rotation = preset_face_indices[min_id_index];
435  node_rotation = preset_indices[min_id_index];
436  }
437 }
438 
439 void
440 prismNodesToTetNodesDeterminer(std::vector<const Node *> & prism_nodes,
441  std::vector<std::vector<unsigned int>> & rotated_tet_face_indices,
442  std::vector<std::vector<const Node *>> & tet_nodes_list)
443 {
444  // Find the node with the minimum id
445  std::vector<dof_id_type> node_ids(6);
446  for (unsigned int i = 0; i < 6; i++)
447  node_ids[i] = prism_nodes[i]->id();
448 
449  const unsigned int min_node_id_index = std::distance(
450  std::begin(node_ids), std::min_element(std::begin(node_ids), std::end(node_ids)));
451 
452  // Rotate the node and face indices based on the identified minimum node
453  // After the rotation, we guarantee that the minimum node is the first node (Node 0)
454  // This makes the splitting process simpler
455  std::vector<unsigned int> face_rotation;
456  std::vector<unsigned int> rotated_indices;
457  nodeRotationPRISM6(min_node_id_index, face_rotation, rotated_indices);
458  std::vector<const Node *> rotated_prism_nodes;
459  for (unsigned int i = 0; i < 6; i++)
460  rotated_prism_nodes.push_back(prism_nodes[rotated_indices[i]]);
461 
462  std::vector<const Node *> key_quad_nodes = {rotated_prism_nodes[1],
463  rotated_prism_nodes[2],
464  rotated_prism_nodes[5],
465  rotated_prism_nodes[4]};
466 
467  // Find the selection of each face's cutting direction
468  const bool diagonal_direction = quadFaceDiagonalDirection(key_quad_nodes);
469 
470  // Based on the determined splitting directions of all the faces, determine the nodes of each
471  // resulting TET4 elements after the splitting.
472  std::vector<std::vector<unsigned int>> tet_face_indices;
473  const auto tet_nodes_set = tetNodesForPrism(diagonal_direction, tet_face_indices);
474  for (const auto & tet_face_index : tet_face_indices)
475  {
476  rotated_tet_face_indices.push_back(std::vector<unsigned int>());
477  for (const auto & face_index : tet_face_index)
478  {
479  if (face_index < 5)
480  rotated_tet_face_indices.back().push_back(face_rotation[face_index]);
481  else
482  rotated_tet_face_indices.back().push_back(5);
483  }
484  }
485 
486  for (const auto & tet_nodes : tet_nodes_set)
487  {
488  tet_nodes_list.push_back(std::vector<const Node *>());
489  for (const auto & tet_node : tet_nodes)
490  tet_nodes_list.back().push_back(rotated_prism_nodes[tet_node]);
491  }
492 }
493 
494 std::vector<std::vector<unsigned int>>
495 tetNodesForPrism(const bool diagonal_direction,
496  std::vector<std::vector<unsigned int>> & tet_face_indices)
497 {
498 
499  if (diagonal_direction)
500  {
501  tet_face_indices = {{4, 3, 5, 1}, {2, 1, 5, 5}, {2, 5, 3, 0}};
502  return {{3, 5, 4, 0}, {1, 4, 5, 0}, {1, 5, 2, 0}};
503  }
504  else
505  {
506  tet_face_indices = {{4, 3, 5, 1}, {2, 1, 5, 0}, {2, 5, 5, 3}};
507  return {{3, 5, 4, 0}, {1, 4, 2, 0}, {2, 4, 5, 0}};
508  }
509 }
510 
511 void
512 nodeRotationPYRAMID5(unsigned int min_id_index,
513  std::vector<unsigned int> & face_rotation,
514  std::vector<unsigned int> & node_rotation)
515 {
516  const std::vector<std::vector<unsigned int>> preset_indices = {
517  {0, 1, 2, 3, 4}, {1, 2, 3, 0, 4}, {2, 3, 0, 1, 4}, {3, 0, 1, 2, 4}};
518 
519  const std::vector<std::vector<unsigned int>> preset_face_indices = {
520  {0, 1, 2, 3, 4}, {1, 2, 3, 0, 4}, {2, 3, 0, 1, 4}, {3, 0, 1, 2, 4}};
521 
522  if (min_id_index > 3)
523  mooseError("The input node index is out of range.");
524  else
525  {
526  // index: new face index; value: old face index
527  face_rotation = preset_face_indices[min_id_index];
528  node_rotation = preset_indices[min_id_index];
529  }
530 }
531 
532 void
533 pyramidNodesToTetNodesDeterminer(std::vector<const Node *> & pyramid_nodes,
534  std::vector<std::vector<unsigned int>> & rotated_tet_face_indices,
535  std::vector<std::vector<const Node *>> & tet_nodes_list)
536 {
537  // Find the node with the minimum id, ignoring the top node
538  std::vector<dof_id_type> node_ids(4);
539  for (unsigned int i = 0; i < 4; i++)
540  node_ids[i] = pyramid_nodes[i]->id();
541 
542  const unsigned int min_node_id_index = std::distance(
543  std::begin(node_ids), std::min_element(std::begin(node_ids), std::end(node_ids)));
544 
545  // Rotate the node and face indices based on the identified minimum nodes
546  // After the rotation, we guarantee that the minimum node is the first node (Node 0)
547  // This makes the splitting process simpler
548  std::vector<unsigned int> face_rotation;
549  std::vector<unsigned int> rotated_indices;
550  nodeRotationPYRAMID5(min_node_id_index, face_rotation, rotated_indices);
551  std::vector<const Node *> rotated_pyramid_nodes;
552  for (unsigned int i = 0; i < 5; i++)
553  rotated_pyramid_nodes.push_back(pyramid_nodes[rotated_indices[i]]);
554 
555  // There is only one quad face in a pyramid element, so the splitting selection is binary
556  const std::vector<std::vector<unsigned int>> tet_nodes_set = {{0, 1, 2, 4}, {0, 2, 3, 4}};
557  const std::vector<std::vector<unsigned int>> tet_face_indices = {{4, 0, 1, 5}, {4, 5, 2, 3}};
558 
559  // Based on the determined splitting direction, determine the nodes of each resulting TET4
560  // elements after the splitting.
561  for (const auto & tet_face_index : tet_face_indices)
562  {
563  rotated_tet_face_indices.push_back(std::vector<unsigned int>());
564  for (const auto & face_index : tet_face_index)
565  {
566  if (face_index < 5)
567  rotated_tet_face_indices.back().push_back(face_rotation[face_index]);
568  else
569  rotated_tet_face_indices.back().push_back(5);
570  }
571  }
572 
573  for (const auto & tet_nodes : tet_nodes_set)
574  {
575  tet_nodes_list.push_back(std::vector<const Node *>());
576  for (const auto & tet_node : tet_nodes)
577  tet_nodes_list.back().push_back(rotated_pyramid_nodes[tet_node]);
578  }
579 }
580 
581 void
583  const std::vector<std::pair<dof_id_type, bool>> & elems_to_process,
584  std::vector<dof_id_type> & converted_elems_ids_to_track,
585  const subdomain_id_type block_id_to_remove,
586  const bool delete_block_to_remove)
587 {
588  std::vector<dof_id_type> converted_elems_ids_to_retain;
589  // Build boundary information of the mesh
590  BoundaryInfo & boundary_info = mesh.get_boundary_info();
591  const auto bdry_side_list = boundary_info.build_side_list();
592  for (const auto & elem_to_process : elems_to_process)
593  {
594  switch (mesh.elem_ptr(elem_to_process.first)->type())
595  {
596  case ElemType::HEX8:
598  bdry_side_list,
599  elem_to_process.first,
600  elem_to_process.second ? converted_elems_ids_to_track
601  : converted_elems_ids_to_retain);
602  mesh.elem_ptr(elem_to_process.first)->subdomain_id() = block_id_to_remove;
603  break;
604  case ElemType::PYRAMID5:
606  bdry_side_list,
607  elem_to_process.first,
608  elem_to_process.second ? converted_elems_ids_to_track
609  : converted_elems_ids_to_retain);
610  mesh.elem_ptr(elem_to_process.first)->subdomain_id() = block_id_to_remove;
611  break;
612  case ElemType::PRISM6:
614  bdry_side_list,
615  elem_to_process.first,
616  elem_to_process.second ? converted_elems_ids_to_track
617  : converted_elems_ids_to_retain);
618  mesh.elem_ptr(elem_to_process.first)->subdomain_id() = block_id_to_remove;
619  break;
620  case ElemType::TET4:
621  if (elem_to_process.second)
622  converted_elems_ids_to_track.push_back(elem_to_process.first);
623  else
624  converted_elems_ids_to_retain.push_back(elem_to_process.first);
625  break;
626  default:
627  mooseError("Unexpected element type.");
628  }
629  }
630 
631  if (delete_block_to_remove)
632  {
633  for (auto elem_it = mesh.active_subdomain_elements_begin(block_id_to_remove);
634  elem_it != mesh.active_subdomain_elements_end(block_id_to_remove);
635  elem_it++)
636  mesh.delete_elem(*elem_it);
637 
638  mesh.contract();
640  }
641 }
642 
643 void
645 {
646  // Subdomain ID for new utility blocks must be new
647  std::set<subdomain_id_type> subdomain_ids_set;
648  mesh.subdomain_ids(subdomain_ids_set);
649  const subdomain_id_type max_subdomain_id = *subdomain_ids_set.rbegin();
650  const subdomain_id_type block_id_to_remove = max_subdomain_id + 1;
651  std::vector<std::pair<dof_id_type, bool>> original_elems;
652 
653  for (auto elem_it = mesh.active_elements_begin(); elem_it != mesh.active_elements_end();
654  elem_it++)
655  {
656  if ((*elem_it)->default_order() != Order::FIRST)
657  mooseError("Only first order elements are supported for cutting.");
658  original_elems.push_back(std::make_pair((*elem_it)->id(), false));
659  }
660 
661  std::vector<dof_id_type> converted_elems_ids_to_track;
662 
664  mesh, original_elems, converted_elems_ids_to_track, block_id_to_remove, true);
665 }
666 
667 void
668 elementBoundaryInfoCollector(const std::vector<libMesh::BoundaryInfo::BCTuple> & bdry_side_list,
669  const dof_id_type elem_id,
670  const unsigned short n_elem_sides,
671  std::vector<std::vector<boundary_id_type>> & elem_side_list)
672 {
673  elem_side_list.resize(n_elem_sides);
674  const auto selected_bdry_side_list =
675  std::equal_range(bdry_side_list.begin(), bdry_side_list.end(), elem_id, BCTupleKeyComp{});
676  for (auto selected_bdry_side = selected_bdry_side_list.first;
677  selected_bdry_side != selected_bdry_side_list.second;
678  ++selected_bdry_side)
679  {
680  elem_side_list[std::get<1>(*selected_bdry_side)].push_back(std::get<2>(*selected_bdry_side));
681  }
682 }
683 }
void pyramidElemSplitter(ReplicatedMesh &mesh, const std::vector< libMesh::BoundaryInfo::BCTuple > &bdry_side_list, const dof_id_type elem_id, std::vector< dof_id_type > &converted_elems_ids)
std::vector< bool > quadFaceDiagonalDirectionsHex(const std::vector< const Node *> &hex_nodes)
void nodeRotationHEX8(const unsigned int min_id_index, const unsigned int sec_min_pos, std::vector< unsigned int > &face_rotation, std::vector< unsigned int > &node_rotation)
Rotate a HEX8 element&#39;s nodes to ensure that the node with the minimum id is the first node; and the ...
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:302
void prepare_for_use(const bool skip_renumber_nodes_and_elements, const bool skip_find_neighbors)
unsigned int n_elem_integers() const
MeshBase & mesh
bool quadFaceDiagonalDirection(const std::vector< const Node *> &quad_nodes)
void hexNodesToTetNodesDeterminer(std::vector< const Node *> &hex_nodes, std::vector< std::vector< unsigned int >> &rotated_tet_face_indices, std::vector< std::vector< const Node *>> &tet_nodes_list)
void elementBoundaryInfoCollector(const std::vector< libMesh::BoundaryInfo::BCTuple > &bdry_side_list, const dof_id_type elem_id, const unsigned short n_elem_sides, std::vector< std::vector< boundary_id_type >> &elem_side_list)
Collect the boundary information of the given element in a mesh.
std::vector< unsigned int > neighborNodeIndicesHEX8(unsigned int min_id_index)
Calculate the indices (within the element nodes) of the three neighboring nodes of a node in a HEX8 e...
The following methods are specializations for using the libMesh::Parallel::packed_range_* routines fo...
const BoundaryInfo & get_boundary_info() const
void build_side_list(std::vector< dof_id_type > &element_id_list, std::vector< unsigned short int > &side_list, std::vector< boundary_id_type > &bc_id_list) const
std::vector< std::vector< unsigned int > > tetNodesForPrism(const bool diagonal_direction, std::vector< std::vector< unsigned int >> &tet_face_indices)
Creates sets of four nodes indices that can form TET4 elements to replace the original PRISM6 element...
virtual void delete_elem(Elem *e)=0
virtual Elem * add_elem(Elem *e)=0
void nodeRotationPRISM6(unsigned int min_id_index, std::vector< unsigned int > &face_rotation, std::vector< unsigned int > &node_rotation)
Rotate a PRISM6 element nodes to ensure that the node with the minimum id is the first node...
void subdomain_ids(std::set< subdomain_id_type > &ids, const bool global=true) const
void nodeRotationPYRAMID5(unsigned int min_id_index, std::vector< unsigned int > &face_rotation, std::vector< unsigned int > &node_rotation)
Rotate a PYRAMID5 element nodes to ensure that the node with the minimum id is the first node for the...
virtual const Elem * elem_ptr(const dof_id_type i) const=0
void prismElemSplitter(ReplicatedMesh &mesh, const std::vector< libMesh::BoundaryInfo::BCTuple > &bdry_side_list, const dof_id_type elem_id, std::vector< dof_id_type > &converted_elems_ids)
virtual bool contract()=0
subdomain_id_type subdomain_id() const
const Node * node_ptr(const unsigned int i) const
void pyramidNodesToTetNodesDeterminer(std::vector< const Node *> &pyramid_nodes, std::vector< std::vector< unsigned int >> &rotated_tet_face_indices, std::vector< std::vector< const Node *>> &tet_nodes_list)
void hexElemSplitter(ReplicatedMesh &mesh, const std::vector< libMesh::BoundaryInfo::BCTuple > &bdry_side_list, const dof_id_type elem_id, std::vector< dof_id_type > &converted_elems_ids)
void add_side(const dof_id_type elem, const unsigned short int side, const boundary_id_type id)
std::vector< std::vector< unsigned int > > tetNodesForHex(const std::vector< bool > diagonal_directions, std::vector< std::vector< unsigned int >> &tet_face_indices)
Creates sets of four nodes indices that can form TET4 elements to replace the original HEX8 element...
void prismNodesToTetNodesDeterminer(std::vector< const Node *> &prism_nodes, std::vector< std::vector< unsigned int >> &rotated_tet_face_indices, std::vector< std::vector< const Node *>> &tet_nodes_list)
virtual ElemType type() const=0
auto index_range(const T &sizable)
dof_id_type get_extra_integer(const unsigned int index) const
uint8_t dof_id_type