1

I have a few array variables in a module that are dynamic, and later allocated in one of two subroutines outside of the module. However, in one subroutine I want the array to be 1D, in the other subroutine I want it to be 2D.

In principle I would want something like this in the module, but I don't believe this is possible in the declaration area?:

if (option1) then
  real (kind=8), allocatable :: arr1(:)
else
  real (kind=8), allocatable :: arr1(:,:)
endif

Is there a way in the allocatable declarations to have the dimension be dynamic?

Edit1: The reason I'm doing this is I'm adding a new subroutine to an existing codebase, but I want to be backwards compatible. arr1 is only used by the two separate subroutines, the main program doesn't use it at all. Here is some more complete code showing the idea:

program myprog
use inputs

call read_inputs

if (option1) then
    call do1
else
    call do2
endif

contains
    subroutine read_inputs
        use inputs
        use mymod
        !!!read from file .logical. option1, integers N1, N2

        !allocate arrays
        if (option1) then 

        else

        endif
    end subroutine read_inputs

    subroutine do1
        use inputs
        use mymod

        allocate(arr1(N1))

        !do stuff with arr1
    end subroutine do1

    subroutine do2
        use inputs
        use mymod

        allocate(arr1(N1,N2))

        !do stuff with arr1
    end subroutine do2

end program

module inputs
    logical :: option1
    integer :: N1, N2

end module inputs

module mymod
    use inputs
    !!!!can I do something here to make the rank of arr1 dynamic? I don't think the following will work
    if (option1)
        real (kind=8), allocatable :: arr1(:)
    else
        real (kind=8), allocatable :: arr1(:,:)
    endif
end module mymod

I may just have two separate variable in mymod, arr1 and arr1_new. I was just hoping to avoid that.

Michael
  • 486
  • 6
  • 19
  • 2
    You can overload the `allocate` statement with the subroutine that does the job. Fortran 90 is nearly 26 years old. What compiler are you using? If using a modern Fortran (2003+) compiler, I'd suggest defining a derived data type with both 2 and 3 dimensional allocatable type-components with corresponding type-bound procedures instead. Fortran 90 does not allow this, instead you'd have to used pointers which are memory leak prone. – jlokimlin Jul 15 '16 at 04:34
  • 2
    I don't understand your question. How does the array look like in the main program or module ? Is it 1D or 2D there? Does the main program or module care about how the subroutine treat it (1D or 2D)? Perhaps you can use simple storage association, but you should really show more code. Ceterum censeo `kind=8` is ugly, smelly and evil http://stackoverflow.com/a/856243/721644 – Vladimir F Героям слава Jul 15 '16 at 09:27
  • @jlokimlin compilers aren't a problem, but unfortunately this is a large code base with multiple users, so for the foreseeable future it will have to be Fortran90. – Michael Jul 15 '16 at 11:02
  • Can you explain what you actually do with those arrays. It is not clear at all, really.... We can't help you how to pass such arrays when we don't know what they are in the calling scope. Or are they just local? What do you do with them then? Can't you just have two arrays? There were similar questions asked here before, but I can't point you to them when it is not clear which one could apply. – Vladimir F Героям слава Jul 15 '16 at 11:24
  • 2
    What about just allocating it to size 1 in the second dimension (something like `arr1(n,1)`) and use just the first dimension? – Vladimir F Героям слава Jul 15 '16 at 13:53
  • @VladimirF if I do arr1(n,1) in subroutine do1, can I do normal operations like arr1(10) = 5D0, or do I have to change all of the code to do arr1(10,1) = 5D0? Also, I'd have to make sure its compatible with MPI_REDUCE calls. – Michael Jul 15 '16 at 14:05
  • Hard to tell, the overall design you sketched above looks overy complicated and hard to understand. There are ways how to pass 2D array to a subroutine expecting 1D. But you seem to use it as a global variable and that complicates everything. MPI calls would be fine. – Vladimir F Героям слава Jul 15 '16 at 18:31

2 Answers2

1

I think the 'olden' ways to do something like this is to pass the first element instead of the whole array and the size of the array separately:

program dyn_array
    implicit none
    integer :: a(2, 3)
    integer :: i
    call set1d(a(1,1), size(a))
    do i = 1, 3
        write(*, '(2I4)') a(:,i)
    end do
    contains
        subroutine set1d(array, s)
            implicit none
            integer, intent(in) :: s
            integer, intent(out) :: array(s)
            integer :: i
            do i = 1, s
                array(i) = 3 * i
            end do
        end subroutine set1d
end program dyn_array
chw21
  • 7,970
  • 1
  • 16
  • 31
  • Not today Satan! The power of assumed-shape arrays compels you! Why not pass `array(:)` and use the inquiry function `s=size(array)` – jlokimlin Jul 15 '16 at 13:21
  • I haven't tried. Can you pass a 2D array into a subroutine that expects a 1D array and get the right size? – chw21 Jul 16 '16 at 07:54
  • The syntax is identical to that of deferred-shape arrays when defining arrays with the `pointer` or `allocatable` attribute. See: http://stackoverflow.com/questions/38140951/fortran-subroutine-returning-wrong-values/38154225#38154225 – jlokimlin Jul 16 '16 at 15:50
0

"Can you pass a 2D array into a subroutine that expects a 1D array and get the right size?"

You can use reshape, but if you code is relying on the compiler to help then a 2D into a 1D is dicey. You could use RESHAPE before and after... Or you can have 2 routines, which we can call set1d and set2d. Then in the module you can have it choose what one you want to use. You could have integer(s), float(s), complex(s), byte.

You would CALL Array$Set(Array,s)

MODULE Stuff
  PUBLIC :: Array$Set

  PRIVATE
    INTERFACE Array$Set
  MODULE PROCEDURE Set1d_Float, Set1D_Double, set2D_Float, Set2D_Double
END INTERFACE Array$Set

CONTAINS

SUBROUTINE  Set1D_Float(Array,S)...
!$DIR ATRIBUTES ASUUME_ALIGND:64                :: Array
REAL, DIMENSION(:,:), CONTIGUOUS, INTENT(INOUT) :: Array
REAL, DIMENSION(:,:),             INTENT(IN   ) :: S
REAL, DIMENSION(2)                              :: Shapez
...
Shapez = Shape(Array)
DO I =  1, Shapez(1)
  DO J = 1, Shapez(2)
...
END SUBROUTINE Set1D_Float

END MODULE Stuff

For your example: if (option1) then real (kind=8), allocatable :: arr1(:) else real (kind=8), allocatable :: arr1(:,:) endif

I would suggest this:

!DIR ATTRIBUTES ALIGN:64        :: Arr1
REAL, DIMENSION(:), ALLOCATABLE :: Arr1
...


if (option1) then
  ALLOCATE(Arr1(<#>))
else
  ALLOCATE(Arr1(<#>*<#2>))
  RESHAPE(Arr1, SHAPE=/(#1,#2)) !Check the syntax
endif

CALL Array$Set(Arr1,s) !It'll find the right one...

!... at the bottom ...
IF(ALLOCATED(Arr1)) DEALLOCATE(Arr1)
END PROGRAM
Holmz
  • 714
  • 7
  • 14