www.mooseframework.org
CombinerGenerator.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://www.mooseframework.org
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 
16 #include "libmesh/replicated_mesh.h"
17 #include "libmesh/unstructured_mesh.h"
18 #include "libmesh/mesh_modification.h"
19 #include "libmesh/point.h"
20 #include "libmesh/node.h"
21 
23 
26 {
28 
29  params.addClassDescription(
30  "Combine multiple meshes (or copies of one mesh) together into one (disjoint) mesh. Can "
31  "optionally translate those meshes before combining them.");
32 
33  params.addRequiredParam<std::vector<MeshGeneratorName>>(
34  "inputs",
35  "The input MeshGenerators. This can either be N generators or 1 generator. If only 1 is "
36  "given then 'positions' must also be given.");
37 
38  params.addParam<std::vector<Point>>(
39  "positions",
40  "The (optional) position of each given mesh. If N 'inputs' were given then this must either "
41  "be left blank or N positions must be given. If 1 input was given then this MUST be "
42  "provided.");
43 
44  params.addParam<std::vector<FileName>>(
45  "positions_file", "Alternative way to provide the position of each given mesh.");
46 
47  return params;
48 }
49 
51  : MeshGenerator(parameters),
52  _meshes(getMeshes("inputs")),
53  _input_names(getParam<std::vector<MeshGeneratorName>>("inputs"))
54 {
55  if (_input_names.empty())
56  paramError("input_names", "You need to specify at least one MeshGenerator as an input.");
57 
58  if (isParamValid("positions") && isParamValid("positions_file"))
59  mooseError("Both 'positions' and 'positions_file' cannot be specified simultaneously in "
60  "CombinerGenerator ",
61  _name);
62 
63  if (_input_names.size() == 1)
64  if (!isParamValid("positions") && !isParamValid("positions_file"))
65  paramError("positions",
66  "If only one input mesh is given, then 'positions' or 'positions_file' must also "
67  "be supplied");
68 }
69 
70 void
72 {
73  if (isParamValid("positions"))
74  {
75  _positions = getParam<std::vector<Point>>("positions");
76 
77  // the check in the constructor wont catch error where the user sets positions = ''
78  if ((_input_names.size() == 1) && _positions.empty())
79  paramError("positions", "If only one input mesh is given, then 'positions' cannot be empty.");
80 
81  if (_input_names.size() != 1)
82  if (_positions.size() && (_input_names.size() != _positions.size()))
83  paramError(
84  "positions",
85  "If more than one input mesh is provided then the number of positions provided must "
86  "exactly match the number of input meshes.");
87  }
88  else if (isParamValid("positions_file"))
89  {
90  std::vector<FileName> positions_file = getParam<std::vector<FileName>>("positions_file");
91 
92  // the check in the constructor wont catch error where the user sets positions_file = ''
93  if ((_input_names.size() == 1) && positions_file.empty())
94  paramError("positions_file",
95  "If only one input mesh is given, then 'positions_file' cannot be empty.");
96 
97  for (const auto & f : positions_file)
98  {
101  file.read();
102 
103  const std::vector<Point> & data = file.getDataAsPoints();
104 
105  if (_input_names.size() != 1)
106  if (data.size() && (_input_names.size() != data.size()))
107  paramError("positions_file",
108  "If more than one input mesh is provided then the number of positions must "
109  "exactly match the number of input meshes.");
110 
111  for (const auto & d : data)
112  _positions.push_back(d);
113  }
114  }
115 }
116 
117 std::unique_ptr<MeshBase>
119 {
120  // Two cases:
121  // 1. Multiple input meshes and optional positions
122  // 2. One input mesh and multiple positions
123  fillPositions();
124 
125  // Case 1
126  if (_meshes.size() != 1)
127  {
128  // merge all meshes into the first one
129  auto mesh = dynamic_pointer_cast<UnstructuredMesh>(*_meshes[0]);
130 
131  if (!mesh)
132  paramError("inputs", _input_names[0], " is not a valid unstructured mesh");
133 
134  // Read in all of the other meshes
135  for (MooseIndex(_meshes) i = 1; i < _meshes.size(); ++i)
136  {
137  auto other_mesh = dynamic_pointer_cast<UnstructuredMesh>(*_meshes[i]);
138 
139  if (!other_mesh)
140  paramError("inputs", _input_names[i], " is not a valid unstructured mesh");
141 
142  // Move It
143  if (_positions.size())
144  {
145  MeshTools::Modification::translate(
146  *other_mesh, _positions[i](0), _positions[i](1), _positions[i](2));
147  }
148 
149  copyIntoMesh(*mesh, *other_mesh);
150  }
151 
152  mesh->set_isnt_prepared();
153  return dynamic_pointer_cast<MeshBase>(mesh);
154  }
155  else // Case 2
156  {
157  auto input_mesh = dynamic_pointer_cast<UnstructuredMesh>(*_meshes[0]);
158 
159  if (!input_mesh)
160  paramError("inputs", _input_names[0], " is not a valid unstructured mesh");
161 
162  // Make a copy and displace it in order to get the final mesh started
163  auto copy =
164  input_mesh->clone(); // This is required because dynamic_pointer_cast() requires an l-value
165  auto final_mesh = dynamic_pointer_cast<UnstructuredMesh>(copy);
166 
167  if (!final_mesh)
168  mooseError("Unable to copy mesh!");
169 
170  MeshTools::Modification::translate(
171  *final_mesh, _positions[0](0), _positions[0](1), _positions[0](2));
172 
173  // Here's the way this is going to work:
174  // I'm going to make one more copy of the input_mesh so that I can move it and copy it in
175  // Then, after it's copied in I'm going to reset its coordinates by looping over the input_mesh
176  // and resetting the nodal positions.
177  // This could be done without the copy - you would translate the mesh then translate it back...
178  // However, I'm worried about floating point roundoff. If you were doing this 100,000 times or
179  // more then the mesh could "drift" away from its original position. I really want the
180  // translations to be exact each time.
181  // I suppose that it is technically possible to just save off a datastructure (map, etc.) that
182  // could hold the nodal positions only (instead of a copy of the mesh) but I'm not sure that
183  // would really save much... we'll see if it shows up in profiling somewhere
184  copy = input_mesh->clone();
185  auto translated_mesh = dynamic_pointer_cast<UnstructuredMesh>(copy);
186 
187  if (!translated_mesh)
188  mooseError("Unable to copy mesh!");
189 
190  for (MooseIndex(_meshes) i = 1; i < _positions.size(); ++i)
191  {
192  // Move
193  MeshTools::Modification::translate(
194  *translated_mesh, _positions[i](0), _positions[i](1), _positions[i](2));
195 
196  // Copy into final mesh
197  copyIntoMesh(*final_mesh, *translated_mesh);
198 
199  // Reset nodal coordinates
200  for (auto translated_node_ptr : translated_mesh->node_ptr_range())
201  {
202  auto & translated_node = *translated_node_ptr;
203  auto & input_node = input_mesh->node_ref(translated_node_ptr->id());
204 
205  for (MooseIndex(LIBMESH_DIM) i = 0; i < LIBMESH_DIM; i++)
206  translated_node(i) = input_node(i);
207  }
208  }
209 
210  final_mesh->set_isnt_prepared();
211  return dynamic_pointer_cast<MeshBase>(final_mesh);
212  }
213 }
214 
215 void
216 CombinerGenerator::copyIntoMesh(UnstructuredMesh & destination, const UnstructuredMesh & source)
217 {
218  dof_id_type node_delta = destination.max_node_id();
219  dof_id_type elem_delta = destination.max_elem_id();
220 
221  unique_id_type unique_delta =
222 #ifdef LIBMESH_ENABLE_UNIQUE_ID
223  destination.parallel_max_unique_id();
224 #else
225  0;
226 #endif
227 
228  // Copy mesh data over from the other mesh
229  destination.copy_nodes_and_elements(source,
230  // Skipping this should cause the neighors
231  // to simply be copied from the other mesh
232  // (which makes sense and is way faster)
233  /*skip_find_neighbors = */ true,
234  elem_delta,
235  node_delta,
236  unique_delta);
237 
238  // Note: the code below originally came from ReplicatedMesh::stitch_mesh_helper()
239  // in libMesh replicated_mesh.C around line 1203
240 
241  // Copy BoundaryInfo from other_mesh too. We do this via the
242  // list APIs rather than element-by-element for speed.
243  BoundaryInfo & boundary = destination.get_boundary_info();
244  const BoundaryInfo & other_boundary = source.get_boundary_info();
245  for (const auto & t : other_boundary.build_node_list())
246  boundary.add_node(std::get<0>(t) + node_delta, std::get<1>(t));
247 
248  for (const auto & t : other_boundary.build_side_list())
249  boundary.add_side(std::get<0>(t) + elem_delta, std::get<1>(t), std::get<2>(t));
250 
251  for (const auto & t : other_boundary.build_edge_list())
252  boundary.add_edge(std::get<0>(t) + elem_delta, std::get<1>(t), std::get<2>(t));
253 
254  for (const auto & t : other_boundary.build_shellface_list())
255  boundary.add_shellface(std::get<0>(t) + elem_delta, std::get<1>(t), std::get<2>(t));
256 
257  for (auto block_name_id_pair : source.get_subdomain_name_map())
258  destination.set_subdomain_name_map().insert(block_name_id_pair);
259 
260  for (auto nodeset_name_id_pair : other_boundary.get_nodeset_name_map())
261  boundary.set_nodeset_name_map().insert(nodeset_name_id_pair);
262 
263  for (auto sideset_name_id_pair : other_boundary.get_sideset_name_map())
264  boundary.set_sideset_name_map().insert(sideset_name_id_pair);
265 
266  for (auto edgeset_name_id_pair : other_boundary.get_edgeset_name_map())
267  boundary.set_edgeset_name_map().insert(edgeset_name_id_pair);
268 }
void read()
Perform the actual data reading.
Collects multiple meshes into a single (unconnected) mesh.
Utility class for reading delimited data (e.g., CSV data).
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 std::vector< Point > getDataAsPoints() const
Get the data in Point format.
bool isParamValid(const std::string &name) const
Test if the supplied parameter is valid.
registerMooseObject("MooseApp", CombinerGenerator)
const std::vector< MeshGeneratorName > & _input_names
The mesh generators to use.
void setFormatFlag(FormatFlag value)
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 ...
static InputParameters validParams()
Definition: MeshGenerator.C:23
const std::string _name
The name of this class.
Definition: MooseBase.h:90
static InputParameters validParams()
void copyIntoMesh(UnstructuredMesh &destination, const UnstructuredMesh &source)
Helper funciton for copying one mesh into another.
std::vector< Point > _positions
The (offsets) positions for each mesh.
const std::vector< std::unique_ptr< MeshBase > * > _meshes
CombinerGenerator(const InputParameters &parameters)
void mooseError(Args &&... args) const
Emits an error prefixed with object name and type.
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 option parameter and a documentation string to the InputParameters object...
MeshGenerators are objects that can modify or add to an existing mesh.
Definition: MeshGenerator.h:32
uint8_t unique_id_type
uint8_t dof_id_type