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