PROGRAMMING IN FORTRAN77


               If you do not have a WORKER window below
         ***   open one now by invoking the 'Worker Window'  ***
               option in the 'Coursework' menu.

Table of Contents

0. Introduction

1. Fortran Statements

2. Data Types, Constants and Variables

3. Operators

4. Task Repetition Using DO Loops

5. Conditional Constructs

6. Arrays

7. Input/Output

8. The Use of Format

9. Subprograms

10. Common


0. INTRODUCTION

You should already have met some of the features of the Fortran77 programming language in the Introductory Course. IF YOU HAVE NOT COMPLETED THE PROGRAMMING TUTORIAL IN THE INTRODUCTORY COURSE, YOU ARE STRONGLY ADVISED NOT TO PROCEED WITH THIS TUTORIAL, BUT TO GO BACK AND DO IT!

The Fortran language is still evolving; Fortran77 is an internationally agreed standard to which nearly all current compilers conform, but there is now a revised standard, Fortran90, which has been agreed but which has not yet been widely implemented. However, many compilers, including that available on this machine, already incorporate some of the new features of the Fortran90 standard as extensions to Fortran77. We will introduce some of these new features in this tutorial but will always point out that they are not part of the Fortran77 standard, but are extensions thereof by labelling them F90. However, YOU ARE STRONGLY ENCOURAGED TO USE THE F90 FEATURES.

We will begin by reminding you of the structure of the projectile program that featured in the Introductory Course but we will now try to be a little more systematic in our description! Get hold of a copy of the program by going to the WORKER window, clicking the mouse and typing

      cp /usr/local/text/cplabwork/examples/fortprogs/proj1.f proj1.f
or
      cp $TFILES/cplabwork/examples/fortprogs/proj1.f proj1.f

Then invoke the emacs editor on your copy of the file by typing

      ue proj1.f

Click here if you wish to return to the Table of Contents.


1. FORTRAN PROGRAM STATEMENTS

Fortran77 programs are typed in lines of up to 72 characters, with the first six columns of each line reserved for special purposes. As a result, Fortran77 statements ALWAYS BEGIN AT, OR AFTER, COLUMN 7.

One of the special purposes for which the first six columns may be used is shown in the projectile program: if the first column contains a 'c' or a '*' then the whole line is treated as a COMMENT and is ignored when the program is compiled into executable code.

A second special use of the first six columns is to specify a CONTINUATION LINE. If column 6 of a line contains any character other than a space or a zero then the line is treated as a continuation of the previous line. In this case columns 1-5 of the continuation line MUST BE BLANK, and column 7 is treated as if it came immediately after column 72 of the previous line!

The third special use of the first six columns (actually, the first five columns) is for identifying a statement by means of a STATEMENT LABEL, which is an integer, written in columns 1-5. The number chosen can be any integer in the range 1-99999 as long as it is unique to the statement being labelled. We will encounter uses of statement labels later in the tutorial; for now, note that they are not generally required and their use is DISCOURAGED.

!!!!!!!!!!!!!!!!!!!!!!!!!!!! F90 FEATURE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

The strict Fortran77 standard requires the use of upper-case letters only in program statements, except in comment statements or when writing strings of text to be used in printed output. Fortran90 allows the use of lower-case as well as upper-case letters. Note, however, that upper- and lower-case are EQUIVALENT; they are distinguished only when they form part of character sequences, NOT when used in names of variables or constants.

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Click here if you wish to return to the Table of Contents.


2. DATA TYPES, CONSTANTS AND VARIABLES

TYPES

The data items which appear in a Fortran77 program have a type associated with them. The allowed types are:

real
exponent and significant fraction stored separately - known as floating point storage
integer
stored as integer
character
used to store text, or character strings
logical
only two possible values denoted .TRUE. and .FALSE.
double precision
like 'real' but uses twice the number of bits
complex
equivalent to a pair of 'reals'

CONSTANTS

Real constants are numbers containing a decimal point and (optionally) preceded by a sign e.g.

  4.7    -0.01    +3.14159       17.    -.1

The exponent form represents real constants by a (signed) number (the mantissa), with or without a decimal point, followed by the letter e (or E) and a second number (the exponent), so the previous examples could be written:

 47E-1  -1E-2     3.14159E0   0.17E2   -1E-1

Integer constants are numbers which do not contain any decimal point and which may, optionally, be preceded by a sign e.g.

  4  -1  +275

Integers up to a certain size (dependent upon the particular computer) are stored with complete precision.

Character constants are sequences of characters, known as strings, enclosed in single quotes, or apostrophes, e.g.

 'Computational Physics' 'a4'   'aaaa' 

The apostrophes do not form part of the string and are not stored in memory, unless repeated e.g. 'I don''t' is a string containing a single apostrophe.

VARIABLES

Fortran variables have names which denote storage locations in memory. The strict Fortran77 rules for names are that they

Particular implementations of Fortran may relax these rules e.g. in our example proj1.f, there is a variable called theta_rad which is clearly longer than 6 characters and includes the underscore character, but if you wish your code to be portable between different machines you should stick to the standard!

Variable names can be declared before the first executable statement of a program, e.g.

      real x, height, range
      integer month, minute
      character*9 name, class, degree
      logical test
      double precision xplot, yplot

NOTE that in declaring a character variable, the length of the character string is specified by appending * followed by the number of characters in the string to the type. In the above example, name, class and degree are all declared to be character strings of length 9.

IMPLICIT TYPING

Fortran77 has some potentially dangerous rules about variable types. If a variable name is used without declaration, IT IS ASSUMED TO BE REAL if its name starts with a letter in the range a-h or o-z, whereas IT IS ASSUMED TO BE INTEGER if it starts in the range i-n.

