I'm getting a weird error when compiling my simulation code written in Fortran 90, I was hoping to get some help by any chance. I'm using ifort version 18.0.3.
Before telling the problem, here's what I have that are working properly:
Below module prng
is a pseudo-random number generator(it's in fortran 77 but I have tested it just in case any compatibility concerns, it works fine!):
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
I also created a module in order to declare the seed:
module seed
implicit none
type mod_seed
integer :: seedvalue
end type mod_seed
end module seed
In order to use seedvalue, type(mod_seed) :: seedval
needs to be declared first, then genrand_real(seedval%seedvalue)
returns a real value in (0,1).
So far above mentioned are all working fine! Below is what I'm trying to implement, basically I adopted a gaussian deviate function, function gauss_dev() result(harvest)
, from Numerical recipes in Fortran (page 280), see the source code below:
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
The seedval%seedvalue
is initialised by
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
When I compile the code, I get an error message: error #6404: This name does not have a type, and must have an explicit type. [SEEDVAL]
, this is expected as the seedvalue
must be declared before calling!
Since the seedvalue
gets reassigned in prng
, intuitively I'd use intent(inout)
option. And here's my fix:
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
However when I compile the code, I get error message:
error #6451: A dummy argument name is required in this context. [SEEDVAL]
type (mod_seed), intent(inout) :: seedval
I'm not certain what caused the error. But as I was randomly trying with intent()
options, I accidentally found out that without specifying intent()
, the code gets compiled WITHOUT errors, which is weird because I thought without specifying intent()
, fortran compiler takes inout
as the default option? But as a result of NOT specifying intent()
, the simulation gets stuck in the 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
because seedval%seedvalue
returns 0, which causes rsq
constantly failing if (rsq > 0.0 .and. rsq < 1.0) exit
condition.
Before posting this thread, I read Fortran intent(inout) versus omitting intent, I see the potential compatibility issue out there, but that's introduced since Fortran 2003 according to the thread.
Coming to the end I bear two questions: 1. In fortran 90, is there a difference between specifying intent(inout)
and not specifying at all?
2. In regard to the error message when specifying intent(inout)
, what's the cause of it?
Any hint would be appreciated!
- In fortran 90, is there a difference between specifying intent(inout) and not specifying at all?
Yes: already in the Fortran 90 standard, a dummy argument with intent(inout)
needed to be definable, and this was not a requirement for dummy arguments without an intent attribute, see section 5.1.2.3 of the Fortran 90 standard.
Before posting this thread, I read Fortran intent(inout) versus omitting intent, I see the potential compatibility issue out there, but that's introduced since Fortran 2003 according to the thread.
Please read that thread more carefully, while they do discuss the issue citing Fortran 2003 references, at no point they say anything about this being introduced in the 2003 version of the standard.
- In regard to the error message when specifying intent(inout), what's the cause of it?
When you did not specify the intent
of seedval
and you didn't list seedval
as a dummy argument of the function, the compiler thought that seedval
was a local variable and was happy with it. The moment you defined an intent
for seedval
, without listing it as a dummy variable, the compiler was naturally unhappy (intent
can only be provided for dummy arguments), so it raised the error.
with/without having intent(inout) specified, code returns different results, any clue?
Maybe I am missing it, but can you please clarify where you initialize seedval%seedvalue
? If you are using an uninitialized variable, that would easily explain why different compilations produce different values.
If I follow your description of the issue correctly (please correct me otherwise), your code only ran when either (a) seedval
was not listed as a dummy argument of function gauss_dev
, and it didn't have an intent
attribute; or (b) seedval
was listed as a dummy argument of the function and it had intent(inout)
.
The code had a very different behaviour in (a) and (b) because in (b) the component seedval%seedvalue
had been appropriately initialized by your read_iseed
subroutine, while in (a) seedval
was a variable local to gauss_dev
, which was being used by genrand_real
before being initialised. In this case, the compiler was probably initialising seedval%seedvalue
to zero in gauss_dev
, and your genrand_real
function returns zeroes (for both ir
and genrand_real
) when variable ir
is zero at input, hence the infinite loop you described.
Note that multiple runs of a binary compiled following (b) will most likely produce different numerical results, since the system call you make within read_iseed
od -vAn -N4 -td4 < /dev/urandom
will usually return different integer values for your seed.