MAST
multiphysics_nonlinear_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/nonlinear_system.h"
26 
27 // libMesh includes
28 #include "libmesh/dof_map.h"
29 #include "libmesh/petsc_matrix.h"
30 #include "libmesh/petsc_vector.h"
31 
32 
33 //---------------------------------------------------------------
34 // method for matrix vector multiplicaiton of the off-diagonal component y=Ax
35 struct
37  unsigned int i;
38  unsigned int j;
39  SNES snes;
41 };
42 
43 
44 PetscErrorCode
45 __mast_multiphysics_petsc_mat_mult(Mat mat,Vec dx,Vec y) {
46 
47  LOG_SCOPE("mat_mult()", "PetscNonlinearSolver");
48 
49  PetscErrorCode ierr=0;
50 
51  libmesh_assert(mat);
52  libmesh_assert(dx);
53  libmesh_assert(y);
54 
55 
56  void * ctx = PETSC_NULL;
57 
58  // get the matrix context. This is for the i^th row-block and j^th column
59  // block, which uses the linear perturbation in the j^th block.
60  ierr = MatShellGetContext(mat, &ctx);
61 
63  *mat_ctx = static_cast<__mast_multiphysics_petsc_shell_context*> (ctx);
64 
65 
67  *solver = mat_ctx->solver;
68 
69  // get the current nonlinear solution
70  Vec x;
71 
72  ierr = SNESGetSolution(mat_ctx->snes, &x); CHKERRABORT(solver->comm().get(), ierr);
73 
74 
75  const unsigned int
76  nd = solver->n_disciplines();
77 
78  std::vector<Vec>
79  sol (nd);
80 
81  std::vector<libMesh::NumericVector<Real>*>
82  sys_sols (nd, nullptr),
83  sys_dsols(nd, nullptr);
84 
85 
87  // get the subvectors for each discipline
89  for (unsigned int i=0; i< nd; i++) {
90 
91  // system for this discipline
93 
94  // get the IS for this system
95  IS sys_is = solver->index_sets()[i];
96 
97  // extract the subvector for this system
98  ierr = VecGetSubVector( x, sys_is, &sol[i]); CHKERRABORT(solver->comm().get(), ierr);
99 
100  // use the nonlinear sol of all disciplines, by the perturbed sol of only
101  // one should be nonzero.
102  sys_sols[i] = new libMesh::PetscVector<Real>( sol[i], sys.comm());
103  if (i == mat_ctx->j) {
104  sys_dsols[i] = new libMesh::PetscVector<Real>(dx, sys.comm());
105 
106  // Enforce constraints (if any) exactly on the
107  // current_local_solution. This is the solution vector that is
108  // actually used in the computation of the residual below, and is
109  // not locked by debug-enabled PETSc the way that "x" is.
110  sys.get_dof_map().enforce_constraints_exactly(sys, sys_dsols[i],
111  true /* homogeneous = true */);
112  }
113  else
114  sys_dsols[i] = sys_sols[i]->zero_clone().release();
115  }
116 
118  // initialize the data structures before calculation of residuals
120  if (solver->get_pre_residual_update_object())
122  sys_dsols);
123 
125  // calculate the matrix-vector product
127 
128  std::unique_ptr<libMesh::NumericVector<Real> >
129  res(new libMesh::PetscVector<Real>(y, solver->comm()));
130 
131  // system for this discipline
132  MAST::NonlinearSystem& sys = solver->get_system_assembly(mat_ctx->i).system();
133 
135  (*sys_sols [mat_ctx->i],
136  *sys_dsols[mat_ctx->i],
137  *res,
138  sys);
139 
140  res->close();
141 
143  // resotre the subvectors
145  for (unsigned int i=0; i< nd; i++) {
146 
147  // system for this discipline
149 
150  // get the IS for this system
151  IS sys_is = solver->index_sets()[i];
152 
153  // delete the NumericVector wrappers
154  delete sys_sols[i];
155  delete sys_dsols[i];
156 
157  // now restore the subvectors
158  ierr = VecRestoreSubVector(x, sys_is, &sol[i]); CHKERRABORT(solver->comm().get(), ierr);
159  }
160 
161  return ierr;
162 }
163 
164 
165 //---------------------------------------------------------------
166 // this function is called by PETSc to evaluate the residual at X
167 PetscErrorCode
168 __mast_multiphysics_petsc_snes_residual (SNES snes, Vec x, Vec r, void * ctx) {
169 
170  LOG_SCOPE("residual()", "PetscMultiphysicsNonlinearSolver");
171 
172  PetscErrorCode ierr=0;
173 
174  libmesh_assert(x);
175  libmesh_assert(r);
176  libmesh_assert(ctx);
177 
179  static_cast<MAST::MultiphysicsNonlinearSolverBase*> (ctx);
180 
181  const unsigned int
182  nd = solver->n_disciplines();
183 
184  std::vector<Vec>
185  sol(nd),
186  res(nd);
187 
188  std::vector<libMesh::NumericVector<Real>*>
189  sys_sols(nd, nullptr),
190  sys_res (nd, nullptr);
191 
192 
194  // get the subvectors for each discipline
196  for (unsigned int i=0; i< nd; i++) {
197 
198  // system for this discipline
200 
201  // get the IS for this system
202  IS sys_is = solver->index_sets()[i];
203 
204  // extract the subvector for this system
205  ierr = VecGetSubVector(x, sys_is, &sol[i]); CHKERRABORT(solver->comm().get(), ierr);
206  ierr = VecGetSubVector(r, sys_is, &res[i]); CHKERRABORT(solver->comm().get(), ierr);
207 
208  sys_sols[i] = new libMesh::PetscVector<Real>(sol[i], sys.comm()),
209  sys_res[i] = new libMesh::PetscVector<Real>(res[i], sys.comm());
210 
211  // Enforce constraints (if any) exactly on the
212  // current_local_solution. This is the solution vector that is
213  // actually used in the computation of the residual below, and is
214  // not locked by debug-enabled PETSc the way that "x" is.
215  sys.get_dof_map().enforce_constraints_exactly(sys, sys_sols[i]);
216  }
217 
219  // initialize the data structures before calculation of residuals
221  if (solver->get_pre_residual_update_object())
223 
225  // calculate the residuals
227  std::vector<Real>
228  l2_norms(nd, 0.);
229 
230  Real
231  global_l2 = 0.;
232 
233  for (unsigned int i=0; i< nd; i++) {
234 
235  // system for this discipline
237 
238  solver->get_system_assembly(i).residual_and_jacobian (*sys_sols[i],
239  sys_res[i],
240  nullptr,
241  sys);
242 
243  sys_res[i]->close();
244 
245  // now calculate the norms
246  l2_norms[i] = sys_res[i]->l2_norm();
247  global_l2 += pow(l2_norms[i], 2);
248  }
249 
250  global_l2 = pow(global_l2, 0.5);
251 
252  // write the global and component-wise l2 norms of the residual
253  libMesh::out
254  << "|| R ||_2 = " << global_l2 << " : || R_i ||_2 = ( ";
255  for (unsigned int i=0; i<nd; i++) {
256  libMesh::out << l2_norms[i];
257  if (i < nd-1)
258  libMesh::out << " , ";
259  }
260  libMesh::out << " )" << std::endl;
261 
263  // resotre the subvectors
265  for (unsigned int i=0; i< nd; i++) {
266 
267  // get the IS for this system
268  IS sys_is = solver->index_sets()[i];
269 
270  // delete the NumericVector wrappers
271  delete sys_sols[i];
272  delete sys_res[i];
273 
274  // now restore the subvectors
275  ierr = VecRestoreSubVector(x, sys_is, &sol[i]); CHKERRABORT(solver->comm().get(), ierr);
276  ierr = VecRestoreSubVector(r, sys_is, &res[i]); CHKERRABORT(solver->comm().get(), ierr);
277  }
278 
279  return ierr;
280 }
281 
282 
283 
284 //---------------------------------------------------------------
285 // this function is called by PETSc to evaluate the Jacobian at X
286 PetscErrorCode
287 __mast_multiphysics_petsc_snes_jacobian(SNES snes, Vec x, Mat jac, Mat pc, void * ctx)
288 {
289  LOG_SCOPE("jacobian()", "PetscMultiphysicsNonlinearSolver");
290 
291  PetscErrorCode ierr=0;
292 
293  libmesh_assert(x);
294  libmesh_assert(jac);
295  libmesh_assert(pc);
296  libmesh_assert(ctx);
297 
298 
300  static_cast<MAST::MultiphysicsNonlinearSolverBase*> (ctx);
301 
302  const unsigned int
303  nd = solver->n_disciplines();
304 
305  std::vector<Vec>
306  sol(nd);
307 
308  std::vector<libMesh::NumericVector<Real>*>
309  sys_sols(nd, nullptr);
310 
311 
313  // get the subvectors for each discipline
315  for (unsigned int i=0; i< nd; i++) {
316 
317  // system for this discipline
319 
320  // get the IS for this system
321  IS sys_is = solver->index_sets()[i];
322 
323  // extract the subvector for this system
324  ierr = VecGetSubVector(x, sys_is, &sol[i]); CHKERRABORT(solver->comm().get(), ierr);
325 
326  sys_sols[i] = new libMesh::PetscVector<Real>(sol[i], sys.comm());
327 
328 
329  // Enforce constraints (if any) exactly on the
330  // current_local_solution. This is the solution vector that is
331  // actually used in the computation of the residual below, and is
332  // not locked by debug-enabled PETSc the way that "x" is.
333  sys.get_dof_map().enforce_constraints_exactly(sys, sys_sols[i]);
334  }
335 
336 
338  // initialize the data structures before calculation of residuals
340  if (solver->get_pre_residual_update_object())
342 
344  // calculate the residuals
346  for (unsigned int i=0; i< nd; i++) {
347 
348  // system for this discipline
350 
351  //PetscMatrix<Number> PC(pc, sys.comm());
352  //PC.attach_dof_map(sys.get_dof_map());
353  //PC.close();
354 
355  solver->get_system_assembly(i).residual_and_jacobian (*sys_sols[i],
356  nullptr,
357  sys.matrix,
358  sys);
359 
360  sys.matrix->close();
361  }
362 
364  // resotre the subvectors
366  for (unsigned int i=0; i< nd; i++) {
367 
368  // get the IS for this system
369  IS sys_is = solver->index_sets()[i];
370 
371  // delete the NumericVector wrappers
372  delete sys_sols[i];
373 
374  // now restore the subvectors
375  ierr = VecRestoreSubVector(x, sys_is, &sol[i]); CHKERRABORT(solver->comm().get(), ierr);
376  }
377 
378 
379  // call assembly of the global matrix
380  ierr = MatAssemblyBegin(jac, MAT_FINAL_ASSEMBLY); CHKERRABORT(solver->comm().get(), ierr);
381  ierr = MatAssemblyEnd(jac, MAT_FINAL_ASSEMBLY); CHKERRABORT(solver->comm().get(), ierr);
382 
383 
384  return ierr;
385 }
386 
387 
388 
389 
390 
392 MultiphysicsNonlinearSolverBase(const libMesh::Parallel::Communicator& comm_in,
393  const std::string& nm,
394  unsigned int n):
395 libMesh::ParallelObject (comm_in),
396 _name (nm),
397 _n_disciplines (n),
398 _update (nullptr),
399 _discipline_assembly (n, nullptr),
400 _is (_n_disciplines, PETSC_NULL),
401 _sub_mats (_n_disciplines*_n_disciplines, PETSC_NULL),
402 _n_dofs (0) {
403 
404 }
405 
406 
407 
408 
409 
411 
412 }
413 
414 
415 
416 
417 void
419 set_system_assembly(unsigned int i,
420  MAST::TransientAssembly& assembly) {
421 
422  // make sure that the index is within bounds
423  libmesh_assert_less(i, _n_disciplines);
424 
425  // also make sure that the specific discipline has not been set already
426  libmesh_assert(!_discipline_assembly[i]);
427 
428  _discipline_assembly[i] = &assembly;
429 }
430 
431 
432 
435 get_system_assembly(unsigned int i) {
436 
437  // make sure that the index is within bounds
438  libmesh_assert_less(i, _n_disciplines);
439 
440  // also make sure that the specific discipline has not been set already
441  libmesh_assert(_discipline_assembly[i]);
442 
443  return *_discipline_assembly[i];
444 }
445 
446 
447 
448 void
450 
451 
452  // make sure that all systems have been specified
453  bool p = true;
454  for (unsigned int i=0; i<_n_disciplines; i++)
455  p = ((_discipline_assembly[i] != nullptr) && p);
456  libmesh_assert(p);
457 
458 
460  // create the solver context. This will be partially initialized now
461  // since it is needed in the shell matrix context
463  PetscErrorCode ierr;
464  SNES snes;
465 
466  // setup the solver context
467  ierr = SNESCreate(this->comm().get(), &snes); CHKERRABORT(this->comm().get(), ierr);
468 
470  // create a petsc nested matrix of size NxN
472  const bool sys_name = libMesh::on_command_line("--solver_system_names");
473  std::string nm;
474  unsigned int n_local_dofs = 0;
475  std::vector<__mast_multiphysics_petsc_shell_context>
476  mat_ctx(_n_disciplines*_n_disciplines);
477 
478 
479  // all diagonal blocks use the system matrcices, while shell matrices
480  // are created for the off-diagonal terms.
481  _n_dofs = 0.;
482  for (unsigned int i=0; i<_n_disciplines; i++) {
483 
484  MAST::NonlinearSystem& sys = _discipline_assembly[i]->system();
485 
486  // add the number of dofs in this system to the global count
487  _n_dofs += sys.n_dofs();
488  n_local_dofs += sys.n_local_dofs();
489 
490  for (unsigned int j=0; j<_n_disciplines; j++) {
491 
492  if (i==j) {
493 
494  // the diagonal matrix
495  _sub_mats[i*_n_disciplines+j] =
496  dynamic_cast<libMesh::PetscMatrix<Real>*>(sys.matrix)->mat();
497  }
498  else {
499 
500  MAST::NonlinearSystem& sys_j = _discipline_assembly[j]->system();
501 
502  PetscInt
503  mat_i_m = sys.get_dof_map().n_dofs(),
504  mat_i_n_l = sys.get_dof_map().n_dofs_on_processor(sys.processor_id()),
505  mat_j_m = sys_j.get_dof_map().n_dofs(),
506  mat_j_n_l = sys_j.get_dof_map().n_dofs_on_processor(sys.processor_id());
507 
508  // the off-diagonal matrix
509  ierr = MatCreateShell(this->comm().get(),
510  mat_i_n_l,
511  mat_j_n_l,
512  mat_i_m,
513  mat_j_m,
514  PETSC_NULL,
515  &_sub_mats[i*_n_disciplines+j]);
516  CHKERRABORT(this->comm().get(), ierr);
517 
518  // initialize the context and tell the matrix about it
519  mat_ctx[i*_n_disciplines+j].i = i;
520  mat_ctx[i*_n_disciplines+j].j = j;
521  mat_ctx[i*_n_disciplines+j].snes = snes;
522  mat_ctx[i*_n_disciplines+j].solver = this;
523 
524  ierr = MatShellSetContext(_sub_mats[i*_n_disciplines+j],
525  &mat_ctx[i*_n_disciplines+j]);
526  CHKERRABORT(this->comm().get(), ierr);
527 
528  // set the mat-vec multiplication operation for this
529  // shell matrix
530  ierr = MatShellSetOperation(_sub_mats[i*_n_disciplines+j],
531  MATOP_MULT,
532  (void(*)(void))__mast_multiphysics_petsc_mat_mult);
533  CHKERRABORT(this->comm().get(), ierr);
534  }
535  }
536  }
537 
538 
539  ierr = MatCreateNest(this->comm().get(),
540  _n_disciplines, PETSC_NULL,
541  _n_disciplines, PETSC_NULL,
542  &_sub_mats[0],
543  &_mat);
544  CHKERRABORT(this->comm().get(), ierr);
545 
546  // we need to turn off MULT_TRANSPOSE operator for the global matrix
547  // since that is not implemented for the shell matrices pushes PETSc 3.7.4
548  // to produce an error about it.
549  ierr = MatShellSetOperation(_mat,
550  MATOP_MULT_TRANSPOSE,
551  PETSC_NULL);
552  CHKERRABORT(this->comm().get(), ierr);
553 
554  if (sys_name) {
555 
556  nm = this->name() + "_";
557  MatSetOptionsPrefix(_mat, nm.c_str());
558  }
559  ierr = MatSetFromOptions(_mat);
560  CHKERRABORT(this->comm().get(), ierr);
561 
562  // get the IS belonging to each block
563  ierr = MatNestGetISs(_mat, &_is[0], PETSC_NULL);
564  CHKERRABORT(this->comm().get(), ierr);
565 
567  // setup the vector for solution
569  ierr = VecCreate(this->comm().get(), &_sol); CHKERRABORT(this->comm().get(), ierr);
570  ierr = VecSetSizes(_sol, n_local_dofs, _n_dofs); CHKERRABORT(this->comm().get(), ierr);
571  ierr = VecSetType(_sol, VECMPI); CHKERRABORT(this->comm().get(), ierr);
572  ierr = VecDuplicate(_sol, &_res); CHKERRABORT(this->comm().get(), ierr);
573 
574  ierr = MatShellSetOperation(_mat,
575  MATOP_MULT_TRANSPOSE_ADD,
576  PETSC_NULL);
577  CHKERRABORT(this->comm().get(), ierr);
578  ierr = MatShellSetOperation(_mat,
579  MATOP_TRANSPOSE,
580  PETSC_NULL);
581  CHKERRABORT(this->comm().get(), ierr);
582 
584  // now initialize the vector from the system solutions, which will serve
585  // as the initial solution for the coupled system
587  for (unsigned int i=0; i<_n_disciplines; i++) {
588 
589  // solution from the provided system
590  libMesh::NumericVector<Real>
591  &sys_sol = *_discipline_assembly[i]->system().solution;
592 
593  // limiting indices for the system, and multiphysics assembly. Should
594  // be the same
595  int
596  multiphysics_first = 0,
597  multiphysics_last = 0,
598  first = sys_sol.first_local_index(),
599  last = sys_sol.last_local_index();
600 
601  // get the subvector corresponding to the the discipline
602  Vec sub_vec;
603 
604  ierr = VecGetSubVector(_sol, _is[i], &sub_vec); CHKERRABORT(this->comm().get(), ierr);
605  ierr = VecGetOwnershipRange(sub_vec,
606  &multiphysics_first,
607  &multiphysics_last); CHKERRABORT(this->comm().get(), ierr);
608 
609  // the first and last indices must match between the two representations
610  libmesh_assert_equal_to(multiphysics_first, first);
611  libmesh_assert_equal_to( multiphysics_last, last);
612 
613  std::unique_ptr<libMesh::NumericVector<Real> >
614  multiphysics_sol(new libMesh::PetscVector<Real>(sub_vec, this->comm()));
615 
616  for (unsigned int i=first; i<last; i++)
617  multiphysics_sol->set(i, sys_sol(i));
618 
619  multiphysics_sol->close();
620 
621  ierr = VecRestoreSubVector(_sol, _is[i], &sub_vec); CHKERRABORT(this->comm().get(), ierr);
622  }
623 
624 
625 
626 
628  // initialize the solver context
630  KSP ksp;
631  PC pc;
632 
633 
634 
635  // tell the solver where to store the Jacobian and how to calculate it
636  ierr = SNESSetFunction (snes,
637  _res,
639  this);
640  ierr = SNESSetJacobian(snes,
641  _mat,
642  _mat,
644  this);
645 
646 
647  if (sys_name) {
648 
649  nm = this->name() + "_";
650  SNESSetOptionsPrefix(snes, nm.c_str());
651  }
652 
653 
654 
655  // setup the ksp
656  ierr = SNESGetKSP (snes, &ksp); CHKERRABORT(this->comm().get(), ierr);
657  ierr = SNESSetFromOptions(snes); CHKERRABORT(this->comm().get(), ierr);
658 
659 
660 
661  // setup the pc
662  ierr = KSPGetPC(ksp, &pc); CHKERRABORT(this->comm().get(), ierr);
663 
664  for (unsigned int i=0; i<_n_disciplines; i++) {
665 
666  if (sys_name) {
667 
668  nm = _discipline_assembly[i]->system().name();
669  ierr = PCFieldSplitSetIS(pc, nm.c_str(), _is[i]); CHKERRABORT(this->comm().get(), ierr);
670  }
671  else
672  ierr = PCFieldSplitSetIS(pc, nullptr, _is[i]);CHKERRABORT(this->comm().get(), ierr);
673  }
674 
675 
676  //ierr = SNESSetSolution(snes, _sol);
677  //this->verify_gateaux_derivatives(snes);
678 
680  // now, solve
682  START_LOG("SNESSolve", this->name()+"_MultiphysicsSolve");
683 
684  // now solve
685  ierr = SNESSolve(snes, PETSC_NULL, _sol);
686 
687  STOP_LOG("SNESSolve", this->name()+"_MultiphysicsSolve");
688 
689 
691  // now copy the solution back to the system solution vector
693  for (unsigned int i=0; i<_n_disciplines; i++) {
694 
695  // solution from the provided system
696  libMesh::NumericVector<Real>
697  &sys_sol = *_discipline_assembly[i]->system().solution;
698 
699  // limiting indices for the system, and multiphysics assembly. Should
700  // be the same
701  int
702  multiphysics_first = 0,
703  multiphysics_last = 0,
704  first = sys_sol.first_local_index(),
705  last = sys_sol.last_local_index();
706 
707  // get the subvector corresponding to the the discipline
708  Vec sub_vec;
709 
710  ierr = VecGetSubVector(_sol, _is[i], &sub_vec); CHKERRABORT(this->comm().get(), ierr);
711  ierr = VecGetOwnershipRange(sub_vec,
712  &multiphysics_first,
713  &multiphysics_last); CHKERRABORT(this->comm().get(), ierr);
714 
715  // the first and last indices must match between the two representations
716  libmesh_assert_equal_to(multiphysics_first, first);
717  libmesh_assert_equal_to( multiphysics_last, last);
718 
719  std::unique_ptr<libMesh::NumericVector<Real> >
720  multiphysics_sol(new libMesh::PetscVector<Real>(sub_vec, this->comm()));
721 
722  for (unsigned int i=first; i<last; i++)
723  sys_sol.set(i, (*multiphysics_sol)(i));
724 
725  ierr = VecRestoreSubVector(_sol, _is[i], &sub_vec); CHKERRABORT(this->comm().get(), ierr);
726  }
727 
728 
729 
730  // destroy the Petsc contexts
731  ierr = SNESDestroy(&snes); CHKERRABORT(this->comm().get(), ierr);
732  for (unsigned int i=0; i<_n_disciplines; i++)
733  for (unsigned int j=0; j<_n_disciplines; j++)
734  if (i != j) {
735  ierr = MatDestroy(&_sub_mats[i*_n_disciplines+j]);
736  CHKERRABORT(this->comm().get(), ierr);
737  }
738 
739  ierr = MatDestroy(&_mat); CHKERRABORT(this->comm().get(), ierr);
740  ierr = VecDestroy(&_sol); CHKERRABORT(this->comm().get(), ierr);
741  ierr = VecDestroy(&_res); CHKERRABORT(this->comm().get(), ierr);
742 
743 }
744 
745 
746 
747 
748 void
750 
751  PetscErrorCode ierr=0;
752 
753  // create vectors for the sol, dsol, and res of the global and
754  // disciplinary systems
755  std::unique_ptr<libMesh::NumericVector<Real> >
756  global_sol (new libMesh::PetscVector<Real>(_sol, this->comm())),
757  global_res (new libMesh::PetscVector<Real>(_res, this->comm())),
758  global_res0 (global_sol->zero_clone().release());
759 
760  // first calculate the baseline residual for the global solution vector
762  _sol,
763  dynamic_cast<libMesh::PetscVector<Real>*>(global_res0.get())->vec(),
764  this);
765 
766  // value of perturbation used
767  const Real
768  delta = 1.0e-4;
769 
770  // now, iterate over each dof on the disciplines, and calculate the
771  // Gateaux derivative
772  for (unsigned int i=0; i<_n_disciplines; i++) {
773 
774  // system for this discipline
776 
777  // get the IS for this system
778  IS sys_is_i = this->index_sets()[i];
779 
780  // verify the derivative wrt dofs from other disciplines
781  for (unsigned int j=0; j<_n_disciplines; j++) {
782 
783  if (i != j) {
784 
785  IS sys_is_j = this->index_sets()[j];
786 
787  // system for this discipline
789 
790  std::unique_ptr<libMesh::NumericVector<Real> >
791  dsol_j (sys_j.solution->zero_clone().release()),
792  dJac_ij_dXj(sys_i.solution->zero_clone().release());
793 
794  for (unsigned int k=0; k<sys_j.n_dofs(); k++) {
795 
796  dsol_j->zero();
797  dsol_j->add(k, delta);
798  dsol_j->close();
799 
800  Vec
801  vecx = dynamic_cast<libMesh::PetscVector<Real>*>(dsol_j.get())->vec(),
802  vecy = dynamic_cast<libMesh::PetscVector<Real>*>(dJac_ij_dXj.get())->vec();
803 
804  __mast_multiphysics_petsc_mat_mult(_sub_mats[i*_n_disciplines+j], vecx, vecy);
805 
806  // now perform the same calculation with the finite differencing
807  // using residual calculation
808  // extract the subvector for this system
809  Vec
810  res_i,
811  sol_j;
812  ierr = VecGetSubVector(_sol, sys_is_j, &sol_j); CHKERRABORT(this->comm().get(), ierr);
813 
814  Real
815  old_val = 0.;
816 
817  // create a vector for modification of the ith value of this solution
818  {
819  libMesh::PetscVector<Real> v(sol_j, this->comm());
820 
821  // perturb the solution vector
822  old_val = v.el(k);
823  v.add(k, delta);
824  v.close();
825  }
826 
827  ierr = VecRestoreSubVector( _sol, sys_is_j, &sol_j); CHKERRABORT(this->comm().get(), ierr);
828 
829  // now calculate the residual
831  _sol,
832  _res,
833  this);
834 
835  // reset the global solution vector
836  ierr = VecGetSubVector(_sol, sys_is_j, &sol_j); CHKERRABORT(this->comm().get(), ierr);
837 
838  // create a vector for modification of the ith value of this solution
839  {
840  libMesh::PetscVector<Real> v(sol_j, this->comm());
841 
842  // perturb the solution vector
843  v.set(k, old_val);
844  v.close();
845  }
846 
847  ierr = VecRestoreSubVector( _sol, sys_is_j, &sol_j); CHKERRABORT(this->comm().get(), ierr);
848 
849  global_res->close();
850  global_res->add(-1., *global_res0);
851  global_res->close();
852 
853 
854  // get the finite differenced residual
855  ierr = VecGetSubVector(_res, sys_is_i, &res_i); CHKERRABORT(this->comm().get(), ierr);
856 
857  // create a vector for comparison
858  {
859  libMesh::PetscVector<Real> v(res_i, this->comm());
860  for (unsigned int l=0; l<sys_i.n_dofs(); l++) {
861 
862  if (dJac_ij_dXj->el(l) != v.el(l))
863  libMesh::out
864  << k << " "
865  << l << " "
866  << dJac_ij_dXj->el(l) << " "
867  << v.el(l) << std::endl;
868  }
869 
870  libMesh::out << std::endl;
871  }
872 
873  ierr = VecRestoreSubVector( _res, sys_is_i, &res_i); CHKERRABORT(this->comm().get(), ierr);
874 
875  }
876  }
877  }
878  }
879 }
880 
881 
882 
MultiphysicsNonlinearSolverBase(const libMesh::Parallel::Communicator &comm_in, const std::string &nm, unsigned int n)
default constructor
MAST::MultiphysicsNonlinearSolverBase * solver
This class implements a system for solution of nonlinear systems.
PetscErrorCode __mast_multiphysics_petsc_snes_residual(SNES snes, Vec x, Vec r, void *ctx)
libMesh::Real Real
PetscErrorCode __mast_multiphysics_petsc_snes_jacobian(SNES snes, Vec x, Mat jac, Mat pc, void *ctx)
virtual void linearized_jacobian_solution_product(const libMesh::NumericVector< Real > &X, const libMesh::NumericVector< Real > &dX, libMesh::NumericVector< Real > &JdX, libMesh::NonlinearImplicitSystem &S)
calculates the product of the Jacobian and a perturbation in solution vector .
std::vector< MAST::TransientAssembly * > _discipline_assembly
vector of assembly objects for each discipline in this multiphysics system
const unsigned int _n_disciplines
number of disciplines
PetscErrorCode __mast_multiphysics_petsc_mat_mult(Mat mat, Vec dx, Vec y)
MAST::TransientAssembly & get_system_assembly(unsigned int i)
MAST::MultiphysicsNonlinearSolverBase::PreResidualUpdate * get_pre_residual_update_object()
returns a pointer to the update object
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 ...
virtual void update_at_perturbed_solution(std::vector< libMesh::NumericVector< Real > * > &sol_vecs, std::vector< libMesh::NumericVector< Real > * > &dsol_vecs)=0
sol_vecs is the vector containing the solution for each discipline in this multiphysics solution...
void solve()
solves the system using the nested matrices that uses the discipline specific solver options ...
const MAST::NonlinearSystem & system() const
virtual void update_at_solution(std::vector< libMesh::NumericVector< Real > * > &sol_vecs)=0
sol_vecs is the vector containing the solution for each discipline in this multiphysics solution ...
void set_system_assembly(unsigned int i, MAST::TransientAssembly &assembly)
method to set the n^th discipline of this multiphysics system assembly.