我的 Fortran 动态分配代码有啥问题?

Posted

技术标签:

【中文标题】我的 Fortran 动态分配代码有啥问题?【英文标题】:What is wrong with my Fortran dynamic allocation code?我的 Fortran 动态分配代码有什么问题? 【发布时间】:2020-02-24 21:18:24 【问题描述】:

这是我在互联网上的第一个问题!!!另外,这是一个 Fortan 问题,我刚开始学习这种语言。所以,请原谅我的无知。具体来说,如果我的示例代码很大,我深表歉意。我已尽最大努力将其尺寸缩小到最低限度,同时又不影响其清晰度。

这是问题所在:我正在尝试创建一个动态仿真模型,该模型使用一个在模型运行期间将被多次调用的函数。函数本身是一个动态集成系统,因此必须在模拟的每个时间步保留函数的值,以便在下一个时间步使用它们。

该模型包括一个简单的库存 (P),它随着时间的推移以恒定的速率(每次 10%)减少。该函数是指数延迟。它需要 4 个参数:(输入、延迟时间、延迟顺序、输入初始值)。请注意,函数的输出具有 3 个维度。第一个维度代表程序中延迟调用的不同实例。第二个维度表示延迟的顺序。第三个维度代表模拟时间。

这里的主要挑战是动态分配将由函数创建的数组。为了解决这个问题,我尝试遵循此处提供的指南:How to increase array size on-the-fly in Fortran? 我编写的程序听起来在某些情况下运行良好,但并非总是如此。例如,如果您编译如下所示的代码,您将得到正确的结果(正确的行为是所有变量都应从 100 开始并逐渐下降)。但是如果你把 U 的延迟顺序从 5 改到 6,那么结果就会出错(U 从 100 开始,然后波动,有时会超过 100)。

我在这里缺少什么?我实现 move_alloc 的方式有什么问题吗?还是问题出在其他地方?可能是编译器(gfortran)失败了吗? 我真的很感谢您的帮助,提前!

program model

  implicit none
  real, parameter :: start = 0, end = 10, dt = 0.25
  integer, parameter :: n = int((end - start)/dt)
  real, dimension(0:n+1) :: time, P, R, S, W, U
  integer :: i
  real, dimension(:,:,:), allocatable :: DN, temp

  P(0) = 100
  time(0) = start

  open (unit=10, file='out.txt', status='replace', action='write', position='rewind')
  write (10, *) 'Time, ', 'P, ', 'R, ', 'S, ', 'W, ', 'U'

  do i = 0, n
     P(i+1) = P(i) - .1*P(i)*dt
     R(i) = delay(P(i), 3.0, 4, P(0))
     S(i) = delay(P(i), 2.0, 4, P(0))
     W(i) = delay(P(i), 4.0, 5, P(0))
     U(i) = delay(P(i), 1.0, 5, P(0))
     time(i+1) = time(i) + dt
     write (10, *) time(i), ',', P(i), ',', R(i), ',', S(i), ',', W(i), ',', U(i)
  end do

  close (10)

contains
  function delay(input, dtime, order, init)
    real :: delay
    real, intent(in) :: input, dtime, init
    integer, save :: j, k
    integer, intent(in) :: order
    integer :: l
    data j /0/
    data k /0/
    if (j == i) then
       k = k + 1
       allocate(temp(1:k, 1:order, 0:n+1))
       if (allocated(DN)) temp(1:k-1, 1:order, 0:n+1) = DN
       call move_alloc(temp, DN)
    else
       k = 1
       j = i
    end if
    if (i == 0) then
       do l = 1, order
          DN(k, l, 0) = init * dtime / order
       end do
    end if
    DN(k, 1, i+1) = DN(k, 1, i) + (input - DN(k, 1, i) * order / dtime) * dt
    if (order > 1) then
       do l = 2, order
          DN(k, l, i+1) = DN(k, l, i) + (DN(k, l-1, i) - DN(k, l, i)) * order * dt / dtime
       end do
    end if
    delay = DN(k, order, i) * order / dtime
  end function delay

end program model

【问题讨论】:

【参考方案1】:

您的数组索引不正确,应该使用编译器运行时检查来检测程序中的这些错误。

在函数delay 中,您依赖于存储在DN 中的状态,并使用order 虚拟参数引用此数组的部分。不幸的是,order 没有正确绑定到DN 的实际形状。

考虑线条

   allocate(temp(1:k, 1:order, 0:n+1))
   if (allocated(DN)) temp(1:k-1, 1:order, 0:n+1) = DN
   call move_alloc(temp, DN)

很明显,在这组行第一次执行后,DN 被分配并具有[1, 4, 42] 的形状。在第二个之后,同样清楚的是它的形状是[2 4 42]。来到函数的第三个条目,order 现在是 5,因此分配中的形状不匹配。

我发现代码非常不清楚,因此不会尝试提供有关如何更正它的建议。特别是,保存的本地状态和来自主机范围的变量的混合令人困惑。

【讨论】:

非常感谢@francescalus 花时间检查我的业余代码。我同意订单是导致问题的原因,但我想不出任何其他方法来解决它。我想我需要更多地尝试不同的编程技术来找到解决方案。

以上是关于我的 Fortran 动态分配代码有啥问题?的主要内容,如果未能解决你的问题,请参考以下文章

有啥方法可以防止类的动态分配?

试图将一个连续的动态二维数组从 C 传递到 Fortran

试图将一个连续的动态二维数组从 C 传递到 Fortran

Fortran 子程序中的数组分配

引用传递和动态内存分配之间有啥区别[关闭]

c和c ++中的动态内存分配和堆有啥区别