KratosMultiphysics
KRATOS Multiphysics (Kratos) is a framework for building parallel, multi-disciplinary simulation software, aiming at modularity, extensibility, and high performance. Kratos is written in C++, and counts with an extensive Python interface.
feast_eigensystem_solver.h
Go to the documentation of this file.
1 /* KRATOS _ _ ____ _
2 // | | (_)_ __ ___ __ _ _ __/ ___| ___ | |_ _____ _ __ ___
3 // | | | | '_ \ / _ \/ _` | '__\___ \ / _ \| \ \ / / _ \ '__/ __|
4 // | |___| | | | | __/ (_| | | ___) | (_) | |\ V / __/ | \__ |
5 // |_____|_|_| |_|\___|\__,_|_| |____/ \___/|_| \_/ \___|_| |___/ Application
6 //
7 // Author: Quirin Aumann
8 */
9 
10 #if !defined(KRATOS_FEAST_EIGENSYSTEM_SOLVER_H_INCLUDED)
11 #define KRATOS_FEAST_EIGENSYSTEM_SOLVER_H_INCLUDED
12 
13 // External includes
14 
15 // Project includes
16 #include "includes/define.h"
21 
22 extern "C" {
23  #include <feast.h>
24  #include <feast_sparse.h>
25 }
26 
27 namespace Kratos {
28 
29 namespace { // helpers namespace
30  template<typename TScalar>
31  struct SettingsHelper
32  {
33  SettingsHelper(Parameters SolverParams) : mParam(SolverParams) {};
34 
35  Parameters GetDefaultParameters();
36  void CheckParameters();
37  TScalar GetE1();
38  double GetE2();
39  private:
40  Parameters mParam;
41  };
42 
43  template<>
44  Parameters SettingsHelper<double>::GetDefaultParameters()
45  {
46  return Parameters(R"({
47  "e_min" : 0.0,
48  "e_max" : 0.0
49  })");
50  }
51 
52  template<>
53  Parameters SettingsHelper<std::complex<double>>::GetDefaultParameters()
54  {
55  return Parameters(R"({
56  "e_mid_re" : 0.0,
57  "e_mid_im" : 0.0,
58  "e_r" : 0.0
59  })");
60  }
61 
62  template<>
63  void SettingsHelper<double>::CheckParameters()
64  {
65  KRATOS_ERROR_IF( mParam["search_lowest_eigenvalues"].GetBool() && mParam["search_highest_eigenvalues"].GetBool() ) <<
66  "Cannot search for highest and lowest eigenvalues at the same time" << std::endl;
67 
68  KRATOS_ERROR_IF( mParam["e_max"].GetDouble() <= mParam["e_min"].GetDouble() ) <<
69  "Invalid eigenvalue limits provided" << std::endl;
70  }
71 
72  template<>
73  void SettingsHelper<std::complex<double>>::CheckParameters()
74  {
75  KRATOS_ERROR_IF( mParam["e_r"].GetDouble() <= 0.0 ) <<
76  "Invalid search radius provided" << std::endl;
77 
78  KRATOS_ERROR_IF( mParam["search_lowest_eigenvalues"].GetBool() || mParam["search_highest_eigenvalues"].GetBool() ) <<
79  "Search for extremal eigenvalues is only available for real symmetric problems" << std::endl;
80  }
81 
82  template<> double SettingsHelper<double>::GetE1() {return mParam["e_min"].GetDouble();}
83  template<> std::complex<double> SettingsHelper<std::complex<double>>::GetE1() {return std::complex<double>(mParam["e_mid_re"].GetDouble(), mParam["e_mid_im"].GetDouble());}
84 
85  template<>double SettingsHelper<double>::GetE2() {return mParam["e_max"].GetDouble();}
86  template<> double SettingsHelper<std::complex<double>>::GetE2() {return mParam["e_r"].GetDouble();}
87 
88  template<typename TScalar>
89  struct SortingHelper
90  {
91  SortingHelper(std::string Order) : mOrder(Order) {};
92  // void Check();
93  template<typename MatrixType, typename VectorType>
94  void SortEigenvalues(VectorType&, MatrixType&);
95  private:
96  std::string mOrder;
97  };
98 
99  template<> template<typename MatrixType, typename VectorType>
100  void SortingHelper<double>::SortEigenvalues(VectorType &rEigenvalues, MatrixType &rEigenvectors)
101  {
102  KRATOS_WARNING_IF("FeastEigensystemSolver", mOrder == "si") << "Attempting to sort by imaginary value. Falling back on \"sr\"" << std::endl;
103  KRATOS_WARNING_IF("FeastEigensystemSolver", mOrder == "li") << "Attempting to sort by imaginary value. Falling back on \"lr\"" << std::endl;
104 
105  std::vector<std::size_t> idx(rEigenvalues.size());
106  std::iota(idx.begin(), idx.end(), 0);
107 
108  if( mOrder == "sr" || mOrder == "si" ) {
109  std::stable_sort(idx.begin(), idx.end(),
110  [&rEigenvalues](std::size_t i1, std::size_t i2) {return rEigenvalues[i1] < rEigenvalues[i2];});
111  } else if( mOrder == "sm") {
112  std::stable_sort(idx.begin(), idx.end(),
113  [&rEigenvalues](std::size_t i1, std::size_t i2) {return std::abs(rEigenvalues[i1]) < std::abs(rEigenvalues[i2]);});
114  } else if( mOrder == "lr" || mOrder == "li" ) {
115  std::stable_sort(idx.begin(), idx.end(),
116  [&rEigenvalues](std::size_t i1, std::size_t i2) {return rEigenvalues[i1] > rEigenvalues[i2];});
117  } else if( mOrder == "lm") {
118  std::stable_sort(idx.begin(), idx.end(),
119  [&rEigenvalues](std::size_t i1, std::size_t i2) {return std::abs(rEigenvalues[i1]) > std::abs(rEigenvalues[i2]);});
120  } else {
121  KRATOS_ERROR << "Invalid sort type. Allowed are sr, sm, si, lr, lm, li" << std::endl;
122  }
123 
124  VectorType tmp_eigenvalues(rEigenvalues.size());
125  MatrixType tmp_eigenvectors(rEigenvectors.size1(), rEigenvectors.size2());
126 
127  for( std::size_t i=0; i<rEigenvalues.size(); ++i ) {
128  tmp_eigenvalues[i] = rEigenvalues[idx[i]];
129  column(tmp_eigenvectors, i).swap(column(rEigenvectors, idx[i]));
130  }
131  rEigenvalues.swap(tmp_eigenvalues);
132  rEigenvectors.swap(tmp_eigenvectors);
133  }
134 
135  template<> template<typename MatrixType, typename VectorType>
136  void SortingHelper<std::complex<double>>::SortEigenvalues(VectorType &rEigenvalues, MatrixType &rEigenvectors)
137  {
138  std::vector<std::size_t> idx(rEigenvalues.size());
139  std::iota(idx.begin(), idx.end(), 0);
140 
141  if( mOrder == "sr" ) {
142  std::stable_sort(idx.begin(), idx.end(),
143  [&rEigenvalues](std::size_t i1, std::size_t i2) {return std::real(rEigenvalues[i1]) < std::real(rEigenvalues[i2]);});
144  } else if( mOrder == "sm") {
145  std::stable_sort(idx.begin(), idx.end(),
146  [&rEigenvalues](std::size_t i1, std::size_t i2) {return std::abs(rEigenvalues[i1]) < std::abs(rEigenvalues[i2]);});
147  } else if( mOrder == "si") {
148  std::stable_sort(idx.begin(), idx.end(),
149  [&rEigenvalues](std::size_t i1, std::size_t i2) {return std::imag(rEigenvalues[i1]) < std::imag(rEigenvalues[i2]);});
150  } else if( mOrder == "lr" ) {
151  std::stable_sort(idx.begin(), idx.end(),
152  [&rEigenvalues](std::size_t i1, std::size_t i2) {return std::real(rEigenvalues[i1]) > std::real(rEigenvalues[i2]);});
153  } else if( mOrder == "lm") {
154  std::stable_sort(idx.begin(), idx.end(),
155  [&rEigenvalues](std::size_t i1, std::size_t i2) {return std::abs(rEigenvalues[i1]) > std::abs(rEigenvalues[i2]);});
156  } else if( mOrder == "li") {
157  std::stable_sort(idx.begin(), idx.end(),
158  [&rEigenvalues](std::size_t i1, std::size_t i2) {return std::imag(rEigenvalues[i1]) > std::imag(rEigenvalues[i2]);});
159  } else {
160  KRATOS_ERROR << "Invalid sort type. Allowed are sr, sm, si, lr, lm, li" << std::endl;
161  }
162 
163  VectorType tmp_eigenvalues(rEigenvalues.size());
164  MatrixType tmp_eigenvectors(rEigenvectors.size1(), rEigenvectors.size2());
165 
166  for( std::size_t i=0; i<rEigenvalues.size(); ++i ) {
167  tmp_eigenvalues[i] = rEigenvalues[idx[i]];
168  column(tmp_eigenvectors, i).swap(column(rEigenvectors, idx[i]));
169  }
170  rEigenvalues.swap(tmp_eigenvalues);
171  rEigenvectors.swap(tmp_eigenvectors);
172  }
173 }
174 
175 template<
176  bool TSymmetric,
177  typename TScalarIn,
178  typename TScalarOut,
179  class TSparseSpaceTypeIn = TUblasSparseSpace<TScalarIn>,
180  class TDenseSpaceTypeIn = TUblasDenseSpace<TScalarIn>,
181  class TSparseSpaceTypeOut = TUblasSparseSpace<TScalarOut>,
182  class TDenseSpaceTypeOut = TUblasDenseSpace<TScalarOut>>
184  : public LinearSolver<TSparseSpaceTypeIn, TDenseSpaceTypeOut>
185 {
186  Parameters mParam;
187 
188  public:
190 
192 
194 
196 
198 
199  typedef matrix<TScalarOut, column_major> FEASTMatrixType;
200 
201  typedef TScalarIn ValueTypeIn;
202 
203  typedef TScalarOut ValueTypeOut;
204 
206  Parameters param
207  ) : mParam(param)
208  {
209  Parameters default_params(R"(
210  {
211  "solver_type" : "feast",
212  "symmetric" : true,
213  "number_of_eigenvalues" : 0,
214  "search_lowest_eigenvalues" : false,
215  "search_highest_eigenvalues" : false,
216  "sort_eigenvalues" : false,
217  "sort_order" : "sr",
218  "subspace_size" : 0,
219  "max_iteration" : 20,
220  "tolerance" : 1e-12,
221  "echo_level" : 0
222  })");
223 
224  default_params.AddMissingParameters(SettingsHelper<TScalarOut>(mParam).GetDefaultParameters());
225 
226  mParam.ValidateAndAssignDefaults(default_params);
227 
228  KRATOS_ERROR_IF( mParam["number_of_eigenvalues"].GetInt() < 0 ) <<
229  "Invalid number of eigenvalues provided" << std::endl;
230 
231  KRATOS_ERROR_IF( mParam["subspace_size"].GetInt() < 0 ) <<
232  "Invalid subspace size provided" << std::endl;
233 
234  KRATOS_ERROR_IF( mParam["max_iteration"].GetInt() < 1 ) <<
235  "Invalid maximal number of iterations provided" << std::endl;
236 
237  KRATOS_ERROR_IF( (mParam["search_lowest_eigenvalues"].GetBool() || mParam["search_highest_eigenvalues"].GetBool())
238  && mParam["number_of_eigenvalues"].GetInt() == 0 ) << "Please specify the number of eigenvalues to be found" << std::endl;
239 
240  KRATOS_ERROR_IF( mParam["subspace_size"].GetInt() == 0 && mParam["number_of_eigenvalues"].GetInt() == 0 ) <<
241  "Please specify either \"subspace_size\" or \"number_of_eigenvalues\"" << std::endl;
242 
243  KRATOS_INFO_IF( "FEASTEigensystemSolver",
244  mParam["number_of_eigenvalues"].GetInt() > 0 && mParam["subspace_size"].GetInt() > 0 ) <<
245  "Manually defined subspace size will be overwritten to match the defined number of eigenvalues" << std::endl;
246 
247  const std::string s = mParam["sort_order"].GetString();
248  KRATOS_ERROR_IF( !(s=="sr" || s=="sm" || s=="si" || s=="lr" || s=="lm" || s=="li") ) <<
249  "Invalid sort type. Allowed are sr, sm, si, lr, lm, li" << std::endl;
250 
251  SettingsHelper<TScalarOut>(mParam).CheckParameters();
252  }
253 
254  ~FEASTEigensystemSolver() override = default;
255 
263  void Solve(
264  SparseMatrixType& rK,
265  SparseMatrixType& rM,
266  DenseVectorType& rEigenvalues,
267  DenseMatrixType& rEigenvectors) override
268  {
269  // settings
270  const std::size_t system_size = rK.size1();
271  std::size_t subspace_size;
272 
273  if( mParam["search_lowest_eigenvalues"].GetBool() || mParam["search_highest_eigenvalues"].GetBool() ) {
274  subspace_size = 2 * static_cast<std::size_t>(mParam["number_of_eigenvalues"].GetInt());
275  } else if( mParam["subspace_size"].GetInt() == 0 ) {
276  subspace_size = 1.5 * static_cast<std::size_t>(mParam["number_of_eigenvalues"].GetInt());
277  } else {
278  subspace_size = static_cast<std::size_t>(mParam["subspace_size"].GetInt());
279  }
280 
281  // create column based matrix for the fortran routine
282  FEASTMatrixType tmp_eigenvectors(system_size, subspace_size);
283  DenseVectorType tmp_eigenvalues(subspace_size);
284  DenseVectorType residual(subspace_size);
285 
286  // set FEAST settings
287  int fpm[64] = {};
288  feastinit(fpm);
289 
290  mParam["echo_level"].GetInt() > 0 ? fpm[0] = 1 : fpm[0] = 0;
291 
292  fpm[2] = -std::log10(mParam["tolerance"].GetDouble());
293  fpm[3] = mParam["max_iteration"].GetInt();
294 
295  // compute only right eigenvectors
296  if( !TSymmetric ) {
297  fpm[14] = 1;
298  }
299 
300  if( mParam["search_lowest_eigenvalues"].GetBool() ) {
301  fpm[39] = -1;
302  }
303  if( mParam["search_highest_eigenvalues"].GetBool() ) {
304  fpm[39] = 1;
305  }
306 
307  char UPLO = 'F';
308  int N = static_cast<int>(system_size);
309 
310  // provide matrices in array form. fortran indices start with 1, must be int
311  double* A = reinterpret_cast<double*>(rK.value_data().begin());
312 
313  std::vector<int> IA(N+1);
314  CreateFortranIndices(rK.index1_data(), IA);
315 
316  std::vector<int> JA(IA[N]-1);
317  CreateFortranIndices(rK.index2_data(), JA);
318 
319  double* B = reinterpret_cast<double*>(rM.value_data().begin());
320 
321  std::vector<int> IB(N+1);
322  CreateFortranIndices(rM.index1_data(), IB);
323 
324  std::vector<int> JB(IB[N]-1);
325  CreateFortranIndices(rM.index2_data(), JB);
326 
327  double epsout;
328  int loop;
329  TScalarOut E1 = SettingsHelper<TScalarOut>(mParam).GetE1();
330  double E2 = SettingsHelper<TScalarOut>(mParam).GetE2();
331  double* Emin = reinterpret_cast<double*>(&E1);
332  double* Emax = reinterpret_cast<double*>(&E2);
333  int M0 = static_cast<int>(subspace_size);
334  double* E = reinterpret_cast<double*>(tmp_eigenvalues.data().begin());
335  double* X = reinterpret_cast<double*>(tmp_eigenvectors.data().begin());
336  int M;
337  double* res = reinterpret_cast<double*>(residual.data().begin());
338  int info;
339 
340  // call feast
341  auto feast = CreateFeast<TScalarIn>(TSymmetric);
342  feast(&UPLO, &N, A, IA.data(), JA.data(), B, IB.data(), JB.data(), fpm, &epsout, &loop, Emin, Emax, &M0, E, X, &M, res, &info);
343 
344  KRATOS_ERROR_IF(info < 0 || info > 99) << "FEAST encounterd error " << info << ". Please check FEAST output." << std::endl;
345  KRATOS_INFO_IF("FeastEigensystemSolver", info > 1 && info < 6) << "FEAST finished with warning " << info << ". Please check FEAST output." << std::endl;
346  KRATOS_ERROR_IF(info == 1) << "FEAST finished with warning " << info << ", no eigenvalues could be found in the given search interval." << std::endl;
347  KRATOS_ERROR_IF(info == 7) << "FEAST finished with warning " << info << ", no extremal eigenvalues could be found. Please check FEAST output." << std::endl;
348 
349  // truncate non-converged results
350  tmp_eigenvalues.resize(M, true);
351  tmp_eigenvectors.resize(system_size, M, true);
352 
353  // sort if required
354  if( mParam["sort_eigenvalues"].GetBool() ) {
355  SortingHelper<TScalarOut>(mParam["sort_order"].GetString()).SortEigenvalues(tmp_eigenvalues, tmp_eigenvectors);
356  }
357 
358  // copy eigenvalues to result vector
359  rEigenvalues.swap(tmp_eigenvalues);
360 
361  // copy eigenvectors to result matrix
362  if( rEigenvectors.size1() != tmp_eigenvectors.size1() || rEigenvectors.size2() != tmp_eigenvectors.size2() )
363  rEigenvectors.resize(tmp_eigenvectors.size1(), tmp_eigenvectors.size2(), false);
364 
365  noalias(rEigenvectors) = tmp_eigenvectors;
366 
367  // the eigensolver strategy expects an eigenvector matrix of shape [n_eigenvalues, n_dofs], so FEAST's eigenvector matrix has to be transposed
368  rEigenvectors = trans(rEigenvectors);
369  }
373  void PrintInfo(std::ostream &rOStream) const override
374  {
375  rOStream << "FEASTEigensystemSolver";
376  }
377 
381  void PrintData(std::ostream &rOStream) const override
382  {
383  }
384 
385  private:
386 
387  typedef void (feast_ptr)(char*, int*, double*, int*, int*, double*, int*, int*, int*, double*, int*, double*, double*, int*, double*, double*, int*, double*, int*);
388 
402  template<typename TScalar, typename std::enable_if<std::is_same<double, TScalar>::value, int>::type = 0>
403  std::function<feast_ptr> CreateFeast(bool symmetric)
404  {
405  using namespace std::placeholders;
406 
407  if( symmetric ) {
408  return std::bind(dfeast_scsrgv, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19);
409  } else {
410  return std::bind(dfeast_gcsrgv, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19);
411  }
412  }
413 
414  template<typename TScalar, typename std::enable_if<std::is_same<std::complex<double>, TScalar>::value, int>::type = 0>
415  std::function<feast_ptr> CreateFeast(bool symmetric)
416  {
417  using namespace std::placeholders;
418 
419  if( symmetric ) {
420  return std::bind(zfeast_scsrgv, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19);
421  } else {
422  return std::bind(zfeast_gcsrgv, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19);
423  }
424  }
425 
426  template<typename IndexDataType>
427  void CreateFortranIndices(const IndexDataType& rIndexData, std::vector<int>& rFortranIndices)
428  {
429  #pragma omp parallel for
430  for( int i=0; i<static_cast<int>(rFortranIndices.size()); ++i ) {
431  rFortranIndices[i] = static_cast<int>(rIndexData[i]) + 1;
432  }
433  }
434 
435 }; // class FEASTEigensystemSolver
436 
437 
441 template<bool TSymmetric, typename TScalarIn, typename TScalarOut>
442 inline std::istream& operator >>(
443  std::istream& rIStream,
445 {
446  return rIStream;
447 }
448 
452 template<bool TSymmetric, typename TScalarIn, typename TScalarOut>
453 inline std::ostream& operator <<(
454  std::ostream& rOStream,
456 {
457  rThis.PrintInfo(rOStream);
458  rOStream << std::endl;
459  rThis.PrintData(rOStream);
460 
461  return rOStream;
462 }
463 
464 } // namespace Kratos
465 
466 #endif // defined(KRATOS_FEAST_EIGENSYSTEM_SOLVER_H_INCLUDED)
Definition: feast_eigensystem_solver.h:185
KRATOS_CLASS_POINTER_DEFINITION(FEASTEigensystemSolver)
void PrintData(std::ostream &rOStream) const override
Definition: feast_eigensystem_solver.h:381
FEASTEigensystemSolver(Parameters param)
Definition: feast_eigensystem_solver.h:205
TScalarOut ValueTypeOut
Definition: feast_eigensystem_solver.h:203
TDenseSpaceTypeOut::MatrixType DenseMatrixType
Definition: feast_eigensystem_solver.h:197
TSparseSpaceTypeIn::MatrixType SparseMatrixType
Definition: feast_eigensystem_solver.h:193
void PrintInfo(std::ostream &rOStream) const override
Definition: feast_eigensystem_solver.h:373
TScalarIn ValueTypeIn
Definition: feast_eigensystem_solver.h:201
TDenseSpaceTypeOut::VectorType DenseVectorType
Definition: feast_eigensystem_solver.h:195
LinearSolver< TSparseSpaceTypeIn, TDenseSpaceTypeIn > BaseType
Definition: feast_eigensystem_solver.h:191
matrix< TScalarOut, column_major > FEASTMatrixType
Definition: feast_eigensystem_solver.h:199
void Solve(SparseMatrixType &rK, SparseMatrixType &rM, DenseVectorType &rEigenvalues, DenseMatrixType &rEigenvectors) override
Definition: feast_eigensystem_solver.h:263
~FEASTEigensystemSolver() override=default
Base class for all the linear solvers in Kratos.
Definition: linear_solver.h:65
This class provides to Kratos a data structure for I/O based on the standard of JSON.
Definition: kratos_parameters.h:59
double GetDouble() const
This method returns the double contained in the current Parameter.
Definition: kratos_parameters.cpp:657
void AddMissingParameters(const Parameters &rDefaultParameters)
This function is designed to verify that the parameters under testing contain at least all parameters...
Definition: kratos_parameters.cpp:1369
#define KRATOS_ERROR
Definition: exception.h:161
#define KRATOS_ERROR_IF(conditional)
Definition: exception.h:162
#define KRATOS_INFO_IF(label, conditional)
Definition: logger.h:251
#define KRATOS_WARNING_IF(label, conditional)
Definition: logger.h:266
Vector VectorType
Definition: geometrical_transformation_utilities.h:56
Matrix MatrixType
Definition: geometrical_transformation_utilities.h:55
REF: G. R. Cowper, GAUSSIAN QUADRATURE FORMULAS FOR TRIANGLES.
Definition: mesh_condition.cpp:21
array_1d< double, 3 > VectorType
Definition: solid_mechanics_application_variables.cpp:19
std::istream & operator>>(std::istream &rIStream, LinearMasterSlaveConstraint &rThis)
input stream function
T & noalias(T &TheMatrix)
Definition: amatrix_interface.h:484
AMatrix::TransposeMatrix< const T > trans(const T &TheMatrix)
Definition: amatrix_interface.h:486
AMatrix::MatrixColumn< const TExpressionType > column(AMatrix::MatrixExpression< TExpressionType, TCategory > const &TheExpression, std::size_t ColumnIndex)
Definition: amatrix_interface.h:637
std::ostream & operator<<(std::ostream &rOStream, const LinearMasterSlaveConstraint &rThis)
output stream function
Definition: linear_master_slave_constraint.h:432
res
Definition: generate_convection_diffusion_explicit_element.py:211
type
Definition: generate_gid_list_file.py:35
E
Definition: generate_hyper_elastic_simo_taylor_neo_hookean.py:26
residual
Definition: hinsberg_optimization_4.py:433
A
Definition: sensitivityMatrix.py:70
N
Definition: sensitivityMatrix.py:29
B
Definition: sensitivityMatrix.py:76
integer i
Definition: TensorModule.f:17