LCOV - code coverage report
Current view: top level - src/numerics - petsc_preconditioner.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4476 (4beb67) with base a68cc6 Lines: 139 216 64.4 %
Date: 2026-06-03 20:22:46 Functions: 4 9 44.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // The libMesh Finite Element Library.
       2             : // Copyright (C) 2002-2026 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
       3             : 
       4             : // This library is free software; you can redistribute it and/or
       5             : // modify it under the terms of the GNU Lesser General Public
       6             : // License as published by the Free Software Foundation; either
       7             : // version 2.1 of the License, or (at your option) any later version.
       8             : 
       9             : // This library is distributed in the hope that it will be useful,
      10             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12             : // Lesser General Public License for more details.
      13             : 
      14             : // You should have received a copy of the GNU Lesser General Public
      15             : // License along with this library; if not, write to the Free Software
      16             : // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
      17             : 
      18             : #include "libmesh/libmesh_common.h"
      19             : 
      20             : #ifdef LIBMESH_HAVE_PETSC
      21             : 
      22             : // Local Includes
      23             : #include "libmesh/petsc_preconditioner.h"
      24             : #include "libmesh/petsc_macro.h"
      25             : #include "libmesh/petsc_matrix.h"
      26             : #include "libmesh/petsc_vector.h"
      27             : #include "libmesh/libmesh_common.h"
      28             : #include "libmesh/enum_preconditioner_type.h"
      29             : #include "libmesh/elem.h"
      30             : #include "libmesh/equation_systems.h"
      31             : #include "libmesh/dof_map.h"
      32             : 
      33             : namespace libMesh
      34             : {
      35             : 
      36             : template <typename T>
      37           0 : PetscPreconditioner<T>::PetscPreconditioner (const libMesh::Parallel::Communicator & comm_in) :
      38           0 :   Preconditioner<T>(comm_in)
      39           0 : {}
      40             : 
      41             : 
      42             : 
      43             : template <typename T>
      44           0 : void PetscPreconditioner<T>::apply(const NumericVector<T> & x, NumericVector<T> & y)
      45             : {
      46           0 :   PetscVector<T> & x_pvec = cast_ref<PetscVector<T> &>(const_cast<NumericVector<T> &>(x));
      47           0 :   PetscVector<T> & y_pvec = cast_ref<PetscVector<T> &>(const_cast<NumericVector<T> &>(y));
      48             : 
      49           0 :   Vec x_vec = x_pvec.vec();
      50           0 :   Vec y_vec = y_pvec.vec();
      51             : 
      52           0 :   LibmeshPetscCall(PCApply(_pc, x_vec, y_vec));
      53           0 : }
      54             : 
      55             : 
      56             : 
      57             : 
      58             : template <typename T>
      59           0 : void PetscPreconditioner<T>::init ()
      60             : {
      61           0 :   libmesh_error_msg_if(!this->_matrix, "ERROR: No matrix set for PetscPreconditioner, but init() called");
      62             : 
      63             :   // Clear the preconditioner in case it has been created in the past
      64           0 :   if (!this->_is_initialized)
      65             :     {
      66             :       // Should probably use PCReset(), but it's not working at the moment so we'll destroy instead
      67           0 :       if (_pc)
      68           0 :         _pc.destroy();
      69             : 
      70           0 :       LibmeshPetscCall(PCCreate(this->comm().get(), _pc.get()));
      71             : 
      72           0 :       auto pmatrix = cast_ptr<PetscMatrixBase<T> *>(this->_matrix);
      73           0 :       _mat = pmatrix->mat();
      74             :     }
      75             : 
      76           0 :   LibmeshPetscCall(PCSetOperators(_pc, _mat, _mat));
      77             : 
      78             :   // Set the PCType.  Note: this used to be done *before* the call to
      79             :   // PCSetOperators(), and only when !_is_initialized, but
      80             :   // 1.) Some preconditioners (those employing sub-preconditioners,
      81             :   // for example) have to call PCSetUp(), and can only do this after
      82             :   // the operators have been set.
      83             :   // 2.) It should be safe to call set_petsc_preconditioner_type()
      84             :   // multiple times.
      85           0 :   set_petsc_preconditioner_type(this->_preconditioner_type, *_pc);
      86             : 
      87           0 :   this->_is_initialized = true;
      88           0 : }
      89             : 
      90             : 
      91             : 
      92             : template <typename T>
      93           0 : void PetscPreconditioner<T>::clear()
      94             : {
      95             :   // Calls custom deleter
      96           0 :   _pc.destroy();
      97           0 : }
      98             : 
      99             : 
     100             : 
     101             : template <typename T>
     102           0 : PC PetscPreconditioner<T>::pc()
     103             : {
     104           0 :   return _pc;
     105             : }
     106             : 
     107             : 
     108             : 
     109             : template <typename T>
     110       55429 : void PetscPreconditioner<T>::set_petsc_preconditioner_type (const PreconditionerType & preconditioner_type, PC & pc)
     111             : {
     112             :   // get the communicator from the PETSc object
     113             :   Parallel::communicator comm;
     114       55429 :   PetscErrorCode ierr = PetscObjectGetComm((PetscObject)pc, & comm);
     115       55429 :   if (ierr != LIBMESH_PETSC_SUCCESS)
     116           0 :     libmesh_error_msg("Error retrieving communicator");
     117             : 
     118             :   #define CasePCSetType(PreconditionerType, PCType)                       \
     119             :   case PreconditionerType:                                                \
     120             :     LibmeshPetscCallA(comm, PCSetType (pc, const_cast<KSPType>(PCType))); \
     121             :     break;
     122             : 
     123       55429 :   switch (preconditioner_type)
     124             :     {
     125         140 :     CasePCSetType(IDENTITY_PRECOND,     PCNONE)
     126           0 :     CasePCSetType(CHOLESKY_PRECOND,     PCCHOLESKY)
     127           0 :     CasePCSetType(ICC_PRECOND,          PCICC)
     128         733 :     CasePCSetType(ILU_PRECOND,          PCILU)
     129           0 :     CasePCSetType(LU_PRECOND,           PCLU)
     130           0 :     CasePCSetType(ASM_PRECOND,          PCASM)
     131           0 :     CasePCSetType(JACOBI_PRECOND,       PCJACOBI)
     132       50706 :     CasePCSetType(BLOCK_JACOBI_PRECOND, PCBJACOBI)
     133           0 :     CasePCSetType(SOR_PRECOND,          PCSOR)
     134           0 :     CasePCSetType(EISENSTAT_PRECOND,    PCEISENSTAT)
     135           0 :     CasePCSetType(AMG_PRECOND,          PCHYPRE)
     136           0 :     CasePCSetType(SVD_PRECOND,          PCSVD)
     137           0 :     CasePCSetType(USER_PRECOND,         PCMAT)
     138        3850 :     CasePCSetType(SHELL_PRECOND,        PCSHELL)
     139             : 
     140           0 :     default:
     141           0 :       libMesh::err << "ERROR:  Unsupported PETSC Preconditioner: "
     142           0 :                    << Utility::enum_to_string(preconditioner_type) << std::endl
     143           0 :                    << "Continuing with PETSC defaults" << std::endl;
     144             :     }
     145             : 
     146             :   // Set additional options if we are doing AMG and
     147             :   // HYPRE is available
     148             : #ifdef LIBMESH_HAVE_PETSC_HYPRE
     149       55429 :   if (preconditioner_type == AMG_PRECOND)
     150           0 :     LibmeshPetscCallA(comm, PCHYPRESetType(pc, "boomeramg"));
     151             : #endif
     152             : 
     153             :   // Let the commandline override stuff
     154       55429 :   LibmeshPetscCallA(comm, PCSetFromOptions(pc));
     155       55429 : }
     156             : 
     157             : 
     158             : 
     159             : #ifdef LIBMESH_HAVE_PETSC_HYPRE
     160             : template <typename T>
     161      259204 : void PetscPreconditioner<T>::set_petsc_aux_data(PC & pc, System & sys, const unsigned v)
     162             : {
     163             :   // Get the communicator from the PETSc object
     164             :   Parallel::communicator comm;
     165      259204 :   PetscErrorCode ierr = PetscObjectGetComm((PetscObject)pc, &comm);
     166      259204 :   libmesh_error_msg_if(ierr != LIBMESH_PETSC_SUCCESS,
     167             :                        "Error retrieving communicator");
     168             : 
     169             :   // Make sure the preconditioner options are set
     170      259204 :   LibmeshPetscCallA(comm, PCSetFromOptions(pc));
     171             : 
     172             :   // Get the type of preconditioner we are using
     173      259204 :   PCType pc_type = nullptr;
     174      259204 :   LibmeshPetscCallA(comm, PCGetType(pc, &pc_type));
     175             : 
     176             :   // Check if hypre ams/ads, otherwise we quit with nothing to do
     177     1012936 :   if (pc_type && std::string(pc_type) == PCHYPRE)
     178             :   {
     179             :     // Get the hypre preconditioner we are using
     180         804 :     PCType hypre_type = nullptr;
     181         804 :     LibmeshPetscCallA(comm, PCHYPREGetType(pc, &hypre_type));
     182             : 
     183             :     // If not ams/ads, we quit with nothing to do
     184        1608 :     if (std::string(hypre_type) == "ams")
     185             :     {
     186             :       // If multiple variables, we error out as senseless
     187         800 :       libmesh_error_msg_if(sys.n_vars() > 1,
     188             :                            "Error applying hypre AMS to a system with multiple "
     189             :                            "variables");
     190             :       // If not a 1st order Nédélec or a 2d 1st order Raviart-Thomas system, we
     191             :       // error out as we do not support anything else at the moment
     192         816 :       libmesh_error_msg_if(sys.variable(v).type() != FEType(1, NEDELEC_ONE) &&
     193             :                           (sys.variable(v).type() != FEType(1, RAVIART_THOMAS) ||
     194             :                            sys.get_mesh().mesh_dimension() != 2),
     195             :                            "Error applying hypre AMS to a system "
     196             :                            "whose variable is not 1st order Nedelec or 1st "
     197             :                            "order Raviart-Thomas on a 2d mesh");
     198         800 :       set_hypre_ams_data(pc, sys, v);
     199             :     }
     200           8 :     else if (std::string(hypre_type) == "ads")
     201             :     {
     202             :       // If multiple variables, we error out as senseless
     203           4 :       libmesh_error_msg_if(sys.n_vars() > 1,
     204             :                            "Error applying hypre ADS to a system with multiple "
     205             :                            "variables");
     206             :       // If not a 3d 1st order Raviart-Thomas system, we error out as we do not
     207             :       // support anything else at the moment
     208           8 :       libmesh_error_msg_if(sys.variable(v).type() != FEType(1, RAVIART_THOMAS) ||
     209             :                            sys.get_mesh().mesh_dimension() != 3,
     210             :                            "Error applying hypre ADS to a system "
     211             :                            "whose variable is not 1st "
     212             :                            "order Raviart-Thomas on a 3d mesh");
     213           4 :       set_hypre_ads_data(pc, sys, v);
     214             :     }
     215             :   }
     216      259204 : }
     217             : 
     218             : 
     219             : 
     220             : template <typename T>
     221         800 : void PetscPreconditioner<T>::set_hypre_ams_data(PC & pc, System & sys, const unsigned v)
     222             : {
     223             :   // Get the communicator from the PETSc object
     224             :   Parallel::communicator comm;
     225         800 :   PetscErrorCode ierr = PetscObjectGetComm((PetscObject)pc, &comm);
     226         800 :   libmesh_error_msg_if(ierr != LIBMESH_PETSC_SUCCESS,
     227             :                        "Error retrieving communicator");
     228           0 :   Parallel::Communicator Comm(comm);
     229             : 
     230             :   // The mesh/problem dimension
     231         800 :   const unsigned dim = sys.get_mesh().mesh_dimension();
     232             : 
     233             :   // Dummy Lagrange system defined over the same mesh so we can enumerate the vertices
     234         800 :   System & lagrange_sys = sys.get_equation_systems().add_system<System>("__hypre_ams_vertices");
     235         800 :   lagrange_sys.hide_output() = true;
     236         800 :   lagrange_sys.add_variable("__lagrange");
     237         800 :   lagrange_sys.reinit_mesh();
     238             : 
     239             :   // Global (i.e. total) and local (i.e. to this processor) number of edges and vertices
     240         800 :   const std::vector<dof_id_type> n_all_edges = sys.get_dof_map().n_dofs_per_processor(v);
     241         800 :   const dof_id_type n_glb_edges = std::accumulate(n_all_edges.begin(), n_all_edges.end(), 0);
     242         800 :   const dof_id_type n_loc_edges = n_all_edges[global_processor_id()];
     243         800 :   const dof_id_type n_glb_verts = lagrange_sys.n_dofs();
     244         800 :   const dof_id_type n_loc_verts = lagrange_sys.n_local_dofs();
     245             : 
     246             :   // We require local indexing through the vertices and contiguous indexing
     247             :   // through the edges: since the vertices are enumerated on their own system,
     248             :   // a local index can simply be obtained by subtracting those vertices in all
     249             :   // preceding processors; for the edges, if the system we are given by the
     250             :   // user has multiple variables/splits, we effectively need to add local edge
     251             :   // numbers to those edges in all preceding processors
     252             : 
     253             :   // The number of vertices and edges in all preceding processors
     254           0 :   dof_id_signed_type vert_offset = -lagrange_sys.get_dof_map().first_dof();
     255         800 :   dof_id_signed_type edge_offset =
     256           0 :     std::accumulate(n_all_edges.begin(), n_all_edges.begin() + global_processor_id(), 0);
     257             : 
     258             :   // Whether dofs are in variable-major or node-major order
     259         800 :   bool var_major = !libMesh::on_command_line("--node-major-dofs");
     260             : 
     261             :   // If variable-major order, we need only subtract all the preceding dofs; but
     262             :   // if node-major order, we need to enumerate the dofs on this processor
     263           0 :   std::vector<dof_id_type> idx_edges;
     264         800 :   if (var_major)
     265             :   {
     266         800 :     edge_offset -= sys.get_dof_map().first_dof();
     267         800 :     for (auto i : make_range(v))
     268           0 :       edge_offset -= sys.get_dof_map().n_local_dofs(i);
     269             :   }
     270             :   else
     271           0 :     sys.get_dof_map().local_variable_indices(idx_edges, sys.get_mesh(), v);
     272             : 
     273             :   // Create the discrete grandient matrix, representing the edges in terms of its vertices
     274             :   // Preallocate 2 diagonal + 2 off-diagonal nonzeros as the vertices could fall on either
     275         800 :   PetscMatrix<Real> G(Comm, n_glb_edges, n_glb_verts, n_loc_edges, n_loc_verts, 2, 2);
     276             : 
     277             :   // Create array for the coordinates of the local vertices
     278             :   PetscReal * coords;
     279         800 :   LibmeshPetscCallA(comm, PetscMalloc1(dim * n_loc_verts, &coords));
     280             : 
     281             :   // Populate the discrete gradient matrix and the coordinates array
     282      149100 :   for (const auto & elem : sys.get_mesh().active_local_element_ptr_range())
     283      401562 :     for (auto edge : make_range(elem->n_edges()))
     284             :     {
     285             :       // The edge's two vertices
     286             :       dof_id_type vert_dofs[2];
     287      984636 :       for (auto vert : make_range(2))
     288             :       {
     289      656424 :         const unsigned loc_vert_node_id = elem->local_edge_node(edge, vert);
     290           0 :         libmesh_assert(elem->is_vertex(loc_vert_node_id));
     291             : 
     292      656424 :         const Node & vert_node = elem->node_ref(loc_vert_node_id);
     293      656424 :         vert_dofs[vert] = vert_node.dof_number(lagrange_sys.number(), 0, 0);
     294             : 
     295             :         // If owned, populate coordinates array
     296      656424 :         if (vert_node.processor_id() == global_processor_id())
     297             :         {
     298           0 :           const dof_id_type loc_vert_dof = vert_offset + vert_dofs[vert];
     299           0 :           libmesh_assert(loc_vert_dof <  n_loc_verts);
     300             : 
     301     1902888 :           for (auto d : make_range(dim))
     302     1360376 :             coords[dim * loc_vert_dof + d] = vert_node(d);
     303             :         }
     304             :       }
     305             : 
     306             :       // The edge's (middle) node
     307      328212 :       const unsigned loc_edge_node_id = elem->local_edge_node(edge, 2);
     308           0 :       libmesh_assert(elem->is_edge(loc_edge_node_id));
     309             : 
     310      328212 :       const Node & edge_node = elem->node_ref(loc_edge_node_id);
     311             : 
     312             :       // If owned, populate discrete gradient matrix
     313      328212 :       if (edge_node.processor_id() == global_processor_id())
     314             :       {
     315      303502 :         const dof_id_type edge_dof = edge_node.dof_number(sys.number(), v, 0);
     316             : 
     317      303502 :         const dof_id_type cont_edge_dof = edge_offset +
     318      303502 :           (var_major ? edge_dof
     319           0 :                      : std::distance(idx_edges.begin(),
     320             :                        std::find(idx_edges.begin(), idx_edges.end(), edge_dof)));
     321             : 
     322      303502 :         const Real sign = elem->positive_edge_orientation(edge) ? 1 : -1;
     323             : 
     324           0 :         libmesh_assert(cont_edge_dof >= G.row_start());
     325           0 :         libmesh_assert(cont_edge_dof <  G.row_stop());
     326      303502 :         G.set(cont_edge_dof, vert_dofs[0],  sign);
     327      303502 :         G.set(cont_edge_dof, vert_dofs[1], -sign);
     328             :       }
     329             :     }
     330             : 
     331             :   // Assemble the discrete gradient matrix
     332         800 :   G.close();
     333             : 
     334             :   // Hand over the matrix and coordinates array
     335         800 :   LibmeshPetscCallA(comm, PCHYPRESetDiscreteGradient(pc, G.mat()));
     336         800 :   LibmeshPetscCallA(comm, PCSetCoordinates(pc, dim, n_loc_verts, coords));
     337             : 
     338             :   // Free allocated memory for the coordinates array
     339         800 :   LibmeshPetscCallA(comm, PetscFree(coords));
     340        1600 : }
     341             : 
     342             : 
     343             : 
     344             : template <typename T>
     345           4 : void PetscPreconditioner<T>::set_hypre_ads_data(PC & pc, System & sys, const unsigned v)
     346             : {
     347             :   // Get the communicator from the PETSc object
     348             :   Parallel::communicator comm;
     349           4 :   PetscErrorCode ierr = PetscObjectGetComm((PetscObject)pc, &comm);
     350           4 :   libmesh_error_msg_if(ierr != LIBMESH_PETSC_SUCCESS,
     351             :                        "Error retrieving communicator");
     352           0 :   Parallel::Communicator Comm(comm);
     353             : 
     354             :   // The mesh/problem dimension
     355           4 :   const unsigned dim = sys.get_mesh().mesh_dimension();
     356             : 
     357             :   // Dummy Lagrange and Nédélec systems defined over the same mesh so we can
     358             :   // enumerate the vertices and edges, respectively
     359           4 :   System & lagrange_sys = sys.get_equation_systems().add_system<System>("__hypre_ads_vertices");
     360           4 :   lagrange_sys.hide_output() = true;
     361           4 :   lagrange_sys.add_variable("__lagrange");
     362           4 :   lagrange_sys.reinit_mesh();
     363           4 :   System & nedelec_sys = sys.get_equation_systems().add_system<System>("__hypre_ads_edges");
     364           4 :   nedelec_sys.hide_output() = true;
     365           4 :   nedelec_sys.add_variable("__nedelec", FIRST, NEDELEC_ONE);
     366           4 :   nedelec_sys.reinit_mesh();
     367             : 
     368             :   // Global (i.e. total) and local (i.e. to this processor) number of faces,
     369             :   // edges and vertices
     370           4 :   const std::vector<dof_id_type> n_all_faces = sys.get_dof_map().n_dofs_per_processor(v);
     371           4 :   const dof_id_type n_glb_faces = std::accumulate(n_all_faces.begin(), n_all_faces.end(), 0);
     372           4 :   const dof_id_type n_loc_faces = n_all_faces[global_processor_id()];
     373           4 :   const dof_id_type n_glb_edges = nedelec_sys.n_dofs();
     374           4 :   const dof_id_type n_loc_edges = nedelec_sys.n_local_dofs();
     375           4 :   const dof_id_type n_glb_verts = lagrange_sys.n_dofs();
     376           4 :   const dof_id_type n_loc_verts = lagrange_sys.n_local_dofs();
     377             : 
     378             :   // We require local indexing through the vertices and contiguous indexing
     379             :   // through the edges and faces: since the vertices are enumerated on their own
     380             :   // system, a local index can simply be obtained by subtracting those vertices
     381             :   // in all preceding processors; since the edges are enumerated on their own
     382             :   // system, we already have contiguous indexing; for the faces, if the system
     383             :   // we are given by the user has multiple variables/splits, we effectively need
     384             :   // to add local face numbers to those faces in all preceding processors
     385             : 
     386             :   // The number of vertices and faces in all preceding processors
     387           0 :   dof_id_signed_type vert_offset = -lagrange_sys.get_dof_map().first_dof();
     388           4 :   dof_id_signed_type face_offset =
     389           0 :     std::accumulate(n_all_faces.begin(), n_all_faces.begin() + global_processor_id(), 0);
     390             : 
     391             :   // Whether dofs are in variable-major or node-major order
     392           4 :   bool var_major = !libMesh::on_command_line("--node-major-dofs");
     393             : 
     394             :   // If variable-major order, we need only subtract all the preceding dofs; but
     395             :   // if node-major order, we need to enumerate the dofs on this processor
     396           0 :   std::vector<dof_id_type> idx_faces;
     397           4 :   if (var_major)
     398             :   {
     399           4 :     face_offset -= sys.get_dof_map().first_dof();
     400           4 :     for (auto i : make_range(v))
     401           0 :       face_offset -= sys.get_dof_map().n_local_dofs(i);
     402             :   }
     403             :   else
     404           0 :     sys.get_dof_map().local_variable_indices(idx_faces, sys.get_mesh(), v);
     405             : 
     406             :   // Create the discrete grandient matrix, representing the edges in terms of its vertices
     407             :   // Preallocate 2 diagonal + 2 off-diagonal nonzeros as the vertices could fall on either
     408           4 :   PetscMatrix<Real> G(Comm, n_glb_edges, n_glb_verts, n_loc_edges, n_loc_verts, 2, 2);
     409             : 
     410             :   // Create the discrete curl matrix, representing the faces in terms of its edges
     411             :   // Preallocate 4 diagonal + 4 off-diagonal nonzeros as the edges could fall on either
     412           4 :   PetscMatrix<Real> C(Comm, n_glb_faces, n_glb_edges, n_loc_faces, n_loc_edges, 4, 4);
     413             : 
     414             :   // Create array for the coordinates of the local vertices
     415             :   PetscReal * coords;
     416           4 :   LibmeshPetscCallA(comm, PetscMalloc1(dim * n_loc_verts, &coords));
     417             : 
     418             :   // Populate the discrete gradient matrix and the coordinates array
     419       17130 :   for (const auto & elem : sys.get_mesh().active_local_element_ptr_range())
     420       49545 :     for (auto face : make_range(elem->n_faces()))
     421             :     {
     422             :       // The number of edges on this face
     423       40986 :       const unsigned n_face_edges = Elem::type_to_n_sides_map[elem->side_type(face)];
     424             : 
     425             :       // The faces's three/four edges
     426       40986 :       std::vector<dof_id_type> edge_dofs(n_face_edges);
     427           0 :       std::vector<bool> edge_orients(n_face_edges);
     428      184194 :       for (auto face_edge : make_range(n_face_edges))
     429             :       {
     430             :         // Convert from face-wise to element-wise edge id
     431      143208 :         const unsigned edge = elem->local_side_node(face, n_face_edges + face_edge)
     432      143208 :                             - elem->n_vertices();
     433           0 :         libmesh_assert(elem->is_edge_on_side(edge, face));
     434             : 
     435             :         // The edge's two vertices
     436             :         dof_id_type vert_dofs[2];
     437      429624 :         for (auto vert : make_range(2))
     438             :         {
     439      286416 :           const unsigned loc_vert_node_id = elem->local_edge_node(edge, vert);
     440           0 :           libmesh_assert(elem->is_vertex(loc_vert_node_id));
     441             : 
     442      286416 :           const Node & vert_node = elem->node_ref(loc_vert_node_id);
     443      286416 :           vert_dofs[vert] = vert_node.dof_number(lagrange_sys.number(), 0, 0);
     444             : 
     445             :           // If owned, populate coordinates array
     446      286416 :           if (vert_node.processor_id() == global_processor_id())
     447             :           {
     448           0 :             const dof_id_type loc_vert_dof = vert_offset + vert_dofs[vert];
     449           0 :             libmesh_assert(loc_vert_dof <  n_loc_verts);
     450             : 
     451     1085712 :             for (auto d : make_range(dim))
     452      814284 :               coords[dim * loc_vert_dof + d] = vert_node(d);
     453             :           }
     454             :         }
     455             : 
     456             :         // The edge's (middle) node
     457      143208 :         const unsigned loc_edge_node_id = elem->local_edge_node(edge, 2);
     458           0 :         libmesh_assert(elem->is_edge(loc_edge_node_id));
     459             : 
     460      143208 :         const Node & edge_node = elem->node_ref(loc_edge_node_id);
     461      143208 :         edge_dofs[face_edge] = edge_node.dof_number(nedelec_sys.number(), 0, 0);
     462      286416 :         edge_orients[face_edge] = elem->positive_edge_orientation(edge) ^
     463      143208 :                                   elem->relative_edge_face_order(edge, face);
     464             : 
     465             :         // If owned, populate discrete gradient matrix
     466      143208 :         if (edge_node.processor_id() == global_processor_id())
     467             :         {
     468      139630 :           const Real sign = elem->positive_edge_orientation(edge) ? 1 : -1;
     469             : 
     470           0 :           libmesh_assert(edge_dofs[face_edge] >= G.row_start());
     471           0 :           libmesh_assert(edge_dofs[face_edge] <  G.row_stop());
     472      139630 :           G.set(edge_dofs[face_edge], vert_dofs[0],  sign);
     473      139630 :           G.set(edge_dofs[face_edge], vert_dofs[1], -sign);
     474             :         }
     475             :       }
     476             : 
     477             :       // The faces's (middle) node
     478       40986 :       const unsigned loc_face_node_id = elem->local_side_node(face, 2 * n_face_edges);
     479           0 :       libmesh_assert(elem->is_face(loc_face_node_id));
     480             : 
     481       40986 :       const Node & face_node = elem->node_ref(loc_face_node_id);
     482             : 
     483             :       // If owned, populate discrete curl matrix
     484       40986 :       if (face_node.processor_id() == global_processor_id())
     485             :       {
     486       40580 :         const dof_id_type face_dof = face_node.dof_number(sys.number(), v, 0);
     487             : 
     488       40580 :         const dof_id_type cont_face_dof = face_offset +
     489       40580 :           (var_major ? face_dof
     490           0 :                      : std::distance(idx_faces.begin(),
     491             :                        std::find(idx_faces.begin(), idx_faces.end(), face_dof)));
     492             : 
     493       40580 :         const bool face_orient = elem->positive_face_orientation(face);
     494             : 
     495      182324 :         for (auto face_edge : make_range(n_face_edges))
     496             :         {
     497      141744 :           const Real sign = face_orient ^ edge_orients[face_edge] ? 1 : -1;
     498             : 
     499           0 :           libmesh_assert(cont_face_dof >= C.row_start());
     500           0 :           libmesh_assert(cont_face_dof <  C.row_stop());
     501      141744 :           C.set(cont_face_dof, edge_dofs[face_edge], sign);
     502             :         }
     503             :       }
     504             :     }
     505             : 
     506             :   // Assemble the discrete gradient and discrete curl matrices
     507           4 :   G.close();
     508           4 :   C.close();
     509             : 
     510             : #ifndef NDEBUG
     511             :   // The product CG of the two matrices should be the zero matrix
     512           0 :   PetscMatrix<Real> CG(Comm);
     513           0 :   C.matrix_matrix_mult(G, CG);
     514           0 :   libmesh_assert(CG.linfty_norm() == 0);
     515             : #endif
     516             : 
     517             :   // Hand over the matrices and coordinates array
     518           4 :   LibmeshPetscCallA(comm, PCHYPRESetDiscreteGradient(pc, G.mat()));
     519           4 :   LibmeshPetscCallA(comm, PCHYPRESetDiscreteCurl(pc, C.mat()));
     520           4 :   LibmeshPetscCallA(comm, PCSetCoordinates(pc, dim, n_loc_verts, coords));
     521             : 
     522             :   // Free allocated memory for the coordinates array
     523           4 :   LibmeshPetscCallA(comm, PetscFree(coords));
     524           8 : }
     525             : #endif
     526             : 
     527             : 
     528             : //------------------------------------------------------------------
     529             : // Explicit instantiations
     530             : template class LIBMESH_EXPORT PetscPreconditioner<Number>;
     531             : 
     532             : } // namespace libMesh
     533             : 
     534             : #endif // #ifdef LIBMESH_HAVE_PETSC

Generated by: LCOV version 1.14