oomph::ComplexGMRES< MATRIX > Class Template Reference

The GMRES method rewritten for complex matrices. More...

#include <complex_smoother.h>

+ Inheritance diagram for oomph::ComplexGMRES< MATRIX >:

Public Member Functions

 ComplexGMRES ()
 Constructor. More...
 
 ~ComplexGMRES ()
 Empty destructor. More...
 
 ComplexGMRES (const ComplexGMRES &)=delete
 Broken copy constructor. More...
 
void operator= (const ComplexGMRES &)=delete
 Broken assignment operator. More...
 
void disable_resolve ()
 Overload disable resolve so that it cleans up memory too. More...
 
void solve (Problem *const &problem_pt, DoubleVector &result)
 
void solve (DoubleMatrixBase *const &matrix_pt, const Vector< double > &rhs, Vector< double > &result)
 
unsigned iterations () const
 Number of iterations taken. More...
 
void complex_smoother_setup (Vector< CRDoubleMatrix * > helmholtz_matrix_pt)
 Setup: Pass pointer to the matrix and store in cast form. More...
 
void complex_smoother_solve (const Vector< DoubleVector > &rhs, Vector< DoubleVector > &solution)
 
- Public Member Functions inherited from oomph::HelmholtzSmoother
 HelmholtzSmoother ()
 Empty constructor. More...
 
virtual ~HelmholtzSmoother ()
 Virtual empty destructor. More...
 
void complex_matrix_multiplication (Vector< CRDoubleMatrix * > matrices_pt, const Vector< DoubleVector > &x, Vector< DoubleVector > &soln)
 
template<typename MATRIX >
void check_validity_of_solve_helper_inputs (CRDoubleMatrix *const &real_matrix_pt, CRDoubleMatrix *const &imag_matrix_pt, const Vector< DoubleVector > &rhs, Vector< DoubleVector > &solution, const double &n_dof)
 
- Public Member Functions inherited from oomph::IterativeLinearSolver
 IterativeLinearSolver ()
 
 IterativeLinearSolver (const IterativeLinearSolver &)=delete
 Broken copy constructor. More...
 
void operator= (const IterativeLinearSolver &)=delete
 Broken assignment operator. More...
 
virtual ~IterativeLinearSolver ()
 Destructor (empty) More...
 
Preconditioner *& preconditioner_pt ()
 Access function to preconditioner. More...
 
Preconditioner *const & preconditioner_pt () const
 Access function to preconditioner (const version) More...
 
doubletolerance ()
 Access to convergence tolerance. More...
 
unsignedmax_iter ()
 Access to max. number of iterations. More...
 
void enable_doc_convergence_history ()
 Enable documentation of the convergence history. More...
 
void disable_doc_convergence_history ()
 Disable documentation of the convergence history. More...
 
void open_convergence_history_file_stream (const std::string &file_name, const std::string &zone_title="")
 
void close_convergence_history_file_stream ()
 Close convergence history output stream. More...
 
double jacobian_setup_time () const
 
double linear_solver_solution_time () const
 return the time taken to solve the linear system More...
 
virtual double preconditioner_setup_time () const
 returns the the time taken to setup the preconditioner More...
 
void enable_setup_preconditioner_before_solve ()
 Setup the preconditioner before the solve. More...
 
void disable_setup_preconditioner_before_solve ()
 Don't set up the preconditioner before the solve. More...
 
void enable_error_after_max_iter ()
 Throw an error if we don't converge within max_iter. More...
 
void disable_error_after_max_iter ()
 Don't throw an error if we don't converge within max_iter (default). More...
 
void enable_iterative_solver_as_preconditioner ()
 
void disable_iterative_solver_as_preconditioner ()
 
- Public Member Functions inherited from oomph::LinearSolver
 LinearSolver ()
 Empty constructor, initialise the member data. More...
 
 LinearSolver (const LinearSolver &dummy)=delete
 Broken copy constructor. More...
 
void operator= (const LinearSolver &)=delete
 Broken assignment operator. More...
 
virtual ~LinearSolver ()
 Empty virtual destructor. More...
 
void enable_doc_time ()
 Enable documentation of solve times. More...
 
void disable_doc_time ()
 Disable documentation of solve times. More...
 
bool is_doc_time_enabled () const
 Is documentation of solve times enabled? More...
 
bool is_resolve_enabled () const
 Boolean flag indicating if resolves are enabled. More...
 
virtual void enable_resolve ()
 
virtual void solve (DoubleMatrixBase *const &matrix_pt, const DoubleVector &rhs, DoubleVector &result)
 
virtual void solve_transpose (Problem *const &problem_pt, DoubleVector &result)
 
virtual void solve_transpose (DoubleMatrixBase *const &matrix_pt, const DoubleVector &rhs, DoubleVector &result)
 
virtual void solve_transpose (DoubleMatrixBase *const &matrix_pt, const Vector< double > &rhs, Vector< double > &result)
 
virtual void resolve (const DoubleVector &rhs, DoubleVector &result)
 
virtual void resolve_transpose (const DoubleVector &rhs, DoubleVector &result)
 
virtual void enable_computation_of_gradient ()
 
void disable_computation_of_gradient ()
 
void reset_gradient ()
 
void get_gradient (DoubleVector &gradient)
 function to access the gradient, provided it has been computed More...
 
- Public Member Functions inherited from oomph::DistributableLinearAlgebraObject
 DistributableLinearAlgebraObject ()
 Default constructor - create a distribution. More...
 
 DistributableLinearAlgebraObject (const DistributableLinearAlgebraObject &matrix)=delete
 Broken copy constructor. More...
 
void operator= (const DistributableLinearAlgebraObject &)=delete
 Broken assignment operator. More...
 
virtual ~DistributableLinearAlgebraObject ()
 Destructor. More...
 
LinearAlgebraDistributiondistribution_pt () const
 access to the LinearAlgebraDistribution More...
 
unsigned nrow () const
 access function to the number of global rows. More...
 
unsigned nrow_local () const
 access function for the num of local rows on this processor. More...
 
unsigned nrow_local (const unsigned &p) const
 access function for the num of local rows on this processor. More...
 
unsigned first_row () const
 access function for the first row on this processor More...
 
unsigned first_row (const unsigned &p) const
 access function for the first row on this processor More...
 
bool distributed () const
 distribution is serial or distributed More...
 
bool distribution_built () const
 
void build_distribution (const LinearAlgebraDistribution *const dist_pt)
 
void build_distribution (const LinearAlgebraDistribution &dist)
 

Private Member Functions

void clean_up_memory ()
 Cleanup data that's stored for resolve (if any has been stored) More...
 
