https://mooseframework.inl.gov
Loading...
Searching...
No Matches
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.
 
bool pointOnLine (const Real px, const Real py, const Real param_1, const Real param_2, const Real param_3, const Real dis_tol=libMesh::TOLERANCE)
 Determines whether a point on XY-plane is on a given line, to within a tolerance.
 
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.
 
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.
 
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.
 
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.
 
std::vector< std::pair< Real, unsigned int > > vertex_angles (const Elem &elem)
 Calculates the internal angles of a given 2D element.
 
std::vector< std::pair< Real, unsigned int > > vertex_distances (const Elem &elem)
 Calculates the distances between the vertices of a given 2D element.
 
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.
 
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.
 
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.
 
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.
 
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.
 
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 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.
 
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.
 
void lineRemoverMoveNode (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, const bool assign_ext_to_new, const bool side_to_remove)
 
bool quasiTriElementsFixer (ReplicatedMesh &mesh, const std::set< subdomain_id_type > &subdomain_ids_set, const subdomain_id_type tri_elem_subdomain_shift, const SubdomainName tri_elem_subdomain_name_suffix)
 
void triElemSplitter (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)
 
void triElemSplitter (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)
 
void quadElemSplitter (ReplicatedMesh &mesh, const dof_id_type elem_id, const subdomain_id_type tri_elem_subdomain_shift)
 
void quadToTriOnLine (ReplicatedMesh &mesh, const std::vector< Real > &cut_line_params, const dof_id_type tri_subdomain_id_shift, const SubdomainName tri_elem_subdomain_name_suffix)
 
void lineRemoverCutElemTri (ReplicatedMesh &mesh, const std::vector< Real > &cut_line_params, const subdomain_id_type block_id_to_remove, const boundary_id_type new_boundary_id)
 
void lineRemoverCutElem (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)
 
void boundaryTriElemImprover (ReplicatedMesh &mesh, const boundary_id_type boundary_to_improve)
 
void makeImprovedTriElement (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, const std::vector< boundary_id_type > &boundary_ids_for_side_0, const std::vector< boundary_id_type > &boundary_ids_for_side_2)
 
bool elemSideLocator (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)
 

Function Documentation

◆ boundaryTriElemImprover() [1/2]

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

Referenced by lineRemoverCutElem().

◆ boundaryTriElemImprover() [2/2]

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

Definition at line 1015 of file MooseMeshXYCuttingUtils.C.

1016{
1017 if (!MooseMeshUtils::hasBoundaryID(mesh, boundary_to_improve))
1018 mooseError(
1019 "MooseMeshXYCuttingUtils::boundaryTriElemImprover(): The boundary_to_improve provided "
1020 "does not exist in the given mesh.");
1021 BoundaryInfo & boundary_info = mesh.get_boundary_info();
1022 auto side_list = boundary_info.build_side_list();
1023 // Here we would like to collect the following information for all the TRI3 elements on the
1024 // boundary: Key: node id of the off-boundary node Value: a vector of tuples, each tuple contains
1025 // the following information:
1026 // 1. The element id of the element that is on the boundary to improve
1027 // 2. the one node id of that element that is on the boundary to improve
1028 // 3. the other node id of the element that is on the boundary to improve
1029 std::map<dof_id_type, std::vector<std::tuple<dof_id_type, dof_id_type, dof_id_type>>>
1030 tri3_elem_info;
1031 for (const auto & side : side_list)
1032 {
1033 if (std::get<2>(side) == boundary_to_improve)
1034 {
1035 Elem * elem = mesh.elem_ptr(std::get<0>(side));
1036 if (elem->type() == TRI3)
1037 {
1038 const auto key_node_id = elem->node_id((std::get<1>(side) + 2) % 3);
1039 const auto value_elem_id = elem->id();
1040 const auto value_node_id_1 = elem->node_id(std::get<1>(side));
1041 const auto value_node_id_2 = elem->node_id((std::get<1>(side) + 1) % 3);
1042 tri3_elem_info[key_node_id].push_back(
1043 std::make_tuple(value_elem_id, value_node_id_1, value_node_id_2));
1044 }
1045 }
1046 }
1047 // Elements that need to be removed
1048 std::vector<dof_id_type> elems_to_remove;
1049 // Now check if any group of TRI3 sharing an off-boundary node can be improved.
1050 for (const auto & tri_group : tri3_elem_info)
1051 {
1052 // It is possible to improve only when more than one TRI3 elements share the same off-boundary
1053 // node
1054 std::vector<std::pair<dof_id_type, dof_id_type>> node_assm;
1055 std::vector<dof_id_type> elem_id_list;
1056 for (const auto & tri : tri_group.second)
1057 {
1058 node_assm.push_back(std::make_pair(std::get<1>(tri), std::get<2>(tri)));
1059 elem_id_list.push_back(std::get<0>(tri));
1060 }
1061 std::vector<dof_id_type> ordered_node_list;
1062 std::vector<dof_id_type> ordered_elem_list;
1064 node_assm, elem_id_list, ordered_node_list, ordered_elem_list);
1065
1066 // For all the elements sharing the same off-boundary node, we need to know how many separated
1067 // subdomains are involved
1068 // If there are extra element ids defined on the mesh, they also want to retain their boundaries
1069 // Only triangle elements that share a side can be merged
1070 const unsigned int n_elem_extra_ids = mesh.n_elem_integers();
1071 std::vector<std::tuple<subdomain_id_type, std::vector<dof_id_type>, unsigned int>> blocks_info;
1072 for (const auto & elem_id : ordered_elem_list)
1073 {
1074 std::vector<dof_id_type> exist_extra_ids(n_elem_extra_ids);
1075 // Record all the element extra integers of the original quad element
1076 for (const auto j : make_range(n_elem_extra_ids))
1077 exist_extra_ids[j] = mesh.elem_ptr(elem_id)->get_extra_integer(j);
1078 if (!blocks_info.empty())
1079 {
1080 if (mesh.elem_ptr(elem_id)->subdomain_id() == std::get<0>(blocks_info.back()) &&
1081 exist_extra_ids == std::get<1>(blocks_info.back()))
1082 {
1083 std::get<2>(blocks_info.back())++;
1084 continue;
1085 }
1086 }
1087 blocks_info.push_back(
1088 std::make_tuple(mesh.elem_ptr(elem_id)->subdomain_id(), exist_extra_ids, 1));
1089 }
1090 // For each separated subdomain / set of extra ids, we try to improve the boundary elements
1091 unsigned int side_counter = 0;
1092 for (const auto & block_info : blocks_info)
1093 {
1094 const auto node_1 = mesh.node_ptr(ordered_node_list[side_counter]);
1095 // we do not need to subtract 1 for node_2
1096 const auto node_2 = mesh.node_ptr(ordered_node_list[side_counter + std::get<2>(block_info)]);
1097 const auto node_0 = mesh.node_ptr(tri_group.first);
1098 const Point v1 = *node_1 - *node_0;
1099 const Point v2 = *node_2 - *node_0;
1100 const Real angle = std::acos(v1 * v2 / v1.norm() / v2.norm()) / M_PI * 180.0;
1101 const std::vector<dof_id_type> block_elems(ordered_elem_list.begin() + side_counter,
1102 ordered_elem_list.begin() + side_counter +
1103 std::get<2>(block_info));
1104 // We assume that there are no sidesets defined inside a subdomain
1105 // For the first TRI3 element, we want to check if its side defined by node_0 and node_1 is
1106 // defined in any sidesets
1107 unsigned short side_id_0;
1108 unsigned short side_id_t;
1109 bool is_inverse_0;
1110 bool is_inverse_t;
1111 elemSideLocator(mesh,
1112 block_elems.front(),
1113 tri_group.first,
1114 ordered_node_list[side_counter],
1115 side_id_0,
1116 is_inverse_0);
1117 elemSideLocator(mesh,
1118 block_elems.back(),
1119 ordered_node_list[side_counter + std::get<2>(block_info)],
1120 tri_group.first,
1121 side_id_t,
1122 is_inverse_t);
1123 // Collect boundary information of the identified sides
1124 std::vector<boundary_id_type> side_0_boundary_ids;
1125 boundary_info.boundary_ids(
1126 mesh.elem_ptr(block_elems.front()), side_id_0, side_0_boundary_ids);
1127 std::vector<boundary_id_type> side_t_boundary_ids;
1128 boundary_info.boundary_ids(mesh.elem_ptr(block_elems.back()), side_id_t, side_t_boundary_ids);
1129
1130 // Ideally we want this angle to be 60 degrees
1131 // In reality, we want one TRI3 element if the angle is less than 90 degrees;
1132 // we want two TRI3 elements if the angle is greater than 90 degrees and less than 135
1133 // degrees; we want three TRI3 elements if the angle is greater than 135 degrees and less than
1134 // 180 degrees.
1135 if (angle < 90.0)
1136 {
1137 if (std::get<2>(block_info) > 1)
1138 {
1140 tri_group.first,
1141 ordered_node_list[side_counter],
1142 ordered_node_list[side_counter + std::get<2>(block_info)],
1143 std::get<0>(block_info),
1144 std::get<1>(block_info),
1145 {boundary_to_improve},
1146 side_0_boundary_ids,
1147 side_t_boundary_ids);
1148 elems_to_remove.insert(elems_to_remove.end(), block_elems.begin(), block_elems.end());
1149 }
1150 }
1151 else if (angle < 135.0)
1152 {
1153 // We can just add the middle node because there's nothing on the other side
1154 const auto node_m = mesh.add_point((*node_1 + *node_2) / 2.0);
1156 tri_group.first,
1157 ordered_node_list[side_counter],
1158 node_m->id(),
1159 std::get<0>(block_info),
1160 std::get<1>(block_info),
1161 {boundary_to_improve},
1162 side_0_boundary_ids,
1163 std::vector<boundary_id_type>());
1165 tri_group.first,
1166 node_m->id(),
1167 ordered_node_list[side_counter + std::get<2>(block_info)],
1168 std::get<0>(block_info),
1169 std::get<1>(block_info),
1170 {boundary_to_improve},
1171 std::vector<boundary_id_type>(),
1172 side_t_boundary_ids);
1173 elems_to_remove.insert(elems_to_remove.end(), block_elems.begin(), block_elems.end());
1174 }
1175 else
1176 {
1177 const auto node_m1 = mesh.add_point((*node_1 * 2.0 + *node_2) / 3.0);
1178 const auto node_m2 = mesh.add_point((*node_1 + *node_2 * 2.0) / 3.0);
1180 tri_group.first,
1181 ordered_node_list[side_counter],
1182 node_m1->id(),
1183 std::get<0>(block_info),
1184 std::get<1>(block_info),
1185 {boundary_to_improve},
1186 side_0_boundary_ids,
1187 std::vector<boundary_id_type>());
1189 tri_group.first,
1190 node_m1->id(),
1191 node_m2->id(),
1192 std::get<0>(block_info),
1193 std::get<1>(block_info),
1194 {boundary_to_improve},
1195 std::vector<boundary_id_type>(),
1196 std::vector<boundary_id_type>());
1198 tri_group.first,
1199 node_m2->id(),
1200 ordered_node_list[side_counter + std::get<2>(block_info)],
1201 std::get<0>(block_info),
1202 std::get<1>(block_info),
1203 {boundary_to_improve},
1204 std::vector<boundary_id_type>(),
1205 side_t_boundary_ids);
1206 elems_to_remove.insert(elems_to_remove.end(), block_elems.begin(), block_elems.end());
1207 }
1208 side_counter += std::get<2>(block_info);
1209 }
1210 // TODO: Need to check if the new element is inverted?
1211 }
1212 // Delete the original elements
1213 for (const auto & elem_to_remove : elems_to_remove)
1214 mesh.delete_elem(mesh.elem_ptr(elem_to_remove));
1215 mesh.contract();
1216}
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
MeshBase & mesh
bool hasBoundaryID(const MeshBase &input_mesh, const BoundaryID id)
Whether a particular boundary ID exists in the mesh.
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...
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,...
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.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
IntRange< T > make_range(T beg, T end)

◆ elemSideLocator() [1/2]

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

Referenced by boundaryTriElemImprover().

◆ elemSideLocator() [2/2]

bool MooseMeshXYCuttingUtils::elemSideLocator ( 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 
)

Definition at line 1249 of file MooseMeshXYCuttingUtils.C.

1255{
1256 Elem * elem = mesh.elem_ptr(elem_id);
1257 for (unsigned short i = 0; i < elem->n_sides(); i++)
1258 {
1259 if (elem->side_ptr(i)->node_ptr(0)->id() == node_id_0 &&
1260 elem->side_ptr(i)->node_ptr(1)->id() == node_id_1)
1261 {
1262 side_id = i;
1263 is_inverse = false;
1264 return true;
1265 }
1266 else if (elem->side_ptr(i)->node_ptr(0)->id() == node_id_1 &&
1267 elem->side_ptr(i)->node_ptr(1)->id() == node_id_0)
1268 {
1269 side_id = i;
1270 is_inverse = true;
1271 return true;
1272 }
1273 }
1274 return false;
1275}
const boundary_id_type side_id

◆ lineRemoverCutElem() [1/2]

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

Referenced by XYMeshLineCutter::generate().

◆ lineRemoverCutElem() [2/2]

void MooseMeshXYCuttingUtils::lineRemoverCutElem ( 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 
)

Definition at line 996 of file MooseMeshXYCuttingUtils.C.

1003{
1004 // Convert any quad elements crossed by the line into tri elements
1005 quadToTriOnLine(mesh, cut_line_params, tri_subdomain_id_shift, tri_elem_subdomain_name_suffix);
1006 // Then do the cutting for the preprocessed mesh that only contains tri elements crossed by the
1007 // cut line
1008 lineRemoverCutElemTri(mesh, cut_line_params, block_id_to_remove, new_boundary_id);
1009
1010 if (improve_boundary_tri_elems)
1011 boundaryTriElemImprover(mesh, new_boundary_id);
1012}
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 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 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() [1/2]

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

Referenced by lineRemoverCutElem().

◆ lineRemoverCutElemTri() [2/2]

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

Definition at line 774 of file MooseMeshXYCuttingUtils.C.

778{
779 // Find all the elements that are across the cutting line
780 std::vector<dof_id_type> cross_elems;
781 // A vector for element specific information
782 std::vector<std::vector<std::pair<dof_id_type, dof_id_type>>> node_pairs_vec;
783 // A set for unique pairs
784 std::vector<std::pair<dof_id_type, dof_id_type>> node_pairs_unique_vec;
785 for (auto elem_it = mesh.active_elements_begin(); elem_it != mesh.active_elements_end();
786 elem_it++)
787 {
788 const auto n_vertices = (*elem_it)->n_vertices();
789 unsigned int n_points_on_line = 0;
790 std::vector<unsigned short> node_side_rec(n_vertices, 0);
791 for (const auto i : make_range(n_vertices))
792 {
793 // First check if the vertex is in the XY Plane
794 if (!MooseUtils::absoluteFuzzyEqual((*elem_it)->point(i)(2), 0.0))
795 mooseError("MooseMeshXYCuttingUtils::lineRemoverCutElemTri() only works for 2D meshes in "
796 "XY Plane.");
797 const Point v_point = (*elem_it)->point(i);
798 if (pointOnLine(
799 v_point(0), v_point(1), cut_line_params[0], cut_line_params[1], cut_line_params[2]))
800 ++n_points_on_line;
801 else
802 node_side_rec[i] = lineSideDeterminator(v_point(0),
803 v_point(1),
804 cut_line_params[0],
805 cut_line_params[1],
806 cut_line_params[2],
807 true);
808 }
809 // This counts the booleans in node_side_rec, which does not include nodes
810 // that are exactly on the line (these nodes are excluded from the
811 // decision). In this case, num_nodes node lie on one side of the line and
812 // node_side_rec.size() - n_nodes lie on the other side. In the case that
813 // there are nodes on both sides of the line, we mark the element for
814 // removal.
815 const unsigned int num_nodes = std::accumulate(node_side_rec.begin(), node_side_rec.end(), 0);
816 if (num_nodes == node_side_rec.size() - n_points_on_line)
817 {
818 (*elem_it)->subdomain_id() = block_id_to_remove;
819 }
820 else if (num_nodes > 0)
821 {
822 if ((*elem_it)->n_vertices() != 3 || (*elem_it)->n_nodes() != 3)
823 mooseError("The element across the cutting line is not TRI3, which is not supported.");
824 cross_elems.push_back((*elem_it)->id());
825 // Then we need to check pairs of nodes that are on the different side
826 std::vector<std::pair<dof_id_type, dof_id_type>> node_pairs;
827 for (const auto i : index_range(node_side_rec))
828 {
829 // first node on removal side and second node on retaining side
830 if (node_side_rec[i] > 0 && node_side_rec[(i + 1) % node_side_rec.size()] == 0)
831 {
832 // Removal side first
833 node_pairs.push_back(
834 std::make_pair((*elem_it)->node_ptr(i)->id(),
835 (*elem_it)->node_ptr((i + 1) % node_side_rec.size())->id()));
836 node_pairs_unique_vec.push_back(node_pairs.back());
837 }
838 // first node on retaining side and second node on removal side
839 else if (node_side_rec[i] == 0 && node_side_rec[(i + 1) % node_side_rec.size()] > 0)
840 {
841 // Removal side first
842 node_pairs.push_back(
843 std::make_pair((*elem_it)->node_ptr((i + 1) % node_side_rec.size())->id(),
844 (*elem_it)->node_ptr(i)->id()));
845 node_pairs_unique_vec.push_back(node_pairs.back());
846 }
847 }
848 node_pairs_vec.push_back(node_pairs);
849 }
850 }
851 auto vec_ip = std::unique(node_pairs_unique_vec.begin(), node_pairs_unique_vec.end());
852 node_pairs_unique_vec.resize(std::distance(node_pairs_unique_vec.begin(), vec_ip));
853
854 // Loop over all the node pairs to define new nodes that sit on the cutting line
855 std::vector<Node *> nodes_on_line;
856 // whether the on-line node is overlapped with the node pairs or a brand new node
857 std::vector<unsigned short> nodes_on_line_overlap;
858 for (const auto & node_pair : node_pairs_unique_vec)
859 {
860 const Point pt1 = *mesh.node_ptr(node_pair.first);
861 const Point pt2 = *mesh.node_ptr(node_pair.second);
862 const Point pt_line = twoPointandLineIntersection(
863 pt1, pt2, cut_line_params[0], cut_line_params[1], cut_line_params[2]);
864 if ((pt_line - pt1).norm() < libMesh::TOLERANCE)
865 {
866 nodes_on_line.push_back(mesh.node_ptr(node_pair.first));
867 nodes_on_line_overlap.push_back(1);
868 }
869 else if ((pt_line - pt2).norm() < libMesh::TOLERANCE)
870 {
871 nodes_on_line.push_back(mesh.node_ptr(node_pair.second));
872 nodes_on_line_overlap.push_back(2);
873 }
874 else
875 {
876 nodes_on_line.push_back(mesh.add_point(pt_line));
877 nodes_on_line_overlap.push_back(0);
878 }
879 }
880
881 // make new elements
882 for (const auto i : index_range(cross_elems))
883 {
884 // Only TRI elements are involved after preprocessing
885 auto cross_elem = mesh.elem_ptr(cross_elems[i]);
886 auto node_0 = cross_elem->node_ptr(0);
887 auto node_1 = cross_elem->node_ptr(1);
888 auto node_2 = cross_elem->node_ptr(2);
889 const std::vector<dof_id_type> tri_nodes = {node_0->id(), node_1->id(), node_2->id()};
890
891 const auto online_node_index_1 = std::distance(node_pairs_unique_vec.begin(),
892 std::find(node_pairs_unique_vec.begin(),
893 node_pairs_unique_vec.end(),
894 node_pairs_vec[i][0]));
895 const auto online_node_index_2 = std::distance(node_pairs_unique_vec.begin(),
896 std::find(node_pairs_unique_vec.begin(),
897 node_pairs_unique_vec.end(),
898 node_pairs_vec[i][1]));
899 auto node_3 = nodes_on_line[online_node_index_1];
900 auto node_4 = nodes_on_line[online_node_index_2];
901 const auto node_3_overlap_flag = nodes_on_line_overlap[online_node_index_1];
902 const auto node_4_overlap_flag = nodes_on_line_overlap[online_node_index_2];
903 // Most common case, no overlapped nodes
904 if (node_3_overlap_flag == 0 && node_4_overlap_flag == 0)
905 {
906 // True if the common node is on the removal side; false if on the retaining side
907 const bool common_node_side = node_pairs_vec[i][0].first == node_pairs_vec[i][1].first;
908 const subdomain_id_type block_id_to_assign_1 =
909 common_node_side ? block_id_to_remove : cross_elem->subdomain_id();
910 const subdomain_id_type block_id_to_assign_2 =
911 common_node_side ? cross_elem->subdomain_id() : block_id_to_remove;
912 // The reference node ids need to be adjusted according to the common node of the two cut
913 // sides
914 const dof_id_type common_node_id =
915 common_node_side ? node_pairs_vec[i][0].first : node_pairs_vec[i][0].second;
916
917 triElemSplitter(mesh,
918 cross_elem->id(),
919 std::distance(tri_nodes.begin(),
920 std::find(tri_nodes.begin(), tri_nodes.end(), common_node_id)),
921 node_3->id(),
922 node_4->id(),
923 block_id_to_assign_1,
924 block_id_to_assign_2);
925 mesh.delete_elem(cross_elem);
926 }
927 // both node_3 and node_4 are overlapped
928 else if (node_3_overlap_flag > 0 && node_4_overlap_flag > 0)
929 {
930 // In this case, the entire element is on one side of the cutting line
931 // No change needed just check which side the element is on
932 cross_elem->subdomain_id() = lineSideDeterminator(cross_elem->vertex_average()(0),
933 cross_elem->vertex_average()(1),
934 cut_line_params[0],
935 cut_line_params[1],
936 cut_line_params[2],
937 true)
938 ? block_id_to_remove
939 : cross_elem->subdomain_id();
940 }
941 // node_3 or node_4 is overlapped
942 else
943 {
944 const auto node_3_finder = std::distance(
945 tri_nodes.begin(), std::find(tri_nodes.begin(), tri_nodes.end(), node_3->id()));
946 const auto node_4_finder = std::distance(
947 tri_nodes.begin(), std::find(tri_nodes.begin(), tri_nodes.end(), node_4->id()));
948 // As only one of the two above values should be less than the three, the smaller one should
949 // be used
950 const dof_id_type node_id = node_3_finder < node_4_finder ? node_4->id() : node_3->id();
951 const auto node_finder = std::min(node_3_finder, node_4_finder);
952
954 mesh,
955 cross_elem->id(),
956 node_finder,
957 node_id,
958 tri_nodes[(node_finder + 1) % 3] == node_pairs_vec[i][node_3_finder > node_4_finder].first
959 ? block_id_to_remove
960 : cross_elem->subdomain_id(),
961 tri_nodes[(node_finder + 1) % 3] == node_pairs_vec[i][node_3_finder > node_4_finder].first
962 ? cross_elem->subdomain_id()
963 : block_id_to_remove);
964 mesh.delete_elem(cross_elem);
965 }
966 }
967 mesh.contract();
968 // Due to the complexity, we identify the new boundary here together instead of during cutting of
969 // each element, because the preexisting element edges that are aligned with the cutting line also
970 // need to be added to the new boundary.
971 mesh.find_neighbors();
972 BoundaryInfo & boundary_info = mesh.get_boundary_info();
973 for (auto elem_it = mesh.active_elements_begin(); elem_it != mesh.active_elements_end();
974 elem_it++)
975 {
976 if ((*elem_it)->subdomain_id() != block_id_to_remove)
977 {
978 for (const auto j : make_range((*elem_it)->n_sides()))
979 {
980 if ((*elem_it)->neighbor_ptr(j) != nullptr)
981 if ((*elem_it)->neighbor_ptr(j)->subdomain_id() == block_id_to_remove)
982 boundary_info.add_side(*elem_it, j, new_boundary_id);
983 }
984 }
985 }
986
987 // Delete the block to remove
988 for (auto elem_it = mesh.active_subdomain_elements_begin(block_id_to_remove);
989 elem_it != mesh.active_subdomain_elements_end(block_id_to_remove);
990 elem_it++)
991 mesh.delete_elem(*elem_it);
992 mesh.contract();
993}
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.
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...
bool pointOnLine(const Real px, const Real py, const Real param_1, const Real param_2, const Real param_3, const Real dis_tol=libMesh::TOLERANCE)
Determines whether a point on XY-plane is on a given line, to within a tolerance.
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.
auto norm(const T &a)
auto index_range(const T &sizable)
TestClass subdomain_id_type
static constexpr Real TOLERANCE
uint8_t dof_id_type

