GEO4060: Intro to Fortran 2003 programming Gunnar Wollan

advertisement

UNIVERSITY OF OSLO

Department of

Geosciences

GEO4060: Intro to

Fortran 2003 programming

Gunnar Wollan

Spring 2014

Contents

1 Introduction 2

1.1

Why use Fortran? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

2

1.2

Historical background . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

3

2 The Fortran syntax 4

2.1

The structure of Fortran . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

4

2.2

Datatypes in Fortran . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

4

2.2.1

INTEGER . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

4

2.2.2

REAL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

5

2.2.3

LOGICAL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

5

2.2.4

CHARACTER . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

5

2.2.5

Derived datatypes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

5

2.3

Declaration of variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

5

2.3.1

Declaration of INTEGERS . . . . . . . . . . . . . . . . . . . . . . . . . .

6

2.3.2

Declaration of REAL numbers . . . . . . . . . . . . . . . . . . . . . . . .

6

2.3.3

Declaration of LOGICAL variables . . . . . . . . . . . . . . . . . . . . . .

7

2.3.4

Declaration of characters . . . . . . . . . . . . . . . . . . . . . . . . . . . .

7

2.3.5

Declaration of derived datatypes . . . . . . . . . . . . . . . . . . . . . . .

7

2.4

Instructions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

8

2.4.1

Instructions for giving a variable a value . . . . . . . . . . . . . . . . . . .

8

2.4.2

Instructions for program control . . . . . . . . . . . . . . . . . . . . . . .

8

3 A very simple Fortran 95 program 9

3.1

Programming our first problem . . . . . . . . . . . . . . . . . . . . . . . . . . . .

9

3.2

What have we learned here?? . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

10

3.3

A small exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

10

4 A more userfriendly Fortran 95 program 11

4.1

Extending our program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

11

4.2

What have we learned here?? . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

11

4.3

A small exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

12

5 The use of arrays 13

5.1

A one dimensional array example . . . . . . . . . . . . . . . . . . . . . . . . . . .

13

5.2

What have we learned here?? . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

14

5.3

A small exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

14

6 The use of 2D arrays 15

6.1

A two dimensional array example . . . . . . . . . . . . . . . . . . . . . . . . . . .

15

6.2

What have we learned here?? . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

17

6.3

A small exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

17

7 Getting data from a text file 18

7.1

How to read data from a file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

18

7.2

What have we learned here?? . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

20

7.3

A small exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

20

1

8 Programming formulas 21

8.1

What is a function in Fortran? . . . . . . . . . . . . . . . . . . . . . . . . . . . .

21

8.2

What have we learned here?? . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

23

8.3

A small exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

23

9 Programming formulas using functions 24

9.1

Programming the standard deviation function . . . . . . . . . . . . . . . . . . . .

24

9.2

What have we learned here?? . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

26

9.3

A small exercise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

26

10 Programming subroutines 27

10.1 What is a subroutine in Fortran? . . . . . . . . . . . . . . . . . . . . . . . . . . .

27

10.2 What have we learned here?? . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

29

11 Programming formulas using subroutines 30

11.1 Programming a subroutine calling another subroutine . . . . . . . . . . . . . . . .

30

11.2 What have we learned here?? . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

31

12 Introduction to modules 32

12.1 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

35

13 Introduction to Object Oriented Programming 36

13.1 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

39

A Operators 40

A.1 Overview of the operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

40

B Intrinsic functions in Fortran 95 41

B.1 Intrinsic Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

41

1 Introduction

The purpose of this compendium is to give a good insight in the Fortran 2003 programming language by going through a number of examples showing how computational problems can be solved using Fortran 2003.

1.1

Why use Fortran?

In the last 15 to 20 years Fortran has been looked upon as an old-fashioned unstructured programming language by researchers and students in the field of Informatics. Fortran has lacked most of the features found in modern programming languages like C++, Java etc. Especially the lack of object orientation has been the main drawback of Fortran. This is no longer true because Fortran 2003 has all the modern features including use of objects, operator overloading and genuine OOP functionality like in C++ and Java.

But why not forget Fortran and concentrate on using available OOP languages? The answer is very simple, speed . In the field of natural sciences, computer simulations of natural phenomena are becoming increasingly more important. Laboratory experiments are getting too complex and too costly to be performed. The only alternative is to use a computer simulation to try to solve the problem under study. Thus the need to have your code execute faster becomes more and more important when the simulations grows larger and larger. In number-crunching

2

Fortran still has an edge in speed over C and C++. Tests has shown that an optimised Fortran program in some cases runs up to 30 percent faster than the equivalent C or C++ program, but the difference is shrinking. For programs with a runtime of weeks even a small increase in speed will reduce the overall time it takes to solve a problem.

1.2

Historical background

Seen in a historical perspective Fortran is an old programming language. 1954 John Backus and his team at IBM begin developing the scientific programming language Fortran. It was first introduced in 1957 for a limited set of computer architectures. In a short time the language spread to other architectures and has since been the most widely used programming language for solving numerical problems.

The name Fortran is derived from For mula Tran slation and it is still the language of choice for fast numerical computations. A couple of years later in 1959 a new version, Fortran II was introduced. This version was more advanced and among the new features was the ability to use complex numbers and splitting a program into various subroutines. In the following years Fortran was further developed to become a programming language that was fairly easy to understand and well adapted to solve numerical problems.

In 1962 a new version called Fortran IV emerged. This version had among it’s features the ability to read and write direct access files and also had a new data-type called LOGICAL.

This was a Boolean data-type with two states true or false. At the end of the seventies Fortran

77 was introduced. This version contained better loop and test structures. In 1992 Fortran

90 was formally introduced as an ANSI/ISO standard. This version of Fortran has made the language into a modern programming language. Fortran 2003 is a small extension of Fortran 90.

These latest versions of Fortran has many of the features we expect from a modern programming languages. The Fortran 2003 version which incorporates object-oriented programming support with type extension and inheritance, polymorphism, dynamic type allocation and type-bound procedures. We shall come back to what these terms mean.

3

2 The Fortran syntax

As in other programming languages Fortran 2003 has it’s own syntax. We shall now take a look at the Fortran 2003 syntax and also that of the Fortran 77 syntax.

2.1

The structure of Fortran

To start programming in Fortran a knowledge of the syntax of the language is necessary. Therefore let us start immediately to see how the Fortran syntax look like.

Fortran has, as other programming languages, a division of the code into variable declarations and instructions for manipulating the contents of the variables.

An important difference between Fortran 77 and Fortran 2003 is the way the code is written.

In Fortran 77 the code is written in fixed format where each line of code is divided into 80 columns and each column has its own meaning. This division of the code lines into columns has an historically background. In the 1960s and 1970s the standard media for data input was the punched cards. They were divided into 80 columns and it was therefore naturally to set the length of each line of code to 80 characters. In table 1 an overview of the subdivision of the line of code is given.

Column number

1

2 - 5

6

7 - 72

73 - 80

Meaning

A character here means the line is a comment

Jump address and format number

A character here is a continuation from previous line

Program code

Comment

Table 1: F77 fixed format

Fortran 77 is a subset of Fortran 2003 and all programs written in Fortran 77 can be compiled using a Fortran 2003 compiler. In addition to the fixed code format from Fortran 77, Fortran

2003 also supports free format coding. This means that the division into columns are no longer necessary and the program code can be written in a more structured way which makes it more readable and easier to maintain. Today the free format is the default settings for the F95 compiler.

2.2

Datatypes in Fortran

Traditionally Fortran has had four basic datatypes. These were INTEGER and REAL numbers,

LOGICAL which is a boolean type and CHARACTER which represent the alphabet and other special non numeric types. Later the REAL data type was split into the REAL and COMPLEX data type. In addition to this a derived datatype can be used in Fortran 2003. A derived datatype can contain one or more of the basic datatypes and other derived datatypes.

2.2.1

INTEGER

An INTEGER datatype is identified with the reserved word INTEGER. It has a valid range which varies with the way it is declared and the architecture of the computer it is compiled on.

When nothing else is given an INTEGER has a length of 32 bits on a typical workstation and can have a value from [

2 31 ] to [2 30 ] . The 64 bit INTEGER has a minimum value from [

2 63 ]

4

to a maximum value of [2 62 ] . To declare a 64 bit INTEGER we use the construct INTEGER

(KIND=8) which means each variable uses 8 bytes each consisting of 8 bits.

2.2.2

REAL

In the same manner a REAL number can be specified with various ranges and accuracies. A

REAL number is identified with the reserved word REAL and can be declared with single or double precision. In table 2 the number of bits and minimum and maximum values are given.

Precision Sign Exponent Significand Max. value Min. value

Single

Double

1

1

8

11

23

52

2 128

2 1024

2

2

126

1022

Table 2: REAL numbers

A double precision real number are declared using the reserved words REAL (KIND=8) just like the 64 bit INTEGER.

An extension of REAL numbers are COMPLEX numbers with their real and imaginary parts. A COMPLEX number are identified with the reserved word COMPLEX. The real part can be extracted by the function REAL() and the imaginary part with the function AIMAG() or just IMAG() depending on the compiler implementation. There is no need for writing explicit calculation functions for COMPLEX numbers like one has to do in C / C++ which lacks the

COMPLEX data type.

2.2.3

LOGICAL

The LOGICAL datatype is identified by the reserved word LOGICAL and has only two values true or false. These values are identified with .TRUE. or .FALSE. and it is important to notice that the point at the beginning and end is a necessary part of the syntax. To omit one or more points will give a compilation error.

2.2.4

CHARACTER

The CHARACTER datatype is identified by the reserved word CHARACTER and contains letters and characters in order to represent data in a readable form. Legal characters are a to z ,

A to Z and some special characters like + , , * , / , = etc.

2.2.5

Derived datatypes

These are datatypes which are defined for special purposes. A derived datatype are put together of components from one or more of the four basic datatypes and also of other derived datatypes.

A derived datatype is always identified by the reserved word TYPE name as prefix and END

TYPE name as postfix.

2.3

Declaration of variables

In Fortran there are two ways to declare a variable. The first is called implicit declaration and is inherited from the earliest versions of Fortran. Implicit declaration means that a variable is declared when needed by giving it a value anywhere in the source code. The datatype is determined by the first letter in the variable name. An INTEGER is recognised by starting with the letters I to N and a REAL variable by the rest of the alphabet. It is important to notice

5

that no special characters are allowed in a variable name only the letters A - Z , the numbers 0

- 9 and the underscore character _ . A variable cannot start with a number.

The other way of declaring a variable is by explicit declaration. This is in accordance with other programming languages where all variables has to be declared within a block of code before any instructions occurs.

As a general rule an implicit declaration is not a good way to program. It gives a code that is not very readable and also it is easily introduce errors in a program due to typing errors.

Therefore always use explicit declaration of variables. Some variables must always be declared.

These are arrays in one or more dimensions and character strings.

2.3.1

Declaration of INTEGERS

First an example of how to declare an INTEGER in Fortran 2003. Note that the KIND parameter is architecture dependent.

INTEGER

INTEGER ( KIND =2)

:: i ! Declaration of an INTEGER

! length (32 bit)

:: j ! Declaration of an INTEGER (16 bit)

INTEGER ( KIND =4)

INTEGER ( KIND =8)

:: k ! Declaration of an INTEGER (32 bit)

:: m ! Declaration of an INTEGER (64 bit)

INTEGER , DIMENSION (100) :: n ! Declaration of an INTEGER array

! (100 elements)

As seen in the preceding examples there are certain differences in the Fortran 77 and the Fortran

2003 way of declaring variables. It is less to write when the variables are declared in the Fortran

77 style but this is offset by greater readability in the Fortran 2003 style. One thing to note is that in Fortran 2003 a comment can start anywhere on the code line and is always preceded by an exclamation point.

2.3.2

Declaration of REAL numbers

The REAL datatype is now in most compilers confirming to the IEEE standard for floating point numbers. Declarations of single and double precision is declared like in the next example.

REAL

REAL

REAL

(

,

KIND =8)

DIMENSION (200)

:: x ! Declaration of REAL

! default length (32 bit)

:: y ! Declaration of REAL

! double precision (64 bit)

:: z ! Declaration of REAL array

! (200 elements)

Fortran has, unlike C/C++, an intrinsic datatype of complex numbers. Declaration of complex variables in Fortran are shown here.

COMPLEX :: a ! Complex number

COMPLEX , DIMENSION (100) :: b ! Array of complex numbers

! (100 elements)

6

2.3.3

Declaration of LOGICAL variables

Unlike INTEGERS and REAL numbers a LOGICAL variable has only two values, .TRUE.

or .FALSE. and therefore only uses a minimum of space. The number of bits a LOGICAL variable are using depends on the architecture and the compiler. It is possible to declare a single LOGICAL variable or an array of them. The following examples shows a Fortran 77 and a Fortran 2003 declaration. In other programming languages the LOGICAL variable is often called a BOOLEAN variable after Boole the mathematician.

LOGICAL :: l1 ! Single LOGICAL variable

LOGICAL , DIMENSION (100) :: l2 ! Array of LOGICAL variables

! (100 elements)

2.3.4

Declaration of characters

Characters can either be declared as a single CHARACTER variable, a string of characters or an array of single characters or character strings.

CHARACTER

CHARACTER

CHARACTER ,

( LEN =80)

DIMENSION (10)

:: c1 ! Single character

:: c2 ! String of characters

:: c3 ! Array of single

! characters

CHARACTER ( LEN =80), DIMENSION (10) :: c4 ! Array of character

! strings (10 elements)

2.3.5

Declaration of derived datatypes

TYPE derived

! Internal variables

INTEGER

REAL

LOGICAL

:: counter

:: num

:: used

CHARACTER ( LEN =10) :: string

END TYPE derived

! A declaration of a variable of

! the new derived datatype

TYPE (derived) :: my_type

One question arises: why use derived datatypes? One answer to that is that sometimes it is desirable to group variables together and to refer to these variables under a common name. It is usually a good practice to select a name of the abstract datatype to indicate the contents and area of use.

7

2.4

Instructions

There are two main types of instructions. One is for program control and the other is for giving a variable a value.

2.4.1

Instructions for giving a variable a value

To give a numeric variable (i.e. INTEGER, REAL etc.) we use the equal sign

INTEGER

REAL i = 0 r = 0.0

:: i

:: r where we initialises the variables to zero. Note that we have to use 0 .

0 or just 0 .

to initialise the

REAL number.

2.4.2

Instructions for program control

Instructions for program control can be split into three groups, one for loops, one for tests (even though a loop usually have an implicit test) and the last for assigning values to variables and perform mathematical operations on the variables. In Fortran all loops starts with the reserved word DO . A short example on a simple loop is given in the following piece of code.

DO i = 1, 100

!// Here instructions are performed 100 times

!// before the loop is finished

END DO

The next example shows a non terminating loop where a test inside the loop is used to exit the loop when the result of the test is true.

DO a = a * SQRT(b) + c

IF (a > z) THEN

!// Jump out of the loop

EXIT

END IF

END DO

This small piece of code gives the variable a a value from the calculation of the square root of the variable b and multiplied with the last value of a and the addition of variable c . When the value of a is greater then the value of variable z the program transfer control to the next instruction after the loop. We assumes here that all the variables has been initialised somewhere in the program before the loop. The various Fortran instructions will be described in the following chapters through the examples on how problems can be solved by simple Fortran programs.

8

3 A very simple Fortran 95 program

The best way of learning a new programming language is to start using it. What we shall do now is to take a small problem and program a solution to that problem.

3.1

Programming our first problem

We want to make a small program solving the conversion of a temperature measured in Farenheit to Centigrade.

The formula is C = ( F

32)

·

5

9

.

All Fortran programs starts with the PROGRAM programname sentence and ends with the END

PROGRAM programname . In contrast to other programming languages is Fortran case insensitive.

It means that a variable name written in uppercase letters is the same variable as one written in lowercase letters. So let us write some code namely the first and last line in a program.

PROGRAM farenheit_to_centigrades

END PROGRAM farenheit_to_centigrades

Next is to declare the variables and perform the conversion.

PROGRAM farenheit_to_centigrades

IMPLICIT NONE

REAL

REAL

:: F ! Farenheit

:: C ! Centigrade

F = 75

C = (F - 32)*5/9

PRINT *, C

END PROGRAM farenheit_to_centigrades

So what have we been doing here??

After the first line we wrote IMPLICIT NONE . This means that we have to declare all the variables we use in the program. A heritage from the older versions of Fortran is the use of implicit declaration of variables. As a default all variables starting with the letter i

− n is automatically an integer and the rest of the alphabet is a real variable. The use of implicit declarations is a source of errors and should not be used. Next we declare the variable to hold the temperature in Farenheit ( F ) and also the same for the temperature in Centigrade ( C ). Note the construct !

