Skip to main content

7. Program units and procedures

1. Module definition

In f90, a program unit can be either the main body of the program (started by the program statement you have seen at the top of every code you've been given in this Session so far) or a module.

A module is a device often used in large coding projects that involve several people. Modules perform many functions:

  • modules provide a central location of constants and variables, so that declarations for these objects do not need to be repeated in many places (can provide global declarations).
  • modules can be used to group subprograms together
  • modules are a handy location for storing derived data types (more on this subject later)

A module can be compiled separately, but must be compiled before the main code is compiled. If a module appears in the same file as the main body of code, it must come before the program statement. A module is invoked in the main code by the use statement. Modules and programs can contain program procedures (more later).

Putting it into Practice:

Code up the statements below - it is a trivial example of using a module in a program (called area) where the module myconstants defines some constants.
! declare your module
module myconstants
implicit none
real, parameter :: pi=3.1415927, ee=2.7182818
end module myconstants
! start the main body of code
program area
! include your module with the *use* statement
use myconstants
implicit none
real :: radius, result

print*,"Please enter the radius of a circle"
read*,radius

result = pi * radius**2
print*,"the area of the circle is ",result," units"

end program area
Compile and run this code. Try writing some variations on this code.
Back to top

2. Subroutine/function definition

In f90, a program procedure can be either a subroutine or a function. A subroutine is a named section of code that performs a given task, and can be invoked from within a program unit. A function is similar, but is used to generate a single answer, and is invoked by referring to the function name. The function may take any type (eg integer, real, etc). It is often the case that there is no need to write a procedure yourself, since f90 has many useful intrinsic functions, plus there are many numerical libraries (eg, NAG, BLAS, LAPACK, ATLAS etc) that are available. These libraries will be optimised for running on your particular platform and should be very fast - probably much faster than anything you could write yourself.

A program procedure may be declared and specified within the main body of code, or may be external to the main body of code. A procedure must have arguments (variables or constants) passed to it. Furthermore, a procedure can contain local variables/constants. These local objects are created when the procedure is invoked and destroyed when the program exits from this procedure - these local objects do not retain values between invocations (calls). The procedure declaration must contain placeholders for these passed arguments - called dummy arguments. This is known as argument association.

Example:

In the main body of a code, suppose we make a call to a function called work, passing it a real variable called a, eg
  print*,work(a)
The corresponding definition (declaration) of this function in this code is:
 real function work(x) 
So in this example, the dummy variable is x. When the program enters this function, it will replace the value of x with the current value of a
Back to top

3. Intent attributes

To make your code clearer, you can use something called argument intent in your procedures. This gives hints to the compile about whether your dummy argument will:

  • just be referenced (not changed) - use the intent(in) statement
  • be assigned a value before use - use the intent(out) statement
  • be referenced and assigned a value - use the intent(inout) statement

When a procedure is defined internally, the procedure definition should come just before the end of the main program. The declaration should be preceded by the statement contains.

Putting it into Practice:

Code up the statements below - it brings together a few of the concepts just covered in this section.
program example
implicit none
real :: x,y, result

print*,"Please enter the lengths of the rectangle"
read*,x,y
!***********************************************
! make a call to the subroutine called area
!***
! the "real" attribute makes sure the input numbers are real, even
! if the user puts in integers
!***********************************************
call area(real(x),real(y),result) 

print*,"the area of the rectangle is ",result," units"

! here is a list of one (or more) procedure defintions
! this is for internal procedures
contains
! subroutine declaration contains dummy arguments a,b,c
! these will be substituted for x,y,result (as taken from the call statement)
subroutine area(a,b,c)

real, intent(out) :: c
real, intent(in) :: a,b
! note the intent attributes of these variables
c = a * b
end subroutine area

end program example
Compile and run this code. Try writing some variations on this code.
Back to top

4. Scope rules

Sometimes it's preferable to use variables in your procedures that are defined in the main body of the program. To help with managing this, f90 comes with scope rules. Scope rules tell us if an entity (i.e., variable, parameter and function) is "visible" or accessible at certain places. The places where an entity can be accessed or visible is referred to the scope of that entity. The simplest scope rule is that the scope of an entity is the program or function in which it is declared (this is certainly the case in this last exercise).

Another scope rule is that internal procedures can inherit entities from the main program by host association . Below is an example of some code that might make this clearer:

Example:

  
program prog
implicit none
real :: a,b,c

a = 1.0
b = 2.0
c = 3.0

print*,"a,b,c before subroutine call",a,b,c

call sub(a)

print*,"a,b,c after subroutine call",a,b,c

contains
subroutine sub(d)
real :: d
real :: c

c = a**3
d = d**3 + c
b = c
end subroutine sub

end program prog

Notes on this code:

  • d is a dummy variable (will be substituted for a in the subroutine)
  • a and b are global variables: they are accessible in the subroutine
  • b may have its value changed in the subroutine - and this will carry to the main part of the program
  • the variable a may not have its value changed in the subroutine since it is argument associated. Therefore the variable a must be only referred to in the subroutine.
  • the variable c in the subroutine is totally different to the variable c in the main part of the program. If the value of c changes in the subroutine, this does not alter the value of c in the main program.
Back to top

5. Static variables

You've already been told that local variables in subroutines are destroyed upon exit, so under normal circumstances you can't keep the values of your local variables. However, if you would like to havethe values of your subroutine variables preserved between calls, you can achieve this with the save statement.

Putting it into Practice:

Code up the statements below, to see how the save statement works.
program prog1
implicit none
integer :: a,b,x,y

a = 1
b = 2

print*,"a,b before subroutine call",a,b

call sub1(a)

print*,"a,b after subroutine call",a,b

a = 2 * a
call sub1(a)

print*,"a,b after another subroutine call",a,b

contains
subroutine sub1(d)
integer, save :: number_of_calls = 0
integer :: d

number_of_calls = number_of_calls + 1
b = number_of_calls*d
print*,"number of calls so far is",number_of_calls

end subroutine sub1

end program prog1
Compile and run this code. Try writing some variations on this code.
Back to top

6. Find out more

There is much much more to find out about generic code encapsulation in f90, including interfaces, generic functions, overloaded operators and recursive functions. Consult any f90 book to find out more about this area.