https://mooseframework.inl.gov
Loading...
Searching...
No Matches
GeneratedMeshGenerator.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
11#include "CastUniquePointer.h"
12
13#include "libmesh/replicated_mesh.h"
14#include "libmesh/mesh_generation.h"
15#include "libmesh/mesh_modification.h"
16#include "libmesh/string_to_enum.h"
17#include "libmesh/periodic_boundaries.h"
18#include "libmesh/periodic_boundary_base.h"
19#include "libmesh/unstructured_mesh.h"
20#include "libmesh/elem.h"
21
22// C++ includes
23#include <cmath> // provides round, not std::round (see http://www.cplusplus.com/reference/cmath/round/)
24
26
29{
31
32 MooseEnum elem_types(LIST_GEOM_ELEM); // no default
33
34 MooseEnum dims("1=1 2 3");
35 params.addRequiredParam<MooseEnum>("dim", dims, "The dimension of the mesh to be generated");
36
37 params.addParam<unsigned int>("nx", 1, "Number of elements in the X direction");
38 params.addParam<unsigned int>("ny", 1, "Number of elements in the Y direction");
39 params.addParam<unsigned int>("nz", 1, "Number of elements in the Z direction");
40 params.addParam<Real>("xmin", 0.0, "Lower X Coordinate of the generated mesh");
41 params.addParam<Real>("ymin", 0.0, "Lower Y Coordinate of the generated mesh");
42 params.addParam<Real>("zmin", 0.0, "Lower Z Coordinate of the generated mesh");
43 params.addParam<Real>("xmax", 1.0, "Upper X Coordinate of the generated mesh");
44 params.addParam<Real>("ymax", 1.0, "Upper Y Coordinate of the generated mesh");
45 params.addParam<Real>("zmax", 1.0, "Upper Z Coordinate of the generated mesh");
46 params.addParam<MooseEnum>("elem_type",
47 elem_types,
48 "The type of element from libMesh to "
49 "generate (default: linear element for "
50 "requested dimension)");
51 params.addParam<std::vector<SubdomainID>>(
52 "subdomain_ids",
53 "Subdomain IDs for each element, default to all zero. If a single number is specified, that "
54 "subdomain id is used for all elements.");
55 params.addParam<SubdomainName>("subdomain_name",
56 "If specified, single subdomain name for all elements");
57
58 params.addParam<bool>(
59 "gauss_lobatto_grid",
60 false,
61 "Grade mesh into boundaries according to Gauss-Lobatto quadrature spacing.");
62 params.addRangeCheckedParam<Real>(
63 "bias_x",
64 1.,
65 "bias_x>=0.5 & bias_x<=2",
66 "The amount by which to grow (or shrink) the cells in the x-direction.");
67 params.addRangeCheckedParam<Real>(
68 "bias_y",
69 1.,
70 "bias_y>=0.5 & bias_y<=2",
71 "The amount by which to grow (or shrink) the cells in the y-direction.");
72 params.addRangeCheckedParam<Real>(
73 "bias_z",
74 1.,
75 "bias_z>=0.5 & bias_z<=2",
76 "The amount by which to grow (or shrink) the cells in the z-direction.");
77
78 params.addParam<std::string>("boundary_name_prefix",
79 "If provided, prefix the built in boundary names with this string");
80 params.addParam<boundary_id_type>(
81 "boundary_id_offset", 0, "This offset is added to the generated boundary IDs");
82
83 params.addParam<std::vector<ExtraElementIDName>>("extra_element_integers",
84 "Names of extra element integers");
85
87 "Create a line, square, or cube mesh with uniformly spaced or biased elements.");
88
89 return params;
90}
91
93 : MeshGenerator(parameters),
94 _dim(getParam<MooseEnum>("dim")),
95 _nx(declareMeshProperty("num_elements_x", getParam<unsigned int>("nx"))),
96 _ny(declareMeshProperty("num_elements_y", getParam<unsigned int>("ny"))),
97 _nz(declareMeshProperty("num_elements_z", getParam<unsigned int>("nz"))),
98 _xmin(declareMeshProperty("xmin", getParam<Real>("xmin"))),
99 _xmax(declareMeshProperty("xmax", getParam<Real>("xmax"))),
100 _ymin(declareMeshProperty("ymin", getParam<Real>("ymin"))),
101 _ymax(declareMeshProperty("ymax", getParam<Real>("ymax"))),
102 _zmin(declareMeshProperty("zmin", getParam<Real>("zmin"))),
103 _zmax(declareMeshProperty("zmax", getParam<Real>("zmax"))),
104 _has_subdomain_ids(isParamValid("subdomain_ids")),
105 _gauss_lobatto_grid(getParam<bool>("gauss_lobatto_grid")),
106 _bias_x(getParam<Real>("bias_x")),
107 _bias_y(getParam<Real>("bias_y")),
108 _bias_z(getParam<Real>("bias_z")),
109 _boundary_name_prefix(isParamValid("boundary_name_prefix")
110 ? getParam<std::string>("boundary_name_prefix") + "_"
111 : ""),
112 _boundary_id_offset(getParam<boundary_id_type>("boundary_id_offset"))
113{
114 if (_gauss_lobatto_grid && (_bias_x != 1.0 || _bias_y != 1.0 || _bias_z != 1.0))
115 mooseError("Cannot apply both Gauss-Lobatto mesh grading and biasing at the same time.");
116 if (_xmax < _xmin)
117 paramError("xmax", "xmax must be larger than xmin.");
118 if (_ymax < _ymin)
119 paramError("ymax", "ymax must be larger than ymin.");
120 if (_zmax < _zmin)
121 paramError("zmax", "zmax must be larger than zmin.");
122}
123
124std::unique_ptr<MeshBase>
126{
127 // Have MOOSE construct the correct libMesh::Mesh object using Mesh block and CLI parameters.
129
130 if (isParamValid("extra_element_integers"))
131 {
132 for (auto & name : getParam<std::vector<ExtraElementIDName>>("extra_element_integers"))
133 mesh->add_elem_integer(name);
134 }
135
136 MooseEnum elem_type_enum = getParam<MooseEnum>("elem_type");
137
138 if (!isParamValid("elem_type"))
139 {
140 // Switching on MooseEnum
141 switch (_dim)
142 {
143 case 1:
144 elem_type_enum = "EDGE2";
145 break;
146 case 2:
147 elem_type_enum = "QUAD4";
148 break;
149 case 3:
150 elem_type_enum = "HEX8";
151 break;
152 }
153 }
154
155 ElemType elem_type = Utility::string_to_enum<ElemType>(elem_type_enum);
156
157 // Switching on MooseEnum
158 switch (_dim)
159 {
160 // The build_XYZ mesh generation functions take an
161 // UnstructuredMesh& as the first argument, hence the static_cast.
162 case 1:
163 MeshTools::Generation::build_line(
164 cast_ref<UnstructuredMesh &>(*mesh), _nx, _xmin, _xmax, elem_type, _gauss_lobatto_grid);
165 break;
166 case 2:
167 MeshTools::Generation::build_square(cast_ref<UnstructuredMesh &>(*mesh),
168 _nx,
169 _ny,
170 _xmin,
171 _xmax,
172 _ymin,
173 _ymax,
174 elem_type,
176 break;
177 case 3:
178 MeshTools::Generation::build_cube(cast_ref<UnstructuredMesh &>(*mesh),
179 _nx,
180 _ny,
181 _nz,
182 _xmin,
183 _xmax,
184 _ymin,
185 _ymax,
186 _zmin,
187 _zmax,
188 elem_type,
190 break;
191 }
192
194 {
195 auto & bids = getParam<std::vector<SubdomainID>>("subdomain_ids");
196 if (bids.size() != _nx * _ny * _nz && bids.size() != 1)
197 paramError("subdomain_ids",
198 "Size must equal to the product of number of elements in all directions, or one.");
199 for (auto & elem : mesh->element_ptr_range())
200 {
201 const Point p = elem->vertex_average();
202 unsigned int ix = std::floor((p(0) - _xmin) / (_xmax - _xmin) * _nx);
203 unsigned int iy = std::floor((p(1) - _ymin) / (_ymax - _ymin) * _ny);
204 unsigned int iz = std::floor((p(2) - _zmin) / (_zmax - _zmin) * _nz);
205 unsigned int i = iz * _nx * _ny + iy * _nx + ix;
206 if (bids.size() == 1)
207 elem->subdomain_id() = bids[0];
208 else
209 elem->subdomain_id() = bids[i];
210 }
211 }
212
213 if (isParamValid("subdomain_name"))
214 {
215 const auto & subdomain_name = getParam<SubdomainName>("subdomain_name");
216 if (isParamValid("subdomain_ids"))
217 {
218 const auto & bids = getParam<std::vector<SubdomainID>>("subdomain_ids");
219 if (bids.size() > 1)
221 "subdomain_ids",
222 "Specifying a subdomain_name is only supported for a single entry in subdomain_ids");
223 else
224 mesh->set_subdomain_name(bids[0], subdomain_name, true);
225 }
226 else
227 mesh->set_subdomain_name(0, subdomain_name, true);
228 }
229
230 // rename and shift boundaries
231 BoundaryInfo & boundary_info = mesh->get_boundary_info();
232
233 // Copy, since we're modifying the container mid-iteration
234 const auto mesh_boundary_ids = boundary_info.get_global_boundary_ids();
235 for (auto rit = mesh_boundary_ids.rbegin(); rit != mesh_boundary_ids.rend(); ++rit)
236 {
237 const std::string old_sideset_name = boundary_info.sideset_name(*rit);
238 const std::string old_nodeset_name = boundary_info.nodeset_name(*rit);
239
240 MeshTools::Modification::change_boundary_id(*mesh, *rit, *rit + _boundary_id_offset);
241
242 boundary_info.sideset_name(*rit + _boundary_id_offset) =
243 _boundary_name_prefix + old_sideset_name;
244 boundary_info.nodeset_name(*rit + _boundary_id_offset) =
245 _boundary_name_prefix + old_nodeset_name;
246 }
247
248 // FIXME: change_boundary_id() was *supposed* to leave the mesh in
249 // a fully prepared state, but there's a libMesh bug to fix first.
250 mesh->unset_has_boundary_id_sets();
251
252 // Apply the bias if any exists
253 if (_bias_x != 1.0 || _bias_y != 1.0 || _bias_z != 1.0)
254 {
255 const auto MIN = std::numeric_limits<Real>::max();
256
257 // Biases
258 std::array<Real, LIBMESH_DIM> bias = {
259 {_bias_x, _dim > 1 ? _bias_y : 1.0, _dim > 2 ? _bias_z : 1.0}};
260
261 // "width" of the mesh in each direction
262 std::array<Real, LIBMESH_DIM> width = {
263 {_xmax - _xmin, _dim > 1 ? _ymax - _ymin : 0, _dim > 2 ? _zmax - _zmin : 0}};
264
265 // Min mesh extent in each direction.
266 std::array<Real, LIBMESH_DIM> mins = {{_xmin, _dim > 1 ? _ymin : MIN, _dim > 2 ? _zmin : MIN}};
267
268 // Number of elements in each direction.
269 std::array<unsigned int, LIBMESH_DIM> nelem = {{_nx, _dim > 1 ? _ny : 1, _dim > 2 ? _nz : 1}};
270
271 // We will need the biases raised to integer powers in each
272 // direction, so let's pre-compute those...
273 std::array<std::vector<Real>, LIBMESH_DIM> pows;
274 for (unsigned int dir = 0; dir < LIBMESH_DIM; ++dir)
275 {
276 pows[dir].resize(nelem[dir] + 1);
277 pows[dir][0] = 1.0;
278 for (unsigned int i = 1; i < pows[dir].size(); ++i)
279 pows[dir][i] = pows[dir][i - 1] * bias[dir];
280 }
281
282 // Loop over the nodes and move them to the desired location
283 for (auto & node_ptr : mesh->node_ptr_range())
284 {
285 Node & node = *node_ptr;
286
287 for (unsigned int dir = 0; dir < LIBMESH_DIM; ++dir)
288 {
289 if (width[dir] != 0. && bias[dir] != 1.)
290 {
291 // Compute the scaled "index" of the current point. This
292 // will either be close to a whole integer or a whole
293 // integer+0.5 for quadratic nodes.
294 Real float_index = (node(dir) - mins[dir]) * nelem[dir] / width[dir];
295
296 Real integer_part = 0;
297 Real fractional_part = std::modf(float_index, &integer_part);
298
299 // Figure out where to move the node...
300 if (std::abs(fractional_part) < TOLERANCE || std::abs(fractional_part - 1.0) < TOLERANCE)
301 {
302 // If the fractional_part ~ 0.0 or 1.0, this is a vertex node, so
303 // we don't need an average.
304 //
305 // Compute the "index" we are at in the current direction. We
306 // round to the nearest integral value to get this instead
307 // of using "integer_part", since that could be off by a
308 // lot (e.g. we want 3.9999 to map to 4.0 instead of 3.0).
309 int index = round(float_index);
310
311 mooseAssert(index >= static_cast<int>(0) && index < static_cast<int>(pows[dir].size()),
312 "Scaled \"index\" out of range");
313
314 // Move node to biased location.
315 node(dir) =
316 mins[dir] + width[dir] * (1. - pows[dir][index]) / (1. - pows[dir][nelem[dir]]);
317 }
318 else if (std::abs(fractional_part - 0.5) < TOLERANCE)
319 {
320 // If the fractional_part ~ 0.5, this is a midedge/face
321 // (i.e. quadratic) node. We don't move those with the same
322 // bias as the vertices, instead we put them midway between
323 // their respective vertices.
324 //
325 // Also, since the fractional part is nearly 0.5, we know that
326 // the integer_part will be the index of the vertex to the
327 // left, and integer_part+1 will be the index of the
328 // vertex to the right.
329 node(dir) = mins[dir] +
330 width[dir] *
331 (1. - 0.5 * (pows[dir][integer_part] + pows[dir][integer_part + 1])) /
332 (1. - pows[dir][nelem[dir]]);
333 }
334 else
335 {
336 // We don't yet handle anything higher order than quadratic...
337 mooseError("Unable to bias node at node(", dir, ")=", node(dir));
338 }
339 }
340 }
341 }
342 }
343
344 mesh->unset_is_prepared();
345 return dynamic_pointer_cast<MeshBase>(mesh);
346}
registerMooseObject("MooseApp", GeneratedMeshGenerator)
const std::string LIST_GEOM_ELEM
Definition MooseMesh.h:63
void ErrorVector unsigned int
Generates a line, square, or cube mesh with uniformly spaced or biased elements.
const boundary_id_type _boundary_id_offset
offset that is added to the boundary IDs
Real & _xmin
The min/max values for x,y,z component.
static InputParameters validParams()
const std::string _boundary_name_prefix
prefix string for the boundary names
MooseEnum _dim
The dimension of the mesh.
std::unique_ptr< MeshBase > generate() override
Generate / modify the mesh.
bool _has_subdomain_ids
Whether or not subdomain_ids parameter is set.
GeneratedMeshGenerator(const InputParameters &parameters)
const bool _gauss_lobatto_grid
All of the libmesh build_line/square/cube routines support an option to grade the mesh into the bound...
const Real _bias_x
The amount by which to bias the cells in the x,y,z directions.
unsigned int & _nx
Number of elements in x, y, z direction.
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system.
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.
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...
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.
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
MeshGenerators are objects that can modify or add to an existing mesh.
static InputParameters validParams()
std::unique_ptr< MeshBase > buildMeshBaseObject(unsigned int dim=libMesh::invalid_uint)
Build a MeshBase object whose underlying type will be determined by the Mesh input file block.
const std::string & name() const
Get the name of the class.
Definition MooseBase.h:103
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:457
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
bool isParamValid(const std::string &name) const
Test if the supplied parameter is valid.
Definition MooseBase.h:199
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type It sho...
Definition MooseEnum.h:55
MeshBase & mesh