libMesh
Loading...
Searching...
No Matches
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
49namespace libMesh
50{
51
58template <typename T>
59struct TypeToSend {
60 typedef T type;
61};
62
63template <typename T>
64const typename TypeToSend<T>::type convert_to_send (const T& in)
65{ return in; }
66
67template <typename SendT, typename T>
68void convert_from_receive (SendT & received, T & converted)
69{ converted = received; }
70
81template <typename FFunctor, typename GFunctor,
82 typename FValue, typename ProjectionAction>
84{
85public:
90 typedef std::unordered_map<dof_id_type, std::vector<dof_id_type>> NodesToElemMap;
91
92private:
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
121public:
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),
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<
199 std::is_same<typename ProjectionAction::InsertInput, InsertInput>::value,
200 int>::type = 0>
201 void insert_id(dof_id_type id, const InsertInput & val, processor_id_type pid);
202
203 template <typename InsertInput,
204 typename std::enable_if<
205 !std::is_same<typename ProjectionAction::InsertInput, InsertInput>::value,
206 int>::type = 0>
207 void insert_id(dof_id_type id, const InsertInput & val, processor_id_type pid);
208
209 template <typename InsertInput,
210 typename std::enable_if<
211 std::is_same<typename ProjectionAction::InsertInput, InsertInput>::value,
212 int>::type = 0>
213 void insert_ids(const std::vector<dof_id_type> & ids,
214 const std::vector<InsertInput> & vals,
216
217 template <typename InsertInput,
218 typename std::enable_if<
219 !std::is_same<typename ProjectionAction::InsertInput, InsertInput>::value,
220 int>::type = 0>
221 void insert_ids(const std::vector<dof_id_type> & ids,
222 const std::vector<InsertInput> & vals,
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
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;
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
325
326
345
364
365
368
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
404template <typename Val>
406{
407private:
409
410public:
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 {
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
459template <typename Output>
461{
462protected:
464
465public:
468 typedef Output FunctorValue;
469
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
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
516private:
517 std::unique_ptr<FEMFunctionBase<Output>> _f;
518};
519
520
531#ifdef LIBMESH_ENABLE_AMR
532template <typename Output,
533 void (FEMContext::*point_output) (unsigned int,
534 const Point &,
535 Output &,
536 const Real) const>
538{
539protected:
541public:
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
608protected:
610 {
611 LOG_SCOPE ("check_old_context(c)", "OldSolutionBase");
612 const Elem & elem = c.get_elem();
613 if (last_elem != &elem)
614 {
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 {
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
709protected:
711 const System & sys;
713 std::vector<unsigned int> component_to_var;
714
715 static const Real out_of_elem_tol;
716};
717
718
727template <typename Output,
728 void (FEMContext::*point_output) (unsigned int,
729 const Point &,
730 Output &,
731 const Real) const>
732class OldSolutionValue : public OldSolutionBase<Output, point_output>
733{
734public:
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 =
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
951private:
953};
954
955template<>
956inline void
961
962
963template<>
964inline void
969
970template<>
971inline void
976
977
978template<>
979inline void
984
985
986#ifdef LIBMESH_USE_COMPLEX_NUMBERS
987template<>
988inline void
993
994
995template<>
996inline void
1001
1002template<>
1003inline void
1008
1009
1010template<>
1011inline void
1016#endif // LIBMESH_USE_COMPLEX_NUMBERS
1017
1018
1019template<>
1020inline
1021Number
1023eval_at_node(const FEMContext & c,
1024 unsigned int i,
1025 unsigned int /* elem_dim */,
1026 const Node & n,
1027 bool extra_hanging_dofs,
1028 Real /* time */)
1029{
1030 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
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
1068template <>
1069inline
1072eval_at_node(const FEMContext & c,
1073 unsigned int i,
1074 unsigned int /* elem_dim */,
1075 const Node & n,
1076 bool extra_hanging_dofs,
1077 Real /* time */)
1078{
1079 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
1128template<>
1129inline
1132eval_at_node(const FEMContext & c,
1133 unsigned int i,
1134 unsigned int elem_dim,
1135 const Node & n,
1136 bool extra_hanging_dofs,
1137 Real /* time */)
1138{
1139 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
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
1182template<>
1183inline
1184Tensor
1186eval_at_node(const FEMContext &,
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
1198template <typename Output,
1199 void (FEMContext::*point_output) (unsigned int,
1200 const Point &,
1201 Output &,
1202 const Real) const>
1203const Real OldSolutionBase<Output, point_output>::out_of_elem_tol = 10 * TOLERANCE;
1204
1205#endif // LIBMESH_ENABLE_AMR
1206
1211template <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
1283template <typename FFunctor, typename GFunctor,
1284 typename FValue, typename ProjectionAction>
1286 (GenericProjector & p) :
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 =
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
1346 }
1347 }
1348
1349 // Now initialize any user requested shape functions, xyz vals, etc
1350 f.init_context(context);
1351}
1352
1353
1354template <typename FFunctor, typename GFunctor,
1355 typename FValue, typename ProjectionAction>
1357 (GenericProjector & p) :
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
1372template <typename FFunctor, typename GFunctor, typename FValue, typename ProjectionAction>
1373template <typename InsertInput,
1374 typename std::enable_if<
1375 !std::is_same<typename ProjectionAction::InsertInput, InsertInput>::value,
1376 int>::type>
1377void
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
1385template <typename FFunctor, typename GFunctor, typename FValue, typename ProjectionAction>
1386template <typename InsertInput,
1387 typename std::enable_if<
1388 std::is_same<typename ProjectionAction::InsertInput, InsertInput>::value,
1389 int>::type>
1390void
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
1414template <typename FFunctor, typename GFunctor, typename FValue, typename ProjectionAction>
1415template <typename InsertInput,
1416 typename std::enable_if<
1417 !std::is_same<typename ProjectionAction::InsertInput, InsertInput>::value,
1418 int>::type>
1419void
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
1429template <typename FFunctor, typename GFunctor, typename FValue, typename ProjectionAction>
1430template <typename InsertInput,
1431 typename std::enable_if<
1432 std::is_same<typename ProjectionAction::InsertInput, InsertInput>::value,
1433 int>::type>
1434void
1436 const std::vector<dof_id_type> & ids,
1437 const std::vector<InsertInput> & vals,
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
1468template <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
1478template <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
1939template <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
1981namespace
1982{
1983template <typename Output, typename Input>
1984typename std::enable_if<ScalarTraits<Input>::value, Output>::type
1985raw_value(const Input & input, unsigned int /*index*/)
1986{
1987 return input;
1988}
1989
1990template <typename Output, template <typename> class WrapperClass, typename T>
1991typename std::enable_if<ScalarTraits<T>::value &&
1993 Output>::type
1994raw_value(const WrapperClass<T> & input, unsigned int index)
1995{
1996 return input.slice(index);
1997}
1998
1999template <typename T>
2000typename T::value_type
2001grad_component(const T &, unsigned int);
2002
2003template <typename T>
2005grad_component(const VectorValue<T> & grad, unsigned int component)
2006{
2007 return grad(component);
2008}
2009
2010template <typename T>
2012grad_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
2022template <typename FFunctor, typename GFunctor,
2023 typename FValue, typename ProjectionAction>
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
2345template <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() &&
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 );
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
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
2511template <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() &&
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 );
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
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
2698template <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
2840template <typename FFunctor, typename GFunctor,
2841 typename FValue, typename ProjectionAction>
2842void
2843GenericProjector<FFunctor, GFunctor, FValue,
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
2900template <typename FFunctor, typename GFunctor,
2901 typename FValue, typename ProjectionAction>
2902template <typename Value>
2903void
2904GenericProjector<FFunctor, GFunctor, FValue,
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
2987 (system.comm(), pushed_dof_ids, ids_action_functor);
2988
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
3000template <typename FFunctor, typename GFunctor,
3001 typename FValue, typename ProjectionAction>
3002void
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
unsigned int n_vars
unsigned int dim
void ErrorVector unsigned int
Defines a dense matrix for use in Finite Element-type computations.
void cholesky_solve(const DenseVector< T2 > &b, DenseVector< T2 > &x)
For symmetric positive definite (SPD) matrices.
Defines a dense vector for use in Finite Element-type computations.
std::vector< T > & get_values()
virtual unsigned int size() const override final
This class handles the numbering of degrees of freedom on a mesh.
Definition dof_map.h:181
void dof_indices(const Elem *const elem, std::vector< dof_id_type > &di) const
Definition dof_map.C:2201
The DofObject defines an abstract base class for objects that have degrees of freedom associated with...
Definition dof_object.h:55
unsigned int n_systems() const
Definition dof_object.h:913
unsigned int n_comp(const unsigned int s, const unsigned int var) const
Definition dof_object.h:978
unsigned int n_comp_group(const unsigned int s, const unsigned int vg) const
Definition dof_object.h:991
dof_id_type dof_number(const unsigned int s, const unsigned int var, const unsigned int comp) const
std::pair< unsigned int, unsigned int > var_to_vg_and_offset(const unsigned int s, const unsigned int var) const
processor_id_type processor_id() const
Definition dof_object.h:881
static constexpr dof_id_type invalid_id
An invalid id to distinguish an uninitialized DofObject.
Definition dof_object.h:473
DofObject * get_old_dof_object()
Pointer accessor for previously public old_dof_object.
Definition dof_object.h:96
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
dof_id_type id() const
Definition dof_object.h:819
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
unsigned int n_vars(const unsigned int s, const unsigned int vg) const
Definition dof_object.h:943
This is the base class from which all geometric element types are derived.
Definition elem.h:96
virtual bool is_node_on_side(const unsigned int n, const unsigned int s) const =0
virtual bool is_child_on_side(const unsigned int c, const unsigned int s) const =0
RefinementState refinement_flag() const
Definition elem.h:3227
bool active() const
Definition elem.h:2958
virtual unsigned int n_vertices() const =0
const Point & point(const unsigned int i) const
Definition elem.h:2462
virtual bool is_child_on_edge(const unsigned int c, const unsigned int e) const
Definition elem.C:2333
virtual unsigned int n_nodes() const =0
const Elem * parent() const
Definition elem.h:3047
const Elem * child_ptr(unsigned int i) const
Definition elem.h:3180
virtual bool is_vertex_on_parent(unsigned int c, unsigned int n) const
Definition elem.C:3532
RefinementState
Enumeration of possible element refinement states.
Definition elem.h:1446
@ JUST_COARSENED
Definition elem.h:1450
virtual bool is_face(const unsigned int i) const =0
unsigned int which_child_am_i(const Elem *e) const
Definition elem.h:3209
virtual Real hmin() const
Definition elem.C:683
virtual unsigned short dim() const =0
subdomain_id_type subdomain_id() const
Definition elem.h:2591
SimpleRange< ChildRefIter > child_ref_range()
Returns a range with all children of a parent element, usable in range-based for loops.
Definition elem.h:2355
virtual Real hmax() const
Definition elem.C:707
const Node * node_ptr(const unsigned int i) const
Definition elem.h:2516
virtual unsigned int n_children() const =0
unsigned int get_node_index(const Node *node_ptr) const
Definition elem.h:2554
unsigned int p_level() const
Definition elem.h:3125
RefinementState p_refinement_flag() const
Definition elem.h:3243
This class forms the foundation from which generic finite elements may be derived.
virtual FEContinuity get_continuity() const =0
virtual_for_inffe const std::vector< Real > & get_JxW() const
virtual_for_inffe const std::vector< Point > & get_xyz() const
virtual void request_phi() const =0
request phi calculations
FEType get_fe_type() const
virtual void request_dphi() const =0
request dphi calculations
This class forms the foundation from which generic finite elements may be derived.
Definition fe_base.h:86
const std::vector< std::vector< OutputShape > > & get_phi() const
Definition fe_base.h:207
const std::vector< std::vector< OutputGradient > > & get_dphi() const
Definition fe_base.h:230
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...
static FEFieldType field_type(const FEType &fe_type)
static unsigned int n_dofs_at_node(const unsigned int dim, const FEType &fe_t, const ElemType t, const unsigned int n)
static unsigned int n_dofs_per_elem(const unsigned int dim, const FEType &fe_t, const ElemType t)
static bool extra_hanging_dofs(const FEType &fe_t)
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...
This class provides all data required for a physics package (e.g.
Definition fem_context.h:63
void get_side_fe(unsigned int var, FEGenericBase< OutputShape > *&fe) const
Accessor for edge/face (2D/3D) finite element object for variable var for the largest dimension in th...
const std::set< unsigned char > & elem_dimensions() const
void get_edge_fe(unsigned int var, FEGenericBase< OutputShape > *&fe) const
Accessor for edge (3D only!) finite element object for variable var.
const Elem & get_elem() const
Accessor for current Elem object.
void get_element_fe(unsigned int var, FEGenericBase< OutputShape > *&fe) const
Accessor for interior finite element object for variable var for the largest dimension in the mesh.
void set_algebraic_type(const AlgebraicType atype)
Setting which determines whether to initialize algebraic structures (elem_*) on each element and set ...
FEMFunctionBase is a base class from which users can derive in order to define "function-like" object...
The FEMFunctionWrapper input functor class can be used with a GenericProjector to read values from an...
Output eval_at_point(const FEMContext &c, unsigned int i, const Point &n, const Real time, bool)
void eval_mixed_derivatives(const FEMContext &, unsigned int, unsigned int, const Node &, std::vector< Output > &)
TensorTools::MakeBaseNumber< Output >::type DofValueType
void eval_old_dofs(const Elem &, const FEType &, unsigned int, unsigned int, std::vector< dof_id_type > &, std::vector< Output > &)
FEMFunctionWrapper(const FEMFunctionBase< Output > &f)
std::unique_ptr< FEMFunctionBase< Output > > _f
void init_context(FEMContext &c)
void eval_old_dofs(const Elem &, unsigned int, unsigned int, std::vector< dof_id_type > &, std::vector< Output > &)
TensorTools::MakeReal< Output >::type RealType
FEMFunctionWrapper(const FEMFunctionWrapper< Output > &fw)
Output eval_at_node(const FEMContext &c, unsigned int i, unsigned int, const Node &n, bool, const Real time)
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
class FEType hides (possibly multiple) FEFamily and approximation orders, thereby enabling specialize...
Definition fe_type.h:197
std::unique_ptr< QBase > default_quadrature_rule(const unsigned int dim, const int extraorder=0) const
Definition fe_type.C:34
OrderWrapper order
The approximation order of the element (at 0 p-refinement level).
Definition fe_type.h:203
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
FEFamily family
The type of finite element.
Definition fe_type.h:228
The GenericProjector class implements the core of other projection operations, using two input functo...
GenericProjector(const GenericProjector &in)
const std::vector< unsigned int > & variables
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.
std::unordered_map< dof_id_type, typename FFunctor::ValuePushType > ids_to_save
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)
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...
ProjectionAction & master_action
StoredRange< std::vector< const Elem * >::const_iterator, const Elem * > interior_range
StoredRange< std::vector< node_projection >::const_iterator, node_projection > node_range
void send_and_insert_dof_values(std::unordered_map< dof_id_type, std::pair< Value, processor_id_type > > &ids_to_push, ProjectionAction &action) const
std::pair< const Node *, std::tuple< const Elem *, unsigned short, var_set > > node_projection
std::unique_ptr< GFunctor > master_g_deepcopy
Needed for C1 type elements only.
std::set< unsigned int > var_set
void project(const ConstElemRange &range)
Function definitions.
This is the MeshBase class.
Definition mesh_base.h:81
virtual const Elem & elem_ref(const dof_id_type i) const
Definition mesh_base.h:788
A Node is like a Point, but with more information.
Definition node.h:55
Provides a uniform interface to vector storage schemes for different linear algebra libraries.
virtual void set(const numeric_index_type i, const T value)=0
Sets v(i) = value.
virtual numeric_index_type last_local_index() const =0
virtual numeric_index_type first_local_index() const =0
The OldSolutionBase input functor abstract base class is the root of the OldSolutionValue and OldSolu...
std::vector< unsigned int > component_to_var
TensorTools::MakeReal< Output >::type RealType
void init_context(FEMContext &c)
static const Real out_of_elem_tol
bool check_old_context(const FEMContext &c, const Point &p)
void check_old_context(const FEMContext &c)
OldSolutionBase(const OldSolutionBase &in)
static void get_shape_outputs(FEAbstract &fe)
TensorTools::MakeBaseNumber< Output >::type DofValueType
OldSolutionBase(const libMesh::System &sys_in, const std::vector< unsigned int > *vars)
The OldSolutionValue input functor class can be used with GenericProjector to read values from a solu...
Output eval_at_point(const FEMContext &c, unsigned int i, const Point &p, Real, bool skip_context_check)
Output eval_at_node(const FEMContext &c, unsigned int i, unsigned int elem_dim, const Node &n, bool, Real=0.)
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)
OldSolutionValue(const OldSolutionValue &in)
const NumericVector< Number > & old_solution
void eval_mixed_derivatives(const FEMContext &libmesh_dbg_var(c), unsigned int i, unsigned int dim, const Node &n, std::vector< Output > &derivs)
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)
OldSolutionValue(const libMesh::System &sys_in, const NumericVector< Number > &old_sol, const std::vector< unsigned int > *vars)
void eval_mixed_derivatives(const FEMContext &, unsigned int, unsigned int, const Node &, std::vector< Output > &)
processor_id_type processor_id() const
const Parallel::Communicator & comm() const
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition point.h:40
The StoredRange class defines a contiguous, divisible set of objects.
Manages consistently variables, degrees of freedom, and coefficient vectors.
Definition system.h:100
const Variable & variable(unsigned int var) const
Return a constant reference to Variable var.
Definition system.C:2704
Real time
For time-dependent problems, this is the time t at the beginning of the current timestep.
Definition system.h:1677
unsigned int variable_scalar_number(std::string_view var, unsigned int component) const
Definition system.h:2474
const DofMap & get_dof_map() const
Definition system.h:2417
unsigned int number() const
Definition system.h:2393
const MeshBase & get_mesh() const
Definition system.h:2401
This class defines a tensor in LIBMESH_DIM dimensional Real or Complex space.
Dummy "splitting object" used to distinguish splitting constructors from copy constructors.
This class defines the notion of a variable in the system.
Definition variable.h:51
bool active_on_subdomain(subdomain_id_type sid) const
Definition variable.h:167
System * system() const
Definition variable.h:114
const FEType & type() const
Definition variable.h:144
The VectorSetAction output functor class can be used with a GenericProjector to set projection values...
void insert(const std::vector< dof_id_type > &dof_indices, const DenseVector< Val > &Ue)
VectorSetAction(NumericVector< Val > &target_vec)
NumericVector< Val > & target_vector
void insert(dof_id_type id, Val val)
This class defines a vector in LIBMESH_DIM dimensional Real or Complex space.
MeshBase & mesh
void push_parallel_vector_data(const Communicator &comm, MapToVectors &&data, const ActionFunctor &act_on_data)
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
std::enable_if< ScalarTraits< T >::value &&ScalarTraits< T2 >::value, typenameCompareTypes< T, T2 >::supertype >::type inner_product(const T &a, const T2 &b)
spin_mutex spin_mtx
A convenient spin mutex object which can be used for obtaining locks.
Definition threads.C:30
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.
The libMesh namespace provides an interface to certain functionality in the library.
@ SIDE_DISCONTINUOUS
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)
Helper function that allows us to treat a homogenous pair as a range.
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
const TypeToSend< T >::type convert_to_send(const T &in)
void convert_from_receive(SendT &received, T &converted)
libmesh_assert(ctx)
NumberTensorValue Tensor
dof_id_type numeric_index_type
Definition id_types.h:99
void libmesh_merge_move(T &target, T &source)
static constexpr Real TOLERANCE
uint8_t dof_id_type
Definition id_types.h:67
@ RATIONAL_BERNSTEIN
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
uint8_t processor_id_type
Definition id_types.h:104
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
ProjectEdges(ProjectEdges &p_e, Threads::split)
void operator()(const node_range &range)
ProjectInteriors(ProjectInteriors &p_i, Threads::split)
void operator()(const interior_range &range)
ProjectSides(ProjectSides &p_s, Threads::split)
void operator()(const node_range &range)
ProjectVertices(ProjectVertices &p_v, Threads::split)
SortAndCopy(SortAndCopy &other, Threads::split)
void operator()(const ConstElemRange &range)
std::unordered_multimap< const Node *, std::tuple< const Elem *, unsigned short, var_set > > ves_multimap
std::vector< const Elem * > interiors
void join(const SortAndCopy &other)
std::unordered_map< dof_id_type, typename FFunctor::ValuePushType > new_ids_to_save
std::vector< FEFieldType > field_types
void find_dofs_to_send(const Node &node, const Elem &elem, unsigned short node_num, const var_set &vars)
void insert_ids(const std::vector< dof_id_type > &ids, const std::vector< InsertInput > &vals, processor_id_type pid)
void insert_id(dof_id_type id, const InsertInput &val, processor_id_type pid)
std::unordered_map< dof_id_type, std::pair< typename FFunctor::ValuePushType, processor_id_type > > new_ids_to_push
void join(const SubFunctor &other)
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)
void insert_ids(const std::vector< dof_id_type > &ids, const std::vector< InsertInput > &vals, processor_id_type pid)
void insert_id(dof_id_type id, const InsertInput &val, processor_id_type pid)
This helper structure is used to determine whether a template class is one of our mathematical struct...
For ease of communication, we allow users to translate their own value types to a more easily computa...
const dof_id_type n_nodes
Definition tecplot_io.C:67
static const bool value
Definition xdr_io.C:55