Fortran program fails depending on a write statement before subroutine call

牧云@^-^@ 提交于 2019-12-24 14:33:16

问题


It's been a number of years since I've worked with Fortran, so maybe I'm missing a fundamental issue, but here it goes. I'm not even sure how to properly describe this issue, so I apologize in advance for a lack of descriptive information.

I'm writing some Fortran modules to supplement a Python program using f2py. Everything seems to be working fine, but I am encountering some strange errors in one subroutine. I couldn't replicate the issue in a small sample program, so I stripped out the relevant subroutines from the module and generated a small test main program. The main program is:

PROGRAM MAIN
USE EVALUATE
IMPLICIT NONE

INTEGER :: N=8, P=2, D, I, J
DOUBLE PRECISION :: U, UK(0:11), CPW(0:8, 0:3), CK(0:1, 0:3)

D = 1
U = 0.45
UK = (/0.D0, 0.D0, 0.D0, 0.25D0, 0.25D0, 0.5D0, 0.5D0, 0.75D0, &
     0.75D0, 1.D0, 1.D0, 1.D0 /)

CPW(0, :) = (/1.D0, 0.D0, 0.D0, 1.D0 /)
CPW(1, :) = (/.707D0, .707D0, 0.D0, .707D0 /)
CPW(2, :) = (/0.D0, 1.D0, 0.D0, 1.D0 /)
CPW(3, :) = (/-.707D0, .707D0, 0.D0, .707D0 /)
CPW(4, :) = (/-1.D0, 0.D0, 0.D0, 1.D0 /)
CPW(5, :) = (/-.707D0, -.707D0, 0.D0, .707D0 /)
CPW(6, :) = (/0.D0, -1.D0, 0.D0, 1.D0 /)
CPW(7, :) = (/.707D0, -.707D0, 0.D0, .707D0 /)
CPW(8, :) = (/1.D0, 0.D0, 0.D0, 1.D0 /)

! This is commented out for the first and second results.
WRITE(*,*) "FOO.BAR"

CALL RAT_CURVE_DERIVS(N, P, UK, CPW, U, D, CK)

WRITE(*,*) "WRITING RESULTS"
DO I = 0, D
WRITE(*, '(100G15.5)') (CK(I, J), J = 0, 2)
END DO

END PROGRAM

Note that all my arrays start at 0. I am doing this since I usually develop the methods in Python first using numpy and then rewrite in Fortran, and for the program as a whole, it's more natural to start arrays at 0 rather than 1. In the actual program all the variables specified in the main program come from Python.

The subroutine RAT_CURVE_DERIVS in EVALUATE is:

SUBROUTINE RAT_CURVE_DERIVS(N, P, UK, CPW, U, D, CK)
IMPLICIT NONE

!F2PY INTENT(IN) N, P, UK, CPW, U, D
!F2PY INTENT(OUT) CK
!F2PY DEPEND(N, P) UK
!F2PY DEPEND(N) CPW
!F2PY DEPEND(D) CK

INTEGER, INTENT(IN) :: N, P, D
DOUBLE PRECISION, INTENT(IN) :: U, UK(0:N + P + 1), CPW(0:N, 0:3)
DOUBLE PRECISION, INTENT(OUT) :: CK(0:D, 0:2)

INTEGER :: I, K, J, X
DOUBLE PRECISION :: BC, V(0:2), CDERS(0:D, 0:3)
DOUBLE PRECISION :: ADERS(0:D, 0:2), WDERS(0:D)

CALL CURVE_DERIVS_ALG1(N, P, UK, CPW, U, D, CDERS)
ADERS = CDERS(:, 0:2)
WDERS = CDERS(:, 3)
DO K = 0, D
    V = ADERS(K, :)
    DO I = 1, K
        CALL BINOMIAL(K, I, BC)
        V = V - BC * WDERS(I) * CK(K - I, :)
    END DO
    CK(K, :) = V / WDERS(0)
END DO
END SUBROUTINE RAT_CURVE_DERIVS

Again the arrays start at 0 and the upper bound usually depends on an input to the subroutine. This subroutine calls others, but they are not shown.

The compile commands and results are shown below. You can see the first results are bogus. The second results using -fno-backtrace are the correct results. The third results are compiled as the first, but a write statements is inserted before the call to the subroutine, and the results are correct.

C:\Users\Trevor\Documents\Temp>gfortran evaluate.f90 main.f90

C:\Users\Trevor\Documents\Temp>a.exe
 WRITING RESULTS
   -0.16453-170    0.19209E-33    0.69763E+58
    0.70809E-65   -0.82668E+72      -Infinity

