UW AMath High Performance Scientific Computing
 
Coursera Edition

Table Of Contents

Previous topic

Useful gfortran flags

Next topic

Fortran examples: Taylor series

This Page

Fortran subroutines and functions

Functions

Here’s an example of the use of a function:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
! $UWHPSC/codes/fortran/fcn1.f90

program fcn1
    implicit none
    real(kind=8) :: y,z
    real(kind=8), external :: f

    y = 2.
    z = f(y)
    print *, "z = ",z
end program fcn1

real(kind=8) function f(x)
    implicit none
    real(kind=8), intent(in) :: x
    f = x**2
end function f

It prints out:

z =    4.00000000000000

Comments:

  • A function returns a single value. Since this function is named f, the value of f must be set in the function somewhere. You cannot use f on the right hand side of any expression, e.g. you cannot set g = f in the function.
  • f is declared external in the main program to let the compiler know it is a function defined elsewhere rather than a variable.
  • The intent(in) statement tells the compiler that x is a variable passed into the function that will not be modified in the function.
  • Here the program and function are in the same file. Later we will see how to break things up so each function or subroutine is in a separate file.

Modifying arguments

The input argument(s) to a function might also be modified, though this is not so common as using a subroutine as described below. But here’s an example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
! $UWHPSC/codes/fortran/fcn2.f90

program fcn2
    implicit none
    real(kind=8) :: y,z
    real(kind=8), external :: f

    y = 2.
    print *, "Before calling f: y = ",y
    z = f(y)
    print *, "After calling f:  y = ",y
    print *, "z = ",z
end program fcn2

real(kind=8) function f(x)
    implicit none
    real(kind=8), intent(inout) :: x
    f = x**2
    x = 5.
end function f

This produces:

Before calling f: y =    2.00000000000000
After calling f:  y =    5.00000000000000
z =    4.00000000000000

The use of intent

You are not required to specify the intent of each argument, but there are several good reasons for doing so:

  • It helps catch bugs. If you specify intent(in) and then this variable is changed in the function or subroutine, the compiler will give an error.
  • It can help the compiler produce machine code that runs faster. For example, if it is known to the compiler that some variables will never change during execution, this fact can be used.

Subroutines

In Fortran, subroutines are generally used much more frequently than functions. Functions are expected to produce a single output variable and examples like the one just given where an argument is modified are considered bad programming style.

Subroutines are more flexible since they can have any number of inputs and outputs. In particular they may have not output variable. For example a subroutine might take an array as an argument and print the array to some file on disk but not return a value to the calling program.

Here is a version of the same program as fcn1 above, that instead uses a subroutine:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
! $UWHPSC/codes/fortran/sub1.f90

program sub1
    implicit none
    real(kind=8) :: y,z

    y = 2.
    call fsub(y,z)
    print *, "z = ",z
end program sub1

subroutine fsub(x,f)
    implicit none
    real(kind=8), intent(in) :: x
    real(kind=8), intent(out) :: f
    f = x**2
end subroutine fsub

Comments:

  • Now we tell the compiler that x is an input variable and y is an output variable for the subroutine. The intent declarations are optional but sometimes help the compiler optimize code.

Here is a version that works on an array x instead of a single value:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
! $UWHPSC/codes/fortran/sub2.f90

program sub2
    implicit none
    real(kind=8), dimension(3) :: y,z
    integer n

    y = (/2., 3., 4./)
    n = size(y)
    call fsub(y,n,z)
    print *, "z = ",z
end program sub2

subroutine fsub(x,n,f)
  ! compute f(x) = x**2 for all elements of the array x 
  ! of length n.
  implicit none
  integer, intent(in) :: n
  real(kind=8), dimension(n), intent(in) :: x
  real(kind=8), dimension(n), intent(out) :: f
  f = x**2
end subroutine fsub

This produces:

4.00000000000000        9.00000000000000        16.0000000000000

Comments:

  • The length of the array is also passed into the subroutine. You can avoid this in Fortran 90 (see the next example below), but it was unavoidable in Fortran 77 and subroutines working on arrays in Fortran are often written so that the dimensions are passed in as arguments.

Subroutine in a module

Here is a version that avoids passing the length of the array into the subroutine. In order for this to work some additional interface information must be specified. This is most easily done by including the subroutine in a module.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
! $UWHPSC/codes/fortran/sub3.f90

module sub3module

contains 

subroutine fsub(x,f)
  ! compute f(x) = x**2 for all elements of the array x. 
  implicit none
  real(kind=8), dimension(:), intent(in) :: x
  real(kind=8), dimension(size(x)), intent(out) :: f
  f = x**2
end subroutine fsub

end module sub3module

!----------------------------------------------

program sub3
    use sub3module
    implicit none
    real(kind=8), dimension(3) :: y,z

    y = (/2., 3., 4./)
    call fsub(y,z)
    print *, "z = ",z
end program sub3

Comments:

  • See the section Fortran modules for more information about modules. Note in particular that the module must be defined first and then used in the program via the use statement.

  • The declaration of x in the subroutine uses dimension(:) to indicate that it is an array with a single index (having rank 1), without specifying how long the array is. If x was a rank 2 array indexed by x(i,j) then the dimension statement would be dimension(:,:).

  • The declaration of f in the subroutine uses dimension(size(x)) to indicate that it is an array with one index and the length of the array is the same as that of x. In fact it would be sufficient to tell the compiler that it is an array of rank 1:

    real(kind=8), dimension(:), intent(out) :: f

    but indicating that it has the same size ax x is useful for someone trying to understand the code.