Some programmers adopt the practice of always declaring all the variables used in a program, regardless of whether or not they conform to the implicit typing. Going back to proj1.f we see that x, y, u, vx, vy, g, dt, theta, theta_rad, and pi are all declared to be of type real, whereas nstep and max_steps are declared to be of type integer. In this case, the declarations conform to the implicit typing rules.

!!!!!!!!!!!!!!!!!!!!!!!!!!!! F90 FEATURE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

The statement

     implicit none

at the start of a program overrides the implicit typing of Fortran77, requiring that all variables then be declared explicitly. Its use is widely regarded as good programming practice and you are STRONGLY ENCOURAGED TO USE IT.

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Click here if you wish to return to the Table of Contents.


3. OPERATORS

ASSIGNMENT OPERATOR

Remember that the assignment operator, =, has a different meaning to the usual arithmetic 'equals'. Thus

  x = x+vx*dt

means 'evaluate the expression on the right hand side using the current values of the variables x, vx and dt, and then use it to overwrite the current value of x i.e. store the value of the expression in the memory location labelled by x'.

ARITHMETIC OPERATORS

The arithmetic operators are

  +   -   /   *   **  
representing, respectively, addition, subtraction, division, multiplication and exponentiation. The first four appear explicitly in our example program, proj1.f.

It is important to be aware that there is an order of precedence for these operators which may matter when evaluating complicated expressions. The priority rule is: ** has highest priority followed by / and * followed by + and -

Within any one priority level, evaluation is carried out from left to right. For example in the expression

    3.0+4.0**2.0-8.0/4.0+2.0
exponentiation is performed first, causing the expression to reduce to
    3.0+16.0-8.0/4.0+2.0
followed by any multiplication or division, leading to
    3.0+16.0-2.0+2.0
which finally reduces to 19.0.

There is one important exception to the left -> right evaluation rule: multiple exponentiation. Thus for example

   a**b**c
is evaluated from right to left!

Ambiguities can always be resolved by the use of parentheses. Thus for example, in the expression

   x+vx*dt
the multiplication of vx and dt is performed first, followed by the addition of the result to x. Had we wished first to add x to vx and then multiply by dt we could have written
   (x+vx)*dt
In general, ANY EXPRESSION ENCLOSED IN PARENTHESES IS EVALUATED FIRST.

-------------------------------------------------------------------

Exercise: what are the values of the following Fortran expressions?

 5.+2.*3.   (5.+2.)*3.  2.*3.**3.   (2.*3.)**3.   2.**3.**2.

-------------------------------------------------------------------
In the evaluation of an arithmetic expression of the form
        operand operator operand
in Fortran77, one of the rules is that both operands must be of the same type (other than in expressions involving exponentiation of the form x**2). If one is an integer and the other is a real (a MIXED-MODE expression), then the integer will be converted to real before the operation is performed. The result is of the same type as the operands. MIXED-MODE assignment is also possible but should be used with care.

Examples:

              9*tempc/5
Here tempc is real by default, so 9 is converted to 9.0 and the multiplication is performed according to the rules of precedence discussed above. The result is of type real and thus the denominator is first converted to 5.0 before the division is carried out, the final result being of type real.
              a = 7*8
Both operands on the r.h.s. are of type integer, so the result of the multiply is 56, of type integer. This is then assigned to a, which is by default of type real, so a takes the value 56.0
              j = 6.0*7.0/9.0
Here the r.h.s. is a real expression which evaluates to 4.666... The value assigned to the integer j is then 4, the fractional part being discarded (this is known as truncation).
                     *******************************
                     * Warning on integer division *
                     *******************************
Any fractional part remaining on integer division is also discarded, which can catch out the unwary programmer! For example,
    i = 7
    j = 2
    k = i/j    
gives k = 3.

Similarly

    i = 7
    j = 2
    a = i/j
gives a = 3.0, from the rules for mixed-mode assignment.

TYPE CONVERSION OPERATORS

Type conversion is possible using type conversion operators; thus applied to the previous example

    i = 7
    j = 2
    a = real(i)/real(j)
gives a = 3.5, which is probably what you intended!

It is also possible to convert from type real to type integer; int(a) yields an integer result, the integer part of a.

RELATIONAL OPERATORS

In proj1.f you see an example of a comparator, or relational operator, which is used in the line

      do while(y.ge.0.0)
to test whether or not y is greater than or equal to zero. The expression y.ge.0.0 is called a relational expression because it expresses a relation between two values. There are six relational operators in Fortran which may be used to construct relational expressions of type 'logical' which are either true or false - that is, they correspond in value to one of the two logical (or Boolean) constants .TRUE. or .FALSE.

The complete set of relational operators is as follows:

a.lt.b
evaluates to .TRUE. if a is less than b, otherwise .FALSE.
a.le.b
evaluates to .TRUE. if a is less than or equal to b, otherwise .FALSE.
a.gt.b
evaluates to .TRUE. if a is greater than b, otherwise .FALSE.
a.ge.b
evaluates to .TRUE. if a is greater than or equal to b, otherwise .FALSE.
a.eq.b
evaluates to .TRUE. if a is equal to b, otherwise .FALSE.
a.ne.b
evaluates to .TRUE. if a is not equal to b, otherwise .FALSE.

                   ******************************
                   * Warning on comparing reals *
                   ******************************
It can be dangerous to use the relational operators .ne. and .eq. for comparing reals because of rounding errors. For example, in finite precision, 1./3. has the value 0.3333333, say, so that 3.0*(1.0/3.0) has the value 0.9999999 NOT 1.0, so that the comparison
   3.0*(1.0/3.0).eq.1.0
