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