Farenheit which tells the compiler that the rest of the line is a comment. Before we can start the computation we have to give the F variable a value, in this case 75 degrees Farenheit. Now that we have the temperature we simply calculate the corresponding temperature in degrees

Centigrade. To se the result of the calculation we simply writes the result to the computer screen using the PRINT * and the name of the variable containing the value we want so display.

Note that the PRINT * and the variable name C is separated with a comma.

All we have to do now is to compile the program and run it to see if it works. The process of compiling a program will be dealt with later, but the simplest form for compiling can be done like this: gfortran -o farenheit_to_centigrades farenheit_to_centigrades.f90

The name of the compiler is here gfortran where the -o tells the compiler to create the executable program with the name occurring after the -o and last the name of the file containing the source code. Note that all Fortran 95 source files has the ending .f90

. To run the program we simply type:

9

./farenheit_to_centigrades and then the result will be printed to the screen which in this case is 23 .

88889 . Note the use of ./ before the program name. This tells the command interpreter to use the program in the current directory.

3.2

What have we learned here??

We have learned

• Never to use implicit declarations

• How to declare real variables

• How to assign a value to a variable

• How to perform a simple calculation

• How to print a value to the screen

3.3

A small exercise

Rewrite the temperature conversion program to make the conversion the other way from Centigrade to Farenheit (Note: the formula for the conversion is F = C

·

5

9

+ 32 ).

10

4 A more userfriendly Fortran 95 program

In the previous section we made a program converting a temperture from Farenheit to Centigrades. It was not a very userfriendly program since we had to change the value of the Farenheit variable and recompile the program for each new temperature we would convert. We shall now try to make the program asking the user for a temperature before we perform the calculation.

4.1

Extending our program

Let us take the source code from our first program and simply add some new lines of code to ask the user for a temperature and then read the temperature from the keyboard.

PROGRAM farenheit_to_centigrades2

IMPLICIT NONE

! The temperature variables

REAL :: F ! Farenheit

REAL :: C ! Centigrade

! A text string

CHARACTER ( LEN =32) :: prompt

! The text that will be displayed prompt = ’Enter a temperature in Farenheit:’

! Display the prompt

PRINT *, prompt

! Get the input from the keyboard

! and put it into the F variable

READ (*,*) F

! Perform the calculation

C = (F - 32)*5/9

! Display the result

PRINT *, C

END PROGRAM farenheit_to_centigrades2

The text string prompt is used to contain a message to the user telling the user to enter a temperature in degrees Farenheit. To get the temperature entered by the user we simply use the READ(*,*) function to capture the input from the keyboard. The syntax (*,*) tells the program to retrieve the temperature form the keyboard. The first * identifies the input source as the keyboard and the second * tells the system to use default formatting of the input. The rest of the program is just like the first version where we hardcoded the temperature.

Again w compile the program like this: gfortran -o farenheit_to_centigrades2 farenheit_to_centigrades2.f90

To run the program we type:

./farenheit_to_centigrades2 and then the result will be printed to the screen with the Centigrade temperature corresponding to the Farenheit temperature.

4.2

What have we learned here??

We have learned

• To declare a character string

11

• To give the character string a value

• Displaying the contents of the character string

• Reading a number form the keyboard

4.3

A small exercise

Extend the program with code to ask if the temperature is in Centigrades or Farenheit and perform the conversion either way. In addition print a line of test telling if it is a conversion from Farenheit to Centigrade or from Centigrade to Farenheit.

Hint: The PRINT *, statement can take several variables as arguments to be displayed on the screen. The arguments are separated with a comma like this PRINT *, variable1, variable2, variable3 .

12

5 The use of arrays

In this compendium we use the name array for both a vector and a matrix. A one dimensional array is another name for a vector and an array with two or more dimensions is the same as a matrix with two or more dimensions. A one dimensional array can be seen as a set of containers where each container is an element in the array like in this figure

68.2 69.6 73.3 75.0 77.6 79.5 81.2

Here we have an array with seven elements each containing a value.

5.1

A one dimensional array example

Let us take the example array above and use the values as temperatures in degrees Farenheit.

We shall now program the array, give each element the value from the example and convert the values into degrees Centigrade stored in a separate array.

PROGRAM array1D

IMPLICIT NONE

! The temperature arrays

REAL , DIMENSION (7) :: farenheit

REAL , DIMENSION (7) :: centigrade

! Some help variables

! An index variable

INTEGER :: i

! Insert the temperature values into the

! farenheit array farenheit(1) = 68.2

farenheit(2) = 69.6

farenheit(3) = 73.3

farenheit(4) = 75.0

farenheit(5) = 77.6

farenheit(6) = 79.2

farenheit(7) = 81.2

! Performing the conversion using a loop

DO i = 1, 7 centigrade(i) = (farenheit(i) - 32)*5/9

END DO

! Print the value pairs for each element in the

! two arrays

DO i = 1, 7

PRINT *, ’Farenheit: ’ , farenheit(i), &

’ Centigrade: ’ , centigrade(i)

END DO

END PROGRAM array1D

The declaration of the temperature arrays is straight forward using the DIMENSION(7) parameter in addition to the datatype which is REAL in this case. To give the Farenheit temperature to each array element we have to use the element number to put the value in the right place. To convert the values we use a loop with an index variable taking the number from one to seven.

13

The loop is declared using the DO END DO construct. Note that for each time we go through the loop the index variable is incremented by one. When the index variable get a value larger than the given end value (seven in this case) the loop is terminated and the program continues from the line after the END DO statement.

Then again we compile the program gfortran -o array1D array1D.f90

and run it like this

./array1D

5.2

What have we learned here??

We have learned

• To declare a vector of real numbers

• To initialise the vector

• Perform calculations on each element of the array

• Printing the result of our calculations to the screen

5.3

A small exercise

Try to find out how we can print out every second element from the array starting with element number two.

14

6 The use of 2D arrays

Using a one dimensional array is quite simple. Using a two dimensional array is somewhat more complex, but we will take a look at how we do this here.

6.1

A two dimensional array example

Again we shall use a temperature array like in the previous example, but now we have temperature measurements from two different places. That means we will have to use a two dimensional array to store the temperatures from the two places. The array will look like this:

68.2

65.4

69.6

63.7

73.3

66.1

75.0

68.0

77.6

70.1

79.2

71.4

81.2

73.2

So let us go on programming it. First we will have to declare the arrays for both Farenheit and

Centigrade. Note that in this declaration we define the number of rows before the number of columns. In this case the number of rows is 7 and the number of columns is 2.

PROGRAM array2D

IMPLICIT NONE

! The temperature arrays

REAL , DIMENSION (7,2) :: farenheit

REAL , DIMENSION (7,2) :: centigrade

! Some help variables

! Two index variables

INTEGER

INTEGER

:: i

:: j

! Insert the temperature values into the

! farenheit array farenheit(1,1) = 68.2

farenheit(2,1) = 69.6

farenheit(3,1) = 73.3

farenheit(4,1) = 75.0

farenheit(5,1) = 77.6

farenheit(6,1) = 79.2

farenheit(7,1) = 81.2

farenheit(1,2) = 65.4

farenheit(2,2) = 63.7

farenheit(3,2) = 66.1

15

farenheit(4,2) = 68.0

farenheit(5,2) = 70.1

farenheit(6,2) = 71.4

farenheit(7,2) = 73.2

! Performing the conversion using a loop

DO j = 1, 2

DO i = 1, 7 centigrade(i,j) = (farenheit(i,j) - 32)*5/9

END DO

END DO

! Print the value pairs for each element in the

! two arrays

DO j = 1,2

DO i = 1, 7

PRINT *, ’Farenheit: ’ , farenheit(i,j), &

’ Centigrade: ’ , centigrade(i,j)

END DO

END DO

END PROGRAM array2D

Some explanation is needed here. First of all the structure of this array consists of two columns with seven rows in each column. Fortran always access the element column-wise. That is each row element of a column is accessed before we proceed to the next column. It is important to notice the use of the index variables. We have the first index in the innermost loop thus traversing the array as indicated in the ??

. Note also in the two lines containing the PRINT statement we use an ampersand & at the end of the first line in the statement. This is to tell the compiler that this line continues on the next line. In Fortran we do not have any begin end structure because Fortran is a line oriented language due to the use of punched cards as an input media.

✞ ☎

❄ ❄

Why is it so important to traverse the array in this manner? The answer has to do with how the computer is constructed. In the figure below we can see that the CPU (Central Processing Unit) get the data and instructions via a Cache memory which is almost as fast as the CPU . The Cache

16

memory get the data and instructions from the Main memory which is rather slow compared to the Cache memory and the CPU . To utilize the CPU as effective as possible the Cache memory has to be filled with as much relevant data as possible. If we traverse an array in the wrong direction we have to go to the slow Main memory to fetch the data we need for each iteration in the innermost loop. For very large arrays this is costly in CPU-cycles and will slow down the execution speed of the program.

CPU

Cache memory

Main memory

So now we see how important it is to traverse an array in the right manner to keep the CPU operating at full speed.

Then again we compile the program gfortran -o array2D array2D.f90

and run it like this

./array2D

6.2

What have we learned here??

We have learned

• To declare a two dimensional array of real numbers

• To initialise the array

• Perform calculations on each element of the array in a nested loop

• Printing the result of our calculations to the screen

6.3

A small exercise

Try to find out how we can print out only the elements from the first column in the array and program the solution.

17

7 Getting data from a text file

In the previous sections we have hardcoded the temperature in the program source. This is of course not what we normally do. The input data is usually stored in a file which can be an

ASCII-file which is a readable text file or in a binary file. We will use the ASCII-file type in our problem solving here.

7.1

How to read data from a file

The dataset containing the temperatures is stored in an ASCII-file with two real numbers separated by a space like this:

68.2 65.4

69.6 63.7

73.3 66.1

75.0 68.0

77.6 70.1

79.2 71.4

81.2 73.2

What we have to do here is to read the contents of each line into the corresponding array elements. To be able to read the contents of a file into an array we have to open the file just like we would open the drawer in a file cabinet to get access to the content of the drawer. We use the OPEN function with a set of arguments to do this. After the opening of the file we should always test to see if the opening was successful. If not we just terminate the program. To read the contents of the file w use the READ function with a set of arguments. Since we are reading the file one line at a time we have to use a loop to put the values into the correct position in the array. We again should test to see if the read operation was successful and close the file and terminate the program if an error occurred. Finally we close the file using the CLOSE function with one argument. The program code below illustrates how we can do this.

PROGRAM readtemp

IMPLICIT NONE

! The temperature arrays

REAL , DIMENSION (7,2) :: farenheit

REAL , DIMENSION (7,2) :: centigrade

! The filename

CHARACTER ( LEN =16) :: filename

! A unit number to reference the file

INTEGER , PARAMETER :: lun = 10

! Some help variables

! Two index variables

INTEGER :: i

INTEGER :: j

! A result variable

INTEGER :: res

! Set the filename into the variable filename = ’temperature.txt’

! Open the file for reading

OPEN ( UNIT =lun, FILE =filename, FORM = ’FORMATTED’ , &

IOSTAT =res)

18

! Test if the file was opened correctly

IF (res /= 0) THEN

! Print an error message

PRINT *, ’Error in opening file, status: ’ , res

! Stop the program

STOP

END IF

! Read the data into the farenheit array

DO i = 1, 7

READ ( UNIT =lun, FMT = ’(F4.1,X,F4.1)’ , IOSTAT =res) & farenheit(i,1), farenheit(i,2)

IF (res /= 0) THEN

PRINT *, ’Error in reading file, status: ’ , res

CLOSE ( UNIT =lun)

STOP

END IF

END

! Perform the conversion

DO j = 1, 2

DO i = 1, 7 centigrade(i,j) = (farenheit(i,j) - 32)*5/9

END DO

END DO

! Print the value pairs for each element in the

! two arrays

DO j = 1,2

DO i = 1, 7

PRINT *, ’Farenheit: ’ , farenheit(i,j), &

’ Centigrade: ’ , centigrade(i,j)

END DO

END DO

END PROGRAM readtemp

Some explanations might be useful. We already know how to declare variables, but in this program we also declare a constant value referred to by a name using this syntax INTEGER,

PARAMETER :: lun = 10 . Here the constant value is the number 10. Why, you ask, do we do it this way and not just write the number 10 instead of using the lun variable. The answer is to prevent errors in case we need to change the value. Using a constant we only have to change the value in one place and not everywhere in the source code as we had to do if we used the number in several places in the code. To open a file for reading we use the OPEN function with four keyword-argument pairs. The first argument is the unit number we use as a reference when we access the file later in the program preceded by the keyword UNIT= . The next is the filename again preceded by a keyword-argument pair which is here FILE=filename . The third argument is the type of file wer are opening. Here it is an ASCII file (a human readable text file) which the keyword-argument pair is FORM=’FORMATTED’ . The last keyword-argument pair is IOSTAT=res where the argument is the name of an integer variable which will contain the result of the opening of the file. Any number except zero marks a failure to open the file. The if test IF(res /= 0) returns true it means that the res variable contains an error number telling what went wrong in opening the file. The problem could for example be that the filename is misspelled or we are in a wrong directory or some other error situation. Then we proceed to

19

read the contents of the file into the farenheit array. We use the function READ with a number of keyword-argument pairs. The first is the same as for the OPEN function. The next describes how the format is for the values in the input file. Here the format of the file is FMT=’(F4.1,X,F4.1)’ which means that we have two real numbers on each line in the file. The numbers have a total of 4 positions each including the decimal point and with one decimal which is denoted by F4.1

.

Each number is separated by a character which is just one space character and is referred to by the letter X which tells the system to ignore this character in the reading process. Note that we put the first number in the first column of the farenheit array and the second number in the second column. The index variable points to the corresponding row in the array while we here are hardcoding the second index. Note also the use of an ampersand & at the end of the line containing the PRINT statement and the one at the beginning of the next line. This is the syntax allowing a text constant to span over more than one line.

7.2

What have we learned here??

We have learned

• We have declared a constant using the PARAMETER keyword

• The process of opening a file for reading, specifying the filetype and test if the opening process was a success

• In order to read the contents of the file we have passed the format specifications to the

READ function and storing the values into the farenheit array. Then testing for errors in the reading process

• To have a text constant span more than one line using two ampersands one at the end of the first line and the second at the beginning of the next line.

7.3

A small exercise

Change the program to just print out the first column of the centigrade array

20

8 Programming formulas

Now that we have learned to read a set of data into an array we will start to program some functions which will operate on the dataset.

8.1

What is a function in Fortran?

A function in Fortran 95 is just like a mathematical function. It receives one or more input arguments and returns a result. One such function we shall program is the mean function which will return the mean value of the argument which will have to be a 1D array. The mathematical formula for the mean value is n

¯ =

1 n

·

X x i

(1) i =1

The skeleton of our mean value function for a dataset can be like this:

FUNCTION mean(arg) RESULT (res)

IMPLICIT NONE

! Input array

REAL , DIMENSION (7) :: arg

! The result of the calculations

REAL :: res

! Index variable

INTEGER :: i

! Temporary variable

REAL :: tmp

! Initialize the temporary variable tmp = 0.

DO i = 1, 7 tmp = tmp + arg(i)

END DO res = tmp/7.

RETURN

END FUNCTION mean

All functions declared in Fortran begins with the keyword FUNCTION with the name of the function and the input argument enclosed in parenthesis. The type of value returned by the function is defined by the datatype in the RESULT(res) statement. Note that we have hardcoded the length of the input argument. This is of course not desirable, but as an example on how we program a function it will be ok for now. Note the initialising of the tmp variable where we use the construct tmp = 0.

where the decimal point after the number is necessary to tell the compiler that we have a real number and not an integer. The same notion goes for the division at the end of the function. The RETURN stement returns the program to the calling process. In this case it will be our main program. Let us change our program to incorporate the calculation of the mean function.

PROGRAM readtemp

IMPLICIT NONE

! The temperature arrays

REAL , DIMENSION (7,2) :: farenheit

REAL , DIMENSION (7,2) :: centigrade

! The filename

21

CHARACTER ( LEN =16) :: filename

! A unit number to reference the file

INTEGER , PARAMETER :: lun = 10

! External declaration of the mean function

REAL , EXTERNAL :: mean

! The variable to hold the mean value

REAL ::mvalue

! Some help variables

! Two index variables

INTEGER

INTEGER

:: i

:: j

! A result variable

INTEGER :: res

! Set the filename into the variable filename = ’temperature.txt’

! Open the file for reading

OPEN ( UNIT =lun, FILE =filename, FORM = ’FORMATTED’ , &

IOSTAT =res)

! Test if the file was opened correctly

IF (res /= 0) THEN

! Print an error message

PRINT *, ’Error in opening file, status: ’ , res

