LCOV - code coverage report
Current view: top level - src/utils - PetscDMMoose.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: fef103 Lines: 609 1126 54.1 %
Date: 2025-09-03 20:01:23 Functions: 27 51 52.9 %
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 "PetscDMMoose.h"
      11             : 
      12             : // PETSc includes
      13             : #include <petscerror.h>
      14             : #include <petsc/private/dmimpl.h>
      15             : 
      16             : // MOOSE includes
      17             : #include "FEProblem.h"
      18             : #include "DisplacedProblem.h"
      19             : #include "MooseMesh.h"
      20             : #include "NonlinearSystem.h"
      21             : #include "PenetrationLocator.h"
      22             : #include "NearestNodeLocator.h"
      23             : #include "GeometricSearchData.h"
      24             : #include "MooseVariableScalar.h"
      25             : 
      26             : #include "libmesh/nonlinear_implicit_system.h"
      27             : #include "libmesh/nonlinear_solver.h"
      28             : #include "libmesh/petsc_macro.h"
      29             : #include "libmesh/petsc_vector.h"
      30             : #include "libmesh/petsc_matrix.h"
      31             : #include "libmesh/dof_map.h"
      32             : #include "libmesh/preconditioner.h"
      33             : #include "libmesh/elem_side_builder.h"
      34             : 
      35             : using namespace libMesh;
      36             : 
      37             : template <typename I1, typename I2>
      38             : void
      39         157 : checkSize(const std::string & split_name, const I1 split_size, const I2 size_expected_by_parent)
      40             : {
      41         314 :   if (libMesh::cast_int<libMesh::numeric_index_type>(split_size) !=
      42         157 :       libMesh::cast_int<libMesh::numeric_index_type>(size_expected_by_parent))
      43           8 :     mooseError("Split '",
      44             :                split_name,
      45             :                "' has size ",
      46           8 :                libMesh::cast_int<libMesh::numeric_index_type>(split_size),
      47             :                " but the parent split expected size ",
      48           8 :                libMesh::cast_int<libMesh::numeric_index_type>(size_expected_by_parent),
      49             :                ". Make sure that you have non-overlapping complete sets for variables and "
      50             :                "blocks as well as consistency in sides/unsides, contacts/uncontacts, etc.");
      51         149 : }
      52             : 
      53             : struct DM_Moose
      54             : {
      55             :   NonlinearSystemBase * _nl; // nonlinear system context
      56             :   const DofMapBase * _dof_map;
      57             :   const System * _system;
      58             :   DM_Moose * _parent = nullptr;
      59             :   std::set<std::string> * _vars; // variables
      60             :   std::map<std::string, unsigned int> * _var_ids;
      61             :   std::map<unsigned int, std::string> * _var_names;
      62             :   bool _all_vars;                  // whether all system variables are included
      63             :   std::set<std::string> * _blocks; // mesh blocks
      64             :   std::map<std::string, subdomain_id_type> * _block_ids;
      65             :   std::map<unsigned int, std::string> * _block_names;
      66             :   bool _all_blocks;               // all blocks are included
      67             :   std::set<std::string> * _sides; // mesh surfaces (edges in 2D)
      68             :   std::map<BoundaryID, std::string> * _side_names;
      69             :   std::map<std::string, BoundaryID> * _side_ids;
      70             :   std::set<std::string> * _unsides; // excluded sides
      71             :   std::map<std::string, BoundaryID> * _unside_ids;
      72             :   std::map<BoundaryID, std::string> * _unside_names;
      73             :   std::set<std::string> * _unside_by_var; // excluded sides by variable
      74             :   std::set<std::pair<BoundaryID, unsigned int>> * _unside_by_var_set;
      75             :   bool _nosides;   // whether to include any sides
      76             :   bool _nounsides; // whether to exclude any sides
      77             :   bool _nounside_by_var;
      78             :   typedef std::pair<std::string, std::string> ContactName;
      79             :   typedef std::pair<BoundaryID, BoundaryID> ContactID;
      80             :   std::set<ContactName> * _contacts;
      81             :   std::map<ContactID, ContactName> * _contact_names;
      82             :   std::set<ContactName> * _uncontacts;
      83             :   std::map<ContactID, ContactName> * _uncontact_names;
      84             :   std::map<ContactName, PetscBool> * _contact_displaced;
      85             :   std::map<ContactName, PetscBool> * _uncontact_displaced;
      86             :   bool _nocontacts;
      87             :   bool _nouncontacts;
      88             :   bool _include_all_contact_nodes;
      89             :   // to locate splits without having to search, however,
      90             :   // maintain a multimap from names to split locations (to enable
      91             :   // the same split to appear in multiple spots (this might
      92             :   // break the current implementation of PCFieldSplit, though).
      93             :   std::multimap<std::string, unsigned int> * _splitlocs;
      94             :   struct SplitInfo
      95             :   {
      96             :     DM _dm;
      97             :     IS _rembedding; // relative embedding
      98             :   };
      99             :   std::map<std::string, SplitInfo> * _splits;
     100             : 
     101             :   IS _embedding;
     102             :   PetscBool _print_embedding;
     103             : 
     104             :   /// The name of this DM
     105             :   std::string * _name;
     106             : 
     107             :   /**
     108             :    * Check whether the size of the child matches the size we expect
     109             :    */
     110             :   void checkChildSize(DM child, PetscInt child_size, const std::string & child_name);
     111             : };
     112             : 
     113             : void
     114          28 : DM_Moose::checkChildSize(DM child, const PetscInt child_size, const std::string & child_name)
     115             : {
     116          40 :   for (const auto & split : *_splits)
     117          40 :     if (split.second._dm == child)
     118             :     {
     119             :       mooseAssert(split.first == child_name, "These should match");
     120             :       PetscInt parent_expected_size;
     121          28 :       auto ierr = ISGetLocalSize(split.second._rembedding, &parent_expected_size);
     122          28 :       if (ierr)
     123           0 :         mooseError("Unable to get size");
     124          28 :       checkSize(child_name, child_size, parent_expected_size);
     125          24 :       return;
     126             :     }
     127             : 
     128           0 :   mooseError("No child DM match");
     129             : }
     130             : 
     131             : PetscErrorCode
     132        4896 : DMMooseValidityCheck(DM dm)
     133             : {
     134             :   PetscBool ismoose;
     135             : 
     136             :   PetscFunctionBegin;
     137             :   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
     138        4896 :   LibmeshPetscCallQ(PetscObjectTypeCompare((PetscObject)dm, DMMOOSE, &ismoose));
     139        4896 :   if (!ismoose)
     140           0 :     LIBMESH_SETERRQ2(((PetscObject)dm)->comm,
     141             :                      PETSC_ERR_ARG_WRONG,
     142             :                      "Got DM of type %s, not of type %s",
     143             :                      ((PetscObject)dm)->type_name,
     144             :                      DMMOOSE);
     145        4896 :   PetscFunctionReturn(PETSC_SUCCESS);
     146             : }
     147             : 
     148             : PetscErrorCode
     149           0 : DMMooseGetContacts(DM dm,
     150             :                    std::vector<std::pair<std::string, std::string>> & contact_names,
     151             :                    std::vector<PetscBool> & displaced)
     152             : {
     153             :   PetscFunctionBegin;
     154           0 :   LibmeshPetscCallQ(DMMooseValidityCheck(dm));
     155           0 :   DM_Moose * dmm = (DM_Moose *)dm->data;
     156           0 :   for (const auto & it : *(dmm->_contact_names))
     157             :   {
     158           0 :     contact_names.push_back(it.second);
     159           0 :     displaced.push_back((*dmm->_contact_displaced)[it.second]);
     160             :   }
     161           0 :   PetscFunctionReturn(PETSC_SUCCESS);
     162             : }
     163             : 
     164             : PetscErrorCode
     165           0 : DMMooseGetUnContacts(DM dm,
     166             :                      std::vector<std::pair<std::string, std::string>> & uncontact_names,
     167             :                      std::vector<PetscBool> & displaced)
     168             : {
     169             :   PetscFunctionBegin;
     170           0 :   LibmeshPetscCallQ(DMMooseValidityCheck(dm));
     171           0 :   DM_Moose * dmm = (DM_Moose *)dm->data;
     172           0 :   for (const auto & it : *(dmm->_uncontact_names))
     173             :   {
     174           0 :     uncontact_names.push_back(it.second);
     175           0 :     displaced.push_back((*dmm->_uncontact_displaced)[it.second]);
     176             :   }
     177           0 :   PetscFunctionReturn(PETSC_SUCCESS);
     178             : }
     179             : 
     180             : PetscErrorCode
     181           0 : DMMooseGetSides(DM dm, std::vector<std::string> & side_names)
     182             : {
     183             :   PetscFunctionBegin;
     184           0 :   LibmeshPetscCallQ(DMMooseValidityCheck(dm));
     185           0 :   DM_Moose * dmm = (DM_Moose *)dm->data;
     186           0 :   for (const auto & it : *(dmm->_side_ids))
     187           0 :     side_names.push_back(it.first);
     188           0 :   PetscFunctionReturn(PETSC_SUCCESS);
     189             : }
     190             : 
     191             : PetscErrorCode
     192           0 : DMMooseGetUnSides(DM dm, std::vector<std::string> & side_names)
     193             : {
     194             :   PetscFunctionBegin;
     195           0 :   LibmeshPetscCallQ(DMMooseValidityCheck(dm));
     196           0 :   DM_Moose * dmm = (DM_Moose *)dm->data;
     197           0 :   for (const auto & it : *(dmm->_unside_ids))
     198           0 :     side_names.push_back(it.first);
     199           0 :   PetscFunctionReturn(PETSC_SUCCESS);
     200             : }
     201             : 
     202             : PetscErrorCode
     203           0 : DMMooseGetBlocks(DM dm, std::vector<std::string> & block_names)
     204             : {
     205             :   PetscFunctionBegin;
     206           0 :   LibmeshPetscCallQ(DMMooseValidityCheck(dm));
     207           0 :   DM_Moose * dmm = (DM_Moose *)dm->data;
     208           0 :   for (const auto & it : *(dmm->_block_ids))
     209           0 :     block_names.push_back(it.first);
     210           0 :   PetscFunctionReturn(PETSC_SUCCESS);
     211             : }
     212             : 
     213             : PetscErrorCode
     214           0 : DMMooseGetVariables(DM dm, std::vector<std::string> & var_names)
     215             : {
     216             :   PetscFunctionBegin;
     217           0 :   LibmeshPetscCallQ(DMMooseValidityCheck(dm));
     218           0 :   DM_Moose * dmm = (DM_Moose *)(dm->data);
     219           0 :   for (const auto & it : *(dmm->_var_ids))
     220           0 :     var_names.push_back(it.first);
     221           0 :   PetscFunctionReturn(PETSC_SUCCESS);
     222             : }
     223             : 
     224             : PetscErrorCode
     225         443 : DMMooseSetNonlinearSystem(DM dm, NonlinearSystemBase & nl)
     226             : {
     227             :   PetscFunctionBegin;
     228         443 :   LibmeshPetscCallQ(DMMooseValidityCheck(dm));
     229         443 :   if (dm->setupcalled)
     230           0 :     SETERRQ(((PetscObject)dm)->comm,
     231             :             PETSC_ERR_ARG_WRONGSTATE,
     232             :             "Cannot reset the NonlinearSystem after DM has been set up.");
     233         443 :   DM_Moose * dmm = (DM_Moose *)(dm->data);
     234         443 :   dmm->_nl = &nl;
     235         443 :   PetscFunctionReturn(PETSC_SUCCESS);
     236             : }
     237             : 
     238             : PetscErrorCode
     239         443 : DMMooseSetDofMap(DM dm, const DofMapBase & dof_map)
     240             : {
     241             :   PetscFunctionBegin;
     242         443 :   LibmeshPetscCallQ(DMMooseValidityCheck(dm));
     243         443 :   if (dm->setupcalled)
     244           0 :     SETERRQ(((PetscObject)dm)->comm,
     245             :             PETSC_ERR_ARG_WRONGSTATE,
     246             :             "Cannot reset the degree of freedom map after DM has been set up.");
     247         443 :   DM_Moose * dmm = (DM_Moose *)(dm->data);
     248         443 :   dmm->_dof_map = &dof_map;
     249         443 :   PetscFunctionReturn(PETSC_SUCCESS);
     250             : }
     251             : 
     252             : PetscErrorCode
     253         443 : DMMooseSetSystem(DM dm, const System & system)
     254             : {
     255             :   PetscFunctionBegin;
     256         443 :   LibmeshPetscCallQ(DMMooseValidityCheck(dm));
     257         443 :   if (dm->setupcalled)
     258           0 :     SETERRQ(((PetscObject)dm)->comm,
     259             :             PETSC_ERR_ARG_WRONGSTATE,
     260             :             "Cannot reset the degree of freedom map after DM has been set up.");
     261         443 :   DM_Moose * dmm = (DM_Moose *)(dm->data);
     262         443 :   dmm->_system = &system;
     263         443 :   PetscFunctionReturn(PETSC_SUCCESS);
     264             : }
     265             : 
     266             : PetscErrorCode
     267         443 : DMMooseSetName(DM dm, const std::string & dm_name)
     268             : {
     269             :   PetscFunctionBegin;
     270         443 :   LibmeshPetscCallQ(DMMooseValidityCheck(dm));
     271         443 :   if (dm->setupcalled)
     272           0 :     SETERRQ(((PetscObject)dm)->comm,
     273             :             PETSC_ERR_ARG_WRONGSTATE,
     274             :             "Cannot reset the MOOSE DM name after DM has been set up.");
     275         443 :   DM_Moose * dmm = (DM_Moose *)(dm->data);
     276         443 :   *dmm->_name = dm_name;
     277         443 :   PetscFunctionReturn(PETSC_SUCCESS);
     278             : }
     279             : 
     280             : PetscErrorCode
     281         314 : DMMooseSetParentDM(DM dm, DM_Moose * parent)
     282             : {
     283             :   PetscFunctionBegin;
     284         314 :   LibmeshPetscCallQ(DMMooseValidityCheck(dm));
     285         314 :   if (dm->setupcalled)
     286           0 :     SETERRQ(((PetscObject)dm)->comm,
     287             :             PETSC_ERR_ARG_WRONGSTATE,
     288             :             "Cannot reset the parent DM after the child DM has been set up.");
     289             : 
     290         314 :   DM_Moose * dmm = (DM_Moose *)(dm->data);
     291         314 :   dmm->_parent = parent;
     292         314 :   PetscFunctionReturn(PETSC_SUCCESS);
     293             : }
     294             : 
     295             : PetscErrorCode
     296         302 : DMMooseSetVariables(DM dm, const std::set<std::string> & vars)
     297             : {
     298         302 :   DM_Moose * dmm = (DM_Moose *)dm->data;
     299             : 
     300             :   PetscFunctionBegin;
     301         302 :   LibmeshPetscCallQ(DMMooseValidityCheck(dm));
     302         302 :   if (dm->setupcalled)
     303           0 :     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Not for an already setup DM");
     304         302 :   if (dmm->_vars)
     305           0 :     delete dmm->_vars;
     306         302 :   std::set<std::string> processed_vars;
     307         604 :   for (const auto & var_name : vars)
     308             :   {
     309             :     const auto * const var =
     310         302 :         dmm->_nl->hasVariable(var_name)
     311         302 :             ? static_cast<MooseVariableBase *>(&dmm->_nl->getVariable(0, var_name))
     312           3 :             : static_cast<MooseVariableBase *>(&dmm->_nl->getScalarVariable(0, var_name));
     313         302 :     if (var->isArray())
     314          36 :       for (const auto i : make_range(var->count()))
     315          24 :         processed_vars.insert(var->arrayVariableComponent(i));
     316             :     else
     317         290 :       processed_vars.insert(var_name);
     318             :   }
     319             : 
     320         302 :   dmm->_vars = new std::set<std::string>(std::move(processed_vars));
     321         302 :   PetscFunctionReturn(PETSC_SUCCESS);
     322         302 : }
     323             : 
     324             : PetscErrorCode
     325           0 : DMMooseSetBlocks(DM dm, const std::set<std::string> & blocks)
     326             : {
     327           0 :   DM_Moose * dmm = (DM_Moose *)dm->data;
     328             : 
     329             :   PetscFunctionBegin;
     330           0 :   LibmeshPetscCallQ(DMMooseValidityCheck(dm));
     331           0 :   if (dm->setupcalled)
     332           0 :     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Not for an already setup DM");
     333           0 :   if (dmm->_blocks)
     334           0 :     delete dmm->_blocks;
     335           0 :   dmm->_blocks = new std::set<std::string>(blocks);
     336           0 :   PetscFunctionReturn(PETSC_SUCCESS);
     337             : }
     338             : 
     339             : PetscErrorCode
     340          40 : DMMooseSetSides(DM dm, const std::set<std::string> & sides)
     341             : {
     342          40 :   DM_Moose * dmm = (DM_Moose *)dm->data;
     343             : 
     344             :   PetscFunctionBegin;
     345          40 :   LibmeshPetscCallQ(DMMooseValidityCheck(dm));
     346          40 :   if (dm->setupcalled)
     347           0 :     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Not for an already setup DM");
     348          40 :   if (dmm->_sides)
     349           0 :     delete dmm->_sides;
     350          40 :   dmm->_sides = new std::set<std::string>(sides);
     351          40 :   PetscFunctionReturn(PETSC_SUCCESS);
     352             : }
     353             : 
     354             : PetscErrorCode
     355          28 : DMMooseSetUnSides(DM dm, const std::set<std::string> & unsides)
     356             : {
     357          28 :   DM_Moose * dmm = (DM_Moose *)dm->data;
     358             : 
     359             :   PetscFunctionBegin;
     360          28 :   LibmeshPetscCallQ(DMMooseValidityCheck(dm));
     361          28 :   if (dm->setupcalled)
     362           0 :     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Not for an already setup DM");
     363          28 :   if (dmm->_unsides)
     364           0 :     delete dmm->_unsides;
     365          28 :   dmm->_unsides = new std::set<std::string>(unsides);
     366          28 :   PetscFunctionReturn(PETSC_SUCCESS);
     367             : }
     368             : 
     369             : PetscErrorCode
     370          12 : DMMooseSetUnSideByVar(DM dm, const std::set<std::string> & unside_by_var)
     371             : {
     372          12 :   DM_Moose * dmm = (DM_Moose *)dm->data;
     373             : 
     374             :   PetscFunctionBegin;
     375          12 :   LibmeshPetscCallQ(DMMooseValidityCheck(dm));
     376          12 :   if (dm->setupcalled)
     377           0 :     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Not for an already setup DM");
     378          12 :   if (dmm->_unside_by_var)
     379           0 :     delete dmm->_unside_by_var;
     380          12 :   dmm->_unside_by_var = new std::set<std::string>(unside_by_var);
     381          12 :   PetscFunctionReturn(PETSC_SUCCESS);
     382             : }
     383             : 
     384             : PetscErrorCode
     385           0 : DMMooseSetContacts(DM dm,
     386             :                    const std::vector<std::pair<std::string, std::string>> & contacts,
     387             :                    const std::vector<PetscBool> & displaced)
     388             : {
     389           0 :   DM_Moose * dmm = (DM_Moose *)dm->data;
     390             : 
     391             :   PetscFunctionBegin;
     392           0 :   LibmeshPetscCallQ(DMMooseValidityCheck(dm));
     393           0 :   if (dm->setupcalled)
     394           0 :     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Not for an already setup DM");
     395           0 :   if (contacts.size() != displaced.size())
     396           0 :     LIBMESH_SETERRQ2(PETSC_COMM_SELF,
     397             :                      PETSC_ERR_ARG_SIZ,
     398             :                      "Nonmatching sizes of the contact and displaced arrays: %" LIBMESH_PETSCINT_FMT
     399             :                      " != %" LIBMESH_PETSCINT_FMT,
     400             :                      static_cast<PetscInt>(contacts.size()),
     401             :                      static_cast<PetscInt>(displaced.size()));
     402           0 :   if (dmm->_contacts)
     403           0 :     delete dmm->_contacts;
     404           0 :   dmm->_contact_displaced->clear();
     405           0 :   dmm->_contacts = new std::set<DM_Moose::ContactName>();
     406           0 :   for (unsigned int i = 0; i < contacts.size(); ++i)
     407             :   {
     408           0 :     dmm->_contacts->insert(contacts[i]);
     409           0 :     dmm->_contact_displaced->insert(std::make_pair(contacts[i], displaced[i]));
     410             :   }
     411           0 :   PetscFunctionReturn(PETSC_SUCCESS);
     412             : }
     413             : 
     414             : PetscErrorCode
     415           0 : DMMooseSetUnContacts(DM dm,
     416             :                      const std::vector<std::pair<std::string, std::string>> & uncontacts,
     417             :                      const std::vector<PetscBool> & displaced)
     418             : {
     419           0 :   DM_Moose * dmm = (DM_Moose *)dm->data;
     420             : 
     421             :   PetscFunctionBegin;
     422           0 :   LibmeshPetscCallQ(DMMooseValidityCheck(dm));
     423           0 :   if (dm->setupcalled)
     424           0 :     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Not for an already setup DM");
     425           0 :   if (uncontacts.size() != displaced.size())
     426           0 :     LIBMESH_SETERRQ2(
     427             :         PETSC_COMM_SELF,
     428             :         PETSC_ERR_ARG_SIZ,
     429             :         "Nonmatching sizes of the uncontact and displaced arrays: %" LIBMESH_PETSCINT_FMT
     430             :         " != %" LIBMESH_PETSCINT_FMT,
     431             :         static_cast<PetscInt>(uncontacts.size()),
     432             :         static_cast<PetscInt>(displaced.size()));
     433           0 :   if (dmm->_uncontacts)
     434           0 :     delete dmm->_uncontacts;
     435           0 :   dmm->_uncontact_displaced->clear();
     436           0 :   dmm->_uncontacts = new std::set<DM_Moose::ContactName>();
     437           0 :   for (unsigned int i = 0; i < uncontacts.size(); ++i)
     438             :   {
     439           0 :     dmm->_uncontacts->insert(uncontacts[i]);
     440           0 :     dmm->_uncontact_displaced->insert(std::make_pair(uncontacts[i], displaced[i]));
     441             :   }
     442           0 :   PetscFunctionReturn(PETSC_SUCCESS);
     443             : }
     444             : 
     445             : PetscErrorCode
     446           0 : DMMooseGetNonlinearSystem(DM dm, NonlinearSystemBase *& nl)
     447             : {
     448             :   PetscFunctionBegin;
     449           0 :   LibmeshPetscCallQ(DMMooseValidityCheck(dm));
     450           0 :   DM_Moose * dmm = (DM_Moose *)(dm->data);
     451           0 :   nl = dmm->_nl;
     452           0 :   PetscFunctionReturn(PETSC_SUCCESS);
     453             : }
     454             : 
     455             : PetscErrorCode
     456         161 : DMMooseSetSplitNames(DM dm, const std::vector<std::string> & split_names)
     457             : {
     458             :   PetscFunctionBegin;
     459         161 :   LibmeshPetscCallQ(DMMooseValidityCheck(dm));
     460         161 :   DM_Moose * dmm = (DM_Moose *)(dm->data);
     461             : 
     462         161 :   if (dmm->_splits)
     463             :   {
     464         161 :     for (auto & it : *(dmm->_splits))
     465             :     {
     466           0 :       LibmeshPetscCallQ(DMDestroy(&(it.second._dm)));
     467           0 :       LibmeshPetscCallQ(ISDestroy(&(it.second._rembedding)));
     468             :     }
     469         161 :     delete dmm->_splits;
     470         161 :     dmm->_splits = LIBMESH_PETSC_NULLPTR;
     471             :   }
     472         161 :   if (dmm->_splitlocs)
     473             :   {
     474           0 :     delete dmm->_splitlocs;
     475           0 :     dmm->_splitlocs = LIBMESH_PETSC_NULLPTR;
     476             :   }
     477         161 :   dmm->_splits = new std::map<std::string, DM_Moose::SplitInfo>();
     478         161 :   dmm->_splitlocs = new std::multimap<std::string, unsigned int>();
     479         483 :   for (unsigned int i = 0; i < split_names.size(); ++i)
     480             :   {
     481             :     DM_Moose::SplitInfo info;
     482         322 :     info._dm = LIBMESH_PETSC_NULLPTR;
     483         322 :     info._rembedding = LIBMESH_PETSC_NULLPTR;
     484         322 :     std::string name = split_names[i];
     485         322 :     (*dmm->_splits)[name] = info;
     486         322 :     dmm->_splitlocs->insert(std::make_pair(name, i));
     487         322 :   }
     488         161 :   PetscFunctionReturn(PETSC_SUCCESS);
     489             : }
     490             : 
     491             : PetscErrorCode
     492           0 : DMMooseGetSplitNames(DM dm, std::vector<std::string> & split_names)
     493             : {
     494             :   PetscFunctionBegin;
     495           0 :   LibmeshPetscCallQ(DMMooseValidityCheck(dm));
     496           0 :   DM_Moose * dmm = (DM_Moose *)(dm->data);
     497           0 :   if (!dm->setupcalled)
     498           0 :     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM not set up");
     499           0 :   split_names.clear();
     500           0 :   split_names.reserve(dmm->_splitlocs->size());
     501           0 :   if (dmm->_splitlocs && dmm->_splitlocs->size())
     502           0 :     for (const auto & lit : *(dmm->_splitlocs))
     503             :     {
     504           0 :       std::string sname = lit.first;
     505           0 :       unsigned int sloc = lit.second;
     506           0 :       split_names[sloc] = sname;
     507           0 :     }
     508           0 :   PetscFunctionReturn(PETSC_SUCCESS);
     509             : }
     510             : 
     511             : static PetscErrorCode
     512         314 : DMMooseGetEmbedding_Private(DM dm, IS * embedding)
     513             : {
     514         314 :   DM_Moose * dmm = (DM_Moose *)dm->data;
     515             : 
     516             :   PetscFunctionBegin;
     517         314 :   if (!embedding)
     518           0 :     PetscFunctionReturn(PETSC_SUCCESS);
     519         314 :   if (!dmm->_embedding)
     520             :   {
     521             :     // The rules interpreting the coexistence of blocks (un)sides/(un)contacts are these
     522             :     // [sides and contacts behave similarly, so 'sides' means 'sides/contacts']
     523             :     // ['ANY' means 'not NONE' and covers 'ALL' as well, unless there is a specific 'ALL' clause,
     524             :     // which overrides 'ANY'; 'NOT ALL' means not ALL and not NONE]
     525             :     // [there are always some blocks, since by default 'ALL' is assumed, unless it is overridden by
     526             :     // a specific list, which implies ANY]
     527             :     // In general,
     528             :     // (1)  ALL blocks      and ANY sides are interpreted as the INTERSECTION of blocks and sides,
     529             :     // equivalent to just the sides (since ALL blocks are assumed to be a cover).
     530             :     // (2)  NOT ALL blocks  and ANY or NO sides are interpreted as the UNION of blocks and sides.
     531             :     // (3a) ANY unsides and ANY blocks are interpreted as the DIFFERENCE of blocks and unsides.
     532             :     // (3b) ANY unsides and ANY sides are interpreted as the DIFFERENCE of sides and unsides.
     533             :     // (4)  NO  unsides means NO DIFFERENCE is needed.
     534             :     // The result is easily computed by first computing the result of (1 & 2) followed by difference
     535             :     // with the result of (3 & 4).
     536             :     // To simply (1 & 2) observe the following:
     537             :     // - The intersection is computed only if ALL blocks and ANY sides, and the result is the sides,
     538             :     // so block dofs do not need to be computed.
     539             :     // - Otherwise the union is computed, and initially consists of the blocks' dofs, to which the
     540             :     // sides' dofs are added, if ANY.
     541             :     // - The result is called 'indices'
     542             :     // To satisfy (3 & 4) simply cmpute subtrahend set 'unindices' as all of the unsides' dofs:
     543             :     // Then take the set difference of 'indices' and 'unindices', putting the result in 'dindices'.
     544         314 :     if (!dmm->_all_vars || !dmm->_all_blocks || !dmm->_nosides || !dmm->_nounsides ||
     545          12 :         !dmm->_nounside_by_var || !dmm->_nocontacts || !dmm->_nouncontacts)
     546             :     {
     547         314 :       auto & dofmap = *dmm->_dof_map;
     548             :       // Put this outside the lambda scope to avoid constant memory reallocation
     549         314 :       std::vector<dof_id_type> node_indices;
     550             :       auto process_nodal_dof_indices =
     551       40325 :           [&dofmap, &node_indices](const Node & node,
     552             :                                    const unsigned int var_num,
     553             :                                    std::set<dof_id_type> & local_indices,
     554             :                                    std::set<dof_id_type> * const nonlocal_indices = nullptr)
     555             :       {
     556       40325 :         dofmap.dof_indices(&node, node_indices, var_num);
     557       81370 :         for (const auto index : node_indices)
     558             :         {
     559       41045 :           if (index >= dofmap.first_dof() && index < dofmap.end_dof())
     560       40937 :             local_indices.insert(index);
     561         108 :           else if (nonlocal_indices)
     562           0 :             nonlocal_indices->insert(index);
     563             :         }
     564       40325 :       };
     565             : 
     566             :       auto process_elem_dof_indices =
     567       35482 :           [&dofmap](const std::vector<dof_id_type> & elem_indices,
     568             :                     std::set<dof_id_type> & local_indices,
     569             :                     std::set<dof_id_type> * const nonlocal_indices = nullptr)
     570             :       {
     571      133528 :         for (const auto index : elem_indices)
     572             :         {
     573       98046 :           if (index >= dofmap.first_dof() && index < dofmap.end_dof())
     574       96023 :             local_indices.insert(index);
     575        2023 :           else if (nonlocal_indices)
     576           0 :             nonlocal_indices->insert(index);
     577             :         }
     578       35482 :       };
     579             : 
     580         314 :       std::set<dof_id_type> indices;
     581         314 :       std::set<dof_id_type> unindices;
     582         314 :       std::set<dof_id_type> cached_indices;
     583         314 :       std::set<dof_id_type> cached_unindices;
     584         314 :       auto & lm_mesh = dmm->_system->get_mesh();
     585         314 :       const auto & node_to_elem_map = dmm->_nl->feProblem().mesh().nodeToElemMap();
     586         652 :       for (const auto & vit : *(dmm->_var_ids))
     587             :       {
     588         338 :         unsigned int v = vit.second;
     589             :         // Iterate only over this DM's blocks.
     590         338 :         if (!dmm->_all_blocks || (dmm->_nosides && dmm->_nocontacts))
     591         744 :           for (const auto & bit : *(dmm->_block_ids))
     592             :           {
     593         406 :             subdomain_id_type b = bit.second;
     594         812 :             for (const auto & elem : as_range(lm_mesh.active_local_subdomain_elements_begin(b),
     595       36700 :                                               lm_mesh.active_local_subdomain_elements_end(b)))
     596             :             {
     597             :               // Get the degree of freedom indices for the given variable off the current element.
     598       35482 :               std::vector<dof_id_type> evindices;
     599       35482 :               dofmap.dof_indices(elem, evindices, v);
     600       35482 :               process_elem_dof_indices(evindices, indices);
     601       35888 :             }
     602             : 
     603             :             // Sometime, we own nodes but do not own the elements the nodes are connected to
     604             :             {
     605         406 :               bool is_on_current_block = false;
     606       83801 :               for (auto & node : lm_mesh.local_node_ptr_range())
     607             :               {
     608       83395 :                 const unsigned int n_comp = node->n_comp(dmm->_system->number(), v);
     609             : 
     610             :                 // skip it if no dof
     611       83395 :                 if (!n_comp)
     612       43538 :                   continue;
     613             : 
     614       52493 :                 auto node_to_elem_pair = node_to_elem_map.find(node->id());
     615       52493 :                 is_on_current_block = false;
     616      114219 :                 for (const auto & elem_num : node_to_elem_pair->second)
     617             :                 {
     618             :                   // if one of incident elements belongs to a block, we consider
     619             :                   // the node lives in the block
     620      101583 :                   const Elem & neighbor_elem = lm_mesh.elem_ref(elem_num);
     621      101583 :                   if (neighbor_elem.subdomain_id() == b)
     622             :                   {
     623       39857 :                     is_on_current_block = true;
     624       39857 :                     break;
     625             :                   }
     626             :                 }
     627             :                 // we add indices for the current block only
     628       52493 :                 if (!is_on_current_block)
     629       12636 :                   continue;
     630             : 
     631       39857 :                 process_nodal_dof_indices(*node, v, indices);
     632         406 :               }
     633             :             }
     634             :           }
     635             : 
     636             :         // Iterate over the sides from this split.
     637         338 :         if (dmm->_side_ids->size())
     638             :         {
     639             :           // For some reason the following may return an empty node list
     640             :           // std::vector<dof_id_type> snodes;
     641             :           // std::vector<boundary_id_type> sides;
     642             :           // dmm->nl->system().get_mesh().get_boundary_info().build_node_list(snodes, sides);
     643             :           // // FIXME: make an array of (snode,side) pairs, sort on side and use std::lower_bound
     644             :           // from <algorithm>
     645             :           // for (dof_id_type i = 0; i < sides.size(); ++i) {
     646             :           //   boundary_id_type s = sides[i];
     647             :           //   if (!dmm->sidenames->count(s)) continue;
     648             :           //  const Node& node = dmm->nl->system().get_mesh().node_ref(snodes[i]);
     649             :           //  // determine v's dof on node and insert into indices
     650             :           // }
     651          40 :           ConstBndNodeRange & bnodes = *dmm->_nl->mesh().getBoundaryNodeRange();
     652         520 :           for (const auto & bnode : bnodes)
     653             :           {
     654         480 :             BoundaryID boundary_id = bnode->_bnd_id;
     655         480 :             if (dmm->_side_names->find(boundary_id) == dmm->_side_names->end())
     656         252 :               continue;
     657             : 
     658         228 :             const Node * node = bnode->_node;
     659         228 :             process_nodal_dof_indices(*node, v, indices);
     660             :           }
     661             :         }
     662             : 
     663             :         // Iterate over the sides excluded from this split.
     664         338 :         if (dmm->_unside_ids->size())
     665             :         {
     666          28 :           ConstBndNodeRange & bnodes = *dmm->_nl->mesh().getBoundaryNodeRange();
     667         364 :           for (const auto & bnode : bnodes)
     668             :           {
     669         336 :             BoundaryID boundary_id = bnode->_bnd_id;
     670         336 :             if (dmm->_unside_names->find(boundary_id) == dmm->_unside_names->end())
     671         168 :               continue;
     672         168 :             const Node * node = bnode->_node;
     673         168 :             process_nodal_dof_indices(*node, v, unindices);
     674             :           }
     675             :         }
     676         338 :         if (dmm->_unside_by_var_set->size())
     677             :         {
     678          24 :           std::set<BoundaryID> eligible_bids;
     679          72 :           for (const auto & [bid, var] : *(dmm->_unside_by_var_set))
     680          48 :             if (var == v)
     681          24 :               eligible_bids.insert(bid);
     682             : 
     683          24 :           ConstBndNodeRange & bnodes = *dmm->_nl->mesh().getBoundaryNodeRange();
     684         312 :           for (const auto & bnode : bnodes)
     685             :           {
     686         288 :             BoundaryID boundary_id = bnode->_bnd_id;
     687         288 :             if (eligible_bids.count(boundary_id))
     688             :             {
     689          72 :               const Node * node = bnode->_node;
     690          72 :               process_nodal_dof_indices(*node, v, unindices);
     691             :             }
     692             :           }
     693          24 :         }
     694             : 
     695             :         auto process_contact_all_nodes =
     696           0 :             [dmm, process_nodal_dof_indices, v](const auto & contact_names,
     697             :                                                 auto & indices_to_insert_to)
     698             :         {
     699           0 :           std::set<boundary_id_type> bc_id_set;
     700             :           // loop over contacts
     701           0 :           for (const auto & [contact_bid_pair, contact_bname_pair] : contact_names)
     702             :           {
     703           0 :             libmesh_ignore(contact_bname_pair);
     704           0 :             bc_id_set.insert(contact_bid_pair.first);  // primary
     705           0 :             bc_id_set.insert(contact_bid_pair.second); // secondary
     706             :           }
     707             :           // loop over boundary elements
     708           0 :           ConstBndElemRange & range = *dmm->_nl->feProblem().mesh().getBoundaryElementRange();
     709           0 :           for (const auto & belem : range)
     710             :           {
     711           0 :             const Elem * elem_bdry = belem->_elem;
     712           0 :             const auto side = belem->_side;
     713           0 :             BoundaryID boundary_id = belem->_bnd_id;
     714             : 
     715           0 :             if (bc_id_set.find(boundary_id) == bc_id_set.end())
     716           0 :               continue;
     717             : 
     718           0 :             for (const auto node_idx : elem_bdry->node_index_range())
     719           0 :               if (elem_bdry->is_node_on_side(node_idx, side))
     720           0 :                 process_nodal_dof_indices(elem_bdry->node_ref(node_idx), v, indices_to_insert_to);
     721             :           }
     722           0 :         };
     723             : 
     724             :         auto process_contact_some_nodes =
     725           0 :             [dmm, process_nodal_dof_indices, v, &dofmap, &lm_mesh, process_elem_dof_indices](
     726             :                 const auto & contact_names,
     727             :                 auto & indices_to_insert_to,
     728             :                 auto & nonlocal_indices_to_insert_to)
     729             :         {
     730           0 :           std::vector<dof_id_type> evindices;
     731           0 :           for (const auto & it : contact_names)
     732             :           {
     733           0 :             PetscBool displaced = (*dmm->_uncontact_displaced)[it.second];
     734             :             PenetrationLocator * locator;
     735           0 :             if (displaced)
     736             :             {
     737           0 :               std::shared_ptr<DisplacedProblem> displaced_problem =
     738           0 :                   dmm->_nl->feProblem().getDisplacedProblem();
     739           0 :               if (!displaced_problem)
     740             :               {
     741           0 :                 std::ostringstream err;
     742           0 :                 err << "Cannot use a displaced uncontact (" << it.second.first << ","
     743           0 :                     << it.second.second << ") with an undisplaced problem";
     744           0 :                 mooseError(err.str());
     745           0 :               }
     746           0 :               locator = displaced_problem->geomSearchData()._penetration_locators[it.first];
     747           0 :             }
     748             :             else
     749           0 :               locator = dmm->_nl->feProblem().geomSearchData()._penetration_locators[it.first];
     750             : 
     751           0 :             evindices.clear();
     752             :             // penetration locator
     753           0 :             auto lend = locator->_penetration_info.end();
     754           0 :             for (auto lit = locator->_penetration_info.begin(); lit != lend; ++lit)
     755             :             {
     756           0 :               const dof_id_type secondary_node_num = lit->first;
     757           0 :               PenetrationInfo * pinfo = lit->second;
     758           0 :               if (pinfo && pinfo->isCaptured())
     759             :               {
     760           0 :                 const Node & secondary_node = lm_mesh.node_ref(secondary_node_num);
     761           0 :                 process_nodal_dof_indices(
     762             :                     secondary_node, v, indices_to_insert_to, &nonlocal_indices_to_insert_to);
     763             : 
     764             :                 // indices for primary element
     765           0 :                 evindices.clear();
     766           0 :                 const Elem * primary_side = pinfo->_side;
     767           0 :                 dofmap.dof_indices(primary_side, evindices, v);
     768           0 :                 process_elem_dof_indices(
     769             :                     evindices, indices_to_insert_to, &nonlocal_indices_to_insert_to);
     770             :               } // if pinfo
     771             :             } // for penetration
     772             :           } // for contact name
     773           0 :         };
     774             : 
     775             :         // Include all nodes on the contact surfaces
     776         338 :         if (dmm->_contact_names->size() && dmm->_include_all_contact_nodes)
     777           0 :           process_contact_all_nodes(*dmm->_contact_names, indices);
     778             : 
     779             :         // Iterate over the contacts included in this split.
     780         338 :         if (dmm->_contact_names->size() && !(dmm->_include_all_contact_nodes))
     781           0 :           process_contact_some_nodes(*dmm->_contact_names, indices, cached_indices);
     782             : 
     783             :         // Exclude all nodes on the contact surfaces
     784         338 :         if (dmm->_uncontact_names->size() && dmm->_include_all_contact_nodes)
     785           0 :           process_contact_all_nodes(*dmm->_uncontact_names, unindices);
     786             : 
     787             :         // Iterate over the contacts excluded from this split.
     788         338 :         if (dmm->_uncontact_names->size() && !(dmm->_include_all_contact_nodes))
     789           0 :           process_contact_some_nodes(*dmm->_uncontact_names, unindices, cached_unindices);
     790             :       } // variables
     791             : 
     792         314 :       std::vector<dof_id_type> local_vec_indices(cached_indices.size());
     793         314 :       std::copy(cached_indices.begin(), cached_indices.end(), local_vec_indices.begin());
     794         314 :       if (dmm->_contact_names->size() && !(dmm->_include_all_contact_nodes))
     795           0 :         dmm->_nl->feProblem().mesh().comm().allgather(local_vec_indices, false);
     796             :       // insert indices
     797         314 :       for (const auto & dof : local_vec_indices)
     798           0 :         if (dof >= dofmap.first_dof() && dof < dofmap.end_dof())
     799           0 :           indices.insert(dof);
     800             : 
     801         314 :       local_vec_indices.clear();
     802         314 :       local_vec_indices.resize(cached_unindices.size());
     803         314 :       std::copy(cached_unindices.begin(), cached_unindices.end(), local_vec_indices.begin());
     804         314 :       if (dmm->_uncontact_names->size() && !(dmm->_include_all_contact_nodes))
     805           0 :         dmm->_nl->feProblem().mesh().comm().allgather(local_vec_indices, false);
     806             :       // insert unindices
     807         314 :       for (const auto & dof : local_vec_indices)
     808           0 :         if (dof >= dofmap.first_dof() && dof < dofmap.end_dof())
     809           0 :           unindices.insert(dof);
     810             : 
     811         314 :       std::set<dof_id_type> dindices;
     812         314 :       std::set_difference(indices.begin(),
     813             :                           indices.end(),
     814             :                           unindices.begin(),
     815             :                           unindices.end(),
     816             :                           std::inserter(dindices, dindices.end()));
     817             :       PetscInt * darray;
     818         314 :       LibmeshPetscCallQ(PetscMalloc(sizeof(PetscInt) * dindices.size(), &darray));
     819         314 :       dof_id_type i = 0;
     820       38173 :       for (const auto & dof : dindices)
     821             :       {
     822       37859 :         darray[i] = dof;
     823       37859 :         ++i;
     824             :       }
     825         314 :       LibmeshPetscCallQ(ISCreateGeneral(
     826             :           ((PetscObject)dm)->comm, dindices.size(), darray, PETSC_OWN_POINTER, &dmm->_embedding));
     827         314 :     }
     828             :     else
     829             :     {
     830             :       // if (dmm->allblocks && dmm->allvars && dmm->nosides && dmm->nounsides && dmm->nocontacts &&
     831             :       // dmm->nouncontacts)
     832             :       // DMCreateGlobalVector is defined()
     833             :       Vec v;
     834             :       PetscInt low, high;
     835             : 
     836           0 :       LibmeshPetscCallQ(DMCreateGlobalVector(dm, &v));
     837           0 :       LibmeshPetscCallQ(VecGetOwnershipRange(v, &low, &high));
     838           0 :       LibmeshPetscCallQ(
     839             :           ISCreateStride(((PetscObject)dm)->comm, (high - low), low, 1, &dmm->_embedding));
     840             :     }
     841             :   }
     842         314 :   LibmeshPetscCallQ(PetscObjectReference((PetscObject)(dmm->_embedding)));
     843         314 :   *embedding = dmm->_embedding;
     844             : 
     845         314 :   PetscFunctionReturn(PETSC_SUCCESS);
     846             : }
     847             : 
     848             : static PetscErrorCode
     849         157 : DMCreateFieldDecomposition_Moose(
     850             :     DM dm, PetscInt * len, char *** namelist, IS ** islist, DM ** dmlist)
     851             : {
     852         157 :   DM_Moose * dmm = (DM_Moose *)(dm->data);
     853             : 
     854             :   PetscFunctionBegin;
     855             : 
     856         157 :   PetscInt split_size_sum = 0;
     857             : 
     858             :   /* Only called after DMSetUp(). */
     859         157 :   if (!dmm->_splitlocs)
     860           0 :     PetscFunctionReturn(PETSC_SUCCESS);
     861         157 :   *len = dmm->_splitlocs->size();
     862         157 :   if (namelist)
     863         157 :     LibmeshPetscCallQ(PetscMalloc(*len * sizeof(char *), namelist));
     864         157 :   if (islist)
     865         157 :     LibmeshPetscCallQ(PetscMalloc(*len * sizeof(IS), islist));
     866         157 :   if (dmlist)
     867         157 :     LibmeshPetscCallQ(PetscMalloc(*len * sizeof(DM), dmlist));
     868         471 :   for (const auto & dit : *(dmm->_splitlocs))
     869             :   {
     870         314 :     unsigned int d = dit.second;
     871         314 :     std::string dname = dit.first;
     872         314 :     DM_Moose::SplitInfo & dinfo = (*dmm->_splits)[dname];
     873         314 :     if (!dinfo._dm)
     874             :     {
     875         314 :       LibmeshPetscCallQ(DMCreateMoose(
     876             :           ((PetscObject)dm)->comm, *dmm->_nl, *dmm->_dof_map, *dmm->_system, dname, &dinfo._dm));
     877         314 :       LibmeshPetscCallQ(
     878             :           PetscObjectSetOptionsPrefix((PetscObject)dinfo._dm, ((PetscObject)dm)->prefix));
     879         314 :       std::string suffix = std::string("fieldsplit_") + dname + "_";
     880         314 :       LibmeshPetscCallQ(PetscObjectAppendOptionsPrefix((PetscObject)dinfo._dm, suffix.c_str()));
     881         314 :       LibmeshPetscCallQ(DMMooseSetParentDM(dinfo._dm, dmm));
     882         314 :     }
     883         314 :     LibmeshPetscCallQ(DMSetFromOptions(dinfo._dm));
     884         314 :     LibmeshPetscCallQ(DMSetUp(dinfo._dm));
     885         314 :     if (namelist)
     886         314 :       LibmeshPetscCallQ(PetscStrallocpy(dname.c_str(), (*namelist) + d));
     887         314 :     if (islist)
     888             :     {
     889         314 :       if (!dinfo._rembedding)
     890             :       {
     891             :         IS dembedding, lembedding;
     892         314 :         LibmeshPetscCallQ(DMMooseGetEmbedding_Private(dinfo._dm, &dembedding));
     893         314 :         if (dmm->_embedding)
     894             :         {
     895             :           // Create a relative embedding into the parent's index space.
     896          56 :           LibmeshPetscCallQ(ISEmbed(dembedding, dmm->_embedding, PETSC_TRUE, &lembedding));
     897             :           const PetscInt * lindices;
     898             :           PetscInt len, dlen, llen, *rindices, off, i;
     899          56 :           LibmeshPetscCallQ(ISGetLocalSize(dembedding, &dlen));
     900          56 :           LibmeshPetscCallQ(ISGetLocalSize(lembedding, &llen));
     901          56 :           if (llen != dlen)
     902           0 :             LIBMESH_SETERRQ1(
     903             :                 ((PetscObject)dm)->comm, PETSC_ERR_PLIB, "Failed to embed split %u", d);
     904          56 :           LibmeshPetscCallQ(ISDestroy(&dembedding));
     905             :           // Convert local embedding to global (but still relative) embedding
     906          56 :           LibmeshPetscCallQ(PetscMalloc(llen * sizeof(PetscInt), &rindices));
     907          56 :           LibmeshPetscCallQ(ISGetIndices(lembedding, &lindices));
     908          56 :           LibmeshPetscCallQ(PetscMemcpy(rindices, lindices, llen * sizeof(PetscInt)));
     909          56 :           LibmeshPetscCallQ(ISDestroy(&lembedding));
     910             :           // We could get the index offset from a corresponding global vector, but subDMs don't yet
     911             :           // have global vectors
     912          56 :           LibmeshPetscCallQ(ISGetLocalSize(dmm->_embedding, &len));
     913             : 
     914          56 :           MPI_Scan(&len,
     915             :                    &off,
     916             :                    1,
     917             : #ifdef PETSC_USE_64BIT_INDICES
     918             :                    MPI_LONG_LONG_INT,
     919             : #else
     920             :                    MPI_INT,
     921             : #endif
     922             :                    MPI_SUM,
     923             :                    ((PetscObject)dm)->comm);
     924             : 
     925          56 :           off -= len;
     926         242 :           for (i = 0; i < llen; ++i)
     927         186 :             rindices[i] += off;
     928          56 :           LibmeshPetscCallQ(ISCreateGeneral(
     929             :               ((PetscObject)dm)->comm, llen, rindices, PETSC_OWN_POINTER, &(dinfo._rembedding)));
     930             :         }
     931             :         else
     932             :         {
     933         258 :           dinfo._rembedding = dembedding;
     934             :         }
     935             :       }
     936         314 :       LibmeshPetscCallQ(PetscObjectReference((PetscObject)(dinfo._rembedding)));
     937         314 :       (*islist)[d] = dinfo._rembedding;
     938             :       PetscInt is_size;
     939         314 :       LibmeshPetscCallQ(ISGetLocalSize(dinfo._rembedding, &is_size));
     940         314 :       split_size_sum += is_size;
     941             :     }
     942         314 :     if (dmlist)
     943             :     {
     944         314 :       LibmeshPetscCallQ(PetscObjectReference((PetscObject)dinfo._dm));
     945         314 :       (*dmlist)[d] = dinfo._dm;
     946             :     }
     947         314 :   }
     948             : 
     949             :   mooseAssert(islist, "What does it even mean if this is NULL?");
     950             : 
     951         157 :   if (dmm->_parent)
     952          28 :     dmm->_parent->checkChildSize(dm, split_size_sum, *dmm->_name);
     953             :   else
     954         129 :     checkSize(*dmm->_name, split_size_sum, dmm->_dof_map->n_local_dofs());
     955             : 
     956         149 :   PetscFunctionReturn(PETSC_SUCCESS);
     957             : }
     958             : 
     959             : static PetscErrorCode
     960           0 : DMCreateDomainDecomposition_Moose(
     961             :     DM dm, PetscInt * len, char *** namelist, IS ** innerislist, IS ** outerislist, DM ** dmlist)
     962             : {
     963             :   PetscFunctionBegin;
     964             :   /* Use DMCreateFieldDecomposition_Moose() to obtain everything but outerislist, which is currently
     965             :    * LIBMESH_PETSC_NULLPTR. */
     966           0 :   if (outerislist)
     967           0 :     *outerislist = LIBMESH_PETSC_NULLPTR; /* FIX: allow mesh-based overlap. */
     968           0 :   LibmeshPetscCallQ(DMCreateFieldDecomposition_Moose(dm, len, namelist, innerislist, dmlist));
     969           0 :   PetscFunctionReturn(PETSC_SUCCESS);
     970             : }
     971             : 
     972             : static PetscErrorCode
     973           0 : DMMooseFunction(DM dm, Vec x, Vec r)
     974             : {
     975             :   PetscFunctionBegin;
     976             :   libmesh_assert(x);
     977             :   libmesh_assert(r);
     978             : 
     979           0 :   NonlinearSystemBase * nl = NULL;
     980           0 :   LibmeshPetscCallQ(DMMooseGetNonlinearSystem(dm, nl));
     981           0 :   PetscVector<Number> & X_sys = *cast_ptr<PetscVector<Number> *>(nl->system().solution.get());
     982           0 :   PetscVector<Number> X_global(x, nl->comm()), R(r, nl->comm());
     983             : 
     984             :   // Use the system's update() to get a good local version of the
     985             :   // parallel solution.  system.update() does change the residual vector,
     986             :   // so there's no reason to swap PETSc's residual into the system for
     987             :   // this step.
     988           0 :   X_global.swap(X_sys);
     989           0 :   nl->system().update();
     990           0 :   X_global.swap(X_sys);
     991             : 
     992             :   // Enforce constraints (if any) exactly on the
     993             :   // current_local_solution.  This is the solution vector that is
     994             :   // actually used in the computation of the residual below, and is
     995             :   // not locked by debug-enabled PETSc the way that "x" is.
     996           0 :   nl->system().get_dof_map().enforce_constraints_exactly(nl->system(),
     997           0 :                                                          nl->system().current_local_solution.get());
     998             : 
     999             :   // Zero the residual vector before assembling
    1000           0 :   R.zero();
    1001             : 
    1002             :   // if the user has provided both function pointers and objects only the pointer
    1003             :   // will be used, so catch that as an error
    1004           0 :   if (nl->nonlinearSolver()->residual && nl->nonlinearSolver()->residual_object)
    1005             :   {
    1006           0 :     std::ostringstream err;
    1007           0 :     err << "ERROR: cannot specifiy both a function and object to compute the Residual!"
    1008           0 :         << std::endl;
    1009           0 :     mooseError(err.str());
    1010           0 :   }
    1011           0 :   if (nl->nonlinearSolver()->matvec && nl->nonlinearSolver()->residual_and_jacobian_object)
    1012             :   {
    1013           0 :     std::ostringstream err;
    1014             :     err << "ERROR: cannot specifiy both a function and object to compute the combined Residual & "
    1015           0 :            "Jacobian!"
    1016           0 :         << std::endl;
    1017           0 :     mooseError(err.str());
    1018           0 :   }
    1019           0 :   if (nl->nonlinearSolver()->residual != NULL)
    1020           0 :     nl->nonlinearSolver()->residual(
    1021           0 :         *(nl->system().current_local_solution.get()), R, nl->nonlinearSolver()->system());
    1022           0 :   else if (nl->nonlinearSolver()->residual_object != NULL)
    1023           0 :     nl->nonlinearSolver()->residual_object->residual(
    1024           0 :         *(nl->system().current_local_solution.get()), R, nl->nonlinearSolver()->system());
    1025           0 :   else if (nl->nonlinearSolver()->matvec != NULL)
    1026           0 :     nl->nonlinearSolver()->matvec(
    1027           0 :         *(nl->system().current_local_solution.get()), &R, NULL, nl->nonlinearSolver()->system());
    1028           0 :   else if (nl->nonlinearSolver()->residual_and_jacobian_object != NULL)
    1029           0 :     nl->nonlinearSolver()->residual_and_jacobian_object->residual_and_jacobian(
    1030           0 :         *(nl->system().current_local_solution.get()), &R, NULL, nl->nonlinearSolver()->system());
    1031             :   else
    1032             :   {
    1033           0 :     std::ostringstream err;
    1034           0 :     err << "No suitable residual computation routine found";
    1035           0 :     mooseError(err.str());
    1036           0 :   }
    1037           0 :   R.close();
    1038           0 :   PetscFunctionReturn(PETSC_SUCCESS);
    1039           0 : }
    1040             : 
    1041             : static PetscErrorCode
    1042           0 : SNESFunction_DMMoose(SNES, Vec x, Vec r, void * ctx)
    1043             : {
    1044           0 :   DM dm = (DM)ctx;
    1045             : 
    1046             :   PetscFunctionBegin;
    1047           0 :   LibmeshPetscCallQ(DMMooseFunction(dm, x, r));
    1048           0 :   PetscFunctionReturn(PETSC_SUCCESS);
    1049             : }
    1050             : 
    1051             : static PetscErrorCode
    1052           0 : DMMooseJacobian(DM dm, Vec x, Mat jac, Mat pc)
    1053             : {
    1054           0 :   NonlinearSystemBase * nl = NULL;
    1055             : 
    1056             :   PetscFunctionBegin;
    1057           0 :   LibmeshPetscCallQ(DMMooseGetNonlinearSystem(dm, nl));
    1058             : 
    1059           0 :   PetscMatrix<Number> the_pc(pc, nl->comm());
    1060           0 :   PetscMatrix<Number> Jac(jac, nl->comm());
    1061           0 :   PetscVector<Number> & X_sys = *cast_ptr<PetscVector<Number> *>(nl->system().solution.get());
    1062           0 :   PetscVector<Number> X_global(x, nl->comm());
    1063             : 
    1064             :   // Set the dof maps
    1065           0 :   the_pc.attach_dof_map(nl->system().get_dof_map());
    1066           0 :   Jac.attach_dof_map(nl->system().get_dof_map());
    1067             : 
    1068             :   // Use the system's update() to get a good local version of the
    1069             :   // parallel solution.  system.update() does change the Jacobian, so
    1070             :   // there's no reason to swap PETSc's Jacobian into the system for
    1071             :   // this step.
    1072           0 :   X_global.swap(X_sys);
    1073           0 :   nl->system().update();
    1074           0 :   X_global.swap(X_sys);
    1075             : 
    1076             :   // Enforce constraints (if any) exactly on the
    1077             :   // current_local_solution.  This is the solution vector that is
    1078             :   // actually used in the computation of the Jacobian below, and is
    1079             :   // not locked by debug-enabled PETSc the way that "x" is.
    1080           0 :   nl->system().get_dof_map().enforce_constraints_exactly(nl->system(),
    1081           0 :                                                          nl->system().current_local_solution.get());
    1082             : 
    1083             :   // Zero out the preconditioner before computing the Jacobian.
    1084           0 :   the_pc.zero();
    1085             : 
    1086             :   // if the user has provided both function pointers and objects only the pointer
    1087             :   // will be used, so catch that as an error
    1088           0 :   if (nl->nonlinearSolver()->jacobian && nl->nonlinearSolver()->jacobian_object)
    1089             :   {
    1090           0 :     std::ostringstream err;
    1091           0 :     err << "ERROR: cannot specifiy both a function and object to compute the Jacobian!"
    1092           0 :         << std::endl;
    1093           0 :     mooseError(err.str());
    1094           0 :   }
    1095           0 :   if (nl->nonlinearSolver()->matvec && nl->nonlinearSolver()->residual_and_jacobian_object)
    1096             :   {
    1097           0 :     std::ostringstream err;
    1098             :     err << "ERROR: cannot specifiy both a function and object to compute the combined Residual & "
    1099           0 :            "Jacobian!"
    1100           0 :         << std::endl;
    1101           0 :     mooseError(err.str());
    1102           0 :   }
    1103           0 :   if (nl->nonlinearSolver()->jacobian != NULL)
    1104           0 :     nl->nonlinearSolver()->jacobian(
    1105           0 :         *(nl->system().current_local_solution.get()), the_pc, nl->nonlinearSolver()->system());
    1106           0 :   else if (nl->nonlinearSolver()->jacobian_object != NULL)
    1107           0 :     nl->nonlinearSolver()->jacobian_object->jacobian(
    1108           0 :         *(nl->system().current_local_solution.get()), the_pc, nl->nonlinearSolver()->system());
    1109           0 :   else if (nl->nonlinearSolver()->matvec != NULL)
    1110           0 :     nl->nonlinearSolver()->matvec(*(nl->system().current_local_solution.get()),
    1111             :                                   NULL,
    1112             :                                   &the_pc,
    1113           0 :                                   nl->nonlinearSolver()->system());
    1114           0 :   else if (nl->nonlinearSolver()->residual_and_jacobian_object != NULL)
    1115           0 :     nl->nonlinearSolver()->residual_and_jacobian_object->residual_and_jacobian(
    1116           0 :         *(nl->system().current_local_solution.get()),
    1117             :         NULL,
    1118             :         &the_pc,
    1119           0 :         nl->nonlinearSolver()->system());
    1120             :   else
    1121             :   {
    1122           0 :     std::ostringstream err;
    1123           0 :     err << "No suitable Jacobian routine or object";
    1124           0 :     mooseError(err.str());
    1125           0 :   }
    1126           0 :   the_pc.close();
    1127           0 :   Jac.close();
    1128           0 :   PetscFunctionReturn(PETSC_SUCCESS);
    1129           0 : }
    1130             : 
    1131             : static PetscErrorCode
    1132           0 : SNESJacobian_DMMoose(SNES, Vec x, Mat jac, Mat pc, void * ctx)
    1133             : {
    1134           0 :   DM dm = (DM)ctx;
    1135             : 
    1136             :   PetscFunctionBegin;
    1137           0 :   LibmeshPetscCallQ(DMMooseJacobian(dm, x, jac, pc));
    1138           0 :   PetscFunctionReturn(PETSC_SUCCESS);
    1139             : }
    1140             : 
    1141             : static PetscErrorCode
    1142           0 : DMVariableBounds_Moose(DM dm, Vec xl, Vec xu)
    1143             : {
    1144           0 :   NonlinearSystemBase * nl = NULL;
    1145             : 
    1146             :   PetscFunctionBegin;
    1147           0 :   LibmeshPetscCallQ(DMMooseGetNonlinearSystem(dm, nl));
    1148             : 
    1149           0 :   PetscVector<Number> XL(xl, nl->comm());
    1150           0 :   PetscVector<Number> XU(xu, nl->comm());
    1151             : 
    1152           0 :   LibmeshPetscCallQ(VecSet(xl, PETSC_NINFINITY));
    1153           0 :   LibmeshPetscCallQ(VecSet(xu, PETSC_INFINITY));
    1154           0 :   if (nl->nonlinearSolver()->bounds != NULL)
    1155           0 :     nl->nonlinearSolver()->bounds(XL, XU, nl->nonlinearSolver()->system());
    1156           0 :   else if (nl->nonlinearSolver()->bounds_object != NULL)
    1157           0 :     nl->nonlinearSolver()->bounds_object->bounds(XL, XU, nl->nonlinearSolver()->system());
    1158             :   else
    1159           0 :     SETERRQ(
    1160             :         ((PetscObject)dm)->comm, PETSC_ERR_ARG_WRONG, "No bounds calculation in this Moose object");
    1161           0 :   PetscFunctionReturn(PETSC_SUCCESS);
    1162           0 : }
    1163             : 
    1164             : static PetscErrorCode
    1165          52 : DMCreateGlobalVector_Moose(DM dm, Vec * x)
    1166             : {
    1167          52 :   DM_Moose * dmm = (DM_Moose *)(dm->data);
    1168             : 
    1169             :   PetscFunctionBegin;
    1170          52 :   LibmeshPetscCallQ(DMMooseValidityCheck(dm));
    1171          52 :   if (!dmm->_nl)
    1172           0 :     SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONGSTATE, "No Moose system set for DM_Moose");
    1173             : 
    1174          52 :   NumericVector<Number> * nv = (dmm->_system->solution).get();
    1175          52 :   PetscVector<Number> * pv = dynamic_cast<PetscVector<Number> *>(nv);
    1176          52 :   Vec v = pv->vec();
    1177             :   /* Unfortunately, currently this does not produce a ghosted vector, so nonlinear subproblem solves
    1178             :    aren't going to be easily available.
    1179             :    Should work fine for getting vectors out for linear subproblem solvers. */
    1180          52 :   if (dmm->_embedding)
    1181             :   {
    1182             :     PetscInt n;
    1183          52 :     LibmeshPetscCallQ(VecCreate(((PetscObject)v)->comm, x));
    1184          52 :     LibmeshPetscCallQ(ISGetLocalSize(dmm->_embedding, &n));
    1185          52 :     LibmeshPetscCallQ(VecSetSizes(*x, n, PETSC_DETERMINE));
    1186          52 :     LibmeshPetscCallQ(VecSetType(*x, ((PetscObject)v)->type_name));
    1187          52 :     LibmeshPetscCallQ(VecSetFromOptions(*x));
    1188          52 :     LibmeshPetscCallQ(VecSetUp(*x));
    1189             :   }
    1190             :   else
    1191           0 :     LibmeshPetscCallQ(VecDuplicate(v, x));
    1192             : 
    1193             : #if PETSC_RELEASE_LESS_THAN(3, 13, 0)
    1194             :   LibmeshPetscCallQ(PetscObjectCompose((PetscObject)*x, "DM", (PetscObject)dm));
    1195             : #else
    1196          52 :   LibmeshPetscCallQ(VecSetDM(*x, dm));
    1197             : #endif
    1198          52 :   PetscFunctionReturn(PETSC_SUCCESS);
    1199             : }
    1200             : 
    1201             : static PetscErrorCode
    1202           0 : DMCreateMatrix_Moose(DM dm, Mat * A)
    1203             : {
    1204           0 :   DM_Moose * dmm = (DM_Moose *)(dm->data);
    1205             :   MatType type;
    1206             : 
    1207             :   PetscFunctionBegin;
    1208           0 :   LibmeshPetscCallQ(DMMooseValidityCheck(dm));
    1209           0 :   if (!dmm->_nl)
    1210           0 :     SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONGSTATE, "No Moose system set for DM_Moose");
    1211           0 :   LibmeshPetscCallQ(DMGetMatType(dm, &type));
    1212             : 
    1213             :   /*
    1214             :    The simplest thing for now: compute the sparsity_pattern using dof_map and init the matrix using
    1215             :    that info.
    1216             :    TODO: compute sparsity restricted to this DM's blocks, variables and sides.
    1217             :    Even fancier: compute the sparsity of the coupling of a contact secondary to the contact primary.
    1218             :    In any event, here we are in control of the matrix type and structure.
    1219             :    */
    1220           0 :   const auto & dof_map = *dmm->_dof_map;
    1221             :   PetscInt M, N, m, n;
    1222             :   MPI_Comm comm;
    1223           0 :   M = dof_map.n_dofs();
    1224           0 :   N = M;
    1225           0 :   m = static_cast<PetscInt>(dof_map.n_local_dofs());
    1226           0 :   n = m;
    1227           0 :   LibmeshPetscCallQ(PetscObjectGetComm((PetscObject)dm, &comm));
    1228           0 :   LibmeshPetscCallQ(MatCreate(comm, A));
    1229           0 :   LibmeshPetscCallQ(MatSetSizes(*A, m, n, M, N));
    1230           0 :   LibmeshPetscCallQ(MatSetType(*A, type));
    1231             :   /* TODO: set the prefix for *A and MatSetFromOptions(*A)? Might override the type and other
    1232             :    * settings made here. */
    1233           0 :   LibmeshPetscCallQ(MatSetUp(*A));
    1234           0 :   PetscFunctionReturn(PETSC_SUCCESS);
    1235             : }
    1236             : 
    1237             : static PetscErrorCode
    1238           0 : DMView_Moose(DM dm, PetscViewer viewer)
    1239             : {
    1240             :   PetscBool isascii;
    1241             :   const char *name, *prefix;
    1242           0 :   DM_Moose * dmm = (DM_Moose *)dm->data;
    1243             : 
    1244             :   PetscFunctionBegin;
    1245           0 :   LibmeshPetscCallQ(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isascii));
    1246           0 :   if (isascii)
    1247             :   {
    1248           0 :     LibmeshPetscCallQ(PetscObjectGetName((PetscObject)dm, &name));
    1249           0 :     LibmeshPetscCallQ(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
    1250           0 :     LibmeshPetscCallQ(
    1251             :         PetscViewerASCIIPrintf(viewer, "DM Moose with name %s and prefix %s\n", name, prefix));
    1252           0 :     LibmeshPetscCallQ(PetscViewerASCIIPrintf(viewer, "variables:"));
    1253           0 :     for (const auto & vit : *(dmm->_var_ids))
    1254             :     {
    1255           0 :       LibmeshPetscCallQ(PetscViewerASCIIPrintf(viewer, "(%s,%u) ", vit.first.c_str(), vit.second));
    1256             :     }
    1257           0 :     LibmeshPetscCallQ(PetscViewerASCIIPrintf(viewer, "\n"));
    1258           0 :     LibmeshPetscCallQ(PetscViewerASCIIPrintf(viewer, "blocks:"));
    1259           0 :     for (const auto & bit : *(dmm->_block_ids))
    1260             :     {
    1261           0 :       LibmeshPetscCallQ(PetscViewerASCIIPrintf(viewer, "(%s,%d) ", bit.first.c_str(), bit.second));
    1262             :     }
    1263           0 :     LibmeshPetscCallQ(PetscViewerASCIIPrintf(viewer, "\n"));
    1264             : 
    1265           0 :     if (dmm->_side_ids->size())
    1266             :     {
    1267           0 :       LibmeshPetscCallQ(PetscViewerASCIIPrintf(viewer, "sides:"));
    1268           0 :       for (const auto & sit : *(dmm->_side_ids))
    1269             :       {
    1270           0 :         LibmeshPetscCallQ(
    1271             :             PetscViewerASCIIPrintf(viewer, "(%s,%d) ", sit.first.c_str(), sit.second));
    1272             :       }
    1273           0 :       LibmeshPetscCallQ(PetscViewerASCIIPrintf(viewer, "\n"));
    1274             :     }
    1275             : 
    1276           0 :     if (dmm->_unside_ids->size())
    1277             :     {
    1278           0 :       LibmeshPetscCallQ(PetscViewerASCIIPrintf(viewer, "unsides:"));
    1279           0 :       for (const auto & sit : *(dmm->_unside_ids))
    1280             :       {
    1281           0 :         LibmeshPetscCallQ(
    1282             :             PetscViewerASCIIPrintf(viewer, "(%s,%d) ", sit.first.c_str(), sit.second));
    1283             :       }
    1284           0 :       LibmeshPetscCallQ(PetscViewerASCIIPrintf(viewer, "\n"));
    1285             :     }
    1286             : 
    1287           0 :     if (dmm->_contact_names->size())
    1288             :     {
    1289           0 :       LibmeshPetscCallQ(PetscViewerASCIIPrintf(viewer, "contacts:"));
    1290           0 :       for (const auto & cit : *(dmm->_contact_names))
    1291             :       {
    1292           0 :         LibmeshPetscCallQ(PetscViewerASCIIPrintf(
    1293             :             viewer, "(%s,%s,", cit.second.first.c_str(), cit.second.second.c_str()));
    1294           0 :         if ((*dmm->_contact_displaced)[cit.second])
    1295           0 :           LibmeshPetscCallQ(PetscViewerASCIIPrintf(viewer, "displaced) "));
    1296             :         else
    1297           0 :           LibmeshPetscCallQ(PetscViewerASCIIPrintf(viewer, "undisplaced) "));
    1298             :       }
    1299           0 :       LibmeshPetscCallQ(PetscViewerASCIIPrintf(viewer, "\n"));
    1300             :     }
    1301             : 
    1302           0 :     if (dmm->_uncontact_names->size())
    1303             :     {
    1304           0 :       LibmeshPetscCallQ(PetscViewerASCIIPrintf(viewer, "_uncontacts:"));
    1305           0 :       for (const auto & cit : *(dmm->_uncontact_names))
    1306             :       {
    1307           0 :         LibmeshPetscCallQ(PetscViewerASCIIPrintf(
    1308             :             viewer, "(%s,%s,", cit.second.first.c_str(), cit.second.second.c_str()));
    1309           0 :         if ((*dmm->_uncontact_displaced)[cit.second])
    1310           0 :           LibmeshPetscCallQ(PetscViewerASCIIPrintf(viewer, "displaced) "));
    1311             :         else
    1312           0 :           LibmeshPetscCallQ(PetscViewerASCIIPrintf(viewer, "undisplaced) "));
    1313             :       }
    1314           0 :       LibmeshPetscCallQ(PetscViewerASCIIPrintf(viewer, "\n"));
    1315             :     }
    1316             : 
    1317           0 :     if (dmm->_splitlocs && dmm->_splitlocs->size())
    1318             :     {
    1319           0 :       LibmeshPetscCallQ(PetscViewerASCIIPrintf(viewer, "Field decomposition:"));
    1320             :       // FIX: decompositions might have different sizes and components on different ranks.
    1321           0 :       for (const auto & dit : *(dmm->_splitlocs))
    1322             :       {
    1323           0 :         std::string dname = dit.first;
    1324           0 :         LibmeshPetscCallQ(PetscViewerASCIIPrintf(viewer, " %s", dname.c_str()));
    1325           0 :       }
    1326           0 :       LibmeshPetscCallQ(PetscViewerASCIIPrintf(viewer, "\n"));
    1327             :     }
    1328             :   }
    1329             :   else
    1330           0 :     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Non-ASCII viewers are not supported");
    1331             : 
    1332           0 :   PetscFunctionReturn(PETSC_SUCCESS);
    1333             : }
    1334             : 
    1335             : static PetscErrorCode
    1336         886 : DMMooseGetMeshBlocks_Private(DM dm, std::set<subdomain_id_type> & blocks)
    1337             : {
    1338         886 :   DM_Moose * dmm = (DM_Moose *)(dm->data);
    1339             : 
    1340             :   PetscFunctionBegin;
    1341         886 :   LibmeshPetscCallQ(DMMooseValidityCheck(dm));
    1342         886 :   if (!dmm->_nl)
    1343           0 :     SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONGSTATE, "No Moose system set for DM_Moose");
    1344             : 
    1345         886 :   const MeshBase & mesh = dmm->_system->get_mesh();
    1346             :   /* The following effectively is a verbatim copy of MeshBase::n_subdomains(). */
    1347             :   // This requires an inspection on every processor
    1348             :   libmesh_parallel_only(mesh.comm());
    1349      723330 :   for (const auto & elem : mesh.active_element_ptr_range())
    1350      362108 :     blocks.insert(elem->subdomain_id());
    1351             :   // Some subdomains may only live on other processors
    1352         886 :   mesh.comm().set_union(blocks);
    1353         886 :   PetscFunctionReturn(PETSC_SUCCESS);
    1354             : }
    1355             : 
    1356             : static PetscErrorCode
    1357         443 : DMSetUp_Moose_Pre(DM dm)
    1358             : {
    1359         443 :   DM_Moose * dmm = (DM_Moose *)(dm->data);
    1360             : 
    1361             :   PetscFunctionBegin;
    1362         443 :   LibmeshPetscCallQ(DMMooseValidityCheck(dm));
    1363         443 :   if (!dmm->_nl)
    1364           0 :     SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONGSTATE, "No Moose system set for DM_Moose");
    1365             : 
    1366             :   /* Set up variables, blocks and sides. */
    1367         443 :   const auto & dofmap = *dmm->_dof_map;
    1368             :   /* libMesh mesh */
    1369         443 :   const MeshBase & mesh = dmm->_system->get_mesh();
    1370             : 
    1371             :   // Do sides
    1372         443 :   dmm->_nosides = PETSC_TRUE;
    1373         443 :   dmm->_side_ids->clear();
    1374         443 :   dmm->_side_names->clear();
    1375         443 :   if (dmm->_sides)
    1376             :   {
    1377          40 :     dmm->_nosides = PETSC_FALSE;
    1378         116 :     for (const auto & name : *(dmm->_sides))
    1379             :     {
    1380          76 :       boundary_id_type id = dmm->_nl->mesh().getBoundaryID(name);
    1381          76 :       dmm->_side_names->insert(std::make_pair(id, name));
    1382          76 :       dmm->_side_ids->insert(std::make_pair(name, id));
    1383             :     }
    1384          40 :     delete dmm->_sides;
    1385          40 :     dmm->_sides = LIBMESH_PETSC_NULLPTR;
    1386             :   }
    1387             : 
    1388             :   // Do unsides
    1389         443 :   dmm->_nounsides = PETSC_TRUE;
    1390         443 :   dmm->_unside_ids->clear();
    1391         443 :   dmm->_unside_names->clear();
    1392         443 :   if (dmm->_unsides)
    1393             :   {
    1394          28 :     dmm->_nounsides = PETSC_FALSE;
    1395          84 :     for (const auto & name : *(dmm->_unsides))
    1396             :     {
    1397          56 :       boundary_id_type id = dmm->_nl->mesh().getBoundaryID(name);
    1398          56 :       dmm->_unside_names->insert(std::make_pair(id, name));
    1399          56 :       dmm->_unside_ids->insert(std::make_pair(name, id));
    1400             :     }
    1401          28 :     delete dmm->_unsides;
    1402          28 :     dmm->_unsides = LIBMESH_PETSC_NULLPTR;
    1403             :   }
    1404             : 
    1405             :   // Do unside by var
    1406         443 :   dmm->_nounside_by_var = PETSC_TRUE;
    1407         443 :   dmm->_unside_by_var_set->clear();
    1408         443 :   if (dmm->_unside_by_var)
    1409             :   {
    1410          12 :     dmm->_nounside_by_var = PETSC_FALSE;
    1411          36 :     for (const auto & name : *(dmm->_unside_by_var))
    1412             :     {
    1413          24 :       const auto colon_pos = name.find(":");
    1414          24 :       auto unside_name = name.substr(0, colon_pos);
    1415          24 :       auto var_name = name.substr(colon_pos + 1);
    1416          24 :       boundary_id_type id = dmm->_nl->mesh().getBoundaryID(unside_name);
    1417          24 :       bool var_found = false;
    1418          24 :       for (unsigned int v = 0; v < dofmap.n_variables(); ++v)
    1419             :       {
    1420          24 :         const auto & vname = dofmap.variable(v).name();
    1421          24 :         if (vname == var_name)
    1422             :         {
    1423          24 :           dmm->_unside_by_var_set->insert(std::make_pair(id, v));
    1424          24 :           var_found = true;
    1425          24 :           break;
    1426             :         }
    1427             :       }
    1428          24 :       if (!var_found)
    1429           0 :         mooseError("No variable named '", var_name, "' found");
    1430          24 :     }
    1431          12 :     delete dmm->_unside_by_var;
    1432          12 :     dmm->_unside_by_var = LIBMESH_PETSC_NULLPTR;
    1433             :   }
    1434             : 
    1435         443 :   dmm->_nocontacts = PETSC_TRUE;
    1436             : 
    1437         443 :   if (dmm->_contacts)
    1438             :   {
    1439           0 :     dmm->_nocontacts = PETSC_FALSE;
    1440           0 :     for (const auto & cpair : *(dmm->_contacts))
    1441             :     {
    1442             :       try
    1443             :       {
    1444           0 :         if ((*dmm->_contact_displaced)[cpair])
    1445           0 :           dmm->_nl->feProblem().getDisplacedProblem()->geomSearchData().getPenetrationLocator(
    1446           0 :               cpair.first, cpair.second);
    1447             :         else
    1448           0 :           dmm->_nl->feProblem().geomSearchData().getPenetrationLocator(cpair.first, cpair.second);
    1449             :       }
    1450           0 :       catch (...)
    1451             :       {
    1452           0 :         std::ostringstream err;
    1453           0 :         err << "Problem retrieving contact for PenetrationLocator with primary " << cpair.first
    1454           0 :             << " and secondary " << cpair.second;
    1455           0 :         mooseError(err.str());
    1456           0 :       }
    1457           0 :       BoundaryID primary_id = dmm->_nl->mesh().getBoundaryID(cpair.first);
    1458           0 :       BoundaryID secondary_id = dmm->_nl->mesh().getBoundaryID(cpair.second);
    1459           0 :       DM_Moose::ContactID cid(primary_id, secondary_id);
    1460           0 :       dmm->_contact_names->insert(std::make_pair(cid, cpair));
    1461             :     }
    1462             :   }
    1463             : 
    1464         443 :   dmm->_nouncontacts = PETSC_TRUE;
    1465         443 :   if (dmm->_uncontacts)
    1466             :   {
    1467           0 :     dmm->_nouncontacts = PETSC_FALSE;
    1468           0 :     for (const auto & cpair : *(dmm->_uncontacts))
    1469             :     {
    1470             :       try
    1471             :       {
    1472           0 :         if ((*dmm->_uncontact_displaced)[cpair])
    1473           0 :           dmm->_nl->feProblem().getDisplacedProblem()->geomSearchData().getPenetrationLocator(
    1474           0 :               cpair.first, cpair.second);
    1475             :         else
    1476           0 :           dmm->_nl->feProblem().geomSearchData().getPenetrationLocator(cpair.first, cpair.second);
    1477             :       }
    1478           0 :       catch (...)
    1479             :       {
    1480           0 :         std::ostringstream err;
    1481           0 :         err << "Problem retrieving uncontact for PenetrationLocator with primary " << cpair.first
    1482           0 :             << " and secondary " << cpair.second;
    1483           0 :         mooseError(err.str());
    1484           0 :       }
    1485           0 :       BoundaryID primary_id = dmm->_nl->mesh().getBoundaryID(cpair.first);
    1486           0 :       BoundaryID secondary_id = dmm->_nl->mesh().getBoundaryID(cpair.second);
    1487           0 :       DM_Moose::ContactID cid(primary_id, secondary_id);
    1488           0 :       dmm->_uncontact_names->insert(std::make_pair(cid, cpair));
    1489             :     }
    1490             :   }
    1491             : 
    1492         443 :   dmm->_var_ids->clear();
    1493         443 :   dmm->_var_names->clear();
    1494             :   // FIXME: would be nice to invert this nested loop structure so we could iterate over the
    1495             :   // potentially smaller dmm->vars,
    1496             :   // but checking against dofmap.variable would still require a linear search, hence, no win.  Would
    1497             :   // be nice to endow dofmap.variable
    1498             :   // with a fast search capability.
    1499        1377 :   for (unsigned int v = 0; v < dofmap.n_variables(); ++v)
    1500             :   {
    1501         934 :     std::string vname = dofmap.variable(v).name();
    1502         934 :     if (dmm->_vars && dmm->_vars->size() && dmm->_vars->find(vname) == dmm->_vars->end())
    1503         322 :       continue;
    1504         612 :     dmm->_var_ids->insert(std::pair<std::string, unsigned int>(vname, v));
    1505         612 :     dmm->_var_names->insert(std::pair<unsigned int, std::string>(v, vname));
    1506         934 :   }
    1507         443 :   if (dmm->_var_ids->size() == dofmap.n_variables())
    1508         141 :     dmm->_all_vars = PETSC_TRUE;
    1509             :   else
    1510         302 :     dmm->_all_vars = PETSC_FALSE;
    1511         443 :   if (dmm->_vars)
    1512             :   {
    1513         302 :     delete dmm->_vars;
    1514         302 :     dmm->_vars = LIBMESH_PETSC_NULLPTR;
    1515             :   }
    1516             : 
    1517         443 :   dmm->_block_ids->clear();
    1518         443 :   dmm->_block_names->clear();
    1519         443 :   std::set<subdomain_id_type> blocks;
    1520         443 :   LibmeshPetscCallQ(DMMooseGetMeshBlocks_Private(dm, blocks));
    1521         443 :   if (blocks.empty())
    1522           0 :     SETERRQ(((PetscObject)dm)->comm, PETSC_ERR_PLIB, "No mesh blocks found.");
    1523             : 
    1524        1048 :   for (const auto & bid : blocks)
    1525             :   {
    1526         605 :     std::string bname = mesh.subdomain_name(bid);
    1527         605 :     if (!bname.length())
    1528             :     {
    1529             :       // Block names are currently implemented for Exodus meshes
    1530             :       // only, so we might have to make up our own block names and
    1531             :       // maintain our own mapping of block ids to names.
    1532         551 :       std::ostringstream ss;
    1533         551 :       ss << bid;
    1534         551 :       bname = ss.str();
    1535         551 :     }
    1536         605 :     if (dmm->_nosides && dmm->_nocontacts)
    1537             :     {
    1538             :       // If no sides and no contacts have been specified, by default (null or empty dmm->blocks) all
    1539             :       // blocks are included in the split Thus, skip this block only if it is explicitly excluded
    1540             :       // from a nonempty dmm->blocks.
    1541         565 :       if (dmm->_blocks && dmm->_blocks->size() &&
    1542           0 :           (dmm->_blocks->find(bname) ==
    1543           0 :                dmm->_blocks->end() && // We should allow users to use subdomain IDs
    1544         565 :            dmm->_blocks->find(std::to_string(bid)) == dmm->_blocks->end()))
    1545           0 :         continue;
    1546             :     }
    1547             :     else
    1548             :     {
    1549             :       // If sides or contacts have been specified, only the explicitly-specified blocks (those in
    1550             :       // dmm->blocks, if it's non-null) are in the split. Thus, include this block only if it is
    1551             :       // explicitly specified in a nonempty dmm->blocks. Equivalently, skip this block if
    1552             :       // dmm->blocks is dmm->blocks is null or empty or excludes this block.
    1553          40 :       if (!dmm->_blocks || !dmm->_blocks->size() ||
    1554           0 :           (dmm->_blocks->find(bname) ==
    1555           0 :                dmm->_blocks->end() // We should allow users to use subdomain IDs
    1556          40 :            && dmm->_blocks->find(std::to_string(bid)) == dmm->_blocks->end()))
    1557          40 :         continue;
    1558             :     }
    1559         565 :     dmm->_block_ids->insert(std::make_pair(bname, bid));
    1560         565 :     dmm->_block_names->insert(std::make_pair(bid, bname));
    1561         605 :   }
    1562             : 
    1563         443 :   if (dmm->_block_ids->size() == blocks.size())
    1564         403 :     dmm->_all_blocks = PETSC_TRUE;
    1565             :   else
    1566          40 :     dmm->_all_blocks = PETSC_FALSE;
    1567         443 :   if (dmm->_blocks)
    1568             :   {
    1569           0 :     delete dmm->_blocks;
    1570           0 :     dmm->_blocks = LIBMESH_PETSC_NULLPTR;
    1571             :   }
    1572             : 
    1573         443 :   std::string name = dmm->_system->name();
    1574         443 :   name += "_vars";
    1575        1055 :   for (const auto & vit : *(dmm->_var_names))
    1576         612 :     name += "_" + vit.second;
    1577             : 
    1578         443 :   name += "_blocks";
    1579             : 
    1580        1008 :   for (const auto & bit : *(dmm->_block_names))
    1581         565 :     name += "_" + bit.second;
    1582             : 
    1583         443 :   if (dmm->_side_names && dmm->_side_names->size())
    1584             :   {
    1585          40 :     name += "_sides";
    1586         116 :     for (const auto & sit : *(dmm->_side_names))
    1587          76 :       name += "_" + sit.second;
    1588             :   }
    1589         443 :   if (dmm->_unside_names && dmm->_unside_names->size())
    1590             :   {
    1591          28 :     name += "_unsides";
    1592          84 :     for (const auto & sit : *(dmm->_unside_names))
    1593          56 :       name += "_" + sit.second;
    1594             :   }
    1595         443 :   if (dmm->_contact_names && dmm->_contact_names->size())
    1596             :   {
    1597           0 :     name += "_contacts";
    1598           0 :     for (const auto & cit : *(dmm->_contact_names))
    1599           0 :       name += "_primary_" + cit.second.first + "_secondary_" + cit.second.second;
    1600             :   }
    1601         443 :   if (dmm->_uncontact_names && dmm->_uncontact_names->size())
    1602             :   {
    1603           0 :     name += "_uncontacts";
    1604           0 :     for (const auto & cit : *(dmm->_uncontact_names))
    1605           0 :       name += "_primary_" + cit.second.first + "_secondary_" + cit.second.second;
    1606             :   }
    1607         443 :   LibmeshPetscCallQ(PetscObjectSetName((PetscObject)dm, name.c_str()));
    1608         443 :   PetscFunctionReturn(PETSC_SUCCESS);
    1609         443 : }
    1610             : 
    1611             : PetscErrorCode
    1612           0 : DMMooseReset(DM dm)
    1613             : {
    1614             :   PetscBool ismoose;
    1615           0 :   DM_Moose * dmm = (DM_Moose *)(dm->data);
    1616             : 
    1617             :   PetscFunctionBegin;
    1618           0 :   LibmeshPetscCallQ(PetscObjectTypeCompare((PetscObject)dm, DMMOOSE, &ismoose));
    1619           0 :   if (!ismoose)
    1620           0 :     PetscFunctionReturn(PETSC_SUCCESS);
    1621           0 :   if (!dmm->_nl)
    1622           0 :     SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONGSTATE, "No Moose system set for DM_Moose");
    1623           0 :   LibmeshPetscCallQ(ISDestroy(&dmm->_embedding));
    1624           0 :   for (auto & it : *(dmm->_splits))
    1625             :   {
    1626           0 :     DM_Moose::SplitInfo & split = it.second;
    1627           0 :     LibmeshPetscCallQ(ISDestroy(&split._rembedding));
    1628           0 :     if (split._dm)
    1629           0 :       LibmeshPetscCallQ(DMMooseReset(split._dm));
    1630             :   }
    1631           0 :   dm->setupcalled = PETSC_FALSE;
    1632           0 :   PetscFunctionReturn(PETSC_SUCCESS);
    1633             : }
    1634             : 
    1635             : static PetscErrorCode
    1636         443 : DMSetUp_Moose(DM dm)
    1637             : {
    1638         443 :   DM_Moose * dmm = (DM_Moose *)(dm->data);
    1639             : 
    1640             :   PetscFunctionBegin;
    1641         443 :   LibmeshPetscCallQ(DMMooseValidityCheck(dm));
    1642         443 :   if (!dmm->_nl)
    1643           0 :     SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONGSTATE, "No Moose system set for DM_Moose");
    1644         443 :   if (dmm->_print_embedding)
    1645             :   {
    1646             :     const char *name, *prefix;
    1647             :     IS embedding;
    1648             : 
    1649           0 :     LibmeshPetscCallQ(PetscObjectGetName((PetscObject)dm, &name));
    1650           0 :     LibmeshPetscCallQ(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
    1651           0 :     LibmeshPetscCallQ(PetscViewerASCIIPrintf(PETSC_VIEWER_STDOUT_(((PetscObject)dm)->comm),
    1652             :                                              "DM Moose with name %s and prefix %s\n",
    1653             :                                              name,
    1654             :                                              prefix));
    1655           0 :     if (dmm->_all_vars && dmm->_all_blocks && dmm->_nosides && dmm->_nounsides &&
    1656           0 :         dmm->_nocontacts && dmm->_nouncontacts)
    1657           0 :       LibmeshPetscCallQ(PetscViewerASCIIPrintf(PETSC_VIEWER_STDOUT_(((PetscObject)dm)->comm),
    1658           0 :                                                "\thas a trivial embedding\n"));
    1659             :     else
    1660             :     {
    1661           0 :       LibmeshPetscCallQ(DMMooseGetEmbedding_Private(dm, &embedding));
    1662           0 :       LibmeshPetscCallQ(PetscViewerASCIIPrintf(PETSC_VIEWER_STDOUT_(((PetscObject)dm)->comm),
    1663             :                                                "\thas embedding defined by IS:\n"));
    1664           0 :       LibmeshPetscCallQ(ISView(embedding, PETSC_VIEWER_STDOUT_(((PetscObject)dm)->comm)));
    1665           0 :       LibmeshPetscCallQ(ISDestroy(&embedding));
    1666             :     }
    1667             :   }
    1668             :   /*
    1669             :    Do not evaluate function, Jacobian or bounds for an embedded DM -- the subproblem might not have
    1670             :    enough information for that.
    1671             :    */
    1672         443 :   if (dmm->_all_vars && dmm->_all_blocks && dmm->_nosides && dmm->_nounsides && dmm->_nocontacts &&
    1673         141 :       dmm->_nouncontacts)
    1674             :   {
    1675         141 :     LibmeshPetscCallQ(DMSNESSetFunction(dm, SNESFunction_DMMoose, (void *)dm));
    1676         141 :     LibmeshPetscCallQ(DMSNESSetJacobian(dm, SNESJacobian_DMMoose, (void *)dm));
    1677         141 :     if (dmm->_nl->nonlinearSolver()->bounds || dmm->_nl->nonlinearSolver()->bounds_object)
    1678         141 :       LibmeshPetscCallQ(DMSetVariableBounds(dm, DMVariableBounds_Moose));
    1679             :   }
    1680         443 :   PetscFunctionReturn(PETSC_SUCCESS);
    1681             : }
    1682             : #if !PETSC_VERSION_LESS_THAN(3, 23, 0)
    1683             : PetscErrorCode
    1684         443 : DMSetFromOptions_Moose(DM dm, PetscOptionItems /*options*/)
    1685             : #elif !PETSC_VERSION_LESS_THAN(3, 18, 0)
    1686             : PetscErrorCode
    1687             : DMSetFromOptions_Moose(DM dm, PetscOptionItems * /*options*/) // >= 3.18.0
    1688             : #elif !PETSC_VERSION_LESS_THAN(3, 7, 0)
    1689             : PetscErrorCode
    1690             : DMSetFromOptions_Moose(PetscOptionItems * /*options*/, DM dm) // >= 3.7.0
    1691             : #else
    1692             : PetscErrorCode
    1693             : DMSetFromOptions_Moose(PetscOptions * /*options*/, DM dm) // >= 3.6.0
    1694             : #endif
    1695             : {
    1696         443 :   DM_Moose * dmm = (DM_Moose *)dm->data;
    1697             : 
    1698             :   PetscFunctionBegin;
    1699         443 :   LibmeshPetscCallQ(DMMooseValidityCheck(dm));
    1700         443 :   if (!dmm->_nl)
    1701           0 :     SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONGSTATE, "No Moose system set for DM_Moose");
    1702             : // PETSc changed macro definitions in 3.18; the former correct usage
    1703             : // is now a compiler error and the new usage is now a compiler
    1704             : // warning.
    1705             : #if !PETSC_VERSION_LESS_THAN(3, 18, 0)
    1706         886 :   PetscOptionsBegin(((PetscObject)dm)->comm, ((PetscObject)dm)->prefix, "DMMoose options", "DM");
    1707             : #else
    1708             :   LibmeshPetscCallQ(PetscOptionsBegin(
    1709             :       ((PetscObject)dm)->comm, ((PetscObject)dm)->prefix, "DMMoose options", "DM"));
    1710             : #endif
    1711         443 :   std::string opt, help;
    1712         443 :   PetscInt maxvars = dmm->_dof_map->n_variables();
    1713             :   char ** vars;
    1714         443 :   std::set<std::string> varset;
    1715         443 :   PetscInt nvars = maxvars;
    1716         443 :   LibmeshPetscCallQ(PetscMalloc(maxvars * sizeof(char *), &vars));
    1717         443 :   opt = "-dm_moose_vars";
    1718         443 :   help = "Variables in DMMoose";
    1719         443 :   LibmeshPetscCallQ(PetscOptionsStringArray(
    1720             :       opt.c_str(), help.c_str(), "DMMooseSetVars", vars, &nvars, LIBMESH_PETSC_NULLPTR));
    1721         745 :   for (PetscInt i = 0; i < nvars; ++i)
    1722             :   {
    1723         302 :     varset.insert(std::string(vars[i]));
    1724         302 :     LibmeshPetscCallQ(PetscFree(vars[i]));
    1725             :   }
    1726         443 :   LibmeshPetscCallQ(PetscFree(vars));
    1727         443 :   if (varset.size())
    1728         302 :     LibmeshPetscCallQ(DMMooseSetVariables(dm, varset));
    1729             :   //
    1730         443 :   std::set<subdomain_id_type> meshblocks;
    1731         443 :   LibmeshPetscCallQ(DMMooseGetMeshBlocks_Private(dm, meshblocks));
    1732         443 :   PetscInt maxblocks = meshblocks.size();
    1733             :   char ** blocks;
    1734         443 :   LibmeshPetscCallQ(PetscMalloc(maxblocks * sizeof(char *), &blocks));
    1735         443 :   std::set<std::string> blockset;
    1736         443 :   PetscInt nblocks = maxblocks;
    1737         443 :   opt = "-dm_moose_blocks";
    1738         443 :   help = "Blocks in DMMoose";
    1739         443 :   LibmeshPetscCallQ(PetscOptionsStringArray(
    1740             :       opt.c_str(), help.c_str(), "DMMooseSetBlocks", blocks, &nblocks, LIBMESH_PETSC_NULLPTR));
    1741         443 :   for (PetscInt i = 0; i < nblocks; ++i)
    1742             :   {
    1743           0 :     blockset.insert(std::string(blocks[i]));
    1744           0 :     LibmeshPetscCallQ(PetscFree(blocks[i]));
    1745             :   }
    1746         443 :   LibmeshPetscCallQ(PetscFree(blocks));
    1747         443 :   if (blockset.size())
    1748           0 :     LibmeshPetscCallQ(DMMooseSetBlocks(dm, blockset));
    1749         443 :   PetscInt maxsides = dmm->_system->get_mesh().get_boundary_info().get_global_boundary_ids().size();
    1750             :   char ** sides;
    1751         443 :   LibmeshPetscCallQ(PetscMalloc(maxsides * maxvars * sizeof(char *), &sides));
    1752         443 :   PetscInt nsides = maxsides;
    1753         443 :   std::set<std::string> sideset;
    1754             : 
    1755             :   // Do sides
    1756         443 :   opt = "-dm_moose_sides";
    1757         443 :   help = "Sides to include in DMMoose";
    1758         443 :   LibmeshPetscCallQ(PetscOptionsStringArray(
    1759             :       opt.c_str(), help.c_str(), "DMMooseSetSides", sides, &nsides, LIBMESH_PETSC_NULLPTR));
    1760         519 :   for (PetscInt i = 0; i < nsides; ++i)
    1761             :   {
    1762          76 :     sideset.insert(std::string(sides[i]));
    1763          76 :     LibmeshPetscCallQ(PetscFree(sides[i]));
    1764             :   }
    1765         443 :   if (sideset.size())
    1766          40 :     LibmeshPetscCallQ(DMMooseSetSides(dm, sideset));
    1767             : 
    1768             :   // Do unsides
    1769         443 :   opt = "-dm_moose_unsides";
    1770         443 :   help = "Sides to exclude from DMMoose";
    1771         443 :   nsides = maxsides;
    1772         443 :   LibmeshPetscCallQ(PetscOptionsStringArray(
    1773             :       opt.c_str(), help.c_str(), "DMMooseSetUnSides", sides, &nsides, LIBMESH_PETSC_NULLPTR));
    1774         443 :   sideset.clear();
    1775         499 :   for (PetscInt i = 0; i < nsides; ++i)
    1776             :   {
    1777          56 :     sideset.insert(std::string(sides[i]));
    1778          56 :     LibmeshPetscCallQ(PetscFree(sides[i]));
    1779             :   }
    1780         443 :   if (sideset.size())
    1781          28 :     LibmeshPetscCallQ(DMMooseSetUnSides(dm, sideset));
    1782             : 
    1783             :   // Do unsides by var
    1784         443 :   opt = "-dm_moose_unside_by_var";
    1785         443 :   help = "Sides to exclude from DMMoose on a by-var basis";
    1786         443 :   nsides = maxsides * maxvars;
    1787         443 :   LibmeshPetscCallQ(PetscOptionsStringArray(
    1788             :       opt.c_str(), help.c_str(), "DMMooseSetUnSideByVar", sides, &nsides, LIBMESH_PETSC_NULLPTR));
    1789         443 :   sideset.clear();
    1790         467 :   for (PetscInt i = 0; i < nsides; ++i)
    1791             :   {
    1792          24 :     sideset.insert(std::string(sides[i]));
    1793          24 :     LibmeshPetscCallQ(PetscFree(sides[i]));
    1794             :   }
    1795         443 :   if (sideset.size())
    1796          12 :     LibmeshPetscCallQ(DMMooseSetUnSideByVar(dm, sideset));
    1797             : 
    1798         443 :   LibmeshPetscCallQ(PetscFree(sides));
    1799         443 :   PetscInt maxcontacts = dmm->_nl->feProblem().geomSearchData()._penetration_locators.size();
    1800         443 :   std::shared_ptr<DisplacedProblem> displaced_problem = dmm->_nl->feProblem().getDisplacedProblem();
    1801         443 :   if (displaced_problem)
    1802           0 :     maxcontacts = PetscMax(
    1803             :         maxcontacts, (PetscInt)displaced_problem->geomSearchData()._penetration_locators.size());
    1804             : 
    1805         443 :   std::vector<DM_Moose::ContactName> contacts;
    1806         443 :   std::vector<PetscBool> contact_displaced;
    1807         443 :   PetscInt ncontacts = 0;
    1808         443 :   opt = "-dm_moose_ncontacts";
    1809             :   help =
    1810             :       "Number of contacts to include in DMMoose.  For each <n> < "
    1811             :       "dm_moose_contacts\n\t-dm_moose_contact_<n> is a comma-separated <primary>,<secondary> pair "
    1812             :       "defining the contact surfaces"
    1813             :       "\t-dm_moose_contact_<n>_displaced <bool> determines whether the contact is defined on "
    1814         443 :       "the displaced mesh or not";
    1815         443 :   LibmeshPetscCallQ(PetscOptionsInt(opt.c_str(),
    1816             :                                     help.c_str(),
    1817             :                                     "DMMooseSetContacts",
    1818             :                                     ncontacts,
    1819             :                                     &ncontacts,
    1820             :                                     LIBMESH_PETSC_NULLPTR));
    1821         443 :   if (ncontacts > maxcontacts)
    1822           0 :     LIBMESH_SETERRQ2(((PetscObject)dm)->comm,
    1823             :                      PETSC_ERR_ARG_SIZ,
    1824             :                      "Number of requested contacts %" LIBMESH_PETSCINT_FMT
    1825             :                      " exceeds the maximum number of contacts %" LIBMESH_PETSCINT_FMT,
    1826             :                      ncontacts,
    1827             :                      maxcontacts);
    1828         443 :   for (PetscInt i = 0; i < ncontacts; ++i)
    1829             :   {
    1830             :     {
    1831             :       char * primary_secondary[2];
    1832           0 :       PetscInt sz = 2;
    1833           0 :       std::ostringstream oopt, ohelp;
    1834           0 :       oopt << "-dm_moose_contact_" << i;
    1835           0 :       ohelp << "Primary and secondary for contact " << i;
    1836           0 :       LibmeshPetscCallQ(PetscOptionsStringArray(oopt.str().c_str(),
    1837             :                                                 ohelp.str().c_str(),
    1838             :                                                 "DMMooseSetContacts",
    1839             :                                                 primary_secondary,
    1840             :                                                 &sz,
    1841             :                                                 LIBMESH_PETSC_NULLPTR));
    1842           0 :       if (sz != 2)
    1843           0 :         LIBMESH_SETERRQ2(
    1844             :             ((PetscObject)dm)->comm,
    1845             :             PETSC_ERR_ARG_SIZ,
    1846             :             "Expected 2 sideset IDs (primary & secondary) for contact %" LIBMESH_PETSCINT_FMT
    1847             :             ", got %" LIBMESH_PETSCINT_FMT " instead",
    1848             :             i,
    1849             :             sz);
    1850           0 :       contacts.push_back(DM_Moose::ContactName(std::string(primary_secondary[0]),
    1851           0 :                                                std::string(primary_secondary[1])));
    1852           0 :       LibmeshPetscCallQ(PetscFree(primary_secondary[0]));
    1853           0 :       LibmeshPetscCallQ(PetscFree(primary_secondary[1]));
    1854           0 :     }
    1855             :     {
    1856           0 :       PetscBool displaced = PETSC_FALSE;
    1857           0 :       std::ostringstream oopt, ohelp;
    1858           0 :       oopt << "-dm_moose_contact_" << i << "_displaced";
    1859           0 :       ohelp << "Whether contact " << i << " is determined using displaced mesh or not";
    1860           0 :       LibmeshPetscCallQ(PetscOptionsBool(oopt.str().c_str(),
    1861             :                                          ohelp.str().c_str(),
    1862             :                                          "DMMooseSetContacts",
    1863             :                                          PETSC_FALSE,
    1864             :                                          &displaced,
    1865             :                                          LIBMESH_PETSC_NULLPTR));
    1866           0 :       contact_displaced.push_back(displaced);
    1867           0 :     }
    1868             :   }
    1869         443 :   if (contacts.size())
    1870           0 :     LibmeshPetscCallQ(DMMooseSetContacts(dm, contacts, contact_displaced));
    1871             :   {
    1872         443 :     std::ostringstream oopt, ohelp;
    1873             :     PetscBool is_include_all_nodes;
    1874         443 :     oopt << "-dm_moose_includeAllContactNodes";
    1875         443 :     ohelp << "Whether to include all nodes on the contact surfaces into the subsolver";
    1876         443 :     LibmeshPetscCallQ(PetscOptionsBool(oopt.str().c_str(),
    1877             :                                        ohelp.str().c_str(),
    1878             :                                        "",
    1879             :                                        PETSC_FALSE,
    1880             :                                        &is_include_all_nodes,
    1881             :                                        LIBMESH_PETSC_NULLPTR));
    1882         443 :     dmm->_include_all_contact_nodes = is_include_all_nodes;
    1883         443 :   }
    1884         443 :   std::vector<DM_Moose::ContactName> uncontacts;
    1885         443 :   std::vector<PetscBool> uncontact_displaced;
    1886         443 :   PetscInt nuncontacts = 0;
    1887         443 :   opt = "-dm_moose_nuncontacts";
    1888             :   help =
    1889             :       "Number of contacts to exclude from DMMoose.  For each <n> < "
    1890             :       "dm_moose_contacts\n\t-dm_moose_contact_<n> is a comma-separated <primary>,<secondary> pair "
    1891             :       "defining the contact surfaces"
    1892             :       "\t-dm_moose_contact_<n>_displaced <bool> determines whether the contact is defined on "
    1893         443 :       "the displaced mesh or not";
    1894         443 :   LibmeshPetscCallQ(PetscOptionsInt(opt.c_str(),
    1895             :                                     help.c_str(),
    1896             :                                     "DMMooseSetUnContacts",
    1897             :                                     nuncontacts,
    1898             :                                     &nuncontacts,
    1899             :                                     LIBMESH_PETSC_NULLPTR));
    1900         443 :   if (nuncontacts > maxcontacts)
    1901           0 :     LIBMESH_SETERRQ2(((PetscObject)dm)->comm,
    1902             :                      PETSC_ERR_ARG_SIZ,
    1903             :                      "Number of requested uncontacts %" LIBMESH_PETSCINT_FMT
    1904             :                      " exceeds the maximum number of contacts %" LIBMESH_PETSCINT_FMT,
    1905             :                      nuncontacts,
    1906             :                      maxcontacts);
    1907         443 :   for (PetscInt i = 0; i < nuncontacts; ++i)
    1908             :   {
    1909             :     {
    1910             :       char * primary_secondary[2];
    1911           0 :       PetscInt sz = 2;
    1912           0 :       std::ostringstream oopt, ohelp;
    1913           0 :       oopt << "-dm_moose_uncontact_" << i;
    1914           0 :       ohelp << "Primary and secondary for uncontact " << i;
    1915           0 :       LibmeshPetscCallQ(PetscOptionsStringArray(oopt.str().c_str(),
    1916             :                                                 ohelp.str().c_str(),
    1917             :                                                 "DMMooseSetUnContacts",
    1918             :                                                 primary_secondary,
    1919             :                                                 &sz,
    1920             :                                                 LIBMESH_PETSC_NULLPTR));
    1921           0 :       if (sz != 2)
    1922           0 :         LIBMESH_SETERRQ2(
    1923             :             ((PetscObject)dm)->comm,
    1924             :             PETSC_ERR_ARG_SIZ,
    1925             :             "Expected 2 sideset IDs (primary & secondary) for uncontact %" LIBMESH_PETSCINT_FMT
    1926             :             ", got %" LIBMESH_PETSCINT_FMT " instead",
    1927             :             i,
    1928             :             sz);
    1929           0 :       uncontacts.push_back(DM_Moose::ContactName(std::string(primary_secondary[0]),
    1930           0 :                                                  std::string(primary_secondary[1])));
    1931           0 :       LibmeshPetscCallQ(PetscFree(primary_secondary[0]));
    1932           0 :       LibmeshPetscCallQ(PetscFree(primary_secondary[1]));
    1933           0 :     }
    1934             :     {
    1935           0 :       PetscBool displaced = PETSC_FALSE;
    1936           0 :       std::ostringstream oopt, ohelp;
    1937           0 :       oopt << "-dm_moose_uncontact_" << i << "_displaced";
    1938           0 :       ohelp << "Whether uncontact " << i << " is determined using displaced mesh or not";
    1939           0 :       LibmeshPetscCallQ(PetscOptionsBool(oopt.str().c_str(),
    1940             :                                          ohelp.str().c_str(),
    1941             :                                          "DMMooseSetUnContact",
    1942             :                                          PETSC_FALSE,
    1943             :                                          &displaced,
    1944             :                                          LIBMESH_PETSC_NULLPTR));
    1945           0 :       uncontact_displaced.push_back(displaced);
    1946           0 :     }
    1947             :   }
    1948         443 :   if (uncontacts.size())
    1949           0 :     LibmeshPetscCallQ(DMMooseSetUnContacts(dm, uncontacts, uncontact_displaced));
    1950             : 
    1951         443 :   PetscInt nsplits = 0;
    1952             :   /* Insert the usage of -dm_moose_fieldsplit_names into this help message, since the following
    1953             :    * if-clause might never fire, if -help is requested. */
    1954         443 :   const char * fdhelp = "Number of named fieldsplits defined by the DM.\n\
    1955             :                 \tNames of fieldsplits are defined by -dm_moose_fieldsplit_names <splitname1> <splitname2> ...\n\
    1956             :                 \tEach split can be configured with its own variables, blocks and sides, as any DMMoose";
    1957         443 :   LibmeshPetscCallQ(PetscOptionsInt(
    1958             :       "-dm_moose_nfieldsplits", fdhelp, "DMMooseSetSplitNames", nsplits, &nsplits, NULL));
    1959         443 :   if (nsplits)
    1960             :   {
    1961         161 :     PetscInt nnsplits = nsplits;
    1962         161 :     std::vector<std::string> split_names;
    1963             :     char ** splitnames;
    1964         161 :     LibmeshPetscCallQ(PetscMalloc(nsplits * sizeof(char *), &splitnames));
    1965         161 :     LibmeshPetscCallQ(PetscOptionsStringArray("-dm_moose_fieldsplit_names",
    1966             :                                               "Names of fieldsplits defined by the DM",
    1967             :                                               "DMMooseSetSplitNames",
    1968             :                                               splitnames,
    1969             :                                               &nnsplits,
    1970             :                                               LIBMESH_PETSC_NULLPTR));
    1971         161 :     if (!nnsplits)
    1972             :     {
    1973           0 :       for (PetscInt i = 0; i < nsplits; ++i)
    1974             :       {
    1975           0 :         std::ostringstream s;
    1976           0 :         s << i;
    1977           0 :         split_names.push_back(s.str());
    1978           0 :       }
    1979             :     }
    1980         161 :     else if (nsplits != nnsplits)
    1981           0 :       LIBMESH_SETERRQ2(((PetscObject)dm)->comm,
    1982             :                        PETSC_ERR_ARG_SIZ,
    1983             :                        "Expected %" LIBMESH_PETSCINT_FMT
    1984             :                        " fieldsplit names, got %" LIBMESH_PETSCINT_FMT " instead",
    1985             :                        nsplits,
    1986             :                        nnsplits);
    1987             :     else
    1988             :     {
    1989         483 :       for (PetscInt i = 0; i < nsplits; ++i)
    1990             :       {
    1991         322 :         split_names.push_back(std::string(splitnames[i]));
    1992         322 :         LibmeshPetscCallQ(PetscFree(splitnames[i]));
    1993             :       }
    1994             :     }
    1995         161 :     LibmeshPetscCallQ(PetscFree(splitnames));
    1996         161 :     LibmeshPetscCallQ(DMMooseSetSplitNames(dm, split_names));
    1997         161 :   }
    1998         443 :   LibmeshPetscCallQ(PetscOptionsBool("-dm_moose_print_embedding",
    1999             :                                      "Print IS embedding DM's dofs",
    2000             :                                      "DMMoose",
    2001             :                                      dmm->_print_embedding,
    2002             :                                      &dmm->_print_embedding,
    2003             :                                      LIBMESH_PETSC_NULLPTR));
    2004         443 :   PetscOptionsEnd();
    2005         443 :   LibmeshPetscCallQ(DMSetUp_Moose_Pre(dm)); /* Need some preliminary set up because, strangely
    2006             :                     enough, DMView() is called in DMSetFromOptions(). */
    2007         443 :   PetscFunctionReturn(PETSC_SUCCESS);
    2008             : }
    2009             : 
    2010             : static PetscErrorCode
    2011         411 : DMDestroy_Moose(DM dm)
    2012             : {
    2013         411 :   DM_Moose * dmm = (DM_Moose *)(dm->data);
    2014             : 
    2015             :   PetscFunctionBegin;
    2016         411 :   delete dmm->_name;
    2017         411 :   if (dmm->_vars)
    2018           0 :     delete dmm->_vars;
    2019         411 :   delete dmm->_var_ids;
    2020         411 :   delete dmm->_var_names;
    2021         411 :   if (dmm->_blocks)
    2022           0 :     delete dmm->_blocks;
    2023         411 :   delete dmm->_block_ids;
    2024         411 :   delete dmm->_block_names;
    2025         411 :   if (dmm->_sides)
    2026           0 :     delete dmm->_sides;
    2027         411 :   delete dmm->_side_ids;
    2028         411 :   delete dmm->_side_names;
    2029         411 :   if (dmm->_unsides)
    2030           0 :     delete dmm->_unsides;
    2031         411 :   delete dmm->_unside_ids;
    2032         411 :   delete dmm->_unside_names;
    2033         411 :   if (dmm->_unside_by_var)
    2034           0 :     delete dmm->_unside_by_var;
    2035         411 :   delete dmm->_unside_by_var_set;
    2036         411 :   if (dmm->_contacts)
    2037           0 :     delete dmm->_contacts;
    2038         411 :   delete dmm->_contact_names;
    2039         411 :   delete dmm->_contact_displaced;
    2040         411 :   if (dmm->_uncontacts)
    2041           0 :     delete dmm->_uncontacts;
    2042         411 :   delete dmm->_uncontact_names;
    2043         411 :   delete dmm->_uncontact_displaced;
    2044         411 :   if (dmm->_splits)
    2045             :   {
    2046         701 :     for (auto & sit : *(dmm->_splits))
    2047             :     {
    2048         290 :       LibmeshPetscCallQ(DMDestroy(&(sit.second._dm)));
    2049         290 :       LibmeshPetscCallQ(ISDestroy(&(sit.second._rembedding)));
    2050             :     }
    2051         411 :     delete dmm->_splits;
    2052             :   }
    2053         411 :   if (dmm->_splitlocs)
    2054         145 :     delete dmm->_splitlocs;
    2055         411 :   LibmeshPetscCallQ(ISDestroy(&dmm->_embedding));
    2056         411 :   LibmeshPetscCallQ(PetscFree(dm->data));
    2057         411 :   PetscFunctionReturn(PETSC_SUCCESS);
    2058             : }
    2059             : 
    2060             : PetscErrorCode
    2061         443 : DMCreateMoose(MPI_Comm comm,
    2062             :               NonlinearSystemBase & nl,
    2063             :               const DofMapBase & dof_map,
    2064             :               const System & system,
    2065             :               const std::string & dm_name,
    2066             :               DM * dm)
    2067             : {
    2068             :   PetscFunctionBegin;
    2069         443 :   LibmeshPetscCallQ(DMCreate(comm, dm));
    2070         443 :   LibmeshPetscCallQ(DMSetType(*dm, DMMOOSE));
    2071         443 :   LibmeshPetscCallQ(DMMooseSetNonlinearSystem(*dm, nl));
    2072         443 :   LibmeshPetscCallQ(DMMooseSetDofMap(*dm, dof_map));
    2073         443 :   LibmeshPetscCallQ(DMMooseSetSystem(*dm, system));
    2074         443 :   LibmeshPetscCallQ(DMMooseSetName(*dm, dm_name));
    2075         443 :   PetscFunctionReturn(PETSC_SUCCESS);
    2076             : }
    2077             : 
    2078             : EXTERN_C_BEGIN
    2079             : PetscErrorCode
    2080         443 : DMCreate_Moose(DM dm)
    2081             : {
    2082             :   DM_Moose * dmm;
    2083             : 
    2084             :   PetscFunctionBegin;
    2085             :   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
    2086             : #if PETSC_RELEASE_LESS_THAN(3, 18, 0)
    2087             :   LibmeshPetscCallQ(PetscNewLog(dm, &dmm));
    2088             : #else // PetscNewLog was deprecated
    2089         443 :   LibmeshPetscCallQ(PetscNew(&dmm));
    2090             : #endif
    2091         443 :   dm->data = dmm;
    2092             : 
    2093         443 :   dmm->_name = new (std::string);
    2094         443 :   dmm->_var_ids = new (std::map<std::string, unsigned int>);
    2095         443 :   dmm->_block_ids = new (std::map<std::string, subdomain_id_type>);
    2096         443 :   dmm->_var_names = new (std::map<unsigned int, std::string>);
    2097         443 :   dmm->_block_names = new (std::map<unsigned int, std::string>);
    2098         443 :   dmm->_side_ids = new (std::map<std::string, BoundaryID>);
    2099         443 :   dmm->_side_names = new (std::map<BoundaryID, std::string>);
    2100         443 :   dmm->_unside_ids = new (std::map<std::string, BoundaryID>);
    2101         443 :   dmm->_unside_names = new (std::map<BoundaryID, std::string>);
    2102         443 :   dmm->_unside_by_var_set = new (std::set<std::pair<BoundaryID, unsigned int>>);
    2103         443 :   dmm->_contact_names = new (std::map<DM_Moose::ContactID, DM_Moose::ContactName>);
    2104         443 :   dmm->_uncontact_names = new (std::map<DM_Moose::ContactID, DM_Moose::ContactName>);
    2105         443 :   dmm->_contact_displaced = new (std::map<DM_Moose::ContactName, PetscBool>);
    2106         443 :   dmm->_uncontact_displaced = new (std::map<DM_Moose::ContactName, PetscBool>);
    2107             : 
    2108         443 :   dmm->_splits = new (std::map<std::string, DM_Moose::SplitInfo>);
    2109             : 
    2110         443 :   dmm->_print_embedding = PETSC_FALSE;
    2111             : 
    2112         443 :   dm->ops->createglobalvector = DMCreateGlobalVector_Moose;
    2113         443 :   dm->ops->createlocalvector = 0; // DMCreateLocalVector_Moose;
    2114         443 :   dm->ops->getcoloring = 0;       // DMGetColoring_Moose;
    2115         443 :   dm->ops->creatematrix = DMCreateMatrix_Moose;
    2116         443 :   dm->ops->createinterpolation = 0; // DMCreateInterpolation_Moose;
    2117             : 
    2118         443 :   dm->ops->refine = 0;  // DMRefine_Moose;
    2119         443 :   dm->ops->coarsen = 0; // DMCoarsen_Moose;
    2120             : #if PETSC_RELEASE_LESS_THAN(3, 12, 0)
    2121             :   dm->ops->getinjection = 0;  // DMGetInjection_Moose;
    2122             :   dm->ops->getaggregates = 0; // DMGetAggregates_Moose;
    2123             : #else
    2124         443 :   dm->ops->createinjection = 0;
    2125             : #endif
    2126             : 
    2127         443 :   dm->ops->createfielddecomposition = DMCreateFieldDecomposition_Moose;
    2128         443 :   dm->ops->createdomaindecomposition = DMCreateDomainDecomposition_Moose;
    2129             : 
    2130         443 :   dm->ops->destroy = DMDestroy_Moose;
    2131         443 :   dm->ops->view = DMView_Moose;
    2132         443 :   dm->ops->setfromoptions = DMSetFromOptions_Moose;
    2133         443 :   dm->ops->setup = DMSetUp_Moose;
    2134         443 :   PetscFunctionReturn(PETSC_SUCCESS);
    2135             : }
    2136             : EXTERN_C_END
    2137             : 
    2138             : #undef __FUNCT__
    2139             : #define __FUNCT__ "SNESUpdateDMMoose"
    2140             : PetscErrorCode
    2141           0 : SNESUpdateDMMoose(SNES snes, PetscInt iteration)
    2142             : {
    2143             :   /* This is called any time the structure of the problem changes in a way that affects the Jacobian
    2144             :      sparsity pattern.
    2145             :      For example, this may happen when NodeFaceConstraints change Jacobian's sparsity pattern based
    2146             :      on newly-detected Penetration.
    2147             :      In that case certain preconditioners (e.g., PCASM) will not work, unless we tell them that the
    2148             :      sparsity pattern has changed.
    2149             :      For now we are rebuilding the whole KSP, when necessary.
    2150             :   */
    2151             :   DM dm;
    2152             :   KSP ksp;
    2153             :   const char * prefix;
    2154             :   MPI_Comm comm;
    2155             :   PC pc;
    2156             : 
    2157             :   PetscFunctionBegin;
    2158           0 :   if (iteration)
    2159             :   {
    2160             :     /* TODO: limit this only to situations when displaced (un)contact splits are present, as is
    2161             :      * DisplacedProblem(). */
    2162           0 :     LibmeshPetscCallQ(SNESGetDM(snes, &dm));
    2163           0 :     LibmeshPetscCallQ(DMMooseReset(dm));
    2164           0 :     LibmeshPetscCallQ(DMSetUp(dm));
    2165           0 :     LibmeshPetscCallQ(SNESGetKSP(snes, &ksp));
    2166             :     /* Should we rebuild the whole KSP? */
    2167           0 :     LibmeshPetscCallQ(PetscObjectGetOptionsPrefix((PetscObject)ksp, &prefix));
    2168           0 :     LibmeshPetscCallQ(PetscObjectGetComm((PetscObject)ksp, &comm));
    2169           0 :     LibmeshPetscCallQ(PCCreate(comm, &pc));
    2170           0 :     LibmeshPetscCallQ(PCSetDM(pc, dm));
    2171           0 :     LibmeshPetscCallQ(PCSetOptionsPrefix(pc, prefix));
    2172           0 :     LibmeshPetscCallQ(PCSetFromOptions(pc));
    2173           0 :     LibmeshPetscCallQ(KSPSetPC(ksp, pc));
    2174           0 :     LibmeshPetscCallQ(PCDestroy(&pc));
    2175             :   }
    2176           0 :   PetscFunctionReturn(PETSC_SUCCESS);
    2177             : }
    2178             : 
    2179             : PetscErrorCode
    2180         129 : DMMooseRegisterAll()
    2181             : {
    2182             :   static PetscBool DMMooseRegisterAllCalled = PETSC_FALSE;
    2183             : 
    2184             :   PetscFunctionBegin;
    2185         129 :   if (!DMMooseRegisterAllCalled)
    2186             :   {
    2187         128 :     LibmeshPetscCallQ(DMRegister(DMMOOSE, DMCreate_Moose));
    2188         128 :     DMMooseRegisterAllCalled = PETSC_TRUE;
    2189             :   }
    2190         129 :   PetscFunctionReturn(PETSC_SUCCESS);
    2191             : }

Generated by: LCOV version 1.14