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 "StitchedMeshGenerator.h"
11 :
12 : #include "CastUniquePointer.h"
13 : #include "MooseUtils.h"
14 : #include "MooseMeshUtils.h"
15 :
16 : #include "libmesh/unstructured_mesh.h"
17 :
18 : registerMooseObject("MooseApp", StitchedMeshGenerator);
19 :
20 : InputParameters
21 29036 : StitchedMeshGenerator::validParams()
22 : {
23 29036 : InputParameters params = MeshGenerator::validParams();
24 :
25 29036 : MooseEnum algorithm("BINARY EXHAUSTIVE", "BINARY");
26 :
27 29036 : params.addRequiredParam<std::vector<MeshGeneratorName>>("inputs", "The input MeshGenerators.");
28 87108 : params.addParam<bool>(
29 58072 : "clear_stitched_boundary_ids", true, "Whether or not to clear the stitched boundary IDs");
30 29036 : params.addRequiredParam<std::vector<std::vector<std::string>>>(
31 : "stitch_boundaries_pairs",
32 : "Pairs of boundaries to be stitched together between the 1st mesh in inputs and each "
33 : "consecutive mesh");
34 29036 : params.addParam<MooseEnum>(
35 : "algorithm",
36 : algorithm,
37 : "Control the use of binary search for the nodes of the stitched surfaces.");
38 87108 : params.addParam<bool>("prevent_boundary_ids_overlap",
39 58072 : true,
40 : "Whether to re-number boundaries in stitched meshes to prevent merging of "
41 : "unrelated boundaries");
42 87108 : params.addParam<bool>(
43 : "merge_boundaries_with_same_name",
44 58072 : true,
45 : "If the input meshes have boundaries with the same name (but different IDs), merge them");
46 87108 : params.addParam<bool>(
47 : "subdomain_remapping",
48 58072 : true,
49 : "Treat input subdomain names as primary, preserving them and remapping IDs as needed");
50 87108 : params.addParam<bool>(
51 58072 : "verbose_stitching", false, "Whether mesh stitching should have verbose output.");
52 29036 : params.addClassDescription(
53 : "Allows multiple mesh files to be stitched together to form a single mesh.");
54 :
55 58072 : return params;
56 29036 : }
57 :
58 242 : StitchedMeshGenerator::StitchedMeshGenerator(const InputParameters & parameters)
59 : : MeshGenerator(parameters),
60 242 : _mesh_ptrs(getMeshes("inputs")),
61 242 : _input_names(getParam<std::vector<MeshGeneratorName>>("inputs")),
62 242 : _clear_stitched_boundary_ids(getParam<bool>("clear_stitched_boundary_ids")),
63 242 : _stitch_boundaries_pairs(
64 : getParam<std::vector<std::vector<std::string>>>("stitch_boundaries_pairs")),
65 242 : _algorithm(parameters.get<MooseEnum>("algorithm")),
66 242 : _prevent_boundary_ids_overlap(getParam<bool>("prevent_boundary_ids_overlap")),
67 484 : _merge_boundaries_with_same_name(getParam<bool>("merge_boundaries_with_same_name"))
68 : {
69 242 : }
70 :
71 : std::unique_ptr<MeshBase>
72 237 : StitchedMeshGenerator::generate()
73 : {
74 : // We put the first mesh in a local pointer
75 237 : std::unique_ptr<UnstructuredMesh> mesh = dynamic_pointer_cast<UnstructuredMesh>(*_mesh_ptrs[0]);
76 237 : if (!mesh) // This should never happen until libMesh implements on-the-fly-Elem mesh types
77 0 : mooseError("StitchedMeshGenerator is only implemented for unstructured meshes");
78 :
79 : // Reserve spaces for the other meshes (no need to store the first one another time)
80 237 : std::vector<std::unique_ptr<UnstructuredMesh>> meshes(_mesh_ptrs.size() - 1);
81 :
82 : // Read in all of the other meshes
83 532 : for (MooseIndex(_mesh_ptrs) i = 1; i < _mesh_ptrs.size(); ++i)
84 295 : meshes[i - 1] = dynamic_pointer_cast<UnstructuredMesh>(*_mesh_ptrs[i]);
85 :
86 : // Stitch all the meshes to the first one
87 528 : for (MooseIndex(meshes) i = 0; i < meshes.size(); i++)
88 : {
89 295 : auto boundary_pair = _stitch_boundaries_pairs[i];
90 :
91 : boundary_id_type first, second;
92 :
93 : try
94 : {
95 295 : first = MooseUtils::convert<boundary_id_type>(boundary_pair[0], true);
96 : }
97 202 : catch (...)
98 : {
99 202 : first = mesh->get_boundary_info().get_id_by_name(boundary_pair[0]);
100 :
101 202 : if (first == BoundaryInfo::invalid_id)
102 : {
103 0 : std::stringstream error;
104 :
105 0 : error << "Boundary " << boundary_pair[0] << " doesn't exist on mesh '" << _input_names[0]
106 0 : << "' in generator " << name() << "\n";
107 0 : error << "Boundary (sideset) names that do exist: \n";
108 0 : error << " ID : Name\n";
109 :
110 0 : auto & sideset_id_name_map = mesh->get_boundary_info().get_sideset_name_map();
111 :
112 0 : for (auto & ss_name_map_pair : sideset_id_name_map)
113 0 : error << " " << ss_name_map_pair.first << " : " << ss_name_map_pair.second << "\n";
114 :
115 0 : error << "\nBoundary (nodeset) names that do exist: \n";
116 0 : error << " ID : Name\n";
117 :
118 0 : auto & nodeset_id_name_map = mesh->get_boundary_info().get_nodeset_name_map();
119 :
120 0 : for (auto & ns_name_map_pair : nodeset_id_name_map)
121 0 : error << " " << ns_name_map_pair.first << " : " << ns_name_map_pair.second << "\n";
122 :
123 0 : paramError("stitch_boundaries_pairs", error.str());
124 0 : }
125 202 : }
126 :
127 : try
128 : {
129 295 : second = MooseUtils::convert<boundary_id_type>(boundary_pair[1], true);
130 : }
131 210 : catch (...)
132 : {
133 210 : second = meshes[i]->get_boundary_info().get_id_by_name(boundary_pair[1]);
134 :
135 210 : if (second == BoundaryInfo::invalid_id)
136 : {
137 0 : meshes[i]->print_info();
138 :
139 0 : std::stringstream error;
140 :
141 0 : error << "Boundary " << boundary_pair[1] << " doesn't exist on mesh '" << _input_names[i]
142 0 : << "' in generator " << name() << "\n";
143 0 : error << "Boundary (sideset) names that do exist: \n";
144 0 : error << " ID : Name\n";
145 :
146 0 : auto & sideset_id_name_map = meshes[i]->get_boundary_info().get_sideset_name_map();
147 :
148 0 : for (auto & ss_name_map_pair : sideset_id_name_map)
149 0 : error << " " << ss_name_map_pair.first << " : " << ss_name_map_pair.second << "\n";
150 :
151 0 : error << "\nBoundary (nodeset) names that do exist: \n";
152 0 : error << " ID : Name\n";
153 :
154 0 : auto & nodeset_id_name_map = mesh->get_boundary_info().get_nodeset_name_map();
155 :
156 0 : for (auto & ns_name_map_pair : nodeset_id_name_map)
157 0 : error << " " << ns_name_map_pair.first << " : " << ns_name_map_pair.second << "\n";
158 :
159 0 : paramError("stitch_boundaries_pairs", error.str());
160 0 : }
161 210 : }
162 :
163 295 : const bool use_binary_search = (_algorithm == "BINARY");
164 :
165 : // Meshes must be prepared to get the global boundary ids
166 295 : if (!mesh->is_prepared())
167 168 : mesh->prepare_for_use();
168 295 : if (!meshes[i]->is_prepared())
169 214 : meshes[i]->prepare_for_use();
170 :
171 : // Avoid chaotic boundary merging due to overlapping ids in meshes by simply renumbering the
172 : // boundaries in the meshes we are going to stitch onto the base mesh
173 : // This is only done if there is an overlap
174 295 : if (_prevent_boundary_ids_overlap)
175 : {
176 210 : const auto & base_mesh_bids = mesh->get_boundary_info().get_global_boundary_ids();
177 210 : const auto stitched_mesh_bids = meshes[i]->get_boundary_info().get_global_boundary_ids();
178 :
179 : // Check for an overlap
180 210 : bool overlap_found = false;
181 954 : for (const auto & bid : stitched_mesh_bids)
182 744 : if (base_mesh_bids.count(bid))
183 712 : overlap_found = true;
184 :
185 210 : if (overlap_found)
186 : {
187 194 : const auto max_boundary_id = *base_mesh_bids.rbegin();
188 194 : BoundaryID new_index = 1;
189 938 : for (const auto bid : stitched_mesh_bids)
190 : {
191 744 : const auto new_bid = max_boundary_id + (new_index++);
192 744 : meshes[i]->get_boundary_info().renumber_id(bid, new_bid);
193 744 : if (bid == second)
194 194 : second = new_bid;
195 : }
196 : }
197 210 : }
198 : else
199 : {
200 : // If we don't have renumbering, we can get into situations where the same boundary ID is
201 : // associated with different boundary names. In this case when we stitch, we assign the
202 : // boundary name of the first mesh to the ID everywhere on the domain. We throw a warning
203 : // here to warn the user if this is the case.
204 85 : const auto & base_mesh_bids = mesh->get_boundary_info().get_global_boundary_ids();
205 85 : const auto & other_mesh_bids = meshes[i]->get_boundary_info().get_global_boundary_ids();
206 :
207 : // We check if the same ID is present with different names
208 85 : std::set<boundary_id_type> bd_id_intersection;
209 85 : std::set_intersection(base_mesh_bids.begin(),
210 : base_mesh_bids.end(),
211 : other_mesh_bids.begin(),
212 : other_mesh_bids.end(),
213 : std::inserter(bd_id_intersection, bd_id_intersection.begin()));
214 :
215 374 : for (const auto bid : bd_id_intersection)
216 : {
217 293 : const auto & sideset_name_on_first_mesh = mesh->get_boundary_info().get_sideset_name(bid);
218 : const auto & sideset_name_on_second_mesh =
219 293 : meshes[i]->get_boundary_info().get_sideset_name(bid);
220 :
221 293 : if (sideset_name_on_first_mesh != sideset_name_on_second_mesh)
222 82 : mooseWarning(
223 : "Boundary ID ",
224 : bid,
225 : " corresponds to different boundary names on the input meshes! On the first "
226 : "mesh it corresponds to `",
227 : sideset_name_on_first_mesh,
228 : "` while on the second mesh it corresponds to `",
229 : sideset_name_on_second_mesh,
230 : "`. The final mesh will replace boundary `",
231 : sideset_name_on_second_mesh,
232 : "` with `",
233 : sideset_name_on_first_mesh,
234 : "`. To avoid this situation, use the `prevent_boundary_ids_overlap` parameter!");
235 : }
236 81 : }
237 :
238 582 : mesh->stitch_meshes(*meshes[i],
239 : first,
240 : second,
241 : TOLERANCE,
242 291 : _clear_stitched_boundary_ids,
243 582 : getParam<bool>("verbose_stitching"),
244 : use_binary_search,
245 : /*enforce_all_nodes_match_on_boundaries=*/false,
246 : /*merge_boundary_nodes_all_or_nothing=*/false,
247 582 : getParam<bool>("subdomain_remapping"));
248 :
249 291 : if (_merge_boundaries_with_same_name)
250 185 : MooseMeshUtils::mergeBoundaryIDsWithSameName(*mesh);
251 291 : }
252 :
253 233 : mesh->set_isnt_prepared();
254 466 : return dynamic_pointer_cast<MeshBase>(mesh);
255 233 : }
|