LCOV - code coverage report
Current view: top level - src/meshgenerators - StitchMeshGenerator.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 419b9d Lines: 77 87 88.5 %
Date: 2025-08-08 20:01:16 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       43331 : StitchMeshGenerator::validParams()
      26             : {
      27       43331 :   InputParameters params = StitchMeshGeneratorBase::validParams();
      28       43331 :   params.addRequiredParam<std::vector<MeshGeneratorName>>("inputs", "The input MeshGenerators.");
      29      129993 :   params.addParam<bool>("prevent_boundary_ids_overlap",
      30       86662 :                         true,
      31             :                         "Whether to re-number boundaries in stitched meshes to prevent merging of "
      32             :                         "unrelated boundaries");
      33      129993 :   params.addParam<bool>(
      34             :       "merge_boundaries_with_same_name",
      35       86662 :       true,
      36             :       "If the input meshes have boundaries with the same name (but different IDs), merge them");
      37      129993 :   params.addParam<bool>(
      38             :       "subdomain_remapping",
      39       86662 :       true,
      40             :       "Treat input subdomain names as primary, preserving them and remapping IDs as needed");
      41             : 
      42       43331 :   params.addClassDescription(
      43             :       "Allows multiple mesh files to be stitched together to form a single mesh.");
      44             : 
      45             :   // This is used for compatibility with TestSubGenerators
      46       43331 :   params.addPrivateParam<bool>("_check_inputs", true);
      47       43331 :   return params;
      48           0 : }
      49             : 
      50         257 : StitchMeshGenerator::StitchMeshGenerator(const InputParameters & parameters)
      51             :   : StitchMeshGeneratorBase(parameters),
      52         257 :     _mesh_ptrs(getMeshes("inputs")),
      53         257 :     _input_names(getParam<std::vector<MeshGeneratorName>>("inputs")),
      54         257 :     _prevent_boundary_ids_overlap(getParam<bool>("prevent_boundary_ids_overlap")),
      55         514 :     _merge_boundaries_with_same_name(getParam<bool>("merge_boundaries_with_same_name"))
      56             : {
      57             :   // Check inputs
      58         257 :   if (_input_names.size() - 1 != _stitch_boundaries_pairs.size() && getParam<bool>("_check_inputs"))
      59           0 :     paramError("stitch_boundaries_pairs",
      60           0 :                "Can only stitch one pair of boundary per pair of mesh. We have '" +
      61           0 :                    std::to_string(_input_names.size()) +
      62           0 :                    "' meshes specified (=" + std::to_string(_input_names.size() - 1) +
      63           0 :                    " pairs) and " + std::to_string(_stitch_boundaries_pairs.size()) +
      64             :                    " pairs of boundaries specified");
      65         578 :   for (const auto & pair : _stitch_boundaries_pairs)
      66         321 :     if (pair.size() != 2)
      67           0 :       paramError("stitch_boundaries_pairs",
      68           0 :                  "Stitched boundary pair '" + Moose::stringify(pair) +
      69           0 :                      "' is not of length 2, but of length: " + std::to_string(pair.size()));
      70         257 : }
      71             : 
      72             : std::unique_ptr<MeshBase>
      73         252 : StitchMeshGenerator::generate()
      74             : {
      75             :   // We put the first mesh in a local pointer
      76         252 :   std::unique_ptr<UnstructuredMesh> mesh = dynamic_pointer_cast<UnstructuredMesh>(*_mesh_ptrs[0]);
      77         252 :   if (!mesh) // This should never happen until libMesh implements on-the-fly-Elem mesh types
      78           0 :     mooseError("StitchMeshGenerator is only implemented for unstructured meshes");
      79             : 
      80             :   // Reserve spaces for the other meshes (no need to store the first one another time)
      81         252 :   std::vector<std::unique_ptr<UnstructuredMesh>> meshes(_mesh_ptrs.size() - 1);
      82             : 
      83             :   // Read in all of the other meshes
      84         566 :   for (MooseIndex(_mesh_ptrs) i = 1; i < _mesh_ptrs.size(); ++i)
      85         314 :     meshes[i - 1] = dynamic_pointer_cast<UnstructuredMesh>(*_mesh_ptrs[i]);
      86             : 
      87             :   // Stitch all the meshes to the first one
      88         562 :   for (MooseIndex(meshes) i = 0; i < meshes.size(); i++)
      89             :   {
      90         314 :     const auto boundary_pair = _stitch_boundaries_pairs[i];
      91             : 
      92             :     // Check the input boundaries
      93         628 :     const auto first = getBoundaryIdToStitch(
      94         314 :         *mesh,
      95         628 :         (_input_names.size() ? _input_names[0] : "(unknown)") +
      96         628 :             ((i == 0) ? "" : (" (stitched with " + std::to_string(i) + " previous meshes)")),
      97         314 :         boundary_pair[0]);
      98         314 :     auto second = getBoundaryIdToStitch(*meshes[i], name(), boundary_pair[1]);
      99             : 
     100         314 :     const bool use_binary_search = (_algorithm == "BINARY");
     101             : 
     102             :     // Meshes must be prepared to get the global boundary ids
     103         314 :     if (!mesh->is_prepared())
     104         178 :       mesh->prepare_for_use();
     105         314 :     if (!meshes[i]->is_prepared())
     106         225 :       meshes[i]->prepare_for_use();
     107             : 
     108             :     // Avoid chaotic boundary merging due to overlapping ids in meshes by simply renumbering the
     109             :     // boundaries in the meshes we are going to stitch onto the base mesh
     110             :     // This is only done if there is an overlap
     111         314 :     if (_prevent_boundary_ids_overlap)
     112             :     {
     113         227 :       const auto & base_mesh_bids = mesh->get_boundary_info().get_global_boundary_ids();
     114         227 :       const auto stitched_mesh_bids = meshes[i]->get_boundary_info().get_global_boundary_ids();
     115             : 
     116             :       // Check for an overlap
     117         227 :       bool overlap_found = false;
     118        1031 :       for (const auto & bid : stitched_mesh_bids)
     119         804 :         if (base_mesh_bids.count(bid))
     120         768 :           overlap_found = true;
     121             : 
     122         227 :       if (overlap_found)
     123             :       {
     124             :         const auto max_boundary_id =
     125         211 :             std::max(*base_mesh_bids.rbegin(), *stitched_mesh_bids.rbegin());
     126         211 :         BoundaryID new_index = 1;
     127        1015 :         for (const auto bid : stitched_mesh_bids)
     128             :         {
     129         804 :           const auto new_bid = max_boundary_id + (new_index++);
     130         804 :           meshes[i]->get_boundary_info().renumber_id(bid, new_bid);
     131         804 :           if (bid == second)
     132         211 :             second = new_bid;
     133             :         }
     134             :       }
     135         227 :     }
     136             :     else
     137             :     {
     138             :       // If we don't have renumbering, we can get into situations where the same boundary ID is
     139             :       // associated with different boundary names. In this case when we stitch, we assign the
     140             :       // boundary name of the first mesh to the ID everywhere on the domain. We throw a warning
     141             :       // here to warn the user if this is the case.
     142          87 :       const auto & base_mesh_bids = mesh->get_boundary_info().get_global_boundary_ids();
     143          87 :       const auto & other_mesh_bids = meshes[i]->get_boundary_info().get_global_boundary_ids();
     144             : 
     145             :       // We check if the same ID is present with different names
     146          87 :       std::set<boundary_id_type> bd_id_intersection;
     147          87 :       std::set_intersection(base_mesh_bids.begin(),
     148             :                             base_mesh_bids.end(),
     149             :                             other_mesh_bids.begin(),
     150             :                             other_mesh_bids.end(),
     151             :                             std::inserter(bd_id_intersection, bd_id_intersection.begin()));
     152             : 
     153         384 :       for (const auto bid : bd_id_intersection)
     154             :       {
     155         301 :         const auto & sideset_name_on_first_mesh = mesh->get_boundary_info().get_sideset_name(bid);
     156             :         const auto & sideset_name_on_second_mesh =
     157         301 :             meshes[i]->get_boundary_info().get_sideset_name(bid);
     158             : 
     159         301 :         if (sideset_name_on_first_mesh != sideset_name_on_second_mesh)
     160          82 :           mooseWarning(
     161             :               "Boundary ID ",
     162             :               bid,
     163             :               " corresponds to different boundary names on the input meshes! On the first "
     164             :               "mesh it corresponds to `",
     165             :               sideset_name_on_first_mesh,
     166             :               "` while on the second mesh it corresponds to `",
     167             :               sideset_name_on_second_mesh,
     168             :               "`. The final mesh will replace boundary `",
     169             :               sideset_name_on_second_mesh,
     170             :               "` with `",
     171             :               sideset_name_on_first_mesh,
     172             :               "`. To avoid this situation, use the `prevent_boundary_ids_overlap` parameter!");
     173             :       }
     174          83 :     }
     175             : 
     176         620 :     mesh->stitch_meshes(*meshes[i],
     177             :                         first,
     178             :                         second,
     179         620 :                         getParam<Real>("stitching_hmin_tolerance_factor"),
     180         310 :                         _clear_stitched_boundary_ids,
     181         620 :                         getParam<bool>("verbose_stitching"),
     182             :                         use_binary_search,
     183             :                         /*enforce_all_nodes_match_on_boundaries=*/false,
     184             :                         /*merge_boundary_nodes_all_or_nothing=*/false,
     185         620 :                         getParam<bool>("subdomain_remapping"));
     186             : 
     187         310 :     if (_merge_boundaries_with_same_name)
     188         199 :       MooseMeshUtils::mergeBoundaryIDsWithSameName(*mesh);
     189         310 :   }
     190             : 
     191         248 :   mesh->set_isnt_prepared();
     192         496 :   return dynamic_pointer_cast<MeshBase>(mesh);
     193         248 : }

Generated by: LCOV version 1.14