You are on page 1of 86

1

Einf
uhrung in die Numerische Programmierung mit
dem Schwerpunkt Fortran 90

Georg Kresse, A. Eichler and Robert Lorenz

Universitat Wien
March 2011

Literature
Fortran 90 explained
Michael Metcalf, John Reid, Oxford University Press
Fortran 90
Regionales Rechenzentrum f
ur Niedersachsen RRZN
Fortran 90 programming
T.M.R. Ellis, Ivor R. Philips, Thomas M. Lahey, Addison-Wesley
An Introduction to Computer Simulation Methods
H. Gould, and J. Tobochnik, Addison-Wesley

CONTENTS

Contents
1 Lets get started: a simple example program
1.1 Language elements of F90 . . . . . . . . . . .
1.1.1 Basic elements . . . . . . . . . . . . .
1.2 Source format and statements . . . . . . . . .
1.2.1 Specification statements . . . . . . . .
1.2.2 Assignment . . . . . . . . . . . . . . .
1.2.3 Specification of a named constant . . .
1.2.4 Input and Output statements . . . . .
1.2.5 A few style recommendations . . . . .
1.3 Compilation . . . . . . . . . . . . . . . . . . .
1.4 A first look at the IF statement . . . . . . . .
1.5 A first look at the DO statement . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

6
6
6
7
7
8
8
9
9
9
10
11

2 A little bit more complicated: Types, LOOPs and IFs


2.1 Concept of types . . . . . . . . . . . . . . . . . . . . . . .
2.1.1 Specification of variables and named constants . . .
2.1.2 Single precision and double precision . . . . . . . .
2.1.3 KIND attribute . . . . . . . . . . . . . . . . . . . .
2.1.4 Implicit typing: IMPLICIT NONE . . . . . . . . .
2.2 Vectors, Matrices and Tensors . . . . . . . . . . . . . . . .
2.2.1 Input and output statements . . . . . . . . . . . . .
2.2.2 DO loops . . . . . . . . . . . . . . . . . . . . . . .
2.3 Infinite loops: the conversion program again . . . . . . . .
2.4 The IF construct in more detail . . . . . . . . . . . . . . .
2.5 A small example program: a calculator . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

11
12
13
13
14
15
15
16
17
18
19
20

3 Expressions and assignments in more detail


3.1 Scalar numerical expressions and assignment
3.2 Scalar character expressions and assignment
3.3 Scalar relational operators . . . . . . . . . .
3.4 Scalar logical operators . . . . . . . . . . . .
3.5 Precedence . . . . . . . . . . . . . . . . . . .
3.6 Array-Expressions and array-assignment . .

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

21
22
22
23
23
24
25

4 Some example programs


4.1 Sum of input values . .
4.2 Mean square deviation
4.3 Minimum . . . . . . .
4.4 Sorting . . . . . . . . .
4.5 Inproduct . . . . . . .
4.6 Matrix times Vector .

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

26
26
26
26
27
27
27

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

CONTENTS

5 FUNCTIONS and MODULES: the mean value again


5.1 Mean value . . . . . . . . . . . . . . . . . . . . . . . .
5.2 Functions and Modules . . . . . . . . . . . . . . . . . .
5.3 Sorting again . . . . . . . . . . . . . . . . . . . . . . .
5.4 Subroutines . . . . . . . . . . . . . . . . . . . . . . . .
5.5 Built-in Functions . . . . . . . . . . . . . . . . . . . . .
5.6 Lets add some extra flexibility . . . . . . . . . . . . .

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

28
28
29
30
31
32
35

6 PROGRAM units and MODULES in more detail


6.1 The Main PROGRAM . . . . . . . . . . . . . . . . . .
6.2 External procedure . . . . . . . . . . . . . . . . . . . .
6.3 Modules . . . . . . . . . . . . . . . . . . . . . . . . . .
6.4 Order of statements . . . . . . . . . . . . . . . . . . .
6.5 Dummy arguments - actual arguments - local variables
6.5.1 Local variables . . . . . . . . . . . . . . . . . .
6.6 Functions . . . . . . . . . . . . . . . . . . . . . . . . .
6.7 Examples . . . . . . . . . . . . . . . . . . . . . . . . .
6.7.1 Modules for storing constants . . . . . . . . . .
6.7.2 Reverse polish calculator . . . . . . . . . . . . .
6.8 Recursive Functions and Subroutines: the factorial . . .
6.9 A faster factorial subroutine . . . . . . . . . . . . . . .
6.10 Scope . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.11 Passing functions as arguments . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

36
36
37
37
38
38
39
40
40
40
40
41
42
43
45

. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
or rEw.dEe
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

47
47
47
48
49
49
50
51
51
52
52
52
52
52
53

8 More about arrays


8.1 Literal constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.2 Array constructors . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.3 Rank, shape and size . . . . . . . . . . . . . . . . . . . . . . . . . . . .

54
54
55
55

7 Boring but required: Input and Output


7.1 OPEN and CLOSE statements . . . . . . . .
7.2 READ, WRITE statements . . . . . . . . . .
7.3 List directed input/output, FMT= * . . . . .
7.4 Formated input/output . . . . . . . . . . . .
7.5 Edit descriptors . . . . . . . . . . . . . . . .
7.5.1 Grouping edit descriptors . . . . . . .
7.5.2 Integer Format (I-Format): rIw . . . .
7.5.3 Float Format (F-Format): rFw.d . . .
7.5.4 Exponential Format (E-Format): rEw.d
7.5.5 Logical Format (L-Format): rLw . . . .
7.5.6 Character Format (A-Format): rAw .
7.5.7 General Format (G-Format): rGw.dEe
7.5.8 Blank Format (X-Format): rX . . . .
7.5.9 New Record (/-Format): r/ . . . . . .

CONTENTS

8.4
8.5
8.6
8.7
8.8
8.9
8.10
8.11

Array-Section . . . . . . . . .
Zero sized arrays . . . . . .
Assumed shape array . . . .
Automatic arrays . . . . . . .
Elemental intrinsic functions
Array valued function . . . .
Allocatable arrays . . . . . .
WHERE stmt . . . . . . . .

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

55
56
56
56
57
57
57
58

9 Derived Types and operator overloading


9.1 Derived Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9.2 Operator overloading . . . . . . . . . . . . . . . . . . . . . . . . . . . .

59
59
59

10 Numerical representation of
10.1 Rounding Error . . . . . .
10.2 Floating-point Formats . .
10.3 The IEEE Standard . . . .

62
62
62
62

numbers
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .

11 Integration of Functions
11.1 NewtonCotes formulas . . . . . . . . . . . . . . . . . .
11.2 Derivation of Simpson rule using Lagrange polynomials
11.3 The open NewtonCotes Formulas . . . . . . . . . . . .
11.4 Combined Formulas (Zusammengesetzte Formeln) . . .
11.5 Summary . . . . . . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.

64
65
67
68
68
70

12 Numerical Differentiation
12.1 Interpolation Formulas . . . . . . . . . . . . . . . . . . . . . . . . . . .
12.2 Example: derivative of exp(x) . . . . . . . . . . . . . . . . . . . . . . .

71
71
72

13 Stochastic Methods: Monte-Carlo methods the rejection method

73

14 Ordinary Differential Equations


14.1 Introduction . . . . . . . . . . . . . . . .
14.2 Theory . . . . . . . . . . . . . . . . . . .
14.3 Systems of Differential Equations . . . .
14.4 Numerical Solution Methods . . . . . . .
14.5 One-Step Methods . . . . . . . . . . . .
14.5.1 Taylor Series Methods . . . . . .
14.6 Runge-Kutta Methods . . . . . . . . . .
14.6.1 Classical RungeKutta Formulas
14.7 Example code . . . . . . . . . . . . . . .
14.8 Some Implementation Issues . . . . . . .
14.9 The Codes RKSUITE . . . . . . . . . .
14.9.1 Where to find it . . . . . . . . . .
14.10Multi-Step Methods . . . . . . . . . . . .

76
76
76
77
77
78
79
80
82
83
85
85
86
86

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

LETS GET STARTED: A SIMPLE EXAMPLE PROGRAM

Lets get started: a simple example program

We will set out from a very simple example program; the conversion of a number from
one unit to another unit. The F90 source code is shown below (conversion1.f90):
PROGRAM conversion1
REAL :: a, result
WRITE(*,*) "convert cal to Joules"
READ(*,*) a
! read in a number
result = a * 4.186
WRITE(*,"(F10.5)") result
END PROGRAM conversion1
C source code:
main() {
double a, result ;
printf("convert cal to Joules\n") ;
scanf("%10.5lf", &a) ;
result = a * 4.186
printf("%10.5lf", result) ;
}

1.1
1.1.1

Language elements of F90


Basic elements

A F90 program must contain only characters from the F90 character set, which consists
of the following characters:
the letters A ... Z and a ... z,
the numerals 0 ... 9,
the underscore
and the following special characters:
=
!

:
"

+
%

blank
&

/
<

(
>

)
?

(old Fortran 77)


(new Fortran 90)

From these components tokens are build. A token is similar to a word in human languages.
In F90, upper case and lower case characters are equivalent outside a string; this
implies that a variable result is considered to be equivalent to the variable Result or
RESULT.
F90 distinguishes six classes of tokens:

LETS GET STARTED: A SIMPLE EXAMPLE PROGRAM

Labels:
Constants:
Keywords:
Operators:
Names:
Separators:

123
(1-5 numerals, not recommened)
123.456789
PROGRAM
+ - * / **
solve_equation (up to 31 characters, including underscore _)
/
(
)
(/
/)
,
=
=>
:
::
;
%

In F90 and C, only one name space exists: functions, derived types must have different
names (exceptions to this rule will be discussed later).

1.2

Source format and statements

Tokens are grouped to form statements (loosely speaking sentences):


In F90, 132 characters per line are allowed.
The exclamation mark ! starts a comment and all comments extend to the end
of the line.
Semi-colons ; and a newlines are used to separate statements.
The ampersand & initiates the continuation of a statement on the next line.
In this case the next line may optionally also start with an ampersand &
x= y
x= y
&

&
* 12
&
* 12

!
;
!
;

continue on next line


z = y * 24 ! line continues here
continue on next line
z = y * 24 ! line continues here

In the first example, we can find three classes of statements which are explained in the
following sections.
1.2.1

Specification statements

PROGRAM conversion
REAL :: a, result
...
END PROGRAM conversion
The PROGRAM statement tells the computer that a legal F90 program follows.
The REAL statement declares the two variables a and result. Each variable points
to a storage location, which can hold a numerical number. With the declaration of a
variable sufficient space is allocated (generated) to store a floating point number.

variable a

variable result

storage unit for a

storage unit for result

LETS GET STARTED: A SIMPLE EXAMPLE PROGRAM

In F90 (and most programming languages), all specifications must be placed before
any executable statement, i.e. it is not possible to declare a variable somewhere in the
middle of the program.
1.2.2

Assignment

An assignment is a statement that assigns a value to a variable. In the previous


example, the variable result is set to the current value of variable a multiplied by the
constant 4.186.
result = a * 4.186

variable a

variable result
1.2.3

value read: e.g. 10

41.86 (10 * 4.186)

Specification of a named constant

A handy and commonly used feature of F90, is the declaration of a named constants.
Let us assume that we want to convert calories to joules at many times in the program,
and from the top of the head we know only the first three digits of the conversion
factor. In this case, we simply create a named constant termed cal to joule and use
this named constant instead of the value 4.186 throughout the program:
PROGRAM conversion2
REAL :: a, b, result
REAL, PARAMETER :: cal_to_joule= 4.186
WRITE(*,*) "convert cal to Joules"
READ(*,*) a
result= a * cal_to_joule
WRITE(*,"(F10.5)") result
END PROGRAM conversion2
If we want to change the conversion factor at a later time, we need to change only a
single value in the program. In addition, our program has become more transparent.
Alternatively, we could have stored the value 4.186 in a variable. This, however, is not
particularly save, since variables can be changed anywhere in the program, whereas a
named constant can be set only when the constant is declared. In summary, named
constants make programs less susceptible to errors and more transparent (defensive
programming). In Fortran, named constants behave as conventional constants, and
can be used wherever conventional constants can be used.

LETS GET STARTED: A SIMPLE EXAMPLE PROGRAM

1.2.4

Input and Output statements

To allow the program to interfere with the user, input and output statements are
required. In the previous example, the program stops when it encounters the READ
statement and waits until the user types in a value and finishes his input using the
<return> or <enter> key. The WRITE statement, writes the current value of the variable
result onto the screen.
READ(*,*) a
WRITE(*,"(F10.5)") product
! "(F10.5)" write a floating point number with a total of 10 digits
! with 5 digits after the comma
If we do not care for formating, we could have alternatively used the statement:
WRITE(*,*) "input ",a," result ",result
The WRITE(*,*) statement can be applied to print any number of variables and any
type of variables.
1.2.5

A few style recommendations

All F90 keywords should be written in upper case !


Indent by 2 columns, after the PROGRAM statement, or in IF and DO blocks!

1.3

Compilation

The file that contains the user readable text is usually called source code. In F90, the
source code is stored in a file with the extension .f90.
conversion1.f90
Such files are generated using a standard editor or, preferably, a programming editor
that is specially designed to assist the programmer in writing programs. They are often
able to perform basic syntax checks. In the exercises, we recommend to use a powerful
editor available in the UNIX word (Xemacs).
After the source file has been typed in, a compiler is used to generate a machine
readable executable from the human readable source file. On most systems, the Fortran90 compiler is called by typing f90
f90 conversion.f90 -o conversion
g90 conversion.f90 -o conversion
gfortran conversion.f90 -o conversion
ifc conversion.f90 -o conversion

# Gnu Fortran compiler


# alternative Gnu Fortran compiler
# intel fortran compiler

The compiler generates an executable file with no extension (conversion1) (or the
extension .exe on MS-Windows systems). The program can be executed by typing

LETS GET STARTED: A SIMPLE EXAMPLE PROGRAM

10

conversion1
on the keyboard.
The usual program writing cycle consists of several steps summarised below:
analyse problem.
design program, often setting out from the data and data structures.
Coding, ideally starting with comments or written documentation what the program is supposed to do.
Compile
Test
Amend program
Compile
Test
Testing often involves inserting WRITE statements at critical points in the source code
to determine whether the program does what the programmer expects it to do.

1.4

A first look at the IF statement

PROGRAM conversion_if
INTEGER, PARAMETER :: n = 5
REAL :: a, r
REAL, PARAMETER :: c= 4.186
WRITE(*,*) "convert cal to Joules"
READ(*,*) a
! read in five values and store in a
IF (a==0) THEN
WRITE(*,*) "Null bleibt immer null"
ENDIF
IF (a==1) THEN
WRITE(*,*) "Die Konversionkonstante ist",c
ENDIF
r= a * c
! multiply each value of a with c
WRITE(*,"(F10.5)") r ! write result vector to screen
END PROGRAM conversion_if
The IF statement allows to deviate from the usual line by line execution. If the expression in the IF statement is true, all statements between THEN and ENDIF are executed.
The expression in the parenthesis of the IF statement must yield a logical value. Such
logical expressions are frequently build from comparison between (numerical values),
where the following relational operators for comparison between numerical expressions
are allowed

A LITTLE BIT MORE COMPLICATED: TYPES, LOOPS AND IFS

<

<=

==

/=

>

11

>=

corresponding to less, less or equal, equal, not equal, larger, and larger or equal.

1.5

A first look at the DO statement

PROGRAM conversion_DO
INTEGER, PARAMETER :: n = 5
INTEGER :: i
REAL :: a, r
REAL, PARAMETER :: c= 4.186
WRITE(*,*) "convert cal to Joules"
DO i=1,n
READ(*,*) a
! read in five values and store in a
r=a*c
WRITE(*,*) i,a,r ! write result to screen
ENDDO
WRITE(*,*) "das wars"
END PROGRAM conversion_DO
The DO statement also allows to deviate from the usual line by line execution of a
PROGRAM. The statement between the lines DO and ENDDO are executed n times. In the
body of the loop (between the statements DO and ENDDO) the variable i will take on
the values
1
2
3
...
n

first execution
second execution

n.th execution

It is even possible to do calculations using the value of the variable i. For instance
expressions such as
r=a*c*i
are allowed.

A little bit more complicated: Types, LOOPs


and IFs

Let us assume we want to convert a set of numbers from calories to joules. The following
program will do exactly this:

A LITTLE BIT MORE COMPLICATED: TYPES, LOOPS AND IFS

12

PROGRAM conversion3
INTEGER, PARAMETER :: n = 5
REAL :: a(n), r(n)
REAL, PARAMETER :: c= 4.186
WRITE(*,*) "convert cal to Joules"
READ(*,*) a
! read in five values and store in a
r= a * c
! multiply each value of a with c
WRITE(*,"(F10.5)") r ! write result vector to screen
END PROGRAM conversion3
Thats a couple of new things to digest.

