High Performance Scientific Computing   Coursera Edition

#### Previous topic

Useful gfortran flags

#### Next topic

Fortran examples: Taylor series

# 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

• 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 

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