MAST
Multidisciplinary-design Adaptation and Sensitivity Toolkit (MAST)
complex_solver_base.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 
21 // MAST includes
24 #include "base/nonlinear_system.h"
25 
26 
27 // libMesh includes
28 #include "libmesh/libmesh_logging.h"
29 #include "libmesh/numeric_vector.h"
30 #include "libmesh/system.h"
31 #include "libmesh/petsc_matrix.h"
32 #include "libmesh/petsc_vector.h"
33 #include "libmesh/libmesh_common.h"
34 #include "libmesh/dof_map.h"
35 
36 // PETSc includes
37 #include <petscmat.h>
38 
39 
41 tol (1.0e-3),
42 max_iters (20),
43 _assembly (nullptr) {
44 
45 }
46 
47 
48 
50 
51 }
52 
53 
54 
55 void
57 
58  libmesh_assert(!_assembly);
59 
60  _assembly = &assemble;
61 }
62 
63 
64 
65 void
67 
69 
70  // remove the real part of the vector
71  std::string nm;
72  nm = sys.name();
73  nm += "real_sol";
74 
75  if (sys.have_vector(nm))
76  sys.remove_vector(nm);
77 
78  // and its sensitivity
79  nm += "_sens";
80 
81  if (sys.have_vector(nm))
82  sys.remove_vector(nm);
83 
84  // remove the imaginary part of the vector
85  nm = sys.name();
86  nm += "imag_sol";
87 
88  if (sys.have_vector(nm))
89  sys.remove_vector(nm);
90 
91  // and its sensitivity
92  nm += "_sens";
93 
94  if (sys.have_vector(nm))
95  sys.remove_vector(nm);
96 
97 
98  _assembly = nullptr;
99 }
100 
101 
102 
103 libMesh::NumericVector<Real>&
105 
106  libmesh_assert(_assembly);
107 
109 
110  std::string nm;
111  nm = sys.name();
112  nm += "real_sol";
113  if (if_sens)
114  nm += "_sens";
115 
116  if (!sys.have_vector(nm))
117  sys.add_vector(nm);
118 
119  return sys.get_vector(nm);
120 }
121 
122 
123 
124 const libMesh::NumericVector<Real>&
126 
127  libmesh_assert(_assembly);
128 
130 
131  std::string nm;
132  nm = sys.name();
133  nm += "real_sol";
134  if (if_sens)
135  nm += "_sens";
136 
137  if (!sys.have_vector(nm))
138  sys.add_vector(nm);
139 
140  return sys.get_vector(nm);
141 }
142 
143 
144 libMesh::NumericVector<Real>&
146 
147  libmesh_assert(_assembly);
148 
150 
151  std::string nm;
152  nm = sys.name();
153  nm += "imag_sol";
154  if (if_sens)
155  nm += "_sens";
156 
157  if (!sys.have_vector(nm))
158  sys.add_vector(nm);
159 
160  return sys.get_vector(nm);
161 }
162 
163 
164 const libMesh::NumericVector<Real>&
166 
167  libmesh_assert(_assembly);
168 
170 
171  std::string nm;
172  nm = sys.name();
173  nm += "imag_sol";
174  if (if_sens)
175  nm += "_sens";
176 
177  if (!sys.have_vector(nm))
178  sys.add_vector(nm);
179 
180  return sys.get_vector(nm);
181 }
182 
183 
184 
185 
186 
187 void
189 
190  libmesh_assert(_assembly);
191 
192  START_LOG("complex_solve()", "PetscFieldSplitSolver");
193 
194  // get reference to the system
195  MAST::NonlinearSystem& sys =
196  dynamic_cast<MAST::NonlinearSystem&>(_assembly->system());
197 
198  // create a petsc nested matrix of size 2x2
199  PetscErrorCode ierr;
200  Mat mat;
201  Vec res, sol, res_vec_R, res_vec_I, sol_vec_R, sol_vec_I;
202  std::vector<IS> is(2);
203  std::vector<Mat> sub_mats(4);
204  sub_mats[0] = dynamic_cast<libMesh::PetscMatrix<Real>*>(sys.matrix)->mat(); // real
205  sub_mats[1] = dynamic_cast<libMesh::PetscMatrix<Real>*>(sys.matrix_A)->mat(); // -imag
206  sub_mats[2] = dynamic_cast<libMesh::PetscMatrix<Real>*>(sys.matrix_B)->mat(); // imag
207  sub_mats[3] = dynamic_cast<libMesh::PetscMatrix<Real>*>(sys.matrix)->mat(); // real
208 
209  ierr = MatCreateNest(_assembly->system().comm().get(),
210  2, nullptr,
211  2, nullptr,
212  &sub_mats[0],
213  &mat);
214  CHKERRABORT(sys.comm().get(), ierr);
215 
216 
217  // get the IS belonging to each block
218  ierr = MatNestGetISs(mat, &is[0], nullptr); CHKERRABORT(sys.comm().get(), ierr);
219 
220 
221 
222  // setup vector for residual
223  ierr = VecCreate(sys.comm().get(), &res); CHKERRABORT(sys.comm().get(), ierr);
224  ierr = VecSetSizes(res, PETSC_DECIDE, 2*sys.solution->size()); CHKERRABORT(sys.comm().get(), ierr);
225  ierr = VecSetType(res, VECMPI); CHKERRABORT(sys.comm().get(), ierr);
226 
227 
228  // setup vector for solution
229  ierr = VecDuplicate(res, &sol); CHKERRABORT(sys.comm().get(), ierr);
230 
231 
232 
233  // get the residual subvectors
234  ierr = VecGetSubVector(res, is[0], &res_vec_R); CHKERRABORT(sys.comm().get(), ierr);
235  ierr = VecGetSubVector(res, is[1], &res_vec_I); CHKERRABORT(sys.comm().get(), ierr);
236 
237  // get the solution subvectors
238  ierr = VecGetSubVector(sol, is[0], &sol_vec_R); CHKERRABORT(sys.comm().get(), ierr);
239  ierr = VecGetSubVector(sol, is[1], &sol_vec_I); CHKERRABORT(sys.comm().get(), ierr);
240 
241 
242  // clone vectors for use as system RHS
243  std::unique_ptr<libMesh::NumericVector<Real> >
244  res_R(new libMesh::PetscVector<Real>(res_vec_R, sys.comm())),
245  res_I(new libMesh::PetscVector<Real>(res_vec_I, sys.comm())),
246  sol_R(new libMesh::PetscVector<Real>(sol_vec_R, sys.comm())),
247  sol_I(new libMesh::PetscVector<Real>(sol_vec_I, sys.comm()));
248 
249  sol_R->zero();
250  sol_R->close();
251  sol_I->zero();
252  sol_I->close();
253 
254  // assemble the matrices
256  *sol_I,
257  *res_R,
258  *res_I,
259  *sys.matrix, // J_R
260  *sys.matrix_B); // J_I
261 
262  // restore the subvectors
263  //ierr = VecRestoreSubVector(res, is[0], &res_vec_R); CHKERRABORT(sys.comm().get(), ierr);
264  //ierr = VecRestoreSubVector(res, is[1], &res_vec_I); CHKERRABORT(sys.comm().get(), ierr);
265 
266  // copy the imag Jacobian for the first row of the sys of eq.
267  sys.matrix_A->zero();
268  sys.matrix_A->close();
269  sys.matrix_A->add(-1., *sys.matrix_B);
270  sys.matrix_A->close();
271 
272  ierr = MatAssemblyBegin(mat, MAT_FINAL_ASSEMBLY); CHKERRABORT(sys.comm().get(), ierr);
273  ierr = MatAssemblyEnd(mat, MAT_FINAL_ASSEMBLY); CHKERRABORT(sys.comm().get(), ierr);
274  ierr = VecAssemblyBegin(res); CHKERRABORT(sys.comm().get(), ierr);
275  ierr = VecAssemblyEnd(res); CHKERRABORT(sys.comm().get(), ierr);
276 
277 
278 
279  // now initialize the KSP and ask for solution.
280  KSP ksp;
281  PC pc;
282 
283  // setup the KSP
284  ierr = KSPCreate(sys.comm().get(), &ksp); CHKERRABORT(sys.comm().get(), ierr);
285  ierr = KSPSetOperators(ksp, mat, mat); CHKERRABORT(sys.comm().get(), ierr);
286  ierr = KSPSetFromOptions(ksp); CHKERRABORT(sys.comm().get(), ierr);
287 
288  // setup the PC
289  ierr = KSPGetPC(ksp, &pc); CHKERRABORT(sys.comm().get(), ierr);
290  ierr = PCFieldSplitSetIS(pc, nullptr, is[0]);CHKERRABORT(sys.comm().get(), ierr);
291  ierr = PCFieldSplitSetIS(pc, nullptr, is[1]);CHKERRABORT(sys.comm().get(), ierr);
292  ierr = PCSetFromOptions(pc); CHKERRABORT(sys.comm().get(), ierr);
293 
294 
295 
296  // now solve
297  ierr = KSPSolve(ksp, res, sol);
298 
299 
300  // assemble the matrices
302  *sol_I,
303  *res_R,
304  *res_I,
305  *sys.matrix, // J_R
306  *sys.matrix_B); // J_I
307 
308  // copy solutions for output
309  /*this->real_solution() = *sol_R;
310  this->imag_solution() = *sol_I;
311  this->real_solution().close();
312  this->imag_solution().close();*/
313 
314 
315  // restore the subvectors
316  ierr = VecRestoreSubVector(res, is[0], &res_vec_R); CHKERRABORT(sys.comm().get(), ierr);
317  ierr = VecRestoreSubVector(res, is[1], &res_vec_I); CHKERRABORT(sys.comm().get(), ierr);
318  ierr = VecRestoreSubVector(sol, is[0], &sol_vec_R); CHKERRABORT(sys.comm().get(), ierr);
319  ierr = VecRestoreSubVector(sol, is[1], &sol_vec_I); CHKERRABORT(sys.comm().get(), ierr);
320 
321 
322  // destroy the objects
323  ierr = KSPDestroy(&ksp);
324  ierr = MatDestroy(&mat);
325  ierr = VecDestroy(&res);
326  ierr = VecDestroy(&sol);
327 
328  STOP_LOG("complex_solve()", "PetscFieldSplitSolver");
329 }
330 
331 
332 
333 void
335 
336  libmesh_assert(_assembly);
337 
338  START_LOG("solve_block_matrix()", "ComplexSolve");
339 
340  // get reference to the system
341  MAST::NonlinearSystem& sys =
342  dynamic_cast<MAST::NonlinearSystem&>(_assembly->system());
343 
344  libMesh::DofMap& dof_map = sys.get_dof_map();
345 
346  const PetscInt
347  my_m = dof_map.n_dofs(),
348  my_n = my_m,
349  n_l = dof_map.n_dofs_on_processor(sys.processor_id()),
350  m_l = n_l;
351 
352  const std::vector<libMesh::dof_id_type>
353  & n_nz = dof_map.get_n_nz(),
354  & n_oz = dof_map.get_n_oz();
355 
356  std::vector<libMesh::dof_id_type>
357  complex_n_nz (2*n_nz.size()),
358  complex_n_oz (2*n_oz.size());
359 
360  // create the n_nz and n_oz for the complex matrix without block format
361  for (unsigned int i=0; i<n_nz.size(); i++) {
362 
363  complex_n_nz[2*i] = 2*n_nz[i];
364  complex_n_nz[2*i+1] = 2*n_nz[i];
365  }
366 
367  for (unsigned int i=0; i<n_oz.size(); i++) {
368 
369  complex_n_oz[2*i] = 2*n_oz[i];
370  complex_n_oz[2*i+1] = 2*n_oz[i];
371  }
372 
373 
374 
375  // create the matrix
376  PetscErrorCode ierr;
377  Mat mat;
378 
379  ierr = MatCreate(sys.comm().get(), &mat); CHKERRABORT(sys.comm().get(), ierr);
380  ierr = MatSetSizes(mat, 2*m_l, 2*n_l, 2*my_m, 2*my_n); CHKERRABORT(sys.comm().get(), ierr);
381 
382  if (libMesh::on_command_line("--solver_system_names")) {
383 
384  std::string nm = _assembly->system().name() + "_complex_";
385  MatSetOptionsPrefix(mat, nm.c_str());
386  }
387  ierr = MatSetFromOptions(mat); CHKERRABORT(sys.comm().get(), ierr);
388 
389  //ierr = MatSetType(mat, MATBAIJ); CHKERRABORT(sys.comm().get(), ierr);
390  ierr = MatSetBlockSize(mat, 2); CHKERRABORT(sys.comm().get(), ierr);
391  ierr = MatSeqAIJSetPreallocation(mat,
392  2*my_m,
393  (PetscInt*)&complex_n_nz[0]); CHKERRABORT(sys.comm().get(), ierr);
394  ierr = MatMPIAIJSetPreallocation(mat,
395  0,
396  (PetscInt*)&complex_n_nz[0],
397  0,
398  (PetscInt*)&complex_n_oz[0]); CHKERRABORT(sys.comm().get(), ierr);
399  ierr = MatSeqBAIJSetPreallocation (mat, 2,
400  0, (PetscInt*)&n_nz[0]); CHKERRABORT(sys.comm().get(), ierr);
401  ierr = MatMPIBAIJSetPreallocation (mat, 2,
402  0, (PetscInt*)&n_nz[0],
403  0, (PetscInt*)&n_oz[0]); CHKERRABORT(sys.comm().get(), ierr);
404  ierr = MatSetOption(mat,
405  MAT_NEW_NONZERO_ALLOCATION_ERR,
406  PETSC_TRUE); CHKERRABORT(sys.comm().get(), ierr);
407 
408 
409  // now create the vectors
410  Vec res_vec, sol_vec;
411 
412  ierr = MatCreateVecs(mat, &res_vec, PETSC_NULL); CHKERRABORT(sys.comm().get(), ierr);
413  ierr = MatCreateVecs(mat, &sol_vec, PETSC_NULL); CHKERRABORT(sys.comm().get(), ierr);
414 
415 
416  std::unique_ptr<libMesh::SparseMatrix<Real> >
417  jac_mat(new libMesh::PetscMatrix<Real>(mat, sys.comm()));
418 
419  std::unique_ptr<libMesh::NumericVector<Real> >
420  res(new libMesh::PetscVector<Real>(res_vec, sys.comm())),
421  sol(new libMesh::PetscVector<Real>(sol_vec, sys.comm()));
422 
423 
424  // if sensitivity analysis is requested, then set the complex solution in
425  // the solution vector
426  if (p) {
427 
428  // copy the solution to separate real and imaginary vectors
429  libMesh::NumericVector<Real>
430  &sol_R = this->real_solution(),
431  &sol_I = this->imag_solution();
432 
433  unsigned int
434  first = sol_R.first_local_index(),
435  last = sol_I.last_local_index();
436 
437  for (unsigned int i=first; i<last; i++) {
438 
439  sol->set( 2*i, sol_R(i));
440  sol->set(2*i+1, sol_I(i));
441  }
442 
443  sol->close();
444  }
445 
446 
447  // assemble the matrix
449  *res,
450  *jac_mat,
451  p);
452  res->scale(-1.);
453 
454  // now initialize the KSP and ask for solution.
455  KSP ksp;
456  PC pc;
457 
458  // setup the KSP
459  ierr = KSPCreate(sys.comm().get(), &ksp); CHKERRABORT(sys.comm().get(), ierr);
460 
461  if (libMesh::on_command_line("--solver_system_names")) {
462 
463  std::string nm = _assembly->system().name() + "_complex_";
464  KSPSetOptionsPrefix(ksp, nm.c_str());
465  }
466 
467  ierr = KSPSetOperators(ksp, mat, mat); CHKERRABORT(sys.comm().get(), ierr);
468  ierr = KSPSetFromOptions(ksp); CHKERRABORT(sys.comm().get(), ierr);
469 
470  // setup the PC
471  ierr = KSPGetPC(ksp, &pc); CHKERRABORT(sys.comm().get(), ierr);
472  ierr = PCSetFromOptions(pc); CHKERRABORT(sys.comm().get(), ierr);
473 
474 
475  START_LOG("KSPSolve", "ComplexSolve");
476 
477  // now solve
478  ierr = KSPSolve(ksp, res_vec, sol_vec);
479 
480  STOP_LOG("KSPSolve", "ComplexSolve");
481 
482 
483  // copy the solution to separate real and imaginary vectors
484  libMesh::NumericVector<Real>
485  &sol_R = this->real_solution(p != nullptr),
486  &sol_I = this->imag_solution(p != nullptr);
487 
488  unsigned int
489  first = sol_R.first_local_index(),
490  last = sol_R.last_local_index();
491 
492  for (unsigned int i=first; i<last; i++) {
493  sol_R.set(i, (*sol)( 2*i));
494  sol_I.set(i, (*sol)(2*i+1));
495  }
496 
497  sol_R.close();
498  sol_I.close();
499  sol->close();
500 
501  ierr = KSPDestroy(&ksp); CHKERRABORT(sys.comm().get(), ierr);
502  ierr = MatDestroy(&mat); CHKERRABORT(sys.comm().get(), ierr);
503  ierr = VecDestroy(&res_vec); CHKERRABORT(sys.comm().get(), ierr);
504  ierr = VecDestroy(&sol_vec); CHKERRABORT(sys.comm().get(), ierr);
505 
506  STOP_LOG("solve_block_matrix()", "ComplexSolve");
507 }
508 
509 
510 
511 
const MAST::NonlinearSystem & system() const
void residual_and_jacobian_blocked(const libMesh::NumericVector< Real > &X, libMesh::NumericVector< Real > &R, libMesh::SparseMatrix< Real > &J, MAST::Parameter *p=nullptr)
Assembles the residual and Jacobian of the N complex system of equations split into 2N real system of...
This class implements a system for solution of nonlinear systems.
void residual_and_jacobian_field_split(const libMesh::NumericVector< Real > &X_R, const libMesh::NumericVector< Real > &X_I, libMesh::NumericVector< Real > &R_R, libMesh::NumericVector< Real > &R_I, libMesh::SparseMatrix< Real > &J_R, libMesh::SparseMatrix< Real > &J_I)
void set_assembly(MAST::ComplexAssemblyBase &assemble)
sets the assembly object for this solver
This is a scalar function whose value can be changed and one that can be used as a design variable in...
Definition: parameter.h:35
libMesh::NumericVector< Real > & imag_solution(bool if_sens=false)
void clear_assembly()
clears the assembly object from this solver
virtual void solve_pc_fieldsplit()
solves the complex system of equations using PCFieldSplit
libMesh::SparseMatrix< Real > * matrix_A
The system matrix for standard eigenvalue problems.
virtual void solve_block_matrix(MAST::Parameter *p=nullptr)
solves the complex system of equations using block matrices.
libMesh::SparseMatrix< Real > * matrix_B
A second system matrix for generalized eigenvalue problems.
virtual ~ComplexSolverBase()
destructor
ComplexSolverBase()
default constructor
libMesh::NumericVector< Real > & real_solution(bool if_sens=false)
MAST::ComplexAssemblyBase * _assembly
Associated ComplexAssembly object that provides the element level quantities.