Reputation: 925
I'm new to Fortran programming and I'm trying to understand the call between subroutines. So I wrote a simple program to test it
Here is my code
program dummy
real, allocatable, dimension(:) :: zz,yy
call test2(zz,yy)
print *, zz, yy
contains
subroutine test1(ar1,ar2,arsum)
real, dimension(:) :: ar1,ar2
real, allocatable, dimension(:) :: arsum
allocate(arsum(size(ar1)+size(ar2)))
arsum(1:size(ar1)) = ar1
arsum(size(ar1)+1:size(ar1)+size(ar2)) = ar2
end subroutine test1
subroutine test2(sg1,sg2)
real, dimension(3) :: g1,g3
real, dimension(4) :: g2,g4
real, allocatable,dimension(:) :: sg1,sg2,dum
g1 = 1.0
g2 = 2.0
g3 = 3.0
g4 = 4.0
call test1(g1,g3,dum)
sg1 = 2*dum
call test1(g2,g4,dum)
sg2 = 3*dum
end subroutine test2
end program dummy
However this throws me the following error
forrtl: severe (151): allocatable array is already allocated
Image PC Routine Line Source
dummy.exe 0000000000409B1C Unknown Unknown Unknown
dummy.exe 0000000000402E70 MAIN__ 26 dummy.f90
dummy.exe 0000000000402A2E Unknown Unknown Unknown
libc-2.23.so 00002B3E716C7830 __libc_start_main Unknown Unknown
dummy.exe 0000000000402929 Unknown Unknown Unknown
The test1
subroutine simply concatenates any two given arrays. Test2
subroutine defines the arrays to be concatenated and does some algebra on the output and stores them in new arrays. The program dummy
simply prints the new arrays.
What is it that I'm doing wrong here?
Upvotes: 1
Views: 81
Reputation: 32366
Let's look at the flow of the program here. There's some terminology involved here, but what is used can hopefully be explored through other documentation.
The main program calls the subroutine test2
with a couple of arguments. For the problem here, those arguments are of no interest. Instead, look at the local (to the subroutine) variable dum
.
dum
is an allocatable array. It starts life at execution of test2
as not allocated. It's first an (actual) argument to the call of test1
and later as an (actual) argument to another call of test1
. So, what goes wrong?
Intent of the arguments is crucial here. Really crucial. One should read up on intents elsewhere before continuing with this answer.
So, we're now familiar with argument intents.
On entering the subroutine test1
the dummy argument arsum
has the same allocation status as the actual argument dum
. During test1
's execution there is an allocate
statement.
An allocate
statement may only attempt to allocate a thing not already allocated. This is fine on the first call: on entry dum
/arsum
is not allocated. During execution of the subroutine arsum
is allocated, and this affects the allocation status of test2
's dum
.
On the second call to test1
arsum
is now allocated because the actual argument dum
is allocated. The allocate
statement fails, with the error message given, because of this.
That's the problem; how to fix? We need to ensure arsum
is not allocated. There are two obvious ways:
arsum
and respond accordingly (perhaps with a deallocate
);dum
between calls of test1
.But there is a possibly more appropriate way. Note that dum
is useful only as returning to test2
the value of some operation. Think back to intents: this is what intent(out)
signifies.
If we rewrite the subroutine test1
as
subroutine test1(ar1,ar2,arsum)
real, dimension(:) :: ar1,ar2
real, allocatable, dimension(:), intent(out) :: arsum
allocate(arsum(size(ar1)+size(ar2)))
arsum(1:size(ar1)) = ar1
arsum(size(ar1)+1:size(ar1)+size(ar2)) = ar2
end subroutine test1
then, because of the intent(out)
, attribute arsum
/dum
is automatically deallocated if it is allocated on entry.
Finally (and not shown), one could even consider using a function to return arsum
or even use automatic arrays rather than allocatable ones. Or, even Fortran 2003 automatic allocation and an array constructor:
subroutine test1(ar1,ar2,arsum)
real, dimension(:) :: ar1,ar2
real, allocatable, dimension(:) :: arsum ! Or with `intent(out)`
arsum = [ar1,ar2]
end subroutine test1
Upvotes: 3