MAST
Multidisciplinary-design Adaptation and Sensitivity Toolkit (MAST)
nonlinear_system.cpp
Go to the documentation of this file.
1 /*
2  * MAST: Multidisciplinary-design Adaptation and Sensitivity Toolkit
3  * Copyright (C) 2013-2020 Manav Bhatia and MAST authors
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19 
20 // C++ includes
21 #include <vector>
22 #include <string>
23 #include <fstream>
24 #include <sstream>
25 #include <sys/stat.h>
26 
27 // MAST includes
28 #include "base/nonlinear_system.h"
32 #include "base/parameter.h"
35 
36 // libMesh includes
37 #include "libmesh/numeric_vector.h"
38 #include "libmesh/equation_systems.h"
39 #include "libmesh/sparse_matrix.h"
40 #include "libmesh/dof_map.h"
41 #include "libmesh/nonlinear_solver.h"
42 #include "libmesh/petsc_linear_solver.h"
43 #include "libmesh/xdr_cxx.h"
44 #include "libmesh/mesh_tools.h"
45 #include "libmesh/utility.h"
46 #include "libmesh/libmesh_version.h"
47 #include "libmesh/generic_projector.h"
48 #include "libmesh/wrapped_functor.h"
49 #include "libmesh/fem_context.h"
50 #include "libmesh/parallel.h"
51 
52 
53 MAST::NonlinearSystem::NonlinearSystem(libMesh::EquationSystems& es,
54  const std::string& name,
55  const unsigned int number):
56 libMesh::NonlinearImplicitSystem(es, name, number),
57 _initialize_B_matrix (false),
58 matrix_A (nullptr),
59 matrix_B (nullptr),
60 eigen_solver (nullptr),
61 _condensed_dofs_initialized (false),
62 _exchange_A_and_B (false),
63 _n_requested_eigenpairs (0),
64 _n_converged_eigenpairs (0),
65 _n_iterations (0),
66 _is_generalized_eigenproblem (false),
67 _eigen_problem_type (libMesh::NHEP),
68 _operation (MAST::NonlinearSystem::NONE) {
69 
70 }
71 
72 
73 
75 
76  this->clear();
77 }
78 
79 
80 
81 
82 
83 void
85 
86 
87  // delete the matricies
88  if (matrix_A) delete matrix_A;
89  if (matrix_B) delete matrix_B;
90 
91  // nullptr-out the matricies.
92  matrix_A = nullptr;
93  matrix_B = nullptr;
94 
95  // clear the solver
96  if (eigen_solver.get()) {
97  eigen_solver->clear();
98  }
99 
100  libMesh::NonlinearImplicitSystem::clear();
101 }
102 
103 
104 void
105 MAST::NonlinearSystem::set_eigenproblem_type (libMesh::EigenProblemType ept) {
106 
107  _eigen_problem_type = ept;
108 
109 }
110 
111 
112 
113 
114 void
116 
117  // initialize parent data
118  libMesh::NonlinearImplicitSystem::init_data();
119 
120  // define the type of eigenproblem
121  if (_eigen_problem_type == libMesh::GNHEP ||
122  _eigen_problem_type == libMesh::GHEP ||
123  _eigen_problem_type == libMesh::GHIEP)
125 
126 
127  libMesh::DofMap& dof_map = this->get_dof_map();
128 
129  // build the system matrix
130  matrix_A = libMesh::SparseMatrix<Real>::build(this->comm()).release();
131  dof_map.attach_matrix(*matrix_A);
132  matrix_A->init();
133  matrix_A->zero();
134 
136 
137  matrix_B = libMesh::SparseMatrix<Real>::build(this->comm()).release();
138  dof_map.attach_matrix(*matrix_B);
139  matrix_B->init();
140  matrix_B->zero();
141  }
142 
143  eigen_solver.reset(new MAST::SlepcEigenSolver(this->comm()));
144  if (libMesh::on_command_line("--solver_system_names")) {
145 
146  EPS eps = eigen_solver.get()->eps();
147  std::string nm = this->name() + "_";
148  EPSSetOptionsPrefix(eps, nm.c_str());
149  }
150  eigen_solver->set_eigenproblem_type(_eigen_problem_type);
151 
152 
153  linear_solver.reset(new libMesh::PetscLinearSolver<Real>(this->comm()));
154  if (libMesh::on_command_line("--solver_system_names")) {
155 
156  std::string nm = this->name() + "_";
157  linear_solver->init(nm.c_str());
158  }
159 }
160 
161 
162 
163 
165  // initialize parent data
166  libMesh::NonlinearImplicitSystem::reinit();
167 
168  // Clear the matrices
169  matrix_A->clear();
170 
172  matrix_B->clear();
173 
174  eigen_solver.reset(new MAST::SlepcEigenSolver(this->comm()));
175  if (libMesh::on_command_line("--solver_system_names")) {
176 
177  EPS eps = eigen_solver.get()->eps();
178  std::string nm = this->name() + "_";
179  EPSSetOptionsPrefix(eps, nm.c_str());
180  }
181  eigen_solver->set_eigenproblem_type(_eigen_problem_type);
182 
183 
184  linear_solver.reset(new libMesh::PetscLinearSolver<Real>(this->comm()));
185  if (libMesh::on_command_line("--solver_system_names")) {
186 
187  std::string nm = this->name() + "_";
188  linear_solver->init(nm.c_str());
189  }
190 
191  matrix_A->init();
192  matrix_A->zero();
193 
195  {
196  matrix_B->init();
197  matrix_B->zero();
198  }
199 }
200 
201 std::pair<unsigned int, Real>
203 
204  this->set_solver_parameters();
205  return libMesh::NonlinearImplicitSystem::get_linear_solve_parameters();
206 }
207 
208 
209 void
211  MAST::AssemblyBase& assembly) {
212 
213  libmesh_assert(_operation == MAST::NonlinearSystem::NONE);
214 
216  assembly.set_elem_operation_object(elem_ops);
217 
218  libMesh::NonlinearImplicitSystem::ComputeResidualandJacobian
219  *old_ptr = this->nonlinear_solver->residual_and_jacobian_object;
220 
221  this->nonlinear_solver->residual_and_jacobian_object = &assembly;
222  //if (assembly.get_solver_monitor())
223  // assembly.get_solver_monitor()->init(assembly);
224 
225  libMesh::NonlinearImplicitSystem::solve();
226 
227  // enforce constraints on the solution since NonlinearImplicitSystem only
228  // enforces the constraints on current_local_solution.
229  this->get_dof_map().enforce_constraints_exactly(*this, this->solution.get());
230 
231  this->nonlinear_solver->residual_and_jacobian_object = old_ptr;
232 
233  assembly.clear_elem_operation_object();
235 }
236 
237 
238 
239 void
241  MAST::EigenproblemAssembly& assembly) {
242 
243  libmesh_assert(_operation == MAST::NonlinearSystem::NONE);
244 
246 
247  START_LOG("eigensolve()", "NonlinearSystem");
248 
249  assembly.set_elem_operation_object(elem_ops);
250 
251  // A reference to the EquationSystems
252  libMesh::EquationSystems& es = this->get_equation_systems();
253 
254  // check that necessary parameters have been set
255  libmesh_assert(_n_requested_eigenpairs);
256 
257  es.parameters.set<unsigned int>("eigenpairs") = _n_requested_eigenpairs;
258  es.parameters.set<unsigned int>("basis vectors") = 5*_n_requested_eigenpairs;
259 
260  // Get the tolerance for the solver and the maximum
261  // number of iterations. Here, we simply adopt the linear solver
262  // specific parameters.
263  const Real
264  tol = es.parameters.get<Real>("linear solver tolerance");
265 
266  const unsigned int
267  maxits = es.parameters.get<unsigned int>("linear solver maximum iterations"),
268  nev = es.parameters.get<unsigned int>("eigenpairs"),
269  ncv = es.parameters.get<unsigned int>("basis vectors");
270 
271  std::pair<unsigned int, unsigned int> solve_data;
272 
273  libMesh::SparseMatrix<Real>
274  *eig_A = nullptr,
275  *eig_B = nullptr;
276 
277  // assemble the matrices
279 
280  // If we haven't initialized any condensed dofs,
281  // just use the default eigen_system
283 
284  // call the solver depending on the type of eigenproblem
285  if (generalized()) {
286 
287  //in case of a generalized eigenproblem
288 
289  // exchange the matrices if requested by the user
290  if (!_exchange_A_and_B) {
291  eig_A = matrix_A;
292  eig_B = matrix_B;
293  }
294  else {
295  eig_B = matrix_A;
296  eig_A = matrix_B;
297  }
298 
299  solve_data = eigen_solver->solve_generalized (*eig_A,
300  *eig_B,
301  nev,
302  ncv,
303  tol,
304  maxits);
305  }
306  else {
307 
308  libmesh_assert (!matrix_B);
309 
310  //in case of a standard eigenproblem
311  solve_data = eigen_solver->solve_standard (*matrix_A,
312  nev,
313  ncv,
314  tol,
315  maxits);
316  }
317  }
318  else {
319 
320  // If we reach here, then there should be some non-condensed dofs
321  libmesh_assert(!_local_non_condensed_dofs_vector.empty());
322 
323  std::unique_ptr<libMesh::SparseMatrix<Real> >
324  condensed_matrix_A(libMesh::SparseMatrix<Real>::build(this->comm()).release()),
325  condensed_matrix_B(libMesh::SparseMatrix<Real>::build(this->comm()).release());
326 
327  // Now condense the matrices
328  matrix_A->create_submatrix(*condensed_matrix_A,
331 
332 
333  if (generalized()) {
334 
335  matrix_B->create_submatrix(*condensed_matrix_B,
338  }
339 
340  // call the solver depending on the type of eigenproblem
341  if ( generalized() ) {
342 
343  //in case of a generalized eigenproblem
344 
345  // exchange the matrices if requested by the user
346  if (!_exchange_A_and_B) {
347  eig_A = condensed_matrix_A.get();
348  eig_B = condensed_matrix_B.get();
349  }
350  else {
351  eig_B = condensed_matrix_A.get();
352  eig_A = condensed_matrix_B.get();
353  }
354 
355  solve_data = eigen_solver->solve_generalized(*eig_A,
356  *eig_B,
357  nev,
358  ncv,
359  tol,
360  maxits);
361  }
362  else {
363 
364  libmesh_assert (!matrix_B);
365 
366  //in case of a standard eigenproblem
367  solve_data = eigen_solver->solve_standard (*condensed_matrix_A,
368  nev,
369  ncv,
370  tol,
371  maxits);
372  }
373  }
374 
375  _n_converged_eigenpairs = solve_data.first;
376  _n_iterations = solve_data.second;
377 
378  assembly.clear_elem_operation_object();
379 
380  STOP_LOG("eigensolve()", "NonlinearSystem");
382 }
383 
384 
385 
386 
387 void
389 
390  std::pair<Real, Real>
391  val = this->eigen_solver->get_eigenvalue (i);
392 
393  if (!_exchange_A_and_B) {
394  re = val.first;
395  im = val.second;
396  }
397  else {
398  Complex complex_val (val.first, val.second);
399  complex_val = 1./complex_val;
400  re = complex_val.real();
401  im = complex_val.imag();
402  }
403 }
404 
405 
406 
407 void
409  Real& re,
410  Real& im,
411  libMesh::NumericVector<Real>& vec_re,
412  libMesh::NumericVector<Real>* vec_im) {
413 
414  START_LOG("get_eigenpair()", "NonlinearSystem");
415  std::pair<Real, Real>
416  val;
417 
418  // imaginary component is needed only when the problem is non-Hermitian
419  if (_eigen_problem_type == libMesh::HEP ||
420  _eigen_problem_type == libMesh::GHEP)
421  libmesh_assert (vec_im == nullptr);
422 
423 
424  // If we haven't initialized any condensed dofs,
425  // just use the default eigen_system
427 
428  // call the eigen_solver get_eigenpair method
429  val = this->eigen_solver->get_eigenpair (i, vec_re, vec_im);
430 
431  if (!_exchange_A_and_B) {
432  re = val.first;
433  im = val.second;
434  }
435  else {
436  Complex complex_val (val.first, val.second);
437  complex_val = 1./complex_val;
438  re = complex_val.real();
439  im = complex_val.imag();
440  }
441  }
442  else {
443 
444  // If we reach here, then there should be some non-condensed dofs
445  libmesh_assert(!_local_non_condensed_dofs_vector.empty());
446 
447  std::unique_ptr< libMesh::NumericVector<Real> >
448  temp_re(libMesh::NumericVector<Real>::build(this->comm()).release()),
449  temp_im;
450 
451  unsigned int
452  n_local = (unsigned int)_local_non_condensed_dofs_vector.size(),
453  n = n_local;
454  this->comm().sum(n);
455 
456  // initialize the vectors
457  temp_re->init (n, n_local, false, libMesh::PARALLEL);
458 
459  // imaginary only if the problem is non-Hermitian
460  if (vec_im) {
461 
462  temp_im.reset(libMesh::NumericVector<Real>::build(this->comm()).release());
463  temp_im->init (n, n_local, false, libMesh::PARALLEL);
464  }
465 
466 
467  // call the eigen_solver get_eigenpair method
468  val = this->eigen_solver->get_eigenpair (i, *temp_re, temp_im.get());
469 
470  if (!_exchange_A_and_B) {
471  re = val.first;
472  im = val.second;
473  }
474  else {
475  Complex complex_val (val.first, val.second);
476  complex_val = 1./complex_val;
477  re = complex_val.real();
478  im = complex_val.imag();
479  }
480 
481 
482  // Now map temp to solution. Loop over local entries of local_non_condensed_dofs_vector
483  // the real part
484  vec_re.zero();
485  for (unsigned int j=0; j<_local_non_condensed_dofs_vector.size(); j++) {
486 
487  unsigned int index = _local_non_condensed_dofs_vector[j];
488  vec_re.set(index,(*temp_re)(temp_re->first_local_index()+j));
489  }
490  vec_re.close();
491 
492  // now the imaginary part if it was provided
493  if (vec_im) {
494 
495  vec_im->zero();
496 
497  for (unsigned int j=0; j<_local_non_condensed_dofs_vector.size(); j++) {
498 
499  unsigned int index = _local_non_condensed_dofs_vector[j];
500  vec_im->set(index,(*temp_im)(temp_im->first_local_index()+j));
501  }
502 
503  vec_im->close();
504  }
505  }
506 
507  // scale the eigenvector so that it has a unit inner product
508  // with the B matrix.
509  switch (_eigen_problem_type) {
510  case libMesh::HEP: {
511  Real
512  v = vec_re.dot(vec_re);
513 
514  // make sure that v is a nonzero value
515  libmesh_assert_greater(v, 0.);
516  vec_re.scale(1./std::sqrt(v));
517  }
518  break;
519 
520  case libMesh::GHEP: {
521 
522  std::unique_ptr<libMesh::NumericVector<Real> >
523  tmp(vec_re.zero_clone().release());
524 
525  // inner product with respect to B matrix
526  matrix_B->vector_mult(*tmp, vec_re);
527 
528  Real
529  v = tmp->dot(vec_re);
530 
531  // make sure that v is a nonzero value
532  libmesh_assert_greater(v, 0.);
533  vec_re.scale(1./std::sqrt(v));
534  }
535  break;
536 
537 
538  case libMesh::NHEP: // to be implemented
539  case libMesh::GNHEP: // to be implemented
540  case libMesh::GHIEP: // to be implemented
541  default:
542  libmesh_error(); // should not get here
543  }
544 
545  this->update();
546 
547  STOP_LOG("get_eigenpair()", "NonlinearSystem");
548 }
549 
550 
551 
552 
553 
554 void
557  MAST::EigenproblemAssembly& assembly,
558  const MAST::FunctionBase& f,
559  std::vector<Real>& sens,
560  const std::vector<unsigned int>* indices) {
561 
562  // make sure that eigensolution is already available
563  libmesh_assert(_n_converged_eigenpairs);
564 
565  assembly.set_elem_operation_object(elem_ops);
566 
567  // the sensitivity is calculated based on the inner product of the left and
568  // right eigen vectors.
569  //
570  // y^T [A] x - lambda y^T [B] x = 0
571  // where y and x are the left and right eigenvectors, respectively.
572  // Therefore,
573  // d lambda/dp = (y^T (d[A]/dp - lambda d[B]/dp) x) / (y^T [B] x)
574  //
575  // the denominator remain constant for all sensitivity calculations.
576  //
577  const unsigned int
579  n_calc = indices?(unsigned int)indices->size():nconv;
580 
581  libmesh_assert_equal_to(n_calc, nconv);
582 
583  std::vector<unsigned int> indices_to_calculate;
584  if (indices) {
585  indices_to_calculate = *indices;
586  for (unsigned int i=0; i<n_calc; i++) libmesh_assert_less(indices_to_calculate[i], nconv);
587  }
588  else {
589  // calculate all
590  indices_to_calculate.resize(n_calc);
591  for (unsigned int i=0; i<n_calc; i++) indices_to_calculate[i] = i;
592  }
593 
594  std::vector<Real>
595  denom(n_calc, 0.);
596  sens.resize(n_calc, 0.);
597 
598  std::vector<libMesh::NumericVector<Real>*>
599  x_right (n_calc);
600  //x_left (_n_converged_eigenpairs);
601 
602  std::unique_ptr<libMesh::NumericVector<Real> >
603  tmp (this->solution->zero_clone().release());
604 
605  std::vector<Real>
606  eig (n_calc);
607 
608  Real
609  re = 0.,
610  im = 0.;
611 
612 
613  for (unsigned int i=0; i<n_calc; i++) {
614 
615  x_right[i] = (this->solution->zero_clone().release());
616 
617 
618  switch (_eigen_problem_type) {
619 
620  case libMesh::HEP: {
621  // right and left eigenvectors are same
622  // imaginary part of eigenvector for real matrices is zero
623  this->get_eigenpair(indices_to_calculate[i], re, im, *x_right[i], nullptr);
624  denom[i] = x_right[i]->dot(*x_right[i]); // x^H x
625  eig[i] = re;
626  }
627  break;
628 
629  case libMesh::GHEP: {
630  // imaginary part of eigenvector for real matrices is zero
631  this->get_eigenpair(indices_to_calculate[i], re, im, *x_right[i], nullptr);
632  matrix_B->vector_mult(*tmp, *x_right[i]);
633  denom[i] = x_right[i]->dot(*tmp); // x^H B x
634  eig[i] = re;
635  }
636  break;
637 
638  default:
639  // to be implemented for the non-Hermitian problems
640  libmesh_error();
641  break;
642  }
643  }
644 
645  // calculate sensitivity of matrix quantities
647 
648 
649  // now calculate sensitivity of each eigenvalue for the parameter
650  for (unsigned int i=0; i<n_calc; i++) {
651 
652  switch (_eigen_problem_type) {
653 
654  case libMesh::HEP: {
655 
656  matrix_A->vector_mult(*tmp, *x_right[i]);
657  sens[i] = x_right[i]->dot(*tmp); // x^H A' x
658  sens[i]-= eig[i] * x_right[i]->dot(*x_right[i]); // - lambda x^H x
659  sens[i] /= denom[i]; // x^H x
660  }
661  break;
662 
663  case libMesh::GHEP: {
664 
665  matrix_A->vector_mult(*tmp, *x_right[i]);
666  sens[i] = x_right[i]->dot(*tmp); // x^H A' x
667  matrix_B->vector_mult(*tmp, *x_right[i]);
668  sens[i]-= eig[i] * x_right[i]->dot(*tmp); // - lambda x^H B' x
669  sens[i] /= denom[i]; // x^H B x
670  }
671  break;
672 
673  default:
674  // to be implemented for the non-Hermitian problems
675  libmesh_error();
676  break;
677  }
678  }
679 
680  // now delete the x_right vectors
681  for (unsigned int i=0; i<x_right.size(); i++)
682  delete x_right[i];
683 
684  assembly.clear_elem_operation_object();
685 }
686 
687 
688 
689 void
692 
693  // This has been adapted from
694  // libMesh::CondensedEigenSystem::initialize_condensed_dofs()
695  const libMesh::DofMap & dof_map = this->get_dof_map();
696 
697  std::set<unsigned int> global_dirichlet_dofs_set;
698 
699  physics.get_system_dirichlet_bc_dofs(*this, global_dirichlet_dofs_set);
700 
701 
702  // First, put all local dofs into non_dirichlet_dofs_set and
703  std::set<unsigned int> local_non_condensed_dofs_set;
704  for(unsigned int i=this->get_dof_map().first_dof();
705  i<this->get_dof_map().end_dof();
706  i++)
707  if (!dof_map.is_constrained_dof(i))
708  local_non_condensed_dofs_set.insert(i);
709 
710  // Now erase the condensed dofs
711  std::set<unsigned int>::iterator
712  iter = global_dirichlet_dofs_set.begin(),
713  iter_end = global_dirichlet_dofs_set.end();
714 
715  for ( ; iter != iter_end ; ++iter) {
716 
717  unsigned int condensed_dof_index = *iter;
718 
719  if ( (this->get_dof_map().first_dof() <= condensed_dof_index) &&
720  (condensed_dof_index < this->get_dof_map().end_dof()) )
721  local_non_condensed_dofs_set.erase(condensed_dof_index);
722  }
723 
724  // Finally, move local_non_condensed_dofs_set over to a vector for
725  // convenience in solve()
726  iter = local_non_condensed_dofs_set.begin();
727  iter_end = local_non_condensed_dofs_set.end();
728 
730 
731  for ( ; iter != iter_end; ++iter)
732  _local_non_condensed_dofs_vector.push_back(*iter);
733 
735 }
736 
737 
738 
739 
740 
741 void
742 MAST::NonlinearSystem::sensitivity_solve(const libMesh::NumericVector<Real>& X,
743  bool if_localize_sol,
745  MAST::AssemblyBase& assembly,
746  const MAST::FunctionBase& p,
747  bool if_assemble_jacobian) {
748 
749  libmesh_assert(_operation == MAST::NonlinearSystem::NONE);
750 
752 
753  // Log how long the linear solve takes.
754  LOG_SCOPE("sensitivity_solve()", "NonlinearSystem");
755 
756  assembly.set_elem_operation_object(elem_ops);
757 
758  libMesh::NumericVector<Real>
759  &dsol = this->add_sensitivity_solution(),
760  &rhs = this->add_sensitivity_rhs();
761 
762  if (if_assemble_jacobian)
763  assembly.residual_and_jacobian(X, nullptr, matrix, *this);
764  assembly.sensitivity_assemble(X, if_localize_sol, p, rhs);
765 
766  rhs.scale(-1.);
767 
768  // The sensitivity problem is linear
769  // Our iteration counts and residuals will be sums of the individual
770  // results
771  std::pair<unsigned int, Real>
772  solver_params = this->get_linear_solve_parameters();
773 
774  // Solve the linear system.
775  libMesh::SparseMatrix<Real> * pc = this->request_matrix("Preconditioner");
776 
777  std::pair<unsigned int, Real> rval =
778  this->linear_solver->solve (*matrix, pc,
779  dsol,
780  rhs,
781  solver_params.second,
782  solver_params.first);
783 
784  // The linear solver may not have fit our constraints exactly
785 #ifdef LIBMESH_ENABLE_CONSTRAINTS
786  this->get_dof_map().enforce_constraints_exactly (*this, &dsol, /* homogeneous = */ true);
787 #endif
788 
789  assembly.clear_elem_operation_object();
790 
792 
793 
794 }
795 
796 
797 
798 void
799 MAST::NonlinearSystem::adjoint_solve(const libMesh::NumericVector<Real>& X,
800  bool if_localize_sol,
803  MAST::AssemblyBase& assembly,
804  bool if_assemble_jacobian) {
805 
806 
807  libmesh_assert(_operation == MAST::NonlinearSystem::NONE);
808 
810 
811  // Log how long the linear solve takes.
812  LOG_SCOPE("adjoint_solve()", "NonlinearSystem");
813 
814  libMesh::NumericVector<Real>
815  &dsol = this->add_adjoint_solution(),
816  &rhs = this->add_adjoint_rhs();
817 
818  assembly.set_elem_operation_object(elem_ops);
819 
820  if (if_assemble_jacobian)
821  assembly.residual_and_jacobian(*solution, nullptr, matrix, *this);
822 
823  assembly.calculate_output_derivative(X, if_localize_sol, output, rhs);
824 
825  assembly.clear_elem_operation_object();
826 
827  rhs.scale(-1.);
828 
829  // Our iteration counts and residuals will be sums of the individual
830  // results
831  std::pair<unsigned int, Real>
832  solver_params = this->get_linear_solve_parameters();
833 
834  const std::pair<unsigned int, Real> rval =
835  linear_solver->adjoint_solve (*matrix,
836  dsol,
837  rhs,
838  solver_params.second,
839  solver_params.first);
840 
841  // The linear solver may not have fit our constraints exactly
842 #ifdef LIBMESH_ENABLE_CONSTRAINTS
843  this->get_dof_map().enforce_adjoint_constraints_exactly(dsol, 0);
844 #endif
845 
847 }
848 
849 
850 
851 
852 
853 void
854 MAST::NonlinearSystem::write_out_vector(libMesh::NumericVector<Real>& vec,
855  const std::string & directory_name,
856  const std::string & data_name,
857  const bool write_binary_vectors)
858 {
859  LOG_SCOPE("write_out_vector()", "NonlinearSystem");
860 
861  if (this->processor_id() == 0)
862  {
863  // Make a directory to store all the data files
864  libMesh::Utility::mkdir(directory_name.c_str());
865  }
866 
867  // Make sure processors are synced up before we begin
868  this->comm().barrier();
869 
870  std::ostringstream file_name;
871  const std::string suffix = (write_binary_vectors ? ".xdr" : ".dat");
872 
873  file_name << directory_name << "/" << data_name << "_data" << suffix;
874  libMesh::Xdr bf_data(file_name.str(),
875  write_binary_vectors ? libMesh::ENCODE : libMesh::WRITE);
876 
877  std::string version("libMesh-" + libMesh::get_io_compatibility_version());
878 #ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
879  version += " with infinite elements";
880 #endif
881  bf_data.data(version ,"# File Format Identifier");
882 
883  this->write_header(bf_data, /*(unused arg)*/ version, /*write_additional_data=*/false);
884 
885  // Following EquationSystemsIO::write, we use a temporary numbering (node major)
886  // before writing out the data
887  libMesh::MeshTools::Private::globally_renumber_nodes_and_elements(this->get_mesh());
888 
889  // Write all vectors at once.
890  {
891  // Note the API wants pointers to constant vectors, hence this...
892  std::vector<const libMesh::NumericVector<Real> *> bf_out(1);
893  bf_out[0] = &vec;
894  this->write_serialized_vectors (bf_data, bf_out);
895  }
896 
897 
898  // set the current version
899  bf_data.set_version(LIBMESH_VERSION_ID(LIBMESH_MAJOR_VERSION,
900  LIBMESH_MINOR_VERSION,
901  LIBMESH_MICRO_VERSION));
902 
903 
904  // Undo the temporary renumbering
905  this->get_mesh().fix_broken_node_and_element_numbering();
906 }
907 
908 
909 
910 void
911 MAST::NonlinearSystem::read_in_vector(libMesh::NumericVector<Real>& vec,
912  const std::string & directory_name,
913  const std::string & data_name,
914  const bool read_binary_vector) {
915 
916  LOG_SCOPE("read_in_vector()", "NonlinearSystem");
917 
918  // Make sure processors are synced up before we begin
919  this->comm().barrier();
920 
921 
922  // Following EquationSystemsIO::read, we use a temporary numbering (node major)
923  // before writing out the data. For the sake of efficiency, we do this once for
924  // all the vectors that we read in.
925  libMesh::MeshTools::Private::globally_renumber_nodes_and_elements(this->get_mesh());
926 
927  std::ostringstream file_name;
928  const std::string suffix = (read_binary_vector ? ".xdr" : ".dat");
929  file_name.str("");
930  file_name << directory_name
931  << "/" << data_name
932  << "_data" << suffix;
933 
934  // On processor zero check to be sure the file exists
935  if (this->processor_id() == 0)
936  {
937  struct stat stat_info;
938  int stat_result = stat(file_name.str().c_str(), &stat_info);
939 
940  if (stat_result != 0)
941  libmesh_error_msg("File does not exist: " + file_name.str());
942  }
943 
944  if (!std::ifstream(file_name.str()))
945  libmesh_error_msg("File missing: " + file_name.str());
946 
947  libMesh::Xdr vector_data(file_name.str(),
948  read_binary_vector ? libMesh::DECODE : libMesh::READ);
949 
950  // Read the header data. This block of code is based on EquationSystems::_read_impl.
951  {
952  std::string version;
953  vector_data.data(version);
954 
955  const std::string libMesh_label = "libMesh-";
956  std::string::size_type lm_pos = version.find(libMesh_label);
957  if (lm_pos==std::string::npos)
958  {
959  libmesh_error_msg("version info missing in Xdr header");
960  }
961 
962  std::istringstream iss(version.substr(lm_pos + libMesh_label.size()));
963  int ver_major = 0, ver_minor = 0, ver_patch = 0;
964  char dot;
965  iss >> ver_major >> dot >> ver_minor >> dot >> ver_patch;
966  vector_data.set_version(LIBMESH_VERSION_ID(ver_major, ver_minor, ver_patch));
967 
968  // Actually read the header data. When we do this, set read_header=false
969  // so taht we do not reinit sys, since we assume that it has already been
970  // set up properly (e.g. the appropriate variables have already been added).
971  this->read_header(vector_data, version, /*read_header=*/false, /*read_additional_data=*/false);
972  }
973 
974  std::vector<libMesh::NumericVector<Real> *> bf_in(1);
975  bf_in[0] = &vec;
976  this->read_serialized_vectors (vector_data, bf_in);
977 
978  // Undo the temporary renumbering
979  this->get_mesh().fix_broken_node_and_element_numbering();
980 }
981 
982 
983 
984 void
986 project_vector_without_dirichlet (libMesh::NumericVector<Real> & new_vector,
987  libMesh::FunctionBase<Real>& f) const {
988 
989  LOG_SCOPE ("project_vector_without_dirichlet()", "NonlinearSystem");
990 
991  libMesh::ConstElemRange active_local_range
992  (this->get_mesh().active_local_elements_begin(),
993  this->get_mesh().active_local_elements_end() );
994 
995  libMesh::VectorSetAction<Real> setter(new_vector);
996 
997  const unsigned int n_variables = this->n_vars();
998 
999  std::vector<unsigned int> vars(n_variables);
1000  for (unsigned int i=0; i != n_variables; ++i)
1001  vars[i] = i;
1002 
1003  // Use a typedef to make the calling sequence for parallel_for() a bit more readable
1004  typedef
1005  libMesh::GenericProjector<libMesh::FEMFunctionWrapper<Real>, libMesh::FEMFunctionWrapper<libMesh::Gradient>,
1006  Real, libMesh::VectorSetAction<Real>> FEMProjector;
1007 
1008  libMesh::WrappedFunctor<Real> f_fem(f);
1009  libMesh::FEMFunctionWrapper<Real> fw(f_fem);
1010 
1011 #if (LIBMESH_MAJOR_VERSION == 1 && LIBMESH_MINOR_VERSION < 5)
1012  libMesh::Threads::parallel_for
1013  (active_local_range,
1014  FEMProjector(*this, fw, nullptr, setter, vars));
1015 #else
1016  FEMProjector projector(*this, fw, nullptr, setter, vars);
1017  projector.project(active_local_range);
1018 #endif
1019 
1020  // Also, load values into the SCALAR dofs
1021  // Note: We assume that all SCALAR dofs are on the
1022  // processor with highest ID
1023  if (this->processor_id() == (this->n_processors()-1))
1024  {
1025  // FIXME: Do we want to first check for SCALAR vars before building this? [PB]
1026  libMesh::FEMContext context( *this );
1027 
1028  const libMesh::DofMap & dof_map = this->get_dof_map();
1029  for (unsigned int var=0; var<this->n_vars(); var++)
1030  if (this->variable(var).type().family == libMesh::SCALAR)
1031  {
1032  // FIXME: We reinit with an arbitrary element in case the user
1033  // doesn't override FEMFunctionBase::component. Is there
1034  // any use case we're missing? [PB]
1035  libMesh::Elem * el = const_cast<libMesh::Elem *>(*(this->get_mesh().active_local_elements_begin()));
1036  context.pre_fe_reinit(*this, el);
1037 
1038  std::vector<libMesh::dof_id_type> SCALAR_indices;
1039  dof_map.SCALAR_dof_indices (SCALAR_indices, var);
1040  const unsigned int n_SCALAR_dofs =
1041  libMesh::cast_int<unsigned int>(SCALAR_indices.size());
1042 
1043  for (unsigned int i=0; i<n_SCALAR_dofs; i++)
1044  {
1045  const libMesh::dof_id_type global_index = SCALAR_indices[i];
1046  const unsigned int component_index =
1047  this->variable_scalar_number(var,i);
1048 
1049  new_vector.set(global_index, f_fem.component(context, component_index, libMesh::Point(), this->time));
1050  }
1051  }
1052  }
1053 
1054  new_vector.close();
1055 }
unsigned int _n_iterations
The number of iterations of the eigen solver algorithm.
virtual void eigenproblem_assemble(libMesh::SparseMatrix< Real > *A, libMesh::SparseMatrix< Real > *B)
assembles the matrices for eigenproblem depending on the analysis type
void initialize_condensed_dofs(MAST::PhysicsDisciplineBase &physics)
Loop over the dofs on each processor to initialize the list of non-condensed dofs.
void read_in_vector(libMesh::NumericVector< Real > &vec, const std::string &directory_name, const std::string &data_name, const bool read_binary_vectors)
reads the specified vector with the specified name in a directory.
This class implements a system for solution of nonlinear systems.
bool _is_generalized_eigenproblem
A boolean flag to indicate whether we are dealing with a generalized eigenvalue problem.
virtual void calculate_output_derivative(const libMesh::NumericVector< Real > &X, bool if_localize_sol, MAST::OutputAssemblyElemOperations &output, libMesh::NumericVector< Real > &dq_dX)
calculates
virtual void clear_elem_operation_object()
clears the association of this object with the assembly element operation object. ...
virtual void clear() libmesh_override
Clear all the data structures associated with the system.
void write_out_vector(libMesh::NumericVector< Real > &vec, const std::string &directory_name, const std::string &data_name, const bool write_binary_vectors)
writes the specified vector with the specified name in a directory.
unsigned int _n_requested_eigenpairs
The number of requested eigenpairs.
This provides the base class for definitin of element level contribution of output quantity in an ana...
std::unique_ptr< MAST::SlepcEigenSolver > eigen_solver
The EigenSolver, definig which interface, i.e solver package to use.
#define eps
virtual void set_elem_operation_object(MAST::AssemblyElemOperations &elem_ops)
attaches a element operation to this object, and associated this with the element operation object...
bool _initialize_B_matrix
initialize the B matrix in addition to A, which might be needed for solution of complex system of equ...
This class inherits from libMesh::SlepcEigenSolver<Real> and implements a method for retriving the re...
libMesh::Real Real
void set_eigenproblem_type(libMesh::EigenProblemType ept)
Sets the type of the current eigen problem.
libMesh::Complex Complex
virtual void reinit() libmesh_override
Reinitializes the member data fields associated with the system, so that, e.g., assemble() may be use...
MAST::NonlinearSystem::Operation _operation
current operation of the system
virtual void eigenproblem_sensitivity_solve(MAST::AssemblyElemOperations &elem_ops, MAST::EigenproblemAssembly &assembly, const MAST::FunctionBase &f, std::vector< Real > &sens, const std::vector< unsigned int > *indices=nullptr)
Solves the sensitivity system, for the provided parameters.
virtual bool eigenproblem_sensitivity_assemble(const MAST::FunctionBase &f, libMesh::SparseMatrix< Real > *sensitivity_A, libMesh::SparseMatrix< Real > *sensitivity_B)
Assembly function.
void get_system_dirichlet_bc_dofs(libMesh::System &sys, std::set< unsigned int > &dof_ids) const
Prepares a list of the constrained dofs for system sys and returns in dof_ids.
Assembles the system of equations for an eigenproblem of type .
libMesh::SparseMatrix< Real > * matrix_A
The system matrix for standard eigenvalue problems.
void project_vector_without_dirichlet(libMesh::NumericVector< Real > &new_vector, libMesh::FunctionBase< Real > &f) const
bool _condensed_dofs_initialized
A private flag to indicate whether the condensed dofs have been initialized.
virtual void eigenproblem_solve(MAST::AssemblyElemOperations &elem_ops, MAST::EigenproblemAssembly &assembly)
Assembles & solves the eigen system.
virtual std::pair< unsigned int, Real > get_linear_solve_parameters()
calls NonlinearImplicitSystem::set_solver_parameters() before accessing the values.
virtual void init_data() libmesh_override
Initializes the member data fields associated with the system, so that, e.g., assemble() may be used...
libMesh::SparseMatrix< Real > * matrix_B
A second system matrix for generalized eigenvalue problems.
libMesh::EigenProblemType _eigen_problem_type
The type of the eigenvalue problem.
virtual void get_eigenvalue(unsigned int i, Real &re, Real &im)
gets the real and imaginary parts of the ith eigenvalue for the eigenproblem , and the associated eig...
std::unique_ptr< libMesh::LinearSolver< Real > > linear_solver
The LinearSolver for solution of the linear equations.
unsigned int _n_converged_eigenpairs
The number of converged eigenpairs.
std::vector< libMesh::dof_id_type > _local_non_condensed_dofs_vector
Vector storing the local dof indices that will not be condensed.
virtual void get_eigenpair(unsigned int i, Real &re, Real &im, libMesh::NumericVector< Real > &vec_re, libMesh::NumericVector< Real > *vec_im=nullptr)
gets the real and imaginary parts of the ith eigenvalue for the eigenproblem , and the associated eig...
virtual void residual_and_jacobian(const libMesh::NumericVector< Real > &X, libMesh::NumericVector< Real > *R, libMesh::SparseMatrix< Real > *J, libMesh::NonlinearImplicitSystem &S)
function that assembles the matrices and vectors quantities for nonlinear solution ...
bool _exchange_A_and_B
flag to exchange the A and B matrices in the eigenproblem solution
virtual void sensitivity_solve(const libMesh::NumericVector< Real > &X, bool if_localize_sol, MAST::AssemblyElemOperations &elem_ops, MAST::AssemblyBase &assembly, const MAST::FunctionBase &p, bool if_assemble_jacobian=true)
Solves the sensitivity problem for the provided parameter.
NonlinearSystem(libMesh::EquationSystems &es, const std::string &name, const unsigned int number)
Default constructor.
virtual bool sensitivity_assemble(const libMesh::NumericVector< Real > &X, bool if_localize_sol, const MAST::FunctionBase &f, libMesh::NumericVector< Real > &sensitivity_rhs, bool close_vector=true)
Assembly function.
virtual void adjoint_solve(const libMesh::NumericVector< Real > &X, bool if_localize_sol, MAST::AssemblyElemOperations &elem_ops, MAST::OutputAssemblyElemOperations &output, MAST::AssemblyBase &assembly, bool if_assemble_jacobian=true)
solves the adjoint problem for the provided output function.
virtual void solve(MAST::AssemblyElemOperations &elem_ops, MAST::AssemblyBase &assembly)
solves the nonlinear problem with the specified assembly operation object