编译我用Fortran 90编写的模拟代码时遇到一个奇怪的错误,我希望能有一些帮助。我正在使用ifort版本18.0.3。
在说明问题之前,这是我能正常工作的内容:
module prng
下面是一个伪随机数生成器(它在fortran 77中,但是我已经对其进行了测试,以防万一有兼容性问题,它可以正常工作!):</ p>
module prng
implicit none
contains
real*8 function genrand_real( ir )
implicit real*8 (a-h,o-z)
integer, intent(inout) :: ir
parameter(da=16807.d0,db=2147483647.d0,dc=2147483648.d0)
ir = abs(mod(da*ir,db)+0.5d0)
genrand_real = dfloat(ir)/dc
return
end function genrand_real
end module prng
我还创建了一个模块以声明种子:
module seed
implicit none
type mod_seed
integer :: seedvalue
end type mod_seed
end module seed
要使用种子值,首先需要声明type(mod_seed) :: seedval
,然后genrand_real(seedval%seedvalue)
返回(0,1)中的实值。
到目前为止,上述所有方法都工作正常!以下是我要实现的内容,基本上我采用了Fortran的数值配方(第280页)中的高斯偏差函数function gauss_dev() result(harvest)
,请参见下面的源代码:
module moves
use prng
use seed
implicit none
contains
function gauss_dev() result(harvest)
implicit none
real(kind=8) :: harvest
real(kind=8) :: rsq,v1,v2
real(kind=8), save :: g
logical, save :: gauss_stored = .false.
if (gauss_stored) then
harvest = g
gauss_stored = .false.
else
do
v1 = genrand_real(seedval%seedvalue)
v2 = genrand_real(seedval%seedvalue)
v1 = 2.0*v1-1.0
v2 = 2.0*v2-1.0
rsq = v1**2 + v2**2
if (rsq > 0.0 .and. rsq < 1.0) exit
enddo
rsq = sqrt(-2.0*log(rsq)/rsq)
harvest = v1*rsq
g = v2*rsq
gauss_stored = .true.
endif
end function gauss_dev
! also other subroutines that calls gauss_dev()
end module moves
seedval%seedvalue
的初始化
subroutine read_iseed(seedval,IN_ISEED)
use prng
use seed
type (mod_seed), intent(inout) :: seedval
character(len=80) :: IN_ISEED
integer :: i
call system('od -vAn -N4 -td4 < /dev/urandom > '//trim(IN_ISEED))
open(unit=7,file=trim(IN_ISEED),status='old')
read(7,*) i
close(7)
seedval%seedvalue = abs(i)
return
end
编译代码时,我收到一条错误消息:error #6404: This name does not have a type, and must have an explicit type. [SEEDVAL]
,这是可以预期的,因为必须在调用前声明seedvalue
!
由于在seedvalue
中重新分配了prng
,因此我会直观地使用intent(inout)
选项。这是我的解决方法:
module moves
use prng
use seed
implicit none
contains
function gauss_dev() result(harvest)
implicit none
type (mod_seed), intent(inout) :: seedval
real(kind=8) :: harvest
real(kind=8) :: rsq,v1,v2
real(kind=8), save :: g
logical, save :: gauss_stored = .false.
if (gauss_stored) then
harvest = g
gauss_stored = .false.
else
do
v1 = genrand_real(seedval%seedvalue)
v2 = genrand_real(seedval%seedvalue)
v1 = 2.0*v1-1.0
v2 = 2.0*v2-1.0
rsq = v1**2 + v2**2
if (rsq > 0.0 .and. rsq < 1.0) exit
enddo
rsq = sqrt(-2.0*log(rsq)/rsq)
harvest = v1*rsq
g = v2*rsq
gauss_stored = .true.
endif
end function gauss_dev
! also other subroutines that calls gauss_dev()
end module moves
但是,当我编译代码时,会出现错误消息:
error #6451: A dummy argument name is required in this context. [SEEDVAL]
type (mod_seed), intent(inout) :: seedval
我不确定是什么引起了错误。但是,当我随机尝试使用intent()
选项时,我偶然发现,未指定intent()
时,代码被编译而没有错误,这很奇怪,因为我认为没有指定intent()
,fortran编译器是否将inout
作为默认选项?但是由于未指定intent()
,模拟陷入了do循环:
do
v1 = genrand_real(seedval%seedvalue)
v2 = genrand_real(seedval%seedvalue)
v1 = 2.0*v1-1.0
v2 = 2.0*v2-1.0
rsq = v1**2 + v2**2
if (rsq > 0.0 .and. rsq < 1.0) exit
enddo
因为seedval%seedvalue
返回0,这导致rsq
不断失败的if (rsq > 0.0 .and. rsq < 1.0) exit
条件。
在发布此线程之前,我读了Fortran intent(inout) versus omitting intent,我看到了潜在的兼容性问题,但这是从Fortran 2003开始根据该线程引入的。
最后,我有两个问题:1.在fortran 90中,指定intent(inout)
与根本不指定之间有区别吗?
2.关于指定intent(inout)
时的错误消息,其原因是什么?
任何提示将不胜感激!
答案 0 :(得分:4)
- 在fortran 90中,指定intent(inout)与根本不指定之间有区别吗?
是:已经在Fortran 90标准中,需要定义带有intent(inout)
的伪参数,并且这对没有intent属性的伪参数不是必需的,请参阅Fortran 90标准的5.1.2.3节
在发布此线程之前,我读过Fortran intent(inout) versus omitting intent,我看到了潜在的兼容性问题,但这是自Fortran 2003开始根据该线程引入的。
请认真阅读该线程,尽管他们确实引用Fortran 2003参考文献来讨论该问题,但他们丝毫没有说过在2003版标准中引入的任何内容。
- 关于指定intent(inout)时的错误消息,其原因是什么?
当您未指定intent
中的seedval
并且没有将seedval
列为该函数的伪参数时,编译器认为seedval
是一个局部变量,对此感到满意。在为intent
定义seedval
而不将其列为虚拟变量的那一刻,编译器自然就感到不满意(intent
仅可用于虚拟参数),因此引发了错误
有/没有指定intent(inout),代码返回不同的结果,有任何线索吗?
也许我错过了它,但是请您说明一下seedval%seedvalue
的初始化位置吗?如果您使用的是未初始化的变量,则可以轻松解释为什么不同的编译产生不同的值。
如果我正确地按照您对问题的描述进行操作(请更正我),则只有当(a)seedval
没有被列为function gauss_dev
的伪参数时,您的代码才运行没有intent
属性;或(b)seedval
被列为该函数的伪参数,并且具有intent(inout)
。
代码在(a)和(b)中的行为截然不同,因为在(b)中,seedval%seedvalue
子例程已适当地初始化了组件read_iseed
,而在(a){{ 1}}是seedval
局部变量,gauss_dev
在初始化之前已使用过。在这种情况下,编译器可能在genrand_real
中将seedval%seedvalue
初始化为零,并且当变量gauss_dev
和genrand_real
时,您的ir
函数返回零。 genrand_real
在输入时为零,因此您描述的是无限循环。
请注意,在(b)之后编译的二进制文件的多次运行很可能会产生不同的数值结果,因为您在ir
内进行了系统调用
read_iseed
通常会为您的种子返回不同的整数值。