evaluates to .FALSE. instead of .TRUE.

LOGICAL OPERATORS

Relational operators are used to create a logical expression, which takes the value .TRUE. or .FALSE.; logical expressions can be combined using logical operators to perform more sophisticated logical tests. The logical operators are

  .AND.   .OR.   .EQV.   .NEQV.   .NOT.  
and their use is illustrated by the following table, where L1 and L2 are logical expressions;

   L1         L2        L1.OR.L2      L1.AND.L2   L1.EQV.L2     L1.NEQV.L2
--------------------------------------------------------------------------
 .TRUE.     .TRUE.       .TRUE.        .TRUE.      .TRUE.        .FALSE.
 .TRUE.     .FALSE.      .TRUE.        .FALSE.     .FALSE.       .TRUE.
 .FALSE.    .TRUE.       .TRUE.        .FALSE.     .FALSE.       .TRUE.
 .FALSE.    .FALSE.      .FALSE.       .FALSE.     .TRUE.        .FALSE.
--------------------------------------------------------------------------
Thus .OR. gives a result that is only true if either of its operands is true, whereas .AND. gives the result true only if both operands are true.

.EQV. gives a true result only if both L1 and L2 are logically the same value, whilst .NEQV. gives a true result if L1 and L2 have opposite values. They may be used to simplify more complex logical expressions; e.g. the following two expressions have the same value:

 (a.lt.b.AND.x.lt.y).OR.(a.ge.b.AND.x.ge.y)      a.lt.b.EQV.x.lt.y
The remaining operator, .NOT., takes only a single operand, whose value it inverts. For example, the following two expressions are equivalent:
 .NOT.(a.lt.b.EQV.x.lt.y)       a.lt.b.NEQV.x.lt.y
Priority rules for logical operators apply just as they did for arithmetic operators:
                Operator             Priority
              ----------------------------------
                 .NOT.                  1 (high)
                 .AND.                  2
                 .OR.                   3
                 .EQV./.NEQV.           4 (low)
              ----------------------------------

As before, parentheses may be used to make the order of evaluation clear.

CHARACTER OPERATOR

There is only one character operator, the concatenation operator //, which can be used to combine two strings to form a third string. For example:

   'Wed'//'nesday' is equivalent to the string 'Wednesday'

INITIAL VALUES

Often we need to assign an initial value to some or all of the variables in a program. Fortran77 allows us to do this by means of the data statement, which takes the form:

      data 'namelist1'/'clist1','namelist2'/'clist2',........
or
      data 'namelist1'/'clist1'/'namelist2'/'clist2'.........
where 'namelist*' is a list of variables names and 'clist*' is a list of constant values, with the same number of items as the corresponding 'namelist'. The effect is to give each variable in 'namelist' the initial value specified by the corresponding item in 'clist'. The normal rules of arithmetic apply and the constant will be type-converted if necessary to match the type of the variable. Note: the quotation marks are not part of the sytax.

Example:

      data a,b,n/1.0,2.0,3/,code/8667.0/
gives the real variables a and b initial values of 1.0 and 2.0, the integer variable n an initial value of 3, and the real variable code an initial value of 8667.0.

Several items can be given the same initial value by preceding the constant by a repetition count e.g.

      data i,j,k,l,m,n/6*0/
A data statement must appear AFTER any specification statements. It is usual, but not necessary to put data statements at the beginning of the executable part of the program.

                       *****************************
                       * Warning on initial values *
                       *****************************
Many computer systems initialise all variables to a pre-defined value, usually 0, but IT IS NEVER SAFE to assume that this is the case.

NAMING CONSTANTS

Although the data statement can be used to give a value to a variable that is never changed e.g.

      data pi/3.14159/
there is a preferred way of naming constants, which is to use the parameter statement. It takes the form
      parameter ('name1'='const1', 'name2'='const2',......)
with obvious meaning. Thus we could write
      parameter (pi=3.14159)
Then on every occasion that pi is referred to in the program, the value 3.14159 will be used.

Constant expressions can also be used e.g.

      parameter (third=1.0/3.0, pi3by4=3.0*pi/4.0) 
Where an expression is used there are some restrictions:

Click here if you wish to return to the Table of Contents.


4. TASK REPETITION USING DO LOOPS

THE DO LOOP

The repetition of a number of statements a predetermined number of times is so important that Fortran contains a special construct that allows this to be done. In general, a "do loop" may contain any Fortran statements, including another do statement, known as a "nested do loop".

!!!!!!!!!!!!!!!!!! F90 FEATURE: the 'end do' statement !!!!!!!!!!!!!!!!!!!!!!!!

Fortran90 denotes the terminating statement of a "do loop" by means of the end do statement.

The syntax is:

      do index=int1,int2[,int3]
        'statements'
      end do

where:

Note: The notation [,int3] simply means that int3 is optional; it is assumed to be 1 if omitted. The square brackets are not part of the syntax!

The number of times that the body of the "do loop", denoted by 'statements', is executed (known as the "trip count") is calculated from the formula:

          MAX(0, INT((int2-int1+int3)/int3))
where INT denotes the integer part of the ensuing expression. This number is calculated BEFORE the loop starts. If zero, it means that the body of the "do loop" is NOT executed.

Examples:

      do i=0,100,5
has a trip count of 21 whereas
      do i=0,100,9
has a trip count of 12

Example: a very simple program to compute the squares of the integers from 1 to 10, and print them out.


      program squares
