Search code examples
arraysmultidimensional-arrayfortransubroutine

Pass 1D array to 3D array


I am using a API in Fortran which provides a routine for writing data. Let's say its name is api_write_data. This routine expects an array as argument which can be of dimension 1, 2 or 3.

I want to write a subroutine which works as wrapper for this API routine. But therefore it is necessary for me to write a routine which can handle 1D, 2D or 3D arrays and can pass them correctly to the API routine. How can I do that? Can I do that at all?

My approach was something like this, but it does not work:

subroutine write_data(array)
implicit none
integer, dimension(:,:,:), intent(in):: array

call api_write_data(array)

end subroutine write_data

However when I call this routine with for example an 1D array, I get the known error:

Error: Rank mismatch in argument 'array' at (1) (rank-3 and rank-1)

Is there any way to do that kind of thing in Fortran? For me it is necessary to pass the Array as a 1D, 2D or 3D array to the write_data routine. However, I could pass the array as 1D array to api_write_data.

Do you have any idea how I could do that?


Solution

  • An alternative to the reshape function might be to have a 1D pointer towards the multi-dimensional array:

    p(1:size(c)) => c(:,:,:)
    

    You can pass the pointer as a one-dimensional array without making a copy. In fact, it should be as fast as passing the array itself. Of course, you need some way of telling the subroutine which shape the array has:

    module test_mod
    contains
      subroutine print_arr( arr, dimX, dimY, dimZ )
        integer,intent(in)  :: arr(:)
        integer,intent(in)  :: dimX, dimY, dimZ
    
        if ( dimZ == 0 ) then
          if ( dimY == 0 ) then
            ! 1D
            print *, "1D array provided"
            print *, "a(4) =", arr(4)
          else
            ! 2D
            print *, "2D array provided"
            print *, "a(1,2) =", arr((2-1)*dimX+1)
          endif
        else
          ! 3D
          print *, "3D array provided"
          print *, "a(1,2,1) =", arr( ((1-1)*dimY + (2-1))*dimX+1)
        endif
      end subroutine
    end module
    
    program test
    use test_mod
      integer :: i
      integer, target   :: a(8)
      integer, target   :: b(4,2) 
      integer, target   :: c(2,2,2)
      integer, pointer  :: p(:)
    
      a = [ (i,i=1,8) ]
      b = reshape( a, [4,2] )
      c = reshape( a, [2,2,2] )
    
      p(1:size(a)) => a(:)
      call print_arr( p, 8, 0, 0 )
    
      p(1:size(b)) => b(:,:)
      call print_arr( p, 4, 2, 0 )
    
      p(1:size(c)) => c(:,:,:)
      call print_arr( p, 2, 2, 2 )
    end program
    

    This also works the other way round... You can map a 1D array to a 3D pointer:

    integer, pointer  :: p2(:,:,:)
    !...
    p2(1:4,1:2,1:1) => a