2

I have written a program which reads a file containing multidimensional data (most commonly 3D, but 2D could occur as well). To heighten simplicity I would like to store the data in an array of the same rank (or something pretending to be one), i.e. using a three-dimensional array for 3D data, etc.; the trouble is that the program only learns about the dimensionality on reading the data file.

Currently I store all data in an array of rank one and calculate each element's index in that array from the element's coordinates (this was also suggested here). However, I have also read about pointer rank remapping, which seems very elegant and just what I have been looking for, as it would allow me to scrap my procedures for array index determination (which are probably far less efficient than what goes on behind the scenes). Now, however, it looks like I'm facing the same problem as with directly declaring a multidimensional array - how to do the declaration? Again, it requires information about the rank.

How could I use pointer rank remapping or some other, more suitable technique for setting an array's rank at runtime - in case this can be done at all. Or am I best off sticking to the rank one-array that I am currently using?

Community
  • 1
  • 1
canavanin
  • 2,559
  • 3
  • 25
  • 36

4 Answers4

5

You could use the EQUIVALENCE statement like this:

Program ranks
    integer a_1d(12)
    integer a_2d(2, 6)
    integer a_3d(2, 2, 3)

    equivalence (a_1d, a_2d, a_3d)

    ! fill array 1d
    a_1d = (/1,2,3,4,5,6,7,8,9,10,11,12/)

    print *, a_1d

    print *, a_2d(1,1:6)
    print *, a_2d(2,1:6)

    print *, a_3d(1,1,1:3)
    print *, a_3d(2,1,1:3)
    print *, a_3d(1,2,1:3)
    print *, a_3d(2,2,1:3)

end program ranks
Johny Bergmann
  • 444
  • 3
  • 6
4

If I understand correctly, you read in data in and 1-D array and want to assign it to 2D or 3D arrays, which you know only after reading the file. Why not declare both 2D and 3D arrays as allocatable arrays, and allocate only one of them base on your data shape? You could use the intrinsic function RESHAPE to do this conveniently.

REAL,DIMENSION(:,:),  ALLOCATABLE :: arr2d
REAL,DIMENSION(:,:,:),ALLOCATABLE :: arr3d
...
! Read data into 1-D array, arr1d;
...
IF(L2d)THEN
  ALLOCATE(arr2d(im,jm))
  arr2d=RESHAPE(arr1d,(/im,jm/))
ELSEIF(L3d)THEN
  ALLOCATE(arr3d(im,jm,km))
  arr3d=RESHAPE(arr1d,(/im,jm,km/))
ENDIF
milancurcic
  • 6,202
  • 2
  • 34
  • 47
  • I think the OP wants the same name, but that isn't possible anyway. So I guess this is one of the better solutions. – steabert Sep 22 '11 at 07:35
4

I once asked something similar, i.e. how to treat a two-dimensional array as one dimension, see here: changing array dimensions in fortran.

The answers were about the RESHAPE instrinsic of pointers, however there seems to be no way to use the same array name unless you use subroutine wrappers, but then you need callbacks to have the eventual subroutine with only one name, so the problems get larger.

program test
    real, allocatable :: data(:)
    allocate(data(n_data))
    ! read stuff, set is_2d and sizes
    if (is_2d) then
        call my_sub2(data, nX, nY)
    else
        call my_sub3(data, nX, nY, nZ)
    end if
end program test

subroutine my_sub2(data, nX, nY)
    real :: data(nx,nY)
    ! ...
end subroutine my_sub2

subroutine my_sub3(data, nX, nY, nZ)
    real :: data(nx,nY,nZ)
    ! ...
end subroutine my_sub3

EDIT: as an alternative, set the third rank to 1:

program test
    real, allocatable, target:: data(:)
    real, pointer:: my_array(:,:,:)
    logical is_2d
    n_data = 100
    allocate(data(n_data))
    ! read stuff, determine is_2d and n
    if (is_2d) then
        i=n
        j=n
        k=1
    else
        i=n
        j=n
        k=n
    end if
    my_array(1:i,1:j,1:k) => data
    write(*,*) my_array
end program test

Then you handle the 2D case as a special 3D case with third dimension 1.

EDIT2: also, beware when passing non-contiguous arrays to subroutines with explicit-shape arrays: http://software.intel.com/sites/products/documentation/hpc/compilerpro/en-us/fortran/lin/compiler_f/optaps/fortran/optaps_prg_arrs_f.htm

Community
  • 1
  • 1
steabert
  • 6,540
  • 2
  • 26
  • 32
  • Thanks for your reply. You are right, I would like to use the same name, no matter what rank, and in your post (not in your comment, however) you claimed this was possible. Is the code you posted the method you were referring to in your text? – canavanin Sep 23 '11 at 09:09
  • 1
    yes, I meant that the name is always `data`, however then you are moving the naming problem to the subroutines having different names. The latter can be solved by using callbacks, i.e. having one function `my_sub` which can point to either subroutine, but it's very tedious to do that (I have never done so myself, just read about it that it's possible). On a side note, when I run into this (frequently) I just use rank-1 arrays with manual indexing, or set the superfluous dimension to 1 (I edited my answer accordingly, space here is running out) – steabert Sep 23 '11 at 09:59
  • Thanks! What you suggested seems pretty useful to me :) – canavanin Sep 23 '11 at 12:03
  • thanks also @Vladimir F, who already proposed using a 1:1 third dimension – steabert Sep 23 '11 at 12:11
0

You can write a subroutine for different ranks of array and create an interface Here in example I have shown that how to populate an array of different array using interface statement `

program main 
    use data 
    implicit none 
    real,dimension(:,:,:),allocatable::data 
    integer::nx,ny,nz
    nx = 5
    ny = 10
    nz = 7
    call populate(nx,ny,nz,data)
    print *,data
end program main `

data module is here

module data  
  private 
  public::populate
  interface populate 
      module procedure populate_1d 
      module procedure populate_2d 
      module procedure populate_3d 
  end interface
 contains 
   subroutine populate_1d(x,data)
       implicit none 
       integer,intent(in)::x
       real,dimension(:),allocatable,intent(out):: data
       allocate(data(x))
       data=rand()
   end subroutine populate_1d 
   subroutine populate_2d(x,y,data)
       implicit none 
       integer,intent(in)::x,y
       real,dimension(:,:),allocatable,intent(out):: data
       allocate(data(x,y))
       data=rand()
   end subroutine populate_2d 
   subroutine populate_3d(x,y,z,data)
       implicit none 
       integer,intent(in)::x,y,z
       real,dimension(:,:,:),allocatable,intent(out):: data
       allocate(data(x,y,z))
       data=rand()
   end subroutine populate_3d 
end module data 

There is an interface to populate 1d, 2d and 3d arrays. you can call populate interface instead of calling individual subroutines. It will automatically pick the relevant one.