首页 > 解决方案 > 在不复制大量代码的情况下访问数组的边界

问题描述

当我尝试使用编译我的代码时,-fcheck=all我得到一个运行时错误,因为我似乎超出了我的数组维度大小的范围。它来自下面显示的我的代码部分。我认为这是因为我在 i,j 上的循环仅从-nyto 到to运行ny,但我尝试使用使我超出数组范围的点。当循环从开始时,它需要,所以它立即让我出界,因为我试图访问。同样当。 -nxnxi+1,j+1,i-1,j-1j-nyj-1-ny-1j=ny, i=-nx,nx

我的问题是,如何使用最少的代码有效地解决这个问题?

我需要在array grad(1,i,j)边界上正确定义,并且需要在下面等式的右侧完全定义它,我只是不知道这样做的有效方法。我可以单独明确定义grad(1,nx,j), grad(1,-nx,j), etc,并且只循环,i=-nx+1,nx-1,j=-ny+1,ny-1但这会导致大量重复代码,而且我有很多这样的数组,所以我认为这不是合乎逻辑/有效的方法。如果我这样做,我最终会得到数百行重复的代码,这使得调试变得非常困难。谢谢。

integer :: i,j
integer, parameter :: nx = 50, ny = 50
complex, dimension (3,-nx:nx,-ny:ny) :: grad,psi
real, parameter :: h = 0.1

do j = -ny,ny
do i = -nx,nx

    psi(1,i,j) = sin(i*h)+sin(j*h)
    psi(2,i,j) = sin(i*h)+sin(j*h)    
    psi(3,i,j) = sin(i*h)+sin(j*h)

end do
end do

do j = -ny,ny
do i = -nx,nx

    grad(1,i,j) = (psi(1,i+1,j)+psi(1,i-1,j)+psi(1,i,j+1)+psi(1,i,j-1)-4*psi(1,i,j))/h**2 & 

                - (psi(2,i+1,j)-psi(2,i,j))*psi(1,i,j)/h & 

                - (psi(3,i,j+1)-psi(3,i,j))*psi(1,i,j)/h &

                - psi(2,i,j)*(psi(1,i+1,j)-psi(1,i,j))/h &

                - psi(3,i,j)*(psi(1,i,j+1)-psi(1,i,j))/h

end do
end do

如果我直接为 执行此操作grad(1,nx,j), grad(1,-nx,j),它将由

   do j = -ny+1,ny-1

       grad(1,nx,j) = (psi(1,nx,j)+psi(1,nx-2,j)+psi(1,nx,j+1)+psi(1,nx,j-1)-2*psi(1,nx-1,j)-2*psi(1,nx,j))/h**2 & 

                - (psi(2,nx,j)-psi(2,nx-1,j))*psi(1,nx,j)/h & 

                - (psi(3,nx,j+1)-psi(3,nx,j))*psi(1,nx,j)/h &

                - psi(2,nx,j)*(psi(1,nx,j)-psi(1,nx-1,j))/h &

                - psi(3,nx,j)*(psi(1,nx,j+1)-psi(1,nx,j))/h

       grad(1,-nx,j) = (psi(1,-nx+2,j)+psi(1,-nx,j)+psi(1,-nx,j+1)+psi(1,-nx,j-1)-2*psi(1,-nx+1,j)-2*psi(1,-nx,j))/h**2 & 

                - (psi(2,-nx+1,j)-psi(2,-nx,j))*psi(1,-nx,j)/h & 

                - (psi(3,-nx,j+1)-psi(3,-nx,j))*psi(1,-nx,j)/h &

                - psi(2,-nx,j)*(psi(1,-nx+1,j)-psi(1,-nx,j))/h &

                - psi(3,-nx,j)*(psi(1,-nx,j+1)-psi(1,-nx,j))/h

   end do

标签: arraysfortran

解决方案


一种可能的方法是为边界使用附加索引变量,从原始索引修改以避免越界。我的意思是这样的:

do j = -ny,ny
  jj = max(min(j, ny-1), -ny+1)
  do i = -nx,nx
    ii = max(min(i, nx-1), -nx+1)
    grad(1,i,j) = (psi(1,ii+1,j)+psi(1,ii-1,j)+psi(1,i,jj+1)+psi(1,i,jj-1)-4*psi(1,i,j))/h**2 &
                - (psi(2,ii+1,j)-psi(2,ii,j))*psi(1,i,j)/h &
                - (psi(3,i,jj+1)-psi(3,i,jj))*psi(1,i,j)/h &
                - psi(2,i,j)*(psi(1,ii+1,j)-psi(1,ii,j))/h &
                - psi(3,i,j)*(psi(1,i,jj+1)-psi(1,i,jj))/h
  end do
end do

我很难编写正确的代码,因为您似乎在问题中提出的代码中修剪了部分原始表达式,但我希望您理解这个想法并将其正确应用于您的逻辑。

意见:

  1. 尽管这是您所要求的(据我所知),但我不建议在分析和检查在整个数组操作之后手动分配边界条件是否会更有效之前这样做。也许每次迭代对索引的额外计算可能会影响性能(可以说小于if条件或函数调用)。正如@evets 所建议的那样,使用“幽灵细胞”可能会更加高效。你应该分析和比较。
  2. 我建议您将数组声明为dimension(-nx:nx,-ny:ny,3)。Fortran 以列优先顺序存储数组,并且当您访问“x”和“y”附近的值时,它们将是不连续的内存位置,因为固定的“其他”维度是最左边的,并且可以意味着更少的缓存命中。

推荐阅读