Reference documentation for deal.II version 8.4.2
parpack_solver.h
1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 2010 - 2016 by the deal.II authors
4 //
5 // This file is part of the deal.II library.
6 //
7 // The deal.II library is free software; you can use it, redistribute
8 // it, and/or modify it under the terms of the GNU Lesser General
9 // Public License as published by the Free Software Foundation; either
10 // version 2.1 of the License, or (at your option) any later version.
11 // The full text of the license can be found in the file LICENSE at
12 // the top level of the deal.II distribution.
13 //
14 // ---------------------------------------------------------------------
15 
16 #ifndef dealii__parpack_solver_h
17 #define dealii__parpack_solver_h
18 
19 #include <deal.II/base/config.h>
20 #include <deal.II/base/smartpointer.h>
21 #include <deal.II/base/memory_consumption.h>
22 #include <deal.II/lac/solver_control.h>
23 #include <deal.II/lac/vector.h>
24 #include <deal.II/base/index_set.h>
25 
26 #include <cstring>
27 
28 
29 #ifdef DEAL_II_ARPACK_WITH_PARPACK
30 
31 DEAL_II_NAMESPACE_OPEN
32 
33 extern "C" {
34 
35  // http://www.mathkeisan.com/usersguide/man/pdnaupd.html
36  void pdnaupd_(MPI_Fint *comm, int *ido, char *bmat, int *n, char *which,
37  int *nev, double *tol, double *resid, int *ncv,
38  double *v, int *nloc, int *iparam, int *ipntr,
39  double *workd, double *workl, int *lworkl,
40  int *info);
41 
42  // http://www.mathkeisan.com/usersguide/man/pdsaupd.html
43  void pdsaupd_(MPI_Fint *comm, int *ido, char *bmat, int *n, char *which,
44  int *nev, double *tol, double *resid, int *ncv,
45  double *v, int *nloc, int *iparam, int *ipntr,
46  double *workd, double *workl, int *lworkl,
47  int *info);
48 
49  // http://www.mathkeisan.com/usersguide/man/pdneupd.html
50  void pdneupd_(MPI_Fint *comm, int *rvec, char *howmany, int *select, double *d,
51  double *di, double *z, int *ldz, double *sigmar,
52  double *sigmai, double *workev, char *bmat, int *n, char *which,
53  int *nev, double *tol, double *resid, int *ncv,
54  double *v, int *nloc, int *iparam, int *ipntr,
55  double *workd, double *workl, int *lworkl, int *info);
56 
57  // http://www.mathkeisan.com/usersguide/man/pdseupd.html
58  void pdseupd_(MPI_Fint *comm, int *rvec, char *howmany, int *select, double *d,
59  double *z, int *ldz, double *sigmar,
60  char *bmat, int *n, char *which,
61  int *nev, double *tol, double *resid, int *ncv,
62  double *v, int *nloc, int *iparam, int *ipntr,
63  double *workd, double *workl, int *lworkl, int *info);
64 
65  // other resources:
66  // http://acts.nersc.gov/superlu/example5/pnslac.c.html
67  // https://github.com/phpisciuneri/tijo/blob/master/dvr_parpack.cpp
68 
69 }
70 
129 template <typename VectorType>
130 class PArpackSolver : public Subscriptor
131 {
132 public:
137 
145  enum WhichEigenvalues
146  {
147  algebraically_largest,
148  algebraically_smallest,
149  largest_magnitude,
150  smallest_magnitude,
151  largest_real_part,
152  smallest_real_part,
153  largest_imaginary_part,
154  smallest_imaginary_part,
155  both_ends
156  };
157 
161  template <typename MatrixType>
162  class Shift : public ::Subscriptor
163  {
164  public:
165 
169  Shift (const MatrixType &A,
170  const MatrixType &B,
171  const double sigma)
172  :
173  A(A),
174  B(B),
175  sigma(sigma)
176  {}
177 
181  void vmult (VectorType &dst, const VectorType &src) const
182  {
183  B.vmult(dst,src);
184  dst *= (-sigma);
185  A.vmult_add(dst,src);
186  }
187 
191  void Tvmult (VectorType &dst, const VectorType &src) const
192  {
193  B.Tvmult(dst,src);
194  dst *= (-sigma);
195  A.Tvmult_add(dst,src);
196  }
197 
198  private:
199  const MatrixType &A;
200  const MatrixType &B;
201  const double sigma;
202  };
203 
208  struct AdditionalData
209  {
210  const unsigned int number_of_arnoldi_vectors;
211  const WhichEigenvalues eigenvalue_of_interest;
212  const bool symmetric;
213  AdditionalData(
214  const unsigned int number_of_arnoldi_vectors = 15,
215  const WhichEigenvalues eigenvalue_of_interest = largest_magnitude,
216  const bool symmetric = false);
217  };
218 
222  SolverControl &control () const;
223 
227  PArpackSolver(SolverControl &control,
228  const MPI_Comm &mpi_communicator,
229  const AdditionalData &data = AdditionalData());
230 
234  void reinit(const ::IndexSet &locally_owned_dofs );
235 
239  void set_shift(const double s );
240 
246  template <typename MatrixType1,
247  typename MatrixType2, typename INVERSE>
248  void solve
249  (const MatrixType1 &A,
250  const MatrixType2 &B,
251  const INVERSE &inverse,
252  std::vector<std::complex<double> > &eigenvalues,
253  std::vector<VectorType> &eigenvectors,
254  const unsigned int n_eigenvalues);
255 
256  std::size_t memory_consumption() const;
257 
258 protected:
259 
264  SolverControl &solver_control;
265 
269  const AdditionalData additional_data;
270 
271  // keep MPI communicator non-const as Arpack functions are not const either:
272 
276  MPI_Comm mpi_communicator;
277 
281  MPI_Fint mpi_communicator_fortran;
282 
283  // C++98 guarantees that the elements of a vector are stored contiguously
284 
288  int lworkl;
289 
293  std::vector<double> workl;
294 
298  std::vector<double> workd;
299 
303  int nloc;
304 
308  int ncv;
309 
310 
314  int ldv;
315 
320  std::vector<double> v;
321 
326  std::vector<double> resid;
327 
331  int ldz;
332 
338  std::vector<double> z;
339 
343  int lworkev;
344 
348  std::vector<double> workev;
349 
353  std::vector<int> select;
354 
358  VectorType src,dst,tmp;
359 
363  std::vector< types::global_dof_index > local_indices;
364 
368  double shift_value;
369 
370 private:
371 
375  DeclException2 (PArpackExcConvergedEigenvectors, int, int,
376  << arg1 << "eigenpairs were requested, but only"
377  << arg2 << " converged");
378 
379  DeclException2 (PArpackExcInvalidNumberofEigenvalues, int, int,
380  << "Number of wanted eigenvalues " << arg1
381  << " is larger that the size of the matrix " << arg2);
382 
383  DeclException2 (PArpackExcInvalidEigenvectorSize, int, int,
384  << "Number of wanted eigenvalues " << arg1
385  << " is larger that the size of eigenvectors " << arg2);
386 
387  DeclException2 (PArpackExcInvalidEigenvalueSize, int, int,
388  << "Number of wanted eigenvalues " << arg1
389  << " is larger that the size of eigenvalues " << arg2);
390 
391  DeclException2 (PArpackExcInvalidNumberofArnoldiVectors, int, int,
392  << "Number of Arnoldi vectors " << arg1
393  << " is larger that the size of the matrix " << arg2);
394 
395  DeclException2 (PArpackExcSmallNumberofArnoldiVectors, int, int,
396  << "Number of Arnoldi vectors " << arg1
397  << " is too small to obtain " << arg2
398  << " eigenvalues");
399 
400  DeclException1 (PArpackExcIdo, int, << "This ido " << arg1
401  << " is not supported. Check documentation of ARPACK");
402 
403  DeclException1 (PArpackExcMode, int, << "This mode " << arg1
404  << " is not supported. Check documentation of ARPACK");
405 
406  DeclException1 (PArpackExcInfoPdnaupd, int,
407  << "Error with Pdnaupd, info " << arg1
408  << ". Check documentation of ARPACK");
409 
410  DeclException1 (PArpackExcInfoPdneupd, int,
411  << "Error with Pdneupd, info " << arg1
412  << ". Check documentation of ARPACK");
413 
414  DeclException1 (PArpackExcInfoMaxIt, int,
415  << "Maximum number " << arg1
416  << " of iterations reached.");
417 
418  DeclException1 (PArpackExcNoShifts, int,
419  << "No shifts could be applied during implicit"
420  << " Arnoldi update, try increasing the number of"
421  << " Arnoldi vectors.");
422 };
423 
424 template <typename VectorType>
425 std::size_t
426 PArpackSolver<VectorType>::memory_consumption() const
427 {
428  return MemoryConsumption::memory_consumption (double()) *
429  (workl.size() +
430  workd.size() +
431  v.size() +
432  resid.size() +
433  z.size() +
434  workev.size() ) +
435  src.memory_consumption() +
436  dst.memory_consumption() +
437  tmp.memory_consumption() +
439 }
440 
441 template <typename VectorType>
442 PArpackSolver<VectorType>::AdditionalData::
443 AdditionalData (const unsigned int number_of_arnoldi_vectors,
444  const WhichEigenvalues eigenvalue_of_interest,
445  const bool symmetric)
446  :
447  number_of_arnoldi_vectors(number_of_arnoldi_vectors),
448  eigenvalue_of_interest(eigenvalue_of_interest),
449  symmetric(symmetric)
450 {}
451 
452 template <typename VectorType>
453 PArpackSolver<VectorType>::PArpackSolver (SolverControl &control,
454  const MPI_Comm &mpi_communicator,
455  const AdditionalData &data)
456  :
457  solver_control (control),
458  additional_data (data),
459  mpi_communicator( mpi_communicator ),
460  mpi_communicator_fortran ( MPI_Comm_c2f( mpi_communicator ) ),
461  shift_value(0.0)
462 
463 {}
464 
465 template <typename VectorType>
466 void PArpackSolver<VectorType>::set_shift(const double s )
467 {
468  shift_value = s;
469 }
470 
471 template <typename VectorType>
472 void PArpackSolver<VectorType>::reinit(const ::IndexSet &locally_owned_dofs)
473 {
474  // store local indices to write to vectors
475  locally_owned_dofs.fill_index_vector(local_indices);
476 
477  // scalars
478  nloc = locally_owned_dofs.n_elements ();
479  ncv = additional_data.number_of_arnoldi_vectors;
480 
481  Assert (local_indices.size() == nloc, ExcInternalError() );
482 
483  // vectors
484  ldv = nloc;
485  v.resize (ldv*ncv, 0.0);
486 
487  // TODO: add optional input for resid
488  resid.resize(nloc, 1.0);
489 
490  // work arrays for ARPACK
491  workd.resize(3*nloc,0.0);
492 
493  lworkl = additional_data.symmetric ?
494  ncv*ncv + 8*ncv
495  :
496  3*ncv*ncv+6*ncv;
497  workl.resize (lworkl, 0.);
498 
499  ldz = nloc;
500  z.resize (ldz*ncv, 0.); // TODO we actually need only ldz*nev
501 
502  // WORKEV Double precision work array of dimension 3*NCV.
503  lworkev = additional_data.symmetric ?
504  0 /*not used in symmetric case*/
505  :
506  3*ncv;
507  workev.resize (lworkev, 0.);
508 
509  select.resize (ncv, 0);
510 
511  // deal.II vectors:
512  src.reinit (locally_owned_dofs,mpi_communicator);
513  dst.reinit (locally_owned_dofs,mpi_communicator);
514  tmp.reinit (locally_owned_dofs,mpi_communicator);
515 
516 }
517 
518 template <typename VectorType>
519 template <typename MatrixType1,typename MatrixType2, typename INVERSE>
520 void PArpackSolver<VectorType>::solve
521 (const MatrixType1 &/*system_matrix*/,
522  const MatrixType2 &mass_matrix,
523  const INVERSE &inverse,
524  std::vector<std::complex<double> > &eigenvalues,
525  std::vector<VectorType> &eigenvectors,
526  const unsigned int n_eigenvalues)
527 {
528 
529  Assert (n_eigenvalues <= eigenvectors.size(),
530  PArpackExcInvalidEigenvectorSize(n_eigenvalues, eigenvectors.size()));
531 
532  Assert (n_eigenvalues <= eigenvalues.size(),
533  PArpackExcInvalidEigenvalueSize(n_eigenvalues, eigenvalues.size()));
534 
535 
536  Assert (n_eigenvalues < mass_matrix.m(),
537  PArpackExcInvalidNumberofEigenvalues(n_eigenvalues, mass_matrix.m()));
538 
539  Assert (additional_data.number_of_arnoldi_vectors < mass_matrix.m(),
540  PArpackExcInvalidNumberofArnoldiVectors(
541  additional_data.number_of_arnoldi_vectors, mass_matrix.m()));
542 
543  Assert (additional_data.number_of_arnoldi_vectors > 2*n_eigenvalues+1,
544  PArpackExcSmallNumberofArnoldiVectors(
545  additional_data.number_of_arnoldi_vectors, n_eigenvalues));
546  // ARPACK mode for dnaupd, here only
547  // Mode 3: K*x = lambda*M*x, K symmetric, M symmetric positive semi-definite
548  //c ===> OP = (inv[K - sigma*M])*M and B = M.
549  //c ===> Shift-and-Invert mode
550  int mode = 3;
551 
552  // reverse communication parameter
553  // must be zero on the first call to pdnaupd
554  int ido = 0;
555 
556  // 'G' generalized eigenvalue problem
557  // 'I' standard eigenvalue problem
558  char bmat[2] = "G";
559 
560  // Specify the eigenvalues of interest, possible parameters:
561  // "LA" algebraically largest
562  // "SA" algebraically smallest
563  // "LM" largest magnitude
564  // "SM" smallest magnitude
565  // "LR" largest real part
566  // "SR" smallest real part
567  // "LI" largest imaginary part
568  // "SI" smallest imaginary part
569  // "BE" both ends of spectrum simultaneous
570 
571  char which[3];
572  switch (additional_data.eigenvalue_of_interest)
573  {
574  case algebraically_largest:
575  std::strcpy (which, "LA");
576  break;
577  case algebraically_smallest:
578  std::strcpy (which, "SA");
579  break;
580  case largest_magnitude:
581  std::strcpy (which, "LM");
582  break;
583  case smallest_magnitude:
584  std::strcpy (which, "SM");
585  break;
586  case largest_real_part:
587  std::strcpy (which, "LR");
588  break;
589  case smallest_real_part:
590  std::strcpy (which, "SR");
591  break;
592  case largest_imaginary_part:
593  std::strcpy (which, "LI");
594  break;
595  case smallest_imaginary_part:
596  std::strcpy (which, "SI");
597  break;
598  case both_ends:
599  std::strcpy (which, "BE");
600  break;
601  }
602 
603  // tolerance for ARPACK
604  double tol = control().tolerance();
605 
606  //information to the routines
607  std::vector<int> iparam (11, 0);
608 
609  iparam[0] = 1;
610  // shift strategy: exact shifts with respect to the current Hessenberg matrix H.
611 
612  // maximum number of iterations
613  iparam[2] = control().max_steps();
614 
615  // Parpack currently works only for NB = 1
616  iparam[3] = 1;
617 
618  // Sets the mode of dsaupd:
619  // 1 is exact shifting,
620  // 2 is user-supplied shifts,
621  // 3 is shift-invert mode,
622  // 4 is buckling mode,
623  // 5 is Cayley mode.
624 
625  iparam[6] = mode;
626  std::vector<int> ipntr (14, 0);
627 
628  //information out of the iteration
629  // If INFO .EQ. 0, a random initial residual vector is used.
630  // If INFO .NE. 0, RESID contains the initial residual vector,
631  // possibly from a previous run.
632  // Typical choices in this situation might be to use the final value
633  // of the starting vector from the previous eigenvalue calculation
634  int info = 1;
635 
636  // Number of eigenvalues of OP to be computed. 0 < NEV < N.
637  int nev = n_eigenvalues;
638  int n_inside_arpack = nloc;
639 
640  while (ido != 99)
641  {
642  // call of ARPACK pdnaupd routine
643  if (additional_data.symmetric)
644  pdsaupd_(&mpi_communicator_fortran,&ido, bmat, &n_inside_arpack, which, &nev, &tol,
645  &resid[0], &ncv, &v[0], &ldv, &iparam[0], &ipntr[0],
646  &workd[0], &workl[0], &lworkl, &info);
647  else
648  pdnaupd_(&mpi_communicator_fortran,&ido, bmat, &n_inside_arpack, which, &nev, &tol,
649  &resid[0], &ncv, &v[0], &ldv, &iparam[0], &ipntr[0],
650  &workd[0], &workl[0], &lworkl, &info);
651 
652  if (ido == 99)
653  break;
654 
655  switch (mode)
656  {
657 // OP = (inv[K - sigma*M])*M
658  case 3:
659  {
660  switch (ido)
661  {
662 // compute Y = OP * X where
663 // IPNTR(1) is the pointer into WORKD for X,
664 // IPNTR(2) is the pointer into WORKD for Y.
665  case -1:
666  {
667  const int shift_x = ipntr[0]-1;
668  const int shift_y = ipntr[1]-1;
669  Assert (shift_x>=0, ::ExcInternalError() );
670  Assert (shift_x+nloc <= workd.size(), ::ExcInternalError() );
671  Assert (shift_y>=0, ::ExcInternalError() );
672  Assert (shift_y+nloc <= workd.size(), ::ExcInternalError() );
673 
674  src = 0.0;
675  src.add (nloc,
676  &local_indices[0],
677  &workd[0]+shift_x );
678  src.compress (VectorOperation::add);
679 
680  // multiplication with mass matrix M
681  mass_matrix.vmult(tmp, src);
682  // solving linear system
683  inverse.vmult(dst,tmp);
684 
685  // store the result
686  dst.extract_subvector_to (local_indices.begin(),
687  local_indices.end(),
688  &workd[0]+shift_y );
689  }
690  break;
691 
692 // compute Y = OP * X where
693 // IPNTR(1) is the pointer into WORKD for X,
694 // IPNTR(2) is the pointer into WORKD for Y.
695 // In mode 3,4 and 5, the vector B * X is already
696 // available in WORKD(ipntr(3)). It does not
697 // need to be recomputed in forming OP * X.
698  case 1:
699  {
700  const int shift_x = ipntr[0]-1;
701  const int shift_y = ipntr[1]-1;
702  const int shift_b_x = ipntr[2]-1;
703 
704  Assert (shift_x>=0, ::ExcInternalError() );
705  Assert (shift_x+nloc <= workd.size(), ::ExcInternalError() );
706  Assert (shift_y>=0, ::ExcInternalError() );
707  Assert (shift_y+nloc <= workd.size(), ::ExcInternalError() );
708  Assert (shift_b_x>=0, ::ExcInternalError() );
709  Assert (shift_b_x+nloc <= workd.size(), ::ExcInternalError() );
710  Assert (shift_y>=0, ::ExcInternalError() );
711  Assert (shift_y+nloc <= workd.size(), ::ExcInternalError() );
712 
713  src = 0.0; // B*X
714  src.add (nloc,
715  &local_indices[0],
716  &workd[0]+shift_b_x );
717 
718  tmp = 0.0; // X
719  tmp.add (nloc,
720  &local_indices[0],
721  &workd[0]+shift_x);
722 
723  src.compress (VectorOperation::add);
724  tmp.compress (VectorOperation::add);
725 
726  // solving linear system
727  inverse.vmult(dst,src);
728 
729  // store the result
730  dst.extract_subvector_to (local_indices.begin(),
731  local_indices.end(),
732  &workd[0]+shift_y );
733 
734  }
735  break;
736 
737 // compute Y = B * X where
738 // IPNTR(1) is the pointer into WORKD for X,
739 // IPNTR(2) is the pointer into WORKD for Y.
740  case 2:
741  {
742 
743  const int shift_x = ipntr[0]-1;
744  const int shift_y = ipntr[1]-1;
745  Assert (shift_x>=0, ::ExcInternalError() );
746  Assert (shift_x+nloc <= workd.size(), ::ExcInternalError() );
747  Assert (shift_y>=0, ::ExcInternalError() );
748  Assert (shift_y+nloc <= workd.size(), ::ExcInternalError() );
749 
750  src = 0.0;
751  src.add (nloc,
752  &local_indices[0],
753  &workd[0]+shift_x );
754  src.compress (VectorOperation::add);
755 
756  // Multiplication with mass matrix M
757  mass_matrix.vmult(dst, src);
758 
759  // store the result
760  dst.extract_subvector_to (local_indices.begin(),
761  local_indices.end(),
762  &workd[0]+shift_y);
763 
764  }
765  break;
766 
767  default:
768  Assert (false, PArpackExcIdo(ido));
769  break;
770  }
771  }
772  break;
773  default:
774  Assert (false, PArpackExcMode(mode));
775  break;
776  }
777  }
778 
779  if (info<0)
780  {
781  Assert (false, PArpackExcInfoPdnaupd(info));
782  }
783  else
784  {
785  // 1 - compute eigenvectors,
786  // 0 - only eigenvalues
787  int rvec = 1;
788 
789  // which eigenvectors
790  char howmany[4] = "All";
791 
792  double sigmar = shift_value; // real part of the shift
793  double sigmai = 0.0; // imaginary part of the shift
794 
795  std::vector<double> eigenvalues_real (n_eigenvalues, 0.);
796  std::vector<double> eigenvalues_im (n_eigenvalues, 0.);
797 
798  // call of ARPACK pdneupd routine
799  if (additional_data.symmetric)
800  pdseupd_(&mpi_communicator_fortran, &rvec, howmany, &select[0], &eigenvalues_real[0],
801  &z[0], &ldz, &sigmar,
802  bmat, &n_inside_arpack, which, &nev, &tol,
803  &resid[0], &ncv, &v[0], &ldv,
804  &iparam[0], &ipntr[0], &workd[0], &workl[0], &lworkl, &info);
805  else
806  pdneupd_(&mpi_communicator_fortran, &rvec, howmany, &select[0], &eigenvalues_real[0],
807  &eigenvalues_im[0], &z[0], &ldz, &sigmar, &sigmai,
808  &workev[0], bmat, &n_inside_arpack, which, &nev, &tol,
809  &resid[0], &ncv, &v[0], &ldv,
810  &iparam[0], &ipntr[0], &workd[0], &workl[0], &lworkl, &info);
811 
812  if (info == 1)
813  {
814  Assert (false, PArpackExcInfoMaxIt(control().max_steps()));
815  }
816  else if (info == 3)
817  {
818  Assert (false, PArpackExcNoShifts(1));
819  }
820  else if (info!=0)
821  {
822  Assert (false, PArpackExcInfoPdneupd(info));
823  }
824 
825  for (size_type i=0; i<n_eigenvalues; ++i)
826  {
827  eigenvectors[i] = 0.0;
828  Assert (i*nloc + nloc <= v.size(), ::ExcInternalError() );
829 
830  eigenvectors[i].add (nloc,
831  &local_indices[0],
832  &v[i*nloc] );
833  eigenvectors[i].compress (VectorOperation::add);
834  }
835 
836  for (size_type i=0; i<n_eigenvalues; ++i)
837  eigenvalues[i] = std::complex<double> (eigenvalues_real[i],
838  eigenvalues_im[i]);
839  }
840 
841  Assert (iparam[4] == n_eigenvalues,
842  PArpackExcConvergedEigenvectors(iparam[4], n_eigenvalues));
843 
844  // both PDNAUPD and PDSAUPD compute eigenpairs of inv[A - sigma*M]*M
845  // with respect to a semi-inner product defined by M.
846 
847  // resid likely contains residual with respect to M-norm.
848  {
849 
850  tmp = 0.0;
851  tmp.add (nloc,
852  &local_indices[0],
853  &resid[0]);
854  solver_control.check ( iparam[2], tmp.l2_norm() );
855  }
856 
857 
858 }
859 
860 template <typename VectorType>
861 SolverControl &PArpackSolver<VectorType>::control () const
862 {
863  return solver_control;
864 }
865 
866 DEAL_II_NAMESPACE_CLOSE
867 
868 
869 #endif
870 #endif
#define DeclException2(Exception2, type1, type2, outsequence)
Definition: exceptions.h:552
#define DeclException1(Exception1, type1, outsequence)
Definition: exceptions.h:542
unsigned int global_dof_index
Definition: types.h:88
#define Assert(cond, exc)
Definition: exceptions.h:294
std_cxx11::enable_if< std_cxx11::is_fundamental< T >::value, std::size_t >::type memory_consumption(const T &t)