c
c  a program to calculate the squares of the first ten integers 
c
      implicit none
      integer int,intsq
      do int = 1,10
        intsq=int*int
        write(*,*) 'The square of ',int,' = ',intsq
      end do
      stop
      end

RESTRICTIONS

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
The Fortran77 syntax involves the use of a labelled statement to denote the end of the loop and is:
      do m, index=int1,int2[,int3]
        'statements'
    m continue      

where:

Note: the comma after m in the do statement is optional - you may encounter both forms.

THE DO WHILE LOOP

!!!!!!!!!!!!!!!!!!!! F90 FEATURE:  the 'do while' construct !!!!!!!!!!!!!!!!!!!

There is an example of a "do while loop" in proj1.f. This construct permits the repeated execution of one or more Fortran statements conditional upon the status of a logical expression, rather than for a fixed number of repetitions. The syntax is:

      do while ('logical expression')
        'statements'
      end do
which loops through 'statements' for as long as 'logical expression'evaluates to .TRUE.

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

------------------------------------------------------------------------------
Exercise: Go to the WORKER WINDOW and use the editor to write a program 
to print out the factorial of n (an integer) from  n=1 to 20. NOTE: the
filename that you use for storing your program must have the default
extension .f otherwise it will not be recognised by the compiler, so use
something like 
                         ue fact.f

Your program should not use more than one "do loop". Try compiling and
running your program.   What goes wrong if you use integer variables in
your program ??

Remember, the command which invokes the compiler is 

                         f77 'filename'  

which produces an executable file called a.out
 
or                      f77 -o 'file2' 'file1' 

if you want the executable to be called 'file2'. (Do not type the quote
marks!).  To run your program, simply type the name of the executable file.

*** CHECKPOINT 1 ***

------------------------------------------------------------------------------

Click here if you wish to return to the Table of Contents.


5. CONDITIONAL CONSTRUCTS

SIMPLE IF

There are two forms of the IF construct. The simple form (which is, strictly speaking, a hangover from earlier versions of Fortran) is:

      if ('logical expression') 'statement'
Here 'statement' is any executable statement and is only implemented if 'logical expression' has the value .TRUE.

Example:

      if (volts.gt.threshold) signal=gain*input

BLOCK IF

There is also a block IF construct:

      if ('logical expression 1') then
         'statements1'
      else if ('logical expression 2') then
         'statements2'
      else if ('logical expression 3') then
         'statements3'
      else
         'statements4'
      end if
Here 'statements*' are blocks of one or more lines of Fortran. Only one block gets executed in a particular run; e.g. 'logical expression 2' is only tested if 'logical expression 1' has the value .FALSE. and so on.

Any number, including zero, of else if blocks can be used and the final "catch-all" else block can be omitted. Thus the simplest form is

      if ('logical expression 1') then
        'statements1'
      end if
which is equivalent to the simple form described above in the case where 'statements1' consists of just a single statement.

Example: a program to compute the number of points with positive integer coordinates inside the ellipse x**2/16 + y**2/25 = 1


      program ellipse
c
c  declarations
c
      implicit none
      integer npts,ix,iy
c
c  initialise counter
c
      npts = 0

c
c  loop over x direction
c
      do ix = 1,4
c
c  loop over y direction
c
        do iy = 1,5

c
c  test to see if point lies within ellipse
c
          if(real(ix*ix)/16.0 + real(iy*iy)/25.0.lt.1.0) then
            npts = npts +1
          end if
        end do
      end do
      write(*,*) 'Number of points: ',npts
      stop
      end

Note the use of the "program statement"; not essential, but good programming practice. The name of the program may be freely chosen, within the Fortran naming conventions.

Note that indentation has been used to clarify the loop structure, although not necessary ; the contents of each "do loop" are indented by two spaces with respect to the do and end do statements, as are the contents of the if block.

-----------------------------------------------------------------------------
Exercise: go to the WORKER window and get a copy of the ellipse program by
typing

  cp /usr/local/text/cplabwork/examples/fortprogs/ellipse.f ellipse.f

Try compiling and running this program.   Remember, the command which invokes
the compiler is 

                         f77 'filename'  

which produces an executable file called a.out
 
or                      f77 -o 'file2' 'file1' 

if you want the executable to be called 'file2'. (Do not type the quote
marks!).  To run your program, simply type the name of the executable file.
------------------------------------------------------------------------------

Example: a program to read positive integers and compute their average; negative integers are ignored whilst zero terminates the program.


      program average

c  summary: program to read positive integers and find their average,
c           ignoring negative integers and terminating on zero

c  variable definitions:

c    numint         number of integers read in so far  
c    intsum         running total of integer values
c    int            current integer
c    n              for do loop control

c
c  declarations
c
      implicit none
      integer numint,intsum,int,n
c
c  initialise counter and total
c
      numint = 0
      intsum = 0
      n = 1

      do while (n.eq.1)
c
c  input integer from keyboard
c
        write(*,*) 'Input integer '
        read(*,*) int
c
c  test for negative or zero values
c
        if(int.gt.0) then
          numint = numint + 1
          intsum = intsum + int
        else if(int.eq.0) then
          write(*,*) 'Number of positive integers: ',numint,
     &             ' Average: ',real(intsum)/real(numint)
          stop
        end if
      end do
      end

Note the continuation line, denoted by '&' in column 6.

Note the difference between the stop statement, which signifies the logical end of the program and terminates execution, and the end statement, which denotes the physical end of the program. Any program that you write must have an end statement as its final statement, or it will not compile. It should also have a stop statement, although it may well compile and run without one.

-------------------------------------------------
Exercise: Get a copy of the average program:

  cp /usr/local/text/cplabwork/examples/fortprogs/aver.f aver.f

and try it out.
------------------------------------------------------

Click here if you wish to return to the Table of Contents.


6. ARRAYS

It is often desirable to refer to a group of related items of the same type by giving them the same group name, and identifying the individual items by their positions within the set. Arrays are used for this purpose in Fortran.

Each item within an array is referred to as an array element and is identified by an integer index or subscript which follows the array name in parentheses. An array is always declared in an extended type declaration statement, which also declares the range of possible subscripts, and hence the size of the array. For example

      integer n(100)
reserves 100 integer locations in memory called n(1), n(2), n(3),.....n(100). In statements which reference an array element, the index or subscript may be any integer expression e.g. n(3*j+2).

Array elements may be used anywhere that a variable name can be used, but are especially useful in conjunction with a "do loop", whose index can be used as an array subscript, enabling each pass through the loop to use a different array element.

Example: here are the bare bones of a program to find the largest element of an array of ten integers. You are invited to complete it:


      program largest
c
c  variables:
c
c     ivec               array to hold numbers
c     large              largest array element
c     j                  labels largest array element
c     i                  do loop index

c
c  declarations
c
      implicit none
      integer ivec(10),large,i,j
c
c  input the array elements
c
      write(*,*) 'Enter 10 integers, one per line:'
      read(*,*) (ivec(i), i = 1,10)
c
c  start by assuming first element to be largest....
c
      large = ivec(1)
      j = 1
      do i = 2, 10
c
c  now write the rest of this loop yourself........
c
      end do
      write(*,*) 'Element ',j,' is largest and has value ',large
      stop
      end

Note: in this example the use of a so-called "implied do loop" in the read statement which inputs the ten elements of the array ivec in sequence.

----------------------------------------------------------------------
Exercise: get the skeleton program

  cp /usr/local/text/cplabwork/examples/fortprogs/largest.f largest.f

and try to complete it; check your program by compiling and running it.

*** CHECKPOINT 2 ***

-----------------------------------------------------------------------

By default, array subscripts in Fortran start at 1, but this can be overridden; for example

      real v(-4:3)
declares a real array with 8 elements v(-4), v(-3),.....v(3) The first example could equally well have been written
      integer n(1:100)

MULTI-DIMENSIONAL ARRAYS

There are many instances where it is useful to be able to declare an array with more than one subscript; for example, to store the values of the electrostatic potential at points in the x-y plane. Fortran77 allows us to have up to seven subscripts for a single array. The number of subscripts is referred to as the DIMENSION of the array.

Examples:

      real potl(100,100)
      integer pressure(20,40,30)
The first of these could equally well be written
      real potl(1:100,1:100)
or    
      real potl(1:100,100)
or    
      real potl(100,1:100)

An element of a multi-dimensional array must always be referred to with the correct number of subscripts and can, of course, be used in exactly the same way as a variable or an element of a one-dimensional array.

Arrays may have characters rather than numbers as elements, for example, we might want to process a set of names of students by tutorial group and serial number within the group:

      program namelist

c
c  variable definitions:
c
c     igroup            labels a group
c     ident             labels an individual
c     name              stores names in required order
c     next              next name in sequence
c
c  one '*' terminates a group
c  two '**' terminates the whole data set

c
c  declarations
c
      implicit none
      character*15 name(30,10), next
      integer igroup,ident

      igroup = 1
  10  ident = 1
  20  read(5,*) next
      if(next.eq.'*') then
        igroup = igroup +1
        go to 10
      else if(next.ne.'**') then
        name(ident,igroup) = next
        ident = ident + 1
        go to 20
      end if

c  now process the data in the array name............

Note the use of the go to statement together with statement labels, to transfer program control conditionally. The use of the go to is STRONGLY DISCOURAGED as it makes program logic difficult to follow and can lead to poorly structured code.

---------------------------------------------------------------------------
Exercise: can you devise a way of aavoiding the use of "go to" statements and 
statement labels in the previous example, using the F90 constructs that you
have already met?
---------------------------------------------------------------------------

We can use an implied do loop to input to or output from a part, or the whole of a multi-dimensional array e.g.

      read(5,*) ((potl(i,j),i=2,25),j=10,50)

                        ****************************
                        * Warning on storage order *
                        ****************************

The elements of a one-dimensional array occupy consecutive storage locations in memory but it is not immediately obvious what happens with higher- dimensional arrays. In fact in Fortran, multi-dimensional arrays are stored in such a way that the first subscript changes most rapidly e.g. the elements of

      integer mat(3,3)
are stored with mat(1,1), mat(2,1), mat(3,1), mat(1,2),...... in consecutive locations in memory. This is important to know if you wish to optimise the speed of execution of a program that manipulates arrays. Thus in the example above on the use of implied do loops to read into a two-dimensional array it would be less efficient to write
      read(5,*) ((potl(i,j),j=10,50),i=2,25)
because the innermost loop is accessing elements which do not correspond to consecutive locations in memory.

Click here if you wish to return to the Table of Contents.


7. INPUT/OUTPUT

TERMINAL I/O

The simplest form of input and output, which utilises only the keyboard and screen of your terminal, is known as list-directed. You have already met examples in the program proj1.f. In fact that program did not make use of the very simplest forms which we now describe.

LIST-DIRECTED OUTPUT

uses
      print*, 'expression list'
or
      write(*,*) 'expression list'
or
      write(6,*) 'expression list'
In the third form, 6 denotes the default output device, the terminal screen, for historical reasons. It is better and safer to use the second form in which * denotes the default device.

The values of any literals, variables, constants or expressions in 'expression list' will be printed in the order in which they are listed. The form of numeric output is pre-determined by the compiler.

Examples:

            print*, 'Distance versus velocity'
            print*, 'Slope = ', slope
            write(*,*) 'Specify launch speed u (m/sec) :'

LIST-DIRECTED INPUT

uses
      read*, 'variable list'
or    
      read(*,*) 'variable list'
or    
      read(5,*) 'variable list'
Here 5 denotes the default input device, the terminal keyboard, again for historical reasons. The second form is preferred.

Values are stored in the order in which the variables are listed. Data values must be separated by commas or blanks. As many lines as are needed will be read. Each read* statement will begin searching for values with a new line.

Examples:

             read*, time
             read*, (x(j), j=1,100)
             read(*,*) theta

-----------------------------------------------------------------------------
Exercise: Write a program to read in a and b, the sides of a rectangle,
and print out the perimeter and area.

*** CHECKPOINT 3 ***

-----------------------------------------------------------------------------

We may wish to exercise more control over the appearance of our output than is afforded by the list-directed forms just described. Fortran makes provision for the use of 'format descriptors' to achieve user control over both input and output.

FORMATTED OUTPUT

to the default output device uses
      print k, 'expression list'
or    
      write(*,k) 'expression list'
or    
      write(6,k) 'expression list'

The values of any variables or expressions in 'expression list' will be printed in the order in which they are listed according to the specifications in a format statement with label k. We will discuss format in Section 8.

Examples:

            print 100, alpha, beta
            print 50
            write(*,99) x, (a(i), i=1,6)
This last example again illustrates use of the implied do loop to print out elements of an array.

FORMATTED INPUT

from the default input device uses
      read k, 'expression list'
or    
      read(*,k) 'expression list'
or    
      read(5,k) 'expression list'
The values of any variables or expressions in expression list will be input in the order in which they are listed according to the specifications in a format statement with label k. We will discuss format in Section 8.

Examples:

            read 200, alpha, beta
            read(*,99) x, (a(i), i=1,6)

FILE I/O

We may wish more generally to read from, or write to files rather than the standard input and output devices keyboard and screen. You may already have tried doing this in the Introductory Course Programming Exercises. The way in which this is done in Fortran77 is by means of the open statement, whose form is

      open('olist') 
where 'olist' is a list of open specifiers chosen from the following set:
     unit=u, file=fn, status=st, access=acc, form=fm, err=s, iostat=ios
The first of these, unit, MUST BE PRESENT, the others are all optional. The unit specifier takes the same form as in the read and write statements discussed above, and provided it is first in the list, the string unit= may be omitted, with only an integer value, u, specifying the unit number.

We shall not consider the full generality of the open statement here but restrict consideration to the following form:

 
      open(unit=integer expression, file='filename', status='literal')
designates the file named 'filename' for all input or output statements that refer to the unit number specified by the value of integer expression.

If the string 'literal' is 'old', the file must be an existing one; if 'literal' is 'new', the file must NOT be an existing one. If a file whose status is 'new' is successfully opened, its status is thereby changed to 'old' and any subsequent attempts to open the file as 'new' will fail.

'literal' may also be 'unknown', which is equivalent to omitting the status specifier and is implementation-dependent, or 'scratch' for a temporary file which is deleted when execution finishes.

Examples:

      open(8, file='projdata')
      open(unit=15, file='data/run.1', status='old')
      open(unit=10, file='latest', status='new')

Each open statement should be matched by a corresponding close statement, once the unit in question is no longer needed by the program:

Examples:

      close(8)
      close(unit=15)
      close(unit=10)

LIST-DIRECTED INPUT

then uses
      read(unit number,*,end=n) 'variable list'
to read from the data file referenced by unit number, which must correspond to a unit number assigned to a file by a previous open statement, except for the case where unit number is 5, which is by default the terminal keyboard. * denotes list-directed input. end=n is optional and will cause control to pass to statement n if the read statement is executed after the last line of data has been read.

Examples:

             read(15,*,end=200) x, y, z
             read(11,*) i, j, k, l

FORMATTED INPUT

uses
      read(unit number, k, end=n) 'variable list'
to read using the format statement labelled k. end=n is optional, as before.

Example:

     read(15,99,end=200) u, v, w

LIST-DIRECTED OUTPUT

uses
      write(unit number, *) 'variable list'
to write data to a file specified by unit number , which must have been assigned in an open statement, except when unit number is 6 which is by default the terminal screen.

Example:

      write(13,*) a, b, c

FORMATTED OUTPUT

uses
      write(unit number, k) 'variable list'
to write data to a file specified by unit number , which must have been assigned in an open statement, using the format statement labelled k.

Example:

      write(8,100) a, (r(k), k=1,20)

Click here if you wish to return to the Table of Contents.


8. FORMAT

{{{OPTIONAL

This section should probably be regarded as optional if you are doing this tutorial for the first time. If you find it hard going, skip to section 9. The most readable way to specify the format in which data is to be input or output is by means of the "format" statement, referred to in the previous section. The general form of the format statement is:

    k format('format descriptor')
where 'format descriptor' is a character expression and k is a statement label. Format statements can appear anywhere in a program; a popular choice is to gather them all together just before the end statement.

Format is just about the hardest thing to get right in a Fortran program, so let us start with a simple example!

      print 100, max, res
  100 format(1x,i5,f9.3)
The format descriptor consists of a list of format codes, with the following meanings:

Thus if, for example, max = 12345 and res = 12345.678 we would get the following output:

 1234512345.678
which is not very readable!

On the other hand, if say max = 1 and res = 2.0 we would get

     1    2.000
Your program may crash if you try to print too large a number for the field width; thus max = 876543 is too big for the format code i5.

OUTPUT FORMAT CODES

Here is a table describing the effects of various format codes for output:


 code                            meaning
--------------------------------------------------------------------------
 iw         output an integer in the next w character positions
 fw.d       output a real in the next w positions with d decimal places
 ew.d       output a real in the next w characters using a scientific 
            notation with d decimal places in the mantissa and four
            characters (including e and a possible sign) in the exponent
 dw.d       as ew.d but for double precision numbers
 gw.d       same as fw.d if there is room, otherwise ew.d
 nx         ignore the next n character positions i.e. output n blanks
 an         output character expression in a field of width w, truncating
            or padding with spaces as necessary; for truncation n leftmost
            characters are output; padding is on left
 a          output character expression in a field of its own width
 tc         output next item starting in character position c
 tln        output next item starting n character positions before (tl) or
 trn        after (tr) the current position
 'abcd..'   output the string of characters 'abcd..' starting at the next
            character position
---------------------------------------------------------------------------

You can use a repetition count for multiple instances of the same format code or codes in a format statement e.g.

  10  format(1x,3i5,2(4x,a4))
is equivalent to
  10  format(1x,i5,i5,i5,4x,a4,4x,a4)

Separators in the list of format codes can be of two kinds:

Example:

      print 100, max, res
  100 format(1x,i5/f9.3)
with max = 12345 and res = 12345.678 as in the first example, would produce
 12345
12345.678
What happens if there is a mis-match between the number of items in the output list and the number of format codes?

a) excess codes are ignored, for example,

      print 100, jj
  100 format('a=',i3,' b=',i3)
if jj = 123 would produce
a=123 b=

b) if there are too few codes, a new line is taken, and the codes are repeated from the left parenthesis corresponding to the LAST BUT ONE right hand parenthesis! For example,

      print 100, (a(i), i=1,80)
  100 format(' Results:'/(1x,10f7.3))
gives a header line followed by 8 lines of 10 items. However,
      print 100, (a(i), i=1,80)
  100 format(' Results:'/1x,10f7.3)
would give 8 pairs of lines, each with Results: on the first line and 10 items on the next.

CARRIAGE CONTROL OR FORMAT EFFECTORS

If the output from a Fortran program goes to a lineprinter, or to a file which then gets printed on a lineprinter, then the first character in each line is not printed, but is used for "carriage control" or as a "format effector".

The allowed format effectors are as follows:

      character                     effect
 --------------------------------------------------------------------
      space           no extra action i.e. single spacing
        0             skip an extra line i.e. double spacing
        1             new page
        +             overprint the last line (not always implemented)
 --------------------------------------------------------------------

As an example

  100 format('1Results:'/(1x,10f7.3))
starts printing on a new page.

INPUT FORMAT CODES

Most of the output format codes described above are available as input format codes but are not widely used, as free-format (denoted by *) deals with most situations:

 code                            meaning
---------------------------------------------------------------------------
 iw         read the next w characters as an integer
 fw.d       read the next w characters as a real with d decimal places
            if no decimal point is present; otherwise ignore d
 ew.d       same as fw.d on input
 dw.d       same as fw.d on input
 gw.d       same as fw.d on input
 nx         ignore the next n characters
 an         read next n characters as characters; if n is less than length
            of input list item pad with blanks; in n is greater than
            length, store rightmost characters of input string in item
 a          read sufficient characters to fill input list item
 tc         next character to be read is at position c
 tln        next character to be read is n characters before (tl) or
 trn        after (tr) the current position
---------------------------------------------------------------------------

The treatment of reals by fw.d (or ew.d) perhaps needs an example to clarify;

      read 100, a,b,c,d
  100 format(f3.1,f2.2,f3.2,f1.0)
then the result for two different examples is as follows:
           data        a        b       c       d
     -------------------------------------------------
        123456789     12.3    .45      6.78     9.
        .23.56.8       0.23  0.5       6.8
     -------------------------------------------------  

}}}

Click here if you wish to return to the Table of Contents.


9. SUBPROGRAMS

Subprograms, or procedures, are the basic building blocks of good Fortran programs (in the jargon, WELL-STRUCTURED PROGRAMS). There are two types of procedure or subprogram: subroutines and functions. A complete Fortran program consists of a main program and any number of subprogram program units.

INTRINSIC FUNCTIONS

Fortran contains a library of intrinsic functions providing the commonly used mathematical functions such as the trigonometric functions, which you have already met in the example program proj1.f. You can also write your own functions to supplement or replace those provided as part of the language. The key feature of a function subprogram is that it returns a result through its name, whereas subroutine subprograms do not. Generally a function takes one or more arguments and returns a result.

Here is an example of an intrinsic function, the square root, being used to calculate the area of a triangle of sides a,b,c from the formula

              area = square root{s(s-a)(s-b)(s-c)}
where 2s is the sum of the lengths of the sides:

         program triangle

c  variable definitions:

c    a,b,c            sides of triangle
c    s                (a+b+c)/2
c    area             area of triangle

c
c  declarations
c
      implicit none
      real a,b,c,s,area
c
c  read the lengths of the sides of the triangle
c
      write(*,*) 'Input sides a,b,c :'
      read(*,*) a,b,c

c
c  compute area using intrinsic function sqrt
c
      s = 0.5*(a + b + c)
      area = sqrt(s*(s-a)*(s-b)*(s-c))
      write(*,100) area
  100 format(1x,'Area of triangle is: ',f10.4)
      stop
      end


