https://mooseframework.inl.gov
CombinerGenerator.C
Go to the documentation of this file.
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 
24 
27 {
29 
30  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  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  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  params.addParam<std::vector<FileName>>(
46  "positions_file", "Alternative way to provide the position of each given mesh.");
47 
48  params.addParam<bool>("avoid_merging_subdomains",
49  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  params.addParam<bool>("avoid_merging_boundaries",
54  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  return params;
60 }
61 
63  : MeshGenerator(parameters),
64  _meshes(getMeshes("inputs")),
65  _input_names(getParam<std::vector<MeshGeneratorName>>("inputs")),
66  _avoid_merging_subdomains(getParam<bool>("avoid_merging_subdomains")),
67  _avoid_merging_boundaries(getParam<bool>("avoid_merging_boundaries"))
68 {
69  if (_input_names.empty())
70  paramError("input_names", "You need to specify at least one MeshGenerator as an input.");
71 
72  if (isParamValid("positions") && isParamValid("positions_file"))
73  mooseError("Both 'positions' and 'positions_file' cannot be specified simultaneously in "
74  "CombinerGenerator ",
75  _name);
76 
77  if (_input_names.size() == 1)
78  if (!isParamValid("positions") && !isParamValid("positions_file"))
79  paramError("positions",
80  "If only one input mesh is given, then 'positions' or 'positions_file' must also "
81  "be supplied");
82 }
83 
84 void
86 {
87  if (isParamValid("positions"))
88  {
89  _positions = getParam<std::vector<Point>>("positions");
90 
91  // the check in the constructor wont catch error where the user sets positions = ''
92  if ((_input_names.size() == 1) && _positions.empty())
93  paramError("positions", "If only one input mesh is given, then 'positions' cannot be empty.");
94 
95  if (_input_names.size() != 1)
96  if (_positions.size() && (_input_names.size() != _positions.size()))
97  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  else if (isParamValid("positions_file"))
103  {
104  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  if ((_input_names.size() == 1) && positions_file.empty())
108  paramError("positions_file",
109  "If only one input mesh is given, then 'positions_file' cannot be empty.");
110 
111  for (const auto & f : positions_file)
112  {
115  file.read();
116 
117  const std::vector<Point> & data = file.getDataAsPoints();
118 
119  if (_input_names.size() != 1)
120  if (data.size() && (_input_names.size() != data.size()))
121  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  for (const auto & d : data)
126  _positions.push_back(d);
127  }
128  }
129 }
130 
131 std::unique_ptr<MeshBase>
133 {
134  // Two cases:
135  // 1. Multiple input meshes and optional positions
136  // 2. One input mesh and multiple positions
137  fillPositions();
138 
139  // Case 1
140  if (_meshes.size() != 1)
141  {
142  // merge all meshes into the first one
143  auto mesh = dynamic_pointer_cast<UnstructuredMesh>(*_meshes[0]);
144 
145  if (!mesh)
146  paramError("inputs", _input_names[0], " is not a valid unstructured mesh");
147 
148  // Move the first input mesh if applicable
149  if (_positions.size())
150  {
151  MeshTools::Modification::translate(
152  *mesh, _positions[0](0), _positions[0](1), _positions[0](2));
153  }
154 
155  // Read in all of the other meshes
156  for (MooseIndex(_meshes) i = 1; i < _meshes.size(); ++i)
157  {
158  auto other_mesh = dynamic_pointer_cast<UnstructuredMesh>(*_meshes[i]);
159 
160  if (!other_mesh)
161  paramError("inputs", _input_names[i], " is not a valid unstructured mesh");
162 
163  // Move It
164  if (_positions.size())
165  {
166  MeshTools::Modification::translate(
167  *other_mesh, _positions[i](0), _positions[i](1), _positions[i](2));
168  }
169 
171  *mesh,
172  *other_mesh,
175  _communicator);
176  }
177 
178  mesh->set_isnt_prepared();
179  return dynamic_pointer_cast<MeshBase>(mesh);
180  }
181  else // Case 2
182  {
183  auto input_mesh = dynamic_pointer_cast<UnstructuredMesh>(*_meshes[0]);
184 
185  if (!input_mesh)
186  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  input_mesh->clone(); // This is required because dynamic_pointer_cast() requires an l-value
191  auto final_mesh = dynamic_pointer_cast<UnstructuredMesh>(copy);
192 
193  if (!final_mesh)
194  mooseError("Unable to copy mesh!");
195 
196  MeshTools::Modification::translate(
197  *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  copy = input_mesh->clone();
211  auto translated_mesh = dynamic_pointer_cast<UnstructuredMesh>(copy);
212 
213  if (!translated_mesh)
214  mooseError("Unable to copy mesh!");
215 
216  for (MooseIndex(_meshes) i = 1; i < _positions.size(); ++i)
217  {
218  // Move
219  MeshTools::Modification::translate(
220  *translated_mesh, _positions[i](0), _positions[i](1), _positions[i](2));
221 
222  // Copy into final mesh
224  *final_mesh,
225  *translated_mesh,
228  _communicator);
229 
230  // Reset nodal coordinates
231  for (auto translated_node_ptr : translated_mesh->node_ptr_range())
232  {
233  auto & translated_node = *translated_node_ptr;
234  auto & input_node = input_mesh->node_ref(translated_node_ptr->id());
235 
236  for (MooseIndex(LIBMESH_DIM) i = 0; i < LIBMESH_DIM; i++)
237  translated_node(i) = input_node(i);
238  }
239  }
240 
241  final_mesh->set_isnt_prepared();
242  return dynamic_pointer_cast<MeshBase>(final_mesh);
243  }
244 }
const std::string & _name
The name of this class.
Definition: MooseBase.h:363
void paramError(const std::string &param, Args... args) const
Emits an error prefixed with the file and line number of the given param (from the input file) along ...
Definition: MooseBase.h:439
const bool _avoid_merging_boundaries
Boolean to control whether to prevent merging boundaries.
Collects multiple meshes into a single (unconnected) mesh.
const std::vector< Point > getDataAsPoints() const
Get the data in Point format.
void fillPositions()
Fill the offset positions for each mesh.
MeshBase & mesh
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
std::unique_ptr< T_DEST, T_DELETER > dynamic_pointer_cast(std::unique_ptr< T_SRC, T_DELETER > &src)
These are reworked from https://stackoverflow.com/a/11003103.
const Parallel::Communicator & _communicator
void addRequiredParam(const std::string &name, const std::string &doc_string)
This method adds a parameter and documentation string to the InputParameters object that will be extr...
const bool _avoid_merging_subdomains
Boolean to control whether to prevent merging subdomains.
registerMooseObject("MooseApp", CombinerGenerator)
const std::vector< MeshGeneratorName > & _input_names
The mesh generators to use.
void copyIntoMesh(MeshGenerator &mg, UnstructuredMesh &destination, const UnstructuredMesh &source, const bool avoid_merging_subdomains, const bool avoid_merging_boundaries, const Parallel::Communicator &communicator)
Helper function for copying one mesh into another.
void read()
Perform the actual data reading.
static InputParameters validParams()
Definition: MeshGenerator.C:23
static InputParameters validParams()
Utility class for reading delimited data (e.g., CSV data).
std::vector< Point > _positions
The (offsets) positions for each mesh.
const std::vector< std::unique_ptr< MeshBase > * > _meshes
void mooseError(Args &&... args) const
Emits an error prefixed with object name and type and optionally a file path to the top-level block p...
Definition: MooseBase.h:271
CombinerGenerator(const InputParameters &parameters)
void addClassDescription(const std::string &doc_string)
This method adds a description of the class that will be displayed in the input file syntax dump...
std::unique_ptr< MeshBase > generate() override
Generate / modify the mesh.
void addParam(const std::string &name, const S &value, const std::string &doc_string)
These methods add an optional parameter and a documentation string to the InputParameters object...
bool isParamValid(const std::string &name) const
Test if the supplied parameter is valid.
Definition: MooseBase.h:199
MeshGenerators are objects that can modify or add to an existing mesh.
Definition: MeshGenerator.h:33