Fortran: intent(out) and assumed-size arguments - fortran

Suppose an dummy argument is modified in a subroutine, and the subroutine doesn't care about its initial value. For a scalar of the standard numerical types (no components), intent(out) would be correct, right?
Now, if the dummy argument is allocatable and the subroutine depends on its allocation status, or if it has components that are not modified by the subroutine but should maintain their initial values (i.e. not become undefined), or, similarly, if it's an array and not all elements are set in the subroutine... Then the proper intent would be intent(inout).
My question is what happens if the dummy argument is an assumed-size array (dimension(*))? Assuming that not all elements are modified in the subroutine and one wants the other elements to retain their initial values, would intent(out) be appropriate? My reasoning is that the subroutine doesn't "know" the size of the array, so it cannot make the elements undefined as it can with a fixed-size array.
Does the standard say anything about this or are compilers free to "guess" for example that if I set A(23) = 42, then all elements A(1:22) can be made undefined (or NaN, or garbage...)?
Note: When I say "retain their initial values", I mean the values in the actual argument outside the subroutine. The subroutine itself doesn't care about the values of these elements, it never reads them or writes them.

Another question looks at what "becomes undefined" means in terms of the dummy argument. However, exactly the same aspects apply to the actual argument with which the dummy argument is associated: it becomes undefined.
The Fortran standard itself gives a note on this aspect "retaining" untouched values (Fortran 2018, 8.5.10, Note 4):
INTENT (OUT) means that the value of the argument after invoking the procedure is entirely the result of executing that procedure. If an argument might not be redefined and it is desired to have the argument retain its value in that case, INTENT (OUT) cannot be used because it would cause the argument to become undefined
That note goes on further to consider whether intent(inout) would be appropriate for when the procedure doesn't care about values:
however, INTENT (INOUT) can be used, even if there is no explicit reference to the value of the dummy argument.
That the array is assumed-size plays no part in the undefinition of the actual and dummy arguments. Again, to stress from my answer to the other question: "undefined" doesn't mean the values are changed. As the compiler has no required action to perform to undefine values, it doesn't need to work out which values to undefine: with an assumed-size dummy argument the procedure doesn't know how large it is, but this doesn't excuse the compiler from "undefining" it, because there's nothing to excuse.
You may well see that the "undefined" parts of the assumed-size array remain exactly the same, but intent(inout) (or no specified intent) remains the correct choice for a compliant Fortran program. With copy-in/copy-out mechanics with intent(out), for example, the compiler wouldn't be obliged to ensure the copy is defined and that junk isn't copied back.
Finally, yes a compiler (perhaps in the hope of being a good debugging compiler) may change values which have become undefined. If the procedure references the n-th element of an array, it's allowed to assume that there are n-1 elements before it and set them as it chooses, for an intent(out) dummy, but not an intent(inout) dummy. (If you access A(n)then the compiler is allowed to assume that that element, and all from the declared lower bound, exist.)

Related

Passing scalars and array elements to a procedure expecting an array

