数组赋值会删除数组

时间:2016-05-22 15:03:09

标签: arrays fortran

我正在运行以下代码,即执行Runge-Kutta方法来求解微分方程组。 主代码只调用rk子例程,这是实现本身,myfun只是测试代码的一个例子。

program main

    use ivp_odes

    implicit none

    double precision, allocatable :: t(:), y(:,:)
    double precision :: t0, tf, y0(2), h
    integer :: i

    t0 = 0d0
    tf = 0.5d0
    y0 = [0d0, 0d0]
    h = 0.1d0

    call rk4(t, y, myfun, t0, tf, y0, h)

    do i=0,size(t)
        print *, t(i), y(:,i)
    end do

contains

pure function myfun(t,y) result(dy)
    ! input variables
    double precision, intent(in) :: t, y(:)

    ! output variables
    double precision :: dy(size(y))

    dy(1) = -4*y(1) + 3*y(2) + 6
    dy(2) = -2.4*y(1) + 1.6*y(2) + 3.6
end function myfun

end program main

,子程序在模块内:

module ivp_odes
    implicit none

contains

subroutine rk4(t, y, f, t0, tf, y0, h)
    ! input variables
    double precision, intent(in) :: t0, tf, y0(1:)
    double precision, intent(in) :: h

    interface
        pure function f(t,y0) result(dy)
            double precision, intent(in) :: t, y0(:)
            double precision :: dy(size(y))
        end function
    end interface

    ! output variables
    double precision, allocatable :: t(:), y(:,:)

    ! Variáveis auxiliares
    integer :: i, m, NN
    double precision, allocatable :: k1(:), k2(:), k3(:), k4(:)

    m = size(y0)
    allocate(k1(m),k2(m),k3(m),k4(m))

    NN = ceiling((tf-t0)/h)
    if (.not. allocated(y)) then
        allocate(y(m,0:NN))
    else
        deallocate(y)
        allocate(y(m,0:NN))
    end if

    if (.not. allocated(t)) then
        allocate(t(0:NN))
    else
        deallocate(t)
        allocate(t(0:NN))
    end if

    t(0) = t0
    y(:,0) = y0


    do i=1,NN
        k1(:) = h * f(t(i-1)     , y(:,i-1)        )
        k2(:) = h * f(t(i-1)+h/2 , y(:,i-1)+k1(:)/2)
        k3(:) = h * f(t(i-1)+h/2 , y(:,i-1)+k2(:)/2)
        k4(:) = h * f(t(i-1)+h   , y(:,i-1)+k3(:)  )

        y(:,i) = y(:,i-1) + (k1(:) + 2*k2(:) + 2*k3(:) + k4(:))/6
        t(i) = t(i-1) + h
    end do

    deallocate(k1,k2,k3,k4)
    return

end subroutine rk4

end module ivp_odes

这里的问题是rk子程序

中的赋值
y(:,i) = y(:,i-1) + (k1(:) + 2*k2(:) + 2*k3(:) + k4(:))/6

正在删除之前计算的值。在do-loop的第i次迭代中,它会擦除​​数组y的先前值,并仅分配数组y的第i列,因此当子例程结束时,{{ 1}}只保存最后一个值。 由于Fortran已经实现了元素操作和数组赋值,因此我认为代码更易于阅读,并且可能比对循环中的每个元素进行赋值运行得更快。那么,为什么它不起作用?我在这里的任务中缺少什么?它不应该只改变第i行的值,而不是删除数组的其余部分吗?

1 个答案:

答案 0 :(得分:4)

这是从边界访问数组的典型情况。您可以使用适当的编译器标志轻松找到这些错误。使用gfortran,这将是-fbounds-check

通过这样的检查,您会发现错误是接口块中函数结果的错误大小 - dy应该与y0具有相同的长度(f的一维伪参数{1}}),而不是y

    interface
        pure function f(t,y0) result(dy)
            double precision, intent(in) :: t, y0(:)
            double precision :: dy(size(y0))
        end function
    end interface

此外,虽然与您的特定错误无关,但您开始将ty的第二维索引为零。因此,您需要将主程序运行中的循环仅调整为size(t)-1,或使用ubound(t)。否则,您将再次超出阵列的边界。