SOR程序不收敛(FORTRAN到C转换)
我们正在尝试将FORTRAN程序转换为C。这是一个SOR实现,它应该在大约200次迭代中收敛,但C代码不起作用。大约在第100次迭代时,它变得“奇怪”(它开始非常缓慢地收敛) 以下是原始代码:SOR程序不收敛(FORTRAN到C转换),c,fortran,C,Fortran,我们正在尝试将FORTRAN程序转换为C。这是一个SOR实现,它应该在大约200次迭代中收敛,但C代码不起作用。大约在第100次迭代时,它变得“奇怪”(它开始非常缓慢地收敛) 以下是原始代码: PROGRAM Main ! *** Solution of Laplace's Equation. ! *** ! *** Uxx + Uyy = 0 ! *** 0 <= x <= pi, 0 <= y <= pi ! *** U(x,pi) = sin(x), U(
PROGRAM Main
! *** Solution of Laplace's Equation.
! ***
! *** Uxx + Uyy = 0
! *** 0 <= x <= pi, 0 <= y <= pi
! *** U(x,pi) = sin(x), U(x,0) = U(0,y) = U(pi,y) = 0
! ***
! *** then U(x,y) = (sinh(y)*sin(x)) / sinh(pi)
! ***
! *** Should converge with
! *** tol = 0.001 and M = 20 in 42 iterations.
! *** and with tol = 0.001 and M = 100 in 198 iterations.
! ***
INTEGER M
PARAMETER (M = 100)
REAL*8 PI
REAL*8 DATAN
REAL*8 unew(0:M+1,0:M+1), uold(0:M+1,0:M+1)
REAL*8 solution(0:M+1,0:M+1)
REAL*8 omega, tol, h
INTEGER i, j, iters
PI = 4.0D0*DATAN(1.0D0)
h = PI/FLOAT(M+1)
DO i=0,M+1
uold(i,M+1) = SIN(FLOAT(i)*h)
END DO
DO i=0,M+1
DO j=0,M
uold(i,j) = FLOAT(j)*h*uold(i,M+1)
END DO
END DO
DO i=0,M+1
DO j=0,M+1
solution(i,j) = SINH(FLOAT(j)*h)*SIN(FLOAT(i)*h)/SINH(PI)
END DO
END DO
omega = 2.0/(1.0+SIN(PI/FLOAT(M+1)))
tol = 0.001
CALL SOR (unew, uold, solution, omega, tol, m, iters)
PRINT *, " "
PRINT *, " Omega = ", omega
PRINT *, " It took ", iters, " iterations."
STOP
END PROGRAM Main
REAL*8 FUNCTION ComputeError (solution, u, m, iters)
INTEGER m, iters
REAL*8 u(0:m+1,0:m+1)
REAL*8 solution(0:m+1,0:m+1)
! *** Local variables
REAL*8 error
INTEGER i, j
error = 0.0
DO j=1,m
DO i=1,m
error = MAX(error, ABS(solution(i,j)-u(i,j)))
END DO
END DO
PRINT *, "On iteration ", iters, " error = ", error
ComputeError = error
RETURN
END FUNCTION ComputeError
SUBROUTINE SOR (unew, uold, solution, omega, tol, m, iters)
INTEGER m, iters
REAL*8 unew(0:m+1,0:m+1), uold(0:m+1,0:m+1)
REAL*8 solution(0:m+1,0:m+1)
REAL*8 omega, tol
! *** Local variables
REAL*8 error
INTEGER i, j
! *** External function.
REAL*8 ComputeError
EXTERNAL ComputeError
! *** Copy bonudary conditions.
DO i=0,m+1
! print *,i
unew(i,m+1) = uold(i,m+1)
unew(m+1,i) = uold(m+1,i)
unew(i, 0) = uold(i, 0)
unew(0, i) = uold(0, i)
END DO
! *** Do SOR until 'tol' satisfied.
iters = 0
error = ComputeError (solution, uold, m, iters)
DO WHILE (error .GE. tol)
! *** Do one iteration of SOR
DO j=1,m
DO i=1,m
unew(i,j) = uold(i,j) + 0.25*omega* &
(unew(i-1,j) + unew(i,j-1) + &
uold(i+1,j) + uold(i,j+1) - &
4.0*uold(i,j))
END DO
END DO
! *** Copy new to old.
DO j=1,m
DO i=1,m
uold(i,j) = unew(i,j)
END DO
END DO
! *** Check error every 20 iterations.
iters = iters + 1
IF (MOD(iters,2) .EQ. 0) THEN
error = ComputeError (solution, uold, m, iters)
END IF
END DO
RETURN
END SUBROUTINE SOR
主程序
! *** 拉普拉斯方程的解。
! ***
! *** Uxx+Uyy=0
! *** 0您对iters有问题,您正在通过值传递,但它看起来应该通过引用传递(即作为C中的指针)。(请注意,gcc会为此生成一个警告-请确保您正在使用-Wall
进行编译,以捕获此类问题。)
看起来你也误译了这句话:
h = pi / (M + 1);
它很可能是:
h = pi / (M + 2);
通过此更改,我在196次迭代后获得收敛:
...
On iteration 192 error = 0.001293
On iteration 194 error = 0.001104
On iteration 196 error = 0.000930
Omega = 1.939676
It took 196 iterations.
(这还包括对iters
bug的修复)
当然,可能还有其他的bug需要修复…可能不是直接的问题,但是您的iters
有一个问题,您通过值传递它,但它看起来应该通过引用传递(即作为C中的指针)。请注意,gcc会为此生成警告-请确保您是使用-Wall
编译的。为什么要将Fortran代码转换为这样一个简单的经典问题?有几十个C实现。@VladimirF这是一些类的额外练习。@VladimirF:OK-在iters
单独修复后,它仍然没有收敛,但是修复h
计算似乎也能解决问题。实际上,这取决于你如何度量收敛。因为在这里,你用真正的解来衡量它,它实际上以前没有收敛。它实际上收敛到了一个错误的解,一些测量收敛性的方法会显示它也在收敛。干杯,伙计!你真棒!
...
On iteration 192 error = 0.001293
On iteration 194 error = 0.001104
On iteration 196 error = 0.000930
Omega = 1.939676
It took 196 iterations.