◆ lineRemoverMoveNode() [1/2]

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

Referenced by XYMeshLineCutter::generate().

◆ lineRemoverMoveNode() [2/2]

void MooseMeshXYCuttingUtils::lineRemoverMoveNode ( 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,
const bool  assign_ext_to_new,
const bool  side_to_remove 
)

Definition at line 25 of file MooseMeshXYCuttingUtils.C.

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

◆ 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 273 of file MooseMeshXYCuttingUtils.C.

280{
281 const Real tmp = px * param_1 + py * param_2 + param_3;
282 return direction_param ? tmp >= dis_tol : tmp <= dis_tol;
283}

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

◆ makeImprovedTriElement() [1/2]

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

Referenced by boundaryTriElemImprover().

◆ makeImprovedTriElement() [2/2]

void MooseMeshXYCuttingUtils::makeImprovedTriElement ( 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,
const std::vector< boundary_id_type > &  boundary_ids_for_side_0,
const std::vector< boundary_id_type > &  boundary_ids_for_side_2 
)

Definition at line 1219 of file MooseMeshXYCuttingUtils.C.

1228{
1229 BoundaryInfo & boundary_info = mesh.get_boundary_info();
1230 Elem * elem_Tri3_new = mesh.add_elem(new Tri3);
1231 elem_Tri3_new->set_node(0, mesh.node_ptr(node_id_0));
1232 elem_Tri3_new->set_node(1, mesh.node_ptr(node_id_1));
1233 elem_Tri3_new->set_node(2, mesh.node_ptr(node_id_2));
1234 for (const auto & boundary_id_for_side_0 : boundary_ids_for_side_0)
1235 boundary_info.add_side(elem_Tri3_new, 0, boundary_id_for_side_0);
1236 for (const auto & boundary_id_for_side_1 : boundary_ids_for_side_1)
1237 boundary_info.add_side(elem_Tri3_new, 1, boundary_id_for_side_1);
1238 for (const auto & boundary_id_for_side_2 : boundary_ids_for_side_2)
1239 boundary_info.add_side(elem_Tri3_new, 2, boundary_id_for_side_2);
1240 elem_Tri3_new->subdomain_id() = subdomain_id;
1241 // Retain element extra integers
1242 for (const auto j : index_range(extra_elem_ids))
1243 {
1244 elem_Tri3_new->set_extra_integer(j, extra_elem_ids[j]);
1245 }
1246}

