Loading [MathJax]/extensions/tex2jax.js
libMesh
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
implicit_system.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2025 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License, or (at your option) any later version.
8 
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // Lesser General Public License for more details.
13 
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 
18 
19 
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 
38 namespace libMesh
39 {
40 
42  const std::string & name_in,
43  const unsigned int number_in) :
44 
45  Parent (es, name_in, number_in),
46  matrix (nullptr),
47  zero_out_matrix_and_rhs(true),
48  _sc (nullptr)
49 {
50  if (libMesh::on_command_line("--" + name_in + "-static-condensation"))
52 }
53 
55 
56 
57 
59 {
60  auto sc = std::make_unique<StaticCondensation>(this->get_mesh(), *this, this->get_dof_map());
61  _sc = sc.get();
62  matrix = &(this->add_matrix ("System Matrix", std::move(sc)));
64 }
65 
66 
67 
69 {
70  // clear the parent data
71  Parent::clear();
72 
73  // Restore us to a "basic" state
74  matrix = nullptr;
75  _sc = nullptr;
76 }
77 
78 
79 
81 {
86 
88  {
89  matrix->zero ();
90  rhs->zero ();
91  }
92 
93  // Call the base class assemble function
95 }
96 
97 
98 
100 {
102 
103  // Possible that we cleared the _matrices but
104  // forgot to update the matrix pointer?
105  if (this->n_matrices() == 0)
106  matrix = nullptr;
107 
108  // Only need to add the matrix if it isn't there
109  // already!
110  if (matrix == nullptr)
111  matrix = &(this->add_matrix ("System Matrix"));
112 
114 }
115 
116 
117 
119  this->assemble_before_solve = true;
120  this->get_linear_solver()->reuse_preconditioner(false);
121 }
122 
123 
124 
125 std::pair<unsigned int, Real>
127 {
128  // Log how long the linear solve takes.
129  LOG_SCOPE("sensitivity_solve()", "ImplicitSystem");
130 
131  // The forward system should now already be solved.
132  // Now assemble the corresponding sensitivity system.
133 
134  if (this->assemble_before_solve)
135  {
136  // Build the Jacobian
137  this->assembly(false, true);
138  this->matrix->close();
139 
140  // Reset and build the RHS from the residual derivatives
141  this->assemble_residual_derivatives(parameters_vec);
142  }
143 
144  // The sensitivity problem is linear
145  LinearSolver<Number> * solver = this->get_linear_solver();
146 
147  // Our iteration counts and residuals will be sums of the individual
148  // results
149  std::pair<unsigned int, Real> solver_params =
151  std::pair<unsigned int, Real> totalrval = std::make_pair(0,0.0);
152 
153  // Solve the linear system.
154  SparseMatrix<Number> * pc = this->request_matrix("Preconditioner");
155  for (auto p : make_range(parameters_vec.size()))
156  {
157  std::pair<unsigned int, Real> rval =
158  solver->solve (*matrix, pc,
159  this->add_sensitivity_solution(p),
160  this->get_sensitivity_rhs(p),
161  double(solver_params.second),
162  solver_params.first);
163 
164  totalrval.first += rval.first;
165  totalrval.second += rval.second;
166  }
167 
168  // The linear solver may not have fit our constraints exactly
169 #ifdef LIBMESH_ENABLE_CONSTRAINTS
170  for (auto p : make_range(parameters_vec.size()))
172  (*this, &this->get_sensitivity_solution(p),
173  /* homogeneous = */ true);
174 #endif
175 
176  return totalrval;
177 }
178 
179 
180 
181 std::pair<unsigned int, Real>
183 {
184  // Log how long the linear solve takes.
185  LOG_SCOPE("adjoint_solve()", "ImplicitSystem");
186 
187  if (this->assemble_before_solve)
188  // Assemble the linear system
189  this->assembly (/* get_residual = */ false,
190  /* get_jacobian = */ true);
191 
192  // The adjoint problem is linear
193  LinearSolver<Number> * solver = this->get_linear_solver();
194 
195  // Reset and build the RHS from the QOI derivative
196  this->assemble_qoi_derivative(qoi_indices,
197  /* include_liftfunc = */ false,
198  /* apply_constraints = */ true);
199 
200  // Our iteration counts and residuals will be sums of the individual
201  // results
202  std::pair<unsigned int, Real> solver_params =
204  std::pair<unsigned int, Real> totalrval = std::make_pair(0,0.0);
205 
206  for (auto i : make_range(this->n_qois()))
207  if (qoi_indices.has_index(i))
208  {
209  const std::pair<unsigned int, Real> rval =
210  solver->adjoint_solve (*matrix, this->add_adjoint_solution(i),
211  this->get_adjoint_rhs(i),
212  double(solver_params.second),
213  solver_params.first);
214 
215  totalrval.first += rval.first;
216  totalrval.second += rval.second;
217  }
218 
219  // The linear solver may not have fit our constraints exactly
220 #ifdef LIBMESH_ENABLE_CONSTRAINTS
221  for (auto i : make_range(this->n_qois()))
222  if (qoi_indices.has_index(i))
224  (this->get_adjoint_solution(i), i);
225 #endif
226 
227  return totalrval;
228 }
229 
230 
231 
232 std::pair<unsigned int, Real>
234  const ParameterVector & weights,
235  const QoISet & qoi_indices)
236 {
237  // Log how long the linear solve takes.
238  LOG_SCOPE("weighted_sensitivity_adjoint_solve()", "ImplicitSystem");
239 
240  // We currently get partial derivatives via central differencing
241  const Real delta_p = TOLERANCE;
242 
243  ParameterVector & parameters_vec =
244  const_cast<ParameterVector &>(parameters_in);
245 
246  // The forward system should now already be solved.
247  // The adjoint system should now already be solved.
248  // Now we're assembling a weighted sum of adjoint-adjoint systems:
249  //
250  // dR/du (u, sum_l(w_l*z^l)) = sum_l(w_l*(Q''_ul - R''_ul (u, z)))
251 
252  // FIXME: The derivation here does not yet take adjoint boundary
253  // conditions into account.
254 #ifdef LIBMESH_ENABLE_DIRICHLET
255  for (auto i : make_range(this->n_qois()))
256  if (qoi_indices.has_index(i))
258 #endif
259 
260  // We'll assemble the rhs first, because the R'' term will require
261  // perturbing the jacobian
262 
263  // We'll use temporary rhs vectors, because we haven't (yet) found
264  // any good reasons why users might want to save these:
265 
266  std::vector<std::unique_ptr<NumericVector<Number>>> temprhs(this->n_qois());
267  for (auto i : make_range(this->n_qois()))
268  if (qoi_indices.has_index(i))
269  temprhs[i] = this->rhs->zero_clone();
270 
271  // We approximate the _l partial derivatives via a central
272  // differencing perturbation in the w_l direction:
273  //
274  // sum_l(w_l*v_l) ~= (v(p + dp*w_l*e_l) - v(p - dp*w_l*e_l))/(2*dp)
275 
276  // PETSc doesn't implement SGEMX, so neither does NumericVector,
277  // so we want to avoid calculating f -= R'*z. We'll thus evaluate
278  // the above equation by first adding -v(p+dp...), then multiplying
279  // the intermediate result vectors by -1, then adding -v(p-dp...),
280  // then finally dividing by 2*dp.
281 
282  ParameterVector oldparameters, parameterperturbation;
283  parameters_vec.deep_copy(oldparameters);
284  weights.deep_copy(parameterperturbation);
285  parameterperturbation *= delta_p;
286  parameters_vec += parameterperturbation;
287 
288  this->assembly(false, true);
289  this->matrix->close();
290 
291  // Take the discrete adjoint, so that we can calculate R_u(u,z) with
292  // a matrix-vector product of R_u and z.
294 
295  this->assemble_qoi_derivative(qoi_indices,
296  /* include_liftfunc = */ false,
297  /* apply_constraints = */ true);
298  for (auto i : make_range(this->n_qois()))
299  if (qoi_indices.has_index(i))
300  {
301  this->get_adjoint_rhs(i).close();
302  *(temprhs[i]) -= this->get_adjoint_rhs(i);
303  this->matrix->vector_mult_add(*(temprhs[i]), this->get_adjoint_solution(i));
304  *(temprhs[i]) *= -1.0;
305  }
306 
307  oldparameters.value_copy(parameters_vec);
308  parameterperturbation *= -1.0;
309  parameters_vec += parameterperturbation;
310 
311  this->assembly(false, true);
312  this->matrix->close();
314 
315  this->assemble_qoi_derivative(qoi_indices,
316  /* include_liftfunc = */ false,
317  /* apply_constraints = */ true);
318  for (auto i : make_range(this->n_qois()))
319  if (qoi_indices.has_index(i))
320  {
321  this->get_adjoint_rhs(i).close();
322  *(temprhs[i]) -= this->get_adjoint_rhs(i);
323  this->matrix->vector_mult_add(*(temprhs[i]), this->get_adjoint_solution(i));
324  *(temprhs[i]) /= (2.0*delta_p);
325  }
326 
327  // Finally, assemble the jacobian at the non-perturbed parameter
328  // values. Ignore assemble_before_solve; if we had a good
329  // non-perturbed matrix before we've already overwritten it.
330  oldparameters.value_copy(parameters_vec);
331 
332  // if (this->assemble_before_solve)
333  {
334  // Build the Jacobian
335  this->assembly(false, true);
336  this->matrix->close();
337 
338  // Take the discrete adjoint
340  }
341 
342  // The weighted adjoint-adjoint problem is linear
343  LinearSolver<Number> * solver = this->get_linear_solver();
344 
345  // Our iteration counts and residuals will be sums of the individual
346  // results
347  std::pair<unsigned int, Real> solver_params =
349  std::pair<unsigned int, Real> totalrval = std::make_pair(0,0.0);
350 
351  for (auto i : make_range(this->n_qois()))
352  if (qoi_indices.has_index(i))
353  {
354  const std::pair<unsigned int, Real> rval =
356  *(temprhs[i]),
357  double(solver_params.second),
358  solver_params.first);
359 
360  totalrval.first += rval.first;
361  totalrval.second += rval.second;
362  }
363 
364  // The linear solver may not have fit our constraints exactly
365 #ifdef LIBMESH_ENABLE_CONSTRAINTS
366  for (auto i : make_range(this->n_qois()))
367  if (qoi_indices.has_index(i))
370  /* homogeneous = */ true);
371 #endif
372 
373  return totalrval;
374 }
375 
376 
377 
378 std::pair<unsigned int, Real>
380  const ParameterVector & weights)
381 {
382  // Log how long the linear solve takes.
383  LOG_SCOPE("weighted_sensitivity_solve()", "ImplicitSystem");
384 
385  // We currently get partial derivatives via central differencing
386  const Real delta_p = TOLERANCE;
387 
388  ParameterVector & parameters_vec =
389  const_cast<ParameterVector &>(parameters_in);
390 
391  // The forward system should now already be solved.
392 
393  // Now we're assembling a weighted sum of sensitivity systems:
394  //
395  // dR/du (u, v)(sum(w_l*u'_l)) = -sum_l(w_l*R'_l (u, v)) forall v
396 
397  // We'll assemble the rhs first, because the R' term will require
398  // perturbing the system, and some applications may not be able to
399  // assemble a perturbed residual without simultaneously constructing
400  // a perturbed jacobian.
401 
402  // We approximate the _l partial derivatives via a central
403  // differencing perturbation in the w_l direction:
404  //
405  // sum_l(w_l*v_l) ~= (v(p + dp*w_l*e_l) - v(p - dp*w_l*e_l))/(2*dp)
406 
407  ParameterVector oldparameters, parameterperturbation;
408  parameters_vec.deep_copy(oldparameters);
409  weights.deep_copy(parameterperturbation);
410  parameterperturbation *= delta_p;
411  parameters_vec += parameterperturbation;
412 
413  this->assembly(true, false, true);
414  this->rhs->close();
415 
416  std::unique_ptr<NumericVector<Number>> temprhs = this->rhs->clone();
417 
418  oldparameters.value_copy(parameters_vec);
419  parameterperturbation *= -1.0;
420  parameters_vec += parameterperturbation;
421 
422  this->assembly(true, false, true);
423  this->rhs->close();
424 
425  *temprhs -= *(this->rhs);
426  *temprhs /= (2.0*delta_p);
427 
428  // Finally, assemble the jacobian at the non-perturbed parameter
429  // values
430  oldparameters.value_copy(parameters_vec);
431 
432  // Build the Jacobian
433  this->assembly(false, true);
434  this->matrix->close();
435 
436  // The weighted sensitivity problem is linear
437  LinearSolver<Number> * solver = this->get_linear_solver();
438 
439  std::pair<unsigned int, Real> solver_params =
441 
442  const std::pair<unsigned int, Real> rval =
444  *temprhs,
445  double(solver_params.second),
446  solver_params.first);
447 
448  // The linear solver may not have fit our constraints exactly
449 #ifdef LIBMESH_ENABLE_CONSTRAINTS
451  (*this, &this->get_weighted_sensitivity_solution(),
452  /* homogeneous = */ true);
453 #endif
454 
455  return rval;
456 }
457 
458 
459 
461 {
462  ParameterVector & parameters_vec =
463  const_cast<ParameterVector &>(parameters_in);
464 
465  const unsigned int Np = cast_int<unsigned int>
466  (parameters_vec.size());
467 
468  for (unsigned int p=0; p != Np; ++p)
469  {
470  NumericVector<Number> & sensitivity_rhs = this->add_sensitivity_rhs(p);
471 
472  // Approximate -(partial R / partial p) by
473  // (R(p-dp) - R(p+dp)) / (2*dp)
474 
475  Number old_parameter = *parameters_vec[p];
476 
477  const Real delta_p =
478  TOLERANCE * std::max(std::abs(old_parameter), 1e-3);
479 
480  *parameters_vec[p] -= delta_p;
481 
482  // this->assembly(true, false, true);
483  this->assembly(true, false, false);
484  this->rhs->close();
485  sensitivity_rhs = *this->rhs;
486 
487  *parameters_vec[p] = old_parameter + delta_p;
488 
489  // this->assembly(true, false, true);
490  this->assembly(true, false, false);
491  this->rhs->close();
492 
493  sensitivity_rhs -= *this->rhs;
494  sensitivity_rhs /= (2*delta_p);
495  sensitivity_rhs.close();
496 
497  *parameters_vec[p] = old_parameter;
498  }
499 }
500 
501 
502 
504  const ParameterVector & parameters_in,
505  SensitivityData & sensitivities)
506 {
507  ParameterVector & parameters_vec =
508  const_cast<ParameterVector &>(parameters_in);
509 
510  const unsigned int Np = cast_int<unsigned int>
511  (parameters_vec.size());
512  const unsigned int Nq = this->n_qois();
513 
514  // An introduction to the problem:
515  //
516  // Residual R(u(p),p) = 0
517  // partial R / partial u = J = system matrix
518  //
519  // This implies that:
520  // d/dp(R) = 0
521  // (partial R / partial p) +
522  // (partial R / partial u) * (partial u / partial p) = 0
523 
524  // We first do an adjoint solve:
525  // J^T * z = (partial q / partial u)
526  // if we haven't already or dont have an initial condition for the adjoint
527  if (!this->is_adjoint_already_solved())
528  {
529  this->adjoint_solve(qoi_indices);
530  }
531 
532  this->assemble_residual_derivatives(parameters_in);
533 
534  // Get ready to fill in sensitivities:
535  sensitivities.allocate_data(qoi_indices, *this, parameters_vec);
536 
537  // We use the identities:
538  // dq/dp = (partial q / partial p) + (partial q / partial u) *
539  // (partial u / partial p)
540  // dq/dp = (partial q / partial p) + (J^T * z) *
541  // (partial u / partial p)
542  // dq/dp = (partial q / partial p) + z * J *
543  // (partial u / partial p)
544 
545  // Leading to our final formula:
546  // dq/dp = (partial q / partial p) - z * (partial R / partial p)
547 
548  // In the case of adjoints with heterogenous Dirichlet boundary
549  // function phi, where
550  // q := S(u) - R(u,phi)
551  // the final formula works out to:
552  // dq/dp = (partial S / partial p) - z * (partial R / partial p)
553  // Because we currently have no direct access to
554  // (partial S / partial p), we use the identity
555  // (partial S / partial p) = (partial q / partial p) +
556  // phi * (partial R / partial p)
557  // to derive an equivalent equation:
558  // dq/dp = (partial q / partial p) - (z-phi) * (partial R / partial p)
559 
560  // Since z-phi degrees of freedom are zero for constrained indices,
561  // we can use the same constrained -(partial R / partial p) that we
562  // use for forward sensitivity solves, taking into account the
563  // differing sign convention.
564  //
565  // Since that vector is constrained, its constrained indices are
566  // zero, so its product with phi is zero, so we can neglect the
567  // evaluation of phi terms.
568 
569  for (unsigned int j=0; j != Np; ++j)
570  {
571  // We currently get partial derivatives via central differencing
572 
573  // (partial q / partial p) ~= (q(p+dp)-q(p-dp))/(2*dp)
574  // (partial R / partial p) ~= (rhs(p+dp) - rhs(p-dp))/(2*dp)
575 
576  Number old_parameter = *parameters_vec[j];
577 
578  const Real delta_p =
579  TOLERANCE * std::max(std::abs(old_parameter), 1e-3);
580 
581  *parameters_vec[j] = old_parameter - delta_p;
582  this->assemble_qoi(qoi_indices);
583  const std::vector<Number> qoi_minus = this->get_qoi_values();
584 
585  NumericVector<Number> & neg_partialR_partialp = this->get_sensitivity_rhs(j);
586 
587  *parameters_vec[j] = old_parameter + delta_p;
588  this->assemble_qoi(qoi_indices);
589  const std::vector<Number> qoi_plus = this->get_qoi_values();
590 
591  std::vector<Number> partialq_partialp(Nq, 0);
592  for (unsigned int i=0; i != Nq; ++i)
593  if (qoi_indices.has_index(i))
594  partialq_partialp[i] = (qoi_plus[i] - qoi_minus[i]) / (2.*delta_p);
595 
596  // Don't leave the parameter changed
597  *parameters_vec[j] = old_parameter;
598 
599  for (unsigned int i=0; i != Nq; ++i)
600  if (qoi_indices.has_index(i))
601  sensitivities[i][j] = partialq_partialp[i] +
602  neg_partialR_partialp.dot(this->get_adjoint_solution(i));
603  }
604 
605  // All parameters_vec have been reset.
606  // Reset the original qoi.
607 
608  this->assemble_qoi(qoi_indices);
609 }
610 
611 
612 
614  const ParameterVector & parameters_in,
615  SensitivityData & sensitivities)
616 {
617  ParameterVector & parameters_vec =
618  const_cast<ParameterVector &>(parameters_in);
619 
620  const unsigned int Np = cast_int<unsigned int>
621  (parameters_vec.size());
622  const unsigned int Nq = this->n_qois();
623 
624  // An introduction to the problem:
625  //
626  // Residual R(u(p),p) = 0
627  // partial R / partial u = J = system matrix
628  //
629  // This implies that:
630  // d/dp(R) = 0
631  // (partial R / partial p) +
632  // (partial R / partial u) * (partial u / partial p) = 0
633 
634  // We first solve for (partial u / partial p) for each parameter:
635  // J * (partial u / partial p) = - (partial R / partial p)
636 
637  this->sensitivity_solve(parameters_vec);
638 
639  // Get ready to fill in sensitivities:
640  sensitivities.allocate_data(qoi_indices, *this, parameters_vec);
641 
642  // We use the identity:
643  // dq/dp = (partial q / partial p) + (partial q / partial u) *
644  // (partial u / partial p)
645 
646  // We get (partial q / partial u) from the user
647  this->assemble_qoi_derivative(qoi_indices,
648  /* include_liftfunc = */ true,
649  /* apply_constraints = */ false);
650 
651  // We don't need these to be closed() in this function, but libMesh
652  // standard practice is to have them closed() by the time the
653  // function exits
654  for (auto i : make_range(this->n_qois()))
655  if (qoi_indices.has_index(i))
656  this->get_adjoint_rhs(i).close();
657 
658  for (unsigned int j=0; j != Np; ++j)
659  {
660  // We currently get partial derivatives via central differencing
661 
662  // (partial q / partial p) ~= (q(p+dp)-q(p-dp))/(2*dp)
663 
664  Number old_parameter = *parameters_vec[j];
665 
666  const Real delta_p =
667  TOLERANCE * std::max(std::abs(old_parameter), 1e-3);
668 
669  *parameters_vec[j] = old_parameter - delta_p;
670  this->assemble_qoi(qoi_indices);
671  const std::vector<Number> qoi_minus = this->get_qoi_values();
672 
673  *parameters_vec[j] = old_parameter + delta_p;
674  this->assemble_qoi(qoi_indices);
675  const std::vector<Number> qoi_plus = this->get_qoi_values();
676 
677  std::vector<Number> partialq_partialp(Nq, 0);
678  for (unsigned int i=0; i != Nq; ++i)
679  if (qoi_indices.has_index(i))
680  partialq_partialp[i] = (qoi_plus[i] - qoi_minus[i]) / (2.*delta_p);
681 
682  // Don't leave the parameter changed
683  *parameters_vec[j] = old_parameter;
684 
685  for (unsigned int i=0; i != Nq; ++i)
686  if (qoi_indices.has_index(i))
687  sensitivities[i][j] = partialq_partialp[i] +
688  this->get_adjoint_rhs(i).dot(this->get_sensitivity_solution(j));
689  }
690 
691  // All parameters_vec have been reset.
692  // We didn't cache the original rhs or matrix for memory reasons,
693  // but we can restore them to a state consistent solution -
694  // principle of least surprise.
695  this->assembly(true, true);
696  this->rhs->close();
697  this->matrix->close();
698  this->assemble_qoi(qoi_indices);
699 }
700 
701 
702 
704  const ParameterVector & parameters_in,
705  const ParameterVector & vector,
706  SensitivityData & sensitivities)
707 {
708  // We currently get partial derivatives via finite differencing
709  const Real delta_p = TOLERANCE;
710 
711  ParameterVector & parameters_vec =
712  const_cast<ParameterVector &>(parameters_in);
713 
714  // We'll use a single temporary vector for matrix-vector-vector products
715  std::unique_ptr<NumericVector<Number>> tempvec = this->solution->zero_clone();
716 
717  const unsigned int Np = cast_int<unsigned int>
718  (parameters_vec.size());
719  const unsigned int Nq = this->n_qois();
720 
721  // For each quantity of interest q, the parameter sensitivity
722  // Hessian is defined as q''_{kl} = {d^2 q}/{d p_k d p_l}.
723  // Given a vector of parameter perturbation weights w_l, this
724  // function evaluates the hessian-vector product sum_l(q''_{kl}*w_l)
725  //
726  // We calculate it from values and partial derivatives of the
727  // quantity of interest function Q, solution u, adjoint solution z,
728  // parameter sensitivity adjoint solutions z^l, and residual R, as:
729  //
730  // sum_l(q''_{kl}*w_l) =
731  // sum_l(w_l * Q''_{kl}) + Q''_{uk}(u)*(sum_l(w_l u'_l)) -
732  // R'_k(u, sum_l(w_l*z^l)) - R'_{uk}(u,z)*(sum_l(w_l u'_l) -
733  // sum_l(w_l*R''_{kl}(u,z))
734  //
735  // See the adjoints model document for more details.
736 
737  // We first do an adjoint solve to get z for each quantity of
738  // interest
739  // if we haven't already or dont have an initial condition for the adjoint
740  if (!this->is_adjoint_already_solved())
741  {
742  this->adjoint_solve(qoi_indices);
743  }
744 
745  // Get ready to fill in sensitivities:
746  sensitivities.allocate_data(qoi_indices, *this, parameters_vec);
747 
748  // We can't solve for all the solution sensitivities u'_l or for all
749  // of the parameter sensitivity adjoint solutions z^l without
750  // requiring O(Nq*Np) linear solves. So we'll solve directly for their
751  // weighted sum - this is just O(Nq) solves.
752 
753  // First solve for sum_l(w_l u'_l).
754  this->weighted_sensitivity_solve(parameters_vec, vector);
755 
756  // Then solve for sum_l(w_l z^l).
757  this->weighted_sensitivity_adjoint_solve(parameters_vec, vector, qoi_indices);
758 
759  for (unsigned int k=0; k != Np; ++k)
760  {
761  // We approximate sum_l(w_l * Q''_{kl}) with a central
762  // differencing perturbation:
763  // sum_l(w_l * Q''_{kl}) ~=
764  // (Q(p + dp*w_l*e_l + dp*e_k) - Q(p - dp*w_l*e_l + dp*e_k) -
765  // Q(p + dp*w_l*e_l - dp*e_k) + Q(p - dp*w_l*e_l - dp*e_k))/(4*dp^2)
766 
767  // The sum(w_l*R''_kl) term requires the same sort of perturbation,
768  // and so we subtract it in at the same time:
769  // sum_l(w_l * R''_{kl}) ~=
770  // (R(p + dp*w_l*e_l + dp*e_k) - R(p - dp*w_l*e_l + dp*e_k) -
771  // R(p + dp*w_l*e_l - dp*e_k) + R(p - dp*w_l*e_l - dp*e_k))/(4*dp^2)
772 
773  ParameterVector oldparameters, parameterperturbation;
774  parameters_vec.deep_copy(oldparameters);
775  vector.deep_copy(parameterperturbation);
776  parameterperturbation *= delta_p;
777  parameters_vec += parameterperturbation;
778 
779  Number old_parameter = *parameters_vec[k];
780 
781  *parameters_vec[k] = old_parameter + delta_p;
782  this->assemble_qoi(qoi_indices);
783  this->assembly(true, false, true);
784  this->rhs->close();
785  std::vector<Number> partial2q_term = this->get_qoi_values();
786  std::vector<Number> partial2R_term(this->n_qois());
787  for (unsigned int i=0; i != Nq; ++i)
788  if (qoi_indices.has_index(i))
789  partial2R_term[i] = this->rhs->dot(this->get_adjoint_solution(i));
790 
791  *parameters_vec[k] = old_parameter - delta_p;
792  this->assemble_qoi(qoi_indices);
793  this->assembly(true, false, true);
794  this->rhs->close();
795  for (unsigned int i=0; i != Nq; ++i)
796  if (qoi_indices.has_index(i))
797  {
798  partial2q_term[i] -= this->get_qoi_value(i);
799  partial2R_term[i] -= this->rhs->dot(this->get_adjoint_solution(i));
800  }
801 
802  oldparameters.value_copy(parameters_vec);
803  parameterperturbation *= -1.0;
804  parameters_vec += parameterperturbation;
805 
806  // Re-center old_parameter, which may be affected by vector
807  old_parameter = *parameters_vec[k];
808 
809  *parameters_vec[k] = old_parameter + delta_p;
810  this->assemble_qoi(qoi_indices);
811  this->assembly(true, false, true);
812  this->rhs->close();
813  for (unsigned int i=0; i != Nq; ++i)
814  if (qoi_indices.has_index(i))
815  {
816  partial2q_term[i] -= this->get_qoi_value(i);
817  partial2R_term[i] -= this->rhs->dot(this->get_adjoint_solution(i));
818  }
819 
820  *parameters_vec[k] = old_parameter - delta_p;
821  this->assemble_qoi(qoi_indices);
822  this->assembly(true, false, true);
823  this->rhs->close();
824  for (unsigned int i=0; i != Nq; ++i)
825  if (qoi_indices.has_index(i))
826  {
827  partial2q_term[i] += this->get_qoi_value(i);
828  partial2R_term[i] += this->rhs->dot(this->get_adjoint_solution(i));
829  }
830 
831  for (unsigned int i=0; i != Nq; ++i)
832  if (qoi_indices.has_index(i))
833  {
834  partial2q_term[i] /= (4. * delta_p * delta_p);
835  partial2R_term[i] /= (4. * delta_p * delta_p);
836  }
837 
838  for (unsigned int i=0; i != Nq; ++i)
839  if (qoi_indices.has_index(i))
840  sensitivities[i][k] = partial2q_term[i] - partial2R_term[i];
841 
842  // We get (partial q / partial u), R, and
843  // (partial R / partial u) from the user, but centrally
844  // difference to get q_uk, R_k, and R_uk terms:
845  // (partial R / partial k)
846  // 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)
847  // (partial^2 q / partial u partial k)
848  // q_uk = (q_u(p+dp*e_k) - q_u(p-dp*e_k))/(2*dp)
849  // (partial^2 R / partial u partial k)
850  // 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)
851 
852  // To avoid creating Nq temporary vectors for q_uk or R_uk, we add
853  // subterms to the sensitivities output one by one.
854  //
855  // FIXME: this is probably a bad order of operations for
856  // controlling floating point error.
857 
858  *parameters_vec[k] = old_parameter + delta_p;
859  this->assembly(true, true);
860  this->rhs->close();
861  this->matrix->close();
862  this->assemble_qoi_derivative(qoi_indices,
863  /* include_liftfunc = */ true,
864  /* apply_constraints = */ false);
865 
866  this->matrix->vector_mult(*tempvec, this->get_weighted_sensitivity_solution());
867 
868  for (unsigned int i=0; i != Nq; ++i)
869  if (qoi_indices.has_index(i))
870  {
871  this->get_adjoint_rhs(i).close();
872  sensitivities[i][k] += (this->get_adjoint_rhs(i).dot(this->get_weighted_sensitivity_solution()) -
874  this->get_adjoint_solution(i).dot(*tempvec)) / (2.*delta_p);
875  }
876 
877  *parameters_vec[k] = old_parameter - delta_p;
878  this->assembly(true, true);
879  this->rhs->close();
880  this->matrix->close();
881  this->assemble_qoi_derivative(qoi_indices,
882  /* include_liftfunc = */ true,
883  /* apply_constraints = */ false);
884 
885  this->matrix->vector_mult(*tempvec, this->get_weighted_sensitivity_solution());
886 
887  for (unsigned int i=0; i != Nq; ++i)
888  if (qoi_indices.has_index(i))
889  {
890  this->get_adjoint_rhs(i).close();
891  sensitivities[i][k] += (-this->get_adjoint_rhs(i).dot(this->get_weighted_sensitivity_solution()) +
893  this->get_adjoint_solution(i).dot(*tempvec)) / (2.*delta_p);
894  }
895  }
896 
897  // All parameters have been reset.
898  // Don't leave the qoi or system changed - principle of least
899  // surprise.
900  this->assembly(true, true);
901  this->rhs->close();
902  this->matrix->close();
903  this->assemble_qoi(qoi_indices);
904 }
905 
906 
907 
909  const ParameterVector & parameters_in,
910  SensitivityData & sensitivities)
911 {
912  // We currently get partial derivatives via finite differencing
913  const Real delta_p = TOLERANCE;
914 
915  ParameterVector & parameters_vec =
916  const_cast<ParameterVector &>(parameters_in);
917 
918  // We'll use one temporary vector for matrix-vector-vector products
919  std::unique_ptr<NumericVector<Number>> tempvec = this->solution->zero_clone();
920 
921  // And another temporary vector to hold a copy of the true solution
922  // so we can safely perturb this->solution.
923  std::unique_ptr<NumericVector<Number>> oldsolution = this->solution->clone();
924 
925  const unsigned int Np = cast_int<unsigned int>
926  (parameters_vec.size());
927  const unsigned int Nq = this->n_qois();
928 
929  // For each quantity of interest q, the parameter sensitivity
930  // Hessian is defined as q''_{kl} = {d^2 q}/{d p_k d p_l}.
931  //
932  // We calculate it from values and partial derivatives of the
933  // quantity of interest function Q, solution u, adjoint solution z,
934  // and residual R, as:
935  //
936  // q''_{kl} =
937  // Q''_{kl} + Q''_{uk}(u)*u'_l + Q''_{ul}(u) * u'_k +
938  // Q''_{uu}(u)*u'_k*u'_l -
939  // R''_{kl}(u,z) -
940  // R''_{uk}(u,z)*u'_l - R''_{ul}(u,z)*u'_k -
941  // R''_{uu}(u,z)*u'_k*u'_l
942  //
943  // See the adjoints model document for more details.
944 
945  // We first do an adjoint solve to get z for each quantity of
946  // interest
947  // if we haven't already or dont have an initial condition for the adjoint
948  if (!this->is_adjoint_already_solved())
949  {
950  this->adjoint_solve(qoi_indices);
951  }
952 
953  // And a sensitivity solve to get u_k for each parameter
954  this->sensitivity_solve(parameters_vec);
955 
956  // Get ready to fill in second derivatives:
957  sensitivities.allocate_hessian_data(qoi_indices, *this, parameters_vec);
958 
959  for (unsigned int k=0; k != Np; ++k)
960  {
961  Number old_parameterk = *parameters_vec[k];
962 
963  // The Hessian is symmetric, so we just calculate the lower
964  // triangle and the diagonal, and we get the upper triangle from
965  // the transpose of the lower
966 
967  for (unsigned int l=0; l != k+1; ++l)
968  {
969  // The second partial derivatives with respect to parameters_vec
970  // are all calculated via a central finite difference
971  // stencil:
972  // F''_{kl} ~= (F(p+dp*e_k+dp*e_l) - F(p+dp*e_k-dp*e_l) -
973  // F(p-dp*e_k+dp*e_l) + F(p-dp*e_k-dp*e_l))/(4*dp^2)
974  // We will add Q''_{kl}(u) and subtract R''_{kl}(u,z) at the
975  // same time.
976  //
977  // We have to be careful with the perturbations to handle
978  // the k=l case
979 
980  Number old_parameterl = *parameters_vec[l];
981 
982  *parameters_vec[k] += delta_p;
983  *parameters_vec[l] += delta_p;
984  this->assemble_qoi(qoi_indices);
985  this->assembly(true, false, true);
986  this->rhs->close();
987  std::vector<Number> partial2q_term = this->get_qoi_values();
988  std::vector<Number> partial2R_term(this->n_qois());
989  for (unsigned int i=0; i != Nq; ++i)
990  if (qoi_indices.has_index(i))
991  partial2R_term[i] = this->rhs->dot(this->get_adjoint_solution(i));
992 
993  *parameters_vec[l] -= 2.*delta_p;
994  this->assemble_qoi(qoi_indices);
995  this->assembly(true, false, true);
996  this->rhs->close();
997  for (unsigned int i=0; i != Nq; ++i)
998  if (qoi_indices.has_index(i))
999  {
1000  partial2q_term[i] -= this->get_qoi_value(i);
1001  partial2R_term[i] -= this->rhs->dot(this->get_adjoint_solution(i));
1002  }
1003 
1004  *parameters_vec[k] -= 2.*delta_p;
1005  this->assemble_qoi(qoi_indices);
1006  this->assembly(true, false, true);
1007  this->rhs->close();
1008  for (unsigned int i=0; i != Nq; ++i)
1009  if (qoi_indices.has_index(i))
1010  {
1011  partial2q_term[i] += this->get_qoi_value(i);
1012  partial2R_term[i] += this->rhs->dot(this->get_adjoint_solution(i));
1013  }
1014 
1015  *parameters_vec[l] += 2.*delta_p;
1016  this->assemble_qoi(qoi_indices);
1017  this->assembly(true, false, true);
1018  this->rhs->close();
1019  for (unsigned int i=0; i != Nq; ++i)
1020  if (qoi_indices.has_index(i))
1021  {
1022  partial2q_term[i] -= this->get_qoi_value(i);
1023  partial2R_term[i] -= this->rhs->dot(this->get_adjoint_solution(i));
1024  partial2q_term[i] /= (4. * delta_p * delta_p);
1025  partial2R_term[i] /= (4. * delta_p * delta_p);
1026  }
1027 
1028  for (unsigned int i=0; i != Nq; ++i)
1029  if (qoi_indices.has_index(i))
1030  {
1031  Number current_terms = partial2q_term[i] - partial2R_term[i];
1032  sensitivities.second_derivative(i,k,l) += current_terms;
1033  if (k != l)
1034  sensitivities.second_derivative(i,l,k) += current_terms;
1035  }
1036 
1037  // Don't leave the parameters_vec perturbed
1038  *parameters_vec[l] = old_parameterl;
1039  *parameters_vec[k] = old_parameterk;
1040  }
1041 
1042  // We get (partial q / partial u) and
1043  // (partial R / partial u) from the user, but centrally
1044  // difference to get q_uk and R_uk terms:
1045  // (partial^2 q / partial u partial k)
1046  // q_uk*u'_l = (q_u(p+dp*e_k)*u'_l - q_u(p-dp*e_k)*u'_l)/(2*dp)
1047  // 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)
1048  //
1049  // To avoid creating Nq temporary vectors, we add these
1050  // subterms to the sensitivities output one by one.
1051  //
1052  // FIXME: this is probably a bad order of operations for
1053  // controlling floating point error.
1054 
1055  *parameters_vec[k] = old_parameterk + delta_p;
1056  this->assembly(false, true);
1057  this->matrix->close();
1058  this->assemble_qoi_derivative(qoi_indices,
1059  /* include_liftfunc = */ true,
1060  /* apply_constraints = */ false);
1061 
1062  for (unsigned int l=0; l != Np; ++l)
1063  {
1064  this->matrix->vector_mult(*tempvec, this->get_sensitivity_solution(l));
1065  for (unsigned int i=0; i != Nq; ++i)
1066  if (qoi_indices.has_index(i))
1067  {
1068  this->get_adjoint_rhs(i).close();
1069  Number current_terms =
1070  (this->get_adjoint_rhs(i).dot(this->get_sensitivity_solution(l)) -
1071  tempvec->dot(this->get_adjoint_solution(i))) / (2.*delta_p);
1072  sensitivities.second_derivative(i,k,l) += current_terms;
1073 
1074  // We use the _uk terms twice; symmetry lets us reuse
1075  // these calculations for the _ul terms.
1076 
1077  sensitivities.second_derivative(i,l,k) += current_terms;
1078  }
1079  }
1080 
1081  *parameters_vec[k] = old_parameterk - delta_p;
1082  this->assembly(false, true);
1083  this->matrix->close();
1084  this->assemble_qoi_derivative(qoi_indices,
1085  /* include_liftfunc = */ true,
1086  /* apply_constraints = */ false);
1087 
1088  for (unsigned int l=0; l != Np; ++l)
1089  {
1090  this->matrix->vector_mult(*tempvec, this->get_sensitivity_solution(l));
1091  for (unsigned int i=0; i != Nq; ++i)
1092  if (qoi_indices.has_index(i))
1093  {
1094  this->get_adjoint_rhs(i).close();
1095  Number current_terms =
1096  (-this->get_adjoint_rhs(i).dot(this->get_sensitivity_solution(l)) +
1097  tempvec->dot(this->get_adjoint_solution(i))) / (2.*delta_p);
1098  sensitivities.second_derivative(i,k,l) += current_terms;
1099 
1100  // We use the _uk terms twice; symmetry lets us reuse
1101  // these calculations for the _ul terms.
1102 
1103  sensitivities.second_derivative(i,l,k) += current_terms;
1104  }
1105  }
1106 
1107  // Don't leave the parameter perturbed
1108  *parameters_vec[k] = old_parameterk;
1109 
1110  // Our last remaining terms are -R_uu(u,z)*u_k*u_l and
1111  // Q_uu(u)*u_k*u_l
1112  //
1113  // We take directional central finite differences of R_u and Q_u
1114  // to approximate these terms, e.g.:
1115  //
1116  // Q_uu(u)*u_k ~= (Q_u(u+dp*u_k) - Q_u(u-dp*u_k))/(2*dp)
1117 
1118  *this->solution = this->get_sensitivity_solution(k);
1119  *this->solution *= delta_p;
1120  *this->solution += *oldsolution;
1121 
1122  // We've modified solution, so we need to update before calling
1123  // assembly since assembly may only use current_local_solution
1124  this->update();
1125  this->assembly(false, true);
1126  this->matrix->close();
1127  this->assemble_qoi_derivative(qoi_indices,
1128  /* include_liftfunc = */ true,
1129  /* apply_constraints = */ false);
1130 
1131  // The Hessian is symmetric, so we just calculate the lower
1132  // triangle and the diagonal, and we get the upper triangle from
1133  // the transpose of the lower
1134  //
1135  // Note that, because we took the directional finite difference
1136  // with respect to k and not l, we've added an O(delta_p^2)
1137  // error to any permutational symmetry in the Hessian...
1138  for (unsigned int l=0; l != k+1; ++l)
1139  {
1140  this->matrix->vector_mult(*tempvec, this->get_sensitivity_solution(l));
1141  for (unsigned int i=0; i != Nq; ++i)
1142  if (qoi_indices.has_index(i))
1143  {
1144  this->get_adjoint_rhs(i).close();
1145  Number current_terms =
1146  (this->get_adjoint_rhs(i).dot(this->get_sensitivity_solution(l)) -
1147  tempvec->dot(this->get_adjoint_solution(i))) / (2.*delta_p);
1148  sensitivities.second_derivative(i,k,l) += current_terms;
1149  if (k != l)
1150  sensitivities.second_derivative(i,l,k) += current_terms;
1151  }
1152  }
1153 
1154  *this->solution = this->get_sensitivity_solution(k);
1155  *this->solution *= -delta_p;
1156  *this->solution += *oldsolution;
1157 
1158  // We've modified solution, so we need to update before calling
1159  // assembly since assembly may only use current_local_solution
1160  this->update();
1161  this->assembly(false, true);
1162  this->matrix->close();
1163  this->assemble_qoi_derivative(qoi_indices,
1164  /* include_liftfunc = */ true,
1165  /* apply_constraints = */ false);
1166 
1167  for (unsigned int l=0; l != k+1; ++l)
1168  {
1169  this->matrix->vector_mult(*tempvec, this->get_sensitivity_solution(l));
1170  for (unsigned int i=0; i != Nq; ++i)
1171  if (qoi_indices.has_index(i))
1172  {
1173  this->get_adjoint_rhs(i).close();
1174  Number current_terms =
1175  (-this->get_adjoint_rhs(i).dot(this->get_sensitivity_solution(l)) +
1176  tempvec->dot(this->get_adjoint_solution(i))) / (2.*delta_p);
1177  sensitivities.second_derivative(i,k,l) += current_terms;
1178  if (k != l)
1179  sensitivities.second_derivative(i,l,k) += current_terms;
1180  }
1181  }
1182 
1183  // Don't leave the solution perturbed
1184  *this->solution = *oldsolution;
1185  }
1186 
1187  // All parameters_vec have been reset.
1188  // Don't leave the qoi or system changed - principle of least
1189  // surprise.
1190  // We've modified solution, so we need to update before calling
1191  // assembly since assembly may only use current_local_solution
1192  this->update();
1193  this->assembly(true, true);
1194  this->rhs->close();
1195  this->matrix->close();
1196  this->assemble_qoi(qoi_indices);
1197 }
1198 
1199 
1200 
1202 {
1203  // Note: we always start "from scratch" to mimic the original
1204  // behavior of this function. The goal is not to reuse the
1205  // LinearSolver object, but to manage its lifetime in a more
1206  // consistent manner.
1207  linear_solver.reset();
1208 
1210 
1211  if (this->prefix_with_name())
1212  linear_solver->init(this->prefix().c_str());
1213  else
1214  linear_solver->init();
1215 
1216  linear_solver->init_names(*this);
1217 
1218  return linear_solver.get();
1219 }
1220 
1221 
1222 
1223 std::pair<unsigned int, Real> ImplicitSystem::get_linear_solve_parameters() const
1224 {
1225  return std::make_pair(
1226  parameters.have_parameter<unsigned int>("linear solver maximum iterations")
1227  ? parameters.get<unsigned int>("linear solver maximum iterations")
1228  : this->get_equation_systems().parameters.get<unsigned int>(
1229  "linear solver maximum iterations"),
1230  parameters.have_parameter<Real>("linear solver tolerance")
1231  ? parameters.get<Real>("linear solver tolerance")
1232  : this->get_equation_systems().parameters.get<Real>("linear solver tolerance"));
1233 }
1234 
1235 
1236 
1237 #ifdef LIBMESH_ENABLE_DEPRECATED
1239 {
1240  // This function was originally paired with get_linear_solver()
1241  // calls when that returned a dumb pointer which needed to be
1242  // cleaned up. Since get_linear_solver() now just returns a pointer
1243  // to a LinearSolver object managed by this class, this function no
1244  // longer needs to do any cleanup.
1245  libmesh_deprecated();
1246 }
1247 #endif // LIBMESH_ENABLE_DEPRECATED
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 
1267 } // 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:395
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...
NumericVector< Number > & add_adjoint_solution(unsigned int i=0)
Definition: system.C:1211
Data structure for specifying which Parameters should be independent variables in a parameter sensiti...
virtual void create_static_condensation()
Request that static condensation be performed for this system.
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
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:2364
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:2608
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:1170
NumericVector< Number > & get_sensitivity_rhs(unsigned int i=0)
Definition: system.C:1315
const EquationSystems & get_equation_systems() const
Definition: system.h:718
virtual void assemble()
Prepares matrix and _dof_map for matrix assembly.
Definition: system.C:553
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:1305
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...
NumericVector< Number > & add_weighted_sensitivity_solution()
Definition: system.C:1190
const SparseMatrix< Number > * request_matrix(std::string_view mat_name) const
Definition: system.C:1078
Parameters parameters
Parameters for the system. If a parameter is not provided, it should be retrieved from the EquationSy...
Definition: system.h:1523
const MeshBase & get_mesh() const
Definition: system.h:2345
NumericVector< Number > & get_weighted_sensitivity_solution()
Definition: system.C:1197
virtual void release_linear_solver(LinearSolver< Number > *) const
Currently a no-op.
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:2448
NumericVector< Number > & add_weighted_sensitivity_adjoint_solution(unsigned int i=0)
Definition: system.C:1243
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 add_static_condensation(const StaticCondensation &sc)
Add a static condensation class.
Definition: dof_map.h:1732
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:426
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.
std::string prefix() const
Definition: system.h:1935
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:96
virtual void add_matrices()
Insertion point for adding matrices in derived classes before init_matrices() is called.
Definition: system.h:1951
StaticCondensation * _sc
Static condensation class.
std::unique_ptr< NumericVector< Number > > solution
Data structure to hold solution values.
Definition: system.h:1590
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:2686
bool is_adjoint_already_solved() const
Accessor for the adjoint_already_solved boolean.
Definition: system.h:406
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:497
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:140
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:1223
Parameters parameters
Data structure holding arbitrary parameters.
virtual void assemble() override
Prepares matrix and rhs for system assembly, then calls user assembly function.
bool on_command_line(std::string arg)
Definition: libmesh.C:987
NumericVector< Number > & add_sensitivity_solution(unsigned int i=0)
Definition: system.C:1160
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:1544
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:997
const DofMap & get_dof_map() const
Definition: system.h:2361
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:1102
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.
NumericVector< Number > & get_weighted_sensitivity_adjoint_solution(unsigned int i=0)
Definition: system.C:1255
std::vector< Number > get_qoi_values() const
Returns a copy of qoi, not a reference.
Definition: system.C:2371
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:2444
NumericVector< Number > & get_adjoint_rhs(unsigned int i=0)
Definition: system.C:1285
bool prefix_with_name() const
Definition: system.h:1929