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