2.1

Concept of types

F90 is a strongly typed language; any constant or variable must have a specific type.
F90 supports 5 intrinsic types with constants and variables for each of these types. For
completeness, all types are listed below together with an example for literal constants
of this type:
INTEGER
REAL
COMPLEX
CHARACTER(LEN=40)
LOGICAL

123
-15 +3
1.5
100.76 1.5E+10
1.5
(1.5,1)
(1.5,0.3)
"1234" Hallo "Georgs"
.TRUE. .FALSE.

Note:
REAL constants have a dot, or exponentiation symbol E (single precision) or
D (double precision), whereas INTEGER constants neither have a dot nor an
exponentiation symbol.
CHARACTER constants are enclosed in double quotes () or single quotes (). There
is no difference between single and double quotes.
If CHARACTER constants go over more than one line, a continuation letter should
be used at the end of the current and at the beginning of next line:
"Hallo this is &
&a test"
A LOGICAL constant, can take only the values .TRUE. or .FALSE.. Abbreviations are not allowed in the source code.

13

A LITTLE BIT MORE COMPLICATED: TYPES, LOOPS AND IFS

2.1.1

Specification of variables and named constants

A specification statement creates space for a variable with a specific name, and possibly
initialises the variable. Since F90 is a strongly typed language, each variable must have
one specific type when it is declared, and it can store only values of this type.
Examples for the declaration of the five available types are given below:
INTEGER :: loop=0, i, j
REAL
:: x, y=0
COMPLEX :: c
CHARACTER
:: s1*(20), s2*(20)

! a string variable
! that can hold 20 characters
! same as above

CHARACTER(LEN=20) :: s1, s2

Named constants are created by adding the PARAMETER attribute:


REAL, PARAMETER :: cal_to_joule=

4.186, stop_value=-1

A named constant can not be overwritten in other places of the program. As already explained, this makes the program saver, more readable and less prone to programming
errors.
2.1.2

Single precision and double precision

All CPUs currently on the market allow for the native manipulation of single precision
and double precision floating point and integer numbers. Single precision numbers are
stored in a binary format in four bytes (32 bits), whereas double precision numbers
are stored in 8 bytes (64 bits), and the precise data layout is defined in the IEEE 754
standard for floating point numbers shown below:
float or REAL

31 30

1 word

4 bytes 32 bits

22

sign Exponent

double

2 word

63 62
sign

Mantisse

8 bytes 64 bits

51
Exponent

0
Mantisse

For single precision floating point numbers, 24 bits are reserved for the sign and the
mantissa, and 8 bits for the exponent. This allows for 7-8 significant digits in the
decimal system and a range of values between 1045 1038 .
For double precision floating point numbers, 53 bits are reserved for the sign and
the mantissa, and 11 bits for the exponent. This allows for 15-16 significant digits in
the decimal system and a range of values between 10324 10308 .

A LITTLE BIT MORE COMPLICATED: TYPES, LOOPS AND IFS

14

The most important issue to recall is that numbers are stored in the binary system,
and that only a finite number of digits is stored. Some numbers that can be exactly
represented in the decimal system become periodic bit-streams in the binary system.
This even applies to simple numbers such as 0.1 in the decimal system. The finite
precision has significant impact on the exactness of the final solution, an issue that will
be discussed in much more detail in the lectures Scientific Computing.
2.1.3

KIND attribute

Fortran allows to declare single precision and double precision numbers. This applies
to both floating point as well as integer numbers.
Appending an underscore ( ) and an integer constant (named or literal) specifies
the number of bytes used in the internal representation:
INTEGER, PARAMETER :: prec=8
123_4
12.3E-12_8
123.4E-15_4
123_prec 12.3E-10_prec

12.4_4

12.4_5

(1.5_8,0.3)

Here 4 implies a single precision floating point number or integer, whereas 8 implies
a double precision floating point number or integer.
For variables, the KIND attribute can be used to specify the number of bytes used
in the internal representation:
INTEGER(KIND=4)
REAL(KIND=4)
COMPLEX(KIND=4)

INTEGER(4)
REAL(4)
COMPLEX(4)

INTEGER(8)
REAL(8)
COMPLEX(8)

The keyword KIND= is optional, and can be omitted (although we recommend to use
it to improve the clarity of the code). Furthermore some compilers allow for quadruple
precision by using KIND=16. Use of quadruple precision, however, always leads to severe performance penalty, since current processors do not directly support quadruple
precision, and the corresponding calculations must be emulated using double precision
operations.
For CHARACTERs, the KIND attribute is not allowed. In this case, the value in parentheses specifies the number of characters in the string (length)
CHARACTER (LEN=4):: string
CHARACTER (4):: string
CHARACTER
:: string(4)

! preferred definition
! correct but somewhat confusing
! defines a vector with 4 single characters

Note that the last specification statement is not equivalent to the first two. In this case
a vector is defined. In this vector each element can hold a strong with one character
(one character is the default length).
To make the programs portable, it is advisable to specify the precision at the very
beginning of the program using a named constant, and to declare all variables and
constants with the appropriate KIND attribute:

A LITTLE BIT MORE COMPLICATED: TYPES, LOOPS AND IFS

INTEGER,
REAL
INTEGER
INTEGER
REAL

15

PARAMETER :: p=8
( KIND=p ) :: a,b
( KIND=p ) :: i,j
( p ) :: k
( p ) :: pi=3.14159388_p

If this rule is observed everywhere in the program, the precision of the program can be
easily changed at a later point.
To make programs even more portable it is also possible to specify the desired
number of digits in the mantissa using the internal function SELECTED REAL KIND
INTEGER, PARAMETER :: psingle=SELECTED_REAL_KIND(6)
INTEGER, PARAMETER :: pdouble=SELECTED_REAL_KIND(12)
REAL
( KIND=psingle ) :: pi=3.14159388_psingle
SELECTED REAL KIND(n) returns an integer that is sufficiently large that a variable of this kind can store numbers with at least n significant digits in the decimal system. For current CPUs SELECTED REAL KIND(6) always returns 4, and
SELECTED REAL KIND(12) always returns 8.
2.1.4

Implicit typing: IMPLICIT NONE

Most Fortran compiler allow to introduce and use variables without explicitly specifying them in the declaration part of the PROGRAM. The convention is that all variables
starting with I-N are automatically declared as INTEGER variables, whereas all variables starting with A-H and O-Z are automatically declared as REAL. This behaviour
was commonly in Fortran 77, but it is not particularly safe. For instance typing errors
are difficult to spot. As shown in the example program below.
PROGRAM notalot
DO I0=1,10
WRITE(*,*) IO
ENDDO
END PROGRAM

! the variable I0 is implicitly typed


! typo IO instead of I0 (zero)

We recommend to switch the implicit typing off by including the statement IMPLICIT
NONE before declaring any variable.

2.2

Vectors, Matrices and Tensors

Fortran is a language designed to convert mathematical expressions directly into a F90


expression, and since vectors and matrices are very often used by scientists, these two
data types are natively supported by Fortran 90.
A vector-variable is defined by placing parentheses after the name of the variable:
REAL :: a(5), r(5)
! the variables x and y can store 5 values
INTEGER, PARAMETER :: n = 5
REAL :: a(n),r(n)
! sames as above

A LITTLE BIT MORE COMPLICATED: TYPES, LOOPS AND IFS

16

In an expression, each element of the vector can be addressed with the name of the
variable followed by an integer index in parentheses. The lowest and largest allowed
indices are 1 and n, respectively. If we want to multiply the five numbers stored in the
vector a by a constant, we could use:
r(1)
r(2)
r(3)
r(4)
r(5)

=
=
=
=
=

c*a(1)
c*a(2)
c*a(3)
c*a(4)
c*a(5)

This is obviously not particularly elegant, since whenever the constant n is changed,
we would need to change the program. In F90, however, vectors can be manipulated
in the same way as simple scalars: we can, for instance, multiply each element of the
vector a by a number c and store the result in a second vector:
r= c * a

! multiply each value of a with c

This is very similar to the usual mathematical expression


~r = c~a.
Fortran of course also supports matrices. The declaration is very similar to vectors, one
just needs to add additional dimensions in the declaration:
REAL :: m(5,5)
! a real valued matrix with 5x5 entries
INTEGER, PARAMETER :: n = 5
REAL :: m2(n,n)
Elements of the matrix can be accessed in a similar way as that of vectors
m=0
m(1,1) = 1
m2 = 2 * m
m2 = m + m
WRITE(*,*) m
2.2.1

!
!
!
!
!

initialise all elements of the matrix to 0


set one element
multiply each element of the matrix by 2
same as above
print out the entire matrix

Input and output statements

Input and output statements operate on vectors in the same way as on scalars. In
the example program conversion3, the READ statement reads n numbers from the
keyboard (the numbers must be separated by <blanks>, <enter> or commas ,).
Conversely, the WRITE statement writes the entire vector to the screen.

A LITTLE BIT MORE COMPLICATED: TYPES, LOOPS AND IFS

2.2.2

17

DO loops

We can write the conversion3 program using DO loops instead of the vector constructs.
Although this is not particularly elegant, we will use it to illustrate the concept of
loops. As indicated before, we could have used the following construct to multiply each
element of the vector with a constant:
r(1)
r(2)
r(3)
r(4)
r(5)

=
=
=
=
=

c*a(1)
c*a(2)
c*a(3)
c*a(4)
c*a(5)

With a loop we can write a much more portable program, which again requires no
modifications when n is changed.
PROGRAM conversion4
INTEGER, PARAMETER :: n = 5
INTEGER :: i
REAL :: a(5), r(5)
REAL, PARAMETER :: c= 4.186
WRITE(*,*) "convert cal to Joules"
READ(*,*) a
! read in five values and store in a
DO i=1,n
r(i)=a(i)*c
ENDDO
WRITE(*,"(F10.5)") r ! write result vector to screen
END PROGRAM conversion4
In the construct
DO i=1,n
r(i)=a(i)*c
ENDDO scale
the assignment r(i)=a(i)*c is executed five times, with i taking the values 1, 2, 3, 4
and 5.
In general, the DO statement has the following form:
[name:] DO var=expr1,expr2[,expr3]
f90-statement
...
f90-statement
ENDDO [name]
Here and in the following, terms in brackets [] are optional. var is an integer variable
(which must be declared somewhere at the beginning of the program) and expr1,

A LITTLE BIT MORE COMPLICATED: TYPES, LOOPS AND IFS

18

expr2 and expr3 are three integer expressions. The expression expr1 is used as the
initial value for var, expr2 specifies the final value, and expr3 the step-size. If expr3
is missing, a step-size of 1 is used (this is the case in the previous examples).
Optionally, a DO loop can be given a name. In this case, the name must be placed
before the DO statement (followed by a colon), and after the ENDDO statement (without
a colon !). In few cases, this can make the program easier to read and maintain, but
excessive use of named-DO loops is not recommended.
Between the DO and ENDDO statement any number of legal F90 statements can be
placed. These statements are executed for the variable var taking on the values
expr1, expr1+expr3, expr1+2*expr3, ... , expr1+ k*expr3
In the do loop, var will be always less or equal expr2. Therefore, if expr2 is smaller
than expr1, the loop is not executed (zero trip loop). It is easy to show that the loop
is executed exactly ic times:
ic = max((expr2 expr1 + expr3)/expr3, 0).
The final value of the variable var outside the loop is well defined and given by
expr1 + icexpr3
In our previous example, the variable i is 6 after the loop. One way to memorise this
is that this is the first value for which the loop is not executed!
It is also possible to program infinite loops using the following construct:
[name:] DO
f90-statement
...
f90-statement
ENDDO [name]
where the loop can be terminated anywhere inside the body using the exit statement:
EXIT [name]

2.3

Infinite loops: the conversion program again

To illustrate infinite loops in combination with an EXIT statement, we use another


version of the conversion program:
PROGRAM conversion5
REAL :: a, result
LOGICAL :: is_zero
DO
WRITE(*,*) "please type in a number to convert, 0 to end"
READ(*,*) a
is_zero = a==0

A LITTLE BIT MORE COMPLICATED: TYPES, LOOPS AND IFS

19

IF (is_zero) EXIT
result = a * 4.186
WRITE(*,"(F10.5)") result
ENDDO
! exit jumps right here and the program stops
WRITE(*,*) "stopping now"
END PROGRAM conversion5
This version of the program asks the user to input numbers and terminates, if the user
types in 0 followed by a <enter>. This example also introduces the IF statement, and
the concept of logical or boolean variables. We have already mentioned, that logical
variables can only take on the values .TRUE. and .FALSE.. An assignment to a logical
variable works in essentially the same way as an assignment to a real or integer variable:
logical_variable=logical_expression
A logical expression is usually constructed by a comparison between two numerical
expressions using one of the scalar relational operators:
<
<=
==
/=
>
>=
logical_expression: numerical_exp

relational_operator numerical_exp

For the operation < the result is .TRUE., if the first numerical expression is smaller than
the second numerical expression, and .FALSE. in other cases. Other scalar relational
operators, work essentially along the same line. Therefore, in our example program,
the variable is zero will be set to .TRUE. only if the variable a is zero, otherwise the
value .FALSE. will be assigned to the variable is zero.
The variable is zero is tested in the IF statement. The statement after the IF
statement is only executed if the logical expression is .TRUE. Therefore, the program
will exit the loop, if the user types a 0 followed by <enter> on the keyboard.
The variable is zero was mainly introduced to illustrate the concept of logical
variables. In most cases, one would simply replace the two statements
is_zero = a==0
IF (is_zero) EXIT
by the single statement:
IF (a==0) EXIT

2.4

The IF construct in more detail

In fact, there are two versions of the IF construct in F90. The short one is:
IF (scalar-logical-expr) stmt

A LITTLE BIT MORE COMPLICATED: TYPES, LOOPS AND IFS

20

The parentheses ( and ) around the logical expression are mandatory. The statement stmt must be placed on the same line as the IF statement and is only executed if
the scalar-logical-expr is .TRUE.. The simple IF construct, allows to execute only
a single expression conditionally.
To handle more complex cases, the IF THEN construct can be used.
[name:]

IF (scalar-logical-expr) THEN
f90-statement
...
f90-statement
ENDIF [name]

Similar to the DO statement, the name is optional (both the IF and ENDIF statements
must be named or unnamed). The parentheses surrounding the scalar-logical-expr
are again mandatory. The statements enclosed in the IF and ENDIF block are only
executed if the scalar-logical-expr is .TRUE..
A slightly more elaborated version of the IF statement is the IF ELSEIF ENDIF
construct:
[name:]

IF (scalar-logical-expr1) THEN
block1
[
ELSE IF (scalar-logical-expr2) THEN [name]
block2
[ ELSEIF (scalar-logical-expr3) THEN [name]
block3
[ ELSE [name]
block_else of f90-statements
]
]
]
ENDIF [name]

Here terms in brackets [] are again optional. In principle this construct works
exactly as one would expect. If scalar-logical-expr1 is .TRUE., block1 is executed. If this expression is .FALSE. and scalar-logical-expr2 is .TRUE., block2
is executed. If scalar-logical-expr1 and scalar-logical-expr2 are .FALSE. and
scalar-logical-expr3 is .TRUE., block3 is executed. If none of the scalar logical expressions are .TRUE., the final block block else is executed. Mind, that one block is
executed at most (if the ELSE clause is missing, possibly no block is executed).

2.5

A small example program: a calculator

PROGRAM calculator
REAL :: res, a
CHARACTER (LEN=1) :: operation
res=0

EXPRESSIONS AND ASSIGNMENTS IN MORE DETAIL

21

DO
WRITE(*,*) result ,res
READ(*,*) operation
READ(*,*) a
IF (operation == *) THEN
res=res*a
ELSE IF (operation == +) THEN
res=res+a
ELSE IF (operation == -) THEN
res=res-a
ELSE IF (operation == /) THEN
res=res/a
ELSE
EXIT
ENDIF
ENDDO
END PROGRAM calculator

Expressions and assignments in more detail

We have now discussed sufficient examples to have a closer look at the way expressions
and assignments are constructed and handled in F90. The essential idea to keep in
mind is that F90 maps quite naturally from mathematical expressions to statements.
The formula
f (x) = a + bx + cx2
can be programmed in F90 as:
f = a+ b*x + c*x**2
Mind that the multiplication symbol * must be used; a simple blank between two
variables or constants is not interpreted as a multiplication:
f = a+ b x + c x**2

! compiler will report an error

In general, we can distinguish two classes of operators:


dyadic (binary) operators: + - * / ** .AND. .OR. for instance
expr1 + expr2
monadic (unary) operators: - .NOT. for instance
.NOT. expr1

- expr2

