Sergey Budaev

Nov 20, 2019

How to make an array and initialize it with a sequence of values in Fortran?

How do you make an array and initialize it with a sequence of values? For example, I want a list from 0.25 to 1.5 that is separated with 0.25. In other words I want something similar to seq(0.25,5,0.5) in R.

Equally spaced real array with fixed increment in Fortran

Producing an equally spaced array from V1 to VN with increments ΔV

1. Each of the values in the above vector can be calculated as:

2. The total number of values N in the array ending with a fixed known VN is equal to

3. It is not possible to use a simple piece of code like this to produce real type array in Fortran:

Array = [V1:VN:Incr]

4. Such a construction cannot be used in modern Fortran, even though old versions could accept a similar construction based on implied loop with real type index counter:

real :: r ! Index must be integer in loops!
print *, (r, r=V1,VN,Incr)

5. In modern Fortran standard do loops can only have integer indexing variable. Real indexing in do loops is one of the very few features that had been deleted from the language because it can create lots of problems in float point computations due to finite precision in computer hardware.

The old code might work with modern compilers but it may require special legacy compiler options. The printing-only code as above may still work but would issue a compiler warning.

6. Initialising such equally spaced real type arrays in Fortran implied loops must use the formulas defined in 1. and 2.

# Produce exactly N_VALS values starting from INIT with increments INCR
Array = [( INIT + INCR * (i-1), i=1,N_VALS )]

Where the number of array elements N_VALS is calculated as:

N_VALS = floor( (END - INIT) / INCR + 1 )

N_VALS = ceiling( (END - INIT) / INCR + 1 )

The floor and ceiling functions convert real value to integer as the lower or upper nearest integer; they can give different values when division cannot be done without the remainder

# All values starting from INIT with increments INCR and up to the limit END
Array = [( INIT + INCR * (i-1), i=1,floor((END-INIT)/INCR+1) )]

7. This code does not seem to be a very simple and elegant solution. Ideally, the code should be packaged into a function returning the desired grid array. But such function could not be used in declarations of array parameters. In the later case the one-liner code should be used as above.

Integer arrays

By the way, it is quite easy to produce an integer array, e.g. here is an initialisation for array from 1 to 100 (|1,2,3,...,100|). This can be useful for indexing arrays.

integer, parameter, dimension(*) :: IDX_ARRAY = (/(i,i=1,100)/)

Examples:

A. Produce an array of 10 values starting from 1.0 with increments 0.1

Array = [( 1.0 + (i-1) * 0.1, i=1,10 )]

Result:

1.00000000 1.10000002 1.20000005 1.29999995 1.39999998
1.50000000 1.60000002 1.70000005 1.79999995 1.90000010

Declaration of a parameter array:

real, parameter, dimension(*) :: Array = [( 1.0 + (i-1) * 0.1, i=1,10 )]

However, note that not all compilers may support assumed array size dimension(*) in such array declaration statement, this requires newer Fortran standard (fortunately, recent versions of Intel and GNU Fortran do support assumed size arrays). In such a case declaration must explicitly set the number of array elements:

real, parameter, dimension(10) :: Array = [( 1.0 + (i-1) * 0.1, i=1,10 )]

B. Produce an array of starting from 1.0 to 2.0 with increments 0.145; note that lower value (floor) for the array size is used:

Array = [( 1.0 + 0.145 * (i-1), i=1, floor((2.0-1.0)/0.145 + 1) )]

Result:

1.00000000 1.14499998 1.28999996 1.43499994 1.57999992
1.72499990 1.87000000

C. The same as (B) but the upper value (ceiling) for the array size is used:

Array = [( 1.0 + 0.145 * (i-1), i=1, ceiling((2.0-1.0)/0.145 + 1) )]

Result:

1.00000000 1.14499998 1.28999996 1.43499994 1.57999992
1.72499990 1.87000000 2.01499987

D. In the case B., declarations of parameter arrays can be done like this:

real, parameter, dimension(*) :: Array =                                  &
                 [( 1.0 + 0.145 * (i-1), i=1, floor((2.0-1.0)/0.145 + 1) )]

or, if the compiler does not support assumed size arrays (*), with explicitly calculated array size:

real, parameter, dimension(floor((2.0-1.0)/0.145 + 1)) :: Array =         &
                 [( 1.0 + 0.145 * (i-1), i=1, floor((2.0-1.0)/0.145 + 1) )]

Test program

! This program illustrates how to produce equally spaced real vectors with
! fixed increment in Fortran.
!
! 1. Produce exactly N_VALS values starting from INIT with increments INCR
! Array = [( INIT + INCR * (i-1), i=1,N_VALS )]
!
! 2. All values starting from INIT with increments INCR and up to the limit END
! Array = [( INIT + INCR * (i-1), i=1,floor((END-INIT)/INCR+1) )]
!-------------------------------------------------------------------------------
program spaced_array

    ! Integer counter for implied loops defining vectors.
    integer :: i

    ! Example A. Produce an array of 10 values
    ! starting from 1.0 with increments 0.1
    real, parameter, dimension(*) :: Array1 = [( 1.0 + (i-1) * 0.1, i=1,10 )]


    ! Example B. Produce an array of starting from 1.0 to 2.0
    ! with increments 0.145.
    ! Note that lower value (floor) for the array size is used.
    real, parameter, dimension(*) :: Array2 = &
    [( 1.0 + 0.145 * (i-1), i=1, floor((2.0-1.0)/0.145 + 1) )]

    ! Example C. The same as (B) but the upper value (ceiling) for the
    ! array size is used.
    real, parameter, dimension(*) :: Array3 = &
    [( 1.0 + 0.145 * (i-1), i=1, ceiling((2.0-1.0)/0.145 + 1) )]

    ! Print the sizes of the arrays that were declared above.
    print *, "Array sizes (Array1, Array2, Array3)", &
    size(Array1), size(Array2), size(Array3)

    ! Print the parameter arrays that were declared above.
    print *, "Array1", Array1
    print *, "Array2", Array2
    print *, "Array3", Array3

end program spaced_array

PDF Card

A PDF version of this document is available here: https://budaev.info/images/spaced-array.pdf.