ryanjdillon
ryanjdillon

Reputation: 18988

How to declare an array variable and its size mid-routine in Fortran

I would like to create an array with a dimension based on the number of elements meeting a certain condition in another array. This would require that I initialize an array mid-routine, which Fortran won't let me do.

Is there a way around that?

Example routine:

subroutine example(some_array)

real some_array(50) ! passed array of known dimension

element_count = 0
do i=1,50
  if (some_array.gt.0) then
    element_count = element_count+1
  endif
enddo

real new_array(element_count) ! new array with length based on conditional statement

endsubroutine example

Upvotes: 3

Views: 1858

Answers (4)

francescalus
francescalus

Reputation: 32451

Your question isn't about initializing an array, which involves setting its values.

However, there is a way to do what you want. You even have a choice, depending on how general it's to be.

I'm assuming that the element_count means to have a some_array(i) in that loop.

You can make new_array allocatable:

subroutine example(some_array)
  real some_array(50)
  real, allocatable :: new_array(:)

  allocate(new_array(COUNT(some_array.gt.0)))
end subroutine

Or have it as an automatic object:

subroutine example(some_array)
  real some_array(50)
  real new_array(COUNT(some_array.gt.0))
end subroutine

This latter works only when your condition is "simple". Further, automatic objects cannot be used in the scope of modules or main programs. The allocatable case is much more general, such as when you want to use the full loop rather than the count intrinsic, or want the variable not as a procedure local variable.

In both of these cases you meet the requirement of having all the declarations before executable statements.

Since Fortran 2008 the block construct allows automatic objects even after executable statements and in the main program:

program example

  implicit none

  real some_array(50)
  some_array = ...

  block
    real new_array(COUNT(some_array.gt.0))
  end block

end program example

Upvotes: 4

High Performance Mark
High Performance Mark

Reputation: 78364

Try this

real, dimension(50) :: some_array
real, dimension(:), allocatable :: other_array
integer :: status
...
allocate(other_array(count(some_array>0)),stat=status)

at the end of this sequence of statements other_array will have the one element for each element of some_array greater than 0, there is no need to write a loop to count the non-zero elements of some_array.

Following @AlexanderVogt's advice, do check the status of the allocate statement.

Upvotes: 2

Kyle Kanos
Kyle Kanos

Reputation: 3264

You need to use an allocatable array (see this article for more on it). This would change your routine to

subroutine example(input_array,output_array)

  real,intent(in) :: input_array(50) ! passed array of known dimension
  real, intent(out), allocatable :: output_array(:)
  integer :: element_count, i

  element_count = 0
  do i=1,50
    if (some_array.gt.0) element_count = element_count+1
  enddo

  allocate(output_array(element_count))

end subroutine

Note that the intents may not be necessary, but are probably good practice. If you don't want to call a second array, it is possible to create a reallocate subroutine; though this would require the array to already be declared as allocatable.

Upvotes: 1

Alexander Vogt
Alexander Vogt

Reputation: 18118

You can use allocatable arrays for this task:

subroutine example(some_array)

real             :: some_array(50)
real,allocatable :: new_array(:)
integer          :: i, element_count, status

element_count = 0
do i=lbound(some_array,1),ubound(some_array,1)
  if ( some_array(i) > 0 ) then
    element_count = element_count + 1
  endif
enddo

allocate( new_array(element_count), stat=status )
if ( status /= 0 ) stop 'cannot allocate memory'

! set values of new_array

end subroutine

Upvotes: 1

Related Questions