libMesh
Loading...
Searching...
No Matches
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
37namespace {
38using namespace libMesh;
39
40typedef Threads::spin_mutex femsystem_mutex;
41femsystem_mutex assembly_mutex;
42
43void assemble_unconstrained_element_system(const FEMSystem & _sys,
44 const bool _get_jacobian,
45 const bool _constrain_heterogeneously,
46 FEMContext & _femcontext)
47{
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
244void 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(),
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(),
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
376class AssemblyContributions
377{
378public:
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
416private:
417
418 FEMSystem & _sys;
419
420 const bool _get_residual, _get_jacobian, _constrain_heterogeneously, _no_constraints;
421};
422
423class PostprocessContributions
424{
425public:
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
470private:
471
472 FEMSystem & _sys;
473};
474
475class QoIContributions
476{
477public:
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,
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) &&
508 _sys.get_dof_map().has_heterogenous_adjoint_constraints(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))
538 if (_sys.get_dof_map().has_heterogenous_adjoint_constraint
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) *
571 _sys.get_dof_map().has_heterogenous_adjoint_constraint(q, _femcontext.get_dof_indices()[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
605private:
606
607 FEMSystem & _sys;
608 DifferentiableQoI & _diff_qoi;
609
610 const QoISet _qoi_indices;
611};
612
613class QoIDerivativeContributions
614{
615public:
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) &&
645 _sys.get_dof_map().has_heterogenous_adjoint_constraints(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))
675 if (_sys.get_dof_map().has_heterogenous_adjoint_constraint
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))
790 if (_sys.get_dof_map().has_heterogenous_adjoint_constraint
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
834private:
835
836 FEMSystem & _sys;
837 const QoISet & _qoi_indices;
838 DifferentiableQoI & _qoi;
839 bool _include_liftfunc, _apply_constraints;
840};
841
842
843}
844
845
846namespace 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
864FEMSystem::~FEMSystem () = default;
865
866
867
869{
870 // First initialize LinearImplicitSystem data
872}
873
874
875void 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!
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();
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();
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();
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();
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();
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
1140void 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
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
1201void 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
1337std::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.
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
unsigned int n_vars
unsigned int dim
std::streamsize precision() const
Get the associated write precision.
Defines a dense matrix for use in Finite Element-type computations.
void get_transpose(DenseMatrix< T > &dest) const
Put the tranposed matrix into dest.
void swap(DenseMatrix< T > &other_matrix)
STL-like swap method.
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 zero() override final
Sets all elements of the matrix to 0 and resets any decomposition flag which may have been previously...
Defines a dense vector for use in Finite Element-type computations.
virtual void zero() override final
Set every element in the vector to 0.
virtual unsigned int size() const override final
This class provides all data required for a physics package (e.g.
const std::vector< Number > & get_qois() const
Const accessor for QoI vector.
unsigned int n_vars() const
Number of variables in solution.
const std::vector< dof_id_type > & get_dof_indices() const
Accessor for element dof indices.
const DenseVector< Number > & get_elem_solution() const
Accessor for element solution.
void set_deltat_pointer(Real *dt)
Points the _deltat member of this class at a timestep value stored in the creating System,...
const DenseVector< Number > & get_elem_fixed_solution() const
Accessor for element fixed solution.
const std::vector< DenseVector< Number > > & get_qoi_derivatives() const
Const accessor for QoI derivatives.
const DenseVector< Number > & get_elem_residual() const
Const accessor for element residual.
const DenseMatrix< Number > & get_elem_jacobian() const
Const accessor for element Jacobian.
This class provides a specific system class.
unsigned int get_mesh_y_var() const
unsigned int get_mesh_x_var() const
const System * get_mesh_system() const
unsigned int _mesh_x_var
Variables from which to acquire moving mesh information.
bool is_time_evolving(unsigned int var) const
bool compute_internal_sides
compute_internal_sides is false by default, indicating that side_* computations will only be done on ...
unsigned int get_mesh_z_var() const
System * _mesh_sys
System from which to acquire moving mesh information.
This class provides a specific system class.
Definition diff_qoi.h:53
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
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
This class provides a specific system class.
Definition diff_system.h:57
bool print_jacobians
Set print_jacobians to true to print J whenever it is assembled.
virtual bool get_constrain_in_solver()
const DifferentiablePhysics * get_physics() const
bool postprocess_sides
If postprocess_sides is true (it is false by default), the postprocessing loop will loop over all sid...
bool print_residuals
Set print_residuals to true to print F whenever it is assembled.
Real deltat
For time-dependent problems, this is the amount delta t to advance the solution in time.
bool print_element_jacobians
Set print_element_jacobians to true to print each J_elem contribution.
bool print_solution_norms
Set print_residual_norms to true to print |U| whenever it is used in an assembly() call.
virtual void element_postprocess(DiffContext &)
Does any work that needs to be done on elem in a postprocessing loop.
bool print_solutions
Set print_solutions to true to print U whenever it is used in an assembly() call.
bool print_element_solutions
Set print_element_solutions to true to print each U_elem input.
bool print_residual_norms
Set print_residual_norms to true to print |F| whenever it is assembled.
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
bool print_element_residuals
Set print_element_residuals to true to print each R_elem contribution.
bool print_jacobian_norms
Set print_jacobian_norms to true to print |J| whenever it is assembled.
std::unique_ptr< TimeSolver > time_solver
A pointer to the solver object we're going to use.
const DifferentiableQoI * get_qoi() const
virtual void solve() override
Invokes the solver associated with the system.
virtual void side_postprocess(DiffContext &)
Does any work that needs to be done on side of elem in a postprocessing loop.
void heterogeneously_constrain_element_residual(DenseVector< Number > &rhs, std::vector< dof_id_type > &elem_dofs, NumericVector< Number > &solution_local) const
Constrains the element residual.
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.
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
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
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
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
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
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
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
dof_id_type id() const
Definition dof_object.h:819
const Point & point(const unsigned int i) const
Definition elem.h:2462
bool has_children() const
Definition elem.h:2996
virtual Real hmin() const
Definition elem.C:683
virtual unsigned int n_sides() const =0
const Elem * neighbor_ptr(unsigned int i) const
Definition elem.h:2615
This is the EquationSystems class.
NumericVector< Number > * rhs
The system matrix.
virtual_for_inffe const std::vector< Real > & get_JxW() const
This class forms the foundation from which generic finite elements may be derived.
Definition fe_base.h:86
const std::vector< std::vector< OutputShape > > & get_phi() const
Definition fe_base.h:207
static FEFieldType field_type(const FEType &fe_type)
This class provides all data required for a physics package (e.g.
Definition fem_context.h:63
virtual void side_fe_reinit()
Reinitializes side FE objects on the current geometric element.
const std::set< unsigned char > & elem_dimensions() const
virtual void pre_fe_reinit(const System &, const Elem *e)
Reinitializes local data vectors/matrices on the current geometric element.
unsigned char side
Current side for side_* to examine.
const Elem & get_elem() const
Accessor for current Elem object.
void elem_position_get()
Uses the geometry of elem to set the coordinate data specified by mesh_*_position configuration.
void elem_position_set(Real theta)
Uses the coordinate data specified by mesh_*_position configuration to set the geometry of elem to th...
bool has_elem() const
Test for current Elem object.
virtual void elem_fe_reinit(const std::vector< Point > *const pts=nullptr)
Reinitializes interior FE objects on the current geometric element.
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.
This class provides a specific system class.
Definition fem_system.h:55
virtual void solve() override
Invokes the solver associated with the system.
virtual ~FEMSystem()
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
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.
void numerical_side_jacobian(FEMContext &context) const
Uses the results of multiple side_residual() calls to numerically differentiate the corresponding jac...
Real numerical_jacobian_h
If calculating numeric jacobians is required, the FEMSystem will perturb each solution vector entry b...
Definition fem_system.h:187
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
void mesh_position_get()
Tells the FEMSystem to set the degree of freedom coefficients which should correspond to mesh nodal c...
void numerical_elem_jacobian(FEMContext &context) const
Uses the results of multiple element_residual() calls to numerically differentiate the corresponding ...
virtual std::unique_ptr< DiffContext > build_context() override
Builds a FEMContext object with enough information to do evaluations on each element.
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
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),...
Definition fem_system.h:265
void mesh_position_set()
Tells the FEMSystem to set the mesh nodal coordinates which should correspond to degree of freedom co...
FEMSystem(EquationSystems &es, const std::string &name, const unsigned int number)
Constructor.
Definition fem_system.C:853
void numerical_nonlocal_jacobian(FEMContext &context) const
Uses the results of multiple side_residual() calls to numerically differentiate the corresponding jac...
void numerical_jacobian(TimeSolverResPtr res, FEMContext &context) const
Uses the results of multiple res calls to numerically differentiate the corresponding jacobian.
virtual void postprocess() override
Runs a postprocessing loop over all elements, and if postprocess_sides is true over all sides.
virtual void init_context(DiffContext &) override
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...
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
FEFamily family
The type of finite element.
Definition fe_type.h:228
const SparseMatrix< Number > & get_system_matrix() const
SparseMatrix< Number > * matrix
The system matrix.
This is the MeshBase class.
Definition mesh_base.h:81
const ConstElemRange & active_local_element_stored_range() const
Definition mesh_base.C:1954
virtual Real l1_norm() const =0
virtual void close()=0
Calls the NumericVector's internal assembly routines, ensuring that the values are consistent across ...
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].
virtual void zero()=0
Set all entries to zero.
processor_id_type processor_id() const
const Parallel::Communicator & comm() const
processor_id_type n_processors() const
Data structure for specifying which Quantities of Interest should be calculated in an adjoint or a pa...
Definition qoi_set.h:46
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 Real l1_norm() const =0
virtual void close()=0
Calls the SparseMatrix's internal assembly routines, ensuring that the values are consistent across p...
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.
virtual void zero()=0
Set all entries to 0.
The StoredRange class defines a contiguous, divisible set of objects.
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
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
void set_qoi(unsigned int qoi_index, Number qoi_value)
Definition system.C:2176
dof_id_type n_dofs() const
Definition system.C:118
const FEType & variable_type(const unsigned int i) const
Definition system.C:2721
std::vector< Number > get_qoi_values() const
Returns a copy of qoi, not a reference.
Definition system.C:2191
std::unique_ptr< NumericVector< Number > > solution
Data structure to hold solution values.
Definition system.h:1655
const VariableGroup & variable_group(unsigned int vg) const
Return a constant reference to VariableGroup vg.
Definition system.C:2709
unsigned int n_qois() const
Number of currently active quantities of interest.
Definition system.h:2562
NumericVector< Number > & add_adjoint_rhs(unsigned int i=0)
Definition system.C:1284
virtual void update()
Update the local values to reflect the solution on neighboring processors.
Definition system.C:498
unsigned int n_variable_groups() const
Definition system.C:2699
const DofMap & get_dof_map() const
Definition system.h:2417
const MeshBase & get_mesh() const
Definition system.h:2401
NumericVector< Number > & get_adjoint_rhs(unsigned int i=0)
Definition system.C:1294
Dummy "splitting object" used to distinguish splitting constructors from copy constructors.
void set_is_adjoint(bool _is_adjoint_value)
Accessor for setting whether we need to do a primal or adjoint solve.
bool is_adjoint() const
Accessor for querying whether we need to do a primal or adjoint solve.
virtual bool nonlocal_residual(bool request_jacobian, DiffContext &)=0
This method uses the DifferentiablePhysics nonlocal_time_derivative(), nonlocal_constraint(),...
virtual bool element_residual(bool request_jacobian, DiffContext &)=0
This method uses the DifferentiablePhysics element_time_derivative(), element_constraint(),...
virtual bool side_residual(bool request_jacobian, DiffContext &)=0
This method uses the DifferentiablePhysics side_time_derivative(), side_constraint(),...
const FEType & type() const
Definition variable.h:144
MeshBase & mesh
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.
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.
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.
The libMesh namespace provides an interface to certain functionality in the library.
OStreamProxy err
void libmesh_ignore(const Args &...)
T libmesh_real(T a)
libmesh_assert(ctx)
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
OStreamProxy out
static constexpr Real TOLERANCE
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
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