Fortran90 Tutorial
Fortran90 Tutorial
Einf
uhrung in die Numerische Programmierung mit
dem Schwerpunkt Fortran 90
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
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
11
12
13
13
14
15
15
16
17
18
19
20
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
21
22
22
23
23
24
25
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
26
26
26
26
27
27
27
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
CONTENTS
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
28
28
29
30
31
32
35
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
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
54
54
55
55
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
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
73
76
76
76
77
77
78
79
80
82
83
85
85
86
86
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
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
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
&
/
<
(
>
)
?
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:
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
&
* 12
&
* 12
!
;
!
;
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
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
variable a
variable result
1.2.3
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.
1.2.4
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
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
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
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
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
<
<=
==
/=
>
11
>=
corresponding to less, less or equal, equal, not equal, larger, and larger or equal.
1.5
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.
Let us assume we want to convert a set of numbers from calories to joules. The following
program will do exactly this:
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
2.1.1
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
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
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 .
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:
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
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
We recommend to switch the implicit typing off by including the statement IMPLICIT
NONE before declaring any variable.
2.2
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
!
!
!
!
!
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.
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,
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
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
In fact, there are two versions of the IF construct in F90. The short one is:
IF (scalar-logical-expr) stmt
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
PROGRAM calculator
REAL :: res, a
CHARACTER (LEN=1) :: operation
res=0
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
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
- 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).
3.1
22
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)
a + (b*x) + ((c*(x**2))*2)
Int
Real
Complex
Int
Real
Complex
Real
Real
Complex
Complex
Complex
Complex
3.2
Only the concatenation of strings is supported. Legal operands are CHARACTERS (constants, variables or function calls). The dyadic operand is
//
concatenation
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"
3.3
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
3.4
! -> .TRUE.
! -> .FALSE.
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
3.5
24
Precedence
character concatenation
relational
logical expression
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.
3.6
25
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
4
4.1
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
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
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
PROGRAM mat_vec
INTEGER, PARAMETER :: n=2
INTEGER :: i, j
27
28
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
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
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
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.
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
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
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
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:
32
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
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
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
* INT
NINT
IDNINT
* REAL
I
R
C
I
I
I
R
D
I
I
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.
5.6
35
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.
36
6.1
6.2
37
External procedure
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
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
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)
39
variable a
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.
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
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
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
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.
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
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
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)
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
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
6.11
45
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
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
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.
47
7.1
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)
After the execution of the commands, the content of the file data can be inspected
with an editor (for instance Xemacs).
7.2
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
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).
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
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
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
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
7.5.2
51
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
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.
7.5.4
52
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
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
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
7.5.9
53
54
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)
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)
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
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
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,
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
!
!
Array-Section
8.5
56
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
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
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:
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
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
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.
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
59
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
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:
!
! 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
!
! 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
10
62
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)
10.3
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
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
31 30
22
sign Exponent
double
63 62
sign
Mantisse
51
Exponent
0
Mantisse
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
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)
11
65
INTEGRATION OF FUNCTIONS
"
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)
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
(x 1) (x 2)
2
L1 (x) =
x (x 2)
1
L2 (x) =
x (x 1)
2
2
0
L0 (x)dx =
1
3
2
0
L1 (x)dx =
4
3
2
0
L2 (x)dx =
1
3
11
68
INTEGRATION OF FUNCTIONS
11.3
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
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)
M
1 Z x2i +2
X
i=0 x2i
(
M
1
X
f (x)dx
i=0
M
1
X
h
3
" 1
h MX
"
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)
"
(11.26)
11
70
INTEGRATION OF FUNCTIONS
11.5
Summary
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
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
(b a)h4 (4)
f (),
180
(11.34)
"
(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)
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
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
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
reject
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
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
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
14
14
14.1
76
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
(14.1)
(14.2)
(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
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)
1
90e2t + 99e7t + 13 sin(t) 9 cos(t) .
500
(14.6)
Example (BVP):
The differential equation,
y + 2 y = 0,
(14.7)
14.3
Y(a) = A.
(14.8)
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
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
78
(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 ),
(14.12)
(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
u0 = y(t0 ),
(14.14)
(14.15)
(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
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
(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
k = 2, 3, . . . .
(14.20)
(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
(14.25)
14
81
(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)
2 h 2 2 2
(2 f fyy + 21 2 f fty + 12 ftt )
2
(14.29)
(14.31)
6= 0.
(14.32)
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
82
= 1,
(
1, for 0 t 1,
f (t) =
0, for 1 t 2.
(14.34)
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
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
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
1
h=0.1
h=0.2
h=0.4
0.8
0.6
0.4
0.2
0
-0.2
10
15
20
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
14.8
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
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.