◆ pointOnLine()

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

Determines whether a point on XY-plane is on a given line, to within a tolerance.

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
dis_toltolerance used in determining whether the point is on the line
Returns
whether the point is on the line

Definition at line 262 of file MooseMeshXYCuttingUtils.C.

268{
269 return std::abs(px * param_1 + py * param_2 + param_3) <= dis_tol;
270}

Referenced by lineRemoverCutElemTri(), and quadToTriOnLine().

◆ quadElemSplitter() [1/2]

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

Referenced by quadToTriOnLine().

◆ quadElemSplitter() [2/2]

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

Definition at line 611 of file MooseMeshXYCuttingUtils.C.

614{
615 // Build boundary information of the mesh
616 BoundaryInfo & boundary_info = mesh.get_boundary_info();
617 auto bdry_side_list = boundary_info.build_side_list();
618 // Create a list of sidesets involving the element to be split
619 std::vector<std::vector<boundary_id_type>> elem_side_list;
620 elem_side_list.resize(4);
621 for (const auto i : index_range(bdry_side_list))
622 {
623 if (std::get<0>(bdry_side_list[i]) == elem_id)
624 {
625 elem_side_list[std::get<1>(bdry_side_list[i])].push_back(std::get<2>(bdry_side_list[i]));
626 }
627 }
628
629 auto node_0 = mesh.elem_ptr(elem_id)->node_ptr(0);
630 auto node_1 = mesh.elem_ptr(elem_id)->node_ptr(1);
631 auto node_2 = mesh.elem_ptr(elem_id)->node_ptr(2);
632 auto node_3 = mesh.elem_ptr(elem_id)->node_ptr(3);
633
634 const unsigned int n_elem_extra_ids = mesh.n_elem_integers();
635 std::vector<dof_id_type> exist_extra_ids(n_elem_extra_ids);
636 // Record all the element extra integers of the original quad element
637 for (const auto j : make_range(n_elem_extra_ids))
638 exist_extra_ids[j] = mesh.elem_ptr(elem_id)->get_extra_integer(j);
639
640 // There are two trivial ways to split a quad element
641 // We prefer the way that leads to triangles with similar areas
642 if (std::abs((*node_1 - *node_0).cross(*node_3 - *node_0).norm() -
643 (*node_1 - *node_2).cross(*node_3 - *node_2).norm()) >
644 std::abs((*node_0 - *node_1).cross(*node_2 - *node_1).norm() -
645 (*node_0 - *node_3).cross(*node_2 - *node_3).norm()))
646 {
647 Elem * elem_Tri3_0 = mesh.add_elem(new Tri3);
648 elem_Tri3_0->set_node(0, node_0);
649 elem_Tri3_0->set_node(1, node_1);
650 elem_Tri3_0->set_node(2, node_2);
651 elem_Tri3_0->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id() + tri_elem_subdomain_shift;
652 Elem * elem_Tri3_1 = mesh.add_elem(new Tri3);
653 elem_Tri3_1->set_node(0, node_0);
654 elem_Tri3_1->set_node(1, node_2);
655 elem_Tri3_1->set_node(2, node_3);
656 elem_Tri3_1->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id() + tri_elem_subdomain_shift;
657 // Retain element extra integers
658 for (const auto j : make_range(n_elem_extra_ids))
659 {
660 elem_Tri3_0->set_extra_integer(j, exist_extra_ids[j]);
661 elem_Tri3_1->set_extra_integer(j, exist_extra_ids[j]);
662 }
663
664 // Add sideset information to the new elements
665 for (const auto & side_info_0 : elem_side_list[0])
666 boundary_info.add_side(elem_Tri3_0, 0, side_info_0);
667 for (const auto & side_info_1 : elem_side_list[1])
668 boundary_info.add_side(elem_Tri3_0, 1, side_info_1);
669 for (const auto & side_info_2 : elem_side_list[2])
670 boundary_info.add_side(elem_Tri3_1, 1, side_info_2);
671 for (const auto & side_info_3 : elem_side_list[3])
672 boundary_info.add_side(elem_Tri3_1, 2, side_info_3);
673 }
674 else
675 {
676 Elem * elem_Tri3_0 = mesh.add_elem(new Tri3);
677 elem_Tri3_0->set_node(0, node_0);
678 elem_Tri3_0->set_node(1, node_1);
679 elem_Tri3_0->set_node(2, node_3);
680 elem_Tri3_0->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id() + tri_elem_subdomain_shift;
681 Elem * elem_Tri3_1 = mesh.add_elem(new Tri3);
682 elem_Tri3_1->set_node(0, node_1);
683 elem_Tri3_1->set_node(1, node_2);
684 elem_Tri3_1->set_node(2, node_3);
685 elem_Tri3_1->subdomain_id() = mesh.elem_ptr(elem_id)->subdomain_id() + tri_elem_subdomain_shift;
686 // Retain element extra integers
687 for (const auto j : make_range(n_elem_extra_ids))
688 {
689 elem_Tri3_0->set_extra_integer(j, exist_extra_ids[j]);
690 elem_Tri3_1->set_extra_integer(j, exist_extra_ids[j]);
691 }
692
693 // Add sideset information to the new elements
694 for (const auto & side_info_0 : elem_side_list[0])
695 boundary_info.add_side(elem_Tri3_0, 0, side_info_0);
696 for (const auto & side_info_1 : elem_side_list[1])
697 boundary_info.add_side(elem_Tri3_1, 0, side_info_1);
698 for (const auto & side_info_2 : elem_side_list[2])
699 boundary_info.add_side(elem_Tri3_1, 1, side_info_2);
700 for (const auto & side_info_3 : elem_side_list[3])
701 boundary_info.add_side(elem_Tri3_0, 2, side_info_3);
702 }
703}

◆ quadToTriOnLine() [1/2]

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

Referenced by lineRemoverCutElem().

◆ quadToTriOnLine() [2/2]

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

Definition at line 706 of file MooseMeshXYCuttingUtils.C.

710{
711 // Preprocess: find all the quad elements that are across the cutting line
712 std::vector<dof_id_type> cross_elems_quad;
713 std::set<subdomain_id_type> new_subdomain_ids;
714 for (auto elem_it = mesh.active_elements_begin(); elem_it != mesh.active_elements_end();
715 elem_it++)
716 {
717 if ((*elem_it)->n_vertices() == 4)
718 {
719 std::vector<unsigned short> node_side_rec;
720 for (const auto i : make_range(4))
721 {
722 const Point v_point = (*elem_it)->point(i);
723 if (!pointOnLine(
724 v_point(0), v_point(1), cut_line_params[0], cut_line_params[1], cut_line_params[2]))
725 node_side_rec.push_back(lineSideDeterminator(v_point(0),
726 v_point(1),
727 cut_line_params[0],
728 cut_line_params[1],
729 cut_line_params[2],
730 true));
731 }
732 // This counts the booleans in node_side_rec, which does not include nodes
733 // that are exactly on the line (these nodes are excluded from the
734 // decision). In this case, num_nodes node lie on one side of the line and
735 // node_side_rec.size() - n_nodes lie on the other side. In the case that
736 // there are nodes on both sides of the line, we mark the element for
737 // conversion.
738 const auto num_nodes = std::accumulate(node_side_rec.begin(), node_side_rec.end(), 0);
739 if (num_nodes != (int)node_side_rec.size() && num_nodes > 0)
740 {
741 cross_elems_quad.push_back((*elem_it)->id());
742 new_subdomain_ids.emplace((*elem_it)->subdomain_id() + tri_subdomain_id_shift);
743 }
744 }
745 }
746 // Then convert these quad elements into tri elements
747 for (const auto & cross_elem_quad : cross_elems_quad)
748 {
749 quadElemSplitter(mesh, cross_elem_quad, tri_subdomain_id_shift);
750 mesh.delete_elem(mesh.elem_ptr(cross_elem_quad));
751 }
752 for (auto & nid : new_subdomain_ids)
753 {
754 const SubdomainName old_name = mesh.subdomain_name(nid - tri_subdomain_id_shift);
756 (old_name.empty() ? (SubdomainName)(std::to_string(nid - tri_subdomain_id_shift))
757 : old_name) +
758 "_" + tri_elem_subdomain_name_suffix,
760 throw MooseException("The new subdomain name already exists in the mesh.");
761 mesh.set_subdomain_name(nid,
762 (old_name.empty()
763 ? (SubdomainName)(std::to_string(nid - tri_subdomain_id_shift))
764 : old_name) +
765 "_" + tri_elem_subdomain_name_suffix);
766 mooseWarning("QUAD elements have been converted into TRI elements with a new "
767 "subdomain name: " +
768 mesh.subdomain_name(nid) + ".");
769 }
770 mesh.contract();
771}
void mooseWarning(Args &&... args)
Emit a warning message with the given stringified, concatenated args.
Definition MooseError.h:345
Provides a way for users to bail out of the current solve.
SubdomainID getSubdomainID(const SubdomainName &subdomain_name, const MeshBase &mesh)
Gets the subdomain ID associated with the given SubdomainName.
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.
MOOSE now contains C++17 code, so give a reasonable error message stating what the user can do to add...
const SubdomainID INVALID_BLOCK_ID
Definition MooseTypes.C:20

◆ quasiTriElementsFixer() [1/2]

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

Referenced by XYMeshLineCutter::generate().

◆ quasiTriElementsFixer() [2/2]

bool MooseMeshXYCuttingUtils::quasiTriElementsFixer ( ReplicatedMesh &  mesh,
const std::set< subdomain_id_type > &  subdomain_ids_set,
const subdomain_id_type  tri_elem_subdomain_shift,
const SubdomainName  tri_elem_subdomain_name_suffix 
)

Definition at line 315 of file MooseMeshXYCuttingUtils.C.

319{
320 BoundaryInfo & boundary_info = mesh.get_boundary_info();
321 // Define the subdomain id shift for the new TRI3 element subdomain(s)
322 const subdomain_id_type max_subdomain_id(*subdomain_ids_set.rbegin());
323 const subdomain_id_type tri_subdomain_id_shift =
324 tri_elem_subdomain_shift == Moose::INVALID_BLOCK_ID ? max_subdomain_id
325 : tri_elem_subdomain_shift;
326 mooseAssert(std::numeric_limits<subdomain_id_type>::max() - max_subdomain_id >
327 tri_subdomain_id_shift,
328 "The TRI elements subdomain id to be assigned may exceed the numeric limit.");
329 const unsigned int n_elem_extra_ids = mesh.n_elem_integers();
330 std::vector<dof_id_type> exist_extra_ids(n_elem_extra_ids);
331 std::vector<std::tuple<Elem *, unsigned int, bool, bool>> bad_elems_rec;
332 // Loop over all the active elements to find any degenerate QUAD elements
333 for (auto & elem : as_range(mesh.active_elements_begin(), mesh.active_elements_end()))
334 {
335 // Two types of degenerate QUAD elements are identified:
336 // (1) QUAD elements with three collinear vertices
337 // (2) QUAD elements with two overlapped vertices
338 const auto elem_angles = vertex_angles(*elem);
339 const auto elem_distances = vertex_distances(*elem);
340 // Type 1
341 if (MooseUtils::absoluteFuzzyEqual(elem_angles.front().first, M_PI, 0.001))
342 {
343 bad_elems_rec.push_back(std::make_tuple(elem, elem_angles.front().second, false, true));
344 continue;
345 }
346 // Type 2
347 if (MooseUtils::absoluteFuzzyEqual(elem_distances.front().first, 0.0))
348 {
349 bad_elems_rec.push_back(std::make_tuple(elem, elem_distances.front().second, false, false));
350 }
351 }
352 std::set<subdomain_id_type> new_subdomain_ids;
353 // Loop over all the identified degenerate QUAD elements
354 for (const auto & bad_elem : bad_elems_rec)
355 {
356 std::vector<boundary_id_type> elem_bdry_container_0;
357 std::vector<boundary_id_type> elem_bdry_container_1;
358 std::vector<boundary_id_type> elem_bdry_container_2;
359
360 Elem * elem_0 = std::get<0>(bad_elem);
361 if (std::get<3>(bad_elem))
362 {
363 // elems 1 and 2 are the neighboring elements of the degenerate element corresponding to the
364 // two collinear sides.
365 // For the degenerated element with three colinear vertices, if the elems 1 and 2 do not
366 // exist, the two sides are on the external boundary formed by trimming.
367 Elem * elem_1 = elem_0->neighbor_ptr(std::get<1>(bad_elem));
368 Elem * elem_2 = elem_0->neighbor_ptr((std::get<1>(bad_elem) - 1) % elem_0->n_vertices());
369 if ((elem_1 != nullptr || elem_2 != nullptr))
370 throw MooseException("The input mesh has degenerate quad element before trimming.");
371 }
372 mesh.get_boundary_info().boundary_ids(elem_0, std::get<1>(bad_elem), elem_bdry_container_0);
373 mesh.get_boundary_info().boundary_ids(
374 elem_0, (std::get<1>(bad_elem) + 1) % elem_0->n_vertices(), elem_bdry_container_1);
375 mesh.get_boundary_info().boundary_ids(
376 elem_0, (std::get<1>(bad_elem) + 2) % elem_0->n_vertices(), elem_bdry_container_2);
377 mesh.get_boundary_info().boundary_ids(
378 elem_0, (std::get<1>(bad_elem) + 3) % elem_0->n_vertices(), elem_bdry_container_0);
379
380 // Record subdomain id of the degenerate element
381 auto elem_block_id = elem_0->subdomain_id();
382 // Define the three of four nodes that will be used to generate the TRI element
383 auto pt0 = elem_0->node_ptr((std::get<1>(bad_elem) + 1) % elem_0->n_vertices());
384 auto pt1 = elem_0->node_ptr((std::get<1>(bad_elem) + 2) % elem_0->n_vertices());
385 auto pt2 = elem_0->node_ptr((std::get<1>(bad_elem) + 3) % elem_0->n_vertices());
386 // Record all the element extra integers of the degenerate element
387 for (const auto j : make_range(n_elem_extra_ids))
388 exist_extra_ids[j] = elem_0->get_extra_integer(j);
389 // Delete the degenerate QUAD element
390 mesh.delete_elem(elem_0);
391 // Create the new TRI element
392 Elem * elem_Tri3 = mesh.add_elem(new Tri3);
393 elem_Tri3->set_node(0, pt0);
394 elem_Tri3->set_node(1, pt1);
395 elem_Tri3->set_node(2, pt2);
396 // Retain the boundary information
397 for (auto bdry_id : elem_bdry_container_0)
398 boundary_info.add_side(elem_Tri3, 2, bdry_id);
399 for (auto bdry_id : elem_bdry_container_1)
400 boundary_info.add_side(elem_Tri3, 0, bdry_id);
401 for (auto bdry_id : elem_bdry_container_2)
402 boundary_info.add_side(elem_Tri3, 1, bdry_id);
403 // Assign subdomain id for the TRI element by shifting its original subdomain id
404 elem_Tri3->subdomain_id() = elem_block_id + tri_subdomain_id_shift;
405 new_subdomain_ids.emplace(elem_block_id + tri_subdomain_id_shift);
406 // Retain element extra integers
407 for (const auto j : make_range(n_elem_extra_ids))
408 elem_Tri3->set_extra_integer(j, exist_extra_ids[j]);
409 }
410 // Assign subdomain names for the new TRI elements
411 for (auto & nid : new_subdomain_ids)
412 {
413 const SubdomainName old_name = mesh.subdomain_name(nid - tri_subdomain_id_shift);
415 (old_name.empty() ? (SubdomainName)(std::to_string(nid - tri_subdomain_id_shift))
416 : old_name) +
417 "_" + tri_elem_subdomain_name_suffix,
419 throw MooseException("The new subdomain name already exists in the mesh.");
420 mesh.set_subdomain_name(nid,
421 (old_name.empty()
422 ? (SubdomainName)(std::to_string(nid - tri_subdomain_id_shift))
423 : old_name) +
424 "_" + tri_elem_subdomain_name_suffix);
425 mooseWarning("Degenerate QUAD elements have been converted into TRI elements with a new "
426 "subdomain name: " +
427 mesh.subdomain_name(nid) + ".");
428 }
429 return bad_elems_rec.size();
430}
std::vector< std::pair< Real, unsigned int > > vertex_distances(const Elem &elem)
Calculates the distances between the vertices of a given 2D element.
std::vector< std::pair< Real, unsigned int > > vertex_angles(const Elem &elem)
Calculates the internal angles of a given 2D element.

◆ triElemSplitter() [1/4]

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

Referenced by lineRemoverCutElemTri().

◆ triElemSplitter() [2/4]

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

◆ triElemSplitter() [3/4]

void MooseMeshXYCuttingUtils::triElemSplitter ( 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 
)

Definition at line 469 of file MooseMeshXYCuttingUtils.C.

476{
477 const auto elem_old = mesh.elem_ptr(elem_id);
478 const dof_id_type nid_0 = elem_old->node_ptr(node_shift % 3)->id();
479 const dof_id_type nid_1 = elem_old->node_ptr((1 + node_shift) % 3)->id();
480 const dof_id_type nid_2 = elem_old->node_ptr((2 + node_shift) % 3)->id();
481
482 const bool m1_side_flag =
483 MooseUtils::absoluteFuzzyEqual((*(mesh.node_ptr(nid_3)) - *(mesh.node_ptr(nid_0)))
484 .cross(*(mesh.node_ptr(nid_1)) - *(mesh.node_ptr(nid_0)))
485 .norm(),
486 0.0);
487 const dof_id_type nid_m1 = m1_side_flag ? nid_3 : nid_4;
488 const dof_id_type nid_m2 = m1_side_flag ? nid_4 : nid_3;
489 // Build boundary information of the mesh
490 BoundaryInfo & boundary_info = mesh.get_boundary_info();
491 auto bdry_side_list = boundary_info.build_side_list();
492 // Create a list of sidesets involving the element to be split
493 std::vector<std::vector<boundary_id_type>> elem_side_list;
494 elem_side_list.resize(3);
495 for (const auto i : index_range(bdry_side_list))
496 {
497 if (std::get<0>(bdry_side_list[i]) == elem_id)
498 {
499 elem_side_list[(std::get<1>(bdry_side_list[i]) + 3 - node_shift) % 3].push_back(
500 std::get<2>(bdry_side_list[i]));
501 }
502 }
503
504 const unsigned int n_elem_extra_ids = mesh.n_elem_integers();
505 std::vector<dof_id_type> exist_extra_ids(n_elem_extra_ids);
506 // Record all the element extra integers of the original element
507 for (const auto j : make_range(n_elem_extra_ids))
508 exist_extra_ids[j] = mesh.elem_ptr(elem_id)->get_extra_integer(j);
509
510 Elem * elem_Tri3_0 = mesh.add_elem(new Tri3);
511 elem_Tri3_0->set_node(0, mesh.node_ptr(nid_0));
512 elem_Tri3_0->set_node(1, mesh.node_ptr(nid_m1));
513 elem_Tri3_0->set_node(2, mesh.node_ptr(nid_m2));
514 elem_Tri3_0->subdomain_id() = single_elem_side_id;
515 Elem * elem_Tri3_1 = mesh.add_elem(new Tri3);
516 elem_Tri3_1->set_node(0, mesh.node_ptr(nid_1));
517 elem_Tri3_1->set_node(1, mesh.node_ptr(nid_m2));
518 elem_Tri3_1->set_node(2, mesh.node_ptr(nid_m1));
519 elem_Tri3_1->subdomain_id() = double_elem_side_id;
520 Elem * elem_Tri3_2 = mesh.add_elem(new Tri3);
521 elem_Tri3_2->set_node(0, mesh.node_ptr(nid_2));
522 elem_Tri3_2->set_node(1, mesh.node_ptr(nid_m2));
523 elem_Tri3_2->set_node(2, mesh.node_ptr(nid_1));
524 elem_Tri3_2->subdomain_id() = double_elem_side_id;
525 // Retain element extra integers
526 for (const auto j : make_range(n_elem_extra_ids))
527 {
528 elem_Tri3_0->set_extra_integer(j, exist_extra_ids[j]);
529 elem_Tri3_1->set_extra_integer(j, exist_extra_ids[j]);
530 elem_Tri3_2->set_extra_integer(j, exist_extra_ids[j]);
531 }
532
533 // Add sideset information to the new elements
534 for (const auto & side_info_0 : elem_side_list[0])
535 {
536 boundary_info.add_side(elem_Tri3_0, 0, side_info_0);
537 boundary_info.add_side(elem_Tri3_1, 2, side_info_0);
538 }
539 for (const auto & side_info_1 : elem_side_list[1])
540 boundary_info.add_side(elem_Tri3_2, 2, side_info_1);
541 for (const auto & side_info_2 : elem_side_list[2])
542 {
543 boundary_info.add_side(elem_Tri3_0, 2, side_info_2);
544 boundary_info.add_side(elem_Tri3_2, 0, side_info_2);
545 }
546}