! Stop the program

STOP

END IF

! Read the data into the farenheit array

DO i = 1, 7

READ ( UNIT =lun, FMT = ’(F4.1,X,F4.1)’ , IOSTAT =res) & farenheit(i,1), farenheit(i,2)

IF (res /= 0) THEN

PRINT *, ’Error in reading file, status: ’ , res

CLOSE ( UNIT =lun)

STOP

END IF

END

! Perform the conversion

DO j = 1, 2

DO i = 1, 7 centigrade(i,j) = (farenheit(i,j) - 32)*5/9

END DO

END DO

! Print the value pairs for each element in the

! two arrays

DO j = 1,2

DO i = 1, 7

PRINT *, ’Farenheit: ’ , farenheit(i,j), &

’ Centigrade: ’ , centigrade(i,j)

END DO

END DO

22

! Calculate the mean value of the first column

! of the centigrade array mvalue = mean(centigrade(:,1))

! Print the mena value

PRINT *, ’The mean value of the first column in centigrade’ ,& mvalue

END PROGRAM readtemp

All functions that is not a Fortran intrinsic function has to be declared as an external function.

This is done using the EXTERNAL parameter in addition to the type of data returned by the function. In addition to declare a variable to contain the result of the calling of our mean function we just add one line where we set the mvalue equal to the function mean and just use the centigrade(:,1) to send the values from the first column to the mean function.

8.2

What have we learned here??

We have learned

• To create a function with one input argument and a result

• That all non Fortran intrinsic functions has to be declared external in the procedure calling the function

• To call a function in our main program

8.3

A small exercise

Extend the program to print out the mean from both series and calculate the difference of the two mean values and print the results to the screen

23

9 Programming formulas using functions

Having a working function for the mean we shall now start to write other functions which will use functions we already have programmed

9.1

Programming the standard deviation function

Th function we shall write is the standard deviation function. The mathematical formula for the standard deviation is

σ = v u u t

1

N

N

X i =1

( x i −

µ ) 2 .

(2)

Note that the standard deviation function also uses the mean value of the dataset in the calculation. So we will then call our mean function from within the standard deviation function which we will call std . The skeleton of our standard deviation function can be like this:

FUNCTION std(arg) RESULT (res)

IMPLICIT NONE

! Input array

REAL , DIMENSION (7) :: arg

! The result of the calculations

REAL :: res

! The mean function

REAL , EXTERNAL

! Index variable

:: mean

INTEGER :: i

! Temporary variables

REAL :: tmp

REAL :: tmpmean

! Calculate the mean value tmpmean = mean(arg)

! Initialise the tmp variable tmp = 0.

! Calculate the standard deviation

DO i = 1, 7 tmp = tmp + (arg(i) - tmpmean)**2

END DO

! Divide the result by the number of elements tmp = tmp / 7.

! Take the square root of the tmp res = SQRT(tmp)

! Return to calling process

RETURN

END FUNCTION std

We introduce a couple of new things in the std function. First we declare the mean function as an external function of type REAL . A new variable is used to hold the mean value of the dataset ( tmpmean ). Calling the mean returns the mean value of the input argument. Then we loop through all the elements in the input argument and sum up the square of the element value minus the mean value. Note the use of the ** to denote the square of the value. We divide the sum by the number of elements and finally we take the square root of the value and place it in

24

the res variable using the Fortran intrinsic function SQRT . Note that we do not have to declare the SQRT function as an external function since it is one of a set of intrinsic functions in Fortran.

Let us change our program to incorporate the calculation of the std function.

PROGRAM readtemp

IMPLICIT NONE

! The temperature arrays

REAL , DIMENSION (7,2) :: farenheit

REAL , DIMENSION (7,2) :: centigrade

! The filename

CHARACTER ( LEN =16) :: filename

! A unit number to reference the file

INTEGER , PARAMETER :: lun = 10

! External declaration of the mean function

REAL , EXTERNAL :: std

! The variable to hold the mean value

REAL ::mvalue

REAL ::stdvalue

! Some help variables

! Two index variables

INTEGER

INTEGER

:: i

:: j

! A result variable

INTEGER :: res

! Set the filename into the variable filename = ’temperature.txt’

! Open the file for reading

OPEN ( UNIT =lun, FILE =filename, FORM = ’FORMATTED’ , &

IOSTAT =res)

! Test if the file was opened correctly

IF (res /= 0) THEN

! Print an error message

PRINT *, ’Error in opening file, status: ’ , res

! Stop the program

STOP

END IF

! Read the data into the farenheit array

DO i = 1, 7

READ ( UNIT =lun, FMT = ’(F4.1,X,F4.1)’ , IOSTAT =res) & farenheit(i,1), farenheit(i,2)

IF (res /= 0) THEN

PRINT *, ’Error in reading file, status: ’ , res

CLOSE ( UNIT =lun)

STOP

END IF

END

! Perform the conversion

DO j = 1, 2

DO i = 1, 7 centigrade(i,j) = (farenheit(i,j) - 32)*5/9

25

END DO

END DO

! Print the value pairs for each element in the

! two arrays

DO j = 1,2

DO i = 1, 7

PRINT *, ’Farenheit: ’ , farenheit(i,j), &

’ Centigrade: ’ , centigrade(i,j)

END DO

END DO

! Calculate the mean value of the first column

! of the centigrade array mvalue = mean(centigrade(:,1))

! Print the mean value

PRINT *, ’The mean value of the first column in centigrade’ ,& mvalue

! Calculate the standard deviation value stdvalue = std(centigrade(:,1))

! Print the std value

PRINT *, ’The standard deviation value of the first column &

& in centigrade’ , stdvalue

END PROGRAM readtemp

9.2

What have we learned here??

We have learned

• To make a function use another function which we have written

• To use the square syntax ** to calculate the square of a number

• To take the square root of a value using the SQRT Fortran intrinsic function

9.3

A small exercise

Extend the program to print out the standard deviation from both series and calculate the difference of the two values and print the results to the screen.

26

10 Programming subroutines

Now that we have learned to program functions we will take a look at subroutines which is in many ways like a function .

10.1

What is a subroutine in Fortran?

A subroutine in Fortran 95 is just like a function . It receives one or more input arguments, but returns no result. Instead any returning value is passed through one or more arguments.

We shall now take a look at a subroutine that calculates the mean value of the argument which will have to be a 1D array just like the mean function.

SUBROUTINE mean(arg,res)

IMPLICIT NONE

! Input array

REAL , DIMENSION (7), INTENT ( IN ) :: arg

! The result of the calculations

:: res REAL , INTENT ( OUT )

! Index variable

INTEGER :: i

! Temporary variable

REAL :: tmp

! Initialize the temporary variable tmp = 0.

DO i = 1, 7 tmp = tmp + arg(i)

END DO res = tmp/7.

RETURN

END SUBROUTINE mean

All subroutines declared in Fortran begins with the keyword SUBROUTINE with the name of the subroutine and the input and output arguments enclosed in parenthesis. The result is returned int th res argument. Note the use of the keywords INTENT(IN) and INTENT(OUT) which will flag an error if we try to write something in the variable with the parameter INTENT(IN) and likewise try to read something from the variable with the parameter INTENT(OUT) . The internal functionality is the same as for the mean function. We have only two changes in the main program to be able to use the subroutine. First we remove the line declaring the external mean function. Second we change the line calling the function with the construct CALL mean(centigrade(:,1),res) .

PROGRAM readtemp

IMPLICIT NONE

! The temperature arrays

REAL , DIMENSION (7,2) :: farenheit

REAL , DIMENSION (7,2) :: centigrade

! The filename

CHARACTER ( LEN =16) :: filename

! A unit number to reference the file

INTEGER , PARAMETER :: lun = 10

! The variable to hold the mean value

REAL ::mvalue

27

! Some help variables

! Two index variables

INTEGER

INTEGER

:: i

:: j

! A result variable

REAL :: res

! Set the filename into the variable filename = ’temperature.txt’

! Open the file for reading

OPEN ( UNIT =lun, FILE =filename, FORM = ’FORMATTED’ , &

IOSTAT =res)

! Test if the file was opened correctly

IF (res /= 0) THEN

! Print an error message

PRINT *, ’Error in opening file, status: ’ , res

! Stop the porgram

STOP

END IF

! Read the data into the farenheit array

DO i = 1, 7

READ ( UNIT =lun, FMT = ’(F4.1,X,F4.1)’ , IOSTAT =res) & farenheit(i,1), farenheit(i,2)