A dyadic operator operates on two operands (one to the left and the other one to the
right of the operator), whereas a monadic operator requires only one operand after the
operator. Legal operands are any legal f90 expression: constants, variables, function
calls and expressions (possibly in parentheses).

EXPRESSIONS AND ASSIGNMENTS IN MORE DETAIL

3.1

22

Scalar numerical expressions and assignment

For scalar numerical expressions, the allowed operands are INTEGER, REAL or COMPLEX
scalars (constants, variables, function calls or F90 expressions in parentheses). The
following operations are supported:
**
*
+
+

/
-

exponentiation
multiplication and division
sum and difference
unary operators (change of sign)

The order of precedence is the same as in mathematical expressions


f = a+ b*x + c*x**2*2

a + (b*x) + ((c*(x**2))*2)

but parentheses can be used to regroup the expressions:


f = a+ b*x + (c*x)**2*2
F90 supports mixed type expressions, where conversions from one type to the other
type are automatically handled according to the following rules:
operand 2
operand 1
Int
Real
Complex

Int

Real

Complex

Int
Real
Complex

Real
Real
Complex

Complex
Complex
Complex

A scalar-assignment has the form:


numerical_variable=numerical_expression
Again, conversions are done automatically. It is, for instance, possible (but dangerous)
to assign a floating point number to an integer variable. In this case, the digits after
the comma are simply removed. Conversely, it is also possible to assign an integer value
to a floating point number (with less adverse side effects than in the opposite case)

3.2

Scalar character expressions and assignment

Only the concatenation of strings is supported. Legal operands are CHARACTERS (constants, variables or function calls). The dyadic operand is
//

concatenation

The assignment of a character expression to a character variable is again very simple:


character_variable=character_expression
Some illustrations:

EXPRESSIONS AND ASSIGNMENTS IN MORE DETAIL

CHARACTER (LEN=4):: c
c
= "HE" // "LLO WORLD"
c
= "H"
c(1:2)= "HELLO WORLD"
c(3:4)= "HELLO WORD"(7:8)

23

! -> HELL
! -> "H
"
! -> "HEWO"

Here subindexing is use. The syntax


character_string(integer_exp_1 : integer_exp_2)
creates a substring of the original string, that spans from the integer exp 1 character
to the integer exp 2 character. This construct can be used for character constants
as well as for character variables in an expression as well as in the assignment. In the
example above c(3:4)=, overwrites the 3rd and 4th character in the string c with the
string WO.

3.3

Scalar relational operators

A scalar relational operation takes two INTEGER, REAL, COMPLEX or CHARACTER expressions as arguments and yields as a result one logical value. The following operations
are allowed (where the second is preferred):
.LT.
.LE.
.EQ.
.NE.
.GT.
.GE.

<
<=
==
/=
>
>=

less than
less or equal
equal
not equal
greater than
greater or equal

Again a few examples:


LOGICAL :: f
f= "Hello"(1:2) == "He"
f= 1.5 < 1

3.4

! -> .TRUE.
! -> .FALSE.

Scalar logical operators

A Scalar logical operator can operate only on logical expressions (constants, variables
or function calls). The following operations are supported:
.NOT.
.AND.
.OR.
.EQV.

unary negation

and .NEQV.

LOGICAL :: f
REAL
:: a = 1.5
f= 1 < a .AND. a < 2
f = 1 < a < 2
f= .NOT. (a < 1)

! a between 1 and 2
! ** not allowed **
! same as a >= 1

EXPRESSIONS AND ASSIGNMENTS IN MORE DETAIL

3.5

24

Precedence

The order of precedence is given in the following table:


---**
*
/
+
//
< > ==
.NOT.
.AND.
.OR.
.EQV. .NEQV.
----

monadic user defined


numerical expression

character concatenation
relational
logical expression

dyadic user defined

Monadic user defined functions are executed first, than exponentiation ** is performed,
followed by multiplications and divisions, and so on. Parentheses can be used to change
the order of precedence.

EXPRESSIONS AND ASSIGNMENTS IN MORE DETAIL

3.6

25

Array-Expressions and array-assignment

One exceptional feature of Fortran90 is that any operation can be applied to arrays.
There are however some (quite natural) restrictions:
Arrays used in array expressions must be conformable, i.e., they must have the
same number of dimensions and the same number of elements!
operations are always done element by element!
REAL :: a(5),b(5),c(5)
c= a*b
is the same as
c(1)= a(1)*b(1)
c(2)= a(2)*b(2)
...
One operand may be scalar:
REAL :: a(5),c(5),b
c= a*b
is equivalent to
c(1)= a(1)*b
c(2)= a(2)*b
...
For assignments similar rules apply:
An array expression may be assigned to an array, if both are conform-able. (i.e.
have the same dimensions and the same number of elements).
Scalars can be assigned to arrays:
REAL :: a(5), b=1.5
a=b
! each element of the vector a is set to 1.5
Here are some additional examples:
REAL,
z = x
z = x
z = x
z = 0

DIMENSION(10):: x,y,z
/y
! z_i
+ 1
! z_i
* 2
! z_i
! z_i

= x_i/ y_i
= x_i +1
= x_i *2
=0

i=1,2,...,10

SOME EXAMPLE PROGRAMS

4
4.1

Some example programs


Sum of input values

PROGRAM sum
INTEGER, PARAMETER :: n=5
INTEGER :: i
REAL :: a(n), suma
READ(*,*) a
suma=0
DO i=1,n
suma=suma+a(i)
ENDDO
WRITE(*,*) sum is ,suma
END PROGRAM sum
The statement suma=suma+a(i) is discussed in more detail in the section 5.1.

4.2

Mean square deviation

PROGRAM var
INTEGER, PARAMETER :: n=5
REAL :: a(n), suma, suma2
READ(*,*) a
suma=0
suma2=0
DO i=1,n
suma=suma+a(i)
suma2=suma2+a(i)**2
ENDDO
WRITE(*,*) sqrt(suma2/n-(suma/n)**2)
END PROGRAM var

4.3

Minimum

PROGRAM minimum
INTEGER, PARAMETER :: n=5
REAL :: a(n), amin
INTEGER :: i, ifound
READ(*,*) a
ifound=1
amin=a(1)
DO i=2,n
IF ( a(i) < amin) THEN
ifound=i

26

SOME EXAMPLE PROGRAMS

amin=a(i)
ENDIF
ENDDO
WRITE(*,*) amin, found at ,ifound
END PROGRAM minimum

4.4

Sorting

PROGRAM sort
INTEGER, PARAMETER :: n=5
INTEGER :: a(n), amin, j, i, ifound
READ(*,*) a
DO j=1,n
ifound=j ; amin=a(j)
DO i=j+1,n
IF ( a(i) < amin) THEN
ifound=i
amin=a(i)
ENDIF
ENDDO
a(ifound)=a(j) ; a(j)=amin ! swap data
ENDDO
WRITE(*,*) a
END PROGRAM sort

4.5

Inproduct

PROGRAM dotprod
INTEGER, PARAMETER :: n=5
INTEGER :: j
REAL :: a(n), b(n), cdot
READ(*,*) a ; READ(*,*) b
cdot=0
DO j=1,n
cdot=cdot+a(j)*b(j)
ENDDO
WRITE(*,*) cdot
END PROGRAM dotprod

4.6

Matrix times Vector

PROGRAM mat_vec
INTEGER, PARAMETER :: n=2
INTEGER :: i, j

27

FUNCTIONS AND MODULES: THE MEAN VALUE AGAIN

28

REAL :: a(n,n), b(n), c(n)


READ(*,*) a ; READ(*,*) b
c=0
DO j=1,n
DO i=1,n
c(j)=c(j)+a(j,i)*b(i)
ENDDO
ENDDO
WRITE(*,*) c
END PROGRAM mat_vec

FUNCTIONS and MODULES: the mean value


again

5.1

Mean value

To calculate the mean value of a set of numbers we use the following simple program:
PROGRAM calculate_mean1
REAL :: m
INTEGER, PARAMETER :: n=5
REAL :: a(n)
WRITE(*,*) "now please type in ",n," numbers"
READ(*,*) a
! calculate mean value
WRITE(*,"(F10.5)") m
END PROGRAM calculate_mean1
The program is supposed to calculate the mean value of a set of five values, but the
required lines for the calculation are still missing. The simplest f90 statement that
performs the required calculation is (calculate mean1.f):
m=(a(1)+a(2)+a(3)+a(4)+a(5))/n
Obviously, this is again not a particular elegant way of calculating the sum, since
whenever the named constant n is changed, we need to change the program as well. One
way of calculating the sum is to use DO loops as discussed before (calculate mean2.f):
INTEGER :: i
m =0
DO i=1,n

! must be placed in the declaration part

FUNCTIONS AND MODULES: THE MEAN VALUE AGAIN

29

m=m+a(i)
ENDDO
m=m/n
Only one peculiar new feature needs to be discussed:
m=m+a(i)
In F90 (and most other programming languages), the expression to the right of the
equation sign is evaluated first, and then the calculated value is stored back in the
variable to the left of the assignment! If we work step by step through the program,
we realise that m is initialised to 0 outside the loop. When the loop is executed for the
first time, m is set to 0 + a(1). Then i is increased to 2, and the statements in the DO
ENDDO block are executed again. This time m is set to a(1) + a(2). Therefore, after
the fifth loop, m has the desired value.

5.2

Functions and Modules

To make the program more readable we will now place the routine that calculates the
mean value into a separate module (calculate mean3.f):
MODULE statistics
CONTAINS
FUNCTION mean(b)
REAL :: b(:)
REAL :: mean
INTEGER :: n, i
n=size(b)
mean =0
DO i=1,n
mean=mean+b(i)
ENDDO
mean=mean/n
END FUNCTION
END MODULE statistics
Whenever we want to calculate the mean value we can now simply use the following
statements:
USE statistics

! must be placed at the beginning


! of the declaration part before IMPLICIT NONE

m=mean(a)
A module provides a wrapper for a set of variables and a set of functions and subroutines. All variables, functions and subroutines defined in the module can be imported
to other programs or modules by using the USE statement.

FUNCTIONS AND MODULES: THE MEAN VALUE AGAIN

30

In a module, functions and subroutines must be defined after the CONTAINS statement. When the function mean is called, the dummy argument b in the function is
replaced by the actual argument a. The actual argument must have the same definition as the dummy argument: a real argument can be passed only to a real dummy
variable, and an integer argument only to an integer dummy argument. Vectors can
obviously be passed only to vector arguments.
In the previous example, the vector b in the function mean assumes automatically
the size of the vector a in the calling routine, and by means of the SIZE() function,
the actual (current) size of the vector in the function can be determined. Once the
function has finished, the control is transfered back to the calling program, and the
link between the dummy argument and the actual argument is removed. The function
might now be called by another routine with a different vector.
The function mean also introduces two new variables n and i. These two variables
are only accessible inside the subroutine, and, actually, storage for these two variables
is only made available, when the function is called. After the function is left, n and i
loose their values.
We can now easily extend the program to calculate the variance (actually this is not
quite the usual definition of the variance, but here we do not care for such subtleties):
=

a2 a
2 ,

where a
stands for the mean value of a. We simply rewrite this as
n

= sqrt mean(a2 ) mean(a)2 ,


which can be coded in Fortran as:

sigma = SQRT( mean(a**2) - mean(a)**2)


Clearly this is simple and more elegant than coding a seperate DO loop!

5.3

Sorting again

A nice demonstration how to use functions is the following version of the simple sort
algorithm already implemented above. This version declares a subroutine minpos that
locates the minimum value in a vector and passes back the position of the minimum
value. The main program then swaps the minimum value (located at minpos) with the
current top of the vector. Not only does this avoid the complicted nested loops we have
encountered before, it also separates the entire task into two much simpler tasks that
are easier to code.
MODULE some
CONTAINS
FUNCTION minpos( a)
INTEGER :: a(:)
INTEGER :: minpos

FUNCTIONS AND MODULES: THE MEAN VALUE AGAIN

31

! local
INTEGER :: i
minpos=1
DO i=2, SIZE(a)
IF ( a(i)<a(minpos)) THEN
minpos=i
ENDIF
ENDDO
END FUNCTION
END MODULE some
PROGRAM sort
USE some
INTEGER, PARAMETER :: n=5
INTEGER :: a(n), aswap, i, m
READ(*,*) a
DO i=1,n
m=minpos(a(i:n))
WRITE(*,(8I10)) m, a(i:n)
swap=a(i)
a(i)=a(i+m-1)
a(i+m-1)=swap
WRITE(*,(8I10)) m, a(i:n)
ENDDO
WRITE(*,(8I10)) a
END PROGRAM sort

5.4

Subroutines

In addition to functions, it is possible to define subroutines in modules. The main


difference between subroutines and functions is that subroutines do not directly return
a value. It is however possible to modify the content of the dummy-variables and the
modified values are automatically passed back the to calling routine.
As an example, consider that we add the subroutine mean and variance to the
statistics module (calculate mean4.f):
SUBROUTINE
REAL ::
REAL ::
REAL ::

mean_and_variance(b, m, variance)
b(:)
m
variance

m=mean(b)
variance= SQRT( mean(b**2) - m**2)
END SUBROUTINE
We can now call this subroutine with the CALL statement from the main program:

FUNCTIONS AND MODULES: THE MEAN VALUE AGAIN

32

CALL mean_and_variance(a, m, sigma)


When the subroutine is called, the dummy variables b, m, and variance are linked to the
real arguments a, m, and sigma. Whenever one of the dummy arguments is modified, the
corresponding actual argument (a, m, and sigma) is also modified. This allows to pass
the new values back to the main program. Mind, that the same rules (modification
of actual arguments) are true for function calls, but it is generally considered bad
programming practice to rely on such side effects in functions. In other words, only
subroutines are supposed to modify the content of dummy arguments.

5.5

Built-in Functions

One final note about functions: F90 offers a built-in function that can be used to
calculate the sum of all elements of a vector. This function is called SUM, and can be
used to calculate the mean value and the variance in two simple statements:
m=SUM(a)/n
sigma = SQRT( (SUM(a**2)/n) - m**2),
Generally F90 supports a larger variety of so called built-in or intrinsic functions. We have already encountered three of them: the SUM, SQRT and the
SIZE functions. For a complete and concise list of functions we refer to
http://www.nsc.liu.se/ boein/f77to90/a5.html.
The following list gives an overview of the commonly used mathematical functions.
The arguments of these functions can be either REAL(KIND=4) (R), REAL(KIND=8) (D) or
COMPLEX (D). It is generally recommended to use the Generic name whereever possible
instead of the Specific name.
Function

Generic
name

Specific Data type


name
Arg
Res

Square root

SQRT

SQRT
DSQRT
CSQRT

R
D
C

R
D
C

Exponential

EXP

EXP
DEXP
CEXP

R
D
C

R
D
C

Natural
logarithm

LOG

ALOG
DLOG
CLOG

R
D
C

R
D
C

Common
logarithm

LOG10

ALOG10
DLOG10

R
D

R
D

FUNCTIONS AND MODULES: THE MEAN VALUE AGAIN

Sine

SIN

SIN
DSIN
CSIN

R
D
C

R
D
C

Cosine

COS

COS
DCOS
CCOS

R
D
C

R
D
C

Tangent

TAN

TAN
DTAN

R
D

R
D

Arcsine

ASIN

ASIN
DASIN

R
D

R
D

Arccosine

ACOS

ACOS
DACOS

R
D

R
D

Arctangent

ATAN

ATAN
DATAN
ATAN2
DATAN2

R
D
2R
2D

R
D
R
D

ATAN2

Hyperbolic
sine

SINH

SINH
DSINH

R
D

R
D

Hyperbolic
cosine

COSH

COSH
DCOSH

R
D

R
D

TANH
DTANH

R
D

R
D

Hyperbolic
tangent

TANH

33

A list of the most important numerical functions is given below (again the use of the
generic name is recommended):
Function

Generic
name

Conversion
INT
to integer
(of the real part)
Rounding

NINT

Conversion

REAL

Specific Data type


name
Arg
Res

* INT
NINT
IDNINT
* REAL

I
R
C

I
I
I

R
D

I
I

FUNCTIONS AND MODULES: THE MEAN VALUE AGAIN

34

to real
Conversion
to complex

CMPLX

Absolute
value

ABS

Remainder

MOD

MODULO

I (2I)
R (2R)
D (2D)

C
C
C

IABS
ABS
DABS

I
R
D

I
R
D

MOD
AMOD
DMOD
-

2I
2R
2D
2I
2R
2D

I
R
D
I
R
D

Maximum

MAX

* MAX0
* AMAX1
* DMAX1

I
R
D

I
R
D

Minimum

MIN

* MIN0
* AMIN1
* DMIN1

I
R
D

I
R
D

Imaginary part

AIMAG

Conjugate

CONJG

