LCOV - code coverage report
Current view: top level - src/numerics - petsc_preconditioner.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4493 (1fc31f) with base e7717b Lines: 144 224 64.3 %
Date: 2026-07-08 01:01:35 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         800 :       const FEType & var_type = sys.variable(v).type();
     193           0 :       const bool first_order_nedelec =
     194         800 :         var_type.order == FIRST && var_type.family == NEDELEC_ONE;
     195           0 :       const bool first_order_raviart_thomas =
     196         800 :         var_type.order == FIRST && var_type.family == RAVIART_THOMAS;
     197             : 
     198         800 :       libmesh_error_msg_if(!first_order_nedelec &&
     199             :                            (!first_order_raviart_thomas ||
     200             :                             sys.get_mesh().mesh_dimension() != 2),
     201             :                            "Error applying hypre AMS to a system "
     202             :                            "whose variable is not 1st order Nedelec or 1st "
     203             :                            "order Raviart-Thomas on a 2d mesh");
     204         800 :       set_hypre_ams_data(pc, sys, v);
     205             :     }
     206           8 :     else if (std::string(hypre_type) == "ads")
     207             :     {
     208             :       // If multiple variables, we error out as senseless
     209           4 :       libmesh_error_msg_if(sys.n_vars() > 1,
     210             :                            "Error applying hypre ADS to a system with multiple "
     211             :                            "variables");
     212             :       // If not a 3d 1st order Raviart-Thomas system, we error out as we do not
     213             :       // support anything else at the moment
     214           4 :       const FEType & var_type = sys.variable(v).type();
     215           0 :       const bool first_order_raviart_thomas =
     216           4 :         var_type.order == FIRST && var_type.family == RAVIART_THOMAS;
     217             : 
     218           4 :       libmesh_error_msg_if(!first_order_raviart_thomas ||
     219             :                            sys.get_mesh().mesh_dimension() != 3,
     220             :                            "Error applying hypre ADS to a system "
     221             :                            "whose variable is not 1st "
     222             :                            "order Raviart-Thomas on a 3d mesh");
     223           4 :       set_hypre_ads_data(pc, sys, v);
     224             :     }
     225             :   }
     226      259204 : }
     227             : 
     228             : 
     229             : 
     230             : template <typename T>
     231         800 : void PetscPreconditioner<T>::set_hypre_ams_data(PC & pc, System & sys, const unsigned v)
     232             : {
     233             :   // Get the communicator from the PETSc object
     234             :   Parallel::communicator comm;
     235         800 :   PetscErrorCode ierr = PetscObjectGetComm((PetscObject)pc, &comm);
     236         800 :   libmesh_error_msg_if(ierr != LIBMESH_PETSC_SUCCESS,
     237             :                        "Error retrieving communicator");
     238           0 :   Parallel::Communicator Comm(comm);
     239             : 
     240             :   // The mesh/problem dimension
     241         800 :   const unsigned dim = sys.get_mesh().mesh_dimension();
     242             : 
     243             :   // Dummy Lagrange system defined over the same mesh so we can enumerate the vertices
     244         800 :   System & lagrange_sys = sys.get_equation_systems().add_system<System>("__hypre_ams_vertices");
     245         800 :   lagrange_sys.hide_output() = true;
     246         800 :   lagrange_sys.add_variable("__lagrange");
     247         800 :   lagrange_sys.reinit_mesh();
     248             : 
     249             :   // Global (i.e. total) and local (i.e. to this processor) number of edges and vertices
     250         800 :   const std::vector<dof_id_type> n_all_edges = sys.get_dof_map().n_dofs_per_processor(v);
     251         800 :   const dof_id_type n_glb_edges = std::accumulate(n_all_edges.begin(), n_all_edges.end(), 0);
     252         800 :   const dof_id_type n_loc_edges = n_all_edges[global_processor_id()];
     253         800 :   const dof_id_type n_glb_verts = lagrange_sys.n_dofs();
     254         800 :   const dof_id_type n_loc_verts = lagrange_sys.n_local_dofs();
     255             : 
     256             :   // We require local indexing through the vertices and contiguous indexing
     257             :   // through the edges: since the vertices are enumerated on their own system,
     258             :   // a local index can simply be obtained by subtracting those vertices in all
     259             :   // preceding processors; for the edges, if the system we are given by the
     260             :   // user has multiple variables/splits, we effectively need to add local edge
     261             :   // numbers to those edges in all preceding processors
     262             : 
     263             :   // The number of vertices and edges in all preceding processors
     264           0 :   dof_id_signed_type vert_offset = -lagrange_sys.get_dof_map().first_dof();
     265         800 :   dof_id_signed_type edge_offset =
     266           0 :     std::accumulate(n_all_edges.begin(), n_all_edges.begin() + global_processor_id(), 0);
     267             : 
     268             :   // Whether dofs are in variable-major or node-major order
     269         800 :   bool var_major = !libMesh::on_command_line("--node-major-dofs");
     270             : 
     271             :   // If variable-major order, we need only subtract all the preceding dofs; but
     272             :   // if node-major order, we need to enumerate the dofs on this processor
     273           0 :   std::vector<dof_id_type> idx_edges;
     274         800 :   if (var_major)
     275             :   {
     276         800 :     edge_offset -= sys.get_dof_map().first_dof();
     277         800 :     for (auto i : make_range(v))
     278           0 :       edge_offset -= sys.get_dof_map().n_local_dofs(i);
     279             :   }
     280             :   else
     281           0 :     sys.get_dof_map().local_variable_indices(idx_edges, sys.get_mesh(), v);
     282             : 
     283             :   // Create the discrete grandient matrix, representing the edges in terms of its vertices
     284             :   // Preallocate 2 diagonal + 2 off-diagonal nonzeros as the vertices could fall on either
     285         800 :   PetscMatrix<Real> G(Comm, n_glb_edges, n_glb_verts, n_loc_edges, n_loc_verts, 2, 2);
     286             : 
     287             :   // Create array for the coordinates of the local vertices
     288             :   PetscReal * coords;
     289         800 :   LibmeshPetscCallA(comm, PetscMalloc1(dim * n_loc_verts, &coords));
     290             : 
     291             :   // Populate the discrete gradient matrix and the coordinates array
     292      149100 :   for (const auto & elem : sys.get_mesh().active_local_element_ptr_range())
     293      401562 :     for (auto edge : make_range(elem->n_edges()))
     294             :     {
     295             :       // The edge's two vertices
     296             :       dof_id_type vert_dofs[2];
     297      984636 :       for (auto vert : make_range(2))
     298             :       {
     299      656424 :         const unsigned loc_vert_node_id = elem->local_edge_node(edge, vert);
     300           0 :         libmesh_assert(elem->is_vertex(loc_vert_node_id));
     301             : 
     302      656424 :         const Node & vert_node = elem->node_ref(loc_vert_node_id);
     303      656424 :         vert_dofs[vert] = vert_node.dof_number(lagrange_sys.number(), 0, 0);
     304             : 
     305             :         // If owned, populate coordinates array
     306      656424 :         if (vert_node.processor_id() == global_processor_id())
     307             :         {
     308           0 :           const dof_id_type loc_vert_dof = vert_offset + vert_dofs[vert];
     309           0 :           libmesh_assert(loc_vert_dof <  n_loc_verts);
     310             : 
     311     1902888 :           for (auto d : make_range(dim))
     312     1360376 :             coords[dim * loc_vert_dof + d] = vert_node(d);
     313             :         }
     314             :       }
     315             : 
     316             :       // The edge's (middle) node
     317      328212 :       const unsigned loc_edge_node_id = elem->local_edge_node(edge, 2);
     318           0 :       libmesh_assert(elem->is_edge(loc_edge_node_id));
     319             : 
     320      328212 :       const Node & edge_node = elem->node_ref(loc_edge_node_id);
     321             : 
     322             :       // If owned, populate discrete gradient matrix
     323      328212 :       if (edge_node.processor_id() == global_processor_id())
     324             :       {
     325      303502 :         const dof_id_type edge_dof = edge_node.dof_number(sys.number(), v, 0);
     326             : 
     327      303502 :         const dof_id_type cont_edge_dof = edge_offset +
     328      303502 :           (var_major ? edge_dof
     329           0 :                      : std::distance(idx_edges.begin(),
     330             :                        std::find(idx_edges.begin(), idx_edges.end(), edge_dof)));
     331             : 
     332      303502 :         const Real sign = elem->positive_edge_orientation(edge) ? 1 : -1;
     333             : 
     334           0 :         libmesh_assert(cont_edge_dof >= G.row_start());
     335           0 :         libmesh_assert(cont_edge_dof <  G.row_stop());
     336      303502 :         G.set(cont_edge_dof, vert_dofs[0],  sign);
     337      303502 :         G.set(cont_edge_dof, vert_dofs[1], -sign);
     338             :       }
     339             :     }
     340             : 
     341             :   // Assemble the discrete gradient matrix
     342         800 :   G.close();
     343             : 
     344             :   // Hand over the matrix and coordinates array
     345         800 :   LibmeshPetscCallA(comm, PCHYPRESetDiscreteGradient(pc, G.mat()));
     346         800 :   LibmeshPetscCallA(comm, PCSetCoordinates(pc, dim, n_loc_verts, coords));
     347             : 
     348             :   // Free allocated memory for the coordinates array
     349         800 :   LibmeshPetscCallA(comm, PetscFree(coords));
     350        1600 : }
     351             : 
     352             : 
     353             : 
     354             : template <typename T>
     355           4 : void PetscPreconditioner<T>::set_hypre_ads_data(PC & pc, System & sys, const unsigned v)
     356             : {
     357             :   // Get the communicator from the PETSc object
     358             :   Parallel::communicator comm;
     359           4 :   PetscErrorCode ierr = PetscObjectGetComm((PetscObject)pc, &comm);
     360           4 :   libmesh_error_msg_if(ierr != LIBMESH_PETSC_SUCCESS,
     361             :                        "Error retrieving communicator");
     362           0 :   Parallel::Communicator Comm(comm);
     363             : 
     364             :   // The mesh/problem dimension
     365           4 :   const unsigned dim = sys.get_mesh().mesh_dimension();
     366             : 
     367             :   // Dummy Lagrange and Nédélec systems defined over the same mesh so we can
     368             :   // enumerate the vertices and edges, respectively
     369           4 :   System & lagrange_sys = sys.get_equation_systems().add_system<System>("__hypre_ads_vertices");
     370           4 :   lagrange_sys.hide_output() = true;
     371           4 :   lagrange_sys.add_variable("__lagrange");
     372           4 :   lagrange_sys.reinit_mesh();
     373           4 :   System & nedelec_sys = sys.get_equation_systems().add_system<System>("__hypre_ads_edges");
     374           4 :   nedelec_sys.hide_output() = true;
     375           4 :   nedelec_sys.add_variable("__nedelec", FIRST, NEDELEC_ONE);
     376           4 :   nedelec_sys.reinit_mesh();
     377             : 
     378             :   // Global (i.e. total) and local (i.e. to this processor) number of faces,
     379             :   // edges and vertices
     380           4 :   const std::vector<dof_id_type> n_all_faces = sys.get_dof_map().n_dofs_per_processor(v);
     381           4 :   const dof_id_type n_glb_faces = std::accumulate(n_all_faces.begin(), n_all_faces.end(), 0);
     382           4 :   const dof_id_type n_loc_faces = n_all_faces[global_processor_id()];
     383           4 :   const dof_id_type n_glb_edges = nedelec_sys.n_dofs();
     384           4 :   const dof_id_type n_loc_edges = nedelec_sys.n_local_dofs();
     385           4 :   const dof_id_type n_glb_verts = lagrange_sys.n_dofs();
     386           4 :   const dof_id_type n_loc_verts = lagrange_sys.n_local_dofs();
     387             : 
     388             :   // We require local indexing through the vertices and contiguous indexing
     389             :   // through the edges and faces: since the vertices are enumerated on their own
     390             :   // system, a local index can simply be obtained by subtracting those vertices
     391             :   // in all preceding processors; since the edges are enumerated on their own
     392             :   // system, we already have contiguous indexing; for the faces, if the system
     393             :   // we are given by the user has multiple variables/splits, we effectively need
     394             :   // to add local face numbers to those faces in all preceding processors
     395             : 
     396             :   // The number of vertices and faces in all preceding processors
     397           0 :   dof_id_signed_type vert_offset = -lagrange_sys.get_dof_map().first_dof();
     398           4 :   dof_id_signed_type face_offset =
     399           0 :     std::accumulate(n_all_faces.begin(), n_all_faces.begin() + global_processor_id(), 0);
     400             : 
     401             :   // Whether dofs are in variable-major or node-major order
     402           4 :   bool var_major = !libMesh::on_command_line("--node-major-dofs");
     403             : 
     404             :   // If variable-major order, we need only subtract all the preceding dofs; but
     405             :   // if node-major order, we need to enumerate the dofs on this processor
     406           0 :   std::vector<dof_id_type> idx_faces;
     407           4 :   if (var_major)
     408             :   {
     409           4 :     face_offset -= sys.get_dof_map().first_dof();
     410           4 :     for (auto i : make_range(v))
     411           0 :       face_offset -= sys.get_dof_map().n_local_dofs(i);
     412             :   }
     413             :   else
     414           0 :     sys.get_dof_map().local_variable_indices(idx_faces, sys.get_mesh(), v);
     415             : 
     416             :   // Create the discrete grandient matrix, representing the edges in terms of its vertices
     417             :   // Preallocate 2 diagonal + 2 off-diagonal nonzeros as the vertices could fall on either
     418           4 :   PetscMatrix<Real> G(Comm, n_glb_edges, n_glb_verts, n_loc_edges, n_loc_verts, 2, 2);
     419             : 
     420             :   // Create the discrete curl matrix, representing the faces in terms of its edges
     421             :   // Preallocate 4 diagonal + 4 off-diagonal nonzeros as the edges could fall on either
     422           4 :   PetscMatrix<Real> C(Comm, n_glb_faces, n_glb_edges, n_loc_faces, n_loc_edges, 4, 4);
     423             : 
     424             :   // Create array for the coordinates of the local vertices
     425             :   PetscReal * coords;
     426           4 :   LibmeshPetscCallA(comm, PetscMalloc1(dim * n_loc_verts, &coords));
     427             : 
     428             :   // Populate the discrete gradient matrix and the coordinates array
     429       17130 :   for (const auto & elem : sys.get_mesh().active_local_element_ptr_range())
     430       49545 :     for (auto face : make_range(elem->n_faces()))
     431             :     {
     432             :       // The number of edges on this face
     433       40986 :       const unsigned n_face_edges = Elem::type_to_n_sides_map[elem->side_type(face)];
     434             : 
     435             :       // The faces's three/four edges
     436       40986 :       std::vector<dof_id_type> edge_dofs(n_face_edges);
     437           0 :       std::vector<bool> edge_orients(n_face_edges);
     438      184194 :       for (auto face_edge : make_range(n_face_edges))
     439             :       {
     440             :         // Convert from face-wise to element-wise edge id
     441      143208 :         const unsigned edge = elem->local_side_node(face, n_face_edges + face_edge)
     442      143208 :                             - elem->n_vertices();
     443           0 :         libmesh_assert(elem->is_edge_on_side(edge, face));
     444             : 
     445             :         // The edge's two vertices
     446             :         dof_id_type vert_dofs[2];
     447      429624 :         for (auto vert : make_range(2))
     448             :         {
     449      286416 :           const unsigned loc_vert_node_id = elem->local_edge_node(edge, vert);
     450           0 :           libmesh_assert(elem->is_vertex(loc_vert_node_id));
     451             : 
     452      286416 :           const Node & vert_node = elem->node_ref(loc_vert_node_id);
     453      286416 :           vert_dofs[vert] = vert_node.dof_number(lagrange_sys.number(), 0, 0);
     454             : 
     455             :           // If owned, populate coordinates array
     456      286416 :           if (vert_node.processor_id() == global_processor_id())
     457             :           {
     458           0 :             const dof_id_type loc_vert_dof = vert_offset + vert_dofs[vert];
     459           0 :             libmesh_assert(loc_vert_dof <  n_loc_verts);
     460             : 
     461     1085712 :             for (auto d : make_range(dim))
     462      814284 :               coords[dim * loc_vert_dof + d] = vert_node(d);
     463             :           }
     464             :         }
     465             : 
     466             :         // The edge's (middle) node
     467      143208 :         const unsigned loc_edge_node_id = elem->local_edge_node(edge, 2);
     468           0 :         libmesh_assert(elem->is_edge(loc_edge_node_id));
     469             : 
     470      143208 :         const Node & edge_node = elem->node_ref(loc_edge_node_id);
     471      143208 :         edge_dofs[face_edge] = edge_node.dof_number(nedelec_sys.number(), 0, 0);
     472      286416 :         edge_orients[face_edge] = elem->positive_edge_orientation(edge) ^
     473      143208 :                                   elem->relative_edge_face_order(edge, face);
     474             : 
     475             :         // If owned, populate discrete gradient matrix
     476      143208 :         if (edge_node.processor_id() == global_processor_id())
     477             :         {
     478      139630 :           const Real sign = elem->positive_edge_orientation(edge) ? 1 : -1;
     479             : 
     480           0 :           libmesh_assert(edge_dofs[face_edge] >= G.row_start());
     481           0 :           libmesh_assert(edge_dofs[face_edge] <  G.row_stop());
     482      139630 :           G.set(edge_dofs[face_edge], vert_dofs[0],  sign);
     483      139630 :           G.set(edge_dofs[face_edge], vert_dofs[1], -sign);
     484             :         }
     485             :       }
     486             : 
     487             :       // The faces's (middle) node
     488       40986 :       const unsigned loc_face_node_id = elem->local_side_node(face, 2 * n_face_edges);
     489           0 :       libmesh_assert(elem->is_face(loc_face_node_id));
     490             : 
     491       40986 :       const Node & face_node = elem->node_ref(loc_face_node_id);
     492             : 
     493             :       // If owned, populate discrete curl matrix
     494       40986 :       if (face_node.processor_id() == global_processor_id())
     495             :       {
     496       40580 :         const dof_id_type face_dof = face_node.dof_number(sys.number(), v, 0);
     497             : 
     498       40580 :         const dof_id_type cont_face_dof = face_offset +
     499       40580 :           (var_major ? face_dof
     500           0 :                      : std::distance(idx_faces.begin(),
     501             :                        std::find(idx_faces.begin(), idx_faces.end(), face_dof)));
     502             : 
     503       40580 :         const bool face_orient = elem->positive_face_orientation(face);
     504             : 
     505      182324 :         for (auto face_edge : make_range(n_face_edges))
     506             :         {
     507      141744 :           const Real sign = face_orient ^ edge_orients[face_edge] ? 1 : -1;
     508             : 
     509           0 :           libmesh_assert(cont_face_dof >= C.row_start());
     510           0 :           libmesh_assert(cont_face_dof <  C.row_stop());
     511      141744 :           C.set(cont_face_dof, edge_dofs[face_edge], sign);
     512             :         }
     513             :       }
     514             :     }
     515             : 
     516             :   // Assemble the discrete gradient and discrete curl matrices
     517           4 :   G.close();
     518           4 :   C.close();
     519             : 
     520             : #ifndef NDEBUG
     521             :   // The product CG of the two matrices should be the zero matrix
     522           0 :   PetscMatrix<Real> CG(Comm);
     523           0 :   C.matrix_matrix_mult(G, CG);
     524           0 :   libmesh_assert(CG.linfty_norm() == 0);
     525             : #endif
     526             : 
     527             :   // Hand over the matrices and coordinates array
     528           4 :   LibmeshPetscCallA(comm, PCHYPRESetDiscreteGradient(pc, G.mat()));
     529           4 :   LibmeshPetscCallA(comm, PCHYPRESetDiscreteCurl(pc, C.mat()));
     530           4 :   LibmeshPetscCallA(comm, PCSetCoordinates(pc, dim, n_loc_verts, coords));
     531             : 
     532             :   // Free allocated memory for the coordinates array
     533           4 :   LibmeshPetscCallA(comm, PetscFree(coords));
     534           8 : }
     535             : #endif
     536             : 
     537             : 
     538             : //------------------------------------------------------------------
     539             : // Explicit instantiations
     540             : template class LIBMESH_EXPORT PetscPreconditioner<Number>;
     541             : 
     542             : } // namespace libMesh
     543             : 
     544             : #endif // #ifdef LIBMESH_HAVE_PETSC

Generated by: LCOV version 1.14