-----------------------------------------------------------------------------
Exercise: try modifying the triangle program to check that the input
values of a,b and c do indeed correspond to a valid triangle, that is,
they are all non-negative and the sum of any two is greater than the
third.  As usual you can get a copy by means of the command

cp /usr/local/text/cplabwork/examples/fortprogs/triangle.f triangle.f

*** CHECKPOINT 4 ***

-----------------------------------------------------------------------------

FUNCTION SUBPROGRAMS

Function subprograms are also known as external functions to distinguish them from intrinsic functions. Here is a simple example, a function to calculate the average of five numbers:


      program mean

c  use a function subprogram to calculate the mean of a set of five numbers
c
c  declarations
c
      implicit none
      real a1,a2,a3,a4,a5,av,avrage
c
c  input five real numbers
c
      write(*,*) 'Input five real numbers :'
      read(*,*) a1,a2,a3,a4,a5
c
c  instance of function 
c
      av = avrage(a1,a2,a3,a4,a5)
      write(*,100) av
  100 format(' Average of five numbers is :',1x,f10.4)
      stop
      end

c  end of main program

c  definition of function subprogram  

      real function avrage(x1,x2,x3,x4,x5)

c  this function returns the average of its five arguments
c
c  declarations
c
      implicit none
      real x1,x2,x3,x4,x5,sum

      sum = x1 + x2 + x3 + x4 + x5
      avrage = sum/5.0
      return
      end

Notes:

SUBROUTINE SUBPROGRAMS

A subroutine subprogram has the same overall structure as a function subprogram except that the first statement is a subroutine statement rather than a function statement. The chief difference lies in how they are referenced and how their results are returned.

As we have just seen, a function is referenced by writing its name, followed by any arguments enclosed in parentheses. Control is then transferred from the current statement to the statements contained within the function. The execution of the function uses the actual values provided for its arguments, substituting them for the dummy arguments in the function definition, and calculates a value, the function value, which is then available as the value of the function reference in the main program.

A subroutine, on the other hand, is accessed by means of a call statement, which gives the name of the subroutine and a list of arguments which are used to transfer information between the main program and the subroutine:

      call 'name'('arg1,'arg2',..............)
The call statement transfers control to the subprogram, whose statements are executed until a return statement is encountered, when control returns to the statement immediately following the subroutine call.

As a simple example, let us rework the previous example to use a subroutine instead of a function subprogram:

      program mean

c  use a subroutine subprogram to calculate the mean of a set 
c  of five numbers
c
c  declarations
c
      implicit none
      real a1,a2,a3,a4,a5,av
c
c  input five real numbers
c
      write(*,*) 'Input five real numbers :'
      read(*,*) a1,a2,a3,a4,a5
c
c  subroutine call
c
      call avrage(a1,a2,a3,a4,a5,av)
      write(*,100) av
  100 format(' Average of five numbers is :',1x,f10.4)
      stop
      end
c
c  end of main program
c
c  definition of subroutine subprogram  
c
      subroutine avrage(x1,x2,x3,x4,x5,xbar)

c  this subroutine returns the average of its first five arguments 
c  in the sixth argument
c
c  declarations
c
      implicit none
      real x1,x2,x3,x4,x5,sum,xbar
      sum = x1 + x2 + x3 + x4 + x5
      xbar = sum/5.0
      return
      end

Notes:

Note that it is possible to have array names as subroutine arguments; the array or arrays must be declared within the body of the subroutine as well as in the calling routine. However, unlike in a main program, where the dimensions of an array must be declared of fixed size, an array declaration within a subroutine may specify the name of a variable in the declaration.

For example, here is a fragment of a program which calls a subroutine to take a vector of 'number' data events (up to a maximum of 150) and return a character array of size 70 x 'number' which can then be printed to give a crude histogram


      program analys

      implicit none
      integer number
      real events(150),xmin,xmax
      character*1 matrix(70,150)
            :
            :
      call hist(xmin, xmax, events, matrix, number)
            :
            :
      stop
      end

      subroutine hist(xlow, xhigh, dvec, harray, n)

      implicit none
      integer n
      real dvec(n),xlow,xhigh
      character*1 harray(70,n)
         :
         :
      return 
      end

Click here if you wish to return to the Table of Contents.


10. COMMON

{{{OPTIONAL

Subprograms are useful in structuring large programs into subtasks, but communication between the different program modules via parameter lists is not always appropriate. There may be a body of variables which have to be accessed by many of the subtasks but don't really 'belong' to any of them. Fortran allows us to declare a "common" block of variables for this purpose.

NAMED COMMON BLOCKS

A named block of storage is defined by the statement:

      common/'name'/'n1','n2',....
where 'name' is the global name of the "common" block and 'n1', 'n2',... is a list of local variable names, array names or array declarators. For example,
      integer age(50),num
      real mark(50,6),av,avrage
      common/exam/num,mark,av(50),age,avrage
defines a "common" block called exam which consists of 402 numeric storage items. The first of these is an integer, num, the next 300 a two-dimensional array mark, the next 50 a real array av, the next 50 an integer array age and the last a real variable avrage.

                     *********************************
                     * Warning on common block names *
                     *********************************

Because the name of a "common" block is global, it must be different from the names of any other "common" blocks or program units.

Notes:

BLANK COMMON

As well as blocks of storage of fixed size with global names, Fortran77 allows us to have a single further block of storage that is available to any program unit and that has neither a name or a fixed size. This is known as "blank common" and is declared thus:

      common 'n1','n2',.....
where 'n1', 'n2',... is a list of local variable names, array names or array declarators. For example,
      common x,y,a(-10:10)

Notes:

}}}

Click here if you wish to return to the Table of Contents.