The function INT truncates towards zero, i.e. INT(-3.7) becomes -3. Whereas NINT
rounds towards the next nearest integer number as one would expected (i.e. NINT(-3.7)
becomes -4 and NINT(-3.2) becomes -3).
The function REAL(integer [, KIND=4]) converts to a single precision real, and
REAL(integer, KIND=8) converts to a double precision real.
The function MOD(X,Y) calculates X - INT(X/Y)*Y, whereas MODULO is the correct
mathematical modulo function:
MOD
MOD
MOD
MOD

(8,5)
(-8,5)
(8,-5)
(-8,-5)

gives 3
gives -3
gives 3
gives -3

MODULO
MODULO
MODULO
MODULO

(8,5)
(-8,5)
(8,-5)
(-8,-5)

gives 3
gives 2
gives -2
gives -3

MAX and MIN require a least two arguments of the same type and return the maximum
and minimum value of all supplied values.

FUNCTIONS AND MODULES: THE MEAN VALUE AGAIN

5.6

35

Lets add some extra flexibility

Our mean value program is still limited to five input values. Although we could in
principle recompile the program, whenever we want to change n, it would be better if
we could ask the user to type in n.
We can achieve this with a few minor modifications in the program
(calculate mean5.f):
PROGRAM calculate_mean5
USE statistics
REAL :: m, sigma
INTEGER :: n
REAL, ALLOCATABLE :: a(:)
WRITE(*,*) "how many numbers do you want to type in ?"
READ(*,*) n
ALLOCATE(a(n))
WRITE(*,*) "now please type in the numbers"
READ(*,*) a
! calculate mean value
m
= mean(a)
sigma=SQRT( mean(a**2) - m**2)
WRITE(*,"(F10.5)") m,sigma
DEALLOCATE(a)
END PROGRAM calculate_mean5
The declaration
REAL, ALLOCATABLE :: a(:)
Declares a vector a but leaves the actual size undetermined yet. The size of the vector
is determined later in the program with the ALLOCATE statement:
ALLOCATE(a(n))
The allocated space is freed by calling the DEALLOCATE statement.

PROGRAM UNITS AND MODULES IN MORE DETAIL

36

PROGRAM units and MODULES in more detail

A F90 program consists of


one main program (enclosed in the PROGRAM and END PROGRAM statements)
internal procedures (not recommended)
external procedures (not recommended)
a collection of modules (usually placed in separate source files)
and intrinsic procedures (i.e. built in procedures).
A MODULE is a collection of variables, functions and subroutines. Generally, all data
and variables are local to a module, program or procedure, with the following important
exceptions:
The names of modules and external procedures are global. Usually, names are
not case sensitivity.
All subroutines and functions in a module can be made available using the USE
statement. Variables defined in a module before the CONTAINS statement are also
made accessible by the USE statement.

6.1

The Main PROGRAM

The main program must be defined in the following way:


PROGRAM program-name
specification-stmts
executable-stmts
[ CONTAINS
internal-subprograms ]
END [ PROGRAM [program-name]]
where terms in brackets are optional as before. The CONTAINS statement indicates
that internal procedures follow. These procedures are not executed automatically, but
can be called from the main program (they can be accessed only from the main program). Internal procedures are only used very rarely and will not be discussed in detail,
since they decrease code re-usability. Instead procedures should be defined in separate
MODULES.
Specification statements specification-stmts are for instance USE statements or
the declaration of variables:
INTEGER, PARAMETER :: i=10
REAL :: a , b(i,i)
All USE statements must be placed before other declaration statements.
executable-stmts are assignments, control statements (DO, IF) and IO-statements
etc.

PROGRAM UNITS AND MODULES IN MORE DETAIL

6.2

37

External procedure

The general syntax for an external procedure is:


[ RECURSIVE ]
SUBROUTINE subroutine-name [ ( [ dummy-arg-list] ) ]
FUNCTION
function-name
( [ dummy-arg-list] )
specification-stmts
executable-stmts
[ CONTAINS
internal-subprograms ]
END [ SUBROUTINE [subroutine-name]]
END [ FUNCTION
[function-name]]
The RETURN, END or CONTAINS statement transfer the control back to the calling routine.
The CONTAINS statement introduces procedures that are local to this procedure. Generally external procedures should be used with care, since when an external procedure
is called from a program, the number of arguments and the types of the arguments are
not checked against the dummy argument list. This leads to many unnecessary errors
during programming.
Internal procedures are similar to external procedures but must not have a CONTAINS
clause (remember, internal procedures are defined within the CONTAINS clause of an
surrounding subroutine, program or module). For internal procedures, the dummy argument list is checked by the compiler against the argument list of the calling routine.

6.3

Modules

Whenever possible new functions and procedures should be defined within a MODULE.
The general syntax of a module is specified below:
[ MODULE modul-name]
specification-stmts
[ CONTAINS
module-subprograms ]
END [ MODULE [modul-name]]
The simplest MODULE might contain only the definition of global variables, for instance, the definition of useful constants:
MODULE constants
REAL, PARAMTER :: cal_to_joule= 4.186
REAL,PARAMETER :: pi =3.14159265358979323,tpi=2*pi
END MODULE
But usually a module contains derived data type definitions and a collection of subroutines. To use a module the statement

PROGRAM UNITS AND MODULES IN MORE DETAIL

USE

38

modul-name

must be placed in the program or procedure that wants to call subroutines from this
module.
The syntax for a module subprogram is similar as that for an external subprogram
but the END SUBROUTINE/END FUNCTION statements are mandatory (in an external
subprogram it is sufficient to write END instead of END SUBROUTINE).

6.4

Order of statements

The order of statements is rather strict in F90, as mentioned before. Below a summary
how statements must be placed is show:
PROGRAM, FUNCTION, SUBROUTINE, MODULE statements
USE statements
IMPLICIT statements
derived type definition
variable declarations
executable statements
CONTAINS
internal or module procedures
END PROGRAM, FUNCTION, SUBROUTINE, MODULE statements
Mind that the USE statements must be placed at the very beginning of the program.

6.5

Dummy arguments - actual arguments - local variables

In Fortran 90, arguments are passed by reference. We have already briefly elaborated
on this feature, and we will now discuss it in more detail. Consider for instance the
following small subroutine
SUBROUTINE add(dum1,dum2,dum3)
REAL :: dum1, dum2, dum3
dum1=dum2+dum3
END SUBROUTINE add
If this subroutine is called, by the statement
CALL add(a,b,c)

PROGRAM UNITS AND MODULES IN MORE DETAIL

39

it is exactly the same as writing:


a=b+c
The variables dum1, dum2 and dum3 are called dummy arguments, and a, b and c are
called actual arguments. Fortran behaves as if we would have cut and pasted the code
from the subroutine body to the calling routine, replacing every occurrence of the
dummy argument by the corresponding actual argument.
We can describe this behaviour in another way: upon calling a subroutine, the
dummy arguments are linked to the same storage position as the actual arguments:

variable a

storage unit for a

dummy argument dum1


vector a
vector dummy argument dum1

storage unit for a(1)


storage unit for a(2)
storage unit for a(3)

Whenever the dummy argument dum1 is modified, the contents of the variable a
is also changed. Actually this description is very close to what the compiler does in
reality. This behaviour is called call by reference, as opposed to the call by value
model used by such programming languages as C. In the call by reference model, any
modification of the dummy argument will automatically change the actual argument
(remember both variables point to the same storage unit). Generally the call by reference model is more flexible, but has the disadvantage of being much more prone to
programming errors than the call by value model. Side effects (modifications of the
variables passed down by the calling routine) are difficult to control in F90 and require
the programmers to carefully document their programs.
6.5.1

Local variables

All procedures can have their own set of local variables. These variables are not accessible to any other procedure or program. Storage for these variables is usually created,
when the procedure is called, and such variables loose their values, when the subroutine
is left. If this behaviour is not desirable, the SAVE attribute can be used to protect the
variable.
INTEGER, SAVE :: init
This feature can be used to initialise certain values in a subroutine, and we will see an
example in a minute.

PROGRAM UNITS AND MODULES IN MORE DETAIL

6.6

40

Functions

In Fortran, functions behave in exactly the same manner as subroutines. The only difference is that they return a value, and that they are called differently than subroutines.
Functions are defined by the following clause:
[ RECURSIVE ]
FUNCTION
function-name ( [ dummy-arg-list] ) RESULT (func_res)
specification-stmts
executable-stmts
END [ FUNCTION
[function-name]]
A function is called by an expression of the form:
function-name

( [ actual-arg-list] )

The RESULT(func res) clause is optional and creates a variable with the name
func res, which must be declared and set to a value before exiting. If the RESULT
clause is missing, the name of variable is the function-name. The result of a function
may be any type (scalar, arrays or pointers). In all other respects, functions behave like
procedures, in particular, they can change any of the values of their actual arguments.
This however is not recommended, since such side effects are difficult to control. If a
function has to return two values, either a vector should be returned or a subroutine
should be used.

6.7
6.7.1

Examples
Modules for storing constants

MODULE constants
REAL, PARAMTER :: cal_to_joule= 4.186
REAL,PARAMETER :: pi =3.14159265358979323,tpi=2*pi
END MODULE
6.7.2

Reverse polish calculator

MODULE simple_stack
INTEGER, PARAMETER :: nmax=4
REAL :: a(nmax)
INTEGER :: nstored=0
CONTAINS
SUBROUTINE push(value)
REAL :: value
nstored=nstored+1
IF (nstored>nmax) THEN
WRITE(*,*) stack exhausted
STOP

PROGRAM UNITS AND MODULES IN MORE DETAIL

41

ENDIF
a(nstored)=value
END SUBROUTINE push
FUNCTION pop()
REAL :: pop
IF (nstored<=0) THEN
WRITE(*,*) stack empty
STOP
ENDIF
pop=a(nstored); nstored=nstored-1
END FUNCTION pop
END MODULE simple_stack
PROGRAM rpn
USE euro
USE simple_stack
CHARACTER :: l*(40)
REAL :: b
DO
READ(*,(A)) l
IF (l(1:1)==+) THEN
b=pop()+pop()
ELSEIF (l(1:1)==-) THEN
b=-pop()+pop()
ELSEIF (l(1:1)==s) THEN
b=pop()*euro2sh
ELSE
READ(l,*) b
ENDIF
WRITE(*,*) b
CALL PUSH(b)
ENDDO
END PROGRAM rpn

6.8

Recursive Functions and Subroutines: the factorial

F90 allows the definition of recursive functions and subroutines. This concept maps
again naturally onto mathematical formulas. Lets consider the definition of the factorial which is:
n! =

1
for n <= 1
n(n 1)! for n > 1.

This mathematical formulation can be translated directly into a Fortran function:


RECURSIVE FUNCTION fac1(n) RESULT(f)

PROGRAM UNITS AND MODULES IN MORE DETAIL

42

INTEGER(8):: f,n
IF (n <= 1) THEN
f=1
ELSE
f=n*fac1(n-1)
ENDIF
END FUNCTION fac1
In this case, the RECURSIVE statement is required, since otherwise Fortran does not
allow that the function calls itself. It is also necessary, to use the RESULT clause to
distinguish the function name from the variable (we need to call fac1, and we need to
return a value).
Another feature demonstrated in this program is that Fortran allows to specify
explicitly the number of bytes a variable allocates (see also Sec. 2.1.3). Usually only
the values 4 and 8 are allowed, and the default INTEGER and REAL variables allocated
4 bytes on most machines. In the previous example, this would allow us to calculate
only factorials of up to 10. By increasing the size of an INTEGER variable to 8, we
can calculate the factorial of 20.

6.9

A faster factorial subroutine

The previous sample program for the factorial function is very slow. Whenever the
function is called, it needs to recalculate all factorials, which is not particularly efficient.
It is faster to calculate the required results once, to store them in a list, and to retrieve
the result from the list. With the SAVE statement we can easily achive this behaviour:
FUNCTION fac2(n)
INTEGER(8):: fac2,n
LOGICAL,SAVE
:: initialised=.FALSE.
INTEGER, PARAMETER :: n_max=20
INTEGER(8),SAVE
:: table(n_max)
INTEGER :: i
IF (.NOT. initialised ) THEN
table(1)=1
DO i=2,n_max
table(i)=table(i-1)*i
ENDDO
initialised=.TRUE.
ENDIF
IF (n <= 1) THEN
fac2=1
ELSE IF (n > n_max) THEN
WRITE(*,*) fac2 error: n must be smaller or equal ,n_max
STOP

PROGRAM UNITS AND MODULES IN MORE DETAIL

43

ELSE
fac2=table(n)
ENDIF
END FUNCTION fac2
The only new feature is the STOP command, which immediately stops the execution of
the program.

6.10

Scope

Scope tells us, where a particular variable, function, subroutine or defined type is
known. F90 uses a fairly simple lexical scoping scheme:
Labels are always local to a procedure (labels have not been discussed, since their
use is strongly discouraged).
Names: F90 has only one name space, which is local to one programming unit.
Only the name of the programming unit is known to the outside world (it is
global). This implies that
derived types,
variables,
and subroutines and functions
must have different names to distinguish them from each other inside one unit.
One can not, for instance, define a function and a variable with the same name
in the same programming unit, although context would often allow to distinguish
both (in C this is possible and allowed):
MODULE test
REAL :: f=1.2
CONTAINS
FUNCTION f(x)

! illegal, since f was already defined above

Different programming units may use the same names. These names are local to
the unit in which they are defined.
MODULE test
REAL :: f=1.2, pi = 3.1415926
END MODULE test
MODULE test2
REAL :: pi = 3.1415 ! thats a different pi
CONTAINS
FUNCTION f(x)
! this is legal

PROGRAM UNITS AND MODULES IN MORE DETAIL

44

Names defined inside other units become available be means of the use association. All named entities of a module are made available with the statement
USE modul
Names which are imported with the use command, can be redeclared but the
syntax will not be discussed in this lecture, since it this feature is rarely used.
USE test2
REAL :: pi =
USE test
USE test2

3.1415926

! illegal since pi is defined in test2

! illegal since pi is defined in both modules

Host association: Internal procedures, derived type definitions, and module


procedures know all names of the surrounding block. But in contrast to the
USE association, names can be redeclared locally!
MODULE test
REAL :: x
CONTAINS
SUBROUTINE set_x_local(y)
REAL :: x, y ! x differs from first x
x=y
END SUBROUTINE set_x_local
SUBROUTINE set_x(y)
REAL :: y
x=y
! this will set the x define in the MODULE header
END SUBROUTINE set_x
END MODULE
This is actually a feature leading to errors which are very difficult to find. Therefore
never use internal subroutines.

PROGRAM UNITS AND MODULES IN MORE DETAIL

6.11

45

Passing functions as arguments

The last important feature of F90, that we will discuss in this section, is how to pass the
name of a function to a procedure. Why is this necessary in the first place? Consider
that we want to write a general MODULE for integrating a function. Obviously we would
like to pass the name of the function for which the integral must be calculated to this
module. Exactly this is done in the following example program:
MODULE functions
CONTAINS
FUNCTION f1(x) ! f1(x) = x^2
REAL :: f1,x
f1=x**2
END FUNCTION f1
FUNCTION f2(x) ! f2(x) = e^x
REAL :: f2,x
f2=exp(x)
END FUNCTION f2
END MODULE functions
MODULE integrate
CONTAINS
FUNCTION simple_int(f,xstart,xend,intersections)
INTERFACE
FUNCTION f(y); REAL :: f,y; END FUNCTION
END INTERFACE
REAL :: simple_int,xstart,xend,d
INTEGER:: intersections,i
simple_int=0
d=(xend-xstart)/(intersections-1)
DO i=0,intersections-1
simple_int=simple_int + f(xstart+d*i)*d
ENDDO
END FUNCTION simple_int
END MODULE integrate
PROGRAM test
USE functions
USE integrate
IMPLICIT NONE
REAL a,b
WRITE(*,*) simple_int(f1,0.,1.,100),simple_int(f2,0.,1.,100)
END PROGRAM test

PROGRAM UNITS AND MODULES IN MORE DETAIL

46

The only new feature we encounter here is the INTERFACE statement (which is shown
below in a slightly more expanded form):
INTERFACE
FUNCTION f(y)
REAL :: f,y
END FUNCTION
END INTERFACE
This statement tells the compiler two things. First, it specifies that f is not a conventional dummy variable but a dummy function. It also tells the compiler which
arguments this function takes, and which type of value the function returns. Between
the INTERFACE and the END INTERFACE statements, any number of declaration statements can be placed. Usually we will place here all declaration statements found in the
actual function definition, but no executable statements (the body of the function).
Please compare the INTERFACE statement with the actual definition of the functions
f1 and f2. The names of the variables have of course changed, but their types agree
with that of f1 and f2.
When the function simple int is called the first time, the dummy function f is
linked to the machine executable code of the function f1, and simple int is executed
once.

