libMesh
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 
40 namespace 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 
65 {
68 }
69 
70 template <typename T>
72 {
74  solver.attach_preconditioner(&_sc_system_matrix->get_preconditioner());
75 }
76 
77 
79 {
80  // clear the parent data
81  Parent::clear();
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
108  Parent::assemble ();
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 
133  this->assemble_before_solve = true;
134  this->get_linear_solver()->reuse_preconditioner(false);
135 }
136 
137 
138 
139 std::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,
173  this->add_sensitivity_solution(p),
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 
195 std::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 
246 std::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 
392 std::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] +
702  this->get_adjoint_rhs(i).dot(this->get_sensitivity_solution(j));
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 
1237 std::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
virtual bool initialized() const
This is the EquationSystems class.
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&#39;s quantities of interest q in qoi[qoi_indices], and for a vector of parameters...
bool have_parameter(std::string_view) const
Definition: parameters.h:420
virtual void create_static_condensation()
Request that static condensation be performed for this system.
Definition: system.C:2664
std::size_t size() const
void deep_copy(ParameterVector &target) const
Deep copy constructor: the target will now own new copies of all the parameter values I&#39;m pointing to...
void create_static_condensation_system_matrix()
Create the static condensation system matrix.
NumericVector< Number > & add_adjoint_solution(unsigned int i=0)
Definition: system.C:1220
Data structure for specifying which Parameters should be independent variables in a parameter sensiti...
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.
Definition: linear_solver.C:59
virtual void create_static_condensation() override
Request that static condensation be performed for this system.
static constexpr Real TOLERANCE
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 std::pair< unsigned int, Real > get_linear_solve_parameters() const
Data structure for specifying which Quantities of Interest should be calculated in an adjoint or a pa...
Definition: qoi_set.h:45
Number get_qoi_value(unsigned int qoi_index) const
Definition: system.C:2184
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...
unsigned int n_qois() const
Number of currently active quantities of interest.
Definition: system.h:2562
virtual bool initialized() const
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::unique_ptr< NumericVector< T > > zero_clone() const =0
NumericVector< Number > & get_sensitivity_solution(unsigned int i=0)
Definition: system.C:1179
NumericVector< Number > & get_sensitivity_rhs(unsigned int i=0)
Definition: system.C:1324
const EquationSystems & get_equation_systems() const
Definition: system.h:767
virtual void assemble()
Prepares matrix and _dof_map for matrix assembly.
Definition: system.C:554
virtual void clear() override
Clear all the data structures associated with the system.
NumericVector< Number > * rhs
The system matrix.
const Parallel::Communicator & comm() const
virtual std::unique_ptr< NumericVector< T > > clone() const =0
virtual LinearSolver< Number > * get_linear_solver() const
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...
NumericVector< Number > & add_sensitivity_rhs(unsigned int i=0)
Definition: system.C:1314
bool has_static_condensation() const
Definition: system.C:2669
The libMesh namespace provides an interface to certain functionality in the library.
virtual T dot(const NumericVector< T > &v) const =0
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 > 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...
StaticCondensation * _sc_system_matrix
The system matrix for static condensation problems.
NumericVector< Number > & add_weighted_sensitivity_solution()
Definition: system.C:1199
const SparseMatrix< Number > * request_matrix(std::string_view mat_name) const
Definition: system.C:1087
Parameters parameters
Parameters for the system. If a parameter is not provided, it should be retrieved from the EquationSy...
Definition: system.h:1588
const MeshBase & get_mesh() const
Definition: system.h:2401
void setup_static_condensation_preconditioner(T &solver)
Sets up the static condensation preconditioner for the supplied solver.
NumericVector< Number > & get_weighted_sensitivity_solution()
Definition: system.C:1206
virtual void zero()=0
Set all entries to zero.
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
NumericVector< Number > & add_weighted_sensitivity_adjoint_solution(unsigned int i=0)
Definition: system.C:1252
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&#39;s quantities of interest q in qoi[qoi_indices] with r...
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
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...
virtual void assembly(bool, bool, bool=false, bool=false)
Assembles a residual in rhs and/or a jacobian in matrix, as requested.
const T & get(std::string_view) const
Definition: parameters.h:451
virtual void zero()=0
Set all entries to 0.
bool has_adjoint_dirichlet_boundaries(unsigned int q) const
Data structure for holding completed parameter sensitivity calculations.
StaticCondensationDofMap & get_static_condensation()
Definition: dof_map.h:2859
std::string prefix() const
Definition: system.h:1980
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&#39;s quantities of interest q in qoi[qoi_indices] with r...
Manages consistently variables, degrees of freedom, and coefficient vectors.
Definition: system.h:98
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
libmesh_assert(ctx)
virtual void disable_cache() override
Avoids use of any cached data that might affect any solve result.
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...
unsigned int n_matrices() const
Definition: system.h:2638
bool is_adjoint_already_solved() const
Accessor for the adjoint_already_solved boolean.
Definition: system.h:411
virtual void close()=0
Calls the NumericVector&#39;s internal assembly routines, ensuring that the values are consistent across ...
virtual void clear() override
Clear all the data structures associated with the system.
ImplicitSystem(EquationSystems &es, const std::string &name, const unsigned int number)
Constructor.
virtual void update()
Update the local values to reflect the solution on neighboring processors.
Definition: system.C:498
virtual void close()=0
Calls the SparseMatrix&#39;s internal assembly routines, ensuring that the values are consistent across p...
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual void add_matrices() override
Adds the system matrix.
virtual void reuse_preconditioner(bool)
Set the same_preconditioner flag, which indicates if we reuse the same preconditioner for subsequent ...
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...
const Number & second_derivative(unsigned int qoi_index, unsigned int parameter_index1, unsigned int parameter_index2) const
SparseMatrix< Number > * matrix
The system matrix.
virtual void get_transpose(SparseMatrix< T > &dest) const =0
Copies the transpose of the matrix into dest, which may be *this.
virtual void assemble_qoi(const QoISet &qoi_indices=QoISet()) override
Prepares qoi for quantity of interest assembly, then calls user qoi function.
virtual void assemble_residual_derivatives(const ParameterVector &parameters) override
Residual parameter derivative function.
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
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...
NumericVector< Number > & get_adjoint_solution(unsigned int i=0)
Definition: system.C:1232
Parameters parameters
Data structure holding arbitrary parameters.
virtual void assemble() override
Prepares matrix and rhs for system assembly, then calls user assembly function.
NumericVector< Number > & add_sensitivity_solution(unsigned int i=0)
Definition: system.C:1169
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.
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
std::unique_ptr< LinearSolver< Number > > linear_solver
This class handles all the details of interfacing with various linear algebra packages like PETSc or ...
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
const DofMap & get_dof_map() const
Definition: system.h:2417
virtual void qoi_parameter_hessian(const QoISet &qoi_indices, const ParameterVector &parameters, SensitivityData &hessian) override
For each of the system&#39;s quantities of interest q in qoi[qoi_indices], and for a vector of parameters...
const SparseMatrix< Number > & get_matrix(std::string_view mat_name) const
Definition: system.C:1111
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)), for those parameters p contained within parameters, weighted by the values w_p found within weights.
const SparseMatrix< Number > & get_system_matrix() const
void value_copy(ParameterVector &target) const
Value copy method: the target, which should already have as many parameters as I do, will now have those parameters set to my values.
StaticCondensationPreconditioner & get_preconditioner()
Get the preconditioning wrapper.
NumericVector< Number > & get_weighted_sensitivity_adjoint_solution(unsigned int i=0)
Definition: system.C:1264
std::vector< Number > get_qoi_values() const
Returns a copy of qoi, not a reference.
Definition: system.C:2191
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
NumericVector< Number > & get_adjoint_rhs(unsigned int i=0)
Definition: system.C:1294
bool prefix_with_name() const
Definition: system.h:1974