LCOV - code coverage report
Current view: top level - src/meshgenerators - StitchMeshGenerator.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #32971 (54bef8) with base c6cf66 Lines: 85 95 89.5 %
Date: 2026-05-29 20:35:17 Functions: 3 3 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://mooseframework.inl.gov
       3             : //*
       4             : //* All rights reserved, see COPYRIGHT for full restrictions
       5             : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
       6             : //*
       7             : //* Licensed under LGPL 2.1, please see LICENSE for details
       8             : //* https://www.gnu.org/licenses/lgpl-2.1.html
       9             : 
      10             : #include "StitchMeshGenerator.h"
      11             : 
      12             : #include "CastUniquePointer.h"
      13             : #include "MooseUtils.h"
      14             : #include "MooseMeshUtils.h"
      15             : 
      16             : #include "libmesh/unstructured_mesh.h"
      17             : 
      18             : registerMooseObjectRenamed("MooseApp",
      19             :                            StitchedMeshGenerator,
      20             :                            "06/30/2026 24:00",
      21             :                            StitchMeshGenerator);
      22             : registerMooseObject("MooseApp", StitchMeshGenerator);
      23             : 
      24             : InputParameters
      25        9971 : StitchMeshGenerator::validParams()
      26             : {
      27        9971 :   InputParameters params = StitchMeshGeneratorBase::validParams();
      28       39884 :   params.addRequiredParam<std::vector<MeshGeneratorName>>("inputs", "The input MeshGenerators.");
      29       29913 :   params.addParam<bool>("prevent_boundary_ids_overlap",
      30       19942 :                         true,
      31             :                         "Whether to re-number boundaries in stitched meshes to prevent merging of "
      32             :                         "unrelated boundaries");
      33       29913 :   params.addParam<bool>(
      34             :       "merge_boundaries_with_same_name",
      35       19942 :       true,
      36             :       "If the input meshes have boundaries with the same name (but different IDs), merge them");
      37       29913 :   params.addParam<bool>(
      38             :       "subdomain_remapping",
      39       19942 :       true,
      40             :       "Treat input subdomain names as primary, preserving them and remapping IDs as needed");
      41       29913 :   params.addParam<bool>(
      42       19942 :       "verbose_stitching", false, "Whether mesh stitching should have verbose output.");
      43       29913 :   params.addParam<bool>(
      44             :       "enforce_all_nodes_match_on_boundaries",
      45       19942 :       false,
      46             :       "Whether to have the stitcher be very picky about the nodes being stitched.");
      47       29913 :   params.addParam<bool>("merge_boundary_nodes_all_or_nothing",
      48       19942 :                         false,
      49             :                         "Whether the stitcher is set to merge all nodes or none.");
      50        9971 :   params.addClassDescription(
      51             :       "Allows multiple mesh files to be stitched together to form a single mesh.");
      52             : 
      53             :   // This is used for compatibility with TestSubGenerators
      54       19942 :   params.addPrivateParam<bool>("_check_inputs", true);
      55        9971 :   return params;
      56           0 : }
      57             : 
      58         387 : StitchMeshGenerator::StitchMeshGenerator(const InputParameters & parameters)
      59             :   : StitchMeshGeneratorBase(parameters),
      60         387 :     _mesh_ptrs(getMeshes("inputs")),
      61         774 :     _input_names(getParam<std::vector<MeshGeneratorName>>("inputs")),
      62         774 :     _prevent_boundary_ids_overlap(getParam<bool>("prevent_boundary_ids_overlap")),
      63        1161 :     _merge_boundaries_with_same_name(getParam<bool>("merge_boundaries_with_same_name"))
      64             : {
      65             :   // Check inputs
      66         403 :   if (_input_names.size() - 1 != _stitch_boundaries_pairs.size() && getParam<bool>("_check_inputs"))
      67           0 :     paramError("stitch_boundaries_pairs",
      68           0 :                "Can only stitch one pair of boundary per pair of mesh. We have '" +
      69           0 :                    std::to_string(_input_names.size()) +
      70           0 :                    "' meshes specified (=" + std::to_string(_input_names.size() - 1) +
      71           0 :                    " pairs) and " + std::to_string(_stitch_boundaries_pairs.size()) +
      72             :                    " pairs of boundaries specified");
      73         974 :   for (const auto & pair : _stitch_boundaries_pairs)
      74         587 :     if (pair.size() != 2)
      75           0 :       paramError("stitch_boundaries_pairs",
      76           0 :                  "Stitched boundary pair '" + Moose::stringify(pair) +
      77           0 :                      "' is not of length 2, but of length: " + std::to_string(pair.size()));
      78         387 : }
      79             : 
      80             : std::unique_ptr<MeshBase>
      81         366 : StitchMeshGenerator::generate()
      82             : {
      83             :   // We put the first mesh in a local pointer
      84         366 :   std::unique_ptr<UnstructuredMesh> mesh = dynamic_pointer_cast<UnstructuredMesh>(*_mesh_ptrs[0]);
      85         366 :   if (!mesh) // This should never happen until libMesh implements on-the-fly-Elem mesh types
      86           0 :     mooseError("StitchMeshGenerator is only implemented for unstructured meshes");
      87             : 
      88             :   // Reserve spaces for the other meshes (no need to store the first one another time)
      89         366 :   std::vector<std::unique_ptr<UnstructuredMesh>> meshes(_mesh_ptrs.size() - 1);
      90             : 
      91             :   // Read in all of the other meshes
      92         866 :   for (MooseIndex(_mesh_ptrs) i = 1; i < _mesh_ptrs.size(); ++i)
      93         500 :     meshes[i - 1] = dynamic_pointer_cast<UnstructuredMesh>(*_mesh_ptrs[i]);
      94             : 
      95             :   // Stitch all the meshes to the first one
      96         863 :   for (MooseIndex(meshes) i = 0; i < meshes.size(); i++)
      97             :   {
      98         500 :     const auto boundary_pair = _stitch_boundaries_pairs[i];
      99             : 
     100             :     // Check the input boundaries
     101        1000 :     const auto first = getBoundaryIdToStitch(
     102         500 :         *mesh,
     103        1000 :         (_input_names.size() ? _input_names[0] : "(unknown)") +
     104        1732 :             ((i == 0) ? "" : (" (stitched with " + std::to_string(i) + " previous meshes)")),
     105         500 :         boundary_pair[0]);
     106         500 :     auto second = getBoundaryIdToStitch(*meshes[i], name(), boundary_pair[1]);
     107             : 
     108         500 :     const bool use_binary_search = (_algorithm == "BINARY");
     109             : 
     110             :     // Meshes must be prepared to get the global boundary ids
     111         500 :     if (!mesh->is_prepared())
     112         278 :       mesh->prepare_for_use();
     113         500 :     if (!meshes[i]->is_prepared())
     114         351 :       meshes[i]->prepare_for_use();
     115             : 
     116             :     // Avoid chaotic boundary merging due to overlapping ids in meshes by simply renumbering the
     117             :     // boundaries in the meshes we are going to stitch onto the base mesh
     118             :     // This is only done if there is an overlap
     119         500 :     if (_prevent_boundary_ids_overlap)
     120             :     {
     121         386 :       const auto & base_mesh_bids = mesh->get_boundary_info().get_global_boundary_ids();
     122         386 :       const auto stitched_mesh_bids = meshes[i]->get_boundary_info().get_global_boundary_ids();
     123             : 
     124             :       // Check for an overlap
     125         386 :       bool overlap_found = false;
     126        1475 :       for (const auto & bid : stitched_mesh_bids)
     127        1089 :         if (base_mesh_bids.count(bid))
     128        1021 :           overlap_found = true;
     129             : 
     130         386 :       if (overlap_found)
     131             :       {
     132             :         const auto max_boundary_id =
     133         372 :             std::max(*base_mesh_bids.rbegin(), *stitched_mesh_bids.rbegin());
     134         372 :         BoundaryID new_index = 1;
     135        1461 :         for (const auto bid : stitched_mesh_bids)
     136             :         {
     137        1089 :           const auto new_bid = max_boundary_id + (new_index++);
     138        1089 :           meshes[i]->get_boundary_info().renumber_id(bid, new_bid);
     139        1089 :           if (bid == second)
     140         372 :             second = new_bid;
     141             :         }
     142             :       }
     143         386 :     }
     144             :     else
     145             :     {
     146             :       // If we don't have renumbering, we can get into situations where the same boundary ID is
     147             :       // associated with different boundary names. In this case when we stitch, we assign the
     148             :       // boundary name of the first mesh to the ID everywhere on the domain. We throw a warning
     149             :       // here to warn the user if this is the case.
     150         114 :       const auto & base_mesh_bids = mesh->get_boundary_info().get_global_boundary_ids();
     151         114 :       const auto & other_mesh_bids = meshes[i]->get_boundary_info().get_global_boundary_ids();
     152             : 
     153             :       // We check if the same ID is present with different names
     154         114 :       std::set<boundary_id_type> bd_id_intersection;
     155         114 :       std::set_intersection(base_mesh_bids.begin(),
     156             :                             base_mesh_bids.end(),
     157             :                             other_mesh_bids.begin(),
     158             :                             other_mesh_bids.end(),
     159             :                             std::inserter(bd_id_intersection, bd_id_intersection.begin()));
     160             : 
     161         408 :       for (const auto bid : bd_id_intersection)
     162             :       {
     163         297 :         const auto & sideset_name_on_first_mesh = mesh->get_boundary_info().get_sideset_name(bid);
     164             :         const auto & sideset_name_on_second_mesh =
     165         297 :             meshes[i]->get_boundary_info().get_sideset_name(bid);
     166             : 
     167         297 :         if (sideset_name_on_first_mesh != sideset_name_on_second_mesh)
     168          69 :           mooseWarning(
     169             :               "Boundary ID ",
     170             :               bid,
     171             :               " corresponds to different boundary names on the input meshes! On the first "
     172             :               "mesh it corresponds to `",
     173             :               sideset_name_on_first_mesh,
     174             :               "` while on the second mesh it corresponds to `",
     175             :               sideset_name_on_second_mesh,
     176             :               "`. The final mesh will replace boundary `",
     177             :               sideset_name_on_second_mesh,
     178             :               "` with `",
     179             :               sideset_name_on_first_mesh,
     180             :               "`. To avoid this situation, use the `prevent_boundary_ids_overlap` parameter!");
     181             :       }
     182         111 :     }
     183             : 
     184         994 :     mesh->stitch_meshes(*meshes[i],
     185             :                         first,
     186             :                         second,
     187         994 :                         getParam<Real>("stitching_hmin_tolerance_factor"),
     188         497 :                         _clear_stitched_boundary_ids,
     189         994 :                         getParam<bool>("verbose_stitching"),
     190             :                         use_binary_search,
     191             :                         /*enforce_all_nodes_match_on_boundaries=*/
     192         994 :                         getParam<bool>("enforce_all_nodes_match_on_boundaries"),
     193             :                         /*merge_boundary_nodes_all_or_nothing=*/
     194         994 :                         getParam<bool>("merge_boundary_nodes_all_or_nothing"),
     195        1491 :                         getParam<bool>("subdomain_remapping"));
     196             : 
     197         497 :     if (_merge_boundaries_with_same_name)
     198         401 :       MooseMeshUtils::mergeBoundaryIDsWithSameName(*mesh);
     199         497 :   }
     200             : 
     201         363 :   mesh->unset_is_prepared();
     202         726 :   return dynamic_pointer_cast<MeshBase>(mesh);
     203         363 : }

Generated by: LCOV version 1.14