We assume in the following course basic knowledge in computer programming: CPU, Memory, source code vs. executable code, compilers vs. interpreter, etc.
Fortran: Le langage normalisé by M. Dubesset & J. Vignes, Technip Ed.
fortran 95/2003 explained by M. Metcalf, J. Reid, and M. Cohen, Oxford Univ. Press
Fortran is a programming language widely used in the scientific community. It is also one of the oldest programming language.
If C language was described as the "mother" of modern programming languages (C++, Java, Python, PHP, Perl, etc.), Fortran could be regarded as the "father": it is older (1960's) but it is still widely used, especially in the HPC (High Performance Computing) community since its programs can be translated (compiled) into most efficient executables in terms of CPU time. At the turn of the century, Fortran language has changed to incorporate many features of "modern" programming languages (i.e., modules, interfaces, object-oriented schemes, etc.) as well as unique features targeted to HPC programming (i.e., array management, high-level I/O management, etc.).
Fortran's superiority had always been in the area of numerical, scientific, engineering, and technical applications.
Fortran is an acronym for FORtran TRANslation. It was originally created by John Backus while working at IBM in 1954. It was approved by the American Standards Association (later the American National Standards Institute, ANSI) as the first ever standard for programming language: Fortran 66.
Following the growing success of Fortran 66, a new standard, Fortran 77, was published to incorporate many compiler implementation features targeted to large-scale programs (a.k.a., compiler/vendor extensions).
With the emergence of new modern programming languages (C++, Java, etc.) new Fortran standardization was started in 1990's. To preserve the vast investment in Fortran 77 codes, the whole of Fortran 77 was retained as a subset. However, unlike the previous standard, which resulted almost entirely from an effort to standardize existing practices, the Fortran 90 standard was much more a development of the language, introducing features which were new to Fortran, but were based on experience in other languages.
In 1995, an improved Fortran standard, Fortran 95, was published. This minor revision is mostly compatible with Fortran 90 and includes HPF (High Performance Fortran) features targeted for parallel computers.
The last major Fortran revision was published in 2004 (Fortran 2003). Among other things, it includes object-oriented programming support, floating point exception handling, or interoperability with the C programming language.
Fortran 77: an old language widely used¶
Fortran makes use of words built from characters that are part of an alphabet. These words are combined to form sentences that follow the rules of the Fortran syntax.
The Fortran alphabet¶
The Fortran alphabet is composed of:
- the 26 letters from
Fortran does not make the difference between UPPER case and lower case
the 10 digits from
some special characters like
the space (or blank) character
Fortran makes also use of reserved words (a.k.a., keywords) that have special meanings.
Identifiers are words designed by the user. They are built 1 to 6 alphanumerical characters. The first character of an identifier must be a letter.
Identifiers serve to indicate names of programs, functions, variables, etc.
A label is an integer composed of 1 to 5 digits that serves to identify instructions (see below).
A sentence is composed of a succession of syntax elements and should have a special meaning in the Fortran programming language.
A = 3
A program unit is composed a series of sentences that form a block. This is either a main program or a sub-program.
A main program starts with the declaration:
A sub-program is either:
When the Fortran programming language was created, the only way to write source codes was by using punch-cards. While this support has been now abandoned, Fortran 77 still uses its structure:
a file containing a Fortran program is composed of 80 characters lines
For each line:
- column 1 has a particular role: if starting with
C, it refers to a comment
- column 1 to 5 can contain a label
- column 6 is used as the continuation mark. If the character in column 6 is not a blank character, then column 7 to 72 should be considered as the continuation of the previous line.
- column 7 to 72 contains the Fortran instruction
- column 73 to 80 are not compiled and can therefore be used for user labelling (usually, one puts the name of the program and the line numbering)
- column 1 has a particular role: if starting with
0 -1 91 234242342
3.1415 0. -1. +0.25 -2.47E+1 5.14D-2
D character designs exponentiation for
DOUBLE PRECISION floating point numbers.
Character constants are also named Hollerith constants.
'AR.843-123' 'WITH A SPACE' 'TODAY''S SPECIAL MENU'
PARAMETER (PI=3.141592) PARAMETER (M=1, N=2)
The compiler will replace the symbolic names by their corresponding constant values.
A B TOTO RUDOPH HAL1969 MY_2K_BUG
The Fortran 77 standard implies the use of 1 to 6 characters maximum to name a variable. However, compilers usually accept longer names (= this is a compiler extension, not in the standard).
Standard variable types:
The IMPLICIT statement¶
IMPLICIT statement enable the implicit type declaration of variables.
By default, a Fortran program has the following
default IMPLICIT statement
IMPLICIT INTEGER (I-N), REAL (A-H, O-Z)
This means that all variables with names starting with letters between
N will be declared as
all other variables will be implicitly considered as
IMPLICIT NONE is not part of the Fortran 77 standard. But there again, it is a common Fortran extension
that prevents implicit declaration of variables: any variable used in a program unit must have its type declared.
By default, arrays in Fortran are indexed starting from the index 1.
DIMENSION keyword starts a statement indicating the size of an array.
Parentheses are used to indicate the size of an array as well as to access an element of an array.
PARAMETER (MAXEL=10) DIMENSION A(MAXEL) INTEGER B DIMENSION B(100) DIMENSION C(0:10) DIMENSION D(-10:10) DIMENSION E(10,10) REAL F DIMENSION F(-MAXEL:MAXEL,MAXEL+2:5*MAXEL/3) B1 = B(10) EE = E(5,3)
in Fortran, elements are ordered in memory column by column.
For example, for a two-dimensional array
The storage in memory is contiguous for
CHARACTER*18 NAME, FIRST CHARACTER TOWN*40, ZIP*5 CHARACTER*20 A, B FIRST='RUDOLPH' NAME='VALENTINO' ZIP='54000' A='VANDOEUVRE' B='-LES-NANCY' TOWN=A//B
// operator is the concatenation operator.
Substrings can be accessed via the
TOWN(10:13) TOWN(:10) TOWN(12:)
Logical operators operating on logical variables or constants:
.NEQV.Logical operators operating on integer or real variables and constants
.EQ.(valid also for complexes)
.NEQ.(valid also for complexes)
A = 10 GO TO 100 ... 100 B = -3 ... GOTO 200 ... 200 CONTINUE C = -14.2
K = 2 ... GO TO (1, 2, 3, 4, 5) K PRINT *, 'UNKNOWN VALUE FOR K' GOTO 10 1 PRINT *, 'K = 1' GOTO 10 2 PRINT *, 'K = 2' GOTO 10 3 PRINT *, 'K = 3' GOTO 10 4 PRINT *, 'K = 4' GOTO 10 5 PRINT *, 'K = 5' 10 CONTINUE
DELTA = B*B-4*A*C IF (DELTA) 10, 20, 30 10 PRINT *, 'NO SOLUTION' GOTO 40 20 PRINT *, 'ONE SOLUTION' GOTO 40 30 PRINT *, 'TWO SOLUTION' 40 PRINT *, 'BYE'
IF (A.GT.B) C = 10 IF (L.EQ..TRUE.) THEN PRINT *, 'SOMETHING' END IF IF (RAD.LE.PI) THEN DEG = RAD/PI*180.0 ELSE DEG = -(RAD-PI)/PI*180.0 END IF IF (DELTA.LT.0) THEN ... ELSE IF (DELTA.EQ.0) THEN ... ELSE ... END IF
I1 = 1 I2 = 10 I3 = 3 DO 10 I = I1, I2, I3 PRINT *, I 10 CONTINUE PRINT *, 'LOOP COMPLETED'
Common Fortran extensions:
DO I = I1, I2 ... END DO DO WHILE (SOMETHING.EQ..TRUE.) ... END DO
Functions and subroutines¶
In Fortran, one distinguish
SUBROUTINE. The former is a sub-program which specifically returns a value
while the latter does not.
DOUBLE PRECISION FUNCTION DELTA1(A, B, C) IMPLICIT NONE DOUBLE PRECISION A, B, C DELTA1 = B*B - 4*A*C RETURN END
SUBROUTINE DELTA2(A, B, C, DELTA) IMPLICIT NONE DOUBLE PRECISION A, B, C, DELTA DELTA = B*B - 4*A*C RETURN END
PROGRAM SECOND IMPLICIT NONE DOUBLE PRECISION A, B, C, D1, D2 A = 1.0 B = 4.0 C = 4.0 D1 = DELTA1(A, B, C) CALL DELTA2(A, B, C, D2) END
INTRINSIC functions in Fortran refers to functions provided by the language and therefore handled by the compiler.
Array argument of unknown dimension¶
program second_order implicit none double precision coeff dimension coeff(3) double precision delta data coeff/1.0d0, 6.0d0, 1.0d0/ call delta_calc(coeff, delta) print *, delta end subroutine delta_calc(a, delta) implicit none double precision a, delta dimension a(*) delta = a(2)*a(2)-4*a(1)*a(3) return end
Files are composed of logical entities called records. Records can be lines, variables, arrays, etc. They are groups of related entities.
Records can be formatted or unformatted. Formatted records are composed of characters with significant meaning. Unformatted records are stored on the computer in binary format.
A file is composed of records of the same kind: formatted or unformatted. They can have sequential or direct access.
Fortran does not directly manipulate files. It assigns a logical unit and defines operations for this logical unit.
A logical unit is an integer number (usually < 100).
Reserved logical units are:
5 (or '*'): the standard input
6 (or '*'): the standard output
0: the standard error output
Files can be accessed in three different ways:
To access a file, it must be opened and assigned a logical unit:
Options of the
When working with a logical unit is finished, one must close the file using the
REWIND subroutine allows for starting reading back at the beginning of a file when using sequential access.
INQUIRE subroutine can give the state of a file (e.g., existing or non-existing).
INQUIRE options are:
FILE=filename: to specify the filename
EXIST=logic: put in the
logicvariable a logical number stating if the file exists or not
NUMBER=num: put in
numthe logical unit number of the file, if it has been previously opened
unit1 = 5 unit2 = 6 READ(unit1, 10) a WRITE(unit2, 20) a PRINT 20, a 10 FORMAT (f) 20 FORMAT (f20.10)
READ(5, '(f)') a WRITE(*, '(F20.10)') a PRINT '(f20.10)', a
Common flags for
f: floating point
a: character or string
Flags can be repeated in a format:
dimension ii(4,3) data ii /1,2,3,4,5,6,7,8,9,10,11,12/ 10 FORMAT (3i5) WRITE(*,10) ((ii(i,j),j=1,3),i=1,4)
String can be read and written like logical unit
CHARACTER*20 str WRITE(str, 100) 3.1415 100 FORMAT (f20.5)
Fortran 90/95/03: the new Fortran era¶
No column requirement in Fortran90 source code
implicit statement still active by default
Comment lines start now with
! (instead of
program my_f90 ! a comment line write(*,*) 'Hurray for Fortran90' end
The main difference is the use of
::to declare variables.
integer :: a double precision :: b character(len=20) :: string real, dimension(10) :: T integer, parameter :: constant = 10
In Fortran90, you can define your own type:
program toto type cartesian real :: x real :: y real :: z end type cartesian type(cartesian) :: point1, point2 point1%x = 1.0 point1%y = 2.0 point1%z = 3.0 print *, point1 point2 = cartesian(1.0, -1.0, 3.0) print *, point2 end
|Fortran77 (still accepted)||new Fortran90 syntax|
Better handling of strings with Fortran90 since new INTRINSIC functions exists:
character(len=20) :: town = 'NANCY' character(len=20) :: addon= '-LES-BAINS' character(len=len(town)+len(addon)) :: townname townname = town//addon print '("|",a,"|")', town print '("|",a,"|")', addon print '("|",a,"|")', townname townname = town(:len_trim(town)) // addon(:len_trim(addon)) print '("|",a,"|")', townname end
Control constructs are similar as in Fortran 77. Preferred version are:
IF (test) THEN ... ELSE [IF (test) THEN] ... ENDIF DO I = I1, I2 [, I3] ... END DO
EXIT statement can be used in loops, especially in an endless loop:
integer :: i i = 10 DO PRINT '("IN LOOP, I = ", i3)', i i = i -1 IF (i == 0) EXIT END DO PRINT *, 'Finished'
Program units and procedures¶
In Fortran90, you can control what goes in and what goes out of a function or a subroutine:
program second_order_f90 double precision :: a = 1.0 double precision :: b = 6.0 double precision :: c = 1.0 double precision :: delta print 10, a, b, c call delta_calc(a, b, c, delta) print '(f10.5)', delta 10 format (3f10.5) end subroutine delta_calc(a, b, c, delta) double precision, intent(in) :: a double precision, intent(in) :: b double precision, intent(in) :: c double precision, intent(out) :: delta delta = b*b-4.0d0*a*c end
Fortran90 introduces the concept of module which provides a mechanism for packaging all data, subroutines, etc. related to one specific group.
For example, if one defines a new type, then a module can contain the type definition as well as all functions and subroutines related to the use of that new type.
module second_order type polynome double precision :: a double precision :: b double precision :: c end type polynome contains function delta(P) implicit none double precision :: delta type(polynome) :: P delta = P%b * P%b - 4.0d0 * P%a * P%c return end function delta end module program test use second_order type(polynome) :: my_equation my_equation%a = 1.0 my_equation%b = 6.0 my_equation%c = 1.0 print *, delta(my_equation) end program
DATA statement can be replaced by direct initialization for arrays:
integer, parameter :: constant = 10 integer, dimension(constant) :: Tab = (/1, 2, 3, 4, 5, 6, 7, 8, 9, 10/) integer, dimension(4) :: AnotherTab data AnotherTab/1, 2, 1, 0/ print *, constant print *, Tab print *, AnotherTab end
You can slice arrays !
integer, parameter :: constant = 10 integer, dimension(constant) :: Tab = (/1, 2, 3, 4, 5, 6, 7, 8, 9, 10/) print '(10i4)', constant print '(10i4)', Tab print '(10i4)', Tab(3:5) print '(10i4)', Tab(:5) print '(10i4)', Tab(8:) print '(10i4)', Tab( (/1, 3, 4, 5, 3/) ) print '(10i4)', Tab Tab( (/1, 3, 5, 7, 9/) ) = (/0, -2, 0, -2, 0/) print '(10i4)', Tab end
Vectors or arrays can be directly addressed without the use of
integer, parameter :: constant = 10 integer, dimension(constant) :: Tab1 = (/1, 2, 3, 4, 5, 6, 7, 8, 9, 10/) integer, dimension(constant) :: Tab2 integer, dimension(constant) :: Tab3 data Tab2 /10*2/ Tab3 = Tab1+Tab2 print '(1x,10i3)', Tab1 print '("+",10i3)', Tab2 print '("=",10i3)', Tab3 print * Tab3 = Tab1*Tab2 print '(1x,10i3)', Tab1 print '("*",10i3)', Tab2 print '("=",10i3)', Tab3 end
integer, dimension (4,3) :: B integer, dimension (2,2) :: C integer, dimension (2,2) :: D data B /1,2,3,4,5,6,7,8,9,10,11,12/ data C /-1, -2, -3, -4/ print *, 'B Matrix:' print '(3i3)', ((B(i,j),j=1,3),i=1,4) print *, 'C Matrix:' print '(2i3)', ((C(i,j),j=1,2),i=1,2) D(1:2, 1:2) = B(3:4, 2:3) + C(1:2, 1:2) - 5 print *, 'D Matrix:' print '(2i3)', ((D(i,j),j=1,2),i=1,2) end
Array size does not have to be specified at the compilation. They can be dynamically allocated:
program dynamic double precision, dimension(:), allocatable :: T integer :: my_size read(*,'(i)') my_size allocate(T(my_size)) T = 1.5 print '(10f8.3)', T deallocate(T) end
WHERE statement helps you to perform operation only for certain elements:
program dynamic double precision, dimension(:), allocatable :: T integer :: my_size print *, '("T size ?")' read(*,'(i)') my_size allocate(T(my_size)) do i = 1, my_size write(*,'("element ", i3, "?")') i read(*, '(f)') T(i) end do print *, "T as you defined it:" print '(10f8.3)', T print *, "Absolute value of T:" where (T < 0) T = -T print '(10f8.3)', T deallocate(T) end
program tablog integer, parameter :: my_size = 10 integer, dimension(my_size) :: TAB_I double precision, dimension(my_size) :: TAB_F integer i do i = 1, my_size TAB_I(i) = i*7-5 end do where (mod(TAB_I,2) == 0) TAB_I = -TAB_I print '(10i4)', TAB_I where (TAB_I > 0) TAB_F = log(DBLE(TAB_I)) elsewhere TAB_F = log(-1.0d0*TAB_I) end where print '(10f8.3)', TAB_F end
In some cases,
DO loops can be replaced by
program test_forall double precision, dimension(12) :: A integer, dimension(4, 3) :: B integer :: i, j, k do i = 1, 12 A(i) = 3.0d0*i-2.5d0 end do print '(12f8.3)', A print * forall(i=1:12) A(i) = 3.0d0*i-2.5d0 print '(12f8.3)', A do i = 1,4 do j = 1, 3 B(i,j) = i+(j-1)*4 end do end do print '(3i3)', ((B(i,j), j = 1,3), i = 1, 4) print * forall(i=1:4, j=1:3) B(i,j) = i+(j-1)*4 print '(3i3)', ((B(i,j), j = 1,3), i = 1, 4) print * forall(i=1:4, j=1:3) B(i,j) = FLOOR(A(i+(j-1)*4)) end forall print '(3i3)', ((B(i,j), j = 1,3), i = 1, 4) end
The use of module to contain new type definitions and subroutines/functions related to these new types is the beginning of Object-oriented programming (= it resembles class definition).
Fortran90 provide a way to override common operators using the
module fract type fraction integer :: numerator integer :: denominator end type fraction interface operator(+) module procedure add end interface contains function add(f1, f2) implicit none type(fraction) :: add type(fraction), intent(in) :: f1, f2 integer a, b, c, d a = f1%numerator b = f1%denominator c = f2%numerator d = f2%denominator add%numerator = a*d+b*c add%denominator = b*d return end function add end module fract program test_interface use fract type(fraction) :: F1 type(fraction) :: F2 type(fraction) :: F3 F1%numerator = 2 F1%denominator = 3 F2%numerator = 1 F2%denominator = 4 F3 = F1+F2 print *, F1 print *, F2 print *, F3 end
Fortran90 provides also
PRIVATE statement for attributes and methods (=functions or subroutines); inheritance possibilities using the
EXTENDS statement; encapsulation; polymorphism (using the
INTERFACE block statement); etc.