function f1
dummy argument dum1

machine code for f1


machine code for f1

Whenever the function f is now called in simple int, the code of the function f1 is
executed. Upon the second call of simple int, the dummy argument is linked to the
function f2.

BORING BUT REQUIRED: INPUT AND OUTPUT

47

Boring but required: Input and Output

Output: transfers data from the program to the screen or files


Input:
transfers data to the program from the keyboard or files
As all other executable statments, input and output is performed at run-time. During
the execution, the program performs the required operations and continues after the
user has supplied the input.
In Fortran, the location to which data is written or from which it is read is specified
by an integer expression called a unit. Such units are similar to C file handles. In
Fortran, the following three units are preconnected:
UNIT=5
UNIT=6
UNIT=0

7.1

! stdin usually the keyboard


! stdout usually the screen
! stderr (usually also the screen)

OPEN and CLOSE statements

The OPEN and CLOSE statements are used to open or close a file:
INTEGER :: u=10
CHARCATER :: file=my_file
OPEN([UNIT=] u, FILE=file)
CLOSE([UNIT=] u)
Here file is a string (any legal string expression), and u an integer expression. These
commands establish a connection between the file with the name file, and the unit
with the number u. If the file does not exist, it is created on the hard disc when the
OPEN command is encountered. From now on, the unit number can be used to write or
read data from the file with the READ or WRITE commands:
REAL :: A=12
OPEN(10,FILE=data)
WRITE(10,*) A*2+1
CLOSE(10)

! create the file data


! write the value of A*2+1 to file data

After the execution of the commands, the content of the file data can be inspected
with an editor (for instance Xemacs).

7.2

READ, WRITE statements

The syntax of the READ and WRITE statements is indicated below:


READ ( [UNIT=] u, [FMT=] fmt) [iolist]
WRITE( [UNIT=] u, [FMT=] fmt) [iolist]

BORING BUT REQUIRED: INPUT AND OUTPUT

48

The integer expression u specifies the unit to which the operation is performed, and
the format specification fmt specifies the format that is used for the IO. As always,
terms in square brackets [] are optional. The units 5 and 6 are preconnected, and
since most people lack a good memory, F90 allows to use the * instead of these
two numbers: READ(*,*) is the same as READ(5,*); and WRITE(*,*) is the same as
WRITE(6,*).
The ftm specification might be either
a star * (List directed input/output)
or a string.
a line number refering to a FORMAT statement (not covered in this lecture).
The iolist consists of a list of comma separated expressions or implied do-loops.
For the READ statement, the expressions must be expressions which can also be located
on the left hand side of an assignment (usually variables or vectors).

7.3

List directed input/output, FMT= *

The simplest version for the FMT specifier is the star. It allows to read or write an
arbitrary number of items:
WRITE(*,FMT=*)"Number of datas and comment"
READ(*,FMT=*) n,comment
WRITE(UNIT=*,*)Please input ,n,data
READ(*,*) (a(i),i=1,n)
This requires e.g. the following input:
3 "test" <enter>
1.5 2.3 <enter>
2.4
<enter>
It is quite usual to use list directed input, but the list directed output looks pretty
ugly and the way the output is performed depends on the compiler. Therefore, we will
concentrate on list directed input.
List directed input
When the compiled Fortran program is executed and a READ statement is encountered,
the program stops, and the user is required to perform the input. The input must
conform to the following guidelines:
In the input, each item must be separated by a blank, a comma, a slash or an
end of record mark (newline).
If the slash / is found in the file or typed on the keyboard, the input statement
terminates immediately (end of file condition).

BORING BUT REQUIRED: INPUT AND OUTPUT

49

It is possible to specify a repeat count on the input line: 3*3.5 is the same is
typing 3.5 three times.
The allowed input for logical variables is T and F.
Strings must be enclosed in delimiters (they might go over several records). If
strings are not enclosed by delimiters, the string must not contain a blank, a
comma or a slash.
For complex numbers two numbers must be specified in brackets:
( 1E4 separator 1.456 )
where separator is a blank, a comma or a newline.

7.4

Formated input/output

Formated output makes the output of a program more readable. Each item in the
format must correspond to one item in the input/output list.
INTEGER :: i
REAL
:: flt
WRITE(*,(I10,F10.3,A10))
i,flt*2,hello
WRITE(*,FMT=(I10,F10.3,A10)) i*2+1,flt,hello
READ (*,(I10,F10.3,A10))
i,flt,hello
Here the edit descriptor I10 is used for the integer expression i, the edit descriptor
F10.3 for the real expression flt*2, and the edit descriptor A10 for the character
expression hello.

7.5

Edit descriptors

A format specification is a list of edit descriptors separated by commas, and possibly


grouped with parentheses. The list of edit descriptors must be enclosed in at least
one parentheses (see previous examples). The following list shows the most important
format specifications.
I
F
E
L
A
G

integer
fixed real
exponential real
logical
character
general

rIw
rFw.d
rEw.d
rLw
rAw
rGw.d

X
/

blank
new-line

rX
r/

rEw.dEe

rGw.dEe

BORING BUT REQUIRED: INPUT AND OUTPUT

r
w
d
e

50

repeat count
width of the output field
number of decimal digits after the decimal point
number of digits in the exponential

In F90, the output is generally centred to the right. The repeat count specifies, how
many numbers, strings or logical values can be written using the corresponding edit
descriptor.
The repeat count is particularly useful to output vectors or matrices, but it can be
applied to any list of expressions:
INTEGER :: i
REAL
:: f(10),a,b
WRITE(*,(5F10.5)) f ! prints out the 10 numbers in the vector f
WRITE(*,FMT=(5F10.5)) a,b ! prints out two numbers a and b
If the list of edit descriptors is exhausted, a new line is generated and the list of edit
descriptors is repeated from the beginning. It is allowed, to use an editor descriptor
with more items than in the io-list.
7.5.1

Grouping edit descriptors

It is possible to group edit descriptors, with parentheses and to put a repeat count
before the parenthesis. The following format specification
(3(2F10.4,5X))
allows to read or write 6 double precision numbers, where two numbers are grouped together without additional blanks in-between. The groups are separated by 5 additional
blanks:
dd.dddd
dd.dddd~~~~~
dd.dddd
dd.dddd~~~~~
dd.dddd
dd.dddd
1234567890123456789012345678901234567890123456789012345678901234567890
0
1
2
3
4
5
6
7
If a format list is exhausted, the last group in parentheses is repeated. If no grouping
with parentheses exists, a newline is generated and the format is repeated from the
very beginning (see above). This is illustrated below:
WRITE(*,(I10,F10.3)) i,a,j,b
is
iiii
aa.aaa <newline>
jjj bbbb.bbb
1234567890123456789012345678901234567890123456789012345678901234567890
0
1
2
3
4
5
6
7

BORING BUT REQUIRED: INPUT AND OUTPUT

7.5.2

51

Integer Format (I-Format): rIw

Upon output: the numbers are printed adjusted to the right in a field of width w:
WRITE(8,(2I10)) 12,156
12
156
1234567890123456789012345678901234567890123456789012345678901234567890
0
1
2
3
4
5
6
7
Upon input: read w characters from the record and convert to integer (blanks are
remove):
READ(8,(2I5)) i,j
For the following input, the variables i and j will be set to the values shown below:
12 156
1 2156
1234512345
7.5.3

-> i=12 j=156


-> i=12 j=156

Float Format (F-Format): rFw.d

Output: the number is printed adjusted to the right with d decimal digits, rounding is
done automatically:
WRITE(*,(F8.3)) 13.568924
13.569
1234567890123456789012345678901234567890123456789012345678901234567890
0
1
2
3
4
5
6
7
Input: read w characters from the record, remove all blanks. If no decimal point exists,
one is inserted at the position d from the right (after the removal of all blanks!) and
finally the string is convert to a floating point number:
READ(*,(F10.3)) a
13.569
->
13.569
1233
->
1.233 (decimal point inserted at 3. pos)
1 3 4
->
.134 (blanks removed, dec. point ins. at 3. pos)
1.E10
->
1E10
1E10
->
unpredictable since a dec. point is inserted
not to be used !!!!
1234567890123456789012345678901234567890123456789012345678901234567890
0
1
2
3
4
5
6
7
Use of formated input for keyboard input can lead to undesired results and is hence
not recommended.

BORING BUT REQUIRED: INPUT AND OUTPUT

7.5.4

52

Exponential Format (E-Format): rEw.d or rEw.dEe

Upon input, this format behaves in exactly the same manner as the F format.
For output it produces the following string: s.ddddEveeee
s
d
v
e

blanks to fill the available space


sign
mantissa with d digits
sign of exponent
exponential with e digits (default 2 or 3)

It is recommended to set the field width to at least w = z+e+5 . If the Ee specification


is missing, the field width should be at least w = z+8 .
7.5.5

Logical Format (L-Format): rLw

Output: T or F in the right-most position.


Input: first character differing from <blank> and dot . is used to decide, whether the
input is true or false:
LOGICAL :: L
READ(*,(L3)) L
T
-> L= .TRUE.
.Txx
-> L= .TRUE.
F
-> L= .FALSE.
7.5.6

Character Format (A-Format): rAw

Output: string is printed out centred to right. If w is too short, the rightmost characters
of the string are not printed.
WRITE(*,(A10,A10)) "Hello world",right
Hello worl
right
1234567890123456789012345678901234567890123456789012345678901234567890
0
1
2
3
4
5
6
7
Upon input, the next w characters are read from the keayboard or file and stored in
the specified variable.
7.5.7

General Format (G-Format): rGw.dEe

The general format is used rather rarely. It allows, in principle, to read or write any
expression. For more details we refer to a F90 manual.
7.5.8

Blank Format (X-Format): rX

Output: transfers n blanks to the file or screen.


Input: skips over n characters.

BORING BUT REQUIRED: INPUT AND OUTPUT

7.5.9

New Record (/-Format): r/

Output: start a new record (newline).


Input: go to next record (even if the current one has not been finished).

53

54

MORE ABOUT ARRAYS

More about arrays

Arrays are natively support in F90 and far more flexible than discussed in the main
lectures. Arrays can have any dimension, and the array bounds need not to start at
1. Dimensions can be specified either with the DIMENSION attribute or in parentheses
after the name of the variable. The general syntax for the specification of an array is
given below:
DIMENSION(extent-list)
or
extent:
[lower :] upper

variable_name(extent-list)

Here an extent-list is a list of extents, which are comma separated. An extent


consists of an optional lower bound and an upper bound for the array index. The
default for the lower bound is 1. Below are few examples for the specification of arrays:
REAL, DIMENSION(3) :: x,y
! simple vector
INTEGER, PARAMETER :: n=50, m=100
REAL :: rho(0:n,0:m), vel(3,0:n,0:m) ! rho is a matrix
! vel is a tensor of rank 3
REAL, DIMENSION(3,3) :: a,b
! two matrices
REAL :: c(-10:3)
! a vector
The storage layout of arrays has not been changed since F77 and is a row first
ordering. This meant that a matrix with 3 3 elements
a(1,1)
a(2,1)
a(3,1)

a(1,2)
a(2,2)
a(3,2)

a(1,3)
a(2,3)
a(3,3)

is stored in the memory in the following manner (first row first, then second row and
so on):
a(1,1) a(2,1) a(3,1)

a(1,2) a(2,2) a(3,2)

a(1,3) a(2,3) a(3,3)

This is important for the optimisation of program. If arrays are used in nested DO loops,
one should try to make the first index of all arrays the innermost variable of the DO
loop.

8.1

Literal constants

Literal constants for arrays are constructed by enclosing a list of literal real or integer
constants in (/ and /):
(/ 2, 4, 3 /)

55

MORE ABOUT ARRAYS

8.2

Array constructors

A more flexible way to specify arrays is the array constructor. The general syntax for
this is
array = (expression, var=expr1, expr2, expr3)
The variable var takes on all values it would take in a DO loop of the form:
DO var=expr1, expr2, expr3
This is called implicit DO loop. For any of the possible values of var one element is added
to the final vector. Such implicit DO loops can be nested as shown in the examples below:
(/ (i,i=1,7,2) /)
% same as (/1,3,5,7/)
(/ (j*i,i=1,3),j=1,3) /) % matrix

8.3

Rank, shape and size

The rank of an array, i.e. the number of dimensions, can be changed only during compile
time and is fixed in the source code. The shape and size of an array can be determined
during run-time using the following commands (with examples assuming the previous
declarations):
shape:
size:

sequence of extents,
total number of elements,

SHAPE(rho) (/ 51, 101/)


SIZE(rho) 5151

The extent of an array is the upper bound minus the lower bound plus 1.
For multidimensional arrays, the number of elements in each dimension can be
determined using the following construct
REAL :: a(5,-1:10)
WRITE(*,*) SIZE(a,1)
WRITE(*,*) SIZE(a,2)

8.4

!
!

number of elements along first dim (i.e. 5)


number of elements along 2nd dim (i.e. 12)

Array-Section

It is possible to address only parts of an array using array-sections. This is an extremely


flexible and powerful feature that often allows to avoid complicated DO loops. Some
simple examples are indicated below:
x(1:2)
rho(:,1)
rho(3,:)

! same as vector (/ x(1), x(2) /)


! vector that equals the first column of rho
! vector that equals the third row of rho

One can even select sub-parts of a matrix:


rho(2:4,3:6)
This selects only the area between the 2nd and 4th row and the 3rd and 6th column.

MORE ABOUT ARRAYS

8.5

56

Zero sized arrays

If the lower bound of an array exceeds its upper bound, the array is called zero size
array. All intrinsic subroutines and all F90 expressions can handle such arrays (no
operation for zero sized arrays).

8.6

Assumed shape array

This feature was already encountered before. In subroutines, dummy arguments assume
the shape of actual arguments. Unfortunately the number of dimensions must agree
with the calling routine, which limits the usefulness of the feature somewhat.
REAL :: a(3:12),m(10,10)
CALL t(a,m)

SUBROUTINE t(b,n)
REAL :: b(:)
REAL :: n (-1:,-1:)
One important point is that only the extents (upper minus lower bounds plus 1) are
passed down to the subroutine, and the lower bound in the subroutine always defaults
to 1 (regardless of the lower bound in the calling routine). In the example above the
lower bounds for the array n are -1 and the upper bounds are 8. Furthermore, in the
subroutine the element n(-1,-1) corresponds to m(1,1) in the calling routine, and
n(8,8) to m(10,10).
The general syntax for the declaration of an assumed shape array in a subroutine
is:
dummy-argument(extent-list)
extent: [lower-bound:]
(mind since, the extent is passed by the calling routine, only the lower bound can be
set in the subroutine). The actual bounds of the array can be found by calling the
SHAPE and SIZE functions:
SIZE (b) !
SIZE (n) !
SIZE (b,1)!
SIZE (n,1)!
SIZE (n,2)!
SHAPE(b) !

8.7

total number of elements of b=a: 10


total number of elements of n=m: 100
first extent, result is 10
first extent of n=m is 10
second extent of n=m is 10
vector of extents, in this case (/10/)

Automatic arrays

In subroutines, it is often required to temporarily create an array that has the same
shape as an array passed to the subroutine. This can be done by means of automatic
arrays:

MORE ABOUT ARRAYS

57

SUBROUTINE swap(a,b)
REAL:: a(:), b(:), work(SIZE(a))
work=a ; a=b; b=work
END SUBROUTINE
In this case, the array work is allocated from the stack or an internal heap and automatically freed after the RETURN statement or the end of the procedure.

8.8

Elemental intrinsic functions

All elemental intrinsic functions can be applied to arrays. The resulting array has the
same shape as the initial array:
REAL, DIMENSION (10): a,b,c,d
a=b+c
d=SQRT(a)

8.9

Array valued function

The result of a function can be an array (for instance automatic objects):


FUNCTION gurk(a)
REAL:: a(:),gurk(size(a))
gurk=a**2
END FUNCTION
In this case, the array gurk is freed when it is no longer needed by the calling routine.

8.10

Allocatable arrays

Arrays can be allocated at runtime using the ALLOCATE command. This feature was
already discussed before, however, for completeness you can find another example below:
REAL, ALLOCATABLE :: a(:,:), b(:)
READ(*,*) n
ALLOCATE(a(n,n),b(n))
...
DEALLOCATE(a,b)
Few things should be considered:
Presently, F90 does not offer an automatic garbage collection or deallocation. This
means that one must deallocate any array that has been allocated. Otherwise,
only at the termination of the program the allocated space is freed.

MORE ABOUT ARRAYS

58

