libMesh
generic_projector.h
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2026 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License, or (at your option) any later version.
8 
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // Lesser General Public License for more details.
13 
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 
18 
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 
58 template <typename T>
59 struct TypeToSend {
60  typedef T type;
61 };
62 
63 template <typename T>
64 const typename TypeToSend<T>::type convert_to_send (const T& in)
65 { return in; }
66 
67 template <typename SendT, typename T>
68 void convert_from_receive (SendT & received, T & converted)
69 { converted = received; }
70 
81 template <typename FFunctor, typename GFunctor,
82  typename FValue, typename ProjectionAction>
84 {
85 public:
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 
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 
118 
120 
121 public:
122  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  system(system_in),
129  master_f(f_in),
130  master_g(g_in),
131  master_action(act_in),
132  variables(variables_in),
133  nodes_to_elem(nodes_to_elem_in)
134  {
135  if (!nodes_to_elem_in)
136  {
139  }
140  }
141 
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),
148  variables(in.variables),
150  {}
151 
152  ~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 {
171 
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,
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<
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<
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<
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<
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.
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>>
248 
251 
252 
253  struct SubProjector : public SubFunctor {
255 
256  using SubFunctor::action;
257  using SubFunctor::f;
258  using SubFunctor::system;
259  using SubFunctor::context;
260  using SubFunctor::conts;
262  using SubFunctor::insert_id;
264 
265  protected:
266  // Projections of C1 elements require a gradient as well
267  std::unique_ptr<GFunctor> g;
268 
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,
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 {
282 
284 
285  using SubFunctor::action;
286  using SubFunctor::f;
287  using SubFunctor::system;
288  using SubFunctor::context;
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 
303  std::vector<const Elem *> interiors;
304  };
305 
306 
307  struct ProjectVertices : public SubProjector {
309 
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;
319 
322 
323  void operator() (const node_range & range);
324  };
325 
326 
327  struct ProjectEdges : public SubProjector {
329 
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;
339 
342 
343  void operator() (const node_range & range);
344  };
345 
346  struct ProjectSides : public SubProjector {
348 
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;
358 
361 
362  void operator() (const node_range & range);
363  };
364 
365 
368 
369  struct ProjectInteriors : public SubProjector {
371 
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;
381 
384 
385  void operator() (const interior_range & range);
386  };
387 
388  template <typename Value>
390  (std::unordered_map<dof_id_type, std::pair<Value, processor_id_type>> & ids_to_push,
391  ProjectionAction & action) const;
392 };
393 
394 
404 template <typename Val>
406 {
407 private:
409 
410 public:
411  typedef Val InsertInput;
412 
414  target_vector(target_vec) {}
415 
417  Val val)
418  {
419  // Lock the new vector since it is shared among threads.
420  {
421  Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
422  target_vector.set(id, val);
423  }
424  }
425 
426 
427  void insert(const std::vector<dof_id_type> & dof_indices,
428  const DenseVector<Val> & Ue)
429  {
430  const numeric_index_type
433 
434  unsigned int size = Ue.size();
435 
436  libmesh_assert_equal_to(size, dof_indices.size());
437 
438  // Lock the new vector since it is shared among threads.
439  {
440  Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
441 
442  for (unsigned int i = 0; i != size; ++i)
443  if ((dof_indices[i] >= first) && (dof_indices[i] < last))
444  target_vector.set(dof_indices[i], Ue(i));
445  }
446  }
447 
448 };
449 
450 
459 template <typename Output>
461 {
462 protected:
464 
465 public:
468  typedef Output FunctorValue;
469 
470  FEMFunctionWrapper(const FEMFunctionBase<Output> & f) : _f(f.clone()) {}
471 
473  _f(fw._f->clone()) {}
474 
475  void init_context (FEMContext & c) { _f->init_context(c); }
476 
477  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  { return _f->component(c, i, n, time); }
484 
485  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  { return _f->component(c, i, n, time); }
491 
492  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  { libmesh_error(); } // this is only for grid projections
498 
499  bool is_grid_projection() { return false; }
500 
501  void eval_old_dofs (const Elem &,
502  unsigned int,
503  unsigned int,
504  std::vector<dof_id_type> &,
505  std::vector<Output> &)
506  { libmesh_error(); }
507 
508  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  { libmesh_error(); }
515 
516 private:
517  std::unique_ptr<FEMFunctionBase<Output>> _f;
518 };
519 
520 
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>
538 {
539 protected:
541 public:
543 
545  const std::vector<unsigned int> * vars) :
546  last_elem(nullptr),
547  sys(sys_in),
548  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  auto make_lookup = [this](unsigned int v)
553  {
554  const unsigned int vcomp = sys.variable_scalar_number(v,0);
555  if (vcomp >= component_to_var.size())
556  component_to_var.resize(vcomp+1, static_cast<unsigned int>(-1));
557  component_to_var[vcomp] = v;
558  };
559 
560  if (vars)
561  for (auto v : *vars)
562  make_lookup(v);
563  else
564  for (auto v : make_range(sys.n_vars()))
565  make_lookup(v);
566  }
567 
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.
581  {
583 
584  const std::set<unsigned char> & elem_dims =
585  old_context.elem_dimensions();
586 
587  // Loop over variables and dimensions, to prerequest
588  for (const auto & dim : elem_dims)
589  {
590  FEAbstract * fe = nullptr;
591  if (old_context.active_vars())
592  for (auto var : *old_context.active_vars())
593  {
594  old_context.get_element_fe(var, fe, dim);
595  get_shape_outputs(*fe);
596  }
597  else
598  for (auto var : make_range(sys.n_vars()))
599  {
600  old_context.get_element_fe(var, fe, dim);
601  get_shape_outputs(*fe);
602  }
603  }
604  }
605 
606  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  bool check_old_context (const FEMContext & c, const Point & p)
643  {
644  LOG_SCOPE ("check_old_context(c,p)", "OldSolutionBase");
645  const Elem & elem = c.get_elem();
646  if (last_elem != &elem)
647  {
648  if (elem.refinement_flag() == Elem::JUST_REFINED)
649  {
650  old_context.pre_fe_reinit(sys, elem.parent());
651  }
652  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  const Real master_tol = out_of_elem_tol / elem.hmax() * 2;
662 
663  for (auto & child : elem.child_ref_range())
664  if (child.close_to_point(p, master_tol))
665  {
666  old_context.pre_fe_reinit(sys, &child);
667  break;
668  }
669 
671  (old_context.get_elem().close_to_point(p, master_tol));
672  }
673  else
674  {
675  if (!elem.get_old_dof_object())
676  return false;
677 
678  old_context.pre_fe_reinit(sys, &elem);
679  }
680 
681  last_elem = &elem;
682  }
683  else
684  {
685  libmesh_assert(old_context.has_elem());
686 
687  const Real master_tol = out_of_elem_tol / elem.hmax() * 2;
688 
689  if (!old_context.get_elem().close_to_point(p, master_tol))
690  {
691  libmesh_assert_equal_to
693 
694  for (auto & child : elem.child_ref_range())
695  if (child.close_to_point(p, master_tol))
696  {
697  old_context.pre_fe_reinit(sys, &child);
698  break;
699  }
700 
702  (old_context.get_elem().close_to_point(p, master_tol));
703  }
704  }
705 
706  return true;
707  }
708 
709 protected:
710  const Elem * last_elem;
711  const System & sys;
713  std::vector<unsigned int> component_to_var;
714 
715  static const Real out_of_elem_tol;
716 };
717 
718 
727 template <typename Output,
728  void (FEMContext::*point_output) (unsigned int,
729  const Point &,
730  Output &,
731  const Real) const>
733 {
734 public:
737  typedef Output FunctorValue;
739 
741  const NumericVector<Number> & old_sol,
742  const std::vector<unsigned int> * vars) :
743  OldSolutionBase<Output, point_output>(sys_in, vars),
744  old_solution(old_sol)
745  {
746  this->old_context.set_algebraic_type(FEMContext::OLD);
747  this->old_context.set_custom_solution(&old_solution);
748  }
749 
751  OldSolutionBase<Output, point_output>(in.sys, in.old_context.active_vars()),
752  old_solution(in.old_solution)
753  {
754  this->old_context.set_algebraic_type(FEMContext::OLD);
755  this->old_context.set_custom_solution(&old_solution);
756  }
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  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  LOG_SCOPE ("eval_at_point()", "OldSolutionValue");
773 
774  if (!skip_context_check)
775  if (!this->check_old_context(c, p))
776  return Output(0);
777 
778  // Handle offset from non-scalar components in previous variables
779  libmesh_assert_less(i, this->component_to_var.size());
780  unsigned int var = this->component_to_var[i];
781 
782  Output n;
783  (this->old_context.*point_output)(var, p, n, this->out_of_elem_tol);
784  return n;
785  }
786 
787  template <typename T = Output,
788  typename std::enable_if<std::is_same<T, Number>::value, int>::type = 0>
789  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  LOG_SCOPE ("eval_mixed_derivatives", "OldSolutionValue");
796 
797  // This should only be called on vertices
798  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  libmesh_assert_less(i, this->component_to_var.size());
803  unsigned int var = this->component_to_var[i];
804 
805  // We have 1 mixed derivative in 2D, 4 in 3D
806  const unsigned int n_mixed = (dim-1) * (dim-1);
807  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  const DofObject * old_dof_object = n.get_old_dof_object();
812  if (old_dof_object &&
813  old_dof_object->n_vars(this->sys.number()) &&
814  old_dof_object->n_comp(this->sys.number(), var))
815  {
816  const dof_id_type first_old_id =
817  old_dof_object->dof_number(this->sys.number(), var, dim);
818  std::vector<dof_id_type> old_ids(n_mixed);
819  std::iota(old_ids.begin(), old_ids.end(), first_old_id);
820  old_solution.get(old_ids, derivs);
821  }
822  else
823  {
824  std::fill(derivs.begin(), derivs.end(), 0);
825  }
826  }
827 
828  template <typename T = Output,
829  typename std::enable_if<!std::is_same<T, Number>::value, int>::type = 0>
831  const FEMContext &, unsigned int, unsigned int, const Node &, std::vector<Output> &)
832  {
833  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  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  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  indices.clear();
848 
849  this->sys.get_dof_map().dof_indices(elem, node_num, indices, var_num);
850 
851  std::vector<dof_id_type> old_indices;
852 
853  this->sys.get_dof_map().old_dof_indices(elem, node_num, old_indices, var_num);
854 
855  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  bool invalid_old_index = false;
860  for (const auto & di : old_indices)
861  if (di == DofObject::invalid_id)
862  invalid_old_index = true;
863 
864  values.resize(old_indices.size());
865  if (invalid_old_index)
866  {
867  for (auto i : index_range(old_indices))
868  {
869  const dof_id_type di = old_indices[i];
870  if (di == DofObject::invalid_id)
871  values[i] = 0;
872  else
873  values[i] = old_solution(di);
874  }
875  }
876  else
877  old_solution.get(old_indices, values);
878  }
879 
880 
881  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  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  const Elem & old_elem =
893  (elem.refinement_flag() == Elem::JUST_REFINED) ?
894  *elem.parent() : elem;
895 
896  // If there are any element-based DOF numbers, get them
897  const unsigned int nc = FEInterface::n_dofs_per_elem(fe_type, &elem);
898 
899  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  if (nc != 0)
907  {
908  const DofObject & old_dof_object = old_elem.get_old_dof_object_ref();
909  libmesh_assert_greater(elem.n_systems(), sys_num);
910 
911  const std::pair<unsigned int, unsigned int>
912  vg_and_offset = elem.var_to_vg_and_offset(sys_num,var_num);
913  const unsigned int vg = vg_and_offset.first;
914  const unsigned int vig = vg_and_offset.second;
915 
916  unsigned int n_comp = old_dof_object.n_comp_group(sys_num,vg);
917  n_comp = std::min(n_comp, nc);
918 
919  std::vector<dof_id_type> old_dof_indices(n_comp);
920 
921  for (unsigned int i=0; i != n_comp; ++i)
922  {
923  const dof_id_type d_old =
924  old_dof_object.dof_number(sys_num, vg, vig, i, n_comp);
925  const dof_id_type d_new =
926  elem.dof_number(sys_num, vg, vig, i, n_comp);
927  libmesh_assert_not_equal_to (d_old, DofObject::invalid_id);
928  libmesh_assert_not_equal_to (d_new, DofObject::invalid_id);
929 
930  old_dof_indices[i] = d_old;
931  indices[i] = d_new;
932  }
933 
934  values.resize(n_comp);
935  old_solution.get(old_dof_indices, values);
936 
937  for (unsigned int i=n_comp; i != nc; ++i)
938  {
939  const dof_id_type d_new =
940  elem.dof_number(sys_num, vg, vig, i, n_comp);
941  libmesh_assert_not_equal_to (d_new, DofObject::invalid_id);
942  indices[i] = d_new;
943  }
944 
945  values.resize(nc, 0);
946  }
947  else
948  values.clear();
949  }
950 
951 private:
953 };
954 
955 template<>
956 inline void
958 {
959  fe.request_phi();
960 }
961 
962 
963 template<>
964 inline void
966 {
967  fe.request_dphi();
968 }
969 
970 template<>
971 inline void
973 {
974  fe.request_phi();
975 }
976 
977 
978 template<>
979 inline void
981 {
982  fe.request_dphi();
983 }
984 
985 
986 #ifdef LIBMESH_USE_COMPLEX_NUMBERS
987 template<>
988 inline void
990 {
991  fe.request_phi();
992 }
993 
994 
995 template<>
996 inline void
998 {
999  fe.request_dphi();
1000 }
1001 
1002 template<>
1003 inline void
1005 {
1006  fe.request_phi();
1007 }
1008 
1009 
1010 template<>
1011 inline void
1013 {
1014  fe.request_dphi();
1015 }
1016 #endif // LIBMESH_USE_COMPLEX_NUMBERS
1017 
1018 
1019 template<>
1020 inline
1021 Number
1024  unsigned int i,
1025  unsigned int /* elem_dim */,
1026  const Node & n,
1027  bool extra_hanging_dofs,
1028  Real /* time */)
1029 {
1030  LOG_SCOPE ("Number eval_at_node()", "OldSolutionValue");
1031 
1032  // This should only be called on vertices
1033  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  libmesh_assert_less(i, this->component_to_var.size());
1038  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  const Elem::RefinementState flag = c.get_elem().refinement_flag();
1051 
1052  const DofObject * old_dof_object = n.get_old_dof_object();
1053  if (old_dof_object &&
1054  (!extra_hanging_dofs ||
1055  flag == Elem::JUST_COARSENED ||
1056  flag == Elem::DO_NOTHING) &&
1057  old_dof_object->n_vars(sys.number()) &&
1058  old_dof_object->n_comp(sys.number(), var))
1059  {
1060  const dof_id_type old_id =
1061  old_dof_object->dof_number(sys.number(), var, 0);
1062  return old_solution(old_id);
1063  }
1064 
1065  return this->eval_at_point(c, i, n, 0, false);
1066 }
1067 
1068 template <>
1069 inline
1070 Gradient
1073  unsigned int i,
1074  unsigned int /* elem_dim */,
1075  const Node & n,
1076  bool extra_hanging_dofs,
1077  Real /* time */)
1078 {
1079  LOG_SCOPE ("Number eval_at_node()", "OldSolutionValue");
1080 
1081  // This should only be called on vertices
1082  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  libmesh_assert_less(i, this->component_to_var.size());
1087  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  const auto & elem = c.get_elem();
1100 
1101  const Elem::RefinementState flag = elem.refinement_flag();
1102 
1103  const DofObject * old_dof_object = n.get_old_dof_object();
1104  if (old_dof_object &&
1105  (!extra_hanging_dofs ||
1106  flag == Elem::JUST_COARSENED ||
1107  flag == Elem::DO_NOTHING) &&
1108  old_dof_object->n_vars(sys.number()) &&
1109  old_dof_object->n_comp(sys.number(), var))
1110  {
1111  Gradient return_val;
1112 
1113  for (auto dim : make_range(elem.dim()))
1114  {
1115  const dof_id_type old_id =
1116  old_dof_object->dof_number(sys.number(), var, dim);
1117  return_val(dim) = old_solution(old_id);
1118  }
1119 
1120  return return_val;
1121  }
1122 
1123  return this->eval_at_point(c, i, n, 0, false);
1124 }
1125 
1126 
1127 
1128 template<>
1129 inline
1130 Gradient
1133  unsigned int i,
1134  unsigned int elem_dim,
1135  const Node & n,
1136  bool extra_hanging_dofs,
1137  Real /* time */)
1138 {
1139  LOG_SCOPE ("Gradient eval_at_node()", "OldSolutionValue");
1140 
1141  // This should only be called on vertices
1142  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  libmesh_assert_less(i, this->component_to_var.size());
1147  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  const Elem::RefinementState flag = c.get_elem().refinement_flag();
1160 
1161  const DofObject * old_dof_object = n.get_old_dof_object();
1162  if (old_dof_object &&
1163  (!extra_hanging_dofs ||
1164  flag == Elem::JUST_COARSENED ||
1165  flag == Elem::DO_NOTHING) &&
1166  old_dof_object->n_vars(sys.number()) &&
1167  old_dof_object->n_comp(sys.number(), var))
1168  {
1169  Gradient g;
1170  for (unsigned int d = 0; d != elem_dim; ++d)
1171  {
1172  const dof_id_type old_id =
1173  old_dof_object->dof_number(sys.number(), var, d+1);
1174  g(d) = old_solution(old_id);
1175  }
1176  return g;
1177  }
1178 
1179  return this->eval_at_point(c, i, n, 0, false);
1180 }
1181 
1182 template<>
1183 inline
1184 Tensor
1187  unsigned int,
1188  unsigned int,
1189  const Node &,
1190  bool,
1191  Real)
1192 {
1193  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>
1204 
1205 #endif // LIBMESH_ENABLE_AMR
1206 
1211 template <typename FFunctor, typename GFunctor,
1212  typename FValue, typename ProjectionAction>
1214  (const ConstElemRange & range)
1215 {
1216  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  done_saving_ids = false;
1221 
1222  SortAndCopy sort_work(*this);
1223  Threads::parallel_reduce (range, sort_work);
1224  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  ids_to_push;
1229 
1230  ids_to_push.insert(sort_work.new_ids_to_push.begin(),
1231  sort_work.new_ids_to_push.end());
1232  ids_to_save.insert(sort_work.new_ids_to_save.begin(),
1233  sort_work.new_ids_to_save.end());
1234 
1235  std::vector<node_projection> vertices(sort_work.vertices.begin(),
1236  sort_work.vertices.end());
1237 
1238  done_saving_ids = sort_work.edges.empty() &&
1239  sort_work.sides.empty() && sort_work.interiors.empty();
1240 
1241  {
1242  ProjectVertices project_vertices(*this);
1243  Threads::parallel_reduce (node_range(&vertices), project_vertices);
1244  libmesh_merge_move(ids_to_push, project_vertices.new_ids_to_push);
1245  libmesh_merge_move(ids_to_save, project_vertices.new_ids_to_save);
1246  }
1247 
1248  done_saving_ids = sort_work.sides.empty() && sort_work.interiors.empty();
1249 
1250  this->send_and_insert_dof_values(ids_to_push, action);
1251 
1252  {
1253  std::vector<node_projection> edges(sort_work.edges.begin(), sort_work.edges.end());
1254  ProjectEdges project_edges(*this);
1255  Threads::parallel_reduce (node_range(&edges), project_edges);
1256  libmesh_merge_move(ids_to_push, project_edges.new_ids_to_push);
1257  libmesh_merge_move(ids_to_save, project_edges.new_ids_to_save);
1258  }
1259 
1260  done_saving_ids = sort_work.interiors.empty();
1261 
1262  this->send_and_insert_dof_values(ids_to_push, action);
1263 
1264  {
1265  std::vector<node_projection> sides(sort_work.sides.begin(), sort_work.sides.end());
1266  ProjectSides project_sides(*this);
1267  Threads::parallel_reduce (node_range(&sides), project_sides);
1268  libmesh_merge_move(ids_to_push, project_sides.new_ids_to_push);
1269  libmesh_merge_move(ids_to_save, project_sides.new_ids_to_save);
1270  }
1271 
1272  done_saving_ids = true;
1273  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  ProjectInteriors project_interiors(*this);
1279  project_interiors);
1280 }
1281 
1282 
1283 template <typename FFunctor, typename GFunctor,
1284  typename FValue, typename ProjectionAction>
1287  projector(p),
1288  action(p.master_action),
1289  f(p.master_f),
1290  context(p.system, &p.variables, /* allocate_local_matrices= */ false),
1291  conts(p.system.n_vars()),
1292  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  for (const auto & var : this->projector.variables)
1297  {
1298  // FIXME: Need to generalize this to vector-valued elements. [PB]
1299  FEAbstract * fe = nullptr;
1300  FEAbstract * side_fe = nullptr;
1301  FEAbstract * edge_fe = nullptr;
1302 
1303  const std::set<unsigned char> & elem_dims =
1304  context.elem_dimensions();
1305 
1306  for (const auto & dim : elem_dims)
1307  {
1308  context.get_element_fe( var, fe, dim );
1309  if (fe->get_fe_type().family == SCALAR)
1310  continue;
1311  if (dim > 1)
1312  context.get_side_fe( var, side_fe, dim );
1313  if (dim > 2)
1314  context.get_edge_fe( var, edge_fe );
1315 
1316  fe->get_JxW();
1317  fe->get_xyz();
1318  fe->get_JxW();
1319 
1320  fe->request_phi();
1321  if (dim > 1)
1322  {
1323  side_fe->get_JxW();
1324  side_fe->get_xyz();
1325  side_fe->request_phi();
1326  }
1327  if (dim > 2)
1328  {
1329  edge_fe->get_JxW();
1330  edge_fe->get_xyz();
1331  edge_fe->request_phi();
1332  }
1333 
1334  const FEContinuity cont = fe->get_continuity();
1335  this->conts[var] = cont;
1336  if (cont == C_ONE)
1337  {
1338  fe->request_dphi();
1339  if (dim > 1)
1340  side_fe->request_dphi();
1341  if (dim > 2)
1342  edge_fe->request_dphi();
1343  }
1344 
1345  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  f.init_context(context);
1351 }
1352 
1353 
1354 template <typename FFunctor, typename GFunctor,
1355  typename FValue, typename ProjectionAction>
1358  SubFunctor(p)
1359 {
1360 
1361  // Our C1 elements need gradient information
1362  for (const auto & var : this->projector.variables)
1363  if (this->conts[var] == C_ONE)
1364  {
1366  g = std::make_unique<GFunctor>(*p.master_g);
1367  g->init_context(context);
1368  return;
1369  }
1370 }
1371 
1372 template <typename FFunctor, typename GFunctor, typename FValue, typename ProjectionAction>
1373 template <typename InsertInput,
1374  typename std::enable_if<
1376  int>::type>
1377 void
1379  dof_id_type, const InsertInput & , processor_id_type)
1380 {
1381  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<
1389  int>::type>
1390 void
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  if (id == DofObject::invalid_id)
1397  return;
1398 
1399  auto iter = new_ids_to_push.find(id);
1400  if (iter == new_ids_to_push.end())
1401  action.insert(id, val);
1402  else
1403  {
1405  iter->second = std::make_pair(val, pid);
1406  }
1407  if (!this->projector.done_saving_ids)
1408  {
1409  libmesh_assert(!new_ids_to_save.count(id));
1410  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<
1418  int>::type>
1419 void
1421  const std::vector<dof_id_type> &,
1422  const std::vector<InsertInput> &,
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<
1433  int>::type>
1434 void
1436  const std::vector<dof_id_type> & ids,
1437  const std::vector<InsertInput> & vals,
1438  processor_id_type pid)
1439 {
1440  libmesh_assert_equal_to(ids.size(), vals.size());
1441  for (auto i : index_range(ids))
1442  {
1443  const dof_id_type id = ids[i];
1444 
1445  // We may see invalid ids when expanding a subdomain with a
1446  // restricted variable
1447  if (id == DofObject::invalid_id)
1448  continue;
1449 
1450  const InsertInput & val = vals[i];
1451 
1452  auto iter = new_ids_to_push.find(id);
1453  if (iter == new_ids_to_push.end())
1454  action.insert(id, val);
1455  else
1456  {
1458  iter->second = std::make_pair(val, pid);
1459  }
1460  if (!this->projector.done_saving_ids)
1461  {
1462  libmesh_assert(!new_ids_to_save.count(id));
1463  new_ids_to_save[id] = val;
1464  }
1465  }
1466 }
1467 
1468 template <typename FFunctor, typename GFunctor,
1469  typename FValue, typename ProjectionAction>
1472 {
1473  new_ids_to_push.insert(other.new_ids_to_push.begin(), other.new_ids_to_push.end());
1474  new_ids_to_save.insert(other.new_ids_to_save.begin(), other.new_ids_to_save.end());
1475 }
1476 
1477 
1478 template <typename FFunctor, typename GFunctor,
1479  typename FValue, typename ProjectionAction>
1481  (const ConstElemRange & range)
1482 {
1483  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  std::unordered_map<const Node *, std::pair<var_set, var_set>> copied_nodes;
1510 
1511  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  std::vector<unsigned short> extra_hanging_dofs;
1517  bool all_extra_hanging_dofs = true;
1518  for (auto v_num : this->projector.variables)
1519  {
1520  if (extra_hanging_dofs.size() <= v_num)
1521  extra_hanging_dofs.resize(v_num+1, false);
1522  extra_hanging_dofs[v_num] =
1524 
1525  if (!extra_hanging_dofs[v_num])
1526  all_extra_hanging_dofs = false;
1527  }
1528 
1529  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  bool copy_this_elem = false;
1534 
1535 #ifdef LIBMESH_ENABLE_AMR
1536  // If we're projecting from an old grid
1537  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  const DofObject * old_dof_object = elem->get_old_dof_object();
1544  const Elem::RefinementState h_flag = elem->refinement_flag();
1545  const Elem::RefinementState p_flag = elem->p_refinement_flag();
1546  if (!old_dof_object &&
1547  h_flag != Elem::JUST_REFINED &&
1548  h_flag != Elem::JUST_COARSENED)
1549  continue;
1550 
1551  // If this is an unchanged element, just copy everything
1552  if (h_flag != Elem::JUST_REFINED &&
1553  h_flag != Elem::JUST_COARSENED &&
1554  p_flag != Elem::JUST_REFINED &&
1555  p_flag != Elem::JUST_COARSENED)
1556  copy_this_elem = true;
1557  else
1558  {
1559  bool reinitted = false;
1560 
1561  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  const bool copy_possible =
1566  p_level == 0 &&
1567  h_flag != Elem::JUST_COARSENED &&
1568  p_flag != Elem::JUST_COARSENED;
1569 
1570  std::vector<typename FFunctor::ValuePushType> Ue(1);
1571  std::vector<dof_id_type> elem_dof_ids(1);
1572 
1573  for (auto v_num : this->projector.variables)
1574  {
1575  const Variable & var = system.variable(v_num);
1576  if (!var.active_on_subdomain(elem->subdomain_id()))
1577  continue;
1578  const FEType fe_type = var.type();
1579 
1580  if (fe_type.family == MONOMIAL &&
1581  fe_type.order == CONSTANT &&
1582  copy_possible)
1583  {
1584  if (!reinitted)
1585  {
1586  reinitted = true;
1587  context.pre_fe_reinit(system, elem);
1588  }
1589 
1590  f.eval_old_dofs(*elem, fe_type, sys_num, v_num,
1591  elem_dof_ids, Ue);
1592 
1593  action.insert(elem_dof_ids[0], Ue[0]);
1594  }
1595  }
1596  }
1597  }
1598 #endif // LIBMESH_ENABLE_AMR
1599 
1600  const int dim = elem->dim();
1601 
1602  const unsigned int n_vertices = elem->n_vertices();
1603  const unsigned int n_edges = elem->n_edges();
1604  const unsigned int n_nodes = elem->n_nodes();
1605 
1606  // In 1-D we already handle our sides as vertices
1607  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  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  const bool has_poly_midnode = (elem->type() == C0POLYHEDRON) && (n_nodes == n_vertices + 1);
1615  const bool has_edge_nodes = (n_nodes > n_vertices + has_poly_midnode && dim > 2);
1616 
1617  // If we have even more nodes, the next is a side node.
1618  const bool has_side_nodes =
1619  (n_nodes > n_vertices + ((dim > 2) * n_edges));
1620 
1621  // We may be out of nodes at this point or we have interior
1622  // nodes which may have DoFs to project too
1623  const bool has_interior_nodes =
1624  (n_nodes > n_vertices + ((dim > 2) * n_edges) + n_sides);
1625 
1626  for (auto v_num : this->projector.variables)
1627  {
1628  const Variable & var = this->projector.system.variable(v_num);
1629  if (!var.active_on_subdomain(elem->subdomain_id()))
1630  continue;
1631  const FEType fe_type = var.type();
1632  const bool add_p_level = fe_type.p_refinement;
1633 
1634  // If we're trying to do projections on an isogeometric
1635  // analysis mesh, only the finite element nodes constrained
1636  // by those spline nodes truly have delta function degrees
1637  // of freedom. We'll have to back out the spline degrees of
1638  // freedom indirectly later.
1639  if (fe_type.family == RATIONAL_BERNSTEIN &&
1640  elem->type() == NODEELEM)
1641  continue;
1642 
1643  if (FEInterface::n_dofs_at_node(fe_type, elem, 0, add_p_level))
1644  vertex_vars.insert(vertex_vars.end(), v_num);
1645 
1646  // The first non-vertex node is always an edge node if those
1647  // exist. All edge nodes have the same number of DoFs
1648  if (has_edge_nodes)
1649  if (FEInterface::n_dofs_at_node(fe_type, elem, n_vertices, add_p_level))
1650  edge_vars.insert(edge_vars.end(), v_num);
1651 
1652  if (has_side_nodes)
1653  {
1654  if (dim != 3)
1655  {
1656  if (FEInterface::n_dofs_at_node(fe_type, elem, n_vertices, add_p_level))
1657  side_vars.insert(side_vars.end(), v_num);
1658  }
1659  else
1660  // In 3D, not all face nodes always have the same number of
1661  // DoFs! We'll loop over all sides to be safe.
1662  for (unsigned int n = 0; n != n_nodes; ++n)
1663  if (elem->is_face(n))
1664  if (FEInterface::n_dofs_at_node(fe_type, elem, n, add_p_level))
1665  {
1666  side_vars.insert(side_vars.end(), v_num);
1667  break;
1668  }
1669  }
1670 
1671  if (FEInterface::n_dofs_per_elem(fe_type, elem, add_p_level) ||
1672  (has_interior_nodes &&
1673  FEInterface::n_dofs_at_node(fe_type, elem, n_nodes-1, add_p_level)))
1674  {
1675 #ifdef LIBMESH_ENABLE_AMR
1676  // We may have already just copied constant monomials,
1677  // or we may be about to copy the whole element
1678  if ((f.is_grid_projection() &&
1679  fe_type.family == MONOMIAL &&
1680  fe_type.order == CONSTANT &&
1681  elem->p_level() == 0 &&
1682  elem->refinement_flag() != Elem::JUST_COARSENED &&
1683  elem->p_refinement_flag() != Elem::JUST_COARSENED)
1684  || copy_this_elem
1685  )
1686  continue;
1687 #endif // LIBMESH_ENABLE_AMR
1688 
1689  // We need to project any other variables
1690  if (interiors.empty() || interiors.back() != elem)
1691  interiors.push_back(elem);
1692  }
1693  }
1694 
1695  // We'll use a greedy algorithm in most cases: if another
1696  // element has already claimed some of our DoFs, we'll let it do
1697  // the work.
1698  auto erase_covered_vars = []
1699  (const Node * node, var_set & remaining, const ves_multimap & covered)
1700  {
1701  auto covered_range = covered.equal_range(node);
1702  for (const auto & v_ent : as_range(covered_range))
1703  for (const unsigned int var_covered :
1704  std::get<2>(v_ent.second))
1705  remaining.erase(var_covered);
1706  };
1707 
1708  auto erase_nonhanging_vars = [&extra_hanging_dofs]
1709  (const Node * node, var_set & remaining, const ves_multimap & covered)
1710  {
1711  auto covered_range = covered.equal_range(node);
1712  for (const auto & v_ent : as_range(covered_range))
1713  for (const unsigned int var_covered :
1714  std::get<2>(v_ent.second))
1715  if (!extra_hanging_dofs[var_covered])
1716  remaining.erase(var_covered);
1717  };
1718 
1719  auto erase_copied_vars = [&copied_nodes, &extra_hanging_dofs]
1720  (const Node * node, bool is_vertex, var_set & remaining)
1721  {
1722  auto copying_range = copied_nodes.equal_range(node);
1723  for (const auto & v_ent : as_range(copying_range))
1724  {
1725  for (const unsigned int var_covered :
1726  v_ent.second.first)
1727  if (is_vertex || !extra_hanging_dofs[var_covered])
1728  remaining.erase(var_covered);
1729 
1730  for (const unsigned int var_covered :
1731  v_ent.second.second)
1732  if (!is_vertex || !extra_hanging_dofs[var_covered])
1733  remaining.erase(var_covered);
1734  }
1735  };
1736 
1737  // Treat polyhedron midnode as a vertex
1738  // NOTE: if we start having edge or side nodes on polyhedra, we need to use that +1 offset
1739  // in the edge and side nodes code as well!
1740 #ifndef NDEBUG
1741  for (auto v_num : this->projector.variables)
1742  {
1743  const auto & family = this->projector.system.variable(v_num).type().family;
1744  // Add to the list once known to be correctly functioning with the midnode
1745  libmesh_assert(!has_poly_midnode || (family == LAGRANGE || family == MONOMIAL || family == XYZ));
1746  }
1747 #endif
1748  for (const auto v : make_range(n_vertices + has_poly_midnode))
1749  {
1750  const Node * node = elem->node_ptr(v);
1751 
1752  auto remaining_vars = vertex_vars;
1753 
1754  erase_covered_vars(node, remaining_vars, vertices);
1755 
1756  if (remaining_vars.empty())
1757  continue;
1758 
1759  if (!all_extra_hanging_dofs)
1760  {
1761  erase_nonhanging_vars(node, remaining_vars, edges);
1762  if (remaining_vars.empty())
1763  continue;
1764 
1765  erase_nonhanging_vars(node, remaining_vars, sides);
1766  if (remaining_vars.empty())
1767  continue;
1768  }
1769 
1770  erase_copied_vars(node, true, remaining_vars);
1771  if (remaining_vars.empty())
1772  continue;
1773 
1774  if (copy_this_elem)
1775  {
1776  std::vector<dof_id_type> node_dof_ids;
1777  std::vector<typename FFunctor::ValuePushType> values;
1778 
1779  for (auto var : remaining_vars)
1780  {
1781  f.eval_old_dofs(*elem, v, var, node_dof_ids, values);
1782  insert_ids(node_dof_ids, values, node->processor_id());
1783  }
1784  copied_nodes[node].first.insert(remaining_vars.begin(),
1785  remaining_vars.end());
1786  this->find_dofs_to_send(*node, *elem, v, remaining_vars);
1787  }
1788  else
1789  vertices.emplace
1790  (node, std::make_tuple(elem, v, std::move(remaining_vars)));
1791  }
1792 
1793  if (has_edge_nodes)
1794  {
1795  for (unsigned int e=0; e != n_edges; ++e)
1796  {
1797  const Node * node = elem->node_ptr(n_vertices+e);
1798 
1799  auto remaining_vars = edge_vars;
1800 
1801  erase_covered_vars(node, remaining_vars, edges);
1802  if (remaining_vars.empty())
1803  continue;
1804 
1805  erase_covered_vars(node, remaining_vars, sides);
1806  if (remaining_vars.empty())
1807  continue;
1808 
1809  if (!all_extra_hanging_dofs)
1810  {
1811  erase_nonhanging_vars(node, remaining_vars, vertices);
1812  if (remaining_vars.empty())
1813  continue;
1814  }
1815 
1816  erase_copied_vars(node, false, remaining_vars);
1817  if (remaining_vars.empty())
1818  continue;
1819 
1820  if (copy_this_elem)
1821  {
1822  std::vector<dof_id_type> edge_dof_ids;
1823  std::vector<typename FFunctor::ValuePushType> values;
1824 
1825  for (auto var : remaining_vars)
1826  {
1827  f.eval_old_dofs(*elem, n_vertices+e, var, edge_dof_ids, values);
1828  insert_ids(edge_dof_ids, values, node->processor_id());
1829  }
1830 
1831  copied_nodes[node].second.insert(remaining_vars.begin(),
1832  remaining_vars.end());
1833  this->find_dofs_to_send(*node, *elem, n_vertices+e, remaining_vars);
1834  }
1835  else
1836  edges.emplace
1837  (node, std::make_tuple(elem, e, std::move(remaining_vars)));
1838  }
1839  }
1840 
1841  if (has_side_nodes)
1842  {
1843  for (unsigned int side=0; side != n_sides; ++side)
1844  {
1845  const Node * node = nullptr;
1846  unsigned short node_num = n_vertices+(dim>2)*n_edges+side;
1847  if (dim != 3)
1848  node = elem->node_ptr(node_num);
1849  else
1850  {
1851  // In 3D only some sides may have nodes
1852  for (unsigned int n = 0; n != n_nodes; ++n)
1853  {
1854  if (!elem->is_face(n))
1855  continue;
1856 
1857  if (elem->is_node_on_side(n, side))
1858  {
1859  node_num = n;
1860  node = elem->node_ptr(node_num);
1861  break;
1862  }
1863  }
1864  }
1865 
1866  if (!node)
1867  continue;
1868 
1869  auto remaining_vars = side_vars;
1870 
1871  erase_covered_vars(node, remaining_vars, edges);
1872  if (remaining_vars.empty())
1873  continue;
1874 
1875  erase_covered_vars(node, remaining_vars, sides);
1876  if (remaining_vars.empty())
1877  continue;
1878 
1879  if (!all_extra_hanging_dofs)
1880  {
1881  erase_nonhanging_vars(node, remaining_vars, vertices);
1882  if (remaining_vars.empty())
1883  continue;
1884  }
1885 
1886  erase_copied_vars(node, false, remaining_vars);
1887  if (remaining_vars.empty())
1888  continue;
1889 
1890  if (copy_this_elem)
1891  {
1892  std::vector<dof_id_type> side_dof_ids;
1893  std::vector<typename FFunctor::ValuePushType> values;
1894 
1895  for (auto var : remaining_vars)
1896  {
1897  f.eval_old_dofs(*elem, node_num, var, side_dof_ids, values);
1898  insert_ids(side_dof_ids, values, node->processor_id());
1899  }
1900 
1901  copied_nodes[node].second.insert(remaining_vars.begin(),
1902  remaining_vars.end());
1903  this->find_dofs_to_send(*node, *elem, node_num, remaining_vars);
1904  }
1905  else
1906  sides.emplace
1907  (node, std::make_tuple(elem, side, std::move(remaining_vars)));
1908  }
1909  }
1910 
1911  // Elements with elemental dofs might need those copied too.
1912  if (copy_this_elem)
1913  {
1914  std::vector<typename FFunctor::ValuePushType> U;
1915  std::vector<dof_id_type> dof_ids;
1916 
1917  for (auto v_num : this->projector.variables)
1918  {
1919  const Variable & var = system.variable(v_num);
1920  if (!var.active_on_subdomain(elem->subdomain_id()))
1921  continue;
1922  FEType fe_type = var.type();
1923 
1924  f.eval_old_dofs(*elem, fe_type, sys_num, v_num,
1925  dof_ids, U);
1926  action.insert(dof_ids, U);
1927 
1928  if (has_interior_nodes)
1929  {
1930  f.eval_old_dofs(*elem, n_nodes-1, v_num, dof_ids, U);
1931  action.insert(dof_ids, U);
1932  }
1933  }
1934  }
1935  }
1936 }
1937 
1938 
1939 template <typename FFunctor, typename GFunctor,
1940  typename FValue, typename ProjectionAction>
1943 {
1944  auto merge_multimaps = [](ves_multimap & self, const ves_multimap & other_mm)
1945  {
1946  for (const auto & pair : other_mm)
1947  {
1948  const Node * node = pair.first;
1949  auto remaining_vars = std::get<2>(pair.second);
1950 
1951  auto my_range = self.equal_range(node);
1952  for (const auto & v_ent : as_range(my_range))
1953  for (const unsigned int var_covered :
1954  std::get<2>(v_ent.second))
1955  remaining_vars.erase(var_covered);
1956 
1957  if (!remaining_vars.empty())
1958  self.emplace
1959  (node, std::make_tuple(std::get<0>(pair.second),
1960  std::get<1>(pair.second),
1961  std::move(remaining_vars)));
1962  }
1963  };
1964 
1965  merge_multimaps(vertices, other.vertices);
1966  merge_multimaps(edges, other.edges);
1967  merge_multimaps(sides, other.sides);
1968 
1969  std::sort(interiors.begin(), interiors.end());
1970  std::vector<const Elem *> other_interiors = other.interiors;
1971  std::sort(other_interiors.begin(), other_interiors.end());
1972  std::vector<const Elem *> merged_interiors;
1973  std::set_union(interiors.begin(), interiors.end(),
1974  other_interiors.begin(), other_interiors.end(),
1975  std::back_inserter(merged_interiors));
1976  interiors.swap(merged_interiors);
1977 
1978  SubFunctor::join(other);
1979 }
1980 
1981 namespace
1982 {
1983 template <typename Output, typename Input>
1984 typename std::enable_if<ScalarTraits<Input>::value, Output>::type
1985 raw_value(const Input & input, unsigned int /*index*/)
1986 {
1987  return input;
1988 }
1989 
1990 template <typename Output, template <typename> class WrapperClass, typename T>
1993  Output>::type
1994 raw_value(const WrapperClass<T> & input, unsigned int index)
1995 {
1996  return input.slice(index);
1997 }
1998 
1999 template <typename T>
2000 typename T::value_type
2001 grad_component(const T &, unsigned int);
2002 
2003 template <typename T>
2005 grad_component(const VectorValue<T> & grad, unsigned int component)
2006 {
2007  return grad(component);
2008 }
2009 
2010 template <typename T>
2012 grad_component(const TensorValue<T> & grad, unsigned int component)
2013 {
2014  libmesh_error_msg("This function should only be called for Hermites. "
2015  "I don't know how you got here");
2016  return grad(component, component);
2017 }
2018 
2019 
2020 }
2021 
2022 template <typename FFunctor, typename GFunctor,
2023  typename FValue, typename ProjectionAction>
2024 void GenericProjector<FFunctor, GFunctor, FValue, ProjectionAction>::ProjectVertices::operator()
2025  (const node_range & range)
2026 {
2027  LOG_SCOPE ("project_vertices","GenericProjector");
2028 
2029  const unsigned int sys_num = system.number();
2030 
2031  // Variables with extra hanging dofs can't safely use eval_at_node
2032  // in as many places as variables without can.
2033  std::vector<unsigned short> extra_hanging_dofs;
2034  for (auto v_num : this->projector.variables)
2035  {
2036  if (extra_hanging_dofs.size() <= v_num)
2037  extra_hanging_dofs.resize(v_num+1, false);
2038  extra_hanging_dofs[v_num] =
2040  }
2041 
2042  for (const auto & v_pair : range)
2043  {
2044  const Node & vertex = *v_pair.first;
2045  const Elem & elem = *std::get<0>(v_pair.second);
2046  const unsigned int n = std::get<1>(v_pair.second);
2047  const var_set & vertex_vars = std::get<2>(v_pair.second);
2048 
2049  context.pre_fe_reinit(system, &elem);
2050 
2051  this->find_dofs_to_send(vertex, elem, n, vertex_vars);
2052 
2053  // Look at all the variables we're supposed to interpolate from
2054  // this element on this vertex
2055  for (const auto & var : vertex_vars)
2056  {
2057  const Variable & variable = system.variable(var);
2058  const FEType & base_fe_type = variable.type();
2059  const unsigned int var_component =
2061 
2062  if (base_fe_type.family == SCALAR)
2063  continue;
2064 
2065  const FEContinuity cont = this->conts[var];
2066  const FEFieldType field_type = this->field_types[var];
2067 
2068  if (cont == DISCONTINUOUS)
2069  {
2070  libmesh_assert_equal_to(vertex.n_comp(sys_num, var), 0);
2071  }
2072  else if (cont == C_ZERO ||
2073  cont == SIDE_DISCONTINUOUS)
2074  {
2075  if (cont == SIDE_DISCONTINUOUS &&
2076  elem.dim() != 1)
2077  {
2078  libmesh_assert_equal_to(vertex.n_comp(sys_num, var), 0);
2079  continue;
2080  }
2081 
2082  const FValue val = f.eval_at_node
2083  (context, var_component, /*dim=*/ 0, // Don't care w/C0
2084  vertex, extra_hanging_dofs[var], system.time);
2085 
2086  if (field_type == TYPE_VECTOR)
2087  {
2088  libmesh_assert_equal_to(vertex.n_comp(sys_num, var), elem.dim());
2089 
2090  // We will have a number of nodal value DoFs equal to the elem dim
2091  for (auto i : make_range(elem.dim()))
2092  {
2093  const dof_id_type id = vertex.dof_number(sys_num, var, i);
2094 
2095  // Need this conversion so that this method
2096  // will compile for TYPE_SCALAR instantiations
2097  const auto insert_val =
2098  raw_value<typename ProjectionAction::InsertInput>(val, i);
2099 
2100  insert_id(id, insert_val, vertex.processor_id());
2101  }
2102  }
2103  else
2104  {
2105  // C_ZERO elements have a single nodal value DoF at
2106  // vertices. We can't assert n_comp==1 here,
2107  // because if this is a hanging node then it may have
2108  // more face/edge DoFs, but we don't need to deal with
2109  // those here.
2110 
2111  const dof_id_type id = vertex.dof_number(sys_num, var, 0);
2112  insert_id(id, val, vertex.processor_id());
2113  }
2114  }
2115  else if (cont == C_ONE)
2116  {
2117  libmesh_assert(vertex.n_comp(sys_num, var));
2118  const dof_id_type first_id = vertex.dof_number(sys_num, var, 0);
2119 
2120  // C_ONE elements have a single nodal value and dim
2121  // gradient values at vertices, as well as cross
2122  // gradients for HERMITE. We need to have an element in
2123  // hand to figure out dim and to have in case this
2124  // vertex is a new vertex.
2125  const int dim = elem.dim();
2126 #ifndef NDEBUG
2127  // For now all C1 elements at a vertex had better have
2128  // the same dimension. If anyone hits these asserts let
2129  // me know; we could probably support a mixed-dimension
2130  // mesh IFF the 2D elements were all parallel to xy and
2131  // the 1D elements all parallel to x.
2132  for (const auto e_id : (*this->projector.nodes_to_elem)[vertex.id()])
2133  {
2134  const Elem & e = system.get_mesh().elem_ref(e_id);
2135  libmesh_assert_equal_to(dim, e.dim());
2136  }
2137 #endif
2138 #ifdef LIBMESH_ENABLE_AMR
2139  bool is_old_vertex = true;
2140  if (elem.refinement_flag() == Elem::JUST_REFINED)
2141  {
2142  const int i_am_child =
2143  elem.parent()->which_child_am_i(&elem);
2144  is_old_vertex =
2145  elem.parent()->is_vertex_on_parent(i_am_child, n);
2146  }
2147 #else
2148  const bool is_old_vertex = false;
2149 #endif
2150 
2151  // The hermite element vertex shape functions are weird
2152  if (base_fe_type.family == HERMITE)
2153  {
2154  const FValue val =
2155  f.eval_at_node(context,
2156  var_component,
2157  dim,
2158  vertex,
2159  extra_hanging_dofs[var],
2160  system.time);
2161  insert_id(first_id, val, vertex.processor_id());
2162 
2163  typename GFunctor::FunctorValue grad =
2164  is_old_vertex ?
2165  g->eval_at_node(context,
2166  var_component,
2167  dim,
2168  vertex,
2169  extra_hanging_dofs[var],
2170  system.time) :
2171  g->eval_at_point(context,
2172  var_component,
2173  vertex,
2174  system.time,
2175  false);
2176  // x derivative. Use slice because grad may be a tensor type
2177  insert_id(first_id+1, grad.slice(0),
2178  vertex.processor_id());
2179 #if LIBMESH_DIM > 1
2180  if (dim > 1 && is_old_vertex && f.is_grid_projection())
2181  {
2182  for (int i = 1; i < dim; ++i)
2183  insert_id(first_id+i+1, grad.slice(i),
2184  vertex.processor_id());
2185 
2186  // We can directly copy everything else too
2187  std::vector<FValue> derivs;
2188  f.eval_mixed_derivatives
2189  (context, var_component, dim, vertex, derivs);
2190  for (auto i : index_range(derivs))
2191  insert_id(first_id+dim+1+i, derivs[i],
2192  vertex.processor_id());
2193  }
2194  else if (dim > 1)
2195  {
2196  // We'll finite difference mixed derivatives.
2197  // This delta_x used to be TOLERANCE*hmin, but
2198  // the factor of 10 improved the accuracy in
2199  // some unit test projections
2200  Real delta_x = TOLERANCE * 10 * elem.hmin();
2201 
2202  Point nxminus = elem.point(n),
2203  nxplus = elem.point(n);
2204  nxminus(0) -= delta_x;
2205  nxplus(0) += delta_x;
2206  typename GFunctor::FunctorValue gxminus =
2207  g->eval_at_point(context,
2208  var_component,
2209  nxminus,
2210  system.time,
2211  true);
2212  typename GFunctor::FunctorValue gxplus =
2213  g->eval_at_point(context,
2214  var_component,
2215  nxplus,
2216  system.time,
2217  true);
2218  // y derivative
2219  insert_id(first_id+2, grad.slice(1),
2220  vertex.processor_id());
2221  // xy derivative
2222  insert_id(first_id+3,
2223  (grad_component(gxplus, 1) - grad_component(gxminus, 1)) / 2. / delta_x,
2224  vertex.processor_id());
2225 
2226 #if LIBMESH_DIM > 2
2227  if (dim > 2)
2228  {
2229  // z derivative
2230  insert_id(first_id+4, grad.slice(2),
2231  vertex.processor_id());
2232  // xz derivative
2233  insert_id(first_id+5,
2234  (grad_component(gxplus, 2) - grad_component(gxminus, 2)) / 2. / delta_x,
2235  vertex.processor_id());
2236 
2237  // We need new points for yz
2238  Point nyminus = elem.point(n),
2239  nyplus = elem.point(n);
2240  nyminus(1) -= delta_x;
2241  nyplus(1) += delta_x;
2242  typename GFunctor::FunctorValue gyminus =
2243  g->eval_at_point(context,
2244  var_component,
2245  nyminus,
2246  system.time,
2247  true);
2248  typename GFunctor::FunctorValue gyplus =
2249  g->eval_at_point(context,
2250  var_component,
2251  nyplus,
2252  system.time,
2253  true);
2254  // yz derivative
2255  insert_id(first_id+6,
2256  (grad_component(gyplus, 2) - grad_component(gyminus, 2)) / 2. / delta_x,
2257  vertex.processor_id());
2258  // Getting a 2nd order xyz is more tedious
2259  Point nxmym = elem.point(n),
2260  nxmyp = elem.point(n),
2261  nxpym = elem.point(n),
2262  nxpyp = elem.point(n);
2263  nxmym(0) -= delta_x;
2264  nxmym(1) -= delta_x;
2265  nxmyp(0) -= delta_x;
2266  nxmyp(1) += delta_x;
2267  nxpym(0) += delta_x;
2268  nxpym(1) -= delta_x;
2269  nxpyp(0) += delta_x;
2270  nxpyp(1) += delta_x;
2271  typename GFunctor::FunctorValue gxmym =
2272  g->eval_at_point(context,
2273  var_component,
2274  nxmym,
2275  system.time,
2276  true);
2277  typename GFunctor::FunctorValue gxmyp =
2278  g->eval_at_point(context,
2279  var_component,
2280  nxmyp,
2281  system.time,
2282  true);
2283  typename GFunctor::FunctorValue gxpym =
2284  g->eval_at_point(context,
2285  var_component,
2286  nxpym,
2287  system.time,
2288  true);
2289  typename GFunctor::FunctorValue gxpyp =
2290  g->eval_at_point(context,
2291  var_component,
2292  nxpyp,
2293  system.time,
2294  true);
2295  FValue gxzplus = (grad_component(gxpyp, 2) - grad_component(gxmyp, 2))
2296  / 2. / delta_x;
2297  FValue gxzminus = (grad_component(gxpym, 2) - grad_component(gxmym, 2))
2298  / 2. / delta_x;
2299  // xyz derivative
2300  insert_id(first_id+7,
2301  (gxzplus - gxzminus) / 2. / delta_x,
2302  vertex.processor_id());
2303  }
2304 #endif // LIBMESH_DIM > 2
2305  }
2306 #endif // LIBMESH_DIM > 1
2307  }
2308  else
2309  {
2310  // Currently other C_ONE elements have a single nodal
2311  // value shape function and nodal gradient component
2312  // shape functions
2313  libmesh_assert_equal_to(
2315  base_fe_type,
2316  &elem,
2317  elem.get_node_index(&vertex),
2318  base_fe_type.p_refinement),
2319  (unsigned int)(1 + dim));
2320 
2321  const FValue val =
2322  f.eval_at_node(context, var_component, dim,
2323  vertex, extra_hanging_dofs[var],
2324  system.time);
2325  insert_id(first_id, val, vertex.processor_id());
2326  typename GFunctor::FunctorValue grad =
2327  is_old_vertex ?
2328  g->eval_at_node(context, var_component, dim,
2329  vertex, extra_hanging_dofs[var],
2330  system.time) :
2331  g->eval_at_point(context, var_component, vertex,
2332  system.time, false);
2333  for (int i=0; i!= dim; ++i)
2334  insert_id(first_id + i + 1, grad.slice(i),
2335  vertex.processor_id());
2336  }
2337  }
2338  else
2339  libmesh_error_msg("Unknown continuity " << cont);
2340  }
2341  }
2342 }
2343 
2344 
2345 template <typename FFunctor, typename GFunctor,
2346  typename FValue, typename ProjectionAction>
2348  (const node_range & range)
2349 {
2350  LOG_SCOPE ("project_edges","GenericProjector");
2351 
2352  const unsigned int sys_num = system.number();
2353 
2354  for (const auto & e_pair : range)
2355  {
2356  const Elem & elem = *std::get<0>(e_pair.second);
2357 
2358  // If this is an unchanged element then we already copied all
2359  // its dofs
2360 #ifdef LIBMESH_ENABLE_AMR
2361  if (f.is_grid_projection() &&
2362  (elem.refinement_flag() != Elem::JUST_REFINED &&
2366  continue;
2367 #endif // LIBMESH_ENABLE_AMR
2368 
2369  const Node & edge_node = *e_pair.first;
2370  const int dim = elem.dim();
2371  const var_set & edge_vars = std::get<2>(e_pair.second);
2372 
2373  const unsigned short edge_num = std::get<1>(e_pair.second);
2374  const unsigned short node_num = elem.n_vertices() + edge_num;
2375  context.edge = edge_num;
2376  context.pre_fe_reinit(system, &elem);
2377 
2378  this->find_dofs_to_send(edge_node, elem, node_num, edge_vars);
2379 
2380  // Look at all the variables we're supposed to interpolate from
2381  // this element on this edge
2382  for (const auto & var : edge_vars)
2383  {
2384  const Variable & variable = system.variable(var);
2385  const FEType & base_fe_type = variable.type();
2386  const unsigned int var_component =
2388 
2389  if (base_fe_type.family == SCALAR)
2390  continue;
2391 
2392  FEType fe_type = base_fe_type;
2393 
2394  // This may be a p refined element
2395  fe_type.order = fe_type.order + elem.p_level();
2396 
2397  // If this is a Lagrange element with DoFs on edges then by
2398  // convention we interpolate at the node rather than project
2399  // along the edge.
2400  if (fe_type.family == LAGRANGE || fe_type.family == LAGRANGE_VEC)
2401  {
2402  if (fe_type.order > 1)
2403  {
2404  const processor_id_type pid =
2405  edge_node.processor_id();
2406  FValue fval = f.eval_at_point
2407  (context, var_component, edge_node, system.time,
2408  false);
2409  if (fe_type.family == LAGRANGE_VEC)
2410  {
2411  // We will have a number of nodal value DoFs equal to the elem dim
2412  for (auto i : make_range(elem.dim()))
2413  {
2414  const dof_id_type dof_id =
2415  edge_node.dof_number(sys_num, var, i);
2416 
2417  // Need this conversion so that this method
2418  // will compile for TYPE_SCALAR instantiations
2419  const auto insert_val =
2420  raw_value<typename ProjectionAction::InsertInput>(fval, i);
2421 
2422  insert_id(dof_id, insert_val, pid);
2423  }
2424  }
2425  else // We are LAGRANGE
2426  {
2427  const dof_id_type dof_id =
2428  edge_node.dof_number(sys_num, var, 0);
2429  insert_id(dof_id, fval, pid);
2430  }
2431  }
2432  continue;
2433  }
2434 
2435 #ifdef LIBMESH_ENABLE_AMR
2436  // If this is a low order monomial element which has merely
2437  // been h refined then we already copied all its dofs
2438  if (fe_type.family == MONOMIAL &&
2439  fe_type.order == CONSTANT &&
2442  continue;
2443 #endif // LIBMESH_ENABLE_AMR
2444 
2445  // FIXME: Need to generalize this to vector-valued elements. [PB]
2447  context.get_element_fe( var, fe, dim );
2448  FEGenericBase<typename FFunctor::RealType> * edge_fe = nullptr;
2449  context.get_edge_fe( var, edge_fe );
2450 
2451  // If we're JUST_COARSENED we'll need a custom
2452  // evaluation, not just the standard edge FE
2454 #ifdef LIBMESH_ENABLE_AMR
2456  *fe :
2457 #endif
2458  *edge_fe;
2459 
2460 #ifdef LIBMESH_ENABLE_AMR
2461  if (elem.refinement_flag() == Elem::JUST_COARSENED)
2462  {
2463  std::vector<Point> fine_points;
2464 
2465  std::unique_ptr<FEGenericBase<typename FFunctor::RealType>> fine_fe
2467 
2468  std::unique_ptr<QBase> qrule
2469  (base_fe_type.default_quadrature_rule(1));
2470  fine_fe->attach_quadrature_rule(qrule.get());
2471 
2472  const std::vector<Point> & child_xyz =
2473  fine_fe->get_xyz();
2474 
2475  for (unsigned int c = 0, nc = elem.n_children();
2476  c != nc; ++c)
2477  {
2478  if (!elem.is_child_on_edge(c, context.edge))
2479  continue;
2480 
2481  fine_fe->edge_reinit(elem.child_ptr(c), context.edge);
2482  fine_points.insert(fine_points.end(),
2483  child_xyz.begin(),
2484  child_xyz.end());
2485  }
2486 
2487  std::vector<Point> fine_qp;
2488  FEMap::inverse_map (dim, &elem, fine_points, fine_qp);
2489 
2490  context.elem_fe_reinit(&fine_qp);
2491  }
2492  else
2493 #endif // LIBMESH_ENABLE_AMR
2494  context.edge_fe_reinit();
2495 
2496  const std::vector<dof_id_type> & dof_indices =
2497  context.get_dof_indices(var);
2498 
2499  std::vector<unsigned int> edge_dofs;
2500  FEInterface::dofs_on_edge(&elem, dim, base_fe_type,
2501  context.edge, edge_dofs);
2502 
2503  this->construct_projection
2504  (dof_indices, edge_dofs, var_component,
2505  &edge_node, proj_fe);
2506  }
2507  }
2508 }
2509 
2510 
2511 template <typename FFunctor, typename GFunctor,
2512  typename FValue, typename ProjectionAction>
2514  (const node_range & range)
2515 {
2516  LOG_SCOPE ("project_sides","GenericProjector");
2517 
2518  const unsigned int sys_num = system.number();
2519 
2520  for (const auto & s_pair : range)
2521  {
2522  const Elem & elem = *std::get<0>(s_pair.second);
2523 
2524  // If this is an unchanged element then we already copied all
2525  // its dofs
2526 #ifdef LIBMESH_ENABLE_AMR
2527  if (f.is_grid_projection() &&
2528  (elem.refinement_flag() != Elem::JUST_REFINED &&
2532  continue;
2533 #endif // LIBMESH_ENABLE_AMR
2534 
2535  const Node & side_node = *s_pair.first;
2536  const int dim = elem.dim();
2537  const var_set & side_vars = std::get<2>(s_pair.second);
2538 
2539  const unsigned int side_num = std::get<1>(s_pair.second);
2540  unsigned short node_num = elem.n_vertices()+side_num;
2541  // In 3D only some sides may have nodes
2542  if (dim == 3)
2543  for (auto n : make_range(elem.n_nodes()))
2544  {
2545  if (!elem.is_face(n))
2546  continue;
2547 
2548  if (elem.is_node_on_side(n, side_num))
2549  {
2550  node_num = n;
2551  break;
2552  }
2553  }
2554 
2555  context.side = side_num;
2556  context.pre_fe_reinit(system, &elem);
2557 
2558  this->find_dofs_to_send(side_node, elem, node_num, side_vars);
2559 
2560  // Look at all the variables we're supposed to interpolate from
2561  // this element on this side
2562  for (const auto & var : side_vars)
2563  {
2564  const Variable & variable = system.variable(var);
2565  const FEType & base_fe_type = variable.type();
2566  const unsigned int var_component =
2568 
2569  if (base_fe_type.family == SCALAR)
2570  continue;
2571 
2572  FEType fe_type = base_fe_type;
2573  const bool add_p_level = base_fe_type.p_refinement;
2574 
2575  // This may be a p refined element
2576  fe_type.order = fe_type.order + add_p_level*elem.p_level();
2577 
2578  // If this is a Lagrange element with DoFs on sides then by
2579  // convention we interpolate at the node rather than project
2580  // along the side.
2581  if (fe_type.family == LAGRANGE || fe_type.family == LAGRANGE_VEC)
2582  {
2583  // If order==1 then we only have DoFs on vertices, so
2584  // skip all this.
2585  // If order>1 ... we might still have something tricky
2586  // like order==2 PRISM20, where some side nodes have
2587  // DoFs but others don't. Gotta check.
2588  if (fe_type.order > 1 &&
2589  side_node.n_comp(sys_num, var))
2590  {
2591  const processor_id_type pid =
2592  side_node.processor_id();
2593  FValue fval = f.eval_at_point
2594  (context, var_component, side_node, system.time,
2595  false);
2596 
2597  if (fe_type.family == LAGRANGE_VEC)
2598  {
2599  // We will have a number of nodal value DoFs equal to the elem dim
2600  for (auto i : make_range(elem.dim()))
2601  {
2602  const dof_id_type dof_id = side_node.dof_number(sys_num, var, i);
2603 
2604  // Need this conversion so that this method
2605  // will compile for TYPE_SCALAR instantiations
2606  const auto insert_val =
2607  raw_value<typename ProjectionAction::InsertInput>(fval, i);
2608 
2609  insert_id(dof_id, insert_val, pid);
2610  }
2611  }
2612  else // We are LAGRANGE
2613  {
2614  const dof_id_type dof_id =
2615  side_node.dof_number(sys_num, var, 0);
2616  insert_id(dof_id, fval, pid);
2617  }
2618  }
2619  continue;
2620  }
2621 
2622 #ifdef LIBMESH_ENABLE_AMR
2623  // If this is a low order monomial element which has merely
2624  // been h refined then we already copied all its dofs
2625  if (fe_type.family == MONOMIAL &&
2626  fe_type.order == CONSTANT &&
2629  continue;
2630 #endif // LIBMESH_ENABLE_AMR
2631 
2632  // FIXME: Need to generalize this to vector-valued elements. [PB]
2634  context.get_element_fe( var, fe, dim );
2635  FEGenericBase<typename FFunctor::RealType> * side_fe = nullptr;
2636  context.get_side_fe( var, side_fe );
2637 
2638  // If we're JUST_COARSENED we'll need a custom
2639  // evaluation, not just the standard side FE
2641 #ifdef LIBMESH_ENABLE_AMR
2643  *fe :
2644 #endif
2645  *side_fe;
2646 
2647 #ifdef LIBMESH_ENABLE_AMR
2648  if (elem.refinement_flag() == Elem::JUST_COARSENED)
2649  {
2650  std::vector<Point> fine_points;
2651 
2652  std::unique_ptr<FEGenericBase<typename FFunctor::RealType>> fine_fe
2654 
2655  std::unique_ptr<QBase> qrule
2656  (base_fe_type.default_quadrature_rule(1));
2657  fine_fe->attach_quadrature_rule(qrule.get());
2658 
2659  const std::vector<Point> & child_xyz =
2660  fine_fe->get_xyz();
2661 
2662  for (unsigned int c = 0, nc = elem.n_children();
2663  c != nc; ++c)
2664  {
2665  if (!elem.is_child_on_side(c, context.side))
2666  continue;
2667 
2668  fine_fe->reinit(elem.child_ptr(c), context.side);
2669  fine_points.insert(fine_points.end(),
2670  child_xyz.begin(),
2671  child_xyz.end());
2672  }
2673 
2674  std::vector<Point> fine_qp;
2675  FEMap::inverse_map (dim, &elem, fine_points, fine_qp);
2676 
2677  context.elem_fe_reinit(&fine_qp);
2678  }
2679  else
2680 #endif // LIBMESH_ENABLE_AMR
2681  context.side_fe_reinit();
2682 
2683  const std::vector<dof_id_type> & dof_indices =
2684  context.get_dof_indices(var);
2685 
2686  std::vector<unsigned int> side_dofs;
2687  FEInterface::dofs_on_side(&elem, dim, base_fe_type,
2688  context.side, side_dofs, add_p_level);
2689 
2690  this->construct_projection
2691  (dof_indices, side_dofs, var_component,
2692  &side_node, proj_fe);
2693  }
2694  }
2695 }
2696 
2697 
2698 template <typename FFunctor, typename GFunctor,
2699  typename FValue, typename ProjectionAction>
2701  (const interior_range & range)
2702 {
2703  LOG_SCOPE ("project_interiors","GenericProjector");
2704 
2705  const unsigned int sys_num = system.number();
2706 
2707  // Iterate over all dof-bearing element interiors in the range
2708  for (const auto & elem : range)
2709  {
2710  unsigned char dim = cast_int<unsigned char>(elem->dim());
2711 
2712  context.pre_fe_reinit(system, elem);
2713 
2714  // Loop over all the variables we've been requested to project, to
2715  // do the projection
2716  for (const auto & var : this->projector.variables)
2717  {
2718  const Variable & variable = system.variable(var);
2719 
2720  if (!variable.active_on_subdomain(elem->subdomain_id()))
2721  continue;
2722 
2723  const FEType & base_fe_type = variable.type();
2724 
2725  if (base_fe_type.family == SCALAR)
2726  continue;
2727 
2729  context.get_element_fe( var, fe, dim );
2730 
2731  FEType fe_type = base_fe_type;
2732  const bool add_p_level = fe_type.p_refinement;
2733 
2734  // This may be a p refined element
2735  fe_type.order = fe_type.order + add_p_level * elem->p_level();
2736 
2737  const unsigned int var_component =
2739 
2740  // If this is a Lagrange element with interior DoFs then by
2741  // convention we interpolate at the interior node rather
2742  // than project along the interior.
2743  if (fe_type.family == LAGRANGE || fe_type.family == LAGRANGE_VEC)
2744  {
2745  if (fe_type.order > 1)
2746  {
2747  const unsigned int first_interior_node =
2748  (elem->n_vertices() +
2749  ((elem->dim() > 2) * elem->n_edges()) +
2750  ((elem->dim() > 1) * elem->n_sides()));
2751  const unsigned int n_nodes = elem->n_nodes();
2752 
2753  // < vs != is important here for HEX20, QUAD8!
2754  for (unsigned int n = first_interior_node; n < n_nodes; ++n)
2755  {
2756  const Node & interior_node = elem->node_ref(n);
2757 
2758  FValue fval = f.eval_at_point
2759  (context, var_component, interior_node,
2760  system.time, false);
2761  const processor_id_type pid =
2762  interior_node.processor_id();
2763 
2764  if (fe_type.family == LAGRANGE_VEC)
2765  {
2766  // We will have a number of nodal value DoFs equal to the elem dim
2767  for (unsigned int i = 0; i < elem->dim(); ++i)
2768  {
2769  const dof_id_type dof_id = interior_node.dof_number(sys_num, var, i);
2770 
2771  // Need this conversion so that this method
2772  // will compile for TYPE_SCALAR instantiations
2773  const auto insert_val =
2774  raw_value<typename ProjectionAction::InsertInput>(fval, i);
2775 
2776  insert_id(dof_id, insert_val, pid);
2777  }
2778  }
2779  else // We are LAGRANGE
2780  {
2781  const dof_id_type dof_id =
2782  interior_node.dof_number(sys_num, var, 0);
2783  insert_id(dof_id, fval, pid);
2784  }
2785  }
2786  }
2787  continue;
2788  }
2789 
2790 #ifdef LIBMESH_ENABLE_AMR
2791  if (elem->refinement_flag() == Elem::JUST_COARSENED)
2792  {
2793  std::vector<Point> fine_points;
2794 
2795  std::unique_ptr<FEGenericBase<typename FFunctor::RealType>> fine_fe
2797 
2798  std::unique_ptr<QBase> qrule
2799  (base_fe_type.default_quadrature_rule(dim));
2800  fine_fe->attach_quadrature_rule(qrule.get());
2801 
2802  const std::vector<Point> & child_xyz =
2803  fine_fe->get_xyz();
2804 
2805  for (auto & child : elem->child_ref_range())
2806  {
2807  fine_fe->reinit(&child);
2808  fine_points.insert(fine_points.end(),
2809  child_xyz.begin(),
2810  child_xyz.end());
2811  }
2812 
2813  std::vector<Point> fine_qp;
2814  FEMap::inverse_map (dim, elem, fine_points, fine_qp);
2815 
2816  context.elem_fe_reinit(&fine_qp);
2817  }
2818  else
2819 #endif // LIBMESH_ENABLE_AMR
2820  context.elem_fe_reinit();
2821 
2822  const std::vector<dof_id_type> & dof_indices =
2823  context.get_dof_indices(var);
2824 
2825  const unsigned int n_dofs =
2826  cast_int<unsigned int>(dof_indices.size());
2827 
2828  std::vector<unsigned int> all_dofs(n_dofs);
2829  std::iota(all_dofs.begin(), all_dofs.end(), 0);
2830 
2831  this->construct_projection
2832  (dof_indices, all_dofs, var_component,
2833  nullptr, *fe);
2834  } // end variables loop
2835  } // end elements loop
2836 }
2837 
2838 
2839 
2840 template <typename FFunctor, typename GFunctor,
2841  typename FValue, typename ProjectionAction>
2842 void
2843 GenericProjector<FFunctor, GFunctor, FValue,
2844  ProjectionAction>::SubFunctor::find_dofs_to_send
2845  (const Node & node, const Elem & elem, unsigned short node_num, const var_set & vars)
2846 {
2847  libmesh_assert (&node == elem.node_ptr(node_num));
2848 
2849  // Any ghosted node in our range might have an owner who needs our
2850  // data
2851  const processor_id_type owner = node.processor_id();
2852  if (owner != system.processor_id())
2853  {
2854  const MeshBase & mesh = system.get_mesh();
2855  const DofMap & dof_map = system.get_dof_map();
2856 
2857  // But let's check and see if we can be certain the owner can
2858  // compute any or all of its own dof coefficients on that node.
2859  std::vector<dof_id_type> node_dof_ids, patch_dof_ids;
2860  for (const auto & var : vars)
2861  {
2862  const Variable & variable = system.variable(var);
2863 
2864  if (!variable.active_on_subdomain(elem.subdomain_id()))
2865  continue;
2866 
2867  dof_map.dof_indices(elem, node_num, node_dof_ids, var);
2868  }
2869  libmesh_assert(std::is_sorted(node_dof_ids.begin(),
2870  node_dof_ids.end()));
2871  const std::vector<dof_id_type> & patch =
2872  (*this->projector.nodes_to_elem)[node.id()];
2873  for (const auto & elem_id : patch)
2874  {
2875  const Elem & patch_elem = mesh.elem_ref(elem_id);
2876  if (!patch_elem.active() || owner != patch_elem.processor_id())
2877  continue;
2878  dof_map.dof_indices(&patch_elem, patch_dof_ids);
2879  std::sort(patch_dof_ids.begin(), patch_dof_ids.end());
2880 
2881  // Remove any node_dof_ids that we see can be calculated on
2882  // this element
2883  std::vector<dof_id_type> diff_ids(node_dof_ids.size());
2884  auto it = std::set_difference(node_dof_ids.begin(), node_dof_ids.end(),
2885  patch_dof_ids.begin(), patch_dof_ids.end(), diff_ids.begin());
2886  diff_ids.resize(it-diff_ids.begin());
2887  node_dof_ids.swap(diff_ids);
2888  if (node_dof_ids.empty())
2889  break;
2890  }
2891 
2892  // Give ids_to_push default invalid pid: not yet computed
2893  for (auto id : node_dof_ids)
2894  new_ids_to_push[id].second = DofObject::invalid_processor_id;
2895  }
2896 }
2897 
2898 
2899 
2900 template <typename FFunctor, typename GFunctor,
2901  typename FValue, typename ProjectionAction>
2902 template <typename Value>
2903 void
2904 GenericProjector<FFunctor, GFunctor, FValue,
2905  ProjectionAction>::send_and_insert_dof_values
2906  (std::unordered_map<dof_id_type, std::pair<Value, processor_id_type>> & ids_to_push,
2907  ProjectionAction & action) const
2908 {
2909  LOG_SCOPE ("send_and_insert_dof_values", "GenericProjector");
2910 
2911  // See if we calculated any ids that need to be pushed; get them
2912  // ready to push.
2913  std::unordered_map<processor_id_type, std::vector<dof_id_type>>
2914  pushed_dof_ids, received_dof_ids;
2915  std::unordered_map<processor_id_type, std::vector<typename TypeToSend<Value>::type>>
2916  pushed_dof_values, received_dof_values;
2917  for (auto & id_val_pid : ids_to_push)
2918  {
2919  processor_id_type pid = id_val_pid.second.second;
2921  {
2922  pushed_dof_ids[pid].push_back(id_val_pid.first);
2923  pushed_dof_values[pid].push_back(convert_to_send(id_val_pid.second.first));
2924  }
2925  }
2926 
2927  // If and when we get ids pushed to us, act on them if we have
2928  // corresponding values or save them if not
2929  auto ids_action_functor =
2930  [&action, &received_dof_ids, &received_dof_values]
2931  (processor_id_type pid,
2932  const std::vector<dof_id_type> & data)
2933  {
2934  auto iter = received_dof_values.find(pid);
2935  if (iter == received_dof_values.end())
2936  {
2937  libmesh_assert(received_dof_ids.find(pid) ==
2938  received_dof_ids.end());
2939  received_dof_ids[pid] = data;
2940  }
2941  else
2942  {
2943  auto & vals = iter->second;
2944  std::size_t vals_size = vals.size();
2945  libmesh_assert_equal_to(vals_size, data.size());
2946  for (std::size_t i=0; i != vals_size; ++i)
2947  {
2948  Value val;
2949  convert_from_receive(vals[i], val);
2950  action.insert(data[i], val);
2951  }
2952  received_dof_values.erase(iter);
2953  }
2954  };
2955 
2956  // If and when we get values pushed to us, act on them if we have
2957  // corresponding ids or save them if not
2958  auto values_action_functor =
2959  [&action, &received_dof_ids, &received_dof_values]
2960  (processor_id_type pid,
2961  const std::vector<typename TypeToSend<Value>::type> & data)
2962  {
2963  auto iter = received_dof_ids.find(pid);
2964  if (iter == received_dof_ids.end())
2965  {
2966  // We get no more than 1 values vector from anywhere
2967  libmesh_assert(received_dof_values.find(pid) ==
2968  received_dof_values.end());
2969  received_dof_values[pid] = data;
2970  }
2971  else
2972  {
2973  auto & ids = iter->second;
2974  std::size_t ids_size = ids.size();
2975  libmesh_assert_equal_to(ids_size, data.size());
2976  for (std::size_t i=0; i != ids_size; ++i)
2977  {
2978  Value val;
2979  convert_from_receive(data[i], val);
2980  action.insert(ids[i], val);
2981  }
2982  received_dof_ids.erase(iter);
2983  }
2984  };
2985 
2986  Parallel::push_parallel_vector_data
2987  (system.comm(), pushed_dof_ids, ids_action_functor);
2988 
2989  Parallel::push_parallel_vector_data
2990  (system.comm(), pushed_dof_values, values_action_functor);
2991 
2992  // At this point we shouldn't have any unprocessed data left
2993  libmesh_assert(received_dof_ids.empty());
2994  libmesh_assert(received_dof_values.empty());
2995 
2996 }
2997 
2998 
2999 
3000 template <typename FFunctor, typename GFunctor,
3001  typename FValue, typename ProjectionAction>
3002 void
3004  (const std::vector<dof_id_type> & dof_indices_var,
3005  const std::vector<unsigned int> & involved_dofs,
3006  unsigned int var_component,
3007  const Node * node,
3009 {
3010  const auto & JxW = fe.get_JxW();
3011  const auto & phi = fe.get_phi();
3012  const std::vector<std::vector<typename FEGenericBase<typename FFunctor::RealType>::OutputGradient>> * dphi = nullptr;
3013  const std::vector<Point> & xyz_values = fe.get_xyz();
3014  const FEContinuity cont = fe.get_continuity();
3015  const std::unordered_map<dof_id_type, typename FFunctor::ValuePushType> & ids_to_save =
3016  this->projector.ids_to_save;
3017 
3018  if (cont == C_ONE)
3019  dphi = &(fe.get_dphi());
3020 
3021  const unsigned int n_involved_dofs =
3022  cast_int<unsigned int>(involved_dofs.size());
3023 
3024  std::vector<dof_id_type> free_dof_ids;
3025  DenseVector<typename FFunctor::ValuePushType> Uinvolved(n_involved_dofs);
3026  std::vector<char> dof_is_fixed(n_involved_dofs, false); // bools
3027 
3028  for (auto i : make_range(n_involved_dofs))
3029  {
3030  const dof_id_type id = dof_indices_var[involved_dofs[i]];
3031  auto iter = ids_to_save.find(id);
3032  if (iter == ids_to_save.end())
3033  free_dof_ids.push_back(id);
3034  else
3035  {
3036  dof_is_fixed[i] = true;
3037  Uinvolved(i) = iter->second;
3038  }
3039  }
3040 
3041  const unsigned int free_dofs = free_dof_ids.size();
3042 
3043  // There may be nothing to project
3044  if (!free_dofs)
3045  return;
3046 
3047  // The element matrix and RHS for projections.
3048  // Note that Ke is always real-valued, whereas
3049  // Fe may be complex valued if complex number
3050  // support is enabled
3051  DenseMatrix<Real> Ke(free_dofs, free_dofs);
3053  // The new degree of freedom coefficients to solve for
3055 
3056  const unsigned int n_qp =
3057  cast_int<unsigned int>(xyz_values.size());
3058 
3059  // Loop over the quadrature points
3060  for (unsigned int qp=0; qp<n_qp; qp++)
3061  {
3062  // solution at the quadrature point
3063  FValue fineval = f.eval_at_point(context,
3064  var_component,
3065  xyz_values[qp],
3066  system.time,
3067  false);
3068  // solution grad at the quadrature point
3069  typename GFunctor::FunctorValue finegrad;
3070  if (cont == C_ONE)
3071  finegrad = g->eval_at_point(context,
3072  var_component,
3073  xyz_values[qp],
3074  system.time,
3075  false);
3076 
3077  // Form edge projection matrix
3078  for (unsigned int sidei=0, freei=0;
3079  sidei != n_involved_dofs; ++sidei)
3080  {
3081  unsigned int i = involved_dofs[sidei];
3082  // fixed DoFs aren't test functions
3083  if (dof_is_fixed[sidei])
3084  continue;
3085  for (unsigned int sidej=0, freej=0;
3086  sidej != n_involved_dofs; ++sidej)
3087  {
3088  unsigned int j = involved_dofs[sidej];
3089  if (dof_is_fixed[sidej])
3090  Fe(freei) -= phi[i][qp] * phi[j][qp] *
3091  JxW[qp] * Uinvolved(sidej);
3092  else
3093  Ke(freei,freej) += phi[i][qp] *
3094  phi[j][qp] * JxW[qp];
3095  if (cont == C_ONE)
3096  {
3097  if (dof_is_fixed[sidej])
3098  Fe(freei) -= ( TensorTools::inner_product((*dphi)[i][qp],
3099  (*dphi)[j][qp]) ) *
3100  JxW[qp] * Uinvolved(sidej);
3101  else
3102  Ke(freei,freej) += ( TensorTools::inner_product((*dphi)[i][qp],
3103  (*dphi)[j][qp]) )
3104  * JxW[qp];
3105  }
3106  if (!dof_is_fixed[sidej])
3107  freej++;
3108  }
3109  Fe(freei) += phi[i][qp] * fineval * JxW[qp];
3110  if (cont == C_ONE)
3111  Fe(freei) += (TensorTools::inner_product(finegrad,
3112  (*dphi)[i][qp]) ) *
3113  JxW[qp];
3114  freei++;
3115  }
3116  }
3117 
3118  Ke.cholesky_solve(Fe, Ufree);
3119 
3120  // Transfer new edge solutions to element
3121  const processor_id_type pid = node ?
3123  insert_ids(free_dof_ids, Ufree.get_values(), pid);
3124 }
3125 
3126 
3127 } // namespace libMesh
3128 
3129 #endif // GENERIC_PROJECTOR_H
The GenericProjector class implements the core of other projection operations, using two input functo...
static unsigned int n_dofs_per_elem(const unsigned int dim, const FEType &fe_t, const ElemType t)
Definition: fe_interface.C:531
std::unordered_map< dof_id_type, std::pair< typename FFunctor::ValuePushType, processor_id_type > > new_ids_to_push
class FEType hides (possibly multiple) FEFamily and approximation orders, thereby enabling specialize...
Definition: fe_type.h:196
void send_and_insert_dof_values(std::unordered_map< dof_id_type, std::pair< Value, processor_id_type >> &ids_to_push, ProjectionAction &action) const
virtual bool is_vertex_on_parent(unsigned int c, unsigned int n) const
Definition: elem.C:3532
FEFamily family
The type of finite element.
Definition: fe_type.h:228
void check_old_context(const FEMContext &c)
Real time
For time-dependent problems, this is the time t at the beginning of the current timestep.
Definition: system.h:1677
OldSolutionBase(const libMesh::System &sys_in, const std::vector< unsigned int > *vars)
RefinementState refinement_flag() const
Definition: elem.h:3224
bool p_refinement
Whether or not the finite elements for this type increase their p refinement level on geometric eleme...
Definition: fe_type.h:292
dof_id_type dof_number(const unsigned int s, const unsigned int var, const unsigned int comp) const
Definition: dof_object.h:1008
const Elem * parent() const
Definition: elem.h:3044
unsigned int variable_scalar_number(std::string_view var, unsigned int component) const
Definition: system.h:2474
void eval_old_dofs(const Elem &elem, const FEType &fe_type, unsigned int sys_num, unsigned int var_num, std::vector< dof_id_type > &indices, std::vector< DofValueType > &values)
static constexpr processor_id_type invalid_processor_id
An invalid processor_id to distinguish DoFs that have not been assigned to a processor.
Definition: dof_object.h:484
OldSolutionValue(const OldSolutionValue &in)
A Node is like a Point, but with more information.
Definition: node.h:52
const Variable & variable(unsigned int var) const
Return a constant reference to Variable var.
Definition: system.C:2704
bool check_old_context(const FEMContext &c, const Point &p)
const NumericVector< Number > & old_solution
void eval_old_dofs(const Elem &elem, unsigned int node_num, unsigned int var_num, std::vector< dof_id_type > &indices, std::vector< DofValueType > &values)
unsigned int get_node_index(const Node *node_ptr) const
Definition: elem.h:2551
unsigned int n_comp(const unsigned int s, const unsigned int var) const
Definition: dof_object.h:978
void operator()(const node_range &range)
void construct_projection(const std::vector< dof_id_type > &dof_indices_var, const std::vector< unsigned int > &involved_dofs, unsigned int var_component, const Node *node, const FEGenericBase< typename FFunctor::RealType > &fe)
std::unique_ptr< GFunctor > master_g_deepcopy
Needed for C1 type elements only.
virtual bool is_face(const unsigned int i) const =0
NodesToElemMap * nodes_to_elem
void init_context(FEMContext &c)
void dof_indices(const Elem *const elem, std::vector< dof_id_type > &di) const
Definition: dof_map.C:2201
static constexpr Real TOLERANCE
const Elem & get_elem() const
Accessor for current Elem object.
Definition: fem_context.h:908
unsigned int dim
static Point inverse_map(const unsigned int dim, const Elem *elem, const Point &p, const Real tolerance=TOLERANCE, const bool secure=true, const bool extra_checks=true)
Definition: fe_map.C:1512
ProjectSides(ProjectSides &p_s, Threads::split)
void libmesh_merge_move(T &target, T &source)
The OldSolutionBase input functor abstract base class is the root of the OldSolutionValue and OldSolu...
virtual bool is_child_on_side(const unsigned int c, const unsigned int s) const =0
void eval_mixed_derivatives(const FEMContext &, unsigned int, unsigned int, const Node &, std::vector< Output > &)
void eval_mixed_derivatives(const FEMContext &, unsigned int, unsigned int, const Node &, std::vector< Output > &)
Dummy "splitting object" used to distinguish splitting constructors from copy constructors.
Definition: threads_none.h:63
RefinementState p_refinement_flag() const
Definition: elem.h:3240
static void dofs_on_edge(const Elem *const elem, const unsigned int dim, const FEType &fe_t, unsigned int e, std::vector< unsigned int > &di, const bool add_p_level=true)
Fills the vector di with the local degree of freedom indices associated with edge e of element elem A...
Definition: fe_interface.C:612
std::vector< T > & get_values()
Definition: dense_vector.h:278
FEMFunctionWrapper(const FEMFunctionWrapper< Output > &fw)
std::unordered_multimap< const Node *, std::tuple< const Elem *, unsigned short, var_set > > ves_multimap
ProjectionAction & master_action
This is the base class from which all geometric element types are derived.
Definition: elem.h:94
MeshBase & mesh
FEMFunctionWrapper(const FEMFunctionBase< Output > &f)
static FEFieldType field_type(const FEType &fe_type)
RefinementState
Enumeration of possible element refinement states.
Definition: elem.h:1443
virtual bool is_node_on_side(const unsigned int n, const unsigned int s) const =0
const Parallel::Communicator & comm() const
virtual unsigned int n_children() const =0
unsigned int p_level() const
Definition: elem.h:3122
OrderWrapper order
The approximation order of the element (at 0 p-refinement level).
Definition: fe_type.h:203
void set_algebraic_type(const AlgebraicType atype)
Setting which determines whether to initialize algebraic structures (elem_*) on each element and set ...
Definition: fem_context.h:986
void build_nodes_to_elem_map(const MeshBase &mesh, std::vector< std::vector< dof_id_type >> &nodes_to_elem_map)
After calling this function the input vector nodes_to_elem_map will contain the node to element conne...
Definition: mesh_tools.C:456
ProjectVertices(ProjectVertices &p_v, Threads::split)
This class defines a vector in LIBMESH_DIM dimensional Real or Complex space.
The StoredRange class defines a contiguous, divisible set of objects.
Definition: stored_range.h:54
The libMesh namespace provides an interface to certain functionality in the library.
The OldSolutionValue input functor class can be used with GenericProjector to read values from a solu...
void eval_old_dofs(const Elem &, const FEType &, unsigned int, unsigned int, std::vector< dof_id_type > &, std::vector< Output > &)
virtual Real hmax() const
Definition: elem.C:707
NumericVector< Val > & target_vector
VectorSetAction(NumericVector< Val > &target_vec)
const MeshBase & get_mesh() const
Definition: system.h:2401
void insert_id(dof_id_type id, const InsertInput &val, processor_id_type pid)
void operator()(const interior_range &range)
void join(const SubFunctor &other)
OldSolutionBase(const OldSolutionBase &in)
virtual void request_phi() const =0
request phi calculations
uint8_t processor_id_type
Definition: id_types.h:104
This is the MeshBase class.
Definition: mesh_base.h:80
SimpleRange< ChildRefIter > child_ref_range()
Returns a range with all children of a parent element, usable in range-based for loops.
Definition: elem.h:2352
virtual FEContinuity get_continuity() const =0
FEType get_fe_type() const
Definition: fe_abstract.h:520
std::vector< const Elem * > interiors
static void get_shape_outputs(FEAbstract &fe)
This class handles the numbering of degrees of freedom on a mesh.
Definition: dof_map.h:179
std::unique_ptr< QBase > default_quadrature_rule(const unsigned int dim, const int extraorder=0) const
Definition: fe_type.C:34
const std::vector< std::vector< OutputGradient > > & get_dphi() const
Definition: fe_base.h:230
void eval_old_dofs(const Elem &, unsigned int, unsigned int, std::vector< dof_id_type > &, std::vector< Output > &)
const dof_id_type n_nodes
Definition: tecplot_io.C:67
unsigned int number() const
Definition: system.h:2393
std::unordered_map< dof_id_type, typename FFunctor::ValuePushType > ids_to_save
This class defines the notion of a variable in the system.
Definition: variable.h:50
dof_id_type id() const
Definition: dof_object.h:819
dof_id_type numeric_index_type
Definition: id_types.h:99
OldSolutionValue(const libMesh::System &sys_in, const NumericVector< Number > &old_sol, const std::vector< unsigned int > *vars)
virtual Real hmin() const
Definition: elem.C:683
static constexpr dof_id_type invalid_id
An invalid id to distinguish an uninitialized DofObject.
Definition: dof_object.h:473
void project(const ConstElemRange &range)
Function definitions.
static bool extra_hanging_dofs(const FEType &fe_t)
TensorTools::MakeBaseNumber< Output >::type DofValueType
virtual unsigned int n_nodes() const =0
void insert_ids(const std::vector< dof_id_type > &ids, const std::vector< InsertInput > &vals, processor_id_type pid)
void operator()(const ConstElemRange &range)
unsigned int n_vars(const unsigned int s, const unsigned int vg) const
Definition: dof_object.h:943
StoredRange< std::vector< node_projection >::const_iterator, node_projection > node_range
Manages consistently variables, degrees of freedom, and coefficient vectors.
Definition: system.h:98
unsigned int n_systems() const
Definition: dof_object.h:913
std::unordered_map< dof_id_type, std::vector< dof_id_type > > NodesToElemMap
Convenience typedef for the Node-to-attached-Elem mapping that may be passed in to the constructor...
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)
Helper function that allows us to treat a homogenous pair as a range.
Definition: simple_range.h:57
void insert(dof_id_type id, Val val)
ProjectEdges(ProjectEdges &p_e, Threads::split)
libmesh_assert(ctx)
std::vector< FEFieldType > field_types
virtual_for_inffe const std::vector< Real > & get_JxW() const
Definition: fe_abstract.h:303
This class provides all data required for a physics package (e.g.
Definition: fem_context.h:62
void init_context(FEMContext &c)
std::unique_ptr< FEMFunctionBase< Output > > _f
virtual_for_inffe const std::vector< Point > & get_xyz() const
Definition: fe_abstract.h:280
DofObject * get_old_dof_object()
Pointer accessor for previously public old_dof_object.
Definition: dof_object.h:96
bool active_on_subdomain(subdomain_id_type sid) const
Definition: variable.h:167
void insert(const std::vector< dof_id_type > &dof_indices, const DenseVector< Val > &Ue)
const std::vector< unsigned int > & variables
void eval_mixed_derivatives(const FEMContext &libmesh_dbg_var(c), unsigned int i, unsigned int dim, const Node &n, std::vector< Output > &derivs)
unsigned int which_child_am_i(const Elem *e) const
Definition: elem.h:3206
DofObject & get_old_dof_object_ref()
As above, but do not use in situations where the old_dof_object may be nullptr, since this function a...
Definition: dof_object.h:104
void parallel_reduce(const Range &range, Body &body, unsigned int n_threads=libMesh::n_threads())
Execute the provided reduction operation in parallel on the specified range.
Definition: threads_none.h:109
Output eval_at_point(const FEMContext &c, unsigned int i, const Point &n, const Real time, bool)
virtual void request_dphi() const =0
request dphi calculations
static unsigned int n_dofs_at_node(const unsigned int dim, const FEType &fe_t, const ElemType t, const unsigned int n)
Definition: fe_interface.C:437
void operator()(const node_range &range)
TensorTools::MakeReal< Output >::type RealType
virtual numeric_index_type first_local_index() const =0
GenericProjector(const System &system_in, FFunctor &f_in, GFunctor *g_in, ProjectionAction &act_in, const std::vector< unsigned int > &variables_in, NodesToElemMap *nodes_to_elem_in=nullptr)
void find_dofs_to_send(const Node &node, const Elem &elem, unsigned short node_num, const var_set &vars)
virtual unsigned int n_vertices() const =0
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
static const Real out_of_elem_tol
void convert_from_receive(SendT &received, T &converted)
subdomain_id_type subdomain_id() const
Definition: elem.h:2588
NumberTensorValue Tensor
virtual unsigned short dim() const =0
ProjectInteriors(ProjectInteriors &p_i, Threads::split)
FEContinuity
defines an enum for finite element types to libmesh_assert a certain level (or type? Hcurl?) of continuity.
System * system() const
Definition: variable.h:114
const Node * node_ptr(const unsigned int i) const
Definition: elem.h:2513
TensorTools::MakeReal< Output >::type RealType
For ease of communication, we allow users to translate their own value types to a more easily computa...
std::unordered_map< dof_id_type, typename FFunctor::ValuePushType > new_ids_to_save
static const bool value
Definition: xdr_io.C:55
virtual const Elem & elem_ref(const dof_id_type i) const
Definition: mesh_base.h:778
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
Definition: int_range.h:176
TensorTools::MakeBaseNumber< Output >::type DofValueType
The DofObject defines an abstract base class for objects that have degrees of freedom associated with...
Definition: dof_object.h:54
std::pair< const Node *, std::tuple< const Elem *, unsigned short, var_set > > node_projection
std::set< unsigned int > var_set
virtual unsigned int size() const override final
Definition: dense_vector.h:104
std::enable_if< ScalarTraits< T >::value &&ScalarTraits< T2 >::value, typename CompareTypes< T, T2 >::supertype >::type inner_product(const T &a, const T2 &b)
Definition: tensor_tools.h:51
std::pair< unsigned int, unsigned int > var_to_vg_and_offset(const unsigned int s, const unsigned int var) const
Definition: dof_object.h:1176
This helper structure is used to determine whether a template class is one of our mathematical struct...
Definition: tensor_tools.h:345
SortAndCopy(SortAndCopy &other, Threads::split)
virtual void set(const numeric_index_type i, const T value)=0
Sets v(i) = value.
std::vector< unsigned int > component_to_var
Defines a dense vector for use in Finite Element-type computations.
Definition: dof_map.h:74
void cholesky_solve(const DenseVector< T2 > &b, DenseVector< T2 > &x)
For symmetric positive definite (SPD) matrices.
The VectorSetAction output functor class can be used with a GenericProjector to set projection values...
StoredRange< std::vector< const Elem * >::const_iterator, const Elem * > interior_range
unsigned int n_vars() const
Definition: system.C:2674
This class forms the foundation from which generic finite elements may be derived.
Definition: fe_abstract.h:99
const TypeToSend< T >::type convert_to_send(const T &in)
unsigned int n_comp_group(const unsigned int s, const unsigned int vg) const
Definition: dof_object.h:991
processor_id_type processor_id() const
void operator()(const node_range &range)
Output eval_at_node(const FEMContext &c, unsigned int i, unsigned int elem_dim, const Node &n, bool, Real=0.)
FEMFunctionBase is a base class from which users can derive in order to define "function-like" object...
bool active() const
Definition: elem.h:2955
const DofMap & get_dof_map() const
Definition: system.h:2417
processor_id_type processor_id() const
Definition: dof_object.h:881
Output eval_at_node(const FEMContext &c, unsigned int i, unsigned int, const Node &n, bool, const Real time)
static void dofs_on_side(const Elem *const elem, const unsigned int dim, const FEType &fe_t, unsigned int s, std::vector< unsigned int > &di, const bool add_p_level=true)
Fills the vector di with the local degree of freedom indices associated with side s of element elem A...
Definition: fe_interface.C:598
The FEMFunctionWrapper input functor class can be used with a GenericProjector to read values from an...
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition: point.h:39
const Point & point(const unsigned int i) const
Definition: elem.h:2459
void join(const SortAndCopy &other)
virtual numeric_index_type last_local_index() const =0
void ErrorVector unsigned int
Definition: adjoints_ex3.C:360
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition: int_range.h:153
virtual bool is_child_on_edge(const unsigned int c, const unsigned int e) const
Definition: elem.C:2333
const Elem & get(const ElemType type_in)
This class forms the foundation from which generic finite elements may be derived.
std::vector< FEContinuity > conts
NodesToElemMap nodes_to_elem_ourcopy
nodes_to_elem is either a shallow copy of a map passed in to the constructor, or points to nodes_to_e...
GenericProjector(const GenericProjector &in)
const Elem * child_ptr(unsigned int i) const
Definition: elem.h:3177
This class defines a tensor in LIBMESH_DIM dimensional Real or Complex space.
uint8_t dof_id_type
Definition: id_types.h:67
Output eval_at_point(const FEMContext &c, unsigned int i, const Point &p, Real, bool skip_context_check)
const std::vector< std::vector< OutputShape > > & get_phi() const
Definition: fe_base.h:207
const FEType & type() const
Definition: variable.h:144
spin_mutex spin_mtx
A convenient spin mutex object which can be used for obtaining locks.
Definition: threads.C:30
FEFieldType
defines an enum for finite element field types - i.e.