https://mooseframework.inl.gov
Functions
MooseMeshXYCuttingUtils Namespace Reference

Functions

void lineRemoverMoveNode (libMesh::ReplicatedMesh &mesh, const std::vector< Real > &bdry_pars, const subdomain_id_type block_id_to_remove, const std::set< subdomain_id_type > &subdomain_ids_set, const boundary_id_type trimming_section_boundary_id, const boundary_id_type external_boundary_id, const std::vector< boundary_id_type > &other_boundaries_to_conform=std::vector< boundary_id_type >(), const bool assign_ext_to_new=false, const bool side_to_remove=true)
 Removes all the elements on one side of a given line and deforms the elements intercepted by the line to form a flat new boundary. More...
 
bool lineSideDeterminator (const Real px, const Real py, const Real param_1, const Real param_2, const Real param_3, const bool direction_param, const Real dis_tol=libMesh::TOLERANCE)
 Determines whether a point on XY-plane is on the side of a given line that needs to be removed. More...
 
Point twoLineIntersection (const Real param_11, const Real param_12, const Real param_13, const Real param_21, const Real param_22, const Real param_23)
 Calculates the intersection Point of two given straight lines. More...
 
Point twoPointandLineIntersection (const Point &pt1, const Point &pt2, const Real param_1, const Real param_2, const Real param_3)
 Calculates the intersection Point of a straight line defined by two given points and another straight line. More...
 
bool quasiTriElementsFixer (libMesh::ReplicatedMesh &mesh, const std::set< subdomain_id_type > &subdomain_ids_set, const subdomain_id_type tri_elem_subdomain_shift=Moose::INVALID_BLOCK_ID, const SubdomainName tri_elem_subdomain_name_suffix="tri")
 Fixes degenerate QUAD elements created by the hexagonal mesh trimming by converting them into TRI elements. More...
 
std::vector< std::pair< Real, unsigned int > > vertex_angles (const Elem &elem)
 Calculates the internal angles of a given 2D element. More...
 
std::vector< std::pair< Real, unsigned int > > vertex_distances (const Elem &elem)
 Calculates the distances between the vertices of a given 2D element. More...
 
void triElemSplitter (libMesh::ReplicatedMesh &mesh, const dof_id_type elem_id, const unsigned short node_shift, const dof_id_type nid_3, const dof_id_type nid_4, const subdomain_id_type single_elem_side_id, const subdomain_id_type double_elem_side_id)
 Split a TRI3 element into three TRI3 elements based on two nodes on the two sides of the triangle. More...
 
void triElemSplitter (libMesh::ReplicatedMesh &mesh, const dof_id_type elem_id, const unsigned short node_shift, const dof_id_type nid_m, const subdomain_id_type first_elem_side_id, const subdomain_id_type second_elem_side_id)
 Split a TRI3 element into two TRI3 elements based on one node on one side of the triangle. More...
 
void quadElemSplitter (libMesh::ReplicatedMesh &mesh, const dof_id_type elem_id, const subdomain_id_type tri_elem_subdomain_shift)
 Split a QUAD4 element into two TRI3 elements. More...
 
void quadToTriOnLine (libMesh::ReplicatedMesh &mesh, const std::vector< Real > &cut_line_params, const dof_id_type tri_subdomain_id_shift, const SubdomainName tri_elem_subdomain_name_suffix)
 Convert all the QUAD4 elements in the mesh that are crossed by the given line into TRI3 elements. More...
 
void lineRemoverCutElemTri (libMesh::ReplicatedMesh &mesh, const std::vector< Real > &cut_line_params, const subdomain_id_type block_id_to_remove, const boundary_id_type new_boundary_id)
 Trim the 2D mesh by removing all the elements on one side of the given line. More...
 
void lineRemoverCutElem (libMesh::ReplicatedMesh &mesh, const std::vector< Real > &cut_line_params, const dof_id_type tri_subdomain_id_shift, const SubdomainName tri_elem_subdomain_name_suffix, const subdomain_id_type block_id_to_remove, const boundary_id_type new_boundary_id, const bool improve_boundary_tri_elems=false)
 Trim the 2D mesh by removing all the elements on one side of the given line. More...
 
void boundaryTriElemImprover (libMesh::ReplicatedMesh &mesh, const boundary_id_type boundary_to_improve)
 Improve the element quality of the boundary TRI3 elements of the given boundary. More...
 
void makeImprovedTriElement (libMesh::ReplicatedMesh &mesh, const dof_id_type node_id_0, const dof_id_type node_id_1, const dof_id_type node_id_2, const subdomain_id_type subdomain_id, const std::vector< dof_id_type > &extra_elem_ids, const std::vector< boundary_id_type > &boundary_ids_for_side_1=std::vector< boundary_id_type >(), const std::vector< boundary_id_type > &boundary_ids_for_side_0=std::vector< boundary_id_type >(), const std::vector< boundary_id_type > &boundary_ids_for_side_2=std::vector< boundary_id_type >())
 Make a TRI3 element with the given node ids and subdomain id with boundary information. More...
 
bool elemSideLocator (libMesh::ReplicatedMesh &mesh, const dof_id_type elem_id, const dof_id_type node_id_0, const dof_id_type node_id_1, unsigned short &side_id, bool &is_inverse)
 Check if there is a side in an element that contains the given pair of nodes; if yes, also find the side id and the direction of the two nodes in the side. More...
 
Point twoPointandLineIntersection (const Point &pt1, const Point &pt2, const Real param_1, const Real param_2, const Real param_3)
 
std::vector< std::pair< Real, unsigned int > > vertex_angles (const Elem &elem)
 
std::vector< std::pair< Real, unsigned int > > vertex_distances (const Elem &elem)
 

Function Documentation

◆ boundaryTriElemImprover()

void MooseMeshXYCuttingUtils::boundaryTriElemImprover ( libMesh::ReplicatedMesh mesh,
const boundary_id_type  boundary_to_improve 
)

Improve the element quality of the boundary TRI3 elements of the given boundary.

Parameters
meshinput mesh with the boundary TRI3 elements that need to be improved
boundary_to_improveboundary id of the boundary that needs to be improved

Definition at line 981 of file MooseMeshXYCuttingUtils.C.

Referenced by lineRemoverCutElem().