I have some legacy Fortran 77 code which I'm trying to at least compile without warnings (without disabling warnings). There are subroutine calls that pass a scalar where subroutine expects an array, since the scalar is used as a size-1 array, this causes no problems. But with the Intel compiler, if I enable interface warnings I get this error:
error #8284: If the actual argument is scalar, the dummy argument shall be scalar unless the actual argument is of type character or is an element of an array that is not assumed shape, pointer, or polymorphic.
In some cases, I've tried to solve this by overloading the subroutine with array and scalar variants, but then I face a problem when the argument passed is an "an element of an array", which is detected as a scalar. Consider the following sample (tested with gfortran):
program a
integer n,dum(3)
interface readn
subroutine readn_s(n,m)
integer m,n
end subroutine
subroutine readn_a(n,m)
integer m,n(*)
end subroutine
end interface
call readn(n,1)
write(6,*) 'n=',n
call readn(dum,3)
write(6,*) 'dum=',dum
call readn(dum(2),2)
write(6,*) 'dum=',dum
end program
subroutine readn_s(n,m)
integer i,m,n
n=2
end subroutine
subroutine readn_a(n,m)
integer i,m,n(*)
do i=1,m
n(i)=1
end do
end subroutine
The readn(dum,3) call correctly uses readn_a, while the second one uses readn_s. The intended behaviour is that both should use readn_a. Indeed, if I replace both calls with readn_a everything is as expected.
Is it possible to have this working properly and use the "array" version of the overloaded routine when the actual argument is an array element? I've found out it works if I call the subroutine with readn(dum(2:),2), but I'm afraid that creates a temporary copy of the array...
Original problem:
file.f90
program a
integer n,dum(3)
call readn_a(n,1)
write(6,*) 'n=',n
call readn_a(dum,3)
write(6,*) 'dum=',dum
dum=3
call readn_a(dum(2),2)
write(6,*) 'dum=',dum
end program
file2.f90
subroutine readn_a(n,m)
integer i,m,n(*)
do i=1,m
n(i)=1
end do
end subroutine
Compile with gfortran -Wall file.f90 file2.f90 or ifort file.f90 file2.f90, all is fine and the output is the intended:
n= 1
dum= 1 1 1
dum= 3 1 1
Compile with ifort -warn all file.f90 file2.f90 and I get the error #8284 above. So that's why I wanted a version of the subroutine that would work with scalars or arrays... but would give the array version with an array element.
In your attempted solution the type-kind-rank generic resolution will delegate all array elements to the scalar version of the subroutine and that will not do the intended work on the part of the array. So "The intended behaviour is that both should use readn_a." is not really possible with the approach you chose.
Well, it is possible when you rewrite the code to pass array sections as you noted yourself. But we are again at the same problem as we were before, you example is simplified. We can be certain there there is no temporary array in the example you have shown, we definitely can't say that about your real code. If you use some 2D subsections starting at some random place, you will have temporary arrays for sure and it might be difficult to make the correct subsection at all.
The original warning should be reasonably easy to avoid in your original legacy code. FORTRAN 77 has no non-contiguous arrays so if you keep your arguments to be integer :: a(*) instead of integer :: a and pass that further. If your original variable is scalar, that is a problem.
The problem is that an integer scalar does not constitute an element sequence:
F2008 12.5.2.11 1 An actual argument represents an element sequence if
it is an array expression, an array element designator, a default
character scalar, or a scalar of type character with the C character
kind (15.2.2). ...
An array element designator a(2) is an element sequence but the scalar n is not.
So it is not allowed for sequence association which is used when an array element is passed:
F2008 12.5.2.11 4 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. ...
Your code is not strictly standard Fortran conforming, but very likely to work as expected if you manage to compile it.
Possible solutions:
You can pass the argument as an array expression, but the argument may not be modified inside the subroutine
call readn_a([n],1)
You can just disable the warnings about interface matching
ifort -warn nointerfaces
You can create separate subroutines which only work for scalars and call it under some different name.
You can also disable argument checking for that dummy argument by an Intel Compiler directive
!DEC$ ATTRIBUTES NO_ARG_CHECK :: dummy-arg-name

Impact performance of the TARGET attribute (not the POINTER attribute)

