Reputation: 173
I just spent too much time resolving a bug in my program. I call a subroutine (let's call is mysub
) defined as:
subroutine mysub(hourval,dayval,some,more,args)
use globals
implicit none
real:: hourval(24,ncol,nrow)
real:: dayval(ncol,nrow)
...
end
where hourval
and dayval
are the arrays that contain the output of the calculations in the subroutine, and nrow
and ncol
are integers.
The call to the subroutine looks like
call mysub(daygrid,hourgrid,some,more,args)
where daygrid
and hourgrid
are arrays that were previously allocate
d. hourgrid
has 3 dimensions (24,ncol,nrow)
, while daygrid
has 2 dimensions (ncol,nrow)
. All variables mentioned until now (hourgrid
, daygrid
, nrow
, ncol
) are declared in the module globals
.
I kept getting strange results and finally noticed that I switched the order of hourgrid
and daygrid
in my subroutine call, and changing this solved my problem (the actual variables in my program have less explicit names, which made the mistake a bit harder to spot). However, the program would compile normally, and I would not get any relevant error messages in the logfile.
I'm wondering, therefore, if there is a way to get a message, either at compile-time or at run-time, pointing to that error. I'm using Intel Fortran 11.1 on Linux with the following compiler flags:
-O3 -C -pg -traceback -g
I was actually counting on the -C
flag (equivalent to -check all
) to detect this kind of bugs, as it implies the option -check bounds
. Is there anything else I can do?
Upvotes: 2
Views: 466
Reputation: 6918
The suggestion to use assume-shape is really good (so a +1 there), as that will detect shape mismatches. However, if your arrays have the same shape then you can run again into trouble. The reason is that there is no way the compiler can know what you intended to do if the arguments match what is in the interface.
So, a better solution to avoid the problem might be to use named arguments (in addition to using assumed shape with explicit interface that is). In the example below, sun
and moon
are similar arrays that are to be processed. When you switch them around, the subroutine call still works but interchanges the use of the arrays. When using named arguments, no matter what order you put them in, the subroutine will connect it to the correct dummy argument.
program named_arguments
integer, parameter :: n = 2
integer, allocatable :: sun(:,:), moon(:,:)
allocate(sun(n,n))
allocate(moon(n,n))
call mysub(sun,moon)
write(*,*) sun
write(*,*) moon
call mysub(moon,sun)
write(*,*) sun
write(*,*) moon
call mysub(night=moon,day=sun)
write(*,*) sun
write(*,*) moon
contains
subroutine mysub(day,night)
integer :: day(:,:), night(:,:)
day = 1
night = 2
end subroutine
end program
As you probably give meaningful names to the dummy arguments that reflect their usage, connecting them by name to the actual arguments in the call is a good practice to avoid this kind of problem.
In your case:
call mysub(dayval=daygrid,hourval=hourgrid,...)
would do the correct thing as using named arguments doesn't rely on the order of the dummy arguments.
Upvotes: 2
Reputation: 1276
You may want to use assumed shape dummy arguments to catch errors like this (i.e real, dimension(:,:) :: dayval
and real, dimension(:,:,:) :: hourval
.)
From Modern Fortran Explained by Metcalf, Cohen and Reid (2011) :
Shape, or character disagreement cannot occur when a dummy argument is assumed-shape.
Also, it does not strike me as the most amazing coding style to pass variables already available through the use
statement as the actual arguments to the same subroutine.
Upvotes: 6
Reputation: 32451
It could be possible to have this noticed by the compiler at compile time if the array sizes are "known" at that time. How you manage depends on your code.
If there is an (explicit or implicit) interface available ifort should do this for you without fail.
Even without an interface you may have some joy with the compile option -warn interface
: this essentially has better-than-usual checking where an interface is "obvious" even if not "available". [I hope someone can explain better.]
It's possibly worth noting that, unless there is a failing with the interface, the number of ranks isn't necessarily a problem. Instead it's down to storage extents. In your case the storages are quite different.[1]
In response to your comment, something about interfaces.
You're already using modules, so if mysub
is in a(nother) module use
d by the host of the call then an explicit interface will be available. This is a good thing to do. Many details can be found on SO of interface explanations.
Finally, I'll say that even though hourgrid
, daygrid
are declared in a module, the dummy arguments by those names are distinct things.
Edit, to be more explicit.
[1] Solving the problem where the dimensions "mismatch" is in general a more difficult problem. The Fortran 2008 standard says in section 12.5.2.11, when it comes to your subroutine declaration (my emphasis)
An actual argument that represents an element sequence and corresponds to a dummy argument that is an array is sequence associated with the dummy argument if the dummy argument is an explicit-shape or assumed-size array. The rank and shape of the actual argument need not agree with the rank and shape of the dummy argument, but the number of elements in the dummy argument shall not exceed the number of elements in the element sequence of the actual argument.
Your dummies are explicit shape. So, in general you can't detect a mismatch, but in this case the fact that your dummy hourval
is bigger (by a factor of 24) than the dayval
actual argument, is the part allowing your compiler to help.
More generally, one has to be careful when using explicit shape dummies.
Upvotes: 3