Notes Fortran 4

Math 60 -- D. C. Smolarski, S.J.
Santa Clara University, Department of Mathematics

[Return to Math 60 Homepage | Return to Notes Listing Page]

Contents


Modules [EF 9]

A MODULE is a program segment that can contain type definitions, variable declarations, and even specialized routines. It is most useful when several subprogram segments make use of the same user-defined data-types or a series of variables. (Because of Fortran's separate compilation of each program segment, components declared in one segment may not be visible in another.)

The skeleton of a MODULE is very simple.
    MODULE module_name
        in this section one may define data types and
        declare variables and parameters
    CONTAINS
        in this optional section, one includes
        member subroutines and functions of this module.
    END MODULE
The MODULE name may also follow on the END MODULE line.

Simple Module Example

In the following example, a MODULE is defined and given the name MATHCONSTANTS. Within it PI and E are declared as parameters and given appropriate values.

    MODULE MATHCONSTANTS
      REAL, PARAMETER :: PI = 3.141592654
      REAL, PARAMETER :: E = 2.718281828
    END MODULE
    ! 
    PROGRAM TEST2
      USE MATHCONSTANTS
      IMPLICIT NONE
      WRITE(*,*) "Pi=",PI," e=",E
    END PROGRAM
In this example, even though PI and E (declared in MODULE MATHCONSTANTS) are not declared in the main program, they are nevertheless accessible since the MODULE in which they have been declared has been imported into the main program via the USE MATHCONSTANTS statement.

Common Uses of Modules

Some of the major ways that MODULEs may be used are as follows: The following is an example of the third type of use, declaring variables in a MODULE that will be accessed by several program segments:
   MODULE ONE
      IMPLICIT NONE
      INTEGER A
   END MODULE
!
   PROGRAM TEST3B
      USE ONE
      IMPLICIT NONE
      A = 1
      WRITE(*,*) "A=",A
      CALL TWO
      WRITE(*,*) "A=",A
   END PROGRAM TEST3B
!
   SUBROUTINE TWO
      USE ONE
      A = 2
   END SUBROUTINE TWO
Although we have not yet covered the technicalities of a SUBROUTINE, this example declares the variable A in MODULE ONE, which is accessible both from the main program and from SUBROUTINE TWO and is declared in neither. The main program sets the value of A to 1 and print out that value, and then SUBROUTINE TWO resets the value to 2. When the program control returns to the main program after CALL TWO, the value of 2 is actually printed out. In this example, the variable A has been declared in a MODULE and every program segment which imports that MODULE can change variable values and have the new values accessible to any other program segment which also imports the same MODULE. As another example, look at the following (in file test8.f90):
   module sample1
        integer, dimension (10) :: k
   end module 
!
   program test8
      use sample1
   integer i
   call sub1
   do i = 1,10
        write(6,*) k(i)
   end do
   end
!
   subroutine sub1
      use sample1
   integer i
   do i = 1, 10
        k(i) = 21-2*i
   end do
   return
   end

Fortran Subprograms: Subroutines [EF 10]

Fortran SUBROUTINEs correspond to C++ void functions.

Skeleton (EF 10.2)

        SUBROUTINE name ( parameter_list )
           ...
        RETURN
        END
NOTES: Invoking SUBROUTINES (from the main program or other routines)

One uses the keyword CALL and the subroutine identifier-name with its actual parameter list, as follows:

        CALL name ( actual_parameter_list )

Subroutine Example [EF 10.4]

        SUBROUTINE ROOTS ( A, B, C, PROOT, NROOT )
        REAL A, B, C, PROOT, NROOT, RAD
        RAD = SQRT (B*B - 4.0*A*C)
        PROOT = ( -B + RAD )/(2.0 * A)
        NROOT = ( -B - RAD )/(2.0 * A)
        RETURN
        END
(Cf., C++ NOTES 14, section: "Functions vs. Procedures")

And in the main program we would find code similar to:

        ...
        READ(5,*) A,B,C
        CALL ROOTS (A,B,C,X1, X2)
        WRITE(6,*) X1, X2

Parameter Passing in Fortran [EF 10.6]

When discussing functions in C++, it was mentioned that there were four ways to "pass parameters." (cf. Notes 8) NOTE: sometimes Fortran-77 uses "call by reference" for scalar variables.

CALL BY RESULT/VALUE-RESULT

The subprogram copies VALUES from the main program into the corresponding local variables. The subprogram has independent memory locations for the variables in the subprogram. At the completion, of the subprogram, the corresponding variables in the calling program ARE changed.

5

Problems/Confusion/Examples

                                          v-r     val     ref
                                          I J K   I J K   I J K

        INTEGER FUNCTION ICRAZY(I,J,K)
        I = J+K
        J = J+K
        ICRAZY = I+J+K
        K = J
        RETURN
        END
     --------------
        ...
        N = 5
        IVALUE = ICRAZY(N,N,N)
        IVALUE = IVALUE + N
        WRITE (6,*) IVALUE
        ...

Public and Private Attributes [EF 9.4]

By default, all items in a MODULE are "public," that is, they are accessible when the MODULE is imported via a USE statement. If desired, certain items may be designated as "private." Normally, this is done by switching the default to private for all items by including the single statement
      PRIVATE
at the beginning of the MODULE and then designating the components that will remain "public" explicitly. For example, one can designate variables as public in this way:
      REAL, PUBLIC   :: X, Y, Z
To designate subprograms as "public" (or private), one includes the names in the beginning section of the MODULE even though the body of the code follows after the CONTAINS statement. The following would be an example:
      MODULE THREE
         PRIVATE
         REAL :: X, Y, Z
         PUBLIC :: SUB1
      CONTAINS
         SUBROUTINE SUB1(A, B)
            ! body of subroutine 
         END SUBROUTINE
      END MODULE
Components designated PRIVATE are inaccessible to any program segment which USEs the MODULE, but still remain accessible to subprograms within the MODULE.

Additional Module Features [EF 9.5]

A USE statement can have alternative forms, with various specifiers. For example, one may specify which data elements one wishes to use from a MODULE. As an example,
      USE DATASET3, ONLY: X, Y
would allow the code to access only X and Y from MODULE DATASET3 even though the MODULE might contain numerous other variables.

It is possible to rename variables being accessed from a MODULE. For example, if one wanted to make use of variable Y in MODULE DATASET4, but call it Z2 in the code that is importing the MODULE, one could use this form of the USE statement:

      USE DATASET4, Z2 => Y

Location of Procedure Code [EF 10.3]

There are three majors ways to associate the code for subroutines or functions with a program segment which will invoke them. They are:

Internal Procedures [EF 10.3.1]

Procedures may be included as internal to a specific main program (or other subprogram) by placing the subprogram code within the calling program unit, immediately before the END statement.

If this is done, immediately before the subprogram code, a special CONTAINS statement is added.

Thus, as an example, one might have:

     PROGRAM SAMPLE2
        ! main program code
        STOP ! last main program statement
     CONTAINS
        SUBROUTINE ONE
        ! subprogram code
        RETURN
        END SUBROUTINE
        SUBROUTINE TWO
        ! subprogram code
        RETURN
        END SUBROUTINE
     END PROGRAM
In this approach, the procedures have complete access to all variables and other entities declared in the main program (as an exception to the older Fortran rule). Thus a variable declared in the main program may be changed by an action in the subprogram without the necessity of passing the variable as a argument.

Any subprograms thus declared may not themselves have other internal subprograms.

Although this approach avoid needing parameters, it does not permit the subprogram to be shared among several different main program (or other subprograms).

Module Procedures [EF 10.3.2]

It is also possible to enclose procedures within a module and then import the module into a main program (or other procedure) via a USE statement. The definition of any subroutine or function within this module take precedence over other definitions that may conflict with the routine names. This approach helps to make explicit to the programmer from where the subprogram code comes.

When including procedures in a module, the code must occur after a CONTAINS statement as in the example below.

In this approach, the procedures do not have access to variables or other entities in the calling code.

The following is an example of program which uses a module subroutine:

        MODULE ONE
        CONTAINS
          SUBROUTINE TWO(A,B)
          ! body of subroutine code
          END SUBROUTINE TWO
        END MODULE
        PROGRAM TEST3B
          USE ONE
          ! body of main program
        END PROGRAM TEST3B

External Procedures [EF 10.3.3]

The oldest method of associating procedures with main programs (or other subprograms) is to merely include the code for all procedures after the main program code in the source code file or in independent auxiliary files (which are compiled and linked after the main program code. Such a subprogram is often called an external subroutine or function. No specific additional information is needed in most cases. (Noted below is the precautions that need to be taken with the data type of any FUNCTION if this method is used.)

The following is an example of program which uses this method of subprogram association:

        PROGRAM TEST3B
         ! body of main program
        END PROGRAM TEST3B
        SUBROUTINE TWO(A,B)
         ! body of subroutine code
        END SUBROUTINE TWO

Fortran FUNCTIONS [EF 10.5]

In theory, Fortran FUNCTIONS are identical to C++ functions.

Skeleton

        type FUNCTION name ( parameter_list )
           ...
        RETURN
        END
NOTES: One could rewrite part of the sample subroutine in section 10.4 as a Fortran function as follows:
        REAL FUNCTION PROOT(A, B, C)
        REAL A, B, C
        PROOT = (-B + SQRT(B*B - 4.0*A*C))/(2.0*A)
        RETURN
        END
Then, in the calling section, one could use the function with its arguments (similar to the way the library functions such as SQRT are used within an arithmetic statement), for example,
        ...
        READ(5,101)A,B,C
        X1=2.0*PROOT(A,B,C)
        ...
The function's data type also plays an important rule in the calling section. If one uses the external method to associate subprograms with calling code (cf. EF 10.3.3), one needs to convey to the calling segment the exact data-type of any user-defined function (unless one is using implicit typing). Thus, in any segment of code (main program, subroutine, function) which uses ("calls") a user-defined function, the function name should also be declared as a local variable in a standard type statement to avoid any type-mismatch errors. For example, in the section which uses the function PROOT given above, there should be a declaration statement in which PROOT is declared as if it were a standard variable:
        REAL PROOT, A, B, C

Recursion [EF 11.1]

The concept of recursion in Fortran-90/95/2003 is identical to the concept of recursion in any other language that implements it. In brief, a function or subroutine is recursive if it calls itself.

For a general introduction to recursion (in C++) see this link.

Note that recursion was not implemented in older versions of Fortran!

Recursive Subroutines [EF 11.2]

To designate a Fortran-90/95/2003 subroutine as recursive, one adds the word RECURSIVE before the keyword SUBROUTINE in the header line for the code for that subprogram. No other adaptations are required. Subroutines designated as recursive are then used as they would be in other languages.

The following is an example of a version of a recursive Towers of Hanoi code implemented in Fortran-90/95/2003. (It is also available for downloading at this link.)

     PROGRAM HANOIMAIN
     ! recursive towers of hanoi program in 
     ! fortran 90
         INTEGER N
         N = 5
         CALL HANOI(N,'a','c','b')
     END
     !
     RECURSIVE SUBROUTINE HANOI(N,FROMPEG,AUXPEG,TOPEG)
         INTEGER N
         CHARACTER*1 FROMPEG,AUXPEG,TOPEG
         IF (N == 1) THEN
            WRITE(6,*)"Move disk 1 from peg ", frompeg, " to peg ", topeg
         ELSE
            CALL HANOI(N-1,FROMPEG,AUXPEG,TOPEG)
            WRITE(6,101) N, FROMPEG,TOPEG
            101 FORMAT(1X,"Move disk ",I1," from peg ", A1, " to peg ", A1)
            CALL HANOI(N-1,AUXPEG,TOPEG,FROMPEG)
         ENDIF
     END

Recursive Functions [EF 11.3]

To designate a Fortran-90/95/2003 function as recursive, one adds the word RECURSIVE before the keyword FUNCTION in the header line for the code for that subprogram. In addition, after the parameter list in the header line, one includes the keyword RESULT, followed, in parentheses, by the internal variable that will be used to store the value the function returns. Some authors recommend that this variable use the suffice _RESULT but this is not required by the language definition.

As in other languages, the recursive function can be called on the right side of an assignment statement within the function. The RESULT variable must be given a return value somewhere within the code.

(Recall from section 10.6 that in a non-recursive Fortran function, the return value is assigned to the name of the function which is considered a local variable within the subprogram. In a recursive function, the function name retains its purpose as a means to invoke itself, but a different identifier is designated as the way to return the value computed by the subprogram.)

Functions designated as recursive are then used as they would be in other languages.

The following is an example of a recursive fibonacci function implemented in Fortran-90/95/2003. (It is also available for downloading at this link.)

This implements the well-known formula for fibonacci numbers:

fn = fn-1 + fn-2
f2 = f1 = 1

     PROGRAM FIBONMAIN
     ! recursive fibonacci program in
     ! fortran 90
         INTEGER N, FINAL, FIBON
         N = 5
         FINAL = FIBON(N)
         WRITE(6,*) "Final fibonacci value for input of ",N," is",FINAL
     END
     !
     RECURSIVE FUNCTION FIBON(N) RESULT (FIB_RESULT)
         INTEGER  :: N, FIB_RESULT
         IF (N <= 2) THEN
             FIB_RESULT = 1
         ELSE
             FIB_RESULT = FIBON(N-1) + FIBON(N-2)
         ENDIF
     END

6

Arrays [EF 8]

As to be expected, arrays MUST be declared. If the same array is used in a main program and in subprograms, it must be declared in every segment in which it is used.

By default, the subscript begins at 1, but this can be superceded at the decration to any value.

Fortran uses parentheses to enclose subscripts, both for declaration and actual use. (For multi-dimensional arrays, both [all] subscripts are included in ONE set of parentheses.)

Simplest Declaration [EF 8.2]

To declare a one-dimension array, one merely writes (using Fortran-77 style):
        INTEGER A(50)
Here A is an INTEGER array with 50 elements, and the subscripts go from 1 to 50.

To indicate a different starting subscript value, one writes (e.g.):

        INTEGER A(-25:24)
Fortran-90/95/2003 style permits (suggests):
        REAL, DIMENSION(1:100)  :: C
This Fortran-90/95/2003 style makes use of an older, independent DIMENSION keyword whose use as a separate statement is discouraged.

Possible Compiler Confusion [EF 8.5]

Parentheses are used in 3 ways in Fortran:
  1. arithmetic expressions, e.g., A*(B+C)
  2. FUNCTION arguments, e.g., SQRT(A)
  3. Array subscripts, e.g., D(I)
If one forgets to declare a variable as an array everywhere it is used (i.e., in EVERY subprogram), the compiler may think the array is an unreferenced FUNCTION and give the error "UNKNOWN FUNCTION." However, in such situations, the actual error is an undeclared array. Knowledge of this "erroneous error" can help a programmer to decipher error messages and debug a program.

Example [EF 9.4]

        SUM = 0.0
        DO I = 1,N
           SUM = SUM + A(I) * B(I)
        END DO

Multi-Dimensional Arrays [EF 8.6]

Fortran permits a maximum of 7 subscripts. To declare them (using Fortran-77 style), one merely indicates the upper limits, such as:
        REAL A(3,4)
Fortran 90/95/2003 style is similar to what is given above for the one dimension case, e.g.,
        REAL, DIMENSION(1:3,1:4) :: A
To use a multi-dimensional array, one encloses all subscripts in the same set of parentheses, such as
        X = A(I,J)

Subprograms and Arrays [EF 10.5]

Any array used in a subprogram must be declared in the subprogram. But what if you want to use the SAME subprogram with arrays of different sizes?

With Fortran-66, in the subprogram, the array was often declared as

        REAL A(1)
The existence of some subscript indicated that A was, in fact, an array. Since arrays are always a CALL-BY-REFERENCE link and there was no checking of subscripts to see if they were within bounds, the exact dimension used in the declaration was irrelevant.

Fortran-77 and Fortran-90/95/2003 sometimes check to see if the subscript is "out of range." To avoid this error, one can pass the dimension as a parameter, and declare it with a variable parameter, for example:

        REAL A(N)
where BOTH A and N are passed as parameters.

Fortran-90/95/2003 also allows an indefinite marker in subroutines. E.g.,

        REAL, DIMENSION (:)  ::  A
NOTE: For 1-dimensional arrays, such worries about limits of indices are often not very crucial. But for 2 and higher dimensional arrays, all but the last subscript MUST be the same both in the main program and in the subprogram.

Example

        PROGRAM EXAMPLE2
        CHARACTER*80 A(100)
        CALL IN(A)
        CALL SORT(A)
        CALL OUT(A)
        STOP
        END
        !
        SUBROUTINE IN(A)
        CHARACTER*80 A(100)
        INTEGER I
        DO I = 1,100
           READ(25, "(A80)") A(I)
        END DO
        RETURN
        END
        !
        ...

Storage of Multi-Dimensional Arrays [EF 9.9]

RULE OF STORAGE: The order of storage in FORTRAN is such that the FIRST subscript varies the fastest.

For 2-dimensional arrays, this is referred to as "column major" order. For example,

          REAL A(3,4)
(where 3 refers to the number of rows and 4 to the number of columns) corresponds to an array (matrix):
        ( A11   A12   A13   A14  )
        ( A21   A22   A23   A24  )
        ( A31   A32   A33   A34  )
The Fortran storage order is: A(1,1), then A(2,1), then A(3,1) (now notice that the first subscript is at the maximum value, so we reset it and "up" the next subscript by one), then A(1,2), A(2,2), A(3,2), A(1,3), A(2,3), A(3,3), A(1,4), A(2,4), A(3,4).

Note that this is NOT the way that we usually think of storage of values in an array and NOT the way that other languages (may) store values. The possible difference in storage schemes can cause problems if one writes a Fortran SUBROUTINE which is to be used with a main program written in a different language and one passes arrays back and forth!

If we are dealing with a 3 or higher dimension array, e.g.,

     REAL A(3,4,2)
the same "rule" holds, in that the "first subscript varies the fastest." Thus, the next location after the one containing A(1,3,1) would contain A(2,3,1), then A(3,3,1), and then A(1,4,1), etc.

Arrays and I/O [DCS 9.8]

BASIC PRINCIPLE: Each READ/WRITE statement starts a new line with the related FORMAT statement. For example,
        DO I = 1,5
           READ(5, '(F10.5)') A(I)
        END DO
This segment "wants" to read 5 lines of input since the READ is executed 5 times inside the loop.
        READ(5,'(5F10.5)') A(1), A(2), A(3), A(4), A(5)
This read 1 line, with 5 values on it.
It can be abbreviated to (using an "Implied DO Loop"):
        READ(5,'(5F10.5)') (A(I),I=1,5)
If A was declared of dimension 5, one can omit the subscript entirely, and write:
        READ(5,'(5F10.5)') A
Since A is declared as an array, FORTRAN "expands" A to all its components.

Thus,

        WRITE(6,*) A
will cause the elements to be printed out in storage (i.e., column) order.

Multi-Dimensional Arrays and I/O [EF 8.9]

When using "automatic" input and output with multi-dimensional arrays, one must remember that Fortran will use the column major storage order for higher-dimensional arrays.

For example, with the 2-dimension array

            REAL A(3,4)
(given as an example earlier), a statement such as
            WRITE(*,101) A
will print out the elements in the order, A(1,1), A(2,1), A(3,1), A(1,2), etc.

To "force" row order (i.e., the order most "humans" would use), one needs to use explicit loops in some way. For example, using "implied DO-loops," the following will print out the array in "row-major" (i.e., "human") order.

            WRITE(*,101) ((A(I,J),J=1,4),I=1,3)

Re-Using Formats With Arrays [EF 4.6; 8.10]

A general Fortran "rule" is that when a READ or WRITE statement has more variables than the corresponding FORMAT statement has field descriptors, the FORMAT will be reused (each time beginning with a new line) until all variables are read-in or written-out.

For example,

          WRITE(6,102) (A(I),B(I),I=1,10)
          102 FORMAT(1X,2F20.5)
provides for only two variables per line in the FORMAT statement. The WRITE statement, however, requests that 20 variables be printed out. Thus, the FORMAT statement will be reused 10 times each time printing out two variables per line.

New Array Features [EF 8.11]

As a scientific language, Fortran has been used specially in applications involving arrays. To enhance its capabilities, several new features were introduced into Fortran 90.

For example, if A, B, and C are arrays, all of the same dimension(s), it is possible to write a legal assignment statement such as

        A = B + C
This statement would take every corresponding element in arrays B and C, add them together, and put the sum into the corresponding location in A.

Similarly,

        A = B * C
multiplies corresponding elements, but does not perform a "matrix-matrix" product.

Also allowed are products of constants and matrices and the use of most mathematical functions on matrices. For example,

        A = 3.0*B
and
        A = SQRT(B)
are both legal statements.

This features makes it very easy to write a routine to "swap" (exchange) the values of two matrices without the use of loops. For example,

       SUBROUTINE SWAP(A,B)
         REAL, DIMENSION(:) :: A,B
         REAL, DIMENSION(SIZE(A)) :: WORK
         WORK = A
         A = B
         B = WORK
       END SUBROUTINE SWAP

WHERE Construct and Other Array Features [EF 8.12, 8.13, 8.14, 8.15]

The WHERE structure is similar to an IF structure, except that an array identifier appears in the logical expression being tested, and assignments in the body of the structure involve arrays. The logical expression tests every element of the array and the assignments (or other statements) are performed where-ever the expression tests true. An ELSEWHERE clause is optional and a single statement is permitted as with a single statement IF. For example,
        WHERE (A > 0.0)
           B=SQRT(A)
        ELSEWHERE
           B=0.0
        ENDWHERE
or
        WHERE (A > 0.0) B=SQRT(A)

7
Beginning with Fortran 90, additional intrinsic (library) functions have been included which operate with arrays.

There are also array functions and array statements, (e.g., the WHERE statement, which is very similar to the IF, but assumes an array in the testing condition).

When arrays are used as parameters in functions or subroutines, Fortran 90 permits declaring them without specific values for the arguments, using colons instead for each dimension. The compiler will specify the exact dimension value at compile time. Such an array is called an assumed-shape array. To "help" the compiler, one needs to include an "interface" block in the calling segment (as a kind of "prototype" line).

For example,

             program test
                integer, dimension(2,2) :: a,b,c
                interface
                  subroutine sub1(a,b)                !--line 1
                    integer, dimension(:,:) :: a,b    !--line 2
                  end subroutine sub1                 !--line 3
                end interface
                a = reshape((/1,2,3,4/),(/2,2/))
                b = reshape((/4,3,2,1/),(/2,2/))
                c = matmul(a,b)
                call sub1(a,b)
                write(*, "(2i5)") c
             end program
             subroutine sub1(a,b)                     !--line 1
               integer, dimension(:,:) :: a,b         !--line 2
               write(*,*) "beginning of sub1"
               write(*,"(2i5)") a
               write(*,"(2i5)") b
             end subroutine                           !--line 3
Fortran 90 also introduced the possibility of "allocating" array at runtime if the size of the array would not be know at compile time. In such cases, an array is declared as "allocatable," inserting colons instead of constants for the subscript values, e.g.,
           REAL, ALLOCATABLE :: A(:,:)
Then, before the array is used, the exact space needed can be determined and the actual space allocated, e.g.,
           READ(*,*) N,M
           ALLOCATE( A(N,M) )
When the array is no longer needed, the space can be released via
           DEALLOCATE( A )
Fortran 90/95/2003 also includes other specialized features to deal with arrays. It is possible, for example, to specify segments of an array, e.g., A(2:5) (indicating elements 2 through 5, inclusive) or to specify elements via a "stride" indicator, e.g., A(2:6:2) (indicating elements 2, 4, and 6).


This page is maintained by Dennis C. Smolarski, S.J. dsmolarski@math.scu.edu
© Copyright 1998, 1999, 2006 Dennis C. Smolarski, SJ, All rights reserved. Last changed: 17 January 2006.