LCOV - code coverage report
Current view: top level - include/systems - generic_projector.h (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4229 (6a9aeb) with base 727f46 Lines: 1093 1206 90.6 %
Date: 2025-08-19 19:27:09 Functions: 310 393 78.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // The libMesh Finite Element Library.
       2             : // Copyright (C) 2002-2025 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
       3             : 
       4             : // This library is free software; you can redistribute it and/or
       5             : // modify it under the terms of the GNU Lesser General Public
       6             : // License as published by the Free Software Foundation; either
       7             : // version 2.1 of the License, or (at your option) any later version.
       8             : 
       9             : // This library is distributed in the hope that it will be useful,
      10             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12             : // Lesser General Public License for more details.
      13             : 
      14             : // You should have received a copy of the GNU Lesser General Public
      15             : // License along with this library; if not, write to the Free Software
      16             : // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
      17             : 
      18             : 
      19             : 
      20             : #ifndef GENERIC_PROJECTOR_H
      21             : #define GENERIC_PROJECTOR_H
      22             : 
      23             : // C++ includes
      24             : #include <vector>
      25             : 
      26             : // libMesh includes
      27             : #include "libmesh/dense_matrix.h"
      28             : #include "libmesh/dense_vector.h"
      29             : #include "libmesh/dof_map.h"
      30             : #include "libmesh/elem.h"
      31             : #include "libmesh/fe_base.h"
      32             : #include "libmesh/fe_interface.h"
      33             : #include "libmesh/int_range.h"
      34             : #include "libmesh/libmesh_logging.h"
      35             : #include "libmesh/mesh_tools.h"
      36             : #include "libmesh/numeric_vector.h"
      37             : #include "libmesh/quadrature.h"
      38             : #include "libmesh/system.h"
      39             : #include "libmesh/threads.h"
      40             : #include "libmesh/tensor_tools.h"
      41             : #include "libmesh/libmesh_common.h"
      42             : 
      43             : // TIMPI includes
      44             : #include "timpi/parallel_sync.h"
      45             : 
      46             : // C++ Includes
      47             : #include <memory>
      48             : 
      49             : namespace libMesh
      50             : {
      51             : 
      52             : /**
      53             :  * For ease of communication, we allow users to translate their own
      54             :  * value types to a more easily computable (typically a vector of some
      55             :  * fixed-size type) output, by specializing these calls using
      56             :  * different types.
      57             :  */
      58             : template <typename T>
      59             : struct TypeToSend {
      60             :   typedef T type;
      61             : };
      62             : 
      63             : template <typename T>
      64        1065 : const typename TypeToSend<T>::type convert_to_send (const T& in)
      65       18576 : { return in; }
      66             : 
      67             : template <typename SendT, typename T>
      68        1065 : void convert_from_receive (SendT & received, T & converted)
      69       18576 : { converted = received; }
      70             : 
      71             : /**
      72             :  * The GenericProjector class implements the core of other projection
      73             :  * operations, using two input functors to read values to be projected
      74             :  * and an output functor to set degrees of freedom in the result.
      75             :  *
      76             :  * This may be executed in parallel on multiple threads.
      77             :  *
      78             :  * \author Roy H. Stogner
      79             :  * \date 2016
      80             :  */
      81             : template <typename FFunctor, typename GFunctor,
      82             :           typename FValue, typename ProjectionAction>
      83             : class GenericProjector
      84             : {
      85             : public:
      86             :   /**
      87             :    * Convenience typedef for the Node-to-attached-Elem mapping that
      88             :    * may be passed in to the constructor.
      89             :    */
      90             :   typedef std::unordered_map<dof_id_type, std::vector<dof_id_type>> NodesToElemMap;
      91             : 
      92             : private:
      93             :   const System & system;
      94             : 
      95             :   // For TBB compatibility and thread safety we'll copy these in
      96             :   // operator()
      97             :   FFunctor & master_f;
      98             : 
      99             :   /**
     100             :    * Needed for C1 type elements only. master_g is either a shallow
     101             :    * copy of a GFunctor pointer passed to the constructor, or to
     102             :    * master_g_deepcopy.get(), depending on which constructor was
     103             :    * called.
     104             :    */
     105             :   std::unique_ptr<GFunctor> master_g_deepcopy;
     106             :   GFunctor * master_g;
     107             : 
     108             :   ProjectionAction & master_action;
     109             :   const std::vector<unsigned int> & variables;
     110             : 
     111             :   /**
     112             :    * nodes_to_elem is either a shallow copy of a map passed in to
     113             :    * the constructor, or points to nodes_to_elem_ourcopy, if no
     114             :    * such map was provided.
     115             :    */
     116             :   NodesToElemMap nodes_to_elem_ourcopy;
     117             :   NodesToElemMap * nodes_to_elem;
     118             : 
     119             :   bool done_saving_ids;
     120             : 
     121             : public:
     122      243232 :   GenericProjector (const System & system_in,
     123             :                     FFunctor & f_in,
     124             :                     GFunctor * g_in,
     125             :                     ProjectionAction & act_in,
     126             :                     const std::vector<unsigned int> & variables_in,
     127             :                     NodesToElemMap * nodes_to_elem_in = nullptr) :
     128      228792 :     system(system_in),
     129      228792 :     master_f(f_in),
     130      228792 :     master_g(g_in),
     131      228792 :     master_action(act_in),
     132      228792 :     variables(variables_in),
     133      257672 :     nodes_to_elem(nodes_to_elem_in)
     134             :   {
     135      243232 :     if (!nodes_to_elem_in)
     136             :       {
     137      243232 :         MeshTools::build_nodes_to_elem_map (system.get_mesh(), nodes_to_elem_ourcopy);
     138      243232 :         nodes_to_elem = &nodes_to_elem_ourcopy;
     139             :       }
     140      243232 :   }
     141             : 
     142             :   GenericProjector (const GenericProjector & in) :
     143             :     system(in.system),
     144             :     master_f(in.master_f),
     145             :     master_g_deepcopy(in.master_g ? std::make_unique<GFunctor>(*in.master_g) : nullptr),
     146             :     master_g(in.master_g ? master_g_deepcopy.get() : nullptr),
     147             :     master_action(in.master_action),
     148             :     variables(in.variables),
     149             :     nodes_to_elem(in.nodes_to_elem)
     150             :   {}
     151             : 
     152      472024 :   ~GenericProjector() = default;
     153             : 
     154             :   // The single "project" function handles all the threaded projection
     155             :   // calculations and intervening MPI communications
     156             :   void project(const ConstElemRange & range);
     157             : 
     158             :   // We generally need to hang on to every value we've calculated
     159             :   // until we're all done, because later projection calculations
     160             :   // depend on boundary data from earlier calculations.
     161             :   std::unordered_map<dof_id_type, typename FFunctor::ValuePushType> ids_to_save;
     162             : 
     163             :   // This needs to be sorted so we can get sorted dof indices cheaply
     164             :   // later
     165             :   typedef std::set<unsigned int> var_set;
     166             : 
     167             :   // We now need to divide our threadable work into stages, to allow
     168             :   // for the potential necessity of MPI communication in between them.
     169             :   struct SubFunctor {
     170             :     GenericProjector & projector;
     171             : 
     172             :     SubFunctor (GenericProjector & p);
     173             : 
     174             :     // While we're working on nodes, also figure out which ghosted
     175             :     // nodes will have data we might need to send to their owners
     176             :     // instead of being acted on by ourselves.
     177             :     //
     178             :     // We keep track of which dof ids we might need to send, and what
     179             :     // values those ids should get (along with a pprocessor_id to
     180             :     // leave invalid in case *we* can't compute those values either).
     181             :     std::unordered_map<dof_id_type,
     182             :                        std::pair<typename FFunctor::ValuePushType,
     183             :                                  processor_id_type>> new_ids_to_push;
     184             : 
     185             :     // We'll hang on to any new ids to save on a per-thread basis
     186             :     // because we won't need them until subsequent phases
     187             :     std::unordered_map<dof_id_type, typename FFunctor::ValuePushType> new_ids_to_save;
     188             : 
     189             :     // Helper function for filling new_ids_to_push
     190             :     void find_dofs_to_send (const Node & node,
     191             :                             const Elem & elem,
     192             :                             unsigned short node_num,
     193             :                             const var_set & vars);
     194             : 
     195             :     // When we have new data to act on, we may also need to save it
     196             :     // and get ready to push it.
     197             :     template <typename InsertInput,
     198             :               typename std::enable_if<
     199             :                   std::is_same<typename ProjectionAction::InsertInput, InsertInput>::value,
     200             :                   int>::type = 0>
     201             :     void insert_id(dof_id_type id, const InsertInput & val, processor_id_type pid);
     202             : 
     203             :     template <typename InsertInput,
     204             :               typename std::enable_if<
     205             :                   !std::is_same<typename ProjectionAction::InsertInput, InsertInput>::value,
     206             :                   int>::type = 0>
     207             :     void insert_id(dof_id_type id, const InsertInput & val, processor_id_type pid);
     208             : 
     209             :     template <typename InsertInput,
     210             :               typename std::enable_if<
     211             :                   std::is_same<typename ProjectionAction::InsertInput, InsertInput>::value,
     212             :                   int>::type = 0>
     213             :     void insert_ids(const std::vector<dof_id_type> & ids,
     214             :                     const std::vector<InsertInput> & vals,
     215             :                     processor_id_type pid);
     216             : 
     217             :     template <typename InsertInput,
     218             :               typename std::enable_if<
     219             :                   !std::is_same<typename ProjectionAction::InsertInput, InsertInput>::value,
     220             :                   int>::type = 0>
     221             :     void insert_ids(const std::vector<dof_id_type> & ids,
     222             :                     const std::vector<InsertInput> & vals,
     223             :                     processor_id_type pid);
     224             : 
     225             :     // Our subclasses will need to merge saved ids
     226             :     void join (const SubFunctor & other);
     227             : 
     228             :   protected:
     229             :     // For thread safety and efficiency we'll want thread-local copies
     230             :     // of various functors
     231             :     ProjectionAction action;
     232             :     FFunctor f;
     233             : 
     234             :     // Each stage of the work we do will require us to prepare
     235             :     // FEMContext objects to assist in the work.
     236             :     FEMContext context;
     237             : 
     238             :     // These get used often enough to cache them
     239             :     std::vector<FEContinuity> conts;
     240             :     std::vector<FEFieldType> field_types;
     241             : 
     242             :     const System & system;
     243             :   };
     244             : 
     245             : 
     246             :   typedef std::pair<const Node *, std::tuple<const Elem *, unsigned short, var_set>>
     247             :     node_projection;
     248             : 
     249             :   typedef StoredRange<std::vector<node_projection>::const_iterator,
     250             :                       node_projection> node_range;
     251             : 
     252             : 
     253      921215 :   struct SubProjector : public SubFunctor {
     254             :     SubProjector (GenericProjector & p);
     255             : 
     256             :     using SubFunctor::action;
     257             :     using SubFunctor::f;
     258             :     using SubFunctor::system;
     259             :     using SubFunctor::context;
     260             :     using SubFunctor::conts;
     261             :     using SubFunctor::field_types;
     262             :     using SubFunctor::insert_id;
     263             :     using SubFunctor::insert_ids;
     264             : 
     265             :   protected:
     266             :     // Projections of C1 elements require a gradient as well
     267             :     std::unique_ptr<GFunctor> g;
     268             : 
     269             :     void construct_projection
     270             :       (const std::vector<dof_id_type> & dof_indices_var,
     271             :        const std::vector<unsigned int> & involved_dofs,
     272             :        unsigned int var_component,
     273             :        const Node * node,
     274             :        const FEGenericBase<typename FFunctor::RealType> & fe);
     275             :   };
     276             : 
     277             : 
     278             :   // First we'll copy DoFs where we can, while sorting remaining DoFs
     279             :   // for interpolation and projection later.
     280             :   struct SortAndCopy : public SubFunctor {
     281      243232 :     SortAndCopy (GenericProjector & p) : SubFunctor(p) {}
     282             : 
     283        7718 :     SortAndCopy (SortAndCopy & other, Threads::split) : SubFunctor(other.projector) {}
     284             : 
     285             :     using SubFunctor::action;
     286             :     using SubFunctor::f;
     287             :     using SubFunctor::system;
     288             :     using SubFunctor::context;
     289             :     using SubFunctor::insert_ids;
     290             : 
     291             :     void operator() (const ConstElemRange & range);
     292             : 
     293             :     // We need to merge saved multimaps when working from multiple
     294             :     // threads
     295             :     void join (const SortAndCopy & other);
     296             : 
     297             :     // For vertices, edges and sides, we need to know what element,
     298             :     // what local vertex/edge/side number, and what set of variables
     299             :     // to evaluate.
     300             :     typedef std::unordered_multimap<const Node *, std::tuple<const Elem *, unsigned short, var_set>> ves_multimap;
     301             : 
     302             :     ves_multimap vertices, edges, sides;
     303             :     std::vector<const Elem *> interiors;
     304             :   };
     305             : 
     306             : 
     307      238808 :   struct ProjectVertices : public SubProjector {
     308      243232 :     ProjectVertices (GenericProjector & p) : SubProjector(p) {}
     309             : 
     310        5950 :     ProjectVertices (ProjectVertices & p_v, Threads::split) : SubProjector(p_v.projector) {}
     311             : 
     312             :     using SubProjector::action;
     313             :     using SubProjector::f;
     314             :     using SubProjector::g;
     315             :     using SubProjector::system;
     316             :     using SubProjector::context;
     317             :     using SubProjector::conts;
     318             :     using SubFunctor::field_types;
     319             : 
     320             :     using SubProjector::insert_id;
     321             :     using SubProjector::insert_ids;
     322             : 
     323             :     void operator() (const node_range & range);
     324             :   };
     325             : 
     326             : 
     327        7798 :   struct ProjectEdges : public SubProjector {
     328      243232 :     ProjectEdges (GenericProjector & p) : SubProjector(p) {}
     329             : 
     330        1157 :     ProjectEdges (ProjectEdges & p_e, Threads::split) : SubProjector(p_e.projector) {}
     331             : 
     332             :     using SubProjector::action;
     333             :     using SubProjector::f;
     334             :     using SubProjector::g;
     335             :     using SubProjector::system;
     336             :     using SubProjector::context;
     337             :     using SubProjector::conts;
     338             :     using SubFunctor::field_types;
     339             : 
     340             :     using SubProjector::insert_id;
     341             :     using SubProjector::insert_ids;
     342             : 
     343             :     void operator() (const node_range & range);
     344             :   };
     345             : 
     346        9030 :   struct ProjectSides : public SubProjector {
     347      243232 :     ProjectSides (GenericProjector & p) : SubProjector(p) {}
     348             : 
     349        3680 :     ProjectSides (ProjectSides & p_s, Threads::split) : SubProjector(p_s.projector) {}
     350             : 
     351             :     using SubProjector::action;
     352             :     using SubProjector::f;
     353             :     using SubProjector::g;
     354             :     using SubProjector::system;
     355             :     using SubProjector::context;
     356             :     using SubProjector::conts;
     357             :     using SubFunctor::field_types;
     358             : 
     359             :     using SubProjector::insert_id;
     360             :     using SubProjector::insert_ids;
     361             : 
     362             :     void operator() (const node_range & range);
     363             :   };
     364             : 
     365             : 
     366             :   typedef StoredRange<std::vector<const Elem *>::const_iterator,
     367             :                       const Elem *> interior_range;
     368             : 
     369         863 :   struct ProjectInteriors : public SubProjector {
     370      243232 :     ProjectInteriors (GenericProjector & p) : SubProjector(p) {}
     371             : 
     372        1788 :     ProjectInteriors (ProjectInteriors & p_i, Threads::split) : SubProjector(p_i.projector) {}
     373             : 
     374             :     using SubProjector::action;
     375             :     using SubProjector::f;
     376             :     using SubProjector::g;
     377             :     using SubProjector::system;
     378             :     using SubProjector::context;
     379             :     using SubProjector::conts;
     380             :     using SubFunctor::field_types;
     381             : 
     382             :     using SubProjector::insert_id;
     383             :     using SubProjector::insert_ids;
     384             : 
     385             :     void operator() (const interior_range & range);
     386             :   };
     387             : 
     388             :   template <typename Value>
     389             :   void send_and_insert_dof_values
     390             :     (std::unordered_map<dof_id_type, std::pair<Value, processor_id_type>> & ids_to_push,
     391             :      ProjectionAction & action) const;
     392             : };
     393             : 
     394             : 
     395             : /**
     396             :  * The VectorSetAction output functor class can be used with a
     397             :  * GenericProjector to set projection values (which must be of type
     398             :  * Val) as coefficients of the given NumericVector.
     399             :  *
     400             :  * \author Roy H. Stogner
     401             :  * \date 2016
     402             :  */
     403             : 
     404             : template <typename Val>
     405             : class VectorSetAction
     406             : {
     407             : private:
     408             :   NumericVector<Val> & target_vector;
     409             : 
     410             : public:
     411             :   typedef Val InsertInput;
     412             : 
     413      242602 :   VectorSetAction(NumericVector<Val> & target_vec) :
     414      242602 :     target_vector(target_vec) {}
     415             : 
     416    10907403 :   void insert(dof_id_type id,
     417             :               Val val)
     418             :   {
     419             :     // Lock the new vector since it is shared among threads.
     420             :     {
     421     2197440 :       Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
     422    10907403 :       target_vector.set(id, val);
     423             :     }
     424    10907403 :   }
     425             : 
     426             : 
     427     2409837 :   void insert(const std::vector<dof_id_type> & dof_indices,
     428             :               const DenseVector<Val> & Ue)
     429             :   {
     430             :     const numeric_index_type
     431     2409837 :       first = target_vector.first_local_index(),
     432     2409837 :       last  = target_vector.last_local_index();
     433             : 
     434      275534 :     unsigned int size = Ue.size();
     435             : 
     436      275534 :     libmesh_assert_equal_to(size, dof_indices.size());
     437             : 
     438             :     // Lock the new vector since it is shared among threads.
     439             :     {
     440      551068 :       Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
     441             : 
     442     2546322 :       for (unsigned int i = 0; i != size; ++i)
     443      150652 :         if ((dof_indices[i] >= first) && (dof_indices[i] <  last))
     444      150652 :           target_vector.set(dof_indices[i], Ue(i));
     445             :     }
     446     2409837 :   }
     447             : 
     448             : };
     449             : 
     450             : 
     451             : /**
     452             :  * The FEMFunctionWrapper input functor class can be used with a
     453             :  * GenericProjector to read values from an FEMFunction.
     454             :  *
     455             :  * \author Roy H. Stogner
     456             :  * \date 2016
     457             :  */
     458             : 
     459             : template <typename Output>
     460      398262 : class FEMFunctionWrapper
     461             : {
     462             : protected:
     463             :   typedef typename TensorTools::MakeBaseNumber<Output>::type DofValueType;
     464             : 
     465             : public:
     466             :   typedef typename TensorTools::MakeReal<Output>::type RealType;
     467             :   typedef DofValueType ValuePushType;
     468             :   typedef Output FunctorValue;
     469             : 
     470      396942 :   FEMFunctionWrapper(const FEMFunctionBase<Output> & f) : _f(f.clone()) {}
     471             : 
     472       52951 :   FEMFunctionWrapper(const FEMFunctionWrapper<Output> & fw) :
     473     1031958 :     _f(fw._f->clone()) {}
     474             : 
     475     1013884 :   void init_context (FEMContext & c) { _f->init_context(c); }
     476             : 
     477       80590 :   Output eval_at_node (const FEMContext & c,
     478             :                        unsigned int i,
     479             :                        unsigned int /*elem_dim*/,
     480             :                        const Node & n,
     481             :                        bool /*extra_hanging_dofs*/,
     482             :                        const Real time)
     483      897035 :   { return _f->component(c, i, n, time); }
     484             : 
     485      539701 :   Output eval_at_point (const FEMContext & c,
     486             :                         unsigned int i,
     487             :                         const Point & n,
     488             :                         const Real time,
     489             :                         bool /*skip_context_check*/)
     490     6727421 :   { return _f->component(c, i, n, time); }
     491             : 
     492           0 :   void eval_mixed_derivatives (const FEMContext & /*c*/,
     493             :                                unsigned int /*i*/,
     494             :                                unsigned int /*dim*/,
     495             :                                const Node & /*n*/,
     496             :                                std::vector<Output> & /*derivs*/)
     497           0 :   { libmesh_error(); } // this is only for grid projections
     498             : 
     499      141720 :   bool is_grid_projection() { return false; }
     500             : 
     501           0 :   void eval_old_dofs (const Elem &,
     502             :                       unsigned int,
     503             :                       unsigned int,
     504             :                       std::vector<dof_id_type> &,
     505             :                       std::vector<Output> &)
     506           0 :   { libmesh_error(); }
     507             : 
     508           0 :   void eval_old_dofs (const Elem &,
     509             :                       const FEType &,
     510             :                       unsigned int,
     511             :                       unsigned int,
     512             :                       std::vector<dof_id_type> &,
     513             :                       std::vector<Output> &)
     514           0 :   { libmesh_error(); }
     515             : 
     516             : private:
     517             :   std::unique_ptr<FEMFunctionBase<Output>> _f;
     518             : };
     519             : 
     520             : 
     521             : /**
     522             :  * The OldSolutionBase input functor abstract base class is the root
     523             :  * of the OldSolutionValue and OldSolutionCoefs classes which allow a
     524             :  * GenericProjector to read old solution values or solution
     525             :  * interpolation coefficients for a just-refined-and-coarsened mesh.
     526             :  *
     527             :  * \author Roy H. Stogner
     528             :  * \date 2016
     529             :  */
     530             : 
     531             : #ifdef LIBMESH_ENABLE_AMR
     532             : template <typename Output,
     533             :           void (FEMContext::*point_output) (unsigned int,
     534             :                                             const Point &,
     535             :                                             Output &,
     536             :                                             const Real) const>
     537       99918 : class OldSolutionBase
     538             : {
     539             : protected:
     540             :   typedef typename TensorTools::MakeBaseNumber<Output>::type DofValueType;
     541             : public:
     542             :   typedef typename TensorTools::MakeReal<Output>::type RealType;
     543             : 
     544      338051 :   OldSolutionBase(const libMesh::System & sys_in,
     545             :                   const std::vector<unsigned int> * vars) :
     546      306781 :     last_elem(nullptr),
     547      306781 :     sys(sys_in),
     548      338051 :     old_context(sys_in, vars, /* allocate_local_matrices= */ false)
     549             :   {
     550             :     // We'll be queried for components but we'll typically be looking
     551             :     // up data by variables, and those indices don't always match
     552     1240531 :     auto make_lookup = [this](unsigned int v)
     553             :       {
     554      415488 :         const unsigned int vcomp = sys.variable_scalar_number(v,0);
     555      433364 :         if (vcomp >= component_to_var.size())
     556      415488 :           component_to_var.resize(vcomp+1, static_cast<unsigned int>(-1));
     557      433364 :         component_to_var[vcomp] = v;
     558             :       };
     559             : 
     560      338051 :     if (vars)
     561      753539 :       for (auto v : *vars)
     562      415488 :         make_lookup(v);
     563             :     else
     564           0 :       for (auto v : make_range(sys.n_vars()))
     565           0 :         make_lookup(v);
     566      338051 :   }
     567             : 
     568             :   OldSolutionBase(const OldSolutionBase & in) :
     569             :     last_elem(nullptr),
     570             :     sys(in.sys),
     571             :     old_context(sys, in.old_context.active_vars(), /* allocate_local_matrices= */ false),
     572             :     component_to_var(in.component_to_var)
     573             :   {
     574             :   }
     575             : 
     576             :   static void get_shape_outputs(FEAbstract & fe);
     577             : 
     578             :   // Integrating on new mesh elements, we won't yet have an up to date
     579             :   // current_local_solution.
     580      250025 :   void init_context (FEMContext & c)
     581             :   {
     582       12423 :     c.set_algebraic_type(FEMContext::DOFS_ONLY);
     583             : 
     584             :     const std::set<unsigned char> & elem_dims =
     585       12423 :       old_context.elem_dimensions();
     586             : 
     587             :     // Loop over variables and dimensions, to prerequest
     588      501145 :     for (const auto & dim : elem_dims)
     589             :       {
     590       12463 :         FEAbstract * fe = nullptr;
     591      251120 :         if (old_context.active_vars())
     592      558081 :           for (auto var : *old_context.active_vars())
     593             :             {
     594      306961 :               old_context.get_element_fe(var, fe, dim);
     595       14240 :               get_shape_outputs(*fe);
     596             :             }
     597             :         else
     598           0 :           for (auto var : make_range(sys.n_vars()))
     599             :             {
     600           0 :               old_context.get_element_fe(var, fe, dim);
     601           0 :               get_shape_outputs(*fe);
     602             :             }
     603             :       }
     604      250025 :   }
     605             : 
     606      616957 :   bool is_grid_projection() { return true; }
     607             : 
     608             : protected:
     609             :   void check_old_context (const FEMContext & c)
     610             :   {
     611             :     LOG_SCOPE ("check_old_context(c)", "OldSolutionBase");
     612             :     const Elem & elem = c.get_elem();
     613             :     if (last_elem != &elem)
     614             :       {
     615             :         if (elem.refinement_flag() == Elem::JUST_REFINED)
     616             :           {
     617             :             old_context.pre_fe_reinit(sys, elem.parent());
     618             :           }
     619             :         else if (elem.refinement_flag() == Elem::JUST_COARSENED)
     620             :           {
     621             :             libmesh_error();
     622             :           }
     623             :         else
     624             :           {
     625             :             if (!elem.get_old_dof_object())
     626             :               {
     627             :                 libmesh_error();
     628             :               }
     629             : 
     630             :             old_context.pre_fe_reinit(sys, &elem);
     631             :           }
     632             : 
     633             :         last_elem = &elem;
     634             :       }
     635             :     else
     636             :       {
     637             :         libmesh_assert(old_context.has_elem());
     638             :       }
     639             :   }
     640             : 
     641             : 
     642     4409936 :   bool check_old_context (const FEMContext & c, const Point & p)
     643             :   {
     644      737282 :     LOG_SCOPE ("check_old_context(c,p)", "OldSolutionBase");
     645      737308 :     const Elem & elem = c.get_elem();
     646     4409936 :     if (last_elem != &elem)
     647             :       {
     648     3130220 :         if (elem.refinement_flag() == Elem::JUST_REFINED)
     649             :           {
     650     2951565 :             old_context.pre_fe_reinit(sys, elem.parent());
     651             :           }
     652      164841 :         else if (elem.refinement_flag() == Elem::JUST_COARSENED)
     653             :           {
     654             :             // Find the child with this point.  Use out_of_elem_tol
     655             :             // (in physical space, which may correspond to a large
     656             :             // tolerance in master space!) to allow for out-of-element
     657             :             // finite differencing of mixed gradient terms.  Pray we
     658             :             // have no quadrature locations which are within 1e-5 of
     659             :             // the element subdivision boundary but are not exactly on
     660             :             // that boundary.
     661      156809 :             const Real master_tol = out_of_elem_tol / elem.hmax() * 2;
     662             : 
     663      294154 :             for (auto & child : elem.child_ref_range())
     664      294154 :               if (child.close_to_point(p, master_tol))
     665             :                 {
     666      156809 :                   old_context.pre_fe_reinit(sys, &child);
     667       13151 :                   break;
     668             :                 }
     669             : 
     670       13151 :             libmesh_assert
     671             :               (old_context.get_elem().close_to_point(p, master_tol));
     672             :           }
     673             :         else
     674             :           {
     675        8032 :             if (!elem.get_old_dof_object())
     676           0 :               return false;
     677             : 
     678        8032 :             old_context.pre_fe_reinit(sys, &elem);
     679             :           }
     680             : 
     681     2879256 :         last_elem = &elem;
     682             :       }
     683             :     else
     684             :       {
     685      117691 :         libmesh_assert(old_context.has_elem());
     686             : 
     687     1530680 :         const Real master_tol = out_of_elem_tol / elem.hmax() * 2;
     688             : 
     689     1530680 :         if (!old_context.get_elem().close_to_point(p, master_tol))
     690             :           {
     691         428 :             libmesh_assert_equal_to
     692             :               (elem.refinement_flag(), Elem::JUST_COARSENED);
     693             : 
     694       30099 :             for (auto & child : elem.child_ref_range())
     695       30099 :               if (child.close_to_point(p, master_tol))
     696             :                 {
     697       10731 :                   old_context.pre_fe_reinit(sys, &child);
     698         428 :                   break;
     699             :                 }
     700             : 
     701         428 :             libmesh_assert
     702             :               (old_context.get_elem().close_to_point(p, master_tol));
     703             :           }
     704             :       }
     705             : 
     706      368641 :     return true;
     707             :   }
     708             : 
     709             : protected:
     710             :   const Elem * last_elem;
     711             :   const System & sys;
     712             :   FEMContext old_context;
     713             :   std::vector<unsigned int> component_to_var;
     714             : 
     715             :   static const Real out_of_elem_tol;
     716             : };
     717             : 
     718             : 
     719             : /**
     720             :  * The OldSolutionValue input functor class can be used with
     721             :  * GenericProjector to read values from a solution on a
     722             :  * just-refined-and-coarsened mesh.
     723             :  *
     724             :  * \author Roy H. Stogner
     725             :  * \date 2016
     726             :  */
     727             : template <typename Output,
     728             :           void (FEMContext::*point_output) (unsigned int,
     729             :                                             const Point &,
     730             :                                             Output &,
     731             :                                             const Real) const>
     732      100318 : class OldSolutionValue : public OldSolutionBase<Output, point_output>
     733             : {
     734             : public:
     735             :   using typename OldSolutionBase<Output, point_output>::RealType;
     736             :   using typename OldSolutionBase<Output, point_output>::DofValueType;
     737             :   typedef Output FunctorValue;
     738             :   typedef DofValueType ValuePushType;
     739             : 
     740       46559 :   OldSolutionValue(const libMesh::System & sys_in,
     741             :                    const NumericVector<Number> & old_sol,
     742             :                    const std::vector<unsigned int> * vars) :
     743             :       OldSolutionBase<Output, point_output>(sys_in, vars),
     744       44971 :       old_solution(old_sol)
     745             :   {
     746        3176 :     this->old_context.set_algebraic_type(FEMContext::OLD);
     747        3176 :     this->old_context.set_custom_solution(&old_solution);
     748        3176 :   }
     749             : 
     750      246719 :   OldSolutionValue(const OldSolutionValue & in) :
     751             :       OldSolutionBase<Output, point_output>(in.sys, in.old_context.active_vars()),
     752      247355 :       old_solution(in.old_solution)
     753             :   {
     754       12281 :     this->old_context.set_algebraic_type(FEMContext::OLD);
     755       12281 :     this->old_context.set_custom_solution(&old_solution);
     756      234438 :   }
     757             : 
     758             : 
     759             :   Output eval_at_node (const FEMContext & c,
     760             :                        unsigned int i,
     761             :                        unsigned int elem_dim,
     762             :                        const Node & n,
     763             :                        bool /* extra_hanging_dofs */,
     764             :                        Real /* time */ =0.);
     765             : 
     766     4309877 :   Output eval_at_point(const FEMContext & c,
     767             :                        unsigned int i,
     768             :                        const Point & p,
     769             :                        Real /* time */,
     770             :                        bool skip_context_check)
     771             :   {
     772      719610 :     LOG_SCOPE ("eval_at_point()", "OldSolutionValue");
     773             : 
     774     4309877 :     if (!skip_context_check)
     775     4290497 :       if (!this->check_old_context(c, p))
     776           0 :         return Output(0);
     777             : 
     778             :     // Handle offset from non-scalar components in previous variables
     779      359805 :     libmesh_assert_less(i, this->component_to_var.size());
     780     4329881 :     unsigned int var = this->component_to_var[i];
     781             : 
     782      314760 :     Output n;
     783     4309877 :     (this->old_context.*point_output)(var, p, n, this->out_of_elem_tol);
     784     4309877 :     return n;
     785             :   }
     786             : 
     787             :   template <typename T = Output,
     788             :             typename std::enable_if<std::is_same<T, Number>::value, int>::type = 0>
     789        2816 :   void eval_mixed_derivatives(const FEMContext & libmesh_dbg_var(c),
     790             :                               unsigned int i,
     791             :                               unsigned int dim,
     792             :                               const Node & n,
     793             :                               std::vector<Output> & derivs)
     794             :   {
     795         716 :     LOG_SCOPE ("eval_mixed_derivatives", "OldSolutionValue");
     796             : 
     797             :     // This should only be called on vertices
     798         358 :     libmesh_assert_less(c.get_elem().get_node_index(&n),
     799             :                         c.get_elem().n_vertices());
     800             : 
     801             :     // Handle offset from non-scalar components in previous variables
     802         358 :     libmesh_assert_less(i, this->component_to_var.size());
     803        2816 :     unsigned int var = this->component_to_var[i];
     804             : 
     805             :     // We have 1 mixed derivative in 2D, 4 in 3D
     806        2816 :     const unsigned int n_mixed = (dim-1) * (dim-1);
     807        2816 :     derivs.resize(n_mixed);
     808             : 
     809             :     // Be sure to handle cases where the variable wasn't defined on
     810             :     // this node (e.g. due to changing subdomain support)
     811         358 :     const DofObject * old_dof_object = n.get_old_dof_object();
     812        2816 :     if (old_dof_object &&
     813        5274 :         old_dof_object->n_vars(this->sys.number()) &&
     814        2458 :         old_dof_object->n_comp(this->sys.number(), var))
     815             :       {
     816         716 :         const dof_id_type first_old_id =
     817        2816 :           old_dof_object->dof_number(this->sys.number(), var, dim);
     818        3174 :         std::vector<dof_id_type> old_ids(n_mixed);
     819         358 :         std::iota(old_ids.begin(), old_ids.end(), first_old_id);
     820        2816 :         old_solution.get(old_ids, derivs);
     821             :       }
     822             :     else
     823             :       {
     824           0 :         std::fill(derivs.begin(), derivs.end(), 0);
     825             :       }
     826        2816 :   }
     827             : 
     828             :   template <typename T = Output,
     829             :             typename std::enable_if<!std::is_same<T, Number>::value, int>::type = 0>
     830           0 :   void eval_mixed_derivatives(
     831             :       const FEMContext &, unsigned int, unsigned int, const Node &, std::vector<Output> &)
     832             :   {
     833           0 :     libmesh_error_msg("eval_mixed_derivatives should only be applicable for Hermite finite element "
     834             :                       "types. I don't know how you got here");
     835             :   }
     836             : 
     837     2853864 :   void eval_old_dofs (const Elem & elem,
     838             :                       unsigned int node_num,
     839             :                       unsigned int var_num,
     840             :                       std::vector<dof_id_type> & indices,
     841             :                       std::vector<DofValueType> & values)
     842             :   {
     843      692394 :     LOG_SCOPE ("eval_old_dofs(node)", "OldSolutionValue");
     844             : 
     845             :     // We may be reusing a std::vector here, but the following
     846             :     // dof_indices call appends without first clearing.
     847      346197 :     indices.clear();
     848             : 
     849     3200061 :     this->sys.get_dof_map().dof_indices(elem, node_num, indices, var_num);
     850             : 
     851      692394 :     std::vector<dof_id_type> old_indices;
     852             : 
     853     3200061 :     this->sys.get_dof_map().old_dof_indices(elem, node_num, old_indices, var_num);
     854             : 
     855      346197 :     libmesh_assert_equal_to (old_indices.size(), indices.size());
     856             : 
     857             :     // We may have invalid_id in cases where no old DoF existed, e.g.
     858             :     // due to expansion of a subdomain-restricted variable's subdomain
     859      346197 :     bool invalid_old_index = false;
     860     5802109 :     for (const auto & di : old_indices)
     861     2948245 :       if (di == DofObject::invalid_id)
     862           0 :         invalid_old_index = true;
     863             : 
     864     2853864 :     values.resize(old_indices.size());
     865     2853864 :     if (invalid_old_index)
     866             :       {
     867           0 :         for (auto i : index_range(old_indices))
     868             :           {
     869           0 :             const dof_id_type di = old_indices[i];
     870           0 :             if (di == DofObject::invalid_id)
     871           0 :               values[i] = 0;
     872             :             else
     873           0 :               values[i] = old_solution(di);
     874             :           }
     875             :       }
     876             :     else
     877     2853864 :       old_solution.get(old_indices, values);
     878     2853864 :   }
     879             : 
     880             : 
     881     2287366 :   void eval_old_dofs (const Elem & elem,
     882             :                       const FEType & fe_type,
     883             :                       unsigned int sys_num,
     884             :                       unsigned int var_num,
     885             :                       std::vector<dof_id_type> & indices,
     886             :                       std::vector<DofValueType> & values)
     887             :   {
     888      524980 :     LOG_SCOPE ("eval_old_dofs(elem)", "OldSolutionValue");
     889             : 
     890             :     // We're only to be asked for old dofs on elements that can copy
     891             :     // them through DO_NOTHING or through refinement.
     892      524512 :     const Elem & old_elem =
     893     2287366 :       (elem.refinement_flag() == Elem::JUST_REFINED) ?
     894         936 :       *elem.parent() : elem;
     895             : 
     896             :     // If there are any element-based DOF numbers, get them
     897     2287366 :     const unsigned int nc = FEInterface::n_dofs_per_elem(fe_type, &elem);
     898             : 
     899     2287366 :     indices.resize(nc);
     900             : 
     901             :     // We should never have fewer dofs than necessary on an
     902             :     // element unless we're getting indices on a parent element,
     903             :     // which we should never need, or getting indices on a newly
     904             :     // expanded subdomain, in which case we initialize new values to
     905             :     // zero.
     906     2287366 :     if (nc != 0)
     907             :       {
     908        1811 :         const DofObject & old_dof_object = old_elem.get_old_dof_object_ref();
     909        1811 :         libmesh_assert_greater(elem.n_systems(), sys_num);
     910             : 
     911             :         const std::pair<unsigned int, unsigned int>
     912       19965 :           vg_and_offset = elem.var_to_vg_and_offset(sys_num,var_num);
     913       19965 :         const unsigned int vg = vg_and_offset.first;
     914       19965 :         const unsigned int vig = vg_and_offset.second;
     915             : 
     916       21776 :         unsigned int n_comp = old_dof_object.n_comp_group(sys_num,vg);
     917       21776 :         n_comp = std::min(n_comp, nc);
     918             : 
     919       21776 :         std::vector<dof_id_type> old_dof_indices(n_comp);
     920             : 
     921       64751 :         for (unsigned int i=0; i != n_comp; ++i)
     922             :           {
     923        3554 :             const dof_id_type d_old =
     924       39421 :               old_dof_object.dof_number(sys_num, vg, vig, i, n_comp);
     925        3554 :             const dof_id_type d_new =
     926       39421 :               elem.dof_number(sys_num, vg, vig, i, n_comp);
     927        3554 :             libmesh_assert_not_equal_to (d_old, DofObject::invalid_id);
     928        3554 :             libmesh_assert_not_equal_to (d_new, DofObject::invalid_id);
     929             : 
     930       42975 :             old_dof_indices[i] = d_old;
     931       46529 :             indices[i] = d_new;
     932             :           }
     933             : 
     934       21776 :         values.resize(n_comp);
     935       21776 :         old_solution.get(old_dof_indices, values);
     936             : 
     937       21776 :         for (unsigned int i=n_comp; i != nc; ++i)
     938             :           {
     939           0 :             const dof_id_type d_new =
     940           0 :               elem.dof_number(sys_num, vg, vig, i, n_comp);
     941           0 :             libmesh_assert_not_equal_to (d_new, DofObject::invalid_id);
     942           0 :             indices[i] = d_new;
     943             :           }
     944             : 
     945       21776 :         values.resize(nc, 0);
     946             :       }
     947             :     else
     948      260679 :       values.clear();
     949     2287366 :   }
     950             : 
     951             : private:
     952             :   const NumericVector<Number> & old_solution;
     953             : };
     954             : 
     955             : template<>
     956             : inline void
     957       12634 : OldSolutionBase<Number, &FEMContext::point_value>::get_shape_outputs(FEAbstract & fe)
     958             : {
     959      260738 :   fe.request_phi();
     960       12634 : }
     961             : 
     962             : 
     963             : template<>
     964             : inline void
     965         636 : OldSolutionBase<Gradient, &FEMContext::point_gradient>::get_shape_outputs(FEAbstract & fe)
     966             : {
     967       19588 :   fe.request_dphi();
     968         636 : }
     969             : 
     970             : template<>
     971             : inline void
     972         970 : OldSolutionBase<Gradient, &FEMContext::point_value>::get_shape_outputs(FEAbstract & fe)
     973             : {
     974       26635 :   fe.request_phi();
     975         970 : }
     976             : 
     977             : 
     978             : template<>
     979             : inline void
     980           0 : OldSolutionBase<Tensor, &FEMContext::point_gradient>::get_shape_outputs(FEAbstract & fe)
     981             : {
     982           0 :   fe.request_dphi();
     983           0 : }
     984             : 
     985             : 
     986             : #ifdef LIBMESH_USE_COMPLEX_NUMBERS
     987             : template<>
     988             : inline void
     989             : OldSolutionBase<Real, &FEMContext::point_value>::get_shape_outputs(FEAbstract & fe)
     990             : {
     991           0 :   fe.request_phi();
     992             : }
     993             : 
     994             : 
     995             : template<>
     996             : inline void
     997             : OldSolutionBase<RealGradient, &FEMContext::point_gradient>::get_shape_outputs(FEAbstract & fe)
     998             : {
     999           0 :   fe.request_dphi();
    1000             : }
    1001             : 
    1002             : template<>
    1003             : inline void
    1004             : OldSolutionBase<RealGradient, &FEMContext::point_value>::get_shape_outputs(FEAbstract & fe)
    1005             : {
    1006             :   fe.request_phi();
    1007             : }
    1008             : 
    1009             : 
    1010             : template<>
    1011             : inline void
    1012             : OldSolutionBase<RealTensor, &FEMContext::point_gradient>::get_shape_outputs(FEAbstract & fe)
    1013             : {
    1014             :   fe.request_dphi();
    1015             : }
    1016             : #endif // LIBMESH_USE_COMPLEX_NUMBERS
    1017             : 
    1018             : 
    1019             : template<>
    1020             : inline
    1021             : Number
    1022     1779507 : OldSolutionValue<Number, &FEMContext::point_value>::
    1023             : eval_at_node(const FEMContext & c,
    1024             :              unsigned int i,
    1025             :              unsigned int /* elem_dim */,
    1026             :              const Node & n,
    1027             :              bool extra_hanging_dofs,
    1028             :              Real /* time */)
    1029             : {
    1030      312074 :   LOG_SCOPE ("Number eval_at_node()", "OldSolutionValue");
    1031             : 
    1032             :   // This should only be called on vertices
    1033      156037 :   libmesh_assert_less(c.get_elem().get_node_index(&n),
    1034             :                       c.get_elem().n_vertices());
    1035             : 
    1036             :   // Handle offset from non-scalar components in previous variables
    1037      156037 :   libmesh_assert_less(i, this->component_to_var.size());
    1038     1779507 :   unsigned int var = this->component_to_var[i];
    1039             : 
    1040             :   // Optimize for the common case, where this node was part of the
    1041             :   // old solution.
    1042             :   //
    1043             :   // Be sure to handle cases where the variable wasn't defined on
    1044             :   // this node (due to changing subdomain support) or where the
    1045             :   // variable has no components on this node (due to Elem order
    1046             :   // exceeding FE order) or where the old_dof_object dofs might
    1047             :   // correspond to non-vertex dofs (due to extra_hanging_dofs and
    1048             :   // refinement)
    1049             : 
    1050      312075 :   const Elem::RefinementState flag = c.get_elem().refinement_flag();
    1051             : 
    1052      156037 :   const DofObject * old_dof_object = n.get_old_dof_object();
    1053     1295022 :   if (old_dof_object &&
    1054     1236513 :       (!extra_hanging_dofs ||
    1055     1045776 :        flag == Elem::JUST_COARSENED ||
    1056     1083563 :        flag == Elem::DO_NOTHING) &&
    1057     3950848 :       old_dof_object->n_vars(sys.number()) &&
    1058     1083563 :       old_dof_object->n_comp(sys.number(), var))
    1059             :     {
    1060             :       const dof_id_type old_id =
    1061     1060420 :         old_dof_object->dof_number(sys.number(), var, 0);
    1062     1060420 :       return old_solution(old_id);
    1063             :     }
    1064             : 
    1065      719087 :   return this->eval_at_point(c, i, n, 0, false);
    1066             : }
    1067             : 
    1068             : template <>
    1069             : inline
    1070             : Gradient
    1071       21744 : OldSolutionValue<Gradient, &FEMContext::point_value>::
    1072             : eval_at_node(const FEMContext & c,
    1073             :              unsigned int i,
    1074             :              unsigned int /* elem_dim */,
    1075             :              const Node & n,
    1076             :              bool extra_hanging_dofs,
    1077             :              Real /* time */)
    1078             : {
    1079        2604 :   LOG_SCOPE ("Number eval_at_node()", "OldSolutionValue");
    1080             : 
    1081             :   // This should only be called on vertices
    1082        1302 :   libmesh_assert_less(c.get_elem().get_node_index(&n),
    1083             :                       c.get_elem().n_vertices());
    1084             : 
    1085             :   // Handle offset from non-scalar components in previous variables
    1086        1302 :   libmesh_assert_less(i, this->component_to_var.size());
    1087       21744 :   unsigned int var = this->component_to_var[i];
    1088             : 
    1089             :   // Optimize for the common case, where this node was part of the
    1090             :   // old solution.
    1091             :   //
    1092             :   // Be sure to handle cases where the variable wasn't defined on
    1093             :   // this node (due to changing subdomain support) or where the
    1094             :   // variable has no components on this node (due to Elem order
    1095             :   // exceeding FE order) or where the old_dof_object dofs might
    1096             :   // correspond to non-vertex dofs (due to extra_hanging_dofs and
    1097             :   // refinement)
    1098             : 
    1099        2604 :   const auto & elem = c.get_elem();
    1100             : 
    1101        2604 :   const Elem::RefinementState flag = elem.refinement_flag();
    1102             : 
    1103        1302 :   const DofObject * old_dof_object = n.get_old_dof_object();
    1104       16518 :   if (old_dof_object &&
    1105       16152 :       (!extra_hanging_dofs ||
    1106       14280 :        flag == Elem::JUST_COARSENED ||
    1107       15216 :        flag == Elem::DO_NOTHING) &&
    1108       52176 :       old_dof_object->n_vars(sys.number()) &&
    1109       15216 :       old_dof_object->n_comp(sys.number(), var))
    1110             :     {
    1111         936 :       Gradient return_val;
    1112             : 
    1113       58437 :       for (auto dim : make_range(elem.dim()))
    1114             :       {
    1115             :         const dof_id_type old_id =
    1116       43221 :           old_dof_object->dof_number(sys.number(), var, dim);
    1117       43221 :         return_val(dim) = old_solution(old_id);
    1118             :       }
    1119             : 
    1120       15216 :       return return_val;
    1121             :     }
    1122             : 
    1123        6528 :   return this->eval_at_point(c, i, n, 0, false);
    1124             : }
    1125             : 
    1126             : 
    1127             : 
    1128             : template<>
    1129             : inline
    1130             : Gradient
    1131       12586 : OldSolutionValue<Gradient, &FEMContext::point_gradient>::
    1132             : eval_at_node(const FEMContext & c,
    1133             :              unsigned int i,
    1134             :              unsigned int elem_dim,
    1135             :              const Node & n,
    1136             :              bool extra_hanging_dofs,
    1137             :              Real /* time */)
    1138             : {
    1139        2116 :   LOG_SCOPE ("Gradient eval_at_node()", "OldSolutionValue");
    1140             : 
    1141             :   // This should only be called on vertices
    1142        1058 :   libmesh_assert_less(c.get_elem().get_node_index(&n),
    1143             :                       c.get_elem().n_vertices());
    1144             : 
    1145             :   // Handle offset from non-scalar components in previous variables
    1146        1058 :   libmesh_assert_less(i, this->component_to_var.size());
    1147       12586 :   unsigned int var = this->component_to_var[i];
    1148             : 
    1149             :   // Optimize for the common case, where this node was part of the
    1150             :   // old solution.
    1151             :   //
    1152             :   // Be sure to handle cases where the variable wasn't defined on
    1153             :   // this node (due to changing subdomain support) or where the
    1154             :   // variable has no components on this node (due to Elem order
    1155             :   // exceeding FE order) or where the old_dof_object dofs might
    1156             :   // correspond to non-vertex dofs (due to extra_hanging_dofs and
    1157             :   // refinement)
    1158             : 
    1159        2115 :   const Elem::RefinementState flag = c.get_elem().refinement_flag();
    1160             : 
    1161        1058 :   const DofObject * old_dof_object = n.get_old_dof_object();
    1162       13644 :   if (old_dof_object &&
    1163       13643 :       (!extra_hanging_dofs ||
    1164       12586 :        flag == Elem::JUST_COARSENED ||
    1165         451 :        flag == Elem::DO_NOTHING) &&
    1166       14511 :       old_dof_object->n_vars(sys.number()) &&
    1167         451 :       old_dof_object->n_comp(sys.number(), var))
    1168             :     {
    1169          35 :       Gradient g;
    1170         902 :       for (unsigned int d = 0; d != elem_dim; ++d)
    1171             :         {
    1172             :           const dof_id_type old_id =
    1173         451 :             old_dof_object->dof_number(sys.number(), var, d+1);
    1174         451 :           g(d) = old_solution(old_id);
    1175             :         }
    1176         451 :       return g;
    1177             :     }
    1178             : 
    1179       12135 :   return this->eval_at_point(c, i, n, 0, false);
    1180             : }
    1181             : 
    1182             : template<>
    1183             : inline
    1184             : Tensor
    1185           0 : OldSolutionValue<Tensor, &FEMContext::point_gradient>::
    1186             : eval_at_node(const FEMContext &,
    1187             :              unsigned int,
    1188             :              unsigned int,
    1189             :              const Node &,
    1190             :              bool,
    1191             :              Real)
    1192             : {
    1193           0 :   libmesh_error_msg("You shouldn't need to call eval_at_node for the gradient "
    1194             :                     "functor for a vector-valued finite element type");
    1195             :   return Tensor();
    1196             : }
    1197             : 
    1198             : template <typename Output,
    1199             :           void (FEMContext::*point_output) (unsigned int,
    1200             :                                             const Point &,
    1201             :                                             Output &,
    1202             :                                             const Real) const>
    1203             : const Real OldSolutionBase<Output, point_output>::out_of_elem_tol = 10 * TOLERANCE;
    1204             : 
    1205             : #endif // LIBMESH_ENABLE_AMR
    1206             : 
    1207             : /**
    1208             :  * Function definitions
    1209             :  */
    1210             : 
    1211             : template <typename FFunctor, typename GFunctor,
    1212             :           typename FValue, typename ProjectionAction>
    1213      243232 : void GenericProjector<FFunctor, GFunctor, FValue, ProjectionAction>::project
    1214             :   (const ConstElemRange & range)
    1215             : {
    1216       14440 :   LOG_SCOPE ("project", "GenericProjector");
    1217             : 
    1218             :   // Unless we split sort and copy into two passes we can't know for
    1219             :   // sure ahead of time whether we need to save the copied ids
    1220      243232 :   done_saving_ids = false;
    1221             : 
    1222      257672 :   SortAndCopy sort_work(*this);
    1223      243232 :   Threads::parallel_reduce (range, sort_work);
    1224      243232 :   ProjectionAction action(master_action);
    1225             : 
    1226             :   // Keep track of dof ids and values to send to other ranks
    1227             :   std::unordered_map<dof_id_type, std::pair<typename FFunctor::ValuePushType, processor_id_type>>
    1228       14440 :       ids_to_push;
    1229             : 
    1230      236012 :   ids_to_push.insert(sort_work.new_ids_to_push.begin(),
    1231             :                      sort_work.new_ids_to_push.end());
    1232      236012 :   ids_to_save.insert(sort_work.new_ids_to_save.begin(),
    1233             :                      sort_work.new_ids_to_save.end());
    1234             : 
    1235      257672 :   std::vector<node_projection> vertices(sort_work.vertices.begin(),
    1236             :                                         sort_work.vertices.end());
    1237             : 
    1238      473314 :   done_saving_ids = sort_work.edges.empty() &&
    1239      446004 :     sort_work.sides.empty() && sort_work.interiors.empty();
    1240             : 
    1241             :   {
    1242       14440 :     ProjectVertices project_vertices(*this);
    1243      479244 :     Threads::parallel_reduce (node_range(&vertices), project_vertices);
    1244        7220 :     libmesh_merge_move(ids_to_push, project_vertices.new_ids_to_push);
    1245        7220 :     libmesh_merge_move(ids_to_save, project_vertices.new_ids_to_save);
    1246             :   }
    1247             : 
    1248      243232 :   done_saving_ids = sort_work.sides.empty() && sort_work.interiors.empty();
    1249             : 
    1250      243232 :   this->send_and_insert_dof_values(ids_to_push, action);
    1251             : 
    1252             :   {
    1253      257672 :     std::vector<node_projection> edges(sort_work.edges.begin(), sort_work.edges.end());
    1254       14440 :     ProjectEdges project_edges(*this);
    1255      479244 :     Threads::parallel_reduce (node_range(&edges), project_edges);
    1256        7220 :     libmesh_merge_move(ids_to_push, project_edges.new_ids_to_push);
    1257        7220 :     libmesh_merge_move(ids_to_save, project_edges.new_ids_to_save);
    1258      228792 :   }
    1259             : 
    1260      243232 :   done_saving_ids = sort_work.interiors.empty();
    1261             : 
    1262      243232 :   this->send_and_insert_dof_values(ids_to_push, action);
    1263             : 
    1264             :   {
    1265      257672 :     std::vector<node_projection> sides(sort_work.sides.begin(), sort_work.sides.end());
    1266       14440 :     ProjectSides project_sides(*this);
    1267      479244 :     Threads::parallel_reduce (node_range(&sides), project_sides);
    1268        7220 :     libmesh_merge_move(ids_to_push, project_sides.new_ids_to_push);
    1269        7220 :     libmesh_merge_move(ids_to_save, project_sides.new_ids_to_save);
    1270      228792 :   }
    1271             : 
    1272      243232 :   done_saving_ids = true;
    1273      243232 :   this->send_and_insert_dof_values(ids_to_push, action);
    1274             : 
    1275             :   // No ids to save or push this time, but we still use a reduce since
    1276             :   // nominally ProjectInteriors still has non-const operator()
    1277        7220 :   ProjectInteriors project_interiors(*this);
    1278      479244 :   Threads::parallel_reduce (interior_range(&sort_work.interiors),
    1279             :                             project_interiors);
    1280      472024 : }
    1281             : 
    1282             : 
    1283             : template <typename FFunctor, typename GFunctor,
    1284             :           typename FValue, typename ProjectionAction>
    1285     1242981 : GenericProjector<FFunctor, GFunctor, FValue, ProjectionAction>::SubFunctor::SubFunctor
    1286             :   (GenericProjector & p) :
    1287     1152333 :   projector(p),
    1288     1242981 :   action(p.master_action),
    1289     1242981 :   f(p.master_f),
    1290     1242981 :   context(p.system, &p.variables, /* allocate_local_matrices= */ false),
    1291     1242981 :   conts(p.system.n_vars()),
    1292     1333629 :   field_types(p.system.n_vars()), system(p.system)
    1293             : {
    1294             :   // Loop over all the variables we've been requested to project, to
    1295             :   // pre-request
    1296     2559129 :   for (const auto & var : this->projector.variables)
    1297             :     {
    1298             :       // FIXME: Need to generalize this to vector-valued elements. [PB]
    1299       47893 :       FEAbstract * fe = nullptr;
    1300       47893 :       FEAbstract * side_fe = nullptr;
    1301       47893 :       FEAbstract * edge_fe = nullptr;
    1302             : 
    1303             :       const std::set<unsigned char> & elem_dims =
    1304       47893 :         context.elem_dimensions();
    1305             : 
    1306     2635009 :       for (const auto & dim : elem_dims)
    1307             :         {
    1308     1318861 :           context.get_element_fe( var, fe, dim );
    1309     1318861 :           if (fe->get_fe_type().family == SCALAR)
    1310           0 :             continue;
    1311     1318861 :           if (dim > 1)
    1312       42746 :             context.get_side_fe( var, side_fe, dim );
    1313     1318861 :           if (dim > 2)
    1314       17472 :             context.get_edge_fe( var, edge_fe );
    1315             : 
    1316      114395 :           fe->get_JxW();
    1317      114395 :           fe->get_xyz();
    1318      114395 :           fe->get_JxW();
    1319             : 
    1320     1318861 :           fe->request_phi();
    1321     1318861 :           if (dim > 1)
    1322             :             {
    1323      101602 :               side_fe->get_JxW();
    1324      101602 :               side_fe->get_xyz();
    1325     1152207 :               side_fe->request_phi();
    1326             :             }
    1327     1318861 :           if (dim > 2)
    1328             :             {
    1329       42164 :               edge_fe->get_JxW();
    1330       42164 :               edge_fe->get_xyz();
    1331      521062 :               edge_fe->request_phi();
    1332             :             }
    1333             : 
    1334     1318861 :           const FEContinuity cont = fe->get_continuity();
    1335     1318861 :           this->conts[var] = cont;
    1336     1318861 :           if (cont == C_ONE)
    1337             :             {
    1338       48796 :               fe->request_dphi();
    1339       48796 :               if (dim > 1)
    1340       27153 :                 side_fe->request_dphi();
    1341       48796 :               if (dim > 2)
    1342        3222 :                 edge_fe->request_dphi();
    1343             :             }
    1344             : 
    1345     1318861 :           this->field_types[var] = FEInterface::field_type(fe->get_fe_type());
    1346             :         }
    1347             :     }
    1348             : 
    1349             :   // Now initialize any user requested shape functions, xyz vals, etc
    1350      263974 :   f.init_context(context);
    1351     1242981 : }
    1352             : 
    1353             : 
    1354             : template <typename FFunctor, typename GFunctor,
    1355             :           typename FValue, typename ProjectionAction>
    1356      992031 : GenericProjector<FFunctor, GFunctor, FValue, ProjectionAction>::SubProjector::SubProjector
    1357             :   (GenericProjector & p) :
    1358      992031 :   SubFunctor(p)
    1359             : {
    1360             : 
    1361             :   // Our C1 elements need gradient information
    1362     2003233 :   for (const auto & var : this->projector.variables)
    1363     1087547 :     if (this->conts[var] == C_ONE)
    1364             :       {
    1365        1306 :         libmesh_assert(p.master_g);
    1366       76698 :         g = std::make_unique<GFunctor>(*p.master_g);
    1367       39002 :         g->init_context(context);
    1368        1306 :         return;
    1369             :       }
    1370           0 : }
    1371             : 
    1372             : template <typename FFunctor, typename GFunctor, typename FValue, typename ProjectionAction>
    1373             : template <typename InsertInput,
    1374             :           typename std::enable_if<
    1375             :               !std::is_same<typename ProjectionAction::InsertInput, InsertInput>::value,
    1376             :               int>::type>
    1377             : void
    1378           0 : GenericProjector<FFunctor, GFunctor, FValue, ProjectionAction>::SubFunctor::insert_id(
    1379             :     dof_id_type, const InsertInput & , processor_id_type)
    1380             : {
    1381           0 :   libmesh_error_msg("ID insertion should only occur when the provided input matches that "
    1382             :                     "expected by the ProjectionAction");
    1383             : }
    1384             : 
    1385             : template <typename FFunctor, typename GFunctor, typename FValue, typename ProjectionAction>
    1386             : template <typename InsertInput,
    1387             :           typename std::enable_if<
    1388             :               std::is_same<typename ProjectionAction::InsertInput, InsertInput>::value,
    1389             :               int>::type>
    1390             : void
    1391     5315035 : GenericProjector<FFunctor, GFunctor, FValue, ProjectionAction>::SubFunctor::insert_id(
    1392             :     dof_id_type id, const InsertInput & val, processor_id_type pid)
    1393             : {
    1394             :   // We may see invalid ids when expanding a subdomain with a
    1395             :   // restricted variable
    1396     5315035 :   if (id == DofObject::invalid_id)
    1397           0 :     return;
    1398             : 
    1399      448973 :   auto iter = new_ids_to_push.find(id);
    1400     5315035 :   if (iter == new_ids_to_push.end())
    1401     5309994 :     action.insert(id, val);
    1402             :   else
    1403             :     {
    1404         170 :       libmesh_assert(pid != DofObject::invalid_processor_id);
    1405         900 :       iter->second = std::make_pair(val, pid);
    1406             :     }
    1407     5315035 :   if (!this->projector.done_saving_ids)
    1408             :     {
    1409      208911 :       libmesh_assert(!new_ids_to_save.count(id));
    1410     2477907 :       new_ids_to_save[id] = val;
    1411             :     }
    1412             : }
    1413             : 
    1414             : template <typename FFunctor, typename GFunctor, typename FValue, typename ProjectionAction>
    1415             : template <typename InsertInput,
    1416             :           typename std::enable_if<
    1417             :               !std::is_same<typename ProjectionAction::InsertInput, InsertInput>::value,
    1418             :               int>::type>
    1419             : void
    1420             : GenericProjector<FFunctor, GFunctor, FValue, ProjectionAction>::SubFunctor::insert_ids(
    1421             :     const std::vector<dof_id_type> &,
    1422             :     const std::vector<InsertInput> &,
    1423             :     processor_id_type)
    1424             : {
    1425             :   libmesh_error_msg("ID insertion should only occur when the provided input matches that "
    1426             :                     "expected by the ProjectionAction");
    1427             : }
    1428             : 
    1429             : template <typename FFunctor, typename GFunctor, typename FValue, typename ProjectionAction>
    1430             : template <typename InsertInput,
    1431             :           typename std::enable_if<
    1432             :               std::is_same<typename ProjectionAction::InsertInput, InsertInput>::value,
    1433             :               int>::type>
    1434             : void
    1435     3366765 : GenericProjector<FFunctor, GFunctor, FValue, ProjectionAction>::SubFunctor::insert_ids(
    1436             :     const std::vector<dof_id_type> & ids,
    1437             :     const std::vector<InsertInput> & vals,
    1438             :     processor_id_type pid)
    1439             : {
    1440      382722 :   libmesh_assert_equal_to(ids.size(), vals.size());
    1441    10206930 :   for (auto i : index_range(ids))
    1442             :     {
    1443     6840165 :       const dof_id_type id = ids[i];
    1444             : 
    1445             :       // We may see invalid ids when expanding a subdomain with a
    1446             :       // restricted variable
    1447     6840165 :       if (id == DofObject::invalid_id)
    1448           0 :         continue;
    1449             : 
    1450     1325909 :       const InsertInput & val = vals[i];
    1451             : 
    1452      662955 :       auto iter = new_ids_to_push.find(id);
    1453     6840165 :       if (iter == new_ids_to_push.end())
    1454     6837132 :         action.insert(id, val);
    1455             :       else
    1456             :         {
    1457         390 :           libmesh_assert(pid != DofObject::invalid_processor_id);
    1458         780 :           iter->second = std::make_pair(val, pid);
    1459             :         }
    1460     6840165 :       if (!this->projector.done_saving_ids)
    1461             :         {
    1462      378456 :           libmesh_assert(!new_ids_to_save.count(id));
    1463     3291284 :           new_ids_to_save[id] = val;
    1464             :         }
    1465             :     }
    1466     3366765 : }
    1467             : 
    1468             : template <typename FFunctor, typename GFunctor,
    1469             :           typename FValue, typename ProjectionAction>
    1470       26821 : void GenericProjector<FFunctor, GFunctor, FValue, ProjectionAction>::SubFunctor::join
    1471             :   (const GenericProjector<FFunctor, GFunctor, FValue, ProjectionAction>::SubFunctor & other)
    1472             : {
    1473       17597 :   new_ids_to_push.insert(other.new_ids_to_push.begin(), other.new_ids_to_push.end());
    1474       17597 :   new_ids_to_save.insert(other.new_ids_to_save.begin(), other.new_ids_to_save.end());
    1475       26821 : }
    1476             : 
    1477             : 
    1478             : template <typename FFunctor, typename GFunctor,
    1479             :           typename FValue, typename ProjectionAction>
    1480      250950 : void GenericProjector<FFunctor, GFunctor, FValue, ProjectionAction>::SortAndCopy::operator()
    1481             :   (const ConstElemRange & range)
    1482             : {
    1483       19832 :   LOG_SCOPE ("SortAndCopy::operator()","GenericProjector");
    1484             : 
    1485             :   // Look at all the elements in the range.  Directly copy values from
    1486             :   // unchanged elements.  For other elements, determine sets of
    1487             :   // vertices, edge nodes, and side nodes to project.
    1488             :   //
    1489             :   // As per our other weird nomenclature, "sides" means faces in 3D
    1490             :   // and edges in 2D, and "edges" gets skipped in 2D
    1491             :   //
    1492             :   // This gets tricky in the case of subdomain-restricted
    1493             :   // variables, for which we might need to do the same projection
    1494             :   // from different elements when evaluating different variables.
    1495             :   // We'll keep track of which variables can be projected from which
    1496             :   // elements.
    1497             : 
    1498             :   // If we copy DoFs on a node, keep track of it so we don't bother
    1499             :   // with any redundant interpolations or projections later.
    1500             :   //
    1501             :   // We still need to keep track of *which* variables get copied,
    1502             :   // since we may be able to copy elements which lack some
    1503             :   // subdomain-restricted variables.
    1504             :   //
    1505             :   // For extra_hanging_dofs FE types, we'll keep track of which
    1506             :   // variables were copied from vertices, and which from edges/sides,
    1507             :   // because those types need copies made from *both* on hanging
    1508             :   // nodes.
    1509       19832 :   std::unordered_map<const Node *, std::pair<var_set, var_set>> copied_nodes;
    1510             : 
    1511       54819 :   const unsigned int sys_num = system.number();
    1512             : 
    1513             :   // At hanging nodes for variables with extra hanging dofs we'll need
    1514             :   // to do projections *separately* from vertex elements and side/edge
    1515             :   // elements.
    1516       19832 :   std::vector<unsigned short> extra_hanging_dofs;
    1517        9916 :   bool all_extra_hanging_dofs = true;
    1518      516894 :   for (auto v_num : this->projector.variables)
    1519             :     {
    1520      276494 :       if (extra_hanging_dofs.size() <= v_num)
    1521      265944 :         extra_hanging_dofs.resize(v_num+1, false);
    1522      287044 :       extra_hanging_dofs[v_num] =
    1523      276494 :         FEInterface::extra_hanging_dofs(system.variable(v_num).type());
    1524             : 
    1525      265944 :       if (!extra_hanging_dofs[v_num])
    1526        8263 :         all_extra_hanging_dofs = false;
    1527             :     }
    1528             : 
    1529     5257258 :   for (const auto & elem : range)
    1530             :     {
    1531             :       // If we're doing AMR, we might be able to copy more DoFs than
    1532             :       // we interpolate or project.
    1533      510742 :       bool copy_this_elem = false;
    1534             : 
    1535             : #ifdef LIBMESH_ENABLE_AMR
    1536             :       // If we're projecting from an old grid
    1537      510742 :       if (f.is_grid_projection())
    1538             :         {
    1539             :           // If this element doesn't have an old_dof_object, but it
    1540             :           // wasn't just refined or just coarsened into activity, then
    1541             :           // it must be newly added, so the user is responsible for
    1542             :           // setting the new dofs on it during a grid projection.
    1543     3932594 :           const DofObject * old_dof_object = elem->get_old_dof_object();
    1544      830279 :           const Elem::RefinementState h_flag = elem->refinement_flag();
    1545      830279 :           const Elem::RefinementState p_flag = elem->p_refinement_flag();
    1546     4347734 :           if (!old_dof_object &&
    1547     3517468 :               h_flag != Elem::JUST_REFINED &&
    1548             :               h_flag != Elem::JUST_COARSENED)
    1549         156 :             continue;
    1550             : 
    1551             :           // If this is an unchanged element, just copy everything
    1552     3932438 :           if (h_flag != Elem::JUST_REFINED &&
    1553      261419 :               h_flag != Elem::JUST_COARSENED &&
    1554     2251497 :               p_flag != Elem::JUST_REFINED &&
    1555             :               p_flag != Elem::JUST_COARSENED)
    1556      260796 :             copy_this_elem = true;
    1557             :           else
    1558             :             {
    1559      154330 :               bool reinitted = false;
    1560             : 
    1561      308661 :               const unsigned int p_level = elem->p_level();
    1562             : 
    1563             :               // If this element has a low order monomial which has
    1564             :               // merely been h refined, copy it.
    1565      154330 :               const bool copy_possible =
    1566     1841383 :                 p_level == 0 &&
    1567     1841382 :                 h_flag != Elem::JUST_COARSENED &&
    1568             :                 p_flag != Elem::JUST_COARSENED;
    1569             : 
    1570     1843604 :               std::vector<typename FFunctor::ValuePushType> Ue(1);
    1571     1842116 :               std::vector<dof_id_type> elem_dof_ids(1);
    1572             : 
    1573     3428228 :               for (auto v_num : this->projector.variables)
    1574             :                 {
    1575     1740442 :                   const Variable & var = system.variable(v_num);
    1576     1740442 :                   if (!var.active_on_subdomain(elem->subdomain_id()))
    1577        7368 :                     continue;
    1578     1733074 :                   const FEType fe_type = var.type();
    1579             : 
    1580       41682 :                   if (fe_type.family == MONOMIAL &&
    1581     1741756 :                       fe_type.order == CONSTANT &&
    1582             :                       copy_possible)
    1583             :                     {
    1584        5670 :                       if (!reinitted)
    1585             :                         {
    1586         468 :                           reinitted = true;
    1587        5670 :                           context.pre_fe_reinit(system, elem);
    1588             :                         }
    1589             : 
    1590        5670 :                       f.eval_old_dofs(*elem, fe_type, sys_num, v_num,
    1591             :                                       elem_dof_ids, Ue);
    1592             : 
    1593        5670 :                       action.insert(elem_dof_ids[0], Ue[0]);
    1594             :                     }
    1595             :                 }
    1596       13392 :             }
    1597             :         }
    1598             : #endif // LIBMESH_ENABLE_AMR
    1599             : 
    1600     5006152 :       const int dim = elem->dim();
    1601             : 
    1602     5006152 :       const unsigned int n_vertices = elem->n_vertices();
    1603     5006152 :       const unsigned int n_edges = elem->n_edges();
    1604     5006152 :       const unsigned int n_nodes = elem->n_nodes();
    1605             : 
    1606             :       // In 1-D we already handle our sides as vertices
    1607     5006152 :       const unsigned int n_sides = (dim > 1) * elem->n_sides();
    1608             : 
    1609             :       // What variables are supported on each kind of node on this elem?
    1610     1021458 :       var_set vertex_vars, edge_vars, side_vars;
    1611             : 
    1612             :       // If we have non-vertex nodes, the first is an edge node, but
    1613             :       // if we're in 2D we'll call that a side node
    1614     5006152 :       const bool has_edge_nodes = (n_nodes > n_vertices && dim > 2);
    1615             : 
    1616             :       // If we have even more nodes, the next is a side node.
    1617      510729 :       const bool has_side_nodes =
    1618     5006152 :         (n_nodes > n_vertices + ((dim > 2) * n_edges));
    1619             : 
    1620             :       // We may be out of nodes at this point or we have interior
    1621             :       // nodes which may have DoFs to project too
    1622      510729 :       const bool has_interior_nodes =
    1623     5006152 :         (n_nodes > n_vertices + ((dim > 2) * n_edges) + n_sides);
    1624             : 
    1625    10113494 :       for (auto v_num : this->projector.variables)
    1626             :         {
    1627     5107342 :           const Variable & var = this->projector.system.variable(v_num);
    1628     5107342 :           if (!var.active_on_subdomain(elem->subdomain_id()))
    1629      141930 :             continue;
    1630     5092096 :           const FEType fe_type = var.type();
    1631      516277 :           const auto & dof_map = this->projector.system.get_dof_map();
    1632     5092096 :           const auto vg = dof_map.var_group_from_var_number(v_num);
    1633     5092096 :           const bool add_p_level = dof_map.should_p_refine(vg);
    1634             : 
    1635             :           // If we're trying to do projections on an isogeometric
    1636             :           // analysis mesh, only the finite element nodes constrained
    1637             :           // by those spline nodes truly have delta function degrees
    1638             :           // of freedom.  We'll have to back out the spline degrees of
    1639             :           // freedom indirectly later.
    1640     5104810 :           if (fe_type.family == RATIONAL_BERNSTEIN &&
    1641       12714 :               elem->type() == NODEELEM)
    1642        5435 :             continue;
    1643             : 
    1644     5086188 :           if (FEInterface::n_dofs_at_node(fe_type, elem, 0, add_p_level))
    1645     4365541 :             vertex_vars.insert(vertex_vars.end(), v_num);
    1646             : 
    1647             :           // The first non-vertex node is always an edge node if those
    1648             :           // exist.  All edge nodes have the same number of DoFs
    1649     5086188 :           if (has_edge_nodes)
    1650      191472 :             if (FEInterface::n_dofs_at_node(fe_type, elem, n_vertices, add_p_level))
    1651       58013 :               edge_vars.insert(edge_vars.end(), v_num);
    1652             : 
    1653     5086188 :           if (has_side_nodes)
    1654             :             {
    1655     1065671 :               if (dim != 3)
    1656             :                 {
    1657      913991 :                   if (FEInterface::n_dofs_at_node(fe_type, elem, n_vertices, add_p_level))
    1658      740996 :                     side_vars.insert(side_vars.end(), v_num);
    1659             :                 }
    1660             :               else
    1661             :                 // In 3D, not all face nodes always have the same number of
    1662             :                 // DoFs!  We'll loop over all sides to be safe.
    1663     2276558 :                 for (unsigned int n = 0; n != n_nodes; ++n)
    1664     2235408 :                   if (elem->is_face(n))
    1665      302116 :                     if (FEInterface::n_dofs_at_node(fe_type, elem, n, add_p_level))
    1666             :                       {
    1667      100722 :                         side_vars.insert(side_vars.end(), v_num);
    1668        9808 :                         break;
    1669             :                       }
    1670             :             }
    1671             : 
    1672     5161279 :           if (FEInterface::n_dofs_per_elem(fe_type, elem, add_p_level) ||
    1673      855091 :               (has_interior_nodes &&
    1674      855091 :                FEInterface::n_dofs_at_node(fe_type, elem, n_nodes-1, add_p_level)))
    1675             :             {
    1676             : #ifdef LIBMESH_ENABLE_AMR
    1677             :               // We may have already just copied constant monomials,
    1678             :               // or we may be about to copy the whole element
    1679      930387 :               if ((f.is_grid_projection() &&
    1680      830350 :                    fe_type.family == MONOMIAL &&
    1681       12565 :                    fe_type.order == CONSTANT &&
    1682        9213 :                    elem->p_level() == 0 &&
    1683        9201 :                    elem->refinement_flag() != Elem::JUST_COARSENED &&
    1684        1528 :                    elem->p_refinement_flag() != Elem::JUST_COARSENED)
    1685      899484 :                   || copy_this_elem
    1686             :                   )
    1687      107899 :                 continue;
    1688             : #endif // LIBMESH_ENABLE_AMR
    1689             : 
    1690             :               // We need to project any other variables
    1691      815079 :               if (interiors.empty() || interiors.back() != elem)
    1692      794251 :                 interiors.push_back(elem);
    1693             :             }
    1694             :         }
    1695             : 
    1696             :       // We'll use a greedy algorithm in most cases: if another
    1697             :       // element has already claimed some of our DoFs, we'll let it do
    1698             :       // the work.
    1699    28102228 :       auto erase_covered_vars = []
    1700     5517492 :         (const Node * node, var_set & remaining, const ves_multimap & covered)
    1701             :         {
    1702     2758745 :           auto covered_range = covered.equal_range(node);
    1703    36536873 :           for (const auto & v_ent : as_range(covered_range))
    1704    17003550 :             for (const unsigned int var_covered :
    1705      763644 :                  std::get<2>(v_ent.second))
    1706      776232 :               remaining.erase(var_covered);
    1707             :         };
    1708             : 
    1709    28691333 :       auto erase_nonhanging_vars = [&extra_hanging_dofs]
    1710     5257968 :         (const Node * node, var_set & remaining, const ves_multimap & covered)
    1711             :         {
    1712     2628984 :           auto covered_range = covered.equal_range(node);
    1713    23688834 :           for (const auto & v_ent : as_range(covered_range))
    1714        8304 :             for (const unsigned int var_covered :
    1715         257 :                  std::get<2>(v_ent.second))
    1716        4651 :               if (!extra_hanging_dofs[var_covered])
    1717         273 :                 remaining.erase(var_covered);
    1718             :         };
    1719             : 
    1720    20027996 :       auto erase_copied_vars = [&copied_nodes, &extra_hanging_dofs]
    1721     2929954 :         (const Node * node, bool is_vertex, var_set & remaining)
    1722             :         {
    1723     1464976 :           auto copying_range = copied_nodes.equal_range(node);
    1724    19698856 :           for (const auto & v_ent : as_range(copying_range))
    1725             :             {
    1726    12209235 :               for (const unsigned int var_covered :
    1727             :                    v_ent.second.first)
    1728     6063126 :                 if (is_vertex || !extra_hanging_dofs[var_covered])
    1729      691150 :                   remaining.erase(var_covered);
    1730             : 
    1731     6366686 :               for (const unsigned int var_covered :
    1732             :                    v_ent.second.second)
    1733      220577 :                 if (!is_vertex || !extra_hanging_dofs[var_covered])
    1734       24752 :                   remaining.erase(var_covered);
    1735             :             }
    1736             :         };
    1737             : 
    1738    24016286 :       for (unsigned int v=0; v != n_vertices; ++v)
    1739             :         {
    1740    20968365 :           const Node * node = elem->node_ptr(v);
    1741             : 
    1742     1958229 :           auto remaining_vars = vertex_vars;
    1743             : 
    1744    19010134 :           erase_covered_vars(node, remaining_vars, vertices);
    1745             : 
    1746    19010134 :           if (remaining_vars.empty())
    1747     7301739 :             continue;
    1748             : 
    1749    10984073 :           if (!all_extra_hanging_dofs)
    1750             :             {
    1751    10692724 :               erase_nonhanging_vars(node, remaining_vars, edges);
    1752    10692724 :               if (remaining_vars.empty())
    1753           0 :                 continue;
    1754             : 
    1755    10692724 :               erase_nonhanging_vars(node, remaining_vars, sides);
    1756    10692724 :               if (remaining_vars.empty())
    1757        2127 :                 continue;
    1758             :             }
    1759             : 
    1760    10981693 :           erase_copied_vars(node, true, remaining_vars);
    1761    10981693 :           if (remaining_vars.empty())
    1762     5280538 :             continue;
    1763             : 
    1764     4180647 :           if (copy_this_elem)
    1765             :             {
    1766      581800 :               std::vector<dof_id_type> node_dof_ids;
    1767      581800 :               std::vector<typename FFunctor::ValuePushType> values;
    1768             : 
    1769     4638798 :               for (auto var : remaining_vars)
    1770             :                 {
    1771     2371924 :                   f.eval_old_dofs(*elem, v, var, node_dof_ids, values);
    1772     2371924 :                   insert_ids(node_dof_ids, values, node->processor_id());
    1773             :                 }
    1774     2266874 :               copied_nodes[node].first.insert(remaining_vars.begin(),
    1775             :                                               remaining_vars.end());
    1776     2266874 :               this->find_dofs_to_send(*node, *elem, v, remaining_vars);
    1777           0 :             }
    1778             :           else
    1779             :             vertices.emplace
    1780     2992443 :               (node, std::make_tuple(elem, v, std::move(remaining_vars)));
    1781             :         }
    1782             : 
    1783     5006152 :       if (has_edge_nodes)
    1784             :         {
    1785     1491282 :           for (unsigned int e=0; e != n_edges; ++e)
    1786             :             {
    1787     1423830 :               const Node * node = elem->node_ptr(n_vertices+e);
    1788             : 
    1789      115212 :               auto remaining_vars = edge_vars;
    1790             : 
    1791     1308618 :               erase_covered_vars(node, remaining_vars, edges);
    1792     1308618 :               if (remaining_vars.empty())
    1793      921995 :                 continue;
    1794             : 
    1795      300953 :               erase_covered_vars(node, remaining_vars, sides);
    1796      300953 :               if (remaining_vars.empty())
    1797           0 :                 continue;
    1798             : 
    1799      300953 :               if (!all_extra_hanging_dofs)
    1800             :                 {
    1801      205736 :                   erase_nonhanging_vars(node, remaining_vars, vertices);
    1802      205736 :                   if (remaining_vars.empty())
    1803         383 :                     continue;
    1804             :                 }
    1805             : 
    1806      300570 :               erase_copied_vars(node, false, remaining_vars);
    1807      300570 :               if (remaining_vars.empty())
    1808       16711 :                 continue;
    1809             : 
    1810      127774 :               if (copy_this_elem)
    1811             :                 {
    1812        7764 :                   std::vector<dof_id_type> edge_dof_ids;
    1813        7764 :                   std::vector<typename FFunctor::ValuePushType> values;
    1814             : 
    1815       52764 :                   for (auto var : remaining_vars)
    1816             :                     {
    1817       26382 :                       f.eval_old_dofs(*elem, n_vertices+e, var, edge_dof_ids, values);
    1818       26382 :                       insert_ids(edge_dof_ids, values, node->processor_id());
    1819             :                     }
    1820             : 
    1821       26382 :                   copied_nodes[node].second.insert(remaining_vars.begin(),
    1822             :                                                    remaining_vars.end());
    1823       26382 :                   this->find_dofs_to_send(*node, *elem, n_vertices+e, remaining_vars);
    1824           0 :                 }
    1825             :               else
    1826             :                 edges.emplace
    1827      273901 :                   (node, std::make_tuple(elem, e, std::move(remaining_vars)));
    1828             :             }
    1829             :         }
    1830             : 
    1831     5006152 :       if (has_side_nodes)
    1832             :         {
    1833     4937059 :           for (unsigned int side=0; side != n_sides; ++side)
    1834             :             {
    1835     3933680 :               const Node * node = nullptr;
    1836     3933680 :               unsigned short node_num = n_vertices+(dim>2)*n_edges+side;
    1837     3933680 :               if (dim != 3)
    1838     3587737 :                 node = elem->node_ptr(node_num);
    1839             :               else
    1840             :                 {
    1841             :                   // In 3D only some sides may have nodes
    1842     9840228 :                   for (unsigned int n = 0; n != n_nodes; ++n)
    1843             :                     {
    1844     9822816 :                       if (!elem->is_face(n))
    1845     7371150 :                         continue;
    1846             : 
    1847     1734426 :                       if (elem->is_node_on_side(n, side))
    1848             :                         {
    1849      152038 :                           node_num = n;
    1850      617544 :                           node = elem->node_ptr(node_num);
    1851      617544 :                           break;
    1852             :                         }
    1853             :                     }
    1854             :                 }
    1855             : 
    1856     3933680 :               if (!node)
    1857     1804938 :                 continue;
    1858             : 
    1859      342431 :               auto remaining_vars = side_vars;
    1860             : 
    1861     3916268 :               erase_covered_vars(node, remaining_vars, edges);
    1862     3916268 :               if (remaining_vars.empty())
    1863      320913 :                 continue;
    1864             : 
    1865     3566255 :               erase_covered_vars(node, remaining_vars, sides);
    1866     3566255 :               if (remaining_vars.empty())
    1867     1183405 :                 continue;
    1868             : 
    1869     2271299 :               if (!all_extra_hanging_dofs)
    1870             :                 {
    1871     2093724 :                   erase_nonhanging_vars(node, remaining_vars, vertices);
    1872     2093724 :                   if (remaining_vars.empty())
    1873         815 :                     continue;
    1874             :                 }
    1875             : 
    1876     2270484 :               erase_copied_vars(node, false, remaining_vars);
    1877     2270484 :               if (remaining_vars.empty())
    1878      125453 :                 continue;
    1879             : 
    1880     1823479 :               if (copy_this_elem)
    1881             :                 {
    1882       67314 :                   std::vector<dof_id_type> side_dof_ids;
    1883       67314 :                   std::vector<typename FFunctor::ValuePushType> values;
    1884             : 
    1885      633086 :                   for (auto var : remaining_vars)
    1886             :                     {
    1887      327417 :                       f.eval_old_dofs(*elem, node_num, var, side_dof_ids, values);
    1888      327417 :                       insert_ids(side_dof_ids, values, node->processor_id());
    1889             :                     }
    1890             : 
    1891      305669 :                   copied_nodes[node].second.insert(remaining_vars.begin(),
    1892             :                                                    remaining_vars.end());
    1893      305669 :                   this->find_dofs_to_send(*node, *elem, node_num, remaining_vars);
    1894           0 :                 }
    1895             :               else
    1896             :                 sides.emplace
    1897     1974907 :                   (node, std::make_tuple(elem, side, std::move(remaining_vars)));
    1898             :             }
    1899             :         }
    1900             : 
    1901             :       // Elements with elemental dofs might need those copied too.
    1902     4028041 :       if (copy_this_elem)
    1903             :         {
    1904      521592 :           std::vector<typename FFunctor::ValuePushType> U;
    1905      521592 :           std::vector<dof_id_type> dof_ids;
    1906             : 
    1907     4531166 :           for (auto v_num : this->projector.variables)
    1908             :             {
    1909     2286514 :               const Variable & var = system.variable(v_num);
    1910     2286514 :               if (!var.active_on_subdomain(elem->subdomain_id()))
    1911        4818 :                 continue;
    1912     2281696 :               FEType fe_type = var.type();
    1913             : 
    1914     2281696 :               f.eval_old_dofs(*elem, fe_type, sys_num, v_num,
    1915             :                               dof_ids, U);
    1916     4039348 :               action.insert(dof_ids, U);
    1917             : 
    1918     2281696 :               if (has_interior_nodes)
    1919             :                 {
    1920      128141 :                   f.eval_old_dofs(*elem, n_nodes-1, v_num, dof_ids, U);
    1921      242770 :                   action.insert(dof_ids, U);
    1922             :                 }
    1923             :             }
    1924           0 :         }
    1925             :     }
    1926      250950 : }
    1927             : 
    1928             : 
    1929             : template <typename FFunctor, typename GFunctor,
    1930             :           typename FValue, typename ProjectionAction>
    1931        7718 : void GenericProjector<FFunctor, GFunctor, FValue, ProjectionAction>::SortAndCopy::join
    1932             :   (const GenericProjector<FFunctor, GFunctor, FValue, ProjectionAction>::SortAndCopy & other)
    1933             : {
    1934       38220 :   auto merge_multimaps = [](ves_multimap & self, const ves_multimap & other_mm)
    1935             :     {
    1936      657167 :       for (const auto & pair : other_mm)
    1937             :         {
    1938      634013 :           const Node * node = pair.first;
    1939      433046 :           auto remaining_vars = std::get<2>(pair.second);
    1940             : 
    1941      216523 :           auto my_range = self.equal_range(node);
    1942      705066 :           for (const auto & v_ent : as_range(my_range))
    1943      143363 :             for (const unsigned int var_covered :
    1944       23924 :                  std::get<2>(v_ent.second))
    1945       24343 :               remaining_vars.erase(var_covered);
    1946             : 
    1947      634013 :           if (!remaining_vars.empty())
    1948             :             self.emplace
    1949      756352 :               (node, std::make_tuple(std::get<0>(pair.second),
    1950      192797 :                                      std::get<1>(pair.second),
    1951      192797 :                                      std::move(remaining_vars)));
    1952             :         }
    1953             :     };
    1954             : 
    1955        7718 :   merge_multimaps(vertices, other.vertices);
    1956        7718 :   merge_multimaps(edges, other.edges);
    1957        7718 :   merge_multimaps(sides, other.sides);
    1958             : 
    1959        7718 :   std::sort(interiors.begin(), interiors.end());
    1960       10414 :   std::vector<const Elem *> other_interiors = other.interiors;
    1961        7718 :   std::sort(other_interiors.begin(), other_interiors.end());
    1962        5392 :   std::vector<const Elem *> merged_interiors;
    1963        7718 :   std::set_union(interiors.begin(), interiors.end(),
    1964             :                  other_interiors.begin(), other_interiors.end(),
    1965             :                  std::back_inserter(merged_interiors));
    1966        2696 :   interiors.swap(merged_interiors);
    1967             : 
    1968        7718 :   SubFunctor::join(other);
    1969        7718 : }
    1970             : 
    1971             : namespace
    1972             : {
    1973             : template <typename Output, typename Input>
    1974             : typename std::enable_if<ScalarTraits<Input>::value, Output>::type
    1975           0 : raw_value(const Input & input, unsigned int /*index*/)
    1976             : {
    1977           0 :   return input;
    1978             : }
    1979             : 
    1980             : template <typename Output, template <typename> class WrapperClass, typename T>
    1981             : typename std::enable_if<ScalarTraits<T>::value &&
    1982             :                             TensorTools::MathWrapperTraits<WrapperClass<T>>::value,
    1983             :                         Output>::type
    1984       16143 : raw_value(const WrapperClass<T> & input, unsigned int index)
    1985             : {
    1986      233178 :   return input.slice(index);
    1987             : }
    1988             : 
    1989             : template <typename T>
    1990             : typename T::value_type
    1991             : grad_component(const T &, unsigned int);
    1992             : 
    1993             : template <typename T>
    1994             : typename VectorValue<T>::value_type
    1995        2380 : grad_component(const VectorValue<T> & grad, unsigned int component)
    1996             : {
    1997        4709 :   return grad(component);
    1998             : }
    1999             : 
    2000             : template <typename T>
    2001             : typename TensorValue<T>::value_type
    2002           0 : grad_component(const TensorValue<T> & grad, unsigned int component)
    2003             : {
    2004           0 :   libmesh_error_msg("This function should only be called for Hermites. "
    2005             :                     "I don't know how you got here");
    2006             :   return grad(component, component);
    2007             : }
    2008             : 
    2009             : 
    2010             : }
    2011             : 
    2012             : template <typename FFunctor, typename GFunctor,
    2013             :           typename FValue, typename ProjectionAction>
    2014      252336 : void GenericProjector<FFunctor, GFunctor, FValue, ProjectionAction>::ProjectVertices::operator()
    2015             :   (const node_range & range)
    2016             : {
    2017       20748 :   LOG_SCOPE ("project_vertices","GenericProjector");
    2018             : 
    2019      252336 :   const unsigned int sys_num = system.number();
    2020             : 
    2021             :   // Variables with extra hanging dofs can't safely use eval_at_node
    2022             :   // in as many places as variables without can.
    2023       20748 :   std::vector<unsigned short> extra_hanging_dofs;
    2024      519477 :   for (auto v_num : this->projector.variables)
    2025             :     {
    2026      278086 :       if (extra_hanging_dofs.size() <= v_num)
    2027      267141 :         extra_hanging_dofs.resize(v_num+1, false);
    2028      267141 :       extra_hanging_dofs[v_num] =
    2029      278086 :         FEInterface::extra_hanging_dofs(system.variable(v_num).type());
    2030             :     }
    2031             : 
    2032     2945254 :   for (const auto & v_pair : range)
    2033             :     {
    2034     2692918 :       const Node & vertex = *v_pair.first;
    2035     2692918 :       const Elem & elem = *std::get<0>(v_pair.second);
    2036     2692918 :       const unsigned int n = std::get<1>(v_pair.second);
    2037      233862 :       const var_set & vertex_vars = std::get<2>(v_pair.second);
    2038             : 
    2039     2692918 :       context.pre_fe_reinit(system, &elem);
    2040             : 
    2041     2692918 :       this->find_dofs_to_send(vertex, elem, n, vertex_vars);
    2042             : 
    2043             :       // Look at all the variables we're supposed to interpolate from
    2044             :       // this element on this vertex
    2045     5445179 :       for (const auto & var : vertex_vars)
    2046             :         {
    2047     2752261 :           const Variable & variable = system.variable(var);
    2048      238640 :           const FEType & base_fe_type = variable.type();
    2049      477280 :           const unsigned int var_component =
    2050      238640 :             system.variable_scalar_number(var, 0);
    2051             : 
    2052     2752261 :           if (base_fe_type.family == SCALAR)
    2053           0 :             continue;
    2054             : 
    2055     2752261 :           const FEContinuity cont = this->conts[var];
    2056     2752261 :           const FEFieldType field_type = this->field_types[var];
    2057             : 
    2058     2752261 :           if (cont == DISCONTINUOUS)
    2059             :             {
    2060           0 :               libmesh_assert_equal_to(vertex.n_comp(sys_num, var), 0);
    2061             :             }
    2062     2990902 :           else if (cont == C_ZERO ||
    2063     2513621 :                    cont == SIDE_DISCONTINUOUS)
    2064             :             {
    2065     2665341 :               if (cont == SIDE_DISCONTINUOUS &&
    2066        1518 :                   elem.dim() != 1)
    2067             :                 {
    2068           0 :                   libmesh_assert_equal_to(vertex.n_comp(sys_num, var), 0);
    2069           0 :                   continue;
    2070             :                 }
    2071             : 
    2072     3420288 :               const FValue val = f.eval_at_node
    2073     1771000 :                 (context, var_component, /*dim=*/ 0, // Don't care w/C0
    2074     2663823 :                  vertex, extra_hanging_dofs[var], system.time);
    2075             : 
    2076     2663823 :               if (field_type == TYPE_VECTOR)
    2077             :               {
    2078        1302 :                 libmesh_assert_equal_to(vertex.n_comp(sys_num, var), elem.dim());
    2079             : 
    2080             :                 // We will have a number of nodal value DoFs equal to the elem dim
    2081       83664 :                 for (auto i : make_range(elem.dim()))
    2082             :                 {
    2083       61920 :                   const dof_id_type id = vertex.dof_number(sys_num, var, i);
    2084             : 
    2085             :                   // Need this conversion so that this method
    2086             :                   // will compile for TYPE_SCALAR instantiations
    2087       59112 :                   const auto insert_val =
    2088        6498 :                     raw_value<typename ProjectionAction::InsertInput>(val, i);
    2089             : 
    2090       61920 :                   insert_id(id, insert_val, vertex.processor_id());
    2091             :                 }
    2092             :               }
    2093             :               else
    2094             :               {
    2095             :                 // C_ZERO elements have a single nodal value DoF at
    2096             :                 // vertices.  We can't assert n_comp==1 here,
    2097             :                 // because if this is a hanging node then it may have
    2098             :                 // more face/edge DoFs, but we don't need to deal with
    2099             :                 // those here.
    2100             : 
    2101     2642079 :                 const dof_id_type id = vertex.dof_number(sys_num, var, 0);
    2102     2642079 :                 insert_id(id, val, vertex.processor_id());
    2103      231149 :               }
    2104             :             }
    2105       88438 :           else if (cont == C_ONE)
    2106             :             {
    2107        7491 :               libmesh_assert(vertex.n_comp(sys_num, var));
    2108       88438 :               const dof_id_type first_id = vertex.dof_number(sys_num, var, 0);
    2109             : 
    2110             :               // C_ONE elements have a single nodal value and dim
    2111             :               // gradient values at vertices, as well as cross
    2112             :               // gradients for HERMITE.  We need to have an element in
    2113             :               // hand to figure out dim and to have in case this
    2114             :               // vertex is a new vertex.
    2115       88438 :               const int dim = elem.dim();
    2116             : #ifndef NDEBUG
    2117             :               // For now all C1 elements at a vertex had better have
    2118             :               // the same dimension.  If anyone hits these asserts let
    2119             :               // me know; we could probably support a mixed-dimension
    2120             :               // mesh IFF the 2D elements were all parallel to xy and
    2121             :               // the 1D elements all parallel to x.
    2122       33310 :               for (const auto e_id : (*this->projector.nodes_to_elem)[vertex.id()])
    2123             :                 {
    2124       25819 :                   const Elem & e = system.get_mesh().elem_ref(e_id);
    2125       25819 :                   libmesh_assert_equal_to(dim, e.dim());
    2126             :                 }
    2127             : #endif
    2128             : #ifdef LIBMESH_ENABLE_AMR
    2129        7491 :               bool is_old_vertex = true;
    2130       88438 :               if (elem.refinement_flag() == Elem::JUST_REFINED)
    2131             :                 {
    2132        3064 :                   const int i_am_child =
    2133       42333 :                     elem.parent()->which_child_am_i(&elem);
    2134             :                   is_old_vertex =
    2135       42333 :                     elem.parent()->is_vertex_on_parent(i_am_child, n);
    2136             :                 }
    2137             : #else
    2138             :               const bool is_old_vertex = false;
    2139             : #endif
    2140             : 
    2141             :               // The hermite element vertex shape functions are weird
    2142       88438 :               if (base_fe_type.family == HERMITE)
    2143             :                 {
    2144       98835 :                   const FValue val =
    2145       29398 :                     f.eval_at_node(context,
    2146             :                                    var_component,
    2147             :                                    dim,
    2148             :                                    vertex,
    2149             :                                    extra_hanging_dofs[var],
    2150       74720 :                                    system.time);
    2151       74720 :                   insert_id(first_id, val, vertex.processor_id());
    2152             : 
    2153       13147 :                   typename GFunctor::FunctorValue grad =
    2154       69117 :                     is_old_vertex ?
    2155       13839 :                     g->eval_at_node(context,
    2156             :                                     var_component,
    2157             :                                     dim,
    2158             :                                     vertex,
    2159             :                                     extra_hanging_dofs[var],
    2160       58022 :                                     system.time) :
    2161       15379 :                     g->eval_at_point(context,
    2162             :                                      var_component,
    2163             :                                      vertex,
    2164       16698 :                                      system.time,
    2165             :                                      false);
    2166             :                   // x derivative. Use slice because grad may be a tensor type
    2167       74720 :                   insert_id(first_id+1, grad.slice(0),
    2168             :                             vertex.processor_id());
    2169             : #if LIBMESH_DIM > 1
    2170       70436 :                   if (dim > 1 && is_old_vertex && f.is_grid_projection())
    2171             :                     {
    2172        5632 :                       for (int i = 1; i < dim; ++i)
    2173        3174 :                         insert_id(first_id+i+1, grad.slice(i),
    2174             :                                   vertex.processor_id());
    2175             : 
    2176             :                       // We can directly copy everything else too
    2177         716 :                       std::vector<FValue> derivs;
    2178        1074 :                       f.eval_mixed_derivatives
    2179        2458 :                         (context, var_component, dim, vertex, derivs);
    2180        5632 :                       for (auto i : index_range(derivs))
    2181        3174 :                         insert_id(first_id+dim+1+i, derivs[i],
    2182             :                                   vertex.processor_id());
    2183           0 :                     }
    2184       70464 :                   else if (dim > 1)
    2185             :                     {
    2186             :                       // We'll finite difference mixed derivatives.
    2187             :                       // This delta_x used to be TOLERANCE*hmin, but
    2188             :                       // the factor of 10 improved the accuracy in
    2189             :                       // some unit test projections
    2190       11418 :                       Real delta_x = TOLERANCE * 10 * elem.hmin();
    2191             : 
    2192       11418 :                       Point nxminus = elem.point(n),
    2193       11418 :                             nxplus = elem.point(n);
    2194       11418 :                       nxminus(0) -= delta_x;
    2195       11418 :                       nxplus(0) += delta_x;
    2196        1660 :                       typename GFunctor::FunctorValue gxminus =
    2197        9076 :                         g->eval_at_point(context,
    2198             :                                          var_component,
    2199             :                                          nxminus,
    2200       11418 :                                          system.time,
    2201             :                                          true);
    2202        1660 :                       typename GFunctor::FunctorValue gxplus =
    2203        9076 :                         g->eval_at_point(context,
    2204             :                                          var_component,
    2205             :                                          nxplus,
    2206       11418 :                                          system.time,
    2207             :                                          true);
    2208             :                       // y derivative
    2209       11418 :                       insert_id(first_id+2, grad.slice(1),
    2210             :                                 vertex.processor_id());
    2211             :                       // xy derivative
    2212       12320 :                       insert_id(first_id+3,
    2213       11418 :                         (grad_component(gxplus, 1) - grad_component(gxminus, 1)) / 2. / delta_x,
    2214             :                         vertex.processor_id());
    2215             : 
    2216             : #if LIBMESH_DIM > 2
    2217       11418 :                       if (dim > 2)
    2218             :                         {
    2219             :                           // z derivative
    2220         864 :                           insert_id(first_id+4, grad.slice(2),
    2221             :                                     vertex.processor_id());
    2222             :                           // xz derivative
    2223         936 :                           insert_id(first_id+5,
    2224         864 :                             (grad_component(gxplus, 2) - grad_component(gxminus, 2)) / 2. / delta_x,
    2225             :                             vertex.processor_id());
    2226             : 
    2227             :                           // We need new points for yz
    2228         864 :                           Point nyminus = elem.point(n),
    2229         864 :                             nyplus = elem.point(n);
    2230         864 :                           nyminus(1) -= delta_x;
    2231         864 :                           nyplus(1) += delta_x;
    2232          72 :                           typename GFunctor::FunctorValue gyminus =
    2233          72 :                             g->eval_at_point(context,
    2234             :                                              var_component,
    2235             :                                              nyminus,
    2236         864 :                                              system.time,
    2237             :                                              true);
    2238          72 :                           typename GFunctor::FunctorValue gyplus =
    2239          72 :                             g->eval_at_point(context,
    2240             :                                              var_component,
    2241             :                                              nyplus,
    2242         864 :                                              system.time,
    2243             :                                              true);
    2244             :                           // yz derivative
    2245         936 :                           insert_id(first_id+6,
    2246         864 :                             (grad_component(gyplus, 2) - grad_component(gyminus, 2)) / 2. / delta_x,
    2247             :                             vertex.processor_id());
    2248             :                           // Getting a 2nd order xyz is more tedious
    2249         864 :                           Point nxmym = elem.point(n),
    2250         864 :                             nxmyp = elem.point(n),
    2251         864 :                             nxpym = elem.point(n),
    2252         864 :                             nxpyp = elem.point(n);
    2253         864 :                           nxmym(0) -= delta_x;
    2254         864 :                           nxmym(1) -= delta_x;
    2255         864 :                           nxmyp(0) -= delta_x;
    2256         864 :                           nxmyp(1) += delta_x;
    2257         864 :                           nxpym(0) += delta_x;
    2258         864 :                           nxpym(1) -= delta_x;
    2259         864 :                           nxpyp(0) += delta_x;
    2260         864 :                           nxpyp(1) += delta_x;
    2261          72 :                           typename GFunctor::FunctorValue gxmym =
    2262          72 :                             g->eval_at_point(context,
    2263             :                                              var_component,
    2264             :                                              nxmym,
    2265         864 :                                              system.time,
    2266             :                                              true);
    2267          72 :                           typename GFunctor::FunctorValue gxmyp =
    2268          72 :                             g->eval_at_point(context,
    2269             :                                              var_component,
    2270             :                                              nxmyp,
    2271         864 :                                              system.time,
    2272             :                                              true);
    2273          72 :                           typename GFunctor::FunctorValue gxpym =
    2274          72 :                             g->eval_at_point(context,
    2275             :                                              var_component,
    2276             :                                              nxpym,
    2277         864 :                                              system.time,
    2278             :                                              true);
    2279          72 :                           typename GFunctor::FunctorValue gxpyp =
    2280          72 :                             g->eval_at_point(context,
    2281             :                                              var_component,
    2282             :                                              nxpyp,
    2283         864 :                                              system.time,
    2284             :                                              true);
    2285         864 :                           FValue gxzplus = (grad_component(gxpyp, 2) - grad_component(gxmyp, 2))
    2286         792 :                             / 2. / delta_x;
    2287         864 :                           FValue gxzminus = (grad_component(gxpym, 2) - grad_component(gxmym, 2))
    2288         792 :                             / 2. / delta_x;
    2289             :                           // xyz derivative
    2290         936 :                           insert_id(first_id+7,
    2291         864 :                             (gxzplus - gxzminus) / 2. / delta_x,
    2292             :                             vertex.processor_id());
    2293             :                         }
    2294             : #endif // LIBMESH_DIM > 2
    2295             :                     }
    2296             : #endif // LIBMESH_DIM > 1
    2297             :                 }
    2298             :               else
    2299             :                 {
    2300             :                   // Currently other C_ONE elements have a single nodal
    2301             :                   // value shape function and nodal gradient component
    2302             :                   // shape functions
    2303         918 :                   libmesh_assert_equal_to(
    2304             :                       FEInterface::n_dofs_at_node(
    2305             :                           base_fe_type,
    2306             :                           &elem,
    2307             :                           elem.get_node_index(&vertex),
    2308             :                           this->projector.system.get_dof_map().should_p_refine(
    2309             :                               this->projector.system.get_dof_map().var_group_from_var_number(var))),
    2310             :                       (unsigned int)(1 + dim));
    2311             : 
    2312       16554 :                   const FValue val =
    2313       11864 :                     f.eval_at_node(context, var_component, dim,
    2314             :                                    vertex, extra_hanging_dofs[var],
    2315       13718 :                                    system.time);
    2316       13718 :                   insert_id(first_id, val, vertex.processor_id());
    2317        1836 :                   typename GFunctor::FunctorValue grad =
    2318       12886 :                     is_old_vertex ?
    2319        2082 :                     g->eval_at_node(context, var_component, dim,
    2320             :                                     vertex, extra_hanging_dofs[var],
    2321        3284 :                                     system.time) :
    2322        9710 :                     g->eval_at_point(context, var_component, vertex,
    2323       10434 :                                      system.time, false);
    2324       41154 :                   for (int i=0; i!= dim; ++i)
    2325       29272 :                     insert_id(first_id + i + 1, grad.slice(i),
    2326             :                               vertex.processor_id());
    2327             :                 }
    2328             :             }
    2329             :           else
    2330           0 :             libmesh_error_msg("Unknown continuity " << cont);
    2331             :         }
    2332             :     }
    2333      252336 : }
    2334             : 
    2335             : 
    2336             : template <typename FFunctor, typename GFunctor,
    2337             :           typename FValue, typename ProjectionAction>
    2338      244968 : void GenericProjector<FFunctor, GFunctor, FValue, ProjectionAction>::ProjectEdges::operator()
    2339             :   (const node_range & range)
    2340             : {
    2341       15598 :   LOG_SCOPE ("project_edges","GenericProjector");
    2342             : 
    2343      244968 :   const unsigned int sys_num = system.number();
    2344             : 
    2345      489523 :   for (const auto & e_pair : range)
    2346             :     {
    2347      244555 :       const Elem & elem = *std::get<0>(e_pair.second);
    2348             : 
    2349             :       // If this is an unchanged element then we already copied all
    2350             :       // its dofs
    2351             : #ifdef LIBMESH_ENABLE_AMR
    2352      103562 :       if (f.is_grid_projection() &&
    2353       13692 :           (elem.refinement_flag() != Elem::JUST_REFINED &&
    2354           0 :            elem.refinement_flag() != Elem::JUST_COARSENED &&
    2355           0 :            elem.p_refinement_flag() != Elem::JUST_REFINED &&
    2356           0 :            elem.p_refinement_flag() != Elem::JUST_COARSENED))
    2357           0 :         continue;
    2358             : #endif // LIBMESH_ENABLE_AMR
    2359             : 
    2360      244555 :       const Node & edge_node = *e_pair.first;
    2361      244555 :       const int dim = elem.dim();
    2362       18274 :       const var_set & edge_vars = std::get<2>(e_pair.second);
    2363             : 
    2364      244555 :       const unsigned short edge_num = std::get<1>(e_pair.second);
    2365      244555 :       const unsigned short node_num = elem.n_vertices() + edge_num;
    2366      244555 :       context.edge = edge_num;
    2367      244555 :       context.pre_fe_reinit(system, &elem);
    2368             : 
    2369      244555 :       this->find_dofs_to_send(edge_node, elem, node_num, edge_vars);
    2370             : 
    2371             :       // Look at all the variables we're supposed to interpolate from
    2372             :       // this element on this edge
    2373      489110 :       for (const auto & var : edge_vars)
    2374             :         {
    2375      244555 :           const Variable & variable = system.variable(var);
    2376       18274 :           const FEType & base_fe_type = variable.type();
    2377       36548 :           const unsigned int var_component =
    2378       18274 :             system.variable_scalar_number(var, 0);
    2379             : 
    2380      244555 :           if (base_fe_type.family == SCALAR)
    2381      131941 :             continue;
    2382             : 
    2383      244555 :           FEType fe_type = base_fe_type;
    2384             : 
    2385             :           // This may be a p refined element
    2386       36548 :           fe_type.order = fe_type.order + elem.p_level();
    2387             : 
    2388             :           // If this is a Lagrange element with DoFs on edges then by
    2389             :           // convention we interpolate at the node rather than project
    2390             :           // along the edge.
    2391      244555 :           if (fe_type.family == LAGRANGE || fe_type.family == LAGRANGE_VEC)
    2392             :             {
    2393      131941 :               if (fe_type.order > 1)
    2394             :                 {
    2395             :                   const processor_id_type pid =
    2396       20288 :                     edge_node.processor_id();
    2397      136192 :                   FValue fval = f.eval_at_point
    2398      131941 :                     (context, var_component, edge_node, system.time,
    2399             :                      false);
    2400      131941 :                   if (fe_type.family == LAGRANGE_VEC)
    2401             :                   {
    2402             :                     // We will have a number of nodal value DoFs equal to the elem dim
    2403      142392 :                     for (auto i : make_range(elem.dim()))
    2404             :                     {
    2405             :                       const dof_id_type dof_id =
    2406      106794 :                         edge_node.dof_number(sys_num, var, i);
    2407             : 
    2408             :                       // Need this conversion so that this method
    2409             :                       // will compile for TYPE_SCALAR instantiations
    2410      100350 :                       const auto insert_val =
    2411       13896 :                         raw_value<typename ProjectionAction::InsertInput>(fval, i);
    2412             : 
    2413      106794 :                       insert_id(dof_id, insert_val, pid);
    2414             :                     }
    2415             :                   }
    2416             :                   else // We are LAGRANGE
    2417             :                   {
    2418             :                     const dof_id_type dof_id =
    2419       96343 :                       edge_node.dof_number(sys_num, var, 0);
    2420       96343 :                     insert_id(dof_id, fval, pid);
    2421             :                   }
    2422             :                 }
    2423      131941 :               continue;
    2424      111653 :             }
    2425             : 
    2426             : #ifdef LIBMESH_ENABLE_AMR
    2427             :           // If this is a low order monomial element which has merely
    2428             :           // been h refined then we already copied all its dofs
    2429           0 :           if (fe_type.family == MONOMIAL &&
    2430           0 :               fe_type.order == CONSTANT &&
    2431      112614 :               elem.refinement_flag() != Elem::JUST_COARSENED &&
    2432           0 :               elem.p_refinement_flag() != Elem::JUST_COARSENED)
    2433           0 :             continue;
    2434             : #endif // LIBMESH_ENABLE_AMR
    2435             : 
    2436             :           // FIXME: Need to generalize this to vector-valued elements. [PB]
    2437        8130 :           FEGenericBase<typename FFunctor::RealType> * fe = nullptr;
    2438        8130 :           context.get_element_fe( var, fe, dim );
    2439        8130 :           FEGenericBase<typename FFunctor::RealType> * edge_fe = nullptr;
    2440        8130 :           context.get_edge_fe( var, edge_fe );
    2441             : 
    2442             :           // If we're JUST_COARSENED we'll need a custom
    2443             :           // evaluation, not just the standard edge FE
    2444      112614 :           const FEGenericBase<typename FFunctor::RealType> & proj_fe =
    2445             : #ifdef LIBMESH_ENABLE_AMR
    2446       16260 :             (elem.refinement_flag() == Elem::JUST_COARSENED) ?
    2447             :             *fe :
    2448             : #endif
    2449             :             *edge_fe;
    2450             : 
    2451             : #ifdef LIBMESH_ENABLE_AMR
    2452      112614 :           if (elem.refinement_flag() == Elem::JUST_COARSENED)
    2453             :             {
    2454           0 :               std::vector<Point> fine_points;
    2455             : 
    2456           0 :               std::unique_ptr<FEGenericBase<typename FFunctor::RealType>> fine_fe
    2457             :                 (FEGenericBase<typename FFunctor::RealType>::build (dim, base_fe_type));
    2458             : 
    2459           0 :               std::unique_ptr<QBase> qrule
    2460             :                 (base_fe_type.default_quadrature_rule(1));
    2461           0 :               fine_fe->attach_quadrature_rule(qrule.get());
    2462             : 
    2463           0 :               const std::vector<Point> & child_xyz =
    2464           0 :                         fine_fe->get_xyz();
    2465             : 
    2466           0 :               for (unsigned int c = 0, nc = elem.n_children();
    2467           0 :                    c != nc; ++c)
    2468             :                 {
    2469           0 :                   if (!elem.is_child_on_edge(c, context.edge))
    2470           0 :                     continue;
    2471             : 
    2472           0 :                   fine_fe->edge_reinit(elem.child_ptr(c), context.edge);
    2473           0 :                   fine_points.insert(fine_points.end(),
    2474             :                                      child_xyz.begin(),
    2475             :                                      child_xyz.end());
    2476             :                 }
    2477             : 
    2478           0 :               std::vector<Point> fine_qp;
    2479           0 :               FEMap::inverse_map (dim, &elem, fine_points, fine_qp);
    2480             : 
    2481           0 :               context.elem_fe_reinit(&fine_qp);
    2482           0 :             }
    2483             :           else
    2484             : #endif // LIBMESH_ENABLE_AMR
    2485      112614 :             context.edge_fe_reinit();
    2486             : 
    2487       16260 :           const std::vector<dof_id_type> & dof_indices =
    2488       96354 :             context.get_dof_indices(var);
    2489             : 
    2490       16260 :           std::vector<unsigned int> edge_dofs;
    2491      112614 :           FEInterface::dofs_on_edge(&elem, dim, base_fe_type,
    2492      112614 :                                     context.edge, edge_dofs);
    2493             : 
    2494       16260 :           this->construct_projection
    2495       96354 :             (dof_indices, edge_dofs, var_component,
    2496             :              &edge_node, proj_fe);
    2497             :         }
    2498             :     }
    2499      244968 : }
    2500             : 
    2501             : 
    2502             : template <typename FFunctor, typename GFunctor,
    2503             :           typename FValue, typename ProjectionAction>
    2504      248782 : void GenericProjector<FFunctor, GFunctor, FValue, ProjectionAction>::ProjectSides::operator()
    2505             :   (const node_range & range)
    2506             : {
    2507       18180 :   LOG_SCOPE ("project_sides","GenericProjector");
    2508             : 
    2509      248782 :   const unsigned int sys_num = system.number();
    2510             : 
    2511     2058766 :   for (const auto & s_pair : range)
    2512             :     {
    2513     1809984 :       const Elem & elem = *std::get<0>(s_pair.second);
    2514             : 
    2515             :       // If this is an unchanged element then we already copied all
    2516             :       // its dofs
    2517             : #ifdef LIBMESH_ENABLE_AMR
    2518     1629965 :       if (f.is_grid_projection() &&
    2519      342121 :           (elem.refinement_flag() != Elem::JUST_REFINED &&
    2520        9841 :            elem.refinement_flag() != Elem::JUST_COARSENED &&
    2521         308 :            elem.p_refinement_flag() != Elem::JUST_REFINED &&
    2522          20 :            elem.p_refinement_flag() != Elem::JUST_COARSENED))
    2523           0 :         continue;
    2524             : #endif // LIBMESH_ENABLE_AMR
    2525             : 
    2526     1809984 :       const Node & side_node = *s_pair.first;
    2527     1809984 :       const int dim = elem.dim();
    2528      147471 :       const var_set & side_vars = std::get<2>(s_pair.second);
    2529             : 
    2530     1809984 :       const unsigned int side_num = std::get<1>(s_pair.second);
    2531     1809984 :       unsigned short node_num = elem.n_vertices()+side_num;
    2532             :       // In 3D only some sides may have nodes
    2533     1809984 :       if (dim == 3)
    2534     5091294 :         for (auto n : make_range(elem.n_nodes()))
    2535             :           {
    2536     5091294 :             if (!elem.is_face(n))
    2537     3856492 :               continue;
    2538             : 
    2539      904677 :             if (elem.is_node_on_side(n, side_num))
    2540             :               {
    2541      322626 :                 node_num = n;
    2542      322626 :                 break;
    2543             :               }
    2544             :           }
    2545             : 
    2546     1809984 :       context.side = side_num;
    2547     1809984 :       context.pre_fe_reinit(system, &elem);
    2548             : 
    2549     1809984 :       this->find_dofs_to_send(side_node, elem, node_num, side_vars);
    2550             : 
    2551             :       // Look at all the variables we're supposed to interpolate from
    2552             :       // this element on this side
    2553     3669513 :       for (const auto & var : side_vars)
    2554             :         {
    2555     1859529 :           const Variable & variable = system.variable(var);
    2556      151674 :           const FEType & base_fe_type = variable.type();
    2557      303348 :           const unsigned int var_component =
    2558      151674 :             system.variable_scalar_number(var, 0);
    2559             : 
    2560     1859529 :           if (base_fe_type.family == SCALAR)
    2561     1493696 :             continue;
    2562             : 
    2563     1859529 :           FEType fe_type = base_fe_type;
    2564      151674 :           const auto & dof_map = system.get_dof_map();
    2565     1859529 :           const bool add_p_level = dof_map.should_p_refine_var(var);
    2566             : 
    2567             :           // This may be a p refined element
    2568     2011203 :           fe_type.order = fe_type.order + add_p_level*elem.p_level();
    2569             : 
    2570             :           // If this is a Lagrange element with DoFs on sides then by
    2571             :           // convention we interpolate at the node rather than project
    2572             :           // along the side.
    2573     1859529 :           if (fe_type.family == LAGRANGE || fe_type.family == LAGRANGE_VEC)
    2574             :             {
    2575             :               // If order==1 then we only have DoFs on vertices, so
    2576             :               // skip all this.
    2577             :               // If order>1 ... we might still have something tricky
    2578             :               // like order==2 PRISM20, where some side nodes have
    2579             :               // DoFs but others don't.  Gotta check.
    2580     2987392 :               if (fe_type.order > 1 &&
    2581     1493696 :                   side_node.n_comp(sys_num, var))
    2582             :                 {
    2583             :                   const processor_id_type pid =
    2584      245854 :                     side_node.processor_id();
    2585     1945613 :                   FValue fval = f.eval_at_point
    2586     1488917 :                     (context, var_component, side_node, system.time,
    2587             :                      false);
    2588             : 
    2589     1488917 :                   if (fe_type.family == LAGRANGE_VEC)
    2590             :                   {
    2591             :                     // We will have a number of nodal value DoFs equal to the elem dim
    2592       84492 :                     for (auto i : make_range(elem.dim()))
    2593             :                     {
    2594       62736 :                       const dof_id_type dof_id = side_node.dof_number(sys_num, var, i);
    2595             : 
    2596             :                       // Need this conversion so that this method
    2597             :                       // will compile for TYPE_SCALAR instantiations
    2598       58188 :                       const auto insert_val =
    2599        9405 :                         raw_value<typename ProjectionAction::InsertInput>(fval, i);
    2600             : 
    2601       62736 :                       insert_id(dof_id, insert_val, pid);
    2602             :                     }
    2603             :                   }
    2604             :                   else // We are LAGRANGE
    2605             :                   {
    2606             :                     const dof_id_type dof_id =
    2607     1467161 :                       side_node.dof_number(sys_num, var, 0);
    2608     1467161 :                     insert_id(dof_id, fval, pid);
    2609             :                   }
    2610             :                 }
    2611     1493696 :               continue;
    2612     1247110 :             }
    2613             : 
    2614             : #ifdef LIBMESH_ENABLE_AMR
    2615             :           // If this is a low order monomial element which has merely
    2616             :           // been h refined then we already copied all its dofs
    2617           0 :           if (fe_type.family == MONOMIAL &&
    2618           0 :               fe_type.order == CONSTANT &&
    2619      365833 :               elem.refinement_flag() != Elem::JUST_COARSENED &&
    2620           0 :               elem.p_refinement_flag() != Elem::JUST_COARSENED)
    2621           0 :             continue;
    2622             : #endif // LIBMESH_ENABLE_AMR
    2623             : 
    2624             :           // FIXME: Need to generalize this to vector-valued elements. [PB]
    2625       28381 :           FEGenericBase<typename FFunctor::RealType> * fe = nullptr;
    2626      365833 :           context.get_element_fe( var, fe, dim );
    2627      337452 :           FEGenericBase<typename FFunctor::RealType> * side_fe = nullptr;
    2628      337452 :           context.get_side_fe( var, side_fe );
    2629             : 
    2630             :           // If we're JUST_COARSENED we'll need a custom
    2631             :           // evaluation, not just the standard side FE
    2632      365833 :           const FEGenericBase<typename FFunctor::RealType> & proj_fe =
    2633             : #ifdef LIBMESH_ENABLE_AMR
    2634       56762 :             (elem.refinement_flag() == Elem::JUST_COARSENED) ?
    2635             :             *fe :
    2636             : #endif
    2637             :             *side_fe;
    2638             : 
    2639             : #ifdef LIBMESH_ENABLE_AMR
    2640      365833 :           if (elem.refinement_flag() == Elem::JUST_COARSENED)
    2641             :             {
    2642           0 :               std::vector<Point> fine_points;
    2643             : 
    2644           0 :               std::unique_ptr<FEGenericBase<typename FFunctor::RealType>> fine_fe
    2645             :                 (FEGenericBase<typename FFunctor::RealType>::build (dim, base_fe_type));
    2646             : 
    2647           0 :               std::unique_ptr<QBase> qrule
    2648             :                 (base_fe_type.default_quadrature_rule(1));
    2649           0 :               fine_fe->attach_quadrature_rule(qrule.get());
    2650             : 
    2651           0 :               const std::vector<Point> & child_xyz =
    2652           0 :                         fine_fe->get_xyz();
    2653             : 
    2654           0 :               for (unsigned int c = 0, nc = elem.n_children();
    2655           0 :                    c != nc; ++c)
    2656             :                 {
    2657           0 :                   if (!elem.is_child_on_side(c, context.side))
    2658           0 :                     continue;
    2659             : 
    2660           0 :                   fine_fe->reinit(elem.child_ptr(c), context.side);
    2661           0 :                   fine_points.insert(fine_points.end(),
    2662             :                                      child_xyz.begin(),
    2663             :                                      child_xyz.end());
    2664             :                 }
    2665             : 
    2666           0 :               std::vector<Point> fine_qp;
    2667           0 :               FEMap::inverse_map (dim, &elem, fine_points, fine_qp);
    2668             : 
    2669           0 :               context.elem_fe_reinit(&fine_qp);
    2670           0 :             }
    2671             :           else
    2672             : #endif // LIBMESH_ENABLE_AMR
    2673      365833 :             context.side_fe_reinit();
    2674             : 
    2675       56762 :           const std::vector<dof_id_type> & dof_indices =
    2676      309071 :             context.get_dof_indices(var);
    2677             : 
    2678       56762 :           std::vector<unsigned int> side_dofs;
    2679      422595 :           FEInterface::dofs_on_side(&elem, dim, base_fe_type,
    2680      365833 :                                     context.side, side_dofs, add_p_level);
    2681             : 
    2682       56762 :           this->construct_projection
    2683      309071 :             (dof_indices, side_dofs, var_component,
    2684             :              &side_node, proj_fe);
    2685             :         }
    2686             :     }
    2687      248782 : }
    2688             : 
    2689             : 
    2690             : template <typename FFunctor, typename GFunctor,
    2691             :           typename FValue, typename ProjectionAction>
    2692      245945 : void GenericProjector<FFunctor, GFunctor, FValue, ProjectionAction>::ProjectInteriors::operator()
    2693             :   (const interior_range & range)
    2694             : {
    2695       16290 :   LOG_SCOPE ("project_interiors","GenericProjector");
    2696             : 
    2697      245945 :   const unsigned int sys_num = system.number();
    2698             : 
    2699             :   // Iterate over all dof-bearing element interiors in the range
    2700     1040196 :   for (const auto & elem : range)
    2701             :     {
    2702      794251 :       unsigned char dim = cast_int<unsigned char>(elem->dim());
    2703             : 
    2704      794251 :       context.pre_fe_reinit(system, elem);
    2705             : 
    2706             :       // Loop over all the variables we've been requested to project, to
    2707             :       // do the projection
    2708     1637822 :       for (const auto & var : this->projector.variables)
    2709             :         {
    2710      843571 :           const Variable & variable = system.variable(var);
    2711             : 
    2712      843571 :           if (!variable.active_on_subdomain(elem->subdomain_id()))
    2713      679758 :             continue;
    2714             : 
    2715       71012 :           const FEType & base_fe_type = variable.type();
    2716             : 
    2717      840067 :           if (base_fe_type.family == SCALAR)
    2718           0 :             continue;
    2719             : 
    2720       71012 :           FEGenericBase<typename FFunctor::RealType> * fe = nullptr;
    2721       71012 :           context.get_element_fe( var, fe, dim );
    2722             : 
    2723      840067 :           FEType fe_type = base_fe_type;
    2724       71012 :           const auto & dof_map = system.get_dof_map();
    2725      840067 :           const auto vg = dof_map.var_group_from_var_number(var);
    2726      840067 :           const bool add_p_level = dof_map.should_p_refine(vg);
    2727             : 
    2728             :           // This may be a p refined element
    2729      911082 :           fe_type.order = fe_type.order + add_p_level * elem->p_level();
    2730             : 
    2731      213039 :           const unsigned int var_component =
    2732      840067 :             system.variable_scalar_number(var, 0);
    2733             : 
    2734             :           // If this is a Lagrange element with interior DoFs then by
    2735             :           // convention we interpolate at the interior node rather
    2736             :           // than project along the interior.
    2737      840067 :           if (fe_type.family == LAGRANGE || fe_type.family == LAGRANGE_VEC)
    2738             :             {
    2739      676254 :               if (fe_type.order > 1)
    2740             :                 {
    2741      654476 :                   const unsigned int first_interior_node =
    2742      654476 :                     (elem->n_vertices() +
    2743      654476 :                      ((elem->dim() > 2) * elem->n_edges()) +
    2744      654476 :                      ((elem->dim() > 1) * elem->n_sides()));
    2745      654476 :                   const unsigned int n_nodes = elem->n_nodes();
    2746             : 
    2747             :                   // < vs != is important here for HEX20, QUAD8!
    2748     1308952 :                   for (unsigned int n = first_interior_node; n < n_nodes; ++n)
    2749             :                     {
    2750      654476 :                       const Node & interior_node = elem->node_ref(n);
    2751             : 
    2752      867973 :                       FValue fval = f.eval_at_point
    2753      573125 :                         (context, var_component, interior_node,
    2754      654476 :                          system.time, false);
    2755             :                       const processor_id_type pid =
    2756      110862 :                         interior_node.processor_id();
    2757             : 
    2758      654476 :                       if (fe_type.family == LAGRANGE_VEC)
    2759             :                       {
    2760             :                         // We will have a number of nodal value DoFs equal to the elem dim
    2761        2448 :                         for (unsigned int i = 0; i < elem->dim(); ++i)
    2762             :                         {
    2763        1728 :                           const dof_id_type dof_id = interior_node.dof_number(sys_num, var, i);
    2764             : 
    2765             :                           // Need this conversion so that this method
    2766             :                           // will compile for TYPE_SCALAR instantiations
    2767        1584 :                           const auto insert_val =
    2768         288 :                             raw_value<typename ProjectionAction::InsertInput>(fval, i);
    2769             : 
    2770        1728 :                           insert_id(dof_id, insert_val, pid);
    2771             :                         }
    2772             :                       }
    2773             :                       else // We are LAGRANGE
    2774             :                       {
    2775      110742 :                         const dof_id_type dof_id =
    2776      543014 :                           interior_node.dof_number(sys_num, var, 0);
    2777      653756 :                         insert_id(dof_id, fval, pid);
    2778             :                       }
    2779             :                     }
    2780             :                 }
    2781      676254 :               continue;
    2782      561518 :             }
    2783             : 
    2784             : #ifdef LIBMESH_ENABLE_AMR
    2785      163813 :           if (elem->refinement_flag() == Elem::JUST_COARSENED)
    2786             :             {
    2787          48 :               std::vector<Point> fine_points;
    2788             : 
    2789         312 :               std::unique_ptr<FEGenericBase<typename FFunctor::RealType>> fine_fe
    2790             :                 (FEGenericBase<typename FFunctor::RealType>::build (dim, base_fe_type));
    2791             : 
    2792         312 :               std::unique_ptr<QBase> qrule
    2793             :                 (base_fe_type.default_quadrature_rule(dim));
    2794         312 :               fine_fe->attach_quadrature_rule(qrule.get());
    2795             : 
    2796          48 :               const std::vector<Point> & child_xyz =
    2797          48 :                 fine_fe->get_xyz();
    2798             : 
    2799        2616 :               for (auto & child : elem->child_ref_range())
    2800             :                 {
    2801        2304 :                   fine_fe->reinit(&child);
    2802        2496 :                   fine_points.insert(fine_points.end(),
    2803             :                                      child_xyz.begin(),
    2804             :                                      child_xyz.end());
    2805             :                 }
    2806             : 
    2807          48 :               std::vector<Point> fine_qp;
    2808         288 :               FEMap::inverse_map (dim, elem, fine_points, fine_qp);
    2809             : 
    2810         288 :               context.elem_fe_reinit(&fine_qp);
    2811         240 :             }
    2812             :           else
    2813             : #endif // LIBMESH_ENABLE_AMR
    2814      163525 :             context.elem_fe_reinit();
    2815             : 
    2816       27291 :           const std::vector<dof_id_type> & dof_indices =
    2817      136522 :             context.get_dof_indices(var);
    2818             : 
    2819             :           const unsigned int n_dofs =
    2820       27291 :             cast_int<unsigned int>(dof_indices.size());
    2821             : 
    2822      177457 :           std::vector<unsigned int> all_dofs(n_dofs);
    2823       13644 :           std::iota(all_dofs.begin(), all_dofs.end(), 0);
    2824             : 
    2825       27291 :           this->construct_projection
    2826      136522 :             (dof_indices, all_dofs, var_component,
    2827             :              nullptr, *fe);
    2828             :         } // end variables loop
    2829             :     } // end elements loop
    2830      245945 : }
    2831             : 
    2832             : 
    2833             : 
    2834             : template <typename FFunctor, typename GFunctor,
    2835             :           typename FValue, typename ProjectionAction>
    2836             : void
    2837     7346382 : GenericProjector<FFunctor, GFunctor, FValue,
    2838             :                  ProjectionAction>::SubFunctor::find_dofs_to_send
    2839             :   (const Node & node, const Elem & elem, unsigned short node_num, const var_set & vars)
    2840             : {
    2841      728046 :   libmesh_assert (&node == elem.node_ptr(node_num));
    2842             : 
    2843             :   // Any ghosted node in our range might have an owner who needs our
    2844             :   // data
    2845     1456093 :   const processor_id_type owner = node.processor_id();
    2846     8074429 :   if (owner != system.processor_id())
    2847             :     {
    2848       66080 :       const MeshBase & mesh = system.get_mesh();
    2849       33034 :       const DofMap & dof_map = system.get_dof_map();
    2850             : 
    2851             :       // But let's check and see if we can be certain the owner can
    2852             :       // compute any or all of its own dof coefficients on that node.
    2853       66068 :       std::vector<dof_id_type> node_dof_ids, patch_dof_ids;
    2854     1521170 :       for (const auto & var : vars)
    2855             :         {
    2856      789374 :           const Variable & variable = system.variable(var);
    2857             : 
    2858      789374 :           if (!variable.active_on_subdomain(elem.subdomain_id()))
    2859           0 :             continue;
    2860             : 
    2861      789374 :           dof_map.dof_indices(elem, node_num, node_dof_ids, var);
    2862             :         }
    2863       33034 :       libmesh_assert(std::is_sorted(node_dof_ids.begin(),
    2864             :                                     node_dof_ids.end()));
    2865       66068 :       const std::vector<dof_id_type> & patch =
    2866      731796 :         (*this->projector.nodes_to_elem)[node.id()];
    2867     1887309 :       for (const auto & elem_id : patch)
    2868             :         {
    2869     1877631 :           const Elem & patch_elem = mesh.elem_ref(elem_id);
    2870     1074436 :           if (!patch_elem.active() || owner != patch_elem.processor_id())
    2871     1139980 :             continue;
    2872      737651 :           dof_map.dof_indices(&patch_elem, patch_dof_ids);
    2873      737651 :           std::sort(patch_dof_ids.begin(), patch_dof_ids.end());
    2874             : 
    2875             :           // Remove any node_dof_ids that we see can be calculated on
    2876             :           // this element
    2877      771165 :           std::vector<dof_id_type> diff_ids(node_dof_ids.size());
    2878      737651 :           auto it = std::set_difference(node_dof_ids.begin(), node_dof_ids.end(),
    2879             :                                         patch_dof_ids.begin(), patch_dof_ids.end(), diff_ids.begin());
    2880      737651 :           diff_ids.resize(it-diff_ids.begin());
    2881       33502 :           node_dof_ids.swap(diff_ids);
    2882      737651 :           if (node_dof_ids.empty())
    2883       32402 :             break;
    2884             :         }
    2885             : 
    2886             :       // Give ids_to_push default invalid pid: not yet computed
    2887      747608 :       for (auto id : node_dof_ids)
    2888       15812 :         new_ids_to_push[id].second = DofObject::invalid_processor_id;
    2889             :     }
    2890     7346382 : }
    2891             : 
    2892             : 
    2893             : 
    2894             : template <typename FFunctor, typename GFunctor,
    2895             :           typename FValue, typename ProjectionAction>
    2896             : template <typename Value>
    2897             : void
    2898      729696 : GenericProjector<FFunctor, GFunctor, FValue,
    2899             :                  ProjectionAction>::send_and_insert_dof_values
    2900             :   (std::unordered_map<dof_id_type, std::pair<Value, processor_id_type>> & ids_to_push,
    2901             :    ProjectionAction & action) const
    2902             : {
    2903       43320 :   LOG_SCOPE ("send_and_insert_dof_values", "GenericProjector");
    2904             : 
    2905             :   // See if we calculated any ids that need to be pushed; get them
    2906             :   // ready to push.
    2907             :   std::unordered_map<processor_id_type, std::vector<dof_id_type>>
    2908       43320 :     pushed_dof_ids, received_dof_ids;
    2909             :   std::unordered_map<processor_id_type, std::vector<typename TypeToSend<Value>::type>>
    2910       43320 :     pushed_dof_values, received_dof_values;
    2911      772011 :   for (auto & id_val_pid : ids_to_push)
    2912             :     {
    2913       42315 :       processor_id_type pid = id_val_pid.second.second;
    2914       42315 :       if (pid != DofObject::invalid_processor_id)
    2915             :         {
    2916       19641 :           pushed_dof_ids[pid].push_back(id_val_pid.first);
    2917       19641 :           pushed_dof_values[pid].push_back(convert_to_send(id_val_pid.second.first));
    2918             :         }
    2919             :     }
    2920             : 
    2921             :   // If and when we get ids pushed to us, act on them if we have
    2922             :   // corresponding values or save them if not
    2923      729828 :   auto ids_action_functor =
    2924        5166 :     [&action, &received_dof_ids, &received_dof_values]
    2925             :     (processor_id_type pid,
    2926         132 :      const std::vector<dof_id_type> & data)
    2927             :     {
    2928          66 :       auto iter = received_dof_values.find(pid);
    2929        5298 :       if (iter == received_dof_values.end())
    2930             :         {
    2931          66 :           libmesh_assert(received_dof_ids.find(pid) ==
    2932             :                          received_dof_ids.end());
    2933        5298 :           received_dof_ids[pid] = data;
    2934             :         }
    2935             :       else
    2936             :         {
    2937           0 :           auto & vals = iter->second;
    2938           0 :           std::size_t vals_size = vals.size();
    2939           0 :           libmesh_assert_equal_to(vals_size, data.size());
    2940           0 :           for (std::size_t i=0; i != vals_size; ++i)
    2941             :             {
    2942           0 :               Value val;
    2943           0 :               convert_from_receive(vals[i], val);
    2944           0 :               action.insert(data[i], val);
    2945             :             }
    2946           0 :           received_dof_values.erase(iter);
    2947             :         }
    2948             :     };
    2949             : 
    2950             :   // If and when we get values pushed to us, act on them if we have
    2951             :   // corresponding ids or save them if not
    2952      729828 :   auto values_action_functor =
    2953       27843 :     [&action, &received_dof_ids, &received_dof_values]
    2954             :     (processor_id_type pid,
    2955         132 :      const std::vector<typename TypeToSend<Value>::type> & data)
    2956             :     {
    2957          66 :       auto iter = received_dof_ids.find(pid);
    2958        5298 :       if (iter == received_dof_ids.end())
    2959             :         {
    2960             :           // We get no more than 1 values vector from anywhere
    2961           0 :           libmesh_assert(received_dof_values.find(pid) ==
    2962             :                          received_dof_values.end());
    2963           0 :           received_dof_values[pid] = data;
    2964             :         }
    2965             :       else
    2966             :         {
    2967          66 :           auto & ids = iter->second;
    2968         132 :           std::size_t ids_size = ids.size();
    2969          66 :           libmesh_assert_equal_to(ids_size, data.size());
    2970       24939 :           for (std::size_t i=0; i != ids_size; ++i)
    2971             :             {
    2972           0 :               Value val;
    2973        2130 :               convert_from_receive(data[i], val);
    2974       20706 :               action.insert(ids[i], val);
    2975             :             }
    2976          66 :           received_dof_ids.erase(iter);
    2977             :         }
    2978             :     };
    2979             : 
    2980             :   Parallel::push_parallel_vector_data
    2981      729696 :     (system.comm(), pushed_dof_ids, ids_action_functor);
    2982             : 
    2983             :   Parallel::push_parallel_vector_data
    2984      729696 :     (system.comm(), pushed_dof_values, values_action_functor);
    2985             : 
    2986             :   // At this point we shouldn't have any unprocessed data left
    2987       21660 :   libmesh_assert(received_dof_ids.empty());
    2988       21660 :   libmesh_assert(received_dof_values.empty());
    2989             : 
    2990      729696 : }
    2991             : 
    2992             : 
    2993             : 
    2994             : template <typename FFunctor, typename GFunctor,
    2995             :           typename FValue, typename ProjectionAction>
    2996             : void
    2997      642260 : GenericProjector<FFunctor, GFunctor, FValue, ProjectionAction>::SubProjector::construct_projection
    2998             :   (const std::vector<dof_id_type> & dof_indices_var,
    2999             :    const std::vector<unsigned int> & involved_dofs,
    3000             :    unsigned int var_component,
    3001             :    const Node * node,
    3002             :    const FEGenericBase<typename FFunctor::RealType> & fe)
    3003             : {
    3004      642260 :   const auto & JxW = fe.get_JxW();
    3005       50155 :   const auto & phi = fe.get_phi();
    3006       50155 :   const std::vector<std::vector<typename FEGenericBase<typename FFunctor::RealType>::OutputGradient>> * dphi = nullptr;
    3007      147795 :   const std::vector<Point> & xyz_values = fe.get_xyz();
    3008      642260 :   const FEContinuity cont = fe.get_continuity();
    3009       50155 :   const std::unordered_map<dof_id_type, typename FFunctor::ValuePushType> & ids_to_save =
    3010      642260 :     this->projector.ids_to_save;
    3011             : 
    3012      642260 :   if (cont == C_ONE)
    3013        2810 :     dphi = &(fe.get_dphi());
    3014             : 
    3015             :   const unsigned int n_involved_dofs =
    3016      100313 :     cast_int<unsigned int>(involved_dofs.size());
    3017             : 
    3018       50155 :   std::vector<dof_id_type> free_dof_ids;
    3019      592102 :   DenseVector<typename FFunctor::ValuePushType> Uinvolved(n_involved_dofs);
    3020      642260 :   std::vector<char> dof_is_fixed(n_involved_dofs, false); // bools
    3021             : 
    3022     6568242 :   for (auto i : make_range(n_involved_dofs))
    3023             :     {
    3024     6871356 :       const dof_id_type id = dof_indices_var[involved_dofs[i]];
    3025      472676 :       auto iter = ids_to_save.find(id);
    3026     5925982 :       if (iter == ids_to_save.end())
    3027     3991100 :         free_dof_ids.push_back(id);
    3028             :       else
    3029             :         {
    3030     1934882 :           dof_is_fixed[i] = true;
    3031     2089061 :           Uinvolved(i) = iter->second;
    3032             :         }
    3033             :     }
    3034             : 
    3035      642260 :   const unsigned int free_dofs = free_dof_ids.size();
    3036             : 
    3037             :   // There may be nothing to project
    3038      642260 :   if (!free_dofs)
    3039         118 :     return;
    3040             : 
    3041             :   // The element matrix and RHS for projections.
    3042             :   // Note that Ke is always real-valued, whereas
    3043             :   // Fe may be complex valued if complex number
    3044             :   // support is enabled
    3045      741119 :   DenseMatrix<Real> Ke(free_dofs, free_dofs);
    3046      641039 :   DenseVector<typename FFunctor::ValuePushType> Fe(free_dofs);
    3047             :   // The new degree of freedom coefficients to solve for
    3048      641039 :   DenseVector<typename FFunctor::ValuePushType> Ufree(free_dofs);
    3049             : 
    3050             :   const unsigned int n_qp =
    3051      100077 :     cast_int<unsigned int>(xyz_values.size());
    3052             : 
    3053             :   // Loop over the quadrature points
    3054     8573860 :   for (unsigned int qp=0; qp<n_qp; qp++)
    3055             :     {
    3056             :       // solution at the quadrature point
    3057     7932818 :       FValue fineval = f.eval_at_point(context,
    3058             :                                        var_component,
    3059             :                                        xyz_values[qp],
    3060     7932818 :                                        system.time,
    3061             :                                        false);
    3062             :       // solution grad at the quadrature point
    3063      638341 :       typename GFunctor::FunctorValue finegrad;
    3064     7932818 :       if (cont == C_ONE)
    3065      159130 :         finegrad = g->eval_at_point(context,
    3066             :                                     var_component,
    3067             :                                     xyz_values[qp],
    3068      147430 :                                     system.time,
    3069             :                                     false);
    3070             : 
    3071             :       // Form edge projection matrix
    3072   188517696 :       for (unsigned int sidei=0, freei=0;
    3073   189776932 :            sidei != n_involved_dofs; ++sidei)
    3074             :         {
    3075   181844114 :           unsigned int i = involved_dofs[sidei];
    3076             :           // fixed DoFs aren't test functions
    3077   196764971 :           if (dof_is_fixed[sidei])
    3078    49210600 :             continue;
    3079  4896386950 :           for (unsigned int sidej=0, freej=0;
    3080  5014099588 :                sidej != n_involved_dofs; ++sidej)
    3081             :             {
    3082  4885867566 :               unsigned int j = involved_dofs[sidej];
    3083  5290864547 :               if (dof_is_fixed[sidej])
    3084   545975656 :                 Fe(freei) -= phi[i][qp] * phi[j][qp] *
    3085   364952224 :                   JxW[qp] * Uinvolved(sidej);
    3086             :               else
    3087  5645509480 :                 Ke(freei,freej) += phi[i][qp] *
    3088  5645509383 :                   phi[j][qp] * JxW[qp];
    3089  4885867566 :               if (cont == C_ONE)
    3090             :                 {
    3091     1306973 :                   if (dof_is_fixed[sidej])
    3092     1229476 :                     Fe(freei) -= ( TensorTools::inner_product((*dphi)[i][qp],
    3093     1150160 :                                                               (*dphi)[j][qp]) ) *
    3094      992852 :                       JxW[qp] * Uinvolved(sidej);
    3095             :                   else
    3096      271712 :                     Ke(freei,freej) += ( TensorTools::inner_product((*dphi)[i][qp],
    3097      236053 :                                                                     (*dphi)[j][qp]) )
    3098      236053 :                       * JxW[qp];
    3099             :                 }
    3100  5290864547 :               if (!dof_is_fixed[sidej])
    3101  4521144894 :                 freej++;
    3102             :             }
    3103   159790117 :           Fe(freei) += phi[i][qp] * fineval * JxW[qp];
    3104   128232022 :           if (cont == C_ONE)
    3105      175400 :             Fe(freei) += (TensorTools::inner_product(finegrad,
    3106      188552 :                                                      (*dphi)[i][qp]) ) *
    3107       13000 :               JxW[qp];
    3108   128232022 :           freei++;
    3109             :         }
    3110             :     }
    3111             : 
    3112      641042 :   Ke.cholesky_solve(Fe, Ufree);
    3113             : 
    3114             :   // Transfer new edge solutions to element
    3115      641042 :   const processor_id_type pid = node ?
    3116       72834 :     node->processor_id() : DofObject::invalid_processor_id;
    3117      641042 :   insert_ids(free_dof_ids, Ufree.get_values(), pid);
    3118      540965 : }
    3119             : 
    3120             : 
    3121             : } // namespace libMesh
    3122             : 
    3123             : #endif // GENERIC_PROJECTOR_H

Generated by: LCOV version 1.14