1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2020 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 // Local includes
19 #include "libmesh/dof_map.h"
20 
21 // libMesh includes
22 #include "libmesh/boundary_info.h" // needed for dirichlet constraints
23 #include "libmesh/dense_matrix.h"
24 #include "libmesh/dense_vector.h"
25 #include "libmesh/dirichlet_boundaries.h"
26 #include "libmesh/elem.h"
27 #include "libmesh/elem_range.h"
28 #include "libmesh/fe_base.h"
29 #include "libmesh/fe_interface.h"
30 #include "libmesh/fe_type.h"
31 #include "libmesh/function_base.h"
32 #include "libmesh/int_range.h"
33 #include "libmesh/libmesh_logging.h"
34 #include "libmesh/mesh_base.h"
35 #include "libmesh/mesh_inserter_iterator.h"
36 #include "libmesh/mesh_tools.h" // for libmesh_assert_valid_boundary_ids()
37 #include "libmesh/nonlinear_implicit_system.h"
38 #include "libmesh/numeric_vector.h" // for enforce_constraints_exactly()
39 #include "libmesh/parallel_algebra.h"
40 #include "libmesh/parallel_elem.h"
41 #include "libmesh/parallel_node.h"
42 #include "libmesh/periodic_boundaries.h"
43 #include "libmesh/periodic_boundary.h"
44 #include "libmesh/periodic_boundary_base.h"
45 #include "libmesh/point_locator_base.h"
46 #include "libmesh/quadrature.h" // for dirichlet constraints
47 #include "libmesh/raw_accessor.h"
48 #include "libmesh/sparse_matrix.h" // needed to constrain adjoint rhs
49 #include "libmesh/system.h" // needed by enforce_constraints_exactly()
50 #include "libmesh/tensor_tools.h"
51 #include "libmesh/threads.h"
52 
53 // TIMPI includes
54 #include "timpi/parallel_implementation.h"
55 #include "timpi/parallel_sync.h"
56 
57 // C++ Includes
58 #include <set>
59 #include <algorithm> // for std::count, std::fill
60 #include <sstream>
61 #include <cstdlib> // *must* precede <cmath> for proper std:abs() on PGI, Sun Studio CC
62 #include <cmath>
63 #include <numeric>
64 
65 // Anonymous namespace to hold helper classes
66 namespace {
67 
68 using namespace libMesh;
69 
70 class ComputeConstraints
71 {
72 public:
ComputeConstraints(DofConstraints & constraints,DofMap & dof_map,PeriodicBoundaries & periodic_boundaries,const MeshBase & mesh,const unsigned int variable_number)73   ComputeConstraints (DofConstraints & constraints,
74                       DofMap & dof_map,
75 #ifdef LIBMESH_ENABLE_PERIODIC
76                       PeriodicBoundaries & periodic_boundaries,
77 #endif
78                       const MeshBase & mesh,
79                       const unsigned int variable_number) :
80     _constraints(constraints),
81     _dof_map(dof_map),
82 #ifdef LIBMESH_ENABLE_PERIODIC
83     _periodic_boundaries(periodic_boundaries),
84 #endif
85     _mesh(mesh),
86     _variable_number(variable_number)
87   {}
88 
operator()89   void operator()(const ConstElemRange & range) const
90   {
91     const Variable & var_description = _dof_map.variable(_variable_number);
92 
93 #ifdef LIBMESH_ENABLE_PERIODIC
94     std::unique_ptr<PointLocatorBase> point_locator;
95     const bool have_periodic_boundaries =
96       !_periodic_boundaries.empty();
97     if (have_periodic_boundaries && !range.empty())
98       point_locator = _mesh.sub_point_locator();
99 #endif
100 
101     for (const auto & elem : range)
102       if (var_description.active_on_subdomain(elem->subdomain_id()))
103         {
104 #ifdef LIBMESH_ENABLE_AMR
105           FEInterface::compute_constraints (_constraints,
106                                             _dof_map,
107                                             _variable_number,
108                                             elem);
109 #endif
110 #ifdef LIBMESH_ENABLE_PERIODIC
111           // FIXME: periodic constraints won't work on a non-serial
112           // mesh unless it's kept ghost elements from opposing
113           // boundaries!
114           if (have_periodic_boundaries)
115             FEInterface::compute_periodic_constraints (_constraints,
116                                                        _dof_map,
117                                                        _periodic_boundaries,
118                                                        _mesh,
119                                                        point_locator.get(),
120                                                        _variable_number,
121                                                        elem);
122 #endif
123         }
124   }
125 
126 private:
127   DofConstraints & _constraints;
128   DofMap & _dof_map;
129 #ifdef LIBMESH_ENABLE_PERIODIC
130   PeriodicBoundaries & _periodic_boundaries;
131 #endif
132   const MeshBase & _mesh;
133   const unsigned int _variable_number;
134 };
135 
136 
137 
138 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS
139 class ComputeNodeConstraints
140 {
141 public:
ComputeNodeConstraints(NodeConstraints & node_constraints,PeriodicBoundaries & periodic_boundaries,const MeshBase & mesh)142   ComputeNodeConstraints (NodeConstraints & node_constraints,
143 #ifdef LIBMESH_ENABLE_PERIODIC
144                           PeriodicBoundaries & periodic_boundaries,
145 #endif
146                           const MeshBase & mesh) :
147     _node_constraints(node_constraints),
148 #ifdef LIBMESH_ENABLE_PERIODIC
149     _periodic_boundaries(periodic_boundaries),
150 #endif
151     _mesh(mesh)
152   {}
153 
operator()154   void operator()(const ConstElemRange & range) const
155   {
156 #ifdef LIBMESH_ENABLE_PERIODIC
157     std::unique_ptr<PointLocatorBase> point_locator;
158     bool have_periodic_boundaries = !_periodic_boundaries.empty();
159     if (have_periodic_boundaries && !range.empty())
160       point_locator = _mesh.sub_point_locator();
161 #endif
162 
163     for (const auto & elem : range)
164       {
165 #ifdef LIBMESH_ENABLE_AMR
166         FEBase::compute_node_constraints (_node_constraints, elem);
167 #endif
168 #ifdef LIBMESH_ENABLE_PERIODIC
169         // FIXME: periodic constraints won't work on a non-serial
170         // mesh unless it's kept ghost elements from opposing
171         // boundaries!
172         if (have_periodic_boundaries)
173           FEBase::compute_periodic_node_constraints (_node_constraints,
174                                                      _periodic_boundaries,
175                                                      _mesh,
176                                                      point_locator.get(),
177                                                      elem);
178 #endif
179       }
180   }
181 
182 private:
183   NodeConstraints & _node_constraints;
184 #ifdef LIBMESH_ENABLE_PERIODIC
185   PeriodicBoundaries & _periodic_boundaries;
186 #endif
187   const MeshBase & _mesh;
188 };
189 #endif // LIBMESH_ENABLE_NODE_CONSTRAINTS
190 
191 
192 #ifdef LIBMESH_ENABLE_DIRICHLET
193 
194 /**
195  * This functor class hierarchy adds a constraint row to a DofMap
196  */
197 class AddConstraint
198 {
199 protected:
200   DofMap                  & dof_map;
201 
202 public:
AddConstraint(DofMap & dof_map_in)203   AddConstraint(DofMap & dof_map_in) : dof_map(dof_map_in) {}
204 
205   virtual void operator()(dof_id_type dof_number,
206                           const DofConstraintRow & constraint_row,
207                           const Number constraint_rhs) const = 0;
208 };
209 
210 class AddPrimalConstraint : public AddConstraint
211 {
212 public:
AddPrimalConstraint(DofMap & dof_map_in)213   AddPrimalConstraint(DofMap & dof_map_in) : AddConstraint(dof_map_in) {}
214 
operator()215   virtual void operator()(dof_id_type dof_number,
216                           const DofConstraintRow & constraint_row,
217                           const Number constraint_rhs) const
218   {
219     if (!dof_map.is_constrained_dof(dof_number))
220       dof_map.add_constraint_row (dof_number, constraint_row,
221                                   constraint_rhs, true);
222   }
223 };
224 
225 class AddAdjointConstraint : public AddConstraint
226 {
227 private:
228   const unsigned int qoi_index;
229 
230 public:
AddAdjointConstraint(DofMap & dof_map_in,unsigned int qoi_index_in)231   AddAdjointConstraint(DofMap & dof_map_in, unsigned int qoi_index_in)
232     : AddConstraint(dof_map_in), qoi_index(qoi_index_in) {}
233 
operator()234   virtual void operator()(dof_id_type dof_number,
235                           const DofConstraintRow & constraint_row,
236                           const Number constraint_rhs) const
237   {
238     dof_map.add_adjoint_constraint_row
239       (qoi_index, dof_number, constraint_row, constraint_rhs,
240        true);
241   }
242 };
243 
244 
245 
246 /**
247  * This class implements turning an arbitrary
248  * boundary function into Dirichlet constraints.  It
249  * may be executed in parallel on multiple threads.
250  */
251 class ConstrainDirichlet
252 {
253 private:
254   DofMap                  & dof_map;
255   const MeshBase          & mesh;
256   const Real               time;
257   const DirichletBoundaries & dirichlets;
258 
259   const AddConstraint     & add_fn;
260 
f_component(FunctionBase<Number> * f,FEMFunctionBase<Number> * f_fem,const FEMContext * c,unsigned int i,const Point & p,Real time)261   static Number f_component (FunctionBase<Number> * f,
262                              FEMFunctionBase<Number> * f_fem,
263                              const FEMContext * c,
264                              unsigned int i,
265                              const Point & p,
266                              Real time)
267   {
268     if (f_fem)
269       {
270         if (c)
271           return f_fem->component(*c, i, p, time);
272         else
273           return std::numeric_limits<Real>::quiet_NaN();
274       }
275     return f->component(i, p, time);
276   }
277 
g_component(FunctionBase<Gradient> * g,FEMFunctionBase<Gradient> * g_fem,const FEMContext * c,unsigned int i,const Point & p,Real time)278   static Gradient g_component (FunctionBase<Gradient> * g,
279                                FEMFunctionBase<Gradient> * g_fem,
280                                const FEMContext * c,
281                                unsigned int i,
282                                const Point & p,
283                                Real time)
284   {
285     if (g_fem)
286       {
287         if (c)
288           return g_fem->component(*c, i, p, time);
289         else
290           return std::numeric_limits<Number>::quiet_NaN();
291       }
292     return g->component(i, p, time);
293   }
294 
295 
296 
297   /**
298    * Handy struct to pass around BoundaryInfo for a single Elem.  Must
299    * be created with a reference to a BoundaryInfo object and a map
300    * from boundary_id -> set<DirichletBoundary *> objects involving
301    * that id.
302    */
303   struct SingleElemBoundaryInfo
304   {
SingleElemBoundaryInfoSingleElemBoundaryInfo305     SingleElemBoundaryInfo(const BoundaryInfo & bi,
306                            const std::map<boundary_id_type, std::set<std::pair<unsigned int, DirichletBoundary *>>> & ordered_map_in) :
307       boundary_info(bi),
308       boundary_id_to_ordered_dirichlet_boundaries(ordered_map_in),
309       elem(nullptr),
310       n_sides(0),
311       n_edges(0),
312       n_nodes(0)
313     {}
314 
315     const BoundaryInfo & boundary_info;
316     const std::map<boundary_id_type, std::set<std::pair<unsigned int, DirichletBoundary *>>> & boundary_id_to_ordered_dirichlet_boundaries;
317     const Elem * elem;
318 
319     unsigned short n_sides;
320     unsigned short n_edges;
321     unsigned short n_nodes;
322 
323     // Mapping from DirichletBoundary objects which are active on this
324     // element to sides/nodes/edges/shellfaces of this element which
325     // they are active on.
326     std::map<const DirichletBoundary *, std::vector<bool>> is_boundary_node_map;
327     std::map<const DirichletBoundary *, std::vector<bool>> is_boundary_side_map;
328     std::map<const DirichletBoundary *, std::vector<bool>> is_boundary_edge_map;
329     std::map<const DirichletBoundary *, std::vector<bool>> is_boundary_shellface_map;
330 
331     std::map<const DirichletBoundary *, std::vector<bool>> is_boundary_nodeset_map;
332 
333     // The set of (dirichlet_id, DirichletBoundary) pairs which have at least one boundary
334     // id related to this Elem.
335     std::set<std::pair<unsigned int, DirichletBoundary *>> ordered_dbs;
336 
337     /**
338      * Given a single Elem, fills the SingleElemBoundaryInfo struct with
339      * required data.
340      *
341      * @return true if this Elem has _any_ boundary ids associated with
342      * it, false otherwise.
343      */
reinitSingleElemBoundaryInfo344     bool reinit(const Elem * elem_in)
345     {
346       elem = elem_in;
347 
348       n_sides = elem->n_sides();
349       n_edges = elem->n_edges();
350       n_nodes = elem->n_nodes();
351 
352       // objects and node/side/edge/shellface ids.
353       is_boundary_node_map.clear();
354       is_boundary_side_map.clear();
355       is_boundary_edge_map.clear();
356       is_boundary_shellface_map.clear();
357       is_boundary_nodeset_map.clear();
358 
359       // Clear any DirichletBoundaries from the previous Elem
360       ordered_dbs.clear();
361 
362       // Update has_dirichlet_constraint below, and if it remains false then
363       // we can skip this element since there are not constraints to impose.
364       bool has_dirichlet_constraint = false;
365 
366       // Container to catch boundary ids handed back for sides,
367       // nodes, and edges in the loops below.
368       std::vector<boundary_id_type> ids_vec;
369 
370       for (unsigned char s = 0; s != n_sides; ++s)
371         {
372           // First see if this side has been requested
373           boundary_info.boundary_ids (elem, s, ids_vec);
374 
375           bool do_this_side = false;
376           for (const auto & bc_id : ids_vec)
377             {
378               auto it = boundary_id_to_ordered_dirichlet_boundaries.find(bc_id);
379               if (it != boundary_id_to_ordered_dirichlet_boundaries.end())
380                 {
381                   do_this_side = true;
382 
383                   // Associate every DirichletBoundary object that has this bc_id with the current Elem
384                   ordered_dbs.insert(it->second.begin(), it->second.end());
385 
386                   // Turn on the flag for the current side for each DirichletBoundary
387                   for (const auto & db_pair : it->second)
388                     {
389                       // Attempt to emplace an empty vector. If there
390                       // is already an entry, the insertion will fail
391                       // and we'll get an iterator back to the
392                       // existing entry. Either way, we'll then set
393                       // index s of that vector to "true".
394                       auto pr = is_boundary_side_map.emplace(db_pair.second, std::vector<bool>(n_sides, false));
395                       pr.first->second[s] = true;
396                     }
397                 }
398             }
399 
400           if (!do_this_side)
401             continue;
402 
403           has_dirichlet_constraint = true;
404 
405           // Then determine what nodes are on this side
406           for (unsigned int n = 0; n != n_nodes; ++n)
407             if (elem->is_node_on_side(n,s))
408               {
409                 // Attempt to emplace an empty vector. If there is
410                 // already an entry, the insertion will fail and we'll
411                 // get an iterator back to the existing entry. Either
412                 // way, we'll then set index n of that vector to
413                 // "true".
414                 for (const auto & db_pair : ordered_dbs)
415                   {
416                     // Only add this as a boundary node for this db if
417                     // it is also a boundary side for this db.
418                     auto side_it = is_boundary_side_map.find(db_pair.second);
419                     if (side_it != is_boundary_side_map.end() && side_it->second[s])
420                       {
421                         auto pr = is_boundary_node_map.emplace(db_pair.second, std::vector<bool>(n_nodes, false));
422                         pr.first->second[n] = true;
423                       }
424                   }
425               }
426 
427           // Finally determine what edges are on this side
428           for (unsigned int e = 0; e != n_edges; ++e)
429             if (elem->is_edge_on_side(e,s))
430               {
431                 // Attempt to emplace an empty vector. If there is
432                 // already an entry, the insertion will fail and we'll
433                 // get an iterator back to the existing entry. Either
434                 // way, we'll then set index e of that vector to
435                 // "true".
436                 for (const auto & db_pair : ordered_dbs)
437                   {
438                     // Only add this as a boundary edge for this db if
439                     // it is also a boundary side for this db.
440                     auto side_it = is_boundary_side_map.find(db_pair.second);
441                     if (side_it != is_boundary_side_map.end() && side_it->second[s])
442                       {
443                         auto pr = is_boundary_edge_map.emplace(db_pair.second, std::vector<bool>(n_edges, false));
444                         pr.first->second[e] = true;
445                       }
446                   }
447               }
448         } // for (s = 0..n_sides)
449 
450       // We can also impose Dirichlet boundary conditions on nodes, so we should
451       // also independently check whether the nodes have been requested
452       for (unsigned int n=0; n != n_nodes; ++n)
453         {
454           boundary_info.boundary_ids (elem->node_ptr(n), ids_vec);
455 
456           for (const auto & bc_id : ids_vec)
457             {
458               auto it = boundary_id_to_ordered_dirichlet_boundaries.find(bc_id);
459               if (it != boundary_id_to_ordered_dirichlet_boundaries.end())
460                 {
461                   // Associate every DirichletBoundary object that has this bc_id with the current Elem
462                   ordered_dbs.insert(it->second.begin(), it->second.end());
463 
464                   // Turn on the flag for the current node for each DirichletBoundary
465                   for (const auto & db_pair : it->second)
466                     {
467                       auto pr = is_boundary_node_map.emplace(db_pair.second, std::vector<bool>(n_nodes, false));
468                       pr.first->second[n] = true;
469 
470                       auto pr2 = is_boundary_nodeset_map.emplace(db_pair.second, std::vector<bool>(n_nodes, false));
471                       pr2.first->second[n] = true;
472                     }
473 
474                   has_dirichlet_constraint = true;
475                 }
476             }
477         } // for (n = 0..n_nodes)
478 
479       // We can also impose Dirichlet boundary conditions on edges, so we should
480       // also independently check whether the edges have been requested
481       for (unsigned short e=0; e != n_edges; ++e)
482         {
483           boundary_info.edge_boundary_ids (elem, e, ids_vec);
484 
485           bool do_this_side = false;
486           for (const auto & bc_id : ids_vec)
487             {
488               auto it = boundary_id_to_ordered_dirichlet_boundaries.find(bc_id);
489               if (it != boundary_id_to_ordered_dirichlet_boundaries.end())
490                 {
491                   do_this_side = true;
492 
493                   // We need to loop over all DirichletBoundary objects associated with bc_id
494                   ordered_dbs.insert(it->second.begin(), it->second.end());
495 
496                   // Turn on the flag for the current edge for each DirichletBoundary
497                   for (const auto & db_pair : it->second)
498                     {
499                       auto pr = is_boundary_edge_map.emplace(db_pair.second, std::vector<bool>(n_edges, false));
500                       pr.first->second[e] = true;
501                     }
502                 }
503             }
504 
505           if (!do_this_side)
506             continue;
507 
508           has_dirichlet_constraint = true;
509 
510           // Then determine what nodes are on this edge
511           for (unsigned int n = 0; n != n_nodes; ++n)
512             if (elem->is_node_on_edge(n,e))
513               {
514                 // Attempt to emplace an empty vector. If there is
515                 // already an entry, the insertion will fail and we'll
516                 // get an iterator back to the existing entry. Either
517                 // way, we'll then set index n of that vector to
518                 // "true".
519                 for (const auto & db_pair : ordered_dbs)
520                   {
521                     // Only add this as a boundary node for this db if
522                     // it is also a boundary edge for this db.
523                     auto edge_it = is_boundary_edge_map.find(db_pair.second);
524                     if (edge_it != is_boundary_edge_map.end() && edge_it->second[e])
525                       {
526                         auto pr = is_boundary_node_map.emplace(db_pair.second, std::vector<bool>(n_nodes, false));
527                         pr.first->second[n] = true;
528                       }
529                   }
530               }
531         }
532 
533       // We can also impose Dirichlet boundary conditions on shellfaces, so we should
534       // also independently check whether the shellfaces have been requested
535       for (unsigned short shellface=0; shellface != 2; ++shellface)
536         {
537           boundary_info.shellface_boundary_ids (elem, shellface, ids_vec);
538           bool do_this_shellface = false;
539 
540           for (const auto & bc_id : ids_vec)
541             {
542               auto it = boundary_id_to_ordered_dirichlet_boundaries.find(bc_id);
543               if (it != boundary_id_to_ordered_dirichlet_boundaries.end())
544                 {
545                   has_dirichlet_constraint = true;
546                   do_this_shellface = true;
547 
548                   // We need to loop over all DirichletBoundary objects associated with bc_id
549                   ordered_dbs.insert(it->second.begin(), it->second.end());
550 
551                   // Turn on the flag for the current shellface for each DirichletBoundary
552                   for (const auto & db_pair : it->second)
553                     {
554                       auto pr = is_boundary_shellface_map.emplace(db_pair.second, std::vector<bool>(/*n_shellfaces=*/2, false));
555                       pr.first->second[shellface] = true;
556                     }
557                 }
558             }
559 
560           if (do_this_shellface)
561             {
562               // Shellface BCs induce BCs on all the nodes of a shell Elem
563               for (unsigned int n = 0; n != n_nodes; ++n)
564                 for (const auto & db_pair : ordered_dbs)
565                   {
566                     // Only add this as a boundary node for this db if
567                     // it is also a boundary shellface for this db.
568                     auto side_it = is_boundary_shellface_map.find(db_pair.second);
569                     if (side_it != is_boundary_shellface_map.end() && side_it->second[shellface])
570                       {
571                         auto pr = is_boundary_node_map.emplace(db_pair.second, std::vector<bool>(n_nodes, false));
572                         pr.first->second[n] = true;
573                       }
574                   }
575             }
576         } // for (shellface = 0..2)
577 
578       return has_dirichlet_constraint;
579     } // SingleElemBoundaryInfo::reinit()
580 
581   }; // struct SingleElemBoundaryInfo
582 
583 
584 
585   template<typename OutputType>
apply_lagrange_dirichlet_impl(const SingleElemBoundaryInfo & sebi,const Variable & variable,const DirichletBoundary & dirichlet,FEMContext & fem_context)586   void apply_lagrange_dirichlet_impl(const SingleElemBoundaryInfo & sebi,
587                             const Variable & variable,
588                             const DirichletBoundary & dirichlet,
589                             FEMContext & fem_context) const
590   {
591     // Get pointer to the Elem we are currently working on
592     const Elem * elem = sebi.elem;
593 
594     // Per-subdomain variables don't need to be projected on
595     // elements where they're not active
596     if (!variable.active_on_subdomain(elem->subdomain_id()))
597       return;
598 
599     FunctionBase<Number> * f = dirichlet.f.get();
600     FEMFunctionBase<Number> * f_fem = dirichlet.f_fem.get();
601 
602     const System * f_system = dirichlet.f_system;
603 
604     // We need data to project
605     libmesh_assert(f || f_fem);
606     libmesh_assert(!(f && f_fem));
607 
608     // Iff our data depends on a system, we should have it.
609     libmesh_assert(!(f && f_system));
610     libmesh_assert(!(f_fem && !f_system));
611 
612     // The new element coefficients. For Lagrange FEs, these are the
613     // nodal values.
614     DenseVector<Number> Ue;
615 
616     // Get a reference to the fe_type associated with this variable
617     const FEType & fe_type = variable.type();
618 
619     // Dimension of the vector-valued FE (1 for scalar-valued FEs)
620     unsigned int n_vec_dim = FEInterface::n_vec_dim(mesh, fe_type);
621 
622     const unsigned int var_component =
623       variable.first_scalar_number();
624 
625     // Get this Variable's number, as determined by the System.
626     const unsigned int var = variable.number();
627 
628     // If our supplied functions require a FEMContext, and if we have
629     // an initialized solution to use with that FEMContext, then
630     // create one
631     std::unique_ptr<FEMContext> context;
632     if (f_fem)
633       {
634         libmesh_assert(f_system);
635         if (f_system->current_local_solution->initialized())
636           {
637             context = libmesh_make_unique<FEMContext>(*f_system);
638             f_fem->init_context(*context);
639           }
640       }
641 
642     if (f_system && context.get())
643       context->pre_fe_reinit(*f_system, elem);
644 
645     // Also pre-init the fem_context() we were passed on the current Elem.
646     fem_context.pre_fe_reinit(fem_context.get_system(), elem);
647 
648     // Get a reference to the DOF indices for the current element
649     const std::vector<dof_id_type> & dof_indices =
650       fem_context.get_dof_indices(var);
651 
652     // The number of DOFs on the element
653     const unsigned int n_dofs =
654       cast_int<unsigned int>(dof_indices.size());
655 
656     // Fixed vs. free DoFs on edge/face projections
657     std::vector<char> dof_is_fixed(n_dofs, false); // bools
658 
659     // Zero the interpolated values
660     Ue.resize (n_dofs); Ue.zero();
661 
662     // For Lagrange elements, side, edge, and shellface BCs all
663     // "induce" boundary conditions on the nodes of those entities.
664     // In SingleElemBoundaryInfo::reinit(), we therefore set entries
665     // in the "is_boundary_node_map" container based on side and
666     // shellface BCs, Then, when we actually apply constraints, we
667     // only have to check whether any Nodes are in this container, and
668     // compute values as necessary.
669     unsigned int current_dof = 0;
670     for (unsigned int n=0; n!= sebi.n_nodes; ++n)
671       {
672         // For Lagrange this can return 0 (in case of a lower-order FE
673         // on a higher-order Elem) or 1. This function accounts for the
674         // Elem::p_level() internally.
675         const unsigned int nc =
676           FEInterface::n_dofs_at_node (fe_type, elem, n);
677 
678         // If there are no DOFs at this node, then it doesn't matter
679         // if it's technically a boundary node or not, there's nothing
680         // to constrain.
681         if (!nc)
682           continue;
683 
684         // Check whether the current node is a boundary node
685         auto is_boundary_node_it = sebi.is_boundary_node_map.find(&dirichlet);
686         const bool is_boundary_node =
687           (is_boundary_node_it != sebi.is_boundary_node_map.end() &&
688            is_boundary_node_it->second[n]);
689 
690         // Check whether the current node is in a boundary nodeset
691         auto is_boundary_nodeset_it = sebi.is_boundary_nodeset_map.find(&dirichlet);
692         const bool is_boundary_nodeset =
693           (is_boundary_nodeset_it != sebi.is_boundary_nodeset_map.end() &&
694            is_boundary_nodeset_it->second[n]);
695 
696         // If node is neither a boundary node or from a boundary nodeset, go to the next one.
697         if ( !(is_boundary_node || is_boundary_nodeset) )
698           {
699             current_dof += nc;
700             continue;
701           }
702 
703         // Compute function values, storing them in Ue
704         libmesh_assert_equal_to (nc, n_vec_dim);
705         for (unsigned int c = 0; c < n_vec_dim; c++)
706           {
707             Ue(current_dof+c) =
708               f_component(f, f_fem, context.get(), var_component+c,
709                           elem->point(n), time);
710             dof_is_fixed[current_dof+c] = true;
711           }
712         current_dof += n_vec_dim;
713       } // end for (n=0..n_nodes)
714 
715     // Lock the DofConstraints since it is shared among threads.
716     {
717       Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
718 
719       for (unsigned int i = 0; i < n_dofs; i++)
720         {
721           DofConstraintRow empty_row;
722           if (dof_is_fixed[i] && !libmesh_isnan(Ue(i)))
723             add_fn (dof_indices[i], empty_row, Ue(i));
724         }
725     }
726 
727   } // apply_lagrange_dirichlet_impl
728 
729 
730 
731   template<typename OutputType>
apply_dirichlet_impl(const SingleElemBoundaryInfo & sebi,const Variable & variable,const DirichletBoundary & dirichlet,FEMContext & fem_context)732   void apply_dirichlet_impl(const SingleElemBoundaryInfo & sebi,
733                             const Variable & variable,
734                             const DirichletBoundary & dirichlet,
735                             FEMContext & fem_context) const
736   {
737     // Get pointer to the Elem we are currently working on
738     const Elem * elem = sebi.elem;
739 
740     // Per-subdomain variables don't need to be projected on
741     // elements where they're not active
742     if (!variable.active_on_subdomain(elem->subdomain_id()))
743       return;
744 
745     typedef OutputType                                                      OutputShape;
746     typedef typename TensorTools::IncrementRank<OutputShape>::type          OutputGradient;
747     //typedef typename TensorTools::IncrementRank<OutputGradient>::type       OutputTensor;
748     typedef typename TensorTools::MakeNumber<OutputShape>::type             OutputNumber;
749     typedef typename TensorTools::IncrementRank<OutputNumber>::type         OutputNumberGradient;
750     //typedef typename TensorTools::IncrementRank<OutputNumberGradient>::type OutputNumberTensor;
751 
752     FunctionBase<Number> * f = dirichlet.f.get();
753     FunctionBase<Gradient> * g = dirichlet.g.get();
754 
755     FEMFunctionBase<Number> * f_fem = dirichlet.f_fem.get();
756     FEMFunctionBase<Gradient> * g_fem = dirichlet.g_fem.get();
757 
758     const System * f_system = dirichlet.f_system;
759 
760     // We need data to project
761     libmesh_assert(f || f_fem);
762     libmesh_assert(!(f && f_fem));
763 
764     // Iff our data depends on a system, we should have it.
765     libmesh_assert(!(f && f_system));
766     libmesh_assert(!(f_fem && !f_system));
767 
768     // The element matrix and RHS for projections.
769     // Note that Ke is always real-valued, whereas
770     // Fe may be complex valued if complex number
771     // support is enabled
772     DenseMatrix<Real> Ke;
773     DenseVector<Number> Fe;
774     // The new element coefficients
775     DenseVector<Number> Ue;
776 
777     // The dimensionality of the current mesh
778     const unsigned int dim = mesh.mesh_dimension();
779 
780     // Get a reference to the fe_type associated with this variable
781     const FEType & fe_type = variable.type();
782 
783     // Dimension of the vector-valued FE (1 for scalar-valued FEs)
784     unsigned int n_vec_dim = FEInterface::n_vec_dim(mesh, fe_type);
785 
786     const unsigned int var_component =
787       variable.first_scalar_number();
788 
789     // Get this Variable's number, as determined by the System.
790     const unsigned int var = variable.number();
791 
792     // The type of projections done depend on the FE's continuity.
793     FEContinuity cont = FEInterface::get_continuity(fe_type);
794 
795     // Make sure we have the right data available for C1 projections
796     if ((cont == C_ONE) && (fe_type.family != SUBDIVISION))
797       {
798         // We'll need gradient data for a C1 projection
799         libmesh_assert(g || g_fem);
800 
801         // We currently demand that either neither nor both function
802         // object depend on current FEM data.
803         libmesh_assert(!(g && g_fem));
804         libmesh_assert(!(f && g_fem));
805         libmesh_assert(!(f_fem && g));
806       }
807 
808     // If our supplied functions require a FEMContext, and if we have
809     // an initialized solution to use with that FEMContext, then
810     // create one
811     std::unique_ptr<FEMContext> context;
812     if (f_fem)
813       {
814         libmesh_assert(f_system);
815         if (f_system->current_local_solution->initialized())
816           {
817             context = libmesh_make_unique<FEMContext>(*f_system);
818             f_fem->init_context(*context);
819             if (g_fem)
820               g_fem->init_context(*context);
821           }
822       }
823 
824     // There's a chicken-and-egg problem with FEMFunction-based
825     // Dirichlet constraints: we can't evaluate the FEMFunction
826     // until we have an initialized local solution vector, we
827     // can't initialize local solution vectors until we have a
828     // send list, and we can't generate a send list until we know
829     // all our constraints
830     //
831     // We don't generate constraints on uninitialized systems;
832     // currently user code will have to reinit() before any
833     // FEMFunction-based constraints will be correct.  This should
834     // be fine, since user code would want to reinit() after
835     // setting initial conditions anyway.
836     if (f_system && context.get())
837       context->pre_fe_reinit(*f_system, elem);
838 
839     // Also pre-init the fem_context() we were passed on the current Elem.
840     fem_context.pre_fe_reinit(fem_context.get_system(), elem);
841 
842     // Get a reference to the DOF indices for the current element
843     const std::vector<dof_id_type> & dof_indices =
844       fem_context.get_dof_indices(var);
845 
846     // The number of DOFs on the element
847     const unsigned int n_dofs =
848       cast_int<unsigned int>(dof_indices.size());
849 
850     // Fixed vs. free DoFs on edge/face projections
851     std::vector<char> dof_is_fixed(n_dofs, false); // bools
852     std::vector<int> free_dof(n_dofs, 0);
853 
854     // Zero the interpolated values
855     Ue.resize (n_dofs); Ue.zero();
856 
857     // In general, we need a series of
858     // projections to ensure a unique and continuous
859     // solution.  We start by interpolating boundary nodes, then
860     // hold those fixed and project boundary edges, then hold
861     // those fixed and project boundary faces,
862 
863     // Interpolate node values first. Note that we have a special
864     // case for nodes that have a boundary nodeset, since we do
865     // need to interpolate them directly, even if they're non-vertex
866     // nodes.
867     unsigned int current_dof = 0;
868     for (unsigned int n=0; n!= sebi.n_nodes; ++n)
869       {
870         // FIXME: this should go through the DofMap,
871         // not duplicate dof_indices code badly!
872 
873         // Get the number of DOFs at this node, accounting for
874         // Elem::p_level() internally.
875         const unsigned int nc =
876           FEInterface::n_dofs_at_node (fe_type, elem, n);
877 
878         // Get a reference to the "is_boundary_node" flags for the
879         // current DirichletBoundary object.  In case the map does not
880         // contain an entry for this DirichletBoundary object, it
881         // means there are no boundary nodes active.
882         auto is_boundary_node_it = sebi.is_boundary_node_map.find(&dirichlet);
883 
884         // The current n is not a boundary node if either there is no
885         // boundary_node_map for this DirichletBoundary object, or if
886         // there is but the entry in the corresponding vector is
887         // false.
888         const bool not_boundary_node =
889           (is_boundary_node_it == sebi.is_boundary_node_map.end() ||
890            !is_boundary_node_it->second[n]);
891 
892         // Same thing for nodesets
893         auto is_boundary_nodeset_it = sebi.is_boundary_nodeset_map.find(&dirichlet);
894         const bool not_boundary_nodeset =
895           (is_boundary_nodeset_it == sebi.is_boundary_nodeset_map.end() ||
896            !is_boundary_nodeset_it->second[n]);
897 
898         if ((!elem->is_vertex(n) || not_boundary_node) &&
899             not_boundary_nodeset)
900           {
901             current_dof += nc;
902             continue;
903           }
904         if (cont == DISCONTINUOUS)
905           {
906             libmesh_assert_equal_to (nc, 0);
907           }
908         // Assume that C_ZERO elements have a single nodal
909         // value shape function
910         else if ((cont == C_ZERO) || (fe_type.family == SUBDIVISION))
911           {
912             libmesh_assert_equal_to (nc, n_vec_dim);
913             for (unsigned int c = 0; c < n_vec_dim; c++)
914               {
915                 Ue(current_dof+c) =
916                   f_component(f, f_fem, context.get(), var_component+c,
917                               elem->point(n), time);
918                 dof_is_fixed[current_dof+c] = true;
919               }
920             current_dof += n_vec_dim;
921           }
922         // The hermite element vertex shape functions are weird
923         else if (fe_type.family == HERMITE)
924           {
925             Ue(current_dof) =
926               f_component(f, f_fem, context.get(), var_component,
927                           elem->point(n), time);
928             dof_is_fixed[current_dof] = true;
929             current_dof++;
930             Gradient grad =
931               g_component(g, g_fem, context.get(), var_component,
932                           elem->point(n), time);
933             // x derivative
934             Ue(current_dof) = grad(0);
935             dof_is_fixed[current_dof] = true;
936             current_dof++;
937             if (dim > 1)
938               {
939                 // We'll finite difference mixed derivatives
940                 Point nxminus = elem->point(n),
941                   nxplus = elem->point(n);
942                 nxminus(0) -= TOLERANCE;
943                 nxplus(0) += TOLERANCE;
944                 Gradient gxminus =
945                   g_component(g, g_fem, context.get(), var_component,
946                               nxminus, time);
947                 Gradient gxplus =
948                   g_component(g, g_fem, context.get(), var_component,
949                               nxplus, time);
950                 // y derivative
951                 Ue(current_dof) = grad(1);
952                 dof_is_fixed[current_dof] = true;
953                 current_dof++;
954                 // xy derivative
955                 Ue(current_dof) = (gxplus(1) - gxminus(1))
956                   / 2. / TOLERANCE;
957                 dof_is_fixed[current_dof] = true;
958                 current_dof++;
959 
960                 if (dim > 2)
961                   {
962                     // z derivative
963                     Ue(current_dof) = grad(2);
964                     dof_is_fixed[current_dof] = true;
965                     current_dof++;
966                     // xz derivative
967                     Ue(current_dof) = (gxplus(2) - gxminus(2))
968                       / 2. / TOLERANCE;
969                     dof_is_fixed[current_dof] = true;
970                     current_dof++;
971                     // We need new points for yz
972                     Point nyminus = elem->point(n),
973                       nyplus = elem->point(n);
974                     nyminus(1) -= TOLERANCE;
975                     nyplus(1) += TOLERANCE;
976                     Gradient gyminus =
977                       g_component(g, g_fem, context.get(), var_component,
978                                   nyminus, time);
979                     Gradient gyplus =
980                       g_component(g, g_fem, context.get(), var_component,
981                                   nyplus, time);
982                     // xz derivative
983                     Ue(current_dof) = (gyplus(2) - gyminus(2))
984                       / 2. / TOLERANCE;
985                     dof_is_fixed[current_dof] = true;
986                     current_dof++;
987                     // Getting a 2nd order xyz is more tedious
988                     Point nxmym = elem->point(n),
989                       nxmyp = elem->point(n),
990                       nxpym = elem->point(n),
991                       nxpyp = elem->point(n);
992                     nxmym(0) -= TOLERANCE;
993                     nxmym(1) -= TOLERANCE;
994                     nxmyp(0) -= TOLERANCE;
995                     nxmyp(1) += TOLERANCE;
996                     nxpym(0) += TOLERANCE;
997                     nxpym(1) -= TOLERANCE;
998                     nxpyp(0) += TOLERANCE;
999                     nxpyp(1) += TOLERANCE;
1000                     Gradient gxmym =
1001                       g_component(g, g_fem, context.get(), var_component,
1002                                   nxmym, time);
1003                     Gradient gxmyp =
1004                       g_component(g, g_fem, context.get(), var_component,
1005                                   nxmyp, time);
1006                     Gradient gxpym =
1007                       g_component(g, g_fem, context.get(), var_component,
1008                                   nxpym, time);
1009                     Gradient gxpyp =
1010                       g_component(g, g_fem, context.get(), var_component,
1011                                   nxpyp, time);
1012                     Number gxzplus = (gxpyp(2) - gxmyp(2))
1013                       / 2. / TOLERANCE;
1014                     Number gxzminus = (gxpym(2) - gxmym(2))
1015                       / 2. / TOLERANCE;
1016                     // xyz derivative
1017                     Ue(current_dof) = (gxzplus - gxzminus)
1018                       / 2. / TOLERANCE;
1019                     dof_is_fixed[current_dof] = true;
1020                     current_dof++;
1021                   }
1022               }
1023           }
1024         // Assume that other C_ONE elements have a single nodal
1025         // value shape function and nodal gradient component
1026         // shape functions
1027         else if (cont == C_ONE)
1028           {
1029             libmesh_assert_equal_to (nc, 1 + dim);
1030             Ue(current_dof) =
1031               f_component(f, f_fem, context.get(), var_component,
1032                           elem->point(n), time);
1033             dof_is_fixed[current_dof] = true;
1034             current_dof++;
1035             Gradient grad =
1036               g_component(g, g_fem, context.get(), var_component,
1037                           elem->point(n), time);
1038             for (unsigned int i=0; i!= dim; ++i)
1039               {
1040                 Ue(current_dof) = grad(i);
1041                 dof_is_fixed[current_dof] = true;
1042                 current_dof++;
1043               }
1044           }
1045         else
1046           libmesh_error_msg("Unknown continuity cont = " << cont);
1047       } // end for (n=0..n_nodes)
1048 
1049         // In 3D, project any edge values next
1050     if (dim > 2 && cont != DISCONTINUOUS)
1051       {
1052         // Get a pointer to the 1 dimensional (edge) FE for the current
1053         // var which is stored in the fem_context. This will only be
1054         // different from side_fe in 3D.
1055         FEGenericBase<OutputType> * edge_fe = nullptr;
1056         fem_context.get_edge_fe(var, edge_fe);
1057 
1058         // Set tolerance on underlying FEMap object. This will allow us to
1059         // avoid spurious negative Jacobian errors while imposing BCs by
1060         // simply ignoring them. This should only be required in certain
1061         // special cases, see the DirichletBoundaries comments on this
1062         // parameter for more information.
1063         edge_fe->get_fe_map().set_jacobian_tolerance(dirichlet.jacobian_tolerance);
1064 
1065         // Pre-request FE data
1066         const std::vector<std::vector<OutputShape>> & phi = edge_fe->get_phi();
1067         const std::vector<Point> & xyz_values = edge_fe->get_xyz();
1068         const std::vector<Real> & JxW = edge_fe->get_JxW();
1069 
1070         // Only pre-request gradients for C1 projections
1071         const std::vector<std::vector<OutputGradient>> * dphi = nullptr;
1072         if ((cont == C_ONE) && (fe_type.family != SUBDIVISION))
1073           {
1074             const std::vector<std::vector<OutputGradient>> & ref_dphi = edge_fe->get_dphi();
1075             dphi = &ref_dphi;
1076           }
1077 
1078         // Vector to hold edge local DOF indices
1079         std::vector<unsigned int> edge_dofs;
1080 
1081         // Get a reference to the "is_boundary_edge" flags for the
1082         // current DirichletBoundary object.  In case the map does not
1083         // contain an entry for this DirichletBoundary object, it
1084         // means there are no boundary edges active.
1085         auto is_boundary_edge_it = sebi.is_boundary_edge_map.find(&dirichlet);
1086 
1087         for (unsigned int e=0; e != sebi.n_edges; ++e)
1088           {
1089             if (is_boundary_edge_it == sebi.is_boundary_edge_map.end() ||
1090                 !is_boundary_edge_it->second[e])
1091               continue;
1092 
1093             FEInterface::dofs_on_edge(elem, dim, fe_type, e,
1094                                       edge_dofs);
1095 
1096             const unsigned int n_edge_dofs =
1097               cast_int<unsigned int>(edge_dofs.size());
1098 
1099             // Some edge dofs are on nodes and already
1100             // fixed, others are free to calculate
1101             unsigned int free_dofs = 0;
1102             for (unsigned int i=0; i != n_edge_dofs; ++i)
1103               if (!dof_is_fixed[edge_dofs[i]])
1104                 free_dof[free_dofs++] = i;
1105 
1106             // There may be nothing to project
1107             if (!free_dofs)
1108               continue;
1109 
1110             Ke.resize (free_dofs, free_dofs); Ke.zero();
1111             Fe.resize (free_dofs); Fe.zero();
1112             // The new edge coefficients
1113             DenseVector<Number> Uedge(free_dofs);
1114 
1115             // Initialize FE data on the edge
1116             edge_fe->edge_reinit(elem, e);
1117             const unsigned int n_qp = fem_context.get_edge_qrule().n_points();
1118 
1119             // Loop over the quadrature points
1120             for (unsigned int qp=0; qp<n_qp; qp++)
1121               {
1122                 // solution at the quadrature point
1123                 OutputNumber fineval(0);
1124                 libMesh::RawAccessor<OutputNumber> f_accessor( fineval, dim );
1125 
1126                 for (unsigned int c = 0; c < n_vec_dim; c++)
1127                   f_accessor(c) =
1128                     f_component(f, f_fem, context.get(), var_component+c,
1129                                 xyz_values[qp], time);
1130 
1131                 // solution grad at the quadrature point
1132                 OutputNumberGradient finegrad;
1133                 libMesh::RawAccessor<OutputNumberGradient> g_accessor( finegrad, dim );
1134 
1135                 unsigned int g_rank;
1136                 switch( FEInterface::field_type( fe_type ) )
1137                   {
1138                   case TYPE_SCALAR:
1139                     {
1140                       g_rank = 1;
1141                       break;
1142                     }
1143                   case TYPE_VECTOR:
1144                     {
1145                       g_rank = 2;
1146                       break;
1147                     }
1148                   default:
1149                     libmesh_error_msg("Unknown field type!");
1150                   }
1151 
1152                 if (cont == C_ONE)
1153                   for (unsigned int c = 0; c < n_vec_dim; c++)
1154                     for (unsigned int d = 0; d < g_rank; d++)
1155                       g_accessor(c + d*dim ) =
1156                         g_component(g, g_fem, context.get(), var_component,
1157                                     xyz_values[qp], time)(c);
1158 
1159                 // Form edge projection matrix
1160                 for (unsigned int sidei=0, freei=0; sidei != n_edge_dofs; ++sidei)
1161                   {
1162                     unsigned int i = edge_dofs[sidei];
1163                     // fixed DoFs aren't test functions
1164                     if (dof_is_fixed[i])
1165                       continue;
1166                     for (unsigned int sidej=0, freej=0; sidej != n_edge_dofs; ++sidej)
1167                       {
1168                         unsigned int j = edge_dofs[sidej];
1169                         if (dof_is_fixed[j])
1170                           Fe(freei) -= phi[i][qp] * phi[j][qp] *
1171                             JxW[qp] * Ue(j);
1172                         else
1173                           Ke(freei,freej) += phi[i][qp] *
1174                             phi[j][qp] * JxW[qp];
1175                         if (cont == C_ONE)
1176                           {
1177                             if (dof_is_fixed[j])
1178                               Fe(freei) -= ((*dphi)[i][qp].contract((*dphi)[j][qp]) ) *
1179                                 JxW[qp] * Ue(j);
1180                             else
1181                               Ke(freei,freej) += ((*dphi)[i][qp].contract((*dphi)[j][qp]))
1182                                 * JxW[qp];
1183                           }
1184                         if (!dof_is_fixed[j])
1185                           freej++;
1186                       }
1187                     Fe(freei) += phi[i][qp] * fineval * JxW[qp];
1188                     if (cont == C_ONE)
1189                       Fe(freei) += (finegrad.contract( (*dphi)[i][qp]) ) *
1190                         JxW[qp];
1191                     freei++;
1192                   }
1193               }
1194 
1195             Ke.cholesky_solve(Fe, Uedge);
1196 
1197             // Transfer new edge solutions to element
1198             for (unsigned int i=0; i != free_dofs; ++i)
1199               {
1200                 Number & ui = Ue(edge_dofs[free_dof[i]]);
1201                 libmesh_assert(std::abs(ui) < TOLERANCE ||
1202                                std::abs(ui - Uedge(i)) < TOLERANCE);
1203                 ui = Uedge(i);
1204                 dof_is_fixed[edge_dofs[free_dof[i]]] = true;
1205               }
1206           } // end for (e = 0..n_edges)
1207       } // end if (dim > 2 && cont != DISCONTINUOUS)
1208 
1209         // Project any side values (edges in 2D, faces in 3D)
1210     if (dim > 1 && cont != DISCONTINUOUS)
1211       {
1212         FEGenericBase<OutputType> * side_fe = nullptr;
1213         fem_context.get_side_fe(var, side_fe);
1214 
1215         // Set tolerance on underlying FEMap object. This will allow us to
1216         // avoid spurious negative Jacobian errors while imposing BCs by
1217         // simply ignoring them. This should only be required in certain
1218         // special cases, see the DirichletBoundaries comments on this
1219         // parameter for more information.
1220         side_fe->get_fe_map().set_jacobian_tolerance(dirichlet.jacobian_tolerance);
1221 
1222         // Pre-request FE data
1223         const std::vector<std::vector<OutputShape>> & phi = side_fe->get_phi();
1224         const std::vector<Point> & xyz_values = side_fe->get_xyz();
1225         const std::vector<Real> & JxW = side_fe->get_JxW();
1226 
1227         // Only pre-request gradients for C1 projections
1228         const std::vector<std::vector<OutputGradient>> * dphi = nullptr;
1229         if ((cont == C_ONE) && (fe_type.family != SUBDIVISION))
1230           {
1231             const std::vector<std::vector<OutputGradient>> & ref_dphi = side_fe->get_dphi();
1232             dphi = &ref_dphi;
1233           }
1234 
1235         // Vector to hold side local DOF indices
1236         std::vector<unsigned int> side_dofs;
1237 
1238         // Get a reference to the "is_boundary_side" flags for the
1239         // current DirichletBoundary object.  In case the map does not
1240         // contain an entry for this DirichletBoundary object, it
1241         // means there are no boundary sides active.
1242         auto is_boundary_side_it = sebi.is_boundary_side_map.find(&dirichlet);
1243 
1244         for (unsigned int s=0; s != sebi.n_sides; ++s)
1245           {
1246             if (is_boundary_side_it == sebi.is_boundary_side_map.end() ||
1247                 !is_boundary_side_it->second[s])
1248               continue;
1249 
1250             FEInterface::dofs_on_side(elem, dim, fe_type, s,
1251                                       side_dofs);
1252 
1253             const unsigned int n_side_dofs =
1254               cast_int<unsigned int>(side_dofs.size());
1255 
1256             // Some side dofs are on nodes/edges and already
1257             // fixed, others are free to calculate
1258             unsigned int free_dofs = 0;
1259             for (unsigned int i=0; i != n_side_dofs; ++i)
1260               if (!dof_is_fixed[side_dofs[i]])
1261                 free_dof[free_dofs++] = i;
1262 
1263             // There may be nothing to project
1264             if (!free_dofs)
1265               continue;
1266 
1267             Ke.resize (free_dofs, free_dofs); Ke.zero();
1268             Fe.resize (free_dofs); Fe.zero();
1269             // The new side coefficients
1270             DenseVector<Number> Uside(free_dofs);
1271 
1272             // Initialize FE data on the side
1273             side_fe->reinit(elem, s);
1274             const unsigned int n_qp = fem_context.get_side_qrule().n_points();
1275 
1276             // Loop over the quadrature points
1277             for (unsigned int qp=0; qp<n_qp; qp++)
1278               {
1279                 // solution at the quadrature point
1280                 OutputNumber fineval(0);
1281                 libMesh::RawAccessor<OutputNumber> f_accessor( fineval, dim );
1282 
1283                 for (unsigned int c = 0; c < n_vec_dim; c++)
1284                   f_accessor(c) =
1285                     f_component(f, f_fem, context.get(), var_component+c,
1286                                 xyz_values[qp], time);
1287 
1288                 // solution grad at the quadrature point
1289                 OutputNumberGradient finegrad;
1290                 libMesh::RawAccessor<OutputNumberGradient> g_accessor( finegrad, dim );
1291 
1292                 unsigned int g_rank;
1293                 switch( FEInterface::field_type( fe_type ) )
1294                   {
1295                   case TYPE_SCALAR:
1296                     {
1297                       g_rank = 1;
1298                       break;
1299                     }
1300                   case TYPE_VECTOR:
1301                     {
1302                       g_rank = 2;
1303                       break;
1304                     }
1305                   default:
1306                     libmesh_error_msg("Unknown field type!");
1307                   }
1308 
1309                 if (cont == C_ONE)
1310                   for (unsigned int c = 0; c < n_vec_dim; c++)
1311                     for (unsigned int d = 0; d < g_rank; d++)
1312                       g_accessor(c + d*dim ) =
1313                         g_component(g, g_fem, context.get(), var_component,
1314                                     xyz_values[qp], time)(c);
1315 
1316                 // Form side projection matrix
1317                 for (unsigned int sidei=0, freei=0; sidei != n_side_dofs; ++sidei)
1318                   {
1319                     unsigned int i = side_dofs[sidei];
1320                     // fixed DoFs aren't test functions
1321                     if (dof_is_fixed[i])
1322                       continue;
1323                     for (unsigned int sidej=0, freej=0; sidej != n_side_dofs; ++sidej)
1324                       {
1325                         unsigned int j = side_dofs[sidej];
1326                         if (dof_is_fixed[j])
1327                           Fe(freei) -= phi[i][qp] * phi[j][qp] *
1328                             JxW[qp] * Ue(j);
1329                         else
1330                           Ke(freei,freej) += phi[i][qp] *
1331                             phi[j][qp] * JxW[qp];
1332                         if (cont == C_ONE)
1333                           {
1334                             if (dof_is_fixed[j])
1335                               Fe(freei) -= ((*dphi)[i][qp].contract((*dphi)[j][qp])) *
1336                                 JxW[qp] * Ue(j);
1337                             else
1338                               Ke(freei,freej) += ((*dphi)[i][qp].contract((*dphi)[j][qp]))
1339                                 * JxW[qp];
1340                           }
1341                         if (!dof_is_fixed[j])
1342                           freej++;
1343                       }
1344                     Fe(freei) += (fineval * phi[i][qp]) * JxW[qp];
1345                     if (cont == C_ONE)
1346                       Fe(freei) += (finegrad.contract((*dphi)[i][qp])) *
1347                         JxW[qp];
1348                     freei++;
1349                   }
1350               }
1351 
1352             Ke.cholesky_solve(Fe, Uside);
1353 
1354             // Transfer new side solutions to element
1355             for (unsigned int i=0; i != free_dofs; ++i)
1356               {
1357                 Number & ui = Ue(side_dofs[free_dof[i]]);
1358 
1359                 libmesh_assert(std::abs(ui) < TOLERANCE ||
1360                                std::abs(ui - Uside(i)) < TOLERANCE);
1361                 ui = Uside(i);
1362 
1363                 dof_is_fixed[side_dofs[free_dof[i]]] = true;
1364               }
1365           } // end for (s = 0..n_sides)
1366       } // end if (dim > 1 && cont != DISCONTINUOUS)
1367 
1368         // Project any shellface values
1369     if (dim == 2 && cont != DISCONTINUOUS)
1370       {
1371         FEGenericBase<OutputType> * fe = nullptr;
1372         fem_context.get_element_fe(var, fe, dim);
1373 
1374         // Set tolerance on underlying FEMap object. This will allow us to
1375         // avoid spurious negative Jacobian errors while imposing BCs by
1376         // simply ignoring them. This should only be required in certain
1377         // special cases, see the DirichletBoundaries comments on this
1378         // parameter for more information.
1379         fe->get_fe_map().set_jacobian_tolerance(dirichlet.jacobian_tolerance);
1380 
1381         // Pre-request FE data
1382         const std::vector<std::vector<OutputShape>> & phi = fe->get_phi();
1383         const std::vector<Point> & xyz_values = fe->get_xyz();
1384         const std::vector<Real> & JxW = fe->get_JxW();
1385 
1386         // Only pre-request gradients for C1 projections
1387         const std::vector<std::vector<OutputGradient>> * dphi = nullptr;
1388         if ((cont == C_ONE) && (fe_type.family != SUBDIVISION))
1389           {
1390             const std::vector<std::vector<OutputGradient>> & ref_dphi = fe->get_dphi();
1391             dphi = &ref_dphi;
1392           }
1393 
1394         // Get a reference to the "is_boundary_shellface" flags for the
1395         // current DirichletBoundary object.  In case the map does not
1396         // contain an entry for this DirichletBoundary object, it
1397         // means there are no boundary shellfaces active.
1398         auto is_boundary_shellface_it = sebi.is_boundary_shellface_map.find(&dirichlet);
1399 
1400         for (unsigned int shellface=0; shellface != 2; ++shellface)
1401           {
1402             if (is_boundary_shellface_it == sebi.is_boundary_shellface_map.end() ||
1403                 !is_boundary_shellface_it->second[shellface])
1404               continue;
1405 
1406             // A shellface has the same dof indices as the element itself
1407             std::vector<unsigned int> shellface_dofs(n_dofs);
1408             std::iota(shellface_dofs.begin(), shellface_dofs.end(), 0);
1409 
1410             // Some shellface dofs are on nodes/edges and already
1411             // fixed, others are free to calculate
1412             unsigned int free_dofs = 0;
1413             for (unsigned int i=0; i != n_dofs; ++i)
1414               if (!dof_is_fixed[shellface_dofs[i]])
1415                 free_dof[free_dofs++] = i;
1416 
1417             // There may be nothing to project
1418             if (!free_dofs)
1419               continue;
1420 
1421             Ke.resize (free_dofs, free_dofs); Ke.zero();
1422             Fe.resize (free_dofs); Fe.zero();
1423             // The new shellface coefficients
1424             DenseVector<Number> Ushellface(free_dofs);
1425 
1426             // Initialize FE data on the element
1427             fe->reinit (elem);
1428             const unsigned int n_qp = fem_context.get_element_qrule().n_points();
1429 
1430             // Loop over the quadrature points
1431             for (unsigned int qp=0; qp<n_qp; qp++)
1432               {
1433                 // solution at the quadrature point
1434                 OutputNumber fineval(0);
1435                 libMesh::RawAccessor<OutputNumber> f_accessor( fineval, dim );
1436 
1437                 for (unsigned int c = 0; c < n_vec_dim; c++)
1438                   f_accessor(c) =
1439                     f_component(f, f_fem, context.get(), var_component+c,
1440                                 xyz_values[qp], time);
1441 
1442                 // solution grad at the quadrature point
1443                 OutputNumberGradient finegrad;
1444                 libMesh::RawAccessor<OutputNumberGradient> g_accessor( finegrad, dim );
1445 
1446                 unsigned int g_rank;
1447                 switch( FEInterface::field_type( fe_type ) )
1448                   {
1449                   case TYPE_SCALAR:
1450                     {
1451                       g_rank = 1;
1452                       break;
1453                     }
1454                   case TYPE_VECTOR:
1455                     {
1456                       g_rank = 2;
1457                       break;
1458                     }
1459                   default:
1460                     libmesh_error_msg("Unknown field type!");
1461                   }
1462 
1463                 if (cont == C_ONE)
1464                   for (unsigned int c = 0; c < n_vec_dim; c++)
1465                     for (unsigned int d = 0; d < g_rank; d++)
1466                       g_accessor(c + d*dim ) =
1467                         g_component(g, g_fem, context.get(), var_component,
1468                                     xyz_values[qp], time)(c);
1469 
1470                 // Form shellface projection matrix
1471                 for (unsigned int shellfacei=0, freei=0;
1472                      shellfacei != n_dofs; ++shellfacei)
1473                   {
1474                     unsigned int i = shellface_dofs[shellfacei];
1475                     // fixed DoFs aren't test functions
1476                     if (dof_is_fixed[i])
1477                       continue;
1478                     for (unsigned int shellfacej=0, freej=0;
1479                          shellfacej != n_dofs; ++shellfacej)
1480                       {
1481                         unsigned int j = shellface_dofs[shellfacej];
1482                         if (dof_is_fixed[j])
1483                           Fe(freei) -= phi[i][qp] * phi[j][qp] *
1484                             JxW[qp] * Ue(j);
1485                         else
1486                           Ke(freei,freej) += phi[i][qp] *
1487                             phi[j][qp] * JxW[qp];
1488                         if (cont == C_ONE)
1489                           {
1490                             if (dof_is_fixed[j])
1491                               Fe(freei) -= ((*dphi)[i][qp].contract((*dphi)[j][qp])) *
1492                                 JxW[qp] * Ue(j);
1493                             else
1494                               Ke(freei,freej) += ((*dphi)[i][qp].contract((*dphi)[j][qp]))
1495                                 * JxW[qp];
1496                           }
1497                         if (!dof_is_fixed[j])
1498                           freej++;
1499                       }
1500                     Fe(freei) += (fineval * phi[i][qp]) * JxW[qp];
1501                     if (cont == C_ONE)
1502                       Fe(freei) += (finegrad.contract((*dphi)[i][qp])) *
1503                         JxW[qp];
1504                     freei++;
1505                   }
1506               }
1507 
1508             Ke.cholesky_solve(Fe, Ushellface);
1509 
1510             // Transfer new shellface solutions to element
1511             for (unsigned int i=0; i != free_dofs; ++i)
1512               {
1513                 Number & ui = Ue(shellface_dofs[free_dof[i]]);
1514                 libmesh_assert(std::abs(ui) < TOLERANCE ||
1515                                std::abs(ui - Ushellface(i)) < TOLERANCE);
1516                 ui = Ushellface(i);
1517                 dof_is_fixed[shellface_dofs[free_dof[i]]] = true;
1518               }
1519           } // end for (shellface = 0..2)
1520       } // end if (dim == 2 && cont != DISCONTINUOUS)
1521 
1522     // Lock the DofConstraints since it is shared among threads.
1523     {
1524       Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
1525 
1526       for (unsigned int i = 0; i < n_dofs; i++)
1527         {
1528           DofConstraintRow empty_row;
1529           if (dof_is_fixed[i] && !libmesh_isnan(Ue(i)))
1530             add_fn (dof_indices[i], empty_row, Ue(i));
1531         }
1532     }
1533   } // apply_dirichlet_impl
1534 
1535 public:
ConstrainDirichlet(DofMap & dof_map_in,const MeshBase & mesh_in,const Real time_in,const DirichletBoundaries & dirichlets_in,const AddConstraint & add_in)1536   ConstrainDirichlet (DofMap & dof_map_in,
1537                       const MeshBase & mesh_in,
1538                       const Real time_in,
1539                       const DirichletBoundaries & dirichlets_in,
1540                       const AddConstraint & add_in) :
1541     dof_map(dof_map_in),
1542     mesh(mesh_in),
1543     time(time_in),
1544     dirichlets(dirichlets_in),
1545     add_fn(add_in) { }
1546 
1547   // This class can be default copy/move constructed.
1548   ConstrainDirichlet (ConstrainDirichlet &&) = default;
1549   ConstrainDirichlet (const ConstrainDirichlet &) = default;
1550 
1551   // This class cannot be default copy/move assigned because it
1552   // contains reference members.
1553   ConstrainDirichlet & operator= (const ConstrainDirichlet &) = delete;
1554   ConstrainDirichlet & operator= (ConstrainDirichlet &&) = delete;
1555 
operator()1556   void operator()(const ConstElemRange & range) const
1557   {
1558     /**
1559      * This method examines an arbitrary boundary solution to calculate
1560      * corresponding Dirichlet constraints on the current mesh.  The
1561      * input function \p f gives the arbitrary solution.
1562      */
1563 
1564     // Check for a quick return in case there's no work to be done.
1565     if (dirichlets.empty())
1566       return;
1567 
1568     // Figure out which System the DirichletBoundary objects are
1569     // defined for. We break out of the loop as soon as we encounter a
1570     // valid System pointer, the assumption is thus that all Variables
1571     // are defined on the same System.
1572     System * system = nullptr;
1573 
1574     // Map from boundary_id -> set<pair<id,DirichletBoundary*>> objects which
1575     // are active on that boundary_id. Later we will use this to determine
1576     // which DirichletBoundary objects to loop over for each Elem.
1577     std::map<boundary_id_type, std::set<std::pair<unsigned int, DirichletBoundary *>>>
1578       boundary_id_to_ordered_dirichlet_boundaries;
1579 
1580     for (auto dirichlet_id : index_range(dirichlets))
1581       {
1582         // Pointer to the current DirichletBoundary object
1583         const auto & dirichlet = dirichlets[dirichlet_id];
1584 
1585         // Construct mapping from boundary_id -> (dirichlet_id, DirichletBoundary)
1586         for (const auto & b_id : dirichlet->b)
1587           boundary_id_to_ordered_dirichlet_boundaries[b_id].emplace(dirichlet_id, dirichlet);
1588 
1589         for (const auto & var : dirichlet->variables)
1590           {
1591             const Variable & variable = dof_map.variable(var);
1592             auto current_system = variable.system();
1593 
1594             if (!system)
1595               system = current_system;
1596             else
1597               libmesh_error_msg_if(current_system != system,
1598                                    "All variables should be defined on the same System");
1599           }
1600       }
1601 
1602     // If we found no System, it could be because none of the
1603     // Variables have one defined, or because there are
1604     // DirichletBoundary objects with no Variables defined on
1605     // them. These situations both indicate a likely error in the
1606     // setup of a problem, so let's throw an error in this case.
1607     libmesh_error_msg_if(!system, "Valid System not found for any Variables.");
1608 
1609     // Construct a FEMContext object for the System on which the
1610     // Variables in our DirichletBoundary objects are defined. This
1611     // will be used in the apply_dirichlet_impl() function.
1612     auto fem_context = libmesh_make_unique<FEMContext>(*system);
1613 
1614     // At the time we are using this FEMContext, the current_local_solution
1615     // vector is not initialized, but also we don't need it, so set
1616     // the algebraic_type flag to DOFS_ONLY.
1617     fem_context->set_algebraic_type(FEMContext::DOFS_ONLY);
1618 
1619     // Boundary info for the current mesh
1620     const BoundaryInfo & boundary_info = mesh.get_boundary_info();
1621 
1622     // This object keeps track of the BoundaryInfo for a single Elem
1623     SingleElemBoundaryInfo sebi(boundary_info, boundary_id_to_ordered_dirichlet_boundaries);
1624 
1625     // Iterate over all the elements in the range
1626     for (const auto & elem : range)
1627       {
1628         // We only calculate Dirichlet constraints on active
1629         // elements
1630         if (!elem->active())
1631           continue;
1632 
1633         // Reinitialize BoundaryInfo data structures for the current elem
1634         bool has_dirichlet_constraint = sebi.reinit(elem);
1635 
1636         // If this Elem has no boundary ids, go to the next one.
1637         if (!has_dirichlet_constraint)
1638           continue;
1639 
1640         for (const auto & db_pair : sebi.ordered_dbs)
1641           {
1642             // Get pointer to the DirichletBoundary object
1643             const auto & dirichlet = db_pair.second;
1644 
1645             // TODO: Add sanity check that the boundary ids associated
1646             // with the DirichletBoundary objects are actually present in
1647             // the mesh. Currently this is a private function on DofMap so
1648             // we can't call it here, but maybe it could be made public.
1649             // dof_map.check_dirichlet_bcid_consistency(mesh, *dirichlet);
1650 
1651             // Loop over all the variables which this DirichletBoundary object is responsible for
1652             for (const auto & var : dirichlet->variables)
1653               {
1654                 const Variable & variable = dof_map.variable(var);
1655 
1656                 // Make sure that the Variable and the DofMap agree on
1657                 // what number this variable is.
1658                 libmesh_assert_equal_to(variable.number(), var);
1659 
1660                 const FEType & fe_type = variable.type();
1661 
1662                 if (fe_type.family == SCALAR)
1663                   continue;
1664 
1665                 switch( FEInterface::field_type( fe_type ) )
1666                   {
1667                   case TYPE_SCALAR:
1668                     {
1669                       // For Lagrange FEs we don't need to do a full
1670                       // blown projection, we can just interpolate
1671                       // values directly.
1672                       if (fe_type.family == LAGRANGE)
1673                         this->apply_lagrange_dirichlet_impl<Real>( sebi, variable, *dirichlet, *fem_context );
1674                       else
1675                         this->apply_dirichlet_impl<Real>( sebi, variable, *dirichlet, *fem_context );
1676                       break;
1677                     }
1678                   case TYPE_VECTOR:
1679                     {
1680                       this->apply_dirichlet_impl<RealGradient>( sebi, variable, *dirichlet, *fem_context );
1681                       break;
1682                     }
1683                   default:
1684                     libmesh_error_msg("Unknown field type!");
1685                   }
1686               } // for (var : variables)
1687           } // for (db_pair : ordered_dbs)
1688       } // for (elem : range)
1689   } // operator()
1690 
1691 }; // class ConstrainDirichlet
1692 
1693 
1694 #endif // LIBMESH_ENABLE_DIRICHLET
1695 
1696 
1697 } // anonymous namespace
1698 
1699 
1700 
1701 namespace libMesh
1702 {
1703 
1704 // ------------------------------------------------------------
1705 // DofMap member functions
1706 
1707 #ifdef LIBMESH_ENABLE_CONSTRAINTS
1708 
1709 
n_constrained_dofs()1710 dof_id_type DofMap::n_constrained_dofs() const
1711 {
1712   parallel_object_only();
1713 
1714   dof_id_type nc_dofs = this->n_local_constrained_dofs();
1715   this->comm().sum(nc_dofs);
1716   return nc_dofs;
1717 }
1718 
1719 
n_local_constrained_dofs()1720 dof_id_type DofMap::n_local_constrained_dofs() const
1721 {
1722   const DofConstraints::const_iterator lower =
1723     _dof_constraints.lower_bound(this->first_dof()),
1724     upper =
1725     _dof_constraints.upper_bound(this->end_dof()-1);
1726 
1727   return cast_int<dof_id_type>(std::distance(lower, upper));
1728 }
1729 
1730 
1731 
create_dof_constraints(const MeshBase & mesh,Real time)1732 void DofMap::create_dof_constraints(const MeshBase & mesh, Real time)
1733 {
1734   parallel_object_only();
1735 
1736   LOG_SCOPE("create_dof_constraints()", "DofMap");
1737 
1738   libmesh_assert (mesh.is_prepared());
1739 
1740   // The user might have set boundary conditions after the mesh was
1741   // prepared; we should double-check that those boundary conditions
1742   // are still consistent.
1743 #ifdef DEBUG
1744   MeshTools::libmesh_assert_valid_boundary_ids(mesh);
1745 #endif
1746 
1747   // We might get constraint equations from AMR hanging nodes in 2D/3D
1748   // or from boundary conditions in any dimension
1749   const bool possible_local_constraints = false
1750     || !mesh.n_elem()
1751 #ifdef LIBMESH_ENABLE_AMR
1752     || mesh.mesh_dimension() > 1
1753 #endif
1754 #ifdef LIBMESH_ENABLE_PERIODIC
1755     || !_periodic_boundaries->empty()
1756 #endif
1757 #ifdef LIBMESH_ENABLE_DIRICHLET
1758     || !_dirichlet_boundaries->empty()
1759 #endif
1760     ;
1761 
1762   // Even if we don't have constraints, another processor might.
1763   bool possible_global_constraints = possible_local_constraints;
1764 #if defined(LIBMESH_ENABLE_PERIODIC) || defined(LIBMESH_ENABLE_DIRICHLET) || defined(LIBMESH_ENABLE_AMR)
1765   libmesh_assert(this->comm().verify(mesh.is_serial()));
1766 
1767   this->comm().max(possible_global_constraints);
1768 #endif
1769 
1770   if (!possible_global_constraints)
1771     {
1772       // Clear out any old constraints; maybe the user just deleted
1773       // their last remaining dirichlet/periodic/user constraint?
1774       // Note: any _stashed_dof_constraints are not cleared as it
1775       // may be the user's intention to restore them later.
1776 #ifdef LIBMESH_ENABLE_CONSTRAINTS
1777       _dof_constraints.clear();
1778       _primal_constraint_values.clear();
1779       _adjoint_constraint_values.clear();
1780 #endif
1781 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS
1782       _node_constraints.clear();
1783 #endif
1784 
1785       return;
1786     }
1787 
1788   // Here we build the hanging node constraints.  This is done
1789   // by enforcing the condition u_a = u_b along hanging sides.
1790   // u_a = u_b is collocated at the nodes of side a, which gives
1791   // one row of the constraint matrix.
1792 
1793   // Processors only compute their local constraints
1794   ConstElemRange range (mesh.local_elements_begin(),
1795                         mesh.local_elements_end());
1796 
1797   // Global computation fails if we're using a FEMFunctionBase BC on a
1798   // ReplicatedMesh in parallel
1799   // ConstElemRange range (mesh.elements_begin(),
1800   //                       mesh.elements_end());
1801 
1802   // compute_periodic_constraints requires a point_locator() from our
1803   // Mesh, but point_locator() construction is parallel and threaded.
1804   // Rather than nest threads within threads we'll make sure it's
1805   // preconstructed.
1806 #ifdef LIBMESH_ENABLE_PERIODIC
1807   bool need_point_locator = !_periodic_boundaries->empty() && !range.empty();
1808 
1809   this->comm().max(need_point_locator);
1810 
1811   if (need_point_locator)
1812     mesh.sub_point_locator();
1813 #endif
1814 
1815 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS
1816   // recalculate node constraints from scratch
1817   _node_constraints.clear();
1818 
1819   Threads::parallel_for (range,
1820                          ComputeNodeConstraints (_node_constraints,
1821 #ifdef LIBMESH_ENABLE_PERIODIC
1822                                                  *_periodic_boundaries,
1823 #endif
1824                                                  mesh));
1825 #endif // LIBMESH_ENABLE_NODE_CONSTRAINTS
1826 
1827 
1828   // recalculate dof constraints from scratch
1829   // Note: any _stashed_dof_constraints are not cleared as it
1830   // may be the user's intention to restore them later.
1831   _dof_constraints.clear();
1832   _primal_constraint_values.clear();
1833   _adjoint_constraint_values.clear();
1834 
1835   // Look at all the variables in the system.  Reset the element
1836   // range at each iteration -- there is no need to reconstruct it.
1837   for (unsigned int variable_number=0; variable_number<this->n_variables();
1838        ++variable_number, range.reset())
1839     Threads::parallel_for (range,
1840                            ComputeConstraints (_dof_constraints,
1841                                                *this,
1842 #ifdef LIBMESH_ENABLE_PERIODIC
1843                                                *_periodic_boundaries,
1844 #endif
1845                                                mesh,
1846                                                variable_number));
1847 
1848 #ifdef LIBMESH_ENABLE_DIRICHLET
1849 
1850   // Threaded loop over local over elems applying all Dirichlet BCs
1851   Threads::parallel_for
1852     (range,
1853      ConstrainDirichlet(*this, mesh, time, *_dirichlet_boundaries,
1854                         AddPrimalConstraint(*this)));
1855 
1856   // Threaded loop over local over elems per QOI applying all adjoint
1857   // Dirichlet BCs.  Note that the ConstElemRange is reset before each
1858   // execution of Threads::parallel_for().
1859 
1860   for (auto qoi_index : index_range(_adjoint_dirichlet_boundaries))
1861     {
1862       Threads::parallel_for
1863         (range.reset(),
1864          ConstrainDirichlet(*this, mesh, time, *(_adjoint_dirichlet_boundaries[qoi_index]),
1865                             AddAdjointConstraint(*this, qoi_index)));
1866     }
1867 
1868 #endif // LIBMESH_ENABLE_DIRICHLET
1869 }
1870 
1871 
1872 
add_constraint_row(const dof_id_type dof_number,const DofConstraintRow & constraint_row,const Number constraint_rhs,const bool forbid_constraint_overwrite)1873 void DofMap::add_constraint_row (const dof_id_type dof_number,
1874                                  const DofConstraintRow & constraint_row,
1875                                  const Number constraint_rhs,
1876                                  const bool forbid_constraint_overwrite)
1877 {
1878   // Optionally allow the user to overwrite constraints.  Defaults to false.
1879   libmesh_error_msg_if(forbid_constraint_overwrite && this->is_constrained_dof(dof_number),
1880                        "ERROR: DOF " << dof_number << " was already constrained!");
1881 
1882   libmesh_assert_less(dof_number, this->n_dofs());
1883 
1884   // There is an implied "1" on the diagonal of the constraint row, and the user
1885   // should not try to manually set _any_ value on the diagonal.
1886   libmesh_assert_msg(!constraint_row.count(dof_number),
1887                      "Error: constraint_row for dof " << dof_number <<
1888                      " should not contain an entry for dof " << dof_number);
1889 
1890 #ifndef NDEBUG
1891   for (const auto & pr : constraint_row)
1892     libmesh_assert_less(pr.first, this->n_dofs());
1893 #endif
1894 
1895   // We don't get insert_or_assign until C++17 so we make do.
1896   std::pair<DofConstraints::iterator, bool> it =
1897     _dof_constraints.emplace(dof_number, constraint_row);
1898   if (!it.second)
1899     it.first->second = constraint_row;
1900 
1901   std::pair<DofConstraintValueMap::iterator, bool> rhs_it =
1902     _primal_constraint_values.emplace(dof_number, constraint_rhs);
1903   if (!rhs_it.second)
1904     rhs_it.first->second = constraint_rhs;
1905 }
1906 
1907 
add_adjoint_constraint_row(const unsigned int qoi_index,const dof_id_type dof_number,const DofConstraintRow &,const Number constraint_rhs,const bool forbid_constraint_overwrite)1908 void DofMap::add_adjoint_constraint_row (const unsigned int qoi_index,
1909                                          const dof_id_type dof_number,
1910                                          const DofConstraintRow & /*constraint_row*/,
1911                                          const Number constraint_rhs,
1912                                          const bool forbid_constraint_overwrite)
1913 {
1914   // Optionally allow the user to overwrite constraints.  Defaults to false.
1915   if (forbid_constraint_overwrite)
1916     {
1917       libmesh_error_msg_if(!this->is_constrained_dof(dof_number),
1918                            "ERROR: DOF " << dof_number << " has no corresponding primal constraint!");
1919 #ifndef NDEBUG
1920       // No way to do this without a non-normalized tolerance?
1921 
1922       // // If the user passed in more than just the rhs, let's check the
1923       // // coefficients for consistency
1924       // if (!constraint_row.empty())
1925       //   {
1926       //     DofConstraintRow row = _dof_constraints[dof_number];
1927       //     for (const auto & pr : row)
1928       //       libmesh_assert(constraint_row.find(pr.first)->second == pr.second);
1929       //   }
1930       //
1931       // if (_adjoint_constraint_values[qoi_index].find(dof_number) !=
1932       //     _adjoint_constraint_values[qoi_index].end())
1933       //   libmesh_assert_equal_to(_adjoint_constraint_values[qoi_index][dof_number],
1934       //                           constraint_rhs);
1935 
1936 #endif
1937     }
1938 
1939   // Creates the map of rhs values if it doesn't already exist; then
1940   // adds the current value to that map
1941 
1942   // We don't get insert_or_assign until C++17 so we make do.
1943   std::pair<DofConstraintValueMap::iterator, bool> rhs_it =
1944     _adjoint_constraint_values[qoi_index].emplace(dof_number, constraint_rhs);
1945   if (!rhs_it.second)
1946     rhs_it.first->second = constraint_rhs;
1947 }
1948 
1949 
1950 
1951 
print_dof_constraints(std::ostream & os,bool print_nonlocal)1952 void DofMap::print_dof_constraints(std::ostream & os,
1953                                    bool print_nonlocal) const
1954 {
1955   parallel_object_only();
1956 
1957   std::string local_constraints =
1958     this->get_local_constraints(print_nonlocal);
1959 
1960   if (this->processor_id())
1961     {
1962       this->comm().send(0, local_constraints);
1963     }
1964   else
1965     {
1966       os << "Processor 0:\n";
1967       os << local_constraints;
1968 
1969       for (auto p : IntRange<processor_id_type>(1, this->n_processors()))
1970         {
1971           this->comm().receive(p, local_constraints);
1972           os << "Processor " << p << ":\n";
1973           os << local_constraints;
1974         }
1975     }
1976 }
1977 
get_local_constraints(bool print_nonlocal)1978 std::string DofMap::get_local_constraints(bool print_nonlocal) const
1979 {
1980   std::ostringstream os;
1981 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS
1982   if (print_nonlocal)
1983     os << "All ";
1984   else
1985     os << "Local ";
1986 
1987   os << "Node Constraints:"
1988      << std::endl;
1989 
1990   for (const auto & pr : _node_constraints)
1991     {
1992       const Node * node = pr.first;
1993 
1994       // Skip non-local nodes if requested
1995       if (!print_nonlocal &&
1996           node->processor_id() != this->processor_id())
1997         continue;
1998 
1999       const NodeConstraintRow & row = pr.second.first;
2000       const Point & offset = pr.second.second;
2001 
2002       os << "Constraints for Node id " << node->id()
2003          << ": \t";
2004 
2005       for (const auto & item : row)
2006         os << " (" << item.first->id() << "," << item.second << ")\t";
2007 
2008       os << "rhs: " << offset;
2009 
2010       os << std::endl;
2011     }
2012 #endif // LIBMESH_ENABLE_NODE_CONSTRAINTS
2013 
2014   if (print_nonlocal)
2015     os << "All ";
2016   else
2017     os << "Local ";
2018 
2019   os << "DoF Constraints:"
2020      << std::endl;
2021 
2022   for (const auto & pr : _dof_constraints)
2023     {
2024       const dof_id_type i = pr.first;
2025 
2026       // Skip non-local dofs if requested
2027       if (!print_nonlocal && !this->local_index(i))
2028         continue;
2029 
2030       const DofConstraintRow & row = pr.second;
2031       DofConstraintValueMap::const_iterator rhsit =
2032         _primal_constraint_values.find(i);
2033       const Number rhs = (rhsit == _primal_constraint_values.end()) ?
2034         0 : rhsit->second;
2035 
2036       os << "Constraints for DoF " << i
2037          << ": \t";
2038 
2039       for (const auto & item : row)
2040         os << " (" << item.first << "," << item.second << ")\t";
2041 
2042       os << "rhs: " << rhs;
2043       os << std::endl;
2044     }
2045 
2046   for (unsigned int qoi_index = 0,
2047        n_qois = cast_int<unsigned int>(_adjoint_dirichlet_boundaries.size());
2048        qoi_index != n_qois; ++qoi_index)
2049     {
2050       os << "Adjoint " << qoi_index << " DoF rhs values:"
2051          << std::endl;
2052 
2053       AdjointDofConstraintValues::const_iterator adjoint_map_it =
2054         _adjoint_constraint_values.find(qoi_index);
2055 
2056       if (adjoint_map_it != _adjoint_constraint_values.end())
2057         for (const auto & pr : adjoint_map_it->second)
2058           {
2059             const dof_id_type i = pr.first;
2060 
2061             // Skip non-local dofs if requested
2062             if (!print_nonlocal && !this->local_index(i))
2063               continue;
2064 
2065             const Number rhs = pr.second;
2066 
2067             os << "RHS for DoF " << i
2068                << ": " << rhs;
2069 
2070             os << std::endl;
2071           }
2072     }
2073 
2074   return os.str();
2075 }
2076 
2077 
2078 
constrain_element_matrix(DenseMatrix<Number> & matrix,std::vector<dof_id_type> & elem_dofs,bool asymmetric_constraint_rows)2079 void DofMap::constrain_element_matrix (DenseMatrix<Number> & matrix,
2080                                        std::vector<dof_id_type> & elem_dofs,
2081                                        bool asymmetric_constraint_rows) const
2082 {
2083   libmesh_assert_equal_to (elem_dofs.size(), matrix.m());
2084   libmesh_assert_equal_to (elem_dofs.size(), matrix.n());
2085 
2086   // check for easy return
2087   if (this->_dof_constraints.empty())
2088     return;
2089 
2090   // The constrained matrix is built up as C^T K C.
2091   DenseMatrix<Number> C;
2092 
2093 
2094   this->build_constraint_matrix (C, elem_dofs);
2095 
2096   LOG_SCOPE("constrain_elem_matrix()", "DofMap");
2097 
2098   // It is possible that the matrix is not constrained at all.
2099   if ((C.m() == matrix.m()) &&
2100       (C.n() == elem_dofs.size())) // It the matrix is constrained
2101     {
2102       // Compute the matrix-matrix-matrix product C^T K C
2103       matrix.left_multiply_transpose  (C);
2104       matrix.right_multiply (C);
2105 
2106 
2107       libmesh_assert_equal_to (matrix.m(), matrix.n());
2108       libmesh_assert_equal_to (matrix.m(), elem_dofs.size());
2109       libmesh_assert_equal_to (matrix.n(), elem_dofs.size());
2110 
2111 
2112       for (unsigned int i=0,
2113            n_elem_dofs = cast_int<unsigned int>(elem_dofs.size());
2114            i != n_elem_dofs; i++)
2115         // If the DOF is constrained
2116         if (this->is_constrained_dof(elem_dofs[i]))
2117           {
2118             for (auto j : make_range(matrix.n()))
2119               matrix(i,j) = 0.;
2120 
2121             matrix(i,i) = 1.;
2122 
2123             if (asymmetric_constraint_rows)
2124               {
2125                 DofConstraints::const_iterator
2126                   pos = _dof_constraints.find(elem_dofs[i]);
2127 
2128                 libmesh_assert (pos != _dof_constraints.end());
2129 
2130                 const DofConstraintRow & constraint_row = pos->second;
2131 
2132                 // This is an overzealous assertion in the presence of
2133                 // heterogenous constraints: we now can constrain "u_i = c"
2134                 // with no other u_j terms involved.
2135                 //
2136                 // libmesh_assert (!constraint_row.empty());
2137 
2138                 for (const auto & item : constraint_row)
2139                   for (unsigned int j=0; j != n_elem_dofs; j++)
2140                     if (elem_dofs[j] == item.first)
2141                       matrix(i,j) = -item.second;
2142               }
2143           }
2144     } // end if is constrained...
2145 }
2146 
2147 
2148 
constrain_element_matrix_and_vector(DenseMatrix<Number> & matrix,DenseVector<Number> & rhs,std::vector<dof_id_type> & elem_dofs,bool asymmetric_constraint_rows)2149 void DofMap::constrain_element_matrix_and_vector (DenseMatrix<Number> & matrix,
2150                                                   DenseVector<Number> & rhs,
2151                                                   std::vector<dof_id_type> & elem_dofs,
2152                                                   bool asymmetric_constraint_rows) const
2153 {
2154   libmesh_assert_equal_to (elem_dofs.size(), matrix.m());
2155   libmesh_assert_equal_to (elem_dofs.size(), matrix.n());
2156   libmesh_assert_equal_to (elem_dofs.size(), rhs.size());
2157 
2158   // check for easy return
2159   if (this->_dof_constraints.empty())
2160     return;
2161 
2162   // The constrained matrix is built up as C^T K C.
2163   // The constrained RHS is built up as C^T F
2164   DenseMatrix<Number> C;
2165 
2166   this->build_constraint_matrix (C, elem_dofs);
2167 
2168   LOG_SCOPE("cnstrn_elem_mat_vec()", "DofMap");
2169 
2170   // It is possible that the matrix is not constrained at all.
2171   if ((C.m() == matrix.m()) &&
2172       (C.n() == elem_dofs.size())) // It the matrix is constrained
2173     {
2174       // Compute the matrix-matrix-matrix product C^T K C
2175       matrix.left_multiply_transpose  (C);
2176       matrix.right_multiply (C);
2177 
2178 
2179       libmesh_assert_equal_to (matrix.m(), matrix.n());
2180       libmesh_assert_equal_to (matrix.m(), elem_dofs.size());
2181       libmesh_assert_equal_to (matrix.n(), elem_dofs.size());
2182 
2183 
2184       for (unsigned int i=0,
2185            n_elem_dofs = cast_int<unsigned int>(elem_dofs.size());
2186            i != n_elem_dofs; i++)
2187         if (this->is_constrained_dof(elem_dofs[i]))
2188           {
2189             for (auto j : make_range(matrix.n()))
2190               matrix(i,j) = 0.;
2191 
2192             // If the DOF is constrained
2193             matrix(i,i) = 1.;
2194 
2195             // This will put a nonsymmetric entry in the constraint
2196             // row to ensure that the linear system produces the
2197             // correct value for the constrained DOF.
2198             if (asymmetric_constraint_rows)
2199               {
2200                 DofConstraints::const_iterator
2201                   pos = _dof_constraints.find(elem_dofs[i]);
2202 
2203                 libmesh_assert (pos != _dof_constraints.end());
2204 
2205                 const DofConstraintRow & constraint_row = pos->second;
2206 
2207                 // p refinement creates empty constraint rows
2208                 //    libmesh_assert (!constraint_row.empty());
2209 
2210                 for (const auto & item : constraint_row)
2211                   for (unsigned int j=0; j != n_elem_dofs; j++)
2212                     if (elem_dofs[j] == item.first)
2213                       matrix(i,j) = -item.second;
2214               }
2215           }
2216 
2217 
2218       // Compute the matrix-vector product C^T F
2219       DenseVector<Number> old_rhs(rhs);
2220 
2221       // compute matrix/vector product
2222       C.vector_mult_transpose(rhs, old_rhs);
2223     } // end if is constrained...
2224 }
2225 
2226 
2227 
heterogenously_constrain_element_matrix_and_vector(DenseMatrix<Number> & matrix,DenseVector<Number> & rhs,std::vector<dof_id_type> & elem_dofs,bool asymmetric_constraint_rows,int qoi_index)2228 void DofMap::heterogenously_constrain_element_matrix_and_vector (DenseMatrix<Number> & matrix,
2229                                                                  DenseVector<Number> & rhs,
2230                                                                  std::vector<dof_id_type> & elem_dofs,
2231                                                                  bool asymmetric_constraint_rows,
2232                                                                  int qoi_index) const
2233 {
2234   libmesh_assert_equal_to (elem_dofs.size(), matrix.m());
2235   libmesh_assert_equal_to (elem_dofs.size(), matrix.n());
2236   libmesh_assert_equal_to (elem_dofs.size(), rhs.size());
2237 
2238   // check for easy return
2239   if (this->_dof_constraints.empty())
2240     return;
2241 
2242   // The constrained matrix is built up as C^T K C.
2243   // The constrained RHS is built up as C^T (F - K H)
2244   DenseMatrix<Number> C;
2245   DenseVector<Number> H;
2246 
2247   this->build_constraint_matrix_and_vector (C, H, elem_dofs, qoi_index);
2248 
2249   LOG_SCOPE("hetero_cnstrn_elem_mat_vec()", "DofMap");
2250 
2251   // It is possible that the matrix is not constrained at all.
2252   if ((C.m() == matrix.m()) &&
2253       (C.n() == elem_dofs.size())) // It the matrix is constrained
2254     {
2255       // We may have rhs values to use later
2256       const DofConstraintValueMap * rhs_values = nullptr;
2257       if (qoi_index < 0)
2258         rhs_values = &_primal_constraint_values;
2259       else
2260         {
2261           const AdjointDofConstraintValues::const_iterator
2262             it = _adjoint_constraint_values.find(qoi_index);
2263           if (it != _adjoint_constraint_values.end())
2264             rhs_values = &it->second;
2265         }
2266 
2267       // Compute matrix/vector product K H
2268       DenseVector<Number> KH;
2269       matrix.vector_mult(KH, H);
2270 
2271       // Compute the matrix-vector product C^T (F - KH)
2272       DenseVector<Number> F_minus_KH(rhs);
2273       F_minus_KH -= KH;
2274       C.vector_mult_transpose(rhs, F_minus_KH);
2275 
2276       // Compute the matrix-matrix-matrix product C^T K C
2277       matrix.left_multiply_transpose  (C);
2278       matrix.right_multiply (C);
2279 
2280       libmesh_assert_equal_to (matrix.m(), matrix.n());
2281       libmesh_assert_equal_to (matrix.m(), elem_dofs.size());
2282       libmesh_assert_equal_to (matrix.n(), elem_dofs.size());
2283 
2284       for (unsigned int i=0,
2285            n_elem_dofs = cast_int<unsigned int>(elem_dofs.size());
2286            i != n_elem_dofs; i++)
2287         {
2288           const dof_id_type dof_id = elem_dofs[i];
2289 
2290           if (this->is_constrained_dof(dof_id))
2291             {
2292               for (auto j : make_range(matrix.n()))
2293                 matrix(i,j) = 0.;
2294 
2295               // If the DOF is constrained
2296               matrix(i,i) = 1.;
2297 
2298               // This will put a nonsymmetric entry in the constraint
2299               // row to ensure that the linear system produces the
2300               // correct value for the constrained DOF.
2301               if (asymmetric_constraint_rows)
2302                 {
2303                   DofConstraints::const_iterator
2304                     pos = _dof_constraints.find(dof_id);
2305 
2306                   libmesh_assert (pos != _dof_constraints.end());
2307 
2308                   const DofConstraintRow & constraint_row = pos->second;
2309 
2310                   for (const auto & item : constraint_row)
2311                     for (unsigned int j=0; j != n_elem_dofs; j++)
2312                       if (elem_dofs[j] == item.first)
2313                         matrix(i,j) = -item.second;
2314 
2315                   if (rhs_values)
2316                     {
2317                       const DofConstraintValueMap::const_iterator valpos =
2318                         rhs_values->find(dof_id);
2319 
2320                       rhs(i) = (valpos == rhs_values->end()) ?
2321                         0 : valpos->second;
2322                     }
2323                 }
2324               else
2325                 rhs(i) = 0.;
2326             }
2327         }
2328 
2329     } // end if is constrained...
2330 }
2331 
2332 
2333 
heterogenously_constrain_element_vector(const DenseMatrix<Number> & matrix,DenseVector<Number> & rhs,std::vector<dof_id_type> & elem_dofs,bool asymmetric_constraint_rows,int qoi_index)2334 void DofMap::heterogenously_constrain_element_vector (const DenseMatrix<Number> & matrix,
2335                                                       DenseVector<Number> & rhs,
2336                                                       std::vector<dof_id_type> & elem_dofs,
2337                                                       bool asymmetric_constraint_rows,
2338                                                       int qoi_index) const
2339 {
2340   libmesh_assert_equal_to (elem_dofs.size(), matrix.m());
2341   libmesh_assert_equal_to (elem_dofs.size(), matrix.n());
2342   libmesh_assert_equal_to (elem_dofs.size(), rhs.size());
2343 
2344   // check for easy return
2345   if (this->_dof_constraints.empty())
2346     return;
2347 
2348   // The constrained matrix is built up as C^T K C.
2349   // The constrained RHS is built up as C^T (F - K H)
2350   DenseMatrix<Number> C;
2351   DenseVector<Number> H;
2352 
2353   this->build_constraint_matrix_and_vector (C, H, elem_dofs, qoi_index);
2354 
2355   LOG_SCOPE("hetero_cnstrn_elem_vec()", "DofMap");
2356 
2357   // It is possible that the matrix is not constrained at all.
2358   if ((C.m() == matrix.m()) &&
2359       (C.n() == elem_dofs.size())) // It the matrix is constrained
2360     {
2361       // We may have rhs values to use later
2362       const DofConstraintValueMap * rhs_values = nullptr;
2363       if (qoi_index < 0)
2364         rhs_values = &_primal_constraint_values;
2365       else
2366         {
2367           const AdjointDofConstraintValues::const_iterator
2368             it = _adjoint_constraint_values.find(qoi_index);
2369           if (it != _adjoint_constraint_values.end())
2370             rhs_values = &it->second;
2371         }
2372 
2373       // Compute matrix/vector product K H
2374       DenseVector<Number> KH;
2375       matrix.vector_mult(KH, H);
2376 
2377       // Compute the matrix-vector product C^T (F - KH)
2378       DenseVector<Number> F_minus_KH(rhs);
2379       F_minus_KH -= KH;
2380       C.vector_mult_transpose(rhs, F_minus_KH);
2381 
2382       for (unsigned int i=0,
2383            n_elem_dofs = cast_int<unsigned int>(elem_dofs.size());
2384            i != n_elem_dofs; i++)
2385         {
2386           const dof_id_type dof_id = elem_dofs[i];
2387 
2388           if (this->is_constrained_dof(dof_id))
2389             {
2390               // This will put a nonsymmetric entry in the constraint
2391               // row to ensure that the linear system produces the
2392               // correct value for the constrained DOF.
2393               if (asymmetric_constraint_rows && rhs_values)
2394                 {
2395                   const DofConstraintValueMap::const_iterator valpos =
2396                     rhs_values->find(dof_id);
2397 
2398                   rhs(i) = (valpos == rhs_values->end()) ?
2399                     0 : valpos->second;
2400                 }
2401               else
2402                 rhs(i) = 0.;
2403             }
2404         }
2405 
2406     } // end if is constrained...
2407 }
2408 
2409 
2410 
2411 
constrain_element_matrix(DenseMatrix<Number> & matrix,std::vector<dof_id_type> & row_dofs,std::vector<dof_id_type> & col_dofs,bool asymmetric_constraint_rows)2412 void DofMap::constrain_element_matrix (DenseMatrix<Number> & matrix,
2413                                        std::vector<dof_id_type> & row_dofs,
2414                                        std::vector<dof_id_type> & col_dofs,
2415                                        bool asymmetric_constraint_rows) const
2416 {
2417   libmesh_assert_equal_to (row_dofs.size(), matrix.m());
2418   libmesh_assert_equal_to (col_dofs.size(), matrix.n());
2419 
2420   // check for easy return
2421   if (this->_dof_constraints.empty())
2422     return;
2423 
2424   // The constrained matrix is built up as R^T K C.
2425   DenseMatrix<Number> R;
2426   DenseMatrix<Number> C;
2427 
2428   // Safeguard against the user passing us the same
2429   // object for row_dofs and col_dofs.  If that is done
2430   // the calls to build_matrix would fail
2431   std::vector<dof_id_type> orig_row_dofs(row_dofs);
2432   std::vector<dof_id_type> orig_col_dofs(col_dofs);
2433 
2434   this->build_constraint_matrix (R, orig_row_dofs);
2435   this->build_constraint_matrix (C, orig_col_dofs);
2436 
2437   LOG_SCOPE("constrain_elem_matrix()", "DofMap");
2438 
2439   row_dofs = orig_row_dofs;
2440   col_dofs = orig_col_dofs;
2441 
2442   bool constraint_found = false;
2443 
2444   // K_constrained = R^T K C
2445 
2446   if ((R.m() == matrix.m()) &&
2447       (R.n() == row_dofs.size()))
2448     {
2449       matrix.left_multiply_transpose  (R);
2450       constraint_found = true;
2451     }
2452 
2453   if ((C.m() == matrix.n()) &&
2454       (C.n() == col_dofs.size()))
2455     {
2456       matrix.right_multiply (C);
2457       constraint_found = true;
2458     }
2459 
2460   // It is possible that the matrix is not constrained at all.
2461   if (constraint_found)
2462     {
2463       libmesh_assert_equal_to (matrix.m(), row_dofs.size());
2464       libmesh_assert_equal_to (matrix.n(), col_dofs.size());
2465 
2466 
2467       for (unsigned int i=0,
2468            n_row_dofs = cast_int<unsigned int>(row_dofs.size());
2469            i != n_row_dofs; i++)
2470         if (this->is_constrained_dof(row_dofs[i]))
2471           {
2472             for (auto j : make_range(matrix.n()))
2473               {
2474                 if (row_dofs[i] != col_dofs[j])
2475                   matrix(i,j) = 0.;
2476                 else // If the DOF is constrained
2477                   matrix(i,j) = 1.;
2478               }
2479 
2480             if (asymmetric_constraint_rows)
2481               {
2482                 DofConstraints::const_iterator
2483                   pos = _dof_constraints.find(row_dofs[i]);
2484 
2485                 libmesh_assert (pos != _dof_constraints.end());
2486 
2487                 const DofConstraintRow & constraint_row = pos->second;
2488 
2489                 libmesh_assert (!constraint_row.empty());
2490 
2491                 for (const auto & item : constraint_row)
2492                   for (unsigned int j=0,
2493                        n_col_dofs = cast_int<unsigned int>(col_dofs.size());
2494                        j != n_col_dofs; j++)
2495                     if (col_dofs[j] == item.first)
2496                       matrix(i,j) = -item.second;
2497               }
2498           }
2499     } // end if is constrained...
2500 }
2501 
2502 
2503 
constrain_element_vector(DenseVector<Number> & rhs,std::vector<dof_id_type> & row_dofs,bool)2504 void DofMap::constrain_element_vector (DenseVector<Number> & rhs,
2505                                        std::vector<dof_id_type> & row_dofs,
2506                                        bool) const
2507 {
2508   libmesh_assert_equal_to (rhs.size(), row_dofs.size());
2509 
2510   // check for easy return
2511   if (this->_dof_constraints.empty())
2512     return;
2513 
2514   // The constrained RHS is built up as R^T F.
2515   DenseMatrix<Number> R;
2516 
2517   this->build_constraint_matrix (R, row_dofs);
2518 
2519   LOG_SCOPE("constrain_elem_vector()", "DofMap");
2520 
2521   // It is possible that the vector is not constrained at all.
2522   if ((R.m() == rhs.size()) &&
2523       (R.n() == row_dofs.size())) // if the RHS is constrained
2524     {
2525       // Compute the matrix-vector product
2526       DenseVector<Number> old_rhs(rhs);
2527       R.vector_mult_transpose(rhs, old_rhs);
2528 
2529       libmesh_assert_equal_to (row_dofs.size(), rhs.size());
2530 
2531       for (unsigned int i=0,
2532            n_row_dofs = cast_int<unsigned int>(row_dofs.size());
2533            i != n_row_dofs; i++)
2534         if (this->is_constrained_dof(row_dofs[i]))
2535           {
2536             // If the DOF is constrained
2537             libmesh_assert (_dof_constraints.find(row_dofs[i]) != _dof_constraints.end());
2538 
2539             rhs(i) = 0;
2540           }
2541     } // end if the RHS is constrained.
2542 }
2543 
2544 
2545 
constrain_element_dyad_matrix(DenseVector<Number> & v,DenseVector<Number> & w,std::vector<dof_id_type> & row_dofs,bool)2546 void DofMap::constrain_element_dyad_matrix (DenseVector<Number> & v,
2547                                             DenseVector<Number> & w,
2548                                             std::vector<dof_id_type> & row_dofs,
2549                                             bool) const
2550 {
2551   libmesh_assert_equal_to (v.size(), row_dofs.size());
2552   libmesh_assert_equal_to (w.size(), row_dofs.size());
2553 
2554   // check for easy return
2555   if (this->_dof_constraints.empty())
2556     return;
2557 
2558   // The constrained RHS is built up as R^T F.
2559   DenseMatrix<Number> R;
2560 
2561   this->build_constraint_matrix (R, row_dofs);
2562 
2563   LOG_SCOPE("cnstrn_elem_dyad_mat()", "DofMap");
2564 
2565   // It is possible that the vector is not constrained at all.
2566   if ((R.m() == v.size()) &&
2567       (R.n() == row_dofs.size())) // if the RHS is constrained
2568     {
2569       // Compute the matrix-vector products
2570       DenseVector<Number> old_v(v);
2571       DenseVector<Number> old_w(w);
2572 
2573       // compute matrix/vector product
2574       R.vector_mult_transpose(v, old_v);
2575       R.vector_mult_transpose(w, old_w);
2576 
2577       libmesh_assert_equal_to (row_dofs.size(), v.size());
2578       libmesh_assert_equal_to (row_dofs.size(), w.size());
2579 
2580       /* Constrain only v, not w.  */
2581 
2582       for (unsigned int i=0,
2583            n_row_dofs = cast_int<unsigned int>(row_dofs.size());
2584            i != n_row_dofs; i++)
2585         if (this->is_constrained_dof(row_dofs[i]))
2586           {
2587             // If the DOF is constrained
2588             libmesh_assert (_dof_constraints.find(row_dofs[i]) != _dof_constraints.end());
2589 
2590             v(i) = 0;
2591           }
2592     } // end if the RHS is constrained.
2593 }
2594 
2595 
2596 
constrain_nothing(std::vector<dof_id_type> & dofs)2597 void DofMap::constrain_nothing (std::vector<dof_id_type> & dofs) const
2598 {
2599   // check for easy return
2600   if (this->_dof_constraints.empty())
2601     return;
2602 
2603   // All the work is done by \p build_constraint_matrix.  We just need
2604   // a dummy matrix.
2605   DenseMatrix<Number> R;
2606   this->build_constraint_matrix (R, dofs);
2607 }
2608 
2609 
2610 
enforce_constraints_exactly(const System & system,NumericVector<Number> * v,bool homogeneous)2611 void DofMap::enforce_constraints_exactly (const System & system,
2612                                           NumericVector<Number> * v,
2613                                           bool homogeneous) const
2614 {
2615   parallel_object_only();
2616 
2617   if (!this->n_constrained_dofs())
2618     return;
2619 
2620   LOG_SCOPE("enforce_constraints_exactly()","DofMap");
2621 
2622   if (!v)
2623     v = system.solution.get();
2624 
2625   NumericVector<Number> * v_local  = nullptr; // will be initialized below
2626   NumericVector<Number> * v_global = nullptr; // will be initialized below
2627   std::unique_ptr<NumericVector<Number>> v_built;
2628   if (v->type() == SERIAL)
2629     {
2630       v_built = NumericVector<Number>::build(this->comm());
2631       v_built->init(this->n_dofs(), this->n_local_dofs(), true, PARALLEL);
2632       v_built->close();
2633 
2634       for (dof_id_type i=v_built->first_local_index();
2635            i<v_built->last_local_index(); i++)
2636         v_built->set(i, (*v)(i));
2637       v_built->close();
2638       v_global = v_built.get();
2639 
2640       v_local = v;
2641       libmesh_assert (v_local->closed());
2642     }
2643   else if (v->type() == PARALLEL)
2644     {
2645       v_built = NumericVector<Number>::build(this->comm());
2646       v_built->init (v->size(), v->local_size(),
2647                      this->get_send_list(), true,
2648                      GHOSTED);
2649       v->localize(*v_built, this->get_send_list());
2650       v_built->close();
2651       v_local = v_built.get();
2652 
2653       v_global = v;
2654     }
2655   else if (v->type() == GHOSTED)
2656     {
2657       v_local = v;
2658       v_global = v;
2659     }
2660   else // unknown v->type()
2661     libmesh_error_msg("ERROR: Unknown v->type() == " << v->type());
2662 
2663   // We should never hit these asserts because we should error-out in
2664   // else clause above.  Just to be sure we don't try to use v_local
2665   // and v_global uninitialized...
2666   libmesh_assert(v_local);
2667   libmesh_assert(v_global);
2668   libmesh_assert_equal_to (this, &(system.get_dof_map()));
2669 
2670   for (const auto & pr : _dof_constraints)
2671     {
2672       dof_id_type constrained_dof = pr.first;
2673       if (!this->local_index(constrained_dof))
2674         continue;
2675 
2676       const DofConstraintRow constraint_row = pr.second;
2677 
2678       Number exact_value = 0;
2679       if (!homogeneous)
2680         {
2681           DofConstraintValueMap::const_iterator rhsit =
2682             _primal_constraint_values.find(constrained_dof);
2683           if (rhsit != _primal_constraint_values.end())
2684             exact_value = rhsit->second;
2685         }
2686       for (const auto & j : constraint_row)
2687         exact_value += j.second * (*v_local)(j.first);
2688 
2689       v_global->set(constrained_dof, exact_value);
2690     }
2691 
2692   // If the old vector was serial, we probably need to send our values
2693   // to other processors
2694   if (v->type() == SERIAL)
2695     {
2696 #ifndef NDEBUG
2697       v_global->close();
2698 #endif
2699       v_global->localize (*v);
2700     }
2701   v->close();
2702 }
2703 
enforce_constraints_on_residual(const NonlinearImplicitSystem & system,NumericVector<Number> * rhs,NumericVector<Number> const * solution,bool homogeneous)2704 void DofMap::enforce_constraints_on_residual (const NonlinearImplicitSystem & system,
2705                                               NumericVector<Number> * rhs,
2706                                               NumericVector<Number> const * solution,
2707                                               bool homogeneous) const
2708 {
2709   parallel_object_only();
2710 
2711   if (!this->n_constrained_dofs())
2712     return;
2713 
2714   if (!rhs)
2715     rhs = system.rhs;
2716   if (!solution)
2717     solution = system.solution.get();
2718 
2719   NumericVector<Number> const * solution_local  = nullptr; // will be initialized below
2720   std::unique_ptr<NumericVector<Number>> solution_built;
2721   if (solution->type() == SERIAL || solution->type() == GHOSTED)
2722       solution_local = solution;
2723   else if (solution->type() == PARALLEL)
2724     {
2725       solution_built = NumericVector<Number>::build(this->comm());
2726       solution_built->init (solution->size(), solution->local_size(),
2727                             this->get_send_list(), true, GHOSTED);
2728       solution->localize(*solution_built, this->get_send_list());
2729       solution_built->close();
2730       solution_local = solution_built.get();
2731     }
2732   else // unknown solution->type()
2733     libmesh_error_msg("ERROR: Unknown solution->type() == " << solution->type());
2734 
2735   // We should never hit these asserts because we should error-out in
2736   // else clause above.  Just to be sure we don't try to use solution_local
2737   libmesh_assert(solution_local);
2738   libmesh_assert_equal_to (this, &(system.get_dof_map()));
2739 
2740   for (const auto & pr : _dof_constraints)
2741     {
2742       dof_id_type constrained_dof = pr.first;
2743       if (!this->local_index(constrained_dof))
2744         continue;
2745 
2746       const DofConstraintRow constraint_row = pr.second;
2747 
2748       Number exact_value = 0;
2749       for (const auto & j : constraint_row)
2750         exact_value -= j.second * (*solution_local)(j.first);
2751       exact_value += (*solution_local)(constrained_dof);
2752       if (!homogeneous)
2753         {
2754           DofConstraintValueMap::const_iterator rhsit =
2755             _primal_constraint_values.find(constrained_dof);
2756           if (rhsit != _primal_constraint_values.end())
2757             exact_value += rhsit->second;
2758         }
2759 
2760       rhs->set(constrained_dof, exact_value);
2761     }
2762 }
2763 
enforce_constraints_on_jacobian(const NonlinearImplicitSystem & system,SparseMatrix<Number> * jac)2764 void DofMap::enforce_constraints_on_jacobian (const NonlinearImplicitSystem & system,
2765                                               SparseMatrix<Number> * jac) const
2766 {
2767   parallel_object_only();
2768 
2769   if (!this->n_constrained_dofs())
2770     return;
2771 
2772   if (!jac)
2773     jac = system.matrix;
2774 
2775   libmesh_assert_equal_to (this, &(system.get_dof_map()));
2776 
2777   for (const auto & pr : _dof_constraints)
2778     {
2779       dof_id_type constrained_dof = pr.first;
2780       if (!this->local_index(constrained_dof))
2781         continue;
2782 
2783       const DofConstraintRow constraint_row = pr.second;
2784 
2785       for (const auto & j : constraint_row)
2786         jac->set(constrained_dof, j.first, -j.second);
2787       jac->set(constrained_dof, constrained_dof, 1);
2788     }
2789 }
2790 
2791 
enforce_adjoint_constraints_exactly(NumericVector<Number> & v,unsigned int q)2792 void DofMap::enforce_adjoint_constraints_exactly (NumericVector<Number> & v,
2793                                                   unsigned int q) const
2794 {
2795   parallel_object_only();
2796 
2797   if (!this->n_constrained_dofs())
2798     return;
2799 
2800   LOG_SCOPE("enforce_adjoint_constraints_exactly()", "DofMap");
2801 
2802   NumericVector<Number> * v_local  = nullptr; // will be initialized below
2803   NumericVector<Number> * v_global = nullptr; // will be initialized below
2804   std::unique_ptr<NumericVector<Number>> v_built;
2805   if (v.type() == SERIAL)
2806     {
2807       v_built = NumericVector<Number>::build(this->comm());
2808       v_built->init(this->n_dofs(), this->n_local_dofs(), true, PARALLEL);
2809       v_built->close();
2810 
2811       for (dof_id_type i=v_built->first_local_index();
2812            i<v_built->last_local_index(); i++)
2813         v_built->set(i, v(i));
2814       v_built->close();
2815       v_global = v_built.get();
2816 
2817       v_local = &v;
2818       libmesh_assert (v_local->closed());
2819     }
2820   else if (v.type() == PARALLEL)
2821     {
2822       v_built = NumericVector<Number>::build(this->comm());
2823       v_built->init (v.size(), v.local_size(),
2824                      this->get_send_list(), true, GHOSTED);
2825       v.localize(*v_built, this->get_send_list());
2826       v_built->close();
2827       v_local = v_built.get();
2828 
2829       v_global = &v;
2830     }
2831   else if (v.type() == GHOSTED)
2832     {
2833       v_local = &v;
2834       v_global = &v;
2835     }
2836   else // unknown v.type()
2837     libmesh_error_msg("ERROR: Unknown v.type() == " << v.type());
2838 
2839   // We should never hit these asserts because we should error-out in
2840   // else clause above.  Just to be sure we don't try to use v_local
2841   // and v_global uninitialized...
2842   libmesh_assert(v_local);
2843   libmesh_assert(v_global);
2844 
2845   // Do we have any non_homogeneous constraints?
2846   const AdjointDofConstraintValues::const_iterator
2847     adjoint_constraint_map_it = _adjoint_constraint_values.find(q);
2848   const DofConstraintValueMap * constraint_map =
2849     (adjoint_constraint_map_it == _adjoint_constraint_values.end()) ?
2850     nullptr : &adjoint_constraint_map_it->second;
2851 
2852   for (const auto & pr : _dof_constraints)
2853     {
2854       dof_id_type constrained_dof = pr.first;
2855       if (!this->local_index(constrained_dof))
2856         continue;
2857 
2858       const DofConstraintRow constraint_row = pr.second;
2859 
2860       Number exact_value = 0;
2861       if (constraint_map)
2862         {
2863           const DofConstraintValueMap::const_iterator
2864             adjoint_constraint_it =
2865             constraint_map->find(constrained_dof);
2866           if (adjoint_constraint_it != constraint_map->end())
2867             exact_value = adjoint_constraint_it->second;
2868         }
2869 
2870       for (const auto & j : constraint_row)
2871         exact_value += j.second * (*v_local)(j.first);
2872 
2873       v_global->set(constrained_dof, exact_value);
2874     }
2875 
2876   // If the old vector was serial, we probably need to send our values
2877   // to other processors
2878   if (v.type() == SERIAL)
2879     {
2880 #ifndef NDEBUG
2881       v_global->close();
2882 #endif
2883       v_global->localize (v);
2884     }
2885   v.close();
2886 }
2887 
2888 
2889 
2890 std::pair<Real, Real>
max_constraint_error(const System & system,NumericVector<Number> * v)2891 DofMap::max_constraint_error (const System & system,
2892                               NumericVector<Number> * v) const
2893 {
2894   if (!v)
2895     v = system.solution.get();
2896   NumericVector<Number> & vec = *v;
2897 
2898   // We'll assume the vector is closed
2899   libmesh_assert (vec.closed());
2900 
2901   Real max_absolute_error = 0., max_relative_error = 0.;
2902 
2903   const MeshBase & mesh = system.get_mesh();
2904 
2905   libmesh_assert_equal_to (this, &(system.get_dof_map()));
2906 
2907   // indices on each element
2908   std::vector<dof_id_type> local_dof_indices;
2909 
2910   for (const auto & elem : mesh.active_local_element_ptr_range())
2911     {
2912       this->dof_indices(elem, local_dof_indices);
2913       std::vector<dof_id_type> raw_dof_indices = local_dof_indices;
2914 
2915       // Constraint matrix for each element
2916       DenseMatrix<Number> C;
2917 
2918       this->build_constraint_matrix (C, local_dof_indices);
2919 
2920       // Continue if the element is unconstrained
2921       if (!C.m())
2922         continue;
2923 
2924       libmesh_assert_equal_to (C.m(), raw_dof_indices.size());
2925       libmesh_assert_equal_to (C.n(), local_dof_indices.size());
2926 
2927       for (auto i : make_range(C.m()))
2928         {
2929           // Recalculate any constrained dof owned by this processor
2930           dof_id_type global_dof = raw_dof_indices[i];
2931           if (this->is_constrained_dof(global_dof) &&
2932               global_dof >= vec.first_local_index() &&
2933               global_dof < vec.last_local_index())
2934             {
2935 #ifndef NDEBUG
2936               DofConstraints::const_iterator
2937                 pos = _dof_constraints.find(global_dof);
2938 
2939               libmesh_assert (pos != _dof_constraints.end());
2940 #endif
2941 
2942               Number exact_value = 0;
2943               DofConstraintValueMap::const_iterator rhsit =
2944                 _primal_constraint_values.find(global_dof);
2945               if (rhsit != _primal_constraint_values.end())
2946                 exact_value = rhsit->second;
2947 
2948               for (auto j : make_range(C.n()))
2949                 {
2950                   if (local_dof_indices[j] != global_dof)
2951                     exact_value += C(i,j) *
2952                       vec(local_dof_indices[j]);
2953                 }
2954 
2955               max_absolute_error = std::max(max_absolute_error,
2956                                             std::abs(vec(global_dof) - exact_value));
2957               max_relative_error = std::max(max_relative_error,
2958                                             std::abs(vec(global_dof) - exact_value)
2959                                             / std::abs(exact_value));
2960             }
2961         }
2962     }
2963 
2964   return std::pair<Real, Real>(max_absolute_error, max_relative_error);
2965 }
2966 
2967 
2968 
build_constraint_matrix(DenseMatrix<Number> & C,std::vector<dof_id_type> & elem_dofs,const bool called_recursively)2969 void DofMap::build_constraint_matrix (DenseMatrix<Number> & C,
2970                                       std::vector<dof_id_type> & elem_dofs,
2971                                       const bool called_recursively) const
2972 {
2973   LOG_SCOPE_IF("build_constraint_matrix()", "DofMap", !called_recursively);
2974 
2975   // Create a set containing the DOFs we already depend on
2976   typedef std::set<dof_id_type> RCSet;
2977   RCSet dof_set;
2978 
2979   bool we_have_constraints = false;
2980 
2981   // Next insert any other dofs the current dofs might be constrained
2982   // in terms of.  Note that in this case we may not be done: Those
2983   // may in turn depend on others.  So, we need to repeat this process
2984   // in that case until the system depends only on unconstrained
2985   // degrees of freedom.
2986   for (const auto & dof : elem_dofs)
2987     if (this->is_constrained_dof(dof))
2988       {
2989         we_have_constraints = true;
2990 
2991         // If the DOF is constrained
2992         DofConstraints::const_iterator
2993           pos = _dof_constraints.find(dof);
2994 
2995         libmesh_assert (pos != _dof_constraints.end());
2996 
2997         const DofConstraintRow & constraint_row = pos->second;
2998 
2999         // Constraint rows in p refinement may be empty
3000         //libmesh_assert (!constraint_row.empty());
3001 
3002         for (const auto & item : constraint_row)
3003           dof_set.insert (item.first);
3004       }
3005 
3006   // May be safe to return at this point
3007   // (but remember to stop the perflog)
3008   if (!we_have_constraints)
3009     return;
3010 
3011   for (const auto & dof : elem_dofs)
3012     dof_set.erase (dof);
3013 
3014   // If we added any DOFS then we need to do this recursively.
3015   // It is possible that we just added a DOF that is also
3016   // constrained!
3017   //
3018   // Also, we need to handle the special case of an element having DOFs
3019   // constrained in terms of other, local DOFs
3020   if (!dof_set.empty() ||  // case 1: constrained in terms of other DOFs
3021       !called_recursively) // case 2: constrained in terms of our own DOFs
3022     {
3023       const unsigned int old_size =
3024         cast_int<unsigned int>(elem_dofs.size());
3025 
3026       // Add new dependency dofs to the end of the current dof set
3027       elem_dofs.insert(elem_dofs.end(),
3028                        dof_set.begin(), dof_set.end());
3029 
3030       // Now we can build the constraint matrix.
3031       // Note that resize also zeros for a DenseMatrix<Number>.
3032       C.resize (old_size,
3033                 cast_int<unsigned int>(elem_dofs.size()));
3034 
3035       // Create the C constraint matrix.
3036       for (unsigned int i=0; i != old_size; i++)
3037         if (this->is_constrained_dof(elem_dofs[i]))
3038           {
3039             // If the DOF is constrained
3040             DofConstraints::const_iterator
3041               pos = _dof_constraints.find(elem_dofs[i]);
3042 
3043             libmesh_assert (pos != _dof_constraints.end());
3044 
3045             const DofConstraintRow & constraint_row = pos->second;
3046 
3047             // p refinement creates empty constraint rows
3048             //    libmesh_assert (!constraint_row.empty());
3049 
3050             for (const auto & item : constraint_row)
3051               for (unsigned int j=0,
3052                    n_elem_dofs = cast_int<unsigned int>(elem_dofs.size());
3053                    j != n_elem_dofs; j++)
3054                 if (elem_dofs[j] == item.first)
3055                   C(i,j) = item.second;
3056           }
3057         else
3058           {
3059             C(i,i) = 1.;
3060           }
3061 
3062       // May need to do this recursively.  It is possible
3063       // that we just replaced a constrained DOF with another
3064       // constrained DOF.
3065       DenseMatrix<Number> Cnew;
3066 
3067       this->build_constraint_matrix (Cnew, elem_dofs, true);
3068 
3069       if ((C.n() == Cnew.m()) &&
3070           (Cnew.n() == elem_dofs.size())) // If the constraint matrix
3071         C.right_multiply(Cnew);           // is constrained...
3072 
3073       libmesh_assert_equal_to (C.n(), elem_dofs.size());
3074     }
3075 }
3076 
3077 
3078 
build_constraint_matrix_and_vector(DenseMatrix<Number> & C,DenseVector<Number> & H,std::vector<dof_id_type> & elem_dofs,int qoi_index,const bool called_recursively)3079 void DofMap::build_constraint_matrix_and_vector (DenseMatrix<Number> & C,
3080                                                  DenseVector<Number> & H,
3081                                                  std::vector<dof_id_type> & elem_dofs,
3082                                                  int qoi_index,
3083                                                  const bool called_recursively) const
3084 {
3085   LOG_SCOPE_IF("build_constraint_matrix_and_vector()", "DofMap", !called_recursively);
3086 
3087   // Create a set containing the DOFs we already depend on
3088   typedef std::set<dof_id_type> RCSet;
3089   RCSet dof_set;
3090 
3091   bool we_have_constraints = false;
3092 
3093   // Next insert any other dofs the current dofs might be constrained
3094   // in terms of.  Note that in this case we may not be done: Those
3095   // may in turn depend on others.  So, we need to repeat this process
3096   // in that case until the system depends only on unconstrained
3097   // degrees of freedom.
3098   for (const auto & dof : elem_dofs)
3099     if (this->is_constrained_dof(dof))
3100       {
3101         we_have_constraints = true;
3102 
3103         // If the DOF is constrained
3104         DofConstraints::const_iterator
3105           pos = _dof_constraints.find(dof);
3106 
3107         libmesh_assert (pos != _dof_constraints.end());
3108 
3109         const DofConstraintRow & constraint_row = pos->second;
3110 
3111         // Constraint rows in p refinement may be empty
3112         //libmesh_assert (!constraint_row.empty());
3113 
3114         for (const auto & item : constraint_row)
3115           dof_set.insert (item.first);
3116       }
3117 
3118   // May be safe to return at this point
3119   // (but remember to stop the perflog)
3120   if (!we_have_constraints)
3121     return;
3122 
3123   for (const auto & dof : elem_dofs)
3124     dof_set.erase (dof);
3125 
3126   // If we added any DOFS then we need to do this recursively.
3127   // It is possible that we just added a DOF that is also
3128   // constrained!
3129   //
3130   // Also, we need to handle the special case of an element having DOFs
3131   // constrained in terms of other, local DOFs
3132   if (!dof_set.empty() ||  // case 1: constrained in terms of other DOFs
3133       !called_recursively) // case 2: constrained in terms of our own DOFs
3134     {
3135       const DofConstraintValueMap * rhs_values = nullptr;
3136       if (qoi_index < 0)
3137         rhs_values = &_primal_constraint_values;
3138       else
3139         {
3140           const AdjointDofConstraintValues::const_iterator
3141             it = _adjoint_constraint_values.find(qoi_index);
3142           if (it != _adjoint_constraint_values.end())
3143             rhs_values = &it->second;
3144         }
3145 
3146       const unsigned int old_size =
3147         cast_int<unsigned int>(elem_dofs.size());
3148 
3149       // Add new dependency dofs to the end of the current dof set
3150       elem_dofs.insert(elem_dofs.end(),
3151                        dof_set.begin(), dof_set.end());
3152 
3153       // Now we can build the constraint matrix and vector.
3154       // Note that resize also zeros for a DenseMatrix and DenseVector
3155       C.resize (old_size,
3156                 cast_int<unsigned int>(elem_dofs.size()));
3157       H.resize (old_size);
3158 
3159       // Create the C constraint matrix.
3160       for (unsigned int i=0; i != old_size; i++)
3161         if (this->is_constrained_dof(elem_dofs[i]))
3162           {
3163             // If the DOF is constrained
3164             DofConstraints::const_iterator
3165               pos = _dof_constraints.find(elem_dofs[i]);
3166 
3167             libmesh_assert (pos != _dof_constraints.end());
3168 
3169             const DofConstraintRow & constraint_row = pos->second;
3170 
3171             // p refinement creates empty constraint rows
3172             //    libmesh_assert (!constraint_row.empty());
3173 
3174             for (const auto & item : constraint_row)
3175               for (unsigned int j=0,
3176                    n_elem_dofs = cast_int<unsigned int>(elem_dofs.size());
3177                    j != n_elem_dofs; j++)
3178                 if (elem_dofs[j] == item.first)
3179                   C(i,j) = item.second;
3180 
3181             if (rhs_values)
3182               {
3183                 DofConstraintValueMap::const_iterator rhsit =
3184                   rhs_values->find(elem_dofs[i]);
3185                 if (rhsit != rhs_values->end())
3186                   H(i) = rhsit->second;
3187               }
3188           }
3189         else
3190           {
3191             C(i,i) = 1.;
3192           }
3193 
3194       // May need to do this recursively.  It is possible
3195       // that we just replaced a constrained DOF with another
3196       // constrained DOF.
3197       DenseMatrix<Number> Cnew;
3198       DenseVector<Number> Hnew;
3199 
3200       this->build_constraint_matrix_and_vector (Cnew, Hnew, elem_dofs,
3201                                                 qoi_index, true);
3202 
3203       if ((C.n() == Cnew.m()) &&          // If the constraint matrix
3204           (Cnew.n() == elem_dofs.size())) // is constrained...
3205         {
3206           // If x = Cy + h and y = Dz + g
3207           // Then x = (CD)z + (Cg + h)
3208           C.vector_mult_add(H, 1, Hnew);
3209 
3210           C.right_multiply(Cnew);
3211         }
3212 
3213       libmesh_assert_equal_to (C.n(), elem_dofs.size());
3214     }
3215 }
3216 
3217 
allgather_recursive_constraints(MeshBase & mesh)3218 void DofMap::allgather_recursive_constraints(MeshBase & mesh)
3219 {
3220   // This function must be run on all processors at once
3221   parallel_object_only();
3222 
3223   // Return immediately if there's nothing to gather
3224   if (this->n_processors() == 1)
3225     return;
3226 
3227   // We might get to return immediately if none of the processors
3228   // found any constraints
3229   unsigned int has_constraints = !_dof_constraints.empty()
3230 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS
3231     || !_node_constraints.empty()
3232 #endif // LIBMESH_ENABLE_NODE_CONSTRAINTS
3233     ;
3234   this->comm().max(has_constraints);
3235   if (!has_constraints)
3236     return;
3237 
3238   // If we have heterogenous adjoint constraints we need to
3239   // communicate those too.
3240   const unsigned int max_qoi_num =
3241     _adjoint_constraint_values.empty() ?
3242     0 : _adjoint_constraint_values.rbegin()->first+1;
3243 
3244   // We might have calculated constraints for constrained dofs
3245   // which have support on other processors.
3246   // Push these out first.
3247   {
3248     std::map<processor_id_type, std::set<dof_id_type>> pushed_ids;
3249 
3250 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS
3251     std::map<processor_id_type, std::set<dof_id_type>> pushed_node_ids;
3252 #endif
3253 
3254     const unsigned int sys_num = this->sys_number();
3255 
3256     // Collect the constraints to push to each processor
3257     for (auto & elem : as_range(mesh.active_not_local_elements_begin(),
3258                                 mesh.active_not_local_elements_end()))
3259       {
3260         const unsigned short n_nodes = elem->n_nodes();
3261 
3262         // Just checking dof_indices on the foreign element isn't
3263         // enough.  Consider a central hanging node between a coarse
3264         // Q2/Q1 element and its finer neighbors on a higher-ranked
3265         // processor.  The coarse element's processor will own the node,
3266         // and will thereby own the pressure dof on that node, despite
3267         // the fact that that pressure dof doesn't directly exist on the
3268         // coarse element!
3269         //
3270         // So, we loop through dofs manually.
3271 
3272         {
3273           const unsigned int n_vars = elem->n_vars(sys_num);
3274           for (unsigned int v=0; v != n_vars; ++v)
3275             {
3276               const unsigned int n_comp = elem->n_comp(sys_num,v);
3277               for (unsigned int c=0; c != n_comp; ++c)
3278                 {
3279                   const unsigned int id =
3280                     elem->dof_number(sys_num,v,c);
3281                   if (this->is_constrained_dof(id))
3282                     pushed_ids[elem->processor_id()].insert(id);
3283                 }
3284             }
3285         }
3286 
3287         for (unsigned short n = 0; n != n_nodes; ++n)
3288           {
3289             const Node & node = elem->node_ref(n);
3290             const unsigned int n_vars = node.n_vars(sys_num);
3291             for (unsigned int v=0; v != n_vars; ++v)
3292               {
3293                 const unsigned int n_comp = node.n_comp(sys_num,v);
3294                 for (unsigned int c=0; c != n_comp; ++c)
3295                   {
3296                     const unsigned int id =
3297                       node.dof_number(sys_num,v,c);
3298                     if (this->is_constrained_dof(id))
3299                       pushed_ids[elem->processor_id()].insert(id);
3300                   }
3301               }
3302           }
3303 
3304 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS
3305         for (unsigned short n = 0; n != n_nodes; ++n)
3306           if (this->is_constrained_node(elem->node_ptr(n)))
3307             pushed_node_ids[elem->processor_id()].insert(elem->node_id(n));
3308 #endif
3309       }
3310 
3311     // Rewrite those id sets as vectors for sending and receiving,
3312     // then find the corresponding data for each id, then push it all.
3313     std::map<processor_id_type, std::vector<dof_id_type>>
3314       pushed_id_vecs, received_id_vecs;
3315     for (auto & p : pushed_ids)
3316       pushed_id_vecs[p.first].assign(p.second.begin(), p.second.end());
3317 
3318     std::map<processor_id_type, std::vector<std::vector<std::pair<dof_id_type,Real>>>>
3319       pushed_keys_vals, received_keys_vals;
3320     std::map<processor_id_type, std::vector<std::vector<Number>>> pushed_rhss, received_rhss;
3321     for (auto & p : pushed_id_vecs)
3322       {
3323         auto & keys_vals = pushed_keys_vals[p.first];
3324         keys_vals.reserve(p.second.size());
3325 
3326         auto & rhss = pushed_rhss[p.first];
3327         rhss.reserve(p.second.size());
3328         for (auto & pushed_id : p.second)
3329           {
3330             const DofConstraintRow & row = _dof_constraints[pushed_id];
3331             keys_vals.emplace_back(row.begin(), row.end());
3332 
3333             rhss.push_back(std::vector<Number>(max_qoi_num+1));
3334             std::vector<Number> & rhs = rhss.back();
3335             DofConstraintValueMap::const_iterator rhsit =
3336               _primal_constraint_values.find(pushed_id);
3337             rhs[max_qoi_num] =
3338               (rhsit == _primal_constraint_values.end()) ?
3339               0 : rhsit->second;
3340             for (unsigned int q = 0; q != max_qoi_num; ++q)
3341               {
3342                 AdjointDofConstraintValues::const_iterator adjoint_map_it =
3343                   _adjoint_constraint_values.find(q);
3344 
3345                 if (adjoint_map_it == _adjoint_constraint_values.end())
3346                   continue;
3347 
3348                 const DofConstraintValueMap & constraint_map =
3349                   adjoint_map_it->second;
3350 
3351                 DofConstraintValueMap::const_iterator adj_rhsit =
3352                   constraint_map.find(pushed_id);
3353 
3354                 rhs[q] =
3355                   (adj_rhsit == constraint_map.end()) ?
3356                   0 : adj_rhsit->second;
3357               }
3358           }
3359       }
3360 
3361     auto ids_action_functor =
3362       [& received_id_vecs]
3363       (processor_id_type pid,
3364        const std::vector<dof_id_type> & data)
3365       {
3366         received_id_vecs[pid] = data;
3367       };
3368 
3369     Parallel::push_parallel_vector_data
3370       (this->comm(), pushed_id_vecs, ids_action_functor);
3371 
3372     auto keys_vals_action_functor =
3373       [& received_keys_vals]
3374       (processor_id_type pid,
3375        const std::vector<std::vector<std::pair<dof_id_type,Real>>> & data)
3376       {
3377         received_keys_vals[pid] = data;
3378       };
3379 
3380     Parallel::push_parallel_vector_data
3381       (this->comm(), pushed_keys_vals, keys_vals_action_functor);
3382 
3383     auto rhss_action_functor =
3384       [& received_rhss]
3385       (processor_id_type pid,
3386        const std::vector<std::vector<Number>> & data)
3387       {
3388         received_rhss[pid] = data;
3389       };
3390 
3391     Parallel::push_parallel_vector_data
3392       (this->comm(), pushed_rhss, rhss_action_functor);
3393 
3394     // Now we have all the DofConstraint rows and rhs values received
3395     // from others, so add the DoF constraints that we've been sent
3396 
3397 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS
3398     std::map<processor_id_type, std::vector<dof_id_type>>
3399       pushed_node_id_vecs, received_node_id_vecs;
3400     for (auto & p : pushed_node_ids)
3401       pushed_node_id_vecs[p.first].assign(p.second.begin(), p.second.end());
3402 
3403     std::map<processor_id_type, std::vector<std::vector<std::pair<dof_id_type,Real>>>>
3404       pushed_node_keys_vals, received_node_keys_vals;
3405     std::map<processor_id_type, std::vector<Point>> pushed_offsets, received_offsets;
3406 
3407     for (auto & p : pushed_node_id_vecs)
3408       {
3409         auto & node_keys_vals = pushed_node_keys_vals[p.first];
3410         node_keys_vals.reserve(p.second.size());
3411 
3412         auto & offsets = pushed_offsets[p.first];
3413         offsets.reserve(p.second.size());
3414 
3415         for (auto & pushed_node_id : p.second)
3416           {
3417             const Node * node = mesh.node_ptr(pushed_node_id);
3418             NodeConstraintRow & row = _node_constraints[node].first;
3419             const std::size_t row_size = row.size();
3420             node_keys_vals.push_back
3421               (std::vector<std::pair<dof_id_type,Real>>());
3422             std::vector<std::pair<dof_id_type,Real>> & this_node_kv =
3423               node_keys_vals.back();
3424             this_node_kv.reserve(row_size);
3425             for (const auto & j : row)
3426               this_node_kv.emplace_back(j.first->id(), j.second);
3427 
3428             offsets.push_back(_node_constraints[node].second);
3429           }
3430       }
3431 
3432     auto node_ids_action_functor =
3433       [& received_node_id_vecs]
3434       (processor_id_type pid,
3435        const std::vector<dof_id_type> & data)
3436       {
3437         received_node_id_vecs[pid] = data;
3438       };
3439 
3440     Parallel::push_parallel_vector_data
3441       (this->comm(), pushed_node_id_vecs, node_ids_action_functor);
3442 
3443     auto node_keys_vals_action_functor =
3444       [& received_node_keys_vals]
3445       (processor_id_type pid,
3446        const std::vector<std::vector<std::pair<dof_id_type,Real>>> & data)
3447       {
3448         received_node_keys_vals[pid] = data;
3449       };
3450 
3451     Parallel::push_parallel_vector_data
3452       (this->comm(), pushed_node_keys_vals,
3453        node_keys_vals_action_functor);
3454 
3455     auto node_offsets_action_functor =
3456       [& received_offsets]
3457       (processor_id_type pid,
3458        const std::vector<Point> & data)
3459       {
3460         received_offsets[pid] = data;
3461       };
3462 
3463     Parallel::push_parallel_vector_data
3464       (this->comm(), pushed_offsets, node_offsets_action_functor);
3465 
3466 #endif
3467 
3468     // Add all the dof constraints that I've been sent
3469     for (auto & p : received_id_vecs)
3470       {
3471         const processor_id_type pid = p.first;
3472         const auto & pushed_ids_to_me = p.second;
3473         libmesh_assert(received_keys_vals.count(pid));
3474         libmesh_assert(received_rhss.count(pid));
3475         const auto & pushed_keys_vals_to_me = received_keys_vals.at(pid);
3476         const auto & pushed_rhss_to_me = received_rhss.at(pid);
3477 
3478         libmesh_assert_equal_to (pushed_ids_to_me.size(),
3479                                  pushed_keys_vals_to_me.size());
3480         libmesh_assert_equal_to (pushed_ids_to_me.size(),
3481                                  pushed_rhss_to_me.size());
3482 
3483         for (auto i : index_range(pushed_ids_to_me))
3484           {
3485             dof_id_type constrained = pushed_ids_to_me[i];
3486 
3487             // If we don't already have a constraint for this dof,
3488             // add the one we were sent
3489             if (!this->is_constrained_dof(constrained))
3490               {
3491                 DofConstraintRow & row = _dof_constraints[constrained];
3492                 for (auto & kv : pushed_keys_vals_to_me[i])
3493                   {
3494                     libmesh_assert_less(kv.first, this->n_dofs());
3495                     row[kv.first] = kv.second;
3496                   }
3497 
3498                 const Number primal_rhs = pushed_rhss_to_me[i][max_qoi_num];
3499 
3500                 if (libmesh_isnan(primal_rhs))
3501                   libmesh_assert(pushed_keys_vals_to_me[i].empty());
3502 
3503                 if (primal_rhs != Number(0))
3504                   _primal_constraint_values[constrained] = primal_rhs;
3505                 else
3506                   _primal_constraint_values.erase(constrained);
3507 
3508                 for (unsigned int q = 0; q != max_qoi_num; ++q)
3509                   {
3510                     AdjointDofConstraintValues::iterator adjoint_map_it =
3511                       _adjoint_constraint_values.find(q);
3512 
3513                     const Number adj_rhs = pushed_rhss_to_me[i][q];
3514 
3515                     if ((adjoint_map_it == _adjoint_constraint_values.end()) &&
3516                         adj_rhs == Number(0))
3517                       continue;
3518 
3519                     if (adjoint_map_it == _adjoint_constraint_values.end())
3520                       adjoint_map_it = _adjoint_constraint_values.emplace
3521                         (q, DofConstraintValueMap()).first;
3522 
3523                     DofConstraintValueMap & constraint_map =
3524                       adjoint_map_it->second;
3525 
3526                     if (adj_rhs != Number(0))
3527                       constraint_map[constrained] = adj_rhs;
3528                     else
3529                       constraint_map.erase(constrained);
3530                   }
3531               }
3532           }
3533       }
3534 
3535 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS
3536     // Add all the node constraints that I've been sent
3537     for (auto & p : received_node_id_vecs)
3538       {
3539         const processor_id_type pid = p.first;
3540         const auto & pushed_node_ids_to_me = p.second;
3541         libmesh_assert(received_node_keys_vals.count(pid));
3542         libmesh_assert(received_offsets.count(pid));
3543         const auto & pushed_node_keys_vals_to_me = received_node_keys_vals.at(pid);
3544         const auto & pushed_offsets_to_me = received_offsets.at(pid);
3545 
3546         libmesh_assert_equal_to (pushed_node_ids_to_me.size(),
3547                                  pushed_node_keys_vals_to_me.size());
3548         libmesh_assert_equal_to (pushed_node_ids_to_me.size(),
3549                                  pushed_offsets_to_me.size());
3550 
3551         for (auto i : index_range(pushed_node_ids_to_me))
3552           {
3553             dof_id_type constrained_id = pushed_node_ids_to_me[i];
3554 
3555             // If we don't already have a constraint for this node,
3556             // add the one we were sent
3557             const Node * constrained = mesh.node_ptr(constrained_id);
3558             if (!this->is_constrained_node(constrained))
3559               {
3560                 NodeConstraintRow & row = _node_constraints[constrained].first;
3561                 for (auto & kv : pushed_node_keys_vals_to_me[i])
3562                   {
3563                     const Node * key_node = mesh.node_ptr(kv.first);
3564                     libmesh_assert(key_node);
3565                     row[key_node] = kv.second;
3566                   }
3567                 _node_constraints[constrained].second = pushed_offsets_to_me[i];
3568               }
3569           }
3570       }
3571 #endif // LIBMESH_ENABLE_NODE_CONSTRAINTS
3572   }
3573 
3574   // Now start checking for any other constraints we need
3575   // to know about, requesting them recursively.
3576 
3577   // Create sets containing the DOFs and nodes we already depend on
3578   typedef std::set<dof_id_type> DoF_RCSet;
3579   DoF_RCSet unexpanded_dofs;
3580 
3581   for (const auto & i : _dof_constraints)
3582     unexpanded_dofs.insert(i.first);
3583 
3584   // Gather all the dof constraints we need
3585   this->gather_constraints(mesh, unexpanded_dofs, false);
3586 
3587   // Gather all the node constraints we need
3588 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS
3589   typedef std::set<const Node *> Node_RCSet;
3590   Node_RCSet unexpanded_nodes;
3591 
3592   for (const auto & i : _node_constraints)
3593     unexpanded_nodes.insert(i.first);
3594 
3595   // We have to keep recursing while the unexpanded set is
3596   // nonempty on *any* processor
3597   bool unexpanded_set_nonempty = !unexpanded_nodes.empty();
3598   this->comm().max(unexpanded_set_nonempty);
3599 
3600   // We may be receiving packed_range sends out of order with
3601   // parallel_sync tags, so make sure they're received correctly.
3602   Parallel::MessageTag range_tag = this->comm().get_unique_tag();
3603 
3604   while (unexpanded_set_nonempty)
3605     {
3606       // Let's make sure we don't lose sync in this loop.
3607       parallel_object_only();
3608 
3609       // Request sets
3610       Node_RCSet node_request_set;
3611 
3612       // Request sets to send to each processor
3613       std::map<processor_id_type, std::vector<dof_id_type>>
3614         requested_node_ids;
3615 
3616       // And the sizes of each
3617       std::map<processor_id_type, dof_id_type> node_ids_on_proc;
3618 
3619       // Fill (and thereby sort and uniq!) the main request sets
3620       for (const auto & i : unexpanded_nodes)
3621         {
3622           NodeConstraintRow & row = _node_constraints[i].first;
3623           for (const auto & j : row)
3624             {
3625               const Node * const node = j.first;
3626               libmesh_assert(node);
3627 
3628               // If it's non-local and we haven't already got a
3629               // constraint for it, we might need to ask for one
3630               if ((node->processor_id() != this->processor_id()) &&
3631                   !_node_constraints.count(node))
3632                 node_request_set.insert(node);
3633             }
3634         }
3635 
3636       // Clear the unexpanded constraint sets; we're about to expand
3637       // them
3638       unexpanded_nodes.clear();
3639 
3640       // Count requests by processor
3641       for (const auto & node : node_request_set)
3642         {
3643           libmesh_assert(node);
3644           libmesh_assert_less (node->processor_id(), this->n_processors());
3645           node_ids_on_proc[node->processor_id()]++;
3646         }
3647 
3648       for (auto pair : node_ids_on_proc)
3649         requested_node_ids[pair.first].reserve(pair.second);
3650 
3651       // Prepare each processor's request set
3652       for (const auto & node : node_request_set)
3653         requested_node_ids[node->processor_id()].push_back(node->id());
3654 
3655       typedef std::vector<std::pair<dof_id_type, Real>> row_datum;
3656 
3657       // We may need to send nodes ahead of data about them
3658       std::vector<Parallel::Request> packed_range_sends;
3659 
3660       auto node_row_gather_functor =
3661         [this,
3662          & mesh,
3663          & packed_range_sends,
3664          & range_tag]
3665         (processor_id_type pid,
3666          const std::vector<dof_id_type> & ids,
3667          std::vector<row_datum> & data)
3668         {
3669           // Do we need to keep track of requested nodes to send
3670           // later?
3671           const bool dist_mesh = !mesh.is_serial();
3672 
3673           // FIXME - this could be an unordered set, given a
3674           // hash<pointers> specialization
3675           std::set<const Node *> nodes_requested;
3676 
3677           // Fill those requests
3678           const std::size_t query_size = ids.size();
3679 
3680           data.resize(query_size);
3681           for (std::size_t i=0; i != query_size; ++i)
3682             {
3683               dof_id_type constrained_id = ids[i];
3684               const Node * constrained_node = mesh.node_ptr(constrained_id);
3685               if (_node_constraints.count(constrained_node))
3686                 {
3687                   const NodeConstraintRow & row = _node_constraints[constrained_node].first;
3688                   std::size_t row_size = row.size();
3689                   data[i].reserve(row_size);
3690                   for (const auto & j : row)
3691                     {
3692                       const Node * node = j.first;
3693                       data[i].emplace_back(node->id(), j.second);
3694 
3695                       // If we're not sure whether our send
3696                       // destination already has this node, let's give
3697                       // it a copy.
3698                       if (node->processor_id() != pid && dist_mesh)
3699                         nodes_requested.insert(node);
3700 
3701                       // We can have 0 nodal constraint
3702                       // coefficients, where no Lagrange constraint
3703                       // exists but non-Lagrange basis constraints
3704                       // might.
3705                       // libmesh_assert(j.second);
3706                     }
3707                 }
3708               else
3709                 {
3710                   // We have to distinguish "constraint with no
3711                   // constraining nodes" (e.g. due to user node
3712                   // constraint equations) from "no constraint".
3713                   // We'll use invalid_id for the latter.
3714                   data[i].emplace_back(DofObject::invalid_id, Real(0));
3715                 }
3716             }
3717 
3718           // Constraining nodes might not even exist on our
3719           // correspondant's subset of a distributed mesh, so let's
3720           // make them exist.
3721           if (dist_mesh)
3722             {
3723               packed_range_sends.push_back(Parallel::Request());
3724               this->comm().send_packed_range
3725                 (pid, &mesh, nodes_requested.begin(), nodes_requested.end(),
3726                  packed_range_sends.back(), range_tag);
3727             }
3728         };
3729 
3730       typedef Point node_rhs_datum;
3731 
3732       auto node_rhs_gather_functor =
3733         [this,
3734          & mesh]
3735         (processor_id_type,
3736          const std::vector<dof_id_type> & ids,
3737          std::vector<node_rhs_datum> & data)
3738         {
3739           // Fill those requests
3740           const std::size_t query_size = ids.size();
3741 
3742           data.resize(query_size);
3743           for (std::size_t i=0; i != query_size; ++i)
3744             {
3745               dof_id_type constrained_id = ids[i];
3746               const Node * constrained_node = mesh.node_ptr(constrained_id);
3747               if (_node_constraints.count(constrained_node))
3748                 data[i] = _node_constraints[constrained_node].second;
3749               else
3750                 data[i](0) = std::numeric_limits<Real>::quiet_NaN();
3751             }
3752         };
3753 
3754       auto node_row_action_functor =
3755         [this,
3756          & mesh,
3757          & range_tag,
3758          & unexpanded_nodes]
3759         (processor_id_type pid,
3760          const std::vector<dof_id_type> & ids,
3761          const std::vector<row_datum> & data)
3762         {
3763           // Before we act on any new constraint rows, we may need to
3764           // make sure we have all the nodes involved!
3765           if (!mesh.is_serial())
3766             this->comm().receive_packed_range
3767               (pid, &mesh, mesh_inserter_iterator<Node>(mesh),
3768                (Node**)nullptr, range_tag);
3769 
3770           // Add any new constraint rows we've found
3771           const std::size_t query_size = ids.size();
3772 
3773           for (std::size_t i=0; i != query_size; ++i)
3774             {
3775               const dof_id_type constrained_id = ids[i];
3776 
3777               // An empty row is an constraint with an empty row; for
3778               // no constraint we use a "no row" placeholder
3779               if (data[i].empty())
3780                 {
3781                   const Node * constrained_node = mesh.node_ptr(constrained_id);
3782                   NodeConstraintRow & row = _node_constraints[constrained_node].first;
3783                   row.clear();
3784                 }
3785               else if (data[i][0].first != DofObject::invalid_id)
3786                 {
3787                   const Node * constrained_node = mesh.node_ptr(constrained_id);
3788                   NodeConstraintRow & row = _node_constraints[constrained_node].first;
3789                   row.clear();
3790                   for (auto & pair : data[i])
3791                     {
3792                       const Node * key_node =
3793                         mesh.node_ptr(pair.first);
3794                       libmesh_assert(key_node);
3795                       row[key_node] = pair.second;
3796                     }
3797 
3798                   // And prepare to check for more recursive constraints
3799                   unexpanded_nodes.insert(constrained_node);
3800                 }
3801             }
3802         };
3803 
3804       auto node_rhs_action_functor =
3805         [this,
3806          & mesh]
3807         (processor_id_type,
3808          const std::vector<dof_id_type> & ids,
3809          const std::vector<node_rhs_datum> & data)
3810         {
3811           // Add rhs data for any new node constraint rows we've found
3812           const std::size_t query_size = ids.size();
3813 
3814           for (std::size_t i=0; i != query_size; ++i)
3815             {
3816               dof_id_type constrained_id = ids[i];
3817               const Node * constrained_node = mesh.node_ptr(constrained_id);
3818 
3819               if (!libmesh_isnan(data[i](0)))
3820                 _node_constraints[constrained_node].second = data[i];
3821               else
3822                 _node_constraints.erase(constrained_node);
3823             }
3824         };
3825 
3826       // Now request node constraint rows from other processors
3827       row_datum * node_row_ex = nullptr;
3828       Parallel::pull_parallel_vector_data
3829         (this->comm(), requested_node_ids, node_row_gather_functor,
3830          node_row_action_functor, node_row_ex);
3831 
3832       // And request node constraint right hand sides from other procesors
3833       node_rhs_datum * node_rhs_ex = nullptr;
3834       Parallel::pull_parallel_vector_data
3835         (this->comm(), requested_node_ids, node_rhs_gather_functor,
3836          node_rhs_action_functor, node_rhs_ex);
3837 
3838       Parallel::wait(packed_range_sends);
3839 
3840       // We have to keep recursing while the unexpanded set is
3841       // nonempty on *any* processor
3842       unexpanded_set_nonempty = !unexpanded_nodes.empty();
3843       this->comm().max(unexpanded_set_nonempty);
3844     }
3845 #endif // LIBMESH_ENABLE_NODE_CONSTRAINTS
3846 }
3847 
3848 
3849 
process_constraints(MeshBase & mesh)3850 void DofMap::process_constraints (MeshBase & mesh)
3851 {
3852   // We've computed our local constraints, but they may depend on
3853   // non-local constraints that we'll need to take into account.
3854   this->allgather_recursive_constraints(mesh);
3855 
3856   if (_error_on_constraint_loop)
3857   {
3858     // Optionally check for constraint loops and throw an error
3859     // if they're detected. We always do this check below in dbg/devel
3860     // mode but here we optionally do it in opt mode as well.
3861     check_for_constraint_loops();
3862   }
3863 
3864   // Adjoints will be constrained where the primal is
3865   // Therefore, we will expand the adjoint_constraint_values
3866   // map whenever the primal_constraint_values map is expanded
3867 
3868   // First, figure out the total number of QoIs
3869   const unsigned int max_qoi_num =
3870     _adjoint_constraint_values.empty() ?
3871     0 : _adjoint_constraint_values.rbegin()->first+1;
3872 
3873   // Create a set containing the DOFs we already depend on
3874   typedef std::set<dof_id_type> RCSet;
3875   RCSet unexpanded_set;
3876 
3877   for (const auto & i : _dof_constraints)
3878     unexpanded_set.insert(i.first);
3879 
3880   while (!unexpanded_set.empty())
3881     for (RCSet::iterator i = unexpanded_set.begin();
3882          i != unexpanded_set.end(); /* nothing */)
3883       {
3884         // If the DOF is constrained
3885         DofConstraints::iterator
3886           pos = _dof_constraints.find(*i);
3887 
3888         libmesh_assert (pos != _dof_constraints.end());
3889 
3890         DofConstraintRow & constraint_row = pos->second;
3891 
3892         DofConstraintValueMap::iterator rhsit =
3893           _primal_constraint_values.find(*i);
3894         Number constraint_rhs = (rhsit == _primal_constraint_values.end()) ?
3895           0 : rhsit->second;
3896 
3897         // A vector of DofConstraintValueMaps for each adjoint variable
3898         std::vector<DofConstraintValueMap::iterator> adjoint_rhs_iterators;
3899         adjoint_rhs_iterators.resize(max_qoi_num);
3900 
3901         // Another to hold the adjoint constraint rhs
3902         std::vector<Number> adjoint_constraint_rhs(max_qoi_num, 0.0);
3903 
3904         // Find and gather recursive constraints for each adjoint variable
3905         for (auto & adjoint_map : _adjoint_constraint_values)
3906           {
3907             const std::size_t q = adjoint_map.first;
3908             adjoint_rhs_iterators[q] = adjoint_map.second.find(*i);
3909 
3910             adjoint_constraint_rhs[q] =
3911               (adjoint_rhs_iterators[q] == adjoint_map.second.end()) ?
3912               0 : adjoint_rhs_iterators[q]->second;
3913           }
3914 
3915         std::vector<dof_id_type> constraints_to_expand;
3916 
3917         for (const auto & item : constraint_row)
3918           if (item.first != *i && this->is_constrained_dof(item.first))
3919             {
3920               unexpanded_set.insert(item.first);
3921               constraints_to_expand.push_back(item.first);
3922             }
3923 
3924         for (const auto & expandable : constraints_to_expand)
3925           {
3926             const Real this_coef = constraint_row[expandable];
3927 
3928             DofConstraints::const_iterator
3929               subpos = _dof_constraints.find(expandable);
3930 
3931             libmesh_assert (subpos != _dof_constraints.end());
3932 
3933             const DofConstraintRow & subconstraint_row = subpos->second;
3934 
3935             for (const auto & item : subconstraint_row)
3936               {
3937                 // Assert that the constraint does not form a cycle.
3938                 libmesh_assert(item.first != expandable);
3939                 constraint_row[item.first] += item.second * this_coef;
3940               }
3941 
3942             DofConstraintValueMap::const_iterator subrhsit =
3943               _primal_constraint_values.find(expandable);
3944             if (subrhsit != _primal_constraint_values.end())
3945               constraint_rhs += subrhsit->second * this_coef;
3946 
3947             // Find and gather recursive constraints for each adjoint variable
3948             for (const auto & adjoint_map : _adjoint_constraint_values)
3949               {
3950                 const std::size_t q = adjoint_map.first;
3951 
3952                 DofConstraintValueMap::const_iterator adjoint_subrhsit =
3953                   adjoint_map.second.find(expandable);
3954 
3955                 if (adjoint_subrhsit != adjoint_map.second.end())
3956                 adjoint_constraint_rhs[q] += adjoint_subrhsit->second * this_coef;
3957               }
3958 
3959             constraint_row.erase(expandable);
3960           }
3961 
3962         if (rhsit == _primal_constraint_values.end())
3963           {
3964             if (constraint_rhs != Number(0))
3965               _primal_constraint_values[*i] = constraint_rhs;
3966             else
3967               _primal_constraint_values.erase(*i);
3968           }
3969         else
3970           {
3971             if (constraint_rhs != Number(0))
3972               rhsit->second = constraint_rhs;
3973             else
3974               _primal_constraint_values.erase(rhsit);
3975           }
3976 
3977         // Finally fill in the adjoint constraints for each adjoint variable if possible
3978         for (auto & adjoint_map : _adjoint_constraint_values)
3979           {
3980             const std::size_t q = adjoint_map.first;
3981 
3982             if(adjoint_rhs_iterators[q] == adjoint_map.second.end())
3983               {
3984                 if (adjoint_constraint_rhs[q] != Number(0))
3985                    (adjoint_map.second)[*i] = adjoint_constraint_rhs[q];
3986                 else
3987                   adjoint_map.second.erase(*i);
3988               }
3989             else
3990               {
3991                 if (adjoint_constraint_rhs[q] != Number(0))
3992                   adjoint_rhs_iterators[q]->second = adjoint_constraint_rhs[q];
3993                 else
3994                   adjoint_map.second.erase(adjoint_rhs_iterators[q]);
3995               }
3996           }
3997 
3998         if (constraints_to_expand.empty())
3999           i = unexpanded_set.erase(i);
4000         else
4001           ++i;
4002       }
4003 
4004   // In parallel we can't guarantee that nodes/dofs which constrain
4005   // others are on processors which are aware of that constraint, yet
4006   // we need such awareness for sparsity pattern generation.  So send
4007   // other processors any constraints they might need to know about.
4008   this->scatter_constraints(mesh);
4009 
4010   // Now that we have our root constraint dependencies sorted out, add
4011   // them to the send_list
4012   this->add_constraints_to_send_list();
4013 }
4014 
4015 
4016 #ifdef LIBMESH_ENABLE_CONSTRAINTS
check_for_cyclic_constraints()4017 void DofMap::check_for_cyclic_constraints()
4018 {
4019   // Eventually make this officially libmesh_deprecated();
4020   check_for_constraint_loops();
4021 }
4022 
check_for_constraint_loops()4023 void DofMap::check_for_constraint_loops()
4024 {
4025   // Create a set containing the DOFs we already depend on
4026   typedef std::set<dof_id_type> RCSet;
4027   RCSet unexpanded_set;
4028 
4029   // Use dof_constraints_copy in this method so that we don't
4030   // mess with _dof_constraints.
4031   DofConstraints dof_constraints_copy = _dof_constraints;
4032 
4033   for (const auto & i : dof_constraints_copy)
4034     unexpanded_set.insert(i.first);
4035 
4036   while (!unexpanded_set.empty())
4037     for (RCSet::iterator i = unexpanded_set.begin();
4038          i != unexpanded_set.end(); /* nothing */)
4039       {
4040         // If the DOF is constrained
4041         DofConstraints::iterator
4042           pos = dof_constraints_copy.find(*i);
4043 
4044         libmesh_assert (pos != dof_constraints_copy.end());
4045 
4046         DofConstraintRow & constraint_row = pos->second;
4047 
4048         // Comment out "rhs" parts of this method copied from process_constraints
4049         // DofConstraintValueMap::iterator rhsit =
4050         //   _primal_constraint_values.find(*i);
4051         // Number constraint_rhs = (rhsit == _primal_constraint_values.end()) ?
4052         //   0 : rhsit->second;
4053 
4054         std::vector<dof_id_type> constraints_to_expand;
4055 
4056         for (const auto & item : constraint_row)
4057           if (item.first != *i && this->is_constrained_dof(item.first))
4058             {
4059               unexpanded_set.insert(item.first);
4060               constraints_to_expand.push_back(item.first);
4061             }
4062 
4063         for (const auto & expandable : constraints_to_expand)
4064           {
4065             const Real this_coef = constraint_row[expandable];
4066 
4067             DofConstraints::const_iterator
4068               subpos = dof_constraints_copy.find(expandable);
4069 
4070             libmesh_assert (subpos != dof_constraints_copy.end());
4071 
4072             const DofConstraintRow & subconstraint_row = subpos->second;
4073 
4074             for (const auto & item : subconstraint_row)
4075               {
4076                 libmesh_error_msg_if(item.first == expandable, "Constraint loop detected");
4077 
4078                 constraint_row[item.first] += item.second * this_coef;
4079               }
4080 
4081             // Comment out "rhs" parts of this method copied from process_constraints
4082             // DofConstraintValueMap::const_iterator subrhsit =
4083             //   _primal_constraint_values.find(expandable);
4084             // if (subrhsit != _primal_constraint_values.end())
4085             //   constraint_rhs += subrhsit->second * this_coef;
4086 
4087             constraint_row.erase(expandable);
4088           }
4089 
4090         // Comment out "rhs" parts of this method copied from process_constraints
4091         // if (rhsit == _primal_constraint_values.end())
4092         //   {
4093         //     if (constraint_rhs != Number(0))
4094         //       _primal_constraint_values[*i] = constraint_rhs;
4095         //     else
4096         //       _primal_constraint_values.erase(*i);
4097         //   }
4098         // else
4099         //   {
4100         //     if (constraint_rhs != Number(0))
4101         //       rhsit->second = constraint_rhs;
4102         //     else
4103         //       _primal_constraint_values.erase(rhsit);
4104         //   }
4105 
4106         if (constraints_to_expand.empty())
4107           i = unexpanded_set.erase(i);
4108         else
4109           ++i;
4110       }
4111 }
4112 #else
check_for_constraint_loops()4113 void DofMap::check_for_constraint_loops() {}
check_for_cyclic_constraints()4114 void DofMap::check_for_cyclic_constraints()
4115 {
4116   // Do nothing
4117 }
4118 #endif
4119 
4120 
scatter_constraints(MeshBase & mesh)4121 void DofMap::scatter_constraints(MeshBase & mesh)
4122 {
4123   // At this point each processor with a constrained node knows
4124   // the corresponding constraint row, but we also need each processor
4125   // with a constrainer node to know the corresponding row(s).
4126 
4127   // This function must be run on all processors at once
4128   parallel_object_only();
4129 
4130   // Return immediately if there's nothing to gather
4131   if (this->n_processors() == 1)
4132     return;
4133 
4134   // We might get to return immediately if none of the processors
4135   // found any constraints
4136   unsigned int has_constraints = !_dof_constraints.empty()
4137 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS
4138     || !_node_constraints.empty()
4139 #endif // LIBMESH_ENABLE_NODE_CONSTRAINTS
4140     ;
4141   this->comm().max(has_constraints);
4142   if (!has_constraints)
4143     return;
4144 
4145   // We may be receiving packed_range sends out of order with
4146   // parallel_sync tags, so make sure they're received correctly.
4147   Parallel::MessageTag range_tag = this->comm().get_unique_tag();
4148 
4149 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS
4150   std::map<processor_id_type, std::set<dof_id_type>> pushed_node_ids;
4151 #endif // LIBMESH_ENABLE_NODE_CONSTRAINTS
4152 
4153   std::map<processor_id_type, std::set<dof_id_type>> pushed_ids;
4154 
4155   // Collect the dof constraints I need to push to each processor
4156   dof_id_type constrained_proc_id = 0;
4157   for (auto & i : _dof_constraints)
4158     {
4159       const dof_id_type constrained = i.first;
4160       while (constrained >= _end_df[constrained_proc_id])
4161         constrained_proc_id++;
4162 
4163       if (constrained_proc_id != this->processor_id())
4164         continue;
4165 
4166       DofConstraintRow & row = i.second;
4167       for (auto & j : row)
4168         {
4169           const dof_id_type constraining = j.first;
4170 
4171           processor_id_type constraining_proc_id = 0;
4172           while (constraining >= _end_df[constraining_proc_id])
4173             constraining_proc_id++;
4174 
4175           if (constraining_proc_id != this->processor_id() &&
4176               constraining_proc_id != constrained_proc_id)
4177             pushed_ids[constraining_proc_id].insert(constrained);
4178         }
4179     }
4180 
4181   // Pack the dof constraint rows and rhs's to push
4182 
4183   std::map<processor_id_type,
4184           std::vector<std::vector<std::pair<dof_id_type, Real>>>>
4185     pushed_keys_vals, pushed_keys_vals_to_me;
4186 
4187   std::map<processor_id_type, std::vector<std::pair<dof_id_type, Number>>>
4188     pushed_ids_rhss, pushed_ids_rhss_to_me;
4189 
4190   auto gather_ids =
4191     [this,
4192      & pushed_ids,
4193      & pushed_keys_vals,
4194      & pushed_ids_rhss]
4195     ()
4196     {
4197       for (auto & pid_id_pair : pushed_ids)
4198         {
4199           const processor_id_type pid = pid_id_pair.first;
4200           const std::set<dof_id_type>
4201             & pid_ids = pid_id_pair.second;
4202 
4203           const std::size_t ids_size = pid_ids.size();
4204           std::vector<std::vector<std::pair<dof_id_type, Real>>> &
4205             keys_vals = pushed_keys_vals[pid];
4206           std::vector<std::pair<dof_id_type,Number>> &
4207             ids_rhss = pushed_ids_rhss[pid];
4208           keys_vals.resize(ids_size);
4209           ids_rhss.resize(ids_size);
4210 
4211           std::size_t push_i;
4212           std::set<dof_id_type>::const_iterator it;
4213           for (push_i = 0, it = pid_ids.begin();
4214                it != pid_ids.end(); ++push_i, ++it)
4215             {
4216               const dof_id_type constrained = *it;
4217               DofConstraintRow & row = _dof_constraints[constrained];
4218               keys_vals[push_i].assign(row.begin(), row.end());
4219 
4220               DofConstraintValueMap::const_iterator rhsit =
4221                 _primal_constraint_values.find(constrained);
4222               ids_rhss[push_i].first = constrained;
4223               ids_rhss[push_i].second =
4224                 (rhsit == _primal_constraint_values.end()) ?
4225                 0 : rhsit->second;
4226             }
4227         }
4228     };
4229 
4230   gather_ids();
4231 
4232   auto ids_rhss_action_functor =
4233     [& pushed_ids_rhss_to_me]
4234     (processor_id_type pid,
4235      const std::vector<std::pair<dof_id_type, Number>> & data)
4236     {
4237       pushed_ids_rhss_to_me[pid] = data;
4238     };
4239 
4240   auto keys_vals_action_functor =
4241     [& pushed_keys_vals_to_me]
4242     (processor_id_type pid,
4243      const std::vector<std::vector<std::pair<dof_id_type, Real>>> & data)
4244     {
4245       pushed_keys_vals_to_me[pid] = data;
4246     };
4247 
4248   Parallel::push_parallel_vector_data
4249     (this->comm(), pushed_ids_rhss, ids_rhss_action_functor);
4250   Parallel::push_parallel_vector_data
4251     (this->comm(), pushed_keys_vals, keys_vals_action_functor);
4252 
4253   // Now work on traded dof constraint rows
4254   auto receive_dof_constraints =
4255     [this,
4256      & pushed_ids_rhss_to_me,
4257      & pushed_keys_vals_to_me]
4258     ()
4259     {
4260       for (auto & pid_id_pair : pushed_ids_rhss_to_me)
4261         {
4262           const processor_id_type pid = pid_id_pair.first;
4263           const auto & ids_rhss = pid_id_pair.second;
4264           const auto & keys_vals = pushed_keys_vals_to_me[pid];
4265 
4266           libmesh_assert_equal_to
4267             (ids_rhss.size(), keys_vals.size());
4268 
4269           // Add the dof constraints that I've been sent
4270           for (auto i : index_range(ids_rhss))
4271             {
4272               dof_id_type constrained = ids_rhss[i].first;
4273 
4274               // If we don't already have a constraint for this dof,
4275               // add the one we were sent
4276               if (!this->is_constrained_dof(constrained))
4277                 {
4278                   DofConstraintRow & row = _dof_constraints[constrained];
4279                   for (auto & key_val : keys_vals[i])
4280                     {
4281                       libmesh_assert_less(key_val.first, this->n_dofs());
4282                       row[key_val.first] = key_val.second;
4283                     }
4284                   if (ids_rhss[i].second != Number(0))
4285                     _primal_constraint_values[constrained] =
4286                       ids_rhss[i].second;
4287                   else
4288                     _primal_constraint_values.erase(constrained);
4289                 }
4290             }
4291         }
4292     };
4293 
4294   receive_dof_constraints();
4295 
4296 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS
4297   // Collect the node constraints to push to each processor
4298   for (auto & i : _node_constraints)
4299     {
4300       const Node * constrained = i.first;
4301 
4302       if (constrained->processor_id() != this->processor_id())
4303         continue;
4304 
4305       NodeConstraintRow & row = i.second.first;
4306       for (auto & j : row)
4307         {
4308           const Node * constraining = j.first;
4309 
4310           if (constraining->processor_id() != this->processor_id() &&
4311               constraining->processor_id() != constrained->processor_id())
4312             pushed_node_ids[constraining->processor_id()].insert(constrained->id());
4313         }
4314     }
4315 
4316   // Pack the node constraint rows and rhss to push
4317   std::map<processor_id_type,
4318           std::vector<std::vector<std::pair<dof_id_type,Real>>>>
4319     pushed_node_keys_vals, pushed_node_keys_vals_to_me;
4320   std::map<processor_id_type, std::vector<std::pair<dof_id_type, Point>>>
4321     pushed_node_ids_offsets, pushed_node_ids_offsets_to_me;
4322   std::map<processor_id_type, std::set<Node *>> pushed_node_sets;
4323 
4324   for (auto & pid_id_pair : pushed_node_ids)
4325     {
4326       const processor_id_type pid = pid_id_pair.first;
4327       const std::set<dof_id_type>
4328         & pid_ids = pid_id_pair.second;
4329 
4330       const std::size_t ids_size = pid_ids.size();
4331       std::vector<std::vector<std::pair<dof_id_type,Real>>> &
4332         keys_vals = pushed_node_keys_vals[pid];
4333       std::vector<std::pair<dof_id_type, Point>> &
4334         ids_offsets = pushed_node_ids_offsets[pid];
4335       keys_vals.resize(ids_size);
4336       ids_offsets.resize(ids_size);
4337       std::set<Node *> nodes;
4338 
4339       std::size_t push_i;
4340       std::set<dof_id_type>::const_iterator it;
4341       for (push_i = 0, it = pid_ids.begin();
4342            it != pid_ids.end(); ++push_i, ++it)
4343         {
4344           Node * constrained = mesh.node_ptr(*it);
4345 
4346           if (constrained->processor_id() != pid)
4347             nodes.insert(constrained);
4348 
4349           NodeConstraintRow & row = _node_constraints[constrained].first;
4350           std::size_t row_size = row.size();
4351           keys_vals[push_i].reserve(row_size);
4352           for (const auto & j : row)
4353             {
4354               Node * constraining = const_cast<Node *>(j.first);
4355 
4356               keys_vals[push_i].emplace_back(constraining->id(), j.second);
4357 
4358               if (constraining->processor_id() != pid)
4359                 nodes.insert(constraining);
4360             }
4361 
4362           ids_offsets[push_i].first = *it;
4363           ids_offsets[push_i].second = _node_constraints[constrained].second;
4364         }
4365 
4366       if (!mesh.is_serial())
4367         {
4368           auto & pid_nodes = pushed_node_sets[pid];
4369           pid_nodes.insert(nodes.begin(), nodes.end());
4370         }
4371     }
4372 
4373   auto node_ids_offsets_action_functor =
4374     [& pushed_node_ids_offsets_to_me]
4375     (processor_id_type pid,
4376      const std::vector<std::pair<dof_id_type, Point>> & data)
4377     {
4378       pushed_node_ids_offsets_to_me[pid] = data;
4379     };
4380 
4381   auto node_keys_vals_action_functor =
4382     [& pushed_node_keys_vals_to_me]
4383     (processor_id_type pid,
4384      const std::vector<std::vector<std::pair<dof_id_type, Real>>> & data)
4385     {
4386       pushed_node_keys_vals_to_me[pid] = data;
4387     };
4388 
4389   // Trade pushed node constraint rows
4390   Parallel::push_parallel_vector_data
4391     (this->comm(), pushed_node_ids_offsets, node_ids_offsets_action_functor);
4392   Parallel::push_parallel_vector_data
4393     (this->comm(), pushed_node_keys_vals, node_keys_vals_action_functor);
4394 
4395   // Constraining nodes might not even exist on our subset of a
4396   // distributed mesh, so let's make them exist.
4397   auto insert_nodes_functor =
4398     [& mesh]
4399     (processor_id_type /* pid */,
4400      const std::set<Node *> & nodes)
4401     {
4402       for (Node * node : nodes)
4403         mesh.add_node(node);
4404     };
4405 
4406   if (!mesh.is_serial())
4407     Parallel::push_parallel_packed_range
4408       (this->comm(), pushed_node_sets, &mesh, insert_nodes_functor);
4409 
4410   for (auto & pid_id_pair : pushed_node_ids_offsets_to_me)
4411     {
4412       const processor_id_type pid = pid_id_pair.first;
4413       const auto & ids_offsets = pid_id_pair.second;
4414       const auto & keys_vals = pushed_node_keys_vals_to_me[pid];
4415 
4416       libmesh_assert_equal_to
4417         (ids_offsets.size(), keys_vals.size());
4418 
4419       // Add the node constraints that I've been sent
4420       for (auto i : index_range(ids_offsets))
4421         {
4422           dof_id_type constrained_id = ids_offsets[i].first;
4423 
4424           // If we don't already have a constraint for this node,
4425           // add the one we were sent
4426           const Node * constrained = mesh.node_ptr(constrained_id);
4427           if (!this->is_constrained_node(constrained))
4428             {
4429               NodeConstraintRow & row = _node_constraints[constrained].first;
4430               for (auto & key_val : keys_vals[i])
4431                 {
4432                   const Node * key_node = mesh.node_ptr(key_val.first);
4433                   row[key_node] = key_val.second;
4434                 }
4435               _node_constraints[constrained].second =
4436                 ids_offsets[i].second;
4437             }
4438         }
4439     }
4440 #endif // LIBMESH_ENABLE_NODE_CONSTRAINTS
4441 
4442   // Next we need to push constraints to processors which don't own
4443   // the constrained dof, don't own the constraining dof, but own an
4444   // element supporting the constraining dof.
4445   //
4446   // We need to be able to quickly look up constrained dof ids by what
4447   // constrains them, so that we can handle the case where we see a
4448   // foreign element containing one of our constraining DoF ids and we
4449   // need to push that constraint.
4450   //
4451   // Getting distributed adaptive sparsity patterns right is hard.
4452 
4453   typedef std::map<dof_id_type, std::set<dof_id_type>> DofConstrainsMap;
4454   DofConstrainsMap dof_id_constrains;
4455 
4456   for (auto & i : _dof_constraints)
4457     {
4458       const dof_id_type constrained = i.first;
4459       DofConstraintRow & row = i.second;
4460       for (const auto & j : row)
4461         {
4462           const dof_id_type constraining = j.first;
4463 
4464           dof_id_type constraining_proc_id = 0;
4465           while (constraining >= _end_df[constraining_proc_id])
4466             constraining_proc_id++;
4467 
4468           if (constraining_proc_id == this->processor_id())
4469             dof_id_constrains[constraining].insert(constrained);
4470         }
4471     }
4472 
4473   // Loop over all foreign elements, find any supporting our
4474   // constrained dof indices.
4475   pushed_ids.clear();
4476 
4477   for (const auto & elem : as_range(mesh.active_not_local_elements_begin(),
4478                                     mesh.active_not_local_elements_end()))
4479     {
4480       std::vector<dof_id_type> my_dof_indices;
4481       this->dof_indices (elem, my_dof_indices);
4482 
4483       for (const auto & dof : my_dof_indices)
4484         {
4485           DofConstrainsMap::const_iterator dcmi = dof_id_constrains.find(dof);
4486           if (dcmi != dof_id_constrains.end())
4487             {
4488               for (const auto & constrained : dcmi->second)
4489                 {
4490                   dof_id_type the_constrained_proc_id = 0;
4491                   while (constrained >= _end_df[the_constrained_proc_id])
4492                     the_constrained_proc_id++;
4493 
4494                   const processor_id_type elemproc = elem->processor_id();
4495                   if (elemproc != the_constrained_proc_id)
4496                     pushed_ids[elemproc].insert(constrained);
4497                 }
4498             }
4499         }
4500     }
4501 
4502   pushed_ids_rhss.clear();
4503   pushed_ids_rhss_to_me.clear();
4504   pushed_keys_vals.clear();
4505   pushed_keys_vals_to_me.clear();
4506 
4507   gather_ids();
4508 
4509   // Trade pushed dof constraint rows
4510   Parallel::push_parallel_vector_data
4511     (this->comm(), pushed_ids_rhss, ids_rhss_action_functor);
4512   Parallel::push_parallel_vector_data
4513     (this->comm(), pushed_keys_vals, keys_vals_action_functor);
4514 
4515   receive_dof_constraints();
4516 
4517   // Finally, we need to handle the case of remote dof coupling.  If a
4518   // processor's element is coupled to a ghost element, then the
4519   // processor needs to know about all constraints which affect the
4520   // dofs on that ghost element, so we'll have to query the ghost
4521   // element's owner.
4522 
4523   GhostingFunctor::map_type elements_to_couple;
4524 
4525   // Man, I wish we had guaranteed unique_ptr availability...
4526   std::set<CouplingMatrix*> temporary_coupling_matrices;
4527 
4528   this->merge_ghost_functor_outputs
4529     (elements_to_couple,
4530      temporary_coupling_matrices,
4531      this->coupling_functors_begin(),
4532      this->coupling_functors_end(),
4533      mesh.active_local_elements_begin(),
4534      mesh.active_local_elements_end(),
4535      this->processor_id());
4536 
4537   // Each ghost-coupled element's owner should get a request for its dofs
4538   std::set<dof_id_type> requested_dofs;
4539 
4540   for (const auto & pr : elements_to_couple)
4541     {
4542       const Elem * elem = pr.first;
4543 
4544       // FIXME - optimize for the non-fully-coupled case?
4545       std::vector<dof_id_type> element_dofs;
4546       this->dof_indices(elem, element_dofs);
4547 
4548       for (auto dof : element_dofs)
4549         requested_dofs.insert(dof);
4550     }
4551 
4552   this->gather_constraints(mesh, requested_dofs, false);
4553 }
4554 
4555 
gather_constraints(MeshBase &,std::set<dof_id_type> & unexpanded_dofs,bool)4556 void DofMap::gather_constraints (MeshBase & /*mesh*/,
4557                                  std::set<dof_id_type> & unexpanded_dofs,
4558                                  bool /*look_for_constrainees*/)
4559 {
4560   typedef std::set<dof_id_type> DoF_RCSet;
4561 
4562   // If we have heterogenous adjoint constraints we need to
4563   // communicate those too.
4564   const unsigned int max_qoi_num =
4565     _adjoint_constraint_values.empty() ?
4566     0 : _adjoint_constraint_values.rbegin()->first+1;
4567 
4568   // We have to keep recursing while the unexpanded set is
4569   // nonempty on *any* processor
4570   bool unexpanded_set_nonempty = !unexpanded_dofs.empty();
4571   this->comm().max(unexpanded_set_nonempty);
4572 
4573   while (unexpanded_set_nonempty)
4574     {
4575       // Let's make sure we don't lose sync in this loop.
4576       parallel_object_only();
4577 
4578       // Request sets
4579       DoF_RCSet   dof_request_set;
4580 
4581       // Request sets to send to each processor
4582       std::map<processor_id_type, std::vector<dof_id_type>>
4583         requested_dof_ids;
4584 
4585       // And the sizes of each
4586       std::map<processor_id_type, dof_id_type>
4587         dof_ids_on_proc;
4588 
4589       // Fill (and thereby sort and uniq!) the main request sets
4590       for (const auto & unexpanded_dof : unexpanded_dofs)
4591         {
4592           DofConstraints::const_iterator
4593             pos = _dof_constraints.find(unexpanded_dof);
4594 
4595           // If we were asked for a DoF and we don't already have a
4596           // constraint for it, then we need to check for one.
4597           if (pos == _dof_constraints.end())
4598             {
4599               if (!this->local_index(unexpanded_dof) &&
4600                   !_dof_constraints.count(unexpanded_dof) )
4601                 dof_request_set.insert(unexpanded_dof);
4602             }
4603           // If we were asked for a DoF and we already have a
4604           // constraint for it, then we need to check if the
4605           // constraint is recursive.
4606           else
4607             {
4608               const DofConstraintRow & row = pos->second;
4609               for (const auto & j : row)
4610                 {
4611                   const dof_id_type constraining_dof = j.first;
4612 
4613                   // If it's non-local and we haven't already got a
4614                   // constraint for it, we might need to ask for one
4615                   if (!this->local_index(constraining_dof) &&
4616                       !_dof_constraints.count(constraining_dof))
4617                     dof_request_set.insert(constraining_dof);
4618                 }
4619             }
4620         }
4621 
4622       // Clear the unexpanded constraint set; we're about to expand it
4623       unexpanded_dofs.clear();
4624 
4625       // Count requests by processor
4626       processor_id_type proc_id = 0;
4627       for (const auto & i : dof_request_set)
4628         {
4629           while (i >= _end_df[proc_id])
4630             proc_id++;
4631           dof_ids_on_proc[proc_id]++;
4632         }
4633 
4634       for (auto & pair : dof_ids_on_proc)
4635         {
4636           requested_dof_ids[pair.first].reserve(pair.second);
4637         }
4638 
4639       // Prepare each processor's request set
4640       proc_id = 0;
4641       for (const auto & i : dof_request_set)
4642         {
4643           while (i >= _end_df[proc_id])
4644             proc_id++;
4645           requested_dof_ids[proc_id].push_back(i);
4646         }
4647 
4648       typedef std::vector<std::pair<dof_id_type, Real>> row_datum;
4649 
4650       typedef std::vector<Number> rhss_datum;
4651 
4652       auto row_gather_functor =
4653         [this]
4654         (processor_id_type,
4655          const std::vector<dof_id_type> & ids,
4656          std::vector<row_datum> & data)
4657         {
4658           // Fill those requests
4659           const std::size_t query_size = ids.size();
4660 
4661           data.resize(query_size);
4662           for (std::size_t i=0; i != query_size; ++i)
4663             {
4664               dof_id_type constrained = ids[i];
4665               if (_dof_constraints.count(constrained))
4666                 {
4667                   DofConstraintRow & row = _dof_constraints[constrained];
4668                   std::size_t row_size = row.size();
4669                   data[i].reserve(row_size);
4670                   for (const auto & j : row)
4671                     {
4672                       data[i].push_back(j);
4673 
4674                       // We should never have an invalid constraining
4675                       // dof id
4676                       libmesh_assert(j.first != DofObject::invalid_id);
4677 
4678                       // We should never have a 0 constraint
4679                       // coefficient; that's implicit via sparse
4680                       // constraint storage
4681                       //
4682                       // But we can't easily control how users add
4683                       // constraints, so we can't safely assert that
4684                       // we're being efficient here.
4685                       //
4686                       // libmesh_assert(j.second);
4687                     }
4688                 }
4689               else
4690                 {
4691                   // We have to distinguish "constraint with no
4692                   // constraining dofs" (e.g. due to Dirichlet
4693                   // constraint equations) from "no constraint".
4694                   // We'll use invalid_id for the latter.
4695                   data[i].emplace_back(DofObject::invalid_id, Real(0));
4696                 }
4697             }
4698         };
4699 
4700       auto rhss_gather_functor =
4701         [this,
4702          max_qoi_num]
4703         (processor_id_type,
4704          const std::vector<dof_id_type> & ids,
4705          std::vector<rhss_datum> & data)
4706         {
4707           // Fill those requests
4708           const std::size_t query_size = ids.size();
4709 
4710           data.resize(query_size);
4711           for (std::size_t i=0; i != query_size; ++i)
4712             {
4713               dof_id_type constrained = ids[i];
4714               data[i].clear();
4715               if (_dof_constraints.count(constrained))
4716                 {
4717                   DofConstraintValueMap::const_iterator rhsit =
4718                     _primal_constraint_values.find(constrained);
4719                   data[i].push_back
4720                     ((rhsit == _primal_constraint_values.end()) ?
4721                      0 : rhsit->second);
4722 
4723                   for (unsigned int q = 0; q != max_qoi_num; ++q)
4724                     {
4725                       AdjointDofConstraintValues::const_iterator adjoint_map_it =
4726                         _adjoint_constraint_values.find(q);
4727 
4728                       if (adjoint_map_it == _adjoint_constraint_values.end())
4729                         {
4730                           data[i].push_back(0);
4731                           continue;
4732                         }
4733 
4734                       const DofConstraintValueMap & constraint_map =
4735                         adjoint_map_it->second;
4736 
4737                       DofConstraintValueMap::const_iterator adj_rhsit =
4738                         constraint_map.find(constrained);
4739                       data[i].push_back
4740                         ((adj_rhsit == constraint_map.end()) ?
4741                          0 : adj_rhsit->second);
4742                     }
4743                 }
4744             }
4745         };
4746 
4747       auto row_action_functor =
4748         [this,
4749          & unexpanded_dofs]
4750         (processor_id_type,
4751          const std::vector<dof_id_type> & ids,
4752          const std::vector<row_datum> & data)
4753         {
4754           // Add any new constraint rows we've found
4755           const std::size_t query_size = ids.size();
4756 
4757           for (std::size_t i=0; i != query_size; ++i)
4758             {
4759               const dof_id_type constrained = ids[i];
4760 
4761               // An empty row is an constraint with an empty row; for
4762               // no constraint we use a "no row" placeholder
4763               if (data[i].empty())
4764                 {
4765                   DofConstraintRow & row = _dof_constraints[constrained];
4766                   row.clear();
4767                 }
4768               else if (data[i][0].first != DofObject::invalid_id)
4769                 {
4770                   DofConstraintRow & row = _dof_constraints[constrained];
4771                   row.clear();
4772                   for (auto & pair : data[i])
4773                     {
4774                       libmesh_assert_less(pair.first, this->n_dofs());
4775                       row[pair.first] = pair.second;
4776                     }
4777 
4778                   // And prepare to check for more recursive constraints
4779                   unexpanded_dofs.insert(constrained);
4780                 }
4781             }
4782         };
4783 
4784       auto rhss_action_functor =
4785         [this,
4786          max_qoi_num]
4787         (processor_id_type,
4788          const std::vector<dof_id_type> & ids,
4789          const std::vector<rhss_datum> & data)
4790         {
4791           // Add rhs data for any new constraint rows we've found
4792           const std::size_t query_size = ids.size();
4793 
4794           for (std::size_t i=0; i != query_size; ++i)
4795             {
4796               if (!data[i].empty())
4797                 {
4798                   dof_id_type constrained = ids[i];
4799                   if (data[i][0] != Number(0))
4800                     _primal_constraint_values[constrained] = data[i][0];
4801                   else
4802                     _primal_constraint_values.erase(constrained);
4803 
4804                   for (unsigned int q = 0; q != max_qoi_num; ++q)
4805                     {
4806                       AdjointDofConstraintValues::iterator adjoint_map_it =
4807                         _adjoint_constraint_values.find(q);
4808 
4809                       if ((adjoint_map_it == _adjoint_constraint_values.end()) &&
4810                           data[i][q+1] == Number(0))
4811                         continue;
4812 
4813                       if (adjoint_map_it == _adjoint_constraint_values.end())
4814                         adjoint_map_it = _adjoint_constraint_values.emplace
4815                           (q, DofConstraintValueMap()).first;
4816 
4817                       DofConstraintValueMap & constraint_map =
4818                         adjoint_map_it->second;
4819 
4820                       if (data[i][q+1] != Number(0))
4821                         constraint_map[constrained] =
4822                           data[i][q+1];
4823                       else
4824                         constraint_map.erase(constrained);
4825                     }
4826                 }
4827             }
4828 
4829         };
4830 
4831       // Now request constraint rows from other processors
4832       row_datum * row_ex = nullptr;
4833       Parallel::pull_parallel_vector_data
4834         (this->comm(), requested_dof_ids, row_gather_functor,
4835          row_action_functor, row_ex);
4836 
4837       // And request constraint right hand sides from other procesors
4838       rhss_datum * rhs_ex = nullptr;
4839       Parallel::pull_parallel_vector_data
4840         (this->comm(), requested_dof_ids, rhss_gather_functor,
4841          rhss_action_functor, rhs_ex);
4842 
4843       // We have to keep recursing while the unexpanded set is
4844       // nonempty on *any* processor
4845       unexpanded_set_nonempty = !unexpanded_dofs.empty();
4846       this->comm().max(unexpanded_set_nonempty);
4847     }
4848 }
4849 
add_constraints_to_send_list()4850 void DofMap::add_constraints_to_send_list()
4851 {
4852   // This function must be run on all processors at once
4853   parallel_object_only();
4854 
4855   // Return immediately if there's nothing to gather
4856   if (this->n_processors() == 1)
4857     return;
4858 
4859   // We might get to return immediately if none of the processors
4860   // found any constraints
4861   unsigned int has_constraints = !_dof_constraints.empty();
4862   this->comm().max(has_constraints);
4863   if (!has_constraints)
4864     return;
4865 
4866   for (const auto & i : _dof_constraints)
4867     {
4868       dof_id_type constrained_dof = i.first;
4869 
4870       // We only need the dependencies of our own constrained dofs
4871       if (!this->local_index(constrained_dof))
4872         continue;
4873 
4874       const DofConstraintRow & constraint_row = i.second;
4875       for (const auto & j : constraint_row)
4876         {
4877           dof_id_type constraint_dependency = j.first;
4878 
4879           // No point in adding one of our own dofs to the send_list
4880           if (this->local_index(constraint_dependency))
4881             continue;
4882 
4883           _send_list.push_back(constraint_dependency);
4884         }
4885     }
4886 }
4887 
4888 
4889 
4890 #endif // LIBMESH_ENABLE_CONSTRAINTS
4891 
4892 
4893 #ifdef LIBMESH_ENABLE_AMR
4894 
constrain_p_dofs(unsigned int var,const Elem * elem,unsigned int s,unsigned int p)4895 void DofMap::constrain_p_dofs (unsigned int var,
4896                                const Elem * elem,
4897                                unsigned int s,
4898                                unsigned int p)
4899 {
4900   // We're constraining dofs on elem which correspond to p refinement
4901   // levels above p - this only makes sense if elem's p refinement
4902   // level is above p.
4903   libmesh_assert_greater (elem->p_level(), p);
4904   libmesh_assert_less (s, elem->n_sides());
4905 
4906   const unsigned int sys_num = this->sys_number();
4907   FEType fe_type = this->variable_type(var);
4908 
4909   const unsigned int n_nodes = elem->n_nodes();
4910   for (unsigned int n = 0; n != n_nodes; ++n)
4911     if (elem->is_node_on_side(n, s))
4912       {
4913         const Node & node = elem->node_ref(n);
4914         const unsigned int low_nc =
4915           FEInterface::n_dofs_at_node (fe_type, p, elem, n);
4916         const unsigned int high_nc =
4917           FEInterface::n_dofs_at_node (fe_type, elem, n);
4918 
4919         // since we may be running this method concurrently
4920         // on multiple threads we need to acquire a lock
4921         // before modifying the _dof_constraints object.
4922         Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
4923 
4924         if (elem->is_vertex(n))
4925           {
4926             // Add "this is zero" constraint rows for high p vertex
4927             // dofs
4928             for (unsigned int i = low_nc; i != high_nc; ++i)
4929               {
4930                 _dof_constraints[node.dof_number(sys_num,var,i)].clear();
4931                 _primal_constraint_values.erase(node.dof_number(sys_num,var,i));
4932               }
4933           }
4934         else
4935           {
4936             const unsigned int total_dofs = node.n_comp(sys_num, var);
4937             libmesh_assert_greater_equal (total_dofs, high_nc);
4938             // Add "this is zero" constraint rows for high p
4939             // non-vertex dofs, which are numbered in reverse
4940             for (unsigned int j = low_nc; j != high_nc; ++j)
4941               {
4942                 const unsigned int i = total_dofs - j - 1;
4943                 _dof_constraints[node.dof_number(sys_num,var,i)].clear();
4944                 _primal_constraint_values.erase(node.dof_number(sys_num,var,i));
4945               }
4946           }
4947       }
4948 }
4949 
4950 #endif // LIBMESH_ENABLE_AMR
4951 
4952 
4953 #ifdef LIBMESH_ENABLE_DIRICHLET
add_dirichlet_boundary(const DirichletBoundary & dirichlet_boundary)4954 void DofMap::add_dirichlet_boundary (const DirichletBoundary & dirichlet_boundary)
4955 {
4956   _dirichlet_boundaries->push_back(new DirichletBoundary(dirichlet_boundary));
4957 }
4958 
4959 
add_adjoint_dirichlet_boundary(const DirichletBoundary & dirichlet_boundary,unsigned int qoi_index)4960 void DofMap::add_adjoint_dirichlet_boundary (const DirichletBoundary & dirichlet_boundary,
4961                                              unsigned int qoi_index)
4962 {
4963   unsigned int old_size = cast_int<unsigned int>
4964     (_adjoint_dirichlet_boundaries.size());
4965   for (unsigned int i = old_size; i <= qoi_index; ++i)
4966     _adjoint_dirichlet_boundaries.push_back(new DirichletBoundaries());
4967 
4968   _adjoint_dirichlet_boundaries[qoi_index]->push_back
4969     (new DirichletBoundary(dirichlet_boundary));
4970 }
4971 
4972 
has_adjoint_dirichlet_boundaries(unsigned int q)4973 bool DofMap::has_adjoint_dirichlet_boundaries(unsigned int q) const
4974 {
4975   if (_adjoint_dirichlet_boundaries.size() > q)
4976     return true;
4977 
4978   return false;
4979 }
4980 
4981 
4982 const DirichletBoundaries *
get_adjoint_dirichlet_boundaries(unsigned int q)4983 DofMap::get_adjoint_dirichlet_boundaries(unsigned int q) const
4984 {
4985   libmesh_assert_greater(_adjoint_dirichlet_boundaries.size(),q);
4986   return _adjoint_dirichlet_boundaries[q];
4987 }
4988 
4989 
4990 DirichletBoundaries *
get_adjoint_dirichlet_boundaries(unsigned int q)4991 DofMap::get_adjoint_dirichlet_boundaries(unsigned int q)
4992 {
4993   unsigned int old_size = cast_int<unsigned int>
4994     (_adjoint_dirichlet_boundaries.size());
4995   for (unsigned int i = old_size; i <= q; ++i)
4996     _adjoint_dirichlet_boundaries.push_back(new DirichletBoundaries());
4997 
4998   return _adjoint_dirichlet_boundaries[q];
4999 }
5000 
5001 
remove_dirichlet_boundary(const DirichletBoundary & boundary_to_remove)5002 void DofMap::remove_dirichlet_boundary (const DirichletBoundary & boundary_to_remove)
5003 {
5004   // Find a boundary condition matching the one to be removed
5005   auto lam = [&boundary_to_remove](const DirichletBoundary * bdy)
5006     {return bdy->b == boundary_to_remove.b && bdy->variables == boundary_to_remove.variables;};
5007 
5008   auto it = std::find_if(_dirichlet_boundaries->begin(), _dirichlet_boundaries->end(), lam);
5009 
5010   // Delete it and remove it
5011   libmesh_assert (it != _dirichlet_boundaries->end());
5012   delete *it;
5013   _dirichlet_boundaries->erase(it);
5014 }
5015 
5016 
remove_adjoint_dirichlet_boundary(const DirichletBoundary & boundary_to_remove,unsigned int qoi_index)5017 void DofMap::remove_adjoint_dirichlet_boundary (const DirichletBoundary & boundary_to_remove,
5018                                                 unsigned int qoi_index)
5019 {
5020   libmesh_assert_greater(_adjoint_dirichlet_boundaries.size(),
5021                          qoi_index);
5022 
5023   auto lam = [&boundary_to_remove](const DirichletBoundary * bdy)
5024     {return bdy->b == boundary_to_remove.b && bdy->variables == boundary_to_remove.variables;};
5025 
5026   auto it = std::find_if(_adjoint_dirichlet_boundaries[qoi_index]->begin(),
5027                          _adjoint_dirichlet_boundaries[qoi_index]->end(),
5028                          lam);
5029 
5030   // Delete it and remove it
5031   libmesh_assert (it != _adjoint_dirichlet_boundaries[qoi_index]->end());
5032   delete *it;
5033   _adjoint_dirichlet_boundaries[qoi_index]->erase(it);
5034 }
5035 
5036 
~DirichletBoundaries()5037 DirichletBoundaries::~DirichletBoundaries()
5038 {
5039   for (auto & item : *this)
5040     delete item;
5041 }
5042 
check_dirichlet_bcid_consistency(const MeshBase & mesh,const DirichletBoundary & boundary)5043 void DofMap::check_dirichlet_bcid_consistency (const MeshBase & mesh,
5044                                                const DirichletBoundary & boundary) const
5045 {
5046   const std::set<boundary_id_type>& mesh_bcids = mesh.get_boundary_info().get_boundary_ids();
5047   const std::set<boundary_id_type>& dbc_bcids = boundary.b;
5048 
5049   // DirichletBoundary id sets should be consistent across all ranks
5050   libmesh_assert(mesh.comm().verify(dbc_bcids.size()));
5051 
5052   for (const auto & bc_id : dbc_bcids)
5053     {
5054       // DirichletBoundary id sets should be consistent across all ranks
5055       libmesh_assert(mesh.comm().verify(bc_id));
5056 
5057       bool found_bcid = (mesh_bcids.find(bc_id) != mesh_bcids.end());
5058 
5059       // On a distributed mesh, boundary id sets may *not* be
5060       // consistent across all ranks, since not all ranks see all
5061       // boundaries
5062       mesh.comm().max(found_bcid);
5063 
5064       libmesh_error_msg_if(!found_bcid,
5065                            "Could not find Dirichlet boundary id " << bc_id << " in mesh!");
5066     }
5067 }
5068 
5069 #endif // LIBMESH_ENABLE_DIRICHLET
5070 
5071 
5072 #ifdef LIBMESH_ENABLE_PERIODIC
5073 
add_periodic_boundary(const PeriodicBoundaryBase & periodic_boundary)5074 void DofMap::add_periodic_boundary (const PeriodicBoundaryBase & periodic_boundary)
5075 {
5076   // See if we already have a periodic boundary associated myboundary...
5077   PeriodicBoundaryBase * existing_boundary = _periodic_boundaries->boundary(periodic_boundary.myboundary);
5078 
5079   if (existing_boundary == nullptr)
5080     {
5081       // ...if not, clone the input (and its inverse) and add them to the PeriodicBoundaries object
5082       PeriodicBoundaryBase * boundary = periodic_boundary.clone().release();
5083       PeriodicBoundaryBase * inverse_boundary = periodic_boundary.clone(PeriodicBoundaryBase::INVERSE).release();
5084 
5085       // _periodic_boundaries takes ownership of the pointers
5086       _periodic_boundaries->emplace(boundary->myboundary, boundary);
5087       _periodic_boundaries->emplace(inverse_boundary->myboundary, inverse_boundary);
5088     }
5089   else
5090     {
5091       // ...otherwise, merge this object's variable IDs with the existing boundary object's.
5092       existing_boundary->merge(periodic_boundary);
5093 
5094       // Do the same merging process for the inverse boundary.  Note: the inverse better already exist!
5095       PeriodicBoundaryBase * inverse_boundary = _periodic_boundaries->boundary(periodic_boundary.pairedboundary);
5096       libmesh_assert(inverse_boundary);
5097       inverse_boundary->merge(periodic_boundary);
5098     }
5099 }
5100 
5101 
5102 
5103 
add_periodic_boundary(const PeriodicBoundaryBase & boundary,const PeriodicBoundaryBase & inverse_boundary)5104 void DofMap::add_periodic_boundary (const PeriodicBoundaryBase & boundary,
5105                                     const PeriodicBoundaryBase & inverse_boundary)
5106 {
5107   libmesh_assert_equal_to (boundary.myboundary, inverse_boundary.pairedboundary);
5108   libmesh_assert_equal_to (boundary.pairedboundary, inverse_boundary.myboundary);
5109 
5110   // Allocate copies on the heap.  The _periodic_boundaries object will manage this memory.
5111   // Note: this also means that the copy constructor for the PeriodicBoundary (or user class
5112   // derived therefrom) must be implemented!
5113   // PeriodicBoundary * p_boundary = new PeriodicBoundary(boundary);
5114   // PeriodicBoundary * p_inverse_boundary = new PeriodicBoundary(inverse_boundary);
5115 
5116   // We can't use normal copy construction since this leads to slicing with derived classes.
5117   // Use clone() "virtual constructor" instead.  But, this *requires* user to override the clone()
5118   // method.  Note also that clone() allocates memory.  In this case, the _periodic_boundaries object
5119   // takes responsibility for cleanup.
5120   PeriodicBoundaryBase * p_boundary = boundary.clone().release();
5121   PeriodicBoundaryBase * p_inverse_boundary = inverse_boundary.clone().release();
5122 
5123   // Add the periodic boundary and its inverse to the PeriodicBoundaries data structure.  The
5124   // PeriodicBoundaries data structure takes ownership of the pointers.
5125   _periodic_boundaries->emplace(p_boundary->myboundary, p_boundary);
5126   _periodic_boundaries->emplace(p_inverse_boundary->myboundary, p_inverse_boundary);
5127 }
5128 
5129 
5130 #endif
5131 
5132 
5133 } // namespace libMesh
5134