libMesh
system_projection.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2026 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License, or (at your option) any later version.
8 
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // Lesser General Public License for more details.
13 
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 
18 
19 
20 // C++ includes
21 #include <vector>
22 #include <numeric> // std::iota
23 
24 // Local includes
25 #include "libmesh/libmesh_config.h"
26 
27 #ifdef LIBMESH_HAVE_METAPHYSICL
28 
29 // With quad precision we need the shim function declarations to
30 // precede the MetaPhysicL use of them
31 #include "libmesh/libmesh_common.h"
32 #include "libmesh/compare_types.h"
33 
34 // Template specialization declarations in here need to *precede* code
35 // using them.
36 #include "metaphysicl/dynamicsparsenumberarray_decl.h"
37 
38 using MetaPhysicL::DynamicSparseNumberArray;
39 
40 namespace libMesh
41 {
42 // From the perspective of libMesh gradient vectors, a DSNA is a
43 // scalar component
44 template <typename T, typename IndexType>
45 struct ScalarTraits<MetaPhysicL::DynamicSparseNumberArray<T,IndexType> >
46 {
47  static const bool value = true;
48 };
49 
50 // And although MetaPhysicL knows how to combine DSNA with something
51 // else, we need to teach libMesh too.
52 template <typename T, typename IndexType, typename T2>
53 struct CompareTypes<MetaPhysicL::DynamicSparseNumberArray<T,IndexType>, T2>
54 {
55  typedef typename
56  MetaPhysicL::DynamicSparseNumberArray
58 };
59 
60 template <typename T> struct TypeToSend;
61 
62 template <typename T, typename IndexType>
63 struct TypeToSend<MetaPhysicL::DynamicSparseNumberArray<T,IndexType>> {
64  typedef std::vector<std::pair<IndexType,T>> type;
65 };
66 
67 template <typename T, typename IndexType>
68 const std::vector<std::pair<IndexType,T>>
69 convert_to_send(MetaPhysicL::DynamicSparseNumberArray<T,IndexType> & in)
70 {
71  const std::size_t in_size = in.size();
72  std::vector<std::pair<IndexType,T>> returnval(in_size);
73 
74  for (std::size_t i=0; i != in_size; ++i)
75  {
76  returnval[i].first = in.raw_index(i);
77  returnval[i].second = in.raw_at(i);
78  }
79  return returnval;
80 }
81 
82 template <typename SendT, typename T, typename IndexType>
83 void convert_from_receive (SendT & received,
84  MetaPhysicL::DynamicSparseNumberArray<T,IndexType> & converted)
85 {
86  const std::size_t received_size = received.size();
87  converted.resize(received_size);
88  for (std::size_t i=0; i != received_size; ++i)
89  {
90  converted.raw_index(i) = received[i].first;
91  converted.raw_at(i) = received[i].second;
92  }
93 }
94 
95 }
96 
97 
98 #endif
99 
100 #include "libmesh/boundary_info.h"
101 #include "libmesh/dense_matrix.h"
102 #include "libmesh/dense_vector.h"
103 #include "libmesh/dof_map.h"
104 #include "libmesh/elem.h"
105 #include "libmesh/fe_base.h"
106 #include "libmesh/fe_interface.h"
107 #include "libmesh/generic_projector.h"
108 #include "libmesh/int_range.h"
109 #include "libmesh/libmesh_logging.h"
110 #include "libmesh/linear_solver.h"
111 #include "libmesh/mesh_base.h"
112 #include "libmesh/numeric_vector.h"
113 #include "libmesh/quadrature.h"
114 #include "libmesh/sparse_matrix.h"
115 #include "libmesh/system.h"
116 #include "libmesh/threads.h"
117 #include "libmesh/wrapped_function.h"
118 #include "libmesh/wrapped_functor.h"
119 #include "libmesh/fe_interface.h"
120 
121 
122 
123 #ifdef LIBMESH_HAVE_METAPHYSICL
124 // Include MetaPhysicL definitions finally
125 #include "metaphysicl/dynamicsparsenumberarray.h"
126 
127 // And make sure we instantiate the methods we'll need to use on them.
128 #include "libmesh/dense_matrix_impl.h"
129 
130 namespace libMesh {
131 typedef DynamicSparseNumberArray<Real, dof_id_type> DSNAN;
132 
133 template LIBMESH_EXPORT void
136 template LIBMESH_EXPORT void
138  DenseVector<DSNAN> &) const;
139 }
140 #endif
141 
142 
143 
144 namespace libMesh
145 {
146 
147 // ------------------------------------------------------------
148 // Helper class definitions
149 
150 #ifdef LIBMESH_ENABLE_AMR
151 
163 {
164 private:
165  const System & system;
166 
167 public:
168  BuildProjectionList (const System & system_in) :
169  system(system_in),
170  send_list()
171  {}
172 
174  system(other.system),
175  send_list()
176  {}
177 
178  void unique();
179  void operator()(const ConstElemRange & range);
180  void join (const BuildProjectionList & other);
181  std::vector<dof_id_type> send_list;
182 };
183 
184 #endif // LIBMESH_ENABLE_AMR
185 
186 
193 {
194 private:
195  const std::set<boundary_id_type> & b;
196  const std::vector<unsigned int> & variables;
197  const System & system;
198  std::unique_ptr<FunctionBase<Number>> f;
199  std::unique_ptr<FunctionBase<Gradient>> g;
202 
203 public:
204  BoundaryProjectSolution (const std::set<boundary_id_type> & b_in,
205  const std::vector<unsigned int> & variables_in,
206  const System & system_in,
207  FunctionBase<Number> * f_in,
208  FunctionBase<Gradient> * g_in,
209  const Parameters & parameters_in,
210  NumericVector<Number> & new_v_in) :
211  b(b_in),
212  variables(variables_in),
213  system(system_in),
214  f(f_in ? f_in->clone() : std::unique_ptr<FunctionBase<Number>>()),
215  g(g_in ? g_in->clone() : std::unique_ptr<FunctionBase<Gradient>>()),
216  parameters(parameters_in),
217  new_vector(new_v_in)
218  {
219  libmesh_assert(f.get());
220  f->init();
221  if (g.get())
222  g->init();
223  }
224 
226  b(in.b),
227  variables(in.variables),
228  system(in.system),
229  f(in.f.get() ? in.f->clone() : std::unique_ptr<FunctionBase<Number>>()),
230  g(in.g.get() ? in.g->clone() : std::unique_ptr<FunctionBase<Gradient>>()),
231  parameters(in.parameters),
232  new_vector(in.new_vector)
233  {
234  libmesh_assert(f.get());
235  f->init();
236  if (g.get())
237  g->init();
238  }
239 
240  void operator()(const ConstElemRange & range) const;
241 };
242 
243 
244 
245 // ------------------------------------------------------------
246 // System implementation
247 void System::project_vector (NumericVector<Number> & vector,
248  int is_adjoint,
249  std::optional<ConstElemRange> active_local_range,
250  std::optional<std::vector<unsigned int>> variable_numbers) const
251 {
252  // Create a copy of the vector, which currently
253  // contains the old data.
254  std::unique_ptr<NumericVector<Number>>
255  old_vector (vector.clone());
256 
257  // Project the old vector to the new vector
258  this->project_vector (*old_vector, vector, is_adjoint, active_local_range, variable_numbers);
259 }
260 
261 
267 void System::project_vector (const NumericVector<Number> & old_v,
268  NumericVector<Number> & new_v,
269  int is_adjoint,
270  std::optional<ConstElemRange> active_local_range,
271  std::optional<std::vector<unsigned int>> variable_numbers) const
272 {
273  LOG_SCOPE ("project_vector(old,new)", "System");
274 
281  new_v.clear();
282 
283 #ifdef LIBMESH_ENABLE_AMR
284 
285  // Resize the new vector and get a serial version.
286  NumericVector<Number> * new_vector_ptr = nullptr;
287  std::unique_ptr<NumericVector<Number>> new_vector_built;
288  NumericVector<Number> * local_old_vector;
289  std::unique_ptr<NumericVector<Number>> local_old_vector_built;
290  const NumericVector<Number> * old_vector_ptr = nullptr;
291 
292  if (!active_local_range)
293  {
294  active_local_range.emplace
295  (this->get_mesh().active_local_elements_begin(),
296  this->get_mesh().active_local_elements_end());
297  }
298 
299  // If the old vector was uniprocessor, make the new
300  // vector uniprocessor
301  if (old_v.type() == SERIAL)
302  {
303  new_v.init (this->n_dofs(), false, SERIAL);
304  new_vector_ptr = &new_v;
305  old_vector_ptr = &old_v;
306  }
307 
308  // Otherwise it is a parallel, distributed vector, which
309  // we need to localize.
310  else if (old_v.type() == PARALLEL)
311  {
312  // Build a send list for efficient localization
313  BuildProjectionList projection_list(*this);
314  Threads::parallel_reduce (active_local_range.value(),
315  projection_list);
316 
317  // Create a sorted, unique send_list
318  projection_list.unique();
319 
320  new_v.init (this->n_dofs(), this->n_local_dofs(), false, PARALLEL);
321  new_vector_built = NumericVector<Number>::build(this->comm());
322  local_old_vector_built = NumericVector<Number>::build(this->comm());
323  new_vector_ptr = new_vector_built.get();
324  local_old_vector = local_old_vector_built.get();
325  new_vector_ptr->init(this->n_dofs(), this->n_local_dofs(),
326  this->get_dof_map().get_send_list(), false,
327  GHOSTED);
328  local_old_vector->init(old_v.size(), old_v.local_size(),
329  projection_list.send_list, false, GHOSTED);
330  old_v.localize(*local_old_vector, projection_list.send_list);
331  local_old_vector->close();
332  old_vector_ptr = local_old_vector;
333  }
334  else if (old_v.type() == GHOSTED)
335  {
336  // Build a send list for efficient localization
337  BuildProjectionList projection_list(*this);
338  Threads::parallel_reduce (active_local_range.value(),
339  projection_list);
340 
341  // Create a sorted, unique send_list
342  projection_list.unique();
343 
344  new_v.init (this->n_dofs(), this->n_local_dofs(),
345  this->get_dof_map().get_send_list(), false, GHOSTED);
346 
347  local_old_vector_built = NumericVector<Number>::build(this->comm());
348  new_vector_ptr = &new_v;
349  local_old_vector = local_old_vector_built.get();
350  local_old_vector->init(old_v.size(), old_v.local_size(),
351  projection_list.send_list, false, GHOSTED);
352  old_v.localize(*local_old_vector, projection_list.send_list);
353  local_old_vector->close();
354  old_vector_ptr = local_old_vector;
355  }
356  else // unknown old_v.type()
357  libmesh_error_msg("ERROR: Unknown old_v.type() == " << old_v.type());
358 
359  // Note that the above will have zeroed the new_vector.
360  // Just to be sure, assert that new_vector_ptr and old_vector_ptr
361  // were successfully set before trying to deref them.
362  libmesh_assert(new_vector_ptr);
363  libmesh_assert(old_vector_ptr);
364 
365  NumericVector<Number> & new_vector = *new_vector_ptr;
366  const NumericVector<Number> & old_vector = *old_vector_ptr;
367 
368  const unsigned int n_variables = this->n_vars();
369 
370  if (n_variables)
371  {
372  std::vector<unsigned int> vars;
373  if (variable_numbers)
374  {
375  vars = *variable_numbers;
376  for (auto v : vars)
377  if (v >= n_variables)
378  libmesh_error_msg("ERROR: variable number " << v <<
379  " out of range for system with " <<
380  n_variables << " variables.");
381  }
382  else
383  {
384  vars.resize(n_variables);
385  std::iota(vars.begin(), vars.end(), 0);
386  }
387 
388  std::vector<unsigned int> regular_vars, vector_vars;
389  for (auto var : vars)
390  {
391  if (FEInterface::field_type(this->variable_type(var)) == TYPE_SCALAR)
392  regular_vars.push_back(var);
393  else
394  vector_vars.push_back(var);
395  }
396 
397  VectorSetAction<Number> setter(new_vector);
398 
399  if (!regular_vars.empty())
400  {
401  // Use a typedef to make the calling sequence for parallel_for() a bit more readable
402  typedef
405  Number, VectorSetAction<Number>> FEMProjector;
406 
408  f(*this, old_vector, &regular_vars);
410  g(*this, old_vector, &regular_vars);
411 
412  FEMProjector projector(*this, f, &g, setter, regular_vars);
413  projector.project(active_local_range.value());
414  }
415 
416  if (!vector_vars.empty())
417  {
418  typedef
421  Gradient, VectorSetAction<Number>> FEMVectorProjector;
422 
423  OldSolutionValue<Gradient, &FEMContext::point_value> f_vector(*this, old_vector, &vector_vars);
424  OldSolutionValue<Tensor, &FEMContext::point_gradient> g_vector(*this, old_vector, &vector_vars);
425 
426  FEMVectorProjector vector_projector(*this, f_vector, &g_vector, setter, vector_vars);
427  vector_projector.project(active_local_range.value());
428  }
429 
430  // Copy the SCALAR dofs from old_vector to new_vector
431  // Note: We assume that all SCALAR dofs are on the
432  // processor with highest ID
433  if (this->processor_id() == (this->n_processors()-1))
434  {
435  const DofMap & dof_map = this->get_dof_map();
436  for (auto var : vars)
437  if (this->variable(var).type().family == SCALAR)
438  {
439  // We can just map SCALAR dofs directly across
440  std::vector<dof_id_type> new_SCALAR_indices, old_SCALAR_indices;
441  dof_map.SCALAR_dof_indices (new_SCALAR_indices, var, false);
442  dof_map.SCALAR_dof_indices (old_SCALAR_indices, var, true);
443  for (auto i : index_range(new_SCALAR_indices))
444  new_vector.set(new_SCALAR_indices[i], old_vector(old_SCALAR_indices[i]));
445  }
446  }
447  }
448 
449  new_vector.close();
450 
451  // If the old vector was serial, we probably need to send our values
452  // to other processors
453  //
454  // FIXME: I'm not sure how to make a NumericVector do that without
455  // creating a temporary parallel vector to use localize! - RHS
456  if (old_v.type() == SERIAL)
457  {
458  std::unique_ptr<NumericVector<Number>> dist_v = NumericVector<Number>::build(this->comm());
459  dist_v->init(this->n_dofs(), this->n_local_dofs(), false, PARALLEL);
460  dist_v->close();
461 
462  for (auto i : make_range(dist_v->size()))
463  if (new_vector(i) != 0.0)
464  dist_v->set(i, new_vector(i));
465 
466  dist_v->close();
467 
468  dist_v->localize (new_v, this->get_dof_map().get_send_list());
469  new_v.close();
470  }
471  // If the old vector was parallel, we need to update it
472  // and free the localized copies
473  else if (old_v.type() == PARALLEL)
474  {
475  // We may have to set dof values that this processor doesn't
476  // own in certain special cases, like LAGRANGE FIRST or
477  // HERMITE THIRD elements on second-order meshes?
478  new_v = new_vector;
479  new_v.close();
480  }
481 
482 
483  // Apply constraints only if we we are asked to
484  if(this->project_with_constraints)
485  {
486  if (is_adjoint == -1)
487  {
488  this->get_dof_map().enforce_constraints_exactly(*this, &new_v);
489  }
490  else if (is_adjoint >= 0)
491  {
492  this->get_dof_map().enforce_adjoint_constraints_exactly(new_v,
493  is_adjoint);
494  }
495  }
496 #else
497 
498  // AMR is disabled: simply copy the vector
499  new_v = old_v;
500 
501  libmesh_ignore(is_adjoint, active_local_range, variable_numbers);
502 
503 #endif // #ifdef LIBMESH_ENABLE_AMR
504 }
505 
506 
507 #ifdef LIBMESH_ENABLE_AMR
508 #ifdef LIBMESH_HAVE_METAPHYSICL
509 
510 template <typename Output>
512 {
513 public:
514  typedef DynamicSparseNumberArray<Output, dof_id_type> type;
515 };
516 
517 template <typename InnerOutput>
518 class DSNAOutput<VectorValue<InnerOutput> >
519 {
520 public:
522 };
523 
533 template <typename Output,
534  void (FEMContext::*point_output) (unsigned int,
535  const Point &,
536  Output &,
537  const Real) const>
539 {
540 public:
541  typedef typename DSNAOutput<Output>::type DSNA;
544 
546  const std::vector<unsigned int> * vars) :
547  OldSolutionBase<Output, point_output>(sys_in, vars)
548  {
549  this->old_context.set_algebraic_type(FEMContext::OLD_DOFS_ONLY);
550  }
551 
553  OldSolutionBase<Output, point_output>(in.sys, in.old_context.active_vars())
554  {
555  this->old_context.set_algebraic_type(FEMContext::OLD_DOFS_ONLY);
556  }
557 
558  DSNA eval_at_node (const FEMContext & c,
559  unsigned int i,
560  unsigned int elem_dim,
561  const Node & n,
562  bool extra_hanging_dofs,
563  Real /* time */ = 0.);
564 
565  DSNA eval_at_point(const FEMContext & c,
566  unsigned int i,
567  const Point & p,
568  Real time,
569  bool skip_context_check);
570 
571  void eval_mixed_derivatives (const FEMContext & libmesh_dbg_var(c),
572  unsigned int i,
573  unsigned int dim,
574  const Node & n,
575  std::vector<DSNA> & derivs)
576  {
577  LOG_SCOPE ("eval_mixed_derivatives", "OldSolutionCoefs");
578 
579  // This should only be called on vertices
580  libmesh_assert_less(c.get_elem().get_node_index(&n),
581  c.get_elem().n_vertices());
582 
583  // Handle offset from non-scalar components in previous variables
584  libmesh_assert_less(i, this->component_to_var.size());
585  unsigned int var = this->component_to_var[i];
586 
587  // We have 1 mixed derivative in 2D, 4 in 3D
588  const unsigned int n_mixed = (dim-1) * (dim-1);
589  derivs.resize(n_mixed);
590 
591  // Be sure to handle cases where the variable wasn't defined on
592  // this node (e.g. due to changing subdomain support)
593  const DofObject * old_dof_object = n.get_old_dof_object();
594  if (old_dof_object &&
595  old_dof_object->n_vars(this->sys.number()) &&
596  old_dof_object->n_comp(this->sys.number(), var))
597  {
598  const dof_id_type first_old_id =
599  old_dof_object->dof_number(this->sys.number(), var, dim);
600  std::vector<dof_id_type> old_ids(n_mixed);
601  std::iota(old_ids.begin(), old_ids.end(), first_old_id);
602 
603  for (auto d_i : index_range(derivs))
604  {
605  derivs[d_i].resize(1);
606  derivs[d_i].raw_at(0) = 1;
607  derivs[d_i].raw_index(0) = old_ids[d_i];
608  }
609  }
610  else
611  {
612  std::fill(derivs.begin(), derivs.end(), 0);
613  }
614  }
615 
616 
617  void eval_old_dofs (const Elem & elem,
618  unsigned int node_num,
619  unsigned int var_num,
620  std::vector<dof_id_type> & indices,
621  std::vector<DSNA> & values)
622  {
623  LOG_SCOPE ("eval_old_dofs(node)", "OldSolutionCoefs");
624 
625  // We may be reusing a std::vector here, but the following
626  // dof_indices call appends without first clearing.
627  indices.clear();
628 
629  this->sys.get_dof_map().dof_indices(elem, node_num, indices, var_num);
630 
631  std::vector<dof_id_type> old_indices;
632 
633  this->sys.get_dof_map().old_dof_indices(elem, node_num, old_indices, var_num);
634 
635  libmesh_assert_equal_to (old_indices.size(), indices.size());
636 
637  values.resize(old_indices.size());
638 
639  for (auto i : index_range(values))
640  {
641  values[i].resize(1);
642  values[i].raw_at(0) = 1;
643  values[i].raw_index(0) = old_indices[i];
644  }
645  }
646 
647 
648  void eval_old_dofs (const Elem & elem,
649  const FEType & fe_type,
650  unsigned int sys_num,
651  unsigned int var_num,
652  std::vector<dof_id_type> & indices,
653  std::vector<DSNA> & values)
654  {
655  LOG_SCOPE ("eval_old_dofs(elem)", "OldSolutionCoefs");
656 
657  // We're only to be asked for old dofs on elements that can copy
658  // them through DO_NOTHING or through refinement.
659  const Elem & old_elem =
660  (elem.refinement_flag() == Elem::JUST_REFINED) ?
661  *elem.parent() : elem;
662 
663  // If there are any element-based DOF numbers, get them
664  const unsigned int nc =
665  FEInterface::n_dofs_per_elem(fe_type, &elem);
666 
667  std::vector<dof_id_type> old_dof_indices(nc);
668  indices.resize(nc);
669 
670  // We should never have fewer dofs than necessary on an
671  // element unless we're getting indices on a parent element,
672  // and we should never need those indices
673  if (nc != 0)
674  {
675  const DofObject & old_dof_object = old_elem.get_old_dof_object_ref();
676 
677  const auto [vg, vig] =
678  elem.var_to_vg_and_offset(sys_num,var_num);
679 
680  const unsigned int n_comp = elem.n_comp_group(sys_num,vg);
681  libmesh_assert_greater(elem.n_systems(), sys_num);
682  libmesh_assert_greater_equal(n_comp, nc);
683 
684  for (unsigned int i=0; i<nc; i++)
685  {
686  const dof_id_type d_old =
687  old_dof_object.dof_number(sys_num, vg, vig, i, n_comp);
688  const dof_id_type d_new =
689  elem.dof_number(sys_num, vg, vig, i, n_comp);
690  libmesh_assert_not_equal_to (d_old, DofObject::invalid_id);
691  libmesh_assert_not_equal_to (d_new, DofObject::invalid_id);
692 
693  old_dof_indices[i] = d_old;
694  indices[i] = d_new;
695  }
696  }
697 
698  values.resize(old_dof_indices.size());
699 
700  for (auto i : index_range(values))
701  {
702  values[i].resize(1);
703  values[i].raw_at(0) = 1;
704  values[i].raw_index(0) = old_dof_indices[i];
705  }
706  }
707 };
708 
709 
710 
711 template<>
712 inline
713 DynamicSparseNumberArray<Real, dof_id_type>
714 OldSolutionCoefs<Real, &FEMContext::point_value>::
715 eval_at_point(const FEMContext & c,
716  unsigned int i,
717  const Point & p,
718  Real /* time */,
719  bool skip_context_check)
720 {
721  LOG_SCOPE ("eval_at_point()", "OldSolutionCoefs");
722 
723  if (!skip_context_check)
724  if (!this->check_old_context(c, p))
725  return 0;
726 
727  // Get finite element object
728  FEGenericBase<Real> * fe = nullptr;
729  this->old_context.get_element_fe<Real>
730  (i, fe, this->old_context.get_elem_dim());
731 
732  // Build a FE for calculating phi(p)
733  FEGenericBase<Real> * fe_new =
734  this->old_context.build_new_fe(fe, p);
735 
736  // Get the values and global indices of the shape functions
737  const std::vector<std::vector<Real> > & phi = fe_new->get_phi();
738  const std::vector<dof_id_type> & dof_indices =
739  this->old_context.get_dof_indices(i);
740 
741  const std::size_t n_dofs = phi.size();
742  libmesh_assert_equal_to(n_dofs, dof_indices.size());
743 
744  DynamicSparseNumberArray<Real, dof_id_type> returnval;
745  returnval.resize(n_dofs);
746 
747  for (auto j : index_range(phi))
748  {
749  returnval.raw_at(j) = phi[j][0];
750  returnval.raw_index(j) = dof_indices[j];
751  }
752 
753  return returnval;
754 }
755 
756 
757 
758 template<>
759 inline
763  unsigned int i,
764  const Point & p,
765  Real /* time */,
766  bool skip_context_check)
767 {
768  LOG_SCOPE ("eval_at_point()", "OldSolutionCoefs");
769 
770  if (!skip_context_check)
771  if (!this->check_old_context(c, p))
772  return 0;
773 
774  // Get finite element object
775  FEGenericBase<Real> * fe = nullptr;
776  this->old_context.get_element_fe<Real>
777  (i, fe, this->old_context.get_elem_dim());
778 
779  // Build a FE for calculating phi(p)
780  FEGenericBase<Real> * fe_new =
781  this->old_context.build_new_fe(fe, p);
782 
783  // Get the values and global indices of the shape functions
784  const std::vector<std::vector<RealGradient> > & dphi = fe_new->get_dphi();
785  const std::vector<dof_id_type> & dof_indices =
786  this->old_context.get_dof_indices(i);
787 
788  const std::size_t n_dofs = dphi.size();
789  libmesh_assert_equal_to(n_dofs, dof_indices.size());
790 
792 
793  for (unsigned int d = 0; d != LIBMESH_DIM; ++d)
794  returnval(d).resize(n_dofs);
795 
796  for (auto j : index_range(dphi))
797  for (int d = 0; d != LIBMESH_DIM; ++d)
798  {
799  returnval(d).raw_at(j) = dphi[j][0](d);
800  returnval(d).raw_index(j) = dof_indices[j];
801  }
802 
803  return returnval;
804 }
805 
806 
807 template<>
808 inline
809 DynamicSparseNumberArray<Real, dof_id_type>
812  unsigned int i,
813  unsigned int /* elem_dim */,
814  const Node & n,
815  bool extra_hanging_dofs,
816  Real /* time */)
817 {
818  LOG_SCOPE ("Real eval_at_node()", "OldSolutionCoefs");
819 
820  // Optimize for the common case, where this node was part of the
821  // old solution.
822  //
823  // Be sure to handle cases where the variable wasn't defined on
824  // this node (due to changing subdomain support) or where the
825  // variable has no components on this node (due to Elem order
826  // exceeding FE order) or where the old_dof_object dofs might
827  // correspond to non-vertex dofs (due to extra_hanging_dofs and
828  // refinement)
829 
831 
832  const DofObject * old_dof_object = n.get_old_dof_object();
833  if (old_dof_object &&
834  (!extra_hanging_dofs ||
835  flag == Elem::JUST_COARSENED ||
836  flag == Elem::DO_NOTHING) &&
837  old_dof_object->n_vars(sys.number()) &&
838  old_dof_object->n_comp(sys.number(), i))
839  {
840  DynamicSparseNumberArray<Real, dof_id_type> returnval;
841  const dof_id_type old_id =
842  old_dof_object->dof_number(sys.number(), i, 0);
843  returnval.resize(1);
844  returnval.raw_at(0) = 1;
845  returnval.raw_index(0) = old_id;
846  return returnval;
847  }
848 
849  return this->eval_at_point(c, i, n, 0, false);
850 }
851 
852 
853 
854 template<>
855 inline
859  unsigned int i,
860  unsigned int elem_dim,
861  const Node & n,
862  bool extra_hanging_dofs,
863  Real /* time */)
864 {
865  LOG_SCOPE ("RealGradient eval_at_node()", "OldSolutionCoefs");
866 
867  // Optimize for the common case, where this node was part of the
868  // old solution.
869  //
870  // Be sure to handle cases where the variable wasn't defined on
871  // this node (due to changing subdomain support) or where the
872  // variable has no components on this node (due to Elem order
873  // exceeding FE order) or where the old_dof_object dofs might
874  // correspond to non-vertex dofs (due to extra_hanging_dofs and
875  // refinement)
876 
878 
879  const DofObject * old_dof_object = n.get_old_dof_object();
880  if (old_dof_object &&
881  (!extra_hanging_dofs ||
882  flag == Elem::JUST_COARSENED ||
883  flag == Elem::DO_NOTHING) &&
884  old_dof_object->n_vars(sys.number()) &&
885  old_dof_object->n_comp(sys.number(), i))
886  {
888  for (unsigned int d = 0; d != elem_dim; ++d)
889  {
890  const dof_id_type old_id =
891  old_dof_object->dof_number(sys.number(), i, d+1);
892  g(d).resize(1);
893  g(d).raw_at(0) = 1;
894  g(d).raw_index(0) = old_id;
895  }
896  return g;
897  }
898 
899  return this->eval_at_point(c, i, n, 0, false);
900 }
901 
902 
903 
912 template <typename ValIn, typename ValOut>
914 {
915 public:
916  typedef DynamicSparseNumberArray<ValIn, dof_id_type> InsertInput;
917 private:
919 
920 public:
922  target_matrix(target_mat) {}
923 
925  const DynamicSparseNumberArray<ValIn, dof_id_type> & val)
926  {
927  // Lock the target matrix since it is shared among threads.
928  {
929  Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
930 
931  const std::size_t dnsa_size = val.size();
932  for (unsigned int j = 0; j != dnsa_size; ++j)
933  {
934  const dof_id_type dof_j = val.raw_index(j);
935  const ValIn dof_val = val.raw_at(j);
936  target_matrix.set(id, dof_j, dof_val);
937  }
938  }
939  }
940 
941 
942  void insert(const std::vector<dof_id_type> & dof_indices,
943  const std::vector<DynamicSparseNumberArray<ValIn, dof_id_type> > & Ue)
944  {
945  const numeric_index_type
946  begin_dof = target_matrix.row_start(),
947  end_dof = target_matrix.row_stop();
948 
949  unsigned int size = Ue.size();
950 
951  libmesh_assert_equal_to(size, dof_indices.size());
952 
953  // Lock the target matrix since it is shared among threads.
954  {
955  Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
956 
957  for (unsigned int i = 0; i != size; ++i)
958  {
959  const dof_id_type dof_i = dof_indices[i];
960  if ((dof_i >= begin_dof) && (dof_i < end_dof))
961  {
962  const DynamicSparseNumberArray<ValIn,dof_id_type> & dnsa = Ue[i];
963  const std::size_t dnsa_size = dnsa.size();
964  for (unsigned int j = 0; j != dnsa_size; ++j)
965  {
966  const dof_id_type dof_j = dnsa.raw_index(j);
967  const ValIn dof_val = dnsa.raw_at(j);
968  target_matrix.set(dof_i, dof_j, dof_val);
969  }
970  }
971  }
972  }
973  }
974 };
975 
976 
977 
982 void System::projection_matrix (SparseMatrix<Number> & proj_mat) const
983 {
984  LOG_SCOPE ("projection_matrix()", "System");
985 
986  const unsigned int n_variables = this->n_vars();
987 
988  if (n_variables)
989  {
990  ConstElemRange active_local_elem_range
991  (this->get_mesh().active_local_elements_begin(),
992  this->get_mesh().active_local_elements_end());
993 
994  std::vector<unsigned int> vars(n_variables);
995  std::iota(vars.begin(), vars.end(), 0);
996 
997  // Use a typedef to make the calling sequence for parallel_for() a bit more readable
998  typedef OldSolutionCoefs<Real, &FEMContext::point_value> OldSolutionValueCoefs;
999  typedef OldSolutionCoefs<RealGradient, &FEMContext::point_gradient> OldSolutionGradientCoefs;
1000 
1001  typedef
1002  GenericProjector<OldSolutionValueCoefs,
1003  OldSolutionGradientCoefs,
1004  DynamicSparseNumberArray<Real,dof_id_type>,
1005  MatrixFillAction<Real, Number> > ProjMatFiller;
1006 
1007  OldSolutionValueCoefs f(*this, &vars);
1008  OldSolutionGradientCoefs g(*this, &vars);
1009  MatrixFillAction<Real, Number> setter(proj_mat);
1010 
1011  ProjMatFiller mat_filler(*this, f, &g, setter, vars);
1012  mat_filler.project(active_local_elem_range);
1013 
1014  // Set the SCALAR dof transfer entries too.
1015  // Note: We assume that all SCALAR dofs are on the
1016  // processor with highest ID
1017  if (this->processor_id() == (this->n_processors()-1))
1018  {
1019  const DofMap & dof_map = this->get_dof_map();
1020  for (auto var : make_range(this->n_vars()))
1021  if (this->variable(var).type().family == SCALAR)
1022  {
1023  // We can just map SCALAR dofs directly across
1024  std::vector<dof_id_type> new_SCALAR_indices, old_SCALAR_indices;
1025  dof_map.SCALAR_dof_indices (new_SCALAR_indices, var, false);
1026  dof_map.SCALAR_dof_indices (old_SCALAR_indices, var, true);
1027  const unsigned int new_n_dofs =
1028  cast_int<unsigned int>(new_SCALAR_indices.size());
1029 
1030  for (unsigned int i=0; i<new_n_dofs; i++)
1031  {
1032  proj_mat.set( new_SCALAR_indices[i],
1033  old_SCALAR_indices[i], 1);
1034  }
1035  }
1036  }
1037  }
1038 }
1039 #endif // LIBMESH_HAVE_METAPHYSICL
1040 #endif // LIBMESH_ENABLE_AMR
1041 
1042 
1043 
1048 void System::project_solution (ValueFunctionPointer fptr,
1049  GradientFunctionPointer gptr,
1050  const Parameters & function_parameters,
1051  std::optional<ConstElemRange> active_local_range,
1052  std::optional<std::vector<unsigned int>> variable_numbers) const
1053 {
1054  WrappedFunction<Number> f(*this, fptr, &function_parameters);
1055  WrappedFunction<Gradient> g(*this, gptr, &function_parameters);
1056  this->project_solution(&f, &g, active_local_range, variable_numbers);
1057 }
1058 
1059 
1064 void System::project_solution (FunctionBase<Number> * f,
1066  std::optional<ConstElemRange> active_local_range,
1067  std::optional<std::vector<unsigned int>> variable_numbers) const
1068 {
1069  this->project_vector(*solution, f, g, /*is_adjoint=*/-1, active_local_range, variable_numbers);
1070 
1071  solution->localize(*current_local_solution, _dof_map->get_send_list());
1072 }
1073 
1074 
1079 void System::project_solution (FEMFunctionBase<Number> * f,
1081  std::optional<ConstElemRange> active_local_range,
1082  std::optional<std::vector<unsigned int>> variable_numbers) const
1083 {
1084  this->project_vector(*solution, f, g, /*is_adjoint=*/-1, active_local_range, variable_numbers);
1085 
1086  solution->localize(*current_local_solution, _dof_map->get_send_list());
1087 }
1088 
1089 
1094 void System::project_vector (ValueFunctionPointer fptr,
1095  GradientFunctionPointer gptr,
1096  const Parameters & function_parameters,
1097  NumericVector<Number> & new_vector,
1098  int is_adjoint,
1099  std::optional<ConstElemRange> active_local_range,
1100  std::optional<std::vector<unsigned int>> variable_numbers) const
1101 {
1102  WrappedFunction<Number> f(*this, fptr, &function_parameters);
1103  WrappedFunction<Gradient> g(*this, gptr, &function_parameters);
1104  this->project_vector(new_vector, &f, &g, is_adjoint, active_local_range, variable_numbers);
1105 }
1106 
1111 void System::project_vector (NumericVector<Number> & new_vector,
1114  int is_adjoint,
1115  std::optional<ConstElemRange> active_local_range,
1116  std::optional<std::vector<unsigned int>> variable_numbers) const
1117 {
1118  LOG_SCOPE ("project_vector(FunctionBase)", "System");
1119 
1120  libmesh_assert(f);
1121 
1122  WrappedFunctor<Number> f_fem(*f);
1123 
1124  if (g)
1125  {
1126  WrappedFunctor<Gradient> g_fem(*g);
1127 
1128  this->project_vector(new_vector, &f_fem, &g_fem, is_adjoint, active_local_range, variable_numbers);
1129  }
1130  else
1131  this->project_vector(new_vector, &f_fem, nullptr, is_adjoint, active_local_range, variable_numbers);
1132 }
1133 
1134 
1139 void System::project_vector (NumericVector<Number> & new_vector,
1142  int is_adjoint,
1143  std::optional<ConstElemRange> active_local_range,
1144  std::optional<std::vector<unsigned int>> variable_numbers) const
1145 {
1146  LOG_SCOPE ("project_fem_vector()", "System");
1147 
1148  libmesh_assert (f);
1149 
1150  if (!active_local_range)
1151  {
1152  active_local_range.emplace
1153  (this->get_mesh().active_local_elements_begin(),
1154  this->get_mesh().active_local_elements_end());
1155  }
1156 
1157  VectorSetAction<Number> setter(new_vector);
1158 
1159  const unsigned int n_variables = this->n_vars();
1160 
1161  std::vector<unsigned int> vars;
1162  if (variable_numbers)
1163  {
1164  vars = *variable_numbers;
1165  for (auto v : vars)
1166  if (v >= n_variables)
1167  libmesh_error_msg("ERROR: variable number " << v <<
1168  " out of range for system with " <<
1169  n_variables << " variables.");
1170  }
1171  else
1172  {
1173  vars.resize(n_variables);
1174  std::iota(vars.begin(), vars.end(), 0);
1175  }
1176 
1177 
1178  // Use a typedef to make the calling sequence for parallel_for() a bit more readable
1179  typedef
1181  Number, VectorSetAction<Number>> FEMProjector;
1182 
1184 
1185  if (g)
1186  {
1188 
1189  FEMProjector projector(*this, fw, &gw, setter, vars);
1190  projector.project(active_local_range.value());
1191  }
1192  else
1193  {
1194  FEMProjector projector(*this, fw, nullptr, setter, vars);
1195  projector.project(active_local_range.value());
1196  }
1197 
1198  // Also, load values into the SCALAR dofs
1199  // Note: We assume that all SCALAR dofs are on the
1200  // processor with highest ID
1201  if (this->processor_id() == (this->n_processors()-1))
1202  {
1203  // FIXME: Do we want to first check for SCALAR vars before building this? [PB]
1204  FEMContext context( *this );
1205 
1206  const DofMap & dof_map = this->get_dof_map();
1207  for (auto var : vars)
1208  if (this->variable(var).type().family == SCALAR)
1209  {
1210  // FIXME: We reinit with an arbitrary element in case the user
1211  // doesn't override FEMFunctionBase::component. Is there
1212  // any use case we're missing? [PB]
1213  context.pre_fe_reinit(*this, *(this->get_mesh().active_local_elements_begin()));
1214 
1215  std::vector<dof_id_type> SCALAR_indices;
1216  dof_map.SCALAR_dof_indices (SCALAR_indices, var);
1217  const unsigned int n_SCALAR_dofs =
1218  cast_int<unsigned int>(SCALAR_indices.size());
1219 
1220  for (unsigned int i=0; i<n_SCALAR_dofs; i++)
1221  {
1222  const dof_id_type global_index = SCALAR_indices[i];
1223  const unsigned int component_index =
1224  this->variable_scalar_number(var,i);
1225 
1226  new_vector.set(global_index, f->component(context, component_index, Point(), this->time));
1227  }
1228  }
1229  }
1230 
1231  new_vector.close();
1232 
1233  // Look for spline bases, in which case we need to backtrack
1234  // to calculate the spline DoF values.
1235  std::vector<const Variable *> rational_vars;
1236  for (auto varnum : vars)
1237  {
1238  const Variable & var = this->get_dof_map().variable(varnum);
1239  if (var.type().family == RATIONAL_BERNSTEIN)
1240  rational_vars.push_back(&var);
1241  }
1242 
1243  // Okay, but are we really using any *spline* bases, or just
1244  // unconstrained rational bases?
1245  bool using_spline_bases = false;
1246  if (!rational_vars.empty())
1247  {
1248  // Look for a spline node: a NodeElem with a rational variable
1249  // on it.
1250  for (auto & elem : active_local_range.value())
1251  if (elem->type() == NODEELEM)
1252  for (auto rational_var : rational_vars)
1253  if (rational_var->active_on_subdomain(elem->subdomain_id()))
1254  {
1255  using_spline_bases = true;
1256  goto checked_on_splines;
1257  }
1258  }
1259 
1260 checked_on_splines:
1261 
1262  // Not every processor may have a NodeElem, especially while
1263  // we're not partitioning them efficiently yet.
1264  this->comm().max(using_spline_bases);
1265 
1266  if (using_spline_bases)
1267  this->solve_for_unconstrained_dofs(new_vector, is_adjoint);
1268 
1269 #ifdef LIBMESH_ENABLE_CONSTRAINTS
1270  if (is_adjoint == -1)
1271  this->get_dof_map().enforce_constraints_exactly(*this, &new_vector);
1272  else if (is_adjoint >= 0)
1273  this->get_dof_map().enforce_adjoint_constraints_exactly(new_vector,
1274  is_adjoint);
1275 #else
1276  libmesh_ignore(is_adjoint);
1277 #endif
1278 }
1279 
1280 
1286 void System::boundary_project_solution (const std::set<boundary_id_type> & b,
1287  const std::vector<unsigned int> & variables,
1288  ValueFunctionPointer fptr,
1289  GradientFunctionPointer gptr,
1290  const Parameters & function_parameters,
1291  std::optional<ConstElemRange> active_local_range)
1292 
1293 {
1294  WrappedFunction<Number> f(*this, fptr, &function_parameters);
1295  WrappedFunction<Gradient> g(*this, gptr, &function_parameters);
1296  this->boundary_project_solution(b, variables, &f, &g, active_local_range);
1297 }
1298 
1299 
1305 void System::boundary_project_solution (const std::set<boundary_id_type> & b,
1306  const std::vector<unsigned int> & variables,
1309  std::optional<ConstElemRange> active_local_range)
1310 {
1311  this->boundary_project_vector(b, variables, *solution, f, g, -1 /*is_adjoint*/, active_local_range);
1312 
1313  solution->localize(*current_local_solution);
1314 }
1315 
1316 
1317 
1318 
1319 
1324 void System::boundary_project_vector (const std::set<boundary_id_type> & b,
1325  const std::vector<unsigned int> & variables,
1326  ValueFunctionPointer fptr,
1327  GradientFunctionPointer gptr,
1328  const Parameters & function_parameters,
1329  NumericVector<Number> & new_vector,
1330  int is_adjoint,
1331  std::optional<ConstElemRange> active_local_range) const
1332 {
1333  WrappedFunction<Number> f(*this, fptr, &function_parameters);
1334  WrappedFunction<Gradient> g(*this, gptr, &function_parameters);
1335  this->boundary_project_vector(b, variables, new_vector, &f, &g,
1336  is_adjoint, active_local_range);
1337 }
1338 
1343 void System::boundary_project_vector (const std::set<boundary_id_type> & b,
1344  const std::vector<unsigned int> & variables,
1345  NumericVector<Number> & new_vector,
1348  int is_adjoint,
1349  std::optional<ConstElemRange> active_local_range) const
1350 {
1351  LOG_SCOPE ("boundary_project_vector()", "System");
1352 
1353  if (!active_local_range)
1354  {
1355  active_local_range.emplace
1356  (this->get_mesh().active_local_elements_begin(),
1357  this->get_mesh().active_local_elements_end());
1358  }
1359 
1361  (active_local_range.value(),
1362  BoundaryProjectSolution(b, variables, *this, f, g,
1363  this->get_equation_systems().parameters,
1364  new_vector)
1365  );
1366 
1367  // We don't do SCALAR dofs when just projecting the boundary, so
1368  // we're done here.
1369 
1370  new_vector.close();
1371 
1372 #ifdef LIBMESH_ENABLE_CONSTRAINTS
1373  if (is_adjoint == -1)
1374  this->get_dof_map().enforce_constraints_exactly(*this, &new_vector);
1375  else if (is_adjoint >= 0)
1376  this->get_dof_map().enforce_adjoint_constraints_exactly(new_vector,
1377  is_adjoint);
1378 #else
1379  libmesh_ignore(is_adjoint);
1380 #endif
1381 }
1382 
1383 
1384 
1385 #ifdef LIBMESH_ENABLE_AMR
1386 void BuildProjectionList::unique()
1387 {
1388  // Sort the send list. After this duplicated
1389  // elements will be adjacent in the vector
1390  std::sort(this->send_list.begin(),
1391  this->send_list.end());
1392 
1393  // Now use std::unique to remove duplicate entries
1394  std::vector<dof_id_type>::iterator new_end =
1395  std::unique (this->send_list.begin(),
1396  this->send_list.end());
1397 
1398  // Remove the end of the send_list. Use the "swap trick"
1399  // from Effective STL
1400  std::vector<dof_id_type>
1401  (this->send_list.begin(), new_end).swap (this->send_list);
1402 }
1403 
1404 
1405 
1406 void BuildProjectionList::operator()(const ConstElemRange & range)
1407 {
1408  // The DofMap for this system
1409  const DofMap & dof_map = system.get_dof_map();
1410 
1411  const dof_id_type first_old_dof = dof_map.first_old_dof();
1412  const dof_id_type end_old_dof = dof_map.end_old_dof();
1413 
1414  // We can handle all the variables at once.
1415  // The old global DOF indices
1416  std::vector<dof_id_type> di;
1417 
1418  // Iterate over the elements in the range
1419  for (const auto & elem : range)
1420  {
1421  // If this element doesn't have an old_dof_object with dofs for the
1422  // current system, then it must be newly added, so the user
1423  // is responsible for setting the new dofs.
1424 
1425  // ... but we need a better way to test for that; the code
1426  // below breaks on any FE type for which the elem stores no
1427  // dofs.
1428  // if (!elem->get_old_dof_object() || !elem->get_old_dof_object()->has_dofs(system.number()))
1429  // continue;
1430 
1431  // Examining refinement flags instead should distinguish
1432  // between refinement-added and user-added elements lacking
1433  // old_dof_object
1434  const DofObject * old_dof_object = elem->get_old_dof_object();
1435  if (!old_dof_object &&
1436  elem->refinement_flag() != Elem::JUST_REFINED &&
1437  elem->refinement_flag() != Elem::JUST_COARSENED)
1438  continue;
1439 
1440  const Elem * parent = elem->parent();
1441 
1442  if (elem->refinement_flag() == Elem::JUST_REFINED)
1443  {
1444  libmesh_assert(parent);
1445 
1446  // We used to hack_p_level here, but that wasn't thread-safe
1447  // so now we take p refinement flags into account in
1448  // old_dof_indices
1449 
1450  dof_map.old_dof_indices (parent, di);
1451 
1452  for (auto & node : elem->node_ref_range())
1453  {
1454  const DofObject * old_dofs = node.get_old_dof_object();
1455 
1456  if (old_dofs)
1457  {
1458  const unsigned int sysnum = system.number();
1459  const unsigned int nvg = old_dofs->n_var_groups(sysnum);
1460 
1461  for (unsigned int vg=0; vg != nvg; ++vg)
1462  {
1463  const unsigned int nvig =
1464  old_dofs->n_vars(sysnum, vg);
1465  for (unsigned int vig=0; vig != nvig; ++vig)
1466  {
1467  const unsigned int n_comp =
1468  old_dofs->n_comp_group(sysnum, vg);
1469  for (unsigned int c=0; c != n_comp; ++c)
1470  {
1471  const dof_id_type old_id =
1472  old_dofs->dof_number(sysnum, vg, vig,
1473  c, n_comp);
1474 
1475  // We should either have no old id
1476  // (e.g. on a newly expanded subdomain)
1477  // or an id from the old system.
1478  libmesh_assert(old_id < dof_map.n_old_dofs() ||
1479  old_id == DofObject::invalid_id);
1480  di.push_back(old_id);
1481  }
1482  }
1483  }
1484  }
1485  }
1486 
1487  std::sort(di.begin(), di.end());
1488  std::vector<dof_id_type>::iterator new_end =
1489  std::unique(di.begin(), di.end());
1490  std::vector<dof_id_type>(di.begin(), new_end).swap(di);
1491  }
1492  else if (elem->refinement_flag() == Elem::JUST_COARSENED)
1493  {
1494  std::vector<dof_id_type> di_child;
1495  di.clear();
1496  for (auto & child : elem->child_ref_range())
1497  {
1498  dof_map.old_dof_indices (&child, di_child);
1499  di.insert(di.end(), di_child.begin(), di_child.end());
1500  }
1501  }
1502  else
1503  dof_map.old_dof_indices (elem, di);
1504 
1505  for (auto di_i : di)
1506  {
1507  // If we've just expanded a subdomain for a
1508  // subdomain-restricted variable, then we may have an
1509  // old_dof_object that doesn't have an old DoF for every
1510  // local index.
1511  if (di_i == DofObject::invalid_id)
1512  continue;
1513 
1514  libmesh_assert_less(di_i, dof_map.n_old_dofs());
1515  if (di_i < first_old_dof || di_i >= end_old_dof)
1516  this->send_list.push_back(di_i);
1517  }
1518  } // end elem loop
1519 }
1520 
1521 
1522 
1523 void BuildProjectionList::join(const BuildProjectionList & other)
1524 {
1525  // Joining simply requires I add the dof indices from the other object
1526  this->send_list.insert(this->send_list.end(),
1527  other.send_list.begin(),
1528  other.send_list.end());
1529 }
1530 #endif // LIBMESH_ENABLE_AMR
1531 
1532 
1533 
1534 void BoundaryProjectSolution::operator()(const ConstElemRange & range) const
1535 {
1536  // We need data to project
1537  libmesh_assert(f.get());
1538 
1546  // The dimensionality of the current mesh
1547  const unsigned int dim = system.get_mesh().mesh_dimension();
1548 
1549  // The DofMap for this system
1550  const DofMap & dof_map = system.get_dof_map();
1551 
1552  // Boundary info for the current mesh
1553  const BoundaryInfo & boundary_info =
1554  system.get_mesh().get_boundary_info();
1555 
1556  // The element matrix and RHS for projections.
1557  // Note that Ke is always real-valued, whereas
1558  // Fe may be complex valued if complex number
1559  // support is enabled
1560  DenseMatrix<Real> Ke;
1562  // The new element coefficients
1564 
1565 
1566  // Loop over all the variables we've been requested to project
1567  for (auto v : make_range(variables.size()))
1568  {
1569  const unsigned int var = variables[v];
1570 
1571  const Variable & variable = dof_map.variable(var);
1572 
1573  const FEType & fe_type = variable.type();
1574 
1575  if (fe_type.family == SCALAR)
1576  continue;
1577 
1578  const unsigned int var_component =
1579  system.variable_scalar_number(var, 0);
1580 
1581  // Get FE objects of the appropriate type
1582  std::unique_ptr<FEBase> fe (FEBase::build(dim, fe_type));
1583 
1584  // Prepare variables for projection
1585  std::unique_ptr<QBase> qedgerule (fe_type.default_quadrature_rule(1));
1586  std::unique_ptr<QBase> qsiderule (fe_type.default_quadrature_rule(dim-1));
1587 
1588  // The values of the shape functions at the quadrature
1589  // points
1590  const std::vector<std::vector<Real>> & phi = fe->get_phi();
1591 
1592  // The gradients of the shape functions at the quadrature
1593  // points on the child element.
1594  const std::vector<std::vector<RealGradient>> * dphi = nullptr;
1595 
1596  const FEContinuity cont = fe->get_continuity();
1597 
1598  if (cont == C_ONE)
1599  {
1600  // We'll need gradient data for a C1 projection
1601  libmesh_assert(g.get());
1602 
1603  const std::vector<std::vector<RealGradient>> &
1604  ref_dphi = fe->get_dphi();
1605  dphi = &ref_dphi;
1606  }
1607 
1608  // The Jacobian * quadrature weight at the quadrature points
1609  const std::vector<Real> & JxW =
1610  fe->get_JxW();
1611 
1612  // The XYZ locations of the quadrature points
1613  const std::vector<Point> & xyz_values =
1614  fe->get_xyz();
1615 
1616  // The global DOF indices
1617  std::vector<dof_id_type> dof_indices;
1618  // Side/edge DOF indices
1619  std::vector<unsigned int> side_dofs;
1620 
1621  // Container to catch IDs passed back from BoundaryInfo.
1622  std::vector<boundary_id_type> bc_ids;
1623 
1624  // Iterate over all the elements in the range
1625  for (const auto & elem : range)
1626  {
1627  // Per-subdomain variables don't need to be projected on
1628  // elements where they're not active
1629  if (!variable.active_on_subdomain(elem->subdomain_id()))
1630  continue;
1631 
1632  const unsigned short n_nodes = elem->n_nodes();
1633  const unsigned short n_edges = elem->n_edges();
1634  const unsigned short n_sides = elem->n_sides();
1635 
1636  // Find out which nodes, edges and sides are on a requested
1637  // boundary:
1638  std::vector<bool> is_boundary_node(n_nodes, false),
1639  is_boundary_edge(n_edges, false),
1640  is_boundary_side(n_sides, false);
1641 
1642  // We also maintain a separate list of nodeset-based boundary nodes
1643  std::vector<bool> is_boundary_nodeset(n_nodes, false);
1644 
1645  for (unsigned char s=0; s != n_sides; ++s)
1646  {
1647  // First see if this side has been requested
1648  boundary_info.boundary_ids (elem, s, bc_ids);
1649  bool do_this_side = false;
1650  for (const auto & bc_id : bc_ids)
1651  if (b.count(bc_id))
1652  {
1653  do_this_side = true;
1654  break;
1655  }
1656  if (!do_this_side)
1657  continue;
1658 
1659  is_boundary_side[s] = true;
1660 
1661  // Then see what nodes and what edges are on it
1662  for (unsigned int n=0; n != n_nodes; ++n)
1663  if (elem->is_node_on_side(n,s))
1664  is_boundary_node[n] = true;
1665  for (unsigned int e=0; e != n_edges; ++e)
1666  if (elem->is_edge_on_side(e,s))
1667  is_boundary_edge[e] = true;
1668  }
1669 
1670  // We can also project on nodes, so we should also independently
1671  // check whether the nodes have been requested
1672  for (unsigned int n=0; n != n_nodes; ++n)
1673  {
1674  boundary_info.boundary_ids (elem->node_ptr(n), bc_ids);
1675 
1676  for (const auto & bc_id : bc_ids)
1677  if (b.count(bc_id))
1678  {
1679  is_boundary_node[n] = true;
1680  is_boundary_nodeset[n] = true;
1681  }
1682  }
1683 
1684  // We can also project on edges, so we should also independently
1685  // check whether the edges have been requested
1686  for (unsigned short e=0; e != n_edges; ++e)
1687  {
1688  boundary_info.edge_boundary_ids (elem, e, bc_ids);
1689 
1690  for (const auto & bc_id : bc_ids)
1691  if (b.count(bc_id))
1692  is_boundary_edge[e] = true;
1693  }
1694 
1695  // Update the DOF indices for this element based on
1696  // the current mesh
1697  dof_map.dof_indices (elem, dof_indices, var);
1698 
1699  // The number of DOFs on the element
1700  const unsigned int n_dofs =
1701  cast_int<unsigned int>(dof_indices.size());
1702 
1703  // Fixed vs. free DoFs on edge/face projections
1704  std::vector<char> dof_is_fixed(n_dofs, false); // bools
1705  std::vector<int> free_dof(n_dofs, 0);
1706 
1707  // Zero the interpolated values
1708  Ue.resize (n_dofs); Ue.zero();
1709 
1710  // In general, we need a series of
1711  // projections to ensure a unique and continuous
1712  // solution. We start by interpolating boundary nodes, then
1713  // hold those fixed and project boundary edges, then hold
1714  // those fixed and project boundary faces,
1715 
1716  // Interpolate node values first
1717  unsigned int current_dof = 0;
1718  for (unsigned short n = 0; n != n_nodes; ++n)
1719  {
1720  // FIXME: this should go through the DofMap,
1721  // not duplicate dof_indices code badly!
1722 
1723  // This call takes into account elem->p_level() internally.
1724  const unsigned int nc =
1725  FEInterface::n_dofs_at_node (fe_type, elem, n);
1726 
1727  if ((!elem->is_vertex(n) || !is_boundary_node[n]) &&
1728  !is_boundary_nodeset[n])
1729  {
1730  current_dof += nc;
1731  continue;
1732  }
1733  if (cont == DISCONTINUOUS)
1734  {
1735  libmesh_assert_equal_to (nc, 0);
1736  }
1737  // Assume that C_ZERO elements have a single nodal
1738  // value shape function
1739  else if (cont == C_ZERO)
1740  {
1741  libmesh_assert_equal_to (nc, 1);
1742  Ue(current_dof) = f->component(var_component,
1743  elem->point(n),
1744  system.time);
1745  dof_is_fixed[current_dof] = true;
1746  current_dof++;
1747  }
1748  // The hermite element vertex shape functions are weird
1749  else if (fe_type.family == HERMITE)
1750  {
1751  Ue(current_dof) = f->component(var_component,
1752  elem->point(n),
1753  system.time);
1754  dof_is_fixed[current_dof] = true;
1755  current_dof++;
1756  Gradient grad = g->component(var_component,
1757  elem->point(n),
1758  system.time);
1759  // x derivative
1760  Ue(current_dof) = grad(0);
1761  dof_is_fixed[current_dof] = true;
1762  current_dof++;
1763 #if LIBMESH_DIM > 1
1764  if (dim > 1)
1765  {
1766  // We'll finite difference mixed derivatives
1767  Point nxminus = elem->point(n),
1768  nxplus = elem->point(n);
1769  nxminus(0) -= TOLERANCE;
1770  nxplus(0) += TOLERANCE;
1771  Gradient gxminus = g->component(var_component,
1772  nxminus,
1773  system.time);
1774  Gradient gxplus = g->component(var_component,
1775  nxplus,
1776  system.time);
1777  // y derivative
1778  Ue(current_dof) = grad(1);
1779  dof_is_fixed[current_dof] = true;
1780  current_dof++;
1781  // xy derivative
1782  Ue(current_dof) = (gxplus(1) - gxminus(1))
1783  / 2. / TOLERANCE;
1784  dof_is_fixed[current_dof] = true;
1785  current_dof++;
1786 
1787 #if LIBMESH_DIM > 2
1788  if (dim > 2)
1789  {
1790  // z derivative
1791  Ue(current_dof) = grad(2);
1792  dof_is_fixed[current_dof] = true;
1793  current_dof++;
1794  // xz derivative
1795  Ue(current_dof) = (gxplus(2) - gxminus(2))
1796  / 2. / TOLERANCE;
1797  dof_is_fixed[current_dof] = true;
1798  current_dof++;
1799  // We need new points for yz
1800  Point nyminus = elem->point(n),
1801  nyplus = elem->point(n);
1802  nyminus(1) -= TOLERANCE;
1803  nyplus(1) += TOLERANCE;
1804  Gradient gyminus = g->component(var_component,
1805  nyminus,
1806  system.time);
1807  Gradient gyplus = g->component(var_component,
1808  nyplus,
1809  system.time);
1810  // xz derivative
1811  Ue(current_dof) = (gyplus(2) - gyminus(2))
1812  / 2. / TOLERANCE;
1813  dof_is_fixed[current_dof] = true;
1814  current_dof++;
1815  // Getting a 2nd order xyz is more tedious
1816  Point nxmym = elem->point(n),
1817  nxmyp = elem->point(n),
1818  nxpym = elem->point(n),
1819  nxpyp = elem->point(n);
1820  nxmym(0) -= TOLERANCE;
1821  nxmym(1) -= TOLERANCE;
1822  nxmyp(0) -= TOLERANCE;
1823  nxmyp(1) += TOLERANCE;
1824  nxpym(0) += TOLERANCE;
1825  nxpym(1) -= TOLERANCE;
1826  nxpyp(0) += TOLERANCE;
1827  nxpyp(1) += TOLERANCE;
1828  Gradient gxmym = g->component(var_component,
1829  nxmym,
1830  system.time);
1831  Gradient gxmyp = g->component(var_component,
1832  nxmyp,
1833  system.time);
1834  Gradient gxpym = g->component(var_component,
1835  nxpym,
1836  system.time);
1837  Gradient gxpyp = g->component(var_component,
1838  nxpyp,
1839  system.time);
1840  Number gxzplus = (gxpyp(2) - gxmyp(2))
1841  / 2. / TOLERANCE;
1842  Number gxzminus = (gxpym(2) - gxmym(2))
1843  / 2. / TOLERANCE;
1844  // xyz derivative
1845  Ue(current_dof) = (gxzplus - gxzminus)
1846  / 2. / TOLERANCE;
1847  dof_is_fixed[current_dof] = true;
1848  current_dof++;
1849  }
1850 #endif // LIBMESH_DIM > 2
1851  }
1852 #endif // LIBMESH_DIM > 1
1853  }
1854  // Assume that other C_ONE elements have a single nodal
1855  // value shape function and nodal gradient component
1856  // shape functions
1857  else if (cont == C_ONE)
1858  {
1859  libmesh_assert_equal_to (nc, 1 + dim);
1860  Ue(current_dof) = f->component(var_component,
1861  elem->point(n),
1862  system.time);
1863  dof_is_fixed[current_dof] = true;
1864  current_dof++;
1865  Gradient grad = g->component(var_component,
1866  elem->point(n),
1867  system.time);
1868  for (unsigned int i=0; i!= dim; ++i)
1869  {
1870  Ue(current_dof) = grad(i);
1871  dof_is_fixed[current_dof] = true;
1872  current_dof++;
1873  }
1874  }
1875  else
1876  libmesh_error_msg("Unknown continuity " << cont);
1877  }
1878 
1879  // In 3D, project any edge values next
1880  if (dim > 2 && cont != DISCONTINUOUS)
1881  for (unsigned short e = 0; e != n_edges; ++e)
1882  {
1883  if (!is_boundary_edge[e])
1884  continue;
1885 
1886  FEInterface::dofs_on_edge(elem, dim, fe_type, e,
1887  side_dofs);
1888 
1889  const unsigned int n_side_dofs =
1890  cast_int<unsigned int>(side_dofs.size());
1891 
1892  // Some edge dofs are on nodes and already
1893  // fixed, others are free to calculate
1894  unsigned int free_dofs = 0;
1895  for (auto i : make_range(n_side_dofs))
1896  if (!dof_is_fixed[side_dofs[i]])
1897  free_dof[free_dofs++] = i;
1898 
1899  // There may be nothing to project
1900  if (!free_dofs)
1901  continue;
1902 
1903  Ke.resize (free_dofs, free_dofs); Ke.zero();
1904  Fe.resize (free_dofs); Fe.zero();
1905  // The new edge coefficients
1906  DenseVector<Number> Uedge(free_dofs);
1907 
1908  // Initialize FE data on the edge
1909  fe->attach_quadrature_rule (qedgerule.get());
1910  fe->edge_reinit (elem, e);
1911  const unsigned int n_qp = qedgerule->n_points();
1912 
1913  // Loop over the quadrature points
1914  for (unsigned int qp=0; qp<n_qp; qp++)
1915  {
1916  // solution at the quadrature point
1917  Number fineval = f->component(var_component,
1918  xyz_values[qp],
1919  system.time);
1920  // solution grad at the quadrature point
1921  Gradient finegrad;
1922  if (cont == C_ONE)
1923  finegrad = g->component(var_component,
1924  xyz_values[qp],
1925  system.time);
1926 
1927  // Form edge projection matrix
1928  for (unsigned int sidei=0, freei=0;
1929  sidei != n_side_dofs; ++sidei)
1930  {
1931  unsigned int i = side_dofs[sidei];
1932  // fixed DoFs aren't test functions
1933  if (dof_is_fixed[i])
1934  continue;
1935  for (unsigned int sidej=0, freej=0;
1936  sidej != n_side_dofs; ++sidej)
1937  {
1938  unsigned int j = side_dofs[sidej];
1939  if (dof_is_fixed[j])
1940  Fe(freei) -= phi[i][qp] * phi[j][qp] *
1941  JxW[qp] * Ue(j);
1942  else
1943  Ke(freei,freej) += phi[i][qp] *
1944  phi[j][qp] * JxW[qp];
1945  if (cont == C_ONE)
1946  {
1947  if (dof_is_fixed[j])
1948  Fe(freei) -= ((*dphi)[i][qp] *
1949  (*dphi)[j][qp]) *
1950  JxW[qp] * Ue(j);
1951  else
1952  Ke(freei,freej) += ((*dphi)[i][qp] *
1953  (*dphi)[j][qp])
1954  * JxW[qp];
1955  }
1956  if (!dof_is_fixed[j])
1957  freej++;
1958  }
1959  Fe(freei) += phi[i][qp] * fineval * JxW[qp];
1960  if (cont == C_ONE)
1961  Fe(freei) += (finegrad * (*dphi)[i][qp]) *
1962  JxW[qp];
1963  freei++;
1964  }
1965  }
1966 
1967  Ke.cholesky_solve(Fe, Uedge);
1968 
1969  // Transfer new edge solutions to element
1970  for (unsigned int i=0; i != free_dofs; ++i)
1971  {
1972  Number & ui = Ue(side_dofs[free_dof[i]]);
1973  libmesh_assert(std::abs(ui) < TOLERANCE ||
1974  std::abs(ui - Uedge(i)) < TOLERANCE);
1975  ui = Uedge(i);
1976  dof_is_fixed[side_dofs[free_dof[i]]] = true;
1977  }
1978  }
1979 
1980  // Project any side values (edges in 2D, faces in 3D)
1981  if (dim > 1 && cont != DISCONTINUOUS)
1982  for (unsigned short s = 0; s != n_sides; ++s)
1983  {
1984  if (!is_boundary_side[s])
1985  continue;
1986 
1987  FEInterface::dofs_on_side(elem, dim, fe_type, s,
1988  side_dofs);
1989 
1990  // Some side dofs are on nodes/edges and already
1991  // fixed, others are free to calculate
1992  unsigned int free_dofs = 0;
1993  for (auto i : index_range(side_dofs))
1994  if (!dof_is_fixed[side_dofs[i]])
1995  free_dof[free_dofs++] = i;
1996 
1997  // There may be nothing to project
1998  if (!free_dofs)
1999  continue;
2000 
2001  Ke.resize (free_dofs, free_dofs); Ke.zero();
2002  Fe.resize (free_dofs); Fe.zero();
2003  // The new side coefficients
2004  DenseVector<Number> Uside(free_dofs);
2005 
2006  // Initialize FE data on the side
2007  fe->attach_quadrature_rule (qsiderule.get());
2008  fe->reinit (elem, s);
2009  const unsigned int n_qp = qsiderule->n_points();
2010 
2011  const unsigned int n_side_dofs =
2012  cast_int<unsigned int>(side_dofs.size());
2013 
2014  // Loop over the quadrature points
2015  for (unsigned int qp=0; qp<n_qp; qp++)
2016  {
2017  // solution at the quadrature point
2018  Number fineval = f->component(var_component,
2019  xyz_values[qp],
2020  system.time);
2021  // solution grad at the quadrature point
2022  Gradient finegrad;
2023  if (cont == C_ONE)
2024  finegrad = g->component(var_component,
2025  xyz_values[qp],
2026  system.time);
2027 
2028  // Form side projection matrix
2029  for (unsigned int sidei=0, freei=0;
2030  sidei != n_side_dofs; ++sidei)
2031  {
2032  unsigned int i = side_dofs[sidei];
2033  // fixed DoFs aren't test functions
2034  if (dof_is_fixed[i])
2035  continue;
2036  for (unsigned int sidej=0, freej=0;
2037  sidej != n_side_dofs; ++sidej)
2038  {
2039  unsigned int j = side_dofs[sidej];
2040  if (dof_is_fixed[j])
2041  Fe(freei) -= phi[i][qp] * phi[j][qp] *
2042  JxW[qp] * Ue(j);
2043  else
2044  Ke(freei,freej) += phi[i][qp] *
2045  phi[j][qp] * JxW[qp];
2046  if (cont == C_ONE)
2047  {
2048  if (dof_is_fixed[j])
2049  Fe(freei) -= ((*dphi)[i][qp] *
2050  (*dphi)[j][qp]) *
2051  JxW[qp] * Ue(j);
2052  else
2053  Ke(freei,freej) += ((*dphi)[i][qp] *
2054  (*dphi)[j][qp])
2055  * JxW[qp];
2056  }
2057  if (!dof_is_fixed[j])
2058  freej++;
2059  }
2060  Fe(freei) += (fineval * phi[i][qp]) * JxW[qp];
2061  if (cont == C_ONE)
2062  Fe(freei) += (finegrad * (*dphi)[i][qp]) *
2063  JxW[qp];
2064  freei++;
2065  }
2066  }
2067 
2068  Ke.cholesky_solve(Fe, Uside);
2069 
2070  // Transfer new side solutions to element
2071  for (unsigned int i=0; i != free_dofs; ++i)
2072  {
2073  Number & ui = Ue(side_dofs[free_dof[i]]);
2074  libmesh_assert(std::abs(ui) < TOLERANCE ||
2075  std::abs(ui - Uside(i)) < TOLERANCE);
2076  ui = Uside(i);
2077  dof_is_fixed[side_dofs[free_dof[i]]] = true;
2078  }
2079  }
2080 
2081  const dof_id_type
2082  first = new_vector.first_local_index(),
2083  last = new_vector.last_local_index();
2084 
2085  // Lock the new_vector since it is shared among threads.
2086  {
2087  Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
2088 
2089  for (unsigned int i = 0; i < n_dofs; i++)
2090  if (dof_is_fixed[i] &&
2091  (dof_indices[i] >= first) &&
2092  (dof_indices[i] < last))
2093  {
2094  new_vector.set(dof_indices[i], Ue(i));
2095  }
2096  }
2097  } // end elem loop
2098  } // end variables loop
2099 }
2100 
2101 
2102 void System::solve_for_unconstrained_dofs(NumericVector<Number> & vec,
2103  int is_adjoint) const
2104 {
2105  const DofMap & dof_map = this->get_dof_map();
2106 
2107  std::unique_ptr<SparseMatrix<Number>> mat =
2108  SparseMatrix<Number>::build(this->comm());
2109 
2110  std::unique_ptr<SparsityPattern::Build> sp;
2111 
2112  if (dof_map.computed_sparsity_already())
2113  dof_map.update_sparsity_pattern(*mat);
2114  else
2115  {
2116  mat->attach_dof_map(dof_map);
2117  sp = dof_map.build_sparsity(this->get_mesh());
2118  mat->attach_sparsity_pattern(*sp);
2119  }
2120 
2121  mat->init();
2122 
2123  libmesh_assert_equal_to(vec.size(), dof_map.n_dofs());
2124  libmesh_assert_equal_to(vec.local_size(), dof_map.n_local_dofs());
2125 
2126  std::unique_ptr<NumericVector<Number>> rhs =
2127  NumericVector<Number>::build(this->comm());
2128 
2129  rhs->init(dof_map.n_dofs(), dof_map.n_local_dofs(), false,
2130  PARALLEL);
2131 
2132  // Here we start with the unconstrained (and indeterminate) linear
2133  // system, K*u = f, where K is the identity matrix for constrained
2134  // DoFs and 0 elsewhere, and f is the current solution values for
2135  // constrained DoFs and 0 elsewhere.
2136  // We then apply the usual heterogeneous constraint matrix C and
2137  // offset h, where u = C*x + h,
2138  // to get C^T*K*C*x = C^T*f - C^T*K*h
2139  // - a constrained and no-longer-singular system that finds the
2140  // closest approximation for the unconstrained degrees of freedom.
2141  //
2142  // Here, though "closest" is in an algebraic sense; we're
2143  // effectively using a pseudoinverse that optimizes in a
2144  // discretization-dependent norm. That only seems to give ~0.1%
2145  // excess error even in coarse unit test cases, but at some point it
2146  // might be reasonable to weight K and f properly.
2147 
2148  for (dof_id_type d : IntRange<dof_id_type>(dof_map.first_dof(),
2149  dof_map.end_dof()))
2150  {
2151  if (dof_map.is_constrained_dof(d))
2152  {
2153  DenseMatrix<Number> K(1,1);
2154  DenseVector<Number> F(1);
2155  std::vector<dof_id_type> dof_indices(1, d);
2156  K(0,0) = 1;
2157  F(0) = (*this->solution)(d);
2159  (K, F, dof_indices, false, is_adjoint);
2160  mat->add_matrix(K, dof_indices);
2161  rhs->add_vector(F, dof_indices);
2162  }
2163  }
2164 
2165  std::unique_ptr<LinearSolver<Number>> linear_solver =
2166  LinearSolver<Number>::build(this->comm());
2167 
2168  linear_solver->solve(*mat, vec, *rhs,
2169  double(this->get_equation_systems().parameters.get<Real>("linear solver tolerance")),
2170  this->get_equation_systems().parameters.get<unsigned int>("linear solver maximum iterations"));
2171 }
2172 
2173 
2174 } // namespace libMesh
The GenericProjector class implements the core of other projection operations, using two input functo...
class FEType hides (possibly multiple) FEFamily and approximation orders, thereby enabling specialize...
Definition: fe_type.h:196
FEFamily family
The type of finite element.
Definition: fe_type.h:228
void parallel_for(const Range &range, const Body &body, unsigned int n_threads=libMesh::n_threads())
Execute the provided function object in parallel on the specified range.
Definition: threads_none.h:73
void _cholesky_back_substitute(const DenseVector< T2 > &b, DenseVector< T2 > &x) const
Solves the equation Ax=b for the unknown value x and rhs b based on the Cholesky factorization of A...
RefinementState refinement_flag() const
Definition: elem.h:3224
void eval_old_dofs(const Elem &elem, const FEType &fe_type, unsigned int sys_num, unsigned int var_num, std::vector< dof_id_type > &indices, std::vector< DSNA > &values)
Wrap a libMesh-style function pointer into a FunctionBase object.
dof_id_type end_dof(const processor_id_type proc) const
Definition: dof_map_base.h:191
dof_id_type dof_number(const unsigned int s, const unsigned int var, const unsigned int comp) const
Definition: dof_object.h:1008
const Elem * parent() const
Definition: elem.h:3044
OldSolutionCoefs(const libMesh::System &sys_in, const std::vector< unsigned int > *vars)
A Node is like a Point, but with more information.
Definition: node.h:52
virtual void zero() override final
Set every element in the vector to 0.
Definition: dense_vector.h:420
virtual void pre_fe_reinit(const System &, const Elem *e)
Reinitializes local data vectors/matrices on the current geometric element.
Definition: fem_context.C:1683
unsigned int n_comp(const unsigned int s, const unsigned int var) const
Definition: dof_object.h:978
BuildProjectionList(const System &system_in)
SparseMatrix< ValOut > & target_matrix
This class provides the ability to map between arbitrary, user-defined strings and several data types...
Definition: parameters.h:74
unsigned int n_var_groups(const unsigned int s) const
Definition: dof_object.h:933
The IntRange templated class is intended to make it easy to loop over integers which are indices of a...
Definition: int_range.h:53
virtual void get(const std::vector< numeric_index_type > &index, T *values) const
Access multiple components at once.
void dof_indices(const Elem *const elem, std::vector< dof_id_type > &di) const
Definition: dof_map.C:2201
virtual void zero() override final
Sets all elements of the matrix to 0 and resets any decomposition flag which may have been previously...
Definition: dense_matrix.h:911
static constexpr Real TOLERANCE
const Elem & get_elem() const
Accessor for current Elem object.
Definition: fem_context.h:908
unsigned int dim
MetaPhysicL::DynamicSparseNumberArray< typename CompareTypes< T, T2 >::supertype, IndexType > supertype
std::unique_ptr< FunctionBase< Number > > f
dof_id_type end_old_dof(const processor_id_type proc) const
Definition: dof_map_base.h:210
The OldSolutionBase input functor abstract base class is the root of the OldSolutionValue and OldSolu...
BoundaryProjectSolution(const std::set< boundary_id_type > &b_in, const std::vector< unsigned int > &variables_in, const System &system_in, FunctionBase< Number > *f_in, FunctionBase< Gradient > *g_in, const Parameters &parameters_in, NumericVector< Number > &new_v_in)
virtual numeric_index_type size() const =0
Dummy "splitting object" used to distinguish splitting constructors from copy constructors.
Definition: threads_none.h:63
void resize(const unsigned int n)
Resize the vector.
Definition: dense_vector.h:396
DynamicSparseNumberArray< Output, dof_id_type > type
std::unique_ptr< SparsityPattern::Build > build_sparsity(const MeshBase &mesh, bool calculate_constrained=false, bool use_condensed_system=false) const
Builds a sparsity pattern for matrices using the current degree-of-freedom numbering and coupling...
Definition: dof_map.C:63
This is the base class from which all geometric element types are derived.
Definition: elem.h:94
This class implements projecting an arbitrary boundary function to the current mesh.
RefinementState
Enumeration of possible element refinement states.
Definition: elem.h:1443
virtual std::unique_ptr< NumericVector< T > > clone() const =0
void boundary_ids(const Node *node, std::vector< boundary_id_type > &vec_to_fill) const
Fills a user-provided std::vector with the boundary ids associated with Node node.
This class defines a vector in LIBMESH_DIM dimensional Real or Complex space.
dof_id_type n_dofs(const unsigned int vn) const
Definition: dof_map.h:776
The StoredRange class defines a contiguous, divisible set of objects.
Definition: stored_range.h:54
void eval_mixed_derivatives(const FEMContext &libmesh_dbg_var(c), unsigned int i, unsigned int dim, const Node &n, std::vector< DSNA > &derivs)
The libMesh namespace provides an interface to certain functionality in the library.
The OldSolutionValue input functor class can be used with GenericProjector to read values from a solu...
const std::vector< unsigned int > & variables
dof_id_type n_local_dofs(const unsigned int vn) const
Definition: dof_map.h:794
virtual void init(const numeric_index_type n, const numeric_index_type n_local, const bool fast=false, const ParallelType ptype=AUTOMATIC)=0
Change the dimension of the vector to n.
void SCALAR_dof_indices(std::vector< dof_id_type > &di, const unsigned int vn, const bool old_dofs=false) const
Fills the vector di with the global degree of freedom indices corresponding to the SCALAR variable vn...
Definition: dof_map.C:2605
virtual numeric_index_type row_stop() const =0
const Variable & variable(const unsigned int c) const override
Definition: dof_map.h:2358
virtual void set(const numeric_index_type i, const numeric_index_type j, const T value)=0
Set the element (i,j) to value.
Number fptr(const Point &p, const Parameters &, const std::string &libmesh_dbg_var(sys_name), const std::string &unknown_name)
Definition: projection.C:81
This class provides a wrapper with which to evaluate a (libMesh-style) function pointer in a Function...
BuildProjectionList(BuildProjectionList &other, Threads::split)
This class handles the numbering of degrees of freedom on a mesh.
Definition: dof_map.h:179
std::unique_ptr< QBase > default_quadrature_rule(const unsigned int dim, const int extraorder=0) const
Definition: fe_type.C:34
This base class can be inherited from to provide interfaces to linear solvers from different packages...
const std::vector< std::vector< OutputGradient > > & get_dphi() const
Definition: fe_base.h:230
This class builds the send_list of old dof indices whose coefficients are needed to perform a project...
void libmesh_ignore(const Args &...)
const dof_id_type n_nodes
Definition: tecplot_io.C:67
This class defines the notion of a variable in the system.
Definition: variable.h:50
dof_id_type numeric_index_type
Definition: id_types.h:99
unsigned int n_vars
The MatrixFillAction output functor class can be used with GenericProjector to write solution transfe...
NumericVector< Number > & new_vector
unsigned int n_vars(const unsigned int s, const unsigned int vg) const
Definition: dof_object.h:943
VectorValue< DynamicSparseNumberArray< InnerOutput, dof_id_type > > type
Manages consistently variables, degrees of freedom, and coefficient vectors.
Definition: system.h:98
unsigned int n_systems() const
Definition: dof_object.h:913
NumberVectorValue Gradient
The BoundaryInfo class contains information relevant to boundary conditions including storing faces...
Definition: boundary_info.h:57
libmesh_assert(ctx)
bool is_constrained_dof(const dof_id_type dof) const
Definition: dof_map.h:2426
This class provides all data required for a physics package (e.g.
Definition: fem_context.h:62
void insert(dof_id_type id, const DynamicSparseNumberArray< ValIn, dof_id_type > &val)
dof_id_type n_old_dofs() const
Definition: dof_map_base.h:123
DofObject * get_old_dof_object()
Pointer accessor for previously public old_dof_object.
Definition: dof_object.h:96
bool active_on_subdomain(subdomain_id_type sid) const
Definition: variable.h:167
bool computed_sparsity_already() const
Returns true iff a sparsity pattern has already been computed.
Definition: dof_map.C:258
void update_sparsity_pattern(SparseMatrix< Number > &matrix) const
Additional matrices may be be temporarily initialized by this DofMap.
Definition: dof_map.C:269
BoundaryProjectSolution(const BoundaryProjectSolution &in)
virtual void close()=0
Calls the NumericVector&#39;s internal assembly routines, ensuring that the values are consistent across ...
DofObject & get_old_dof_object_ref()
As above, but do not use in situations where the old_dof_object may be nullptr, since this function a...
Definition: dof_object.h:104
void parallel_reduce(const Range &range, Body &body, unsigned int n_threads=libMesh::n_threads())
Execute the provided reduction operation in parallel on the specified range.
Definition: threads_none.h:109
OldSolutionCoefs(const OldSolutionCoefs &in)
ParallelType type() const
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
Definition: dof_map.h:1388
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
void convert_from_receive(SendT &received, T &converted)
DynamicSparseNumberArray< Real, dof_id_type > DSNAN
std::vector< dof_id_type > send_list
FEContinuity
defines an enum for finite element types to libmesh_assert a certain level (or type? Hcurl?) of continuity.
void eval_old_dofs(const Elem &elem, unsigned int node_num, unsigned int var_num, std::vector< dof_id_type > &indices, std::vector< DSNA > &values)
virtual numeric_index_type row_start() const =0
static const Real b
For ease of communication, we allow users to translate their own value types to a more easily computa...
virtual numeric_index_type local_size() const =0
The OldSolutionCoefs input functor class can be used with GenericProjector to read solution transfer ...
virtual Output component(const FEMContext &, unsigned int i, const Point &p, Real time=0.)
void resize(const unsigned int new_m, const unsigned int new_n)
Resizes the matrix to the specified size and calls zero().
Definition: dense_matrix.h:895
Gradient gptr(const Point &p, const Parameters &, const std::string &libmesh_dbg_var(sys_name), const std::string &unknown_name)
Definition: projection.C:96
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
Definition: int_range.h:176
The DofObject defines an abstract base class for objects that have degrees of freedom associated with...
Definition: dof_object.h:54
DynamicSparseNumberArray< ValIn, dof_id_type > InsertInput
MatrixFillAction(SparseMatrix< ValOut > &target_mat)
std::pair< unsigned int, unsigned int > var_to_vg_and_offset(const unsigned int s, const unsigned int var) const
Definition: dof_object.h:1176
virtual void clear()
Restores the NumericVector<T> to a pristine state.
virtual void set(const numeric_index_type i, const T value)=0
Sets v(i) = value.
void edge_boundary_ids(const Elem *const elem, const unsigned short int edge, std::vector< boundary_id_type > &vec_to_fill) const
Defines a dense vector for use in Finite Element-type computations.
Definition: dof_map.h:74
void cholesky_solve(const DenseVector< T2 > &b, DenseVector< T2 > &x)
For symmetric positive definite (SPD) matrices.
dof_id_type first_old_dof(const processor_id_type proc) const
Definition: dof_map_base.h:204
dof_id_type first_dof(const processor_id_type proc) const
Definition: dof_map_base.h:185
The VectorSetAction output functor class can be used with a GenericProjector to set projection values...
const std::set< boundary_id_type > & b
std::unique_ptr< FunctionBase< Gradient > > g
const TypeToSend< T >::type convert_to_send(const T &in)
unsigned int n_comp_group(const unsigned int s, const unsigned int vg) const
Definition: dof_object.h:991
static const bool value
Definition: compare_types.h:66
The FEMFunctionWrapper input functor class can be used with a GenericProjector to read values from an...
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition: point.h:39
DSNAOutput< Output >::type DSNA
void ErrorVector unsigned int
Definition: adjoints_ex3.C:360
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition: int_range.h:153
void insert(const std::vector< dof_id_type > &dof_indices, const std::vector< DynamicSparseNumberArray< ValIn, dof_id_type > > &Ue)
const Elem & get(const ElemType type_in)
This class forms the foundation from which generic finite elements may be derived.
void old_dof_indices(const Elem &elem, unsigned int n, std::vector< dof_id_type > &di, const unsigned int vn) const
Appends to the vector di the old global degree of freedom indices for elem.node_ref(n), for one variable vn.
Definition: dof_map.C:2478
uint8_t dof_id_type
Definition: id_types.h:67
const std::vector< std::vector< OutputShape > > & get_phi() const
Definition: fe_base.h:207
const FEType & type() const
Definition: variable.h:144
spin_mutex spin_mtx
A convenient spin mutex object which can be used for obtaining locks.
Definition: threads.C:30
virtual void localize(std::vector< T > &v_local) const =0
Creates a copy of the global vector in the local vector v_local.