2

Suppose I have some subroutine in Fortran, and the arguments are multidimensional arrays. Suppose the rank can be various. Is that possible to have a general setting, than a predefined some array(:,:,:)?

For example, the following code, how to generalize it that I can use rank_A to declare the rank of A, preferably not if rank_A==1 real dimension(:) ... elif rank_A ==2 real dimension(:,:) elif rank_A == 3, real dimension(:,:,:)... and possibly reshape to adjust it. The reshape can depend the rank of the input array, so introduces variations to the array after reshape as well.

program test_array
    implicit none
    real, dimension(:,:,:), allocatable :: my_array
    integer :: rank_A
    
    rank_A = 3
    
    allocate(my_array(3, 3, 3))
    my_array = 1.0
    
    call print_element(my_array, rank_A)
    deallocate(my_array)

  contains
  
  subroutine print_element(A, rank_A)
    real, dimension(:,:,:), intent(in) :: A
    real, dimension(:,:,:), allocatable :: my_array_2
    integer, intent(in) :: rank_A
    integer :: n1, n2, n3

    n1 = size(A, 1)
    n2 = size(A, 2)
    n3 = size(A, 3)    

    if (rank_A == 3) then
      write(*, *) A(1, 1, 1)
      allocate(my_array_2(n3, n2, n1))

      my_array_2 = reshape(A, shape=[n3, n2, n1], order=[3, 2, 1])
      write(*, *) my_array_2(1, 1, 1)
      deallocate(my_array_2)

    end if
  end subroutine
  
end program

Updated. I tried to use assume size (*) to pass multidimensional array into 1 dimensional and reconstruct it by indices. However, reshape does not work. May I know why reshape does not work here and could it work by some fixing?

Updated 2. Adding B = reshape(A(:size(B)), (/ 3, 3, 3 /)) would work, but this approach still assumes we know the rank of B after given transpose. Seems the only solution is like Fortran: Choosing the rank of an allocatable array define higher rank entry, and allocate to 1 dimension if needed


   25 |             B = reshape(A, (/ 3, 3, 3 /))  ! Corrected syntax
      |                        1
Error: The upper bound in the last dimension must appear in the reference to the assumed size array ‘a’ at (1)
module array_utils
    implicit none
    private
    public :: print_element

contains

    subroutine print_element(A, dims)
        real, intent(in) :: A(*)
        integer, intent(in) :: dims(:)
        real  :: B(3,3,3)

        integer :: i, j, k, n1, n2, n3

        if (size(dims) == 3) then
            n1 = dims(1)
            n2 = dims(2)
            n3 = dims(3)
            i = 1
            j = 1
            k = 1
            write(*, *) "A(1, 1, 1) = ", A((k - 1) * n1 * n2 + (j - 1) * n1 + i)
!comment out the reshape line will work
            B = reshape(A, (/ 3, 3, 3 /))  
        end if
    end subroutine

end module array_utils

program test_array
    use array_utils
    implicit none
    real, dimension(:,:,:), allocatable :: my_array
    integer, dimension(3) :: dims

    allocate(my_array(3, 3, 3))
    my_array = 1.0
    dims = shape(my_array)

    call print_element(my_array, dims)

end program    
Geositta
  • 81
  • 7
  • 1
    You can't declare it explicitly but if your compiler is updated to F2015 you can have nonfixed rank passed implicitly; see https://stackoverflow.com/questions/38058080/fortran-choosing-the-rank-of-an-allocatable-array and https://stackoverflow.com/questions/39749056/array-of-unknown-rank-as-subroutine-argument . Before that AFAIK the best you can do is to have separate implementations grouped in an INTERFACE BLOCK so that it appears generic _to the callers_. – dave_thompson_085 May 06 '23 at 02:50
  • Thanks. One linked IBM webpage https://www.ibm.com/support/knowledgecenter/SSGH4D_15.1.0/com.ibm.xlf151.aix.doc/language_ref/assumedrankobject.html is expired. Let me check `assumed rank` https://www.ibm.com/docs/en/xl-fortran-linux/16.1.1?topic=concepts-assumed-rank-objects-ts-29113 – Geositta May 06 '23 at 03:25
  • seems has to be each case, declare relevant array, than auto declaring based on input rank – Geositta May 06 '23 at 03:48
  • or passing by assume size and reconstruct arrays.. – Geositta May 06 '23 at 04:28
  • 1
    Try `some array(*)` – John Alexiou May 06 '23 at 04:37
  • @JohnAlexiou I tried `(*)`. Updated the question. Is there any way to let `reshape` work with it. Thanks. – Geositta May 06 '23 at 05:06
  • 1
    You cannot use an assumed size array (`A`) as a whole array in that context. The error message is trying to say that. Try `reshape(A(:size(B), ...)`. – francescalus May 06 '23 at 14:29
  • @francescalus `reshape(A(:size(B))` it works! Thank you! – Geositta May 06 '23 at 14:47
  • RESHAPE is a standard, but the old DEC extension of UNION and MAP, effectively can do it. UNION/MAP can also do a bit more and was rolled into gfortran. If the arrays were an inconvenient size like 9,9,9, then running through it as a rank 1 (vector) makes SIMD easier to not have many peal(s), – Holmz May 07 '23 at 21:45

0 Answers0