LCOV - code coverage report
Current view: top level - src/mesh - PETScDMDAMesh.C (source / functions) Hit Total Coverage
Test: idaholab/moose external_petsc_solver: #31405 (292dce) with base fef103 Lines: 90 128 70.3 %
Date: 2025-09-04 07:53:02 Functions: 6 12 50.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 "PETScDMDAMesh.h"
      11             : 
      12             : #include "libmesh/mesh_generation.h"
      13             : #include "libmesh/string_to_enum.h"
      14             : #include "libmesh/periodic_boundaries.h"
      15             : #include "libmesh/periodic_boundary_base.h"
      16             : #include "libmesh/unstructured_mesh.h"
      17             : #include "libmesh/partitioner.h"
      18             : #include "libmesh/metis_csr_graph.h"
      19             : #include "libmesh/edge_edge2.h"
      20             : #include "libmesh/edge_edge3.h"
      21             : #include "libmesh/edge_edge4.h"
      22             : #include "libmesh/mesh_communication.h"
      23             : #include "libmesh/remote_elem.h"
      24             : #include "libmesh/face_quad4.h"
      25             : #include "libmesh/cell_hex8.h"
      26             : #include "libmesh/petsc_solver_exception.h"
      27             : #include "ExternalPetscSolverApp.h"
      28             : // C++ includes
      29             : #include <cmath> // provides round, not std::round (see http://www.cplusplus.com/reference/cmath/round/)
      30             : 
      31             : registerMooseObject("ExternalPetscSolverApp", PETScDMDAMesh);
      32             : 
      33             : InputParameters
      34         740 : PETScDMDAMesh::validParams()
      35             : {
      36         740 :   InputParameters params = MooseMesh::validParams();
      37             : 
      38         740 :   params.addClassDescription("Create a square mesh from PETSc DMDA.");
      39             : 
      40             :   // This mesh is always distributed
      41        1480 :   params.set<MooseEnum>("parallel_type") = "DISTRIBUTED";
      42             : 
      43         740 :   return params;
      44           0 : }
      45             : 
      46         370 : PETScDMDAMesh::PETScDMDAMesh(const InputParameters & parameters) : MooseMesh(parameters)
      47             : {
      48             :   // Get TS from ExternalPetscSolverApp
      49         370 :   ExternalPetscSolverApp * petsc_app = dynamic_cast<ExternalPetscSolverApp *>(&_app);
      50             : 
      51         370 :   if (petsc_app && petsc_app->getPetscTS())
      52             :     // Retrieve mesh from TS
      53         370 :     LibmeshPetscCall(TSGetDM(petsc_app->getPetscTS(), &_dmda));
      54             :   else
      55           0 :     mooseError(" PETSc external solver TS does not exist or this is not a petsc external solver");
      56         370 : }
      57             : 
      58             : std::unique_ptr<MooseMesh>
      59           0 : PETScDMDAMesh::safeClone() const
      60             : {
      61           0 :   return _app.getFactory().copyConstruct(*this);
      62             : }
      63             : 
      64             : inline dof_id_type
      65             : node_id_Quad4(const dof_id_type nx,
      66             :               const dof_id_type /*ny*/,
      67             :               const dof_id_type i,
      68             :               const dof_id_type j,
      69             :               const dof_id_type /*k*/)
      70             : 
      71             : {
      72             :   // Transform a grid coordinate (i, j) to its global node ID
      73             :   // This match what PETSc does
      74       61200 :   return i + j * (nx + 1);
      75             : }
      76             : 
      77             : void
      78       15300 : add_element_Quad4(DM da,
      79             :                   const dof_id_type nx,
      80             :                   const dof_id_type ny,
      81             :                   const dof_id_type i,
      82             :                   const dof_id_type j,
      83             :                   const dof_id_type elem_id,
      84             :                   const processor_id_type pid,
      85             :                   MeshBase & mesh)
      86             : {
      87             :   BoundaryInfo & boundary_info = mesh.get_boundary_info();
      88             : 
      89             :   // Mx: number of grid points in x direction for all processors
      90             :   // My: number of grid points in y direction for all processors
      91             :   // xp: number of processors in x direction
      92             :   // yp: number of processors in y direction
      93             :   PetscInt Mx, My, xp, yp;
      94       15300 :   LibmeshPetscCallA(mesh.comm().get(),
      95             :                     DMDAGetInfo(da,
      96             :                                 PETSC_IGNORE,
      97             :                                 &Mx,
      98             :                                 &My,
      99             :                                 PETSC_IGNORE,
     100             :                                 &xp,
     101             :                                 &yp,
     102             :                                 PETSC_IGNORE,
     103             :                                 PETSC_IGNORE,
     104             :                                 PETSC_IGNORE,
     105             :                                 PETSC_IGNORE,
     106             :                                 PETSC_IGNORE,
     107             :                                 PETSC_IGNORE,
     108             :                                 PETSC_IGNORE));
     109             : 
     110             :   const PetscInt *lx, *ly;
     111             :   PetscInt *lxo, *lyo;
     112             :   // PETSc-3.8.x or older use PetscDataType
     113             : #if PETSC_VERSION_LESS_THAN(3, 9, 0)
     114             :   LibmeshPetscCallA(mesh.comm().get(), DMGetWorkArray(da, xp + yp + 2, PETSC_INT, &lxo));
     115             : #else
     116             :   // PETSc-3.9.x or newer use MPI_DataType
     117       15300 :   LibmeshPetscCallA(mesh.comm().get(), DMGetWorkArray(da, xp + yp + 2, MPIU_INT, &lxo));
     118             : #endif
     119             : 
     120             :   // Gets the ranges of indices in the x, y and z direction that are owned by each process
     121             :   // Ranges here are different from what we have in Mat and Vec.
     122             :   // It means how many points each processor holds
     123       15300 :   LibmeshPetscCallA(mesh.comm().get(), DMDAGetOwnershipRanges(da, &lx, &ly, NULL));
     124       15300 :   lxo[0] = 0;
     125       33000 :   for (PetscInt i = 0; i < xp; i++)
     126       17700 :     lxo[i + 1] = lxo[i] + lx[i];
     127             : 
     128       15300 :   lyo = lxo + xp + 1;
     129       15300 :   lyo[0] = 0;
     130       43800 :   for (PetscInt i = 0; i < yp; i++)
     131       28500 :     lyo[i + 1] = lyo[i] + ly[i];
     132             : 
     133             :   // Try to calculate processor-grid coordinate (xpid, ypid)
     134             :   PetscInt xpid, ypid, xpidplus, ypidplus;
     135             :   // Finds integer in a sorted array of integers
     136             :   // Loc:  the location if found, otherwise -(slot+1)
     137             :   // where slot is the place the value would go
     138       15300 :   LibmeshPetscCallA(mesh.comm().get(), PetscFindInt(i, xp + 1, lxo, &xpid));
     139             : 
     140       15300 :   xpid = xpid < 0 ? -xpid - 1 - 1 : xpid;
     141             : 
     142       15300 :   LibmeshPetscCallA(mesh.comm().get(), PetscFindInt(i + 1, xp + 1, lxo, &xpidplus));
     143             : 
     144       15300 :   xpidplus = xpidplus < 0 ? -xpidplus - 1 - 1 : xpidplus;
     145             : 
     146       15300 :   LibmeshPetscCallA(mesh.comm().get(), PetscFindInt(j, yp + 1, lyo, &ypid));
     147             : 
     148       15300 :   ypid = ypid < 0 ? -ypid - 1 - 1 : ypid;
     149             : 
     150       15300 :   LibmeshPetscCallA(mesh.comm().get(), PetscFindInt(j + 1, yp + 1, lyo, &ypidplus));
     151             : 
     152       15300 :   ypidplus = ypidplus < 0 ? -ypidplus - 1 - 1 : ypidplus;
     153             : #if PETSC_VERSION_LESS_THAN(3, 9, 0)
     154             :   LibmeshPetscCallA(mesh.comm().get(), DMRestoreWorkArray(da, xp + yp + 2, PETSC_INT, &lxo));
     155             : #else
     156       15300 :   LibmeshPetscCallA(mesh.comm().get(), DMRestoreWorkArray(da, xp + yp + 2, MPIU_INT, &lxo));
     157             : #endif
     158             : 
     159             :   // Bottom Left
     160       15300 :   auto node0_ptr = mesh.add_point(Point(static_cast<Real>(i) / nx, static_cast<Real>(j) / ny, 0),
     161             :                                   node_id_Quad4(nx, 0, i, j, 0));
     162             :   node0_ptr->set_unique_id(node_id_Quad4(nx, 0, i, j, 0));
     163       15300 :   node0_ptr->set_id() = node0_ptr->unique_id();
     164             :   // xpid + ypid * xp is the global processor ID
     165       15300 :   node0_ptr->processor_id() = xpid + ypid * xp;
     166             : 
     167             :   // Bottom Right
     168             :   auto node1_ptr =
     169       15300 :       mesh.add_point(Point(static_cast<Real>(i + 1) / nx, static_cast<Real>(j) / ny, 0),
     170             :                      node_id_Quad4(nx, 0, i + 1, j, 0));
     171             :   node1_ptr->set_unique_id(node_id_Quad4(nx, 0, i + 1, j, 0));
     172       15300 :   node1_ptr->set_id() = node1_ptr->unique_id();
     173       15300 :   node1_ptr->processor_id() = xpidplus + ypid * xp;
     174             : 
     175             :   // Top Right
     176             :   auto node2_ptr =
     177       15300 :       mesh.add_point(Point(static_cast<Real>(i + 1) / nx, static_cast<Real>(j + 1) / ny, 0),
     178             :                      node_id_Quad4(nx, 0, i + 1, j + 1, 0));
     179             :   node2_ptr->set_unique_id(node_id_Quad4(nx, 0, i + 1, j + 1, 0));
     180       15300 :   node2_ptr->set_id() = node2_ptr->unique_id();
     181       15300 :   node2_ptr->processor_id() = xpidplus + ypidplus * xp;
     182             : 
     183             :   // Top Left
     184             :   auto node3_ptr =
     185       15300 :       mesh.add_point(Point(static_cast<Real>(i) / nx, static_cast<Real>(j + 1) / ny, 0),
     186             :                      node_id_Quad4(nx, 0, i, j + 1, 0));
     187             :   node3_ptr->set_unique_id(node_id_Quad4(nx, 0, i, j + 1, 0));
     188       15300 :   node3_ptr->set_id() = node3_ptr->unique_id();
     189       15300 :   node3_ptr->processor_id() = xpid + ypidplus * xp;
     190             : 
     191             :   // New an element and attach four nodes to it
     192       15300 :   Elem * elem = new Quad4;
     193             :   elem->set_id(elem_id);
     194       15300 :   elem->processor_id() = pid;
     195             :   // Make sure our unique_id doesn't overlap any nodes'
     196       15300 :   elem->set_unique_id(elem_id + (nx + 1) * (ny + 1));
     197       15300 :   elem = mesh.add_elem(elem);
     198       15300 :   elem->set_node(0, node0_ptr);
     199       15300 :   elem->set_node(1, node1_ptr);
     200       15300 :   elem->set_node(2, node2_ptr);
     201       15300 :   elem->set_node(3, node3_ptr);
     202             : 
     203             :   // Bottom
     204       15300 :   if (j == 0)
     205        1530 :     boundary_info.add_side(elem, 0, 0);
     206             : 
     207             :   // Right
     208       15300 :   if (i == nx - 1)
     209        1530 :     boundary_info.add_side(elem, 1, 1);
     210             : 
     211             :   // Top
     212       15300 :   if (j == ny - 1)
     213        1530 :     boundary_info.add_side(elem, 2, 2);
     214             : 
     215             :   // Left
     216       15300 :   if (i == 0)
     217        1530 :     boundary_info.add_side(elem, 3, 3);
     218       15300 : }
     219             : 
     220             : void
     221         345 : set_boundary_names_Quad4(BoundaryInfo & boundary_info)
     222             : {
     223         345 :   boundary_info.sideset_name(0) = "bottom";
     224         345 :   boundary_info.sideset_name(1) = "right";
     225         345 :   boundary_info.sideset_name(2) = "top";
     226         345 :   boundary_info.sideset_name(3) = "left";
     227         345 : }
     228             : 
     229             : void
     230           0 : get_indices_Quad4(const dof_id_type nx,
     231             :                   const dof_id_type /*ny*/,
     232             :                   const dof_id_type elem_id,
     233             :                   dof_id_type & i,
     234             :                   dof_id_type & j)
     235             : {
     236           0 :   i = elem_id % nx;
     237           0 :   j = (elem_id - i) / nx;
     238           0 : }
     239             : 
     240             : dof_id_type
     241           0 : elem_id_Quad4(const dof_id_type nx,
     242             :               const dof_id_type /*nx*/,
     243             :               const dof_id_type i,
     244             :               const dof_id_type j,
     245             :               const dof_id_type /*k*/)
     246             : {
     247           0 :   return (j * nx) + i;
     248             : }
     249             : 
     250             : inline void
     251           0 : get_neighbors_Quad4(const dof_id_type nx,
     252             :                     const dof_id_type ny,
     253             :                     const dof_id_type i,
     254             :                     const dof_id_type j,
     255             :                     std::vector<dof_id_type> & neighbors)
     256             : {
     257             :   std::fill(neighbors.begin(), neighbors.end(), Elem::invalid_id);
     258             : 
     259             :   // Bottom
     260           0 :   if (j != 0)
     261           0 :     neighbors[0] = elem_id_Quad4(nx, 0, i, j - 1, 0);
     262             : 
     263             :   // Right
     264           0 :   if (i != nx - 1)
     265           0 :     neighbors[1] = elem_id_Quad4(nx, 0, i + 1, j, 0);
     266             : 
     267             :   // Top
     268           0 :   if (j != ny - 1)
     269           0 :     neighbors[2] = elem_id_Quad4(nx, 0, i, j + 1, 0);
     270             : 
     271             :   // Left
     272           0 :   if (i != 0)
     273           0 :     neighbors[3] = elem_id_Quad4(nx, 0, i - 1, j, 0);
     274           0 : }
     275             : 
     276             : void
     277           0 : get_ghost_neighbors_Quad4(const dof_id_type nx,
     278             :                           const dof_id_type ny,
     279             :                           const MeshBase & mesh,
     280             :                           std::set<dof_id_type> & ghost_elems)
     281             : {
     282             :   auto & boundary_info = mesh.get_boundary_info();
     283             : 
     284             :   dof_id_type i, j;
     285             : 
     286           0 :   std::vector<dof_id_type> neighbors(4);
     287             : 
     288           0 :   for (auto elem_ptr : mesh.element_ptr_range())
     289             :   {
     290           0 :     for (unsigned int s = 0; s < elem_ptr->n_sides(); s++)
     291             :     {
     292             :       // No current neighbor
     293           0 :       if (!elem_ptr->neighbor_ptr(s))
     294             :       {
     295             :         // Not on a boundary
     296           0 :         if (!boundary_info.n_boundary_ids(elem_ptr, s))
     297             :         {
     298             :           auto elem_id = elem_ptr->id();
     299             : 
     300           0 :           get_indices_Quad4(nx, 0, elem_id, i, j);
     301             : 
     302           0 :           get_neighbors_Quad4(nx, ny, i, j, neighbors);
     303             : 
     304           0 :           ghost_elems.insert(neighbors[s]);
     305             :         }
     306             :       }
     307             :     }
     308           0 :   }
     309           0 : }
     310             : 
     311             : void
     312           0 : add_node_Qua4(dof_id_type nx,
     313             :               dof_id_type ny,
     314             :               dof_id_type i,
     315             :               dof_id_type j,
     316             :               processor_id_type pid,
     317             :               MeshBase & mesh)
     318             : {
     319             :   // Bottom Left
     320           0 :   auto node0_ptr = mesh.add_point(Point(static_cast<Real>(i) / nx, static_cast<Real>(j) / ny, 0),
     321             :                                   node_id_Quad4(nx, 0, i, j, 0));
     322             :   node0_ptr->set_unique_id(node_id_Quad4(nx, 0, i, j, 0));
     323           0 :   node0_ptr->processor_id() = pid;
     324           0 : }
     325             : 
     326             : void
     327         345 : build_cube_Quad4(UnstructuredMesh & mesh, DM da)
     328             : {
     329             :   const auto pid = mesh.comm().rank();
     330             : 
     331             :   BoundaryInfo & boundary_info = mesh.get_boundary_info();
     332             :   // xs: start grid point (not element) index on local in x direction
     333             :   // ys: start grid point index on local in y direction
     334             :   // xm: number of grid points owned by the local processor in x direction
     335             :   // ym: number of grid points owned by the local processor in y direction
     336             :   // Mx: number of grid points on all processors in x direction
     337             :   // My: number of grid points on all processors in y direction
     338             :   // xp: number of processor cores in x direction
     339             :   // yp: number of processor cores in y direction
     340             :   PetscInt xs, ys, xm, ym, Mx, My, xp, yp;
     341             : 
     342             :   /* Get local grid boundaries */
     343         345 :   LibmeshPetscCallA(mesh.comm().get(),
     344             :                     DMDAGetCorners(da, &xs, &ys, PETSC_IGNORE, &xm, &ym, PETSC_IGNORE));
     345         345 :   LibmeshPetscCallA(mesh.comm().get(),
     346             :                     DMDAGetInfo(da,
     347             :                                 PETSC_IGNORE,
     348             :                                 &Mx,
     349             :                                 &My,
     350             :                                 PETSC_IGNORE,
     351             :                                 &xp,
     352             :                                 &yp,
     353             :                                 PETSC_IGNORE,
     354             :                                 PETSC_IGNORE,
     355             :                                 PETSC_IGNORE,
     356             :                                 PETSC_IGNORE,
     357             :                                 PETSC_IGNORE,
     358             :                                 PETSC_IGNORE,
     359             :                                 PETSC_IGNORE));
     360             : 
     361        2292 :   for (PetscInt j = ys; j < ys + ym; j++)
     362       20460 :     for (PetscInt i = xs; i < xs + xm; i++)
     363             :     {
     364             :       // We loop over grid points, but we are
     365             :       // here building elements. So that we just
     366             :       // simply skip the first x and y points since the
     367             :       // number of grid ponts is one more than
     368             :       // the number of grid elements
     369       18513 :       if (!i || !j)
     370        3213 :         continue;
     371             : 
     372       15300 :       dof_id_type ele_id = (i - 1) + (j - 1) * (Mx - 1);
     373             : 
     374       15300 :       add_element_Quad4(da, Mx - 1, My - 1, i - 1, j - 1, ele_id, pid, mesh);
     375             :     }
     376             : 
     377             :   // If there is no element at the given processor
     378             :   // We need to manually add all mesh nodes
     379         345 :   if ((ys == 0 && ym == 1) || (xs == 0 && xm == 1))
     380           0 :     for (PetscInt j = ys; j < ys + ym; j++)
     381           0 :       for (PetscInt i = xs; i < xs + xm; i++)
     382           0 :         add_node_Qua4(Mx, My, i, j, pid, mesh);
     383             : 
     384             :   // Need to link up the local elements before we can know what's missing
     385         345 :   mesh.find_neighbors();
     386             : 
     387         345 :   mesh.find_neighbors(true);
     388             : 
     389             :   // Set RemoteElem neighbors
     390       31290 :   for (auto & elem_ptr : mesh.element_ptr_range())
     391       76500 :     for (unsigned int s = 0; s < elem_ptr->n_sides(); s++)
     392       61200 :       if (!elem_ptr->neighbor_ptr(s) && !boundary_info.n_boundary_ids(elem_ptr, s))
     393        3465 :         elem_ptr->set_neighbor(s, const_cast<RemoteElem *>(remote_elem));
     394             : 
     395         345 :   set_boundary_names_Quad4(boundary_info);
     396             :   // Already partitioned!
     397             :   mesh.skip_partitioning(true);
     398             : 
     399             :   // We've already set our own unique_id values; now make sure that
     400             :   // future mesh modifications use subsequent values.
     401         345 :   mesh.set_next_unique_id(Mx * My + (Mx - 1) * (My - 1));
     402             : 
     403             :   // No need to renumber or find neighbors - done did it.
     404             :   mesh.allow_renumbering(false);
     405             :   mesh.allow_find_neighbors(false);
     406         345 :   mesh.prepare_for_use();
     407             :   mesh.allow_find_neighbors(true);
     408         345 : }
     409             : 
     410             : void
     411         345 : PETScDMDAMesh::buildMesh()
     412             : {
     413             :   // Reference to the libmesh mesh
     414         345 :   MeshBase & mesh = getMesh();
     415             : 
     416         345 :   build_cube_Quad4(dynamic_cast<UnstructuredMesh &>(mesh), _dmda);
     417         345 : }

Generated by: LCOV version 1.14