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)
 
Point twoPointandLineIntersection (const Point &pt1, const Point &pt2, const Real param_1, const Real param_2, const Real param_3)
 
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)
 
std::vector< std::pair< Real, unsigned int > > vertex_angles (const Elem &elem)
 
std::vector< std::pair< Real, unsigned int > > vertex_distances (const Elem &elem)
 
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 1017 of file MooseMeshXYCuttingUtils.C.

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

1257{
1258 Elem * elem = mesh.elem_ptr(elem_id);
1259 for (unsigned short i = 0; i < elem->n_sides(); i++)
1260 {
1261 if (elem->side_ptr(i)->node_ptr(0)->id() == node_id_0 &&
1262 elem->side_ptr(i)->node_ptr(1)->id() == node_id_1)
1263 {
1264 side_id = i;
1265 is_inverse = false;
1266 return true;
1267 }
1268 else if (elem->side_ptr(i)->node_ptr(0)->id() == node_id_1 &&
1269 elem->side_ptr(i)->node_ptr(1)->id() == node_id_0)
1270 {
1271 side_id = i;
1272 is_inverse = true;
1273 return true;
1274 }
1275 }
1276 return false;
1277}
virtual std::unique_ptr< Elem > side_ptr(unsigned int i)=0
virtual unsigned int n_sides() const=0
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 998 of file MooseMeshXYCuttingUtils.C.

1005{
1006 // Convert any quad elements crossed by the line into tri elements
1007 quadToTriOnLine(mesh, cut_line_params, tri_subdomain_id_shift, tri_elem_subdomain_name_suffix);
1008 // Then do the cutting for the preprocessed mesh that only contains tri elements crossed by the
1009 // cut line
1010 lineRemoverCutElemTri(mesh, cut_line_params, block_id_to_remove, new_boundary_id);
1011
1012 if (improve_boundary_tri_elems)
1013 boundaryTriElemImprover(mesh, new_boundary_id);
1014}
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 776 of file MooseMeshXYCuttingUtils.C.

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

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 (const auto i : index_range(bdry_side_list))
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 (const auto i : make_range((*elem_it)->n_vertices()))
58 {
59 // First check if the vertex is on the XY-Plane
60 if (!MooseUtils::absoluteFuzzyEqual((*elem_it)->point(i)(2), 0.0))
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 (const auto i : make_range(mesh.elem_ptr(elem_id)->n_sides()))
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 (const auto i : make_range((*elem_it)->n_sides()))
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 (const auto i : index_range(slc_bdry_side_list))
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 (const auto i : index_range(node_list))
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 (const auto i : make_range((*elem_it)->n_sides()))
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}
void remove_side(const Elem *elem, const unsigned short int side)
const Elem * neighbor_ptr(unsigned int i) const
void prepare_for_use(const bool skip_renumber_nodes_and_elements, const bool skip_find_neighbors)
if(subdm)

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

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

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

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

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

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

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

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

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

712{
713 // Preprocess: find all the quad elements that are across the cutting line
714 std::vector<dof_id_type> cross_elems_quad;
715 std::set<subdomain_id_type> new_subdomain_ids;
716 for (auto elem_it = mesh.active_elements_begin(); elem_it != mesh.active_elements_end();
717 elem_it++)
718 {
719 if ((*elem_it)->n_vertices() == 4)
720 {
721 std::vector<unsigned short> node_side_rec;
722 for (const auto i : make_range(4))
723 {
724 const Point v_point = (*elem_it)->point(i);
725 if (!pointOnLine(
726 v_point(0), v_point(1), cut_line_params[0], cut_line_params[1], cut_line_params[2]))
727 node_side_rec.push_back(lineSideDeterminator(v_point(0),
728 v_point(1),
729 cut_line_params[0],
730 cut_line_params[1],
731 cut_line_params[2],
732 true));
733 }
734 // This counts the booleans in node_side_rec, which does not include nodes
735 // that are exactly on the line (these nodes are excluded from the
736 // decision). In this case, num_nodes node lie on one side of the line and
737 // node_side_rec.size() - n_nodes lie on the other side. In the case that
738 // there are nodes on both sides of the line, we mark the element for
739 // conversion.
740 const auto num_nodes = std::accumulate(node_side_rec.begin(), node_side_rec.end(), 0);
741 if (num_nodes != (int)node_side_rec.size() && num_nodes > 0)
742 {
743 cross_elems_quad.push_back((*elem_it)->id());
744 new_subdomain_ids.emplace((*elem_it)->subdomain_id() + tri_subdomain_id_shift);
745 }
746 }
747 }
748 // Then convert these quad elements into tri elements
749 for (const auto & cross_elem_quad : cross_elems_quad)
750 {
751 quadElemSplitter(mesh, cross_elem_quad, tri_subdomain_id_shift);
752 mesh.delete_elem(mesh.elem_ptr(cross_elem_quad));
753 }
754 for (auto & nid : new_subdomain_ids)
755 {
756 const SubdomainName old_name = mesh.subdomain_name(nid - tri_subdomain_id_shift);
758 (old_name.empty() ? (SubdomainName)(std::to_string(nid - tri_subdomain_id_shift))
759 : old_name) +
760 "_" + tri_elem_subdomain_name_suffix,
762 throw MooseException("The new subdomain name already exists in the mesh.");
764 (old_name.empty()
765 ? (SubdomainName)(std::to_string(nid - tri_subdomain_id_shift))
766 : old_name) +
767 "_" + tri_elem_subdomain_name_suffix);
768 mooseWarning("QUAD elements have been converted into TRI elements with a new "
769 "subdomain name: " +
770 mesh.subdomain_name(nid) + ".");
771 }
772 mesh.contract();
773}
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.
void set_subdomain_name(subdomain_id_type id, const std::string &name, bool synchronous=false)
std::string & subdomain_name(subdomain_id_type id)
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 317 of file MooseMeshXYCuttingUtils.C.

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

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

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

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

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

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

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

Referenced by twoPointandLineIntersection().

◆ 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

Referenced by lineRemoverCutElemTri(), and lineRemoverMoveNode().

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

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

Referenced by quasiTriElementsFixer().

◆ vertex_angles() [2/2]

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

Definition at line 435 of file MooseMeshXYCuttingUtils.C.

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

◆ 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

Referenced by quasiTriElementsFixer().

◆ vertex_distances() [2/2]

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

Definition at line 456 of file MooseMeshXYCuttingUtils.C.

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