The question of performance impact due to the presence of the TARGET attribute was asked many times, but the answers lack of concrete examples.
The question Why does a Fortran POINTER require a TARGET? has a nice anwser:
An item that might be pointed to could be aliased to another item, and the compiler must allow for this. Items without the target attribute should not be aliased and the compiler can make assumptions based on this and therefore produce more efficient code.
I realize that the optimizations depend on the compiler and the processor instruction set, but what actually are these optimizations?
Consider the following code:
subroutine complexSubroutine(tab)
double precision, dimension(:) :: tab
!...
! very mysterious complex instructions performed on tab
!...
end subroutine
What are the optimization that the compiler could perform for this code
double precision, dimension(very large number) :: A
call complexSubroutine(A)
and not for this code?
double precision, dimension(very large number), target :: A
call complexSubroutine(A)
Note that the dummy argument tab in complexSubroutine in the question does not have the TARGET attribute. Inside the subroutine the compiler can assume that tab is not aliased, and the below discussion is moot. The issue applies to the scope calling the subroutine.
If the compiler knows that there is no possibility of aliasing, then the compiler knows that there is no possibility of the storage for two objects overlapping. The potential for overlap has direct implications for some operations. As a simple example, consider:
INTEGER, xxx :: forward(3)
INTEGER, xxx :: reverse(3)
forward = [1,2,3]
reverse(1:3:1) = forward(3:1:-1)
The Fortran standard defines the semantics of assignment such that reverse should end up with the value [3,2,1].
If xxx does not include POINTER or TARGET, then the compiler knows that forward and reverse do not overlap. It can execute the array assignment in a straight forward manner, perhaps by working backwards through the elements on the right hand side and forwards through the elements on the left hand, as suggested by the subscript triplets, and doing the element by element assignment "directly".
However, if forward and reverse are TARGETs, then they may well overlap. The straight forward manner suggested above may fail to produce the result required by the standard. If the two names are associated with exactly the same underlying sequence of data objects, then the transfer of reverse(3) to forward(1) will change the value that reverse(1) later references. With the naive approach above that fails to accommodate aliasing, reverse would end up with the value [3,2,3].
To deliver the result required by the standard, compilers may create a temporary object to hold the result of evaluating the right hand side of the assignment, effectively:
INTEGER, TARGET :: forward(3)
INTEGER, TARGET :: reverse(3)
INTEGER :: temp(3)
temp = forward(3:1:-1)
reverse(1:3:1) = temp
The presence and additional operations associated with the temporary may result in a performance impact.
The potential for aliasing to break the otherwise straight forward and simple approach to an operation is a general problem for many situations. In the absence of compiler and runtime smarts to determine that aliasing isn't a problem in a particular situation, the creation and use of temporaries is a general solution, with the general potential for a performance impact.
The potential for aliasing not immediately apparent to the compiler may also prevent the compiler assuming that the value of an object won't change when it doesn't see any explicit reference to the object that would imply a change.
INTEGER, TARGET :: x
...much later...
x = 4
CALL abc
IF (x == 4) THEN
...
The compiler, not knowing anything about procedure abc, cannot, in general, assume that x is not modified inside procedure abc - perhaps a pointer to x is available to the procedure in some way and the procedure has used that pointer to indirectly modify x. If x did not have the TARGET attribute, then the compiler knows that abc could not legitimately change its value. This has implications for the compiler's ability to analyse possible code paths at compile time, and to resequence operations/move operations out of loops, etc.

Scope of subroutine parameters in FORTRAN 77

