LCOV - code coverage report
Current view: top level - src/partitioner - HierarchicalGridPartitioner.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 129 146 88.4 %
Date: 2025-07-17 01:28:37 Functions: 6 6 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 "HierarchicalGridPartitioner.h"
      11             : 
      12             : #include "GeneratedMesh.h"
      13             : #include "MooseApp.h"
      14             : 
      15             : #include "libmesh/elem.h"
      16             : #include "libmesh/mesh_tools.h"
      17             : #include "libmesh/replicated_mesh.h"
      18             : #include "libmesh/mesh_generation.h"
      19             : #include "libmesh/linear_partitioner.h"
      20             : 
      21             : registerMooseObject("MooseApp", HierarchicalGridPartitioner);
      22             : 
      23             : #include <memory>
      24             : 
      25             : InputParameters
      26       15977 : HierarchicalGridPartitioner::validParams()
      27             : {
      28       15977 :   auto params = MoosePartitioner::validParams();
      29             : 
      30             :   // Options for automatic grid computations
      31       15977 :   MooseEnum method("manual automatic", "manual");
      32       15977 :   params.addParam<MooseEnum>(
      33             :       "nodes_grid_computation",
      34             :       method,
      35             :       "Whether to determine the compute node grid manually (using nx_nodes, ny_nodes and nz_nodes) "
      36             :       "or automatically. When using the automatic mode, the user can impose a certain value for "
      37             :       "nx, ny or nz, and the automatic factorization will adjust the number of processors in the "
      38             :       "other directions.");
      39       15977 :   params.addParam<unsigned int>(
      40             :       "number_nodes", "Number of nodes. Used for determining the node grid automatically");
      41       15977 :   params.addParam<MooseEnum>(
      42             :       "processors_grid_computation",
      43             :       method,
      44             :       "Whether to determine the processors grid on each node manually (using nx_procs, ny_procs "
      45             :       "and nz_procs) or automatically. When using the automatic mode, the user can impose a "
      46             :       "certain value for nx, ny or nz, and the automatic factorization will adjust the number of "
      47             :       "processors in the other directions.");
      48       15977 :   params.addParam<unsigned int>("number_procs_per_node",
      49             :                                 "Number of processors per node. Used for determining the processor "
      50             :                                 "grid on each node automatically");
      51             : 
      52             :   // Node grid
      53       15977 :   params.addRangeCheckedParam<unsigned int>(
      54             :       "nx_nodes", "nx_nodes >= 1", "Number of compute nodes in the X direction");
      55       15977 :   params.addRangeCheckedParam<unsigned int>(
      56             :       "ny_nodes", "ny_nodes >= 1", "Number of compute nodes in the Y direction");
      57       15977 :   params.addRangeCheckedParam<unsigned int>(
      58             :       "nz_nodes", "nz_nodes >= 1", "Number of compute nodes in the Z direction");
      59             : 
      60             :   // Processor grid on each node
      61       15977 :   params.addRangeCheckedParam<unsigned int>(
      62             :       "nx_procs", "nx_procs >= 1", "Number of processors in the X direction within each node");
      63       15977 :   params.addRangeCheckedParam<unsigned int>(
      64             :       "ny_procs", "ny_procs >= 1", "Number of processors in the Y direction within each node");
      65       15977 :   params.addRangeCheckedParam<unsigned int>(
      66             :       "nz_procs", "nz_procs >= 1", "Number of processors in the Z direction within each node");
      67             : 
      68       15977 :   params.addClassDescription("Partitions a mesh into sub-partitions for each computational node"
      69             :                              " then into partitions within that node.  All partitions are made"
      70             :                              " using a regular grid.");
      71             : 
      72       31954 :   return params;
      73       15977 : }
      74             : 
      75        1292 : HierarchicalGridPartitioner::HierarchicalGridPartitioner(const InputParameters & params)
      76        1292 :   : MoosePartitioner(params), _mesh(*getCheckedPointerParam<MooseMesh *>("mesh"))
      77             : {
      78        1292 : }
      79             : 
      80        2584 : HierarchicalGridPartitioner::~HierarchicalGridPartitioner() {}
      81             : 
      82             : std::unique_ptr<Partitioner>
      83         872 : HierarchicalGridPartitioner::clone() const
      84             : {
      85         872 :   return _app.getFactory().clone(*this);
      86             : }
      87             : 
      88             : void
      89         400 : HierarchicalGridPartitioner::_do_partition(MeshBase & mesh, const unsigned int /*n*/)
      90             : {
      91         400 :   const auto dim = mesh.spatial_dimension();
      92             : 
      93             :   // Process user parameters
      94         400 :   _nx_nodes = isParamValid("nx_nodes") ? getParam<unsigned int>("nx_nodes") : 0;
      95         400 :   _ny_nodes = isParamValid("ny_nodes") ? getParam<unsigned int>("ny_nodes") : 0;
      96         400 :   _nz_nodes = isParamValid("nz_nodes") ? getParam<unsigned int>("nz_nodes") : 0;
      97         400 :   _nx_procs = isParamValid("nx_procs") ? getParam<unsigned int>("nx_procs") : 0;
      98         400 :   _ny_procs = isParamValid("ny_procs") ? getParam<unsigned int>("ny_procs") : 0;
      99         400 :   _nz_procs = isParamValid("nz_procs") ? getParam<unsigned int>("nz_procs") : 0;
     100             : 
     101         400 :   if (getParam<MooseEnum>("nodes_grid_computation") == "manual")
     102             :   {
     103         288 :     if (_nx_nodes == 0)
     104           0 :       paramError("nx_nodes", "Required for manual nodes grid specification");
     105         288 :     if (_ny_nodes == 0 && dim > 1)
     106           0 :       paramError("ny_nodes", "Required for ", dim, "D meshes");
     107         288 :     if (_nz_nodes == 0 && dim == 3)
     108           0 :       paramError("nz_nodes", "Required for 3D meshes");
     109             :   }
     110             :   else
     111             :   {
     112         112 :     if (!isParamValid("number_nodes"))
     113           0 :       paramError("number_nodes", "Required for automatic nodes grid computation");
     114             :     // 0 means no restriction on which number to choose
     115         112 :     int dims[] = {int(_nx_nodes), int(_ny_nodes), int(_nz_nodes)};
     116             :     // This will error if the factorization is not possible
     117         112 :     MPI_Dims_create(getParam<unsigned int>("number_nodes"), dim, dims);
     118             : 
     119         112 :     _nx_nodes = dims[0];
     120         112 :     _ny_nodes = (dim >= 2) ? dims[1] : 0;
     121         112 :     _nz_nodes = (dim == 3) ? dims[2] : 0;
     122             :   }
     123             : 
     124         400 :   if (getParam<MooseEnum>("processors_grid_computation") == "manual")
     125             :   {
     126         288 :     if (_nx_procs == 0)
     127           0 :       paramError("nx_procs", "Required for manual processors grid specification");
     128         288 :     if (_ny_procs == 0 && dim > 1)
     129           0 :       paramError("ny_procs", "Required for ", dim, "D meshes");
     130         288 :     if (_nz_procs == 0 && dim == 3)
     131           0 :       paramError("nz_procs", "Required for 3D meshes");
     132             :   }
     133             :   else
     134             :   {
     135         112 :     if (!isParamValid("number_procs_per_node"))
     136           0 :       paramError("number_procs_per_node", "Required for automatic processors grid computation");
     137             :     // 0 means no restriction on which number to choose
     138         112 :     int dims[] = {int(_nx_procs), int(_ny_procs), int(_nz_procs)};
     139             :     // This will error if the factorization is not possible
     140         112 :     MPI_Dims_create(getParam<unsigned int>("number_procs_per_node"), dim, dims);
     141             : 
     142         112 :     _nx_procs = dims[0];
     143         112 :     _ny_procs = (dim >= 2) ? dims[1] : 0;
     144         112 :     _nz_procs = (dim == 3) ? dims[2] : 0;
     145             :   }
     146             : 
     147             :   // Sanity checks on both grids
     148         400 :   auto total_nodes = _nx_nodes;
     149         400 :   if (mesh.spatial_dimension() >= 2)
     150         400 :     total_nodes *= _ny_nodes;
     151         400 :   if (mesh.spatial_dimension() == 3)
     152          64 :     total_nodes *= _nz_nodes;
     153         400 :   if (isParamValid("number_nodes") && total_nodes != getParam<unsigned int>("number_nodes") &&
     154           0 :       processor_id() == 0)
     155           0 :     paramError("number_nodes",
     156           0 :                "Computed number of nodes (" + std::to_string(total_nodes) + ") does not match");
     157             : 
     158         400 :   auto procs_per_node = _nx_procs;
     159         400 :   if (mesh.spatial_dimension() >= 2)
     160         400 :     procs_per_node *= _ny_procs;
     161         400 :   if (mesh.spatial_dimension() == 3)
     162          64 :     procs_per_node *= _nz_procs;
     163         800 :   if (isParamValid("number_procs_per_node") &&
     164         800 :       procs_per_node != getParam<unsigned int>("number_procs_per_node") && processor_id() == 0)
     165           0 :     paramError("number_procs_per_node",
     166           0 :                "Computed number of processors per node (" + std::to_string(procs_per_node) +
     167             :                    ") does not match");
     168             : 
     169         400 :   if (procs_per_node * total_nodes != mesh.n_partitions() && processor_id() == 0)
     170           0 :     mooseError("Partitioning creates ",
     171           0 :                procs_per_node * total_nodes,
     172             :                " partitions, which does not add up to the total number of processors: ",
     173           0 :                mesh.n_partitions());
     174             : 
     175             :   // Figure out the physical bounds of the given mesh
     176         400 :   auto nodes_bounding_box = MeshTools::create_bounding_box(mesh);
     177         400 :   const auto & nodes_min = nodes_bounding_box.min();
     178         400 :   const auto & nodes_max = nodes_bounding_box.max();
     179             : 
     180             :   // Bound the coarse mesh (n_nodes * n_nodes)
     181         400 :   auto nodes_mesh = std::make_unique<ReplicatedMesh>(this->_communicator);
     182         400 :   nodes_mesh->partitioner() = std::make_unique<libMesh::LinearPartitioner>();
     183             : 
     184         400 :   if (mesh.spatial_dimension() == 2)
     185         336 :     MeshTools::Generation::build_cube(*nodes_mesh,
     186             :                                       _nx_nodes,
     187             :                                       _ny_nodes,
     188             :                                       _nz_nodes,
     189         336 :                                       nodes_min(0),
     190         336 :                                       nodes_max(0),
     191         336 :                                       nodes_min(1),
     192         336 :                                       nodes_max(1),
     193         336 :                                       nodes_min(2),
     194         336 :                                       nodes_max(2),
     195             :                                       QUAD4);
     196             :   else
     197          64 :     MeshTools::Generation::build_cube(*nodes_mesh,
     198             :                                       _nx_nodes,
     199             :                                       _ny_nodes,
     200             :                                       _nz_nodes,
     201          64 :                                       nodes_min(0),
     202          64 :                                       nodes_max(0),
     203          64 :                                       nodes_min(1),
     204          64 :                                       nodes_max(1),
     205          64 :                                       nodes_min(2),
     206          64 :                                       nodes_max(2),
     207             :                                       HEX8);
     208             : 
     209             :   // Now build the procs meshes
     210         400 :   std::vector<std::unique_ptr<ReplicatedMesh>> procs_meshes(nodes_mesh->n_elem());
     211             : 
     212        3344 :   for (const auto & elem_ptr : nodes_mesh->active_element_ptr_range())
     213             :   {
     214             :     // Need to find the bounds of the elem
     215             :     Point min(std::numeric_limits<Real>::max(),
     216             :               std::numeric_limits<Real>::max(),
     217        1472 :               std::numeric_limits<Real>::max());
     218        1472 :     Point max(-std::numeric_limits<Real>::max(),
     219        1472 :               -std::numeric_limits<Real>::max(),
     220        1472 :               -std::numeric_limits<Real>::max());
     221             : 
     222             :     // Loop over all the nodes
     223        7872 :     for (const auto & node : elem_ptr->node_ref_range())
     224             :     {
     225        6400 :       min(0) = std::min(min(0), node(0));
     226        6400 :       min(1) = std::min(min(1), node(1));
     227        6400 :       min(2) = std::min(min(2), node(2));
     228             : 
     229        6400 :       max(0) = std::max(max(0), node(0));
     230        6400 :       max(1) = std::max(max(1), node(1));
     231        6400 :       max(2) = std::max(max(2), node(2));
     232             :     }
     233             : 
     234        1472 :     auto procs_mesh = std::make_unique<ReplicatedMesh>(this->_communicator);
     235        1472 :     procs_mesh->partitioner() = std::make_unique<libMesh::LinearPartitioner>();
     236             : 
     237        1472 :     if (mesh.spatial_dimension() == 2)
     238        1344 :       MeshTools::Generation::build_cube(*procs_mesh,
     239             :                                         _nx_procs,
     240             :                                         _ny_procs,
     241             :                                         _nz_procs,
     242        1344 :                                         min(0),
     243        1344 :                                         max(0),
     244        1344 :                                         min(1),
     245        1344 :                                         max(1),
     246        1344 :                                         min(2),
     247        1344 :                                         max(2),
     248             :                                         QUAD4);
     249             :     else
     250         128 :       MeshTools::Generation::build_cube(*procs_mesh,
     251             :                                         _nx_procs,
     252             :                                         _ny_procs,
     253             :                                         _nz_procs,
     254         128 :                                         min(0),
     255         128 :                                         max(0),
     256         128 :                                         min(1),
     257         128 :                                         max(1),
     258         128 :                                         min(2),
     259         128 :                                         max(2),
     260             :                                         HEX8);
     261             : 
     262        1472 :     procs_meshes[elem_ptr->id()] = std::move(procs_mesh);
     263        1872 :   }
     264             : 
     265         400 :   auto nodes_point_locator_ptr = nodes_mesh->sub_point_locator();
     266             : 
     267         400 :   std::vector<std::unique_ptr<libMesh::PointLocatorBase>> procs_point_locators(procs_meshes.size());
     268             : 
     269        1872 :   for (unsigned int i = 0; i < procs_meshes.size(); i++)
     270        1472 :     procs_point_locators[i] = procs_meshes[i]->sub_point_locator();
     271             : 
     272             :   // Loop over all of the elements in the given mesh
     273      107408 :   for (auto & elem_ptr : mesh.active_element_ptr_range())
     274             :   {
     275       53504 :     auto elem_centroid = elem_ptr->vertex_average();
     276             : 
     277             :     // Find the element it lands in in the Nodes mesh
     278       53504 :     auto nodes_elem_ptr = (*nodes_point_locator_ptr)(elem_centroid);
     279             : 
     280       53504 :     auto nodes_elem_id = nodes_elem_ptr->id();
     281             : 
     282             :     // True if we found something
     283       53504 :     if (nodes_elem_ptr)
     284             :     {
     285             :       // Now see where it lands within the procs mesh of that node
     286       53504 :       auto procs_elem_ptr = (*procs_point_locators[nodes_elem_id])(elem_centroid);
     287             : 
     288             :       // Assign the _id_ of the cell to the processor_id (plus an offset for which node we're in)
     289       53504 :       elem_ptr->processor_id() = procs_elem_ptr->id() + (nodes_elem_id * procs_per_node);
     290             :     }
     291             :     else // Should never happen (seriously - we create bounding boxes that should disallow this!)
     292           0 :       mooseError("HierarchicalGridPartitioner unable to locate element within the grid!");
     293         400 :   }
     294         400 : }

Generated by: LCOV version 1.14