设线性方程组 Ax=b 的系数矩阵 A 为 n 阶对称正定矩阵,用对称逐步超松弛迭代(SSOR法)的分裂矩阵作为预处理矩阵M,
M=\left ( 2-\omega \right )^{-1}\left ( D/\omega +L \right )\left ( D/\omega \right )^{-1}\left ( D/\omega +L \right )^{T}
式中:D 为 A 的对角阵;L 为 A 的严格下三角矩阵;0<\omega<2 为松弛因子。
SSOR-PCG法的迭代格式为:
林绍忠对其进行了改写,节省了一定的计算量,其具体算法为:令W=D/\omega+L,V=(2-\omega)D/\omega,y=W^{-1}g,Z=W^{T}d,则:
于是,上述迭代公式可以改写为:
代码及部分演示代码(请自行补充输入输出部分)如下:
Subroutine ssor_pcg_csr(nd, nnz, a, b, x, irw, jcol, eps, itmax, w, it, reg_err) ! *********************************************************************************** ! ** 本程序参考林绍忠《用预处理共轭梯度法求解有限元方程组及程序设计》改进的迭代格式 ! ** 采用SSOR预处理,预处理矩阵为:M=(2-W)^{-1}*(D/W+L)*(D/W)^{-1}*(D/W+L)^{T} ! ** 在原来的程序基础上去掉ID映射的部分,而是一个小技巧来解决这个问题 ! ** aliouying ! ** 2013-7-31 ! *********************************************************************************** Implicit None ! *********************************************************************************** ! ** 输入和输出 ! *********************************************************************************** Integer, Intent (In) :: nd, nnz Integer, Intent (In) :: irw(nnz), jcol(nd+1) ! CSR压缩存储索引矩阵pointB,pointE Complex (Kind=8), Intent (In) :: a(nnz), b(nd) ! 矩阵变量A*x=B Complex (Kind=8), Intent (Inout) :: x(nd) ! 输入初始估计值和输出最终结果 Real (Kind=8), Intent (In) :: eps ! 收敛误差 Integer, Intent (In) :: itmax ! 最大迭代次数 Real (Kind=8), Intent (In) :: w ! 松弛因子 Integer, Intent (Out) :: it ! 收敛总计迭代次数 Real (Kind=8), Intent (Out) :: reg_err ! 迭代后的残差 ! *********************************************************************************** ! ** 程序中间变量 ! *********************************************************************************** Complex (Kind=8) :: y(nd), z(nd), d(nd), v(nd), temp(nd) Integer :: i Complex (Kind=8) :: delta, tao, beta, delta1, delta0 Complex (Kind=8), External :: m_dot_product ! *********************************************************************************** ! ** 设置初始值 ! *********************************************************************************** Forall (i=1:nd) v(i) = (2.0-w)*a(jcol(i))/w ! 这里V当成了一维向量,实际上它是一个对角矩阵,及A的对角阵计算来 ! 若想节约内存,可以把用到V的地方用A(jcol())来代替,这里我主要是为 ! 了程序跟理论公式的一致性 End Forall ! *********************************************************************************** ! ** 开始计算 ! *********************************************************************************** Call matmulvec_csr(nd, nnz, a, x, y, irw, jcol) y = y - b Call forward_solve(nd, nnz, a, y, y, irw, jcol, w) Forall (i=1:nd) ! 这里可以直接用Z=V*Y代替,主要目的是为了方便替换V,而且forall的效率不错^_^ z(i) = -v(i)*y(i) End Forall Call back_solve(nd, nnz, a, z, d, irw, jcol, w) it = 0 delta = m_dot_product(y, v*y, nd) delta0 = delta beta = 1 Do i = 1, itmax ! if( abs(delta) < EPS) then ! return ! endif tao = delta/m_dot_product(d, 2.0*z-v*d, nd) x = x + tao*d ! 多种收敛方式 ! if( sqrt(sum(abs(tao*D/X)**2)) < EPS ) then ! return ! endif Call forward_solve(nd, nnz, a, z-v*d, temp, irw, jcol, w) y = y + tao*(temp+d) delta1 = m_dot_product(y, v*y, nd) beta = delta1/delta z = -v*y + beta*z Call back_solve(nd, nnz, a, z, d, irw, jcol, w) delta = delta1 it = it + 1 reg_err = abs(delta/delta0) ! write(*,*) 'PCG iteration:',i,'of res:',reg_err If (reg_err<eps) Then Write (*, *) 'PCG iteration:', i, 'of res:', reg_err Return End If End Do Return End Subroutine ssor_pcg_csr ! ================this is split line================================================! Subroutine matmulvec_csr(nd, nnz, mat, vec, vec_out, irw, jcol) Implicit None ! *********************************************************************************** ! ** 输入和输出 ! *********************************************************************************** Integer, Intent (In) :: nd, nnz Integer, Intent (In) :: irw(nnz), jcol(nd+1) ! CSR压缩存储索引矩阵pointB,pointE Complex (Kind=8), Intent (In) :: mat(nnz), vec(nd) ! 矩阵变量MAT和向量VEC Complex (Kind=8) :: vec_out(nd) ! 输出最终结果 ! *********************************************************************************** ! ** 程序中间变量 ! *********************************************************************************** Integer :: i, j vec_out = (0.0D0, 0.0D0) Do i = 1, nd vec_out(i) = vec_out(i) + mat(jcol(i))*vec(i) Do j = jcol(i) + 1, jcol(i+1) - 1 vec_out(i) = vec_out(i) + mat(j)*vec(irw(j)) vec_out(irw(j)) = vec_out(irw(j)) + mat(j)*vec(i) End Do End Do Return End Subroutine matmulvec_csr ! ================this is split line================================================! Subroutine forward_solve(nd, nnz, mat, vec, vec_out, irw, jcol, w) Implicit None ! *********************************************************************************** ! ** 输入和输出 ! *********************************************************************************** Integer, Intent (In) :: nd, nnz Integer, Intent (In) :: irw(nnz), jcol(nd+1) ! CSR压缩存储索引矩阵pointB,pointE Complex (Kind=8), Intent (In) :: mat(nnz), vec(nd) ! 矩阵变量MAT和向量VEC Real (Kind=8), Intent (In) :: w Complex (Kind=8), Intent (Out) :: vec_out(nd) ! 输出最终结果 ! *********************************************************************************** ! ** 程序中间变量 ! *********************************************************************************** Integer :: i, j vec_out = vec Do i = 1, nd vec_out(i) = vec_out(i)/mat(jcol(i))*w Do j = jcol(i) + 1, jcol(i+1) - 1 vec_out(irw(j)) = vec_out(irw(j)) - mat(j)*vec_out(i) End Do End Do Return End Subroutine forward_solve ! ================this is split line================================================! Subroutine back_solve(nd, nnz, mat, vec, vec_out, irw, jcol, w) Implicit None ! *********************************************************************************** ! ** 输入和输出 ! *********************************************************************************** Integer, Intent (In) :: nd, nnz Integer, Intent (In) :: irw(nnz), jcol(nd+1) ! CSR压缩存储索引矩阵pointB,pointE Complex (Kind=8), Intent (In) :: mat(nnz), vec(nd) ! 矩阵变量MAT和向量VEC Real (Kind=8), Intent (In) :: w Complex (Kind=8), Intent (Out) :: vec_out(nd) ! 输出最终结果 ! *********************************************************************************** ! ** 程序中间变量 ! *********************************************************************************** Integer :: i, j Complex (Kind=8) :: sum Do i = nd, 1, -1 sum = (0.0D0, 0.0D0) Do j = jcol(i+1) - 1, jcol(i) + 1, -1 sum = sum + mat(j)*vec_out(irw(j)) End Do vec_out(i) = (vec(i)-sum)/mat(jcol(i))*w End Do Return End Subroutine back_solve ! ================this is split line================================================! Function m_dot_product(a_in, b_in, n) ! *********************************************************************************** ! ** 向量的内积 ! *********************************************************************************** Implicit None Integer :: n Complex (Kind=8) :: a_in(n), b_in(n) Complex (Kind=8) :: m_dot_product Integer :: i m_dot_product = 0.0 Do i = 1, n m_dot_product = m_dot_product + a_in(i)*b_in(i) End Do Return End Function m_dot_product ! ================this is split line================================================! Subroutine id_change(nd, nnz, irw, jcol, icol, jrow, id_ch) ! *********************************************************************************** ! ** 矩阵A影射,由下三角CSC压缩存储影射到下三角CSR压缩存储 ! *********************************************************************************** Implicit None ! *********************************************************************************** ! ** 输入和输出 ! *********************************************************************************** Integer, Intent (In) :: nd, nnz Integer, Intent (In) :: irw(nnz), jcol(nd+1) ! CSR压缩存储索引矩阵pointB,pointE Integer, Intent (Out) :: icol(nnz), jrow(nd) ! CSC压缩存储索引矩阵pointB,pointE Integer, Intent (Out) :: id_ch(nnz) ! *********************************************************************************** ! ** 程序中间变量 ! *********************************************************************************** Integer :: i, j, k, now_col_nnz, n icol(1) = 1 jrow(1) = 1 now_col_nnz = 1 id_ch(1) = 1 n = 1 Do i = 2, nd now_col_nnz = 0 Do j = 1, i - 1 Do k = jcol(j) + 1, jcol(j+1) - 1 If (irw(k)==i) Then now_col_nnz = now_col_nnz + 1 n = n + 1 icol(n) = j id_ch(n) = k End If End Do End Do now_col_nnz = now_col_nnz + 1 n = n + 1 icol(n) = i id_ch(n) = jcol(i) jrow(i) = jrow(i-1) + now_col_nnz End Do Return End Subroutine id_change ! 下面是测试程序 Program www_fcode_cn Implicit None Complex (Kind=8) , allocatable :: x(:) , a(:) , rhs(:) integer , allocatable :: jcol(:) , irw(:) Real (Kind=8) :: w, eps, reg_err Integer :: it , itmax , n , nnz itmax = 1000 eps = 1.0D-20 w = 1 ! 获得 n 和 nnz Allocate (x(n),a(nnz),rhs(n),irw(nnz),jcol(n+1)) ! 获得 a rhs x = 0.0D0 ! 调用程序 Call ssor_pcg_csr(n, nnz, a, rhs, x, irw ,jcol, eps, itmax, w, it, reg_err) ! 输出解 ! write(*,*) X,IT,REG_ERR(IT) Stop End Program www_fcode_cn