Specifically, it is important to deallocate arrays that are allocated in subroutines. If this is not done properly, memory might become unaccessible (memory
leakage).
The general syntax for the allocation of an array is:
ALLOCATE (allocation-list, STAT=stat)
DEALLOCATE (allocate-object-list, STAT=stat)
NULLIFY (pointer-object-list, STAT=stat)
allocation-list:
allocate-object [(array-bounds-list)][, allocation-list]
array-bound
[lower-bound:] upper-bound
The default for the lower bound is 1. The variable stat must be an INTEGER variable:
if stat is not 0 after the ALLOCATE command, the allocation failed. If STAT=stat is
missing, the ALLOCATE commands terminates the program, if it fails to allocate sufficient
space.
It is possible to test, whether arrays were already allocated somewhere else in the
program by means of the ALLOCATED intrinsic function:
ALLOCATED(pointer)
This function returns .TRUE. (array already allocated) or .FALSE.

8.11

WHERE stmt

Many Do-loops with arrays may be replaced by the WHERE construct:


REAL :: A(10)
WHERE (A>0)
A=1.0 /A
ELSE WHERE
A=0
END WHERE
This construct has the following general syntax:
WHERE (logical-array-expr)
array-assignments
[ ELSE WHERE
array-assignments ]
END WHERE
or
WHERE (logical-array-expr) array-assignments
The logical-array-expr must have the same shape as each array-assignment.

DERIVED TYPES AND OPERATOR OVERLOADING

59

Derived Types and operator overloading

9.1

Derived Types

In F90, it is possible to define derived data types. For anything but the most trivial
program this is a feature that one should rely on heavily. A derived type is a collection
of variables into one single combined structure. A derived type is declared for instance
using the following statement:
TYPE person
CHARACTER(10) ::
REAL
::
INTEGER
::
END TYPE person

name
age
id

Types can be nested, for instance:


TYPE couple
TYPE(person) :: male,female
TYPE(date)
:: date_of_marriage
END TYPE couple
As for other types, one can define variables of a derived type. Such variables are called
structures in Fortran90:
TYPE(person) :: you, me

! declare two variables you and me

Literal constants of a derived type are constructed using the following syntax:
you=person(Georg,27,210767)
where values must be supplied for each of the elements of the derived type. Within
expressions it is possible to select specific components of a derived type, using the %
operator followed by the name of the component:
you%age

9.2

-> 27

you%name

-> Georg

couple%male%age

Operator overloading

Initially none of the operations are defined for derived types. But F90 allows to redefine
the meaning of the built-in operators for derived types using operator overloading (it
is even possible to define new operators). Such a feature can be used to extend the
flexibility and the power of F90 significantly. Since the feature is used rather rarely
(and since the syntax is not particularly intuitive), we limit the discussion to a brief
example program:

DERIVED TYPES AND OPERATOR OVERLOADING

!
! modul which implements rational numbers
!
MODULE rational_numbers
IMPLICIT NONE
TYPE rational
INTEGER :: nom,denom
END TYPE rational
INTERFACE ASSIGNMENT(=) ! overload the = operator for type rational
MODULE PROCEDURE rat_to_real,rat_to_int
END INTERFACE
INTERFACE OPERATOR(*)
! overload the * operator for type rational
MODULE PROCEDURE mul_rat
END INTERFACE
INTERFACE OPERATOR(+)
MODULE PROCEDURE add_rat
END INTERFACE
CONTAINS
SUBROUTINE rat_to_real(res,r)
TYPE (rational),INTENT(IN) :: r
REAL,INTENT(OUT) :: res
res=REAL(r%nom)/REAL(r%denom)
END SUBROUTINE rat_to_real
SUBROUTINE rat_to_int(res,r)
TYPE (rational),INTENT(IN) :: r
INTEGER,INTENT(OUT) :: res
res=r%nom/r%denom
END SUBROUTINE rat_to_int
FUNCTION mul_rat(r1,r2)
TYPE (rational),INTENT(IN) :: r1,r2
TYPE (rational) :: mul_rat
mul_rat%nom
=r1%nom
*r2%nom
mul_rat%denom=r1%denom*r2%denom
END FUNCTION mul_rat
FUNCTION add_rat(r1,r2)
TYPE (rational),INTENT(IN) :: r1,r2
TYPE (rational) :: add_rat
add_rat%nom
=r1%nom*r2%denom+r2%nom*r1%denom
add_rat%denom =r1%denom*r2%denom
END FUNCTION add_rat
END MODULE rational_numbers

60

DERIVED TYPES AND OPERATOR OVERLOADING

!
! small test program to test the module
!
PROGRAM test_rational
USE rational_numbers
TYPE (rational):: a,b,c
REAL :: d
INTEGER :: i
a= rational(3,4)
b= rational(5,7)
c=a*b; d=a*b; i=a*b
WRITE(*,*)c,d,i
c=a+b; d=c; i=c
WRITE(*,*)c,d,i
END PROGRAM test_rational

61

10

NUMERICAL REPRESENTATION OF NUMBERS

10

62

Numerical representation of numbers

Most computational problems in science and engineering can be broken down into
wellknown types of calculations, such as solving a system of linear equations, Fourier
transforms, finding eigenvalues etc. Consequently, frequently one only has to write a
fairly short driver routine for the particular problem at hand, since software to solve
the subtasks is often already available.
Nevertheless, it is important to understand the principles of numeric algorithms.
The second part of the lecture deals with selected simple problems in applied mathematics.

10.1

Rounding Error

Squeezing infinitely many real numbers into a finite number of bits requires an approximate representation. Integer numbers can be represented exactly on the computer,
as long as the number is not too large and can be stored in 32 (KIND=4) or 64 bits
(KIND=8).
In contrast, using any fixed number of bits, most calculations with real numbers
will produce quantities that cannot be exactly represented. Results of floating-point
calculations must be rounded in order to fit into a finite representation. This rounding
error is the characteristic feature of floating-point computations.
In general, a value exists with the property
1.0 + = 1.0
This problem is particularly problematic when functions are differentiated numerically.

10.2

Floating-point Formats

For real numbers, several representations have been proposed, but by far the most
widely used one is the floating-point representation. Floating-point representations
have a base (which is always assumed to be even) and a precision p. If = 10
and p = 3, then the number 0.1 is represented as 1.00 101 . If = 2 and p =
24, then the decimal number 0.1 cannot be represented exactly but is approximately
1.10011001100110011001101 24 . In general, a floating-point number will be represented as d.dd...d be, where d.dd . . . d is called the significant (or mantisa) and has p
digits. More precisely d0 .d1 d2 . . . dp1 e represents the number


d0 + d1 1 + . . . + dp1 (p1) e ,

(0 di ).

(10.1)

Currently floating point numbers on computers are realized by chosing = 2.

10.3

The IEEE Standard

There are two different IEEE standards for floating-point computations. IEEE 754 is a
binary standard that requires = 2, p = 24 for single precision and p = 53 for double

10

63

NUMERICAL REPRESENTATION OF NUMBERS

precision [IEEE 1987]. It also specifies the precise layout of bits in single and double
precision. IEEE 854 allows either = 2 or = 10 and, unlike 754, does not specify
how floating-point numbers are encoded into bits [Cody et al. 1984]. It does not require
a particular value for p, but instead it specifies constraints on the allowable values of
p for single and double precision.
IEEE floating
p
single precision 2 24
double precision 2 53

point numbers
emin emax Bits Bytes
-126 128
32
4
-1022 1024 64
8

float or REAL 1 word 4 bytes 32 bits

31 30

22

sign Exponent

double

63 62
sign

Mantisse

2 word 8 bytes 64 bits

51
Exponent

0
Mantisse

Figure 1: IEEE single and double precision floating point numbers

11

11

INTEGRATION OF FUNCTIONS

64

Integration of Functions

In this section, numerical methods for calculating the approximate of an integral of the
form
Z

I(f ) =

b
a

f (x)dx ,

(11.1)

with finite limits a and b are discussed. Numerical methods are not only used for non
2
elementary solvable functions like f (x) = ex /x, ex , 1/ ln(x), . . ., but also for datasets consisting of discrete data points (x, y): in numerical applications, we often do not
know the analytical form of f (x), but we have the function values y = f (x) given on a
specific grid x. Our aim is to calculate a good approximation of I(f ) with a minimum
number of function evaluation, of the form
n
X

I(f )

wi f (xi ) .

(11.2)

i=0

Definition: An approximation of the definite integral


Z

I(f ) =

b
a

f (x)dx ,

(11.3)

of the form
Qn (f ) =

n
X

wi f (xi )

(11.4)

i=0

is called a quadrature formula. The xi give a sequence of abscissas within the range of
integration, the wi are weights at the positions i.
Example:
Consider a one-dimensional (1D) integral of the form
F =

b
a

f (x)dx.

(11.5)

In the combined trapezoidal approximation, the integral is estimated by computing the


area under a trapezoid with one side equal to f (x) at the beginning of the interval and
the other side equal to f (x) at the end of the interval:

11

65

INTEGRATION OF FUNCTIONS

Assuming that the width of the intervals is h, one obtains:


n1
X
h
F
f (xi ) + f (xn )
f (x0 ) + 2
2
i=1

"

This is equivalent to a piecewise linearly interpolation of the function in each subinterval.


More accurate results can be obtained by a quadratic interpolation through three
points. This leads to the combined Simpson rule of integration
F

hh
f (x0 ) + 4f (x1 ) + 2f (x2 ) + 4f (x3 ) + 2f (x4 )...
6
i
...2f (xn2 ) + 4f (xn1 ) + f (xn ) .

Obviously, Simpsons rule requires that n is even. It can be shown that the error in the
combined trapezoidal integration is quadratic in h2 (ba) (if the interval h is halved the
error decreases by a factor of four), and using the combined Simpson rule the error is
of the order h4 (b a). The trapezoidal rule and Simpsons rule are special realizations
of NewtonCotes formulas, which are discussed in the next section.

11.1

NewtonCotes formulas

We want to integrate the function f (x) between a lower limit a and an upper limit
b with a minimum number of evaluations of the function f (x). For equally spaced
abscissas xi , the NewtonCotes formulas achieve this goal.
The basic idea of the Newton-Cotes formulas is simple. We replace the integral f (x)
by an interpolationpolynom that possesses the values fi := f (xi ) at the given points
xi . The integral of the interpolationpolynom (which can be calculated analytically)

11

66

INTEGRATION OF FUNCTIONS

gives an approximation for the integral I(f ). Using the Lagrange representation of the
interpolationpolynom gives
f (x) =

n
X

(n)

fi Li (x) + rn (x)

(11.6)

i=0

with
n
Y
x xj

(n)

Li (x) =

j=0
j6=i

xi xj

for i = 0, 1, . . . , n

(n)

Li (xk ) = ik

(11.7)

and a rest rn (x) defining the accuracy of the interpolation. Below the Lagrange polynomials for the grid points {0, 1, 2} are shown.

It is clearly visible that the polynomials are zero at all grid points but the grid point
i. This handy property allows to write the interpolation explicitly, if the values of the
function are known at the grid points (compare Equ. 11.6).
The integral over the range [a, b] can be now calculated straightforwardly yielding:
Z

b
a

f (x)dx =

n
bX

a i=0
n
X

(n)

fi Li (x)dx +

wi fi + Rn (f )

i=0

b
a

rn (x)dx

wi =

b
a

(11.8)

(n)

Li (x)dx.

(11.9)

It is common practice to transform these integrals using the substitution x := a + sh,


xi = a + ih and xj = a + jh, where h is the step size:
wi =

b
a

= h

(n)

Li (x)dx =

n
n Y
j=0
j6=i

n
b Y

j=0
j6=i

x xj
dx =
xi xj

(11.10)

n
baZ n Y
sj
(a + sh) (a + jh)
ds =
ds.
(a + ih) (a + jh)
n
0 j=0 i j

(11.11)

j6=i

After applying the variable transformation, the following final form is obtained:
I(f ) = (b a)

n
X
i=0

(n)

(n)

i f (xi ) + Rn (f ) = (b a)

n
X
i=0

(n) (n)

i f i

+ Rn (f )

(11.12)

11

67

INTEGRATION OF FUNCTIONS

with
(n)

:= a + i

(n)

:=

xi

ba
= a + ih
n

(11.13)

and
i

n
1ZnY
sj
ds for i = 0, 1, . . . , n
n 0 j=0 i j

(11.14)

j6=i

(n)

si

32

12

32

19

75

50

50

75

19

41

216

27

272

27

216

11.2

Rn (f )

h3

12 f ()

Trapezoidal

5
h90 f (4) ()
5
3h
f (4) ()
80
7
8h
f (6) ()
945
7
275h
f (6) ()
12096
9h9 (8)
1400
f ()

Simpson

8
90
288
41

840

name

3
8

rule

Milne rule
Wedde rule

Derivation of Simpson rule using Lagrange polynomials

Without loss of generality, we assume the following grid points: x0 = 0 , x1 = 1 , x2 = 2


Than the Lagrange polynomials are given by:
L0 (x) =

(x 1) (x 2)
2

L1 (x) =

x (x 2)
1

L2 (x) =

x (x 1)
2

Integration of these polynomials is straightforward, yielding


Z

2
0

L0 (x)dx =

1
3

2
0

L1 (x)dx =

4
3

2
0

L2 (x)dx =

1
3

Thus on the intervall [0,2] we obtain the following integration rule


(2 0)
(f0 + 4f1 + f2 ),
6
which corrsponds to the weights given the table. The higher order formulas can be
derived in a similar fashion.

11

68

INTEGRATION OF FUNCTIONS

11.3

The open NewtonCotes Formulas

Besides the NewtonCotes Formulas discussed in the last section, we can also formulate
closed formulas avoiding the endpoints of the given interval range. In this case, the
endpoints a and b are neglected in the interpolation and the integral can be written as
(note that the indices in the sum have changed):
n1
X

I(f ) =

fi

i=1

b
a

= (b a)

(n)

L
i (x)dx + Rn (f )

n1
X

(11.15)

(n)
n (f )

i fi + R

(11.16)

i=1

with
(n)
L
i (x) =

n1
Y
j=1
j6=i

(n)

x xj
xi xj

(11.17)

Y sj
1 Z n n1
ds .
n 0 j=1 i j

(11.18)

j6=i

(n)
Note that the Lagrange polynomials L
sum from i = 1 to i = n 1 (as opposed to
i
i = 0 to i = n in the case of the closed Newton-Cotes Formulas).
(n)

si

11.4

-1

11

11

11

-14

26

-14

11

Rn (f )

h3

3 f ()

name
Rectangular

h3

4 f ()

14h
f (4) ()
45

24

95h
f (4) ()
144

20

41h
f (6) ()
140

5
5

Combined Formulas (Zusammengesetzte Formeln)

The closed NewtonCotes formula are non-convergent, i.e. increasing the order of the
interpolation and integration does not necessarily decrease the error of the numerical
quadrature. In fact, in many cases quite the opposite is observed: if the integrated
function is not sufficiently smooth, the error might increase with the order beyond
n = 2 (Simpsons rule).
Hence, one prefers formulas with low order and subdivides the entire interval into
smaller intervals. In the simplest case, the integration interval [a, b] is divided into N
subranges with equal length. This results in a sum of integrals:
I(f ) =

b
a

f (x)dx =

N
1 Z xi +1
X
i=0

xi

f (x)dx

(11.19)

11

69

INTEGRATION OF FUNCTIONS

with
xi = a + ih for i = 0, 1, . . . , N

and h :=

ba
N

(11.20)

to calculate the integral in each subrange [xi , xi+1 ], one applies the basic Newton
Cotes formulas. Using the trapezoidal rule
I(f ) =
=

N
1
X

"

i=0
N
1
X

h
2

f (i )
f (xi ) f (xi+1 )
(xi+1 xi )

+
(xi+1 xi )3
2
2
12
[f (xi ) + f (xi+1 )]

i=0

1
h3 NX
f (i )
12 i=0

N
1
1
X
h2 (b a) NX
h
f (xi ) + f (b)
f (i ) ,
f (a) + 2
=
2
12
i=1
i=0

"

(11.21)

neglecting the rest yields an approximation for the integral using the combined trapezoidal rule,
I(f ) = T (f ; h),

(11.22)

with
1
1
T (f ; h) = h f (a) + f (a + h) + . . . + f (b h) + f (b) .
2
2


(11.23)

If N is even (N = 2M ), the Simpson rule on the intervals [x2i , x2i+2 ] for i =


0, 1, . . . , M 1 can be applied yielding:
I(f ) =
=

