⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ublaskrylovsolver.h

📁 利用C
💻 H
字号:
// Copyright (C) 2006 Garth N. Wells.// Licensed under the GNU LGPL Version 2.1.//// Modified by Anders Logg, 2006-2008.//// First added:  2006-05-31// Last changed: 2008-05-15#ifndef __UBLAS_KRYLOV_SOLVER_H#define __UBLAS_KRYLOV_SOLVER_H#include <dolfin/common/types.h>#include "ublas.h"#include <dolfin/parameter/Parametrized.h>#include "SolverType.h"#include "PreconditionerType.h"#include "uBlasKrylovMatrix.h"#include "uBlasMatrix.h"#include "uBlasVector.h"#include "uBlasPreconditioner.h"namespace dolfin{  /// This class implements Krylov methods for linear systems  /// of the form Ax = b using uBlas data types.  class uBlasKrylovSolver : public Parametrized  {  public:    /// Create Krylov solver for a particular method and preconditioner    uBlasKrylovSolver(SolverType solver_type=default_solver, PreconditionerType pc_type=default_pc);    /// Create Krylov solver for a particular preconditioner (set by name)    uBlasKrylovSolver(PreconditionerType pc);    /// Create Krylov solver for a particular uBlasPreconditioner    uBlasKrylovSolver(uBlasPreconditioner& pc);    /// Create Krylov solver for a particular method and uBlasPreconditioner    uBlasKrylovSolver(SolverType solver_type, uBlasPreconditioner& preconditioner);    /// Destructor    ~uBlasKrylovSolver();    /// Solve linear system Ax = b and return number of iterations (dense matrix)    uint solve(const uBlasMatrix<ublas_dense_matrix>& A, uBlasVector& x,                const uBlasVector& b);    /// Solve linear system Ax = b and return number of iterations (sparse matrix)    uint solve(const uBlasMatrix<ublas_sparse_matrix>& A, uBlasVector& x,                const uBlasVector& b);        /// Solve linear system Ax = b and return number of iterations (virtual matrix)    uint solve(const uBlasKrylovMatrix& A, uBlasVector& x, const uBlasVector& b);  private:    /// Select solver and solve linear system Ax = b and return number of iterations    template<class Mat>    uint solveKrylov(const Mat& A, uBlasVector& x, const uBlasVector& b);    /// Solve linear system Ax = b using restarted GMRES    template<class Mat>        uint solveGMRES(const Mat& A, uBlasVector& x, const uBlasVector& b,                         bool& converged) const;    /// Solve linear system Ax = b using BiCGStab    template<class Mat>        uint solveBiCGStab(const Mat& A, uBlasVector& x, const uBlasVector& b,                         bool& converged) const;        /// Select and create named preconditioner    void selectPreconditioner(PreconditionerType pc_type);    /// Read solver parameters    void readParameters();    /// Krylov method    SolverType solver_type;    /// Preconditioner    uBlasPreconditioner* pc;    /// True if a user has provided a preconditioner    bool pc_user;    /// Solver parameters    real rtol, atol, div_tol;    uint max_it, restart;    bool report;    /// True if we have read parameters    bool parameters_read;  };  //---------------------------------------------------------------------------  // Implementation of template functions  //---------------------------------------------------------------------------  template<class Mat>  dolfin::uint uBlasKrylovSolver::solveKrylov(const Mat& A, uBlasVector& x,         const uBlasVector& b)  {    // Check dimensions    uint M = A.size(0);    uint N = A.size(1);    if ( N != b.size() )      error("Non-matching dimensions for linear system.");    // Reinitialise x if necessary     // FIXME: this erases initial guess    x.init(b.size());    // Read parameters if not done    if ( !parameters_read )      readParameters();    // Write a message    if ( report )      message("Solving linear system of size %d x %d (uBlas Krylov solver).", M, N);    // Initialise preconditioner if necessary    pc->init(A);    // Choose solver    bool converged;    uint iterations;    switch (solver_type)    {     case gmres:      iterations = solveGMRES(A, x, b, converged);      break;    case bicgstab:      iterations = solveBiCGStab(A, x, b, converged);      break;    case default_solver:      iterations = solveBiCGStab(A, x, b, converged);      break;    default:      warning("Requested Krylov method unknown. Using BiCGStab.");      iterations = solveBiCGStab(A, x, b, converged);    }      // Check for convergence    if( !converged )      warning("Krylov solver failed to converge.");    else if ( report )      message("Krylov solver converged in %d iterations.", iterations);      return iterations;   }  //-----------------------------------------------------------------------------  template<class Mat>   dolfin::uint uBlasKrylovSolver::solveGMRES(const Mat& A,					   uBlasVector& x, 					   const uBlasVector& b,					   bool& converged) const  {    // Get underlying uBLAS vectors    ublas_vector& _x = x.vec();     const ublas_vector& _b = b.vec();     // Get size of system    const uint size = A.size(0);    // Create residual vector    uBlasVector r(size);    ublas_vector& _r = r.vec();     // Create H matrix and h vector    ublas_matrix_cmajor_tri H(restart, restart);    ublas_vector _h(restart+1);    // Create gamma vector    ublas_vector _gamma(restart+1);    // Matrix containing v_k as columns.    ublas_matrix_cmajor V(size, restart+1);    // w vector        uBlasVector w(size);    ublas_vector& _w = w.vec();     // Givens vectors    ublas_vector _c(restart), _s(restart);    // Miscellaneous storage    real nu, temp1, temp2, r_norm = 0.0, beta0 = 0;    converged = false;    uint iteration = 0;    while (iteration < max_it && !converged)     {       // Compute residual r = b -A*x      //noalias(r) = b;      //axpy_prod(A, -x, r, false);       A.mult(x, r);      _r *= -1.0;      noalias(_r) += _b;      // Apply preconditioner (use w for temporary storage)      _w.assign(_r);      pc->solve(r, w);      // L2 norm of residual (for most recent restart)      const real beta = norm_2(_r);      // Save intial residual (from restart 0)     if(iteration == 0)       beta0 = beta;     if(beta < atol)     {        converged = true;       return iteration;     }      // Intialise gamma      _gamma.clear();      _gamma(0) = beta;      // Create first column of V      noalias(column(V, 0)) = _r/beta;      // Modified Gram-Schmidt procedure      uint subiteration = 0;          uint j = 0;       while (subiteration < restart && iteration < max_it && !converged && r_norm/beta < div_tol)       {        // Compute product w = A*V_j (use r for temporary storage)        //axpy_prod(A, column(V, j), w, true);        noalias(_r) = column(V, j);        A.mult(r, w);        // Apply preconditioner (use r for temporary storage)        _r.assign(_w);        pc->solve(w, r);          for (uint i=0; i <= j; ++i)         {          _h(i)= inner_prod(_w, column(V,i));          noalias(_w) -= _h(i)*column(V,i);        }        _h(j+1) = norm_2(_w);        // Insert column of V (inserting v_(j+1)        noalias(column(V,j+1)) = _w/_h(j+1);        // Apply previous Givens rotations to the "new" column         // (this could be improved? - use more uBlas functions.         //  The below has been taken from old DOLFIN code.)        for(uint i=0; i<j; ++i)        {          temp1 = _h(i);                  temp2 = _h(i+1);                  _h(i)   = _c(i)*temp1 - _s(i)*temp2;                _h(i+1) = _s(i)*temp1 + _c(i)*temp2 ;             }        // Compute new c_i and s_i        nu = sqrt( _h(j)*_h(j) + _h(j+1)*_h(j+1) );        // Direct access to c & s below leads to some strange compiler errors        // when using vector expressions and noalias(). By using "subrange",        // we are working with vector expressions rather than reals        //c(j) =  h(j)/nu;         //s(j) = -h(j+1)/nu;         subrange(_c, j,j+1) =  subrange(_h, j,j+1)/nu;        subrange(_s, j,j+1) = -subrange(_h, j+1,j+2)/nu;        // Apply new rotation to last column           _h(j)   = _c(j)*_h(j) - _s(j)*_h(j+1);        _h(j+1) = 0.0;        // Apply rotations to gamma        temp1 = _c(j)*_gamma(j) - _s(j)*_gamma(j+1);        _gamma(j+1) = _s(j)*_gamma(j) + _c(j)*_gamma(j+1);         _gamma(j) = temp1;         r_norm = fabs(_gamma(j+1));        // Add h to H matrix. Would ne nice to use          //   noalias(column(H, j)) = subrange(h, 0, restart);        // but this gives an error when uBlas debugging is turned onand H         // is a triangular matrix        for(uint i=0; i<j+1; ++i)          H(i,j) = _h(i);            // Check for convergence        if( r_norm/beta0 < rtol || r_norm < atol )          converged = true;          ++iteration;        ++subiteration;        ++j;      }      // Eliminate extra rows and columns (this does not resize or copy, just addresses a range)      ublas_matrix_cmajor_tri_range Htrunc(H, ublas::range(0,subiteration), ublas::range(0,subiteration) );      ublas_vector_range _g(_gamma, ublas::range(0,subiteration));      // Solve triangular system H*g and return result in g       ublas::inplace_solve(Htrunc, _g, ublas::upper_tag () );          // x_m = x_0 + V*y      ublas_matrix_cmajor_range _v( V, ublas::range(0,V.size1()), ublas::range(0,subiteration) );      axpy_prod(_v, _g, _x, false);    }    return iteration;  }  //-----------------------------------------------------------------------------  template<class Mat>   dolfin::uint uBlasKrylovSolver::solveBiCGStab(const Mat& A,					      uBlasVector& x,					      const uBlasVector& b,					      bool& converged) const  {    // Get uderlying uBLAS vectors    ublas_vector& _x = x.vec();     const ublas_vector& _b = b.vec();    // Get size of system    const uint size = A.size(0);    // Allocate vectors    uBlasVector r(size), rstar(size), p(size), s(size), v(size), t(size), y(size), z(size);    ublas_vector& _r = r.vec();     ublas_vector& _rstar = rstar.vec();     ublas_vector& _p = p.vec();     ublas_vector& _s = s.vec();     ublas_vector& _v = v.vec();     ublas_vector& _t = t.vec();     ublas_vector& _y = y.vec();     ublas_vector& _z = z.vec();     real alpha = 1.0, beta = 0.0, omega = 1.0, r_norm = 0.0;     real rho_old = 1.0, rho = 1.0;    // Compute residual r = b -A*x    //r.assign(b);    //axpy_prod(A, -x, r, false);    A.mult(x, r);    r *= -1.0;    noalias(_r) += _b;    const real r0_norm = norm_2(_r);    if( r0_norm < atol )      {      converged = true;          return 0;    }      // Initialise r^star, v and p    _rstar.assign(_r);    _v.clear();    _p.clear();    // Apply preconditioner to r^start. This is a trick to avoid problems in which     // (r^start, r) = 0  after the first iteration (such as PDE's with homogeneous     // Neumann bc's and no forcing/source term.    pc->solve(rstar, r);      // Right-preconditioned Bi-CGSTAB    // Start iterations    converged = false;    uint iteration = 0;    while (iteration < max_it && !converged && r_norm/r0_norm < div_tol)     {      // Set rho_n = rho_n+1      rho_old = rho;       // Compute new rho      rho = ublas::inner_prod(_r, _rstar);       if( fabs(rho) < 1e-25 )        error("BiCGStab breakdown. rho = %g", rho);      beta = (rho/rho_old)*(alpha/omega);      // p = r1 + beta*p - beta*omega*A*p      p *= beta;      noalias(_p) += _r - beta*omega*_v;      // My = p      pc->solve(y, p);      // v = A*y      //axpy_prod(A, y, v, true);      A.mult(y, v);      // alpha = (r, rstart) / (v, rstar)      alpha = rho/ublas::inner_prod(_v, _rstar);      // s = r - alpha*v      noalias(_s) = _r - alpha*_v;          // Mz = s      pc->solve(z, s);      // t = A*z      //axpy_prod(A, z, t, true);      A.mult(z, t);      // omega = (t, s) / (t,t)      omega = ublas::inner_prod(_t, _s)/ublas::inner_prod(_t, _t);      // x = x + alpha*p + omega*s      noalias(_x) += alpha*_y + omega*_z;      // r = s - omega*t      noalias(_r) = _s - omega*_t;      // Compute norm of the residual and check for convergence      r_norm = norm_2(_r);      if( r_norm/r0_norm < rtol || r_norm < atol)        converged = true;      ++iteration;      }    return iteration;  }  //-----------------------------------------------------------------------------}#endif

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -