libMesh
implicit_system.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2019 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/utility.h"
35 
36 namespace libMesh
37 {
38 
39 // ------------------------------------------------------------
40 // ImplicitSystem implementation
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  _can_add_matrices (true)
49 {
50  // Add the system matrix.
51  this->add_system_matrix ();
52 }
53 
54 
55 
57 {
58  // Clear data
59  this->clear();
60 
61  remove_matrix("System Matrix");
62 }
63 
64 
65 
67 {
68  // clear the parent data
69  Parent::clear();
70 
71  // clear any user-added matrices
72  {
73  for (auto & pr : _matrices)
74  {
75  pr.second->clear ();
76  delete pr.second;
77  pr.second = nullptr;
78  }
79 
80  _matrices.clear();
81  _can_add_matrices = true;
82  }
83 
84  // Restore us to a "basic" state
85  this->add_system_matrix ();
86 }
87 
88 
89 
91 {
92  // initialize parent data
94 
95  // Clear any existing matrices
96  for (auto & pr : _matrices)
97  pr.second->clear();
98 
99  // Initialize the matrices for the system
100  this->init_matrices ();
101 }
102 
103 
104 
106 {
108 
109  // Check for quick return in case the system matrix
110  // (and by extension all the matrices) has already
111  // been initialized
112  if (matrix->initialized())
113  return;
114 
115  // Get a reference to the DofMap
116  DofMap & dof_map = this->get_dof_map();
117 
118  // no chance to add other matrices
119  _can_add_matrices = false;
120 
121  // Tell the matrices about the dof map, and vice versa
122  for (auto & pr : _matrices)
123  {
124  SparseMatrix<Number> & m = *(pr.second);
126 
127  // We want to allow repeated init() on systems, but we don't
128  // want to attach the same matrix to the DofMap twice
129  if (!dof_map.is_attached(m))
130  dof_map.attach_matrix (m);
131  }
132 
133  // Compute the sparsity pattern for the current
134  // mesh and DOF distribution. This also updates
135  // additional matrices, \p DofMap now knows them
136  dof_map.compute_sparsity (this->get_mesh());
137 
138  // Initialize matrices
139  for (auto & pr : _matrices)
140  pr.second->init ();
141 
142  // Set the additional matrices to 0.
143  for (auto & pr : _matrices)
144  pr.second->zero ();
145 }
146 
147 
148 
150 {
151  // initialize parent data
152  Parent::reinit();
153 
154  // Get a reference to the DofMap
155  DofMap & dof_map = this->get_dof_map();
156 
157  // Clear the matrices
158  for (auto & pr : _matrices)
159  {
160  pr.second->clear();
161  pr.second->attach_dof_map (dof_map);
162  }
163 
164  // Clear the sparsity pattern
165  this->get_dof_map().clear_sparsity();
166 
167  // Compute the sparsity pattern for the current
168  // mesh and DOF distribution. This also updates
169  // additional matrices, \p DofMap now knows them
170  dof_map.compute_sparsity (this->get_mesh());
171 
172  // Initialize matrices
173  for (auto & pr : _matrices)
174  pr.second->init ();
175 
176  // Set the additional matrices to 0.
177  for (auto & pr : _matrices)
178  pr.second->zero ();
179 }
180 
181 
182 
184 {
189 
191  {
192  matrix->zero ();
193  rhs->zero ();
194  }
195 
196  // Call the base class assemble function
197  Parent::assemble ();
198 }
199 
200 
201 
202 SparseMatrix<Number> & ImplicitSystem::add_matrix (const std::string & mat_name)
203 {
204  // only add matrices before initializing...
205  if (!_can_add_matrices)
206  libmesh_error_msg("ERROR: Too late. Cannot add matrices to the system after initialization"
207  << "\n any more. You should have done this earlier.");
208 
209  // Return the matrix if it is already there.
210  if (this->have_matrix(mat_name))
211  return *(_matrices[mat_name]);
212 
213  // Otherwise build the matrix and return it.
214  SparseMatrix<Number> * buf = SparseMatrix<Number>::build(this->comm()).release();
215  _matrices.insert (std::make_pair (mat_name, buf));
216 
217  return *buf;
218 }
219 
220 
221 void ImplicitSystem::remove_matrix (const std::string & mat_name)
222 {
223  matrices_iterator pos = _matrices.find (mat_name);
224 
225  //Return if the matrix does not exist
226  if (pos == _matrices.end())
227  return;
228 
229  delete pos->second;
230 
231  _matrices.erase(pos);
232 }
233 
234 
235 
236 const SparseMatrix<Number> * ImplicitSystem::request_matrix (const std::string & mat_name) const
237 {
238  // Make sure the matrix exists
239  const_matrices_iterator pos = _matrices.find (mat_name);
240 
241  if (pos == _matrices.end())
242  return nullptr;
243 
244  return pos->second;
245 }
246 
247 
248 
249 SparseMatrix<Number> * ImplicitSystem::request_matrix (const std::string & mat_name)
250 {
251  // Make sure the matrix exists
252  matrices_iterator pos = _matrices.find (mat_name);
253 
254  if (pos == _matrices.end())
255  return nullptr;
256 
257  return pos->second;
258 }
259 
260 
261 
262 const SparseMatrix<Number> & ImplicitSystem::get_matrix (const std::string & mat_name) const
263 {
264  return *(libmesh_map_find(_matrices, mat_name));
265 }
266 
267 
268 
269 SparseMatrix<Number> & ImplicitSystem::get_matrix (const std::string & mat_name)
270 {
271  return *(libmesh_map_find(_matrices, mat_name));
272 }
273 
274 
275 
277 {
278  // Possible that we cleared the _matrices but
279  // forgot to update the matrix pointer?
280  if (_matrices.empty())
281  matrix = nullptr;
282 
283  // Only need to add the matrix if it isn't there
284  // already!
285  if (matrix == nullptr)
286  matrix = &(this->add_matrix ("System Matrix"));
287 
289 }
290 
291 
292 
294  this->assemble_before_solve = true;
295  this->get_linear_solver()->reuse_preconditioner(false);
296 }
297 
298 
299 
300 std::pair<unsigned int, Real>
302 {
303  // Log how long the linear solve takes.
304  LOG_SCOPE("sensitivity_solve()", "ImplicitSystem");
305 
306  // The forward system should now already be solved.
307  // Now assemble the corresponding sensitivity system.
308 
309  if (this->assemble_before_solve)
310  {
311  // Build the Jacobian
312  this->assembly(false, true);
313  this->matrix->close();
314 
315  // Reset and build the RHS from the residual derivatives
316  this->assemble_residual_derivatives(parameters);
317  }
318 
319  // The sensitivity problem is linear
320  LinearSolver<Number> * linear_solver = this->get_linear_solver();
321 
322  // Our iteration counts and residuals will be sums of the individual
323  // results
324  std::pair<unsigned int, Real> solver_params =
326  std::pair<unsigned int, Real> totalrval = std::make_pair(0,0.0);
327 
328  // Solve the linear system.
329  SparseMatrix<Number> * pc = this->request_matrix("Preconditioner");
330  for (auto p : IntRange<unsigned int>(0, parameters.size()))
331  {
332  std::pair<unsigned int, Real> rval =
333  linear_solver->solve (*matrix, pc,
334  this->add_sensitivity_solution(p),
335  this->get_sensitivity_rhs(p),
336  double(solver_params.second),
337  solver_params.first);
338 
339  totalrval.first += rval.first;
340  totalrval.second += rval.second;
341  }
342 
343  // The linear solver may not have fit our constraints exactly
344 #ifdef LIBMESH_ENABLE_CONSTRAINTS
345  for (auto p : IntRange<unsigned int>(0, parameters.size()))
347  (*this, &this->get_sensitivity_solution(p),
348  /* homogeneous = */ true);
349 #endif
350 
351  this->release_linear_solver(linear_solver);
352 
353  return totalrval;
354 }
355 
356 
357 
358 std::pair<unsigned int, Real>
360 {
361  // Log how long the linear solve takes.
362  LOG_SCOPE("adjoint_solve()", "ImplicitSystem");
363 
364  if (this->assemble_before_solve)
365  // Assemble the linear system
366  this->assembly (/* get_residual = */ false,
367  /* get_jacobian = */ true);
368 
369  // The adjoint problem is linear
370  LinearSolver<Number> * linear_solver = this->get_linear_solver();
371 
372  // Reset and build the RHS from the QOI derivative
373  this->assemble_qoi_derivative(qoi_indices,
374  /* include_liftfunc = */ false,
375  /* apply_constraints = */ true);
376 
377  // Our iteration counts and residuals will be sums of the individual
378  // results
379  std::pair<unsigned int, Real> solver_params =
381  std::pair<unsigned int, Real> totalrval = std::make_pair(0,0.0);
382 
383  for (auto i : IntRange<unsigned int>(0, this->n_qois()))
384  if (qoi_indices.has_index(i))
385  {
386  const std::pair<unsigned int, Real> rval =
387  linear_solver->adjoint_solve (*matrix, this->add_adjoint_solution(i),
388  this->get_adjoint_rhs(i),
389  double(solver_params.second),
390  solver_params.first);
391 
392  totalrval.first += rval.first;
393  totalrval.second += rval.second;
394  }
395 
396  this->release_linear_solver(linear_solver);
397 
398  // The linear solver may not have fit our constraints exactly
399 #ifdef LIBMESH_ENABLE_CONSTRAINTS
400  for (auto i : IntRange<unsigned int>(0, this->n_qois()))
401  if (qoi_indices.has_index(i))
403  (this->get_adjoint_solution(i), i);
404 #endif
405 
406  return totalrval;
407 }
408 
409 
410 
411 std::pair<unsigned int, Real>
413  const ParameterVector & weights,
414  const QoISet & qoi_indices)
415 {
416  // Log how long the linear solve takes.
417  LOG_SCOPE("weighted_sensitivity_adjoint_solve()", "ImplicitSystem");
418 
419  // We currently get partial derivatives via central differencing
420  const Real delta_p = TOLERANCE;
421 
422  ParameterVector & parameters =
423  const_cast<ParameterVector &>(parameters_in);
424 
425  // The forward system should now already be solved.
426  // The adjoint system should now already be solved.
427  // Now we're assembling a weighted sum of adjoint-adjoint systems:
428  //
429  // dR/du (u, sum_l(w_l*z^l)) = sum_l(w_l*(Q''_ul - R''_ul (u, z)))
430 
431  // FIXME: The derivation here does not yet take adjoint boundary
432  // conditions into account.
433 #ifdef LIBMESH_ENABLE_DIRICHLET
434  for (auto i : IntRange<unsigned int>(0, this->n_qois()))
435  if (qoi_indices.has_index(i))
437 #endif
438 
439  // We'll assemble the rhs first, because the R'' term will require
440  // perturbing the jacobian
441 
442  // We'll use temporary rhs vectors, because we haven't (yet) found
443  // any good reasons why users might want to save these:
444 
445  std::vector<std::unique_ptr<NumericVector<Number>>> temprhs(this->n_qois());
446  for (auto i : IntRange<unsigned int>(0, this->n_qois()))
447  if (qoi_indices.has_index(i))
448  temprhs[i] = this->rhs->zero_clone();
449 
450  // We approximate the _l partial derivatives via a central
451  // differencing perturbation in the w_l direction:
452  //
453  // sum_l(w_l*v_l) ~= (v(p + dp*w_l*e_l) - v(p - dp*w_l*e_l))/(2*dp)
454 
455  // PETSc doesn't implement SGEMX, so neither does NumericVector,
456  // so we want to avoid calculating f -= R'*z. We'll thus evaluate
457  // the above equation by first adding -v(p+dp...), then multiplying
458  // the intermediate result vectors by -1, then adding -v(p-dp...),
459  // then finally dividing by 2*dp.
460 
461  ParameterVector oldparameters, parameterperturbation;
462  parameters.deep_copy(oldparameters);
463  weights.deep_copy(parameterperturbation);
464  parameterperturbation *= delta_p;
465  parameters += parameterperturbation;
466 
467  this->assembly(false, true);
468  this->matrix->close();
469 
470  // Take the discrete adjoint, so that we can calculate R_u(u,z) with
471  // a matrix-vector product of R_u and z.
473 
474  this->assemble_qoi_derivative(qoi_indices,
475  /* include_liftfunc = */ false,
476  /* apply_constraints = */ true);
477  for (auto i : IntRange<unsigned int>(0, this->n_qois()))
478  if (qoi_indices.has_index(i))
479  {
480  this->get_adjoint_rhs(i).close();
481  *(temprhs[i]) -= this->get_adjoint_rhs(i);
482  this->matrix->vector_mult_add(*(temprhs[i]), this->get_adjoint_solution(i));
483  *(temprhs[i]) *= -1.0;
484  }
485 
486  oldparameters.value_copy(parameters);
487  parameterperturbation *= -1.0;
488  parameters += parameterperturbation;
489 
490  this->assembly(false, true);
491  this->matrix->close();
493 
494  this->assemble_qoi_derivative(qoi_indices,
495  /* include_liftfunc = */ false,
496  /* apply_constraints = */ true);
497  for (auto i : IntRange<unsigned int>(0, this->n_qois()))
498  if (qoi_indices.has_index(i))
499  {
500  this->get_adjoint_rhs(i).close();
501  *(temprhs[i]) -= this->get_adjoint_rhs(i);
502  this->matrix->vector_mult_add(*(temprhs[i]), this->get_adjoint_solution(i));
503  *(temprhs[i]) /= (2.0*delta_p);
504  }
505 
506  // Finally, assemble the jacobian at the non-perturbed parameter
507  // values. Ignore assemble_before_solve; if we had a good
508  // non-perturbed matrix before we've already overwritten it.
509  oldparameters.value_copy(parameters);
510 
511  // if (this->assemble_before_solve)
512  {
513  // Build the Jacobian
514  this->assembly(false, true);
515  this->matrix->close();
516 
517  // Take the discrete adjoint
519  }
520 
521  // The weighted adjoint-adjoint problem is linear
522  LinearSolver<Number> * linear_solver = this->get_linear_solver();
523 
524  // Our iteration counts and residuals will be sums of the individual
525  // results
526  std::pair<unsigned int, Real> solver_params =
528  std::pair<unsigned int, Real> totalrval = std::make_pair(0,0.0);
529 
530  for (auto i : IntRange<unsigned int>(0, this->n_qois()))
531  if (qoi_indices.has_index(i))
532  {
533  const std::pair<unsigned int, Real> rval =
534  linear_solver->solve (*matrix, this->add_weighted_sensitivity_adjoint_solution(i),
535  *(temprhs[i]),
536  double(solver_params.second),
537  solver_params.first);
538 
539  totalrval.first += rval.first;
540  totalrval.second += rval.second;
541  }
542 
543  this->release_linear_solver(linear_solver);
544 
545  // The linear solver may not have fit our constraints exactly
546 #ifdef LIBMESH_ENABLE_CONSTRAINTS
547  for (auto i : IntRange<unsigned int>(0, this->n_qois()))
548  if (qoi_indices.has_index(i))
551  /* homogeneous = */ true);
552 #endif
553 
554  return totalrval;
555 }
556 
557 
558 
559 std::pair<unsigned int, Real>
561  const ParameterVector & weights)
562 {
563  // Log how long the linear solve takes.
564  LOG_SCOPE("weighted_sensitivity_solve()", "ImplicitSystem");
565 
566  // We currently get partial derivatives via central differencing
567  const Real delta_p = TOLERANCE;
568 
569  ParameterVector & parameters =
570  const_cast<ParameterVector &>(parameters_in);
571 
572  // The forward system should now already be solved.
573 
574  // Now we're assembling a weighted sum of sensitivity systems:
575  //
576  // dR/du (u, v)(sum(w_l*u'_l)) = -sum_l(w_l*R'_l (u, v)) forall v
577 
578  // We'll assemble the rhs first, because the R' term will require
579  // perturbing the system, and some applications may not be able to
580  // assemble a perturbed residual without simultaneously constructing
581  // a perturbed jacobian.
582 
583  // We approximate the _l partial derivatives via a central
584  // differencing perturbation in the w_l direction:
585  //
586  // sum_l(w_l*v_l) ~= (v(p + dp*w_l*e_l) - v(p - dp*w_l*e_l))/(2*dp)
587 
588  ParameterVector oldparameters, parameterperturbation;
589  parameters.deep_copy(oldparameters);
590  weights.deep_copy(parameterperturbation);
591  parameterperturbation *= delta_p;
592  parameters += parameterperturbation;
593 
594  this->assembly(true, false, true);
595  this->rhs->close();
596 
597  std::unique_ptr<NumericVector<Number>> temprhs = this->rhs->clone();
598 
599  oldparameters.value_copy(parameters);
600  parameterperturbation *= -1.0;
601  parameters += parameterperturbation;
602 
603  this->assembly(true, false, true);
604  this->rhs->close();
605 
606  *temprhs -= *(this->rhs);
607  *temprhs /= (2.0*delta_p);
608 
609  // Finally, assemble the jacobian at the non-perturbed parameter
610  // values
611  oldparameters.value_copy(parameters);
612 
613  // Build the Jacobian
614  this->assembly(false, true);
615  this->matrix->close();
616 
617  // The weighted sensitivity problem is linear
618  LinearSolver<Number> * linear_solver = this->get_linear_solver();
619 
620  std::pair<unsigned int, Real> solver_params =
622 
623  const std::pair<unsigned int, Real> rval =
624  linear_solver->solve (*matrix, this->add_weighted_sensitivity_solution(),
625  *temprhs,
626  double(solver_params.second),
627  solver_params.first);
628 
629  this->release_linear_solver(linear_solver);
630 
631  // The linear solver may not have fit our constraints exactly
632 #ifdef LIBMESH_ENABLE_CONSTRAINTS
634  (*this, &this->get_weighted_sensitivity_solution(),
635  /* homogeneous = */ true);
636 #endif
637 
638  return rval;
639 }
640 
641 
642 
644 {
645  ParameterVector & parameters =
646  const_cast<ParameterVector &>(parameters_in);
647 
648  const unsigned int Np = cast_int<unsigned int>
649  (parameters.size());
650 
651  for (unsigned int p=0; p != Np; ++p)
652  {
653  NumericVector<Number> & sensitivity_rhs = this->add_sensitivity_rhs(p);
654 
655  // Approximate -(partial R / partial p) by
656  // (R(p-dp) - R(p+dp)) / (2*dp)
657 
658  Number old_parameter = *parameters[p];
659 
660  const Real delta_p =
661  TOLERANCE * std::max(std::abs(old_parameter), 1e-3);
662 
663  *parameters[p] -= delta_p;
664 
665  // this->assembly(true, false, true);
666  this->assembly(true, false, false);
667  this->rhs->close();
668  sensitivity_rhs = *this->rhs;
669 
670  *parameters[p] = old_parameter + delta_p;
671 
672  // this->assembly(true, false, true);
673  this->assembly(true, false, false);
674  this->rhs->close();
675 
676  sensitivity_rhs -= *this->rhs;
677  sensitivity_rhs /= (2*delta_p);
678  sensitivity_rhs.close();
679 
680  *parameters[p] = old_parameter;
681  }
682 }
683 
684 
685 
687  const ParameterVector & parameters_in,
688  SensitivityData & sensitivities)
689 {
690  ParameterVector & parameters =
691  const_cast<ParameterVector &>(parameters_in);
692 
693  const unsigned int Np = cast_int<unsigned int>
694  (parameters.size());
695  const unsigned int Nq = this->n_qois();
696 
697  // An introduction to the problem:
698  //
699  // Residual R(u(p),p) = 0
700  // partial R / partial u = J = system matrix
701  //
702  // This implies that:
703  // d/dp(R) = 0
704  // (partial R / partial p) +
705  // (partial R / partial u) * (partial u / partial p) = 0
706 
707  // We first do an adjoint solve:
708  // J^T * z = (partial q / partial u)
709  // if we havent already or dont have an initial condition for the adjoint
710  if (!this->is_adjoint_already_solved())
711  {
712  this->adjoint_solve(qoi_indices);
713  }
714 
715  this->assemble_residual_derivatives(parameters_in);
716 
717  // Get ready to fill in sensitivities:
718  sensitivities.allocate_data(qoi_indices, *this, parameters);
719 
720  // We use the identities:
721  // dq/dp = (partial q / partial p) + (partial q / partial u) *
722  // (partial u / partial p)
723  // dq/dp = (partial q / partial p) + (J^T * z) *
724  // (partial u / partial p)
725  // dq/dp = (partial q / partial p) + z * J *
726  // (partial u / partial p)
727 
728  // Leading to our final formula:
729  // dq/dp = (partial q / partial p) - z * (partial R / partial p)
730 
731  // In the case of adjoints with heterogenous Dirichlet boundary
732  // function phi, where
733  // q := S(u) - R(u,phi)
734  // the final formula works out to:
735  // dq/dp = (partial S / partial p) - z * (partial R / partial p)
736  // Because we currently have no direct access to
737  // (partial S / partial p), we use the identity
738  // (partial S / partial p) = (partial q / partial p) +
739  // phi * (partial R / partial p)
740  // to derive an equivalent equation:
741  // dq/dp = (partial q / partial p) - (z-phi) * (partial R / partial p)
742 
743  // Since z-phi degrees of freedom are zero for constrained indices,
744  // we can use the same constrained -(partial R / partial p) that we
745  // use for forward sensitivity solves, taking into account the
746  // differing sign convention.
747  //
748  // Since that vector is constrained, its constrained indices are
749  // zero, so its product with phi is zero, so we can neglect the
750  // evaluation of phi terms.
751 
752  for (unsigned int j=0; j != Np; ++j)
753  {
754  // We currently get partial derivatives via central differencing
755 
756  // (partial q / partial p) ~= (q(p+dp)-q(p-dp))/(2*dp)
757  // (partial R / partial p) ~= (rhs(p+dp) - rhs(p-dp))/(2*dp)
758 
759  Number old_parameter = *parameters[j];
760 
761  const Real delta_p =
762  TOLERANCE * std::max(std::abs(old_parameter), 1e-3);
763 
764  *parameters[j] = old_parameter - delta_p;
765  this->assemble_qoi(qoi_indices);
766  std::vector<Number> qoi_minus = this->qoi;
767 
768  NumericVector<Number> & neg_partialR_partialp = this->get_sensitivity_rhs(j);
769 
770  *parameters[j] = old_parameter + delta_p;
771  this->assemble_qoi(qoi_indices);
772  std::vector<Number> & qoi_plus = this->qoi;
773 
774  std::vector<Number> partialq_partialp(Nq, 0);
775  for (unsigned int i=0; i != Nq; ++i)
776  if (qoi_indices.has_index(i))
777  partialq_partialp[i] = (qoi_plus[i] - qoi_minus[i]) / (2.*delta_p);
778 
779  // Don't leave the parameter changed
780  *parameters[j] = old_parameter;
781 
782  for (unsigned int i=0; i != Nq; ++i)
783  if (qoi_indices.has_index(i))
784  sensitivities[i][j] = partialq_partialp[i] +
785  neg_partialR_partialp.dot(this->get_adjoint_solution(i));
786  }
787 
788  // All parameters have been reset.
789  // Reset the original qoi.
790 
791  this->assemble_qoi(qoi_indices);
792 }
793 
794 
795 
797  const ParameterVector & parameters_in,
798  SensitivityData & sensitivities)
799 {
800  ParameterVector & parameters =
801  const_cast<ParameterVector &>(parameters_in);
802 
803  const unsigned int Np = cast_int<unsigned int>
804  (parameters.size());
805  const unsigned int Nq = this->n_qois();
806 
807  // An introduction to the problem:
808  //
809  // Residual R(u(p),p) = 0
810  // partial R / partial u = J = system matrix
811  //
812  // This implies that:
813  // d/dp(R) = 0
814  // (partial R / partial p) +
815  // (partial R / partial u) * (partial u / partial p) = 0
816 
817  // We first solve for (partial u / partial p) for each parameter:
818  // J * (partial u / partial p) = - (partial R / partial p)
819 
820  this->sensitivity_solve(parameters);
821 
822  // Get ready to fill in sensitivities:
823  sensitivities.allocate_data(qoi_indices, *this, parameters);
824 
825  // We use the identity:
826  // dq/dp = (partial q / partial p) + (partial q / partial u) *
827  // (partial u / partial p)
828 
829  // We get (partial q / partial u) from the user
830  this->assemble_qoi_derivative(qoi_indices,
831  /* include_liftfunc = */ true,
832  /* apply_constraints = */ false);
833 
834  // We don't need these to be closed() in this function, but libMesh
835  // standard practice is to have them closed() by the time the
836  // function exits
837  for (auto i : IntRange<unsigned int>(0, this->n_qois()))
838  if (qoi_indices.has_index(i))
839  this->get_adjoint_rhs(i).close();
840 
841  for (unsigned int j=0; j != Np; ++j)
842  {
843  // We currently get partial derivatives via central differencing
844 
845  // (partial q / partial p) ~= (q(p+dp)-q(p-dp))/(2*dp)
846 
847  Number old_parameter = *parameters[j];
848 
849  const Real delta_p =
850  TOLERANCE * std::max(std::abs(old_parameter), 1e-3);
851 
852  *parameters[j] = old_parameter - delta_p;
853  this->assemble_qoi(qoi_indices);
854  std::vector<Number> qoi_minus = this->qoi;
855 
856  *parameters[j] = old_parameter + delta_p;
857  this->assemble_qoi(qoi_indices);
858  std::vector<Number> & qoi_plus = this->qoi;
859 
860  std::vector<Number> partialq_partialp(Nq, 0);
861  for (unsigned int i=0; i != Nq; ++i)
862  if (qoi_indices.has_index(i))
863  partialq_partialp[i] = (qoi_plus[i] - qoi_minus[i]) / (2.*delta_p);
864 
865  // Don't leave the parameter changed
866  *parameters[j] = old_parameter;
867 
868  for (unsigned int i=0; i != Nq; ++i)
869  if (qoi_indices.has_index(i))
870  sensitivities[i][j] = partialq_partialp[i] +
871  this->get_adjoint_rhs(i).dot(this->get_sensitivity_solution(j));
872  }
873 
874  // All parameters have been reset.
875  // We didn't cache the original rhs or matrix for memory reasons,
876  // but we can restore them to a state consistent solution -
877  // principle of least surprise.
878  this->assembly(true, true);
879  this->rhs->close();
880  this->matrix->close();
881  this->assemble_qoi(qoi_indices);
882 }
883 
884 
885 
887  const ParameterVector & parameters_in,
888  const ParameterVector & vector,
889  SensitivityData & sensitivities)
890 {
891  // We currently get partial derivatives via finite differencing
892  const Real delta_p = TOLERANCE;
893 
894  ParameterVector & parameters =
895  const_cast<ParameterVector &>(parameters_in);
896 
897  // We'll use a single temporary vector for matrix-vector-vector products
898  std::unique_ptr<NumericVector<Number>> tempvec = this->solution->zero_clone();
899 
900  const unsigned int Np = cast_int<unsigned int>
901  (parameters.size());
902  const unsigned int Nq = this->n_qois();
903 
904  // For each quantity of interest q, the parameter sensitivity
905  // Hessian is defined as q''_{kl} = {d^2 q}/{d p_k d p_l}.
906  // Given a vector of parameter perturbation weights w_l, this
907  // function evaluates the hessian-vector product sum_l(q''_{kl}*w_l)
908  //
909  // We calculate it from values and partial derivatives of the
910  // quantity of interest function Q, solution u, adjoint solution z,
911  // parameter sensitivity adjoint solutions z^l, and residual R, as:
912  //
913  // sum_l(q''_{kl}*w_l) =
914  // sum_l(w_l * Q''_{kl}) + Q''_{uk}(u)*(sum_l(w_l u'_l)) -
915  // R'_k(u, sum_l(w_l*z^l)) - R'_{uk}(u,z)*(sum_l(w_l u'_l) -
916  // sum_l(w_l*R''_{kl}(u,z))
917  //
918  // See the adjoints model document for more details.
919 
920  // We first do an adjoint solve to get z for each quantity of
921  // interest
922  // if we havent already or dont have an initial condition for the adjoint
923  if (!this->is_adjoint_already_solved())
924  {
925  this->adjoint_solve(qoi_indices);
926  }
927 
928  // Get ready to fill in sensitivities:
929  sensitivities.allocate_data(qoi_indices, *this, parameters);
930 
931  // We can't solve for all the solution sensitivities u'_l or for all
932  // of the parameter sensitivity adjoint solutions z^l without
933  // requiring O(Nq*Np) linear solves. So we'll solve directly for their
934  // weighted sum - this is just O(Nq) solves.
935 
936  // First solve for sum_l(w_l u'_l).
937  this->weighted_sensitivity_solve(parameters, vector);
938 
939  // Then solve for sum_l(w_l z^l).
940  this->weighted_sensitivity_adjoint_solve(parameters, vector, qoi_indices);
941 
942  for (unsigned int k=0; k != Np; ++k)
943  {
944  // We approximate sum_l(w_l * Q''_{kl}) with a central
945  // differencing perturbation:
946  // sum_l(w_l * Q''_{kl}) ~=
947  // (Q(p + dp*w_l*e_l + dp*e_k) - Q(p - dp*w_l*e_l + dp*e_k) -
948  // Q(p + dp*w_l*e_l - dp*e_k) + Q(p - dp*w_l*e_l - dp*e_k))/(4*dp^2)
949 
950  // The sum(w_l*R''_kl) term requires the same sort of perturbation,
951  // and so we subtract it in at the same time:
952  // sum_l(w_l * R''_{kl}) ~=
953  // (R(p + dp*w_l*e_l + dp*e_k) - R(p - dp*w_l*e_l + dp*e_k) -
954  // R(p + dp*w_l*e_l - dp*e_k) + R(p - dp*w_l*e_l - dp*e_k))/(4*dp^2)
955 
956  ParameterVector oldparameters, parameterperturbation;
957  parameters.deep_copy(oldparameters);
958  vector.deep_copy(parameterperturbation);
959  parameterperturbation *= delta_p;
960  parameters += parameterperturbation;
961 
962  Number old_parameter = *parameters[k];
963 
964  *parameters[k] = old_parameter + delta_p;
965  this->assemble_qoi(qoi_indices);
966  this->assembly(true, false, true);
967  this->rhs->close();
968  std::vector<Number> partial2q_term = this->qoi;
969  std::vector<Number> partial2R_term(this->n_qois());
970  for (unsigned int i=0; i != Nq; ++i)
971  if (qoi_indices.has_index(i))
972  partial2R_term[i] = this->rhs->dot(this->get_adjoint_solution(i));
973 
974  *parameters[k] = old_parameter - delta_p;
975  this->assemble_qoi(qoi_indices);
976  this->assembly(true, false, true);
977  this->rhs->close();
978  for (unsigned int i=0; i != Nq; ++i)
979  if (qoi_indices.has_index(i))
980  {
981  partial2q_term[i] -= this->qoi[i];
982  partial2R_term[i] -= this->rhs->dot(this->get_adjoint_solution(i));
983  }
984 
985  oldparameters.value_copy(parameters);
986  parameterperturbation *= -1.0;
987  parameters += parameterperturbation;
988 
989  // Re-center old_parameter, which may be affected by vector
990  old_parameter = *parameters[k];
991 
992  *parameters[k] = old_parameter + delta_p;
993  this->assemble_qoi(qoi_indices);
994  this->assembly(true, false, true);
995  this->rhs->close();
996  for (unsigned int i=0; i != Nq; ++i)
997  if (qoi_indices.has_index(i))
998  {
999  partial2q_term[i] -= this->qoi[i];
1000  partial2R_term[i] -= this->rhs->dot(this->get_adjoint_solution(i));
1001  }
1002 
1003  *parameters[k] = old_parameter - 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->qoi[i];
1011  partial2R_term[i] += this->rhs->dot(this->get_adjoint_solution(i));
1012  }
1013 
1014  for (unsigned int i=0; i != Nq; ++i)
1015  if (qoi_indices.has_index(i))
1016  {
1017  partial2q_term[i] /= (4. * delta_p * delta_p);
1018  partial2R_term[i] /= (4. * delta_p * delta_p);
1019  }
1020 
1021  for (unsigned int i=0; i != Nq; ++i)
1022  if (qoi_indices.has_index(i))
1023  sensitivities[i][k] = partial2q_term[i] - partial2R_term[i];
1024 
1025  // We get (partial q / partial u), R, and
1026  // (partial R / partial u) from the user, but centrally
1027  // difference to get q_uk, R_k, and R_uk terms:
1028  // (partial R / partial k)
1029  // 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)
1030  // (partial^2 q / partial u partial k)
1031  // q_uk = (q_u(p+dp*e_k) - q_u(p-dp*e_k))/(2*dp)
1032  // (partial^2 R / partial u partial k)
1033  // 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)
1034 
1035  // To avoid creating Nq temporary vectors for q_uk or R_uk, we add
1036  // subterms to the sensitivities output one by one.
1037  //
1038  // FIXME: this is probably a bad order of operations for
1039  // controlling floating point error.
1040 
1041  *parameters[k] = old_parameter + delta_p;
1042  this->assembly(true, true);
1043  this->rhs->close();
1044  this->matrix->close();
1045  this->assemble_qoi_derivative(qoi_indices,
1046  /* include_liftfunc = */ true,
1047  /* apply_constraints = */ false);
1048 
1049  this->matrix->vector_mult(*tempvec, this->get_weighted_sensitivity_solution());
1050 
1051  for (unsigned int i=0; i != Nq; ++i)
1052  if (qoi_indices.has_index(i))
1053  {
1054  this->get_adjoint_rhs(i).close();
1055  sensitivities[i][k] += (this->get_adjoint_rhs(i).dot(this->get_weighted_sensitivity_solution()) -
1057  this->get_adjoint_solution(i).dot(*tempvec)) / (2.*delta_p);
1058  }
1059 
1060  *parameters[k] = old_parameter - delta_p;
1061  this->assembly(true, true);
1062  this->rhs->close();
1063  this->matrix->close();
1064  this->assemble_qoi_derivative(qoi_indices,
1065  /* include_liftfunc = */ true,
1066  /* apply_constraints = */ false);
1067 
1068  this->matrix->vector_mult(*tempvec, this->get_weighted_sensitivity_solution());
1069 
1070  for (unsigned int i=0; i != Nq; ++i)
1071  if (qoi_indices.has_index(i))
1072  {
1073  this->get_adjoint_rhs(i).close();
1074  sensitivities[i][k] += (-this->get_adjoint_rhs(i).dot(this->get_weighted_sensitivity_solution()) +
1076  this->get_adjoint_solution(i).dot(*tempvec)) / (2.*delta_p);
1077  }
1078  }
1079 
1080  // All parameters have been reset.
1081  // Don't leave the qoi or system changed - principle of least
1082  // surprise.
1083  this->assembly(true, true);
1084  this->rhs->close();
1085  this->matrix->close();
1086  this->assemble_qoi(qoi_indices);
1087 }
1088 
1089 
1090 
1092  const ParameterVector & parameters_in,
1093  SensitivityData & sensitivities)
1094 {
1095  // We currently get partial derivatives via finite differencing
1096  const Real delta_p = TOLERANCE;
1097 
1098  ParameterVector & parameters =
1099  const_cast<ParameterVector &>(parameters_in);
1100 
1101  // We'll use one temporary vector for matrix-vector-vector products
1102  std::unique_ptr<NumericVector<Number>> tempvec = this->solution->zero_clone();
1103 
1104  // And another temporary vector to hold a copy of the true solution
1105  // so we can safely perturb this->solution.
1106  std::unique_ptr<NumericVector<Number>> oldsolution = this->solution->clone();
1107 
1108  const unsigned int Np = cast_int<unsigned int>
1109  (parameters.size());
1110  const unsigned int Nq = this->n_qois();
1111 
1112  // For each quantity of interest q, the parameter sensitivity
1113  // Hessian is defined as q''_{kl} = {d^2 q}/{d p_k d p_l}.
1114  //
1115  // We calculate it from values and partial derivatives of the
1116  // quantity of interest function Q, solution u, adjoint solution z,
1117  // and residual R, as:
1118  //
1119  // q''_{kl} =
1120  // Q''_{kl} + Q''_{uk}(u)*u'_l + Q''_{ul}(u) * u'_k +
1121  // Q''_{uu}(u)*u'_k*u'_l -
1122  // R''_{kl}(u,z) -
1123  // R''_{uk}(u,z)*u'_l - R''_{ul}(u,z)*u'_k -
1124  // R''_{uu}(u,z)*u'_k*u'_l
1125  //
1126  // See the adjoints model document for more details.
1127 
1128  // We first do an adjoint solve to get z for each quantity of
1129  // interest
1130  // if we havent already or dont have an initial condition for the adjoint
1131  if (!this->is_adjoint_already_solved())
1132  {
1133  this->adjoint_solve(qoi_indices);
1134  }
1135 
1136  // And a sensitivity solve to get u_k for each parameter
1137  this->sensitivity_solve(parameters);
1138 
1139  // Get ready to fill in second derivatives:
1140  sensitivities.allocate_hessian_data(qoi_indices, *this, parameters);
1141 
1142  for (unsigned int k=0; k != Np; ++k)
1143  {
1144  Number old_parameterk = *parameters[k];
1145 
1146  // The Hessian is symmetric, so we just calculate the lower
1147  // triangle and the diagonal, and we get the upper triangle from
1148  // the transpose of the lower
1149 
1150  for (unsigned int l=0; l != k+1; ++l)
1151  {
1152  // The second partial derivatives with respect to parameters
1153  // are all calculated via a central finite difference
1154  // stencil:
1155  // F''_{kl} ~= (F(p+dp*e_k+dp*e_l) - F(p+dp*e_k-dp*e_l) -
1156  // F(p-dp*e_k+dp*e_l) + F(p-dp*e_k-dp*e_l))/(4*dp^2)
1157  // We will add Q''_{kl}(u) and subtract R''_{kl}(u,z) at the
1158  // same time.
1159  //
1160  // We have to be careful with the perturbations to handle
1161  // the k=l case
1162 
1163  Number old_parameterl = *parameters[l];
1164 
1165  *parameters[k] += delta_p;
1166  *parameters[l] += delta_p;
1167  this->assemble_qoi(qoi_indices);
1168  this->assembly(true, false, true);
1169  this->rhs->close();
1170  std::vector<Number> partial2q_term = this->qoi;
1171  std::vector<Number> partial2R_term(this->n_qois());
1172  for (unsigned int i=0; i != Nq; ++i)
1173  if (qoi_indices.has_index(i))
1174  partial2R_term[i] = this->rhs->dot(this->get_adjoint_solution(i));
1175 
1176  *parameters[l] -= 2.*delta_p;
1177  this->assemble_qoi(qoi_indices);
1178  this->assembly(true, false, true);
1179  this->rhs->close();
1180  for (unsigned int i=0; i != Nq; ++i)
1181  if (qoi_indices.has_index(i))
1182  {
1183  partial2q_term[i] -= this->qoi[i];
1184  partial2R_term[i] -= this->rhs->dot(this->get_adjoint_solution(i));
1185  }
1186 
1187  *parameters[k] -= 2.*delta_p;
1188  this->assemble_qoi(qoi_indices);
1189  this->assembly(true, false, true);
1190  this->rhs->close();
1191  for (unsigned int i=0; i != Nq; ++i)
1192  if (qoi_indices.has_index(i))
1193  {
1194  partial2q_term[i] += this->qoi[i];
1195  partial2R_term[i] += this->rhs->dot(this->get_adjoint_solution(i));
1196  }
1197 
1198  *parameters[l] += 2.*delta_p;
1199  this->assemble_qoi(qoi_indices);
1200  this->assembly(true, false, true);
1201  this->rhs->close();
1202  for (unsigned int i=0; i != Nq; ++i)
1203  if (qoi_indices.has_index(i))
1204  {
1205  partial2q_term[i] -= this->qoi[i];
1206  partial2R_term[i] -= this->rhs->dot(this->get_adjoint_solution(i));
1207  partial2q_term[i] /= (4. * delta_p * delta_p);
1208  partial2R_term[i] /= (4. * delta_p * delta_p);
1209  }
1210 
1211  for (unsigned int i=0; i != Nq; ++i)
1212  if (qoi_indices.has_index(i))
1213  {
1214  Number current_terms = partial2q_term[i] - partial2R_term[i];
1215  sensitivities.second_derivative(i,k,l) += current_terms;
1216  if (k != l)
1217  sensitivities.second_derivative(i,l,k) += current_terms;
1218  }
1219 
1220  // Don't leave the parameters perturbed
1221  *parameters[l] = old_parameterl;
1222  *parameters[k] = old_parameterk;
1223  }
1224 
1225  // We get (partial q / partial u) and
1226  // (partial R / partial u) from the user, but centrally
1227  // difference to get q_uk and R_uk terms:
1228  // (partial^2 q / partial u partial k)
1229  // q_uk*u'_l = (q_u(p+dp*e_k)*u'_l - q_u(p-dp*e_k)*u'_l)/(2*dp)
1230  // 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)
1231  //
1232  // To avoid creating Nq temporary vectors, we add these
1233  // subterms to the sensitivities output one by one.
1234  //
1235  // FIXME: this is probably a bad order of operations for
1236  // controlling floating point error.
1237 
1238  *parameters[k] = old_parameterk + delta_p;
1239  this->assembly(false, true);
1240  this->matrix->close();
1241  this->assemble_qoi_derivative(qoi_indices,
1242  /* include_liftfunc = */ true,
1243  /* apply_constraints = */ false);
1244 
1245  for (unsigned int l=0; l != Np; ++l)
1246  {
1247  this->matrix->vector_mult(*tempvec, this->get_sensitivity_solution(l));
1248  for (unsigned int i=0; i != Nq; ++i)
1249  if (qoi_indices.has_index(i))
1250  {
1251  this->get_adjoint_rhs(i).close();
1252  Number current_terms =
1253  (this->get_adjoint_rhs(i).dot(this->get_sensitivity_solution(l)) -
1254  tempvec->dot(this->get_adjoint_solution(i))) / (2.*delta_p);
1255  sensitivities.second_derivative(i,k,l) += current_terms;
1256 
1257  // We use the _uk terms twice; symmetry lets us reuse
1258  // these calculations for the _ul terms.
1259 
1260  sensitivities.second_derivative(i,l,k) += current_terms;
1261  }
1262  }
1263 
1264  *parameters[k] = old_parameterk - delta_p;
1265  this->assembly(false, true);
1266  this->matrix->close();
1267  this->assemble_qoi_derivative(qoi_indices,
1268  /* include_liftfunc = */ true,
1269  /* apply_constraints = */ false);
1270 
1271  for (unsigned int l=0; l != Np; ++l)
1272  {
1273  this->matrix->vector_mult(*tempvec, this->get_sensitivity_solution(l));
1274  for (unsigned int i=0; i != Nq; ++i)
1275  if (qoi_indices.has_index(i))
1276  {
1277  this->get_adjoint_rhs(i).close();
1278  Number current_terms =
1279  (-this->get_adjoint_rhs(i).dot(this->get_sensitivity_solution(l)) +
1280  tempvec->dot(this->get_adjoint_solution(i))) / (2.*delta_p);
1281  sensitivities.second_derivative(i,k,l) += current_terms;
1282 
1283  // We use the _uk terms twice; symmetry lets us reuse
1284  // these calculations for the _ul terms.
1285 
1286  sensitivities.second_derivative(i,l,k) += current_terms;
1287  }
1288  }
1289 
1290  // Don't leave the parameter perturbed
1291  *parameters[k] = old_parameterk;
1292 
1293  // Our last remaining terms are -R_uu(u,z)*u_k*u_l and
1294  // Q_uu(u)*u_k*u_l
1295  //
1296  // We take directional central finite differences of R_u and Q_u
1297  // to approximate these terms, e.g.:
1298  //
1299  // Q_uu(u)*u_k ~= (Q_u(u+dp*u_k) - Q_u(u-dp*u_k))/(2*dp)
1300 
1301  *this->solution = this->get_sensitivity_solution(k);
1302  *this->solution *= delta_p;
1303  *this->solution += *oldsolution;
1304 
1305  // We've modified solution, so we need to update before calling
1306  // assembly since assembly may only use current_local_solution
1307  this->update();
1308  this->assembly(false, true);
1309  this->matrix->close();
1310  this->assemble_qoi_derivative(qoi_indices,
1311  /* include_liftfunc = */ true,
1312  /* apply_constraints = */ false);
1313 
1314  // The Hessian is symmetric, so we just calculate the lower
1315  // triangle and the diagonal, and we get the upper triangle from
1316  // the transpose of the lower
1317  //
1318  // Note that, because we took the directional finite difference
1319  // with respect to k and not l, we've added an O(delta_p^2)
1320  // error to any permutational symmetry in the Hessian...
1321  for (unsigned int l=0; l != k+1; ++l)
1322  {
1323  this->matrix->vector_mult(*tempvec, this->get_sensitivity_solution(l));
1324  for (unsigned int i=0; i != Nq; ++i)
1325  if (qoi_indices.has_index(i))
1326  {
1327  this->get_adjoint_rhs(i).close();
1328  Number current_terms =
1329  (this->get_adjoint_rhs(i).dot(this->get_sensitivity_solution(l)) -
1330  tempvec->dot(this->get_adjoint_solution(i))) / (2.*delta_p);
1331  sensitivities.second_derivative(i,k,l) += current_terms;
1332  if (k != l)
1333  sensitivities.second_derivative(i,l,k) += current_terms;
1334  }
1335  }
1336 
1337  *this->solution = this->get_sensitivity_solution(k);
1338  *this->solution *= -delta_p;
1339  *this->solution += *oldsolution;
1340 
1341  // We've modified solution, so we need to update before calling
1342  // assembly since assembly may only use current_local_solution
1343  this->update();
1344  this->assembly(false, true);
1345  this->matrix->close();
1346  this->assemble_qoi_derivative(qoi_indices,
1347  /* include_liftfunc = */ true,
1348  /* apply_constraints = */ false);
1349 
1350  for (unsigned int l=0; l != k+1; ++l)
1351  {
1352  this->matrix->vector_mult(*tempvec, this->get_sensitivity_solution(l));
1353  for (unsigned int i=0; i != Nq; ++i)
1354  if (qoi_indices.has_index(i))
1355  {
1356  this->get_adjoint_rhs(i).close();
1357  Number current_terms =
1358  (-this->get_adjoint_rhs(i).dot(this->get_sensitivity_solution(l)) +
1359  tempvec->dot(this->get_adjoint_solution(i))) / (2.*delta_p);
1360  sensitivities.second_derivative(i,k,l) += current_terms;
1361  if (k != l)
1362  sensitivities.second_derivative(i,l,k) += current_terms;
1363  }
1364  }
1365 
1366  // Don't leave the solution perturbed
1367  *this->solution = *oldsolution;
1368  }
1369 
1370  // All parameters have been reset.
1371  // Don't leave the qoi or system changed - principle of least
1372  // surprise.
1373  // We've modified solution, so we need to update before calling
1374  // assembly since assembly may only use current_local_solution
1375  this->update();
1376  this->assembly(true, true);
1377  this->rhs->close();
1378  this->matrix->close();
1379  this->assemble_qoi(qoi_indices);
1380 }
1381 
1382 
1383 
1385 {
1386  // This function allocates memory and hands it back to the user as a
1387  // naked pointer. This makes it too easy to leak memory, and
1388  // therefore this function is deprecated. After a period of
1389  // deprecation, this function will eventually be marked with a
1390  // libmesh_error_msg().
1391  libmesh_deprecated();
1392  // libmesh_error_msg("This function should be overridden by derived classes. "
1393  // "It does not contain a valid LinearSolver to hand back to "
1394  // "the user, so it creates one, opening up the possibility "
1395  // "of a memory leak.");
1396 
1397  LinearSolver<Number> * new_solver =
1398  LinearSolver<Number>::build(this->comm()).release();
1399 
1400  if (libMesh::on_command_line("--solver-system-names"))
1401  new_solver->init((this->name()+"_").c_str());
1402  else
1403  new_solver->init();
1404 
1405  return new_solver;
1406 }
1407 
1408 
1409 
1410 std::pair<unsigned int, Real> ImplicitSystem::get_linear_solve_parameters() const
1411 {
1412  return std::make_pair(this->get_equation_systems().parameters.get<unsigned int>("linear solver maximum iterations"),
1413  this->get_equation_systems().parameters.get<Real>("linear solver tolerance"));
1414 }
1415 
1416 
1417 
1419 {
1420  // This is the counterpart of the get_linear_solver() function, which is now deprecated.
1421  libmesh_deprecated();
1422 
1423  delete s;
1424 }
1425 
1426 } // namespace libMesh
libMesh::LinearSolver::reuse_preconditioner
virtual void reuse_preconditioner(bool)
Set the same_preconditioner flag, which indicates if we reuse the same preconditioner for subsequent ...
Definition: linear_solver.C:129
libMesh::ParameterVector::size
std::size_t size() const
Definition: parameter_vector.h:90
libMesh::ImplicitSystem::assemble_residual_derivatives
virtual void assemble_residual_derivatives(const ParameterVector &parameters) override
Residual parameter derivative function.
Definition: implicit_system.C:643
libMesh::SparseMatrix::close
virtual void close()=0
Calls the SparseMatrix's internal assembly routines, ensuring that the values are consistent across p...
libMesh::System
Manages consistently variables, degrees of freedom, and coefficient vectors.
Definition: system.h:100
libMesh::ImplicitSystem::request_matrix
const SparseMatrix< Number > * request_matrix(const std::string &mat_name) const
Definition: implicit_system.C:236
libMesh::NumericVector::zero
virtual void zero()=0
Set all entries to zero.
libMesh::Number
Real Number
Definition: libmesh_common.h:195
libMesh::DofMap::compute_sparsity
void compute_sparsity(const MeshBase &)
Computes the sparsity pattern for the matrices corresponding to proc_id and sends that data to Linear...
Definition: dof_map.C:1768
libMesh::System::get_equation_systems
const EquationSystems & get_equation_systems() const
Definition: system.h:720
libMesh::DofMap::is_attached
bool is_attached(SparseMatrix< Number > &matrix)
Matrices should not be attached more than once.
Definition: dof_map.C:310
libMesh::System::reinit
virtual void reinit()
Reinitializes degrees of freedom and other required data on the current mesh.
Definition: system.C:390
libMesh::System::get_sensitivity_rhs
NumericVector< Number > & get_sensitivity_rhs(unsigned int i=0)
Definition: system.C:1049
libMesh::ExplicitSystem::rhs
NumericVector< Number > * rhs
The system matrix.
Definition: explicit_system.h:114
libMesh::ImplicitSystem::get_linear_solve_parameters
virtual std::pair< unsigned int, Real > get_linear_solve_parameters() const
Definition: implicit_system.C:1410
libMesh::System::add_adjoint_solution
NumericVector< Number > & add_adjoint_solution(unsigned int i=0)
Definition: system.C:945
libMesh::ImplicitSystem::adjoint_solve
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...
Definition: implicit_system.C:359
libMesh::LinearSolver::solve
virtual std::pair< unsigned int, Real > solve(SparseMatrix< T > &, NumericVector< T > &, NumericVector< T > &, const double, const unsigned int)=0
This function calls the solver _solver_type preconditioned with the _preconditioner_type precondition...
libMesh::QoISet::has_index
bool has_index(std::size_t) const
Return whether or not this index is in the set to be calculated.
Definition: qoi_set.h:221
libMesh::DofMap::has_adjoint_dirichlet_boundaries
bool has_adjoint_dirichlet_boundaries(unsigned int q) const
Definition: dof_map_constraints.C:4409
libMesh::System::get_adjoint_solution
NumericVector< Number > & get_adjoint_solution(unsigned int i=0)
Definition: system.C:957
libMesh::System::get_weighted_sensitivity_adjoint_solution
NumericVector< Number > & get_weighted_sensitivity_adjoint_solution(unsigned int i=0)
Definition: system.C:989
libMesh::SparseMatrix::vector_mult
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.
Definition: sparse_matrix.C:170
libMesh::ImplicitSystem::reinit
virtual void reinit() override
Reinitializes the member data fields associated with the system, so that, e.g., assemble() may be use...
Definition: implicit_system.C:149
libMesh
The libMesh namespace provides an interface to certain functionality in the library.
Definition: factoryfunction.C:55
libMesh::System::add_sensitivity_solution
NumericVector< Number > & add_sensitivity_solution(unsigned int i=0)
Definition: system.C:894
libMesh::NumericVector::close
virtual void close()=0
Calls the NumericVector's internal assembly routines, ensuring that the values are consistent across ...
libMesh::SparseMatrix::initialized
virtual bool initialized() const
Definition: sparse_matrix.h:103
libMesh::ImplicitSystem::add_matrix
SparseMatrix< Number > & add_matrix(const std::string &mat_name)
Adds the additional matrix mat_name to this system.
Definition: implicit_system.C:202
libMesh::ImplicitSystem::assemble
virtual void assemble() override
Prepares matrix and rhs for system assembly, then calls user assembly function.
Definition: implicit_system.C:183
libMesh::SensitivityData::allocate_data
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.
Definition: sensitivity_data.h:170
libMesh::TOLERANCE
static const Real TOLERANCE
Definition: libmesh_common.h:128
libMesh::ImplicitSystem::_matrices
std::map< std::string, SparseMatrix< Number > * > _matrices
Some systems need an arbitrary number of matrices.
Definition: implicit_system.h:426
libMesh::ParallelObject::comm
const Parallel::Communicator & comm() const
Definition: parallel_object.h:94
libMesh::ImplicitSystem::qoi_parameter_hessian_vector_product
virtual void qoi_parameter_hessian_vector_product(const QoISet &qoi_indices, const ParameterVector &parameters, const ParameterVector &vector, SensitivityData &product) override
For each of the system's quantities of interest q in qoi[qoi_indices], and for a vector of parameters...
Definition: implicit_system.C:886
libMesh::System::get_adjoint_rhs
NumericVector< Number > & get_adjoint_rhs(unsigned int i=0)
Definition: system.C:1019
libMesh::SensitivityData
Data structure for holding completed parameter sensitivity calculations.
Definition: sensitivity_data.h:46
libMesh::NumericVector::clone
virtual std::unique_ptr< NumericVector< T > > clone() const =0
libMesh::ImplicitSystem::get_linear_solver
virtual LinearSolver< Number > * get_linear_solver() const
Definition: implicit_system.C:1384
libMesh::ParameterVector::value_copy
void value_copy(ParameterVector &target) const
Value copy method: the target, which should already have as many parameters as I do,...
Definition: parameter_vector.C:63
libMesh::System::is_adjoint_already_solved
bool is_adjoint_already_solved() const
Accessor for the adjoint_already_solved boolean.
Definition: system.h:396
libMesh::LinearSolver::init
virtual void init(const char *name=nullptr)=0
Initialize data structures if not done so already.
libMesh::SparseMatrix< Number >
libMesh::ImplicitSystem::const_matrices_iterator
std::map< std::string, SparseMatrix< Number > * >::const_iterator const_matrices_iterator
Definition: implicit_system.h:307
libMesh::System::get_weighted_sensitivity_solution
NumericVector< Number > & get_weighted_sensitivity_solution()
Definition: system.C:931
libMesh::System::assemble
virtual void assemble()
Prepares matrix and _dof_map for matrix assembly.
Definition: system.C:462
libMesh::NumericVector< Number >
libMesh::System::n_qois
unsigned int n_qois() const
Number of currently active quantities of interest.
Definition: system.h:2328
libMesh::DofMap::enforce_adjoint_constraints_exactly
void enforce_adjoint_constraints_exactly(NumericVector< Number > &v, unsigned int q) const
Heterogenously constrains the numeric vector v, which represents an adjoint solution defined on the m...
Definition: dof_map.h:2058
libMesh::System::assemble_before_solve
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:1493
libMesh::ImplicitSystem::weighted_sensitivity_solve
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...
Definition: implicit_system.C:560
libMesh::libmesh_assert
libmesh_assert(ctx)
libMesh::IntRange
The IntRange templated class is intended to make it easy to loop over integers which are indices of a...
Definition: int_range.h:53
libMesh::LinearSolver::build
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:57
libMesh::ImplicitSystem::~ImplicitSystem
virtual ~ImplicitSystem()
Destructor.
Definition: implicit_system.C:56
std::abs
MetaPhysicL::DualNumber< T, D > abs(const MetaPhysicL::DualNumber< T, D > &in)
libMesh::System::qoi
std::vector< Number > qoi
Values of the quantities of interest.
Definition: system.h:1574
libMesh::QoISet
Data structure for specifying which Quantities of Interest should be calculated in an adjoint or a pa...
Definition: qoi_set.h:45
libMesh::ImplicitSystem::clear
virtual void clear() override
Clear all the data structures associated with the system.
Definition: implicit_system.C:66
libMesh::NumericVector::initialized
virtual bool initialized() const
Definition: numeric_vector.h:155
libMesh::ImplicitSystem::_can_add_matrices
bool _can_add_matrices
true when additional matrices may still be added, false otherwise.
Definition: implicit_system.h:431
libMesh::ImplicitSystem::assembly
virtual void assembly(bool, bool, bool=false, bool=false)
Assembles a residual in rhs and/or a jacobian in matrix, as requested.
Definition: implicit_system.h:161
libMesh::System::get_mesh
const MeshBase & get_mesh() const
Definition: system.h:2083
libMesh::DofMap::enforce_constraints_exactly
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:2054
libMesh::ImplicitSystem::adjoint_qoi_parameter_sensitivity
virtual void adjoint_qoi_parameter_sensitivity(const QoISet &qoi_indices, const ParameterVector &parameters, SensitivityData &sensitivities) override
Solves for the derivative of each of the system's quantities of interest q in qoi[qoi_indices] with r...
Definition: implicit_system.C:686
libMesh::System::get_sensitivity_solution
NumericVector< Number > & get_sensitivity_solution(unsigned int i=0)
Definition: system.C:904
libMesh::ImplicitSystem::init_matrices
virtual void init_matrices()
Initializes the matrices associated with this system.
Definition: implicit_system.C:105
libMesh::ImplicitSystem::matrix
SparseMatrix< Number > * matrix
The system matrix.
Definition: implicit_system.h:393
libMesh::ParameterVector
Data structure for specifying which Parameters should be independent variables in a parameter sensiti...
Definition: parameter_vector.h:44
libMesh::SparseMatrix::vector_mult_add
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.
Definition: sparse_matrix.C:180
libMesh::ImplicitSystem::sensitivity_solve
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...
Definition: implicit_system.C:301
libMesh::ImplicitSystem::have_matrix
bool have_matrix(const std::string &mat_name) const
Definition: implicit_system.h:439
libMesh::ExplicitSystem::assemble_qoi
virtual void assemble_qoi(const QoISet &qoi_indices=QoISet()) override
Prepares qoi for quantity of interest assembly, then calls user qoi function.
Definition: explicit_system.C:56
libMesh::ImplicitSystem::matrices_iterator
std::map< std::string, SparseMatrix< Number > * >::iterator matrices_iterator
Matrix iterator typedefs.
Definition: implicit_system.h:306
libMesh::SensitivityData::allocate_hessian_data
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.
Definition: sensitivity_data.h:191
libMesh::ImplicitSystem::release_linear_solver
virtual void release_linear_solver(LinearSolver< Number > *) const
Releases a pointer to a linear solver acquired by this->get_linear_solver()
Definition: implicit_system.C:1418
libMesh::EquationSystems
This is the EquationSystems class.
Definition: equation_systems.h:74
libMesh::DofMap::attach_matrix
void attach_matrix(SparseMatrix< Number > &matrix)
Additional matrices may be handled with this DofMap.
Definition: dof_map.C:274
libMesh::LinearSolver::adjoint_solve
virtual std::pair< unsigned int, Real > adjoint_solve(SparseMatrix< T > &, NumericVector< T > &, NumericVector< T > &, const double, const unsigned int)
Function to solve the adjoint system.
Definition: linear_solver.C:145
libMesh::System::solution
std::unique_ptr< NumericVector< Number > > solution
Data structure to hold solution values.
Definition: system.h:1539
libMesh::ParameterVector::deep_copy
void deep_copy(ParameterVector &target) const
Deep copy constructor: the target will now own new copies of all the parameter values I'm pointing to...
Definition: parameter_vector.C:38
libMesh::SparseMatrix::build
static std::unique_ptr< SparseMatrix< T > > build(const Parallel::Communicator &comm, const SolverPackage solver_package=libMesh::default_solver_package())
Builds a SparseMatrix<T> using the linear solver package specified by solver_package.
Definition: sparse_matrix.C:130
libMesh::ImplicitSystem::ImplicitSystem
ImplicitSystem(EquationSystems &es, const std::string &name, const unsigned int number)
Constructor.
Definition: implicit_system.C:41
libMesh::DofMap
This class handles the numbering of degrees of freedom on a mesh.
Definition: dof_map.h:176
libMesh::System::name
const std::string & name() const
Definition: system.h:2067
libMesh::DofMap::clear_sparsity
void clear_sparsity()
Clears the sparsity pattern.
Definition: dof_map.C:1800
libMesh::LinearSolver< Number >
libMesh::System::init_data
virtual void init_data()
Initializes the data for the system.
Definition: system.C:262
libMesh::SensitivityData::second_derivative
const Number & second_derivative(unsigned int qoi_index, unsigned int parameter_index1, unsigned int parameter_index2) const
Definition: sensitivity_data.h:238
libMesh::ImplicitSystem::remove_matrix
void remove_matrix(const std::string &mat_name)
Removes the additional matrix mat_name from this system.
Definition: implicit_system.C:221
libMesh::on_command_line
bool on_command_line(std::string arg)
Definition: libmesh.C:898
libMesh::ExplicitSystem::clear
virtual void clear() override
Clear all the data structures associated with the system.
Definition: explicit_system.C:45
libMesh::System::get_dof_map
const DofMap & get_dof_map() const
Definition: system.h:2099
libMesh::Real
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
Definition: libmesh_common.h:121
libMesh::ImplicitSystem::forward_qoi_parameter_sensitivity
virtual void forward_qoi_parameter_sensitivity(const QoISet &qoi_indices, const ParameterVector &parameters, SensitivityData &sensitivities) override
Solves for the derivative of each of the system's quantities of interest q in qoi[qoi_indices] with r...
Definition: implicit_system.C:796
libMesh::SparseMatrix::get_transpose
virtual void get_transpose(SparseMatrix< T > &dest) const =0
Copies the transpose of the matrix into dest, which may be *this.
libMesh::ImplicitSystem::weighted_sensitivity_adjoint_solve
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)),...
Definition: implicit_system.C:412
libMesh::ImplicitSystem::zero_out_matrix_and_rhs
bool zero_out_matrix_and_rhs
By default, the system will zero out the matrix and the right hand side.
Definition: implicit_system.h:400
libMesh::ImplicitSystem::init_data
virtual void init_data() override
Initializes the member data fields associated with the system, so that, e.g., assemble() may be used.
Definition: implicit_system.C:90
libMesh::System::add_weighted_sensitivity_solution
NumericVector< Number > & add_weighted_sensitivity_solution()
Definition: system.C:924
libMesh::System::add_weighted_sensitivity_adjoint_solution
NumericVector< Number > & add_weighted_sensitivity_adjoint_solution(unsigned int i=0)
Definition: system.C:977
libMesh::Parameters::get
const T & get(const std::string &) const
Definition: parameters.h:421
libMesh::ImplicitSystem::get_matrix
const SparseMatrix< Number > & get_matrix(const std::string &mat_name) const
Definition: implicit_system.C:262
libMesh::ImplicitSystem::add_system_matrix
void add_system_matrix()
Add the system matrix to the _matrices data structure.
Definition: implicit_system.C:276
libMesh::System::update
virtual void update()
Update the local values to reflect the solution on neighboring processors.
Definition: system.C:408
libMesh::SparseMatrix::zero
virtual void zero()=0
Set all entries to 0.
libMesh::System::add_sensitivity_rhs
NumericVector< Number > & add_sensitivity_rhs(unsigned int i=0)
Definition: system.C:1039
libMesh::NumericVector::zero_clone
virtual std::unique_ptr< NumericVector< T > > zero_clone() const =0
libMesh::ImplicitSystem::qoi_parameter_hessian
virtual void qoi_parameter_hessian(const QoISet &qoi_indices, const ParameterVector &parameters, SensitivityData &hessian) override
For each of the system's quantities of interest q in qoi[qoi_indices], and for a vector of parameters...
Definition: implicit_system.C:1091
libMesh::NumericVector::dot
virtual T dot(const NumericVector< T > &v) const =0
libMesh::ImplicitSystem::disable_cache
virtual void disable_cache() override
Avoids use of any cached data that might affect any solve result.
Definition: implicit_system.C:293
libMesh::ExplicitSystem::assemble_qoi_derivative
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...
Definition: explicit_system.C:69
libMesh::EquationSystems::parameters
Parameters parameters
Data structure holding arbitrary parameters.
Definition: equation_systems.h:557