使用OpenMP的Fortran Gauss Siedel迭代求解器无法收敛

时间:2019-04-07 13:57:43

标签: fortran openmp

我想使用OpenMP编写高斯Siedel迭代求解器。我的求解器无法收敛到正确的结果,因此我无法弄清原因。

控制方程是稳态热方程:del ^ 2(T)= S,其中S是热源函数。 S = -35 * pi / 2 * cos(pi * x / 2)* cos(3 * pi * y / 2)* cos(5 * pi * z / 2)

我在dowhile循环内实现OpenMP,因为它不允许我从do while循环开始并行运行。有什么方法可以将do while循环更改为do循环吗?

结果在没有并行计算的情况下会收敛,但是在添加了openmp之后,它将不再收敛。

这是我的代码:

    PROGRAM GAUSS_MP
        USE omp_lib
        IMPLICIT NONE

        REAL*8, DIMENSION(:,:,:), ALLOCATABLE :: S, T
        REAL*8 :: X, Y, Z
        REAL*8, PARAMETER :: PI=2*ASIN(1.0)
        REAL*8 :: DX                     ! STEP SIZE DX=DY=DZ
        REAL*8, PARAMETER :: TOL=1.0E-5  ! TOLERANCE 
        REAL*8 :: DUMAX
        REAL*8 :: T_OLD
        REAL*8 :: T1,T2

        INTEGER, PARAMETER :: N=100        ! GRID NUMBER, START FROM 1
        INTEGER, PARAMETER :: ITERMAX=1E5   ! MAXIMUM ITERATION
        INTEGER :: I, J, ITER, K
        INTEGER :: POINT_NUM
        INTEGER, PARAMETER :: NUM_THREADS=32
    !    INTEGER :: A

        ! INITIALIZE OPENMP THREADS
        CALL OMP_SET_NUM_THREADS(NUM_THREADS)


        ! COMPUTE STEP SIZE
        DX=2.0/REAL(N-1, KIND=8)  
    !    PRINT *, "DX=", DX

        ! INITIALIZE THE T ARRAYS AND S(I)
        ALLOCATE(S(N,N,N), T(N,N,N))
            ! INITIAL GUESS
            T=1.0
        ! BOUNDARY CONDITION
        T(1,:,:)=0.0; T(N,:,:)=0.0; T(:,:,1)=0.0; T(:,:,N)=0.0;
        T(:,1,:)=0.0; T(:,N,:)=0.0;

        X=0.0D0 ! COORDINATES
        Y=0.0D0

        S=0.0D0 ! SOURCE
            ! SOURCE MATRIX
            !$OMP PARALLEL DO PRIVATE(I,J,K)
             DO K=2,N-1
                Z=-1.0+(K-1)*DX
                DO I=2,N-1
                    Y=-1.0+(I-1)*DX
                    DO J=2,N-1
                        X=-1.0+(J-1)*DX
                        S(I,J,K)=-35.0*PI/2.0*COS(PI*X/2.0)*COS(3.0*PI*Y/2.0)&
                                *COS(5.0*PI*Z/2.0)
                    END DO
                END DO
            END DO
            !$OMP END PARALLEL DO
        ! GAUSS-SEIDEL ITERATION
        PRINT *, 'PARALLEL START'
        T1=OMP_GET_WTIME()

        ITER=0
        DUMAX=1.0D0 ! UPDATE DIFFERENCE
        DO WHILE(ITER <= ITERMAX .AND. DUMAX >= TOL)
            ITER=ITER+1
            DUMAX=0.0D0
            !$OMP PARALLEL PRIVATE(T_OLD, K, I, J, DUMAX)
            !$OMP DO REDUCTION(MAX:DUMAX)
            DO K=2,N-1
                DO I=2, N-1
                    DO J=2, N-1
                        T_OLD=T(I,J,K)
                        T(I,J,K)=1.0/6.0*(T(I+1,J,K)+T(I-1,J,K)+T(I,J-1,K)+T(I,J+1,K) &
                                    +T(I,J,K+1)+T(I,J,K-1) &
                                    -DX**2*S(I,J,K))
                        DUMAX=MAX(DUMAX, ABS(T_OLD-T(I,J,K)))
                    END DO
                END DO
            END DO
            !$OMP END DO
           !$OMP END PARALLEL
        END DO

        T2=OMP_GET_WTIME()

    END PROGRAM GAUSS_MP

1 个答案:

答案 0 :(得分:0)

Gauss-Seidel是无法轻松并行化的顺序算法。如果查看T数组的更新,您会看到您正在从其他线程读取值,这些值在当前线程尝试处理它们时可能已更新或未更新。那是典型的比赛条件。

您基本上有两个选择:

  • 使用倾斜的回路将“回路”嵌套“旋转” 45度,并使用波前穿过网格。这样,当当前线程要读取更新的值时,更新的单元格将可用。

  • 使用OpenMP 4.5功能“有序依赖”来表达代码中的数据依赖性,并让OpenMP编译器添加适当的同步以避免竞争条件。