我有一些Fortran代码,包含一个子程序和一个调用它。它应该使用定义的窗口大小来计算矩阵中元素的平均值。例如,使用winsize = 2在(10,10)数组上调用子例程将返回一个(5,5)数组。
代码如下所示:
SUBROUTINE avgwin(ts, sizelat,sizelon,winsize,size2,size3,ts_new)
implicit none
double precision, dimension(10,sizelat,sizelon) :: ts
double precision, dimension(winsize,winsize) :: store
double precision, dimension(10,size2,size3) :: ts_new
double precision :: par,ave
integer :: sizelat, sizelon,i,j,k,winsize,size2,size3
integer :: A, B,p,m,numb
A=0
B=0
par = 11 !Hypothetical value to be excluded
do i=1,10 !Looping through time
do j=1,sizelat !Looping through latitude
if ((j+winsize) > sizelat) then !Checks if it will exceed bounds
exit !If it'll exceed, discard remaining cells
end if
do k=1,sizelon !Looping through longitude
if ((k+winsize)>sizelon) then
exit
end if
store = ts(i,j:j+winsize,k:k+winsize) !Gets the values for that window
where (store == par) store = -99 !Replaces masked with -99
ave = 0
numb = 0 !Variable to count
do p=1,winsize
do m=1,winsize
if (store(p,m)==-99) then !Evaluates if it's masked, i.e., =-99
ave = ave
else
ave = ave + store(p,m) !Sum of existent values
numb = numb +1 !Updates counting variable
end if
end do
end do
ave = ave/numb !Calculates the mean
ts_new(i,A,B) = ave
B=B+1
end do
B=0
A=A+1
end do
A=0
B=0
end do
END SUBROUTINE
program testefor
implicit none
double precision, dimension(10,10,10) :: teste
double precision, dimension(10,5,5) :: oi
integer :: i,j,k
do i=1,10
do j=1,10
do k=1,10
teste(i,j,k)=i
end do
end do
end do
CALL avgwin(teste,10,10,2,5,5,oi)
print*, oi(1,5,5)
end program testefor
但是,当我运行它时,我会收到分段错误。我尝试用GDB调试它,令我惊讶的是,它返回正确的结果但退出程序时会出现段错误。我从gdb得到的内容可以在下面找到:
Breakpoint 1, testefor () at testefor.f90:56
56 do i=1,10
(gdb) cont
Continuing.
1.0000000000000000
Program received signal SIGSEGV, Segmentation fault.
0x0000000000400f73 in testefor () at testefor.f90:67
67 end program testefor
所以程序返回正确的(1,5,5)元素= 1.0,但在其他地方发生错误。
有人可以帮我识别问题吗?感谢
答案 0 :(得分:3)
可以帮助的一件事是在调试模式下编译程序,并激活许多调试开关。
在我的情况下,我使用gfortran
进行编译,并使用了:
-g
包含调试符号-fbacktrace
以获得更好的堆栈跟踪-Wall
启用所有编译器警告-fcheck=all
启用运行时检查(使程序变慢,但在调试过程中非常有用)。这些运行时检查立即发现错误:
At line 21 of file teste.F
Fortran runtime error: Array bound mismatch for dimension 1 of array 'store' (2/3)
store
的大小为(winsize, winsize)
,但您将数组复制到大小为(windsize+1, winsize+1)
的数组中。如果在Fortran中对数组进行切片,则它包括开始和结束索引:(1:10)
从1到10.如果有(1:1+10)
,则从1到11,这意味着它的大小为11
没有运行时数组边界检查(在这种情况下由-fcheck=all
激活),这很难调试,并且可能导致各种意外行为。
如果使用与gfortran
不同的编译器,则需要了解如何在编译器上打开此类测试,因为交换机未标准化。
答案 1 :(得分:2)
这不是你问题的答案(我已经回答了),但有一些提示可以帮助你。
如果循环遍历一个多维数组,你应该总是在第一个索引上有最里面的循环,依此类推,所以
! Inefficient way to do it:
do i = 1, 10
do j = 1, 10
do k = 1, 10
a(i, j, k) = i*j+k
end do
end do
end do
!Efficient way to do it:
do k = 1, 10 ! <-+
do j = 1, 10 ! | swapped
do i = 1, 10 ! <-+
a(i, j, k) = i*j+k
end do
end do
end do
Fortran存储多维数组的方式是第一个索引变化最快。因此,有效的方法会读取连续的元素,而效率低下的方法需要在内存中大量跳转。
您可以轻松使用SUM
和COUNT
来代替这个错综复杂的循环:
COUNT
计算数组中.TRUE.
的实例,因此numb
的值可以轻松计算为numb=COUNT(store/=-99)
SUM
具有可选参数MASK
,可用于省略某些值。要总结所有非-99
的值,您可以使用:s = SUM(store, MASK=(store /= -99))
所以你可以用以下代码替换大约15行代码:
ave = sum(store, MASK=(store/=par)) / count(store/=par)
store
和par
都是double precision
,比较浮点变量的绝对相等是tricky