void complex_solve_helper (const Vector< DoubleVector > &rhs, Vector< DoubleVector > &solution)
 This is where the actual work is done. More...
 
void update (const unsigned &k, const Vector< Vector< std::complex< double >>> &hessenberg, const Vector< std::complex< double >> &s, const Vector< Vector< DoubleVector >> &v, Vector< DoubleVector > &x)
 Helper function to update the result vector. More...
 
void generate_plane_rotation (std::complex< double > &dx, std::complex< double > &dy, std::complex< double > &cs, std::complex< double > &sn)
 
void apply_plane_rotation (std::complex< double > &dx, std::complex< double > &dy, std::complex< double > &cs, std::complex< double > &sn)
 

Private Attributes

unsigned Iterations
 Number of iterations taken. More...
 
Vector< CRDoubleMatrix * > Matrices_storage_pt
 Vector of pointers to the real and imaginary part of the system matrix. More...
 
bool Resolving
 
bool Matrix_can_be_deleted
 

Additional Inherited Members

- Protected Member Functions inherited from oomph::DistributableLinearAlgebraObject
void clear_distribution ()
 
- Protected Attributes inherited from oomph::HelmholtzSmoother
bool Use_as_smoother
 
- Protected Attributes inherited from oomph::IterativeLinearSolver
bool Doc_convergence_history
 
std::ofstream Output_file_stream
 Output file stream for convergence history. More...
 
double Tolerance
 Convergence tolerance. More...
 
unsigned Max_iter
 Maximum number of iterations. More...
 
PreconditionerPreconditioner_pt
 Pointer to the preconditioner. More...
 
double Jacobian_setup_time
 Jacobian setup time. More...
 
double Solution_time
 linear solver solution time More...
 
double Preconditioner_setup_time
 Preconditioner setup time. More...
 
bool Setup_preconditioner_before_solve
 
bool Throw_error_after_max_iter
 
bool Use_iterative_solver_as_preconditioner
 Use the iterative solver as preconditioner. More...
 
bool First_time_solve_when_used_as_preconditioner
 
- Protected Attributes inherited from oomph::LinearSolver
bool Enable_resolve
 
bool Doc_time
 Boolean flag that indicates whether the time taken. More...
 
bool Compute_gradient
 
bool Gradient_has_been_computed
 flag that indicates whether the gradient was computed or not More...
 
DoubleVector Gradient_for_glob_conv_newton_solve
 
- Static Protected Attributes inherited from oomph::IterativeLinearSolver
static IdentityPreconditioner Default_preconditioner
 

Detailed Description

template<typename MATRIX>
class oomph::ComplexGMRES< MATRIX >

The GMRES method rewritten for complex matrices.

Constructor & Destructor Documentation

◆ ComplexGMRES() [1/2]

template<typename MATRIX >
oomph::ComplexGMRES< MATRIX >::ComplexGMRES ( )
inline

Constructor.

859  : Iterations(0),
861  Resolving(false),
863  {
864  } // End of ComplexGMRES constructor
unsigned Iterations
Number of iterations taken.
Definition: complex_smoother.h:1201
bool Resolving
Definition: complex_smoother.h:1208
bool Matrix_can_be_deleted
Definition: complex_smoother.h:1212
Vector< CRDoubleMatrix * > Matrices_storage_pt
Vector of pointers to the real and imaginary part of the system matrix.
Definition: complex_smoother.h:1204

◆ ~ComplexGMRES()

template<typename MATRIX >
oomph::ComplexGMRES< MATRIX >::~ComplexGMRES ( )
inline

Empty destructor.

868  {
869  // Run clean_up_memory
870  clean_up_memory();
871  } // End of ~ComplexGMRES
void clean_up_memory()
Cleanup data that's stored for resolve (if any has been stored)
Definition: complex_smoother.h:999

References oomph::ComplexGMRES< MATRIX >::clean_up_memory().

◆ ComplexGMRES() [2/2]

template<typename MATRIX >
oomph::ComplexGMRES< MATRIX >::ComplexGMRES ( const ComplexGMRES< MATRIX > &  )
delete

Broken copy constructor.

Member Function Documentation

◆ apply_plane_rotation()

template<typename MATRIX >
void oomph::ComplexGMRES< MATRIX >::apply_plane_rotation ( std::complex< double > &  dx,
std::complex< double > &  dy,
std::complex< double > &  cs,
std::complex< double > &  sn 
)
inlineprivate

Helper function: Apply plane rotation. This is done using the update:

\[ \begin{bmatrix} dx \\ dy \end{bmatrix} \leftarrow \begin{bmatrix} \overline{\cos\theta} & \overline{\sin\theta} \\ -\sin\theta & \cos\theta \end{bmatrix} \begin{bmatrix} dx \\ dy \end{bmatrix}. \]

Taken from: Saad Y."Iterative methods for sparse linear systems", p.193.

1189  {
1190  // Calculate the value of dx but don't update it yet
1191  std::complex<double> temp = std::conj(cs) * dx + std::conj(sn) * dy;
1192 
1193  // Set the value of dy
1194  dy = -sn * dx + cs * dy;
1195 
1196  // Set the value of dx using the correct values of dx and dy
1197  dx = temp;
1198  } // End of apply_plane_rotation
AnnoyingScalar conj(const AnnoyingScalar &x)
Definition: AnnoyingScalar.h:133

References conj().

◆ clean_up_memory()

template<typename MATRIX >
void oomph::ComplexGMRES< MATRIX >::clean_up_memory ( )
inlineprivatevirtual

Cleanup data that's stored for resolve (if any has been stored)

Reimplemented from oomph::LinearSolver.

1000  {
1001  // If the real matrix pointer isn't null AND we're allowed to delete
1002  // the matrix which is only when we create the matrix ourselves
1003  if ((Matrices_storage_pt[0] != 0) && (Matrix_can_be_deleted))
1004  {
1005  // Delete the matrix
1006  delete Matrices_storage_pt[0];
1007 
1008  // Assign the associated pointer the value NULL
1009  Matrices_storage_pt[0] = 0;
1010  }
1011 
1012  // If the real matrix pointer isn't null AND we're allowed to delete
1013  // the matrix which is only when we create the matrix ourselves
1014  if ((Matrices_storage_pt[1] != 0) && (Matrix_can_be_deleted))
1015  {
1016  // Delete the matrix
1017  delete Matrices_storage_pt[1];
1018 
1019  // Assign the associated pointer the value NULL
1020  Matrices_storage_pt[1] = 0;
1021  }
1022  } // End of clean_up_memory

References oomph::ComplexGMRES< MATRIX >::Matrices_storage_pt, and oomph::ComplexGMRES< MATRIX >::Matrix_can_be_deleted.

