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