982 {
983  if (!MooseMeshUtils::hasBoundaryID(mesh, boundary_to_improve))
984  mooseError(
985  "MooseMeshXYCuttingUtils::boundaryTriElemImprover(): The boundary_to_improve provided "
986  "does not exist in the given mesh.");
987  BoundaryInfo & boundary_info = mesh.get_boundary_info();
988  auto side_list = boundary_info.build_side_list();
989  // Here we would like to collect the following information for all the TRI3 elements on the
990  // boundary: Key: node id of the off-boundary node Value: a vector of tuples, each tuple contains
991  // the following information:
992  // 1. The element id of the element that is on the boundary to improve
993  // 2. the one node id of that element that is on the boundary to improve
994  // 3. the other node id of the element that is on the boundary to improve
995  std::map<dof_id_type, std::vector<std::tuple<dof_id_type, dof_id_type, dof_id_type>>>
996  tri3_elem_info;
997  for (const auto & side : side_list)
998  {
999  if (std::get<2>(side) == boundary_to_improve)
1000  {
1001  Elem * elem = mesh.elem_ptr(std::get<0>(side));
1002  if (elem->type() == TRI3)
1003  {
1004  const auto key_node_id = elem->node_id((std::get<1>(side) + 2) % 3);
1005  const auto value_elem_id = elem->id();
1006  const auto value_node_id_1 = elem->node_id(std::get<1>(side));
1007  const auto value_node_id_2 = elem->node_id((std::get<1>(side) + 1) % 3);
1008  tri3_elem_info[key_node_id].push_back(
1009  std::make_tuple(value_elem_id, value_node_id_1, value_node_id_2));
1010  }
1011  }
1012  }
1013  // Elements that need to be removed
1014  std::vector<dof_id_type> elems_to_remove;
1015  // Now check if any group of TRI3 sharing an off-boundary node can be improved.
1016  for (const auto & tri_group : tri3_elem_info)
1017  {
1018  // It is possible to improve only when more than one TRI3 elements share the same off-boundary
1019  // node
1020  std::vector<std::pair<dof_id_type, dof_id_type>> node_assm;
1021  std::vector<dof_id_type> elem_id_list;
1022  for (const auto & tri : tri_group.second)
1023  {
1024  node_assm.push_back(std::make_pair(std::get<1>(tri), std::get<2>(tri)));
1025  elem_id_list.push_back(std::get<0>(tri));
1026  }
1027  std::vector<dof_id_type> ordered_node_list;
1028  std::vector<dof_id_type> ordered_elem_list;
1030  node_assm, elem_id_list, ordered_node_list, ordered_elem_list);
1031 
1032  // For all the elements sharing the same off-boundary node, we need to know how many separated
1033  // subdomains are involved
1034  // If there are extra element ids defined on the mesh, they also want to retain their boundaries
1035  // Only triangle elements that share a side can be merged
1036  const unsigned int n_elem_extra_ids = mesh.n_elem_integers();
1037  std::vector<std::tuple<subdomain_id_type, std::vector<dof_id_type>, unsigned int>> blocks_info;
1038  for (const auto & elem_id : ordered_elem_list)
1039  {
1040  std::vector<dof_id_type> exist_extra_ids(n_elem_extra_ids);
1041  // Record all the element extra integers of the original quad element
1042  for (unsigned int j = 0; j < n_elem_extra_ids; j++)
1043  exist_extra_ids[j] = mesh.elem_ptr(elem_id)->get_extra_integer(j);
1044  if (!blocks_info.empty())
1045  {
1046  if (mesh.elem_ptr(elem_id)->subdomain_id() == std::get<0>(blocks_info.back()) &&
1047  exist_extra_ids == std::get<1>(blocks_info.back()))
1048  {
1049  std::get<2>(blocks_info.back())++;
1050  continue;
1051  }
1052  }
1053  blocks_info.push_back(
1054  std::make_tuple(mesh.elem_ptr(elem_id)->subdomain_id(), exist_extra_ids, 1));
1055  }
1056  // For each separated subdomain / set of extra ids, we try to improve the boundary elements
1057  unsigned int side_counter = 0;
1058  for (const auto & block_info : blocks_info)
1059  {
1060  const auto node_1 = mesh.node_ptr(ordered_node_list[side_counter]);
1061  // we do not need to subtract 1 for node_2
1062  const auto node_2 = mesh.node_ptr(ordered_node_list[side_counter + std::get<2>(block_info)]);
1063  const auto node_0 = mesh.node_ptr(tri_group.first);
1064  const Point v1 = *node_1 - *node_0;
1065  const Point v2 = *node_2 - *node_0;
1066  const Real angle = std::acos(v1 * v2 / v1.norm() / v2.norm()) / M_PI * 180.0;
1067  const std::vector<dof_id_type> block_elems(ordered_elem_list.begin() + side_counter,
1068  ordered_elem_list.begin() + side_counter +
1069  std::get<2>(block_info));
1070  // We assume that there are no sidesets defined inside a subdomain
1071  // For the first TRI3 element, we want to check if its side defined by node_0 and node_1 is
1072  // defined in any sidesets
1073  unsigned short side_id_0;
1074  unsigned short side_id_t;
1075  bool is_inverse_0;
1076  bool is_inverse_t;
1077  elemSideLocator(mesh,
1078  block_elems.front(),
1079  tri_group.first,
1080  ordered_node_list[side_counter],
1081  side_id_0,
1082  is_inverse_0);
1083  elemSideLocator(mesh,
1084  block_elems.back(),
1085  ordered_node_list[side_counter + std::get<2>(block_info)],
1086  tri_group.first,
1087  side_id_t,
1088  is_inverse_t);
1089  // Collect boundary information of the identified sides
1090  std::vector<boundary_id_type> side_0_boundary_ids;
1091  boundary_info.boundary_ids(
1092  mesh.elem_ptr(block_elems.front()), side_id_0, side_0_boundary_ids);
1093  std::vector<boundary_id_type> side_t_boundary_ids;
1094  boundary_info.boundary_ids(mesh.elem_ptr(block_elems.back()), side_id_t, side_t_boundary_ids);
1095 
1096  // Ideally we want this angle to be 60 degrees
1097  // In reality, we want one TRI3 element if the angle is less than 90 degrees;
1098  // we want two TRI3 elements if the angle is greater than 90 degrees and less than 135
1099  // degrees; we want three TRI3 elements if the angle is greater than 135 degrees and less than
1100  // 180 degrees.
1101  if (angle < 90.0)
1102  {
1103  if (std::get<2>(block_info) > 1)
1104  {
1106  tri_group.first,
1107  ordered_node_list[side_counter],
1108  ordered_node_list[side_counter + std::get<2>(block_info)],
1109  std::get<0>(block_info),
1110  std::get<1>(block_info),
1111  {boundary_to_improve},
1112  side_0_boundary_ids,
1113  side_t_boundary_ids);
1114  elems_to_remove.insert(elems_to_remove.end(), block_elems.begin(), block_elems.end());
1115  }
1116  }
1117  else if (angle < 135.0)
1118  {
1119  // We can just add the middle node because there's nothing on the other side
1120  const auto node_m = mesh.add_point((*node_1 + *node_2) / 2.0);
1122  tri_group.first,
1123  ordered_node_list[side_counter],
1124  node_m->id(),
1125  std::get<0>(block_info),
1126  std::get<1>(block_info),
1127  {boundary_to_improve},
1128  side_0_boundary_ids,
1129  std::vector<boundary_id_type>());
1131  tri_group.first,
1132  node_m->id(),
1133  ordered_node_list[side_counter + std::get<2>(block_info)],
1134  std::get<0>(block_info),
1135  std::get<1>(block_info),
1136  {boundary_to_improve},
1137  std::vector<boundary_id_type>(),
1138  side_t_boundary_ids);
1139  elems_to_remove.insert(elems_to_remove.end(), block_elems.begin(), block_elems.end());
1140  }
1141  else
1142  {
1143  const auto node_m1 = mesh.add_point((*node_1 * 2.0 + *node_2) / 3.0);
1144  const auto node_m2 = mesh.add_point((*node_1 + *node_2 * 2.0) / 3.0);
1146  tri_group.first,
1147  ordered_node_list[side_counter],
1148  node_m1->id(),
1149  std::get<0>(block_info),
1150  std::get<1>(block_info),
1151  {boundary_to_improve},
1152  side_0_boundary_ids,
1153  std::vector<boundary_id_type>());
1155  tri_group.first,
1156  node_m1->id(),
1157  node_m2->id(),
1158  std::get<0>(block_info),
1159  std::get<1>(block_info),
1160  {boundary_to_improve},
1161  std::vector<boundary_id_type>(),
1162  std::vector<boundary_id_type>());
1164  tri_group.first,
1165  node_m2->id(),
1166  ordered_node_list[side_counter + std::get<2>(block_info)],
1167  std::get<0>(block_info),
1168  std::get<1>(block_info),
1169  {boundary_to_improve},
1170  std::vector<boundary_id_type>(),
1171  side_t_boundary_ids);
1172  elems_to_remove.insert(elems_to_remove.end(), block_elems.begin(), block_elems.end());
1173  }
1174  side_counter += std::get<2>(block_info);
1175  }
1176  // TODO: Need to check if the new element is inverted?
1177  }
1178  // Delete the original elements
1179  for (const auto & elem_to_remove : elems_to_remove)
1180  mesh.delete_elem(mesh.elem_ptr(elem_to_remove));
1181  mesh.contract();
1182 }
void makeOrderedNodeList(std::vector< std::pair< dof_id_type, dof_id_type >> &node_assm, std::vector< dof_id_type > &elem_id_list, std::vector< dof_id_type > &midpoint_node_list, std::vector< dof_id_type > &ordered_node_list, std::vector< dof_id_type > &ordered_elem_id_list)
Convert a list of sides in the form of a vector of pairs of node ids into a list of ordered nodes bas...
auto norm() const -> decltype(std::norm(Real()))
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:302
void makeImprovedTriElement(libMesh::ReplicatedMesh &mesh, const dof_id_type node_id_0, const dof_id_type node_id_1, const dof_id_type node_id_2, const subdomain_id_type subdomain_id, const std::vector< dof_id_type > &extra_elem_ids, const std::vector< boundary_id_type > &boundary_ids_for_side_1=std::vector< boundary_id_type >(), const std::vector< boundary_id_type > &boundary_ids_for_side_0=std::vector< boundary_id_type >(), const std::vector< boundary_id_type > &boundary_ids_for_side_2=std::vector< boundary_id_type >())
Make a TRI3 element with the given node ids and subdomain id with boundary information.
unsigned int n_elem_integers() const
MeshBase & mesh
bool hasBoundaryID(const MeshBase &input_mesh, const BoundaryID id)
Whether a particular boundary ID exists in the mesh.
void boundary_ids(const Node *node, std::vector< boundary_id_type > &vec_to_fill) const
const BoundaryInfo & get_boundary_info() const
virtual Node * add_point(const Point &p, const dof_id_type id=DofObject::invalid_id, const processor_id_type proc_id=DofObject::invalid_processor_id)=0
TRI3
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
virtual void delete_elem(Elem *e)=0
dof_id_type id() const
bool elemSideLocator(libMesh::ReplicatedMesh &mesh, const dof_id_type elem_id, const dof_id_type node_id_0, const dof_id_type node_id_1, unsigned short &side_id, bool &is_inverse)
Check if there is a side in an element that contains the given pair of nodes; if yes, also find the side id and the direction of the two nodes in the side.
virtual const Elem * elem_ptr(const dof_id_type i) const=0
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual bool contract()=0
subdomain_id_type subdomain_id() const
virtual const Node * node_ptr(const dof_id_type i) const=0
virtual ElemType type() const=0
dof_id_type node_id(const unsigned int i) const
dof_id_type get_extra_integer(const unsigned int index) const

◆ elemSideLocator()

bool MooseMeshXYCuttingUtils::elemSideLocator ( libMesh::ReplicatedMesh mesh,
const dof_id_type  elem_id,
const dof_id_type  node_id_0,
const dof_id_type  node_id_1,
unsigned short &  side_id,
bool &  is_inverse 
)

Check if there is a side in an element that contains the given pair of nodes; if yes, also find the side id and the direction of the two nodes in the side.

Parameters
meshinput mesh with the element that needs to be checked
elem_idid of the element that needs to be checked
node_id_0id of the first node of the pair
node_id_1id of the second node of the pair
side_idid of the side that contains the pair of nodes
is_inverseflag to indicate if the two nodes are in the same direction as the side
Returns
true if the element contains the side with the given pair of nodes

Definition at line 1215 of file MooseMeshXYCuttingUtils.C.

Referenced by boundaryTriElemImprover().

1221 {
1222  Elem * elem = mesh.elem_ptr(elem_id);
1223  for (unsigned short i = 0; i < elem->n_sides(); i++)
1224  {
1225  if (elem->side_ptr(i)->node_ptr(0)->id() == node_id_0 &&
1226  elem->side_ptr(i)->node_ptr(1)->id() == node_id_1)
1227  {
1228  side_id = i;
1229  is_inverse = false;
1230  return true;
1231  }
1232  else if (elem->side_ptr(i)->node_ptr(0)->id() == node_id_1 &&
1233  elem->side_ptr(i)->node_ptr(1)->id() == node_id_0)
1234  {
1235  side_id = i;
1236  is_inverse = true;
1237  return true;
1238  }
1239  }
1240  return false;
1241 }
const boundary_id_type side_id
MeshBase & mesh
virtual const Elem * elem_ptr(const dof_id_type i) const=0
virtual unsigned int n_sides() const=0
virtual std::unique_ptr< Elem > side_ptr(unsigned int i)=0

◆ lineRemoverCutElem()

void MooseMeshXYCuttingUtils::lineRemoverCutElem ( libMesh::ReplicatedMesh mesh,
const std::vector< Real > &  cut_line_params,
const dof_id_type  tri_subdomain_id_shift,
const SubdomainName  tri_elem_subdomain_name_suffix,
const subdomain_id_type  block_id_to_remove,
const boundary_id_type  new_boundary_id,
const bool  improve_boundary_tri_elems = false 
)

Trim the 2D mesh by removing all the elements on one side of the given line.

Note that the mesh can only contain QUAD4 and TRI3 elements

Parameters
meshinput mesh that need to be trimmed
cut_line_paramsparameters of the line that cuts the input mesh
tri_subdomain_id_shiftsubdomain id shift used to define the TRI element subdomains formed due to the trimming
tri_elem_subdomain_name_suffixsuffix used to name the TRI element subdomains formed due to the trimming
block_id_to_removea temporary subdomain id used to mark the elements that need to be removed
new_boundary_idboundary id of the new boundary that forms due to the trimming
improve_boundary_tri_elemsflag to indicate whether the boundary TRI3 elements need to be improved

Definition at line 962 of file MooseMeshXYCuttingUtils.C.

Referenced by XYMeshLineCutter::generate().

969 {
970  // Convert any quad elements crossed by the line into tri elements
971  quadToTriOnLine(mesh, cut_line_params, tri_subdomain_id_shift, tri_elem_subdomain_name_suffix);
972  // Then do the cutting for the preprocessed mesh that only contains tri elements crossed by the
973  // cut line
974  lineRemoverCutElemTri(mesh, cut_line_params, block_id_to_remove, new_boundary_id);
975 
976  if (improve_boundary_tri_elems)
977  boundaryTriElemImprover(mesh, new_boundary_id);
978 }
void boundaryTriElemImprover(libMesh::ReplicatedMesh &mesh, const boundary_id_type boundary_to_improve)
Improve the element quality of the boundary TRI3 elements of the given boundary.
void quadToTriOnLine(libMesh::ReplicatedMesh &mesh, const std::vector< Real > &cut_line_params, const dof_id_type tri_subdomain_id_shift, const SubdomainName tri_elem_subdomain_name_suffix)
Convert all the QUAD4 elements in the mesh that are crossed by the given line into TRI3 elements...
void lineRemoverCutElemTri(libMesh::ReplicatedMesh &mesh, const std::vector< Real > &cut_line_params, const subdomain_id_type block_id_to_remove, const boundary_id_type new_boundary_id)
Trim the 2D mesh by removing all the elements on one side of the given line.

◆ lineRemoverCutElemTri()

void MooseMeshXYCuttingUtils::lineRemoverCutElemTri ( libMesh::ReplicatedMesh mesh,
const std::vector< Real > &  cut_line_params,
const subdomain_id_type  block_id_to_remove,
const boundary_id_type  new_boundary_id 
)

Trim the 2D mesh by removing all the elements on one side of the given line.

Note that the mesh needs to be pre-processed so that only TRI3 are crossed by the given line

Parameters
meshinput mesh that need to be trimmed
cut_line_paramsparameters of the line that cuts the input mesh
block_id_to_removea temporary subdomain id used to mark the elements that need to be removed
new_boundary_idboundary id of the new boundary that forms due to the trimming

Definition at line 755 of file MooseMeshXYCuttingUtils.C.

Referenced by lineRemoverCutElem().

759 {
760  // Find all the elements that are across the cutting line
761  std::vector<dof_id_type> cross_elems;
762  // A vector for element specific information
763  std::vector<std::vector<std::pair<dof_id_type, dof_id_type>>> node_pairs_vec;
764  // A set for unique pairs
765  std::vector<std::pair<dof_id_type, dof_id_type>> node_pairs_unique_vec;
766  for (auto elem_it = mesh.active_elements_begin(); elem_it != mesh.active_elements_end();
767  elem_it++)
768  {
769  std::vector<unsigned short> node_side_rec;
770  const auto n_vertices = (*elem_it)->n_vertices();
771  node_side_rec.resize(n_vertices);
772  for (unsigned int i = 0; i < n_vertices; i++)
773  {
774  // First check if the vertex is in the XY Plane
775  if (!MooseUtils::absoluteFuzzyEqual((*elem_it)->point(i)(2), 0.0))
776  mooseError("MooseMeshXYCuttingUtils::lineRemoverCutElemTri() only works for 2D meshes in "
777  "XY Plane.");
778  const Point v_point = (*elem_it)->point(i);
779  node_side_rec[i] = lineSideDeterminator(
780  v_point(0), v_point(1), cut_line_params[0], cut_line_params[1], cut_line_params[2], true);
781  }
782  if (std::accumulate(node_side_rec.begin(), node_side_rec.end(), 0) == (int)node_side_rec.size())
783  {
784  (*elem_it)->subdomain_id() = block_id_to_remove;
785  }
786  else if (std::accumulate(node_side_rec.begin(), node_side_rec.end(), 0) > 0)
787  {
788  if ((*elem_it)->n_vertices() != 3 || (*elem_it)->n_nodes() != 3)
789  mooseError("The element across the cutting line is not TRI3, which is not supported.");
790  cross_elems.push_back((*elem_it)->id());
791  // Then we need to check pairs of nodes that are on the different side
792  std::vector<std::pair<dof_id_type, dof_id_type>> node_pairs;
793  for (unsigned int i = 0; i < node_side_rec.size(); i++)
794  {
795  // first node on removal side and second node on retaining side
796  if (node_side_rec[i] > 0 && node_side_rec[(i + 1) % node_side_rec.size()] == 0)
797  {
798  // Removal side first
799  node_pairs.push_back(
800  std::make_pair((*elem_it)->node_ptr(i)->id(),
801  (*elem_it)->node_ptr((i + 1) % node_side_rec.size())->id()));
802  node_pairs_unique_vec.push_back(node_pairs.back());
803  }
804  // first node on retaining side and second node on removal side
805  else if (node_side_rec[i] == 0 && node_side_rec[(i + 1) % node_side_rec.size()] > 0)
806  {
807  // Removal side first
808  node_pairs.push_back(
809  std::make_pair((*elem_it)->node_ptr((i + 1) % node_side_rec.size())->id(),
810  (*elem_it)->node_ptr(i)->id()));
811  node_pairs_unique_vec.push_back(node_pairs.back());
812  }
813  }
814  node_pairs_vec.push_back(node_pairs);
815  }
816  }
817  auto vec_ip = std::unique(node_pairs_unique_vec.begin(), node_pairs_unique_vec.end());
818  node_pairs_unique_vec.resize(std::distance(node_pairs_unique_vec.begin(), vec_ip));
819 
820  // Loop over all the node pairs to define new nodes that sit on the cutting line
821  std::vector<Node *> nodes_on_line;
822  // whether the on-line node is overlapped with the node pairs or a brand new node
823  std::vector<unsigned short> nodes_on_line_overlap;
824  for (const auto & node_pair : node_pairs_unique_vec)
825  {
826  const Point pt1 = *mesh.node_ptr(node_pair.first);
827  const Point pt2 = *mesh.node_ptr(node_pair.second);
828  const Point pt_line = twoPointandLineIntersection(
829  pt1, pt2, cut_line_params[0], cut_line_params[1], cut_line_params[2]);
830  if ((pt_line - pt1).norm() < libMesh::TOLERANCE)
831  {
832  nodes_on_line.push_back(mesh.node_ptr(node_pair.first));
833  nodes_on_line_overlap.push_back(1);
834  }
835  else if ((pt_line - pt2).norm() < libMesh::TOLERANCE)
836  {
837  nodes_on_line.push_back(mesh.node_ptr(node_pair.second));
838  nodes_on_line_overlap.push_back(2);
839  }
840  else
841  {
842  nodes_on_line.push_back(mesh.add_point(pt_line));
843  nodes_on_line_overlap.push_back(0);
844  }
845  }
846 
847  // make new elements
848  for (unsigned int i = 0; i < cross_elems.size(); i++)
849  {
850  // Only TRI elements are involved after preprocessing
851  auto cross_elem = mesh.elem_ptr(cross_elems[i]);
852  auto node_0 = cross_elem->node_ptr(0);
853  auto node_1 = cross_elem->node_ptr(1);
854  auto node_2 = cross_elem->node_ptr(2);
855  const std::vector<dof_id_type> tri_nodes = {node_0->id(), node_1->id(), node_2->id()};
856 
857  const auto online_node_index_1 = std::distance(node_pairs_unique_vec.begin(),
858  std::find(node_pairs_unique_vec.begin(),
859  node_pairs_unique_vec.end(),
860  node_pairs_vec[i][0]));
861  const auto online_node_index_2 = std::distance(node_pairs_unique_vec.begin(),
862  std::find(node_pairs_unique_vec.begin(),
863  node_pairs_unique_vec.end(),
864  node_pairs_vec[i][1]));
865  auto node_3 = nodes_on_line[online_node_index_1];
866  auto node_4 = nodes_on_line[online_node_index_2];
867  const auto node_3_overlap_flag = nodes_on_line_overlap[online_node_index_1];
868  const auto node_4_overlap_flag = nodes_on_line_overlap[online_node_index_2];
869  // Most common case, no overlapped nodes
870  if (node_3_overlap_flag == 0 && node_4_overlap_flag == 0)
871  {
872  // True if the common node is on the removal side; false if on the retaining side
873  const bool common_node_side = node_pairs_vec[i][0].first == node_pairs_vec[i][1].first;
874  const subdomain_id_type block_id_to_assign_1 =
875  common_node_side ? block_id_to_remove : cross_elem->subdomain_id();
876  const subdomain_id_type block_id_to_assign_2 =
877  common_node_side ? cross_elem->subdomain_id() : block_id_to_remove;
878  // The reference node ids need to be adjusted according to the common node of the two cut
879  // sides
880  const dof_id_type common_node_id =
881  common_node_side ? node_pairs_vec[i][0].first : node_pairs_vec[i][0].second;
882 
883  triElemSplitter(mesh,
884  cross_elem->id(),
885  std::distance(tri_nodes.begin(),
886  std::find(tri_nodes.begin(), tri_nodes.end(), common_node_id)),
887  node_3->id(),
888  node_4->id(),
889  block_id_to_assign_1,
890  block_id_to_assign_2);
891  mesh.delete_elem(cross_elem);
892  }
893  // both node_3 and node_4 are overlapped
894  else if (node_3_overlap_flag > 0 && node_4_overlap_flag > 0)
895  {
896  // In this case, the entire element is on one side of the cutting line
897  // No change needed just check which side the element is on
898  cross_elem->subdomain_id() = lineSideDeterminator(cross_elem->vertex_average()(0),
899  cross_elem->vertex_average()(1),
900  cut_line_params[0],
901  cut_line_params[1],
902  cut_line_params[2],
903  true)
904  ? block_id_to_remove
905  : cross_elem->subdomain_id();
906  }
907  // node_3 or node_4 is overlapped
908  else
909  {
910  const auto node_3_finder = std::distance(
911  tri_nodes.begin(), std::find(tri_nodes.begin(), tri_nodes.end(), node_3->id()));
912  const auto node_4_finder = std::distance(
913  tri_nodes.begin(), std::find(tri_nodes.begin(), tri_nodes.end(), node_4->id()));
914  // As only one of the two above values should be less than the three, the smaller one should
915  // be used
916  const dof_id_type node_id = node_3_finder < node_4_finder ? node_4->id() : node_3->id();
917  const auto node_finder = std::min(node_3_finder, node_4_finder);
918 
920  mesh,
921  cross_elem->id(),
922  node_finder,
923  node_id,
924  tri_nodes[(node_finder + 1) % 3] == node_pairs_vec[i][node_3_finder > node_4_finder].first
925  ? block_id_to_remove
926  : cross_elem->subdomain_id(),
927  tri_nodes[(node_finder + 1) % 3] == node_pairs_vec[i][node_3_finder > node_4_finder].first
928  ? cross_elem->subdomain_id()
929  : block_id_to_remove);
930  mesh.delete_elem(cross_elem);
931  }
932  }
933  mesh.contract();
934  // Due to the complexity, we identify the new boundary here together instead of during cutting of
935  // each element, because the preexisting element edges that are aligned with the cutting line also
936  // need to be added to the new boundary.
938  BoundaryInfo & boundary_info = mesh.get_boundary_info();
939  for (auto elem_it = mesh.active_elements_begin(); elem_it != mesh.active_elements_end();
940  elem_it++)
941  {
942  if ((*elem_it)->subdomain_id() != block_id_to_remove)
943  {
944  for (unsigned int j = 0; j < (*elem_it)->n_sides(); j++)
945  {
946  if ((*elem_it)->neighbor_ptr(j) != nullptr)
947  if ((*elem_it)->neighbor_ptr(j)->subdomain_id() == block_id_to_remove)
948  boundary_info.add_side(*elem_it, j, new_boundary_id);
949  }
950  }
951  }
952 
953  // Delete the block to remove
954  for (auto elem_it = mesh.active_subdomain_elements_begin(block_id_to_remove);
955  elem_it != mesh.active_subdomain_elements_end(block_id_to_remove);
956  elem_it++)
957  mesh.delete_elem(*elem_it);
958  mesh.contract();
959 }
void triElemSplitter(libMesh::ReplicatedMesh &mesh, const dof_id_type elem_id, const unsigned short node_shift, const dof_id_type nid_3, const dof_id_type nid_4, const subdomain_id_type single_elem_side_id, const subdomain_id_type double_elem_side_id)
Split a TRI3 element into three TRI3 elements based on two nodes on the two sides of the triangle...
bool absoluteFuzzyEqual(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
Function to check whether two variables are equal within an absolute tolerance.
Definition: MooseUtils.h:380
Point twoPointandLineIntersection(const Point &pt1, const Point &pt2, const Real param_1, const Real param_2, const Real param_3)
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:302
static constexpr Real TOLERANCE
MeshBase & mesh
const BoundaryInfo & get_boundary_info() const
virtual Node * add_point(const Point &p, const dof_id_type id=DofObject::invalid_id, const processor_id_type proc_id=DofObject::invalid_processor_id)=0
virtual void find_neighbors(const bool reset_remote_elements=false, const bool reset_current_list=true)=0
virtual void delete_elem(Elem *e)=0
dof_id_type id() const
auto norm(const T &a) -> decltype(std::abs(a))
virtual const Elem * elem_ptr(const dof_id_type i) const=0
virtual bool contract()=0
const Node * node_ptr(const unsigned int i) const
bool lineSideDeterminator(const Real px, const Real py, const Real param_1, const Real param_2, const Real param_3, const bool direction_param, const Real dis_tol=libMesh::TOLERANCE)
Determines whether a point on XY-plane is on the side of a given line that needs to be removed...
void add_side(const dof_id_type elem, const unsigned short int side, const boundary_id_type id)
virtual const Node * node_ptr(const dof_id_type i) const=0
auto min(const L &left, const R &right)
uint8_t dof_id_type

◆ lineRemoverMoveNode()

void MooseMeshXYCuttingUtils::lineRemoverMoveNode ( libMesh::ReplicatedMesh mesh,
const std::vector< Real > &  bdry_pars,
const subdomain_id_type  block_id_to_remove,
const std::set< subdomain_id_type > &  subdomain_ids_set,
const boundary_id_type  trimming_section_boundary_id,
const boundary_id_type  external_boundary_id,
const std::vector< boundary_id_type > &  other_boundaries_to_conform = std::vector<boundary_id_type>(),
const bool  assign_ext_to_new = false,
const bool  side_to_remove = true 
)

Removes all the elements on one side of a given line and deforms the elements intercepted by the line to form a flat new boundary.

Parameters
meshinput mesh to perform line-based elements removing on
bdry_parsline parameter sets {a, b, c} as in a*x+b*y+c=0
block_id_to_removesubdomain id used to mark the elements that need to be removed
subdomain_ids_setall the subdomain ids in the input mesh
trimming_section_boundary_idID of the new external boundary formed due to trimming
external_boundary_idID of the external boundary of the input mesh
other_boundaries_to_conformIDs of the other boundaries that need to be conformed to during nodes moving
assign_ext_to_newwhether to assign external_boundary_id to the new boundary formed by removal
side_to_removewhich side of the mesh needs to be removed: true means ax+by+c>0 and false means ax+by+c<0

Definition at line 27 of file MooseMeshXYCuttingUtils.C.

Referenced by XYMeshLineCutter::generate().

36 {
37  // Build boundary information of the mesh
38  BoundaryInfo & boundary_info = mesh.get_boundary_info();
39  auto bdry_side_list = boundary_info.build_side_list();
40  // Only select the boundaries_to_conform
41  std::vector<std::tuple<dof_id_type, unsigned short int, boundary_id_type>> slc_bdry_side_list;
42  for (unsigned int i = 0; i < bdry_side_list.size(); i++)
43  if (std::get<2>(bdry_side_list[i]) == external_boundary_id ||
44  std::find(other_boundaries_to_conform.begin(),
45  other_boundaries_to_conform.end(),
46  std::get<2>(bdry_side_list[i])) != other_boundaries_to_conform.end())
47  slc_bdry_side_list.push_back(bdry_side_list[i]);
48 
49  // Assign block id for elements to be removed
50  // Also record the elements crossed by the line and with its average vertices on the removal side
51  std::vector<dof_id_type> crossed_elems_to_remove;
52  for (auto elem_it = mesh.active_elements_begin(); elem_it != mesh.active_elements_end();
53  elem_it++)
54  {
55  // Check all the vertices of the element
56  unsigned short removal_side_count = 0;
57  for (unsigned int i = 0; i < (*elem_it)->n_vertices(); i++)
58  {
59  // First check if the vertex is on the XY-Plane
60  if (!MooseUtils::absoluteFuzzyEqual((*elem_it)->point(i)(2), 0.0))
61  mooseError(
62  "MooseMeshXYCuttingUtils::lineRemoverMoveNode() only works for 2D meshes in XY plane.");
63  if (lineSideDeterminator((*elem_it)->point(i)(0),
64  (*elem_it)->point(i)(1),
65  bdry_pars[0],
66  bdry_pars[1],
67  bdry_pars[2],
68  side_to_remove))
69  removal_side_count++;
70  }
71  if (removal_side_count == (*elem_it)->n_vertices())
72  {
73  (*elem_it)->subdomain_id() = block_id_to_remove;
74  continue;
75  }
76  // Check the average of the vertices of the element
77  if (lineSideDeterminator((*elem_it)->vertex_average()(0),
78  (*elem_it)->vertex_average()(1),
79  bdry_pars[0],
80  bdry_pars[1],
81  bdry_pars[2],
82  side_to_remove))
83  crossed_elems_to_remove.push_back((*elem_it)->id());
84  }
85  // Check each crossed element to see if removing it would lead to boundary moving
86  for (const auto & elem_id : crossed_elems_to_remove)
87  {
88  bool remove_flag = true;
89  for (unsigned int i = 0; i < mesh.elem_ptr(elem_id)->n_sides(); i++)
90  {
91  if (mesh.elem_ptr(elem_id)->neighbor_ptr(i) != nullptr)
92  if (mesh.elem_ptr(elem_id)->neighbor_ptr(i)->subdomain_id() != block_id_to_remove &&
93  std::find(crossed_elems_to_remove.begin(),
94  crossed_elems_to_remove.end(),
95  mesh.elem_ptr(elem_id)->neighbor_ptr(i)->id()) ==
96  crossed_elems_to_remove.end())
97  {
98  if (mesh.elem_ptr(elem_id)->subdomain_id() !=
99  mesh.elem_ptr(elem_id)->neighbor_ptr(i)->subdomain_id())
100  {
101  remove_flag = false;
102  break;
103  }
104  }
105  }
106  if (remove_flag)
107  mesh.elem_ptr(elem_id)->subdomain_id() = block_id_to_remove;
108  }
109 
110  // Identify all the nodes that are on the interface between block_id_to_remove and other blocks
111  // !!! We need a check here: if a node is on the retaining side, the removed element has a
112  // different subdomain id
113  std::vector<dof_id_type> node_list;
114  for (auto elem_it = mesh.active_subdomain_set_elements_begin(subdomain_ids_set);
115  elem_it != mesh.active_subdomain_set_elements_end(subdomain_ids_set);
116  elem_it++)
117  {
118  for (unsigned int i = 0; i < (*elem_it)->n_sides(); i++)
119  {
120  if ((*elem_it)->neighbor_ptr(i) != nullptr)
121  if ((*elem_it)->neighbor_ptr(i)->subdomain_id() == block_id_to_remove)
122  {
123  node_list.push_back((*elem_it)->side_ptr(i)->node_ptr(0)->id());
124  node_list.push_back((*elem_it)->side_ptr(i)->node_ptr(1)->id());
125  boundary_info.add_side(*elem_it, i, trimming_section_boundary_id);
126  if (assign_ext_to_new && trimming_section_boundary_id != external_boundary_id)
127  boundary_info.add_side(*elem_it, i, external_boundary_id);
128  }
129  }
130  }
131  // Remove duplicate nodes
132  const auto unique_it = std::unique(node_list.begin(), node_list.end());
133  node_list.resize(std::distance(node_list.begin(), unique_it));
134  // Mark those nodes that are on a boundary that requires conformality
135  // If both nodes of a side are involved, we should only move one node
136  std::vector<bool> node_list_flag(node_list.size(), false);
137  std::vector<Point> node_list_point(node_list.size(), Point(0.0, 0.0, 0.0));
138  // Loop over all the selected sides
139  for (unsigned int i = 0; i < slc_bdry_side_list.size(); i++)
140  {
141  // Get the two node ids of the side
142  dof_id_type side_id_0 = mesh.elem_ptr(std::get<0>(slc_bdry_side_list[i]))
143  ->side_ptr(std::get<1>(slc_bdry_side_list[i]))
144  ->node_ptr(0)
145  ->id();
146  dof_id_type side_id_1 = mesh.elem_ptr(std::get<0>(slc_bdry_side_list[i]))
147  ->side_ptr(std::get<1>(slc_bdry_side_list[i]))
148  ->node_ptr(1)
149  ->id();
150  // True means the selected bdry node is in the node list of the trimming interface
151  bool side_id_0_in =
152  !(std::find(node_list.begin(), node_list.end(), side_id_0) == node_list.end());
153  bool side_id_1_in =
154  !(std::find(node_list.begin(), node_list.end(), side_id_1) == node_list.end());
155 
156  // True means the selected bdry node is on the removal side of the trimming interface
157  bool side_node_0_remove = lineSideDeterminator((*mesh.node_ptr(side_id_0))(0),
158  (*mesh.node_ptr(side_id_0))(1),
159  bdry_pars[0],
160  bdry_pars[1],
161  bdry_pars[2],
162  side_to_remove);
163  bool side_node_1_remove = lineSideDeterminator((*mesh.node_ptr(side_id_1))(0),
164  (*mesh.node_ptr(side_id_1))(1),
165  bdry_pars[0],
166  bdry_pars[1],
167  bdry_pars[2],
168  side_to_remove);
169  // If both nodes of that side are involved in the trimming interface
170  if (side_id_0_in && side_id_1_in)
171  // The side needs to be removed from the sideset because it is not longer an interface
172  // The other node will be handled by other element's side
173  boundary_info.remove_side(mesh.elem_ptr(std::get<0>(slc_bdry_side_list[i])),
174  std::get<1>(slc_bdry_side_list[i]),
175  std::get<2>(slc_bdry_side_list[i]));
176  // If node 0 is on the trimming interface, and the side is cut by the trimming line
177  else if (side_id_0_in && (side_node_0_remove != side_node_1_remove))
178  {
179  // Use the intersection point as the destination of the node after moving
180  node_list_flag[std::distance(
181  node_list.begin(), std::find(node_list.begin(), node_list.end(), side_id_0))] = true;
182  const Point p0 = *mesh.node_ptr(side_id_0);
183  const Point p1 = *mesh.node_ptr(side_id_1);
184 
185  node_list_point[std::distance(node_list.begin(),
186  std::find(node_list.begin(), node_list.end(), side_id_0))] =
187  twoPointandLineIntersection(p0, p1, bdry_pars[0], bdry_pars[1], bdry_pars[2]);
188  }
189  // If node 1 is on the trimming interface, and the side is cut by the trimming line
190  else if (side_id_1_in && (side_node_0_remove != side_node_1_remove))
191  {
192  // Use the intersection point as the destination of the node after moving
193  node_list_flag[std::distance(
194  node_list.begin(), std::find(node_list.begin(), node_list.end(), side_id_1))] = true;
195  const Point p0 = *mesh.node_ptr(side_id_0);
196  const Point p1 = *mesh.node_ptr(side_id_1);
197 
198  node_list_point[std::distance(node_list.begin(),
199  std::find(node_list.begin(), node_list.end(), side_id_1))] =
200  twoPointandLineIntersection(p0, p1, bdry_pars[0], bdry_pars[1], bdry_pars[2]);
201  }
202  }
203 
204  // move nodes
205  for (unsigned int i = 0; i < node_list.size(); i++)
206  {
207  // This means the node is on both the trimming boundary and the original external
208  // boundary/selected interface boundaries. In order to keep the shape of the original external
209  // boundary, the node is moved along the original external boundary.
210  if (node_list_flag[i])
211  *(mesh.node_ptr(node_list[i])) = node_list_point[i];
212  // This means the node does not need to conform to any boundaries.
213  // Just move it along the normal direction of the trimming line.
214  else
215  {
216  const Real x0 = (*(mesh.node_ptr(node_list[i])))(0);
217  const Real y0 = (*(mesh.node_ptr(node_list[i])))(1);
218  (*(mesh.node_ptr(node_list[i])))(0) =
219  (bdry_pars[1] * (bdry_pars[1] * x0 - bdry_pars[0] * y0) - bdry_pars[0] * bdry_pars[2]) /
220  (bdry_pars[0] * bdry_pars[0] + bdry_pars[1] * bdry_pars[1]);
221  (*(mesh.node_ptr(node_list[i])))(1) =
222  (bdry_pars[0] * (-bdry_pars[1] * x0 + bdry_pars[0] * y0) - bdry_pars[1] * bdry_pars[2]) /
223  (bdry_pars[0] * bdry_pars[0] + bdry_pars[1] * bdry_pars[1]);
224  }
225  }
226 
227  // Delete the block
228  for (auto elem_it = mesh.active_subdomain_elements_begin(block_id_to_remove);
229  elem_it != mesh.active_subdomain_elements_end(block_id_to_remove);
230  elem_it++)
231  mesh.delete_elem(*elem_it);
232  mesh.contract();
234  // Delete zero volume elements
235  std::vector<dof_id_type> zero_elems;
236  for (auto elem_it = mesh.elements_begin(); elem_it != mesh.elements_end(); elem_it++)
237  {
238  if (MooseUtils::absoluteFuzzyEqual((*elem_it)->volume(), 0.0))
239  {
240  for (unsigned int i = 0; i < (*elem_it)->n_sides(); i++)
241  {
242  if ((*elem_it)->neighbor_ptr(i) != nullptr)
243  {
244  boundary_info.add_side((*elem_it)->neighbor_ptr(i),
245  ((*elem_it)->neighbor_ptr(i))->which_neighbor_am_i(*elem_it),
246  external_boundary_id);
247  boundary_info.add_side((*elem_it)->neighbor_ptr(i),
248  ((*elem_it)->neighbor_ptr(i))->which_neighbor_am_i(*elem_it),
249  trimming_section_boundary_id);
250  }
251  }
252  zero_elems.push_back((*elem_it)->id());
253  }
254  }
255  for (const auto & zero_elem : zero_elems)
256  mesh.delete_elem(mesh.elem_ptr(zero_elem));
257  mesh.contract();
258  // As we modified the side_list, it is safer to clear the node_list
259  boundary_info.clear_boundary_node_ids();
261 }
bool absoluteFuzzyEqual(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
Function to check whether two variables are equal within an absolute tolerance.
Definition: MooseUtils.h:380
Point twoPointandLineIntersection(const Point &pt1, const Point &pt2, const Real param_1, const Real param_2, const Real param_3)
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)
MeshBase & mesh
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
virtual void find_neighbors(const bool reset_remote_elements=false, const bool reset_current_list=true)=0
virtual void delete_elem(Elem *e)=0
dof_id_type id() const
virtual const Elem * elem_ptr(const dof_id_type i) const=0
virtual unsigned int n_sides() const=0
const Elem * neighbor_ptr(unsigned int i) const
void remove_side(const Elem *elem, const unsigned short int side)
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual bool contract()=0
virtual std::unique_ptr< Elem > side_ptr(unsigned int i)=0
subdomain_id_type subdomain_id() const
bool lineSideDeterminator(const Real px, const Real py, const Real param_1, const Real param_2, const Real param_3, const bool direction_param, const Real dis_tol=libMesh::TOLERANCE)
Determines whether a point on XY-plane is on the side of a given line that needs to be removed...
void add_side(const dof_id_type elem, const unsigned short int side, const boundary_id_type id)
virtual const Node * node_ptr(const dof_id_type i) const=0
uint8_t dof_id_type

◆ lineSideDeterminator()

bool MooseMeshXYCuttingUtils::lineSideDeterminator ( const Real  px,
const Real  py,
const Real  param_1,
const Real  param_2,
const Real  param_3,
const bool  direction_param,
const Real  dis_tol = libMesh::TOLERANCE 
)

Determines whether a point on XY-plane is on the side of a given line that needs to be removed.

Parameters
pxx coordinate of the point
pyy coordinate of the point
param_1parameter 1 (a) in line formula a*x+b*y+c=0
param_2parameter 2 (b) in line formula a*x+b*y+c=0
param_3parameter 3 (c) in line formula a*x+b*y+c=0
direction_paramwhich side is the side that needs to be removed
dis_toltolerance used in determining side
Returns
whether the point is on the side of the line that needed to be removed

Definition at line 264 of file MooseMeshXYCuttingUtils.C.

Referenced by lineRemoverCutElemTri(), lineRemoverMoveNode(), and quadToTriOnLine().

271 {
272  const Real tmp = px * param_1 + py * param_2 + param_3;
273  return direction_param ? tmp >= dis_tol : tmp <= dis_tol;
274 }
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real

◆ makeImprovedTriElement()

void MooseMeshXYCuttingUtils::makeImprovedTriElement ( libMesh::ReplicatedMesh mesh,
const dof_id_type  node_id_0,
const dof_id_type  node_id_1,
const dof_id_type  node_id_2,
const subdomain_id_type  subdomain_id,
const std::vector< dof_id_type > &  extra_elem_ids,
const std::vector< boundary_id_type > &  boundary_ids_for_side_1 = std::vector<boundary_id_type>(),
const std::vector< boundary_id_type > &  boundary_ids_for_side_0 = std::vector<boundary_id_type>(),
const std::vector< boundary_id_type > &  boundary_ids_for_side_2 = std::vector<boundary_id_type>() 
)

Make a TRI3 element with the given node ids and subdomain id with boundary information.

Parameters
meshinput mesh where the TRI3 element needs to be added
node_id_0id of the first node of the TRI3 element
node_id_1id of the second node of the TRI3 element
node_id_2id of the third node of the TRI3 element
subdomain_idsubdomain id of the TRI3 element
extra_elem_idsextra element ids to be assigned to the TRI3 element
boundary_ids_for_side_1boundary ids of the second side of the TRI3 element
boundary_ids_for_side_0boundary ids of the first side of the TRI3 element
boundary_ids_for_side_2boundary ids of the third side of the TRI3 element

Definition at line 1185 of file MooseMeshXYCuttingUtils.C.

Referenced by boundaryTriElemImprover().

1194 {
1195  BoundaryInfo & boundary_info = mesh.get_boundary_info();
1196  Elem * elem_Tri3_new = mesh.add_elem(new Tri3);
1197  elem_Tri3_new->set_node(0, mesh.node_ptr(node_id_0));
1198  elem_Tri3_new->set_node(1, mesh.node_ptr(node_id_1));
1199  elem_Tri3_new->set_node(2, mesh.node_ptr(node_id_2));
1200  for (const auto & boundary_id_for_side_0 : boundary_ids_for_side_0)
1201  boundary_info.add_side(elem_Tri3_new, 0, boundary_id_for_side_0);
1202  for (const auto & boundary_id_for_side_1 : boundary_ids_for_side_1)
1203  boundary_info.add_side(elem_Tri3_new, 1, boundary_id_for_side_1);
1204  for (const auto & boundary_id_for_side_2 : boundary_ids_for_side_2)
1205  boundary_info.add_side(elem_Tri3_new, 2, boundary_id_for_side_2);
1206  elem_Tri3_new->subdomain_id() = subdomain_id;
1207  // Retain element extra integers
1208  for (unsigned int j = 0; j < extra_elem_ids.size(); j++)
1209  {
1210  elem_Tri3_new->set_extra_integer(j, extra_elem_ids[j]);
1211  }
1212 }
virtual Node *& set_node(const unsigned int i)
MeshBase & mesh
const BoundaryInfo & get_boundary_info() const
virtual Elem * add_elem(Elem *e)=0
subdomain_id_type subdomain_id() const
void add_side(const dof_id_type elem, const unsigned short int side, const boundary_id_type id)
virtual const Node * node_ptr(const dof_id_type i) const=0
void set_extra_integer(const unsigned int index, const dof_id_type value)

◆ quadElemSplitter()

void MooseMeshXYCuttingUtils::quadElemSplitter ( libMesh::ReplicatedMesh mesh,
const dof_id_type  elem_id,
const subdomain_id_type  tri_elem_subdomain_shift 
)

Split a QUAD4 element into two TRI3 elements.

Parameters
meshinput mesh with the QUAD4 element that needs to be split
elem_idid of the QUAD4 element that needs to be split
tri_elem_subdomain_shiftsubdomain id shift used to define the TRI element subdomains

Definition at line 601 of file MooseMeshXYCuttingUtils.C.

Referenced by quadToTriOnLine().

604 {
605  // Build boundary information of the mesh
606  BoundaryInfo & boundary_info = mesh.get_boundary_info();
607  auto bdry_side_list = boundary_info.build_side_list();
608  // Create a list of sidesets involving the element to be split
609  std::vector<std::vector<boundary_id_type>> elem_side_list;
610  elem_side_list.resize(4);
611  for (unsigned int i = 0; i < bdry_side_list.size(); i++)
612  {
613  if (std::get<0>(bdry_side_list[i]) == elem_id)
614  {
615  elem_side_list[std::get<1>(bdry_side_list[i])].push_back(std::get<2>(bdry_side_list[i]));
616  }
617  }
618 
619  auto node_0 = mesh.elem_ptr(elem_id)->node_ptr(0);
620  auto node_1 = mesh.elem_ptr(elem_id)->node_ptr(1);
621  auto node_2 = mesh.elem_ptr(elem_id)->node_ptr(2);
622  auto node_3 = mesh.elem_ptr(elem_id)->node_ptr(3);
623 
624  const unsigned int n_elem_extra_ids = mesh.n_elem_integers();
625  std::vector<dof_id_type> exist_extra_ids(n_elem_extra_ids);
626  // Record all the element extra integers of the original quad element
627  for (unsigned int j = 0; j < n_elem_extra_ids; j++)
628  exist_extra_ids[j] = mesh.elem_ptr(elem_id)->get_extra_integer(j);
629 
630  // There are two trivial ways to split a quad element
631  // We prefer the way that leads to triangles with similar areas
632  if (std::abs((*node_1 - *node_0).cross(*node_3 - *node_0).norm() -
633  (*node_1 - *node_2).cross(*node_3 - *node_2).norm()) >
634  std::abs((*node_0 - *node_1).cross(*node_2 - *node_1).norm() -
635  (*node_0 - *node_3).cross(*node_2 - *node_3).norm()))
636  {
637  Elem * elem_Tri3_0 = mesh.add_elem(new Tri3);
638  elem_Tri3_0->set_node(0, node_0);
639  elem_Tri3_0->set_node(1, node_1);
640  elem_Tri3_0->set_node(2, node_2);
641  elem_Tri3_0->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id() + tri_elem_subdomain_shift;
642  Elem * elem_Tri3_1 = mesh.add_elem(new Tri3);
643  elem_Tri3_1->set_node(0, node_0);
644  elem_Tri3_1->set_node(1, node_2);
645  elem_Tri3_1->set_node(2, node_3);
646  elem_Tri3_1->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id() + tri_elem_subdomain_shift;
647  // Retain element extra integers
648  for (unsigned int j = 0; j < n_elem_extra_ids; j++)
649  {
650  elem_Tri3_0->set_extra_integer(j, exist_extra_ids[j]);
651  elem_Tri3_1->set_extra_integer(j, exist_extra_ids[j]);
652  }
653 
654  // Add sideset information to the new elements
655  for (const auto & side_info_0 : elem_side_list[0])
656  boundary_info.add_side(elem_Tri3_0, 0, side_info_0);
657  for (const auto & side_info_1 : elem_side_list[1])
658  boundary_info.add_side(elem_Tri3_0, 1, side_info_1);
659  for (const auto & side_info_2 : elem_side_list[2])
660  boundary_info.add_side(elem_Tri3_1, 1, side_info_2);
661  for (const auto & side_info_3 : elem_side_list[3])
662  boundary_info.add_side(elem_Tri3_1, 2, side_info_3);
663  }
664  else
665  {
666  Elem * elem_Tri3_0 = mesh.add_elem(new Tri3);
667  elem_Tri3_0->set_node(0, node_0);
668  elem_Tri3_0->set_node(1, node_1);
669  elem_Tri3_0->set_node(2, node_3);
670  elem_Tri3_0->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id() + tri_elem_subdomain_shift;
671  Elem * elem_Tri3_1 = mesh.add_elem(new Tri3);
672  elem_Tri3_1->set_node(0, node_1);
673  elem_Tri3_1->set_node(1, node_2);
674  elem_Tri3_1->set_node(2, node_3);
675  elem_Tri3_1->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id() + tri_elem_subdomain_shift;
676  // Retain element extra integers
677  for (unsigned int j = 0; j < n_elem_extra_ids; j++)
678  {
679  elem_Tri3_0->set_extra_integer(j, exist_extra_ids[j]);
680  elem_Tri3_1->set_extra_integer(j, exist_extra_ids[j]);
681  }
682 
683  // Add sideset information to the new elements
684  for (const auto & side_info_0 : elem_side_list[0])
685  boundary_info.add_side(elem_Tri3_0, 0, side_info_0);
686  for (const auto & side_info_1 : elem_side_list[1])
687  boundary_info.add_side(elem_Tri3_1, 0, side_info_1);
688  for (const auto & side_info_2 : elem_side_list[2])
689  boundary_info.add_side(elem_Tri3_1, 1, side_info_2);
690  for (const auto & side_info_3 : elem_side_list[3])
691  boundary_info.add_side(elem_Tri3_0, 2, side_info_3);
692  }
693 }
MetaPhysicL::DualNumber< V, D, asd > abs(const MetaPhysicL::DualNumber< V, D, asd > &a)
Definition: EigenADReal.h:42
virtual Node *& set_node(const unsigned int i)
unsigned int n_elem_integers() const
MeshBase & mesh
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
virtual Elem * add_elem(Elem *e)=0
auto norm(const T &a) -> decltype(std::abs(a))
virtual const Elem * elem_ptr(const dof_id_type i) const=0
subdomain_id_type subdomain_id() const
const Node * node_ptr(const unsigned int i) const
void add_side(const dof_id_type elem, const unsigned short int side, const boundary_id_type id)
void set_extra_integer(const unsigned int index, const dof_id_type value)
dof_id_type get_extra_integer(const unsigned int index) const

◆ quadToTriOnLine()

void MooseMeshXYCuttingUtils::quadToTriOnLine ( libMesh::ReplicatedMesh mesh,
const std::vector< Real > &  cut_line_params,
const dof_id_type  tri_subdomain_id_shift,
const SubdomainName  tri_elem_subdomain_name_suffix 
)

Convert all the QUAD4 elements in the mesh that are crossed by the given line into TRI3 elements.

Parameters
meshinput mesh with the QUAD4 elements that need to be converted
cut_line_paramsparameters of the line that cuts the input mesh
tri_subdomain_id_shiftsubdomain id shift used to define the TRI element subdomains generated due to the conversion
tri_elem_subdomain_name_suffixsuffix used to name the TRI element subdomains generated due to the conversion

Definition at line 696 of file MooseMeshXYCuttingUtils.C.

Referenced by lineRemoverCutElem().

700 {
701  // Preprocess: find all the quad elements that are across the cutting line
702  std::vector<dof_id_type> cross_elems_quad;
703  std::set<subdomain_id_type> new_subdomain_ids;
704  for (auto elem_it = mesh.active_elements_begin(); elem_it != mesh.active_elements_end();
705  elem_it++)
706  {
707  if ((*elem_it)->n_vertices() == 4)
708  {
709  std::vector<unsigned short> node_side_rec;
710  for (unsigned int i = 0; i < 4; i++)
711  {
712  const Point v_point = (*elem_it)->point(i);
713  node_side_rec.push_back(lineSideDeterminator(v_point(0),
714  v_point(1),
715  cut_line_params[0],
716  cut_line_params[1],
717  cut_line_params[2],
718  true));
719  }
720  if (std::accumulate(node_side_rec.begin(), node_side_rec.end(), 0) != 4 &&
721  std::accumulate(node_side_rec.begin(), node_side_rec.end(), 0) > 0)
722  {
723  cross_elems_quad.push_back((*elem_it)->id());
724  new_subdomain_ids.emplace((*elem_it)->subdomain_id() + tri_subdomain_id_shift);
725  }
726  }
727  }
728  // Then convert these quad elements into tri elements
729  for (const auto & cross_elem_quad : cross_elems_quad)
730  {
731  quadElemSplitter(mesh, cross_elem_quad, tri_subdomain_id_shift);
732  mesh.delete_elem(mesh.elem_ptr(cross_elem_quad));
733  }
734  for (auto & nid : new_subdomain_ids)
735  {
736  const SubdomainName old_name = mesh.subdomain_name(nid - tri_subdomain_id_shift);
738  (old_name.empty() ? (SubdomainName)(std::to_string(nid - tri_subdomain_id_shift))
739  : old_name) +
740  "_" + tri_elem_subdomain_name_suffix,
742  throw MooseException("The new subdomain name already exists in the mesh.");
743  mesh.subdomain_name(nid) =
744  (old_name.empty() ? (SubdomainName)(std::to_string(nid - tri_subdomain_id_shift))
745  : old_name) +
746  "_" + tri_elem_subdomain_name_suffix;
747  mooseWarning("QUAD elements have been converted into TRI elements with a new "
748  "subdomain name: " +
749  mesh.subdomain_name(nid) + ".");
750  }
751  mesh.contract();
752 }
void mooseWarning(Args &&... args)
Emit a warning message with the given stringified, concatenated args.
Definition: MooseError.h:336
MeshBase & mesh
void quadElemSplitter(libMesh::ReplicatedMesh &mesh, const dof_id_type elem_id, const subdomain_id_type tri_elem_subdomain_shift)
Split a QUAD4 element into two TRI3 elements.
SubdomainID getSubdomainID(const SubdomainName &subdomain_name, const MeshBase &mesh)
Gets the subdomain ID associated with the given SubdomainName.
const SubdomainID INVALID_BLOCK_ID
Definition: MooseTypes.C:20
virtual void delete_elem(Elem *e)=0
std::string & subdomain_name(subdomain_id_type id)
virtual const Elem * elem_ptr(const dof_id_type i) const=0
Provides a way for users to bail out of the current solve.
virtual bool contract()=0
bool lineSideDeterminator(const Real px, const Real py, const Real param_1, const Real param_2, const Real param_3, const bool direction_param, const Real dis_tol=libMesh::TOLERANCE)
Determines whether a point on XY-plane is on the side of a given line that needs to be removed...

◆ quasiTriElementsFixer()

bool MooseMeshXYCuttingUtils::quasiTriElementsFixer ( libMesh::ReplicatedMesh mesh,
const std::set< subdomain_id_type > &  subdomain_ids_set,
const subdomain_id_type  tri_elem_subdomain_shift = Moose::INVALID_BLOCK_ID,
const SubdomainName  tri_elem_subdomain_name_suffix = "tri" 
)

Fixes degenerate QUAD elements created by the hexagonal mesh trimming by converting them into TRI elements.

Parameters
meshinput mesh with degenerate QUAD elements that need to be fixed
subdomain_ids_setall the subdomain ids in the input mesh
tri_elem_subdomain_shiftsubdomain id shift used to define the TRI element subdomains
tri_elem_subdomain_name_suffixsuffix used to name the TRI element subdomains
Returns
whether any elements have been fixed

Definition at line 306 of file MooseMeshXYCuttingUtils.C.

Referenced by XYMeshLineCutter::generate().

310 {
311  BoundaryInfo & boundary_info = mesh.get_boundary_info();
312  // Define the subdomain id shift for the new TRI3 element subdomain(s)
313  const subdomain_id_type max_subdomain_id(*subdomain_ids_set.rbegin());
314  const subdomain_id_type tri_subdomain_id_shift =
315  tri_elem_subdomain_shift == Moose::INVALID_BLOCK_ID ? max_subdomain_id
316  : tri_elem_subdomain_shift;
317  mooseAssert(std::numeric_limits<subdomain_id_type>::max() - max_subdomain_id >
318  tri_subdomain_id_shift,
319  "The TRI elements subdomain id to be assigned may exceed the numeric limit.");
320  const unsigned int n_elem_extra_ids = mesh.n_elem_integers();
321  std::vector<dof_id_type> exist_extra_ids(n_elem_extra_ids);
322  std::vector<std::tuple<Elem *, unsigned int, bool, bool>> bad_elems_rec;
323  // Loop over all the active elements to find any degenerate QUAD elements
324  for (auto & elem : as_range(mesh.active_elements_begin(), mesh.active_elements_end()))
325  {
326  // Two types of degenerate QUAD elements are identified:
327  // (1) QUAD elements with three collinear vertices
328  // (2) QUAD elements with two overlapped vertices
329  const auto elem_angles = vertex_angles(*elem);
330  const auto elem_distances = vertex_distances(*elem);
331  // Type 1
332  if (MooseUtils::absoluteFuzzyEqual(elem_angles.front().first, M_PI, 0.001))
333  {
334  bad_elems_rec.push_back(std::make_tuple(elem, elem_angles.front().second, false, true));
335  continue;
336  }
337  // Type 2
338  if (MooseUtils::absoluteFuzzyEqual(elem_distances.front().first, 0.0))
339  {
340  bad_elems_rec.push_back(std::make_tuple(elem, elem_distances.front().second, false, false));
341  }
342  }
343  std::set<subdomain_id_type> new_subdomain_ids;
344  // Loop over all the identified degenerate QUAD elements
345  for (const auto & bad_elem : bad_elems_rec)
346  {
347  std::vector<boundary_id_type> elem_bdry_container_0;
348  std::vector<boundary_id_type> elem_bdry_container_1;
349  std::vector<boundary_id_type> elem_bdry_container_2;
350 
351  Elem * elem_0 = std::get<0>(bad_elem);
352  if (std::get<3>(bad_elem))
353  {
354  // elems 1 and 2 are the neighboring elements of the degenerate element corresponding to the
355  // two collinear sides.
356  // For the degenerated element with three colinear vertices, if the elems 1 and 2 do not
357  // exist, the two sides are on the external boundary formed by trimming.
358  Elem * elem_1 = elem_0->neighbor_ptr(std::get<1>(bad_elem));
359  Elem * elem_2 = elem_0->neighbor_ptr((std::get<1>(bad_elem) - 1) % elem_0->n_vertices());
360  if ((elem_1 != nullptr || elem_2 != nullptr))
361  throw MooseException("The input mesh has degenerate quad element before trimming.");
362  }
363  mesh.get_boundary_info().boundary_ids(elem_0, std::get<1>(bad_elem), elem_bdry_container_0);
365  elem_0, (std::get<1>(bad_elem) + 1) % elem_0->n_vertices(), elem_bdry_container_1);
367  elem_0, (std::get<1>(bad_elem) + 2) % elem_0->n_vertices(), elem_bdry_container_2);
369  elem_0, (std::get<1>(bad_elem) + 3) % elem_0->n_vertices(), elem_bdry_container_0);
370 
371  // Record subdomain id of the degenerate element
372  auto elem_block_id = elem_0->subdomain_id();
373  // Define the three of four nodes that will be used to generate the TRI element
374  auto pt0 = elem_0->node_ptr((std::get<1>(bad_elem) + 1) % elem_0->n_vertices());
375  auto pt1 = elem_0->node_ptr((std::get<1>(bad_elem) + 2) % elem_0->n_vertices());
376  auto pt2 = elem_0->node_ptr((std::get<1>(bad_elem) + 3) % elem_0->n_vertices());
377  // Record all the element extra integers of the degenerate element
378  for (unsigned int j = 0; j < n_elem_extra_ids; j++)
379  exist_extra_ids[j] = elem_0->get_extra_integer(j);
380  // Delete the degenerate QUAD element
381  mesh.delete_elem(elem_0);
382  // Create the new TRI element
383  Elem * elem_Tri3 = mesh.add_elem(new Tri3);
384  elem_Tri3->set_node(0, pt0);
385  elem_Tri3->set_node(1, pt1);
386  elem_Tri3->set_node(2, pt2);
387  // Retain the boundary information
388  for (auto bdry_id : elem_bdry_container_0)
389  boundary_info.add_side(elem_Tri3, 2, bdry_id);
390  for (auto bdry_id : elem_bdry_container_1)
391  boundary_info.add_side(elem_Tri3, 0, bdry_id);
392  for (auto bdry_id : elem_bdry_container_2)
393  boundary_info.add_side(elem_Tri3, 1, bdry_id);
394  // Assign subdomain id for the TRI element by shifting its original subdomain id
395  elem_Tri3->subdomain_id() = elem_block_id + tri_subdomain_id_shift;
396  new_subdomain_ids.emplace(elem_block_id + tri_subdomain_id_shift);
397  // Retain element extra integers
398  for (unsigned int j = 0; j < n_elem_extra_ids; j++)
399  elem_Tri3->set_extra_integer(j, exist_extra_ids[j]);
400  }
401  // Assign subdomain names for the new TRI elements
402  for (auto & nid : new_subdomain_ids)
403  {
404  const SubdomainName old_name = mesh.subdomain_name(nid - tri_subdomain_id_shift);
406  (old_name.empty() ? (SubdomainName)(std::to_string(nid - tri_subdomain_id_shift))
407  : old_name) +
408  "_" + tri_elem_subdomain_name_suffix,
410  throw MooseException("The new subdomain name already exists in the mesh.");
411  mesh.subdomain_name(nid) =
412  (old_name.empty() ? (SubdomainName)(std::to_string(nid - tri_subdomain_id_shift))
413  : old_name) +
414  "_" + tri_elem_subdomain_name_suffix;
415  mooseWarning("Degenerate QUAD elements have been converted into TRI elements with a new "
416  "subdomain name: " +
417  mesh.subdomain_name(nid) + ".");
418  }
419  return bad_elems_rec.size();
420 }
std::vector< std::pair< Real, unsigned int > > vertex_angles(const Elem &elem)
virtual Node *& set_node(const unsigned int i)
bool absoluteFuzzyEqual(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
Function to check whether two variables are equal within an absolute tolerance.
Definition: MooseUtils.h:380
void mooseWarning(Args &&... args)
Emit a warning message with the given stringified, concatenated args.
Definition: MooseError.h:336
unsigned int n_elem_integers() const
MeshBase & mesh
void boundary_ids(const Node *node, std::vector< boundary_id_type > &vec_to_fill) const
const BoundaryInfo & get_boundary_info() const
SubdomainID getSubdomainID(const SubdomainName &subdomain_name, const MeshBase &mesh)
Gets the subdomain ID associated with the given SubdomainName.
auto max(const L &left, const R &right)
const SubdomainID INVALID_BLOCK_ID
Definition: MooseTypes.C:20
virtual void delete_elem(Elem *e)=0
std::vector< std::pair< Real, unsigned int > > vertex_distances(const Elem &elem)
virtual Elem * add_elem(Elem *e)=0
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)
std::string & subdomain_name(subdomain_id_type id)
const Elem * neighbor_ptr(unsigned int i) const
Provides a way for users to bail out of the current solve.
virtual unsigned int n_vertices() const=0
subdomain_id_type subdomain_id() const
const Node * node_ptr(const unsigned int i) const
void add_side(const dof_id_type elem, const unsigned short int side, const boundary_id_type id)
void set_extra_integer(const unsigned int index, const dof_id_type value)
dof_id_type get_extra_integer(const unsigned int index) const

