3

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.

2
  • Do you really require Fortran 90 or 95 or do you allow Fortran 2003 (the automatic re-allocation the answers use is from Fortran 2003 and is very useful). Commented Feb 13, 2016 at 9:29
  • Both are possible (prefer Fortran 90/95), but need to deal with loops and indices as @roygvib presented his answer. Commented Feb 14, 2016 at 4:30

2 Answers 2

3

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.)

Sign up to request clarification or add additional context in comments.

Comments

2

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.