bosona
bosona

Reputation: 43

Resize array generated by the loop

Let's have an array A(:,:)

Real, Allocatable:: A(:,:), B(:) 
Integer, Allocatable:: rowin(:) 
Integer:: m,j,k, nl

Allocate(A(max1,max2))

defined by several loops over m,j,k

 nl = 0

 A(rowin(m)+j,k) = ...

and now I want to select subset of A(:,:) such that their values are negative and store them into array B(:) unknown length yet, i.e.

if(A(rowin(m)+j,k).lt.0.d0) then
  nl = nl + 1
  B(nl) = A(rowin(m)+j,k)               
end if

Since max1 and max2 are very large numbers, I don't want to allocate B(:) of the length max1*max2, but exactly of the length that A(:,:) contains negative values. Also, I don't want to go through complicated loops over m,j and k again. Thank you.

Upvotes: 3

Views: 1884

Answers (2)

roygvib
roygvib

Reputation: 7395

If pre-allocation of B to some fixed size is not desirable, we can emulate a dynamic array by using automatic reallocation of an allocatable array. In the simplest case, we can expand B element-by-element by adding a new value. For example, suppose that we have double loops over m and j, select values that match a given condition, and add tho values to B. Here, the size of B grows from 0, 1, 2, ... automatically.

integer, allocatable :: B(:)
integer :: m, j

allocate( B( 0 ) ) !! start from an empty array

do m = 1, 10000
do j = 1, 10000

    !! Some condition to add an element, e.g.
    if ( m == j .and. mod( m, 1000 ) == 0 ) then

        !! Add a new element to B.
        B = [ B, m ]
    endif
enddo
enddo

print *, "size( B ) = ", size( B )
print *, B(:)

The result becomes

size( B ) =  10
1000 2000 3000 4000 5000 6000 7000 8000 9000 10000

In your case you can probably add desired values of A in a similar way, for example

allocate( B( 0 ) )

do m = 1, ...
do j = 1, ...
do k = 1, ...
    if ( A( rowin(m)+j, k ) < 0.d0 ) then   !! or any condition
        B = [ B, A(rowin(m)+j,k) ]
    end if
enddo
enddo
enddo

A drawback of this approach is that it is very inefficient because B is re-allocated every time a new element is added. Although this is no problem for small B, it may become a serious bottleneck for large B. In this case one can increase the size of B geometrically rather than increasing by 1. For example, the following code doubles the size of B as needed so as to decrease the number of reallocation.

integer, allocatable :: B(:)
integer :: m, j, nl, p

allocate( B( 0 ) ) !! start from an empty array
nl = 0             !! number of elements found

do m = 1, 10000
do j = 1, 10000

    if ( m == j .and. mod( m, 1000 ) == 0 ) then

        nl = nl + 1

        !! Expand B if the size becomes insufficient.
        if ( size(B) < nl ) B = [ B, ( 0, p=1,nl ) ]

        !! Add a new value.
        B( nl ) = m 
    endif
enddo
enddo

B = B( 1 : nl )  !! adjust B to the optimal size

Some more notes:

  • If a similar approach is used very often, it is convenient to write a utility routine like resize() that takes an allocatable array as an argument and changes its size. If the efficiency truly matters, it may be better to use allocate() and move_alloc() explicitly in resize() to eliminate one temporary array.
  • The above code needs relatively new compilers that supports automatic reallocation of allocatable arrays in the left-hand side.
  • If you use Intel fortran, it is necessary to add the -assume realloc_lhs option. If the size of B can become rather large, you will also need -heap-arrays option. For gfortran or Oracle fortran, no special options are necessary.
  • We should not attach colon in the left-hand side; i.e., B(:) = [ B, m ] is NG
  • For more details about dynamic arrays (e.g., growth rate), please see Wiki page.

Upvotes: 2

High Performance Mark
High Performance Mark

Reputation: 78316

This is fairly straightforward:

b = pack(a,a<0.0)

will pack the negative elements of a into the rank-1 array b. With a recent compiler b will be automatically allocated to the correct size during the assignment.

(The latest Fortran standard gives a very similar operation as an example in the documentation of the pack function.)

Upvotes: 3

Related Questions