I have a subroutine that declares i and passes it to other small subroutines. Within those small subroutines there are declared other variables with the same name, i.e i, and it is internally used. Once out of the small subroutines, one would expect to have the same i value that was originally passed, however that is not the real situation and i contains the value of the last parameter assigned within the small subroutines. Here is a brief example:
subroutine smallSub1(i)
integer i,start,end
start=i
end = start +10
do i=start, end
write(*,*) i
enddo
end subroutine smallSub1
subroutine caller
integer i
i = 1
call smallSub1(i)
write(*,*) i
end subroutine caller
My question is, how could I avoid this behavior in F77?
My concern here is: think about a situation where a subroutine is a black box, and you just need to pass an integer, but you do not want that integer value to change from the smallSub1 subroutine. The variables start and end would depend on the value of i however they should not replace the original value of i out of smallSub1
As noted in the other answer, Fortran typically passes by reference and when that is not possible it does things like copy-in/copy-out. As said succinctly by others in comments, if you don't want to change i then don't change i.
In your subroutine smallSub1, i is being used as a loop iteration variable and and you don't want its value changes visible to the caller. The reason changing i is visible to caller is because i isn't a local variable, it is a dummy argument. The solution is call the loop variable and the dummy argument by different names. One such solution is:
subroutine smallSub1(i_from_caller)
integer i,i_from_caller,start,end
start = i_from_caller
end = start +10
do i=start, end
write (*,*) i
end do
end subroutine smallSub1
In this case the dummy argument has been renamed to i_from_caller and is used to initialize start. Now the loop variable i is truly local to the subroutine (as it is no longer the name of a dummy argument) and changing i here won't change i in caller
To help avoid this kind of behavior you can give the compiler hints that dummy arguments are for input, output or both. If you had declared i as integer, intent(in) :: i in smallSub1 in your original example, the compiler would complain:
do i=start, end
1
Error: Dummy argument ā€˜iā€™ with INTENT(IN) in variable definition context (iterator variable) at (1)
making you aware that you are making unwanted changes to a dummy argument.
In the code example given there are two variables i: one in each subroutine. In subroutine caller the i is a local variable, and in the subroutine smallSub1 it is a dummy argument.
When you have call smallSub1(i) you are associating the two i variables with each other through argument association. In this simple case, then, any change to i in smallSub1 affects the i in caller. There is how argument association works here.
Traditionally one did have black-boxes where an argument is changed in the subroutine when that wasn't desired. Where it is used as a work-space, for example. In that case, one would do something like
inew = i
call smallSub1(inew)
... continue using i
In this case, however, one can easily (I imagine) change the subroutine. Introduce an extra local variable:
subroutine smallSub1(i)
integer i ! Dummy argument - we don't want to change it
integer start,end
integer j ! Local variable - we're quite free to change it
! In general, we'd have j=i at the start and use that instead
start=i
end = start +10
do j=start, end
write(*,*) j
enddo
end subroutine smallSub1
And with modern Fortran one even has the value attribute, which can be applied to the dummy argument allowing one to change it without impact on the actual argument.
from https://gcc.gnu.org/onlinedocs/gfortran/Argument-passing-conventions.html
Fortran passes most arguments by reference
Therefore, if you don't want the variable to change, don't change it in the subroutine. You are allowed to rename the variable smallSub1(i) to smallSub1(j)
you don't want to change the entire function.

Difference between intent(out) and intent(inout)

According to the Fortran standard:
The INTENT (OUT) attribute for a nonpointer dummy argument specifies that the dummy argument becomes undefined on invocation of the procedure
However this simple code gives me 5 as output, so it seems the argument didn't become undefined at the start of the procedure (in this case a subroutine).
subroutine useless(a)
integer, intent(out) :: a
print *,a
end subroutine useless
program test
integer :: n=5
call useless(n)
end program test
What am I getting wrong? It seems that intent(inout) and intent(out) are the same.
intent(inout) and intent(out) are certainly not the same. You have noted why, although you don't draw the correct conclusion. On entering the subroutine useless a is undefined, rather than defined.
Having a variable "undefined" means that you cannot rely on a specific behaviour when you reference it. You observed that the variable a had a value 5 but this does not mean that the only value you could observe is 5. In particular "undefined" does not mean "takes a specific value like NaN".
Your code is not standard conforming because of this reference to an undefined variable. See Fortran 2008 6.2 (similar meaning will be somewhere in Fortran 90 as initially tagged). Of particular note is that the compiler doesn't have to point out your mistake.
With intent(inout) the variable a would be defined when referenced and it will be guaranteed to have the value 5 (for a conforming processor).
More widely, there are other differences between the two intent attributes and this "coincidental" appearance of the similarity of the definition of the variable a could be more troublesome.
Allocatable arrays and objects with deferred type parameters, for example, are deallocated; derived types become undefined (and any allocatable components deallocated) and components with default initialization are "re-initalized"; pointers have their association status become undefined.
All of these latter things have potential for very awkward results, much more so than with a scalar integer, if they are referenced without being defined first.

Fortran: Variables changing on their own

