From 2e36dce14572f097c9f06accb3ab9087893e926d Mon Sep 17 00:00:00 2001
From: Christophe Geuzaine <cgeuzaine@ulg.ac.be>
Date: Mon, 26 Oct 2009 20:57:38 +0000
Subject: [PATCH] better slepc interface

---
 Solver/eigenSolver.cpp | 67 +++++++++++++++++++++---------------------
 Solver/eigenSolver.h   |  7 +++--
 2 files changed, 39 insertions(+), 35 deletions(-)

diff --git a/Solver/eigenSolver.cpp b/Solver/eigenSolver.cpp
index 64176c5764..ddfade0e24 100644
--- a/Solver/eigenSolver.cpp
+++ b/Solver/eigenSolver.cpp
@@ -22,7 +22,7 @@ eigenSolver::eigenSolver(dofManager<double, double> *manager, std::string A,
   }
 }
 
-void eigenSolver::solve()
+void eigenSolver::solve(int numEigenValues, std::string which)
 {
   if(!_A) return;
   Mat A = _A->getMatrix();
@@ -33,41 +33,47 @@ void eigenSolver::solve()
   PetscInt N, M;
   _try(MatGetSize(A, &N, &M));
 
-  // treatment of a generalized eigenvalue problem A x - \lambda B x = 0
+  // generalized eigenvalue problem A x - \lambda B x = 0
   EPS eps;
   _try(EPSCreate(PETSC_COMM_WORLD, &eps));
   _try(EPSSetOperators(eps, A, B));
-  // FIXME: check if hermitian (EPS_HEP or EPS_GHEP)
-  //_try(EPSSetProblemType(eps, _B ? EPS_GNHEP : EPS_NHEP));
-  _try(EPSSetProblemType(eps, _B ? EPS_GHEP : EPS_HEP));
-
-  // set options
-  //_try(EPSSetWhichEigenpairs(eps, EPS_LARGEST_MAGNITUDE));
-  //_try(EPSSetWhichEigenpairs(eps, EPS_SMALLEST_REAL));
-  _try(EPSSetTarget(eps, 0.)); // find eigenvalues close to given target
-  PetscInt nev = 2; // number of eigenvalues to compute
-  PetscInt mpd = nev; // max dim allowed for the projected problem
-  PetscInt ncv = nev + mpd; // max dim of the subspace to be used by the solver
-  _try(EPSSetDimensions(eps, nev, ncv, mpd));
-  //_try(EPSSetType(eps, EPSPOWER));
-
-  // override options at runtime, petsc-style
+  bool hermitian = false; // FIXME
+  if(hermitian)
+    _try(EPSSetProblemType(eps, _B ? EPS_GHEP : EPS_HEP));
+  else
+    _try(EPSSetProblemType(eps, _B ? EPS_GNHEP : EPS_NHEP));
+
+  // set some default options
+  _try(EPSSetDimensions(eps, 5, PETSC_DECIDE, PETSC_DECIDE));
+
+  // override these options at runtime, petsc-style
   _try(EPSSetFromOptions(eps));
 
+  // force options specified directly as arguments
+  if(numEigenValues)
+    _try(EPSSetDimensions(eps, numEigenValues, PETSC_DECIDE, PETSC_DECIDE));
+  if(which == "smallest")
+    _try(EPSSetWhichEigenpairs(eps, EPS_SMALLEST_MAGNITUDE));
+  else if(which == "largest")
+    _try(EPSSetWhichEigenpairs(eps, EPS_LARGEST_MAGNITUDE));
+
   // print info
   const EPSType type;
   _try(EPSGetType(eps, &type));
   Msg::Info("SLEPc solution method: %s", type);
-  _try(EPSGetDimensions(eps, &nev, &ncv, &mpd));
+  PetscInt nev;
+  _try(EPSGetDimensions(eps, &nev, PETSC_NULL, PETSC_NULL));
   Msg::Info("SLEPc number of requested eigenvalues: %d", nev);
   PetscReal tol;
   PetscInt maxit;
   _try(EPSGetTolerances(eps, &tol, &maxit));
   Msg::Info("SLEPc stopping condition: tol=%g, maxit=%d", tol, maxit);
- 	
+
+  // solve
   Msg::Info("SLEPc solving...");
   _try(EPSSolve(eps));
 
+  // check convergence
   int its;
   _try(EPSGetIterationNumber(eps, &its));
   EPSConvergedReason reason;
@@ -75,7 +81,7 @@ void eigenSolver::solve()
   if(reason == EPS_CONVERGED_TOL)
     Msg::Info("SLEPc converged in %d iterations", its);
   else if(reason == EPS_DIVERGED_ITS)
-    Msg::Error("SLEPc did not converge in %d iterations", its);
+    Msg::Error("SLEPc diverged after %d iterations", its);
   else if(reason == EPS_DIVERGED_BREAKDOWN)
     Msg::Error("SLEPc generic breakdown in method");
   else if(reason == EPS_DIVERGED_NONSYMMETRIC)
@@ -86,20 +92,15 @@ void eigenSolver::solve()
   _try(EPSGetConverged(eps, &nconv));
   Msg::Info("SLEPc number of converged eigenpairs: %d", nconv);
 
-  // if number of converged solutions is greated than requested
-  // discarded surplus solutions
+  // ignore additional eigenvalues if we get more than what we asked
   if(nconv > nev) nconv = nev;
-  // if number of converged solutions is less than requested display
-  // only converged solutions
-  if(nconv < nev) nev = nconv;
 
   Vec xr, xi;
   _try(MatGetVecs(A, PETSC_NULL, &xr));
   _try(MatGetVecs(A, PETSC_NULL, &xi));
   Msg::Info("         Re[EigenValue]          Im[EigenValue]"
             "          Relative error");
-  for (int i = nconv - 1; i > nconv - nev - 1; i--){
-    std::vector<std::complex<double> > ev(N);
+  for (int i = 0; i < nconv; i++){
     PetscScalar kr, ki;
     _try(EPSGetEigenpair(eps, i, &kr, &ki, xr, xi));
     PetscReal error;
@@ -111,15 +112,15 @@ void eigenSolver::solve()
     PetscReal re = kr;
     PetscReal im = ki;
 #endif
-    // Output eigenvalues and relative error (= ||Ax - eig*Bx||/||eig*x||)
-    Msg::Info("EIG %03d %s%.16e %s%.16e  %3.6e", (nconv - i), 
-              (re < 0) ? "" : " ", re, (im < 0) ? "" : " ", im, error);
-    _eigenValues.push_back(std::complex<double>(re, im));
+    Msg::Info("EIG %03d %s%.16e %s%.16e  %3.6e", 
+              i, (re < 0) ? "" : " ", re, (im < 0) ? "" : " ", im, error);
 
-    // Output eigenvector
+    // store eigenvalues and eigenvectors
+    _eigenValues.push_back(std::complex<double>(re, im));
     PetscScalar *tmpr, *tmpi;
     _try(VecGetArray(xr, &tmpr));
     _try(VecGetArray(xi, &tmpi));
+    std::vector<std::complex<double> > ev(N);
     for(int i = 0; i < N; i++){
 #if defined(PETSC_USE_COMPLEX)
       ev[i] = tmpr[i];
@@ -130,7 +131,7 @@ void eigenSolver::solve()
     _eigenVectors.push_back(ev);
   }
   
-  // Free work space
+  // cleanup
   _try(EPSDestroy(eps));
   _try(VecDestroy(xr));
   _try(VecDestroy(xi));
diff --git a/Solver/eigenSolver.h b/Solver/eigenSolver.h
index 709cd60b06..3fffcaf6f4 100644
--- a/Solver/eigenSolver.h
+++ b/Solver/eigenSolver.h
@@ -26,7 +26,7 @@ class eigenSolver{
  public:
   eigenSolver(dofManager<double, double> *manager, std::string A, 
               std::string B="");
-  void solve();
+  void solve(int numEigenValues=0, std::string which="");
   int getNumEigenValues(){ return _eigenValues.size(); }
   std::complex<double> getEigenValue(int num){ return _eigenValues[num]; }
   std::vector<std::complex<double> > &getEigenVector(int num){ return _eigenVectors[num]; }
@@ -40,7 +40,10 @@ class eigenSolver{
  public:
   eigenSolver(dofManager<double, double> *manager, std::string A, 
               std::string B=""){}
-  void solve(){ Msg::Error("Eigen solver requires SLEPc"); }
+  void solve(int numEigenValues=0, std::string which="")
+  {
+    Msg::Error("Eigen solver requires SLEPc");
+  }
   int getNumEigenValues(){ return 0.; }
   std::complex<double> getEigenValue(int num){ return 0.; }
   std::vector<std::complex<double> > &getEigenVector(int num){ return _dummy; }
-- 
GitLab