LCOV - code coverage report
Current view: top level - src/meshgenerators - GeneratedMeshGenerator.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 167 176 94.9 %
Date: 2025-07-17 01:28:37 Functions: 3 3 100.0 %
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 "GeneratedMeshGenerator.h"
      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             : 
      25             : registerMooseObject("MooseApp", GeneratedMeshGenerator);
      26             : 
      27             : InputParameters
      28       80500 : GeneratedMeshGenerator::validParams()
      29             : {
      30       80500 :   InputParameters params = MeshGenerator::validParams();
      31             : 
      32       80500 :   MooseEnum elem_types(LIST_GEOM_ELEM); // no default
      33             : 
      34       80500 :   MooseEnum dims("1=1 2 3");
      35       80500 :   params.addRequiredParam<MooseEnum>("dim", dims, "The dimension of the mesh to be generated");
      36             : 
      37       80500 :   params.addParam<unsigned int>("nx", 1, "Number of elements in the X direction");
      38       80500 :   params.addParam<unsigned int>("ny", 1, "Number of elements in the Y direction");
      39       80500 :   params.addParam<unsigned int>("nz", 1, "Number of elements in the Z direction");
      40       80500 :   params.addParam<Real>("xmin", 0.0, "Lower X Coordinate of the generated mesh");
      41       80500 :   params.addParam<Real>("ymin", 0.0, "Lower Y Coordinate of the generated mesh");
      42       80500 :   params.addParam<Real>("zmin", 0.0, "Lower Z Coordinate of the generated mesh");
      43       80500 :   params.addParam<Real>("xmax", 1.0, "Upper X Coordinate of the generated mesh");
      44       80500 :   params.addParam<Real>("ymax", 1.0, "Upper Y Coordinate of the generated mesh");
      45       80500 :   params.addParam<Real>("zmax", 1.0, "Upper Z Coordinate of the generated mesh");
      46       80500 :   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       80500 :   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       80500 :   params.addParam<SubdomainName>("subdomain_name",
      56             :                                  "If specified, single subdomain name for all elements");
      57             : 
      58      241500 :   params.addParam<bool>(
      59             :       "gauss_lobatto_grid",
      60      161000 :       false,
      61             :       "Grade mesh into boundaries according to Gauss-Lobatto quadrature spacing.");
      62      241500 :   params.addRangeCheckedParam<Real>(
      63             :       "bias_x",
      64      161000 :       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      241500 :   params.addRangeCheckedParam<Real>(
      68             :       "bias_y",
      69      161000 :       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      241500 :   params.addRangeCheckedParam<Real>(
      73             :       "bias_z",
      74      161000 :       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       80500 :   params.addParam<std::string>("boundary_name_prefix",
      79             :                                "If provided, prefix the built in boundary names with this string");
      80      241500 :   params.addParam<boundary_id_type>(
      81      161000 :       "boundary_id_offset", 0, "This offset is added to the generated boundary IDs");
      82             : 
      83       80500 :   params.addParam<std::vector<ExtraElementIDName>>("extra_element_integers",
      84             :                                                    "Names of extra element integers");
      85             : 
      86       80500 :   params.addClassDescription(
      87             :       "Create a line, square, or cube mesh with uniformly spaced or biased elements.");
      88             : 
      89      161000 :   return params;
      90       80500 : }
      91             : 
      92       18754 : GeneratedMeshGenerator::GeneratedMeshGenerator(const InputParameters & parameters)
      93             :   : MeshGenerator(parameters),
      94       18746 :     _dim(getParam<MooseEnum>("dim")),
      95       18746 :     _nx(declareMeshProperty("num_elements_x", getParam<unsigned int>("nx"))),
      96       18746 :     _ny(declareMeshProperty("num_elements_y", getParam<unsigned int>("ny"))),
      97       18746 :     _nz(declareMeshProperty("num_elements_z", getParam<unsigned int>("nz"))),
      98       18746 :     _xmin(declareMeshProperty("xmin", getParam<Real>("xmin"))),
      99       18746 :     _xmax(declareMeshProperty("xmax", getParam<Real>("xmax"))),
     100       18746 :     _ymin(declareMeshProperty("ymin", getParam<Real>("ymin"))),
     101       18746 :     _ymax(declareMeshProperty("ymax", getParam<Real>("ymax"))),
     102       18746 :     _zmin(declareMeshProperty("zmin", getParam<Real>("zmin"))),
     103       18746 :     _zmax(declareMeshProperty("zmax", getParam<Real>("zmax"))),
     104       18746 :     _has_subdomain_ids(isParamValid("subdomain_ids")),
     105       18746 :     _gauss_lobatto_grid(getParam<bool>("gauss_lobatto_grid")),
     106       18746 :     _bias_x(getParam<Real>("bias_x")),
     107       18746 :     _bias_y(getParam<Real>("bias_y")),
     108       18746 :     _bias_z(getParam<Real>("bias_z")),
     109       37492 :     _boundary_name_prefix(isParamValid("boundary_name_prefix")
     110       18746 :                               ? getParam<std::string>("boundary_name_prefix") + "_"
     111             :                               : ""),
     112       37500 :     _boundary_id_offset(getParam<boundary_id_type>("boundary_id_offset"))
     113             : {
     114       18746 :   if (_gauss_lobatto_grid && (_bias_x != 1.0 || _bias_y != 1.0 || _bias_z != 1.0))
     115           0 :     mooseError("Cannot apply both Gauss-Lobatto mesh grading and biasing at the same time.");
     116       18746 :   if (_xmax < _xmin)
     117           4 :     paramError("xmax", "xmax must be larger than xmin.");
     118       18742 :   if (_ymax < _ymin)
     119           4 :     paramError("ymax", "ymax must be larger than ymin.");
     120       18738 :   if (_zmax < _zmin)
     121           4 :     paramError("zmax", "zmax must be larger than zmin.");
     122       18734 : }
     123             : 
     124             : std::unique_ptr<MeshBase>
     125       17899 : GeneratedMeshGenerator::generate()
     126             : {
     127             :   // Have MOOSE construct the correct libMesh::Mesh object using Mesh block and CLI parameters.
     128       17899 :   auto mesh = buildMeshBaseObject(_dim);
     129             : 
     130       17899 :   if (isParamValid("extra_element_integers"))
     131             :   {
     132         399 :     for (auto & name : getParam<std::vector<ExtraElementIDName>>("extra_element_integers"))
     133         216 :       mesh->add_elem_integer(name);
     134             :   }
     135             : 
     136       17899 :   MooseEnum elem_type_enum = getParam<MooseEnum>("elem_type");
     137             : 
     138       17899 :   if (!isParamValid("elem_type"))
     139             :   {
     140             :     // Switching on MooseEnum
     141       14969 :     switch (_dim)
     142             :     {
     143        4954 :       case 1:
     144        4954 :         elem_type_enum = "EDGE2";
     145        4954 :         break;
     146        8251 :       case 2:
     147        8251 :         elem_type_enum = "QUAD4";
     148        8251 :         break;
     149        1764 :       case 3:
     150        1764 :         elem_type_enum = "HEX8";
     151        1764 :         break;
     152             :     }
     153             :   }
     154             : 
     155       17899 :   ElemType elem_type = Utility::string_to_enum<ElemType>(elem_type_enum);
     156             : 
     157             :   // Switching on MooseEnum
     158       17899 :   switch (_dim)
     159             :   {
     160             :     // The build_XYZ mesh generation functions take an
     161             :     // UnstructuredMesh& as the first argument, hence the static_cast.
     162        5034 :     case 1:
     163        5034 :       MeshTools::Generation::build_line(static_cast<UnstructuredMesh &>(*mesh),
     164        5034 :                                         _nx,
     165        5034 :                                         _xmin,
     166        5034 :                                         _xmax,
     167             :                                         elem_type,
     168        5034 :                                         _gauss_lobatto_grid);
     169        5034 :       break;
     170       10061 :     case 2:
     171       10061 :       MeshTools::Generation::build_square(static_cast<UnstructuredMesh &>(*mesh),
     172       10061 :                                           _nx,
     173       10061 :                                           _ny,
     174       10061 :                                           _xmin,
     175       10061 :                                           _xmax,
     176       10061 :                                           _ymin,
     177       10061 :                                           _ymax,
     178             :                                           elem_type,
     179       10061 :                                           _gauss_lobatto_grid);
     180       10061 :       break;
     181        2804 :     case 3:
     182        2804 :       MeshTools::Generation::build_cube(static_cast<UnstructuredMesh &>(*mesh),
     183        2804 :                                         _nx,
     184        2804 :                                         _ny,
     185        2804 :                                         _nz,
     186        2804 :                                         _xmin,
     187        2804 :                                         _xmax,
     188        2804 :                                         _ymin,
     189        2804 :                                         _ymax,
     190        2804 :                                         _zmin,
     191        2804 :                                         _zmax,
     192             :                                         elem_type,
     193        2804 :                                         _gauss_lobatto_grid);
     194        2804 :       break;
     195             :   }
     196             : 
     197       17899 :   if (_has_subdomain_ids)
     198             :   {
     199        1489 :     auto & bids = getParam<std::vector<SubdomainID>>("subdomain_ids");
     200        1489 :     if (bids.size() != _nx * _ny * _nz && bids.size() != 1)
     201           0 :       paramError("subdomain_ids",
     202             :                  "Size must equal to the product of number of elements in all directions, or one.");
     203       19540 :     for (auto & elem : mesh->element_ptr_range())
     204             :     {
     205       18051 :       const Point p = elem->vertex_average();
     206       18051 :       unsigned int ix = std::floor((p(0) - _xmin) / (_xmax - _xmin) * _nx);
     207       18051 :       unsigned int iy = std::floor((p(1) - _ymin) / (_ymax - _ymin) * _ny);
     208       18051 :       unsigned int iz = std::floor((p(2) - _zmin) / (_zmax - _zmin) * _nz);
     209       18051 :       unsigned int i = iz * _nx * _ny + iy * _nx + ix;
     210       18051 :       if (bids.size() == 1)
     211        6795 :         elem->subdomain_id() = bids[0];
     212             :       else
     213       11256 :         elem->subdomain_id() = bids[i];
     214        1489 :     }
     215             :   }
     216             : 
     217       17899 :   if (isParamValid("subdomain_name"))
     218             :   {
     219         223 :     const auto & subdomain_name = getParam<SubdomainName>("subdomain_name");
     220         223 :     if (isParamValid("subdomain_ids"))
     221             :     {
     222          25 :       const auto & bids = getParam<std::vector<SubdomainID>>("subdomain_ids");
     223          25 :       if (bids.size() > 1)
     224           0 :         paramError(
     225             :             "subdomain_ids",
     226             :             "Specifying a subdomain_name is only supported for a single entry in subdomain_ids");
     227             :       else
     228          25 :         mesh->subdomain_name(bids[0]) = subdomain_name;
     229             :     }
     230             :     else
     231         198 :       mesh->subdomain_name(0) = subdomain_name;
     232             :   }
     233             : 
     234             :   // rename and shift boundaries
     235       17899 :   BoundaryInfo & boundary_info = mesh->get_boundary_info();
     236             : 
     237             :   // Copy, since we're modifying the container mid-iteration
     238       17899 :   const auto mesh_boundary_ids = boundary_info.get_global_boundary_ids();
     239       85035 :   for (auto rit = mesh_boundary_ids.rbegin(); rit != mesh_boundary_ids.rend(); ++rit)
     240             :   {
     241       67136 :     const std::string old_sideset_name = boundary_info.sideset_name(*rit);
     242       67136 :     const std::string old_nodeset_name = boundary_info.nodeset_name(*rit);
     243             : 
     244       67136 :     MeshTools::Modification::change_boundary_id(*mesh, *rit, *rit + _boundary_id_offset);
     245             : 
     246       67136 :     boundary_info.sideset_name(*rit + _boundary_id_offset) =
     247      134272 :         _boundary_name_prefix + old_sideset_name;
     248       67136 :     boundary_info.nodeset_name(*rit + _boundary_id_offset) =
     249      134272 :         _boundary_name_prefix + old_nodeset_name;
     250       67136 :   }
     251             : 
     252             :   // Apply the bias if any exists
     253       17899 :   if (_bias_x != 1.0 || _bias_y != 1.0 || _bias_z != 1.0)
     254             :   {
     255         160 :     const auto MIN = std::numeric_limits<Real>::max();
     256             : 
     257             :     // Biases
     258             :     std::array<Real, LIBMESH_DIM> bias = {
     259         160 :         {_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         160 :         {_xmax - _xmin, _dim > 1 ? _ymax - _ymin : 0, _dim > 2 ? _zmax - _zmin : 0}};
     264             : 
     265             :     // Min mesh extent in each direction.
     266         160 :     std::array<Real, LIBMESH_DIM> mins = {{_xmin, _dim > 1 ? _ymin : MIN, _dim > 2 ? _zmin : MIN}};
     267             : 
     268             :     // Number of elements in each direction.
     269         160 :     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         160 :     std::array<std::vector<Real>, LIBMESH_DIM> pows;
     274         640 :     for (unsigned int dir = 0; dir < LIBMESH_DIM; ++dir)
     275             :     {
     276         480 :       pows[dir].resize(nelem[dir] + 1);
     277         480 :       pows[dir][0] = 1.0;
     278        2000 :       for (unsigned int i = 1; i < pows[dir].size(); ++i)
     279        1520 :         pows[dir][i] = pows[dir][i - 1] * bias[dir];
     280             :     }
     281             : 
     282             :     // Loop over the nodes and move them to the desired location
     283        8600 :     for (auto & node_ptr : mesh->node_ptr_range())
     284             :     {
     285        8440 :       Node & node = *node_ptr;
     286             : 
     287       33760 :       for (unsigned int dir = 0; dir < LIBMESH_DIM; ++dir)
     288             :       {
     289       25320 :         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       14800 :           Real float_index = (node(dir) - mins[dir]) * nelem[dir] / width[dir];
     295             : 
     296       14800 :           Real integer_part = 0;
     297       14800 :           Real fractional_part = std::modf(float_index, &integer_part);
     298             : 
     299             :           // Figure out where to move the node...
     300       14800 :           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       14800 :             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       14800 :             node(dir) =
     316       14800 :                 mins[dir] + width[dir] * (1. - pows[dir][index]) / (1. - pows[dir][nelem[dir]]);
     317             :           }
     318           0 :           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           0 :             node(dir) = mins[dir] +
     330           0 :                         width[dir] *
     331           0 :                             (1. - 0.5 * (pows[dir][integer_part] + pows[dir][integer_part + 1])) /
     332           0 :                             (1. - pows[dir][nelem[dir]]);
     333             :           }
     334             :           else
     335             :           {
     336             :             // We don't yet handle anything higher order than quadratic...
     337           0 :             mooseError("Unable to bias node at node(", dir, ")=", node(dir));
     338             :           }
     339             :         }
     340             :       }
     341         160 :     }
     342         160 :   }
     343             : 
     344       17899 :   mesh->set_isnt_prepared();
     345       35798 :   return dynamic_pointer_cast<MeshBase>(mesh);
     346       17899 : }

Generated by: LCOV version 1.14