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 : }
|