M
1 Z x2i +2
X
i=0 x2i
(
M
1
X

f (x)dx

f (x2i ) 4f (x2i+1 ) 4f (x2i+2 )


h5
(x2i+2 x2i )
f (4) (i )
+
+
6
6
6
90

i=0
M
1
X

h
3

" 1
h MX

"

[f (x2i ) + 4f (x2i+1 ) + f (x2i+2 )]

i=0

i=0

f (x2i ) + 4

M
1
X

f (x2i+1 ) +

i=0

M
X
i=1

1
h4 b a MX
f (4) (i )
90 2M i=0

f (x2i )

(b a)h4 (4)
f (). (11.24)
180

We can now write the integral I(f ) as the sum of the combined Simpsonrule S(f ; h)
and a rest:
I(f ) = S(f ; h)

(b a)h4 (4)
f (),
180

(11.25)

with the Simpsonsum


M
1
M
1
X
X
h
S(f ; h) =
f (x2i+1 ) + f (b) .
f (x2xi ) + 4
f (a) + 2
3
i=0
i=1

"

(11.26)

11

70

INTEGRATION OF FUNCTIONS

11.5

Summary

Closed NewtonCotes Formulas


include the endpoints of the integration range:
I(f ) = (b a)

n
X

(n)

(n)

i f (xi ) + Rn (f ) = (b a)

n
X

(n) (n)

i f i

+ Rn (f )

(11.27)

i=0

i=0

with
(n)

:= a + i

(n)

:=

xi

ba
= a + ih
n

(11.28)

and
i

n
1ZnY
sj
ds for i = 0, 1, . . . , n
n 0 j=0 i j

(11.29)

j6=i

The open NewtonCotes Formulas


apply integration where the interpolation polynomials do not include the endpoints:
I(f ) =

n1
X

fi

i=1

b
a

= (b a)

(n)

L
i (x)dx + Rn (f )

n1
X

(11.30)

(n)
n (f )

i fi + R

(11.31)

i=1

with
(n)
L
i (x) =

n1
Y
j=1
j6=i

(n)

x xj
xi xj

(11.32)

Y sj
1 Z n n1
=
ds .
n 0 j=1 i j

(11.33)

j6=i

In combined NewtonCotes Formulas


The integrationinterval [a, b] is divided into N subranges with equal length:
I(f ) = S(f ; h)

(b a)h4 (4)
f (),
180

(11.34)

with the Simpsonsum


M
1
M
1
X
X
h
S(f ; h) =
f (x2i+1 ) + f (b) .
f (x2xi ) + 4
f (a) + 2
3
i=0
i=1

"

(11.35)

12

NUMERICAL DIFFERENTIATION

12
12.1

71

Numerical Differentiation
Interpolation Formulas

In this section, we will discuss methods to compute the numerical approximation for
the derivate of f at x, where f must be a real differentiable function. This can be
related to the numerical quadrature of the last section: f (x) is again approximated by
a polynomial , yielding explicit equations for the derivate:
f (x) = (x) + R(x)

(12.1)

Again we have to deal with a rest (the difference between the exact result and the
approximation) R(x). Contrary to integration, numerical differentiation is very prone
to rounding errors and choosing the right step size h becomes a critical issue. The aim
of this section is to find methods that are fast and numerically stable.
Usually the most simple approximation uses a polynom to interpolate f and to
calculate the derivate of .
linear interpolation: x0 = x, x1 = x + h. Using the Lagrange representation we
obtain
x (
x + h)
x x
+ f (
x + h)
x (
x+h
(
x + h) x
f (
x + h) f (
x)
(
x + h)f (
x) xf (
x + h)
=
x+
.
h
h

(x) = f (
x)

(12.2)

Quadratic interpolation: x0 = x h, x1 = x, x2 = x + h. After some rearrangement, the interpolation polynom can be written as:
(x) =

f (
x h) 2f (
x) + f (
x + h) 2
x
(12.3)
2h2
(2
x + h)f (
x h) 4
xf (
x) + (2
x h)f (
x + h)

x+
2
2h
(
x2 + h
x)f (
x h) 2(
x2 h2 )f (
x) + (
x2 h
x)f (
x + h)
+
.
2
2h

Differentiation at x yields:
hf (
x h) + hf (
x + h)
2
2h
f (
x + h) f (
x h)
=
2h

(
x) =

(12.4)

12

72

NUMERICAL DIFFERENTIATION

To increase the accuracy of the interpolation methods further, the number of grid
points can be increased beyond 2. In the general case, the differentiation formulas can
be written as:
f (x) =

n
X

(n)

fi Li (x) + Rn (x)

i=0

n
X
d
d (n)
fi Li (x) + Rn (x).
f (x) =
dx
dx
i=0

(12.5)

Using a symmetric equidistant grid around x (where we want to find the derivate of
f (x)) we can use
k
1 X
(k)
i fi + r2k (f ; x),
f (x) =
h i=k

(12.6)

with
xi = x + ih

and

fi = f (xi )

for

i = k, . . . , k.

(12.7)

The coefficients i (times s) are tabulated in the following table:


2k
2
4
6

12.2

(k)

-1
1
-1

0
-8
9

si
1
0 8
-45 0

-1
45

-9

s
r2k (f ; x)
2
2
h6 f ()
4
12 h30 f (5) ()
h6 (7)
f ()
60 140

Example: derivative of exp(x)

The following table shows the results for numerical differentiation of the function ex at
x = 1.
h
10
104
106
108
1010
1012
exact
2

(e1+h e1 )/h
REAL(KIND=4)
2.858844
2.719879
2.741814
4.768372
0.000000
0.000000
2.718281

REAL(KIND=8)
2.85884195487388
2.71964142253323
2.71829541995672
2.71828196840573
2.71828204390090
2.71831446241322
2.71828182845904

(e1+h e1h )/2h


REAL(KIND=4)
2.718318
2.719164
2.622604
4.768372
0.000000
0.000000
2.718281

REAL(KIND=8)
2.71832713338271
2.71828183298961
2.71828182829559
2.71828182185629
2.71828115572248
2.71849209809716
2.71828182845904

Clearly the simple first order (linear) approximation is unreliable, yielding only few
digits accuracy at single precision, and at most 8 digits accuracy at double precision.
Furthermore the step size h has to be chose very carefully. The second order approximation yields reasonable values over a fairly wide range of step sizes h, but it is also
obvious that single precision does not yield accurate results even with the second order
approximation.

13 STOCHASTIC METHODS: MONTE-CARLO METHODS THE REJECTION


METHOD
73

13

Stochastic Methods: Monte-Carlo methods the


rejection method
H
(r1,r2)
accept

reject

An alternative to the deterministic integration formulas we have learned in the last


section are Monte-Carlo methods. These are widely used to solve high dimensional
integrals, which are commonly encountered in many-particle systems e.g. thermodynamics.
The simplest Monte-Carlo algorithm is the rejection method.
We assume that the maximum of a function f is known in the interval [a, b],
max(f )[a,b] = H.
We generate two random numbers r1 , r2 , r1 in the interval [a, b] and r2 in the
interval [0, H].
Finally we check whether the point (r1 , r2 ) is below or above the curve defined
by the functions y = f (x) (r2 < f (r1 )).
The integral is than simply approximated by
In (b a)H

ns
,
n

where ns is the number of points below the curve, and n is the total number of points.
MODULE functions
CONTAINS
FUNCTION f1(x)
REAL :: f1,x
f1=x**2
END FUNCTION f1
FUNCTION f2(x)
REAL :: f2,x
f2=exp(x)
END FUNCTION f2
END MODULE functions
MODULE integrate
CONTAINS

13 STOCHASTIC METHODS: MONTE-CARLO METHODS THE REJECTION


METHOD
74

FUNCTION mc_int(f,xstart,xend,n,fmax)
INTERFACE
FUNCTION f(x); REAL f,x; END FUNCTION
END INTERFACE
REAL :: mc_int,xstart,xend,d,fmax,x,y
INTEGER:: n,i
mc_int=0
d=(xend-xstart)
DO i=1,n
CALL RANDOM_NUMBER( x)
CALL RANDOM_NUMBER( y)
IF (f(xstart+d*x) > y*fmax) mc_int=mc_int+1
mc_int2=mc_int2+f(xstart+d*x)
ENDDO
mc_int=mc_int/n*(xend-xstart)*fmax
mc_int2=mc_int2/n*(xend-xstart)
END FUNCTION mc_int
FUNCTION simp_int(f,xstart,xend,intersections)
INTERFACE
FUNCTION f(x); REAL f,x; END FUNCTION
END INTERFACE
REAL :: simp_int,xstart,xend,d
INTEGER:: in,intersections,i
in=(intersections/2)*2 ! round towards 2
simp_int=0
d=(xend-xstart)/in
DO i=1,in-1,2
simp_int=simp_int + f(xstart+d*(i-1))*d/3.
simp_int=simp_int + f(xstart+d*i) *d*4/3.
simp_int=simp_int + f(xstart+d*(i+1))*d/3.
ENDDO
END FUNCTION simp_int
END MODULE integrate
PROGRAM test
USE functions
USE integrate
IMPLICIT NONE
REAL :: a,b
INTEGER :: n
READ(*,*) n
WRITE(*,*) mc_int(f1,0.,1.,n,1.),mc_int(f2,0.,1.,n,exp(1.0))
WRITE(*,*) simp_int(f1,0.,1.,n), &
simp_int(f2,0.,1.,n)
END PROGRAM test

13 STOCHASTIC METHODS: MONTE-CARLO METHODS THE REJECTION


METHOD
75

An equally simple method is to sum the integrand at a number of random points.


This is implemented as well in the routines (mc int2), although this particular result
is not returned to the main program.
As shown in the table below Monte-Carlo methods seemingly converge very slowly
with the number of points n:
n
5
10
100
1000
100000

MC
x2
0.2994335
0.4476958
0.3611916
0.3512844
0.3344397

Simpson
x2
0.3333333
0.3333333
0.3333333
0.3333333
0.3333333

MC
exp(x)
2.204745
1.785828
1.793215
1.719919
1.716187

Simspon
exp(x)
1.718319
1.718283
1.718282
1.718282
1.718282

In fact, it can be shown that the error decreases as 1/n, since it is essentially
proportional to the variance.
However, Monte-Carlo methods become competitive for high dimensional integrals.
Consider that we want to calculate the k dimensional integral
I(f ) =

Z Z Z

...

f (~x)dxk

of a function that depends on k arguments. The convergence of Monte-Carlo methods


is not affected by the dimensionality of the integral, whereas it is easy to show that
for conventional integration methods the convergence does depend on the number of
arguments of the function. Consider for instance that the total number of points is n,
and we divide this up into an equal number of points in each direction i = 1, ..., k,
n = n1 n2 n3 ...nk with corresponding spacing hi 1/ni . In this case ni n(1/k) .
Assuming that we use the combined Simpson rule in each direction, we obtain an error
estimation of
Simpson: (max hi )4 1/(ni )4 (1/n1/k )4 = (1/n)4/k
compared to the error
in the Monte-Carlo procedure:
q
Monte-Carlo: 1/n = (1/n)1/2 .
From this simple estimate, we can deduce that Monte-Carlo procedures become competitive if the total number of dimensions approaches k = 8. This is often observed in
real-world applications.

14

ORDINARY DIFFERENTIAL EQUATIONS

14
14.1

76

Ordinary Differential Equations


Introduction

Historically, differential equations have originated in chemistry, physics, and engineering. More recently they have also arisen in models in medicine, biology, anthropology
etc. In this chapter we restrict our attention to ordinary differential equations; a discussion of partial differential equations is a much more complicated issue. We focus
on initial value problems and present some of the more commonly used methods for
solving such problems numerically.

14.2

Theory

A differential equation is an equation involving an unknown function and one or more of


its derivatives. The equation is an ordinary differential equation (ODE) if the unknown
function depends on only one independent variable. Some examples for ODEs are given
below:
du
= F (t)G(t) ,
dt
d2 g
+ sin = F (t) ,
dt2
l
dQ Q
d2 Q
+ = E(t) ,
L 2 +R
dt
dt
C

the growth equation;

(14.1)

the pendulum equation;

(14.2)

the LCR oscillator equation;

(14.3)

In the above equations t is the independent variable; the dependent variables are u,
and Q, respectively. In the following pages we will use the notation y to represent
dy/dt, and y (n) to represent higher orders dn y/dtn .
The order of a differential equation is the order of the highest derivative appearing
in the equation. Equations (14.2,14.3) are second order equations and (14.1) is a first
order equation.
A solution of a general differential equation of the n.th order,
f (t, y, y,
. . . , y (n) ) = 0,

(14.4)

is a real-valued function y(t) defined over some interval I having the following properties:
1. y(t) and its first n derivatives exist for all t in I, so y(t) and its first n 1
derivatives must be continuous in I, and
2. y(t) satisfies the differential equation for all t in I.
With a differential equation, we can associate initial conditions or boundary conditions, auxiliary conditions on the unknown function and its derivatives. If these conditions are specified at a single value of the independent variable, they are referred to as
initial conditions and the combination of the differential equation and an appropriate

14

77

ORDINARY DIFFERENTIAL EQUATIONS

number of initial conditions is called an initial value problem (IVP). If these conditions
are specified at more than one value of the independent variable, they are referred
to as boundary conditions and the combination of the differential equation and the
boundary conditions is called a boundary value problem (BVP).
Example (IVP):
The mass-spring system equation,
x +

a
k
1
x + x = g + F (t),
m
m
m

(14.5)

with the initial conditions x(0) = x0 , x(0)

= v0 ; for m = 10, k = 140, a = 90,


F (t) = 5 sin(t), x0 = 0, v0 = 1, the solution is
x(t) =


1 
90e2t + 99e7t + 13 sin(t) 9 cos(t) .
500

(14.6)

Example (BVP):
The differential equation,
y + 2 y = 0,

(14.7)

with boundary conditions y(0) = 2, y(1) = 2; the solution is


y(t) = 2 cos(t) + c sin(t), where c is an arbitrary constant.

14.3

Systems of Differential Equations

Suppose we want to solve the system of first order ODEs,


= F(t, Y),
Y

Y(a) = A.

(14.8)

Here Y and A are n-vectors and F is a nonlinear vector-valued function on Rn Rn :

Y=

Y1
Y2
...
Yn

,A =

A1
A2
...
An

,F =

F1 (t, Y1 , Y2 , . . . , Yn )
F2 (t, Y1 , Y2 , . . . , Yn )
...
Fn (t, Y1 , Y2 , . . . , Yn )

(14.9)

To insure existence and uniqueness of a solution to (14.8) and hence establish effective
numerical procedures, we require that F(t, Y) and F/Y be continuous in the box
B : |t a| , ||Y A|| where and are positive numbers.

14.4

Numerical Solution Methods

We begin with numerical methods for solving a scalar version of (14.8), i.e., the case
n = 1:
y = f (t, y),

y(a) = A

(14.10)

The methods we develop for solving (14.10) can easily be extended to systems of first
order differential equations and to higher order differential equations. The methods

14

ORDINARY DIFFERENTIAL EQUATIONS

78

are referred to as discrete variable methods and generate a sequence of approximate


values for y(t1 ), y(t2 ), y(t3 ), . . . at points t1 , t2 , t3 . No attempt is made to approximate
the exact solution, y(t), over a continuous range of the independent variable t. In our
development, we will assume a constant spacing h between the points t. In realistic
implementations of these methods, however, h is chosen to satisfy a user-specified
accuracy request. The expression y(ti ) will always be used to denote the exact solution
to (14.10) at t = ti , and ui will always be used for an approximation to y(ti ).
Errors enter into the numerical solution of IVPs from two sources. The first are
discretization errors, and they depends on the method being used. The second are
computational errors including such things as roundoff errors, the error in evaluating
implicit formulas, etc. In general, roundoff error can be controlled by carrying enough
significant figures in the computation (e.g. KIND=8). The control of other computational
errors again depends on the method being used.
There are two measures of discretization errors commonly used in discussing the
accuracy of numerical methods for solving IVPs. The first is the true or global error.
For any t = ti+1 , the global error is simply the difference between the true solution and
our numerical approximation to it:
i+1 = y(ti+1 ) ui+1 .

(14.11)

Even though this is the error we are usually interested in, it is a relatively difficult
(sometimes impossible) to estimate. The other measure of error is the local error. It is
the error incurred in taking a single step using a numerical method. If we let y(t) be
the solution to the IVP,
y = f (t, u),

ui = y(ti ),

i.e. we assume to know y(ti ) at ti

(14.12)

then the local error at t = ti+1 is given by


di+1 = y(ti+1 ) ui+1 .

(14.13)

Most codes for solving IVPs estimate the local error at each step and attempt to
adjust h accordingly. Control of the local error controls the global error indirectly;
this, of course, depends on the stability of the problem itself. Most problems are at
least moderately stable, and the global error is often comparable to the local error. Also,
the cost of estimating global errors is twice or more than the cost of the integration
itself.

14.5

One-Step Methods

A differential equation has no memory. That is, the values of y(t) for t before ti
do not directly affect the values of y(t) for t after ti . Some numerical methods have
memory, and some do not. We shall first describe a class of methods known as one-step
methods. They have no memory; given ui there is a recipe for ui+1 that depends only
on information at ti .
Suppose we want to approximate the solution to (14.10) on the interval [a, b]. Let
the points be equally spaced; i.e. for a positive integer n the step size is given by

14

79

ORDINARY DIFFERENTIAL EQUATIONS

h = (b a)/n, ti = a + ih, i = 0, 1, . . . , n. If a < b, h is positive and we are integrating


forward; if a > b, h is negative and we are integrating backwards. A general one-step
method can then be written in the form
ui+1 = ui + h(ti , ui ),

u0 = y(t0 ),

(14.14)

where is a function that characterizes our method. We seek accurate algorithms of


the form (14.14). By this we mean that the approximate solution should be close to
the true solution given by
y(ti+1 ) = y(ti ) + h(ti , y(ti )) + hi .

(14.15)

A method is consistent with the differential operator, if i vanishes for sufficiently


small steps h. The quantity i is called the local (truncation) error of the method. The
method (14.14) is said to be of order p, if for all ti , a ti b, and for sufficiently small
h, there are constants C and p such that
|i | = Chp .

(14.16)

I.e. |i | goes to zero no slower than Chp , and it is common practice to write this as
i = O(hp ). The constant C depends, in general, on the solution y(t), its derivatives, and
the length of the interval over which the solution is to be found, but it is independent
of h.
Note that, the order di+1 = y(ti+1 ) ui+1 = hi = hO(hp ) of a method is commonly
refered to as p, if the order of the local error is p + 1, because the local errors tend
to accumulate as the integration proceeds. The order of a method may be viewed as a
measure of how fast the error in the computed solution goes to zero when integrating
over a fixed interval t as h is decreased by adding more points in the interval. Our goal
is to find functions that are inexpensive to evaluate, yet of high order in p. In what
follows, different increment functions are used. The methods we discuss here can be
subdivided into the classical Taylor series methods and the Runge-Kutta methods.
14.5.1

Taylor Series Methods

The simplest one-step methods of order p are based on a Taylor series expansion of the
solution y(t). If y (p+1) (t) is continuous on [a, b], then Taylors formula gives
hp1
y(ti+1 ) = y(ti ) + h y(t
i ) + . . . + y (ti )
p!
p+1
h
,
+ y (p+1) (i )
(p + 1)!
(p)

(14.17)

where ti i ti+1 . The continuity of y (p+1) (t) implies that it is bounded on [a, b] and
so
y (p+1) (i )

hp+1
= O(hp+1 ) = hO(hp ).
(p + 1)!

(14.18)

14

80

ORDINARY DIFFERENTIAL EQUATIONS

Using the fact that y = f (t, y), we can write


y(ti+1 ) = y(ti ) + h f (ti , y(ti )) + . . . + f

(p1)

hp1
(ti , y(ti ))
p!

+ hO(hp ),

(14.19)

where the total derivatives of f are defined recursively by (mind dy/dt = y = f (t, y))
f (1) (t, y) =

df
f (t, y) f (t, y) dy
=
+
= ft (t, y) + fy (t, y)f (t, y),
dt
t
y dt
(k1)

f (k) (t, y) = ft

(t, y)f (t, y),

k = 2, 3, . . . .

(14.20)

Comparison shows that to obtain a method of order p, we can use


(t, y(ti )) = f (ti , y(ti )) + . . . + f

(p1)

hp1
(ti , y(ti ))
.
p!

(14.21)

This choice leads to a family of methods known as the Taylor series methods, given in
the following algorithm:
To obtain an approximate solution of order p to the IVP (17) on [a, b], let h =
(b a)/n and generate the sequences
ui+1
ti+1

hp1
= ui + h f (ti , ui ) + . . . + f (p1) (ti , ui )
,
p!
= ti + h,
i = 0, 1, . . . , n 1,
!

(14.22)
(14.23)

where t0 = a, and u0 = A.
In fact these methods are rarely used since the require an analytical evaluation of
the partial derivatives of the function f , which is often not available or difficult to
code. The simplest approximation, though, is to truncate the series at the lowest order
yielding the well known Euler forward equation:
ui+1 = ui + hf (ti , ui ).

14.6

(14.24)

Runge-Kutta Methods

Runge-Kutta methods are designed to approximate Taylor series methods, but have
the advantage of not requiring explicit evaluations of the derivatives of f (t, y). The
basic idea is to use a linear combination of values of f (t, y) to approximate y(t). This
linear combination is matched up as closely as possibly with a Taylor series for y(t)
to obtain methods of the highest possible order p. Eulers method is an example using
one function evaluation.
We illustrate the development of Runge-Kutta formulas by deriving a method using
two evaluations of f (t, y) per step; the technique employed in the derivation extends
easily to the development of all Runge-Kutta type formulas. Given values ti , ui , choose
values ti ,ui and constants 1 , 2 so as to match


ui+1 = ui + h 1 f (ti , ui ) + 2 f (ti , ui )

(14.25)

14

81

ORDINARY DIFFERENTIAL EQUATIONS

with the Taylor expansion,


y(ti+1 ) = ui + f (ti , ui ) + f

(1)

h
h2
(ti , ui ) + f (2) (ti , ui ) . . . ,
2
6
!

(14.26)

as closely as possible. In what follows, all arguments of f and its derivatives will be
suppressed when they are evaluated at (ti , ui ). It will also be convenient to express ti ,ui
as
ti = ti + h1
ui = ui + 2 hf (ti , ui ).

(14.27)
(14.28)

So the objective is to match


R = 1 f + 2 f (ti + 1 h, ui + 2 hf )
= (1 + 2 )f + 2 h(2 f fy + 1 ft ) +
+O(h3 )

2 h 2 2 2
(2 f fyy + 21 2 f fty + 12 ftt )
2
(14.29)

with the Taylor expansion


h2
h
T = f + f (1) + f (2) + O(h3 )
2
6
h
h2
= f + (f fy + ft ) + (f 2 fyy + 2f fty + ftt + ft fy + f fy2 ) + O(h3 ) (14.30)
2
6
Equating coefficients of equal powers of h in the above expressions for R and T , we are
able to obtain agreement in terms involving h0 and h1 :
h0 : 1 + 2 = 1,
1
h1 : 2 2 = 1 2 = .
2

(14.31)

If we choose 2 = , an arbitrary parameter, these equations can be solved exactly to


give
2 = ,
1 = 1 ,
1
1 = 2 =
,
2

6= 0.

(14.32)

Combining all, this yields a one-step method of order p = 2 if 6= 0.


In summary, the algorithm is given by the following steps. To obtain an approximate
solution of order p = 2 to the IVP, let h = (b a) and generate the sequences
ui+1
ti+1

h
h
= ui + h (1 )f (ti , ui ) + f (ti + , ui + f (ti , ui )) ,
2
2
= ti + h,
i = 0, 1, . . . , n 1,

(14.33)

14

ORDINARY DIFFERENTIAL EQUATIONS

82

where 6= 0, t0 = a and u0 = A. Eulers method is the special case = 0, and has


order p = 1; the improved Euler (or Euler-Heun) method uses = 21 , and the EulerCauchy (or mid-point) method uses = 1. These methods have both order 2 (in fact
any method with 6= 0 has order 2).
A basic assumption in the derivation of the family of Runge-Kutta formulas was
that the solution y(t) had three continuous derivatives. What happens if a formula
of order p = 2 is used to solve an initial value problem whose solution has only two
continuous derivatives, but not three. Examination of the local truncation error shows
that the formula is then of order p = 1 and convergence is O(h) and not O(h2 ). The
point here is that higher order procedures can be used on problems whose solutions are
not sufficiently smooth, but their rate of convergence may be dramatically reduced.
Example: A common example of problems whose solutions will not be smooth are
those where the coefficients have a discontinuity (jump) at some point in the range of
integration. In solving such problems numerically, integration should not be performed
across the discontinuity. For example, suppose we are solving the problem
y + y = f (t),
0 t 2,
y(0) = 0,
y(0)

= 1,
(
1, for 0 t 1,
f (t) =
0, for 1 t 2.

(14.34)

A good procedure would be to integrate from t = 0 to t = 1 and then from t = 1


to t = 2. On each subinterval, the differential equation has smooth solutions and
convergence rates will be as discussed before.
A major limitation of Runge-Kutta formulas is the amount of work required; work
is measured in terms of the number of evaluations of the function f . For higher order
formulas, the work goes up dramatically; p evaluations per step lead to procedures of
order p for p = 1, 2, 3, and 4, but not for 5; 6 evaluations are required for a formula of
order 5, 7 for order 6, 9 for order 7, 11 for order 8, etc. For this reason, fourth order
procedures are quite common. As in the second order case, where the parameter was
arbitrary, there is a family of fourth order formulas that depend on several parameters.
One choice leads to the so-called classical formulas.
14.6.1

Classical RungeKutta Formulas

To obtain an approximate solution of order p = 4 to the IVP on [a, b], let h = (b a)/n
and generate the sequences
h
ui+1 = ui + (k1 + 2k2 + 2k3 + k4 ),
6
ti+1 = ti + h,
i = 0, 1, . . . , n 1,
where
k1 = f (ti , ui )
k2 = f (ti + h/2, ui + (h/2)k1 ),

(14.35)

14

ORDINARY DIFFERENTIAL EQUATIONS

k3 = f (ti + h/2, ui + (h/2)k2 ),


k4 = f (ti + h, ui + hk3 ),

83

(14.36)

and t0 = a, u0 = A.
As we pointed out earlier, the methods developed extend readily to systems of first
order IVPs of the form (14.9). As an illustration, the formulas (35) in the case of an
n-dimensional system become
h
Yi+1 = Yi + (K1 + 2K2 + 2K3 + K4 ),
6
ti+1 = ti + h,
i = 0, 1, . . . , n 1,

(14.37)

where
K1
K2
K3
K4

14.7

=
=
=
=

F(ti , Yi ),
F(ti + h/2, Yi + h/2K1 ),
F(ti + h/2, Yi + h/2K2 ),
F(ti + h, Yi + hK3 ).

(14.38)

Example code

Below a full implementation of the Euler-Cauchy algorithm for the damped oscillator
is printed. The method is used to solve the second order differential equation:
0 = U (t) +

1
y + Ry + L
y
C

where U (t) = U sin(t). To apply the Euler-Cauchy relation to a second order differential equation, we first substitute Y1 = y und Y2 = y = Y 1 , yielding
0 = U (t) +

1
Y1 + RY2 + LY 2 .
C

Combined with the defining equation Y2 = Y 1 , we can write these as two first order
differential equations
Y 1 = Y2
1
1
Y 2 =
U (t) + Y1 + RY2 ,
L
C


that can be straightforwardly solved using the Euler-Cauchy method. Note that similar
substitutions can be performed for any differential equation of nth order, yielding a
system of first order differential equation with n independent variables Yi , i = 1, ...n.
MODULE myfunction
REAL, PARAMETER :: OMEGA = 4 , C=1 , R=1 , L=1 , U=0
CONTAINS
FUNCTION der_damped_osz( t, y)

14

ORDINARY DIFFERENTIAL EQUATIONS

REAL :: der_damped_osz(2), t, y(2)


der_damped_osz(1) = y(2)
der_damped_osz(2) = -1/L * &
( - U* sin(OMEGA * t) + 1/C * y(1) + R * y(2))
END FUNCTION der_damped_osz
END MODULE myfunction
MODULE solve_diff
CONTAINS
SUBROUTINE Euler_Cauchy( f, y, t, h)
INTERFACE
FUNCTION f( t, y)
REAL :: f(2), y(2)
END FUNCTION f
END INTERFACE
REAL :: y(2), t, h
! local
REAL :: k1(2), k2(2)
k1= f(t, y)
k2= f(t+h/2, y+k1*h/2)*h
y=y+k2 ; t=t+h
END SUBROUTINE Euler_Cauchy
END MODULE solve_diff

PROGRAM MAIN
USE myfunction
USE solve_diff
REAL :: h
REAL :: y(2), t=0
INTEGER i, nstep
READ(*,*) h,nstep
t=0
y(1)=1
y(2)=0
DO i=1,nstep
WRITE(*,(2F14.7)) t,y(1)
CALL Euler_Cauchy(der_damped_osz, y, t, h)
ENDDO
END PROGRAM MAIN

84

14

85

ORDINARY DIFFERENTIAL EQUATIONS

1
h=0.1
h=0.2
h=0.4

0.8
0.6
0.4
0.2
0
-0.2

10

15

20

Figure 2: Comparison of different timesteps h for Euler-Cauchy integration of the free


damped oscillator equation (U=0).

2
omega=1
omega=4

-1

-2

10

15

20

Figure 3: Comparison for two external source terms (U (t)) with different frequencies.

14

86

ORDINARY DIFFERENTIAL EQUATIONS

14.8

Some Implementation Issues

A too small value of h implies that we are doing unnecessary computations that could
lead to roundoff error (error induced because the arithmetic is not exact); a too large
value of h implies that we are probably not meeting the desired accuracy requirements
(errors induced by discretization). Although we will not develop the details here, something can be said about the qualitative behavior of the global error as a function of h
for a one-step method of order p. If the error in evaluating (ti , ui ) is i with |i |
for all i and the error in forming h(ti , ui ) is i with |i | for all i, then it can be
shown that
|y(ti ) ui | |y(t0 ) u0 |e

L(ba)

eL(ba)
+
L

Chp
++
,
2
h
!

(14.39)

where C and L are constants depending on y and its derivatives. As we said earlier,
most codes estimate the local error at each step and attempt to adjust h accordingly. A
widely used procedure for estimating the local error follows. Suppose we have computed
p
p+1
approximations of order p and p + 1 at ti+1 , yi+1
and yi+1
respectively. The local error
in the pth order approximation can be written as


p
p+1
p+1
p
di+1 = u(ti+1 ) yi+1
= u(ti+1 yi+1
+ yi+1
yi+1
.

(14.40)

p+1
If h is sufficiently small, the term u(ti+1 ) yi+1
can be neglected and we can use the
p+1
p
computed value yi+1 yi+1 as an estimate of the error in the pth order formula. This
sort of approximation has been validated by extensive numerical experimentation over
the years.

14.9

The Codes RKSUITE

RKSUITE is an excellent collection of codes based on Runge-Kutta methods for the


numerical solution of an IVP for a first order system of ODEs and is available in the
public domain. It supersedes some very widely used codes written by the authors and
their coauthors, namely the RKF45 code and its descendant DDERKF in the SLATEC
library and DO2PAF and associated codes in the NAG Fortran library. RKSUITE is
written in standard FORTRAN 77 and is distributed in source form. The advanced
algorithms provide more functionality than is found in earlier codes, including new more
efficient formulas, interpolation, automatic selection of the initial step size, a stiffness
diagnostic, and global error assessment. The advanced software design includes a novel
interface permitting both interval- and step-oriented solutions and is highly portable.
The documentation includes detailed instructions for the effective use of the codes, a
collection of templates illustrating the use of the codes for common tasks, output from
running the templates in a number of variations, and comprehensive instructions for
installing the codes.
RKSUITE implements three Runge-Kutta pairs: (2,3), (4,5), and (7,8). The (4,5)
pair, for example, uses both a 4th and a 5th order approximation to estimate the error
in the 4th order formula; using extrapolation, it then produces a formula of order 5.

REFERENCES

87

Similarly, the (2,3) pair produces a formula of order 3 and the (7,8) pair a formula of
order 8.
14.9.1

Where to find it

The documentation for RKSUITE provided in RKSUITE.DOC is especially recommended. It is fairly short, carefully written document, explaining some of the internal
workings of the suit of codes. The reader should pay particular attention to the description of error control and the effect on delivered accuracy. The documentation also
contains an excellent set of references that describe the formulas and algorithms used
in RKSUITE; references are also included that describe the design, implementation
and testing of codes based on explicit Runge-Kutta formulas. The current code was
written by R. Brankin (NAG). I. Gladwell (SMU) and L. Shampine (SMU).
A copy of RKSUITE, together with sample drivers, can be obtained from netlib
(http://www.netlib.org/ode/rksuite/)

14.10

Multi-Step Methods

The Taylor Series and explicit Runge-Kutta methods that we have discussed so far
have no memory: the value of y(t) for t before ti do not directly affect the values of y(t)
for t after ti . Other methods take advantage of previously computed solution values
and are referred to as multistep methods. The Adams formulas for non-stiff problems
and the Backward Differentiation Formulas for stiff problems furnish important and
widely-used examples of multi-step methods. These methods are not discussed in the
lectures.

References
[1] L.F. Sampine and M.K. Gordon, Computer Solution of Ordinary Differential Equations: the Initial Value Problem, Freeman, 1975.
[2] C.W. Gear, Numerical Initial Value Problems in Ordinary Differential Equations,
Prentice Hall, 1971.
[3] L.F. Shampine and C.W. Gear, A Users view of Solving Stiff Ordinary Differential
Equations, SIAM Review, 21 (1979) pp. 1-17.

You might also like