C:\Users\Trevor\Documents\Temp>gfortran evaluate.f90 main.f90 -fno-backtrace

C:\Users\Trevor\Documents\Temp>a.exe
 WRITING RESULTS
   -0.95586        0.29379         0.0000
    -1.8340        -5.9662         0.0000

C:\Users\Trevor\Documents\Temp>gfortran evaluate.f90 main.f90

C:\Users\Trevor\Documents\Temp>a.exe
 FOO.BAR
 WRITING RESULTS
   -0.95586        0.29379         0.0000
    -1.8340        -5.9662         0.0000

C:\Users\Trevor\Documents\Temp>

For some reason, adding a write statement before calling the subroutine makes it "work." I am not completely familiar with the -fno-backtrace option, but it makes it "work" too. I added this option when compiling using f2py, and I still get strange results, but one thing at a time I guess. In Python, I will call this subroutine 10 times in a loop with the same inputs, and 8 out of 10 result will be correct, but 2 will be bogus, but I digress...

Thanks for the help and any suggestions.

UPDATE 1:

The subroutine CURVE_DERIVS_ALG1 is shown below. It too calls other subroutines, but they are not shown for brevity. I also compiled with -fbounds-check and got the same bogus results shown above.

SUBROUTINE CURVE_DERIVS_ALG1(N, P, UK, CPW, U, D, CK)
    IMPLICIT NONE

    !F2PY INTENT(IN) N, P, UK, CPW, U, D
    !F2PY INTENT(OUT) CK
    !F2PY DEPEND(N, P) UK
    !F2PY DEPEND(N) CPW
    !F2PY DEPEND(D) CK

    INTEGER, INTENT(IN) :: N, P, D
    DOUBLE PRECISION, INTENT(IN) :: U, UK(0:N + P + 1), CPW(0:N, 0:3)
    DOUBLE PRECISION, INTENT(OUT) :: CK(0:D, 0:3)

    INTEGER :: DU, K, SPAN, J, M
    DOUBLE PRECISION :: NDERS(0:MIN(D,P), 0:P)

    DU = MIN(D, P)
    M = N + P + 1
    CALL FIND_SPAN(N, P, U, UK, SPAN)
    CALL DERS_BASIS_FUNS(SPAN, U, P, DU, UK, M, NDERS)
    DO K = 0, DU
        DO J = 0, P
            CK(K, :) = CK(K, :) + NDERS(K, J) * CPW(SPAN - P + J, :)
        END DO
    END DO
END SUBROUTINE CURVE_DERIVS_ALG1

UPDATE 2: Sorry for the long post, but the entire module is posted below in case anyone wants to try and run it with the main program above.

! FORTRAN source for geometry.tools.evaluate
MODULE EVALUATE
CONTAINS


SUBROUTINE FIND_SPAN(N, P, U, UK, MID)
    IMPLICIT NONE

    !F2PY INENT(IN) N, P, U, UK
    !F2PY INTENT(OUT) MID
    !F2PY DEPEND(N, P) UK

    INTEGER, INTENT(IN) :: N, P
    DOUBLE PRECISION, INTENT(IN) :: U
    DOUBLE PRECISION, INTENT(IN) :: UK(0:N + P + 1)
    INTEGER, INTENT(OUT) :: MID

    INTEGER :: LOW, HIGH

    ! SPECIAL CASE
    IF (U .EQ. UK(N + 1)) THEN
        MID = N
        RETURN
    END IF

    LOW = P
    HIGH = N + 1
    MID = (LOW + HIGH) / 2
    DO WHILE ((U .LT. UK(MID)) .OR. (U .GE. UK(MID + 1)))
        IF (U .LT. UK(MID)) THEN
            HIGH = MID
        ELSE
            LOW = MID
        END IF
        MID = (LOW + HIGH) / 2
    END DO
END SUBROUTINE FIND_SPAN


SUBROUTINE BASIS_FUNS(I, U, P, UK, M, N)
    IMPLICIT NONE

    !F2PY INTENT(IN) I, U, P, UK, M
    !F2PY INTENT(OUT) N
    !F2PY DEPEND(M) UK

    INTEGER, INTENT(IN) :: I, P, M
    DOUBLE PRECISION, INTENT(IN) :: U
    DOUBLE PRECISION, INTENT(IN) :: UK(0:M)
    DOUBLE PRECISION, INTENT(OUT) :: N(0:P)

    INTEGER :: J, R
    DOUBLE PRECISION :: TEMP, SAVED
    DOUBLE PRECISION :: LEFT(0:P), RIGHT(0:P)

    N(0) = 1.D0
    DO J = 1, P
        LEFT(J) = U - UK(I + 1 - J)
        RIGHT(J) = UK(I + J) - U
        SAVED = 0.D0
        DO R = 0, J - 1
            TEMP = N(R) / (RIGHT(R + 1) + LEFT(J - R))
            N(R) = SAVED + RIGHT(R + 1) * TEMP
            SAVED = LEFT(J - R) * TEMP
        END DO
        N(J) = SAVED
    END DO
