libMesh
fem_system.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 // libMesh includes
20 #include "libmesh/dof_map.h"
21 #include "libmesh/elem.h"
22 #include "libmesh/equation_systems.h"
23 #include "libmesh/fe_base.h"
24 #include "libmesh/fem_context.h"
25 #include "libmesh/fem_system.h"
26 #include "libmesh/libmesh_logging.h"
27 #include "libmesh/mesh_base.h"
28 #include "libmesh/numeric_vector.h"
29 #include "libmesh/parallel_algebra.h"
30 #include "libmesh/parallel_ghost_sync.h"
31 #include "libmesh/quadrature.h"
32 #include "libmesh/sparse_matrix.h"
33 #include "libmesh/time_solver.h"
34 #include "libmesh/unsteady_solver.h" // For eulerian_residual
35 #include "libmesh/fe_interface.h"
36 
37 namespace {
38 using namespace libMesh;
39 
40 typedef Threads::spin_mutex femsystem_mutex;
41 femsystem_mutex assembly_mutex;
42 
43 void assemble_unconstrained_element_system(const FEMSystem & _sys,
44  const bool _get_jacobian,
45  const bool _constrain_heterogeneously,
46  FEMContext & _femcontext)
47 {
48  if (_sys.print_element_solutions)
49  {
50  std::streamsize old_precision = libMesh::out.precision();
52  if (_femcontext.has_elem())
53  libMesh::out << "U_elem " << _femcontext.get_elem().id();
54  else
55  libMesh::out << "U_scalar ";
56  libMesh::out << " = " << _femcontext.get_elem_solution() << std::endl;
57 
58  if (_sys.use_fixed_solution)
59  {
60  if (_femcontext.has_elem())
61  libMesh::out << "Ufixed_elem " << _femcontext.get_elem().id();
62  else
63  libMesh::out << "Ufixed_scalar ";
64  libMesh::out << " = " << _femcontext.get_elem_fixed_solution() << std::endl;
65  libMesh::out.precision(old_precision);
66  }
67  }
68 
69  // We need jacobians to do heterogeneous residual constraints
70  const bool need_jacobian =
71  (_get_jacobian || _constrain_heterogeneously);
72 
73  bool jacobian_computed =
74  _sys.time_solver->element_residual(need_jacobian, _femcontext);
75 
76  // Compute a numeric jacobian if we have to
77  if (need_jacobian && !jacobian_computed)
78  {
79  // Make sure we didn't compute a jacobian and lie about it
80  libmesh_assert_equal_to (_femcontext.get_elem_jacobian().l1_norm(), 0.0);
81  // Logging of numerical jacobians is done separately
82  _sys.numerical_elem_jacobian(_femcontext);
83  }
84 
85  // Compute a numeric jacobian if we're asked to verify the
86  // analytic jacobian we got
87  if (need_jacobian && jacobian_computed &&
88  _sys.verify_analytic_jacobians != 0.0)
89  {
90  DenseMatrix<Number> analytic_jacobian(_femcontext.get_elem_jacobian());
91 
92  _femcontext.get_elem_jacobian().zero();
93  // Logging of numerical jacobians is done separately
94  _sys.numerical_elem_jacobian(_femcontext);
95 
96  Real analytic_norm = analytic_jacobian.l1_norm();
97  Real numerical_norm = _femcontext.get_elem_jacobian().l1_norm();
98 
99  // If we can continue, we'll probably prefer the analytic jacobian
100  analytic_jacobian.swap(_femcontext.get_elem_jacobian());
101 
102  // The matrix "analytic_jacobian" will now hold the error matrix
103  analytic_jacobian.add(-1.0, _femcontext.get_elem_jacobian());
104  Real error_norm = analytic_jacobian.l1_norm();
105 
106  Real relative_error = error_norm /
107  std::max(analytic_norm, numerical_norm);
108 
109  if (relative_error > _sys.verify_analytic_jacobians)
110  {
111  libMesh::err << "Relative error " << relative_error
112  << " detected in analytic jacobian on element "
113  << _femcontext.get_elem().id() << '!' << std::endl;
114 
115  std::streamsize old_precision = libMesh::out.precision();
117  libMesh::out << "J_analytic " << _femcontext.get_elem().id() << " = "
118  << _femcontext.get_elem_jacobian() << std::endl;
119  analytic_jacobian.add(1.0, _femcontext.get_elem_jacobian());
120  libMesh::out << "J_numeric " << _femcontext.get_elem().id() << " = "
121  << analytic_jacobian << std::endl;
122 
123  libMesh::out.precision(old_precision);
124 
125  libmesh_error_msg("Relative error too large, exiting!");
126  }
127  }
128 
129  const unsigned char n_sides = _femcontext.get_elem().n_sides();
130  for (_femcontext.side = 0; _femcontext.side != n_sides;
131  ++_femcontext.side)
132  {
133  // Don't compute on non-boundary sides unless requested
134  if (!_sys.get_physics()->compute_internal_sides &&
135  _femcontext.get_elem().neighbor_ptr(_femcontext.side) != nullptr)
136  continue;
137 
138  // Any mesh movement has already been done (and restored,
139  // if the TimeSolver isn't broken), but
140  // reinitializing the side FE objects is still necessary
141  _femcontext.side_fe_reinit();
142 
143  DenseMatrix<Number> old_jacobian;
144  // If we're in DEBUG mode, we should always verify that the
145  // user's side_residual function doesn't alter our existing
146  // jacobian and then lie about it
147 #ifndef DEBUG
148  // Even if we're not in DEBUG mode, when we're verifying
149  // analytic jacobians we'll want to verify each side's
150  // jacobian contribution separately.
151  if (_sys.verify_analytic_jacobians != 0.0 && need_jacobian)
152 #endif // ifndef DEBUG
153  {
154  old_jacobian = _femcontext.get_elem_jacobian();
155  _femcontext.get_elem_jacobian().zero();
156  }
157 
158  jacobian_computed =
159  _sys.time_solver->side_residual(need_jacobian, _femcontext);
160 
161  // Compute a numeric jacobian if we have to
162  if (need_jacobian && !jacobian_computed)
163  {
164  // If we have already backed up old_jacobian,
165  // we can make sure side_residual didn't compute a
166  // jacobian and lie about it.
167  //
168  // If we haven't, then we need to, to let
169  // numerical_side_jacobian work.
170  if (old_jacobian.m())
171  libmesh_assert_equal_to (_femcontext.get_elem_jacobian().l1_norm(), 0.0);
172  else
173  {
174  old_jacobian = _femcontext.get_elem_jacobian();
175  _femcontext.get_elem_jacobian().zero();
176  }
177 
178  // Logging of numerical jacobians is done separately
179  _sys.numerical_side_jacobian(_femcontext);
180 
181  // Add back in element interior numerical Jacobian
182  _femcontext.get_elem_jacobian() += old_jacobian;
183  }
184 
185  // Compute a numeric jacobian if we're asked to verify the
186  // analytic jacobian we got
187  else if (need_jacobian && jacobian_computed &&
188  _sys.verify_analytic_jacobians != 0.0)
189  {
190  DenseMatrix<Number> analytic_jacobian(_femcontext.get_elem_jacobian());
191 
192  _femcontext.get_elem_jacobian().zero();
193  // Logging of numerical jacobians is done separately
194  _sys.numerical_side_jacobian(_femcontext);
195 
196  Real analytic_norm = analytic_jacobian.l1_norm();
197  Real numerical_norm = _femcontext.get_elem_jacobian().l1_norm();
198 
199  // If we can continue, we'll probably prefer the analytic jacobian
200  analytic_jacobian.swap(_femcontext.get_elem_jacobian());
201 
202  // The matrix "analytic_jacobian" will now hold the error matrix
203  analytic_jacobian.add(-1.0, _femcontext.get_elem_jacobian());
204  Real error_norm = analytic_jacobian.l1_norm();
205 
206  Real relative_error = error_norm /
207  std::max(analytic_norm, numerical_norm);
208 
209  if (relative_error > _sys.verify_analytic_jacobians)
210  {
211  libMesh::err << "Relative error " << relative_error
212  << " detected in analytic jacobian on element "
213  << _femcontext.get_elem().id()
214  << ", side "
215  << static_cast<unsigned int>(_femcontext.side) << '!' << std::endl;
216 
217  std::streamsize old_precision = libMesh::out.precision();
219  libMesh::out << "J_analytic " << _femcontext.get_elem().id() << " = "
220  << _femcontext.get_elem_jacobian() << std::endl;
221  analytic_jacobian.add(1.0, _femcontext.get_elem_jacobian());
222  libMesh::out << "J_numeric " << _femcontext.get_elem().id() << " = "
223  << analytic_jacobian << std::endl;
224  libMesh::out.precision(old_precision);
225 
226  libmesh_error_msg("Relative error too large, exiting!");
227  }
228  // Once we've verified a side, we'll want to add back the
229  // rest of the accumulated jacobian
230  _femcontext.get_elem_jacobian() += old_jacobian;
231  }
232 
233  // In DEBUG mode, we've set elem_jacobian == 0, and we
234  // may have yet to add the old jacobian back
235 #ifdef DEBUG
236  else
237  {
238  _femcontext.get_elem_jacobian() += old_jacobian;
239  }
240 #endif // ifdef DEBUG
241  }
242 }
243 
244 void add_element_system(FEMSystem & _sys,
245  const bool _get_residual,
246  const bool _get_jacobian,
247  const bool _constrain_heterogeneously,
248  const bool _no_constraints,
249  FEMContext & _femcontext)
250 {
251 #ifdef LIBMESH_ENABLE_CONSTRAINTS
252  if (_get_residual && _sys.print_element_residuals)
253  {
254  std::streamsize old_precision = libMesh::out.precision();
256  if (_femcontext.has_elem())
257  libMesh::out << "Rraw_elem " << _femcontext.get_elem().id();
258  else
259  libMesh::out << "Rraw_scalar ";
260  libMesh::out << " = " << _femcontext.get_elem_residual() << std::endl;
261  libMesh::out.precision(old_precision);
262  }
263 
264  if (_get_jacobian && _sys.print_element_jacobians)
265  {
266  std::streamsize old_precision = libMesh::out.precision();
268  if (_femcontext.has_elem())
269  libMesh::out << "Jraw_elem " << _femcontext.get_elem().id();
270  else
271  libMesh::out << "Jraw_scalar ";
272  libMesh::out << " = " << _femcontext.get_elem_jacobian() << std::endl;
273  libMesh::out.precision(old_precision);
274  }
275 
276  // We turn off the asymmetric constraint application iff we expect
277  // enforce_constraints_exactly() to be called in the solver
278  const bool constrain_in_solver = _sys.get_constrain_in_solver();
279 
280  if (_get_residual && _get_jacobian)
281  {
282  if (constrain_in_solver)
283  {
284  if (_constrain_heterogeneously)
286  (_femcontext.get_elem_jacobian(),
287  _femcontext.get_elem_residual(),
288  _femcontext.get_dof_indices(), false);
289  else if (!_no_constraints)
291  (_femcontext.get_elem_jacobian(),
292  _femcontext.get_elem_residual(),
293  _femcontext.get_dof_indices(), false);
294  }
295  else if (!_no_constraints)
297  (_femcontext.get_elem_jacobian(),
298  _femcontext.get_elem_residual(),
299  _femcontext.get_dof_indices(),
300  *_sys.current_local_solution);
301  // Do nothing if (_no_constraints)
302  }
303  else if (_get_residual)
304  {
305  if (constrain_in_solver)
306  {
307  if (_constrain_heterogeneously)
309  (_femcontext.get_elem_jacobian(),
310  _femcontext.get_elem_residual(),
311  _femcontext.get_dof_indices(), false);
312  else if (!_no_constraints)
314  (_femcontext.get_elem_residual(),
315  _femcontext.get_dof_indices(), false);
316  }
317  else if (!_no_constraints)
319  (_femcontext.get_elem_residual(),
320  _femcontext.get_dof_indices(),
321  *_sys.current_local_solution);
322  // Do nothing if (_no_constraints)
323  }
324  else if (_get_jacobian)
325  {
326  // Heterogeneous and homogeneous constraints are the same on the
327  // matrix
328  // Only get these contribs if we are applying some constraints
329  if (!_no_constraints)
331  _femcontext.get_dof_indices(),
332  !constrain_in_solver);
333  }
334 #else
335  libmesh_ignore(_constrain_heterogeneously, _no_constraints);
336 #endif // #ifdef LIBMESH_ENABLE_CONSTRAINTS
337 
338  if (_get_residual && _sys.print_element_residuals)
339  {
340  std::streamsize old_precision = libMesh::out.precision();
342  if (_femcontext.has_elem())
343  libMesh::out << "R_elem " << _femcontext.get_elem().id();
344  else
345  libMesh::out << "R_scalar ";
346  libMesh::out << " = " << _femcontext.get_elem_residual() << std::endl;
347  libMesh::out.precision(old_precision);
348  }
349 
350  if (_get_jacobian && _sys.print_element_jacobians)
351  {
352  std::streamsize old_precision = libMesh::out.precision();
354  if (_femcontext.has_elem())
355  libMesh::out << "J_elem " << _femcontext.get_elem().id();
356  else
357  libMesh::out << "J_scalar ";
358  libMesh::out << " = " << _femcontext.get_elem_jacobian() << std::endl;
359  libMesh::out.precision(old_precision);
360  }
361 
362  { // A lock is necessary around access to the global system
363  femsystem_mutex::scoped_lock lock(assembly_mutex);
364 
365  if (_get_jacobian)
366  _sys.get_system_matrix().add_matrix (_femcontext.get_elem_jacobian(),
367  _femcontext.get_dof_indices());
368  if (_get_residual)
369  _sys.rhs->add_vector (_femcontext.get_elem_residual(),
370  _femcontext.get_dof_indices());
371  } // Scope for assembly mutex
372 }
373 
374 
375 
376 class AssemblyContributions
377 {
378 public:
382  AssemblyContributions(FEMSystem & sys,
383  bool get_residual,
384  bool get_jacobian,
385  bool constrain_heterogeneously,
386  bool no_constraints) :
387  _sys(sys),
388  _get_residual(get_residual),
389  _get_jacobian(get_jacobian),
390  _constrain_heterogeneously(constrain_heterogeneously),
391  _no_constraints(no_constraints) {}
392 
396  void operator()(const ConstElemRange & range) const
397  {
398  std::unique_ptr<DiffContext> con = _sys.build_context();
399  FEMContext & _femcontext = cast_ref<FEMContext &>(*con);
400  _sys.init_context(_femcontext);
401 
402  for (const auto & elem : range)
403  {
404  _femcontext.pre_fe_reinit(_sys, elem);
405  _femcontext.elem_fe_reinit();
406 
407  assemble_unconstrained_element_system
408  (_sys, _get_jacobian, _constrain_heterogeneously, _femcontext);
409 
410  add_element_system
411  (_sys, _get_residual, _get_jacobian,
412  _constrain_heterogeneously, _no_constraints, _femcontext);
413  }
414  }
415 
416 private:
417 
418  FEMSystem & _sys;
419 
420  const bool _get_residual, _get_jacobian, _constrain_heterogeneously, _no_constraints;
421 };
422 
423 class PostprocessContributions
424 {
425 public:
429  explicit
430  PostprocessContributions(FEMSystem & sys) : _sys(sys) {}
431 
435  void operator()(const ConstElemRange & range) const
436  {
437  std::unique_ptr<DiffContext> con = _sys.build_context();
438  FEMContext & _femcontext = cast_ref<FEMContext &>(*con);
439  _sys.init_context(_femcontext);
440 
441  for (const auto & elem : range)
442  {
443  _femcontext.pre_fe_reinit(_sys, elem);
444 
445  // Optionally initialize all the interior FE objects on elem.
447  _femcontext.elem_fe_reinit();
448 
449  _sys.element_postprocess(_femcontext);
450 
451  const unsigned char n_sides = _femcontext.get_elem().n_sides();
452  for (_femcontext.side = 0; _femcontext.side != n_sides;
453  ++_femcontext.side)
454  {
455  // Don't compute on non-boundary sides unless requested
456  if (!_sys.postprocess_sides ||
458  _femcontext.get_elem().neighbor_ptr(_femcontext.side) != nullptr))
459  continue;
460 
461  // Optionally initialize all the FE objects on this side.
463  _femcontext.side_fe_reinit();
464 
465  _sys.side_postprocess(_femcontext);
466  }
467  }
468  }
469 
470 private:
471 
472  FEMSystem & _sys;
473 };
474 
475 class QoIContributions
476 {
477 public:
481  explicit
482  QoIContributions(FEMSystem & sys,
483  DifferentiableQoI & diff_qoi,
484  const QoISet & qoi_indices) :
485  qoi(sys.n_qois(), 0.), _sys(sys), _diff_qoi(diff_qoi),_qoi_indices(qoi_indices) {}
486 
490  QoIContributions(const QoIContributions & other,
491  Threads::split) :
492  qoi(other._sys.n_qois(), 0.), _sys(other._sys), _diff_qoi(other._diff_qoi) {}
493 
497  void operator()(const ConstElemRange & range)
498  {
499  std::unique_ptr<DiffContext> con = _sys.build_context();
500  FEMContext & _femcontext = cast_ref<FEMContext &>(*con);
501  _diff_qoi.init_context(_femcontext);
502 
503  bool have_some_heterogenous_qoi_bc = false;
504 #ifdef LIBMESH_ENABLE_CONSTRAINTS
505  std::vector<bool> have_heterogenous_qoi_bc(_sys.n_qois(), false);
506  for (auto q : make_range(_sys.n_qois()))
507  if (_qoi_indices.has_index(q) &&
509  {
510  have_heterogenous_qoi_bc[q] = true;
511  have_some_heterogenous_qoi_bc = true;
512  }
513 #endif
514 
515  if (have_some_heterogenous_qoi_bc)
516  _sys.init_context(_femcontext);
517 
518  for (const auto & elem : range)
519  {
520  _femcontext.pre_fe_reinit(_sys, elem);
521 
522  // We might have some heterogenous dofs here; let's see for
523  // certain
524  bool elem_has_some_heterogenous_qoi_bc = false;
525 
526 #ifdef LIBMESH_ENABLE_CONSTRAINTS
527  const unsigned int n_dofs =
528  cast_int<unsigned int>(_femcontext.get_dof_indices().size());
529 
530  std::vector<bool> elem_has_heterogenous_qoi_bc(_sys.n_qois(), false);
531  if (have_some_heterogenous_qoi_bc)
532  {
533  for (auto q : make_range(_sys.n_qois()))
534  {
535  if (have_heterogenous_qoi_bc[q])
536  {
537  for (auto d : make_range(n_dofs))
539  (q, _femcontext.get_dof_indices()[d]) != Number(0))
540  {
541  elem_has_some_heterogenous_qoi_bc = true;
542  elem_has_heterogenous_qoi_bc[q] = true;
543  break;
544  }
545  }
546  }
547  }
548 #endif
549 
550  if (_diff_qoi.assemble_qoi_elements ||
551  elem_has_some_heterogenous_qoi_bc)
552  _femcontext.elem_fe_reinit();
553 
554  if (_diff_qoi.assemble_qoi_elements)
555  _diff_qoi.element_qoi(_femcontext, _qoi_indices);
556 
557  // If we have some heterogenous dofs here, those are
558  // themselves part of a regularized flux QoI which the library
559  // handles integrating
560 #ifdef LIBMESH_ENABLE_CONSTRAINTS
561  if (elem_has_some_heterogenous_qoi_bc)
562  {
563  _sys.time_solver->element_residual(false, _femcontext);
564 
565  for (auto q : make_range(_sys.n_qois()))
566  {
567  if (elem_has_heterogenous_qoi_bc[q])
568  {
569  for (auto d : make_range(n_dofs))
570  this->qoi[q] -= _femcontext.get_elem_residual()(d) *
572 
573  }
574  }
575  }
576 #endif
577 
578  const unsigned char n_sides = _femcontext.get_elem().n_sides();
579  for (_femcontext.side = 0; _femcontext.side != n_sides;
580  ++_femcontext.side)
581  {
582  // Don't compute on non-boundary sides unless requested
583  if (!_diff_qoi.assemble_qoi_sides ||
584  (!_diff_qoi.assemble_qoi_internal_sides &&
585  _femcontext.get_elem().neighbor_ptr(_femcontext.side) != nullptr))
586  continue;
587 
588  _femcontext.side_fe_reinit();
589 
590  _diff_qoi.side_qoi(_femcontext, _qoi_indices);
591  }
592  }
593 
594  this->_diff_qoi.thread_join( this->qoi, _femcontext.get_qois(), _qoi_indices );
595  }
596 
597  void join (const QoIContributions & other)
598  {
599  libmesh_assert_equal_to (this->qoi.size(), other.qoi.size());
600  this->_diff_qoi.thread_join( this->qoi, other.qoi, _qoi_indices );
601  }
602 
603  std::vector<Number> qoi;
604 
605 private:
606 
607  FEMSystem & _sys;
608  DifferentiableQoI & _diff_qoi;
609 
610  const QoISet _qoi_indices;
611 };
612 
613 class QoIDerivativeContributions
614 {
615 public:
619  QoIDerivativeContributions(FEMSystem & sys,
620  const QoISet & qoi_indices,
621  DifferentiableQoI & qoi,
622  bool include_liftfunc,
623  bool apply_constraints) :
624  _sys(sys),
625  _qoi_indices(qoi_indices),
626  _qoi(qoi),
627  _include_liftfunc(include_liftfunc),
628  _apply_constraints(apply_constraints) {}
629 
633  void operator()(const ConstElemRange & range) const
634  {
635  std::unique_ptr<DiffContext> con = _sys.build_context();
636  FEMContext & _femcontext = cast_ref<FEMContext &>(*con);
637  _qoi.init_context(_femcontext);
638 
639  bool have_some_heterogenous_qoi_bc = false;
640 #ifdef LIBMESH_ENABLE_CONSTRAINTS
641  std::vector<bool> have_heterogenous_qoi_bc(_sys.n_qois(), false);
642  if (_include_liftfunc || _apply_constraints)
643  for (auto q : make_range(_sys.n_qois()))
644  if (_qoi_indices.has_index(q) &&
646  {
647  have_heterogenous_qoi_bc[q] = true;
648  have_some_heterogenous_qoi_bc = true;
649  }
650 #endif
651 
652  if (have_some_heterogenous_qoi_bc)
653  _sys.init_context(_femcontext);
654 
655  for (const auto & elem : range)
656  {
657  _femcontext.pre_fe_reinit(_sys, elem);
658 
659  // We might have some heterogenous dofs here; let's see for
660  // certain
661  bool elem_has_some_heterogenous_qoi_bc = false;
662 
663 #ifdef LIBMESH_ENABLE_CONSTRAINTS
664  const unsigned int n_dofs =
665  cast_int<unsigned int>(_femcontext.get_dof_indices().size());
666 
667  std::vector<bool> elem_has_heterogenous_qoi_bc(_sys.n_qois(), false);
668  if (have_some_heterogenous_qoi_bc)
669  {
670  for (auto q : make_range(_sys.n_qois()))
671  {
672  if (have_heterogenous_qoi_bc[q])
673  {
674  for (auto d : make_range(n_dofs))
676  (q, _femcontext.get_dof_indices()[d]) != Number(0))
677  {
678  elem_has_some_heterogenous_qoi_bc = true;
679  elem_has_heterogenous_qoi_bc[q] = true;
680  break;
681  }
682  }
683  }
684  }
685 #endif
686 
687  // If we're going to call a user integral, then we need FE
688  // information to call element_qoi.
689  // If we're going to evaluate lift-function-based components
690  // of a QoI, then we need FE information to assemble the
691  // element residual.
692  if (_qoi.assemble_qoi_elements ||
693  ((_include_liftfunc || _apply_constraints) &&
694  elem_has_some_heterogenous_qoi_bc))
695  _femcontext.elem_fe_reinit();
696 
697  if (_qoi.assemble_qoi_elements)
698  _qoi.element_qoi_derivative(_femcontext, _qoi_indices);
699 
700 #ifdef LIBMESH_ENABLE_CONSTRAINTS
701  // If we need to use heterogenous dofs here, we need the
702  // Jacobian either for the regularized flux QoI integration
703  // and/or for constraint application.
704  if ((_include_liftfunc || _apply_constraints) &&
705  elem_has_some_heterogenous_qoi_bc)
706  {
707  bool jacobian_computed = _sys.time_solver->element_residual(true, _femcontext);
708 
709  // If we're using numerical jacobians, above wont compute them
710  if (!jacobian_computed)
711  {
712  // Make sure we didn't compute a jacobian and lie about it
713  libmesh_assert_equal_to (_femcontext.get_elem_jacobian().l1_norm(), 0.0);
714  // Logging of numerical jacobians is done separately
715  _sys.numerical_elem_jacobian(_femcontext);
716  }
717  }
718 
719  // If we have some heterogenous dofs here, those are
720  // themselves part of a regularized flux QoI which the library
721  // may handle integrating
722  if (_include_liftfunc && elem_has_some_heterogenous_qoi_bc)
723  {
724  for (auto q : make_range(_sys.n_qois()))
725  {
726  if (elem_has_heterogenous_qoi_bc[q])
727  {
728  for (auto i : make_range(n_dofs))
729  {
730  Number liftfunc_val =
732 
733  if (liftfunc_val != Number(0))
734  {
735  for (auto j : make_range(n_dofs))
736  _femcontext.get_qoi_derivatives()[q](j) -=
737  _femcontext.get_elem_jacobian()(i,j) *
738  liftfunc_val;
739  }
740  }
741  }
742  }
743  }
744 #endif
745 
746 
747  const unsigned char n_sides = _femcontext.get_elem().n_sides();
748  for (_femcontext.side = 0; _femcontext.side != n_sides;
749  ++_femcontext.side)
750  {
751  // Don't compute on non-boundary sides unless requested
752  if (!_qoi.assemble_qoi_sides ||
753  (!_qoi.assemble_qoi_internal_sides &&
754  _femcontext.get_elem().neighbor_ptr(_femcontext.side) != nullptr))
755  continue;
756 
757  _femcontext.side_fe_reinit();
758 
759  _qoi.side_qoi_derivative(_femcontext, _qoi_indices);
760  }
761 
762  // We need some unmodified indices to use for constraining
763  // multiple vector
764  // FIXME - there should be a DofMap::constrain_element_vectors
765  // to do this more efficiently
766 #ifdef LIBMESH_ENABLE_CONSTRAINTS
767  std::vector<dof_id_type> original_dofs = _femcontext.get_dof_indices();
768 #endif
769 
770  { // A lock is necessary around access to the global system
771  femsystem_mutex::scoped_lock lock(assembly_mutex);
772 
773 #ifdef LIBMESH_ENABLE_CONSTRAINTS
774  // We'll need to see if any heterogenous constraints apply
775  // to the QoI dofs on this element *or* to any of the dofs
776  // they depend on, so let's get those dependencies
777  if (_apply_constraints)
778  _sys.get_dof_map().constrain_nothing(_femcontext.get_dof_indices());
779 #endif
780 
781  for (auto i : make_range(_sys.n_qois()))
782  if (_qoi_indices.has_index(i))
783  {
784 #ifdef LIBMESH_ENABLE_CONSTRAINTS
785  if (_apply_constraints)
786  {
787 #ifndef NDEBUG
788  bool has_heterogenous_constraint = false;
789  for (auto d : make_range(n_dofs))
791  (i, _femcontext.get_dof_indices()[d]) != Number(0))
792  {
793  has_heterogenous_constraint = true;
794  libmesh_assert(elem_has_heterogenous_qoi_bc[i]);
795  libmesh_assert(elem_has_some_heterogenous_qoi_bc);
796  break;
797  }
798 #else
799  bool has_heterogenous_constraint =
800  elem_has_heterogenous_qoi_bc[i];
801 #endif
802 
803  _femcontext.get_dof_indices() = original_dofs;
804 
805  if (has_heterogenous_constraint)
806  {
807  // Q_u gets used for *adjoint* solves, so we
808  // need K^T here.
809  DenseMatrix<Number> elem_jacobian_transpose;
810  _femcontext.get_elem_jacobian().get_transpose
811  (elem_jacobian_transpose);
812 
814  (elem_jacobian_transpose,
815  _femcontext.get_qoi_derivatives()[i],
816  _femcontext.get_dof_indices(), false, i);
817  }
818  else
819  {
821  (_femcontext.get_qoi_derivatives()[i],
822  _femcontext.get_dof_indices(), false);
823  }
824  }
825 #endif
826 
828  (_femcontext.get_qoi_derivatives()[i], _femcontext.get_dof_indices());
829  }
830  }
831  }
832  }
833 
834 private:
835 
836  FEMSystem & _sys;
837  const QoISet & _qoi_indices;
838  DifferentiableQoI & _qoi;
839  bool _include_liftfunc, _apply_constraints;
840 };
841 
842 
843 }
844 
845 
846 namespace libMesh
847 {
848 
849 
850 
851 
852 
854  const std::string & name_in,
855  const unsigned int number_in)
856  : Parent(es, name_in, number_in),
857  fe_reinit_during_postprocess(true),
858  numerical_jacobian_h(TOLERANCE),
859  verify_analytic_jacobians(0.0)
860 {
861 }
862 
863 
864 FEMSystem::~FEMSystem () = default;
865 
866 
867 
869 {
870  // First initialize LinearImplicitSystem data
872 }
873 
874 
875 void FEMSystem::assembly (bool get_residual, bool get_jacobian,
876  bool apply_heterogeneous_constraints,
877  bool apply_no_constraints)
878 {
879  libmesh_assert(get_residual || get_jacobian);
880 
881  // Log residual and jacobian and combined performance separately
882 #ifdef LIBMESH_ENABLE_PERFORMANCE_LOGGING
883  const char * log_name;
884  if (get_residual && get_jacobian)
885  log_name = "assembly()";
886  else if (get_residual)
887  log_name = "assembly(get_residual)";
888  else
889  log_name = "assembly(get_jacobian)";
890 
891  LOG_SCOPE(log_name, "FEMSystem");
892 #endif
893 
894  const MeshBase & mesh = this->get_mesh();
895 
897  {
898  this->solution->close();
899 
900  std::streamsize old_precision = libMesh::out.precision();
902  libMesh::out << "|U| = "
903  << this->solution->l1_norm()
904  << std::endl;
905  libMesh::out.precision(old_precision);
906  }
907  if (print_solutions)
908  {
909  std::streamsize old_precision = libMesh::out.precision();
911  libMesh::out << "U = [" << *(this->solution)
912  << "];" << std::endl;
913  libMesh::out.precision(old_precision);
914  }
915 
916  // Is this definitely necessary? [RHS]
917  // Yes. [RHS 2012]
918  if (get_jacobian)
919  matrix->zero();
920  if (get_residual)
921  rhs->zero();
922 
923  // Stupid C++ lets you set *Real* verify_analytic_jacobians = true!
924  if (verify_analytic_jacobians > 0.5)
925  {
926  libMesh::err << "WARNING! verify_analytic_jacobians was set "
927  << "to absurdly large value of "
928  << verify_analytic_jacobians << std::endl;
929  libMesh::err << "Resetting to 1e-6!" << std::endl;
931  }
932 
933  // In time-dependent problems, the nonlinear function we're trying
934  // to solve at each timestep may depend on the particular solver
935  // we're using
937 
938  // Build the residual and jacobian contributions on every active
939  // mesh element on this processor
942  AssemblyContributions(*this, get_residual, get_jacobian,
943  apply_heterogeneous_constraints,
944  apply_no_constraints));
945 
946  // Check and see if we have SCALAR variables
947  bool have_scalar = false;
948  for (auto i : make_range(this->n_variable_groups()))
949  {
950  if (this->variable_group(i).type().family == SCALAR)
951  {
952  have_scalar = true;
953  break;
954  }
955  }
956 
957  // SCALAR dofs are stored on the last processor, so we'll evaluate
958  // their equation terms there and only if we have a SCALAR variable
959  if (this->processor_id() == (this->n_processors()-1) && have_scalar)
960  {
961  std::unique_ptr<DiffContext> con = this->build_context();
962  FEMContext & _femcontext = cast_ref<FEMContext &>(*con);
963  this->init_context(_femcontext);
964  _femcontext.pre_fe_reinit(*this, nullptr);
965 
966  bool jacobian_computed =
967  this->time_solver->nonlocal_residual(get_jacobian, _femcontext);
968 
969  // Nonlocal residuals are likely to be length 0, in which case we
970  // don't need to do any more. And we shouldn't try to do any
971  // more; lots of DenseVector/DenseMatrix code assumes rank>0.
972  if (_femcontext.get_elem_residual().size())
973  {
974  // Compute a numeric jacobian if we have to
975  if (get_jacobian && !jacobian_computed)
976  {
977  // Make sure we didn't compute a jacobian and lie about it
978  libmesh_assert_equal_to (_femcontext.get_elem_jacobian().l1_norm(), 0.0);
979  // Logging of numerical jacobians is done separately
980  this->numerical_nonlocal_jacobian(_femcontext);
981  }
982 
983  // Compute a numeric jacobian if we're asked to verify the
984  // analytic jacobian we got
985  if (get_jacobian && jacobian_computed &&
986  this->verify_analytic_jacobians != 0.0)
987  {
988  DenseMatrix<Number> analytic_jacobian(_femcontext.get_elem_jacobian());
989 
990  _femcontext.get_elem_jacobian().zero();
991  // Logging of numerical jacobians is done separately
992  this->numerical_nonlocal_jacobian(_femcontext);
993 
994  Real analytic_norm = analytic_jacobian.l1_norm();
995  Real numerical_norm = _femcontext.get_elem_jacobian().l1_norm();
996 
997  // If we can continue, we'll probably prefer the analytic jacobian
998  analytic_jacobian.swap(_femcontext.get_elem_jacobian());
999 
1000  // The matrix "analytic_jacobian" will now hold the error matrix
1001  analytic_jacobian.add(-1.0, _femcontext.get_elem_jacobian());
1002  Real error_norm = analytic_jacobian.l1_norm();
1003 
1004  Real relative_error = error_norm /
1005  std::max(analytic_norm, numerical_norm);
1006 
1007  if (relative_error > this->verify_analytic_jacobians)
1008  {
1009  libMesh::err << "Relative error " << relative_error
1010  << " detected in analytic jacobian on nonlocal dofs!"
1011  << std::endl;
1012 
1013  std::streamsize old_precision = libMesh::out.precision();
1014  libMesh::out.precision(16);
1015  libMesh::out << "J_analytic nonlocal = "
1016  << _femcontext.get_elem_jacobian() << std::endl;
1017  analytic_jacobian.add(1.0, _femcontext.get_elem_jacobian());
1018  libMesh::out << "J_numeric nonlocal = "
1019  << analytic_jacobian << std::endl;
1020 
1021  libMesh::out.precision(old_precision);
1022 
1023  libmesh_error_msg("Relative error too large, exiting!");
1024  }
1025  }
1026 
1027  add_element_system
1028  (*this, get_residual, get_jacobian,
1029  apply_heterogeneous_constraints, apply_no_constraints, _femcontext);
1030  }
1031  }
1032 
1033  if (get_residual && (print_residual_norms || print_residuals))
1034  this->rhs->close();
1035  if (get_residual && print_residual_norms)
1036  {
1037  std::streamsize old_precision = libMesh::out.precision();
1038  libMesh::out.precision(16);
1039  libMesh::out << "|F| = " << this->rhs->l1_norm() << std::endl;
1040  libMesh::out.precision(old_precision);
1041  }
1042  if (get_residual && print_residuals)
1043  {
1044  std::streamsize old_precision = libMesh::out.precision();
1045  libMesh::out.precision(16);
1046  libMesh::out << "F = [" << *(this->rhs) << "];" << std::endl;
1047  libMesh::out.precision(old_precision);
1048  }
1049 
1050  if (get_jacobian && (print_jacobian_norms || print_jacobians))
1051  this->matrix->close();
1052  if (get_jacobian && print_jacobian_norms)
1053  {
1054  std::streamsize old_precision = libMesh::out.precision();
1055  libMesh::out.precision(16);
1056  libMesh::out << "|J| = " << this->matrix->l1_norm() << std::endl;
1057  libMesh::out.precision(old_precision);
1058  }
1059  if (get_jacobian && print_jacobians)
1060  {
1061  std::streamsize old_precision = libMesh::out.precision();
1062  libMesh::out.precision(16);
1063  libMesh::out << "J = [" << *(this->matrix) << "];" << std::endl;
1064  libMesh::out.precision(old_precision);
1065  }
1066 }
1067 
1068 
1069 
1071 {
1072  // We are solving the primal problem
1073  Parent::solve();
1074 
1075  // On a moving mesh we want the mesh to reflect the new solution
1076  this->mesh_position_set();
1077 }
1078 
1079 
1080 
1082 {
1083  // If we don't need to move the mesh, we're done
1084  if (_mesh_sys != this)
1085  return;
1086 
1087  MeshBase & mesh = this->get_mesh();
1088 
1089  std::unique_ptr<DiffContext> con = this->build_context();
1090  FEMContext & _femcontext = cast_ref<FEMContext &>(*con);
1091  this->init_context(_femcontext);
1092 
1093  // Move every mesh element we can
1094  for (const auto & elem : mesh.active_local_element_ptr_range())
1095  {
1096  // We need the algebraic data
1097  _femcontext.pre_fe_reinit(*this, elem);
1098  // And when asserts are on, we also need the FE so
1099  // we can assert that the mesh data is of the right type.
1100 #ifndef NDEBUG
1101  _femcontext.elem_fe_reinit();
1102 #endif
1103 
1104  // This code won't handle moving subactive elements
1105  libmesh_assert(!_femcontext.get_elem().has_children());
1106 
1107  _femcontext.elem_position_set(1.);
1108  }
1109 
1110  // We've now got positions set on all local nodes (and some
1111  // semilocal nodes); let's request positions for non-local nodes
1112  // from their processors.
1113 
1114  SyncNodalPositions sync_object(mesh);
1116  (this->comm(), mesh.nodes_begin(), mesh.nodes_end(), sync_object);
1117 }
1118 
1119 
1120 
1122 {
1123  LOG_SCOPE("postprocess()", "FEMSystem");
1124 
1125  const MeshBase & mesh = this->get_mesh();
1126 
1127  this->update();
1128 
1129  // Get the time solver object associated with the system, and tell it that
1130  // we are not solving the adjoint problem
1131  this->get_time_solver().set_is_adjoint(false);
1132 
1133  // Loop over every active mesh element on this processor
1135  PostprocessContributions(*this));
1136 }
1137 
1138 
1139 
1140 void FEMSystem::assemble_qoi (const QoISet & qoi_indices)
1141 {
1142  LOG_SCOPE("assemble_qoi()", "FEMSystem");
1143 
1144  const MeshBase & mesh = this->get_mesh();
1145 
1146  this->update();
1147 
1148  const unsigned int Nq = this->n_qois();
1149 
1150  // the quantity of interest is assumed to be a sum of element and
1151  // side terms
1152  for (unsigned int i=0; i != Nq; ++i)
1153  if (qoi_indices.has_index(i))
1154  this->set_qoi(i, 0);
1155 
1156  // Create a non-temporary qoi_contributions object, so we can query
1157  // its results after the reduction
1158  QoIContributions qoi_contributions(*this, *(this->get_qoi()), qoi_indices);
1159 
1160  // Loop over every active mesh element on this processor
1162  qoi_contributions);
1163 
1164  std::vector<Number> global_qoi = this->get_qoi_values();
1165  this->get_qoi()->parallel_op( this->comm(), global_qoi, qoi_contributions.qoi, qoi_indices );
1166  this->set_qoi(std::move(global_qoi));
1167 }
1168 
1169 
1170 
1171 void FEMSystem::assemble_qoi_derivative (const QoISet & qoi_indices,
1172  bool include_liftfunc,
1173  bool apply_constraints)
1174 {
1175  LOG_SCOPE("assemble_qoi_derivative()", "FEMSystem");
1176 
1177  const MeshBase & mesh = this->get_mesh();
1178 
1179  this->update();
1180 
1181  // The quantity of interest derivative assembly accumulates on
1182  // initially zero vectors
1183  for (auto i : make_range(this->n_qois()))
1184  if (qoi_indices.has_index(i))
1185  this->add_adjoint_rhs(i).zero();
1186 
1187  // Loop over every active mesh element on this processor
1189  QoIDerivativeContributions(*this, qoi_indices,
1190  *(this->get_qoi()),
1191  include_liftfunc,
1192  apply_constraints));
1193 
1194  for (auto i : make_range(this->n_qois()))
1195  if (qoi_indices.has_index(i))
1196  this->get_qoi()->finalize_derivative(this->get_adjoint_rhs(i),i);
1197 }
1198 
1199 
1200 
1201 void FEMSystem::numerical_jacobian (TimeSolverResPtr res,
1202  FEMContext & context) const
1203 {
1204  // Logging is done by numerical_elem_jacobian
1205  // or numerical_side_jacobian
1206 
1207  DenseVector<Number> original_residual(context.get_elem_residual());
1208  DenseVector<Number> backwards_residual(context.get_elem_residual());
1209  DenseMatrix<Number> numeric_jacobian(context.get_elem_jacobian());
1210 #ifdef DEBUG
1211  DenseMatrix<Number> old_jacobian(context.get_elem_jacobian());
1212 #endif
1213 
1214  Real numerical_point_h = 0.;
1215  if (_mesh_sys == this)
1216  numerical_point_h = numerical_jacobian_h * context.get_elem().hmin();
1217 
1218  const unsigned int n_dofs =
1219  cast_int<unsigned int>(context.get_dof_indices().size());
1220 
1221  for (auto v : make_range(context.n_vars()))
1222  {
1223  const Real my_h = this->numerical_jacobian_h_for_var(v);
1224 
1225  unsigned int j_offset = libMesh::invalid_uint;
1226 
1227  if (!context.get_dof_indices(v).empty())
1228  {
1229  for (auto i : make_range(n_dofs))
1230  if (context.get_dof_indices()[i] ==
1231  context.get_dof_indices(v)[0])
1232  j_offset = i;
1233 
1234  libmesh_assert_not_equal_to(j_offset, libMesh::invalid_uint);
1235  }
1236 
1237  for (auto j : make_range(context.get_dof_indices(v).size()))
1238  {
1239  const unsigned int total_j = j + j_offset;
1240 
1241  // Take the "minus" side of a central differenced first derivative
1242  Number original_solution = context.get_elem_solution(v)(j);
1243  context.get_elem_solution(v)(j) -= my_h;
1244 
1245  // Make sure to catch any moving mesh terms
1246  Real * coord = nullptr;
1247  if (_mesh_sys == this)
1248  {
1249  if (_mesh_x_var == v)
1250  coord = &(context.get_elem().point(j)(0));
1251  else if (_mesh_y_var == v)
1252  coord = &(context.get_elem().point(j)(1));
1253  else if (_mesh_z_var == v)
1254  coord = &(context.get_elem().point(j)(2));
1255  }
1256  if (coord)
1257  {
1258  // We have enough information to scale the perturbations
1259  // here appropriately
1260  context.get_elem_solution(v)(j) = original_solution - numerical_point_h;
1261  *coord = libmesh_real(context.get_elem_solution(v)(j));
1262  }
1263 
1264  context.get_elem_residual().zero();
1265  ((*time_solver).*(res))(false, context);
1266 #ifdef DEBUG
1267  libmesh_assert_equal_to (old_jacobian, context.get_elem_jacobian());
1268 #endif
1269  backwards_residual = context.get_elem_residual();
1270 
1271  // Take the "plus" side of a central differenced first derivative
1272  context.get_elem_solution(v)(j) = original_solution + my_h;
1273  if (coord)
1274  {
1275  context.get_elem_solution()(j) = original_solution + numerical_point_h;
1276  *coord = libmesh_real(context.get_elem_solution(v)(j));
1277  }
1278  context.get_elem_residual().zero();
1279  ((*time_solver).*(res))(false, context);
1280 #ifdef DEBUG
1281  libmesh_assert_equal_to (old_jacobian, context.get_elem_jacobian());
1282 #endif
1283 
1284  context.get_elem_solution(v)(j) = original_solution;
1285  if (coord)
1286  {
1287  *coord = libmesh_real(context.get_elem_solution(v)(j));
1288  for (auto i : make_range(n_dofs))
1289  {
1290  numeric_jacobian(i,total_j) =
1291  (context.get_elem_residual()(i) - backwards_residual(i)) /
1292  2. / numerical_point_h;
1293  }
1294  }
1295  else
1296  {
1297  for (auto i : make_range(n_dofs))
1298  {
1299  numeric_jacobian(i,total_j) =
1300  (context.get_elem_residual()(i) - backwards_residual(i)) /
1301  2. / my_h;
1302  }
1303  }
1304  }
1305  }
1306 
1307  context.get_elem_residual() = original_residual;
1308  context.get_elem_jacobian() = numeric_jacobian;
1309 }
1310 
1311 
1312 
1314 {
1315  LOG_SCOPE("numerical_elem_jacobian()", "FEMSystem");
1317 }
1318 
1319 
1320 
1322 {
1323  LOG_SCOPE("numerical_side_jacobian()", "FEMSystem");
1325 }
1326 
1327 
1328 
1330 {
1331  LOG_SCOPE("numerical_nonlocal_jacobian()", "FEMSystem");
1333 }
1334 
1335 
1336 
1337 std::unique_ptr<DiffContext> FEMSystem::build_context ()
1338 {
1339  auto fc = std::make_unique<FEMContext>(*this);
1340 
1341  DifferentiablePhysics * phys = this->get_physics();
1342 
1343  libmesh_assert (phys);
1344 
1345  // If we are solving a moving mesh problem, tell that to the Context
1346  fc->set_mesh_system(phys->get_mesh_system());
1347  fc->set_mesh_x_var(phys->get_mesh_x_var());
1348  fc->set_mesh_y_var(phys->get_mesh_y_var());
1349  fc->set_mesh_z_var(phys->get_mesh_z_var());
1350 
1351  fc->set_deltat_pointer( &deltat );
1352 
1353  // If we are solving the adjoint problem, tell that to the Context
1354  fc->is_adjoint() = this->get_time_solver().is_adjoint();
1355 
1356  return fc;
1357 }
1358 
1359 
1360 
1362 {
1363  // Parent::init_context(c); // may be a good idea in derived classes
1364 
1365  // Although we do this in DiffSystem::build_context() and
1366  // FEMSystem::build_context() as well, we do it here just to be
1367  // extra sure that the deltat pointer gets set. Since the
1368  // intended behavior is for classes derived from FEMSystem to
1369  // call Parent::init_context() in their own init_context()
1370  // overloads, we can ensure that those classes get the correct
1371  // deltat pointers even if they have different build_context()
1372  // overloads.
1373  c.set_deltat_pointer ( &deltat );
1374 
1375  FEMContext & context = cast_ref<FEMContext &>(c);
1376 
1377  // Make sure we're prepared to do mass integration
1378  for (auto var : make_range(this->n_vars()))
1379  if (this->get_physics()->is_time_evolving(var))
1380  {
1381  // Request shape functions based on FEType
1382  switch( FEInterface::field_type( this->variable_type(var) ) )
1383  {
1384  case( TYPE_SCALAR ):
1385  {
1386  FEBase * elem_fe = nullptr;
1387  for (auto dim : context.elem_dimensions())
1388  {
1389  context.get_element_fe(var, elem_fe, dim);
1390  elem_fe->get_JxW();
1391  elem_fe->get_phi();
1392  }
1393  }
1394  break;
1395  case( TYPE_VECTOR ):
1396  {
1397  FEGenericBase<RealGradient> * elem_fe = nullptr;
1398  for (auto dim : context.elem_dimensions())
1399  {
1400  context.get_element_fe(var, elem_fe, dim);
1401  elem_fe->get_JxW();
1402  elem_fe->get_phi();
1403  }
1404  }
1405  break;
1406  default:
1407  libmesh_error_msg("Unrecognized field type!");
1408  }
1409  }
1410 }
1411 
1412 
1413 
1415 {
1416  // This function makes no sense unless we've already picked out some
1417  // variable(s) to reflect mesh position coordinates
1418  libmesh_error_msg_if(!_mesh_sys, "_mesh_sys was nullptr!");
1419 
1420  // We currently assume mesh variables are in our own system
1421  if (_mesh_sys != this)
1422  libmesh_not_implemented();
1423 
1424  // Loop over every active mesh element on this processor
1425  const MeshBase & mesh = this->get_mesh();
1426 
1427  std::unique_ptr<DiffContext> con = this->build_context();
1428  FEMContext & _femcontext = cast_ref<FEMContext &>(*con);
1429  this->init_context(_femcontext);
1430 
1431  // Get the solution's mesh variables from every element
1432  for (const auto & elem : mesh.active_local_element_ptr_range())
1433  {
1434  _femcontext.pre_fe_reinit(*this, elem);
1435 
1436  _femcontext.elem_position_get();
1437 
1439  this->solution->insert(_femcontext.get_elem_solution(_mesh_x_var),
1440  _femcontext.get_dof_indices(_mesh_x_var) );
1442  this->solution->insert(_femcontext.get_elem_solution(_mesh_y_var),
1443  _femcontext.get_dof_indices(_mesh_y_var));
1445  this->solution->insert(_femcontext.get_elem_solution(_mesh_z_var),
1446  _femcontext.get_dof_indices(_mesh_z_var));
1447  }
1448 
1449  this->solution->close();
1450 
1451  // And make sure the current_local_solution is up to date too
1452  this->System::update();
1453 }
1454 
1455 } // namespace libMesh
T libmesh_real(T a)
OStreamProxy err
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
virtual std::unique_ptr< DiffContext > build_context() override
Builds a FEMContext object with enough information to do evaluations on each element.
Definition: fem_system.C:1337
const DifferentiableQoI * get_qoi() const
Definition: diff_system.h:221
virtual bool side_residual(bool request_jacobian, DiffContext &)=0
This method uses the DifferentiablePhysics side_time_derivative(), side_constraint(), and side_mass_residual() to build a full residual on an element&#39;s side.
unsigned int get_mesh_x_var() const
Definition: diff_physics.h:625
This is the EquationSystems class.
const std::vector< Number > & get_qois() const
Const accessor for QoI vector.
Definition: diff_context.h:317
virtual void parallel_op(const Parallel::Communicator &communicator, std::vector< Number > &sys_qoi, std::vector< Number > &local_qoi, const QoISet &qoi_indices)
Method to populate system qoi data structure with process-local qoi.
Definition: diff_qoi.C:41
const DenseMatrix< Number > & get_elem_jacobian() const
Const accessor for element Jacobian.
Definition: diff_context.h:274
unsigned int _mesh_x_var
Variables from which to acquire moving mesh information.
Definition: diff_physics.h:536
Real verify_analytic_jacobians
If verify_analytic_jacobian is equal to zero (as it is by default), no numeric jacobians will be calc...
Definition: fem_system.h:215
void elem_position_set(Real theta)
Uses the coordinate data specified by mesh_*_position configuration to set the geometry of elem to th...
Definition: fem_context.h:1270
This class provides all data required for a physics package (e.g.
Definition: diff_context.h:55
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
const unsigned int invalid_uint
A number which is used quite often to represent an invalid or uninitialized value for an unsigned int...
Definition: libmesh.h:303
virtual void assemble_qoi_derivative(const QoISet &qoi_indices=QoISet(), bool include_liftfunc=true, bool apply_constraints=true) override
Runs a qoi derivative assembly loop over all elements, and if assemble_qoi_sides is true over all sid...
Definition: fem_system.C:1171
std::streamsize precision() const
Get the associated write precision.
void constrain_element_matrix_and_vector(DenseMatrix< Number > &matrix, DenseVector< Number > &rhs, std::vector< dof_id_type > &elem_dofs, bool asymmetric_constraint_rows=true) const
Constrains the element matrix and vector.
Definition: dof_map.h:2498
const DenseVector< Number > & get_elem_fixed_solution() const
Accessor for element fixed solution.
Definition: diff_context.h:210
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
Data structure for specifying which Quantities of Interest should be calculated in an adjoint or a pa...
Definition: qoi_set.h:45
bool print_jacobian_norms
Set print_jacobian_norms to true to print |J| whenever it is assembled.
Definition: diff_system.h:353
const Elem & get_elem() const
Accessor for current Elem object.
Definition: fem_context.h:908
unsigned int dim
unsigned int n_qois() const
Number of currently active quantities of interest.
Definition: system.h:2562
virtual void init_context(DiffContext &) override
Definition: fem_system.C:1361
Real numerical_jacobian_h
If calculating numeric jacobians is required, the FEMSystem will perturb each solution vector entry b...
Definition: fem_system.h:187
Number has_heterogenous_adjoint_constraint(const unsigned int qoi_num, const dof_id_type dof) const
Backwards compatibility with misspelling.
Definition: dof_map.h:1253
Dummy "splitting object" used to distinguish splitting constructors from copy constructors.
Definition: threads_none.h:63
virtual void add_vector(const T *v, const std::vector< numeric_index_type > &dof_indices)
Computes , where v is a pointer and each dof_indices[i] specifies where to add value v[i]...
unsigned int n_variable_groups() const
Definition: system.C:2699
std::unique_ptr< TimeSolver > time_solver
A pointer to the solver object we&#39;re going to use.
Definition: diff_system.h:245
MeshBase & mesh
const System * get_mesh_system() const
Definition: diff_physics.h:613
void numerical_elem_jacobian(FEMContext &context) const
Uses the results of multiple element_residual() calls to numerically differentiate the corresponding ...
Definition: fem_system.C:1313
static FEFieldType field_type(const FEType &fe_type)
NumericVector< Number > * rhs
The system matrix.
const Parallel::Communicator & comm() const
unsigned int m() const
bool postprocess_sides
If postprocess_sides is true (it is false by default), the postprocessing loop will loop over all sid...
Definition: diff_system.h:326
The StoredRange class defines a contiguous, divisible set of objects.
Definition: stored_range.h:54
void elem_position_get()
Uses the geometry of elem to set the coordinate data specified by mesh_*_position configuration...
Definition: fem_context.C:1526
The libMesh namespace provides an interface to certain functionality in the library.
bool print_jacobians
Set print_jacobians to true to print J whenever it is assembled.
Definition: diff_system.h:358
const MeshBase & get_mesh() const
Definition: system.h:2401
This class provides a specific system class.
Definition: fem_system.h:53
virtual bool nonlocal_residual(bool request_jacobian, DiffContext &)=0
This method uses the DifferentiablePhysics nonlocal_time_derivative(), nonlocal_constraint(), and nonlocal_mass_residual() to build a full residual of non-local terms.
dof_id_type n_dofs() const
Definition: system.C:118
This is the MeshBase class.
Definition: mesh_base.h:80
virtual void zero()=0
Set all entries to zero.
virtual void elem_fe_reinit(const std::vector< Point > *const pts=nullptr)
Reinitializes interior FE objects on the current geometric element.
Definition: fem_context.C:1476
bool print_element_residuals
Set print_element_residuals to true to print each R_elem contribution.
Definition: diff_system.h:368
void heterogenously_constrain_element_vector(const DenseMatrix< Number > &matrix, DenseVector< Number > &rhs, std::vector< dof_id_type > &elem_dofs, bool asymmetric_constraint_rows=true, int qoi_index=-1) const
Definition: dof_map.h:1432
bool print_element_solutions
Set print_element_solutions to true to print each U_elem input.
Definition: diff_system.h:363
virtual bool get_constrain_in_solver()
Definition: diff_system.h:381
bool has_index(std::size_t) const
Return whether or not this index is in the set to be calculated.
Definition: qoi_set.h:224
virtual bool element_residual(bool request_jacobian, DiffContext &)=0
This method uses the DifferentiablePhysics element_time_derivative(), element_constraint(), and mass_residual() to build a full residual on an element.
bool print_solution_norms
Set print_residual_norms to true to print |U| whenever it is used in an assembly() call...
Definition: diff_system.h:332
unsigned int get_mesh_y_var() const
Definition: diff_physics.h:631
processor_id_type n_processors() const
void set_is_adjoint(bool _is_adjoint_value)
Accessor for setting whether we need to do a primal or adjoint solve.
Definition: time_solver.h:284
void libmesh_ignore(const Args &...)
virtual void add_matrix(const DenseMatrix< T > &dm, const std::vector< numeric_index_type > &rows, const std::vector< numeric_index_type > &cols)=0
Add the full matrix dm to the SparseMatrix.
const DenseVector< Number > & get_elem_solution() const
Accessor for element solution.
Definition: diff_context.h:112
virtual void zero()=0
Set all entries to 0.
bool print_residual_norms
Set print_residual_norms to true to print |F| whenever it is assembled.
Definition: diff_system.h:343
dof_id_type id() const
Definition: dof_object.h:819
virtual void side_fe_reinit()
Reinitializes side FE objects on the current geometric element.
Definition: fem_context.C:1498
virtual void side_postprocess(DiffContext &)
Does any work that needs to be done on side of elem in a postprocessing loop.
Definition: diff_system.h:299
virtual Real hmin() const
Definition: elem.C:683
bool fe_reinit_during_postprocess
If fe_reinit_during_postprocess is true (it is true by default), FE objects will be reinit()ed with t...
Definition: fem_system.h:176
Real deltat
For time-dependent problems, this is the amount delta t to advance the solution in time...
Definition: diff_system.h:272
bool use_fixed_solution
A boolean to be set to true by systems using elem_fixed_solution, for optional use by e...
Definition: system.h:1625
bool is_adjoint() const
Accessor for querying whether we need to do a primal or adjoint solve.
Definition: time_solver.h:277
void get_transpose(DenseMatrix< T > &dest) const
Put the tranposed matrix into dest.
void numerical_nonlocal_jacobian(FEMContext &context) const
Uses the results of multiple side_residual() calls to numerically differentiate the corresponding jac...
Definition: fem_system.C:1329
System * _mesh_sys
System from which to acquire moving mesh information.
Definition: diff_physics.h:531
std::unique_ptr< NumericVector< Number > > solution
Data structure to hold solution values.
Definition: system.h:1655
bool print_residuals
Set print_residuals to true to print F whenever it is assembled.
Definition: diff_system.h:348
virtual void init_data() override
Initializes the member data fields associated with the system, so that, e.g., assemble() may be used...
Definition: fem_system.C:868
libmesh_assert(ctx)
virtual_for_inffe const std::vector< Real > & get_JxW() const
Definition: fe_abstract.h:303
This class provides all data required for a physics package (e.g.
Definition: fem_context.h:62
const ConstElemRange & active_local_element_stored_range() const
Definition: mesh_base.C:1928
FEMSystem(EquationSystems &es, const std::string &name, const unsigned int number)
Constructor.
Definition: fem_system.C:853
const std::vector< dof_id_type > & get_dof_indices() const
Accessor for element dof indices.
Definition: diff_context.h:363
void sync_dofobject_data_by_id(const Communicator &comm, const Iterator &range_begin, const Iterator &range_end, SyncFunctor &sync)
Request data about a range of ghost dofobjects uniquely identified by their id.
virtual void element_postprocess(DiffContext &)
Does any work that needs to be done on elem in a postprocessing loop.
Definition: diff_system.h:293
virtual void postprocess() override
Runs a postprocessing loop over all elements, and if postprocess_sides is true over all sides...
Definition: fem_system.C:1121
NumericVector< Number > & add_adjoint_rhs(unsigned int i=0)
Definition: system.C:1284
virtual Real l1_norm() const =0
virtual void close()=0
Calls the NumericVector&#39;s internal assembly routines, ensuring that the values are consistent across ...
bool print_solutions
Set print_solutions to true to print U whenever it is used in an assembly() call. ...
Definition: diff_system.h:338
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
const DifferentiablePhysics * get_physics() const
Definition: diff_system.h:181
void numerical_side_jacobian(FEMContext &context) const
Uses the results of multiple side_residual() calls to numerically differentiate the corresponding jac...
Definition: fem_system.C:1321
This class provides a specific system class.
Definition: diff_physics.h:76
virtual unsigned int n_sides() const =0
const Elem * neighbor_ptr(unsigned int i) const
Definition: elem.h:2612
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
This class provides a specific system class.
Definition: diff_qoi.h:52
bool compute_internal_sides
compute_internal_sides is false by default, indicating that side_* computations will only be done on ...
Definition: diff_physics.h:156
const DenseVector< Number > & get_elem_residual() const
Const accessor for element residual.
Definition: diff_context.h:242
virtual void update()
Update the local values to reflect the solution on neighboring processors.
Definition: system.C:498
virtual void init_data() override
Initializes the member data fields associated with the system, so that, e.g., assemble() may be used...
Definition: diff_system.C:94
virtual void close()=0
Calls the SparseMatrix&#39;s internal assembly routines, ensuring that the values are consistent across p...
bool has_heterogenous_adjoint_constraints(const unsigned int qoi_num) const
Backwards compatibility with misspelling.
Definition: dof_map.h:1237
virtual void assemble_qoi(const QoISet &indices=QoISet()) override
Runs a qoi assembly loop over all elements, and if assemble_qoi_sides is true over all sides...
Definition: fem_system.C:1140
const FEType & variable_type(const unsigned int i) const
Definition: system.C:2721
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
void constrain_element_vector(DenseVector< Number > &rhs, std::vector< dof_id_type > &dofs, bool asymmetric_constraint_rows=true) const
Constrains the element vector.
Definition: dof_map.h:2494
unsigned char side
Current side for side_* to examine.
Definition: fem_context.h:1013
OStreamProxy out
SparseMatrix< Number > * matrix
The system matrix.
void mesh_position_set()
Tells the FEMSystem to set the mesh nodal coordinates which should correspond to degree of freedom co...
Definition: fem_system.C:1081
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
bool print_element_jacobians
Set print_element_jacobians to true to print each J_elem contribution.
Definition: diff_system.h:373
virtual unsigned int size() const override final
Definition: dense_vector.h:104
std::unique_ptr< NumericVector< Number > > current_local_solution
All the values I need to compute my contribution to the simulation at hand.
Definition: system.h:1667
unsigned int get_mesh_z_var() const
Definition: diff_physics.h:637
void set_deltat_pointer(Real *dt)
Points the _deltat member of this class at a timestep value stored in the creating System...
Definition: diff_context.C:109
void get_element_fe(unsigned int var, FEGenericBase< OutputShape > *&fe) const
Accessor for interior finite element object for variable var for the largest dimension in the mesh...
Definition: fem_context.h:277
virtual void solve() override
Invokes the solver associated with the system.
Definition: fem_system.C:1070
void set_qoi(unsigned int qoi_index, Number qoi_value)
Definition: system.C:2176
void heterogeneously_constrain_element_residual(DenseVector< Number > &rhs, std::vector< dof_id_type > &elem_dofs, NumericVector< Number > &solution_local) const
Constrains the element residual.
virtual ~FEMSystem()
Real numerical_jacobian_h_for_var(unsigned int var_num) const
If numerical_jacobian_h_for_var(var_num) is changed from its default value (numerical_jacobian_h), the FEMSystem will perturb solution vector entries for variable var_num by that amount when calculating finite differences with respect to that variable.
Definition: fem_system.h:265
virtual void solve() override
Invokes the solver associated with the system.
Definition: diff_system.C:138
auto l1_norm() const
bool is_time_evolving(unsigned int var) const
Definition: diff_physics.h:260
void heterogeneously_constrain_element_jacobian_and_residual(DenseMatrix< Number > &matrix, DenseVector< Number > &rhs, std::vector< dof_id_type > &elem_dofs, NumericVector< Number > &solution_local) const
Constrains the element Jacobian and residual.
virtual Real l1_norm() const =0
unsigned int n_vars() const
Definition: system.C:2674
unsigned int n_vars() const
Number of variables in solution.
Definition: diff_context.h:100
void numerical_jacobian(TimeSolverResPtr res, FEMContext &context) const
Uses the results of multiple res calls to numerically differentiate the corresponding jacobian...
Definition: fem_system.C:1201
virtual void assembly(bool get_residual, bool get_jacobian, bool apply_heterogeneous_constraints=false, bool apply_no_constraints=false) override
Prepares matrix or rhs for matrix assembly.
Definition: fem_system.C:875
processor_id_type processor_id() const
void mesh_position_get()
Tells the FEMSystem to set the degree of freedom coefficients which should correspond to mesh nodal c...
Definition: fem_system.C:1414
const std::vector< DenseVector< Number > > & get_qoi_derivatives() const
Const accessor for QoI derivatives.
Definition: diff_context.h:329
bool has_elem() const
Test for current Elem object.
Definition: fem_context.h:902
const DofMap & get_dof_map() const
Definition: system.h:2417
const Point & point(const unsigned int i) const
Definition: elem.h:2459
const SparseMatrix< Number > & get_system_matrix() const
bool has_children() const
Definition: elem.h:2993
const VariableGroup & variable_group(unsigned int vg) const
Return a constant reference to VariableGroup vg.
Definition: system.C:2709
This class forms the foundation from which generic finite elements may be derived.
void constrain_nothing(std::vector< dof_id_type > &dofs) const
Does not actually constrain anything, but modifies dofs in the same way as any of the constrain funct...
Definition: dof_map.h:2516
const std::set< unsigned char > & elem_dimensions() const
Definition: fem_context.h:951
std::enable_if< ScalarTraits< T2 >::value, void >::type add(const T2 factor, const DenseMatrix< T3 > &mat)
Adds factor times mat to this matrix.
virtual void finalize_derivative(NumericVector< Number > &derivatives, std::size_t qoi_index)
Method to finalize qoi derivatives which require more than just a simple sum of element contributions...
Definition: diff_qoi.C:53
std::vector< Number > get_qoi_values() const
Returns a copy of qoi, not a reference.
Definition: system.C:2191
const std::vector< std::vector< OutputShape > > & get_phi() const
Definition: fe_base.h:207
TimeSolver & get_time_solver()
Definition: diff_system.h:448
const FEType & type() const
Definition: variable.h:144
NumericVector< Number > & get_adjoint_rhs(unsigned int i=0)
Definition: system.C:1294
void constrain_element_matrix(DenseMatrix< Number > &matrix, std::vector< dof_id_type > &elem_dofs, bool asymmetric_constraint_rows=true) const
Constrains the element matrix.
Definition: dof_map.h:2485
Manages consistently variables, degrees of freedom, coefficient vectors, and matrices for implicit sy...