oomph::HelmholtzGMRESMG< MATRIX > Class Template Reference

The GMRES method for the Helmholtz solver. More...

#include <complex_smoother.h>

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

Public Member Functions

 HelmholtzGMRESMG ()
 Constructor. More...
 
virtual ~HelmholtzGMRESMG ()
 Destructor (cleanup storage) More...
 
 HelmholtzGMRESMG (const HelmholtzGMRESMG &)=delete
 Broken copy constructor. More...
 
void operator= (const HelmholtzGMRESMG &)=delete
 Broken assignment operator. More...
 
void disable_resolve ()
 Overload disable resolve so that it cleans up memory too. More...
 
void preconditioner_solve (const DoubleVector &r, DoubleVector &z)
 
void setup ()
 
void solve (Problem *const &problem_pt, DoubleVector &result)
 
void solve (DoubleMatrixBase *const &matrix_pt, const DoubleVector &rhs, DoubleVector &solution)
 
void solve (DoubleMatrixBase *const &matrix_pt, const Vector< double > &rhs, Vector< double > &result)
 
void resolve (const DoubleVector &rhs, DoubleVector &result)
 
unsigned iterations () const
 Number of iterations taken. More...
 
void set_preconditioner_LHS ()
 Set left preconditioning (the default) More...
 
void set_preconditioner_RHS ()
 Enable right preconditioning. More...
 
- 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_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_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)
 
- Public Member Functions inherited from oomph::BlockPreconditioner< MATRIX >
 BlockPreconditioner ()
 Constructor. More...
 
virtual ~BlockPreconditioner ()
 Destructor. More...
 
 BlockPreconditioner (const BlockPreconditioner &)=delete
 Broken copy constructor. More...
 
void operator= (const BlockPreconditioner &)=delete
 Broken assignment operator. More...
 
MATRIX * matrix_pt () const
 
void turn_on_recursive_debug_flag ()
 
void turn_off_recursive_debug_flag ()
 
void turn_on_debug_flag ()
 Toggles on the debug flag. More...
 
void turn_off_debug_flag ()
 Toggles off the debug flag. More...
 
void turn_into_subsidiary_block_preconditioner (BlockPreconditioner< MATRIX > *master_block_prec_pt, const Vector< unsigned > &doftype_in_master_preconditioner_coarse)
 
void turn_into_subsidiary_block_preconditioner (BlockPreconditioner< MATRIX > *master_block_prec_pt, const Vector< unsigned > &doftype_in_master_preconditioner_coarse, const Vector< Vector< unsigned >> &doftype_coarsen_map_coarse)
 
virtual void block_setup ()
 
void block_setup (const Vector< unsigned > &dof_to_block_map)
 
void get_block (const unsigned &i, const unsigned &j, MATRIX &output_matrix, const bool &ignore_replacement_block=false) const
 
MATRIX get_block (const unsigned &i, const unsigned &j, const bool &ignore_replacement_block=false) const
 
void set_master_matrix_pt (MATRIX *in_matrix_pt)
 Set the matrix_pt in the upper-most master preconditioner. More...
 
void get_block_other_matrix (const unsigned &i, const unsigned &j, MATRIX *in_matrix_pt, MATRIX &output_matrix)
 
void get_blocks (DenseMatrix< bool > &required_blocks, DenseMatrix< MATRIX * > &block_matrix_pt) const
 
void get_dof_level_block (const unsigned &i, const unsigned &j, MATRIX &output_block, const bool &ignore_replacement_block=false) const
 
MATRIX get_concatenated_block (const VectorMatrix< BlockSelector > &selected_block)
 
void get_concatenated_block_vector (const Vector< unsigned > &block_vec_number, const DoubleVector &v, DoubleVector &b)
 
void return_concatenated_block_vector (const Vector< unsigned > &block_vec_number, const DoubleVector &b, DoubleVector &v) const
 Takes concatenated block ordered vector, b, and copies its. More...
 
void get_block_vectors (const Vector< unsigned > &block_vec_number, const DoubleVector &v, Vector< DoubleVector > &s) const
 
void get_block_vectors (const DoubleVector &v, Vector< DoubleVector > &s) const
 
void return_block_vectors (const Vector< unsigned > &block_vec_number, const Vector< DoubleVector > &s, DoubleVector &v) const
 
void return_block_vectors (const Vector< DoubleVector > &s, DoubleVector &v) const
 
void get_block_vector (const unsigned &n, const DoubleVector &v, DoubleVector &b) const
 
void return_block_vector (const unsigned &n, const DoubleVector &b, DoubleVector &v) const
 
void get_block_ordered_preconditioner_vector (const DoubleVector &v, DoubleVector &w)
 
void return_block_ordered_preconditioner_vector (const DoubleVector &w, DoubleVector &v) const
 
unsigned nblock_types () const
 Return the number of block types. More...
 
unsigned ndof_types () const
 Return the total number of DOF types. More...
 
const Meshmesh_pt (const unsigned &i) const
 
unsigned nmesh () const
 
int block_number (const unsigned &i_dof) const
 Return the block number corresponding to a global index i_dof. More...
 
int index_in_block (const unsigned &i_dof) const
 
const LinearAlgebraDistributionblock_distribution_pt (const unsigned &b) const
 Access function to the block distributions (const version). More...
 
LinearAlgebraDistributionblock_distribution_pt (const unsigned b)
 Access function to the block distributions (non-const version). More...
 
LinearAlgebraDistributiondof_block_distribution_pt (const unsigned &b)
 Access function to the dof-level block distributions. More...
 
const LinearAlgebraDistributionmaster_distribution_pt () const
 
unsigned ndof_types_in_mesh (const unsigned &i) const
 
bool is_subsidiary_block_preconditioner () const
 
bool is_master_block_preconditioner () const
 
void set_block_output_to_files (const std::string &basefilename)
 
void disable_block_output_to_files ()
 Turn off output of blocks (by clearing the basefilename string). More...
 
bool block_output_on () const
 Test if output of blocks is on or not. More...
 
void output_blocks_to_files (const std::string &basefilename, const unsigned &precision=8) const
 
void post_block_matrix_assembly_partial_clear ()
 
BlockPreconditioner< MATRIX > * master_block_preconditioner_pt () const
 Access function to the master block preconditioner pt. More...
 
void clear_block_preconditioner_base ()
 
void document ()
 
Vector< Vector< unsigned > > doftype_coarsen_map_fine () const
 
Vector< unsignedget_fine_grain_dof_types_in (const unsigned &i) const
 
unsigned nfine_grain_dof_types_in (const unsigned &i) const
 
MapMatrix< unsigned, CRDoubleMatrix * > replacement_dof_block_pt () const
 Access function to the replaced dof-level blocks. More...
 
void setup_matrix_vector_product (MatrixVectorProduct *matvec_prod_pt, CRDoubleMatrix *block_pt, const Vector< unsigned > &block_col_indices)
 
void setup_matrix_vector_product (MatrixVectorProduct *matvec_prod_pt, CRDoubleMatrix *block_pt, const unsigned &block_col_index)
 
void internal_get_block_ordered_preconditioner_vector (const DoubleVector &v, DoubleVector &w) const
 
void internal_return_block_ordered_preconditioner_vector (const DoubleVector &w, DoubleVector &v) const
 
unsigned internal_nblock_types () const
 
unsigned internal_ndof_types () const
 
void internal_return_block_vector (const unsigned &n, const DoubleVector &b, DoubleVector &v) const
 
void internal_get_block_vector (const unsigned &n, const DoubleVector &v, DoubleVector &b) const
 
void internal_get_block_vectors (const Vector< unsigned > &block_vec_number, const DoubleVector &v, Vector< DoubleVector > &s) const
 
void internal_get_block_vectors (const DoubleVector &v, Vector< DoubleVector > &s) const
 
void internal_return_block_vectors (const Vector< unsigned > &block_vec_number, const Vector< DoubleVector > &s, DoubleVector &v) const
 
void internal_return_block_vectors (const Vector< DoubleVector > &s, DoubleVector &v) const
 
void internal_get_block (const unsigned &i, const unsigned &j, MATRIX &output_block) const
 
int internal_block_number (const unsigned &i_dof) const
 
int internal_index_in_block (const unsigned &i_dof) const
 
const LinearAlgebraDistributioninternal_block_distribution_pt (const unsigned &b) const
 Access function to the internal block distributions. More...
 
void insert_auxiliary_block_distribution (const Vector< unsigned > &block_vec_number, LinearAlgebraDistribution *dist_pt)
 
void block_matrix_test (const unsigned &i, const unsigned &j, const MATRIX *block_matrix_pt) const
 
template<typename myType >
int get_index_of_value (const Vector< myType > &vec, const myType val, const bool sorted=false) const
 
void internal_get_block (const unsigned &block_i, const unsigned &block_j, CRDoubleMatrix &output_block) const
 
void get_dof_level_block (const unsigned &block_i, const unsigned &block_j, CRDoubleMatrix &output_block, const bool &ignore_replacement_block) const
 
- Public Member Functions inherited from oomph::Preconditioner
 Preconditioner ()
 Constructor. More...
 
 Preconditioner (const Preconditioner &)=delete
 Broken copy constructor. More...
 
void operator= (const Preconditioner &)=delete
 Broken assignment operator. More...
 
virtual ~Preconditioner ()
 Destructor (empty) More...
 
virtual void preconditioner_solve_transpose (const DoubleVector &r, DoubleVector &z)
 
void setup (DoubleMatrixBase *matrix_pt)
 
void setup (const Problem *problem_pt, DoubleMatrixBase *matrix_pt)
 
void enable_silent_preconditioner_setup ()
 Set up the block preconditioner quietly! More...
 
void disable_silent_preconditioner_setup ()
 Be verbose in the block preconditioner setup. More...
 
virtual void set_matrix_pt (DoubleMatrixBase *matrix_pt)
 Set the matrix pointer. More...
 
virtual const OomphCommunicatorcomm_pt () const
 Get function for comm pointer. More...
 