END SUBROUTINE BASIS_FUNS


SUBROUTINE DERS_BASIS_FUNS(I, U, P, N, UK, M, DERS)
    IMPLICIT NONE

    !F2PY INTENT(IN) I, U, P, N, UK, M
    !F2PY INTENT(OUT) DERS
    !F2PY DEPEND(M) UK

    INTEGER, INTENT(IN) :: I, P, N, M
    DOUBLE PRECISION, INTENT(IN) :: U
    DOUBLE PRECISION, INTENT(IN) :: UK(0:M)
    DOUBLE PRECISION, INTENT(OUT) :: DERS(0:N, 0:P)

    INTEGER :: J, K, R, J1, J2, RK, PK, S1, S2
    DOUBLE PRECISION :: SAVED, TEMP, NDU(0:P, 0:P), LEFT(0:P), &
                        RIGHT(0:P), A(0:1, 0:P), D

    NDU(0, 0) = 1.D0
    DO J = 1, P
        LEFT(J) = U - UK(I + 1 - J)
        RIGHT(J) = UK(I + J) - U
        SAVED = 0.D0
        DO R = 0, J - 1
            NDU(J, R) = RIGHT(R + 1) + LEFT(J - R)
            TEMP = NDU(R, J - 1) / NDU(J, R)
            NDU(R, J) = SAVED + RIGHT(R + 1) * TEMP
            SAVED = LEFT(J - R) * TEMP
        END DO
        NDU(J, J) = SAVED
    END DO
    DO J = 0, P
        DERS(0, J) = NDU(J, P)
    END DO
    DO R = 0, P
        S1 = 0
        S2 = 1
        A(0, 0) = 1.D0
        DO K = 1, N
            D = 0.D0
            RK = R - K
            PK = P - K
            IF (R .GE. K) THEN
                A(S2, 0) = A(S1, 0) / NDU(PK + 1, RK)
                D = A(S2, 0) * NDU(RK, PK)
            END IF
            IF (RK .GE. -1) THEN
                J1 = 1
            ELSE
                J1 = -RK
            END IF
            IF (R - 1 .LE. PK) THEN
                J2 = K - 1
            ELSE
                J2 = P - R
            END IF
            DO J = J1, J2
                A(S2, J) = (A(S1, J) - A(S1, J - 1)) / &
                            NDU(PK + 1, RK + J)
                D = D + A(S2, J) * NDU(RK + J, PK)
            END DO
            IF (R .LE. PK) THEN
                A(S2, K) = -A(S1, K - 1) / NDU(PK + 1, R)
                D = D + A(S2, K) * NDU(R, PK)
            END IF
            DERS(K, R) = D
            J = S1
            S1 = S2
            S2 = J
        END DO
    END DO
    R = P
    DO K = 1, N
        DO J = 0, P
            DERS(K, J) = DERS(K, J) * R
        END DO
        R = R * (P - K)
    END DO
END SUBROUTINE DERS_BASIS_FUNS


SUBROUTINE CURVE_DERIVS_ALG1(N, P, UK, CPW, U, D, CK)
    IMPLICIT NONE

    !F2PY INTENT(IN) N, P, UK, CPW, U, D
    !F2PY INTENT(OUT) CK
    !F2PY DEPEND(N, P) UK
    !F2PY DEPEND(N) CPW
    !F2PY DEPEND(D) CK

    INTEGER, INTENT(IN) :: N, P, D
    DOUBLE PRECISION, INTENT(IN) :: U, UK(0:N + P + 1), CPW(0:N, 0:3)
    DOUBLE PRECISION, INTENT(OUT) :: CK(0:D, 0:3)

    INTEGER :: DU, K, SPAN, J, M
    DOUBLE PRECISION :: NDERS(0:MIN(D,P), 0:P)

    DU = MIN(D, P)
    M = N + P + 1
    CALL FIND_SPAN(N, P, U, UK, SPAN)
    CALL DERS_BASIS_FUNS(SPAN, U, P, DU, UK, M, NDERS)
    DO K = 0, DU
        DO J = 0, P
            CK(K, :) = CK(K, :) + NDERS(K, J) * CPW(SPAN - P + J, :)
        END DO
    END DO
END SUBROUTINE CURVE_DERIVS_ALG1


