idaholab/moose: framework coverage diff

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%
code
coverage unchanged
code
coverage increased
code
coverage decreased
+
line added or modified

framework/src/meshgenerators/BoundaryElementConversionGenerator.C

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);
}

framework/src/meshgenerators/CombinerGenerator.C

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

framework/src/meshgenerators/XYZDelaunayGenerator.C

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

framework/src/utils/MooseMeshElementConversionUtils.C

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)

framework/src/utils/MooseMeshUtils.C

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));
}
}