MAST
stabilized_first_order_transient_sensitivity_solver.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 
21 // MAST includes
25 #include "base/elem_base.h"
26 #include "base/nonlinear_system.h"
27 #include "base/assembly_base.h"
30 
31 // libMesh includes
32 #include "libmesh/sparse_matrix.h"
33 #include "libmesh/numeric_vector.h"
34 #include "libmesh/linear_solver.h"
35 #include "libmesh/petsc_matrix.h"
36 #include "libmesh/enum_norm_type.h"
37 #include "libmesh/dof_map.h"
38 
39 
43 max_amp (1.),
44 beta (1.),
45 max_index (0),
46 _use_eigenvalue_stabilization (true),
47 _assemble_mass (false),
48 _t0 (0.),
49 _index0 (0),
50 _index1 (0) {
51 
52 }
53 
54 
57 
58 }
59 
60 
61 void
64 
66 }
67 
68 
69 void
71 set_nolinear_solution_location(std::string& file_root,
72  std::string& dir) {
73 
74  _sol_name_root = file_root;
75  _sol_dir = dir;
76 }
77 
78 
79 void
82  const MAST::FunctionBase& f) {
83 
84  libmesh_assert_greater( max_amp, 0.);
85  libmesh_assert_greater(max_index, 0);
86  libmesh_assert(_sol_name_root.size());
87  libmesh_assert(_sol_dir.size());
88 
89  // Log how long the linear solve takes.
90  LOG_SCOPE("sensitivity_solve()", "StabilizedSensitivity");
91 
92  MAST::NonlinearSystem& sys = assembly.system();
93 
94  assembly.set_elem_operation_object(*this);
95 
96  libMesh::SparseMatrix<Real>
97  &M1 = *sys.matrix,
98  &J_int = *sys.matrix_A,
99  &M_avg = *sys.matrix_B;
100 
101  // build the system matrix
102  std::unique_ptr<libMesh::SparseMatrix<Real>>
103  M0(libMesh::SparseMatrix<Real>::build(sys.comm()).release());
104  sys.get_dof_map().attach_matrix(*M0);
105  M0->init();
106  M0->zero();
107  M0->close();
108 
109 
110 
111  libMesh::NumericVector<Real>
112  &sol = this->solution(),
113  &dsol0 = this->solution_sensitivity(1), // previous solution
114  &dsol1 = this->solution_sensitivity(), // current solution
115  &rhs = sys.add_sensitivity_rhs();
116 
117  std::unique_ptr<libMesh::NumericVector<Real>>
118  f_int(rhs.zero_clone().release()),
119  vec1(rhs.zero_clone().release());
120 
121  J_int.zero();
122  M_avg.zero();
123  rhs.zero();
124  J_int.close();
125  M_avg.close();
126  rhs.close();
127 
128  _index0 = _index1;
129 
130  Real
131  amp = 0.;
132 
133  _t0 = sys.time;
134  sys.time += this->dt;
135  _index1++;
136 
137  bool
138  continue_it = true;
139 
140  // now iterate till a suitable time-step has been found
141  while (continue_it) {
142 
143  // update the solution vector
144  std::ostringstream oss;
145  oss << _sol_name_root << _index1;
146  sys.read_in_vector(sol, _sol_dir, oss.str(), true);
147 
148  // assemble the Jacobian matrix
149  _assemble_mass = false;
150  assembly.residual_and_jacobian(sol, nullptr, &M1, sys);
151  J_int.add(this->dt, M1);
152 
153  // assemble the mass matrix
154  _assemble_mass = true;
155  assembly.residual_and_jacobian(sol, nullptr, &M1, sys);
156  Mat M_avg_mat = dynamic_cast<libMesh::PetscMatrix<Real>&>(M_avg).mat();
157  PetscErrorCode ierr = MatScale(M_avg_mat, (sys.time - _t0 - this->dt)/(sys.time - _t0));
158  CHKERRABORT(sys.comm().get(), ierr);
159  M_avg.add(this->dt/(sys.time - _t0), M1);
160 
161  // close the quantities
162  J_int.close();
163  M_avg.close();
164 
165  // assemble the forcing vector
166  assembly.sensitivity_assemble(f, rhs);
167  rhs.scale(-1.);
168  f_int->add(this->dt, rhs); // int_0^t F dt
169  f_int->close();
170  rhs.zero();
171  M_avg.vector_mult(rhs, dsol0); // M_avg x0
172  rhs.add(1., *f_int); // M_avg x0 + int_0^t F dt
173  rhs.close();
174 
175  // now compute the matrix for the linear solution
176  // Note that the stabilized solver for system definde as
177  // M x_dot = f(x) will define the Jacobian as (M_avg - Jint).
178  // However, since MAST defines the system as M x_dot - f(x) = 0
179  // and returns the Jacobian as J = -df(x)/dx, we will not multiply
180  // the Jacobian with -1.
181  M1.zero();
182  M1.add(1., M_avg);
183  M1.add(this->beta, J_int); // see explanation above for positive sign.
184  M1.close();
185 
186  M0->zero();
187  M0->add(1., M_avg);
188  M0->add(-(1.-this->beta), J_int); // see explanation above for negative sign.
189  M0->close();
190 
191 
192  // The sensitivity problem is linear
193  // Our iteration counts and residuals will be sums of the individual
194  // results
195  std::pair<unsigned int, Real>
196  solver_params = sys.get_linear_solve_parameters();
197 
198  // Solve the linear system.
199  libMesh::SparseMatrix<Real> * pc = sys.request_matrix("Preconditioner");
200 
201  std::pair<unsigned int, Real> rval =
202  sys.linear_solver->solve (M1, pc,
203  dsol1,
204  rhs,
205  solver_params.second,
206  solver_params.first);
207 
208  // The linear solver may not have fit our constraints exactly
209 #ifdef LIBMESH_ENABLE_CONSTRAINTS
210  sys.get_dof_map().enforce_constraints_exactly (sys, &dsol1, /* homogeneous = */ true);
211 #endif
212 
213  M_avg.vector_mult(*vec1, dsol1);
214 
215  // estimate the amplification factor. The norm based method only
216  // works for beta = 1 (backward Euler).
217  if (!_use_eigenvalue_stabilization && this->beta == 1.)
218  amp = _compute_norm_amplification_factor(rhs, *vec1);
219  else
220  amp = _compute_eig_amplification_factor(*M0, M1);
221 
222  if (amp <= this->max_amp || _index1 > max_index) {
223 
224  continue_it = false;
225 
226  // we may have reached here due to end of time integration.
227  // In this case, the final Dmag may be unstable. If this is the
228  // case, we should throw that solution out.
229  if (amp <= this->max_amp) {
230 
231  libMesh::out << "accepting sol" << std::endl;
232  dsol0.zero();
233  dsol0.add(1., dsol1);
234  dsol0.close();
235  }
236  else {
237  // present sol is same as previous sol
238  dsol1.zero();
239  dsol1.add(1., dsol0);
240  dsol1.zero();
241 
242  libMesh::out << "reached final step. Rjecting solution" << std::endl;
243  }
244  }
245  else {
246 
247  _index1++;
248  sys.time += this->dt;
249  }
250  }
251 
252 
253  assembly.clear_elem_operation_object();
254 }
255 
256 
257 Real
260  const MAST::FunctionBase& p,
262 
263 
264  MAST::NonlinearSystem& sys = assembly.system();
265 
266  Real
267  eta = 0.,
268  t1 = sys.time,
269  q = 0.;
270 
271  libMesh::NumericVector<Real>
272  &sol = this->solution(),
273  &dx0 = this->solution_sensitivity(1),
274  &dx1 = this->solution_sensitivity();
275 
276  std::unique_ptr<libMesh::NumericVector<Real>>
277  dx(dx0.zero_clone().release());
278 
279  libmesh_assert_greater(_index1, _index0);
280 
281  for (unsigned int i=_index0; i<_index1; i++) {
282 
283  // update the nonlinear solution
284  std::ostringstream oss;
285  oss << _sol_name_root << _index1;
286  sys.read_in_vector(sol, _sol_dir, oss.str(), true);
287 
288  sys.time = _t0 + this->dt * ( i - _index0);
289 
290  eta = (sys.time-_t0)/(t1-_t0);
291 
292  dx->zero();
293  dx->add(1.-eta, dx0);
294  dx->add( eta, dx1);
295  dx->close();
296 
297  output.zero_for_analysis();
298  assembly.calculate_output_direct_sensitivity(sol, dx.get(), p, output);
299 
300  q += output.output_sensitivity_total(p) * this->dt;
301  }
302 
303  return q;
304 }
305 
306 
307 
308 void
310 set_element_data(const std::vector<libMesh::dof_id_type>& dof_indices,
311  const std::vector<libMesh::NumericVector<Real>*>& sols) {
312 
313  libmesh_assert_equal_to(sols.size(), 2);
314 
315  const unsigned int n_dofs = (unsigned int)dof_indices.size();
316 
317  // get the current state and velocity estimates
318  // also get the current discrete velocity replacement
320  sol = RealVectorX::Zero(n_dofs),
321  vel = RealVectorX::Zero(n_dofs);
322 
323 
324  const libMesh::NumericVector<Real>
325  &sol_global = *sols[0],
326  &vel_global = *sols[1];
327 
328  // get the references to current and previous sol and velocity
329 
330  for (unsigned int i=0; i<n_dofs; i++) {
331 
332  sol(i) = sol_global(dof_indices[i]);
333  vel(i) = vel_global(dof_indices[i]);
334  }
335 
336  _assembly_ops->set_elem_solution(sol);
337  _assembly_ops->set_elem_velocity(vel);
338 }
339 
340 
341 
342 void
344 extract_element_sensitivity_data(const std::vector<libMesh::dof_id_type>& dof_indices,
345  const std::vector<libMesh::NumericVector<Real>*>& sols,
346  std::vector<RealVectorX>& local_sols) {
347 
348  libmesh_assert_equal_to(sols.size(), 2);
349 
350  const unsigned int n_dofs = (unsigned int)dof_indices.size();
351 
352  local_sols.resize(2);
353 
355  &sol = local_sols[0],
356  &vel = local_sols[1];
357 
358  sol = RealVectorX::Zero(n_dofs);
359  vel = RealVectorX::Zero(n_dofs);
360 
361  const libMesh::NumericVector<Real>
362  &sol_global = *sols[0],
363  &vel_global = *sols[1];
364 
365  // get the references to current and previous sol and velocity
366 
367  for (unsigned int i=0; i<n_dofs; i++) {
368 
369  sol(i) = sol_global(dof_indices[i]);
370  vel(i) = vel_global(dof_indices[i]);
371  }
372 
373 }
374 
375 
376 void
378 update_velocity(libMesh::NumericVector<Real>& vec,
379  const libMesh::NumericVector<Real>& sol) {
380 
381  const libMesh::NumericVector<Real>
382  &prev_sol = this->solution(1),
383  &prev_vel = this->velocity(1);
384 
385  vec.zero();
386  vec.add( 1., sol);
387  vec.add(-1., prev_sol);
388  vec.scale(1./beta/dt);
389  vec.close();
390  vec.add(-(1.-beta)/beta, prev_vel);
391 
392  vec.close();
393 }
394 
395 
396 void
398 update_sensitivity_velocity(libMesh::NumericVector<Real>& vec,
399  const libMesh::NumericVector<Real>& sol) {
400 
401  const libMesh::NumericVector<Real>
402  &prev_sol = this->solution_sensitivity(1),
403  &prev_vel = this->velocity_sensitivity(1);
404 
405  vec.zero();
406  vec.add( 1., sol);
407  vec.add(-1., prev_sol);
408  vec.scale(1./beta/dt);
409  vec.close();
410  vec.add(-(1.-beta)/beta, prev_vel);
411 
412  vec.close();
413 }
414 
415 
416 
417 void
419 elem_calculations(bool if_jac,
420  RealVectorX& vec,
421  RealMatrixX& mat) {
422  // make sure that the assembly object is provided
423  libmesh_assert(_assembly_ops);
424  unsigned int n_dofs = (unsigned int)vec.size();
425 
427  f_x = RealVectorX::Zero(n_dofs),
428  f_m = RealVectorX::Zero(n_dofs);
429 
431  f_m_jac_xdot = RealMatrixX::Zero(n_dofs, n_dofs),
432  f_m_jac = RealMatrixX::Zero(n_dofs, n_dofs),
433  f_x_jac = RealMatrixX::Zero(n_dofs, n_dofs);
434 
435  // perform the element assembly
436  _assembly_ops->elem_calculations(if_jac,
437  f_m, // mass vector
438  f_x, // forcing vector
439  f_m_jac_xdot, // Jac of mass wrt x_dot
440  f_m_jac, // Jac of mass wrt x
441  f_x_jac); // Jac of forcing vector wrt x
442 
443  if (_assemble_mass)
444  mat = f_m_jac_xdot;
445  else
446  mat = f_m_jac + f_x_jac;
447 }
448 
449 
450 
451 void
454  RealVectorX& vec) {
455 
456  // make sure that the assembly object is provided
457  libmesh_assert(_assembly_ops);
458  unsigned int n_dofs = (unsigned int)vec.size();
459 
461  f_x = RealVectorX::Zero(n_dofs),
462  f_m = RealVectorX::Zero(n_dofs);
463 
464  // perform the element assembly
465  _assembly_ops->elem_sensitivity_calculations(f,
466  f_m, // mass vector
467  f_x); // forcing vector
468 
469  // sensitivity of system residual involving mass and internal force term
470  vec = (f_m + f_x);
471 }
472 
473 
474 
475 
476 void
478 elem_sensitivity_contribution_previous_timestep(const std::vector<RealVectorX>& prev_sols,
479  RealVectorX& vec) {
480 
481  vec.setZero();
482 }
483 
484 
485 Real
487 _compute_norm_amplification_factor(const libMesh::NumericVector<Real>& sol0,
488  const libMesh::NumericVector<Real>& sol1) {
489 
490  libmesh_assert(_system);
491 
493  sys = _system->system();
494 
495  Real
496  norm0 = 0.,
497  norm1 = 0.;
498 
499  unsigned int
500  n_vars = _system->n_vars();
501 
503  var_norm = RealVectorX::Zero(n_vars);
504 
505  for (unsigned int i=0; i<n_vars; i++) {
506 
507  norm0 = sys.calculate_norm(sol0, i, libMesh::L2);
508  norm1 = sys.calculate_norm(sol1, i, libMesh::L2);
509 
510  if (norm0 > 0.)
511  var_norm(i) = norm1/norm0;
512  }
513 
514  libMesh::out << "amp coeffs: " << var_norm.transpose() << std::endl;
515  return var_norm.maxCoeff();
516 }
517 
518 
519 Real
521 _compute_eig_amplification_factor(libMesh::SparseMatrix<Real>& A,
522  libMesh::SparseMatrix<Real>& B) {
523 
524  libmesh_assert(_system);
525 
527  sys = _system->system();
528 
529  std::unique_ptr<MAST::SlepcEigenSolver>
530  eigen_solver(new MAST::SlepcEigenSolver(sys.comm()));
531 
532  if (libMesh::on_command_line("--solver_system_names")) {
533 
534  EPS eps = eigen_solver.get()->eps();
535  std::string nm = sys.name() + "_";
536  EPSSetOptionsPrefix(eps, nm.c_str());
537  }
538  eigen_solver->set_eigenproblem_type(libMesh::GNHEP);
539  eigen_solver->set_position_of_spectrum(libMesh::LARGEST_MAGNITUDE);
540  eigen_solver->init();
541 
542  // solve the eigenvalue problem
543  // Ax = lambda Bx
544  std::pair<unsigned int, unsigned int>
545  solve_data = eigen_solver->solve_generalized (A, B,
546  1,
547  20,
548  1.e-6,
549  50);
550 
551  // make sure that atleast one eigenvalue was computed
552  libmesh_assert(solve_data.first);
553 
554  std::pair<Real, Real> pair = eigen_solver->get_eigenvalue(0);
555  std::complex<Real> eig(pair.first, pair.second);
556  Real amp = std::abs(eig);
557 
558  libMesh::out << "amp coeffs: " << amp << std::endl;
559  return amp;
560 }
MAST::NonlinearSystem & system()
void set_nolinear_solution_location(std::string &file_root, std::string &dir)
sets the directory where the nonlinear solutions are stored.
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.
virtual void elem_sensitivity_calculations(const MAST::FunctionBase &f, RealVectorX &vec)
performs the element sensitivity calculations over elem, and returns the element residual sensitivity...
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 extract_element_sensitivity_data(const std::vector< libMesh::dof_id_type > &dof_indices, const std::vector< libMesh::NumericVector< Real > * > &sols, std::vector< RealVectorX > &local_sols)
void set_eigenvalue_stabilization(bool f)
sets if the eigenvalue-based stabilization will be used.
This provides the base class for definitin of element level contribution of output quantity in an ana...
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...
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::NumericVector< Real > & solution_sensitivity(unsigned int prev_iter=0) const
libMesh::NumericVector< Real > & velocity(unsigned int prev_iter=0) const
virtual void sensitivity_solve(MAST::AssemblyBase &assembly, const MAST::FunctionBase &f)
solvers the current time step for sensitivity wrt f
virtual void update_sensitivity_velocity(libMesh::NumericVector< Real > &vec, const libMesh::NumericVector< Real > &sol)
update the transient sensitivity velocity based on the current sensitivity solution ...
Real _compute_eig_amplification_factor(libMesh::SparseMatrix< Real > &A, libMesh::SparseMatrix< Real > &B)
libMesh::SparseMatrix< Real > * matrix_A
The system matrix for standard eigenvalue problems.
Matrix< Real, Dynamic, Dynamic > RealMatrixX
virtual void set_element_data(const std::vector< libMesh::dof_id_type > &dof_indices, const std::vector< libMesh::NumericVector< Real > * > &sols)
libMesh::NumericVector< Real > & velocity_sensitivity(unsigned int prev_iter=0) const
virtual std::pair< unsigned int, Real > get_linear_solve_parameters()
calls NonlinearImplicitSystem::set_solver_parameters() before accessing the values.
virtual void calculate_output_direct_sensitivity(const libMesh::NumericVector< Real > &X, const libMesh::NumericVector< Real > *dXdp, const MAST::FunctionBase &p, MAST::OutputAssemblyElemOperations &output)
evaluates the sensitivity of the outputs in the attached discipline with respect to the parametrs in ...
Matrix< Real, Dynamic, 1 > RealVectorX
virtual Real output_sensitivity_total(const MAST::FunctionBase &p)=0
virtual void elem_sensitivity_contribution_previous_timestep(const std::vector< RealVectorX > &prev_sols, RealVectorX &vec)
libMesh::SparseMatrix< Real > * matrix_B
A second system matrix for generalized eigenvalue problems.
libMesh::NumericVector< Real > & solution(unsigned int prev_iter=0) const
Real _compute_norm_amplification_factor(const libMesh::NumericVector< Real > &sol0, const libMesh::NumericVector< Real > &sol1)
virtual void zero_for_analysis()=0
zeroes the output quantity values stored inside this object so that assembly process can begin...
std::unique_ptr< libMesh::LinearSolver< Real > > linear_solver
The LinearSolver for solution of the linear equations.
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 ...
unsigned int max_index
index of solution that is used for current linearization
virtual void elem_calculations(bool if_jac, RealVectorX &vec, RealMatrixX &mat)
performs the element calculations over elem, and returns the element vector and matrix quantities in ...
const MAST::NonlinearSystem & system() const
virtual Real evaluate_q_sens_for_previous_interval(MAST::AssemblyBase &assembly, const MAST::FunctionBase &p, MAST::OutputAssemblyElemOperations &output)
virtual void update_velocity(libMesh::NumericVector< Real > &vel, const libMesh::NumericVector< Real > &sol)
update the transient velocity based on the current solution
MAST::SystemInitialization * _system