IF (res /= 0) THEN

PRINT *, ’Error in reading file, status: ’ , res

CLOSE ( UNIT =lun)

STOP

END IF

END

! Perform the conversion

DO j = 1, 2

DO i = 1, 7 centigrade(i,j) = (farenheit(i,j) - 32)*5/9

END DO

END DO

! Print the value pairs for each element in the

! two arrays

DO j = 1,2

DO i = 1, 7

PRINT *, ’Farenheit: ’ , farenheit(i,j), &

’ Centigrade: ’ , centigrade(i,j)

END DO

END DO

! Calculate the mean value of the first column

! of the centigrade array

CALL mean(centigrade(:,1),res)

! Print the mean value

PRINT *, ’The mean value of the first column in centigrade’ ,& mvalue

END PROGRAM readtemp

28

10.2

What have we learned here??

We have learned

• To create a subroutine with one input argument and an argument to hold the result of the calculations

• That we can add optional parameters to prevent overwriting values in an argument and to prevent unintentionally reading a value from an argument meant for writing

• To call a subroutine in our main program

29

11 Programming formulas using subroutines

11.1

Programming a subroutine calling another subroutine

Th subroutine we shall write has the same functionality as the standard deviation function. Note that the standard deviation subroutine will use the mean value subroutine from the previous section.

SUBROUTINE std(arg,res)

IMPLICIT NONE

! Input array

REAL , DIMENSION (7) :: arg

! The result of the calculations

REAL :: res

! Index variable

INTEGER :: i

! Temporary variables

REAL

REAL

:: tmp

:: tmpmean

! Calculate the mean value

CALL mean(arg,tmpmean)

! Initialise the tmp variable tmp = 0.

! Calculate the standard deviation

DO i = 1, 7 tmp = tmp + (arg(i) - tmpmean)**2

END DO

! Divide the result by the number of elements tmp = tmp / 7.

! Take the square root of the tmp res = SQRT(tmp)

! Return to calling process

RETURN

END SUBROUTINE std

Like in the previous section we just change a couple of lines both in the main program and in the std subroutine

PROGRAM readtemp

IMPLICIT NONE

....

! Calculate the standard deviation value

CALL std(centigrade(:,1),stdvalue)

! Print the std value

PRINT *, ’The standard deviation value of the first column &

& in centigrade’ , stdvalue

END PROGRAM readtemp

30

11.2

What have we learned here??

We have learned

• To make a subroutine use another subroutine which we have written

31

12 Introduction to modules

In the previous sections we have used programs, functions and subroutines to solve our computing problems. With less complex problems this is an ok approach, but when the problem increases in complexity we have to change the way we break down the problem and start to look into the data structures and procedures. To illustrate this we will take the program reading water flow values and the date as the number of days after 01.01.0000 and make a global data structure with procedures working on the global data structures.

To facilitate this we will introduce the Module which is a structure with variable declarations and procedure declarations. We start breaking down the problem by looking at the data structure. We know that the file contains one line with the number of lines with the value pairs (date and water flow) and a line with the description of each column in the file. We need then one variable to store the number ov value pairs and two single dimension arrays to store the date and water flow. We have to have these array allocatable since we do not know in advance the number of value pairs. In addition we need a variable to hold the filename, the ASCII -digits, the unit number, a status variable and a loop variable. The code snippet below shows a working module containing the necessary data structure to solve our problem.

MODULE flowdata

IMPLICIT NONE

INTEGER

INTEGER

INTEGER

, PARAMETER

CHARACTER ( LEN =80)

CHARACTER ( LEN =80)

:: lun = 10

:: res

:: flength

:: filename

:: cbuffer

INTEGER :: c_position, string_length

INTEGER , ALLOCATABLE , DIMENSION (:) :: dates

REAL , ALLOCATABLE , DIMENSION (:) :: water_flow

END MODULE flowdata

In addition to the global data structure we need the procedures to get the data into the variables.

The code below shows how we declare the subroutine to read the number of value pairs into the arrays dates and water_flow variable. the arrays.

MODULE flowdata

IMPLICIT NONE

....

CONTAINS

SUBROUTINE read_data()

IMPLICIT NONE

INTEGER :: i

OPEN ( UNIT =lun, FILE =filename, FORM = ’FORMATTED’ , IOSTAT =res)

READ ( UNIT =lun, FMT = ’(A)’ , IOSTAT =res) cbuffer string_length = LEN_TRIM(cbuffer) c_position = INDEX(cbuffer, ’:’ )

READ (cbuffer(c_position+1:string_length), FMT = ’(I15)’ ) flength

ALLOCATE (dates(flength), STAT =res)

ALLOCATE (water_flow(flength), STAT =res)

READ ( UNIT =lun, FMT = ’(A)’ , IOSTAT =res) cbuffer

DO i = 1, flength

READ ( UNIT =lun, FMT = ’(I6,X,F6.3)’ , IOSTAT =res) dates(i), water_flow(i)

32

END DO

CLOSE ( UNIT =lun)

END SUBROUTINE read_data

....

END MODULE flowdata

A little explanation is in place here. First we separate the declarations of the global variables from the procedure declarations with the keyword CONTAINS . Since the variables with the exception of the index variable in the do-loop are global we can use them directly in the subroutine. The subroutine is declared as usual, but is now residing inside the module. In the same manner we shall proceed with the rest of the necessary procedures in order to extract the data we need from the arrays. We will of course need a function to convert a date to an integer according to the date array in the file. The date should have the format dd-mm-yyyy and the result an integer containing the number of days from 01.01.0000. To make this correct we also need the function leapyear which we solved in the section about functions. In the code below we have the first part of the function date2number .

MODULE flowdata

IMPLICIT NONE

....

CONTAINS

....

FUNCTION date2number(datestr) RESULT (datenum)

IMPLICIT NONE

CHARACTER

INTEGER

INTEGER

INTEGER

INTEGER

( LEN =10)

INTEGER

INTEGER , DIMENSION (12) marray = 31 marray(2) = 28 marray(4) = 30 marray(6) = 30 marray(9) = 30 marray(11) = 30

....

datenum = 0

END MODULE flowdata

:: datestr

:: datenum

:: i

:: year

:: month

:: day

:: marray

This function has a character string as an input argument and an integer as the result. The input argument is in the format of dd-mm-yyyy . the array marray will contain the number of days in each month. Note that we use the marray = 31 to initialise the whole array with the value of 31. Then we just modify the elements for the moths with fewer than 31 days afterwards.

We also initialise the resulting variable to zero so we can add the number of days to it for each iteration.

MODULE flowdata

IMPLICIT NONE

....

READ (datestr(1:2), FMT = ’(I2)’ ) day

33

READ (datestr(4:5), FMT = ’(I2)’ ) month

READ (datestr(7:10), FMT = ’(I4)’ ) year

DO i = 0, year-1

IF (.NOT. leapyear(i)) THEN datenum = datenum + 365

ELSE datenum = datenum + 366

END IF

END DO

DO i = 1,month-1 datenum = datenum + marray(i)

END DO datenum = datenum + day

IF (leapyear(year)) THEN datenum = datenum + 1

END IF

END FUNCTION date2number

....

END MODULE flowdata

Note that we use the READ function to extract part of the input argument and store the binary value in the variables day , month and year . Now we can begin to take a look at the part that extracts the indexes of a subset of the arrays dates and water_flow . We call this subroutine find_indexes . Using this subroutine to find the indexes of the subset we can in our main program access the subset of the water_flow data using the start date and end date of the subset. The code below shows how this can be done.

MODULE flowdata

IMPLICIT NONE

....

SUBROUTINE find_indexes(start_date, end_date, start_index, end_index)

CHARACTER ( LEN =10), INTENT ( IN ) :: start_date

CHARACTER ( LEN =10), INTENT ( IN ) :: end_date

INTEGER , INTENT ( OUT ) :: start_index

INTEGER , INTENT ( OUT ) :: end_index

INTEGER

INTEGER

INTEGER

:: sday

:: eday

:: i

PRINT *, start_date

PRINT *, end_date sday = date2number(start_date) eday = date2number(end_date)

PRINT *, sday, eday start_index = 0 end_index = 0

DO i = 1, flength

IF (sday == dates(i)) THEN start_index = i

END IF

IF (eday == dates(i)) THEN

34

end_index = i

EXIT

END IF

END DO

END SUBROUTINE find_indexes

....

END MODULE flowdata

12.1

Exercises

1. Write a main program using the module flowdata to read the values of the input file, extract data ranging from the date 01.03.1989 to the date 31.05.1989 and display the contents of the water flow on the screen.

2. Take the main program and add code to write the extracted data to a new file. Then use a program to plot the extracted data (you can use any plotting program available for you).

3. Add code to the main program to have a user interface asking for the start and end date.

4. Run the new main program and extract the data from 01.01.1989 to 31.08.1990 which is what is called a hydrological year and plot the extracted data. Look at the plot and try to explain the variations in the water flow.

35

13 Introduction to Object Oriented Programming

It is very likely that you have heard the notion Object Oriented Programming or OOP for short.

An excerpt from Wikipedia explains the various parts of the notion of OOP as:

Object-oriented programming (OOP) is a programming paradigm using "objects", i.e. data structures consisting of data fields and methods together with their interactions, to design applications and computer programs. Programming techniques may include features such as data abstraction, encapsulation, polymorphism, and inheritance.

Data abstraction is explained as:

In computer science, abstraction is the process by which data and programs are defined with a representation similar in form to its meaning (semantics), while hiding away the implementation details. Abstraction tries to reduce and factor out details so that the programmer can focus on a few concepts at a time .

Encapsulation is explained as:

In a programming language encapsulation is used to refer to one of two related but distinct notions, and sometimes to the combination thereof:

• A language mechanism for restricting access to some of the object’s components.

• A language construct that facilitates the bundling of data with the methods (or other functions) operating on that data.

The notion of polymorphism is:

Subtype polymorphism, almost universally called just polymorphism in the context of objectoriented programming, is the ability to create a variable, a function, or an object that has more than one form .

Last we have the inheritance which is explained as:

In object-oriented programming (OOP), inheritance is a way to reuse code of existing objects, establish a subtype from an existing object, or both, depending upon programming language support. In classical inheritance where objects are defined by classes, classes can inherit attributes and behaviour (i.e., previously coded algorithms associated with a class) from pre-existing classes called base classes or superclasses or parent classes or ancestor classes. The new classes are known as derived classes or subclasses or child classes.

Having explained a few things about OOP we are now ready to try out the theory in solving a known problem by using the OOP technique.

In the previous section we used derived data types to encapsulate several variables that was connected to a data structure. We will now use this and change the code putting everything into an object which can have several instances with different data values. The object is a part of a module where the type is declared as usual, but with one exception. The type also has a contains statement where the names of the procedures to be used is declared. The code snippet shows how this is done.

MODULE class_terrain

IMPLICIT NONE

PRIVATE

TYPE , PUBLIC

CHARACTER ( LEN =80)

:: terrain

:: filename

36

INTEGER , POINTER

INTEGER

INTEGER

INTEGER

INTEGER

CONTAINS

PROCEDURE

PROCEDURE

END TYPE terrain

CONTAINS

....

END MODULE class_terrain

:: map(:,:) => null()

:: n_cols = 0

:: n_rows = 0

:: cell_size = 0

:: no_value = 0

:: load => load_data

:: dump => dump_data

The first difference from the usual declaration of the type is that we do not enclose the type name in a set of parenthesis, but use the syntax of an ordinary variable declaration. Note also that we have introduces the PRIVATE keyword which means that anything not specific declared as

PUBLIC is not accessible from outside the module. This is done to prevent procedures not part of the module to make unintentionally changes to values inside the module. The declaration of a procedure with the construct fload => load_data gives an alias for the procedure load_data declared after the contains keyword in the module. In addition we initialise the pointer to a NULL value using the construct map(:,:) => null() . Also the other variables are getting initialised so we can use the intrinsic constructor to create an instance of the object without having to supply values for each variable in the object.

PROGRAM ctest

USE class_terrain

IMPLICIT NONE

TYPE (terrain)

....

td1 = terrain( ’demfile.dat’ )

CALL td1%fload()

....

END PROGRAM ctest

:: td1

Declaring a new object of the terrain type is done the normal way. Then we call the implicit constructor td1 = terrain(’demfile.dat’) where we give the input filename as the only argument.

If we had not initialised the internal variables in the declaration of the type we would have to use td1 = terrain(’demfile.dat’, null(),0,0,0,0) . Omitting these last arguments to the constructor and not having the internal variables initialised in advance, the program would not compile.

Having set up the object and learned how to use the constructor to create an instance of the object we will now take a look at how the rest of the module can be programmed. Using the module from the previous section we now that we need at least two procedures, one to read the file, allocate space etc. and one to take the header lines and extract the values we need to allocate space for the terrain map.

CONTAINS

....

SUBROUTINE load_data(this)

CLASS(terrain)

INTEGER , DIMENSION (4)

INTEGER , PARAMETER

CHARACTER ( LEN =512), DIMENSION (4)

:: this

:: vars

:: u_number = 10

:: buffer

37

INTEGER

INTEGER

....

INTEGER , EXTERNAL

END SUBROUTINE load_data

:: b_start, b_end

:: i, j, k, l, res

:: a2i

The only argument to the load_data subroutine is a class variable of type terrain named this .

The variable is an instance of the class of type terrain and contains the filename, the pointer array and the four integer values. The variables local to this subroutine is used to extract the contents of the file into the variables of the class instance. So let us take a look at the rest of the code for this subroutine.

SUBROUTINE load_data(this)

....

OPEN ( UNIT =u_number, FILE =this%filename, FORM = ’FORMATTED’ , IOSTAT =res) buffer = ’ ’

DO i = 1, 4

READ ( UNIT =u_number, FMT = ’(A)’ , IOSTAT =res) buffer(i)

IF (res /= 0) THEN

PRINT *, ’Error in reading line 1, status ’ , res

CLOSE ( UNIT =u_number)

RETURN

END IF

END DO

....

END SUBROUTINE load_data

The only difference from this version of the subroutine and the one in the previous section is that we precede the name of the variables with this% just like we would for a non object derived type. Of course the local variables for the subroutine is not preceded by this% . In addition we use an array of character strings to hold the header lines in stead of a single character string and another array to hold the integer values extracted from the header lines.

SUBROUTINE load_data(this)

....

CALL extract_header_lines(buffer,vars) this%n_cols = vars(1) this%n_rows = vars(2) this%cell_size = vars(3) this%no_value = vars(4)

ALLOCATE (this%map(this%n_rows,this%n_cols), STAT =res)

IF (res /= 0) THEN

PRINT *, ’Allocation failure, status ’ , res

CLOSE ( UNIT =u_number)

END IF

....

END SUBROUTINE load_data

Here we have modified the extract_header_lines subroutine to take two arrays as input arguments. After extracting the values from the header lines we copy them into the various internal variables for this class instance. The rest of the subroutine is the same as the one in the previous section.

38

The next part we have to program is the dump_data subroutine. It can be constructed like the code snippet below.

SUBROUTINE dump_data(this)

CLASS(terrain) :: this

INTEGER

INTEGER

, PARAMETER :: u_number = 11

:: i, j, res

OPEN ( UNIT =u_number, FILE =this%filename, FORM = ’FORMATTED’ , IOSTAT =res)

WRITE ( UNIT =u_number, FMT = ’(I5)’ , IOSTAT =res) this%n_cols

WRITE ( UNIT =u_number, FMT = ’(I5)’ , IOSTAT =res) this%n_rows

WRITE ( UNIT =u_number, FMT = ’(I5)’ , IOSTAT =res) this%cell_size

WRITE ( UNIT =u_number, FMT = ’(I5)’ , IOSTAT =res) this%no_value

DO i = 1, this%n_rows

DO j = 1, this%n_cols

WRITE ( UNIT =u_number, FMT = ’(I4,A2)’ , ADVANCE = ’NO’ , IOSTAT =res) & this%map(i,j), ’, ’

END DO

WRITE ( UNIT =u_number, FMT = ’(A)’ , IOSTAT =res) ’ ’

END DO

CLOSE ( UNIT =u_number)

END SUBROUTINE dump_data

Like the subroutine load_data we have only the instance of the object as an input argument.

After opening the file we write the four header variables to the file, but in this version we do not use any preceding header text. Then we loop and write each value for the columns separated with a comma to the file. Note the use of ADVANCE=’NO’ which prevents the automatic line feed after each write. To write the column values for the next line we simply write a space character to the file without the ADVANCE=’NO’ to get a new line.

13.1

Exercises

1. Change the dump_data subroutine so that the header lines contains the ncols, nrows, cell_size and no_value text strings in addition to the values.

2. Write a subroutine to clear the contents of an object instance using the same type of argument as the load_data and the dump_data subroutines. Note that the map array has to be deallocated in order so have a completely clean object instance.

