LCOV - code coverage report
Current view: top level - src/mesh - GeneratedMesh.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 123 136 90.4 %
Date: 2025-07-17 01:28:37 Functions: 6 7 85.7 %
Legend: Lines: hit not hit

          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 "GeneratedMesh.h"
      11             : 
      12             : #include "MooseApp.h"
      13             : 
      14             : #include "libmesh/mesh_generation.h"
      15             : #include "libmesh/string_to_enum.h"
      16             : #include "libmesh/periodic_boundaries.h"
      17             : #include "libmesh/periodic_boundary_base.h"
      18             : #include "libmesh/unstructured_mesh.h"
      19             : #include "libmesh/node.h"
      20             : 
      21             : // C++ includes
      22             : #include <cmath> // provides round, not std::round (see http://www.cplusplus.com/reference/cmath/round/)
      23             : #include <array>
      24             : 
      25             : registerMooseObject("MooseApp", GeneratedMesh);
      26             : 
      27             : InputParameters
      28      113335 : GeneratedMesh::validParams()
      29             : {
      30      113335 :   InputParameters params = MooseMesh::validParams();
      31             : 
      32      113335 :   MooseEnum elem_types(LIST_GEOM_ELEM); // no default
      33             : 
      34      113335 :   MooseEnum dims("1=1 2 3");
      35      113335 :   params.addRequiredParam<MooseEnum>("dim", dims, "The dimension of the mesh to be generated");
      36             : 
      37      113335 :   params.addParam<unsigned int>("nx", 1, "Number of elements in the X direction");
      38      113335 :   params.addParam<unsigned int>("ny", 1, "Number of elements in the Y direction");
      39      113335 :   params.addParam<unsigned int>("nz", 1, "Number of elements in the Z direction");
      40      113335 :   params.addParam<Real>("xmin", 0.0, "Lower X Coordinate of the generated mesh");
      41      113335 :   params.addParam<Real>("ymin", 0.0, "Lower Y Coordinate of the generated mesh");
      42      113335 :   params.addParam<Real>("zmin", 0.0, "Lower Z Coordinate of the generated mesh");
      43      113335 :   params.addParam<Real>("xmax", 1.0, "Upper X Coordinate of the generated mesh");
      44      113335 :   params.addParam<Real>("ymax", 1.0, "Upper Y Coordinate of the generated mesh");
      45      113335 :   params.addParam<Real>("zmax", 1.0, "Upper Z Coordinate of the generated mesh");
      46      113335 :   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      340005 :   params.addParam<bool>(
      52             :       "gauss_lobatto_grid",
      53      226670 :       false,
      54             :       "Grade mesh into boundaries according to Gauss-Lobatto quadrature spacing.");
      55      340005 :   params.addRangeCheckedParam<Real>(
      56             :       "bias_x",
      57      226670 :       1.,
      58             :       "bias_x>=0.5 & bias_x<=2",
      59             :       "The amount by which to grow (or shrink) the cells in the x-direction.");
      60      340005 :   params.addRangeCheckedParam<Real>(
      61             :       "bias_y",
      62      226670 :       1.,
      63             :       "bias_y>=0.5 & bias_y<=2",
      64             :       "The amount by which to grow (or shrink) the cells in the y-direction.");
      65      340005 :   params.addRangeCheckedParam<Real>(
      66             :       "bias_z",
      67      226670 :       1.,
      68             :       "bias_z>=0.5 & bias_z<=2",
      69             :       "The amount by which to grow (or shrink) the cells in the z-direction.");
      70             : 
      71      113335 :   params.addParamNamesToGroup("dim", "Required");
      72             : 
      73      113335 :   params.addClassDescription(
      74             :       "Create a line, square, or cube mesh with uniformly spaced or biased elements.");
      75      226670 :   return params;
      76      113335 : }
      77             : 
      78       33853 : GeneratedMesh::GeneratedMesh(const InputParameters & parameters)
      79             :   : MooseMesh(parameters),
      80       33853 :     _dim(getParam<MooseEnum>("dim")),
      81       33853 :     _nx(getParam<unsigned int>("nx")),
      82       33853 :     _ny(getParam<unsigned int>("ny")),
      83       33853 :     _nz(getParam<unsigned int>("nz")),
      84       33853 :     _xmin(getParam<Real>("xmin")),
      85       33853 :     _xmax(getParam<Real>("xmax")),
      86       33853 :     _ymin(getParam<Real>("ymin")),
      87       33853 :     _ymax(getParam<Real>("ymax")),
      88       33853 :     _zmin(getParam<Real>("zmin")),
      89       33853 :     _zmax(getParam<Real>("zmax")),
      90       33853 :     _gauss_lobatto_grid(getParam<bool>("gauss_lobatto_grid")),
      91       33853 :     _bias_x(getParam<Real>("bias_x")),
      92       33853 :     _bias_y(getParam<Real>("bias_y")),
      93       33853 :     _bias_z(getParam<Real>("bias_z")),
      94       33853 :     _dims_may_have_changed(false)
      95             : {
      96       33853 :   if (_gauss_lobatto_grid && (_bias_x != 1.0 || _bias_y != 1.0 || _bias_z != 1.0))
      97           0 :     mooseError("Cannot apply both Gauss-Lobatto mesh grading and biasing at the same time.");
      98             : 
      99             :   // All generated meshes are regular orthogonal meshes - until they get modified ;)
     100       33853 :   _regular_orthogonal_mesh = true;
     101       33853 : }
     102             : 
     103             : void
     104           0 : GeneratedMesh::prepared(bool state)
     105             : {
     106           0 :   MooseMesh::prepared(state);
     107             : 
     108             :   // Fall back on scanning the mesh for coordinates instead of using input parameters for queries
     109           0 :   if (!state)
     110           0 :     _dims_may_have_changed = true;
     111           0 : }
     112             : 
     113             : Real
     114       39776 : GeneratedMesh::getMinInDimension(unsigned int component) const
     115             : {
     116       39776 :   if (_dims_may_have_changed)
     117           0 :     return MooseMesh::getMinInDimension(component);
     118             : 
     119       39776 :   switch (component)
     120             :   {
     121         824 :     case 0:
     122         824 :       return _xmin;
     123       19031 :     case 1:
     124       19031 :       return _dim > 1 ? _ymin : 0;
     125       19921 :     case 2:
     126       19921 :       return _dim > 2 ? _zmin : 0;
     127           0 :     default:
     128           0 :       mooseError("Invalid component");
     129             :   }
     130             : }
     131             : 
     132             : Real
     133       39716 : GeneratedMesh::getMaxInDimension(unsigned int component) const
     134             : {
     135       39716 :   if (_dims_may_have_changed)
     136           0 :     return MooseMesh::getMaxInDimension(component);
     137             : 
     138       39716 :   switch (component)
     139             :   {
     140         804 :     case 0:
     141         804 :       return _xmax;
     142       19011 :     case 1:
     143       19011 :       return _dim > 1 ? _ymax : 0;
     144       19901 :     case 2:
     145       19901 :       return _dim > 2 ? _zmax : 0;
     146           0 :     default:
     147           0 :       mooseError("Invalid component");
     148             :   }
     149             : }
     150             : 
     151             : std::unique_ptr<MooseMesh>
     152        1793 : GeneratedMesh::safeClone() const
     153             : {
     154        1793 :   return _app.getFactory().copyConstruct(*this);
     155             : }
     156             : 
     157             : void
     158       32017 : GeneratedMesh::buildMesh()
     159             : {
     160       32017 :   MooseEnum elem_type_enum = getParam<MooseEnum>("elem_type");
     161             : 
     162       32017 :   if (!isParamValid("elem_type"))
     163             :   {
     164             :     // Switching on MooseEnum
     165       26647 :     switch (_dim)
     166             :     {
     167        4869 :       case 1:
     168        4869 :         elem_type_enum = "EDGE2";
     169        4869 :         break;
     170       20675 :       case 2:
     171       20675 :         elem_type_enum = "QUAD4";
     172       20675 :         break;
     173        1103 :       case 3:
     174        1103 :         elem_type_enum = "HEX8";
     175        1103 :         break;
     176             :     }
     177             :   }
     178             : 
     179       32017 :   ElemType elem_type = Utility::string_to_enum<ElemType>(elem_type_enum);
     180             : 
     181             :   // Switching on MooseEnum
     182       32017 :   switch (_dim)
     183             :   {
     184             :     // The build_XYZ mesh generation functions take an
     185             :     // UnstructuredMesh& as the first argument, hence the dynamic_cast.
     186        5204 :     case 1:
     187        5204 :       MeshTools::Generation::build_line(dynamic_cast<UnstructuredMesh &>(getMesh()),
     188             :                                         _nx,
     189             :                                         _xmin,
     190             :                                         _xmax,
     191             :                                         elem_type,
     192        5204 :                                         _gauss_lobatto_grid);
     193        5204 :       break;
     194       25371 :     case 2:
     195       25371 :       MeshTools::Generation::build_square(dynamic_cast<UnstructuredMesh &>(getMesh()),
     196             :                                           _nx,
     197             :                                           _ny,
     198             :                                           _xmin,
     199             :                                           _xmax,
     200             :                                           _ymin,
     201             :                                           _ymax,
     202             :                                           elem_type,
     203       25371 :                                           _gauss_lobatto_grid);
     204       25371 :       break;
     205        1442 :     case 3:
     206        1442 :       MeshTools::Generation::build_cube(dynamic_cast<UnstructuredMesh &>(getMesh()),
     207             :                                         _nx,
     208             :                                         _ny,
     209             :                                         _nz,
     210             :                                         _xmin,
     211             :                                         _xmax,
     212             :                                         _ymin,
     213             :                                         _ymax,
     214             :                                         _zmin,
     215             :                                         _zmax,
     216             :                                         elem_type,
     217        1442 :                                         _gauss_lobatto_grid);
     218        1442 :       break;
     219             :   }
     220             : 
     221             :   // Apply the bias if any exists
     222       32017 :   if (_bias_x != 1.0 || _bias_y != 1.0 || _bias_z != 1.0)
     223             :   {
     224             :     // Reference to the libmesh mesh
     225          20 :     MeshBase & mesh = getMesh();
     226             : 
     227             :     // Biases
     228             :     std::array<Real, LIBMESH_DIM> bias = {
     229          20 :         {_bias_x, _dim > 1 ? _bias_y : 1.0, _dim > 2 ? _bias_z : 1.0}};
     230             : 
     231             :     // "width" of the mesh in each direction
     232             :     std::array<Real, LIBMESH_DIM> width = {
     233          20 :         {_xmax - _xmin, _dim > 1 ? _ymax - _ymin : 0, _dim > 2 ? _zmax - _zmin : 0}};
     234             : 
     235             :     // Min mesh extent in each direction.
     236             :     std::array<Real, LIBMESH_DIM> mins = {
     237          20 :         {getMinInDimension(0), getMinInDimension(1), getMinInDimension(2)}};
     238             : 
     239             :     // Number of elements in each direction.
     240          20 :     std::array<unsigned int, LIBMESH_DIM> nelem = {{_nx, _dim > 1 ? _ny : 1, _dim > 2 ? _nz : 1}};
     241             : 
     242             :     // We will need the biases raised to integer powers in each
     243             :     // direction, so let's pre-compute those...
     244          20 :     std::array<std::vector<Real>, LIBMESH_DIM> pows;
     245          80 :     for (unsigned int dir = 0; dir < LIBMESH_DIM; ++dir)
     246             :     {
     247          60 :       pows[dir].resize(nelem[dir] + 1);
     248         420 :       for (unsigned int i = 0; i < pows[dir].size(); ++i)
     249         360 :         pows[dir][i] = std::pow(bias[dir], static_cast<int>(i));
     250             :     }
     251             : 
     252             :     // Loop over the nodes and move them to the desired location
     253       14889 :     for (auto & node_ptr : mesh.node_ptr_range())
     254             :     {
     255       14869 :       Node & node = *node_ptr;
     256             : 
     257       59476 :       for (unsigned int dir = 0; dir < LIBMESH_DIM; ++dir)
     258             :       {
     259       44607 :         if (width[dir] != 0. && bias[dir] != 1.)
     260             :         {
     261             :           // Compute the scaled "index" of the current point.  This
     262             :           // will either be close to a whole integer or a whole
     263             :           // integer+0.5 for quadratic nodes.
     264       29738 :           Real float_index = (node(dir) - mins[dir]) * nelem[dir] / width[dir];
     265             : 
     266       29738 :           Real integer_part = 0;
     267       29738 :           Real fractional_part = std::modf(float_index, &integer_part);
     268             : 
     269             :           // Figure out where to move the node...
     270       29738 :           if (std::abs(fractional_part) < TOLERANCE || std::abs(fractional_part - 1.0) < TOLERANCE)
     271             :           {
     272             :             // If the fractional_part ~ 0.0 or 1.0, this is a vertex node, so
     273             :             // we don't need an average.
     274             :             //
     275             :             // Compute the "index" we are at in the current direction.  We
     276             :             // round to the nearest integral value to get this instead
     277             :             // of using "integer_part", since that could be off by a
     278             :             // lot (e.g. we want 3.9999 to map to 4.0 instead of 3.0).
     279       18145 :             int index = round(float_index);
     280             : 
     281             :             mooseAssert(index >= static_cast<int>(0) && index < static_cast<int>(pows[dir].size()),
     282             :                         "Scaled \"index\" out of range");
     283             : 
     284             :             // Move node to biased location.
     285       18145 :             node(dir) =
     286       18145 :                 mins[dir] + width[dir] * (1. - pows[dir][index]) / (1. - pows[dir][nelem[dir]]);
     287             :           }
     288       11593 :           else if (std::abs(fractional_part - 0.5) < TOLERANCE)
     289             :           {
     290             :             // If the fractional_part ~ 0.5, this is a midedge/face
     291             :             // (i.e. quadratic) node.  We don't move those with the same
     292             :             // bias as the vertices, instead we put them midway between
     293             :             // their respective vertices.
     294             :             //
     295             :             // Also, since the fractional part is nearly 0.5, we know that
     296             :             // the integer_part will be the index of the vertex to the
     297             :             // left, and integer_part+1 will be the index of the
     298             :             // vertex to the right.
     299       23186 :             node(dir) = mins[dir] +
     300       11593 :                         width[dir] *
     301       11593 :                             (1. - 0.5 * (pows[dir][integer_part] + pows[dir][integer_part + 1])) /
     302       11593 :                             (1. - pows[dir][nelem[dir]]);
     303             :           }
     304             :           else
     305             :           {
     306             :             // We don't yet handle anything higher order than quadratic...
     307           0 :             mooseError("Unable to bias node at node(", dir, ")=", node(dir));
     308             :           }
     309             :         }
     310             :       }
     311          20 :     }
     312          20 :   }
     313       32017 : }

Generated by: LCOV version 1.14