SUBROUTINE RAT_CURVE_DERIVS(N, P, UK, CPW, U, D, CK)
    IMPLICIT NONE

    !F2PY INTENT(IN) N, P, UK, CPW, U, D
    !F2PY INTENT(OUT) CK
    !F2PY DEPEND(N, P) UK
    !F2PY DEPEND(N) CPW
    !F2PY DEPEND(D) CK

    INTEGER, INTENT(IN) :: N, P, D
    DOUBLE PRECISION, INTENT(IN) :: U, UK(0:N + P + 1), CPW(0:N, 0:3)
    DOUBLE PRECISION, INTENT(OUT) :: CK(0:D, 0:2)

    INTEGER :: I, K, J, X
    DOUBLE PRECISION :: BC, V(0:2), CDERS(0:D, 0:3)
    DOUBLE PRECISION :: ADERS(0:D, 0:2), WDERS(0:D)

    CALL CURVE_DERIVS_ALG1(N, P, UK, CPW, U, D, CDERS)
    ADERS = CDERS(:, 0:2)
    WDERS = CDERS(:, 3)
    DO K = 0, D
        V = ADERS(K, :)
        DO I = 1, K
            CALL BINOMIAL(K, I, BC)
            V = V - BC * WDERS(I) * CK(K - I, :)
        END DO
        CK(K, :) = V / WDERS(0)
    END DO
END SUBROUTINE RAT_CURVE_DERIVS


SUBROUTINE BINOMIAL(N, K, BC)
    IMPLICIT NONE

    !F2PY INTENT(IN) N, K
    !F2PY INTENT(OUT) BC

    INTEGER, INTENT(IN) :: N, K
    DOUBLE PRECISION, INTENT(OUT) :: BC

    INTEGER :: I, KK

    IF ((K .LT. 0) .OR. ( K .GT. N)) THEN
        BC = 0.D0
        RETURN
    END IF
    IF ((K .EQ. 0) .OR. ( K .EQ. N)) THEN
        BC = 1.D0
        RETURN
    END IF
    KK = MIN(K, N - K)
    BC = 1.D0
    DO I = 0, KK - 1
        BC = BC * DBLE(N - I) / DBLE(I + 1)
    END DO
END SUBROUTINE BINOMIAL
END MODULE

回答1:


In the subroutine CURVE_DERIVS_ALG1, the dummy argument CK seems not initialized, so could you check its initial value? If I set it to 0.0d0 before entering the loop, the code seems to work fine, but I am not sure if this initial value is OK. (Please also note that if INTENT(OUT) is given, all the elements must be defined.)

SUBROUTINE CURVE_DERIVS_ALG1(N, P, UK, CPW, U, D, CK)
    ...
    DOUBLE PRECISION, INTENT(OUT) :: CK(0:D, 0:3)
    ...    
    CALL FIND_SPAN(N, P, U, UK, SPAN)
    CALL DERS_BASIS_FUNS(SPAN, U, P, DU, UK, M, NDERS)

    CK(:,:) = 0.0d0          !! <--- here

    DO K = 0, DU
        DO J = 0, P
            CK(K, :) = CK(K, :) + NDERS(K, J) * CPW(SPAN - P + J, :)
            ...

Another potential issue is

IF (U .EQ. UK(N + 1)) THEN

which compares two floating-point numbers. Although this condition seems not met in this program, it is probably safer to rewrite this as, e.g.

IF ( abs( U - UK(N + 1) ) < 1.0d-10 ) THEN    !! threshold depends on your need...

EDIT: To detect the above error of CK automatically, it may be useful to compile as

gfortran -finit-real=snan -ffpe-trap=invalid evaluate.f90 main.f90

which gives (with gfortran4.8 on Linux x86_64)

Program received signal 8 (SIGFPE): Floating-point exception.

Backtrace for this error:
#0  0x00000039becac5f4 in wait () from /lib64/libc.so.6
#1  0x00000039c501400d in ?? () from /usr/lib64/libgfortran.so.3
#2  0x00000039c501582e in ?? () from /usr/lib64/libgfortran.so.3
#3  0x00000039c50146ca in ?? () from /usr/lib64/libgfortran.so.3
#4  <signal handler called>
#5  0x0000000000401787 in __evaluate_MOD_curve_derivs_alg1 ()    <--- here
#6  0x0000000000400fce in __evaluate_MOD_rat_curve_derivs ()
#7  0x0000000000402b26 in MAIN__ ()
#8  0x0000000000402cbb in main ()


来源:https://stackoverflow.com/questions/33975067/fortran-program-fails-depending-on-a-write-statement-before-subroutine-call

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!