3. Extend the main program with code to create a new instance with a different name and copy the contents of the first instance into the new one. Hint: you have to allocate the space for the map array in the new instance before you can copy the contents from the first instance.

39

A Operators

Operators in Fortran are for example IF(a > b) THEN which is a test using numerical values in the variables. For other types of variables like characters and character strings we use the construct IF(C1 .GT. C2) THEN .

A.1

Overview of the operators

Table3 gives an overview of the operators

Numerical Other Explanation

-

==

/=

<

>

<=

>=

**

*

/

+

.EQ.

.NE.

.LT.

Exponentiation

Multiplication

Division

Addition

Subtraction

Equal

Not equal

Less

.GT.

Greater

.LE.

Less or equal

.GE.

Greater or equal

.NOT.

Negation, complement

.AND.

Logical and

.OR.

Logical or

.EQV.

Logical equivalence

.NEQV.

Logical not equivalence, exclusive or

Table 3: Logical operators

40

B Intrinsic functions in Fortran 95

Intrinsic functions in Fortran are functions that can be used without referring to them via include files like in other languages where functions has to be declared before beeing used in the program

B.1

Intrinsic Functions

Table4, table5 and table6 gives an overview of the intrinsic functions in Fortran 95

Function

ABS

ACHAR

ACOS

ADJUSTL

ADJUSTR

AIMAG

AINT

ALL

ALLOCATED

ANINT

ANY

ASIN

ASSOCIATED

ATAN

ATAN2

BIT_SIZE

BTEST

CEILING

CHAR

CMPLX

CONJG

COS

COSH

COUNT

CPU_TIME

CSHIFT

DATE_AND_TIME

DBLE

DIGITS

DIM

DOT_PRODUCT

DPROD

EOSHIFT

EPSILON

EXP

EXPONENT

FLOOR

FRACTION

HUGE

IACHAR

IAND

IBCLR

IBITS

IBSET

ICHAR

IEOR

INDEX

Argument Result

Integer real complex

Integer

Real

Integer real complex

Character

Real

Character string Character string

Character Character

Complex

Real

Real

Real

Logical mask, dim Logical

Array Logical

Real Real

Logical mask, dim Logical

Real

Pointer

Real

Real

Logical

Real

X=Real, Y=Real Real

Integer Integer

I=Integer, Logical

Pos=Integer

Real

Integer

Real

Character

X=Real,Y=Real Complex

Complex

Real Complex

Complex

Real complex

Real Real

Logical mask, dim Integer

Real Real

Array, shift, dim Array

Char D,T,Z,V

Integer real complex

Character

Double precision

Integer real

Integer real

Integer

Integer real

X=Real, Y=Real Real

X=Real, Y=real Double precision

Array, shift, boundary, dim

Array

Real

Real complex

Real

Real

Real

Real

Real complex

Integer

Real

Real

Integer real

Character

Integer, Integer

Integer, pos

Integer real

Integer

Integer

Integer

Integer, pos,len

Integer, pos

Character

Integer, integer

Integer

Integer

Integer

Integer

String, substring Integer

Explanation

The absolute value

Integer to ASCII character

Arcuscosine

Left adjustment

Right adjustment

Imaginary part

Truncate to a whole number

True if all elements == mask

True if allocated in memory

Round to nearest integer

True if all elemnts == mask

Arcsine

True if pointing to target

Arctangent

Arctangent

Number of bits in argument

Test a bit of an integer

Leat integer <= argument

Integer to ASCCI character

Convert to complex number

Conjugate the imaginary part

Cosine

Hyperbolic cosine

Count of true entries in mask

Returns the processor time

Circular shift of elements

Realtime clock

Convert to double precision

Number of bits in argument

Difference operator

Dot product

Double precision dot prod.

Array element shift

Smallest positive number

Exponential

Model exponenet of argument

Integer <= argument

Fractional pert of argument

Largest number

Integer value of argument

Bitwise logical and

Setting bit in pos = 0

Extract len bits from pos

Set pos bit to one

ASCII number of argument

Bitwise logical XOR

Position of substring

Table 4: Intrinsic functions

Function

INT

Argument

Integer real complex

Result

Integer

Explanation

Convert to integer

IOR

ISHFT

ISHFTC

KIND

LBOUND

LEN

LEN_TRIM

LGE

LGT

LLE

LLT

LOG

LOG10

LOGICAL

MATMUL

MAX

MAXEXPONENT

MAXLOC

MAXVAL

MERGE

MIN

MINEXPONENT

MINLOC

MINVAL

MOD

MODULO

MVBITS

NEAREST

NINT

Integer, integer

Integer, shift

Integer

Integer

Integer, shift Integer

Any intrinsic type Integer

Array, dim

Character

Character

A,B

Integer

Integer

Integer

Logical

A,B

A,B

A,B

Real complex

Real

Logical

Matrix, matrix a1, a2, a3,...

Logical

Logical

Logical

Real complex

Real

Logical

Vector matrix

Integer real

Real

Array

Integer

Integer vector

Maximum exponent

Indices in array of max value

Array, dim, mask Array element(s)

Tsource, Fsource, mask

Maximum value

Tsource or Fsource Chosen by mask a1, a2, a3,...

Real

Integer real

Integer

Array Integer vector

Array, dim, mask Array element(s) a=integer real,p Integer real a=integer real,p Integer real

From pos to pos Integer

Real, direction

Real, kind

Real

Real

Bitwise logical OR

Shift bits by shift

Shift circular bits in argument

Value of the kind

Smallest subscript of dim

Number of chars in argument

Length without trailing space

String A <= string B

String A > string B

String A <= string B

String A < string B

Natural logarithm

Logarithm base 10

Convert between logical

Matrix multiplication

Maximum value of args

Minimum value

Minimum exponent

Indices in array of min value

Minimum value a modulo p a modulo p

Move bits

Nearest value in direction

Round to nearest integer value

NOT

PACK

PRECISION

PRESENT

PRODUCT

Integer

Array, mask

Real complex

Argument

Integer

Vector

Integer

Logical

Array, dim, mask Integer real complex

RADIX Integer real

RANDOM_NUMBER Harvest = real

RANDOM_SEED Size, put or get

Integer

Real 0 <

Nothing

= x < = 1

Bitwise logical complement

Vector of array elements

Decimal precision of arg

True if optional arg is set

Product along dim

Radix of integer or real

Subroutine returning a random number in harvest

Subroutine to set a random

RANGE Integer real number seed

Decimal exponent

REAL

REPEAT

Integer real complex

Integer real complex

String, ncopies

Real

String

Convert to real type

Concatenate n copies of string

Table 5: Intrinsic functions

Function

RESHAPE

Argument

Array, pad, order

Real shape,

Result

Array

Explanation

Reshape source array to array

RRSPACING

SCALE

SCAN

SET_EXPONENT

SHAPE

SIGN

SIN

SINH

SIZE

SPACING

SPREAD

SQRT

SUM

SYSTEM_CLOCK

TAN

TANH

TINY

TRANSFER

TRANSPOSE

TRIM

UBOUND

UNPACK

VERIFY

Real, integer

SELECTED_INT_KIND Integer

SELECTED_REAL_KIND Integer

Real,integer

Real

Real

String, set, back Integer

Integer

Integer

Resl

Reciprocal of relative spacing of model

Returns X

· b I

Position of first of set in string

Kind number to represent digits

Kind number to represent digits

Set an integer as exponent of a real X

· bI

− e

Vector of dimension sizes

Absolute value of A

·

B

Array

Integer real, integer real

Real complex

Real

Array, dim

Real

Source, copies

Real complex dim, Array

Array, dim, mask Integer real complex

Count, count count,

Real complex

Trhough the arguments

Real

Real

Real

Real

Real

Real

Source, mold, size Mold type

Matrix

String

Array, dim

Vector, field mask,

Integer vector

Integer real

Real complex

Real

Integer

Real

Matrix

String

Integer

Vector type, mask shape

String, set, back Integer

Sine of angle in radians

Hyperbolic sine

Number of array elements in dim

Spacing of model number near argument

Adding a dimension to source

Square root

Sum of elements

Subroutine returning integer data from a real time clock

Tangent of angle in radians

Hyperbolic tangent

Smallest positive model representation

Same bits, but new type

The transpose of matrix

REmove trailing blanks

Largest subscript of dim in array

Unpack an array of rank one into an array of mask shape

Position in string not in set

Table 6: Intrinsic functions

Download