virtual void set_comm_pt (const OomphCommunicator *const comm_pt)
 Set the communicator pointer. More...
 
double setup_time () const
 Returns the time to setup the preconditioner. More...
 
virtual void turn_into_subsidiary_block_preconditioner (BlockPreconditioner< CRDoubleMatrix > *master_block_prec_pt, const Vector< unsigned > &doftype_in_master_preconditioner_coarse)
 
virtual void turn_into_subsidiary_block_preconditioner (BlockPreconditioner< CRDoubleMatrix > *master_block_prec_pt, const Vector< unsigned > &doftype_in_master_preconditioner_coarse, const Vector< Vector< unsigned >> &doftype_coarsen_map_coarse)
 

Protected Member Functions

void solve_helper (DoubleMatrixBase *const &matrix_pt, const DoubleVector &rhs, DoubleVector &solution)
 General interface to solve function. More...
 
void clean_up_memory ()
 Cleanup data that's stored for resolve (if any has been stored) More...
 
void complex_matrix_multiplication (Vector< CRDoubleMatrix * > const matrices_pt, const Vector< DoubleVector > &x, Vector< DoubleVector > &soln)
 
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)
 
- Protected Member Functions inherited from oomph::DistributableLinearAlgebraObject
void clear_distribution ()
 
- Protected Member Functions inherited from oomph::BlockPreconditioner< MATRIX >
void set_nmesh (const unsigned &n)
 
void set_mesh (const unsigned &i, const Mesh *const mesh_pt, const bool &allow_multiple_element_type_in_mesh=false)
 
void set_replacement_dof_block (const unsigned &block_i, const unsigned &block_j, CRDoubleMatrix *replacement_dof_block_pt)
 
bool any_mesh_distributed () const
 
int internal_dof_number (const unsigned &i_dof) const
 
unsigned internal_index_in_dof (const unsigned &i_dof) const
 
unsigned internal_block_dimension (const unsigned &b) const
 
unsigned internal_dof_block_dimension (const unsigned &i) const
 
unsigned master_nrow () const
 
unsigned internal_master_dof_number (const unsigned &b) const
 
const LinearAlgebraDistributioninternal_preconditioner_matrix_distribution_pt () const
 
const LinearAlgebraDistributionpreconditioner_matrix_distribution_pt () const
 

Protected 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
 
bool Preconditioner_LHS
 
- 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
 
- Protected Attributes inherited from oomph::BlockPreconditioner< MATRIX >
MapMatrix< unsigned, CRDoubleMatrix * > Replacement_dof_block_pt
 The replacement dof-level blocks. More...
 
Vector< LinearAlgebraDistribution * > Block_distribution_pt
 The distribution for the blocks. More...
 
Vector< Vector< unsigned > > Block_to_dof_map_coarse
 
Vector< Vector< unsigned > > Block_to_dof_map_fine
 Mapping for the block types to the most fine grain dof types. More...
 
Vector< Vector< unsigned > > Doftype_coarsen_map_coarse
 
Vector< Vector< unsigned > > Doftype_coarsen_map_fine
 
Vector< LinearAlgebraDistribution * > Internal_block_distribution_pt
 Storage for the default distribution for each internal block. More...
 
Vector< LinearAlgebraDistribution * > Dof_block_distribution_pt
 
Vector< unsignedAllow_multiple_element_type_in_mesh
 
Vector< const Mesh * > Mesh_pt
 
Vector< unsignedNdof_types_in_mesh
 
unsigned Internal_nblock_types
 
unsigned Internal_ndof_types
 
- Protected Attributes inherited from oomph::Preconditioner
bool Silent_preconditioner_setup
 Boolean to indicate whether or not the build should be done silently. More...
 
std::ostream * Stream_pt
 Pointer to the output stream – defaults to std::cout. More...
 

Additional Inherited Members

- Static Protected Attributes inherited from oomph::IterativeLinearSolver
static IdentityPreconditioner Default_preconditioner
 

Detailed Description

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

The GMRES method for the Helmholtz solver.

//////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////

Constructor & Destructor Documentation

◆ HelmholtzGMRESMG() [1/2]

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

Constructor.

1740  : BlockPreconditioner<CRDoubleMatrix>(),
1741  Iterations(0),
1742  Resolving(false),
1743  Matrix_can_be_deleted(true)
1744  {
1745  Preconditioner_LHS = true;
1746  }
bool Matrix_can_be_deleted
Definition: complex_smoother.h:2433
bool Preconditioner_LHS
Definition: complex_smoother.h:2437
bool Resolving
Definition: complex_smoother.h:2429
unsigned Iterations
Number of iterations taken.
Definition: complex_smoother.h:2422

References oomph::HelmholtzGMRESMG< MATRIX >::Preconditioner_LHS.

◆ ~HelmholtzGMRESMG()

template<typename MATRIX >
virtual oomph::HelmholtzGMRESMG< MATRIX >::~HelmholtzGMRESMG ( )
inlinevirtual

Destructor (cleanup storage)

1750  {
1751  clean_up_memory();
1752  }
void clean_up_memory()
Cleanup data that's stored for resolve (if any has been stored)
Definition: complex_smoother.h:2053

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

◆ HelmholtzGMRESMG() [2/2]

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

Broken copy constructor.

Member Function Documentation

◆ apply_plane_rotation()

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

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.

2410  {
2411  // Calculate the value of dx but don't update it yet
2412  std::complex<double> temp = std::conj(cs) * dx + std::conj(sn) * dy;
2413 
2414  // Set the value of dy
2415  dy = -sn * dx + cs * dy;
2416 
2417  // Set the value of dx using the correct values of dx and dy
2418  dx = temp;
2419  } // End of apply_plane_rotation
AnnoyingScalar conj(const AnnoyingScalar &x)
Definition: AnnoyingScalar.h:133

References conj().

◆ clean_up_memory()

template<typename MATRIX >
void oomph::HelmholtzGMRESMG< MATRIX >::clean_up_memory ( )
inlineprotectedvirtual

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

Reimplemented from oomph::LinearSolver.

2054  {
2055  // If the matrix storage has been resized
2056  if (Matrices_storage_pt.size() > 0)
2057  {
2058  // If the real matrix pointer isn't null AND we're allowed to delete
2059  // the matrix which is only when we create the matrix ourselves
2060  if ((Matrices_storage_pt[0] != 0) && (Matrix_can_be_deleted))
2061  {
2062  // Delete the matrix
2063  delete Matrices_storage_pt[0];
2064 
2065  // Assign the associated pointer the value NULL
2066  Matrices_storage_pt[0] = 0;
2067  }
2068 
2069  // If the real matrix pointer isn't null AND we're allowed to delete
2070  // the matrix which is only when we create the matrix ourselves
2071  if ((Matrices_storage_pt[1] != 0) && (Matrix_can_be_deleted))
2072  {
2073  // Delete the matrix
2074  delete Matrices_storage_pt[1];
2075 
2076  // Assign the associated pointer the value NULL
2077  Matrices_storage_pt[1] = 0;
2078  }
2079  }
2080  } // End of clean_up_memory
Vector< CRDoubleMatrix * > Matrices_storage_pt
Vector of pointers to the real and imaginary part of the system matrix.
Definition: complex_smoother.h:2425

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

Referenced by oomph::HelmholtzGMRESMG< MATRIX >::disable_resolve(), oomph::HelmholtzGMRESMG< MATRIX >::solve(), oomph::HelmholtzFGMRESMG< MATRIX >::solve(), oomph::HelmholtzFGMRESMG< MATRIX >::~HelmholtzFGMRESMG(), and oomph::HelmholtzGMRESMG< MATRIX >::~HelmholtzGMRESMG().

◆ complex_matrix_multiplication()

template<typename MATRIX >
void oomph::HelmholtzGMRESMG< MATRIX >::complex_matrix_multiplication ( Vector< CRDoubleMatrix * > const  matrices_pt,
const Vector< DoubleVector > &  x,
Vector< DoubleVector > &  soln 
)
inlineprotected

Helper function to calculate a complex matrix-vector product. Assumes the matrix has been provided as a Vector of length two; the first entry containing the real part of the system matrix and the second entry containing the imaginary part

2090  {
2091 #ifdef PARANOID
2092  // PARANOID check - Make sure the input matrix has the right size
2093  if (matrices_pt.size() != 2)
2094  {
2095  // Create an output stream
2096  std::ostringstream error_message_stream;
2097 
2098  // Create the error message
2099  error_message_stream << "Can only deal with two matrices. You have "
2100  << matrices_pt.size() << " matrices." << std::endl;
2101 
2102  // Throw an error
2103  throw OomphLibError(error_message_stream.str(),
2106  }
2107  // PARANOID check - Make sure the vector x has the right size
2108  if (x.size() != 2)
2109  {
2110  // Create an output stream
2111  std::ostringstream error_message_stream;
2112 
2113  // Create the error message
2114  error_message_stream
2115  << "Can only deal with two input vectors. You have " << x.size()
2116  << " vectors." << std::endl;
2117 
2118  // Throw an error
2119  throw OomphLibError(error_message_stream.str(),
2122  }
2123  // PARANOID check - Make sure the vector soln has the right size
2124  if (soln.size() != 2)
2125  {
2126  // Create an output stream
2127  std::ostringstream error_message_stream;
2128 
2129  // Create the error message
2130  error_message_stream
2131  << "Can only deal with two output vectors. You have " << soln.size()
2132  << " output vectors." << std::endl;
2133 
2134  // Throw an error
2135  throw OomphLibError(error_message_stream.str(),
2138  }
2139 #endif
2140 
2141  // NOTE: We assume all vectors have been distributed at this point but
2142  // code can be written at a later time to build the vectors if they're
2143  // not already built.
2144 
2145  //-----------------------------------------------------------------------
2146  // Suppose we have a complex matrix, A, and two complex vectors, x and
2147  // soln. We wish to compute the product A*x=soln (note, * does not mean
2148  // we are using complex conjugates here, it is simply used to indicate
2149  // a multiplication). To do this we must make use of the fact that we
2150  // possess the real and imaginary separately. As a result, it is computed
2151  // using:
2152  // soln = A*x,
2153  // = (A_r + i*A_c)*(x_r + i*x_c),
2154  // = [A_r*x_r - A_c*x_c] + i*[A_r*x_c + A_c*x_r],
2155  // ==> real(soln) = A_r*x_r - A_c*x_c,
2156  // & imag(soln) = A_r*x_c + A_c*x_r,
2157  // where the subscripts _r and _c are used to identify the real and
2158  // imaginary part, respectively.
2159  //-----------------------------------------------------------------------
2160 
2161  // Store the value of A_r*x_r in the real part of soln
2162  matrices_pt[0]->multiply(x[0], soln[0]);
2163 
2164  // Store the value of A_r*x_c in the imaginary part of soln
2165  matrices_pt[0]->multiply(x[1], soln[1]);
2166 
2167  // Create a temporary vector
2169 
2170  // Calculate the value of A_c*x_c
2171  matrices_pt[1]->multiply(x[1], temp);
2172 
2173  // Subtract the value of temp from soln[0] to get the real part of soln
2174  soln[0] -= temp;
2175 
2176  // Calculate the value of A_c*x_r
2177  matrices_pt[1]->multiply(x[0], temp);
2178 
2179  // Add the value of temp to soln[1] to get the imaginary part of soln
2180  soln[1] += temp;
2181  } // End of complex_matrix_multiplication
std::vector< double > DoubleVector
loads clump configuration
Definition: ClumpInput.h:26
LinearAlgebraDistribution * distribution_pt() const
access to the LinearAlgebraDistribution
Definition: linear_algebra_distribution.h:457
list x
Definition: plotDoE.py:28
#define OOMPH_EXCEPTION_LOCATION
Definition: oomph_definitions.h:61
#define OOMPH_CURRENT_FUNCTION
Definition: oomph_definitions.h:86

References oomph::DistributableLinearAlgebraObject::distribution_pt(), oomph::HelmholtzGMRESMG< MATRIX >::Matrices_storage_pt, OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, and plotDoE::x.

◆ disable_resolve()

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

Overload disable resolve so that it cleans up memory too.

Reimplemented from oomph::LinearSolver.

1762  {
1764  clean_up_memory();
1765  }
virtual void disable_resolve()
Definition: linear_solver.h:144

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

◆ generate_plane_rotation()

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

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} \\ -\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.

2331  {
2332  // If dy=0 then we do not need to apply a rotation
2333  if (dy == 0.0)
2334  {
2335  // Using theta=0 gives cos(theta)=1
2336  cs = 1.0;
2337 
2338  // Using theta=0 gives sin(theta)=0
2339  sn = 0.0;
2340  }
2341  // If dx or dy is large using the original form of calculting cs and sn is
2342  // naive since this may overflow or underflow so instead we calculate
2343  // r=sqrt(pow(|dx|,2)+pow(|dy|,2)) using r=|dy|sqrt(1+pow(|dx|/|dy|,2)) if
2344  // |dy|>|dx| [see <A
2345  // HREF=https://en.wikipedia.org/wiki/Hypot">Hypot</A>.].
2346  else if (std::abs(dy) > std::abs(dx))
2347  {
2348  // Since |dy|>|dx| calculate the ratio |dx|/|dy|
2349  std::complex<double> temp = dx / dy;
2350 
2351  // Calculate the value of sin(theta) using:
2352  // sin(theta)=dy/sqrt(pow(|dx|,2)+pow(|dy|,2))
2353  // =(dy/|dy|)/sqrt(1+pow(|dx|/|dy|,2)).
2354  sn = (dy / std::abs(dy)) / sqrt(1.0 + pow(std::abs(temp), 2.0));
2355 
2356  // Calculate the value of cos(theta) using:
2357  // cos(theta)=dx/sqrt(pow(|dy|,2)+pow(|dx|,2))
2358  // =(dx/|dy|)/sqrt(1+pow(|dx|/|dy|,2))
2359  // =(dx/dy)*sin(theta).
2360  cs = temp * sn;
2361  }
2362  // Otherwise, we have |dx|>=|dy| so to, again, avoid overflow or underflow
2363  // calculate the values of cs and sn using the method above
2364  else
2365  {
2366  // Since |dx|>=|dy| calculate the ratio dy/dx
2367  std::complex<double> temp = dy / dx;
2368 
2369  // Calculate the value of cos(theta) using:
2370  // cos(theta)=dx/sqrt(pow(|dx|,2)+pow(|dy|,2))
2371  // =(dx/|dx|)/sqrt(1+pow(|dy|/|dx|,2)).
2372  cs = (dx / std::abs(dx)) / sqrt(1.0 + pow(std::abs(temp), 2.0));
2373 
2374  // Calculate the value of sin(theta) using:
2375  // sin(theta)=dy/sqrt(pow(|dx|,2)+pow(|dy|,2))
2376  // =(dy/|dx|)/sqrt(1+pow(|dy|/|dx|,2))
2377  // =(dy/dx)*cos(theta).
2378  sn = temp * cs;
2379  }
2380 
2381  // Set the tolerance for sin(theta)
2382  double tolerance = 1.0e-15;
2383 
2384  // Make sure sn is real and nonnegative (it should be!)
2385  if ((std::fabs(sn.imag()) > tolerance) || (sn.real() < 0))
2386  {
2387  // Create an output stream
2388  std::ostringstream error_message_stream;
2389 
2390  // Create an error message
2391  error_message_stream << "The value of sin(theta) is not real "
2392  << "and/or nonnegative. Value is: " << sn
2393  << std::endl;
2394 
2395  // Throw an error
2396  throw OomphLibError(error_message_stream.str(),
2399  }
2400  } // End of generate_plane_rotation
AnnoyingScalar abs(const AnnoyingScalar &x)
Definition: AnnoyingScalar.h:135
AnnoyingScalar sqrt(const AnnoyingScalar &x)
Definition: AnnoyingScalar.h:134
double & tolerance()
Access to convergence tolerance.
Definition: iterative_linear_solver.h:107
EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bfloat16 pow(const bfloat16 &a, const bfloat16 &b)
Definition: BFloat16.h:625
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::HelmholtzGMRESMG< MATRIX >::iterations ( ) const
inlinevirtual

Number of iterations taken.

Implements oomph::IterativeLinearSolver.

2030  {
2031  return Iterations;
2032  }

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

◆ operator=()

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

Broken assignment operator.

◆ preconditioner_solve()

template<typename MATRIX >
void oomph::HelmholtzGMRESMG< MATRIX >::preconditioner_solve ( const DoubleVector r,
DoubleVector z 
)
inlinevirtual

Implementation of the pure virtual base class function. The function has been broken because this is meant to be used as a linear solver

Implements oomph::Preconditioner.

1771  {
1772  // Open an output stream
1773  std::ostringstream error_message_stream;
1774 
1775  // Create an error message
1776  error_message_stream << "Preconditioner_solve(...) is broken. "
1777  << "HelmholtzGMRESMG is only meant to be used as "
1778  << "a linear solver.\n";
1779 
1780  // Throw the error message
1781  throw OomphLibError(error_message_stream.str(),
1784  }

References OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

◆ resolve()

template<typename MATRIX >
void oomph::HelmholtzGMRESMG< MATRIX >::resolve ( const DoubleVector rhs,
DoubleVector result 
)
inlinevirtual

Re-solve the system defined by the last assembled Jacobian and the rhs vector specified here. Solution is returned in the vector result.

Reimplemented from oomph::LinearSolver.

1997  {
1998  // We are re-solving
1999  Resolving = true;
2000 
2001 #ifdef PARANOID
2002  if ((Matrices_storage_pt[0] == 0) || (Matrices_storage_pt[1] == 0))
2003  {
2004  throw OomphLibError("No matrix was stored -- cannot re-solve",
2007  }
2008 #endif
2009 
2010  // Set up a dummy matrix. As we're resolving this won't be used in
2011  // solve_helper but we need to pass a matrix in to fill the input.
2012  // The matrices used in the calculations have already been stored
2013  CRDoubleMatrix* matrix_pt = new CRDoubleMatrix;
2014 
2015  // Call the helper function
2016  solve_helper(matrix_pt, rhs, result);
2017 
2018  // Delete the matrix
2019  delete matrix_pt;
2020 
2021  // Make it a null pointer
2022  matrix_pt = 0;
2023 
2024  // Reset re-solving flag
2025  Resolving = false;
2026  }
MATRIX * matrix_pt() const
Definition: block_preconditioner.h:520
void solve_helper(DoubleMatrixBase *const &matrix_pt, const DoubleVector &rhs, DoubleVector &solution)
General interface to solve function.
Definition: complex_smoother.h:2453

References oomph::HelmholtzGMRESMG< MATRIX >::Matrices_storage_pt, oomph::BlockPreconditioner< MATRIX >::matrix_pt(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, oomph::HelmholtzGMRESMG< MATRIX >::Resolving, and oomph::HelmholtzGMRESMG< MATRIX >::solve_helper().

◆ set_preconditioner_LHS()

template<typename MATRIX >
void oomph::HelmholtzGMRESMG< MATRIX >::set_preconditioner_LHS ( )
inline

Set left preconditioning (the default)

2036  {
2037  Preconditioner_LHS = true;
2038  }

References oomph::HelmholtzGMRESMG< MATRIX >::Preconditioner_LHS.

◆ set_preconditioner_RHS()

template<typename MATRIX >
void oomph::HelmholtzGMRESMG< MATRIX >::set_preconditioner_RHS ( )
inline

Enable right preconditioning.

2042  {
2043  Preconditioner_LHS = false;
2044  }

References oomph::HelmholtzGMRESMG< MATRIX >::Preconditioner_LHS.

Referenced by PMLHelmholtzMGProblem< ELEMENT >::set_gmres_multigrid_solver().

◆ setup()

template<typename MATRIX >
void oomph::HelmholtzGMRESMG< MATRIX >::setup ( )
inlinevirtual

Implementation of the pure virtual base class function. This accompanies the preconditioner_solve function and so is also broken

Implements oomph::Preconditioner.

1789  {
1790  // Open an output stream
1791  std::ostringstream error_message_stream;
1792 
1793  // Create an error message
1794  error_message_stream << "This function is broken. HelmholtzGMRESMG is "
1795  << "only meant to be used as a linear solver.\n";
1796 
1797  // Throw the error message
1798  throw OomphLibError(error_message_stream.str(),
1801  }

References OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

◆ solve() [1/3]

template<typename MATRIX >
void oomph::HelmholtzGMRESMG< MATRIX >::solve ( DoubleMatrixBase *const &  matrix_pt,
const DoubleVector rhs,
DoubleVector solution 
)
inlinevirtual

Linear-algebra-type solver: Takes pointer to a matrix and rhs vector and returns the solution of the linear system.

Reimplemented from oomph::LinearSolver.

1966  {
1967  // Open an output stream
1968  std::ostringstream error_message_stream;
1969 
1970  // Create an error message
1971  error_message_stream
1972  << "This function is broken. The block preconditioner "
1973  << "needs access to the underlying mesh.\n";
1974 
1975  // Throw the error message
1976  throw OomphLibError(error_message_stream.str(),
1979  }

References OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

◆ solve() [2/3]

template<typename MATRIX >
void oomph::HelmholtzGMRESMG< 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.

1989  {
1990  LinearSolver::solve(matrix_pt, rhs, result);
1991  }
virtual void solve(Problem *const &problem_pt, DoubleVector &result)=0

References oomph::BlockPreconditioner< MATRIX >::matrix_pt(), and oomph::LinearSolver::solve().

◆ solve() [3/3]

template<typename MATRIX >
void oomph::HelmholtzGMRESMG< 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.

Reimplemented in oomph::HelmholtzFGMRESMG< MATRIX >.

1807  {
1808 #ifdef OOMPH_HAS_MPI
1809  // Make sure that this is running in serial. Can't guarantee it'll
1810  // work when the problem is distributed over several processors
1811  if (MPI_Helpers::communicator_pt()->nproc() > 1)
1812  {
1813  // Throw a warning
1814  OomphLibWarning("Can't guarantee the MG solver will work in parallel!",
1817  }
1818 #endif
1819 
1820  // Find # of degrees of freedom (variables)
1821  unsigned n_dof = problem_pt->ndof();
1822 
1823  // Initialise timer
1824  double t_start = TimingHelpers::timer();
1825 
1826  // We're not re-solving
1827  Resolving = false;
1828 
1829  // Get rid of any previously stored data
1830  clean_up_memory();
1831 
1832  // Grab the communicator from the MGProblem object and assign it
1833  this->set_comm_pt(problem_pt->communicator_pt());
1834 
1835  // Setup the distribution
1836  LinearAlgebraDistribution dist(
1837  problem_pt->communicator_pt(), n_dof, false);
1838 
1839  // Build the internal distribution in this way because both the
1840  // IterativeLinearSolver and BlockPreconditioner class have base-
1841  // class subobjects of type oomph::DistributableLinearAlgebraObject
1843 
1844  // Get Jacobian matrix in format specified by template parameter
1845  // and nonlinear residual vector
1846  MATRIX* matrix_pt = new MATRIX;
1847  DoubleVector f;
1848  if (dynamic_cast<DistributableLinearAlgebraObject*>(matrix_pt) != 0)
1849  {
1850  if (dynamic_cast<CRDoubleMatrix*>(matrix_pt) != 0)
1851  {
1852  dynamic_cast<CRDoubleMatrix*>(matrix_pt)->build(
1855  }
1856  }
1857 
1858  // Get the Jacobian and residuals vector
1859  problem_pt->get_jacobian(f, *matrix_pt);
1860 
1861  // We've made the matrix, we can delete it...
1862  Matrix_can_be_deleted = true;
1863 
1864  // Replace the current matrix used in Preconditioner by the new matrix
1865  this->set_matrix_pt(matrix_pt);
1866 
1867  // The preconditioner works with one mesh; set it! Since we only use
1868  // the block preconditioner on the finest level, we use the mesh from
1869  // that level
1870  this->set_nmesh(1);
1871 
1872  // Elements in actual pml layer are trivially wrapped versions of
1873  // their bulk counterparts. Technically they are different elements
1874  // so we have to allow different element types
1875  bool allow_different_element_types_in_mesh = true;
1876  this->set_mesh(
1877  0, problem_pt->mesh_pt(), allow_different_element_types_in_mesh);
1878 
1879  // Set up the generic block look up scheme
1880  this->block_setup();
1881 
1882  // Extract the number of blocks.
1883  unsigned nblock_types = this->nblock_types();
1884 
1885 #ifdef PARANOID
1886  // PARANOID check - there must only be two block types
1887  if (nblock_types != 2)
1888  {
1889  // Create the error message
1890  std::stringstream tmp;
1891  tmp << "There are supposed to be two block types.\nYours has "
1892  << nblock_types << std::endl;
1893 
1894  // Throw an error
1895  throw OomphLibError(
1897  }
1898 #endif
1899 
1900  // Resize the storage for the system matrices
1901  Matrices_storage_pt.resize(2, 0);
1902 
1903  // Loop over the rows of the block matrix
1904  for (unsigned i = 0; i < nblock_types; i++)
1905  {
1906  // Fix the column index
1907  unsigned j = 0;
1908 
1909  // Create new CRDoubleMatrix objects
1910  Matrices_storage_pt[i] = new CRDoubleMatrix;
1911 
1912  // Extract the required blocks, i.e. the first column
1913  this->get_block(i, j, *Matrices_storage_pt[i]);
1914  }
1915 
1916  // Doc time for setup
1917  double t_end = TimingHelpers::timer();
1918  Jacobian_setup_time = t_end - t_start;
1919 
1920  if (Doc_time)
1921  {
1922  oomph_info << "\nTime for setup of block Jacobian [sec]: "
1923  << Jacobian_setup_time << std::endl;
1924  }
1925 
1926  // Call linear algebra-style solver
1927  // If the result distribution is wrong, then redistribute
1928  // before the solve and return to original distribution
1929  // afterwards
1930  if ((!(*result.distribution_pt() ==
1932  (result.built()))
1933  {
1934  // Make a distribution object
1935  LinearAlgebraDistribution temp_global_dist(result.distribution_pt());
1936 
1937  // Build the result vector distribution
1938  result.build(IterativeLinearSolver::distribution_pt(), 0.0);
1939 
1940  // Solve the problem
1941  solve_helper(matrix_pt, f, result);
1942 
1943  // Redistribute the vector
1944  result.redistribute(&temp_global_dist);
1945  }
1946  // Otherwise just solve
1947  else
1948  {
1949  // Solve
1950  solve_helper(matrix_pt, f, result);
1951  }
1952 
1953  // Kill matrix unless it's still required for resolve
1954  if (!Enable_resolve)
1955  {
1956  // Clean up anything left in memory
1957  clean_up_memory();
1958  }
1959  } // End of solve
int i
Definition: BiCGSTAB_step_by_step.cpp:9
void get_block(const unsigned &i, const unsigned &j, MATRIX &output_matrix, const bool &ignore_replacement_block=false) const
Definition: block_preconditioner.h:673
unsigned nblock_types() const
Return the number of block types.
Definition: block_preconditioner.h:1670
void set_nmesh(const unsigned &n)
Definition: block_preconditioner.h:2851
virtual void block_setup()
Definition: block_preconditioner.cc:2483
void set_mesh(const unsigned &i, const Mesh *const mesh_pt, const bool &allow_multiple_element_type_in_mesh=false)
Definition: block_preconditioner.h:2866
void build_distribution(const LinearAlgebraDistribution *const dist_pt)
Definition: linear_algebra_distribution.h:507
DistributableLinearAlgebraObject()
Default constructor - create a distribution.
Definition: linear_algebra_distribution.h:438
double Jacobian_setup_time
Jacobian setup time.
Definition: iterative_linear_solver.h:248
bool Doc_time
Boolean flag that indicates whether the time taken.
Definition: linear_solver.h:77
bool Enable_resolve
Definition: linear_solver.h:73
static OomphCommunicator * communicator_pt()
access to the global oomph-lib communicator
Definition: oomph_utilities.cc:1046
virtual void set_comm_pt(const OomphCommunicator *const comm_pt)
Set the communicator pointer.
Definition: preconditioner.h:193
virtual void set_matrix_pt(DoubleMatrixBase *matrix_pt)
Set the matrix pointer.
Definition: preconditioner.h:165
static int f(const TensorMap< Tensor< int, 3 > > &tensor)
Definition: cxx11_tensor_map.cpp:237
Eigen::Matrix< Scalar, Dynamic, Dynamic, ColMajor > tmp
Definition: level3_impl.h:365
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 oomph::BlockPreconditioner< MATRIX >::block_setup(), oomph::DoubleVector::build(), oomph::DistributableLinearAlgebraObject::build_distribution(), oomph::DoubleVector::built(), oomph::HelmholtzGMRESMG< MATRIX >::clean_up_memory(), oomph::MPI_Helpers::communicator_pt(), oomph::Problem::communicator_pt(), oomph::DistributableLinearAlgebraObject::distribution_pt(), oomph::LinearSolver::Doc_time, oomph::LinearSolver::Enable_resolve, f(), oomph::BlockPreconditioner< MATRIX >::get_block(), oomph::Problem::get_jacobian(), i, j, oomph::IterativeLinearSolver::Jacobian_setup_time, oomph::HelmholtzGMRESMG< MATRIX >::Matrices_storage_pt, oomph::HelmholtzGMRESMG< MATRIX >::Matrix_can_be_deleted, oomph::BlockPreconditioner< MATRIX >::matrix_pt(), oomph::Problem::mesh_pt(), oomph::BlockPreconditioner< MATRIX >::nblock_types(), oomph::Problem::ndof(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, oomph::oomph_info, oomph::DoubleVector::redistribute(), oomph::HelmholtzGMRESMG< MATRIX >::Resolving, oomph::Preconditioner::set_comm_pt(), oomph::Preconditioner::set_matrix_pt(), oomph::BlockPreconditioner< MATRIX >::set_mesh(), oomph::BlockPreconditioner< MATRIX >::set_nmesh(), oomph::HelmholtzGMRESMG< MATRIX >::solve_helper(), oomph::TimingHelpers::timer(), and tmp.

◆ solve_helper()

template<typename MATRIX >
void oomph::HelmholtzGMRESMG< MATRIX >::solve_helper ( DoubleMatrixBase *const &  matrix_pt,
const DoubleVector rhs,
DoubleVector solution 
)
protected

General interface to solve function.

//////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// Linear-algebra-type solver: Takes pointer to a matrix and rhs vector and returns the solution of the linear system. based on the algorithm presented in Templates for the Solution of Linear Systems: Building Blocks for Iterative Methods, Barrett, Berry et al, SIAM, 2006 and the implementation in the IML++ library : http://math.nist.gov/iml++/

2457  {
2458  // Set the number of dof types (real and imaginary for this solver)
2459  unsigned n_dof_types = this->ndof_types();
2460 
2461 #ifdef PARANOID
2462  // This only works for 2 dof types
2463  if (n_dof_types != 2)
2464  {
2465  // Create an output stream
2466  std::stringstream error_message_stream;
2467 
2468  // Create the error message
2469  error_message_stream << "This preconditioner only works for problems "
2470  << "with 2 dof types\nYours has " << n_dof_types;
2471 
2472  // Throw the error message
2473  throw OomphLibError(error_message_stream.str(),
2476  }
2477 #endif
2478 
2479  // Get the number of dofs (note, the total number of dofs in the problem
2480  // is 2*n_row but if the constituent vectors and matrices were stored in
2481  // complex objects there would only be (n_row) rows so we use that)
2482  unsigned n_row = Matrices_storage_pt[0]->nrow();
2483 
2484  // Make sure Max_iter isn't greater than n_dof. The user cannot use this
2485  // many iterations when using Krylov subspace methods
2486  if (Max_iter > n_row)
2487  {
2488  // Create an output string stream
2489  std::ostringstream error_message_stream;
2490 
2491  // Create the error message
2492  error_message_stream << "The maximum number of iterations cannot exceed "
2493  << "the number of rows in the problem."
2494  << "\nMaximum number of iterations: " << Max_iter
2495  << "\nNumber of rows: " << n_row << std::endl;
2496 
2497  // Throw the error message
2498  throw OomphLibError(error_message_stream.str(),
2501  }
2502 
2503 #ifdef PARANOID
2504  // Loop over the real and imaginary parts
2505  for (unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
2506  {
2507  // PARANOID check that if the matrix is distributable then it should not
2508  // be then it should not be distributed
2509  if (dynamic_cast<DistributableLinearAlgebraObject*>(
2510  Matrices_storage_pt[dof_type]) != 0)
2511  {
2512  if (dynamic_cast<DistributableLinearAlgebraObject*>(
2513  Matrices_storage_pt[dof_type])
2514  ->distributed())
2515  {
2516  std::ostringstream error_message_stream;
2517  error_message_stream << "The matrix must not be distributed.";
2518  throw OomphLibError(error_message_stream.str(),
2521  }
2522  }
2523  }
2524  // PARANOID check that this rhs distribution is setup
2525  if (!rhs.built())
2526  {
2527  std::ostringstream error_message_stream;
2528  error_message_stream << "The rhs vector distribution must be setup.";
2529  throw OomphLibError(error_message_stream.str(),
2532  }
2533  // PARANOID check that the rhs has the right number of global rows
2534  if (rhs.nrow() != 2 * n_row)
2535  {
2536  std::ostringstream error_message_stream;
2537  error_message_stream << "RHS does not have the same dimension as the"
2538  << " linear system";
2539  throw OomphLibError(error_message_stream.str(),
2542  }
2543  // PARANOID check that the rhs is not distributed
2544  if (rhs.distribution_pt()->distributed())
2545  {
2546  std::ostringstream error_message_stream;
2547  error_message_stream << "The rhs vector must not be distributed.";
2548  throw OomphLibError(error_message_stream.str(),
2551  }
2552  // PARANOID check that if the result is setup it matches the distribution
2553  // of the rhs
2554  if (solution.built())
2555  {
2556  if (!(*rhs.distribution_pt() == *solution.distribution_pt()))
2557  {
2558  std::ostringstream error_message_stream;
2559  error_message_stream << "If the result distribution is setup then it "
2560  << "must be the same as the rhs distribution";
2561  throw OomphLibError(error_message_stream.str(),
2564  }
2565  } // if (solution[dof_type].built())
2566 #endif
2567 
2568  // Set up the solution distribution if it's not already distributed
2569  if (!solution.built())
2570  {
2571  // Build the distribution
2573  }
2574  // Otherwise initialise all entries to zero
2575  else
2576  {
2577  // Initialise the entries in the k-th vector in solution to zero
2578  solution.initialise(0.0);
2579  }
2580 
2581  // Create a vector of DoubleVectors (this is a distributed vector so we have
2582  // to create two separate DoubleVector objects to cope with the arithmetic)
2583  Vector<DoubleVector> block_solution(n_dof_types);
2584 
2585  // Create a vector of DoubleVectors (this is a distributed vector so we have
2586  // to create two separate DoubleVector objects to cope with the arithmetic)
2587  Vector<DoubleVector> block_rhs(n_dof_types);
2588 
2589  // Build the distribution of both vectors
2590  for (unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
2591  {
2592  // Build the distribution of the k-th constituent vector
2593  block_solution[dof_type].build(this->block_distribution_pt(dof_type),
2594  0.0);
2595 
2596  // Build the distribution of the k-th constituent vector
2597  block_rhs[dof_type].build(this->block_distribution_pt(dof_type), 0.0);
2598  }
2599 
2600  // Grab the solution vector in block form
2601  this->get_block_vectors(solution, block_solution);
2602 
2603  // Grab the RHS vector in block form
2604  this->get_block_vectors(rhs, block_rhs);
2605 
2606  // Start the solver timer
2607  double t_start = TimingHelpers::timer();
2608 
2609  // Storage for the relative residual
2610  double resid;
2611 
2612  // Initialise vectors (since these are not distributed vectors we template
2613  // one vector by the type std::complex<double> instead of using two vectors,
2614  // each templated by the type double
2615 
2616  // Vector, s, used in the minimisation problem: J(y)=min||s-R_m*y||
2617  // [see Saad Y."Iterative methods for sparse linear systems", p.176.]
2618  Vector<std::complex<double>> s(n_row + 1, std::complex<double>(0.0, 0.0));
2619 
2620  // Vector to store the value of cos(theta) when using the Givens rotation
2621  Vector<std::complex<double>> cs(n_row + 1, std::complex<double>(0.0, 0.0));
2622 
2623  // Vector to store the value of sin(theta) when using the Givens rotation
2624  Vector<std::complex<double>> sn(n_row + 1, std::complex<double>(0.0, 0.0));
2625 
2626  // Create a vector of DoubleVectors (this is a distributed vector so we have
2627  // to create two separate DoubleVector objects to cope with the arithmetic)
2628  Vector<DoubleVector> block_w(n_dof_types);
2629 
2630  // Build the distribution of both vectors
2631  for (unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
2632  {
2633  // Build the distribution of the k-th constituent vector
2634  block_w[dof_type].build(this->block_distribution_pt(dof_type), 0.0);
2635  }
2636 
2637  // Set up the preconditioner only if we're not re-solving
2638  if (!Resolving)
2639  {
2640  // Only set up the preconditioner before solve if required
2642  {
2643  // Set up the preconditioner from the Jacobian matrix
2644  double t_start_prec = TimingHelpers::timer();
2645 
2646  // Use the setup function in the Preconditioner class
2647  preconditioner_pt()->setup(dynamic_cast<MATRIX*>(matrix_pt));
2648 
2649  // Doc time for setup of preconditioner
2650  double t_end_prec = TimingHelpers::timer();
2651  Preconditioner_setup_time = t_end_prec - t_start_prec;
2652 
2653  // If time documentation is enabled
2654  if (Doc_time)
2655  {
2656  // Output the time taken
2657  oomph_info << "Time for setup of preconditioner [sec]: "
2658  << Preconditioner_setup_time << std::endl;
2659  }
2660  }
2661  }
2662  else
2663  {
2664  // If time documentation is enabled
2665  if (Doc_time)
2666  {
2667  // Notify the user
2668  oomph_info << "Setup of preconditioner is bypassed in resolve mode"
2669  << std::endl;
2670  }
2671  } // if (!Resolving) else
2672 
2673  // Create a vector of DoubleVectors to store the RHS of b-Jx=Mr. We assume
2674  // both x=0 and that a preconditioner is not applied by which we deduce b=r
2675  Vector<DoubleVector> block_r(n_dof_types);
2676 
2677  // Build the distribution of both vectors
2678  for (unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
2679  {
2680  // Build the distribution of the k-th constituent vector
2681  block_r[dof_type].build(this->block_distribution_pt(dof_type), 0.0);
2682  }
2683 
2684  // If we're using LHS preconditioning solve b-Jx=Mr for r (assumes x=0)
2685  // so calculate r=M^{-1}b otherwise set r=b (RHS prec.)
2686  if (Preconditioner_LHS)
2687  {
2688  // Create a vector of the same size as rhs
2690 
2691  // Copy the vectors in r to full_r
2692  this->return_block_vectors(block_r, r);
2693 
2694  // Use the preconditioner
2696 
2697  // Copy the vector full_r into the vectors in r
2698  this->get_block_vectors(r, block_r);
2699  }
2700  else
2701  {
2702  // Store the value of b (the RHS vector) in r
2703  for (unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
2704  {
2705  // Copy the entries of rhs into r
2706  block_r[dof_type] = block_rhs[dof_type];
2707  }
2708  } // if(Preconditioner_LHS)
2709 
2710  // Calculate the norm of the real part of r
2711  double norm_r = block_r[0].norm();
2712 
2713  // Calculate the norm of the imaginary part of r
2714  double norm_c = block_r[1].norm();
2715 
2716  // Compute norm(r)
2717  double normb = sqrt(pow(norm_r, 2.0) + pow(norm_c, 2.0));
2718 
2719  // Set the value of beta (the initial residual)
2720  double beta = normb;
2721 
2722  // Compute the initial relative residual. If the entries of the RHS vector
2723  // are all zero then set normb equal to one. This is because we divide the
2724  // value of the norm at later stages by normb and dividing by zero is not
2725  // definied
2726  if (normb == 0.0)
2727  {
2728  // Set the value of normb
2729  normb = 1.0;
2730  }
2731 
2732  // Calculate the ratio between the initial norm and the current norm.
2733  // Since we haven't completed an iteration yet this will simply be one
2734  // unless normb was zero, in which case resid will have value zero
2735  resid = beta / normb;
2736 
2737  // If required, will document convergence history to screen or file (if
2738  // stream open)
2740  {
2741  // If an output file which is open isn't provided then output to screen
2742  if (!Output_file_stream.is_open())
2743  {
2744  // Output the residual value to the screen
2745  oomph_info << 0 << " " << resid << std::endl;
2746  }
2747  // Otherwise, output to file
2748  else
2749  {
2750  // Output the residual value to file
2751  Output_file_stream << 0 << " " << resid << std::endl;
2752  }
2753  } // if (Doc_convergence_history)
2754 
2755  // If the GMRES algorithm converges immediately
2756  if (resid <= Tolerance)
2757  {
2758  // If time documentation is enabled
2759  if (Doc_time)
2760  {
2761  // Notify the user
2762  oomph_info << "GMRES converged immediately. Normalised residual norm: "
2763  << resid << std::endl;
2764  }
2765 
2766  // Finish running the solver
2767  return;
2768  } // if (resid<=Tolerance)
2769 
2770  // Initialise a vector of orthogonal basis vectors
2771  Vector<Vector<DoubleVector>> block_v;
2772 
2773  // Resize the number of vectors needed
2774  block_v.resize(n_row + 1);
2775 
2776  // Resize each Vector of DoubleVectors to store the real and imaginary
2777  // part of a given vector
2778  for (unsigned dof_type = 0; dof_type < n_row + 1; dof_type++)
2779  {
2780  // Create two DoubleVector objects in each Vector
2781  block_v[dof_type].resize(n_dof_types);
2782  }
2783 
2784  // Initialise the upper hessenberg matrix. Since we are not using
2785  // distributed vectors here, the algebra is best done using entries
2786  // of the type std::complex<double>. NOTE: For implementation purposes
2787  // the upper Hessenberg matrix indices are swapped so the matrix is
2788  // effectively transposed
2789  Vector<Vector<std::complex<double>>> hessenberg(n_row + 1);
2790 
2791  // Build the zeroth basis vector
2792  for (unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
2793  {
2794  // Build the k-th part of the zeroth vector. Here k=0 and k=1 correspond
2795  // to the real and imaginary part of the zeroth vector, respectively
2796  block_v[0][dof_type].build(this->block_distribution_pt(dof_type), 0.0);
2797  }
2798 
2799  // Loop over the real and imaginary parts of v
2800  for (unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
2801  {
2802  // For fast access
2803  double* v0_pt = block_v[0][dof_type].values_pt();
2804 
2805  // For fast access
2806  const double* block_r_pt = block_r[dof_type].values_pt();
2807 
2808  // Set the zeroth basis vector v[0] to r/beta
2809  for (unsigned i = 0; i < n_row; i++)
2810  {
2811  // Assign the i-th entry of the zeroth basis vector
2812  v0_pt[i] = block_r_pt[i] / beta;
2813  }
2814  } // for (unsigned k=0;k<n_dof_types;k++)
2815 
2816  // Set the first entry in the minimisation problem RHS vector (is meant
2817  // to the vector beta*e_1 initially, where e_1 is the unit vector with
2818  // one in its first entry)
2819  s[0] = beta;
2820 
2821  // Compute the next step of the iterative scheme
2822  for (unsigned j = 0; j < Max_iter; j++)
2823  {
2824  // Resize the next column of the upper hessenberg matrix
2825  hessenberg[j].resize(j + 2, std::complex<double>(0.0, 0.0));
2826 
2827  // Calculate w=M^{-1}(Jv[j]) (LHS prec.) or w=JM^{-1}v (RHS prec.)
2828  {
2829  // Create a temporary vector
2831 
2832  // Create a temporary vector
2834 
2835  // Create a temporary vector
2837 
2838  // Create a temporary vector of DoubleVectors
2839  Vector<DoubleVector> block_temp(2);
2840 
2841  // Create two DoubleVectors
2842  for (unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
2843  {
2844  block_temp[dof_type].build(this->block_distribution_pt(dof_type),
2845  0.0);
2846  }
2847 
2848  // If we're using LHS preconditioning
2849  if (Preconditioner_LHS)
2850  {
2851  // Solve Jv[j]=Mw for w. Note, we cannot use inbuilt complex matrix
2852  // algebra here as we're using distributed vectors
2854  Matrices_storage_pt, block_v[j], block_temp);
2855 
2856  // Copy block_temp into temp
2857  this->return_block_vectors(block_temp, temp);
2858 
2859  // Copy block_w into w
2860  this->return_block_vectors(block_w, w);
2861 
2862  // Apply the preconditioner
2863  this->preconditioner_pt()->preconditioner_solve(temp, w);
2864 
2865  // Copy w into block_w
2866  this->get_block_vectors(w, block_w);
2867  }
2868  // If we're using RHS preconditioning
2869  else
2870  {
2871  // Copy the real and imaginary part of v[j] into one vector, vj
2872  this->return_block_vectors(block_v[j], vj);
2873 
2874  // Use w=JM^{-1}v by saad p270
2875  this->preconditioner_pt()->preconditioner_solve(vj, temp);
2876 
2877  // Copy w into block_w
2878  this->get_block_vectors(temp, block_temp);
2879 
2880  // Solve Jv[j] = Mw for w. Note, we cannot use inbuilt complex matrix
2881  // algebra here as we're using distributed vectors
2883  Matrices_storage_pt, block_temp, block_w);
2884  }
2885  } // Calculate w=M^{-1}(Jv[j]) (LHS prec.) or w=JM^{-1}v (RHS prec.)
2886 
2887  // For fast access
2888  double* block_w_r_pt = block_w[0].values_pt();
2889 
2890  // For fast access
2891  double* block_w_c_pt = block_w[1].values_pt();
2892 
2893  // Loop over all of the entries on and above the principal subdiagonal of
2894  // the Hessenberg matrix in the j-th column (remembering that
2895  // the indices of the upper Hessenberg matrix are swapped for the purpose
2896  // of implementation)
2897  for (unsigned i = 0; i < j + 1; i++)
2898  {
2899  // For fast access
2900  const double* vi_r_pt = block_v[i][0].values_pt();
2901 
2902  // For fast access
2903  const double* vi_c_pt = block_v[i][1].values_pt();
2904 
2905  // Loop over the entries of v and w
2906  for (unsigned k = 0; k < n_row; k++)
2907  {
2908  // Store the appropriate entry in v as a complex value
2909  std::complex<double> complex_v(vi_r_pt[k], vi_c_pt[k]);
2910 
2911  // Store the appropriate entry in w as a complex value
2912  std::complex<double> complex_w(block_w_r_pt[k], block_w_c_pt[k]);
2913 
2914  // Update the value of H(i,j) noting we're computing a complex
2915  // inner product here (the ordering is very important here!)
2916  hessenberg[j][i] += std::conj(complex_v) * complex_w;
2917  }
2918 
2919  // Orthonormalise w against all previous orthogonal vectors, v_i by
2920  // looping over its entries and updating them
2921  for (unsigned k = 0; k < n_row; k++)
2922  {
2923  // Update the real part of the k-th entry of w
2924  block_w_r_pt[k] -= (hessenberg[j][i].real() * vi_r_pt[k] -
2925  hessenberg[j][i].imag() * vi_c_pt[k]);
2926 
2927  // Update the imaginary part of the k-th entry of w
2928  block_w_c_pt[k] -= (hessenberg[j][i].real() * vi_c_pt[k] +
2929  hessenberg[j][i].imag() * vi_r_pt[k]);
2930  }
2931  } // for (unsigned i=0;i<j+1;i++)
2932 
2933  // Calculate the norm of the real part of w
2934  norm_r = block_w[0].norm();
2935 
2936  // Calculate the norm of the imaginary part of w
2937  norm_c = block_w[1].norm();
2938 
2939  // Calculate the norm of the vector w using norm_r and norm_c and assign
2940  // its value to the appropriate entry in the Hessenberg matrix
2941  hessenberg[j][j + 1] = sqrt(pow(norm_r, 2.0) + pow(norm_c, 2.0));
2942 
2943  // Build the (j+1)-th basis vector
2944  for (unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
2945  {
2946  // Build the k-th part of the zeroth vector. Here k=0 and k=1 correspond
2947  // to the real and imaginary part of the zeroth vector, respectively
2948  block_v[j + 1][dof_type].build(this->block_distribution_pt(dof_type),
2949  0.0);
2950  }
2951 
2952  // Check if the value of hessenberg[j][j+1] is zero. If it
2953  // isn't then we update the next entries in v
2954  if (hessenberg[j][j + 1] != 0.0)
2955  {
2956  // For fast access
2957  double* v_r_pt = block_v[j + 1][0].values_pt();
2958 
2959  // For fast access
2960  double* v_c_pt = block_v[j + 1][1].values_pt();
2961 
2962  // For fast access
2963  const double* block_w_r_pt = block_w[0].values_pt();
2964 
2965  // For fast access
2966  const double* block_w_c_pt = block_w[1].values_pt();
2967 
2968  // Notice, the value of H(j,j+1), as calculated above, is clearly a real
2969  // number. As such, calculating the division
2970  // v_{j+1}=w_{j}/h_{j+1,j},
2971  // here is simple, i.e. we don't need to worry about cross terms in the
2972  // algebra. To avoid computing h_{j+1,j} several times we precompute it
2973  double h_subdiag_val = hessenberg[j][j + 1].real();
2974 
2975  // Loop over the entries of the new orthogonal vector and set its values
2976  for (unsigned k = 0; k < n_row; k++)
2977  {
2978  // The i-th entry of the real component is given by
2979  v_r_pt[k] = block_w_r_pt[k] / h_subdiag_val;
2980 
2981  // Similarly, the i-th entry of the imaginary component is given by
2982  v_c_pt[k] = block_w_c_pt[k] / h_subdiag_val;
2983  }
2984  }
2985  // Otherwise, we have to jump to the next part of the algorithm; if
2986  // the value of hessenberg[j][j+1] is zero then the norm of the latest
2987  // orthogonal vector is zero. This is only possible if the entries
2988  // in w are all zero. As a result, the Krylov space of A and r_0 has
2989  // been spanned by the previously calculated orthogonal vectors
2990  else
2991  {
2992  // Book says "Set m=j and jump to step 11" (p.172)...
2993  // Do something here!
2994  oomph_info << "Subdiagonal Hessenberg entry is zero. "
2995  << "Do something here..." << std::endl;
2996  } // if (hessenberg[j][j+1]!=0.0)
2997 
2998  // Loop over the entries in the Hessenberg matrix and calculate the
2999  // entries of the Givens rotation matrices
3000  for (unsigned k = 0; k < j; k++)
3001  {
3002  // Apply the plane rotation to all of the previous entries in the
3003  // (j)-th column (remembering the indexing is reversed)
3005  hessenberg[j][k], hessenberg[j][k + 1], cs[k], sn[k]);
3006  }
3007 
3008  // Now calculate the entries of the latest Givens rotation matrix
3010  hessenberg[j][j], hessenberg[j][j + 1], cs[j], sn[j]);
3011 
3012  // Apply the plane rotation using the newly calculated entries
3014  hessenberg[j][j], hessenberg[j][j + 1], cs[j], sn[j]);
3015 
3016  // Apply a plane rotation to the corresponding entry in the vector
3017  // s used in the minimisation problem, J(y)=min||s-R_m*y||
3018  apply_plane_rotation(s[j], s[j + 1], cs[j], sn[j]);
3019 
3020  // Compute current residual using equation (6.42) in Saad Y, "Iterative
3021  // methods for sparse linear systems", p.177.]. Note, since s has complex
3022  // entries we have to use std::abs instead of std::fabs
3023  beta = std::abs(s[j + 1]);
3024 
3025  // Compute the relative residual
3026  resid = beta / normb;
3027 
3028  // If required will document convergence history to screen or file (if
3029  // stream open)
3031  {
3032  // If an output file which is open isn't provided then output to screen
3033  if (!Output_file_stream.is_open())
3034  {
3035  // Output the residual value to the screen
3036  oomph_info << j + 1 << " " << resid << std::endl;
3037  }
3038  // Otherwise, output to file
3039  else
3040  {
3041  // Output the residual value to file
3042  Output_file_stream << j + 1 << " " << resid << std::endl;
3043  }
3044  } // if (Doc_convergence_history)
3045 
3046  // If the required tolerance has been met
3047  if (resid < Tolerance)
3048  {
3049  // Store the number of iterations taken
3050  Iterations = j + 1;
3051 
3052  // Update the result vector using the result, x=x_0+V_m*y (where V_m
3053  // is given by v here)
3054  update(j, hessenberg, s, block_v, block_solution);
3055 
3056  // Copy the vectors in block_solution to solution
3057  this->return_block_vectors(block_solution, solution);
3058 
3059  // If time documentation was enabled
3060  if (Doc_time)
3061  {
3062  // Output the current normalised residual norm
3063  oomph_info << "\nGMRES converged (1). Normalised residual norm: "
3064  << resid << std::endl;
3065 
3066  // Output the number of iterations it took for convergence
3067  oomph_info << "Number of iterations to convergence: " << j + 1 << "\n"
3068  << std::endl;
3069  }
3070 
3071  // Stop the timer
3072  double t_end = TimingHelpers::timer();
3073 
3074  // Calculate the time taken to calculate the solution
3075  Solution_time = t_end - t_start;
3076 
3077  // If time documentation was enabled
3078  if (Doc_time)
3079  {
3080  // Output the time taken to solve the problem using GMRES
3081  oomph_info << "Time for solve with GMRES [sec]: " << Solution_time
3082  << std::endl;
3083  }
3084 
3085  // As we've met the tolerance for the solver and everything that should
3086  // be documented, has been, finish using the solver
3087  return;
3088  } // if (resid<Tolerance)
3089  } // for (unsigned j=0;j<Max_iter;j++)
3090 
3091  // Store the number of iterations taken
3092  Iterations = Max_iter;
3093 
3094  // Only update if we actually did something
3095  if (Max_iter > 0)
3096  {
3097  // Update the result vector using the result, x=x_0+V_m*y (where V_m
3098  // is given by v here)
3099  update(Max_iter - 1, hessenberg, s, block_v, block_solution);
3100 
3101  // Copy the vectors in block_solution to solution
3102  this->return_block_vectors(block_solution, solution);
3103  }
3104 
3105  // Solve Mr=(b-Jx) for r
3106  {
3107  // Create a temporary vector of DoubleVectors
3108  Vector<DoubleVector> block_temp(2);
3109 
3110  // Create two DoubleVectors
3111  for (unsigned dof_type = 0; dof_type < n_dof_types; dof_type++)
3112  {
3113  // Build the distribution of the (dof_type)-th vector
3114  block_temp[dof_type].build(this->block_distribution_pt(dof_type), 0.0);
3115  }
3116 
3117  // Calculate the value of Jx
3119  Matrices_storage_pt, block_solution, block_temp);
3120 
3121  // Get the values pointer of the vector (real)
3122  double* block_temp_r_pt = block_temp[0].values_pt();
3123 
3124  // Get the values pointer of the vector (imaginary)
3125  double* block_temp_c_pt = block_temp[1].values_pt();
3126 
3127  // Get the values pointer of the RHS vector (real)
3128  const double* block_rhs_r_pt = block_rhs[0].values_pt();
3129 
3130  // Get the values pointer of the RHS vector (imaginary)
3131  const double* block_rhs_c_pt = block_rhs[1].values_pt();
3132 
3133  // Loop over the dofs
3134  for (unsigned i = 0; i < n_row; i++)
3135  {
3136  // Calculate b-Jx (real)
3137  block_temp_r_pt[i] = block_rhs_r_pt[i] - block_temp_r_pt[i];
3138 
3139  // Calculate b-Jx (imaginary)
3140  block_temp_c_pt[i] = block_rhs_c_pt[i] - block_temp_c_pt[i];
3141  }
3142 
3143  // If we're using LHS preconditioning
3144  if (Preconditioner_LHS)
3145  {
3146  // Create a temporary DoubleVectors
3148 
3149  // Create a vector of the same size as rhs
3151 
3152  // Copy the vectors in r to full_r
3153  this->return_block_vectors(block_temp, temp);
3154 
3155  // Copy the vectors in r to full_r
3156  this->return_block_vectors(block_r, r);
3157 
3158  // Apply the preconditioner
3160  }
3161  }
3162 
3163  // Compute the current residual
3164  beta = 0.0;
3165 
3166  // Get access to the values pointer (real)
3167  norm_r = block_r[0].norm();
3168 
3169  // Get access to the values pointer (imaginary)
3170  norm_c = block_r[1].norm();
3171 
3172  // Calculate the full norm
3173  beta = sqrt(pow(norm_r, 2.0) + pow(norm_c, 2.0));
3174 
3175  // Calculate the relative residual
3176  resid = beta / normb;
3177 
3178  // If the relative residual lies within tolerance
3179  if (resid < Tolerance)
3180  {
3181  // If time documentation is enabled
3182  if (Doc_time)
3183  {
3184  // Notify the user
3185  oomph_info << "\nGMRES converged (2). Normalised residual norm: "
3186  << resid
3187  << "\nNumber of iterations to convergence: " << Iterations
3188  << "\n"
3189  << std::endl;
3190  }
3191 
3192  // End the timer
3193  double t_end = TimingHelpers::timer();
3194 
3195  // Calculate the time taken for the solver
3196  Solution_time = t_end - t_start;
3197 
3198  // If time documentation is enabled
3199  if (Doc_time)
3200  {
3201  oomph_info << "Time for solve with GMRES [sec]: " << Solution_time
3202  << std::endl;
3203  }
3204  return;
3205  }
3206 
3207  // Otherwise GMRES failed convergence
3208  oomph_info << "\nGMRES did not converge to required tolerance! "
3209  << "\nReturning with normalised residual norm: " << resid
3210  << "\nafter " << Max_iter << " iterations.\n"
3211  << std::endl;
3212 
3213  // Throw an error if requested
3215  {
3216  std::string err = "Solver failed to converge and you requested an error";
3217  err += " on convergence failures.";
3218  throw OomphLibError(
3220  }
3221 
3222  // Finish using the solver
3223  return;
3224  } // End of solve_helper
RowVector3d w
Definition: Matrix_resize_int.cpp:3
void return_block_vectors(const Vector< unsigned > &block_vec_number, const Vector< DoubleVector > &s, DoubleVector &v) const
Definition: block_preconditioner.cc:3443
const LinearAlgebraDistribution * block_distribution_pt(const unsigned &b) const
Access function to the block distributions (const version).
Definition: block_preconditioner.h:1903
unsigned ndof_types() const
Return the total number of DOF types.
Definition: block_preconditioner.h:1694
void get_block_vectors(const Vector< unsigned > &block_vec_number, const DoubleVector &v, Vector< DoubleVector > &s) const
Definition: block_preconditioner.cc:2939
bool distributed() const
distribution is serial or distributed
Definition: linear_algebra_distribution.h:493
void apply_plane_rotation(std::complex< double > &dx, std::complex< double > &dy, std::complex< double > &cs, std::complex< double > &sn)
Definition: complex_smoother.h:2406
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:2184
void complex_matrix_multiplication(Vector< CRDoubleMatrix * > const matrices_pt, const Vector< DoubleVector > &x, Vector< DoubleVector > &soln)
Definition: complex_smoother.h:2086
void generate_plane_rotation(std::complex< double > &dx, std::complex< double > &dy, std::complex< double > &cs, std::complex< double > &sn)
Definition: complex_smoother.h:2327
double Tolerance
Convergence tolerance.
Definition: iterative_linear_solver.h:239
bool Throw_error_after_max_iter
Definition: iterative_linear_solver.h:262
Preconditioner *& preconditioner_pt()
Access function to preconditioner.
Definition: iterative_linear_solver.h:95
double Preconditioner_setup_time
Preconditioner setup time.
Definition: iterative_linear_solver.h:254
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 Setup_preconditioner_before_solve
Definition: iterative_linear_solver.h:258
void setup(DoubleMatrixBase *matrix_pt)
Definition: preconditioner.h:94
virtual void preconditioner_solve(const DoubleVector &r, DoubleVector &z)=0
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
void solution(const Vector< double > &x, Vector< double > &u)
Definition: two_d_biharmonic.cc:113
r
Definition: UniformPSDSelfTest.py:20
std::string string(const unsigned &i)
Definition: oomph_definitions.cc:286

References abs(), beta, oomph::DoubleVector::built(), conj(), oomph::LinearAlgebraDistribution::distributed(), oomph::DistributableLinearAlgebraObject::distribution_pt(), hessenberg(), i, Global_Variables::Iterations, j, k, oomph::BlackBoxFDNewtonSolver::Max_iter, oomph::DistributableLinearAlgebraObject::nrow(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, oomph::oomph_info, Eigen::bfloat16_impl::pow(), UniformPSDSelfTest::r, s, BiharmonicTestFunctions1::solution(), sqrt(), oomph::Global_string_for_annotation::string(), oomph::TimingHelpers::timer(), and w.

Referenced by oomph::HelmholtzGMRESMG< MATRIX >::resolve(), and oomph::HelmholtzGMRESMG< MATRIX >::solve().

◆ update()

template<typename MATRIX >
void oomph::HelmholtzGMRESMG< 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 
)
inlineprotected

Helper function to update the result vector.

2189  {
2190  // Make a local copy of s
2191  Vector<std::complex<double>> y(s);
2192 
2193  //-----------------------------------------------------------------
2194  // The Hessenberg matrix should be an upper triangular matrix at
2195  // this point (although from its storage it would appear to be a
2196  // lower triangular matrix since the indexing has been reversed)
2197  // so finding the minimiser of J(y)=min||s-R_m*y|| where R_m is
2198  // the matrix R in the QR factorisation of the Hessenberg matrix.
2199  // Therefore, to obtain y we simply need to use a backwards
2200  // substitution. Note: The implementation here may appear to be
2201  // somewhat confusing as the indexing in the Hessenberg matrix is
2202  // reversed. This implementation of a backwards substitution does
2203  // not run along the columns of the triangular matrix but rather
2204  // up the rows.
2205  //-----------------------------------------------------------------
2206 
2207  // The outer loop is a loop over the columns of the Hessenberg matrix
2208  // since the indexing is reversed
2209  for (int i = int(k); i >= 0; i--)
2210  {
2211  // Divide the i-th entry of y by the i-th diagonal entry of H
2212  y[i] /= hessenberg[i][i];
2213 
2214  // The inner loop is a loop over the rows of the Hessenberg matrix
2215  for (int j = i - 1; j >= 0; j--)
2216  {
2217  // Update the j-th entry of y
2218  y[j] -= hessenberg[i][j] * y[i];
2219  }
2220  } // for (int i=int(k);i>=0;i--)
2221 
2222  // Calculate the number of entries in x (simply use the real part as
2223  // both the real and imaginary part should have the same length)
2224  unsigned n_row = x[0].nrow();
2225 
2226  // Build a temporary vector with entries initialised to 0.0
2227  Vector<DoubleVector> block_z(2);
2228 
2229  // Build a temporary vector with entries initialised to 0.0
2230  Vector<DoubleVector> block_temp(2);
2231 
2232  // Build the distributions
2233  for (unsigned dof_type = 0; dof_type < 2; dof_type++)
2234  {
2235  // Build the (dof_type)-th vector
2236  block_z[dof_type].build(x[0].distribution_pt(), 0.0);
2237 
2238  // Build the (dof_type)-th vector
2239  block_temp[dof_type].build(x[0].distribution_pt(), 0.0);
2240  }
2241 
2242  // Get access to the underlying values
2243  double* block_temp_r_pt = block_temp[0].values_pt();
2244 
2245  // Get access to the underlying values
2246  double* block_temp_c_pt = block_temp[1].values_pt();
2247 
2248  // Calculate x=Vy
2249  for (unsigned j = 0; j <= k; j++)
2250  {
2251  // Get access to j-th column of Z_m
2252  const double* vj_r_pt = v[j][0].values_pt();
2253 
2254  // Get access to j-th column of Z_m
2255  const double* vj_c_pt = v[j][1].values_pt();
2256 
2257  // Loop over the entries in x and update them
2258  for (unsigned i = 0; i < n_row; i++)
2259  {
2260  // Update the real part of the i-th entry in x
2261  block_temp_r_pt[i] +=
2262  (vj_r_pt[i] * y[j].real()) - (vj_c_pt[i] * y[j].imag());
2263 
2264  // Update the imaginary part of the i-th entry in x
2265  block_temp_c_pt[i] +=
2266  (vj_c_pt[i] * y[j].real()) + (vj_r_pt[i] * y[j].imag());
2267  }
2268  } // for (unsigned j=0;j<=k;j++)
2269 
2270  // If we're using LHS preconditioning
2271  if (Preconditioner_LHS)
2272  {
2273  // Since we're using LHS preconditioning the preconditioner is applied
2274  // to the matrix and RHS vector so we simply update the value of x
2275  for (unsigned dof_type = 0; dof_type < 2; dof_type++)
2276  {
2277  // Update
2278  x[dof_type] += block_temp[dof_type];
2279  }
2280  }
2281  // If we're using RHS preconditioning
2282  else
2283  {
2284  // Create a temporary vector
2286 
2287  // Copy block vectors block_temp back to temp
2288  this->return_block_vectors(block_temp, temp);
2289 
2290  // Create a temporary vector
2292 
2293  // Copy block vectors block_z back to z
2294  this->return_block_vectors(block_z, z);
2295 
2296  // Since we're using RHS preconditioning the preconditioner is applied
2297  // to the solution vector
2299 
2300  // Split up the solution vector into DoubleVectors, whose entries are
2301  // arranged to match the matrix blocks and assign it
2302  this->get_block_vectors(z, block_z);
2303 
2304  // Use the update: x_m=x_0+inv(M)Vy [see Saad Y,"Iterative methods for
2305  // sparse linear systems", p.284]
2306  for (unsigned dof_type = 0; dof_type < 2; dof_type++)
2307  {
2308  // Update
2309  x[dof_type] += block_z[dof_type];
2310  }
2311  } // if(Preconditioner_LHS) else
2312  } // End of update
AnnoyingScalar imag(const AnnoyingScalar &)
Definition: AnnoyingScalar.h:132
Array< int, Dynamic, 1 > v
Definition: Array_initializer_list_vector_cxx11.cpp:1
Scalar * y
Definition: level1_cplx_impl.h:128

References oomph::DistributableLinearAlgebraObject::distribution_pt(), oomph::BlockPreconditioner< MATRIX >::get_block_vectors(), hessenberg(), i, imag(), j, k, oomph::HelmholtzGMRESMG< MATRIX >::Preconditioner_LHS, oomph::IterativeLinearSolver::preconditioner_pt(), oomph::Preconditioner::preconditioner_solve(), oomph::BlockPreconditioner< MATRIX >::return_block_vectors(), s, v, plotDoE::x, and y.

Referenced by smc.smc::recursiveBayesian().

Member Data Documentation

◆ Iterations

template<typename MATRIX >
unsigned oomph::HelmholtzGMRESMG< MATRIX >::Iterations
protected

Number of iterations taken.

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

◆ Matrices_storage_pt

◆ Matrix_can_be_deleted

template<typename MATRIX >
bool oomph::HelmholtzGMRESMG< MATRIX >::Matrix_can_be_deleted
protected

Boolean flag to indicate if the matrix pointed to be Matrix_pt can be deleted.

Referenced by oomph::HelmholtzGMRESMG< MATRIX >::clean_up_memory(), oomph::HelmholtzGMRESMG< MATRIX >::solve(), and oomph::HelmholtzFGMRESMG< MATRIX >::solve().

◆ Preconditioner_LHS

template<typename MATRIX >
bool oomph::HelmholtzGMRESMG< MATRIX >::Preconditioner_LHS
protected

◆ Resolving

template<typename MATRIX >
bool oomph::HelmholtzGMRESMG< MATRIX >::Resolving
protected

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

Referenced by oomph::HelmholtzGMRESMG< MATRIX >::resolve(), oomph::HelmholtzGMRESMG< MATRIX >::solve(), and oomph::HelmholtzFGMRESMG< MATRIX >::solve().


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