◆ triElemSplitter() [4/4]

void MooseMeshXYCuttingUtils::triElemSplitter ( 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 
)

Definition at line 549 of file MooseMeshXYCuttingUtils.C.

555{
556 const auto elem_old = mesh.elem_ptr(elem_id);
557 const dof_id_type nid_0 = elem_old->node_ptr(node_shift % 3)->id();
558 const dof_id_type nid_1 = elem_old->node_ptr((1 + node_shift) % 3)->id();
559 const dof_id_type nid_2 = elem_old->node_ptr((2 + node_shift) % 3)->id();
560 // Build boundary information of the mesh
561 BoundaryInfo & boundary_info = mesh.get_boundary_info();
562 auto bdry_side_list = boundary_info.build_side_list();
563 // Create a list of sidesets involving the element to be split
564 std::vector<std::vector<boundary_id_type>> elem_side_list;
565 elem_side_list.resize(3);
566 for (const auto i : index_range(bdry_side_list))
567 {
568 if (std::get<0>(bdry_side_list[i]) == elem_id)
569 {
570 elem_side_list[(std::get<1>(bdry_side_list[i]) + 3 - node_shift) % 3].push_back(
571 std::get<2>(bdry_side_list[i]));
572 }
573 }
574
575 const unsigned int n_elem_extra_ids = mesh.n_elem_integers();
576 std::vector<dof_id_type> exist_extra_ids(n_elem_extra_ids);
577 // Record all the element extra integers of the original element
578 for (const auto j : make_range(n_elem_extra_ids))
579 exist_extra_ids[j] = mesh.elem_ptr(elem_id)->get_extra_integer(j);
580
581 Elem * elem_Tri3_0 = mesh.add_elem(new Tri3);
582 elem_Tri3_0->set_node(0, mesh.node_ptr(nid_0));
583 elem_Tri3_0->set_node(1, mesh.node_ptr(nid_1));
584 elem_Tri3_0->set_node(2, mesh.node_ptr(nid_m));
585 elem_Tri3_0->subdomain_id() = first_elem_side_id;
586 Elem * elem_Tri3_1 = mesh.add_elem(new Tri3);
587 elem_Tri3_1->set_node(0, mesh.node_ptr(nid_0));
588 elem_Tri3_1->set_node(1, mesh.node_ptr(nid_m));
589 elem_Tri3_1->set_node(2, mesh.node_ptr(nid_2));
590 elem_Tri3_1->subdomain_id() = second_elem_side_id;
591 // Retain element extra integers
592 for (const auto j : make_range(n_elem_extra_ids))
593 {
594 elem_Tri3_0->set_extra_integer(j, exist_extra_ids[j]);
595 elem_Tri3_1->set_extra_integer(j, exist_extra_ids[j]);
596 }
597
598 // Add sideset information to the new elements
599 for (const auto & side_info_0 : elem_side_list[0])
600 boundary_info.add_side(elem_Tri3_0, 0, side_info_0);
601 for (const auto & side_info_1 : elem_side_list[1])
602 {
603 boundary_info.add_side(elem_Tri3_0, 1, side_info_1);
604 boundary_info.add_side(elem_Tri3_1, 1, side_info_1);
605 }
606 for (const auto & side_info_2 : elem_side_list[2])
607 boundary_info.add_side(elem_Tri3_1, 2, side_info_2);
608}

◆ 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 286 of file MooseMeshXYCuttingUtils.C.

292{
293 return Point(
294 (param_12 * param_23 - param_22 * param_13) / (param_11 * param_22 - param_21 * param_12),
295 (param_13 * param_21 - param_23 * param_11) / (param_11 * param_22 - param_21 * param_12),
296 0.0);
297}

Referenced by twoPointandLineIntersection().

◆ twoPointandLineIntersection()

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

Definition at line 300 of file MooseMeshXYCuttingUtils.C.

305{
306 return twoLineIntersection(param_1,
307 param_2,
308 param_3,
309 pt2(1) - pt1(1),
310 pt1(0) - pt2(0),
311 pt2(0) * pt1(1) - pt1(0) * pt2(1));
312}
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.

Referenced by lineRemoverCutElemTri(), and lineRemoverMoveNode().

◆ vertex_angles()

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

Definition at line 433 of file MooseMeshXYCuttingUtils.C.

434{
435 std::vector<std::pair<Real, unsigned int>> angles;
436 const unsigned int n_vertices = elem.n_vertices();
437
438 for (const auto i : make_range(n_vertices))
439 {
440 Point v1 = (*elem.node_ptr((i - 1) % n_vertices) - *elem.node_ptr(i % n_vertices));
441 Point v2 = (*elem.node_ptr((i + 1) % n_vertices) - *elem.node_ptr(i % n_vertices));
442 Real tmp = v1 * v2 / v1.norm() / v2.norm();
443 if (tmp > 1.0)
444 tmp = 1.0;
445 else if (tmp < -1.0)
446 tmp = -1.0;
447 angles.push_back(std::make_pair(acos(tmp), i));
448 }
449 std::sort(angles.begin(), angles.end(), std::greater<>());
450 return angles;
451}

Referenced by quasiTriElementsFixer().

◆ vertex_distances()

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

Definition at line 454 of file MooseMeshXYCuttingUtils.C.

455{
456 std::vector<std::pair<Real, unsigned int>> distances;
457 const unsigned int n_vertices = elem.n_vertices();
458
459 for (const auto i : make_range(n_vertices))
460 {
461 Point v1 = (*elem.node_ptr((i + 1) % n_vertices) - *elem.node_ptr(i % n_vertices));
462 distances.push_back(std::make_pair(v1.norm(), i));
463 }
464 std::sort(distances.begin(), distances.end());
465 return distances;
466}

Referenced by quasiTriElementsFixer().