Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Allocate dynamic array with interdependent dimensions

This is a bit complicated; I'd welcome any comments on how to improve the clarity of the question.

Ok, say I have an array:

real, allocatable :: A(:,:,:)

and I want to allocate it before I use it. Is it possible for the size of third dimension to depend on the size of the second dimension?

E.g.

do i=1,n
allocate(A(3,i,i**2))
end do

Obviously the above doesn't work. I'd like to end up with an array (or a set of arrays) with the shape(s)

(3,1,1), (3,2,4), (3,3,9), ... (3, n, n^2)

where the size of the third dimension is the square of the size of the second dimension.

My rule for the size of the dependent dimension is a bit more complicated, but if squaring is possible I can do the rest.

Is this possible? If so, how might I implement it in Fortran?

What would shape(A) return? That would be interesting.

My other alternative is to allocate to the maximum size required, and be careful to only use certain elements in calculations, i.e.

allocate(A(3,n,n**2))

Even though I'm not hard up on memory at the moment, I'd like to have good programming practices. This is an interesting problem anyway.

Thank you.

EDIT:

What about having the size of a dimension depend on the value of an element in another dimension?

In the answer below the size of array in both dimensions depended on the index of B. I'd like something along the lines of

type myarray
    real :: coord(3)
    integer,allocatable :: lev(:)
    integer, allocatable :: cell(:)
endtype myarray

type(myarray), allocatable :: data

allocate(data(m))
allocate(data%lev(n))

forall (j=1:n) !simple now, for argument's sake
    lev(j)=j
endforall

! I was thinking of using a FORALL loop here, but the errors returned 
! suggested that the compiler (gfortran) didn't expect IF blocks and ALLOCATE 
! statements in a FORALL block
do i=1,m
    do j=1,n
        allocate(data(i)%cell(lev(j)**2))
    enddo
enddo

You get what I mean? But the program falls over as it tries to allocate already allocated variables, e.g. when i=1 it allocates data(1)%cell(1), and then tries to allocate data(1)%cell(2)...uh oh. What I want is something like:

Each data(i) has an array lev(j) of values, with j running from 1 to n, and for each lev(j) value we have a cell of size lev^2. Note that these cell's are unique for each data(i) and each lev, and that the size of that particular cell depends on the corresponding lev value, and possibly the corresponding data(i) too.

Would I have to use a derived type within a derived type?

like image 377
Samuel Tan Avatar asked Dec 19 '11 04:12

Samuel Tan


People also ask

Can we allocate a 2 dimensional array dynamically?

A 2D array can be dynamically allocated in C using a single pointer. This means that a memory block of size row*column*dataTypeSize is allocated using malloc and pointer arithmetic can be used to access the matrix elements.

How do you dynamically allocate an array?

dynamically allocated arrays To dynamically allocate space, use calls to malloc passing in the total number of bytes to allocate (always use the sizeof to get the size of a specific type). A single call to malloc allocates a contiguous chunk of heap space of the passed size.

How can you increase the size of a dynamically allocated array?

Allocate a new[] array and store it in a temporary pointer. Copy over the previous values that you want to keep. Delete[] the old array. Change the member variables, ptr and size to point to the new array and hold the new size.

How do you dynamically allocate an array in C++?

Dynamic arrays in C++ are declared using the new keyword. We use square brackets to specify the number of items to be stored in the dynamic array. Once done with the array, we can free up the memory using the delete operator. Use the delete operator with [] to free the memory of all array elements.


2 Answers

Yes, you can use a derived type to accomplish this:

TYPE array
  REAL,DIMENSION(:,:,:),ALLOCATABLE :: A
ENDTYPE array

INTEGER :: i
INTEGER,PARAMETER :: n=10

TYPE(array),DIMENSION(:),ALLOCATABLE :: B

ALLOCATE(B(n))

DO i=1,n
  ALLOCATE(B(i)%A(3,i,i*i))
  WRITE(*,*)SHAPE(B(i)%A)
ENDDO

END

This approach allows each element of array B to be a multi-dimensional array of a different shape.

The output of the program is as expected:

        3            1            1
        3            2            4
        3            3            9
        3            4           16
        3            5           25
        3            6           36
        3            7           49
        3            8           64
        3            9           81
        3           10          100

EDIT: To further answer OP's edited question. Yes, it seems like you would need to do something like this, use nested derived type (compare to your code example to figure out what you did wrong):

integer,parameter :: m=3,n=5

type cellarray
  integer,dimension(:),allocatable :: c
endtype cellarray

type myarray
  integer,allocatable :: lev(:)
  type(cellarray),dimension(:),allocatable :: cell
endtype myarray

type(myarray),dimension(:),allocatable :: B

allocate(B(m))

! Allocate and assign lev and cell:
do i=1,m
  allocate(B(i)%lev(n))
  allocate(B(i)%cell(n))
  do j=1,n
    B(i)%lev(j)=j
  enddo
enddo

! Based on value of lev, allocate B%cell%c:    
do i=1,m
  do j=1,n
    allocate(B(i)%cell(j)%c(B(i)%lev(j)**2))
  enddo
enddo

! Print out to check that it works:
do j=1,n
  write(*,*)j,B(1)%lev(j),SIZE(B(1)%cell(j)%c)
enddo

end

Tried this with gfortran 4.6.2. It produces expected output:

       1           1           1
       2           2           4
       3           3           9
       4           4          16
       5           5          25
like image 59
milancurcic Avatar answered Sep 18 '22 21:09

milancurcic


I think you can do this simply by allocating/deallocating the array

Program test
    Implicit none
    Real, dimension(:,:,:), allocatable :: A
    Integer  :: i,N
    Write(*,*)"Enter N"; Read(*,*)N
    Do i = 1, N
    if(Allocated(A)) then 
    deallocate(A);Allocate(A(i,i,i*i))
    else
    allocate(A(i,i,i*i))
    end if
    Write(*,*)Shape(A)
    End do
end program test

Compiling the program using gfortran gives:

 Enter N
5
           1           1           1
           2           2           4
           3           3           9
           4           4          16
           5           5          25
like image 33
s.ouchene Avatar answered Sep 18 '22 21:09

s.ouchene