◆ triElemSplitter() [1/2]

void MooseMeshXYCuttingUtils::triElemSplitter ( libMesh::ReplicatedMesh mesh,
const dof_id_type  elem_id,
const unsigned short  node_shift,
const dof_id_type  nid_3,
const dof_id_type  nid_4,
const subdomain_id_type  single_elem_side_id,
const subdomain_id_type  double_elem_side_id 
)

Split a TRI3 element into three TRI3 elements based on two nodes on the two sides of the triangle.

Parameters
meshinput mesh with the TRI3 element that needs to be split
elem_idid of the TRI3 element that needs to be split
node_shiftshift used to rotate the vertices to make sure the vertex that the two cut sides share is the first vertex
nid_3id of the node on the first cut side of the triangle
nid_4id of the node on the second cut side of the triangle
single_elem_side_idsubdomain id of the single element side
double_elem_side_idsubdomain id of the double element side

Definition at line 459 of file MooseMeshXYCuttingUtils.C.

Referenced by lineRemoverCutElemTri().

466 {
467  const auto elem_old = mesh.elem_ptr(elem_id);
468  const dof_id_type nid_0 = elem_old->node_ptr(node_shift % 3)->id();
469  const dof_id_type nid_1 = elem_old->node_ptr((1 + node_shift) % 3)->id();
470  const dof_id_type nid_2 = elem_old->node_ptr((2 + node_shift) % 3)->id();
471 
472  const bool m1_side_flag =
474  .cross(*(mesh.node_ptr(nid_1)) - *(mesh.node_ptr(nid_0)))
475  .norm(),
476  0.0);
477  const dof_id_type nid_m1 = m1_side_flag ? nid_3 : nid_4;
478  const dof_id_type nid_m2 = m1_side_flag ? nid_4 : nid_3;
479  // Build boundary information of the mesh
480  BoundaryInfo & boundary_info = mesh.get_boundary_info();
481  auto bdry_side_list = boundary_info.build_side_list();
482  // Create a list of sidesets involving the element to be split
483  std::vector<std::vector<boundary_id_type>> elem_side_list;
484  elem_side_list.resize(3);
485  for (unsigned int i = 0; i < bdry_side_list.size(); i++)
486  {
487  if (std::get<0>(bdry_side_list[i]) == elem_id)
488  {
489  elem_side_list[(std::get<1>(bdry_side_list[i]) + 3 - node_shift) % 3].push_back(
490  std::get<2>(bdry_side_list[i]));
491  }
492  }
493 
494  const unsigned int n_elem_extra_ids = mesh.n_elem_integers();
495  std::vector<dof_id_type> exist_extra_ids(n_elem_extra_ids);
496  // Record all the element extra integers of the original element
497  for (unsigned int j = 0; j < n_elem_extra_ids; j++)
498  exist_extra_ids[j] = mesh.elem_ptr(elem_id)->get_extra_integer(j);
499 
500  Elem * elem_Tri3_0 = mesh.add_elem(new Tri3);
501  elem_Tri3_0->set_node(0, mesh.node_ptr(nid_0));
502  elem_Tri3_0->set_node(1, mesh.node_ptr(nid_m1));
503  elem_Tri3_0->set_node(2, mesh.node_ptr(nid_m2));
504  elem_Tri3_0->subdomain_id() = single_elem_side_id;
505  Elem * elem_Tri3_1 = mesh.add_elem(new Tri3);
506  elem_Tri3_1->set_node(0, mesh.node_ptr(nid_1));
507  elem_Tri3_1->set_node(1, mesh.node_ptr(nid_m2));
508  elem_Tri3_1->set_node(2, mesh.node_ptr(nid_m1));
509  elem_Tri3_1->subdomain_id() = double_elem_side_id;
510  Elem * elem_Tri3_2 = mesh.add_elem(new Tri3);
511  elem_Tri3_2->set_node(0, mesh.node_ptr(nid_2));
512  elem_Tri3_2->set_node(1, mesh.node_ptr(nid_m2));
513  elem_Tri3_2->set_node(2, mesh.node_ptr(nid_1));
514  elem_Tri3_2->subdomain_id() = double_elem_side_id;
515  // Retain element extra integers
516  for (unsigned int j = 0; j < n_elem_extra_ids; j++)
517  {
518  elem_Tri3_0->set_extra_integer(j, exist_extra_ids[j]);
519  elem_Tri3_1->set_extra_integer(j, exist_extra_ids[j]);
520  elem_Tri3_2->set_extra_integer(j, exist_extra_ids[j]);
521  }
522 
523  // Add sideset information to the new elements
524  for (const auto & side_info_0 : elem_side_list[0])
525  {
526  boundary_info.add_side(elem_Tri3_0, 0, side_info_0);
527  boundary_info.add_side(elem_Tri3_1, 2, side_info_0);
528  }
529  for (const auto & side_info_1 : elem_side_list[1])
530  boundary_info.add_side(elem_Tri3_2, 2, side_info_1);
531  for (const auto & side_info_2 : elem_side_list[2])
532  {
533  boundary_info.add_side(elem_Tri3_0, 2, side_info_2);
534  boundary_info.add_side(elem_Tri3_2, 0, side_info_2);
535  }
536 }
virtual Node *& set_node(const unsigned int i)
bool absoluteFuzzyEqual(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
Function to check whether two variables are equal within an absolute tolerance.
Definition: MooseUtils.h:380
unsigned int n_elem_integers() const
MeshBase & mesh
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
dof_id_type id() const
virtual Elem * add_elem(Elem *e)=0
virtual const Elem * elem_ptr(const dof_id_type i) const=0
subdomain_id_type subdomain_id() const
const Node * node_ptr(const unsigned int i) const
void add_side(const dof_id_type elem, const unsigned short int side, const boundary_id_type id)
virtual const Node * node_ptr(const dof_id_type i) const=0
void set_extra_integer(const unsigned int index, const dof_id_type value)
dof_id_type get_extra_integer(const unsigned int index) const
uint8_t dof_id_type

◆ triElemSplitter() [2/2]

void MooseMeshXYCuttingUtils::triElemSplitter ( libMesh::ReplicatedMesh mesh,
const dof_id_type  elem_id,
const unsigned short  node_shift,
const dof_id_type  nid_m,
const subdomain_id_type  first_elem_side_id,
const subdomain_id_type  second_elem_side_id 
)

Split a TRI3 element into two TRI3 elements based on one node on one side of the triangle.

Parameters
meshinput mesh with the TRI3 element that needs to be split
elem_idid of the TRI3 element that needs to be split
node_shiftshift used to rotate the vertices to make sure the vertex corresponding to the cut side is the first vertex
nid_mid of the node on the cut side of the triangle
first_elem_side_idsubdomain id of the first element side
second_elem_side_idsubdomain id of the second element side

Definition at line 539 of file MooseMeshXYCuttingUtils.C.

545 {
546  const auto elem_old = mesh.elem_ptr(elem_id);
547  const dof_id_type nid_0 = elem_old->node_ptr(node_shift % 3)->id();
548  const dof_id_type nid_1 = elem_old->node_ptr((1 + node_shift) % 3)->id();
549  const dof_id_type nid_2 = elem_old->node_ptr((2 + node_shift) % 3)->id();
550  // Build boundary information of the mesh
551  BoundaryInfo & boundary_info = mesh.get_boundary_info();
552  auto bdry_side_list = boundary_info.build_side_list();
553  // Create a list of sidesets involving the element to be split
554  std::vector<std::vector<boundary_id_type>> elem_side_list;
555  elem_side_list.resize(3);
556  for (unsigned int i = 0; i < bdry_side_list.size(); i++)
557  {
558  if (std::get<0>(bdry_side_list[i]) == elem_id)
559  {
560  elem_side_list[(std::get<1>(bdry_side_list[i]) + 3 - node_shift) % 3].push_back(
561  std::get<2>(bdry_side_list[i]));
562  }
563  }
564 
565  const unsigned int n_elem_extra_ids = mesh.n_elem_integers();
566  std::vector<dof_id_type> exist_extra_ids(n_elem_extra_ids);
567  // Record all the element extra integers of the original element
568  for (unsigned int j = 0; j < n_elem_extra_ids; j++)
569  exist_extra_ids[j] = mesh.elem_ptr(elem_id)->get_extra_integer(j);
570 
571  Elem * elem_Tri3_0 = mesh.add_elem(new Tri3);
572  elem_Tri3_0->set_node(0, mesh.node_ptr(nid_0));
573  elem_Tri3_0->set_node(1, mesh.node_ptr(nid_1));
574  elem_Tri3_0->set_node(2, mesh.node_ptr(nid_m));
575  elem_Tri3_0->subdomain_id() = first_elem_side_id;
576  Elem * elem_Tri3_1 = mesh.add_elem(new Tri3);
577  elem_Tri3_1->set_node(0, mesh.node_ptr(nid_0));
578  elem_Tri3_1->set_node(1, mesh.node_ptr(nid_m));
579  elem_Tri3_1->set_node(2, mesh.node_ptr(nid_2));
580  elem_Tri3_1->subdomain_id() = second_elem_side_id;
581  // Retain element extra integers
582  for (unsigned int j = 0; j < n_elem_extra_ids; j++)
583  {
584  elem_Tri3_0->set_extra_integer(j, exist_extra_ids[j]);
585  elem_Tri3_1->set_extra_integer(j, exist_extra_ids[j]);
586  }
587 
588  // Add sideset information to the new elements
589  for (const auto & side_info_0 : elem_side_list[0])
590  boundary_info.add_side(elem_Tri3_0, 0, side_info_0);
591  for (const auto & side_info_1 : elem_side_list[1])
592  {
593  boundary_info.add_side(elem_Tri3_0, 1, side_info_1);
594  boundary_info.add_side(elem_Tri3_1, 1, side_info_1);
595  }
596  for (const auto & side_info_2 : elem_side_list[2])
597  boundary_info.add_side(elem_Tri3_1, 2, side_info_2);
598 }
virtual Node *& set_node(const unsigned int i)
unsigned int n_elem_integers() const
MeshBase & mesh
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
dof_id_type id() const
virtual Elem * add_elem(Elem *e)=0
virtual const Elem * elem_ptr(const dof_id_type i) const=0
subdomain_id_type subdomain_id() const
const Node * node_ptr(const unsigned int i) const
void add_side(const dof_id_type elem, const unsigned short int side, const boundary_id_type id)
virtual const Node * node_ptr(const dof_id_type i) const=0
void set_extra_integer(const unsigned int index, const dof_id_type value)
dof_id_type get_extra_integer(const unsigned int index) const
uint8_t dof_id_type

◆ twoLineIntersection()

Point MooseMeshXYCuttingUtils::twoLineIntersection ( const Real  param_11,
const Real  param_12,
const Real  param_13,
const Real  param_21,
const Real  param_22,
const Real  param_23 
)

Calculates the intersection Point of two given straight lines.

Parameters
param_11parameter 1 (a) in line formula a*x+b*y+c=0 for the first line
param_12parameter 2 (b) in line formula a*x+b*y+c=0 for the first line
param_13parameter 3 (c) in line formula a*x+b*y+c=0 for the first line
param_21parameter 1 (a) in line formula a*x+b*y+c=0 for the second line
param_22parameter 2 (b) in line formula a*x+b*y+c=0 for the second line
param_23parameter 3 (c) in line formula a*x+b*y+c=0 for the second line
Returns
intersect point of the two lines

Definition at line 277 of file MooseMeshXYCuttingUtils.C.

Referenced by twoPointandLineIntersection().

283 {
284  return Point(
285  (param_12 * param_23 - param_22 * param_13) / (param_11 * param_22 - param_21 * param_12),
286  (param_13 * param_21 - param_23 * param_11) / (param_11 * param_22 - param_21 * param_12),
287  0.0);
288 }

◆ twoPointandLineIntersection() [1/2]

Point MooseMeshXYCuttingUtils::twoPointandLineIntersection ( const Point &  pt1,
const Point &  pt2,
const Real  param_1,
const Real  param_2,
const Real  param_3 
)

Calculates the intersection Point of a straight line defined by two given points and another straight line.

Parameters
pt1point 1 that defines the first straight line
pt2point 2 that defines the first straight line
param_1parameter 1 (a) in line formula a*x+b*y+c=0 for the second straight line
param_2parameter 2 (b) in line formula a*x+b*y+c=0 for the second straight line
param_3parameter 3 (c) in line formula a*x+b*y+c=0 for the second straight line
Returns
intersect point of the two lines

◆ twoPointandLineIntersection() [2/2]

Point MooseMeshXYCuttingUtils::twoPointandLineIntersection ( const Point pt1,
const Point pt2,
const Real  param_1,
const Real  param_2,
const Real  param_3 
)

Definition at line 291 of file MooseMeshXYCuttingUtils.C.

Referenced by lineRemoverCutElemTri(), and lineRemoverMoveNode().

296 {
297  return twoLineIntersection(param_1,
298  param_2,
299  param_3,
300  pt2(1) - pt1(1),
301  pt1(0) - pt2(0),
302  pt2(0) * pt1(1) - pt1(0) * pt2(1));
303 }
Point twoLineIntersection(const Real param_11, const Real param_12, const Real param_13, const Real param_21, const Real param_22, const Real param_23)
Calculates the intersection Point of two given straight lines.

◆ vertex_angles() [1/2]

std::vector<std::pair<Real, unsigned int> > MooseMeshXYCuttingUtils::vertex_angles ( const Elem &  elem)

Calculates the internal angles of a given 2D element.

Parameters
elemthe element that needs to be investigated
Returns
sizes of all the internal angles, sorted by their size

◆ vertex_angles() [2/2]

std::vector<std::pair<Real, unsigned int> > MooseMeshXYCuttingUtils::vertex_angles ( const Elem elem)

Definition at line 423 of file MooseMeshXYCuttingUtils.C.

Referenced by quasiTriElementsFixer().

424 {
425  std::vector<std::pair<Real, unsigned int>> angles;
426  const unsigned int n_vertices = elem.n_vertices();
427 
428  for (unsigned int i = 0; i < n_vertices; i++)
429  {
430  Point v1 = (*elem.node_ptr((i - 1) % n_vertices) - *elem.node_ptr(i % n_vertices));
431  Point v2 = (*elem.node_ptr((i + 1) % n_vertices) - *elem.node_ptr(i % n_vertices));
432  Real tmp = v1 * v2 / v1.norm() / v2.norm();
433  if (tmp > 1.0)
434  tmp = 1.0;
435  else if (tmp < -1.0)
436  tmp = -1.0;
437  angles.push_back(std::make_pair(acos(tmp), i));
438  }
439  std::sort(angles.begin(), angles.end(), std::greater<>());
440  return angles;
441 }
auto norm() const -> decltype(std::norm(Real()))
virtual unsigned int n_vertices() const=0
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
const Node * node_ptr(const unsigned int i) const

◆ vertex_distances() [1/2]

std::vector<std::pair<Real, unsigned int> > MooseMeshXYCuttingUtils::vertex_distances ( const Elem &  elem)

Calculates the distances between the vertices of a given 2D element.

Parameters
elemthe element that needs to be investigated
Returns
values of all the distances, sorted by their value

◆ vertex_distances() [2/2]

std::vector<std::pair<Real, unsigned int> > MooseMeshXYCuttingUtils::vertex_distances ( const Elem elem)

Definition at line 444 of file MooseMeshXYCuttingUtils.C.

Referenced by quasiTriElementsFixer().

445 {
446  std::vector<std::pair<Real, unsigned int>> distances;
447  const unsigned int n_vertices = elem.n_vertices();
448 
449  for (unsigned int i = 0; i < n_vertices; i++)
450  {
451  Point v1 = (*elem.node_ptr((i + 1) % n_vertices) - *elem.node_ptr(i % n_vertices));
452  distances.push_back(std::make_pair(v1.norm(), i));
453  }
454  std::sort(distances.begin(), distances.end());
455  return distances;
456 }
auto norm() const -> decltype(std::norm(Real()))
virtual unsigned int n_vertices() const=0
const Node * node_ptr(const unsigned int i) const