www.oerc.ox.ac.uk
An Introduction to Modern Fortran
Lecture 1
The Basics
Introduction
• In this course we will give an overview of Modern Fortran
• We would need much longer to cover all the syntax, but aim to give you enough
information and practice to start writing your own programs.
• We will guide you to further reading as we go.
Introduction
History of Fortran
• 1957 IBM Fortran.
• 1966 Fortran 66 standard.
• 1978 Fortran 77 standard.
• 1991 Fortran 90 standard. Attempted to modernise the Fortran language and to
bring it closer to modern programming techniques. It also added features to simplify
programming.
• 1997 Fortran 95 standard. Bug fixes and a small number of new features
• 2004 Fortran 2003 standard. Much bigger revision than Fortran 95. Features include
objected orientated programming, error handling and interoperability with C.
• 2009 Fortran 2008 standard. Co-arrays, BITS type, new intrinsics.
Books and Resources
• Lots of good books, find one that suits you.
Metcalf, Reid and Cohen “Modern Fortran” for example.
• man pages often available for Fortran keywords
• Google, of course
• These notes and the comments you make on them!
• Source code
– Readable.
– Changeable.
• Object code
– Intermediary step.
• Machine code
– Executable.
– Definitely not readable.
– Very difficult to change.
PROGRAM hello
IMPLICIT NONE
WRITE (*,*) ’hello world’
END PROGRAM hello
001010110101010101010101101010
010101011010101010101010101001
011010101101010101101010…
011010101111110101010101101010
010001011010101010101010101001
011000011001010101101010…
Programming And Compiled Languages
Compilation
Linking
From Source Code To Executable
Compile (with a compiler called “nagfor”):
nagfor –c hello.f90
Link (to other object code or libraries):
nagfor hello.o –o hello
Or we do both stages together:
nagfor hello.f90 –o hello
Note name of compiler and linker will vary from place to place. Check your local
documentation.
Compilers
• See what is available and recommended for your system.
• The NAG compiler is excellent and is second to none for error checking.
(Developing with more than one compiler is good practise.)
• You have a license for the NAG Compiler.
• The GNU gfortran is also good and free.
• The Intel compiler has a strange licence … but is probably available to you and
most likely creates the fastest executables – use for production!
• For comparisons: http://www.polyhedron.co.uk/compare0html
• Always read documentation for your compiler and pay particular attention to
defaults.
• Compliers DO NOT conform to a particular standard, may only have a subset of
one, or extensions to another.
• NAG compiler available at: http://www.nag.co.uk/nagware/np.asp
Compilers
Compilers are extremely useful.
• They can be used to check for incorrect code.
• (Will however allow code that gives wrong answers!)
• Help you debug your code.
• Make your code faster.
• Give you reports as to how well your code has been optimised.
• Always worth checking what flags are available
nagfor -C -C=undefined -dcfuns -g -gline -info -nan –u hello.f90
Writing Fortran
• Code is a sequence of instructions. These instructions are executed in order, unless
a “decision” in it changes this.
• Fortran codes start and end like this:
Program myprog
… instructions …
End Program myprog
• A lot of Fortran uses blocks that start with a keyword and end with END and that
keyword.
• All Fortran codes must have a main program. They may also use subroutines,
functions and modules
A Simple Program
Program hello
! My first program
Implicit None
Write( *, * ) 'Hello World !'
End Program hello
$ nagfor -o hello hello.f90
$ ./hello
Hello World !
Source Form
• All strictly standard Fortran programs can use only the 26 characters of the English
alphabet and the 10 Arabic numerals 0-9
• Upper and lower case are treated identically (except in character strings) Fortran is
NOT case sensitive.
• The following special characters: =+-*/()_,.$’:!”% &;<>? and space.
• We will use free source form Fortran (as opposed to fixed form, or Fortran 77, style
code.)
• In free source form lines may be up to 132 characters long, but can be broken over
multiple lines.
A More Complicated Program – Data Types
Program arithmetic
Implicit None
Integer :: i, j
Real :: a, b
i = 2 ! Set i to 2
j = 3 ! Set j to 3
a = 2.0 ! Set a to 2.0
b = 3.0 ! Set b to 3.0
i = i * j ! Do arithmetic the obvious way
a = a * b
Write( *, * ) i, a
End Program arithmetic
$ nagfor -o arithmetic arithmetic.f90
$ ./arithmetic
6 6.000000000
Intrinsic Data Types in Fortran
• All data, whether constants or variables, has a type in Fortran
• There are only 5 intrinsic types in Fortran
– Logical - Can only have the values .True. Or .False.
– Integer - Represents whole numbers, e.g. 12, -2
– Real - Represents floating point numbers, e.g. 25.7, -13.8 1e1, -2.3e-1
– Complex - A pair of reals representing a complex number, e.g. (1.0,2.0), i.e. 1+2i
– Character - Representing text, e.g. ‘C’, ‘Ian’
• Note the difference between a real and an integer constant – a real ALWAYS has a
decimal point AND/OR an exponent, and integer always has neither
• Very late on in the course we will see briefly how to create your own types
– But this belongs in a more advanced course really
Variables - Type
• A variable is associated symbol associated with a value that can change (or vary) in
your program
• For instance when solving an equation for x you might think of x as a variable
• Good practice – give a variable a name that indicates what it represents, e.g.
Volume, Density, Charge, Reynolds_Number
• And in Fortran all variables have a type
• And at the declaration or specification stage you say what type the variable has
• The specification stage MUST come before any executable (“doing”) statements
Example Specifications
• LHS of double colons ( :: ) is known as the types and attributes.
• RHS is known as the entity list (the list of variable names.)
• You can also initialise variables at the specification stage
– Note if you do not you can not make any assumption about the initial value of the variable
• Note if you do not have an intialisation strictly the double colons are not needed, but
I strongly recommend you always put them in as that simplifies remembering quite
when you do need them in more complicated cases we will see later
– Putting in the double colons is always write, leaving them out is often wrong
INTEGER :: a
INTEGER :: A=1, b=2, c
LOGICAL :: sym_matrix=.TRUE.
REAL :: f=12.1, m=-1000., p=12.34e10
REAL :: area_under_curve
COMPLEX :: plane_point=(1.0,1.0)
CHARACTER :: ch='a'
CHARACTER (LEN=8) :: string
Variable Names
Variable names
• Case is not important, i.e., the variable my_name is the same as My_Name
• Can have any alphanumeric letters (but must start with an alphabetic character.)
• Can be up to 31 characters long. (63 in F2003)
• Can use the underscore _ character.
Basic Mathematical Operators
• These are as you would expect:
= Assignment, i.e. ‘set to this value’
+ Addition
- Subtraction
* Multiplication
/ Division
** Exponentiation, i.e. ‘to the power of’
• Note that combining two pieces of the data type results in a value of the same type
• Combining data of different types? First convert the “less general” to the more
general type, i.e. integer gets converted to real, and real to complex, and then
perform the operation giving the type of the result as the more general one
A More Complicated Program Revisited
Program arithmetic
Implicit None
Integer :: i, j ! Declare two Integers
Real :: a, b ! Declare two Reals
i = 2 ! Set Integer i to the Integer constant 2
j = 3 ! Set Integer j to the Integer constant 3
a = 2.0 ! Set Real a to the Real constant 2.0
b = 3.0 ! Set Real b to the Real constant 3.0
i = i * j ! Multiplying two Integers gives an Integer
a = a * b ! Multiplying two Reals gives a Real
Write( *, * ) i, a ! Write out an Integer and a Real
End Program arithmetic
$ nagfor -o arithmetic arithmetic.f90
$ ./arithmetic
6 6.000000000 Note the difference – An Integer then a Real
More Complicated Arithmetic - Precedence
i = 3
j = 2
i = 3 * i + 2 * j
• Sets i to 13, i.e. it evaluates as (3*i)+(2*j). The order of evaluation is determined by
‘precedence,’
• () first, then **, then * /, then + -
• Much the same as maths. What if you can’t remember or it’s not clear?
i = j ** 3 * k
• Then use brackets
i = ( j ** 3 ) * k or
i = j ** ( 3 * k ) as required
Integer Division
• Remember integers represent WHOLE NUMBERS.
• And arithmetic combining two integers gives an integer
• So what is the result of
10 / 4 = ?
• The method is to throw away the remainder (equivalent to round toward zero) . So
10 / 4 = 2 remainder 2, so
10 / 4 = 2
• Similarly
1 / 4 = 0
-10 / 4 = -2
-33 / -16= 2
Integer Division – An Easy Bug
Program temperature_convert
Implicit None
Integer :: fahrenheit, celsius
Read( *, * ) fahrenheit
! N.B. NOT 5 / 9 * ...
celsius = 5 * ( fahrenheit - 32 ) / 9
Write( *, * ) celsius
End program temperature_convert
Or you could do it using Reals (probably better here)
Somebody WILL make this mistake in the practicals
A Reminder - Constants have a Data Type
• Before we move away from the basic types a reminder – Constants always have a
Data Type
• So on the previous slide 5 and 9 are integers
– They didn’t have a decimal point or an exponent
• Similarly, the following are integer, real and complex respectively:
1 -99 0 +2
1. -0.1 1e+1 -12.3e-11
(1.09,2.3) (2.0, 1e+1)
• Get used to this - it becomes important when we come on the passing arguments to
subroutines and functions
Implicit None
• Before the declarations you should use the keywords “IMPLICIT NONE”.
• This turns off the implicit typing the variables
– It is possible to use a variable without previously declaring its type and attributes.
– In other languages this is illegal, not in Fortran.
– If we do this the type of a variable is judged by implicit rules.
– It is not a good idea to rely on these rules because of spelling mistakes and to ensure we
have good control over our code.
– Years of experience since Fortran was invented has shown that implicit typing is NOT a
good idea
• Using IMPLICIT NONE avoids this and ensures we have to declare the type of each
variable.
– A little bit more typing up front for a lot less grief later on …
• If you don’t use and are having problems Implicit None I won’t help you until you fix
it!
Source Form – Use of !, The Comment Character
• Whenever the ! character is deployed then the rest of the line is to be ignored by the
compiler.
• These can then be used as comments to describe what important variables are for,
what procedures are for, reminders, what parts of procedures are doing etc.
• Therefore they are extremely important to future users/developers of programs to
understand what you have done and so are highly recommended.
Source Form – Use of &, The Continuation Character
• The “&” symbol is used as the last symbol on a line when the code on the following
line should be appended to it:
area_of_rectangle = height_of_rectangle * &
width_of_rectangle
• If this happens in the middle of a string then an & needs to start the next line to
show where the string starts.
WRITE(*,*) ‘hello &
&world’
hello world
• Useful if lines become very long (remember you are limited to 132 characters per
line)
• Can also make the code more readable, certain expressions look better if
“tabulated”
– Indentation with white space can also help here
Data of Type Character
• So far we have looked at numeric Data Types
• Now lets look at constants and variables of Type Character
• These represent strings of letters
• These strings have a fixed length
– By default 1 character
• And Character constants can be delimited by either ‘ or “ :
Character( Len = 8 ) :: name
name = 'Ian Bush'
Or
name = "Ian Bush"
• Remember character variables have a fixed length
• So …
Program hello2
Implicit None
Character( Len = 8 ) :: greeting
greeting = 'Hello Ian !'
Write( *, * ) greeting
End Program hello2
$ nagfor -o hello2 hello2.f90
$ ./hello2
Hello Ia
Data of Type Character
Operations on Data of Type Character
• Only two operations are allowed on characters
• Concatenation through the // operator:
Character( Len = 8 ) :: full_name
Character( Len = 3 ) :: first_name = ‘Ian’
Character( Len = 4 ) :: last_name = ‘Bush’
full_name = first_name // ‘ ‘ // last_name
Write( *, * ) full_name
Ian Bush
• Substrings
Character( Len = 3 ) :: first_name = ‘Ian’
Write( *, * ) first_name( 1:2 ), first_name( 2: ), first_name( : )
Ia an Ian
Program hello3
Implicit None
Character( Len = 6 ) :: greeting
Character( Len = 6 ) :: name
greeting = 'Hello '
name = 'Cheryl'
Write( *, * ) greeting // name
Write( *, * ) greeting // name( 2:4 )
End Program hello3
$ nagfor -o hello3 hello3.f90
$ ./hello3
Hello Cheryl
Hello her
Character Operations
• Parameters are very useful for code readability – can you remember what value
Euler’s constant takes ?
• Further something like mat_dim is likely to occur many times in a code – much
easier to change in one place than search for every occurrence of 500. And …
Integer , Parameter :: mat_dim = 500
Real , Parameter :: pi = 3.1415927
Real , Parameter :: euler = 0.577216
Character( Len = 2 ), Parameter :: &
mat_type = 'HE'
Symbolic Constants – “Parameters”
• Parameters are constants, you can’t change the value.
Program wrong_params
Implicit None
Integer, Parameter :: mat_dim = 500
mat_dim = mat_dim + 50 ! WRONG – the
! compiler will complain!
End Program wrong_params
Symbolic Constants – “Parameters”
Intrinsic Subprograms
• Fortran has lots of intrinsic subprograms to do useful things for you, like common
mathematical operations.
• These built in routines are called Intrinsics.
• They are either functions that return a result or subroutines that give the result
through one or more if its arguments.
• We will explain these terms in more detail later.
• Some examples ...
Sin( x ) The sine of x (all angles are in radians)
Cos( x ) The cosine of x
Tan( x ) The tangent of x
Asin( x ) The arcsine of x
Acos( x ) The arccosine of x
Atan( x ) The arctangent of x
Exp( x ) The exponent of x
Log( x ) The natural logarithm of x
Sqrt( x ) The square root of x
• Some mathematical functions:
Intrinsic Subprograms
Program trig
Implicit None
Real, Parameter :: pi = 3.1415927
Real :: x
Write( *, * ) Sin( pi ), Cos( pi )
x = Exp( pi )
Write( *, * ) x
End Program trig
$ nagfor -o trig trig.f90
$ ./trig
-0.8742277657E-07 -1.000000000 ! 0 and -1
23.14069557
Trigonometric Functions
Type of The Return Value
Program trig2
Implicit None
Write( *, * ) Sin( 3.1 )
Write( *, * ) Sin( ( 3.1, 0.0 ) )
End Program trig2
$ f90 -o trig2 trig2.f90
$ ./trig2
0.04158076
(0.04158076,0.0E+0)
• Return type is same as the argument
• Similar to multiplying two reals gives a real – you don’t change the type of entities
without being told to do so in Fortran
• So ...
Return Type
Program root
Implicit None
Write( *, * ) Sqrt( -4.0 )
Write( *, * ) Sqrt( ( -4.0, 0.0 ) )
End Program root
$ ifort -o root root.f90
$ ./root
NaN
(0.0000000E+00,2.000000)
Not all compilers are equal!
Program root
Implicit None
Write( *, * ) Sqrt( -4.0 )
Write( *, * ) Sqrt( ( -4.0, 0.0 ) )
End Program root
$ gfortran -o root root.f90
root.f90:3.22:
Write( *, * ) Sqrt( -4.0 )
1
Error: Argument of SQRT at (1) has a negative value
Converting from one Data Type to Another
• We can convert from one type to another:
Int( x ) Convert to integer, round towards zero
Nint( x ) Convert to integer, round to nearest
Real( x ) Convert to real
Cmplx( x ) Convert to complex, imag part = 0
Cmplx( x, y ) Convert to complex
Other Maths Functions
• A few other useful maths functions:
Abs( x ) Absolute value of x
Max( x1, x2, ... ) Maximum
Min( x1, x2, ... ) Minimum
Mod( x, y ) Remainder when x is divided by y
Note that for Max and Min all of the arguments must be of the same type
• If we mixed the types how could we decide upon the type of the returned
value?
Program conv
Implicit None
Real, Parameter :: pi = 3.1415927
Write( *, * ) Int( pi ), &
Real( Int( pi ) ), &
Exp( Cmplx( 0.0, -pi ) )
End Program conv
$ nagfor -o conv conv.f90
$ ./conv
3 3.000000000 ( -1.000000000, -0.8742277657E-07 )
Type conversions
43
Intrinsic Subroutines
• All the intrinsics we have seen so far are functions. There are also intrinsic
subroutines. These are accessed with the call keyword, e.g. for Random_number
Call Random_number( x )
• Returns a random number between 0 and 1 in x. Where x must be of real type.
• Subroutines are very important. We will learn a lot about them in later lectures.
Basic I/O
• We’ve covered some basic input and output, or I/O
Write( *, * ) a, b
Writes a and b to the screen (standard output in Unix terms)
• To read data items in from the keyboard (or standard input) use
Read( *, * ) a, b
• More advance I/O e.g.
– Files
– Making it “pretty”
• Is covered in later lecture notes
Summary of the Basics
• Basic program structure
• Data types
• Constants and Variables
• Basic mathematical operations
• IMPLICIT NONE – always!
• Using comments – often and meaningful
• Some Intrinsics
• Simple I/O
Selection And Repetition
if(a > 0.0)then
log_a=log(a)
else
write(*,*)’Error: a is not positive’
end if
• Much of computing involves the algorithm making a decision, e.g. is this input correct? Is the
error term small enough? Has the algorithm converged?
Selection
IF (logical expression) then
statement
statement
ELSE
statement
END IF
• Fortran provides a data type to help in making decisions: LOGICAL
• Let’s look at Logical data and Logical expressions in some detail first ….
Selection
Logical Variables
• Logical variables can have two values:
– .True.
– .False.
• They are declared as expected
Logical :: error, converged
• They can also have attributes
Logical, Parameter :: debug = .True.
Logical Variables
• Initialization is as expected:
Logical :: verbose = .False.
• Assignment is as expected – they are just a normal Fortran type:
Logical :: converged
Converged = .True.
Program logical_output
Implicit None
Logical :: yes = .True.
Logical :: no = .False.
Write( *, * ) yes, no
End Program logical_output
$ nagfor –o logical_output
logical_output.f90
$ ./logical_output
T F
• Input and output as usual:
write(*,*) converged
• We use T and F for input and output:
Logical variables
n_iter < max_iter
error_flag /= success
’samba’ < ’tiger’
’Oxford’ <= ’Oxfordshire’
• Relational operators:
.eq. == equal to
.ne. /= not equal to
.gt. > greater than
.ge. >= greater than or equal to
.lt. < less than
.le. <= less than or equal to
Logical variables
So we can say:
Logical :: carry_on
carry_on = n_iter < max_iter
Sets carry_on to .true. if n_iter is less than max_iter, .false. otherwise
Logical :: worked
worked = error_flag == success
Sets worked to .true. if error_flag is equal to success, .false. otherwise
Logical Expressions
• Now we've generated our logicals how do we combine them?
• With Logical Operators
.not.
.and.
.or. (inclusive or)
.eqv.
.neqv. (exclusive or)
• Act as one might expect from Boolean algebra.
Logical Expressions
LOGICAL :: log1=.TRUE.
LOGICAL :: log2=.FALSE.
Logical :: log3, log4, lo5, log6
INTEGER :: a=1
log3= .NOT.log1 Evaluates to .FALSE.
log4= log1 .AND. log2 Evaluates to .FALSE.
log5= log1 .OR. log2 Evaluates to .TRUE.
log6= a==2 Evaluates to .FALSE.
Logical Expressions
finished = result < tol .or. &
n_iter > max_iter .or. &
n_iter <= max_iter .and. &
time_taken > max_time
• Long logical expressions frequently get confusing:
• Use brackets for readability and so not to worry about precedence.
Logical Expressions
IF (logical expression) then
statement
statement
ELSE
statement
END IF
• IF Blocks
Conditional Execution
if(a > 0.0)then
log_a=log(a)
else
write(*,*)’Error: a is not positive’
end if
Note indenting - will make code
much easier to read!
Conditional Execution
IF (logical expression) then
statement
if(logical expression) then
statement
else
statement
end if
ELSE
statement
END IF
Which else refers to which if ? See how indenting helps
• Nest IF blocks
Conditional Execution
if (logical expression 1) then
statement
else if(logical expression 2) then
statement
else if(logical expression 3) then
statement
else
statement
end if
Conditional Execution
• Multiple tests with “else if”
• Only one statement can be executed
If( x > 0.0 ) Then
Write( *, * ) ’Positive’
Else If( x == 0.0 ) Then
Write( *, * ) ’Zero’
Else
Write( *, * ) ’Negative’
End If
Conditional Execution
• One use of conditional statements is to detect errors.
• These may be correctable but may not be…
• The command to use for uncorrectable errors is: Stop
• This causes execution of the code to terminate
• Some possible forms of stop:
Stop
Stop ’Invalid Input’
If( error ) Then
Stop
End If
Stop
DO counter = initial, final[, step]
statements
END DO
• By default, i.e. if absent, step is 1 – by far the most common form in practice
Repetitive Execution
program steps_of_one
implicit none
integer :: count
do count = 1, 5
write(*,*) count, count*count
end do
end program steps_of_one
$ ./steps_of_one
1 1
2 4
3 9
4 16
5 25
• Remember no step means step=1 – most important form
Repetitive Execution
program repeating
implicit none
integer :: count
do count = -3, 4, 2
write(*,*) count, count*count
end do
end program repeating
$ nagfor –o rep rep.f90
$ ./rep
-3 9
-1 1
1 1
3 9
Repetitive Execution
Integer :: i, n_in, total, value
Real :: average
Read( *, * ) n_in
total = 0 ! Initialize
Do i = 1, n_in ! Default is step = 1
Read( *, * ) value
total = total + value
End Do
average = Real( total ) / n_in ! Remember integer
! division
Repetitive Execution
INTEGER :: a, b, c
DO a = b, c, 3
READ(*,*) a
a = b-c
END DO
• Do not change the value of the counter. The following is ILLEGAL:
• Likewise don’t change limits or stride.
• Note the stride can be negative, so you can “count down”.
• But it can’t be zero.
Repetitive Execution
Integer :: i, j
Do i = 1, 12
Write( *, * ) ’Multiplication ’, &
’table for ’, i
Do j = 1, 12
Write( *, * ) j, ’ * ’, i, ’ = ’, j * i
End Do
Write( *, * ) ! Write a blank line
End Do
Nested Do Loops
Do i = 1, 10
If( Mod( i, 2 ) == 0 ) Then
Write( *, * ) i, ’ is even’
Else
Write( *, * ) i, ’ is odd’
End If
End Do
Nested Block Structures
• More generally can nest any block structures:
DO
statements
END DO
• There are other forms of the do loop
• Be careful - this is an infinite loop so we will need an exit condition
Repetitive Execution
Two useful commands are exit and cycle
exit exits the do loop
cycle cycles the do loop i.e. go onto the next iteration
Note not restricted to infinite do loops
Perfectly OK in “counting” do loops
Repetitive Execution
REAL :: x, x_sq, x_cube
DO
READ(*,*) x
IF( x < 0.0 ) EXIT
x_sq = x * x
x_cube = x_sq * x
WRITE(*,*)’ value = ’, x
WRITE(*,*)’ square = ’, x_sq
WRITE(*,*)’ cube = ’, x_cube
END DO
Repetitive Execution
DO i = 1, n
DO
If( error ) Exit ! Which loop is exited?
END DO
END DO
• With multiple nested DO loops you will need to be careful which loop you
EXIT/CYCLE:
• By default loop which exit/cycle is directly in is acted upon
Repetitive Execution
Input_loop:Do
…
If( error ) Exit Input_loop
…
If( end_of_line ) Cycle Input_loop
…
End Do Input_loop
Named Do Loops
• Can name any sort of do loop.
• Can also name IF blocks and case statements (less useful in practice)
Outer: DO i = 1, n
Inner: DO
if(condition)then
exit Outer
end if
END DO Inner
END DO Outer
Named Do Loops
Evaluate exp(x) at points from x_init to x_final with a step size of x_step. Use a
power series expansion to converge to within a given tolerance of the intrinsic exp
function. Evaluate the number of terms needed for convergence.
exp(x) = 1 + x/1! + x2/2! + x3/3! + ...
Note: The ith term is the (i-1)th term * x/i
A complete example
READ USER INPUTDO X
IS X IN RANGE?SET INITIAL TERMDO TERMS
IS TERM < TOLERANCE?SUM TERMSGENERATE NEXT TERM
END DO TERMSOUTPUT VALUESINCREMENT X
END DO
A complete example
PROGRAM Exponential
IMPLICIT NONE
INTEGER :: Counter
REAL :: Begin, r_End, Step, X, Tolerance, ExpX, Term, Sums
WRITE(*,*) ’Input Initial, Final, Step and Tol'
READ(*,*) Begin, r_End, Step, Tolerance
X = Begin
DO ! Loop over x values
IF (X > r_End) EXIT
ExpX = EXP(X)
Counter = 1
Term = X
Sums = 1.0
DO ! Do Loop over terms
IF (ABS(Term) < Tolerance) EXIT
Sums = Sums + Term
Counter = Counter + 1
Term = Term * (X / Counter)
END DO
WRITE(*,*) X, ExpX, Sums, Counter
X = X + Step
END DO
END PROGRAM Exponential
A complete example
Summary
• Logical variables can take two states - .TRUE. and .FALSE.
• Condition execution is performed using IF - THEN – ELSE IF - ELSE - END IF
• IF - THEN - ELSE IF can be replaced by SELECT - CASE in some cases
• Use STOP command to terminate a program
• Repetitive execution via DO - END DO construct
• Can also use DO WHILE construct
• CYCLE and EXIT allow control of loops
Further Reading
• Fortran has other selection constructs
– E.g. look at Case – used for comparison against a known possible set of results
• Look at the Fortran intrinsic routines
• These include ones for manipulating strings
• Look at how to compare characters, in particular are number “less than” letters?
Lecture 2
Kind
Kind
• We have seen all constants and variables have a type
– So far one of Integer, Real, Complex, Character, Logical
• Now we shall see all constants and variables of intrinsic type also have a kind
• Consider Integers
– There are an infinite number of Integers
– So to be able to represent all integers on a computer would take an infinite amount of
memory
– We can’t do that
– So the integer type only represents a subset
– Which subset is defined by the kind of the Integer
– And a compiler may support more than one kind
– Essentially the kind specifies the range of the integer
• All Integers are signed in Fortran
• Reals are similar but now the kind specifies the range and the precision
Specifying a Kind
• So far we have only used the default kind for variables
• How do we go about specifying non-default kinds
– Especially important for reals and complex, in computational science you will rarely want to
use the default kind
– It usually only has 6 or 7 significant figures which is almost always not enough for
computational science
Specifying a kind
• Suppose we have a symbolic constant (parameter) wp
• Then we can specify a variable of kind wp by either
REAL (KIND=wp) :: val4
REAL (wp) :: val4
• i.e. the kind = is optional
• Constants of a given kind are specified by
val4 = 1.0_wp
• Remember the kind value MUST be a constant
– i.e. a parameter (much, much, the preferred method)
– Or a literal constant (BAD PRACTICE – not all compilers support the same values for kind
parameters)
• But how do we choose a suitable kind value for each type we want to employ?
Selecting the Real KIND Value
• We can use the intrinsic function SELECTED_REAL_KIND.
• To ask for a kind value for a real with at least 12 decimal digits of precision.
wp = SELECTED_REAL_KIND(12)
• To make sure it also has an exponent range of at least 70.
wp = SELECTED_REAL_KIND(12,70)
• Thus to declare a variable of this type and kind
REAL (wp) :: my_val
• And to specify a constant of a given type and kind
REAL (wp) :: my_val
• Function returns -1 if the precision is unavailable, -2 if exponent range unavailable
and -3 for both.
• Standard says at least 2 real kinds must be supported
– Universally IEEE single and IEEE double are supported
– More may be available
Selecting the Complex Kind Value
• For complex also use selected_real_kind
– Range and precision refer to the real and imaginary parts
• Thus to declare a complex variable where both parts have a precision of at least 12
and have a range of at least 70 we can reuse wp from before and simply say
Complex(wp) :: my_val
• Standard says that for every real kind supported there must be a corresponding
complex
– So at least 2 complex kinds supported
– (Unlike F77)
Selecting The Integer Kind Value
• To ask for an integer type that covers at least the range -1*1012 < x < 1012
ikind_12 = SELECTED_INT_KIND(12)
• Thus to declare a variable of this type
INTEGER (KIND=ikind_12) :: my_val
• And to assign a constant of type integer and kind ikind_12 to it
my_val = 4_ikind_12
• Returns -1 if no corresponding kind is available
• At least 1 Integer kind must be available
– 2 in F2008
– Again more may be supported by a given compiler
Kind for Character and Logical
• You can also use kind with character – used to specify different character sets e.g.
For Greek or Kanji
– But only one set required to be supported
• You can also have kinds for Logical variables
– I have never used this!
KIND Selection: Summary
• Real In computational science it’s almost always right to use what's
returned by Selected_real_kind(12,70), that is IEEE double
• Complex As Real
• Integer Almost always use the default – some exceptions but fairly rare
• Character Use the default
• Logical Use the default
• Looking ahead – store your kind values in a module
Simple Kind Example
Program kind_example
! ( 12, 70 ) This is what you usually want
Integer, Parameter :: wp = selected_real_kind( 12, 70 )
Real :: da
Real( wp ) :: wa
da = 2.0
wa = 2.0_wp
Write( *, * ) da, wa
! Note Intrinsics return same type AND KIND
Write( *, * ) Sqrt( da ), Sqrt( wa )
End Program kind_example
$ f90 -o kind kind.f90
$ ./kind
2.0000000 2.0000000000000000
1.4142135 1.4142135623730951
Just To Make A Point
Program kind_example
! Only this line has changed
Integer, Parameter :: wp = selected_real_kind( 6, 35 )
Real :: da
Real( wp ) :: wa
da = 2.0
wa = 2.0_wp
Write( *, * ) da, wa
Write( *, * ) Sqrt( da ), Sqrt( wa )
End Program kind_example
$ f90 -o kind kind.f90
$ ./kind
2.0000000 2.0000000
1.4142135 1.4142135
Get Your Constants Right
Program conv
Implicit None
Integer, Parameter :: wp = Selected_real_kind( 12, 70 )
Real( wp ) :: wa, wb
Real :: da, db
da = 0.3333333333333333333
db = 0.3333333333333333333_wp
wa = 0.3333333333333333333
wb = 0.3333333333333333333_wp
Write( *, * ) 'Default real assigned to default real: ', da
Write( *, * ) 'Default real assigned to working real: ', db
Write( *, * ) 'Working real assigned to default real: ', wa
Write( *, * ) 'Working real assigned to working real: ', wb
End Program conv
> gfortran -std=f95 conv.f90
> ./a.out
Default real assigned to default real: 0.33333334
Default real assigned to working real: 0.33333334
Working real assigned to default real: 0.33333334326744080
Working real assigned to working real: 0.33333333333333331
A more general rule
• This is an instance of the more general rule about assignments and expression
evaluation in Fortran
• Never look at the LHS of the = before you have finished evaluating the RHS. Once
you have finished the evaluation then look and perform any type AND KIND
conversions as required
– Just because you are assigning to a double won't make the expression a double, you have
to specify all the kinds in the RHS carefully yourself – the earlier integer example didn't
magically turn itself into a real
• Even more generally – don’t assume any context for the evaluation of any given part
of an expression, just do exactly as it says
– A given expression always returns the same result
Integer, Parameter :: wp = selected_real_kind( 12, 70 )
Real :: a
Real :: b
Integer :: I
a = 4.0_wp * ( i / 3 + b * 2 )
The Kind Intrinsic function
• We can retrieve the kind of a number:
real :: a
write(*,*) kind(a)
• Will print out the integer value of the real kind.
Type and Kind Conversion
Integer, Parameter :: float = selected_real_kind( 12, 70 )
Integer :: i
Real (float) :: a
a = real(i) ! Convert to default kind, probably will lose precision
Integer, Parameter :: float = selected_real_kind( 12, 70 )
Integer :: i
Real (float) :: a
a = real(i,float) ! OK
a = real(i,kind(a)) ! Better!
Summary
• ALL variables and constants in Fortran have a type and kind
– Remember constants – a very common bug!
• The kind specifies the range and, for reals, the precision of the variable and
constant
• You use a constant to specify the kind
– Preferably a symbolic constant (parameter)
• Choose the kind value using the selected_*_kind intrinsic
– I suggest you use 12, 70 for reals (and complex)
– And default kinds for all the rest
• Except in one or two specialised cases for integers, typically involved with I/O when you may need very large integers when dealing with very large files
• Remember to specify the kind when converting the type
– A very common bug!
• Remember in Fortran all parts of the expression are evaluated in isolation
– There is never any context for an expression , a given expression always returns the same
result
Arrays
Arrays
• An array is a collection of data of the same type and kind. Array elements are
indexed or subscripted, just like x1, x2, ..., xn in mathematics
• Arrays are tables of data
• Arrays can be real, integer, logical, complex etc
Arrays
• Array has the following important components:
– A name (is a variable)
– A type: this is the type of all the array elements.
– A kind: this is the kind of all the array elements
– A rank: the number of dimensions
– A set of extents: The number of elements in each dimension
– A set of bounds: The range of valid subscripts for each dimensions
• Note scalars, i.e. what we have looked at so far, have rank zero.
• This means that all constants and variables have a type, kind and rank (TKR)
– Though we won’t really touch much on constant arrays here but you can have them
Arrays
• The syntax for declaring a one dimensional array is the type as normal followed by
the DIMENSION attribute with the extent in brackets. For example:
Real( wp ), dimension(10) :: x
• Here we have an array of type real, kind wp and rank 1 with extent 10.
– That is, x has space to store 10 real numbers.
• An alternative which some people prefer:
Real( wp ) :: x( 10 )
Arrays
• Multi-dimensional arrays are a simple extension;
Real( wp ), dimension(10,10) :: x
• Elements of the array are referenced within brackets
integer :: j
integer, dimension(100) :: iwork
do j = 1, 100
iwork(j) = 0 ! initialise elements to zero
end do
1 2 3 4 5
5
4
3
2
1
• By default starts at 1.
– The lower bound is 1 and the upper bound is that specified
• But we can choose other bounds.
• We can specify an lower and upper limit for the indices in the array.
• The picture shows an array declared as:
REAL, DIMENSION(5,5)
REAL, DIMENSION(1:5,1:5)
Arrays Indexing
• We can declare extents like this:
REAL, DIMENSION(5,-2:2) :: a
-2 1 0 1 2
5
4
3
2
1
Arrays Indexing
Array Indexing – Out of Bounds
• Note it is always illegal to use an array index outside of the declared bounds
Integer, Dimension( 1:8 ) :: a
a( 3 ) = 1 ! OK
a( 8 ) = 2 ! Still OK
a( 9 ) = 3 ! WRONG
• Commonly called an “out of bounds” array access – possibly THE most common
error in Fortran programming
Out of Bounds – Use of the Compiler
• In Fortran all good compilers can detect out of bounds array accesses
– Check your documentation
Program bounds
Implicit None
Real, Dimension( 1:5 ) :: a
Integer :: i
Do i = 1, 6
a( i ) = i
End Do
Write(*,*) a
End Program bounds
> nagfor –w=all –C=all t.f90 ! Enable NAG warnings and run time checks (-C=array)
> ./a.out
Runtime Error: t.f95(6) : Subscript 1 of A (value 6) is out of range (1:5)
Out of Bounds – Use of the Compiler
• When developing USE THIS, It will save months of your life!
– nagfor –C=all …
– gfortran –fcheck=all …
– ifort –check all
• More generally learn what your compiler can do for you
• And use more than one compiler
– Not all compilers detect the same things
– But the NAG one is very good for debugging
• Intel is likely better for production though
• gfortran is superb value for money!
• But turn these debugging flags off for production as it slows down your program!
Arrays
• The integers in an extent or bounds can be symbolic constants (parameters):
INTEGER, PARAMETER :: MaximumSize = 100
LOGICAL, DIMENSION( MaximumSize ) :: test
INTEGER, PARAMETER :: LowerB = -10
INTEGER, PARAMETER :: UpperB = 10
REAL, DIMENSION( LowerB:UpperB ) :: Score
Array Inquiry Functions
• You can also enquire properties of the array
• Not so useful now but when we start writing our own subprograms they become very useful!
• Some useful ones are
Integer, Dimension( 0:1, 2:4 ) :: a
Size( a ) returns the total number of elements in a, i.e. 6
Size( a, Dim = 1 ) returns the extent of the given dimension of a, i.e. 2
Lbound( a ) returns an array containing the lower bounds of a, i.e. 0, 2
Lbound( a, Dim = 2 ) returns the lower bound of the given dimension of a, i.e. 2
Ubound( a ) as Lbound but deals with upper rather than lower bounds
A(1,1) A(2,1) A(3,1) etc
Storage of Arrays in Memory
REAL, DIMENSION(3,2) :: a
• Left most index varies most rapidly
• Storage is “column major” order
• This is not a course on optimisation
• However the number 1 tip for good performance in Fortran is that accessing an
array in the order it is stored makes for more efficient code
• So get used to writing loops so the inner most loop goes over the first index where
possible
– Different from C!
Do i = 1, n
Do j = 1, n
a( j, i ) = i + j
End Do
End Do
Storage
Array Expressions and Elemental Operations
• So far we have only looked at scalar expressions
• Fortran allows array expressions as well
• The operations are applied element by element to the array to give an array result
– These are called elemental operations
• Obviously all the arrays must have the same extent
– Must be conformable
– But note don’t need to have the same bounds
• So the following are equivalent
Integer, Parameter :: n
Real, Dimension( 1:n ) :: a, b, c
Integer :: i
…
Do i = 1, n
a( i ) = b( i ) + 2.0 * c( i )
End Do
a = b + 2.0 * c ! Equivalent to the loops above
Multiple Dimensions
• The above extends naturally to multiple dimensions
Integer, Parameter :: wp = Selected_real_kind( 12, 70 )
Integer, Parameter :: m = 50
Integer, Parameter :: n = 35
Real( wp ), Dimension( 1:m, 1:n ) :: a, b, c
a = 5.0_wp * b + 3.0_wp * c
Array Expressions and Scalars
• Note how the scalars in the previous example “filled out” to apply to the whole array
• This leads to some very convenient ways of initialising arrays:
Integer, Parameter :: wp = Selected_real_kind( 12, 70 )
Integer, Parameter :: m = 20
Integer, Parameter :: n = 65
Real( wp ), Dimension( 1:m, 1:n ) :: a, b, c
a = 0.0_wp ! Set all of a to 0.0_wp
b = 2.0_wp ! Set all of b to 2.0_wp
c = 3.0_wp ! Set all of c to 3.0_wp
Array Expression and Intrinsic Functions
• We can even do the following if the intrinsic is an elemental function
Integer, Parameter :: wp = Selected_real_kind( 12, 70 )
Integer, Parameter :: m = 90
Integer, Parameter :: n = 45
Real( wp ), Dimension( 1:m, 1:n ) :: a, b
a = Sqrt( b ) ! Set all of a to the sqrt of b
Accessing Part of An Array
• So far we have only used array expression where the whole array is accessed
• However Fortran also permits array sections where you only access part of the
array
• Referencing a complete row:
– A(3,1:5)
– The 3 indicates the 3rd row.
– The 1 indicates the lowest index and
5 the highest.
• Or equivalently:
– A(3,:)
– The colon on its own picks up the default values for the lowest & highest indices.
• Equally:
– A(3,1:5:1) ! NOT like MATLAB!
– first : last : increment
Accessing Array Sections
• Here we use the 3rd value separated by another colon to indicate a stride.
– a(1::2, 2:4)
• Or:
– a(1:5:2, 2:4:1)
Accessing Array Sections – Multiple Dimensions
Array Sections
• So we can do things like
Integer, Dimension( 1:100 ) :: a
Integer, Dimension( 1:50 ) :: b
a( 1:10 ) = 0 ! Set the first 10 elements of a to 0
a( 11::2 ) = 1 ! Set the remaining odd numbered elements to 1
a( 12::2 ) = 2 ! And set the remaining even numbered elements to 2
b = a( 1:50 ) + a( 51:100 ) ! Note the extents are the same
Masked Elemental Operations – Where
1.0 -2.0 -2.0 6.0
5.0 3.0 4.0
2.0 2.0 -2.0 3.0
0.0 -3.0 0.0
ra rb
REAL, DIMENSION(4,4) :: ra, rb
WHERE (rb > 0.0)
ra = ra/rb
ELSEWHERE (rb < 0.0)
ra = ra/-rb
ELSEWHERE
ra = 0
END WHERE
0.5 -1.0 -1.0 2.0
0.0 1.0 0.0
ra after• Each element in “ra” will be divided by
the absolute value of the corresponding element in “rb”, unless that is zero where the element in “ra” will be set to zero.
Array Reduction Functions
• So far we have looked at elemental operations
• Sometimes we want a reduction operation
• This takes a whole array and reduces it to a single value
– E.g. The sum of the array values, the maximum value …
• Some useful ones in Fortran
Real, Dimension( 1:20 ) :: a
Real :: a_max, a_min, a_av_odd
a_max = Maxval( a )
a_min = Minval( a )
a_av_odd = Sum( a( 1::2 ) ) / Size( a( 1::2 ) )
Logical Array Reduction Functions
• The functions any, all and count apply to logical arrays
• Occasionally useful for things like
Real, Dimension( 1:1000 ) :: a, b
If( all( a >= 0.0 ) ) Then
b = Sqrt( a )
Else
Write( *, * ) ”Error: Some of a are negative so can’t take the sqrt”
End If
real, dimension(10) :: x
integer :: i
do i = 1,10
x(i) = real(i)
end do
write(*,*)x
$ nagfor -o array.f90
$ ./a.out
1.000000 2.000000 3.000000 4.000000
5.000000 6.000000 7.000000 8.000000
9.000000 10.00000
Array I/O
• Another elemental operation Fortran allows is array I/O
• Output:
INTEGER, DIMENSION(1:10) :: x
INTEGER :: n, i
READ(*,*) n ! Hope not > 10
DO i = 1, n
READ(*,*) x(i)
END DO
INTEGER, DIMENSION(1:10) :: x
INTEGER :: n, i
READ(*,*) n
READ(*,*) x
Expects hard return between inputs, at each read.
Can input values one line, space or comma separated.
• Input is a little more complicated, we’ll find out why the following are different later
Array I/O
Input #1 Input #2
-------- --------
3 3
10 10 30
30 40
40
INTEGER,DIMENSION(1:10) :: x
INTEGER :: n, I
READ(*,*) n
READ(*,*) x
Array I/O
Multidimensional Array I/O
• Multidimensional array I/O is performed in storage order
• “Down the columns first”
Allocatable Arrays
• Sometimes we don’t know before we run the program what size our arrays need to be
• Consider this piece of code below.
• We have decided in advance that the array x will hold at most 10 elements.
• But this is not practical for large arrays, particularly if the amount of memory available
is an issue. And what if we the user types 11?
• We would like to delay the decision until we know how much storage is actually
required.
real, dimension(10) :: x
integer :: n,i
read(*,*)n
do i = 1, n
read(*,*)x(i)
end do
Allocatable Arrays
• We can allocate memory for arrays as required.
• Still need to declare the number of dimensions at the specification stage.
• The attribute required is ALLOCATABLE.
• At some stage we need to allocate space for this array. This is done via the
ALLOCATE command
• Another benefit of allocatable arrays is that they reduce the overall storage
requirement as we can free up the memory after we have finished with it so that it
can be used for something else.
Allocatable Arrays
real, dimension(:), allocatable :: x
integer :: n, i, alloc_error
read(*,*)n
allocate(x(n), stat=alloc_error) ! stat optional
if(alloc_error.ne.0)then
stop ’Error during allocation’
end if
do i = 1, n
read(*,*) x(i)
end do
Deallocation
real, dimension(:,:),allocatable :: x ! rank 2
integer :: n,i,alloc_error
read(*,*)n
allocate(x(n,n), stat=alloc_error)
...
...
deallocate(x)
Summary
• Arrays
– Ranks, Extents and Bounds
– Subscripting
– Use of the compiler to detect array problems
• Array storage
• Elemental operations and the Where construct
• Array sections
• Reduction operations
• Array I/O
• Allocatable arrays
Further Reading
• We have very much glossed over what Fortran can do with arrays, covering only the most
commonly used features
• Eventually
– You should read up more on array syntax and practice it
– You should learn about elemental intrinsic functions
– You should learn about array constructors
– You should learn about vector subscripts
– You should learn more about intrinsic reduction functions
– You should learn about intrinsic array manipulation functions
Program Structure
Subprograms
• Real programs can be 1000’s of lines long
• The solution is to break up the program into subprograms: Each subprogram performs one
(and ideally ONLY one) of the tasks the code requires
• This helps by increasing code comprehensibility
• If well written you need only understand that subprogram, not the whole program
• It also enables reusability
• Common operations need only be coded once and then may be used many times – or by a
different program
Subprograms
Program means
! Program to read in data and calculate the arithmetic, harmonic
! and geometric means of that data.
Implicit None
Real, Dimension( : ), Allocatable :: data
Real :: arithmetic_mean
Real :: geometric_mean
Real :: harmonic_mean
! Read in the data
Call read_data
! Calculate the means
Call calc_arithmetic_mean( data, arithmetic_mean )
Call calc_geometric_mean ( data, geometric_mean )
Call calc_harmonic_mean ( data, harmonic_mean )
! Write out the results
Call write_results( arithmetic_mean, geometric_mean, harmonic_mean )
! Free the memory
Call deallocate_data
End Program means
Subprograms
• The sign of a good programmer is well designed, easily understandable
subprograms.
• Fortran has two kinds of subprograms:
• Subroutine, e.g.
Call Random_number( x )
• Function, e.g.
y = Sin( x )
• These are intrinsic subprograms, but you can also define your own subprograms.
Subprogram Types
• There are four types of subprogram
– Intrinsic
– Contained
– Module
– External
• We have met intrinsic subprograms – the ones provided by the language
• In practice you should use Module subprograms most of the time
– But we’ll use contained subprograms as a “stepping stone” to them
• And in Fortran subroutines are much more common than functions
Contained Subprograms
Program hello4
Implicit None
Call greet( 'Ian' ) ! Note write once -
Call greet( 'World' ) ! use many
Contains
Subroutine greet( name )
Character( Len = * ), Intent( In ) :: name
Write( *, * ) 'Hello', name
End Subroutine greet
End Program hello4
$ ./hello4
Hello Ian
Hello World
Functions, Multiple Arguments And Local Variables
Program func_example
Implicit None
Integer, Parameter :: wp = Selected_real_kind( 12, 70 )
Write( *, * ) length( 3.0_wp, 4.0_wp )
Contains
Function length( x, y ) Result( r )
Real( wp ) :: r
Real( wp ), Intent( In ) :: x
Real( wp ), Intent( In ) :: y
Real( wp ) :: x_sq, y_sq ! Local Variables
x_sq = x * x
y_sq = y * y
r = Sqrt( x * x + y * y )
End Function length
End Program func_example
$ ./func_example
5.0000000000000000
Multiple Subprograms
Program mindless_optimism
Implicit None
Call greet( 'Ian' )
Call predict_cricket ! Don’t have to have arguments !
Contains
Subroutine greet( name )
Character( Len = * ), Intent( In ) :: name
Write( *, * ) 'Hello', name
End Subroutine greet
Subroutine predict_cricket
Write( *, * ) 'Cricket Result Prediction:'
Write( *, * ) 'England to beat Australia'
End Subroutine predict_cricket
End Program mindless_optimism
$ ./patriotism
Hello Ian
Cricket Result Prediction:
England to beat Australia
Program hello4
Implicit None
Call greet( 'Ian' )
Call greet( 'World' )
Contains
Subroutine greet( name )
Character( Len = * ), Intent( In ) :: name
Write( *, * ) 'Hello', name
End Subroutine greet
End Program hello4
Actual ArgumentsType :: CharacterRank :: 0
Dummy ArgumentType :: CharacterRank :: 0
Argument Association
Real & Dummy Arguments
Program association
Implicit None
Integer :: i
i = 2
Call zero( i )
Write( *, * ) i
Contains
Subroutine zero( a )
Integer, Intent( Out ) :: a
a = 0
End Subroutine zero
End Program association
$ ./association
0
Actual ArgumentsType :: IntegerKind :: DefaultRank :: 0
Dummy ArgumentsType :: IntegerKind :: DefaultRank :: 0
N.B. Dummy arguments must have the same type, kind and rank as the actual argument – “TKR matching”
Argument Association – What Happens
Program argument_mismatch
Implicit None
Integer :: i
Call zero( i )
Contains
Subroutine zero( a )
Real, Intent( Out ) :: a
a = 0
End Subroutine zero
End Program argument_mismatch
$ gfortran match.f90
In file match.f90:4
Call zero( i )
1
Error: Type mismatch in parameter 'a' at (1). Passing INTEGER(4) to REAL(4)
In file match.f90:3
Integer :: i
1
Error (113): Variable 'i' at (1) is used but not set
Actual Argument: Rank 0, Integer, Default Kind
Dummy Argument: Rank 0, Real, Default Kind
Argument Mismatch
Program mismatch2
Implicit None
Write( *, * ) length( 3, 4 )
Contains
Real Function length( x, y )
Real( wp ), Intent( In ) :: x
Real( wp ), Intent( In ) :: y
length = Sqrt( x * x + y * y )
End Function length
End Program mismatch2
Actual Argument: Rank 0, Integer, Default Kind
Dummy Argument: Rank 0, Real, Kind wp
WRONG !
Argument Mismatch
Dummy Arguments
• Dummy argument specification is very similar to normal variables, with a few extras.
• We’ll cover the extras as we go along, the first being Intent
Intent
• Intent specifies what the subprogram will do to the variable. The possible values are:
– Intent( In )
The real argument is initialized and the subprogram will not change the value
– Intent( Out )
Any value the real argument has will be ignored. The subprogram will set the argument.
– Intent( InOut )
The real argument is initialized and the subprogram may change the value
Intent
• It’s easier than it sounds !
– Input only variables Intent( In )
– Output only variables Intent( Out )
– Both Intent( InOut )
• Good style:
– For subroutines avoid Intent( InOut ) except where it really makes sense
– For functions use only Intent( In ) arguments
Program argument_aliasing
Implicit None
Integer :: i
Call set( i, i )
Contains
Subroutine set( a, b )
Integer, Intent( Out ) :: a
Integer, Intent( Out ) :: b
a = 0
b = 1
End Subroutine set
End Program argument_aliasing
For anything but Intent( In )or pointers this is illegal.Avoid! It leads to really nasty bugs
Argument Aliasing
Program host
Implicit None
Integer :: i
i = 7
Call associate
Contains
Subroutine associate
Integer :: j ! But j is local to the
subroutine
Write( *, * ) i
End Subroutine associate
End Program host
$ ./hello4
7
The Subroutine knows the Value of iby Host Association
Host Association
Program scope
Implicit None
Integer :: i
i = 7
Call associate
Contains
Subroutine associate
Integer :: j
j = 8
Write( *, * ) i, j
End Subroutine associate
End Program scope
Scope of Associate
(and Scope by host
association)
Scope of Scope
Scope
Scope
• Any program unit (we’ve seen Program, Subroutine and Function) can only access
entities that are in scope, e.g.
– Variables
– Parameters
– Subprograms
Program scope2
Implicit None
Integer :: i
i = 7
Call associate
Contains
Subroutine associate
Integer :: j
j = 8
Call wrong
End Subroutine associate
Subroutine wrong
Integer :: k
k = -3
Write( *, * ) i, k
Write( *, * ) j
End Subroutine wrong
End Program scope2
Scope of Associate
Scope of Associate2
Scope of Scope2
Legal – wrong is in scope by host association
Legal – i,k are in scope
Illegal – j is NOTin scope
Scope
Scope
• Scope may seem a bit of a pain, but it’s there for protection – you can only access
and change things you are allowed to, this stops all sorts of nasty bugs from some
piece of the program altering something unexpectedly (what if it is not only you
developing the code?)
Local Variable
• Local variables are variables that are in scope for a subprogram and nowhere else.
• You should NOT make ANY assumptions about their initial values
– Uninitialized variables are a very nasty bug
– The NAG compiler is one of the very few that can do a good job at finding them
• But not all of the time – doesn’t work with external libraries, e.g. MPI, MKL
• Intel and gnu can do a fairly half hearted effort
– But you can give them initial values …
• And by default their values are not guaranteed to be preserved from one call to the
next
Program local
Implicit None
Integer :: j
Do j = 1, 3
Call initialized_var
End Do
Contains
Subroutine initialized_var
Integer :: i = 0
i = i + 1
Write( *, * ) i
End Subroutine initialized_var
End Program local
$ ./local
1
2
3
Initialization applies to the FIRST CALL ONLY.Initialization implies the Save attribute
Local Variables - Initialization
The Save Attribute
• The Save attribute means that the value of a local variable will be preserved
between subroutine calls
Integer, Save :: i
• Initialized variables get the Save attribute by default
• Good practice: Avoid local variables with the Save attribute unless you really, really
need them.
Characters and Arrays
• So far we have only looked at passing numeric scalars to subprograms. Characters
and arrays are a little more complicated, but not much …
• For characters you have to consider the length
• For arrays you have to match the rank, extent and bounds
Program character_arguments
Implicit None
Call char_len( 'Ian' )
Call char_len( 'World' )
Contains
Subroutine char_len( string )
Character( Len = * ), Intent( In ) :: string
Write( *, * ) Len( string )
End Subroutine char_len
End Program character_arguments
$ ./character_arguments
3
5
Pick up the length from that of the actual argument
Character Arguments
Program array_arguments
Implicit None
Real, Dimension( 1:2, 1:3 ) :: a
a = 0
Call array_properties( a )
Contains
Subroutine array_properties( x )
Real, Dimension( :, : ), Intent( In ) :: x
Write( *, * ) Size( x, Dim = 1 ), Size( x, Dim = 2 ), &
Size( x )
Write( *, * ) Lbound( x )
Write( *, * ) Ubound( x, Dim = 1 ), Ubound( x, Dim = 2 )
End Subroutine array_properties
End Program array_arguments
$ ./array_arguments
2 3 6
1 1
2 3
Pick up extents fromactual arguments –and ONLY the extents
Array Arguments – Assumed Shape
Program array_arguments
Implicit None
Real, Dimension( 0:1, 1:3 ) :: a
a = 0
Call array_properties( a )
Contains
Subroutine array_properties( x )
Real, Dimension( :, : ), Intent( In ) :: x
Write( *, * ) Size( x, Dim = 1 ), Size( x, Dim = 2 ), &
Size( x )
Write( *, * ) Lbound( x )
Write( *, * ) Ubound( x, Dim = 1 ), Ubound( x, Dim = 2 )
End Subroutine array_properties
End Program array_arguments
$ ./array_arguments
2 3 6
1 1
2 3
Pick up extents fromactual arguments –lower bound is 1 inthe subprogram
Array Arguments – Assumed Shape
Program array_arguments2
Implicit None
Real, Dimension( 0:1, 1:3 ) :: a
a = 0
Call array_properties( a )
Contains
Subroutine array_properties( x )
Real, Dimension( 3:, :4 ), Intent( In ) :: x
Write( *, * ) Size( x, Dim = 1 ), &
Size( x, Dim = 2 ), Size( x )
Write( *, * ) Lbound( x )
Write( *, * ) Ubound( x, Dim = 1 ), Ubound( x, Dim = 2 )
End Subroutine array_properties
End Program array_arguments2
$ ./array_arguments
2 3 6
3 2
4 4
Always keep the same extent but can chose the bounds we need
Array Arguments – Assumed Shape
Array Arguments - A More Realistic Example
Program array_args
Implicit None
Integer, Parameter :: wp = Selected_real_kind( 12, 70 )
Real( wp ), Dimension( : ), Allocatable :: a
Real( wp ) :: av
Integer :: i, n
Write( *, * ) 'How many values ?'
Read ( *, * ) n
Allocate( a( 1:n ) ) ! Should really check status
Do i = 1, n
Write( *, * ) 'Please input value ', i
Read ( *, * ) a( i )
End Do
Call average( a, av )
Write( *, * ) 'the average is ', av
Contains
Subroutine average( a, av )
Real( wp ), Dimension( : ), Intent( In ) :: a
Real( wp ), Intent( Out ) :: av
Integer :: i
av = 0.0_wp
Do i = 1, Size( a )
av = av + a( i )
End Do
av = av / Size( a )
End Subroutine average
End Program array_args
>./a.out
How many values ?
3
Please input value 1
0
Please input value 2
1
Please input value 3
2
the average is 1.0000000000000000
The Return Statement
• A subprogram normally exits when it reaches the end.
• You can go back to the calling (sub)program at any point by using the Return
statement
If( error_has_occured ) Then
error = 1 ! Return an error flag
Return
End If
• Good Practice: Avoid Return unless the program logic becomes complicated (
except possibly as the last statement – style issue )
Program return_example
Implicit None
Real, Dimension( : ), Allocatable :: a
Integer :: n
Integer :: error
Read( *, * ) n
Call allocate_array( n, error )
If( error /= 0 ) Then
Write( *, * ) 'Allocation Failed'
End If
Contains
Subroutine allocate_array( n, error )
Integer, Intent( In ) :: n
Integer, Intent( Out ) :: error
Allocate( a( 1:n ), Stat = error )
If( error /= 0 ) Then
Return
End If
End Subroutine allocate_array
End Program return_example
The Return Statement
Summary
• Why subprograms
• Fortran subprogram types
• Contained subprograms
• Actual and dummy arguments
• Argument association
• Intent
• Host association and scope
Further Reading
• Passing unallocated allocatable arrays as actual arguments
– If already allocated you need do nothing special and just do what covered here
– If you want to (re-)allocate them in a subprogram the dummy argument needs to have the
allocatable attribute
– Actually you just do the obvious thing from what you have learnt here …
• Optional and Keyword arguments
• Pure and Elemental subprograms
Lecture 3
Programming Structure 2
Modules
• We saw some of the advantages of subprograms.
– Helping us to organise our programs.
– Allowing us to reuse code
• We might also want to reuse variables.
• And packages these variable with the subprograms that act upon them.
• For this we can use MODULES.
Modules
MODULE pleasantries
IMPLICT NONE
Integer :: age = 49
. . .
CONTAINS
SUBROUTINE greet( name )
CHARACTER( LEN = * ), INTENT( IN ) :: name
WRITE( *, * ) 'Hello ', name
END SUBROUTINE greet
. . .
END MODULE pleasantries
USEing Modules
PROGRAM hello
USE pleasentries ! N.B. BEFORE implicit none
IMPLICT NONE
CALL greet( 'Ian' )
Write(*,*) age
END PROGRAM hello
$ ./hello
Hello Ian
43
Program hello
Use mymodule
Implicit None
Call greet( 'Ian' )
Write(*,*) age
End Program hello
Scope of hello andmymodule by Use
Module mymodule
Implicit None
Integer :: age = 43
Contains
Subroutine greet( name )
Character( Len = * ), Intent( In ) :: name
Write( *, * ) 'Hello ', name
End Subroutine greet
End Module mymodule
mymodule in scope byuse association
Scope ofmymodule
Modules and Scope
The Advantages Of Modules
• Modules allow the breaking up of a large program into multiple files by accessing
scopes exterior to the current (sub)program
• This help
– Comprehensibility
If well written you only need understand one file
– Reuseability
If well written the file may be used in many codes
The Advantages of Modules
• So large programs may be built up from many smaller files, and each of those files
may be used in many programs, and this should also aid in comprehension.
• Well designed modules are the very essence of good programming.
• Another advantage is that the scope may be in a totally separate module and is
restricted to that module – Unless you Use it
Modules
• All of the concepts from contained subprograms pass directly over to USEing
Modules.
• So we know all about
– Dummy and real arguments
– Argument association
– Host association and scope
– Passing scalars, characters and arrays
– Assumed shape arrays
Multiple file compilation
• Good practise is one module per file.
• Compiling modules:
$ nagfor –o prog file1.f90 file2.f90 file3.f90 ...
• If file2.f90 Uses a Module from file1.f90, you MUST compile file1.f90 before file2.f90
• Try: http://www.fortran.com/makemake.perl
PROGRAM fred
USE mod1
…
END PROGRAM fred
File main.f90
MODULE mod1
…
CONTAINS
SUBROUTINE a
USE mod2
…
END SUBROUTINE a
…
END MODULE mod1
File mod1.f90
MODULE mod2
…
CONTAINS
…
END MODULE mod2
File mod2.f90
> nagfor –o prog \
mod2.f90 mod1.f90 main.f90
Note you can Use Modulesin subprograms and at thetop of Modules. MultipleUse statements are alsofine
Order of Compilation
Public And Private In Modules
• We can control access to variables and subprograms in modules.
• Through Public and Private we can ‘fine tune’ Use Association.
• Public variables and subprograms come into scope when a module is used.
• Private variables and subprograms are private to the module.
• By default all are Public.
• Personally I think this is a mistake – it’s good practice to keep what is public to a
minimum
Program host4
Use data4
Implicit None
Write( *, * ) a ! Fine
Write( *, * ) b ! ILLEGAL - b not in scope
End Program host4
Scope of host4 andPUBLIC scope of data4 by Use
Module data4
Implicit None
Real, Public :: a = 1.0
Real, Private :: b = 2.0
End Module data4
We gain Use Association for a but NOT for b
Scope ofdata4
Private Variables
Program host4
Use data4
Implicit None
Write( *, * ) a ! Fine
Call print_b
End Program host4
Scope of host4 andPUBLIC scope of data4 by Use
However b is in scope fordata4
Scope ofdata4
Module data4
Implicit None
Real, Public :: a = 1.0
Public :: print_b
Real, Private :: b = 2.0
Contains
Subroutine print_b
Write( *, * ) b
End Subroutine print_b
End Module data4
Private Variables
Program host5
Use data5
Implicit None
Write( *, * ) a ! ILLEGAL – a not in scope
Write( *, * ) b ! ILLEGAL - b not in scope
End Program host5
Scope of host5 andPUBLIC scope of data5 by Use
Module data5
Implicit None
Real :: a = 1.0
Real :: b = 2.0
Private ! All Private
! unless told
! Otherwise
End Module data5
But NOTHING is public !
Scope ofdata5
Private affectsWHOLE Module
Complete Protection with Private
Protection With Private
• As we have said scope is there to protect you – you can avoid accessing things you
shouldn’t be.
• Further, the more entities there are in the namespace cut down the names you can use in
the local scope – this is known as Namespace Pollution.
• Use private as much as you can.
• Consider using your own prefix, like MPI.
Kind And Modules
• Using MODULE’s we now have a powerful way of designing the precision used in
our code
Module Numbers
Implicit None
Integer, Parameter :: wp = Selected_real_kind( 12, 70 )
End Module Numbers
• If all other MODULE’s include a USE Numbers then we can control the precision of
the whole code via that one MODULE
Program Example
Use numbers ! Specify Precision of whole code
Implicit None
Real( float ) :: total, a, average
Integer :: n, i
Read( *, * ) n
total = 0.0_float
Do i = 1, n
Read( *, * ) a
total = total + a
End Do
average = total / Real( n, Kind( average ) )
Write( *, * ) ’the average is ’, average
End Program Example
KIND Example
External Subprograms
• Subprograms do not have to be contained or in a module.
• Such subprograms are termed external.
• Use of external subprograms in new code should be avoided as you, by default,
lose all the safety features of contained and module subprograms – Argument
mismatch is VERY easy
• However this was the only way to write subprograms before Fortran 90 and a lot of
older programs including much good quality software are only available in this form,
so you should be aware of them.
External Subprograms
Subroutine greet( name )
Implicit None
Character( Len = * ), Intent( In ) :: name
Write( *, * ) 'Hello', name
End Subroutine greet
• Note no program or module or contains in sight
Disadvantages of External Subprograms
• You do not import an interface for any called subprogram
– The compiler will not detect argument mismatch
– You have to declare functions at the calling point
• External Subprograms are always in scope
– Namespace pollution
• By default you can’t use optional arguments, keyword arguments …
– In particular by default you can not use assumed shape arrays
– To get around this you have to pass the array bounds explicitly
• All of this is because you don’t have an interface in scope at the calling site
External Subprograms
Program old_array_arguments
Implicit None
Real, Dimension( 1:2, 1:3 ) :: a
a = 0
Call array_properties( 2, 3, a )
End Program old_array_arguments
Subroutine array_properties( n, m, x )
Integer , Intent( In ) :: n ! Note we pass the
Integer , Intent( In ) :: m ! Array bounds explicitly
Real, Dimension( 1:n, 1:m ), Intent( In ) :: x
Write( *, * ) Size( x, Dim = 1 ), Size( x, Dim = 2 ), Size( x )
Write( *, * ) Lbound( x )
Write( *, * ) Ubound( x, Dim = 1 ), Ubound( x, Dim = 2 )
End Subroutine array_properties
Planning Your Programs
• There are two things to think about:
• What operations the program has to perform
– The functional breakdown
– This gives the list of subprograms we require
– Each subprogram performs one function
• What data the program has to deal with
– The data flow breakdown
– This gives the grouping of subprograms, i.e. the Modules you require
– Each Module acts on one sort of data, i.e. matrix test data, not REAL etc
Summary
• Writing and Using modules
• Use association
• Private and Public variables
• External subprograms and why you should not use them in new code
Further Reading
• Interfaces
• Subprogram and operator overloading
• Derived types – making up your own data types
– If you write any sort of more advanced Fortran you really should learn about derived types
– They work very well with modules
• Use a module to define a given data type and the operations on it
– Thee next stage from this is
• OO features in Fortran
– Came in in F2003 and takes modules and derived onto the next level
– If I had a third day these are the things would be what comprise it
Input and Output
Format Specifier
• We have been using write(*,*) and read(*,*)
• What do the *’s mean?
• The second one refers to a format. It means use free format (it’s up to the compiler).
We can also specify explicit formats
integer :: and
real :: hard, fast
...
write(*,’(f12.4,i5,e20.10)’)hard, and, fast
Format
integer :: and
real :: hard, fast
...
write(*,’(f12.4,i5,e20.10)’)hard, and, fast
• The ’(f12.4,i5,e20.10)’ gives formats for the three numbers included in the write
statement:
• f12.4 - floating point number of width 12 characters with four digits to the right of the
decimal place
• i5 - an integer number of width 5 characters
• e20.10 - an exponential format of width 20 characters with 10 decimal places.
• Note it is simply of type character
Formats
• Formats in general are much more useful for output
– Make it look “pretty”
• In general input is perfectly adequately dealt with by free format
• In general a format for reading just makes life harder
• However there is nothing stopping you, and just occasionally you need one
– Generally a sign of a poorly designed input!
Format
• Format specifiers are strings and can be stored (and edited) as strings
Integer :: hard, and, fast
Character( len = 5 ):: style
…
style = ’(3i5)’ ! Could be built up
! Internal I/O useful, see later
Write( *, style ) hard, and, fast
Format – Repeating And Spacing
• Here we print 3 lots of integers followed by a space
Integer :: hard, and, fast
...
write(*,’(3(i5,1x))’) hard, and, fast
323 1645 12345
Edit Descriptors
Purpose Edit Descriptors
Reading/writing INTEGERs Iw Iw.m
Reading/writing REALs
Decimal form Fw.d
Exponential form Ew.d Ew.dEe
Scientific form ESw.d ESw.dEe
Engineering form ENw.d ENw.dEe
Reading/writing LOGICALs Lw
Reading/writing CHARACTERs A Aw
w: the total number of characters
m: the minimum number of positions to be used – leading zeros
d: the number of digits to the right of the decimal point
e: the number of digits in the exponent part
Minimum Width
• w may be zero - use the minimum width possible to display the number:
Write( *, ’( 4I4 )’ ) 22, -444, 0, 55555
22-444 0**** ! Stars mean can’t show
Write( *, ‘( 4I0 )’ ) 22, -444, 0, 55555
22-444055555
Write( 0, ’( f8.4 )’ ) 12.34567
12.3457
Write( 0, ’( f0.4 )’ ) 12.34567
12.3457
Records, Files and Sequential Access
• A file in Fortran 95 ALWAYS consists of a set of records (“lines”), one of which is an
end-of-file marker.
– In Fortran 2003 there is also non-record based I/O but this is comparatively rarely used
• So far for us each Read/Write deals with one record (“line”) at a time – also note for
the future each “line” can be very long
1 2 3 4 5 EOF6
Integer :: i
Real :: a
Do i = 1, 4
Read ( *, * ) a ! New record
! each time
Write( *, '( f4.1 )' ) a
End Do
4.0 3
6.0 .True.
8. 9 12
10 hello
$ ./read_records < input
4.0
6.0
8.0
10.0
Records, Files and Sequential Access
4.0 3
6.0 .True.
8. 9 12
10 helloInteger :: i
Real, Dimension( 1:4 ) :: a
Do i = 1, 4
Read( *, * ) a( i ) ! New record
! each time
End Do
Write( *, '( 2( 2( f4.1, 1x ), / ) )' ) a
$ ./multi_write < input
4.0 6.0
8.0 10.0
Dealing with Multiple Records
• The / edit descriptor can be used to deal with multiple records in
one write ( or read )
• Input – Remember this?
INTEGER, DIMENSION(1:10) :: x
INTEGER :: n, i
READ(*,*) n
READ(*,*) x
Expects hard return between inputs, at each read.
If reach end of record move onto next one
INTEGER, DIMENSION(1:10) :: x
INTEGER :: n, i
READ(*,*) n ! Hope not > 10
DO i = 1, n
READ(*,*) x(i)
END DO
Multiple Records And Array I/O
Non-Advancing I/O
• What if we don’t want to write (or read) one whole record at once ?
Write( *, ’( ”Hello ” )’, Advance = ’No’ )
Write( *, ’( ”Craig.” )’, Advance = ’No’ )
Write( *, * ) ! Finish Record
$ ./no_advance
Hello Craig.
• Can be nice for prompts
Units and Files
• The first * in write(*,*) refers to the default unit.
• So far we have relied on the default output unit being the screen and the default
input unit being the keyboard.
• We can explicitly write to a different unit by
write(unit,*) …
• Unit is simply a positive integer
– For boring historical reasons I suggest you use values of 10 or greater
• A unit will generally refer to a file.
– But in theory (more often the past) could be e.g. a printer
Files
open(unit = 10, file = ’filename’)
• Writing to unit = 10 will now write to the file called filename.
– Technically this connects file filename to unit 10
– Subsequent I/O operations on unit 10 will now use the file filename
• There are a number of useful additions to the open statement - use them as good
defensive programming measures.
Status
open(unit=10, file=’file’, status=file_stat)
• Where file_stat is of type character and has one of the values ’old’ , ’new’ , ’scratch’,
’replace’ , ’unknown’
• If ’scratch’ you must not supply a name
• Default is status = ’unknown’
• Typically input file will be ’old’, output files will be ’new’.
Action
open(unit=1, file=’file’, status=file_stat, &
action=what)
• Where “what” is of type character and has one of the values ’read’, ’write’ ,
’readwrite’
• Default is action = ’readwrite’
• Typically:
– input files will be ’read’
– output files will be ’write’
• Compare with Intent.
IOSTAT
open(unit=1, file=’file’, status=file_stat, &
action=what, iostat=value)
• value is of type integer.
– positive on error
– zero after success.
– Like the stat in allocate statements.
Position
• open(unit=1, file=’file’, position=pos)
• Where pos is of type characters and is one of ’asis’ , ’rewind’ , ’append’
• Default is position = ’asis’
• Note the default is not defined if the file exists and is not already connected, though
in practice it defaults to rewind.
Unformatted Files
• Files can be unformatted (“binary”) files.
– less disk space
– generally faster
– not necessarily transferable between machines
– not human readable
• STILL RECORD BASED
open(unit=36,file=’file’,form=’unformatted’)
• The associated I/O statements become
write(36) x, y, z
read(36) p
• Note no format as the files are unformatted !
Close
• Opened files should be closed
open(unit = 1, file = ’filename’)
...
...
close(unit = 1)
• We can also use status and iostat with close
• iostat behaves exactly as in open statements
• status can be ’keep’ or ’delete’
• The default for scratch files is ’delete’. The default for all other files is ’keep’
Rewind
Rewind( unit )
• Takes the file back to the start
• Also Backspace – go back one record
• and Endfile – guess !
• Look in a book for further details
INQUIRE(FILE=name, IOSTAT=i_var, …)
INQUIRE(UNIT=io_unit, IOSTAT=i_var, …)
Inquire
• We can also investigate the status of a connection with the INQUIRE function
ACCESS=acc ‘SEQUENTIAL’ ‘DIRECT’ ‘UNDEFINED’
ACTION=act ‘READ’ ‘WRITE’ ‘READWRITE’ ‘UNDEFINED’
DIRECT=dir ‘YES’ ‘NO’ ‘UNKNOWN’
EXIST=ex .TRUE. .FALSE.
FORM=fm ‘FORMATTED’ ‘UNFORMATTED’ ‘UNDEFINED’
FORMATTED=fmt ‘YES’ ‘NO’ ‘UNKNOWN’
NAME=nme returns the file name.
NAMED=nmd .TRUE. .FALSE
NUMBER=num unit_number
OPENED=od .TRUE. .FALSE.
POSITION=poss ‘REWIND’ ‘APPEND’ ‘ASIS’ ‘UNDEFINED’
READ=rd ‘YES’ ‘NO’ ‘UNKNOWN’
READWRITE=rdwr ‘YES’ ‘NO’ ‘UNKNOWN’
SEQUENTIAL=seq ‘YES’ ‘NO’ ‘UNKNOWN’
UNFORMATTED=unf ‘YES’ ‘NO’ ‘UNKNOWN’
WRITE=wr ‘YES’ ‘NO’ ‘UNKNOWN’
Inquire
Internal I/O
• It is also possible to access internal files. Internal files are of type default character.
They are formatted sequential files.
• File connection, file positioning and file inquiry must not be used with internal files.
• If the variable representing the internal file is a scalar the file has one record.
• If it is an array then the file has one record for each element of the array.
• The length of the record is the length of one array element.
Internal I/O
• Typically used to convert machine representations to characters or vice versa e.g.
READ(CHAR_VAR, ’( f6.4, 1x, i4 )’) x, J
WRITE(FMT=*,UNIT=CHAR_VAR)x
Summary
• Use format specifiers to produce neat output
• Free format is usually sufficient for input
• There are lots of edit descriptors!
• All Fortran95 I/O is record based
• OPEN will produce a connection to a file
• Use ACTION, STATUS and IOSTAT as defensive measures
• If you OPEN a file then CLOSE it!
• Use INQUIRE to check the status of a connection
Further Reading
Input/Output
• Direct access files
• Scratch Files
• Stream I/O
More generally about Fortran and programming
• Functions as arguments
• Pointers – not as useful in Fortran as other languages but occasionally useful.
– Allocatable arrays, derived types, OO features and the move_alloc intrinsic cover most occurrences
• Interoperability with C
• IEEE floating point exception handling
• Parallelism via co-arrays
• Tools you can use with Fortran– Revision control, unit testing, documentation generation …
• Common libraries– BLAS, LAPACK, FFTW, PetSc, ScaLAPACK, NAG …
• Talk with other programmers and RSE’s• E.g. at the RSE conference in September http://rse.ac.uk/2017/01/25/rse-conference-2017/
• And practice, practice, practice – you only learn programming by doing it!
Acknowledgments
I would like to thank the Numerical Algorithms Group Ltd. (NAG) for providing much of the material
used in the slides and exercises
Finally
Thank You!
Reuse of this material
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0
International License
http://creativecommons.org/licenses/by-nc-sa/4.0/deed.en_US
This means you are free to copy and redistribute the material and adapt and build on the
material under the following terms: You must give appropriate credit, provide a link to the license and indicate if changes were made. If you adapt or build on the material you must
distribute your work under the same license as the original.
Note that this presentation may contain images owned by others. Please seek their
permission before reusing these images.