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