Rhinocerotidae
Rhinocerotidae

Reputation: 925

Call between subroutines

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

Answers (1)

francescalus
francescalus

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:

  • test the allocation status of arsum and respond accordingly (perhaps with a deallocate);
  • 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

Related Questions