Most programming languages allow you to collect commonly-used code into procedures that can be reused by calling them from other sections of code.
Fortran has two forms of procedure:
call
statementBoth subroutines and functions have access to variables in the parent scope by argument association;
unless the VALUE
attribute is specified, this is similar to call by reference.
The subroutine input arguments, known as dummy arguments are specified in parentheses after the subroutine name; the dummy argument types and attributes are declared within the body of the subroutine just like local variables.
Example:
! Print matrix A to screen
subroutine print_matrix(n,m,A)
implicit none
integer, intent(in) :: n
integer, intent(in) :: m
real, intent(in) :: A(n,m)
integer :: i
do i=1,n
print *,A(i,1:m)
end do
end subroutine print_matrix
Note the additional intent
attribute when declaring the dummy arguments; this optional attribute signifies to the compiler whether the argument
is ‘read-only’ (intent(in)
) ‘write-only’ (intent(out)
) or ‘read-write’ (intent(inout)
) within the procedure.
In this example, the subroutine does not modify its arguments, hence all arguments are intent(in)
.
Tip: It is good practice to always specify the intent
attribute for
dummy arguments; this allows the compiler to check for unintentional errors and provides self-documentation.
We can call this subroutine from a program using a call
statement:
program call_sub
implicit none
real :: mat(10,20)
mat(:,:) = 0.0
call print_matrix(10,20,mat)
end program call_sub
Note: This example uses a so-called explicit-shape array argument since we have passed additional variables to describe
the dimensions of the array A
; this will not be necessary if we place our subroutine in a module as described later.
! L2 Norm of a vector
function vector_norm(n,vec) result(norm)
implicit none
integer, intent(in) :: n
real, intent(in) :: vec(n)
real :: norm
norm = sqrt(sum(vec**2))
end function vector_norm
Tip: In production code, the intrinsic function norm2
should be used.
To execute this function:
program run_fcn
implicit none
real :: v(9)
real :: vector_norm
v(:) = 9
print *, 'Vector norm = ',vector_norm(9,v)
end program run_fcn
Tip: It is good programming practice for functions not to modify their arguments - i.e. all function arguments should be intent(in)
- such
functions are known as pure
functions. Use subroutines if your procedure needs to modify its arguments.
Fortran modules contain definitions that are made accessible to programs, procedures and other modules through the use
statement.
They can contain data objects, type definitions, procedures and interfaces.
Tip: It is recommended to always place functions and subroutines within modules.
Example:
module my_mod
implicit none
private ! All entities are module-private by default
public public_var, print_matrix ! Explicitly export public entities
real, parameter :: public_var = 2
integer :: private_var
contains
! Print matrix A to screen
subroutine print_matrix(A)
real, intent(in) :: A(:,:) ! An assumed-shape dummy argument
integer :: i
do i=1,size(A,1)
print *,A(i,:)
end do
end subroutine print_matrix
end module my_mod
Note: Compare this print_matrix
subroutine with that written outside of a module;
we no longer have to explicitly pass the matrix dimensions and can instead take
advantage of assumed-shape arguments since the module will generate the required
explicit interface for us. This results in a much simpler subroutine interface.
To use
the module within a program:
program use_mod
use my_mod
implicit none
real :: mat(10,10)
mat(:,:) = public_var
call print_matrix(mat)
end program use_mod
Example: explicit import list
use my_mod, only: public_var
Example: aliased import
use my_mod, only: printMat=>print_matrix
Note: Each module should be written in a separate .f90 source file. Modules need to be compiled prior to any program units that use
them.