libMesh
Loading...
Searching...
No Matches
implicit_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
20// Local includes
21#include "libmesh/dof_map.h"
22#include "libmesh/equation_systems.h"
23#include "libmesh/implicit_system.h"
24#include "libmesh/int_range.h"
25#include "libmesh/libmesh_logging.h"
26#include "libmesh/linear_solver.h"
27#include "libmesh/mesh_base.h"
28#include "libmesh/numeric_vector.h"
29#include "libmesh/parameters.h"
30#include "libmesh/parameter_vector.h"
31#include "libmesh/qoi_set.h"
32#include "libmesh/sensitivity_data.h"
33#include "libmesh/sparse_matrix.h"
34#include "libmesh/diagonal_matrix.h"
35#include "libmesh/utility.h"
36#include "libmesh/static_condensation.h"
37#include "libmesh/static_condensation_preconditioner.h"
38#include "libmesh/nonlinear_solver.h"
39
40namespace libMesh
41{
42
44 const std::string & name_in,
45 const unsigned int number_in) :
46
47 Parent (es, name_in, number_in),
48 matrix (nullptr),
49 zero_out_matrix_and_rhs(true)
50{
51 if (this->has_static_condensation())
53}
54
56
58{
59 auto sc_system_matrix = std::make_unique<StaticCondensation>(this->get_mesh(), *this, this->get_dof_map(), this->get_dof_map().get_static_condensation());
60 _sc_system_matrix = sc_system_matrix.get();
61 matrix = &(this->add_matrix ("System Matrix", std::move(sc_system_matrix)));
62}
63
69
70template <typename T>
76
77
79{
80 // clear the parent data
82
83 // Restore us to a "basic" state
84 matrix = nullptr;
85 _sc_system_matrix = nullptr;
86
87 // But our "basic" state may still have a StaticCondensation
88 if (this->has_static_condensation())
90}
91
92
93
95{
100
102 {
103 matrix->zero ();
104 rhs->zero ();
105 }
106
107 // Call the base class assemble function
109}
110
111
112
114{
116
117 // Possible that we cleared the _matrices but
118 // forgot to update the matrix pointer?
119 if (this->n_matrices() == 0)
120 matrix = nullptr;
121
122 // Only need to add the matrix if it isn't there
123 // already!
124 if (matrix == nullptr)
125 matrix = &(this->add_matrix ("System Matrix"));
126
128}
129
130
131
136
137
138
139std::pair<unsigned int, Real>
141{
142 // Log how long the linear solve takes.
143 LOG_SCOPE("sensitivity_solve()", "ImplicitSystem");
144
145 // The forward system should now already be solved.
146 // Now assemble the corresponding sensitivity system.
147
148 if (this->assemble_before_solve)
149 {
150 // Build the Jacobian
151 this->assembly(false, true);
152 this->matrix->close();
153
154 // Reset and build the RHS from the residual derivatives
155 this->assemble_residual_derivatives(parameters_vec);
156 }
157
158 // The sensitivity problem is linear
159 LinearSolver<Number> * solver = this->get_linear_solver();
160
161 // Our iteration counts and residuals will be sums of the individual
162 // results
163 std::pair<unsigned int, Real> solver_params =
165 std::pair<unsigned int, Real> totalrval = std::make_pair(0,0.0);
166
167 // Solve the linear system.
168 SparseMatrix<Number> * pc = this->request_matrix("Preconditioner");
169 for (auto p : make_range(parameters_vec.size()))
170 {
171 std::pair<unsigned int, Real> rval =
172 solver->solve (*matrix, pc,
174 this->get_sensitivity_rhs(p),
175 double(solver_params.second),
176 solver_params.first);
177
178 totalrval.first += rval.first;
179 totalrval.second += rval.second;
180 }
181
182 // The linear solver may not have fit our constraints exactly
183#ifdef LIBMESH_ENABLE_CONSTRAINTS
184 for (auto p : make_range(parameters_vec.size()))
186 (*this, &this->get_sensitivity_solution(p),
187 /* homogeneous = */ true);
188#endif
189
190 return totalrval;
191}
192
193
194
195std::pair<unsigned int, Real>
197{
198 // Log how long the linear solve takes.
199 LOG_SCOPE("adjoint_solve()", "ImplicitSystem");
200
201 if (this->assemble_before_solve)
202 // Assemble the linear system
203 this->assembly (/* get_residual = */ false,
204 /* get_jacobian = */ true);
205
206 // The adjoint problem is linear
207 LinearSolver<Number> * solver = this->get_linear_solver();
208
209 // Reset and build the RHS from the QOI derivative
210 this->assemble_qoi_derivative(qoi_indices,
211 /* include_liftfunc = */ false,
212 /* apply_constraints = */ true);
213
214 // Our iteration counts and residuals will be sums of the individual
215 // results
216 std::pair<unsigned int, Real> solver_params =
218 std::pair<unsigned int, Real> totalrval = std::make_pair(0,0.0);
219
220 for (auto i : make_range(this->n_qois()))
221 if (qoi_indices.has_index(i))
222 {
223 const std::pair<unsigned int, Real> rval =
224 solver->adjoint_solve (*matrix, this->add_adjoint_solution(i),
225 this->get_adjoint_rhs(i),
226 double(solver_params.second),
227 solver_params.first);
228
229 totalrval.first += rval.first;
230 totalrval.second += rval.second;
231 }
232
233 // The linear solver may not have fit our constraints exactly
234#ifdef LIBMESH_ENABLE_CONSTRAINTS
235 for (auto i : make_range(this->n_qois()))
236 if (qoi_indices.has_index(i))
238 (this->get_adjoint_solution(i), i);
239#endif
240
241 return totalrval;
242}
243
244
245
246std::pair<unsigned int, Real>
248 const ParameterVector & weights,
249 const QoISet & qoi_indices)
250{
251 // Log how long the linear solve takes.
252 LOG_SCOPE("weighted_sensitivity_adjoint_solve()", "ImplicitSystem");
253
254 // We currently get partial derivatives via central differencing
255 const Real delta_p = TOLERANCE;
256
257 ParameterVector & parameters_vec =
258 const_cast<ParameterVector &>(parameters_in);
259
260 // The forward system should now already be solved.
261 // The adjoint system should now already be solved.
262 // Now we're assembling a weighted sum of adjoint-adjoint systems:
263 //
264 // dR/du (u, sum_l(w_l*z^l)) = sum_l(w_l*(Q''_ul - R''_ul (u, z)))
265
266 // FIXME: The derivation here does not yet take adjoint boundary
267 // conditions into account.
268#ifdef LIBMESH_ENABLE_DIRICHLET
269 for (auto i : make_range(this->n_qois()))
270 if (qoi_indices.has_index(i))
272#endif
273
274 // We'll assemble the rhs first, because the R'' term will require
275 // perturbing the jacobian
276
277 // We'll use temporary rhs vectors, because we haven't (yet) found
278 // any good reasons why users might want to save these:
279
280 std::vector<std::unique_ptr<NumericVector<Number>>> temprhs(this->n_qois());
281 for (auto i : make_range(this->n_qois()))
282 if (qoi_indices.has_index(i))
283 temprhs[i] = this->rhs->zero_clone();
284
285 // We approximate the _l partial derivatives via a central
286 // differencing perturbation in the w_l direction:
287 //
288 // sum_l(w_l*v_l) ~= (v(p + dp*w_l*e_l) - v(p - dp*w_l*e_l))/(2*dp)
289
290 // PETSc doesn't implement SGEMX, so neither does NumericVector,
291 // so we want to avoid calculating f -= R'*z. We'll thus evaluate
292 // the above equation by first adding -v(p+dp...), then multiplying
293 // the intermediate result vectors by -1, then adding -v(p-dp...),
294 // then finally dividing by 2*dp.
295
296 ParameterVector oldparameters, parameterperturbation;
297 parameters_vec.deep_copy(oldparameters);
298 weights.deep_copy(parameterperturbation);
299 parameterperturbation *= delta_p;
300 parameters_vec += parameterperturbation;
301
302 this->assembly(false, true);
303 this->matrix->close();
304
305 // Take the discrete adjoint, so that we can calculate R_u(u,z) with
306 // a matrix-vector product of R_u and z.
308
309 this->assemble_qoi_derivative(qoi_indices,
310 /* include_liftfunc = */ false,
311 /* apply_constraints = */ true);
312 for (auto i : make_range(this->n_qois()))
313 if (qoi_indices.has_index(i))
314 {
315 this->get_adjoint_rhs(i).close();
316 *(temprhs[i]) -= this->get_adjoint_rhs(i);
317 this->matrix->vector_mult_add(*(temprhs[i]), this->get_adjoint_solution(i));
318 *(temprhs[i]) *= -1.0;
319 }
320
321 oldparameters.value_copy(parameters_vec);
322 parameterperturbation *= -1.0;
323 parameters_vec += parameterperturbation;
324
325 this->assembly(false, true);
326 this->matrix->close();
328
329 this->assemble_qoi_derivative(qoi_indices,
330 /* include_liftfunc = */ false,
331 /* apply_constraints = */ true);
332 for (auto i : make_range(this->n_qois()))
333 if (qoi_indices.has_index(i))
334 {
335 this->get_adjoint_rhs(i).close();
336 *(temprhs[i]) -= this->get_adjoint_rhs(i);
337 this->matrix->vector_mult_add(*(temprhs[i]), this->get_adjoint_solution(i));
338 *(temprhs[i]) /= (2.0*delta_p);
339 }
340
341 // Finally, assemble the jacobian at the non-perturbed parameter
342 // values. Ignore assemble_before_solve; if we had a good
343 // non-perturbed matrix before we've already overwritten it.
344 oldparameters.value_copy(parameters_vec);
345
346 // if (this->assemble_before_solve)
347 {
348 // Build the Jacobian
349 this->assembly(false, true);
350 this->matrix->close();
351
352 // Take the discrete adjoint
354 }
355
356 // The weighted adjoint-adjoint problem is linear
357 LinearSolver<Number> * solver = this->get_linear_solver();
358
359 // Our iteration counts and residuals will be sums of the individual
360 // results
361 std::pair<unsigned int, Real> solver_params =
363 std::pair<unsigned int, Real> totalrval = std::make_pair(0,0.0);
364
365 for (auto i : make_range(this->n_qois()))
366 if (qoi_indices.has_index(i))
367 {
368 const std::pair<unsigned int, Real> rval =
370 *(temprhs[i]),
371 double(solver_params.second),
372 solver_params.first);
373
374 totalrval.first += rval.first;
375 totalrval.second += rval.second;
376 }
377
378 // The linear solver may not have fit our constraints exactly
379#ifdef LIBMESH_ENABLE_CONSTRAINTS
380 for (auto i : make_range(this->n_qois()))
381 if (qoi_indices.has_index(i))
384 /* homogeneous = */ true);
385#endif
386
387 return totalrval;
388}
389
390
391
392std::pair<unsigned int, Real>
394 const ParameterVector & weights)
395{
396 // Log how long the linear solve takes.
397 LOG_SCOPE("weighted_sensitivity_solve()", "ImplicitSystem");
398
399 // We currently get partial derivatives via central differencing
400 const Real delta_p = TOLERANCE;
401
402 ParameterVector & parameters_vec =
403 const_cast<ParameterVector &>(parameters_in);
404
405 // The forward system should now already be solved.
406
407 // Now we're assembling a weighted sum of sensitivity systems:
408 //
409 // dR/du (u, v)(sum(w_l*u'_l)) = -sum_l(w_l*R'_l (u, v)) forall v
410
411 // We'll assemble the rhs first, because the R' term will require
412 // perturbing the system, and some applications may not be able to
413 // assemble a perturbed residual without simultaneously constructing
414 // a perturbed jacobian.
415
416 // We approximate the _l partial derivatives via a central
417 // differencing perturbation in the w_l direction:
418 //
419 // sum_l(w_l*v_l) ~= (v(p + dp*w_l*e_l) - v(p - dp*w_l*e_l))/(2*dp)
420
421 ParameterVector oldparameters, parameterperturbation;
422 parameters_vec.deep_copy(oldparameters);
423 weights.deep_copy(parameterperturbation);
424 parameterperturbation *= delta_p;
425 parameters_vec += parameterperturbation;
426
427 this->assembly(true, false, true);
428 this->rhs->close();
429
430 std::unique_ptr<NumericVector<Number>> temprhs = this->rhs->clone();
431
432 oldparameters.value_copy(parameters_vec);
433 parameterperturbation *= -1.0;
434 parameters_vec += parameterperturbation;
435
436 this->assembly(true, false, true);
437 this->rhs->close();
438
439 *temprhs -= *(this->rhs);
440 *temprhs /= (2.0*delta_p);
441
442 // Finally, assemble the jacobian at the non-perturbed parameter
443 // values
444 oldparameters.value_copy(parameters_vec);
445
446 // Build the Jacobian
447 this->assembly(false, true);
448 this->matrix->close();
449
450 // The weighted sensitivity problem is linear
451 LinearSolver<Number> * solver = this->get_linear_solver();
452
453 std::pair<unsigned int, Real> solver_params =
455
456 const std::pair<unsigned int, Real> rval =
458 *temprhs,
459 double(solver_params.second),
460 solver_params.first);
461
462 // The linear solver may not have fit our constraints exactly
463#ifdef LIBMESH_ENABLE_CONSTRAINTS
465 (*this, &this->get_weighted_sensitivity_solution(),
466 /* homogeneous = */ true);
467#endif
468
469 return rval;
470}
471
472
473
475{
476 ParameterVector & parameters_vec =
477 const_cast<ParameterVector &>(parameters_in);
478
479 const unsigned int Np = cast_int<unsigned int>
480 (parameters_vec.size());
481
482 for (unsigned int p=0; p != Np; ++p)
483 {
484 NumericVector<Number> & sensitivity_rhs = this->add_sensitivity_rhs(p);
485
486 // Approximate -(partial R / partial p) by
487 // (R(p-dp) - R(p+dp)) / (2*dp)
488
489 Number old_parameter = *parameters_vec[p];
490
491 const Real delta_p =
492 TOLERANCE * std::max(std::abs(old_parameter), 1e-3);
493
494 *parameters_vec[p] -= delta_p;
495
496 // this->assembly(true, false, true);
497 this->assembly(true, false, false);
498 this->rhs->close();
499 sensitivity_rhs = *this->rhs;
500
501 *parameters_vec[p] = old_parameter + delta_p;
502
503 // this->assembly(true, false, true);
504 this->assembly(true, false, false);
505 this->rhs->close();
506
507 sensitivity_rhs -= *this->rhs;
508 sensitivity_rhs /= (2*delta_p);
509 sensitivity_rhs.close();
510
511 *parameters_vec[p] = old_parameter;
512 }
513}
514
515
516
518 const ParameterVector & parameters_in,
519 SensitivityData & sensitivities)
520{
521 ParameterVector & parameters_vec =
522 const_cast<ParameterVector &>(parameters_in);
523
524 const unsigned int Np = cast_int<unsigned int>
525 (parameters_vec.size());
526 const unsigned int Nq = this->n_qois();
527
528 // An introduction to the problem:
529 //
530 // Residual R(u(p),p) = 0
531 // partial R / partial u = J = system matrix
532 //
533 // This implies that:
534 // d/dp(R) = 0
535 // (partial R / partial p) +
536 // (partial R / partial u) * (partial u / partial p) = 0
537
538 // We first do an adjoint solve:
539 // J^T * z = (partial q / partial u)
540 // if we haven't already or dont have an initial condition for the adjoint
541 if (!this->is_adjoint_already_solved())
542 {
543 this->adjoint_solve(qoi_indices);
544 }
545
546 this->assemble_residual_derivatives(parameters_in);
547
548 // Get ready to fill in sensitivities:
549 sensitivities.allocate_data(qoi_indices, *this, parameters_vec);
550
551 // We use the identities:
552 // dq/dp = (partial q / partial p) + (partial q / partial u) *
553 // (partial u / partial p)
554 // dq/dp = (partial q / partial p) + (J^T * z) *
555 // (partial u / partial p)
556 // dq/dp = (partial q / partial p) + z * J *
557 // (partial u / partial p)
558
559 // Leading to our final formula:
560 // dq/dp = (partial q / partial p) - z * (partial R / partial p)
561
562 // In the case of adjoints with heterogenous Dirichlet boundary
563 // function phi, where
564 // q := S(u) - R(u,phi)
565 // the final formula works out to:
566 // dq/dp = (partial S / partial p) - z * (partial R / partial p)
567 // Because we currently have no direct access to
568 // (partial S / partial p), we use the identity
569 // (partial S / partial p) = (partial q / partial p) +
570 // phi * (partial R / partial p)
571 // to derive an equivalent equation:
572 // dq/dp = (partial q / partial p) - (z-phi) * (partial R / partial p)
573
574 // Since z-phi degrees of freedom are zero for constrained indices,
575 // we can use the same constrained -(partial R / partial p) that we
576 // use for forward sensitivity solves, taking into account the
577 // differing sign convention.
578 //
579 // Since that vector is constrained, its constrained indices are
580 // zero, so its product with phi is zero, so we can neglect the
581 // evaluation of phi terms.
582
583 for (unsigned int j=0; j != Np; ++j)
584 {
585 // We currently get partial derivatives via central differencing
586
587 // (partial q / partial p) ~= (q(p+dp)-q(p-dp))/(2*dp)
588 // (partial R / partial p) ~= (rhs(p+dp) - rhs(p-dp))/(2*dp)
589
590 Number old_parameter = *parameters_vec[j];
591
592 const Real delta_p =
593 TOLERANCE * std::max(std::abs(old_parameter), 1e-3);
594
595 *parameters_vec[j] = old_parameter - delta_p;
596 this->assemble_qoi(qoi_indices);
597 const std::vector<Number> qoi_minus = this->get_qoi_values();
598
599 NumericVector<Number> & neg_partialR_partialp = this->get_sensitivity_rhs(j);
600
601 *parameters_vec[j] = old_parameter + delta_p;
602 this->assemble_qoi(qoi_indices);
603 const std::vector<Number> qoi_plus = this->get_qoi_values();
604
605 std::vector<Number> partialq_partialp(Nq, 0);
606 for (unsigned int i=0; i != Nq; ++i)
607 if (qoi_indices.has_index(i))
608 partialq_partialp[i] = (qoi_plus[i] - qoi_minus[i]) / (2.*delta_p);
609
610 // Don't leave the parameter changed
611 *parameters_vec[j] = old_parameter;
612
613 for (unsigned int i=0; i != Nq; ++i)
614 if (qoi_indices.has_index(i))
615 sensitivities[i][j] = partialq_partialp[i] +
616 neg_partialR_partialp.dot(this->get_adjoint_solution(i));
617 }
618
619 // All parameters_vec have been reset.
620 // Reset the original qoi.
621
622 this->assemble_qoi(qoi_indices);
623}
624
625
626
628 const ParameterVector & parameters_in,
629 SensitivityData & sensitivities)
630{
631 ParameterVector & parameters_vec =
632 const_cast<ParameterVector &>(parameters_in);
633
634 const unsigned int Np = cast_int<unsigned int>
635 (parameters_vec.size());
636 const unsigned int Nq = this->n_qois();
637
638 // An introduction to the problem:
639 //
640 // Residual R(u(p),p) = 0
641 // partial R / partial u = J = system matrix
642 //
643 // This implies that:
644 // d/dp(R) = 0
645 // (partial R / partial p) +
646 // (partial R / partial u) * (partial u / partial p) = 0
647
648 // We first solve for (partial u / partial p) for each parameter:
649 // J * (partial u / partial p) = - (partial R / partial p)
650
651 this->sensitivity_solve(parameters_vec);
652
653 // Get ready to fill in sensitivities:
654 sensitivities.allocate_data(qoi_indices, *this, parameters_vec);
655
656 // We use the identity:
657 // dq/dp = (partial q / partial p) + (partial q / partial u) *
658 // (partial u / partial p)
659
660 // We get (partial q / partial u) from the user
661 this->assemble_qoi_derivative(qoi_indices,
662 /* include_liftfunc = */ true,
663 /* apply_constraints = */ false);
664
665 // We don't need these to be closed() in this function, but libMesh
666 // standard practice is to have them closed() by the time the
667 // function exits
668 for (auto i : make_range(this->n_qois()))
669 if (qoi_indices.has_index(i))
670 this->get_adjoint_rhs(i).close();
671
672 for (unsigned int j=0; j != Np; ++j)
673 {
674 // We currently get partial derivatives via central differencing
675
676 // (partial q / partial p) ~= (q(p+dp)-q(p-dp))/(2*dp)
677
678 Number old_parameter = *parameters_vec[j];
679
680 const Real delta_p =
681 TOLERANCE * std::max(std::abs(old_parameter), 1e-3);
682
683 *parameters_vec[j] = old_parameter - delta_p;
684 this->assemble_qoi(qoi_indices);
685 const std::vector<Number> qoi_minus = this->get_qoi_values();
686
687 *parameters_vec[j] = old_parameter + delta_p;
688 this->assemble_qoi(qoi_indices);
689 const std::vector<Number> qoi_plus = this->get_qoi_values();
690
691 std::vector<Number> partialq_partialp(Nq, 0);
692 for (unsigned int i=0; i != Nq; ++i)
693 if (qoi_indices.has_index(i))
694 partialq_partialp[i] = (qoi_plus[i] - qoi_minus[i]) / (2.*delta_p);
695
696 // Don't leave the parameter changed
697 *parameters_vec[j] = old_parameter;
698
699 for (unsigned int i=0; i != Nq; ++i)
700 if (qoi_indices.has_index(i))
701 sensitivities[i][j] = partialq_partialp[i] +
703 }
704
705 // All parameters_vec have been reset.
706 // We didn't cache the original rhs or matrix for memory reasons,
707 // but we can restore them to a state consistent solution -
708 // principle of least surprise.
709 this->assembly(true, true);
710 this->rhs->close();
711 this->matrix->close();
712 this->assemble_qoi(qoi_indices);
713}
714
715
716
718 const ParameterVector & parameters_in,
719 const ParameterVector & vector,
720 SensitivityData & sensitivities)
721{
722 // We currently get partial derivatives via finite differencing
723 const Real delta_p = TOLERANCE;
724
725 ParameterVector & parameters_vec =
726 const_cast<ParameterVector &>(parameters_in);
727
728 // We'll use a single temporary vector for matrix-vector-vector products
729 std::unique_ptr<NumericVector<Number>> tempvec = this->solution->zero_clone();
730
731 const unsigned int Np = cast_int<unsigned int>
732 (parameters_vec.size());
733 const unsigned int Nq = this->n_qois();
734
735 // For each quantity of interest q, the parameter sensitivity
736 // Hessian is defined as q''_{kl} = {d^2 q}/{d p_k d p_l}.
737 // Given a vector of parameter perturbation weights w_l, this
738 // function evaluates the hessian-vector product sum_l(q''_{kl}*w_l)
739 //
740 // We calculate it from values and partial derivatives of the
741 // quantity of interest function Q, solution u, adjoint solution z,
742 // parameter sensitivity adjoint solutions z^l, and residual R, as:
743 //
744 // sum_l(q''_{kl}*w_l) =
745 // sum_l(w_l * Q''_{kl}) + Q''_{uk}(u)*(sum_l(w_l u'_l)) -
746 // R'_k(u, sum_l(w_l*z^l)) - R'_{uk}(u,z)*(sum_l(w_l u'_l) -
747 // sum_l(w_l*R''_{kl}(u,z))
748 //
749 // See the adjoints model document for more details.
750
751 // We first do an adjoint solve to get z for each quantity of
752 // interest
753 // if we haven't already or dont have an initial condition for the adjoint
754 if (!this->is_adjoint_already_solved())
755 {
756 this->adjoint_solve(qoi_indices);
757 }
758
759 // Get ready to fill in sensitivities:
760 sensitivities.allocate_data(qoi_indices, *this, parameters_vec);
761
762 // We can't solve for all the solution sensitivities u'_l or for all
763 // of the parameter sensitivity adjoint solutions z^l without
764 // requiring O(Nq*Np) linear solves. So we'll solve directly for their
765 // weighted sum - this is just O(Nq) solves.
766
767 // First solve for sum_l(w_l u'_l).
768 this->weighted_sensitivity_solve(parameters_vec, vector);
769
770 // Then solve for sum_l(w_l z^l).
771 this->weighted_sensitivity_adjoint_solve(parameters_vec, vector, qoi_indices);
772
773 for (unsigned int k=0; k != Np; ++k)
774 {
775 // We approximate sum_l(w_l * Q''_{kl}) with a central
776 // differencing perturbation:
777 // sum_l(w_l * Q''_{kl}) ~=
778 // (Q(p + dp*w_l*e_l + dp*e_k) - Q(p - dp*w_l*e_l + dp*e_k) -
779 // Q(p + dp*w_l*e_l - dp*e_k) + Q(p - dp*w_l*e_l - dp*e_k))/(4*dp^2)
780
781 // The sum(w_l*R''_kl) term requires the same sort of perturbation,
782 // and so we subtract it in at the same time:
783 // sum_l(w_l * R''_{kl}) ~=
784 // (R(p + dp*w_l*e_l + dp*e_k) - R(p - dp*w_l*e_l + dp*e_k) -
785 // R(p + dp*w_l*e_l - dp*e_k) + R(p - dp*w_l*e_l - dp*e_k))/(4*dp^2)
786
787 ParameterVector oldparameters, parameterperturbation;
788 parameters_vec.deep_copy(oldparameters);
789 vector.deep_copy(parameterperturbation);
790 parameterperturbation *= delta_p;
791 parameters_vec += parameterperturbation;
792
793 Number old_parameter = *parameters_vec[k];
794
795 *parameters_vec[k] = old_parameter + delta_p;
796 this->assemble_qoi(qoi_indices);
797 this->assembly(true, false, true);
798 this->rhs->close();
799 std::vector<Number> partial2q_term = this->get_qoi_values();
800 std::vector<Number> partial2R_term(this->n_qois());
801 for (unsigned int i=0; i != Nq; ++i)
802 if (qoi_indices.has_index(i))
803 partial2R_term[i] = this->rhs->dot(this->get_adjoint_solution(i));
804
805 *parameters_vec[k] = old_parameter - delta_p;
806 this->assemble_qoi(qoi_indices);
807 this->assembly(true, false, true);
808 this->rhs->close();
809 for (unsigned int i=0; i != Nq; ++i)
810 if (qoi_indices.has_index(i))
811 {
812 partial2q_term[i] -= this->get_qoi_value(i);
813 partial2R_term[i] -= this->rhs->dot(this->get_adjoint_solution(i));
814 }
815
816 oldparameters.value_copy(parameters_vec);
817 parameterperturbation *= -1.0;
818 parameters_vec += parameterperturbation;
819
820 // Re-center old_parameter, which may be affected by vector
821 old_parameter = *parameters_vec[k];
822
823 *parameters_vec[k] = old_parameter + delta_p;
824 this->assemble_qoi(qoi_indices);
825 this->assembly(true, false, true);
826 this->rhs->close();
827 for (unsigned int i=0; i != Nq; ++i)
828 if (qoi_indices.has_index(i))
829 {
830 partial2q_term[i] -= this->get_qoi_value(i);
831 partial2R_term[i] -= this->rhs->dot(this->get_adjoint_solution(i));
832 }
833
834 *parameters_vec[k] = old_parameter - delta_p;
835 this->assemble_qoi(qoi_indices);
836 this->assembly(true, false, true);
837 this->rhs->close();
838 for (unsigned int i=0; i != Nq; ++i)
839 if (qoi_indices.has_index(i))
840 {
841 partial2q_term[i] += this->get_qoi_value(i);
842 partial2R_term[i] += this->rhs->dot(this->get_adjoint_solution(i));
843 }
844
845 for (unsigned int i=0; i != Nq; ++i)
846 if (qoi_indices.has_index(i))
847 {
848 partial2q_term[i] /= (4. * delta_p * delta_p);
849 partial2R_term[i] /= (4. * delta_p * delta_p);
850 }
851
852 for (unsigned int i=0; i != Nq; ++i)
853 if (qoi_indices.has_index(i))
854 sensitivities[i][k] = partial2q_term[i] - partial2R_term[i];
855
856 // We get (partial q / partial u), R, and
857 // (partial R / partial u) from the user, but centrally
858 // difference to get q_uk, R_k, and R_uk terms:
859 // (partial R / partial k)
860 // R_k*sum(w_l*z^l) = (R(p+dp*e_k)*sum(w_l*z^l) - R(p-dp*e_k)*sum(w_l*z^l))/(2*dp)
861 // (partial^2 q / partial u partial k)
862 // q_uk = (q_u(p+dp*e_k) - q_u(p-dp*e_k))/(2*dp)
863 // (partial^2 R / partial u partial k)
864 // R_uk*z*sum(w_l*u'_l) = (R_u(p+dp*e_k)*z*sum(w_l*u'_l) - R_u(p-dp*e_k)*z*sum(w_l*u'_l))/(2*dp)
865
866 // To avoid creating Nq temporary vectors for q_uk or R_uk, we add
867 // subterms to the sensitivities output one by one.
868 //
869 // FIXME: this is probably a bad order of operations for
870 // controlling floating point error.
871
872 *parameters_vec[k] = old_parameter + delta_p;
873 this->assembly(true, true);
874 this->rhs->close();
875 this->matrix->close();
876 this->assemble_qoi_derivative(qoi_indices,
877 /* include_liftfunc = */ true,
878 /* apply_constraints = */ false);
879
880 this->matrix->vector_mult(*tempvec, this->get_weighted_sensitivity_solution());
881
882 for (unsigned int i=0; i != Nq; ++i)
883 if (qoi_indices.has_index(i))
884 {
885 this->get_adjoint_rhs(i).close();
886 sensitivities[i][k] += (this->get_adjoint_rhs(i).dot(this->get_weighted_sensitivity_solution()) -
888 this->get_adjoint_solution(i).dot(*tempvec)) / (2.*delta_p);
889 }
890
891 *parameters_vec[k] = old_parameter - delta_p;
892 this->assembly(true, true);
893 this->rhs->close();
894 this->matrix->close();
895 this->assemble_qoi_derivative(qoi_indices,
896 /* include_liftfunc = */ true,
897 /* apply_constraints = */ false);
898
899 this->matrix->vector_mult(*tempvec, this->get_weighted_sensitivity_solution());
900
901 for (unsigned int i=0; i != Nq; ++i)
902 if (qoi_indices.has_index(i))
903 {
904 this->get_adjoint_rhs(i).close();
905 sensitivities[i][k] += (-this->get_adjoint_rhs(i).dot(this->get_weighted_sensitivity_solution()) +
907 this->get_adjoint_solution(i).dot(*tempvec)) / (2.*delta_p);
908 }
909 }
910
911 // All parameters have been reset.
912 // Don't leave the qoi or system changed - principle of least
913 // surprise.
914 this->assembly(true, true);
915 this->rhs->close();
916 this->matrix->close();
917 this->assemble_qoi(qoi_indices);
918}
919
920
921
923 const ParameterVector & parameters_in,
924 SensitivityData & sensitivities)
925{
926 // We currently get partial derivatives via finite differencing
927 const Real delta_p = TOLERANCE;
928
929 ParameterVector & parameters_vec =
930 const_cast<ParameterVector &>(parameters_in);
931
932 // We'll use one temporary vector for matrix-vector-vector products
933 std::unique_ptr<NumericVector<Number>> tempvec = this->solution->zero_clone();
934
935 // And another temporary vector to hold a copy of the true solution
936 // so we can safely perturb this->solution.
937 std::unique_ptr<NumericVector<Number>> oldsolution = this->solution->clone();
938
939 const unsigned int Np = cast_int<unsigned int>
940 (parameters_vec.size());
941 const unsigned int Nq = this->n_qois();
942
943 // For each quantity of interest q, the parameter sensitivity
944 // Hessian is defined as q''_{kl} = {d^2 q}/{d p_k d p_l}.
945 //
946 // We calculate it from values and partial derivatives of the
947 // quantity of interest function Q, solution u, adjoint solution z,
948 // and residual R, as:
949 //
950 // q''_{kl} =
951 // Q''_{kl} + Q''_{uk}(u)*u'_l + Q''_{ul}(u) * u'_k +
952 // Q''_{uu}(u)*u'_k*u'_l -
953 // R''_{kl}(u,z) -
954 // R''_{uk}(u,z)*u'_l - R''_{ul}(u,z)*u'_k -
955 // R''_{uu}(u,z)*u'_k*u'_l
956 //
957 // See the adjoints model document for more details.
958
959 // We first do an adjoint solve to get z for each quantity of
960 // interest
961 // if we haven't already or dont have an initial condition for the adjoint
962 if (!this->is_adjoint_already_solved())
963 {
964 this->adjoint_solve(qoi_indices);
965 }
966
967 // And a sensitivity solve to get u_k for each parameter
968 this->sensitivity_solve(parameters_vec);
969
970 // Get ready to fill in second derivatives:
971 sensitivities.allocate_hessian_data(qoi_indices, *this, parameters_vec);
972
973 for (unsigned int k=0; k != Np; ++k)
974 {
975 Number old_parameterk = *parameters_vec[k];
976
977 // The Hessian is symmetric, so we just calculate the lower
978 // triangle and the diagonal, and we get the upper triangle from
979 // the transpose of the lower
980
981 for (unsigned int l=0; l != k+1; ++l)
982 {
983 // The second partial derivatives with respect to parameters_vec
984 // are all calculated via a central finite difference
985 // stencil:
986 // F''_{kl} ~= (F(p+dp*e_k+dp*e_l) - F(p+dp*e_k-dp*e_l) -
987 // F(p-dp*e_k+dp*e_l) + F(p-dp*e_k-dp*e_l))/(4*dp^2)
988 // We will add Q''_{kl}(u) and subtract R''_{kl}(u,z) at the
989 // same time.
990 //
991 // We have to be careful with the perturbations to handle
992 // the k=l case
993
994 Number old_parameterl = *parameters_vec[l];
995
996 *parameters_vec[k] += delta_p;
997 *parameters_vec[l] += delta_p;
998 this->assemble_qoi(qoi_indices);
999 this->assembly(true, false, true);
1000 this->rhs->close();
1001 std::vector<Number> partial2q_term = this->get_qoi_values();
1002 std::vector<Number> partial2R_term(this->n_qois());
1003 for (unsigned int i=0; i != Nq; ++i)
1004 if (qoi_indices.has_index(i))
1005 partial2R_term[i] = this->rhs->dot(this->get_adjoint_solution(i));
1006
1007 *parameters_vec[l] -= 2.*delta_p;
1008 this->assemble_qoi(qoi_indices);
1009 this->assembly(true, false, true);
1010 this->rhs->close();
1011 for (unsigned int i=0; i != Nq; ++i)
1012 if (qoi_indices.has_index(i))
1013 {
1014 partial2q_term[i] -= this->get_qoi_value(i);
1015 partial2R_term[i] -= this->rhs->dot(this->get_adjoint_solution(i));
1016 }
1017
1018 *parameters_vec[k] -= 2.*delta_p;
1019 this->assemble_qoi(qoi_indices);
1020 this->assembly(true, false, true);
1021 this->rhs->close();
1022 for (unsigned int i=0; i != Nq; ++i)
1023 if (qoi_indices.has_index(i))
1024 {
1025 partial2q_term[i] += this->get_qoi_value(i);
1026 partial2R_term[i] += this->rhs->dot(this->get_adjoint_solution(i));
1027 }
1028
1029 *parameters_vec[l] += 2.*delta_p;
1030 this->assemble_qoi(qoi_indices);
1031 this->assembly(true, false, true);
1032 this->rhs->close();
1033 for (unsigned int i=0; i != Nq; ++i)
1034 if (qoi_indices.has_index(i))
1035 {
1036 partial2q_term[i] -= this->get_qoi_value(i);
1037 partial2R_term[i] -= this->rhs->dot(this->get_adjoint_solution(i));
1038 partial2q_term[i] /= (4. * delta_p * delta_p);
1039 partial2R_term[i] /= (4. * delta_p * delta_p);
1040 }
1041
1042 for (unsigned int i=0; i != Nq; ++i)
1043 if (qoi_indices.has_index(i))
1044 {
1045 Number current_terms = partial2q_term[i] - partial2R_term[i];
1046 sensitivities.second_derivative(i,k,l) += current_terms;
1047 if (k != l)
1048 sensitivities.second_derivative(i,l,k) += current_terms;
1049 }
1050
1051 // Don't leave the parameters_vec perturbed
1052 *parameters_vec[l] = old_parameterl;
1053 *parameters_vec[k] = old_parameterk;
1054 }
1055
1056 // We get (partial q / partial u) and
1057 // (partial R / partial u) from the user, but centrally
1058 // difference to get q_uk and R_uk terms:
1059 // (partial^2 q / partial u partial k)
1060 // q_uk*u'_l = (q_u(p+dp*e_k)*u'_l - q_u(p-dp*e_k)*u'_l)/(2*dp)
1061 // R_uk*z*u'_l = (R_u(p+dp*e_k)*z*u'_l - R_u(p-dp*e_k)*z*u'_l)/(2*dp)
1062 //
1063 // To avoid creating Nq temporary vectors, we add these
1064 // subterms to the sensitivities output one by one.
1065 //
1066 // FIXME: this is probably a bad order of operations for
1067 // controlling floating point error.
1068
1069 *parameters_vec[k] = old_parameterk + delta_p;
1070 this->assembly(false, true);
1071 this->matrix->close();
1072 this->assemble_qoi_derivative(qoi_indices,
1073 /* include_liftfunc = */ true,
1074 /* apply_constraints = */ false);
1075
1076 for (unsigned int l=0; l != Np; ++l)
1077 {
1078 this->matrix->vector_mult(*tempvec, this->get_sensitivity_solution(l));
1079 for (unsigned int i=0; i != Nq; ++i)
1080 if (qoi_indices.has_index(i))
1081 {
1082 this->get_adjoint_rhs(i).close();
1083 Number current_terms =
1084 (this->get_adjoint_rhs(i).dot(this->get_sensitivity_solution(l)) -
1085 tempvec->dot(this->get_adjoint_solution(i))) / (2.*delta_p);
1086 sensitivities.second_derivative(i,k,l) += current_terms;
1087
1088 // We use the _uk terms twice; symmetry lets us reuse
1089 // these calculations for the _ul terms.
1090
1091 sensitivities.second_derivative(i,l,k) += current_terms;
1092 }
1093 }
1094
1095 *parameters_vec[k] = old_parameterk - delta_p;
1096 this->assembly(false, true);
1097 this->matrix->close();
1098 this->assemble_qoi_derivative(qoi_indices,
1099 /* include_liftfunc = */ true,
1100 /* apply_constraints = */ false);
1101
1102 for (unsigned int l=0; l != Np; ++l)
1103 {
1104 this->matrix->vector_mult(*tempvec, this->get_sensitivity_solution(l));
1105 for (unsigned int i=0; i != Nq; ++i)
1106 if (qoi_indices.has_index(i))
1107 {
1108 this->get_adjoint_rhs(i).close();
1109 Number current_terms =
1110 (-this->get_adjoint_rhs(i).dot(this->get_sensitivity_solution(l)) +
1111 tempvec->dot(this->get_adjoint_solution(i))) / (2.*delta_p);
1112 sensitivities.second_derivative(i,k,l) += current_terms;
1113
1114 // We use the _uk terms twice; symmetry lets us reuse
1115 // these calculations for the _ul terms.
1116
1117 sensitivities.second_derivative(i,l,k) += current_terms;
1118 }
1119 }
1120
1121 // Don't leave the parameter perturbed
1122 *parameters_vec[k] = old_parameterk;
1123
1124 // Our last remaining terms are -R_uu(u,z)*u_k*u_l and
1125 // Q_uu(u)*u_k*u_l
1126 //
1127 // We take directional central finite differences of R_u and Q_u
1128 // to approximate these terms, e.g.:
1129 //
1130 // Q_uu(u)*u_k ~= (Q_u(u+dp*u_k) - Q_u(u-dp*u_k))/(2*dp)
1131
1132 *this->solution = this->get_sensitivity_solution(k);
1133 *this->solution *= delta_p;
1134 *this->solution += *oldsolution;
1135
1136 // We've modified solution, so we need to update before calling
1137 // assembly since assembly may only use current_local_solution
1138 this->update();
1139 this->assembly(false, true);
1140 this->matrix->close();
1141 this->assemble_qoi_derivative(qoi_indices,
1142 /* include_liftfunc = */ true,
1143 /* apply_constraints = */ false);
1144
1145 // The Hessian is symmetric, so we just calculate the lower
1146 // triangle and the diagonal, and we get the upper triangle from
1147 // the transpose of the lower
1148 //
1149 // Note that, because we took the directional finite difference
1150 // with respect to k and not l, we've added an O(delta_p^2)
1151 // error to any permutational symmetry in the Hessian...
1152 for (unsigned int l=0; l != k+1; ++l)
1153 {
1154 this->matrix->vector_mult(*tempvec, this->get_sensitivity_solution(l));
1155 for (unsigned int i=0; i != Nq; ++i)
1156 if (qoi_indices.has_index(i))
1157 {
1158 this->get_adjoint_rhs(i).close();
1159 Number current_terms =
1160 (this->get_adjoint_rhs(i).dot(this->get_sensitivity_solution(l)) -
1161 tempvec->dot(this->get_adjoint_solution(i))) / (2.*delta_p);
1162 sensitivities.second_derivative(i,k,l) += current_terms;
1163 if (k != l)
1164 sensitivities.second_derivative(i,l,k) += current_terms;
1165 }
1166 }
1167
1168 *this->solution = this->get_sensitivity_solution(k);
1169 *this->solution *= -delta_p;
1170 *this->solution += *oldsolution;
1171
1172 // We've modified solution, so we need to update before calling
1173 // assembly since assembly may only use current_local_solution
1174 this->update();
1175 this->assembly(false, true);
1176 this->matrix->close();
1177 this->assemble_qoi_derivative(qoi_indices,
1178 /* include_liftfunc = */ true,
1179 /* apply_constraints = */ false);
1180
1181 for (unsigned int l=0; l != k+1; ++l)
1182 {
1183 this->matrix->vector_mult(*tempvec, this->get_sensitivity_solution(l));
1184 for (unsigned int i=0; i != Nq; ++i)
1185 if (qoi_indices.has_index(i))
1186 {
1187 this->get_adjoint_rhs(i).close();
1188 Number current_terms =
1189 (-this->get_adjoint_rhs(i).dot(this->get_sensitivity_solution(l)) +
1190 tempvec->dot(this->get_adjoint_solution(i))) / (2.*delta_p);
1191 sensitivities.second_derivative(i,k,l) += current_terms;
1192 if (k != l)
1193 sensitivities.second_derivative(i,l,k) += current_terms;
1194 }
1195 }
1196
1197 // Don't leave the solution perturbed
1198 *this->solution = *oldsolution;
1199 }
1200
1201 // All parameters_vec have been reset.
1202 // Don't leave the qoi or system changed - principle of least
1203 // surprise.
1204 // We've modified solution, so we need to update before calling
1205 // assembly since assembly may only use current_local_solution
1206 this->update();
1207 this->assembly(true, true);
1208 this->rhs->close();
1209 this->matrix->close();
1210 this->assemble_qoi(qoi_indices);
1211}
1212
1213
1214
1216{
1217 // Note: we always start "from scratch" to mimic the original
1218 // behavior of this function. The goal is not to reuse the
1219 // LinearSolver object, but to manage its lifetime in a more
1220 // consistent manner.
1221 linear_solver.reset();
1222
1224
1225 if (this->prefix_with_name())
1226 linear_solver->init(this->prefix().c_str());
1227 else
1228 linear_solver->init();
1229
1230 linear_solver->init_systems(*this);
1231
1232 return linear_solver.get();
1233}
1234
1235
1236
1237std::pair<unsigned int, Real> ImplicitSystem::get_linear_solve_parameters() const
1238{
1239 return std::make_pair(
1240 parameters.have_parameter<unsigned int>("linear solver maximum iterations")
1241 ? parameters.get<unsigned int>("linear solver maximum iterations")
1242 : this->get_equation_systems().parameters.get<unsigned int>(
1243 "linear solver maximum iterations"),
1244 parameters.have_parameter<Real>("linear solver tolerance")
1245 ? parameters.get<Real>("linear solver tolerance")
1246 : this->get_equation_systems().parameters.get<Real>("linear solver tolerance"));
1247}
1248
1249
1250
1252{
1254 libmesh_assert_equal_to(&get_matrix("System Matrix"), matrix);
1255 return *matrix;
1256}
1257
1258
1259
1261{
1263 libmesh_assert_equal_to(&get_matrix("System Matrix"), matrix);
1264 return *matrix;
1265}
1266
1269} // namespace libMesh
void enforce_constraints_exactly(const System &system, NumericVector< Number > *v=nullptr, bool homogeneous=false) const
Constrains the numeric vector v, which represents a solution defined on the mesh.
Definition dof_map.h:2518
bool has_adjoint_dirichlet_boundaries(unsigned int q) const
void enforce_adjoint_constraints_exactly(NumericVector< Number > &v, unsigned int q) const
Heterogeneously constrains the numeric vector v, which represents an adjoint solution defined on the ...
Definition dof_map.h:2522
This is the EquationSystems class.
Parameters parameters
Data structure holding arbitrary parameters.
Manages consistently variables, degrees of freedom, and coefficient vectors for explicit systems.
virtual void assemble_qoi_derivative(const QoISet &qoi_indices=QoISet(), bool include_liftfunc=true, bool apply_constraints=true) override
Prepares adjoint_rhs for quantity of interest derivative assembly, then calls user qoi derivative fun...
virtual void clear() override
Clear all the data structures associated with the system.
virtual void assemble_qoi(const QoISet &qoi_indices=QoISet()) override
Prepares qoi for quantity of interest assembly, then calls user qoi function.
NumericVector< Number > * rhs
The system matrix.
virtual void assemble() override
Prepares matrix and rhs for system assembly, then calls user assembly function.
ImplicitSystem(EquationSystems &es, const std::string &name, const unsigned int number)
Constructor.
bool zero_out_matrix_and_rhs
By default, the system will zero out the matrix and the right hand side.
virtual std::pair< unsigned int, Real > weighted_sensitivity_solve(const ParameterVector &parameters, const ParameterVector &weights) override
Assembles & solves the linear system(s) (dR/du)*u_w = sum(w_p*-dR/dp), for those parameters p contain...
virtual std::pair< unsigned int, Real > get_linear_solve_parameters() const
const SparseMatrix< Number > & get_system_matrix() const
StaticCondensation * _sc_system_matrix
The system matrix for static condensation problems.
void create_static_condensation_system_matrix()
Create the static condensation system matrix.
StaticCondensation & get_static_condensation()
virtual LinearSolver< Number > * get_linear_solver() const
virtual void assemble_residual_derivatives(const ParameterVector &parameters) override
Residual parameter derivative function.
SparseMatrix< Number > * matrix
The system matrix.
virtual void clear() override
Clear all the data structures associated with the system.
virtual void adjoint_qoi_parameter_sensitivity(const QoISet &qoi_indices, const ParameterVector &parameters, SensitivityData &sensitivities) override
Solves for the derivative of each of the system's quantities of interest q in qoi[qoi_indices] with r...
virtual void disable_cache() override
Avoids use of any cached data that might affect any solve result.
virtual void qoi_parameter_hessian_vector_product(const QoISet &qoi_indices, const ParameterVector &parameters, const ParameterVector &vector, SensitivityData &product) override
For each of the system's quantities of interest q in qoi[qoi_indices], and for a vector of parameters...
virtual std::pair< unsigned int, Real > adjoint_solve(const QoISet &qoi_indices=QoISet()) override
Assembles & solves the linear system (dR/du)^T*z = dq/du, for those quantities of interest q specifie...
virtual std::pair< unsigned int, Real > weighted_sensitivity_adjoint_solve(const ParameterVector &parameters, const ParameterVector &weights, const QoISet &qoi_indices=QoISet()) override
Assembles & solves the linear system(s) (dR/du)^T*z_w = sum(w_p*(d^2q/dudp - d^2R/dudp*z)),...
virtual void create_static_condensation() override
Request that static condensation be performed for this system.
virtual std::pair< unsigned int, Real > sensitivity_solve(const ParameterVector &parameters) override
Assembles & solves the linear system(s) (dR/du)*u_p = -dR/dp, for those parameters contained within p...
virtual void qoi_parameter_hessian(const QoISet &qoi_indices, const ParameterVector &parameters, SensitivityData &hessian) override
For each of the system's quantities of interest q in qoi[qoi_indices], and for a vector of parameters...
void setup_static_condensation_preconditioner(T &solver)
Sets up the static condensation preconditioner for the supplied solver.
virtual void assembly(bool, bool, bool=false, bool=false)
Assembles a residual in rhs and/or a jacobian in matrix, as requested.
std::unique_ptr< LinearSolver< Number > > linear_solver
This class handles all the details of interfacing with various linear algebra packages like PETSc or ...
virtual void add_matrices() override
Adds the system matrix.
virtual void forward_qoi_parameter_sensitivity(const QoISet &qoi_indices, const ParameterVector &parameters, SensitivityData &sensitivities) override
Solves for the derivative of each of the system's quantities of interest q in qoi[qoi_indices] with r...
This base class can be inherited from to provide interfaces to linear solvers from different packages...
static std::unique_ptr< LinearSolver< T > > build(const libMesh::Parallel::Communicator &comm_in, const SolverPackage solver_package=libMesh::default_solver_package())
Builds a LinearSolver using the linear solver package specified by solver_package.
virtual void reuse_preconditioner(bool)
Set the same_preconditioner flag, which indicates if we reuse the same preconditioner for subsequent ...
virtual std::pair< unsigned int, Real > solve(SparseMatrix< T > &, NumericVector< T > &, NumericVector< T > &, const std::optional< double > tol=std::nullopt, const std::optional< unsigned int > m_its=std::nullopt)=0
This function calls the solver _solver_type preconditioned with the _preconditioner_type precondition...
virtual std::pair< unsigned int, Real > adjoint_solve(SparseMatrix< T > &, NumericVector< T > &, NumericVector< T > &, const std::optional< double > tol=std::nullopt, const std::optional< unsigned int > m_its=std::nullopt)
Function to solve the adjoint system.
This base class can be inherited from to provide interfaces to nonlinear solvers from different packa...
Provides a uniform interface to vector storage schemes for different linear algebra libraries.
virtual void close()=0
Calls the NumericVector's internal assembly routines, ensuring that the values are consistent across ...
virtual T dot(const NumericVector< T > &v) const =0
virtual bool initialized() const
virtual void zero()=0
Set all entries to zero.
virtual std::unique_ptr< NumericVector< T > > clone() const =0
virtual std::unique_ptr< NumericVector< T > > zero_clone() const =0
const Parallel::Communicator & comm() const
Data structure for specifying which Parameters should be independent variables in a parameter sensiti...
void deep_copy(ParameterVector &target) const
Deep copy constructor: the target will now own new copies of all the parameter values I'm pointing to...
void value_copy(ParameterVector &target) const
Value copy method: the target, which should already have as many parameters as I do,...
std::size_t size() const
bool have_parameter(std::string_view) const
Definition parameters.h:420
const T & get(std::string_view) const
Definition parameters.h:451
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
Data structure for holding completed parameter sensitivity calculations.
void allocate_hessian_data(const QoISet &qoi_indices, const System &sys, const ParameterVector &parameter_vector)
Given QoISet and ParameterVector, allocates space for all required second derivative data.
const Number & second_derivative(unsigned int qoi_index, unsigned int parameter_index1, unsigned int parameter_index2) const
void allocate_data(const QoISet &qoi_indices, const System &sys, const ParameterVector &parameter_vector)
Given QoISet and ParameterVector, allocates space for all required first derivative data.
Generic sparse matrix.
virtual void close()=0
Calls the SparseMatrix's internal assembly routines, ensuring that the values are consistent across p...
void vector_mult(NumericVector< T > &dest, const NumericVector< T > &arg) const
Multiplies the matrix by the NumericVector arg and stores the result in NumericVector dest.
virtual bool initialized() const
void vector_mult_add(NumericVector< T > &dest, const NumericVector< T > &arg) const
Multiplies the matrix by the NumericVector arg and adds the result to the NumericVector dest.
virtual void zero()=0
Set all entries to 0.
virtual void get_transpose(SparseMatrix< T > &dest) const =0
Copies the transpose of the matrix into dest, which may be *this.
StaticCondensationPreconditioner & get_preconditioner()
Get the preconditioning wrapper.
SparseMatrix< Number > & add_matrix(std::string_view mat_name, ParallelType type=PARALLEL, MatrixBuildType mat_build_type=MatrixBuildType::AUTOMATIC)
Adds the additional matrix mat_name to this system.
Definition system.C:998
NumericVector< Number > & add_weighted_sensitivity_adjoint_solution(unsigned int i=0)
Definition system.C:1252
const SparseMatrix< Number > & get_matrix(std::string_view mat_name) const
Definition system.C:1111
bool prefix_with_name() const
Definition system.h:1974
NumericVector< Number > & get_weighted_sensitivity_adjoint_solution(unsigned int i=0)
Definition system.C:1264
virtual void create_static_condensation()
Request that static condensation be performed for this system.
Definition system.C:2664
NumericVector< Number > & add_sensitivity_rhs(unsigned int i=0)
Definition system.C:1314
bool assemble_before_solve
Flag which tells the system to whether or not to call the user assembly function during each call to ...
Definition system.h:1609
unsigned int n_matrices() const
Definition system.h:2638
virtual void assemble()
Prepares matrix and _dof_map for matrix assembly.
Definition system.C:554
Number get_qoi_value(unsigned int qoi_index) const
Definition system.C:2184
std::vector< Number > get_qoi_values() const
Returns a copy of qoi, not a reference.
Definition system.C:2191
NumericVector< Number > & add_sensitivity_solution(unsigned int i=0)
Definition system.C:1169
NumericVector< Number > & get_sensitivity_solution(unsigned int i=0)
Definition system.C:1179
virtual void add_matrices()
Insertion point for adding matrices in derived classes before init_matrices() is called.
Definition system.h:2017
std::unique_ptr< NumericVector< Number > > solution
Data structure to hold solution values.
Definition system.h:1655
unsigned int n_qois() const
Number of currently active quantities of interest.
Definition system.h:2562
NumericVector< Number > & get_sensitivity_rhs(unsigned int i=0)
Definition system.C:1324
NumericVector< Number > & add_adjoint_solution(unsigned int i=0)
Definition system.C:1220
virtual void update()
Update the local values to reflect the solution on neighboring processors.
Definition system.C:498
bool has_static_condensation() const
Definition system.C:2669
Parameters parameters
Parameters for the system. If a parameter is not provided, it should be retrieved from the EquationSy...
Definition system.h:1588
bool is_adjoint_already_solved() const
Accessor for the adjoint_already_solved boolean.
Definition system.h:411
NumericVector< Number > & get_weighted_sensitivity_solution()
Definition system.C:1206
std::string prefix() const
Definition system.h:1980
NumericVector< Number > & add_weighted_sensitivity_solution()
Definition system.C:1199
const DofMap & get_dof_map() const
Definition system.h:2417
NumericVector< Number > & get_adjoint_solution(unsigned int i=0)
Definition system.C:1232
const EquationSystems & get_equation_systems() const
Definition system.h:767
const MeshBase & get_mesh() const
Definition system.h:2401
const SparseMatrix< Number > * request_matrix(std::string_view mat_name) const
Definition system.C:1087
NumericVector< Number > & get_adjoint_rhs(unsigned int i=0)
Definition system.C:1294
The libMesh namespace provides an interface to certain functionality in the library.
libmesh_assert(ctx)
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