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