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 "CombinerGenerator.h"
11 :
12 : #include "CastUniquePointer.h"
13 : #include "MooseUtils.h"
14 : #include "DelimitedFileReader.h"
15 : #include "MooseMeshUtils.h"
16 :
17 : #include "libmesh/replicated_mesh.h"
18 : #include "libmesh/unstructured_mesh.h"
19 : #include "libmesh/mesh_modification.h"
20 : #include "libmesh/point.h"
21 : #include "libmesh/node.h"
22 :
23 : registerMooseObject("MooseApp", CombinerGenerator);
24 :
25 : InputParameters
26 15671 : CombinerGenerator::validParams()
27 : {
28 15671 : InputParameters params = MeshGenerator::validParams();
29 :
30 31342 : params.addClassDescription(
31 : "Combine multiple meshes (or copies of one mesh) together into one (disjoint) mesh. Can "
32 : "optionally translate those meshes before combining them.");
33 :
34 62684 : params.addRequiredParam<std::vector<MeshGeneratorName>>(
35 : "inputs",
36 : "The input MeshGenerators. This can either be N generators or 1 generator. If only 1 is "
37 : "given then 'positions' must also be given.");
38 :
39 62684 : params.addParam<std::vector<Point>>(
40 : "positions",
41 : "The (optional) position of each given mesh. If N 'inputs' were given then this must either "
42 : "be left blank or N positions must be given. If 1 input was given then this MUST be "
43 : "provided.");
44 :
45 62684 : params.addParam<std::vector<FileName>>(
46 : "positions_file", "Alternative way to provide the position of each given mesh.");
47 :
48 47013 : params.addParam<bool>("avoid_merging_subdomains",
49 31342 : false,
50 : "Whether to prevent merging subdomains by offsetting ids. The first mesh "
51 : "in the input will keep the same subdomains ids, the others will have "
52 : "offsets. All subdomain names will remain valid");
53 31342 : params.addParam<bool>("avoid_merging_boundaries",
54 31342 : false,
55 : "Whether to prevent merging sidesets by offsetting ids. The first mesh "
56 : "in the input will keep the same boundary ids, the others will have "
57 : "offsets. All boundary names will remain valid");
58 :
59 15671 : return params;
60 0 : }
61 :
62 702 : CombinerGenerator::CombinerGenerator(const InputParameters & parameters)
63 : : MeshGenerator(parameters),
64 702 : _meshes(getMeshes("inputs")),
65 1404 : _input_names(getParam<std::vector<MeshGeneratorName>>("inputs")),
66 1404 : _avoid_merging_subdomains(getParam<bool>("avoid_merging_subdomains")),
67 2808 : _avoid_merging_boundaries(getParam<bool>("avoid_merging_boundaries"))
68 : {
69 702 : if (_input_names.empty())
70 0 : paramError("input_names", "You need to specify at least one MeshGenerator as an input.");
71 :
72 2636 : if (isParamValid("positions") && isParamValid("positions_file"))
73 0 : mooseError("Both 'positions' and 'positions_file' cannot be specified simultaneously in "
74 : "CombinerGenerator ",
75 0 : _name);
76 :
77 702 : if (_input_names.size() == 1)
78 183 : if (!isParamValid("positions") && !isParamValid("positions_file"))
79 0 : paramError("positions",
80 : "If only one input mesh is given, then 'positions' or 'positions_file' must also "
81 : "be supplied");
82 702 : }
83 :
84 : void
85 683 : CombinerGenerator::fillPositions()
86 : {
87 2049 : if (isParamValid("positions"))
88 : {
89 528 : _positions = getParam<std::vector<Point>>("positions");
90 :
91 : // the check in the constructor wont catch error where the user sets positions = ''
92 264 : if ((_input_names.size() == 1) && _positions.empty())
93 8 : paramError("positions", "If only one input mesh is given, then 'positions' cannot be empty.");
94 :
95 260 : if (_input_names.size() != 1)
96 229 : if (_positions.size() && (_input_names.size() != _positions.size()))
97 8 : paramError(
98 : "positions",
99 : "If more than one input mesh is provided then the number of positions provided must "
100 : "exactly match the number of input meshes.");
101 : }
102 1257 : else if (isParamValid("positions_file"))
103 : {
104 60 : std::vector<FileName> positions_file = getParam<std::vector<FileName>>("positions_file");
105 :
106 : // the check in the constructor wont catch error where the user sets positions_file = ''
107 30 : if ((_input_names.size() == 1) && positions_file.empty())
108 8 : paramError("positions_file",
109 : "If only one input mesh is given, then 'positions_file' cannot be empty.");
110 :
111 48 : for (const auto & f : positions_file)
112 : {
113 26 : MooseUtils::DelimitedFileReader file(f, &_communicator);
114 26 : file.setFormatFlag(MooseUtils::DelimitedFileReader::FormatFlag::ROWS);
115 26 : file.read();
116 :
117 26 : const std::vector<Point> & data = file.getDataAsPoints();
118 :
119 26 : if (_input_names.size() != 1)
120 15 : if (data.size() && (_input_names.size() != data.size()))
121 8 : paramError("positions_file",
122 : "If more than one input mesh is provided then the number of positions must "
123 : "exactly match the number of input meshes.");
124 :
125 88 : for (const auto & d : data)
126 66 : _positions.push_back(d);
127 22 : }
128 22 : }
129 667 : }
130 :
131 : std::unique_ptr<MeshBase>
132 683 : CombinerGenerator::generate()
133 : {
134 : // Two cases:
135 : // 1. Multiple input meshes and optional positions
136 : // 2. One input mesh and multiple positions
137 683 : fillPositions();
138 :
139 : // Case 1
140 667 : if (_meshes.size() != 1)
141 : {
142 : // merge all meshes into the first one
143 625 : auto mesh = dynamic_pointer_cast<UnstructuredMesh>(*_meshes[0]);
144 :
145 625 : if (!mesh)
146 0 : paramError("inputs", _input_names[0], " is not a valid unstructured mesh");
147 :
148 : // Move the first input mesh if applicable
149 625 : if (_positions.size())
150 : {
151 236 : MeshTools::Modification::translate(
152 236 : *mesh, _positions[0](0), _positions[0](1), _positions[0](2));
153 : }
154 :
155 : // Read in all of the other meshes
156 2715 : for (MooseIndex(_meshes) i = 1; i < _meshes.size(); ++i)
157 : {
158 2090 : auto other_mesh = dynamic_pointer_cast<UnstructuredMesh>(*_meshes[i]);
159 :
160 2090 : if (!other_mesh)
161 0 : paramError("inputs", _input_names[i], " is not a valid unstructured mesh");
162 :
163 : // Move It
164 2090 : if (_positions.size())
165 : {
166 774 : MeshTools::Modification::translate(
167 774 : *other_mesh, _positions[i](0), _positions[i](1), _positions[i](2));
168 : }
169 :
170 2090 : MooseMeshUtils::copyIntoMesh(*this,
171 2090 : *mesh,
172 2090 : *other_mesh,
173 2090 : _avoid_merging_subdomains,
174 2090 : _avoid_merging_boundaries,
175 : _communicator);
176 2090 : }
177 :
178 625 : mesh->set_isnt_prepared();
179 625 : return dynamic_pointer_cast<MeshBase>(mesh);
180 625 : }
181 : else // Case 2
182 : {
183 42 : auto input_mesh = dynamic_pointer_cast<UnstructuredMesh>(*_meshes[0]);
184 :
185 42 : if (!input_mesh)
186 0 : paramError("inputs", _input_names[0], " is not a valid unstructured mesh");
187 :
188 : // Make a copy and displace it in order to get the final mesh started
189 : auto copy =
190 42 : input_mesh->clone(); // This is required because dynamic_pointer_cast() requires an l-value
191 42 : auto final_mesh = dynamic_pointer_cast<UnstructuredMesh>(copy);
192 :
193 42 : if (!final_mesh)
194 0 : mooseError("Unable to copy mesh!");
195 :
196 42 : MeshTools::Modification::translate(
197 42 : *final_mesh, _positions[0](0), _positions[0](1), _positions[0](2));
198 :
199 : // Here's the way this is going to work:
200 : // I'm going to make one more copy of the input_mesh so that I can move it and copy it in
201 : // Then, after it's copied in I'm going to reset its coordinates by looping over the input_mesh
202 : // and resetting the nodal positions.
203 : // This could be done without the copy - you would translate the mesh then translate it back...
204 : // However, I'm worried about floating point roundoff. If you were doing this 100,000 times or
205 : // more then the mesh could "drift" away from its original position. I really want the
206 : // translations to be exact each time.
207 : // I suppose that it is technically possible to just save off a datastructure (map, etc.) that
208 : // could hold the nodal positions only (instead of a copy of the mesh) but I'm not sure that
209 : // would really save much... we'll see if it shows up in profiling somewhere
210 42 : copy = input_mesh->clone();
211 42 : auto translated_mesh = dynamic_pointer_cast<UnstructuredMesh>(copy);
212 :
213 42 : if (!translated_mesh)
214 0 : mooseError("Unable to copy mesh!");
215 :
216 86 : for (MooseIndex(_meshes) i = 1; i < _positions.size(); ++i)
217 : {
218 : // Move
219 44 : MeshTools::Modification::translate(
220 44 : *translated_mesh, _positions[i](0), _positions[i](1), _positions[i](2));
221 :
222 : // Copy into final mesh
223 44 : MooseMeshUtils::copyIntoMesh(*this,
224 44 : *final_mesh,
225 44 : *translated_mesh,
226 44 : _avoid_merging_subdomains,
227 44 : _avoid_merging_boundaries,
228 : _communicator);
229 :
230 : // Reset nodal coordinates
231 5016 : for (auto translated_node_ptr : translated_mesh->node_ptr_range())
232 : {
233 4972 : auto & translated_node = *translated_node_ptr;
234 4972 : auto & input_node = input_mesh->node_ref(translated_node_ptr->id());
235 :
236 19888 : for (MooseIndex(LIBMESH_DIM) i = 0; i < LIBMESH_DIM; i++)
237 14916 : translated_node(i) = input_node(i);
238 44 : }
239 : }
240 :
241 42 : final_mesh->set_isnt_prepared();
242 42 : return dynamic_pointer_cast<MeshBase>(final_mesh);
243 42 : }
244 : }
|