| Base 701993 | Head #31761 28487c | ||||
|---|---|---|---|---|---|
| Total | Total | +/- | New | ||
| Rate | 85.97% | 85.97% | +0.00% | 100.00% | |
| Hits | 124953 | 124986 | +33 | 136 | |
| Misses | 20387 | 20389 | +2 | 0 | |
| Filename | Stmts | Miss | Cover |
|---|---|---|---|
| framework/src/meshgenerators/BoundaryElementConversionGenerator.C | +1 | 0 | +0.35% |
| framework/src/meshgenerators/CombinerGenerator.C | -70 | -15 | +4.77% |
| framework/src/meshgenerators/XYZDelaunayGenerator.C | +27 | +2 | -0.45% |
| framework/src/utils/MooseMeshUtils.C | +77 | +15 | -0.54% |
| TOTAL | +35 | +2 | +0.00% |
codecodecode+
64 65 66 67 + 68 69 + 70 71 72 |
std::unique_ptr<MeshBase> BoundaryElementConversionGenerator::generate() { UnstructuredMesh & mesh = dynamic_cast<UnstructuredMesh &>(*_input); libMesh::MeshSerializer serial_mesh(mesh); // collect the subdomain ids of the original mesh std::set<subdomain_id_type> original_subdomain_ids; |
110 111 112 113 + 114 + 115 116 + 117 118 119 |
} // MeshSerializer's destructor calls delete_remote_elements() if (!mesh.is_replicated()) mesh.prepare_for_use(); else mesh.set_isnt_prepared(); return std::move(_input); } |
167 168 169 170 + 171 + 172 + 173 + 174 + 175 176 177 |
*other_mesh, _positions[i](0), _positions[i](1), _positions[i](2)); } MooseMeshUtils::copyIntoMesh(*this, *mesh, *other_mesh, _avoid_merging_subdomains, _avoid_merging_boundaries, _communicator); } |
220 221 222 223 + 224 + 225 + 226 + 227 + 228 229 230 |
*translated_mesh, _positions[i](0), _positions[i](1), _positions[i](2)); // Copy into final mesh MooseMeshUtils::copyIntoMesh(*this, *final_mesh, *translated_mesh, _avoid_merging_subdomains, _avoid_merging_boundaries, _communicator); // Reset nodal coordinates |
83 84 85 86 + 87 + 88 89 90 91 92 93 94 + 95 96 + 97 98 99 |
"Whether to convert the 3D hole meshes with non-TRI3 surface sides into " "a compatible form."); MooseEnum conversion_method("ALL SURFACE", "ALL"); params.addParam<MooseEnum>( "conversion_method", conversion_method, "The method to convert 3D hole meshes into compatible meshes. Options are " "ALL: convert all elements into TET4; SURFACE: convert only the surface elements " "that are stitched to the generated Delaunay mesh."); params.addParam<bool>( "combined_stitching", false, "Whether to stitch all holes in one combined stitching step. This is efficient if a great " "number of holes are to be stitched. But it is hard to debug if problems arise."); |
125 126 127 128 + 129 + 130 131 132 |
_hole_ptrs(getMeshes("holes")), _stitch_holes(getParam<std::vector<bool>>("stitch_holes")), _convert_holes_for_stitching(getParam<bool>("convert_holes_for_stitching")), _conversion_method(parameters.get<MooseEnum>("conversion_method")), _combined_stitching(parameters.get<bool>("combined_stitching")), _algorithm(parameters.get<MooseEnum>("algorithm")), _verbose_stitching(parameters.get<bool>("verbose_stitching")) { |
140 141 142 143 + 144 + 145 146 147 |
paramError("hole_boundaries", "Need one hole_boundaries entry per hole, if specified."); } if (isParamSetByUser("conversion_method") && !_convert_holes_for_stitching) paramError( "conversion_method", "This parameter is only applicable when convert_holes_for_stitching is set to true."); } |
178 179 180 181 + 182 183 184 |
// We do not need to worry about element order here as libMesh checks it std::set<ElemType> hole_elem_types; std::set<unsigned short> hole_elem_dims; std::vector<std::pair<dof_id_type, unsigned int>> hole_elem_external_sides; for (auto elem : hole_mesh.element_ptr_range()) { hole_elem_dims.emplace(elem->dim()); |
192 193 194 195 + 196 197 198 |
if (!elem->neighbor_ptr(s)) { hole_elem_types.emplace(elem->side_ptr(s)->type()); hole_elem_external_sides.emplace_back(elem->id(), s); } } // For a non-3D element, we just need to record the element type |
215 216 217 218 + 219 220 221 + 222 + 223 + 224 + 225 + 226 + 227 228 229 + 230 + 231 232 + 233 234 235 |
paramError("holes", "3D hole meshes with non-TRI3 surface elements cannot be stitched without " "converting them to TET4. Consider setting convert_holes_for_stitching=true."); else if (_stitch_holes.size() && _stitch_holes[hole_i] && _conversion_method == "SURFACE") { // Create a transition layer with triangle sides on the external boundary of the hole BoundaryID temp_ext_bid = MooseMeshUtils::getNextFreeBoundaryID(hole_mesh); for (const auto & hees : hole_elem_external_sides) hole_mesh.get_boundary_info().add_side(hees.first, hees.second, temp_ext_bid); MooseMeshElementConversionUtils::transitionLayerGenerator( hole_mesh, std::vector<BoundaryName>({std::to_string(temp_ext_bid)}), 1, false); hole_mesh.get_boundary_info().remove_id(temp_ext_bid); // MeshSerializer's destructor calls delete_remote_elements() // For replicated meshes, we need to refresh the neighbor information if (!hole_mesh.is_replicated()) hole_mesh.prepare_for_use(); else hole_mesh.find_neighbors(); } else MeshTools::Modification::all_tri(**_hole_ptrs[hole_i]); |
558 559 560 561 + 562 + 563 564 565 + 566 567 568 569 570 + 571 572 573 + 574 + 575 576 577 578 + 579 580 + 581 582 583 584 585 586 + 587 + 588 589 590 |
const auto & increment_subdomain_map = hole_mesh.get_subdomain_name_map(); main_subdomain_map.insert(increment_subdomain_map.begin(), increment_subdomain_map.end()); if (_combined_stitching) MooseMeshUtils::copyIntoMesh(*this, *mesh, hole_mesh, false, false, _communicator); else { std::size_t n_nodes_stitched = mesh->stitch_meshes(hole_mesh, inner_bcid, new_hole_bcid, TOLERANCE, /*clear_stitched_bcids*/ true, _verbose_stitching, use_binary_search); if (!n_nodes_stitched) mooseError("Failed to stitch hole mesh ", hole_i, " to new tetrahedralization."); } } } if (doing_stitching && _combined_stitching) { std::size_t n_nodes_stitched = mesh->stitch_surfaces(inner_bcid, new_hole_bcid, TOLERANCE, /*clear_stitched_bcids*/ true, _verbose_stitching, use_binary_search); if (!n_nodes_stitched) mooseError("Failed to stitch combined hole meshes to new tetrahedralization."); } // Add user-specified sideset names |
29 30 31 32 + 33 34 35 |
namespace MooseMeshElementConversionUtils { void hexElemSplitter(MeshBase & mesh, const std::vector<libMesh::BoundaryInfo::BCTuple> & bdry_side_list, const dof_id_type elem_id, std::vector<dof_id_type> & converted_elems_ids) |
95 96 97 98 + 99 100 101 |
} void prismElemSplitter(MeshBase & mesh, const std::vector<libMesh::BoundaryInfo::BCTuple> & bdry_side_list, const dof_id_type elem_id, std::vector<dof_id_type> & converted_elems_ids) |
157 158 159 160 + 161 162 163 |
} void pyramidElemSplitter(MeshBase & mesh, const std::vector<libMesh::BoundaryInfo::BCTuple> & bdry_side_list, const dof_id_type elem_id, std::vector<dof_id_type> & converted_elems_ids) |
582 583 584 585 + 586 587 588 |
} void convert3DMeshToAllTet4(MeshBase & mesh, const std::vector<std::pair<dof_id_type, bool>> & elems_to_process, std::vector<dof_id_type> & converted_elems_ids_to_track, const subdomain_id_type block_id_to_remove, |
647 648 649 650 + 651 652 653 |
} void convert3DMeshToAllTet4(MeshBase & mesh) { mooseAssert(mesh.is_serial(), "This method only supports serial meshes. If you are calling this method with a " |
691 692 693 694 + 695 696 697 |
} void convertElem(MeshBase & mesh, const dof_id_type & elem_id, const std::vector<unsigned int> & side_indices, const std::vector<std::vector<boundary_id_type>> & elem_side_info, |
723 724 725 726 + 727 728 729 |
} void convertHex8Elem(MeshBase & mesh, const dof_id_type & elem_id, const std::vector<unsigned int> & side_indices, const std::vector<std::vector<boundary_id_type>> & elem_side_info, |
747 748 749 750 + 751 752 753 |
} void createUnitPyramid5FromHex8(MeshBase & mesh, const dof_id_type & elem_id, const unsigned int & side_index, const Node * new_node, |
768 769 770 771 + 772 773 774 |
} void createUnitTet4FromHex8(MeshBase & mesh, const dof_id_type & elem_id, const unsigned int & side_index, const Node * new_node, |
829 830 831 832 + 833 834 835 |
} void convertPrism6Elem(MeshBase & mesh, const dof_id_type & elem_id, const std::vector<unsigned int> & side_indices, const std::vector<std::vector<boundary_id_type>> & elem_side_info, |
853 854 855 856 + 857 858 859 |
} void createUnitTet4FromPrism6(MeshBase & mesh, const dof_id_type & elem_id, const unsigned int & side_index, const Node * new_node, |
923 924 925 926 + 927 928 929 |
} void createUnitPyramid5FromPrism6(MeshBase & mesh, const dof_id_type & elem_id, const unsigned int & side_index, const Node * new_node, |
936 937 938 939 + 940 941 942 |
} void convertPyramid5Elem(MeshBase & mesh, const dof_id_type & elem_id, const std::vector<std::vector<boundary_id_type>> & elem_side_info, const SubdomainID & subdomain_id_shift_base) |
1001 1002 1003 1004 + 1005 1006 1007 |
} void retainEEID(MeshBase & mesh, const dof_id_type & elem_id, Elem * new_elem_ptr) { const unsigned int n_eeid = mesh.n_elem_integers(); for (const auto & i : make_range(n_eeid)) |
1009 1010 1011 1012 + 1013 1014 1015 |
} void transitionLayerGenerator(MeshBase & mesh, const std::vector<BoundaryName> & boundary_names, const unsigned int conversion_element_layer_number, const bool external_boundaries_checking) |
874 875 876 877 + 878 879 880 881 882 883 884 + 885 + 886 887 888 889 + 890 891 892 893 894 895 + 896 + 897 + 898 899 900 + 901 + 902 + 903 + 904 905 906 + 907 + 908 + 909 + 910 + 911 + 912 + 913 + 914 915 916 + 917 918 919 |
} void copyIntoMesh(MeshGenerator & mg, UnstructuredMesh & destination, const UnstructuredMesh & source, const bool avoid_merging_subdomains, const bool avoid_merging_boundaries, const Parallel::Communicator & communicator) { dof_id_type node_delta = destination.max_node_id(); dof_id_type elem_delta = destination.max_elem_id(); unique_id_type unique_delta = #ifdef LIBMESH_ENABLE_UNIQUE_ID destination.parallel_max_unique_id(); #else 0; #endif // Prevent overlaps by offsetting the subdomains in std::unordered_map<subdomain_id_type, subdomain_id_type> id_remapping; unsigned int block_offset = 0; if (avoid_merging_subdomains) { // Note: if performance becomes an issue, this is overkill for just getting the max node id std::set<subdomain_id_type> source_ids; std::set<subdomain_id_type> dest_ids; source.subdomain_ids(source_ids, true); destination.subdomain_ids(dest_ids, true); mooseAssert(source_ids.size(), "Should have a subdomain"); mooseAssert(dest_ids.size(), "Should have a subdomain"); unsigned int max_dest_bid = *dest_ids.rbegin(); unsigned int min_source_bid = *source_ids.begin(); communicator.max(max_dest_bid); communicator.min(min_source_bid); block_offset = 1 + max_dest_bid - min_source_bid; for (const auto bid : source_ids) id_remapping[bid] = block_offset + bid; } // Copy mesh data over from the other mesh destination.copy_nodes_and_elements(source, // Skipping this should cause the neighbors // to simply be copied from the other mesh // (which makes sense and is way faster) |
924 925 926 927 + 928 + 929 930 + 931 + 932 933 + 934 + 935 + 936 + 937 + 938 + 939 + 940 + 941 942 943 944 945 946 947 + 948 + 949 950 + 951 + 952 953 + 954 + 955 956 + 957 + 958 + 959 960 961 + 962 963 964 965 966 + 967 + 968 + 969 + 970 971 + 972 + 973 + 974 975 976 977 978 + 979 + 980 + 981 982 983 + 984 985 986 987 988 + 989 + 990 + 991 + 992 993 + 994 + 995 + 996 997 998 + 999 + 1000 + 1001 + 1002 1003 + 1004 + 1005 + 1006 1007 1008 1009 1010 + 1011 + 1012 + 1013 1014 + 1015 + 1016 + 1017 1018 + 1019 + 1020 + 1021 1022 1023 |
avoid_merging_subdomains ? &id_remapping : nullptr); // Get an offset to prevent overlaps / wild merging between boundaries BoundaryInfo & boundary = destination.get_boundary_info(); const BoundaryInfo & other_boundary = source.get_boundary_info(); unsigned int bid_offset = 0; if (avoid_merging_boundaries) { const auto boundary_ids = boundary.get_boundary_ids(); const auto other_boundary_ids = other_boundary.get_boundary_ids(); unsigned int max_dest_bid = boundary_ids.size() ? *boundary_ids.rbegin() : 0; unsigned int min_source_bid = other_boundary_ids.size() ? *other_boundary_ids.begin() : 0; communicator.max(max_dest_bid); communicator.min(min_source_bid); bid_offset = 1 + max_dest_bid - min_source_bid; } // Note: the code below originally came from ReplicatedMesh::stitch_mesh_helper() // in libMesh replicated_mesh.C around line 1203 // Copy BoundaryInfo from other_mesh too. We do this via the // list APIs rather than element-by-element for speed. for (const auto & t : other_boundary.build_node_list()) boundary.add_node(std::get<0>(t) + node_delta, bid_offset + std::get<1>(t)); for (const auto & t : other_boundary.build_side_list()) boundary.add_side(std::get<0>(t) + elem_delta, std::get<1>(t), bid_offset + std::get<2>(t)); for (const auto & t : other_boundary.build_edge_list()) boundary.add_edge(std::get<0>(t) + elem_delta, std::get<1>(t), bid_offset + std::get<2>(t)); for (const auto & t : other_boundary.build_shellface_list()) boundary.add_shellface( std::get<0>(t) + elem_delta, std::get<1>(t), bid_offset + std::get<2>(t)); // Check for the case with two block ids sharing the same name if (avoid_merging_subdomains) { mooseAssert(mg.parameters().isParamDefined("avoid_merging_subdomains"), "Missing parameter in the mesh generator calling this function: " "avoid_merging_subdomains. Considering setting avoid_merging_subdomains to true."); for (const auto & [block_id, block_name] : destination.get_subdomain_name_map()) for (const auto & [source_id, source_name] : source.get_subdomain_name_map()) if (block_name == source_name) mg.paramWarning( "avoid_merging_subdomains", "Not merging subdomains is creating two subdomains with the same name '" + block_name + "' but different ids: " + std::to_string(source_id) + " & " + std::to_string(block_id + block_offset) + ".\n We recommend using a RenameBlockGenerator to prevent this as you " "will get errors reading the Exodus output later."); } for (const auto & [block_id, block_name] : source.get_subdomain_name_map()) destination.set_subdomain_name_map().insert( std::make_pair<SubdomainID, SubdomainName>(block_id + block_offset, block_name)); // Check for the case with two boundary ids sharing the same name if (avoid_merging_boundaries) { mooseAssert(mg.parameters().isParamDefined("avoid_merging_boundaries"), "Missing parameter in the mesh generator calling this function: " "avoid_merging_boundaries. Considering setting avoid_merging_boundaries to true."); for (const auto & [b_id, b_name] : other_boundary.get_sideset_name_map()) for (const auto & [source_id, source_name] : boundary.get_sideset_name_map()) if (b_name == source_name) mg.paramWarning( "avoid_merging_boundaries", "Not merging boundaries is creating two sidesets with the same name '" + b_name + "' but different ids: " + std::to_string(source_id) + " & " + std::to_string(b_id + bid_offset) + ".\n We recommend using a RenameBoundaryGenerator to prevent this as you " "will get errors reading the Exodus output later."); for (const auto & [b_id, b_name] : other_boundary.get_nodeset_name_map()) for (const auto & [source_id, source_name] : boundary.get_nodeset_name_map()) if (b_name == source_name) mg.paramWarning( "avoid_merging_boundaries", "Not merging boundaries is creating two nodesets with the same name '" + b_name + "' but different ids: " + std::to_string(source_id) + " & " + std::to_string(b_id + bid_offset) + ".\n We recommend using a RenameBoundaryGenerator to prevent this as you " "will get errors reading the Exodus output later."); } for (const auto & [nodeset_id, nodeset_name] : other_boundary.get_nodeset_name_map()) boundary.set_nodeset_name_map().insert( std::make_pair<BoundaryID, BoundaryName>(nodeset_id + bid_offset, nodeset_name)); for (const auto & [sideset_id, sideset_name] : other_boundary.get_sideset_name_map()) boundary.set_sideset_name_map().insert( std::make_pair<BoundaryID, BoundaryName>(sideset_id + bid_offset, sideset_name)); for (const auto & [edgeset_id, edgeset_name] : other_boundary.get_edgeset_name_map()) boundary.set_edgeset_name_map().insert( std::make_pair<BoundaryID, BoundaryName>(edgeset_id + bid_offset, edgeset_name)); } } |