LCOV - code coverage report
Current view: top level - src/utils - PetscDMMoose.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 598 1119 53.4 %
Date: 2025-07-17 01:28:37 Functions: 25 49 51.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14