I'm having a problem of variables getting over-written for I don't know what reason. I've posted a chunk of the code below so you can see how things are declared. The variables strain,Qi,Qf,Qd,tel and Gc are passed into the subroutine and used to calculate ssgrad,strn0,strss0.
My problem is that tel and Gc are passed into the subroutine OK but are for some reason change value during this chunk of code.
Using print statements I've found that the problem first occurs during the 2nd do loop. When I set strss0 to 0, Gc and tel change value from both being equal to 1, to seemingly random numbers: tel=11.52822 Gc=-8.789086 (Just shown for the sake of example)
Each time I run the code they are set to the same values though.
Just to let you know, this subroutine interfaces with a commercial finite element package.
Many thanks in advance for any help with this
subroutine initcalcs(strain,Qi,Qf,Qd,tel,Gc,ssgrad,strn0,strss0)
implicit none
integer :: i,j
real*8:: nstrn0,nstrs0,strn0,strnf,varsq,normvar,lmbda0,lmbdaf,
# ssgrad,t0,tt,tel,nstrnf,nstrsf,Gc
real*8, dimension(3) :: strain,stran0,stranf,strss0,strssf,var
real*8, dimension(3,3) :: Qd,Qi,Qf
lmbda0=1.0d0
nstrn0=0.0d0
do i=1,3
stran0(i)=0.0d0
stran0(i)=strain(i)*lmbda0
nstrn0=nstrn0+stran0(i)**2
end do
nstrn0=dsqrt(nstrn0)
do i=1,3
strss0(i)=0.0d0
end do
In Fortran, there are two common causes of the corruption of memory values. One is a subscript error, where you assign to an array element using an incorrect subscript value. This writes to a memory location outside of the array. The other is a disagreement between the arguments in the call to a procedure (subroutine or function) and the dummy arguments of the procedure. Either can cause problems to appear at source code locations different from the actual cause. Suggestions: inspect your code for these problems. Turn on stringent warning and error checking options of your compiler. The use of Fortran >=90 and modules gives Fortran much better ability to automatically find argument consistency problems. You could monitor the memory locations with a debugger and see what it modifying it.
I concur with M. S. B.: turn on stringent warnings and error checking and verify the subroutine calls are passing arguments that have the same type and shape (array dimensions) as the subroutine expects.
The colons in the variable declaration syntax imply this is Fortran90 or later. If that's the case, I strongly suggest using the INTENT modifier to specify whether arguments are intended to be read-only.
For example, let's assume that of the arguments passed to this routine, strain, Qi, Qf, Qd, tel, and Gc are read-only input and the arguments are ssgrad, strn0, and strss0 are returned as output; that is, whatever value they have is overwritten by this routine.
The variable declarations for the arguments would change to:
real*8, dimension(3), intent(in) :: strain
real*8, dimension(3,3), intent(in) :: Qi, Qf, Qd
real*8, intent(in) :: tel, Gc
real*8, intent(out) :: strn0, ssgrad
real*8, dimension(3), intent(out) :: strss0
The INTENT keyword is an addition to Fortran 90 which allows the user to specify which arguments are read-only (INTENT(IN)), initialized but which may be modified within the routine (INTENT(INOUT)) and which are treated as uninitialized and will be set within the routine (INTENT(OUT)).
If INTENT is not specified, it is defaults to INOUT which is consistent with FORTRAN 77 (Note that there are minor differences between INTENT(INOUT) and INTENT not being specified but they aren't relevant in this example).
A good compiler will throw an error if a routine tries to assign a value to a variable declared INTENT(IN) and will at least throw a warning if a variable declared INTENT(OUT) doesn't get assigned a value.
If possible, set INTENT(IN) on all the variables which are supposed to be read-only. This may not be possible, depending on how those variables are passed to other routines. If INTENT isn't specified on arguments to routines called within this routine, it will default to INOUT. If you pass an INTENT(IN) variable as an INTENT(INOUT) argument, the compiler will throw an error. If this is happening in code you control, you have have to specify INTENT in a number of routines. This may or may not be desirable depending on whether you want to generally improve your code or just fix this one problem really quickly.
I'm assuming some of these variables are passed to external routines in the finite element package which I 'm guessing is linked to your code rather than compiled; I'm not sure how compile-time intent checking is handled in that case.