首页 > 解决方案 > 错误 #6451:在此上下文中需要一个虚拟参数名称

问题描述

在编译用 Fortran 90 编写的模拟代码时,我遇到了一个奇怪的错误,我希望能得到一些帮助。我正在使用 ifort 版本 18.0.3。

在说出问题之前,这是我工作正常的东西:下面module prng是一个伪随机数生成器(它在 fortran 77 中,但我已经对其进行了测试以防万一有任何兼容性问题,它工作正常!):

  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

为了使用seedvalue,type(mod_seed) :: seedval需要先声明,然后genrand_real(seedval%seedvalue)在(0,1)中返回一个实值。

到目前为止,上面提到的都工作正常!下面是我要实现的,基本上我采用了一个高斯偏差函数,function gauss_dev() result(harvest)来自 Fortran 中的数字食谱(第 280 页),请参见下面的源代码:

 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)option. 这是我的解决方法:

  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-loop 中:

       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) 与 omitting intent,我看到了潜在的兼容性问题,但根据线程,这是自 Fortran 2003 以来引入的。

intent(inout)到最后我有两个问题:1.在fortran 90中,指定和不指定有区别吗?2. 关于指定时的错误信息intent(inout),是什么原因造成的?

任何提示将不胜感激!

标签: fortranfortran90intel-fortran

解决方案


  1. 在 fortran 90 中,指定意图(输入输出)和根本不指定之间有区别吗?

是的:已经在 Fortran 90 标准中,intent(inout)需要定义一个虚拟参数,这不是没有意图属性的虚拟参数的要求,请参阅 Fortran 90 标准的第 5.1.2.3 节。

在发布此线程之前,我阅读了Fortran intent(inout) 与 omitting intent,我看到了潜在的兼容性问题,但根据线程,这是自 Fortran 2003 以来引入的。

请更仔细地阅读该线程,虽然他们确实讨论了引用 Fortran 2003 参考的问题,但他们从未对 2003 版标准中引入的这个问题发表任何评论。

  1. 关于指定intent(inout)时的错误信息,是什么原因造成的?

当您没有指定intentofseedval并且没有将seedval其列为函数的虚拟参数时,编译器会认为这seedval是一个局部变量并对此感到满意。intent在您定义for的那一刻seedval,没有将其列为虚拟变量,编译器自然不高兴(intent只能为虚拟参数提供),因此引发了错误。

有/没有指定意图(inout),代码返回不同的结果,有什么线索吗?

也许我错过了它,但你能澄清一下你在哪里初始化seedval%seedvalue吗?如果您使用的是未初始化的变量,那将很容易解释为什么不同的编译会产生不同的值。


如果我正确地遵循了您对问题的描述(否则请更正我),您的代码仅在 (a)seedval未列为 的虚拟参数function gauss_dev且没有intent属性时运行;或 (b)seedval被列为该函数的虚拟参数,并且它具有intent(inout).

代码在 (a) 和 (b) 中具有非常不同的行为,因为在 (b) 中,组件seedval%seedvalue已由您的read_iseed子例程适当地初始化,而在 (a)seedval中是局部变量gauss_devgenrand_real在初始化之前被使用。在这种情况下,编译器可能在 中初始化seedval%seedvalue为零,并且当变量在输入时为零时gauss_dev,您的genrand_real函数返回零(对于ir和) ,因此您描述了无限循环。genrand_realir

请注意,在 (b) 之后编译的二进制文件的多次运行很可能会产生不同的数值结果,因为您在其中进行的系统调用read_iseed

od -vAn -N4 -td4 < /dev/urandom

通常会为您的种子返回不同的整数值。


推荐阅读