Referenced by oomph::ComplexGMRES< MATRIX >::disable_resolve(), and oomph::ComplexGMRES< MATRIX >::~ComplexGMRES().

◆ complex_smoother_setup()

template<typename MATRIX >
void oomph::ComplexGMRES< MATRIX >::complex_smoother_setup ( Vector< CRDoubleMatrix * >  helmholtz_matrix_pt)
inlinevirtual

Setup: Pass pointer to the matrix and store in cast form.

Implements oomph::HelmholtzSmoother.

926  {
927  // Assume the matrices have been passed in from the outside so we must
928  // not delete it. This is needed to avoid pre- and post-smoothers
929  // deleting the same matrix in the MG solver. If this was originally
930  // set to TRUE then this will be sorted out in the other functions
931  // from which this was called
932  Matrix_can_be_deleted = false;
933 
934 #ifdef PARANOID
935  // PARANOID check - Make sure the input has the right number of matrices
936  if (helmholtz_matrix_pt.size() != 2)
937  {
938  std::ostringstream error_message_stream;
939  error_message_stream << "Can only deal with two matrices. You have "
940  << helmholtz_matrix_pt.size() << " matrices."
941  << std::endl;
942 
943  // Throw an error
944  throw OomphLibError(error_message_stream.str(),
947  }
948 #endif
949 
950  // Resize the storage for the system matrices
951  Matrices_storage_pt.resize(2, 0);
952 
953  // Assign the real matrix pointer
954  Matrices_storage_pt[0] = helmholtz_matrix_pt[0];
955 
956  // Assign the imaginary matrix pointers
957  Matrices_storage_pt[1] = helmholtz_matrix_pt[1];
958 
959 #ifdef PARANOID
960  // PARANOID check - Make sure that the constituent matrices have the same
961  // number of rows
963  {
964  std::ostringstream error_message_stream;
965  error_message_stream << "Incompatible real and imag. matrix sizes.";
966  throw OomphLibError(error_message_stream.str(),
969  }
970  // PARANOID check - Make sure that the constituent matrices have the same
971  // number of columns
972  if (Matrices_storage_pt[0]->ncol() != Matrices_storage_pt[1]->ncol())
973  {
974  std::ostringstream error_message_stream;
975  error_message_stream << "Incompatible real and imag. matrix sizes.";
976  throw OomphLibError(error_message_stream.str(),
979  }
980 #endif
981  } // End of complex_smoother_setup
unsigned nrow() const
access function to the number of global rows.
Definition: linear_algebra_distribution.h:463
#define OOMPH_EXCEPTION_LOCATION
Definition: oomph_definitions.h:61
#define OOMPH_CURRENT_FUNCTION
Definition: oomph_definitions.h:86

References oomph::ComplexGMRES< MATRIX >::Matrices_storage_pt, oomph::ComplexGMRES< MATRIX >::Matrix_can_be_deleted, oomph::DistributableLinearAlgebraObject::nrow(), OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

◆ complex_smoother_solve()

template<typename MATRIX >
void oomph::ComplexGMRES< MATRIX >::complex_smoother_solve ( const Vector< DoubleVector > &  rhs,
Vector< DoubleVector > &  solution 
)
inlinevirtual

The smoother_solve function performs fixed number of iterations on the system A*result=rhs. The number of (smoothing) iterations is the same as the max. number of iterations in the underlying IterativeLinearSolver class.

Implements oomph::HelmholtzSmoother.

989  {
990  // If you use a smoother but you don't want to calculate the residual
991  Use_as_smoother = true;
992 
993  // Use the helper function where the work is actually done
995  } // End of complex_smoother_solve
void complex_solve_helper(const Vector< DoubleVector > &rhs, Vector< DoubleVector > &solution)
This is where the actual work is done.
Definition: complex_smoother.h:1219
bool Use_as_smoother
Definition: complex_smoother.h:176
void solution(const Vector< double > &x, Vector< double > &u)
Definition: two_d_biharmonic.cc:113

References oomph::ComplexGMRES< MATRIX >::complex_solve_helper(), BiharmonicTestFunctions1::solution(), and oomph::HelmholtzSmoother::Use_as_smoother.

◆ complex_solve_helper()

template<typename MATRIX >
void oomph::ComplexGMRES< MATRIX >::complex_solve_helper ( const Vector< DoubleVector > &  rhs,
Vector< DoubleVector > &  solution 
)
private

This is where the actual work is done.

1221  {
1222  // Set the number of dof types (real and imaginary for this solver)
1223  unsigned n_dof_types = 2;
1224 
1225  // Get the number of dofs (note, the total number of dofs in the problem
1226  // is 2*n_row but if the constituent vectors and matrices were stored in
1227  // complex objects there would only be (n_row) rows so we use that)
1228  unsigned n_row = Matrices_storage_pt[0]->nrow();
1229 
1230  // Make sure Max_iter isn't greater than n_dof. The user cannot use this
1231  // many iterations when using Krylov subspace methods
1232  if (Max_iter > n_row)
1233  {
1234  // Create an output string stream
1235  std::ostringstream error_message_stream;
1236 
1237  // Create the error message
1238  error_message_stream << "The maximum number of iterations cannot exceed "
1239  << "the number of rows in the problem."
1240  << "\nMaximum number of iterations: " << Max_iter
1241  << "\nNumber of rows: " << n_row << std::endl;
1242 
1243  // Throw the error message
1244  throw OomphLibError(error_message_stream.str(),
1247  }
1248 
1249  // Loop over the real and imaginary parts
1250  for (unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
1251  {
1252 #ifdef PARANOID
1253  // PARANOID check that if the matrix is distributable then it should not
1254  // be then it should not be distributed
1255  if (dynamic_cast<DistributableLinearAlgebraObject*>(
1256  Matrices_storage_pt[dof_type]) != 0)
1257  {
1258  if (dynamic_cast<DistributableLinearAlgebraObject*>(
1259  Matrices_storage_pt[dof_type])
1260  ->distributed())
1261  {
1262  std::ostringstream error_message_stream;
1263  error_message_stream << "The matrix must not be distributed.";
1264  throw OomphLibError(error_message_stream.str(),
1267  }
1268  }
1269  // PARANOID check that this rhs distribution is setup
1270  if (!rhs[dof_type].built())
1271  {
1272  std::ostringstream error_message_stream;
1273  error_message_stream << "The rhs vector distribution must be setup.";
1274  throw OomphLibError(error_message_stream.str(),
1277  }
1278  // PARANOID check that the rhs has the right number of global rows
1279  if (rhs[dof_type].nrow() != n_row)
1280  {
1281  std::ostringstream error_message_stream;
1282  error_message_stream << "RHS does not have the same dimension as the"
1283  << " linear system";
1284  throw OomphLibError(error_message_stream.str(),
1287  }
1288  // PARANOID check that the rhs is not distributed
1289  if (rhs[dof_type].distribution_pt()->distributed())
1290  {
1291  std::ostringstream error_message_stream;
1292  error_message_stream << "The rhs vector must not be distributed.";
1293  throw OomphLibError(error_message_stream.str(),
1296  }
1297  // PARANOID check that if the result is setup it matches the distribution
1298  // of the rhs
1299  if (solution[dof_type].built())
1300  {
1301  if (!(*rhs[dof_type].distribution_pt() ==
1302  *solution[dof_type].distribution_pt()))
1303  {
1304  std::ostringstream error_message_stream;
1305  error_message_stream << "If the result distribution is setup then it "
1306  << "must be the same as the rhs distribution";
1307  throw OomphLibError(error_message_stream.str(),
1310  }
1311  } // if (solution[dof_type].built())
1312 #endif
1313  // Set up the solution distribution if it's not already distributed
1314  if (!solution[dof_type].built())
1315  {
1316  // Build the distribution
1317  solution[dof_type].build(this->distribution_pt(), 0.0);
1318  }
1319  // Otherwise initialise all entries to zero
1320  else
1321  {
1322  // Initialise the entries in the k-th vector in solution to zero
1323  solution[dof_type].initialise(0.0);
1324  }
1325  } // for (unsigned dof_type=0;dof_type<n_dof_types;dof_type++)
1326 
1327  // Start the solver timer
1328  double t_start = TimingHelpers::timer();
1329 
1330  // Storage for the relative residual
1331  double resid;
1332 
1333  // Initialise vectors (since these are not distributed vectors we template
1334  // one vector by the type std::complex<double> instead of using two vectors,
1335  // each templated by the type double
1336 
1337  // Vector, s, used in the minimisation problem: J(y)=min||s-R_m*y||
1338  // [see Saad Y."Iterative methods for sparse linear systems", p.176.]
1339  Vector<std::complex<double>> s(n_row + 1, std::complex<double>(0.0, 0.0));
1340 
1341  // Vector to store the value of cos(theta) when using the Givens rotation
1342  Vector<std::complex<double>> cs(n_row + 1, std::complex<double>(0.0, 0.0));
1343 
1344  // Vector to store the value of sin(theta) when using the Givens rotation
1345  Vector<std::complex<double>> sn(n_row + 1, std::complex<double>(0.0, 0.0));
1346 
1347  // Create a vector of DoubleVectors (this is a distributed vector so we have
1348  // to create two separate DoubleVector objects to cope with the arithmetic)
1349  Vector<DoubleVector> w(n_dof_types);
1350 
1351  // Build the distribution of both vectors
1352  for (unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
1353  {
1354  // Build the distribution of the k-th constituent vector
1355  w[dof_type].build(this->distribution_pt(), 0.0);
1356  }
1357 
1358  // Create a vector of DoubleVectors to store the RHS of b-Jx=Mr. We assume
1359  // both x=0 and that a preconditioner is not applied by which we deduce b=r
1360  Vector<DoubleVector> r(n_dof_types);
1361 
1362  // Build the distribution of both vectors
1363  for (unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
1364  {
1365  // Build the distribution of the k-th constituent vector
1366  r[dof_type].build(this->distribution_pt(), 0.0);
1367  }
1368 
1369  // Store the value of b (the RHS vector) in r
1370  for (unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
1371  {
1372  // Build the distribution of the k-th constituent vector
1373  r[dof_type] = rhs[dof_type];
1374  }
1375 
1376  // Calculate the norm of the real part of r
1377  double norm_r = r[0].norm();
1378 
1379  // Calculate the norm of the imaginary part of r
1380  double norm_c = r[1].norm();
1381 
1382  // Compute norm(r)
1383  double normb = sqrt(pow(norm_r, 2.0) + pow(norm_c, 2.0));
1384 
1385  // Set the value of beta (the initial residual)
1386  double beta = normb;
1387 
1388  // Compute the initial relative residual. If the entries of the RHS vector
1389  // are all zero then set normb equal to one. This is because we divide the
1390  // value of the norm at later stages by normb and dividing by zero is not
1391  // definied
1392  if (normb == 0.0)
1393  {
1394  // Set the value of normb
1395  normb = 1.0;
1396  }
1397 
1398  // Calculate the ratio between the initial norm and the current norm.
1399  // Since we haven't completed an iteration yet this will simply be one
1400  // unless normb was zero, in which case resid will have value zero
1401  resid = beta / normb;
1402 
1403  // If required, will document convergence history to screen or file (if
1404  // stream open)
1406  {
1407  // If an output file which is open isn't provided then output to screen
1408  if (!Output_file_stream.is_open())
1409  {
1410  // Output the residual value to the screen
1411  oomph_info << 0 << " " << resid << std::endl;
1412  }
1413  // Otherwise, output to file
1414  else
1415  {
1416  // Output the residual value to file
1417  Output_file_stream << 0 << " " << resid << std::endl;
1418  }
1419  } // if (Doc_convergence_history)
1420 
1421  // If the GMRES algorithm converges immediately
1422  if (resid <= Tolerance)
1423  {
1424  // If time documentation is enabled
1425  if (Doc_time)
1426  {
1427  // Notify the user
1428  oomph_info << "GMRES converged immediately. Normalised residual norm: "
1429  << resid << std::endl;
1430  }
1431 
1432  // Finish running the solver
1433  return;
1434  } // if (resid<=Tolerance)
1435 
1436  // Initialise a vector of orthogonal basis vectors
1437  Vector<Vector<DoubleVector>> v;
1438 
1439  // Resize the number of vectors needed
1440  v.resize(n_row + 1);
1441 
1442  // Resize each Vector of DoubleVectors to store the real and imaginary
1443  // part of a given vector
1444  for (unsigned dof_type = 0; dof_type < n_row + 1; dof_type++)
1445  {
1446  // Create two DoubleVector objects in each Vector
1447  v[dof_type].resize(n_dof_types);
1448  }
1449 
1450  // Initialise the upper hessenberg matrix. Since we are not using
1451  // distributed vectors here, the algebra is best done using entries
1452  // of the type std::complex<double>. NOTE: For implementation purposes
1453  // the upper Hessenberg matrix indices are swapped so the matrix is
1454  // effectively transposed
1455  Vector<Vector<std::complex<double>>> hessenberg(n_row + 1);
1456 
1457  // Build the zeroth basis vector
1458  for (unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
1459  {
1460  // Build the k-th part of the zeroth vector. Here k=0 and k=1 correspond
1461  // to the real and imaginary part of the zeroth vector, respectively
1462  v[0][dof_type].build(this->distribution_pt(), 0.0);
1463  }
1464 
1465  // Loop over the real and imaginary parts of v
1466  for (unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
1467  {
1468  // For fast access
1469  double* v0_pt = v[0][dof_type].values_pt();
1470 
1471  // For fast access
1472  const double* r_pt = r[dof_type].values_pt();
1473 
1474  // Set the zeroth basis vector v[0] to r/beta
1475  for (unsigned i = 0; i < n_row; i++)
1476  {
1477  // Assign the i-th entry of the zeroth basis vector
1478  v0_pt[i] = r_pt[i] / beta;
1479  }
1480  } // for (unsigned k=0;k<n_dof_types;k++)
1481 
1482  // Set the first entry in the minimisation problem RHS vector (is meant
1483  // to the vector beta*e_1 initially, where e_1 is the unit vector with
1484  // one in its first entry)
1485  s[0] = beta;
1486 
1487  // Compute the next step of the iterative scheme
1488  for (unsigned j = 0; j < Max_iter; j++)
1489  {
1490  // Resize the next column of the upper hessenberg matrix
1491  hessenberg[j].resize(j + 2, std::complex<double>(0.0, 0.0));
1492 
1493  // Calculate w=J*v_j. Note, we cannot use inbuilt complex matrix algebra
1494  // here as we're using distributed vectors
1496 
1497  // For fast access
1498  double* w_r_pt = w[0].values_pt();
1499 
1500  // For fast access
1501  double* w_c_pt = w[1].values_pt();
1502 
1503  // Loop over all of the entries on and above the principal subdiagonal of
1504  // the Hessenberg matrix in the j-th column (remembering that
1505  // the indices of the upper Hessenberg matrix are swapped for the purpose
1506  // of implementation)
1507  for (unsigned i = 0; i < j + 1; i++)
1508  {
1509  // For fast access
1510  const double* vi_r_pt = v[i][0].values_pt();
1511 
1512  // For fast access
1513  const double* vi_c_pt = v[i][1].values_pt();
1514 
1515  // Loop over the entries of v and w
1516  for (unsigned k = 0; k < n_row; k++)
1517  {
1518  // Store the appropriate entry in v as a complex value
1519  std::complex<double> complex_v(vi_r_pt[k], vi_c_pt[k]);
1520 
1521  // Store the appropriate entry in w as a complex value
1522  std::complex<double> complex_w(w_r_pt[k], w_c_pt[k]);
1523 
1524  // Update the value of H(i,j) noting we're computing a complex
1525  // inner product here
1526  hessenberg[j][i] += std::conj(complex_v) * complex_w;
1527  }
1528 
1529  // Orthonormalise w against all previous orthogonal vectors, v_i by
1530  // looping over its entries and updating them
1531  for (unsigned k = 0; k < n_row; k++)
1532  {
1533  // Update the real part of the k-th entry of w
1534  w_r_pt[k] -= (hessenberg[j][i].real() * vi_r_pt[k] -
1535  hessenberg[j][i].imag() * vi_c_pt[k]);
1536 
1537  // Update the imaginary part of the k-th entry of w
1538  w_c_pt[k] -= (hessenberg[j][i].real() * vi_c_pt[k] +
1539  hessenberg[j][i].imag() * vi_r_pt[k]);
1540  }
1541  } // for (unsigned i=0;i<j+1;i++)
1542 
1543  // Calculate the norm of the real part of w
1544  norm_r = w[0].norm();
1545 
1546  // Calculate the norm of the imaginary part of w
1547  norm_c = w[1].norm();
1548 
1549  // Calculate the norm of the vector w using norm_r and norm_c and assign
1550  // its value to the appropriate entry in the Hessenberg matrix
1551  hessenberg[j][j + 1] = sqrt(pow(norm_r, 2.0) + pow(norm_c, 2.0));
1552 
1553  // Build the real part of the next orthogonal vector
1554  v[j + 1][0].build(this->distribution_pt(), 0.0);
1555 
1556  // Build the imaginary part of the next orthogonal vector
1557  v[j + 1][1].build(this->distribution_pt(), 0.0);
1558 
1559  // Check if the value of hessenberg[j][j+1] is zero. If it
1560  // isn't then we update the next entries in v
1561  if (hessenberg[j][j + 1] != 0.0)
1562  {
1563  // For fast access
1564  double* v_r_pt = v[j + 1][0].values_pt();
1565 
1566  // For fast access
1567  double* v_c_pt = v[j + 1][1].values_pt();
1568 
1569  // For fast access
1570  const double* w_r_pt = w[0].values_pt();
1571 
1572  // For fast access
1573  const double* w_c_pt = w[1].values_pt();
1574 
1575  // Notice, the value of H(j,j+1), as calculated above, is clearly a real
1576  // number. As such, calculating the division
1577  // v_{j+1}=w_{j}/h_{j+1,j},
1578  // here is simple, i.e. we don't need to worry about cross terms in the
1579  // algebra. To avoid computing h_{j+1,j} several times we precompute it
1580  double h_subdiag_val = hessenberg[j][j + 1].real();
1581 
1582  // Loop over the entries of the new orthogonal vector and set its values
1583  for (unsigned k = 0; k < n_row; k++)
1584  {
1585  // The i-th entry of the real component is given by
1586  v_r_pt[k] = w_r_pt[k] / h_subdiag_val;
1587 
1588  // Similarly, the i-th entry of the imaginary component is given by
1589  v_c_pt[k] = w_c_pt[k] / h_subdiag_val;
1590  }
1591  }
1592  // Otherwise, we have to jump to the next part of the algorithm; if
1593  // the value of hessenberg[j][j+1] is zero then the norm of the latest
1594  // orthogonal vector is zero. This is only possible if the entries
1595  // in w are all zero. As a result, the Krylov space of A and r_0 has
1596  // been spanned by the previously calculated orthogonal vectors
1597  else
1598  {
1599  // Book says "Set m=j and jump to step 11" (p.172)...
1600  // Do something here!
1601  oomph_info << "Subdiagonal Hessenberg entry is zero."
1602  << "Do something here..." << std::endl;
1603  } // if (hessenberg[j][j+1]!=0.0)
1604 
1605  // Loop over the entries in the Hessenberg matrix and calculate the
1606  // entries of the Givens rotation matrices
1607  for (unsigned k = 0; k < j; k++)
1608  {
1609  // Apply the plane rotation to all of the previous entries in the
1610  // (j)-th column (remembering the indexing is reversed)
1612  hessenberg[j][k], hessenberg[j][k + 1], cs[k], sn[k]);
1613  }
1614 
1615  // Now calculate the entries of the latest Givens rotation matrix
1617  hessenberg[j][j], hessenberg[j][j + 1], cs[j], sn[j]);
1618 
1619  // Apply the plane rotation using the newly calculated entries
1621  hessenberg[j][j], hessenberg[j][j + 1], cs[j], sn[j]);
1622 
1623  // Apply a plane rotation to the corresponding entry in the vector
1624  // s used in the minimisation problem, J(y)=min||s-R_m*y||
1625  apply_plane_rotation(s[j], s[j + 1], cs[j], sn[j]);
1626 
1627  // Compute current residual using equation (6.42) in Saad Y, "Iterative
1628  // methods for sparse linear systems", p.177.]. Note, since s has complex
1629  // entries we have to use std::abs instead of std::fabs
1630  beta = std::abs(s[j + 1]);
1631 
1632  // Compute the relative residual
1633  resid = beta / normb;
1634 
1635  // If required will document convergence history to screen or file (if
1636  // stream open)
1638  {
1639  // If an output file which is open isn't provided then output to screen
1640  if (!Output_file_stream.is_open())
1641  {
1642  // Output the residual value to the screen
1643  oomph_info << j + 1 << " " << resid << std::endl;
1644  }
1645  // Otherwise, output to file
1646  else
1647  {
1648  // Output the residual value to file
1649  Output_file_stream << j + 1 << " " << resid << std::endl;
1650  }
1651  } // if (Doc_convergence_history)
1652 
1653  // If the required tolerance has been met
1654  if (resid < Tolerance)
1655  {
1656  // Store the number of iterations taken
1657  Iterations = j + 1;
1658 
1659  // Update the result vector using the result, x=x_0+V_m*y (where V_m
1660  // is given by v here)
1661  update(j, hessenberg, s, v, solution);
1662 
1663  // If time documentation was enabled
1664  if (Doc_time)
1665  {
1666  // Output the current normalised residual norm
1667  oomph_info << "\nGMRES converged (1). Normalised residual norm: "
1668  << resid << std::endl;
1669 
1670  // Output the number of iterations it took for convergence
1671  oomph_info << "Number of iterations to convergence: " << j + 1 << "\n"
1672  << std::endl;
1673  }
1674 
1675  // Stop the timer
1676  double t_end = TimingHelpers::timer();
1677 
1678  // Calculate the time taken to calculate the solution
1679  Solution_time = t_end - t_start;
1680 
1681  // If time documentation was enabled
1682  if (Doc_time)
1683  {
1684  // Output the time taken to solve the problem using GMRES
1685  oomph_info << "Time for solve with GMRES [sec]: " << Solution_time
1686  << std::endl;
1687  }
1688 
1689  // As we've met the tolerance for the solver and everything that should
1690  // be documented, has been, finish using the solver
1691  return;
1692  } // if (resid<Tolerance)
1693  } // for (unsigned j=0;j<Max_iter;j++)
1694 
1695  // Store the number of iterations taken
1696  Iterations = Max_iter;
1697 
1698  // Only update if we actually did something
1699  if (Max_iter > 0)
1700  {
1701  // Update the result vector using the result, x=x_0+V_m*y (where V_m
1702  // is given by v here)
1703  update(Max_iter - 1, hessenberg, s, v, solution);
1704  }
1705 
1706  // Stop the timer
1707  double t_end = TimingHelpers::timer();
1708 
1709  // Calculate the time taken to calculate the solution
1710  Solution_time = t_end - t_start;
1711 
1712  // If time documentation was enabled
1713  if (Doc_time)
1714  {
1715  // Output the time taken to solve the problem using GMRES
1716  oomph_info << "Time for solve with GMRES [sec]: " << Solution_time
1717  << std::endl;
1718  }
1719 
1720  // Finish using the solver
1721  return;
1722  } // End of complex_solve_helper
AnnoyingScalar abs(const AnnoyingScalar &x)
Definition: AnnoyingScalar.h:135
AnnoyingScalar sqrt(const AnnoyingScalar &x)
Definition: AnnoyingScalar.h:134
Array< int, Dynamic, 1 > v
Definition: Array_initializer_list_vector_cxx11.cpp:1
int i
Definition: BiCGSTAB_step_by_step.cpp:9
RowVector3d w
Definition: Matrix_resize_int.cpp:3
void generate_plane_rotation(std::complex< double > &dx, std::complex< double > &dy, std::complex< double > &cs, std::complex< double > &sn)
Definition: complex_smoother.h:1106
void update(const unsigned &k, const Vector< Vector< std::complex< double >>> &hessenberg, const Vector< std::complex< double >> &s, const Vector< Vector< DoubleVector >> &v, Vector< DoubleVector > &x)
Helper function to update the result vector.
Definition: complex_smoother.h:1029
void apply_plane_rotation(std::complex< double > &dx, std::complex< double > &dy, std::complex< double > &cs, std::complex< double > &sn)
Definition: complex_smoother.h:1185
bool distributed() const
distribution is serial or distributed
Definition: linear_algebra_distribution.h:493
LinearAlgebraDistribution * distribution_pt() const
access to the LinearAlgebraDistribution
Definition: linear_algebra_distribution.h:457
DistributableLinearAlgebraObject()
Default constructor - create a distribution.
Definition: linear_algebra_distribution.h:438
void complex_matrix_multiplication(Vector< CRDoubleMatrix * > matrices_pt, const Vector< DoubleVector > &x, Vector< DoubleVector > &soln)
Definition: complex_smoother.h:69
double Tolerance
Convergence tolerance.
Definition: iterative_linear_solver.h:239
unsigned Max_iter
Maximum number of iterations.
Definition: iterative_linear_solver.h:242
double Solution_time
linear solver solution time
Definition: iterative_linear_solver.h:251
std::ofstream Output_file_stream
Output file stream for convergence history.
Definition: iterative_linear_solver.h:231
bool Doc_convergence_history
Definition: iterative_linear_solver.h:228
bool Doc_time
Boolean flag that indicates whether the time taken.
Definition: linear_solver.h:77
void hessenberg(int size=Size)
Definition: hessenberg.cpp:15
RealScalar s
Definition: level1_cplx_impl.h:130
Scalar beta
Definition: level2_cplx_impl.h:36
char char char int int * k
Definition: level2_impl.h:374
EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bfloat16 pow(const bfloat16 &a, const bfloat16 &b)
Definition: BFloat16.h:625
r
Definition: UniformPSDSelfTest.py:20
double timer()
returns the time in seconds after some point in past
Definition: oomph_utilities.cc:1295
OomphInfo oomph_info
Definition: oomph_definitions.cc:319
std::ptrdiff_t j
Definition: tut_arithmetic_redux_minmax.cpp:2

References abs(), beta, conj(), hessenberg(), i, Global_Variables::Iterations, j, k, oomph::BlackBoxFDNewtonSolver::Max_iter, OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, oomph::oomph_info, Eigen::bfloat16_impl::pow(), UniformPSDSelfTest::r, s, BiharmonicTestFunctions1::solution(), sqrt(), oomph::TimingHelpers::timer(), v, and w.

Referenced by oomph::ComplexGMRES< MATRIX >::complex_smoother_solve().

◆ disable_resolve()

template<typename MATRIX >
void oomph::ComplexGMRES< MATRIX >::disable_resolve ( )
inlinevirtual

Overload disable resolve so that it cleans up memory too.

Reimplemented from oomph::LinearSolver.

881  {
882  // Disable resolve using the LinearSolver function
884 
885  // Clean up anything kept in memory
886  clean_up_memory();
887  } // End of disable resolve
virtual void disable_resolve()
Definition: linear_solver.h:144

References oomph::ComplexGMRES< MATRIX >::clean_up_memory(), and oomph::LinearSolver::disable_resolve().

◆ generate_plane_rotation()

template<typename MATRIX >
void oomph::ComplexGMRES< MATRIX >::generate_plane_rotation ( std::complex< double > &  dx,
std::complex< double > &  dy,
std::complex< double > &  cs,
std::complex< double > &  sn 
)
inlineprivate

Helper function: Generate a plane rotation. This is done by finding the value of \( \cos(\theta) \) (i.e. cs) and the value of \( \sin(\theta) \) (i.e. sn) such that:

\[ \begin{bmatrix} \overline{\cos\theta} & \overline{\sin\theta} \cr -\sin\theta & \cos\theta \end{bmatrix} \begin{bmatrix} dx \\ dy \end{bmatrix} = \begin{bmatrix} r \\ 0 \end{bmatrix}, \]

where \( r=\sqrt{pow(|dx|,2)+pow(|dy|,2)} \). The values of a and b are given by: The values of dx and dy are given by:

\[ \cos\theta&=\dfrac{dx}{\sqrt{|dx|^2+|dy|^2}}, \]

and

\[ \sin\theta&=\dfrac{dy}{\sqrt{|dx|^2+|dy|^2}}. \]

Taken from: Saad Y."Iterative methods for sparse linear systems", p.193. We also check to see that sn is always a real (nonnegative) number. See pp.193-194 for an explanation.

1110  {
1111  // If dy=0 then we do not need to apply a rotation
1112  if (dy == 0.0)
1113  {
1114  // Using theta=0 gives cos(theta)=1
1115  cs = 1.0;
1116 
1117  // Using theta=0 gives sin(theta)=0
1118  sn = 0.0;
1119  }
1120  // If dx or dy is large using the original form of calculting cs and sn is
1121  // naive since this may overflow or underflow so instead we calculate
1122  // r=sqrt(pow(|dx|,2)+pow(|dy|,2)) using r=|dy|sqrt(1+pow(|dx|/|dy|,2)) if
1123  // |dy|>|dx| [see <A
1124  // HREF=https://en.wikipedia.org/wiki/Hypot">Hypot</A>.].
1125  else if (std::abs(dy) > std::abs(dx))
1126  {
1127  // Since |dy|>|dx| calculate the ratio |dx|/|dy|
1128  std::complex<double> temp = dx / dy;
1129 
1130  // Calculate the value of sin(theta) using:
1131  // sin(theta)=dy/sqrt(pow(|dx|,2)+pow(|dy|,2))
1132  // =(dy/|dy|)/sqrt(1+pow(|dx|/|dy|,2)).
1133  sn = (dy / std::abs(dy)) / sqrt(1.0 + pow(std::abs(temp), 2.0));
1134 
1135  // Calculate the value of cos(theta) using:
1136  // cos(theta)=dx/sqrt(pow(|dy|,2)+pow(|dx|,2))
1137  // =(dx/|dy|)/sqrt(1+pow(|dx|/|dy|,2))
1138  // =(dx/dy)*sin(theta).
1139  cs = temp * sn;
1140  }
1141  // Otherwise, we have |dx|>=|dy| so to, again, avoid overflow or underflow
1142  // calculate the values of cs and sn using the method above
1143  else
1144  {
1145  // Since |dx|>=|dy| calculate the ratio dy/dx
1146  std::complex<double> temp = dy / dx;
1147 
1148  // Calculate the value of cos(theta) using:
1149  // cos(theta)=dx/sqrt(pow(|dx|,2)+pow(|dy|,2))
1150  // =(dx/|dx|)/sqrt(1+pow(|dy|/|dx|,2)).
1151  cs = (dx / std::abs(dx)) / sqrt(1.0 + pow(std::abs(temp), 2.0));
1152 
1153  // Calculate the value of sin(theta) using:
1154  // sin(theta)=dy/sqrt(pow(|dx|,2)+pow(|dy|,2))
1155  // =(dy/|dx|)/sqrt(1+pow(|dy|/|dx|,2))
1156  // =(dy/dx)*cos(theta).
1157  sn = temp * cs;
1158  }
1159 
1160  // Set the tolerance for sin(theta)
1161  double tolerance = 1.0e-15;
1162 
1163  // Make sure sn is real and nonnegative (it should be!)
1164  if ((std::fabs(sn.imag()) > tolerance) || (sn.real() < 0))
1165  {
1166  // Create an output stream
1167  std::ostringstream error_message_stream;
1168 
1169  // Create an error message
1170  error_message_stream << "The value of sin(theta) is not real "
1171  << "and/or nonnegative. Value is: " << sn
1172  << std::endl;
1173 
1174  // Throw an error
1175  throw OomphLibError(error_message_stream.str(),
1178  }
1179  } // End of generate_plane_rotation
double & tolerance()
Access to convergence tolerance.
Definition: iterative_linear_solver.h:107
Real fabs(const Real &a)
Definition: boostmultiprec.cpp:117

References abs(), boost::multiprecision::fabs(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, Eigen::bfloat16_impl::pow(), sqrt(), and oomph::IterativeLinearSolver::tolerance().

◆ iterations()

template<typename MATRIX >
unsigned oomph::ComplexGMRES< MATRIX >::iterations ( ) const
inlinevirtual

Number of iterations taken.

Implements oomph::IterativeLinearSolver.

919  {
920  // Return the number of iterations used
921  return Iterations;
922  } // End of iterations

References oomph::ComplexGMRES< MATRIX >::Iterations.

◆ operator=()

template<typename MATRIX >
void oomph::ComplexGMRES< MATRIX >::operator= ( const ComplexGMRES< MATRIX > &  )
delete

Broken assignment operator.

◆ solve() [1/2]

template<typename MATRIX >
void oomph::ComplexGMRES< MATRIX >::solve ( DoubleMatrixBase *const &  matrix_pt,
const Vector< double > &  rhs,
Vector< double > &  result 
)
inlinevirtual

Linear-algebra-type solver: Takes pointer to a matrix and rhs vector and returns the solution of the linear system Call the broken base-class version. If you want this, please implement it

Reimplemented from oomph::LinearSolver.

913  {
914  LinearSolver::solve(matrix_pt, rhs, result);
915  } // End of solve
virtual void solve(Problem *const &problem_pt, DoubleVector &result)=0

References oomph::LinearSolver::solve().

◆ solve() [2/2]

template<typename MATRIX >
void oomph::ComplexGMRES< MATRIX >::solve ( Problem *const &  problem_pt,
DoubleVector result 
)
inlinevirtual

Solver: Takes pointer to problem and returns the results vector which contains the solution of the linear system defined by the problem's fully assembled Jacobian and residual vector.

Implements oomph::LinearSolver.

893  {
894  // Write the error message into a string
895  std::string error_message = "Solve function for class\n\n";
896  error_message += "ComplexGMRES\n\n";
897  error_message += "is deliberately broken to avoid the accidental \n";
898  error_message += "use of the inappropriate C++ default. If you \n";
899  error_message += "really need one for this class, write it yourself...\n";
900 
901  // Throw an error
902  throw OomphLibError(
904  } // End of solve
std::string string(const unsigned &i)
Definition: oomph_definitions.cc:286

References OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, and oomph::Global_string_for_annotation::string().

◆ update()

template<typename MATRIX >
void oomph::ComplexGMRES< MATRIX >::update ( const unsigned k,
const Vector< Vector< std::complex< double >>> &  hessenberg,
const Vector< std::complex< double >> &  s,
const Vector< Vector< DoubleVector >> &  v,
Vector< DoubleVector > &  x 
)
inlineprivate

Helper function to update the result vector.

1034  {
1035  // Make a local copy of s
1036  Vector<std::complex<double>> y(s);
1037 
1038  //-----------------------------------------------------------------
1039  // The Hessenberg matrix should be an upper triangular matrix at
1040  // this point (although from its storage it would appear to be a
1041  // lower triangular matrix since the indexing has been reversed)
1042  // so finding the minimiser of J(y)=min||s-R_m*y|| where R_m is
1043  // the matrix R in the QR factorisation of the Hessenberg matrix.
1044  // Therefore, to obtain y we simply need to use a backwards
1045  // substitution. Note: The implementation here may appear to be
1046  // somewhat confusing as the indexing in the Hessenberg matrix is
1047  // reversed. This implementation of a backwards substitution does
1048  // not run along the columns of the triangular matrix but rather
1049  // up the rows.
1050  //-----------------------------------------------------------------
1051  // The outer loop is a loop over the columns of the Hessenberg matrix
1052  // since the indexing is reversed
1053  for (int i = int(k); i >= 0; i--)
1054  {
1055  // Divide the i-th entry of y by the i-th diagonal entry of H
1056  y[i] /= hessenberg[i][i];
1057 
1058  // The inner loop is a loop over the rows of the Hessenberg matrix
1059  for (int j = i - 1; j >= 0; j--)
1060  {
1061  // Update the j-th entry of y
1062  y[j] -= hessenberg[i][j] * y[i];
1063  }
1064  } // for (int i=int(k);i>=0;i--)
1065 
1066  // Calculate the number of entries in x (simply use the real part as
1067  // both the real and imaginary part should have the same length)
1068  unsigned n_row = x[0].nrow();
1069 
1070  // We assume here that the vector x (which is passed in) is actually x_0
1071  // so we simply need to update its entries to calculate the solution, x
1072  // which is given by x=x_0+Vy.
1073  for (unsigned j = 0; j <= k; j++)
1074  {
1075  // For fast access (real part)
1076  const double* vj_r_pt = v[j][0].values_pt();
1077 
1078  // For fast access (imaginary part)
1079  const double* vj_c_pt = v[j][1].values_pt();
1080 
1081  // Loop over the entries in x and update them
1082  for (unsigned i = 0; i < n_row; i++)
1083  {
1084  // Update the real part of the i-th entry in x
1085  x[0][i] += (vj_r_pt[i] * y[j].real()) - (vj_c_pt[i] * y[j].imag());
1086 
1087  // Update the imaginary part of the i-th entry in x
1088  x[1][i] += (vj_c_pt[i] * y[j].real()) + (vj_r_pt[i] * y[j].imag());
1089  }
1090  } // for (unsigned j=0;j<=k;j++)
1091  } // End of update
AnnoyingScalar imag(const AnnoyingScalar &)
Definition: AnnoyingScalar.h:132
Scalar * y
Definition: level1_cplx_impl.h:128
list x
Definition: plotDoE.py:28

References hessenberg(), i, imag(), j, k, s, v, plotDoE::x, and y.

Referenced by smc.smc::recursiveBayesian().

Member Data Documentation

◆ Iterations

template<typename MATRIX >
unsigned oomph::ComplexGMRES< MATRIX >::Iterations
private

Number of iterations taken.

Referenced by oomph::ComplexGMRES< MATRIX >::iterations().

◆ Matrices_storage_pt

template<typename MATRIX >
Vector<CRDoubleMatrix*> oomph::ComplexGMRES< MATRIX >::Matrices_storage_pt
private

Vector of pointers to the real and imaginary part of the system matrix.

Referenced by oomph::ComplexGMRES< MATRIX >::clean_up_memory(), and oomph::ComplexGMRES< MATRIX >::complex_smoother_setup().

◆ Matrix_can_be_deleted

template<typename MATRIX >
bool oomph::ComplexGMRES< MATRIX >::Matrix_can_be_deleted
private

Boolean flag to indicate if the real and imaginary system matrices can be deleted

Referenced by oomph::ComplexGMRES< MATRIX >::clean_up_memory(), and oomph::ComplexGMRES< MATRIX >::complex_smoother_setup().

◆ Resolving

template<typename MATRIX >
bool oomph::ComplexGMRES< MATRIX >::Resolving
private

Boolean flag to indicate if the solve is done in re-solve mode, bypassing setup of matrix and preconditioner


The documentation for this class was generated from the following file: