`Fortran 90' Lessons for Computational Chemistry
------------------------------------------------
Curro Pérez-Bernal
0.0
-------------------------------------------------------------------------------
Abstract
--------
The present document is a basic introduction to the `Fortran'
programming language based in several textbooks and references (see
Chapter 13, `Referencias'). It contains the basic scheme of `Fortran'
programming taught in the _Computational Chemistry_ module (fourth
year, second semester) of the University of Huelva Chemistry Degree.
-------------------------------------------------------------------------------
Contents
--------
1. Introduction
1.1. Objectives
1.2. Main items.
1.3. Example Codes.
2. Basic Operations
2.1. Objectives
2.2. Main items.
2.3. Example Codes.
3. Introduction to `Fortran' Arrays
3.1. Objectivos
3.2. Main items.
3.3. Example Codes.
4. More on Arrays
4.1. Objectives
4.2. Main items.
4.3. Example Codes.
5. Control Structures
5.1. Objectives
5.2. Main items.
5.3. Example codes.
6. `INPUT/OUTPUT' (I)
6.1. Objectivos
6.2. Main Items.
6.3. Example Codes
7. `Input/Output' (II)
7.1. Objectives
7.2. Main items.
7.3. Example Codes
8. Subprograms (I): Functions
8.1. Objectives
8.2. Main items.
8.3. Example Codes
9. Subprograms (II): subroutines
9.1. Objectives
9.2. Main items.
9.3. Example Codes
10. Subprograms (III): modules
10.1. Objectives
10.2. Main items.
10.3. Example codes.
11. Subprogramas (IV)
11.1. Objetivos
11.2. Puntos destacables.
11.3. Programas usados como ejemplo.
12. Instalación y uso de las bibliotecas `BLAS' y `LAPACK'
12.1. Objetivos
12.2. Puntos destacables.
12.3. Programas usados como ejemplo.
13. Referencias
-------------------------------------------------------------------------------
1. Introduction
---------------
1.1. Objectives
---------------
The main aims of this session consist of:
1. giving a short introduction on programming and programming
languages.
2. emphasize the importance of a clear understanding of the problem
under study and the use of flow diagrams for achieving structured
and clear source code.
3. a brief presentation of the main features of the `Fortran'
programming language.
4. installation of the `GNU Fortran' compiler, `gfortran'.
5. Studying two simple codes.
6. Presenting possible sources of information for the interested
student.
1.2. Main items.
----------------
By default we will use the `emacs' text editor. The first examples
are the simple programs Section 1.3.1, ``excode_1_1.f90'' y Section
1.3.2, ``excode_1_2.f90''.
Using the examples the student should be aware of the main sections
included in a program::
1. Head of the code with the statement `PROGRAM' .
2. Variable definition.
3. Main program body, including `I/O' operations.
4. End of the program: `END PROGRAM' .
Things to take into account:
* Importance of remarks and comments. Include many comments in
your code, trying to be as clear as possible. `Fortran' remarks
are introduced with the character `!'. A correct indentation
also improves the code readability. The `emacs' text editor
greatly helps in this task.
* The importance of the `IMPLICIT NONE' statement. Declare and
initialize properly all variables as in example Section 1.3.2,
``excode_1_2.f90''.
* Distinguish the `I/O' operations.
1.3. Example Codes.
-------------------
1.3.1. `excode_1_1.f90'
-----------------------
PROGRAM ex_1_1
!
! This program reads and displays a string.
!
IMPLICIT NONE
CHARACTER(LEN=50) :: Name
!
PRINT *,' Write your name. Do not forget quoting it:'
PRINT *,' (max 50 characters)'
READ(*,*), Name
PRINT *, Name
!
END PROGRAM ex_1_1
1.3.2. `excode_1_2.f90'
-----------------------
PROGRAM ex_1_2
!
! This program reads three numbers and compute their sum and mean value
!
IMPLICIT NONE
REAL :: N1, N2, N3, Average = 0.0, Total = 0.0
INTEGER :: N = 3
PRINT *,' Input three numbers (return, coma, or space separated).'
PRINT *,' '
READ *,N1,N2,N3
Total = N1 + N2 + N3
Average = Total/N
PRINT *,'Sum: ',Total
PRINT *,'Mean value: ',Average
END PROGRAM ex_1_2
-------------------------------------------------------------------------------
2. Basic Operations
-------------------
2.1. Objectives
---------------
The main aims of this session are:
1. introducing basic `Fortran' syntax rules and the characters
allowed in source files.
2. Basic arithmetic operations and operator precedence rules.
3. The `PARAMETER' declaration.
4. Explain the different kinds of numerical variables and its use.
2.2. Main items.
----------------
Basic syntax rules:
* Maximum number of characters per line of code: 132.
* Maximum length of a variable name string: 31.
* '&' denotes that the statement continues in the next line. It is
added at the end of the broken line[1]
* '!' is the character that marks the rest of the line as a
comment.
* ';' is the character that separates several statements in the
same line.
Variable names can include the low hyphen ('`_'') and mix alphanumeric
characters and digits, though variable names first character cannot be
a number.
`Fortran' character set:
A-Z Letters 0-9 Digits
_ Underscore Blank
= Equal + Plus
- Minus * Asterisk
/ Slash or oblique ' Apostrophe
( Left parenthesis ) Right parenthesis
, Comma . Period or decimal point
: Colon ; Semicolon
! Exclamation mark "" Quotation mark
% Percent & Ampersand
< Less than > Greater than
Precedence of arithmetic operators:
* Operators: {`+',`-',`*',`/',`**'}.
* Precedence: (1) `**' (right to left); (2) `*',`/' (compiler
dependent); (3) `+',`-' (compiler dependent).
* Beware of floating point operations rounding, in particular when
mixing different numeric variable types. Minimizing rounding
errors is at times a complex and subtle task.
The compiler transform different type variables to a common type
when performing a calculation. The priority ordering, from lower
to higher is: `INTEGER', `REAL', `DOUBLE PRECISION', and
`COMPLEX'. Therefore, an operation involving an integer and a
double precision float is performed transforming the integer
value to double precision and the result is given in double
precision too. The final result is the transformed to the type
of the variable to which is assigned.
* Integer types:
1. 32 bits ::`(2**31)-1 = 2,147,483,647' (~ 10**9)
2. 64 bits :: `(2**63)-1 = 9,223,372,036,854,774,807' (~
10**19)
* Floats types and precision:
1. Real 32 bits :: precision = 6-9 `~ 0.3E38 - 1.7E38'
2. Real 64 bits :: precision = 15-18 `~ 0.5E308 - 0.8E308'
* Making use of the `PARAMETER' modifier in a variable definition
we can define constant values in a program. See Section 2.3.4,
``excode_2_4.f90''.
* (*) Different kinds of floats and integers in `Fortran' and the
intrinsic functions[2] `KIND', `EPSILON', `PRECISION', and `HUGE'
and how to define a variable in each of the existing types.
`INTEGER' VARIABLES: if we would like to define an integer
variable `i0' that could take values between -999999 y 999999 we
should define a variable, called e.g. `ki', making use of the
intrinsic function `SELECTED_INT_KIND()' and make use of it in
the variable definition.
INTEGER, PARAMETER :: ki = SELECTED_INT_KIND(6)
INTEGER(KIND=ki) :: i0
The intrinsic function[3] `SELECTED_INT_KIND(X)' output is an
integer that indicates the type (_kind_) of an integer variable
capable of storing any integer in the range `(-10E+X, 10E+X)'
where `X' is also an integer. If we want that any integer
constant in our program to be treated with a particular type of
integer this can be done as follows:
-1234_ki
2354_ki
2_ki
The error output of the `SELECTED_INT_KIND(X)' function is `-1'.
Real numbers are more involved. We make use a _floating point
representation_, and all the following are valid real numbers in
`Fortran':
-10.66E-20
0.66E10
1.
-0.4
1.32D-44
2E-12
3.141592653
In this case the statement to control the type of float is
`SELECTED_REAL_KIND(p=X,r=Y)', with two input parameters. The
output is an integer associated with a float that complies with
the following rules:
* it has a precision at least equal to `X' and a range of
decimal exponents given at least by `Y'. The argument
labels are optional.
* Among various possible results, the one with the minimum
decimal precision will be chosen.
* At least one of the two input parameter should be specified.
Both `X' and `Y' are integers. If there is no variable type
that fulfills the requested condiotions the output of the
function will be `-1' if the precision does not reach the
requested level, `-2' if the problem is in the exponent, and
`-3' if both requirements cannot be satisfied.
As an example, if we want to define a real variable called `a0'
with 15 digit precision and exponents in the range -306 to 307:
INTEGER, PARAMETER :: kr = SELECTED_REAL_KIND(15,307)
REAL(KIND=kr) :: a0
Scalar floats can be addressed defining its particular kind as
follows
-10.66E-20_kr
0.66E10_kr
142857._kr
-0.4_kr
2E-12_kr
3.141592653_kr
Program Section 2.3.5, ``excode_2_5.f90'' contains several
examples of the use of the `KIND' statement and the default value
of `KIND' for several variable types.
Program Section 2.3.6, ``excode_2_6.f90'' contains examples of
the different types of viariales, how to define them, and how to
test them using the intrinsics `KIND', `DIGITS', `EPSILON',
`TINY', `HUGE', `EXPONENT', `MAXEXPONENT', `MINEXPONENT',
`PRECISION' , `RADIX' y `RANGE'.
In this program variables are defined using the functions
`SELECTED_INT_KIND' and `SELECTED_REAL_KIND' This is correct
though it is more appropriate to define the variables according
to the process in the notes.
The used functions are
1. `KIND(x)': integer output, type of the variable `x'.
2. `DIGITS(x)': integer output, number of significant digits of
`x'.
3. `EPSILON(x)': if the input `x' is a float the output is
another float, of the same type (_kind_) than `x'. It is
the smallest number of this type such that `1.0 + EPSILON(X)
> 1'.
4. `TINY(x)': for float `x' input the output is of the same
kind than `x', and it is the minimum positive value that can
be defined for such variables.
5. `HUGE(x)': for float `x' input the output is of the same
kind than `x', and it is the maximum positive value that can
be defined for such variables.
6. `EXPONENT(x)': `x' variable exponent. If `x = 0' then
`EXPONENT(x)=0' too.
7. `MAXEXPONENT(x)': maximum exponent possible for `x' type
variables.
8. `MINEXPONENT(x)': minimum exponent possible for `x' type
variables.
9. `PRECISION(x)': if `x' is real or complex the output is an
integer equal to the number of digits of precision of the
variable `x'.
10. `RADIX(x)': integer result equal to the radix basis of `x'.
11. `RANGE(x)': integer result equal to the range of exponent
for the variable `x'.
* (*) Present how float arithmetic involves precision loss and how
an appropriate use of the diferent data types can help to
minimize this problem.
[1] Except if a string is broken in two lines. In this particular case it
is added at the end of the broken line and the beginning of the next
line.
[2] See Section 2.3.4, ``excode_2_4.f90''.
[3] More info on intrinsic functions and function definition in `Fortran'
can be found in Section 8.1, `Objectives'.
2.3. Example Codes.
-------------------
2.3.1. `excode_2_1.f90'
-----------------------
PROGRAM ex_2_1
IMPLICIT NONE
!
! Program computing the energy of a vibrational normal mode
!
! Ge(v) = we (v+1/2) - wexe (v+1/2)^2
!
!
! Definicion de variables
REAL :: energ_0, energ, delta_e ! deltae = energ-energ0
REAL :: we = 250.0, wexe = 0.25 ! Units: cm-1
INTEGER :: v = 0
CHARACTER*60 :: for_mol
! I/O
PRINT *,'Formula de la molecula : '
READ *, for_mol
PRINT *,'Num. de quanta de excitacion : '
READ *, v
! Calculations
energ = we*(v+0.5) - wexe*(v+0.5)**2
energ_0 = we*(0.5) - wexe*(0.5)**2
delta_e = energ - energ_0
! I/O
PRINT *
PRINT *,'Especie molecular: ', for_mol
PRINT *,'num. de quanta: ', v
PRINT *,'energ = ',energ,'cm-1'
PRINT *,'energ_0 = ',energ_0,'cm-1'
PRINT *,'energ - energ_0 = ',delta_e,'cm-1'
END PROGRAM ex_2_1
2.3.2. `excode_2_2.f90'
-----------------------
PROGRAM ex_2_2
IMPLICIT NONE
REAL :: A,B,C
INTEGER :: I
A = 1.5
B = 2.0
C = A / B
I = A / B
PRINT *
PRINT *, 'Case (1), Float variable'
PRINT *,A,'/',B,' = ',C
PRINT *, 'Case (2), Integer variable'
PRINT *,A,'/',B,' = ',I
END PROGRAM ex_2_2
2.3.3. `excode_2_3.f90'
-----------------------
PROGRAM ex_2_3
IMPLICIT NONE
INTEGER :: I,J,K
REAL :: Answer
I = 5
J = 2
K = 4
Answer = I / J * K
PRINT *,'I = ',I
PRINT *,'J = ',J
PRINT *,'K = ',K
PRINT *,'I / J * K = ',Answer
END PROGRAM ex_2_3
2.3.4. `excode_2_4.f90'
-----------------------
PROGRAM ex_2_4
! Program to compute the time that takes to light to travel
! a given distance in AU.
! 1 AU = 1,50E11 m
!
!Definicion de variables
IMPLICIT NONE
! a_u : astronomic unit in km
REAL , PARAMETER :: a_u=1.50*10.0**8
! y_l : year light --> distance travelled by light during a year
REAL , PARAMETER :: y_l=9.46*10.0**12
! m_l : minute light --> distance travelled by light during a minute
REAL :: m_l
! dist : distance travelled in AUs (INPUT)
REAL :: dist
! t_min : time in minutes needed to travel the distance dist
REAL :: t_min
!
! min : integer part of t_min
! seg : seconds from the decimal digits of t_min
INTEGER :: min, seg
!
m_l = y_l/(365.25 * 24.0 * 60.0) ! m_l Calculation
!
PRINT *
PRINT *,'Distance in AUs'
READ *, dist
PRINT *
!
t_min = (dist*a_u)/m_l
min = t_min; seg = (t_min - min) * 60
!
PRINT *,' It takes light ' , min,' minutes and ', seg,' seconds'
Print *,' to travel a distance of ',dist,' AU.'
END PROGRAM ex_2_4
2.3.5. `excode_2_5.f90'
-----------------------
PROGRAM ex_2_5
INTEGER :: i
REAL :: r
CHARACTER(LEN=1) :: c
LOGICAL :: l
COMPLEX :: cp
PRINT *,' Integer ',KIND(i)
PRINT *,' Real ',KIND(r)
PRINT *,' Char ',KIND(c)
PRINT *,' Logical ',KIND(l)
PRINT *,' Complex ',KIND(cp)
END PROGRAM ex_2_5
2.3.6. `excode_2_6.f90'
-----------------------
PROGRAM ex_2_6
! From Program ch0806 of Chivers & Sleightholme
!
! Examples of the use of the kind
! function and the numeric inquiry functions
!
! Integer arithmetic
!
! 32 bits is a common word size,
! and this leads quite cleanly
! to the following
! 8 bit integers
! -128 to 127 10**2
! 16 bit integers
! -32768 to 32767 10**4
! 32 bit integers
! -2147483648 to 2147483647 10**9
!
! 64 bit integers are increasingly available.
! This leads to
! -9223372036854775808 to
! 9223372036854775807 10**19
!
! You may need to comment out some of the following
! depending on the hardware platform and compiler
! that you use.
INTEGER :: I
INTEGER ( SELECTED_INT_KIND( 2)) :: I1
INTEGER ( SELECTED_INT_KIND( 4)) :: I2
INTEGER ( SELECTED_INT_KIND( 8)) :: I3
INTEGER ( SELECTED_INT_KIND(16)) :: I4
! Real arithmetic
!
! 32 and 64 bit reals are normally available.
!
! 32 bit reals 8 bit exponent, 24 bit mantissa
!
! 64 bit reals 11 bit exponent 53 bit mantissa
!
REAL :: R = 1.0
REAL ( SELECTED_REAL_KIND( 6, 37)) :: R1 = 1.0
REAL ( SELECTED_REAL_KIND(15,307)) :: R2 = 1.0
REAL ( SELECTED_REAL_KIND(18,310)) :: R3 = 1.0
PRINT *,' '
PRINT *,' Integer values'
PRINT *,' Kind Huge'
PRINT *,' '
PRINT *,KIND(I ),' ',HUGE(I )
PRINT *,' '
PRINT *,KIND(I1 ),' ',HUGE(I1 )
PRINT *,KIND(I2 ),' ',HUGE(I2 )
PRINT *,KIND(I3 ),' ',HUGE(I3 )
PRINT *,KIND(I4 ),' ',HUGE(I4 )
PRINT *,' '
PRINT *,' ----------------------------------- '
PRINT *,' '
PRINT *,' Real values'
!
PRINT *,' Kind ', KIND(R ), ' Digits ', DIGITS(R )
PRINT *,' Huge = ',HUGE(R ), ' Tiny =', TINY(R)
PRINT *,' Epsilon = ',EPSILON(R),' Precision = ', PRECISION(R)
PRINT *,' Exponent = ',EXPONENT(R), 'MAXExponent = ', MAXEXPONENT(R), ' MINExponent = ',MINEXPONENT(R)
PRINT *,' Radix = ', RADIX(R ), ' Range =', RANGE(R)
PRINT *,' '
!
!
PRINT *,' Kind ', KIND(R1 ), ' Digits ', DIGITS(R1 )
PRINT *,' Huge = ',HUGE(R1 ), ' Tiny =', TINY(R1)
PRINT *,' Epsilon = ',EPSILON(R1),' Precision = ', PRECISION(R1)
PRINT *,' Exponent = ',EXPONENT(R1), 'MAXExponent = ', MAXEXPONENT(R1), ' MINExponent = ',MINEXPONENT(R1)
PRINT *,' Radix = ', RADIX(R1 ), ' Range =', RANGE(R1)
PRINT *,' '
!
!
PRINT *,' Kind ', KIND(R2 ), ' Digits ', DIGITS(R2 )
PRINT *,' Huge = ',HUGE(R2 ), ' Tiny =', TINY(R2)
PRINT *,' Epsilon = ',EPSILON(R2),' Precision = ', PRECISION(R2)
PRINT *,' Exponent = ',EXPONENT(R2), 'MAXExponent = ', MAXEXPONENT(R2), ' MINExponent = ',MINEXPONENT(R2)
PRINT *,' Radix = ', RADIX(R2 ), ' Range =', RANGE(R2)
PRINT *,' '
!
!
PRINT *,' Kind ', KIND(R3 ), ' Digits ', DIGITS(R3 )
PRINT *,' Huge = ',HUGE(R3 ), ' Tiny =', TINY(R3)
PRINT *,' Epsilon = ',EPSILON(R3),' Precision = ', PRECISION(R3)
PRINT *,' Exponent = ',EXPONENT(R3), 'MAXExponent = ', MAXEXPONENT(R3), ' MINExponent = ',MINEXPONENT(R3)
PRINT *,' Radix = ', RADIX(R3 ), ' Range =', RANGE(R3)
PRINT *,' '
!
END PROGRAM ex_2_6
-------------------------------------------------------------------------------
3. Introduction to `Fortran' Arrays
-----------------------------------
3.1. Objectivos
---------------
The main aims of this session are the following
1. present one dimension arrays as `Fortran' data structures.
2. present the different ways of defining an array.
3. present the `DO' loop syntax and the implicit `DO' and their use
with matrices.
4. explore dynamic arrays in `Fortran 90'
5. present multidimensional arrays as `Fortran' data structures.
3.2. Main items.
----------------
Basic Definitions:
1. _rank_: number of indices necessary to indicate unambiguously an
array element.
2. _bounds_: max and min values of the indices labelling array
elements in each dimension.
3. _extent_: number of elements in an array dimension.
4. _size_: total number of a matrix.
5. _conformal_: two arrays are conformal if both have the same rank
and extent.
The following points should be emphasized:
* one dimensional array (vector) definition making use of the `DO'
control structure (see Section 3.3.1, ``excode_3_1.f90'' and
exercise 2_1)
* use of the `PARAMETER' declaration for the definition of array
bounds in static array declaration.
* initialize before use. Beware of surprises. The initialization
to a common constant value is extremely simple: `vec = '.
A possible alternative is the use of _array constructors_. In
the following example, in order to define an integer array with
six elements called `vec_int' three possible and equivalent
options are given
do i = 0, 5
vec_int(i) = 2*i
enddo
vec_int = (/(2*i, i = 0, 5)/)
vec_int = (/0,2,4,6,8,10/)
Last two options involve _array constructors_ and can be carried
out when the array is declared[1]
* use of the `ALLOCATABLE' declaration and the use of the
`ALLOCATE' function, as it is shown in example Section 3.3.2,
``excode_3_2.f90''. The `ALLOCATE' option `STAT = ' allows
to chek if the array has been properly defined. See example in
program Section 9.3.3, ``excode_9_3.f90''.
* implicit `DO' and multidimensional arrays. See example Section
3.3.3, ``excode_3_3.f90''.
* most general form of the `DO' control structure and possibility
of introducing zero or negative array indeces. See example
Section 3.3.4, ``excode_3_4.f90''.
* combination of `bash' redirectioning with `Fortran' programs.
Necessary for exercise 2, it is explained in Chapter 4, `More on
Arrays'.
[1] Beware of this feature in functions and subroutines.
3.3. Example Codes.
-------------------
3.3.1. `excode_3_1.f90'
-----------------------
PROGRAM ex_3_1
!
! VARIABLES DEFINITION
IMPLICIT NONE
REAL :: Total=0.0, Average=0.0
INTEGER, PARAMETER :: Week=7
REAL , DIMENSION(1:semana) :: Lab_Hours
INTEGER :: Day
!
PRINT *,' Labor Time (hours per day during a week):'
DO Day= 1, Week
READ *, Lab_Hours(Day)
ENDDO
!
DO Day = 1, Week
Total = Total + Lab_Hours(Day)
ENDDO
Average = Total / Week
!
PRINT *,' Average Weekly Workload: '
PRINT *, Average, ' hours'
END PROGRAM ex_3_1
3.3.2. `excode_3_2.f90'
-----------------------
PROGRAM ex_3_2
!
! VARIABLE DEFINITION
IMPLICIT NONE
REAL :: Total=0.0, Average=0.0
REAL , DIMENSION(:), ALLOCATABLE :: Lab_Hours
INTEGER :: Day, Number_Days
!
PRINT *,' Number of workdays:'
READ *, Number_Days
!
ALLOCATE(Lab_Hours(1:Number_Days))
!
PRINT *,' Daily hours of work in ', Number_Days, ' days.'
DO Day = 1, Number_Days
READ *, Lab_Hours(Day)
ENDDO
!
DO Day=1, Number_Days
Total = Total + Lab_Hours(Day)
ENDDO
Average = Total / Number_Days
!
PRINT *,' Average daily workhours in ',Number_Days, ' days : '
PRINT *, Average, ' hours'
!
END PROGRAM ex_3_2
3.3.3. `excode_3_3.f90'
-----------------------
PROGRAM ATTEND_CONTROL
IMPLICIT NONE
INTEGER , PARAMETER :: N_students = 3
INTEGER , PARAMETER :: N_courses = 3
INTEGER , PARAMETER :: N_lab = 3
INTEGER :: student, course, lab
CHARACTER*2 , DIMENSION(1:N_lab,1:N_courses,1:N_lab) :: attend = 'NO'
DO student = 1, N_students
DO course = 1,N_courses
READ *,(attend(lab,course,student),lab = 1, N_lab)
ENDDO
ENDDO
PRINT *,' Lab attendance : '
DO student=1, N_students
PRINT *,' Student = ', student
DO course = 1,N_courses
PRINT *,' Course = ', course, ' : ', (attend(lab,course,student),lab=1,N_lab)
ENDDO
ENDDO
END PROGRAM ATTEND_CONTROL
3.3.4. `excode_3_4.f90'
-----------------------
PROGRAM ex_3_4
IMPLICIT NONE
REAL , DIMENSION(-180:180) :: Time=0
INTEGER :: Degree, Strip
REAL :: Value
!
DO Degree=-165,165,15
Value=Degree/15
DO Strip=-7,7
Time(Degree+Strip)=Value
ENDDO
ENDDO
!
DO Strip=0,7
Time(-180 + Strip) = -180/15
Time( 180 - Strip) = 180/15
ENDDO
!
DO Degree=-180,180
PRINT *,Degree,' ',Time(Degree), 12 + Time(Degree)
END DO
END PROGRAM ex_3_4
-------------------------------------------------------------------------------
4. More on Arrays
-----------------
4.1. Objectives
---------------
The main aims of this lesson are the following:
1. presenting storage ordering of multidimensional arrays.
2. presenting how to manipulate whole matrices or arrays sections in
`Fortran'.
3. matrix definition using the `WHERE' statement.
4.2. Main items.
----------------
* Storage ordering
Multidimensional arrays are stored in memory by `Fortran' in such
a way that the first subindex varies faster than the second, that
varies faster than the third and so on and so forth. This is
known as _column major order_.
For example, if we define a `4 x 2' matrix as
`REAL , DIMENSION(1:4,1:2) :: A',
the `A' array has eight elements stored into memory as follows
A(1,1), A(2,1), A(3,1), A(4,1), A(1,2), A(2,2), A(3,2), A(4,2)
The `A' matrix initialization can be carried out in several ways.
Assuming that each element should be initialized with a number
equal to the index of the corresponding row, we could use two
loops[1]
DO I_col = 1, 2
DO I_row = 1, 4
A(I_row, I_col) = I_row
ENDDO
ENDDO
An _array constructor_ can also be of help, though the seemingly
simple solution
A = (/ 1, 2, 3, 4, 1, 2, 3, 4 /)
does not work. The _array constructors_ produce vectors and not
matrices. The vector defined above is of dimension 8, but not a
matrix 4x2. The vector and the array `A' have identical sizes,
but are not conformal. The statement `RESHAPE' gives a possible
solution. The sytax of this statement is
= RESHAPE(, )
Where is a matrix that would be reshaped and
is a vector with the dimensions of the new matrix .
The total number of elements of and
needs to be identical. In the previous example a correct _array
constructor_ is
A = RESHAPE( (/ 1, 2, 3, 4, 1, 2, 3, 4 /), (/ 4, 2 /) )
Another example can be found in code Section 4.3.3,
``excode_4_3.f90''. The `RESHAPE' command can be used in the
array declaration
INTEGER, DIMENSION(1:4,1:2) :: A = &
RESHAPE( (/ 1, 2, 3, 4, 1, 2, 3, 4 /), (/ 4, 2 /) )
The data ordering in storage is specially important in I/O
operations. The command
PRINT*, A
will give as a result
A(1,1), A(2,1), A(3,1), A(4,1), A(1,2), A(2,2), A(3,2), A(4,2)
It is necessary to take this into account also when making use of
the `READ' statement to fill with values the elements of a
multidimensional array: `READ(,*) A'. The _implicit_ `DO'
statement allow to change the standard reading sequence
READ(unit,*) ( ( A(row,col), col = 1, 2 ), row = 1, 4 )
* `FORTRAN' allows to define multidimensional arrays, being seven
is the max number of indices. The code Section 4.3.2,
``excode_4_2.f90'' an array is fully characterized making use of
several _inquiry_ type functions (see Section 8.3.2,
``excode_8_2.f90'').
* The usage of whole matrices is a great advantage. Definig
floating point vectors `V1', `V2', `V3' y `V4' as
REAL , DIMENSION(1:21) :: V1, V2
REAL , DIMENSION(-10:10) :: V3
REAL , DIMENSION(0:10) :: V4
The following tasks are simply performed using this `Fortran 90'
feature.
1. Assigning a particular value to the full array:
V1 = 0.5
2. Equating matrices:
V1 = V2
Making each `V1' element equal to the corresponding element
of `V2'. This is only valid when both matrices are
_conformal_. It is also valid
V3 = V2
but it is _not_ valid
V1 = V4
3. All arithmetic operation for scalars can be also applied to
conformal matrices, though they may not be the expected
mathematical operations.
V1 = V2 + V3
V1 = V2*V3
In the first case `V1' is the sum of two vectors, but in the
second case each `V1' element is the product of the
corresponding `V2' and `V3' elements, which is not the
scalar product. In the two-dimensional matrices case, if we
define
REAL , DIMENSION(1:4,1:4) :: A, B, C
The following are valid statements in `Fortran 90'
A = A**0.5
C = A + B
C = A * B
The last case is not the matrix product but a matrix having
each element as the result of the product of the
corresponding `A' annd `B' elements.
4. A matrix can also be read without a `DO' loop, as in example
Section 4.3.1, ``excode_4_1.f90'', where also the intrinsic
function `SUM' is presented.
* The definition of array slices is possible using the index syntax
`::'
V1(1:10) = 0.5
B(1,1:4) = 100.0
In the first case the first ten elements of the `V1' array take
the value `0.5', while in the second elements in the first row of
`B' take the value `100.0'. See example Section 4.3.1,
``excode_4_1.f90''.
The most general syntax to define a slice is
`::', the first slice element has index
, the last one is less than or equal to and
is the index variable increment. The default value of
is `=1'. Examples:
V1(:) ! the whole vector
V1(3:10) ! elements V1(3), V1(4), ... , V1(10)
V1(3:10:1) ! "" "" "" ""
V1(3:10:2) ! "" V1(3), V1(5), ... , V1(9)
V1(m:n) ! elements V1(m), V1(m+1), ... , V1(n)
V1(9:4:-2) ! "" V1(9), V1(7), V1(5)
V1(m:n:-k) ! elements V1(m), V1(m-k), ... , V1(n)
V1(::2) ! "" V1(1), V1(3), ... , V1(21)
V1(m:m) ! 1 x 1 array
V1(m) ! Scalar
* The assignment of values to an array can be done making use of a
_logic mask_, with the `WHERE' statement. The use of the mask
allows to select those array elements that should undergo the
initialization. If, e.g., we need to compute the square root of
the elements of a floating point array called `data_mat' and
store them in the array `sq_data_mat', we can skip the use of
loops and conditionals as in the following code
DO j_col = 1, dim_2
DO i_row = 1, dim_1
IF ( data_mat(i_row, j_col) >= 0.0 ) THEN
sq_data_mat(i_row, j_col) = SQRT( data_mat(i_row, j_col) )
ELSE
sq_data_mat(i_row, j_col) = -99999.0
ENDIF
ENDDO
ENDDO
The `WHERE' statement greatly simplifies this task. The
statement syntax is
[:] WHERE ()
....
Array assignment block 1
....
ELSEWHERE () []
....
Array assignment block 2
....
ELSEWHERE
....
Array assignment block 3
....
ENDWHERE []
where and are boolean arrays
conformal with the array being assigned. The previous example is
therefore simplified to
WHERE ( data_mat >= 0.0 )
sq_data_mat = SQRT( data_mat )
ELSEWHERE
sq_data_mat = -99999.0
ENDWHERE
* These aspects are treated in the different given examples.
Example Section 4.3.3, ``excode_4_3.f90'' shows how to initialize
vectoras and matrices, in the last case making use of the
`RESHAPE' statement. The example also introduces the `Fortran'
intrinsics `DOT_PRODUCT' (scalar product) and `MATMUL' (matrices
product).
Example Section 4.3.4, ``excode_4_4.f90'' exemplifies the use
`WHERE' in combination with a logical mask.
Example Section 4.3.5, ``excode_4_5.f90'' stress the fact the the
elimination of `DO' loops can sometimes bring surprising results
about.
Example Section 4.3.6, ``excode_4_6.f90'' shows how to use the
`RESHAPE' statement in the definition of a matrix and how to use
slicing in the defined array.
[1] The _column major order_ storage makes optimal to run over columns in
the inner loop, specially when running with large matrices.
4.3. Example Codes.
-------------------
4.3.1. `excode_4_1.f90'
-----------------------
PROGRAM ex_4_1
!
! VARIABLE DEFINITION
IMPLICIT NONE
REAL :: Total=0.0, Average=0.0
REAL , DIMENSION(:), ALLOCATABLE :: t_worked
! Correction Factor
REAL :: correction =1.05
INTEGER :: day, num_days
!
PRINT *,' Number of workdays: '
READ *, num_days
! Dynamic storage definition
ALLOCATE(t_worked(1:num_days))
!
PRINT *,' Worked hours per day in ', num_days, ' days.'
! I/O
READ *, t_worked
!
t_worked(num_days-1:num_days) = correction*t_worked(num_days-1:num_days)
!
DO day=1,num_days
Total = Total + t_worked(day)
ENDDO
Average = Total / num_days
!
PRINT *,' Average daily hours of work in ',num_days, ' days : '
PRINT *, Average
!
END PROGRAM ex_4_1
4.3.2. `excode_4_2.f90'
-----------------------
PROGRAM ex_4_2
!
! Program to characterize an array making use of inquiry functions
!
IMPLICIT NONE
!
REAL, DIMENSION(:,:), ALLOCATABLE :: X_grid
INTEGER :: Ierr
!
!
ALLOCATE(X_grid(-20:20,0:50), STAT = Ierr)
IF (Ierr /= 0) THEN
STOP 'X_grid allocation failed'
ENDIF
!
WRITE(*, 100) SHAPE(X_grid)
100 FORMAT(1X, "Shape : ", 7I7)
!
WRITE(*, 110) SIZE(X_grid)
110 FORMAT(1X, "Size : ", I7)
!
WRITE(*, 120) LBOUND(X_grid)
120 FORMAT(1X, "Lower bounds : ", 7I6)
!
WRITE(*, 130) UBOUND(X_grid)
130 FORMAT(1X, "Upper bounds : ", 7I6)
!
DEALLOCATE(X_grid, STAT = Ierr)
IF (Ierr /= 0) THEN
STOP 'X_grid deallocation failed'
ENDIF
!
END PROGRAM EX_4_2
4.3.3. `excode_4_3.f90'
-----------------------
PROGRAM ex_4_3
!
! VARIABLES DEFINITION
IMPLICIT NONE
REAL, DIMENSION(1:5) :: VA = (/1.0,1.0,1.0,1.0,1.0/), PMAT
INTEGER I
INTEGER, DIMENSION(1:5) :: VB = (/(2*I,I=1,5)/)
REAL :: PE
REAL , DIMENSION(1:5,1:5) :: MC
REAL , DIMENSION(25) :: VC = &
(/ 0.0,0.0,0.0,0.0,1.0,0.5,2.0,3.2,0.0,0.0, &
0.0,0.0,0.0,0.0,11.0,0.5,2.3,3.2,0.0,0.0, &
1.0,3.0,-2.0,-2.0,-0.6 /)
! Scalar Product
PE = DOT_PRODUCT(VA,VB)
!
PRINT *, 'Scalar Product (VA,VB) = ', PE
!
! Product of matrices VAxMC
! RESHAPE VC to make it a 5 x 5 matrix
MC = RESHAPE(VC,(/5,5/))
PMAT = MATMUL(VA,MC)
!
PRINT *, ' VA x MC = ', PMAT(1:5)
!
END PROGRAM ex_4_3
4.3.4. `excode_4_4.f90'
-----------------------
PROGRAM ex_4_4
IMPLICIT NONE
REAL , DIMENSION(-180:180) :: Time=0
INTEGER :: Degree, Strip
REAL :: Value
CHARACTER (LEN=1), DIMENSION(-180:180) :: LEW=' '
!
DO Degree=-165,165,15
Value=Degree/15
DO Strip=-7,7
Time(Degree+Strip)=Value
ENDDO
ENDDO
!
DO Strip=0,7
Time(-180 + Strip) = -180/15
Time( 180 - Strip) = 180/15
ENDDO
!
DO Degree=-180,180
PRINT *,Degree,' ',Time(Degree), 12 + Time(Degree)
END DO
!
WHERE (Time > 0)
LEW='E'
ELSEWHERE (Time < 0)
LEW='W'
ENDWHERE
!
PRINT*, LEW
!
END PROGRAM ex_4_4
4.3.5. `excode_4_5.f90'
-----------------------
PROGRAM ex_4_5
!
! VARIABLE DEFINITION
IMPLICIT NONE
REAL, DIMENSION(1:7) :: VA = (/1.2,2.3,3.4,4.5,5.6,6.7,7.8/)
REAL, DIMENSION(1:7) :: VA1 = 0.0, VA2 = 0.0
INTEGER I
!
VA1 = VA
VA2 = VA
!
DO I = 2, 7
VA1(I) = VA1(I) + VA1(I-1)
ENDDO
!
VA2(2:7) = VA2(2:7) + VA2(1:6)
!
! Previous two operations with VA1 and VA2 seem that
! should provide the same result. Which is not the case.
PRINT*, VA1
PRINT*, VA2
!
! To obtain the same effect without an explicit DO loop we can do
! the following
VA2 = VA
VA2(2:7) = (/ (SUM(VA2(1:I)), I = 2,7) /)
!
PRINT*, VA1
PRINT*, VA2
END PROGRAM ex_4_5
4.3.6. `excode_4_6.f90'
-----------------------
PROGRAM ex_4_6
!
! DEFINITION OF VARIABLES
IMPLICIT NONE
INTEGER, DIMENSION(1:3,1:3) :: A = RESHAPE( (/ 1,2,3,4,5,6,7,8,9 /), (/ 3,3 /) )
!
!
! 1 4 7
! A = 2 5 8
! 3 6 9
!
PRINT*, "Matrix Element", A(2,3)
PRINT*, "Submatrix", A(1:2,2:3)
PRINT*, "Submatrix", A(::2,::2)
PRINT*, "Matrix Column", A(:,3)
PRINT*, "Matrix Row", A(2,:)
PRINT*, "Full Matrix", A
PRINT*, "Transposed Matrix", TRANSPOSE(A)
END PROGRAM ex_4_6
-------------------------------------------------------------------------------
5. Control Structures
---------------------
5.1. Objectives
---------------
The main aims of this session consist of:
1. presenting the different conditional control structures in
`Fortran' (_branching_).
2. presenting the different way of building loops in `Fortran' code.
These structures allows the programmer to control the program flow,
allowing the conditional execution of statements according to the user
input values or the values acquired by variables during the program
execution.
It is extremely important to take into account before starting to
write code in any programming language that a previous step should be
accomplished. In encompasses having a clear idea of the problem, the
inputs and outputs, the program structure, breaking complex tasks into
simpler subtasks, and the optimal algorithm. A flow diagram can be of
great help at this stage.
The division of the problem into simpler and simpler tasks is called
_top-down design_. Each subtasks should be coded and checked in an
independent manner.
5.2. Main items.
----------------
We provide a scheme of the main control structures, strting with
conditionals and later of loops.
* Conditionals.
Depend on the evaluation of boolean expressions for which the
following operators are defined:
* `==' To be equal to.
* `/=' Not to be equal to.
* `>' Greater than.
* `<' Lesser than.
* `>=' Greater or equal than.
* `<=' Lesser or equal than.
There exist also logical operators to combine several logical
expressions:
* `.AND.'
* `.OR.'
* `.NOT.'
* `.EQV.' (Boolean '==' operator)
* `.NEQV.' (Boolean '/=' operator)
The `==' and `/=' shouldn't be used to compare real type
variables, due to their nonexact nature. If e.g. `A' and `B'
are real variables, the following code is discouraged
...
IF (A==B) same = .TRUE.
...
The alternative would be to define a tolerance and compare the
variables as follows
REAL :: TOL = 0.00001
...
IF (ABS(A-B) < TOL) same = .TRUE.
...
The possible conditional statements are
1. `IF THEN ENDIF'
The syntax of this conditional statement is
.
. code
.
IF () THEN
.
. code_1
.
ENDIF
.
. code
.
Only if the is true the `code_1' block
instructions are executed.
If there is only one statement in the `code_1' block the
command can be simplified to a one liner removing the `THEN'
and `ENDIF' keywords as follows
.
. code
.
IF () statement
.
. code
.
2. `IF THEN ELSE ENDIF'
The syntax of this conditional statement is
.
. code
.
IF () THEN
.
. code_1
.
ELSE
.
. code_2
.
ENDIF
.
. code
.
If the is true the `code_1' block
instructions are executed, if it is false then `code_2'
block is run.
3. `IF THEN ELSE IF ENDIF'
The syntax of this conditional statement is
.
. code
.
IF () THEN
.
. code_1
.
ELSE IF () THEN
.
. code_2
.
ENDIF
.
. code
.
In case that the is true the `code_1'
block instructions are executed, if it is false but is true then `code_2' block is run.
4. `IF THEN ELSE IF ELSE ENDIF'
The syntax of this conditional statement is
.
. code
.
IF () THEN
.
. code_1
.
ELSE IF () THEN
.
. code_2
.
ELSE
.
. code_3
.
ENDIF
.
. code
.
In case that the is true the `code_1'
block instructions are executed, if it is false but is true then `code_2' block is run. If both
are false then the `code_3' block is run.
5. `SELECT CASE'
The `CASE' statement allows to choose among different
options in a clear and efficient way, though it has some
limitations.
The syntax of this conditional statement is
SELECT CASE (selector)
CASE (label-1)
block-1
CASE (label-2)
block-2
CASE (label-3)
block-3
.............
CASE (label-n)
block-n
CASE DEFAULT
block-default
END SELECT
The `selector' is either a variable or an expression of the
_integer_, _logical_, or _character_ type. It cannot be a
real or complex number.
The `label-1' ... `label-n' labels have the following
syntax
value
value_1 : value_2
value_1 :
: value_1
The first one is positive if the selector is equal to
`value' and the second if the selector takes a value in the
range `value_1' to `value_2'. The third(fourth) is true if
the selector has a value larger(less) than `value_1'. The
`valor', `value_1', and `value_2' should be constants or
variables defined with the `PARAMETER' declaration.
The `selector' expression is evaluated first. The result is
compared with the values in each one of the labels, running
the block of instructions of the first successful
comparison. If none of the labels is true the
`block-default' is run if it exists.
A simple example:
SELECT CASE (I)
CASE (1)
PRINT*, "I = 1"
CASE (2:9)
PRINT*, "I in [2,9]"
CASE (10:)
PRINT*, "I in [10,INF]"
CASE DEFAULT
PRINT*, "I is negative"
END SELECT CASE
The `SELECT CASE' statement is more elegant than a series of
`IF''s as only one expression controls the access to the
different alternatives.
Conditional control structures can be nested in several levels.
For the sake of clarity in this case the different levels should
be labeled as follows
firstif: IF (a == 0) THEN
PRINT*, "a is zero"
secondif: IF (c /= 0) THEN
PRINT*, "a is zero and c is not zero"
ELSE secondif
PRINT*, "a and c are zero"
ENDIF secondif
ELSEIF (a > 0) THEN firstif
PRINT*, "a is positive"
ELSE firstif
PRINT*, "a is negative"
ENDIF firstif
The role of the lables `firstif' and `secondif' is to clarify the
source code for the reader. Once a label is included in the `IF'
statement, then it has to be present also in the `ENDIF', while
it is optional in the `ELSE' and `ELSEIF'. The number of nested
conditionals is unlimited.
The example code Section 5.3.1, ``excode_5_1.f90'' contains the
`IF THEN ELSE IF ELSE ENDIF' structure and, apparently, the same
task is coped with in example Section 5.3.2, ``excode_5_2.f90''
with the `CASE' structure.
* Loops
1. Basic loop: The `DO' statement
We have been already introduced to the basic `DO' loop:
DO Var = initial_value, final_value, increment
Block of Code
END DO
The variable `Var' changes from `initial_value' to
`final_value' adding `increment' each iteration.
2. The `DO WHILE' loop
This loop has this structure:
DO WHILE (conditional)
Block of code
ENDDO
In this case the block of code is run until the
`conditional' in the head of the block is false. E.g. see
example Section 5.3.4, `Programa ejemplo_5_4.f90'.
3. The `REPEAT UNTIL' loop
This type of loop has the following structure:
DO
Block of code
#
IF (conditional) EXIT
END DO
The loop is executed until the conditional is evaluated
`True'. This case differs from the previous two in that the
code block is run at least once.
In this case we make use of the `EXIT' statement. When this
statement is run into a loop the program leaves inmediately
the loop and keeps running from the order following the
corresponding `ENDDO'. Another interesting statement when
working with loops is `CYCLE'. The execution of the `CYCLE'
statement makes the program to return to the beginning of
the loop, without running the statements in the loop block
between the `CYCLE' statement and the end of the loop.
As in the conditionals case, nested loops can be labeled.
This greatly clarifies the source code and, in particular,
allows to indicate to which loop level refers the statements
`EXIT' and `CYCLE'. By default, they address the inner
loop.
There is a last statement, worth to mention, the `GOTO'
command, though its use is highly discouraged in the moder
programming standards.
5.3. Example codes.
-------------------
5.3.1. `excode_5_1.f90'
-----------------------
PROGRAM ex_5_1
!
IMPLICIT NONE
!
REAL :: Grade
CHARACTER(LEN = 2), DIMENSION(1:5) :: List_Grades=(/'D ','C ','B ','A ','A+'/)
INTEGER :: IN
! READ NOTE
PRINT *, "Student mark??"
READ *, Grade
!
IF (Grade>=0.0.AND.Grade<5.0) THEN
IN=1
ELSE IF (Grade>=5.0.AND.Grade<7.0) THEN
IN=2
ELSE IF (Grade>=7.0.AND.Grade<9.0) THEN
IN=3
ELSE IF (Grade>=9.0.AND.Grade<10.0) THEN
IN=4
ELSE IF (Grade==10.0) THEN
IN=5
ELSE
IN=0
ENDIF
!
IF (IN==0) THEN
PRINT *, "The input : ", Grade," has a wrong value. Only [0,10]"
ELSE
PRINT *, "The student grade is ", List_Grades(IN)
ENDIF
!
END PROGRAM EX_5_1
5.3.2. `excode_5_2.f90'
-----------------------
PROGRAM ex_5_2
!
IMPLICIT NONE
!
REAL :: Grade
INTEGER :: Index, Integer_Grade
CHARACTER(LEN=2), DIMENSION(1:5) :: List_Grades=(/'D ','C ','B ','A ','A+'/)
! READ Grade
PRINT *, "Nota del estudiante?"
READ *, Grade
!
Integer_Grade = NINT(Grade)
!
SELECT CASE (Integer_Grade)
CASE (0:4)
Index = 1
CASE (5,6)
Index = 2
CASE (7,8)
Index = 3
CASE (9)
Index = 4
CASE (10)
Index = 5
CASE DEFAULT
Index = 0
END SELECT
!
IF (Index==0) THEN
PRINT *, "The input grade : ", Grade," is out of bounds. Only [0,10]."
ELSE
PRINT 100, Grade, List_Grades(Index)
ENDIF
!
100 FORMAT(1X,'Student grade is ',F4.1,' (',A3,')')
!
END PROGRAM EX_5_2
5.3.3. `excode_5_3.f90'
-----------------------
PROGRAM ex_5_3
!
IMPLICIT NONE
!
REAL :: PIover2 = ASIN(1.0)
REAL :: ANGLE1 = 0.0, ANGLE2 = 0.0
INTEGER :: I
!
DO I = 0, 16, 2
ANGLE1 = I*PIO2/4.0
!
WRITE(*,*)
WRITE(*,*) 'Cos(',I/2,'PI/4) = ',COS(ANGLE1),'; Cos(',I/2,'PI/4) = ',COS(ANGLE2)
WRITE(*,*) 'Sin(',I/2,'PI/4) = ',SIN(ANGLE1),'; Sin(',I/2,'PI/4) = ',SIN(ANGLE2)
WRITE(*,*)
!
ANGLE2 = ANGLE2 + PIO2/2.0
!
ENDDO
END PROGRAM ex_5_3
5.3.4. Programa ejemplo_5_4.f90
-------------------------------
PROGRAM excode_5_4
!
IMPLICIT NONE
!
REAL :: X_val = 0.0
REAL :: X_app = 0.0, X_sum = 0.0
INTEGER :: I_flag = 1, I_count = 0
!
! Taylor Series: SIN(X) = X - X^3/3! + X^5/5! - X^7/7! + ...
WRITE(*,*) "Introduce the angle X (RAD) :"
READ(*,*) X_val
!
I_count = 1
X_app = X_val
X_sum = X_val
!
PRINT*, ' Order Approx. SIN(X) Approx. - SIN(X)'
!
DO WHILE (I_flag == 1)
!
PRINT*, I_count, X_app, SIN(X_val), X_app - SIN(X_val)
!
X_sum = X_sum*(-1)*X_val*X_val/((I_count*2+1)*(I_count*2))
X_app = X_app + X_sum
!
I_count = I_count + 1
!
WRITE(*,*) "STOP? (0 yes, 1 no)"
READ(*,*) I_flag
IF (I_flag /= 1 .AND. I_flag /= 0) I_flag = 1
!
ENDDO
!
END PROGRAM excode_5_4
-------------------------------------------------------------------------------
6. `INPUT/OUTPUT' (I)
---------------------
6.1. Objectivos
---------------
The main aims of this lesson are the following:
1. present how to make use of the standard `bash' redirection for
reading and writing data in `Fortran'.
2. present the `FORMAT' statement, as well as its differents
descriptors and its use with the commands `PRINT' and `WRITE'.
3. get a basic knowledge about file handling in `Fortran' with the
commands `OPEN', `CLOSE', and `WRITE'.
6.2. Main Items.
----------------
* `bash' shell redirection
The standard input and output (`STDIN/STDOUT') redirection in
`bash' with `<' and `>' allows a `Fortran' program in a simple
and direct way to read from and write to a file.
As an example, the following commands run from a terminal execute
a program called `a.out'. Its output is sent to a file called
`output.dat' in the first case. In the second case, the program
reads its input from a file called `input.dat', instead of the
standard option, the keyboard. In the third case bot options are
combined.
a.out > output.dat
a.out < input.dat
a.out output.dat
The assignment number 4 can be quite done quite easily making use
of standard redirection.
The error output (`STDERR') can be redirected too as follows
a.out 2> output.dat
a.out 2>&1 ouput.dat
In the second case `STDERR' and `STDOUT' are merged together in
file `output.dat'.
* In order to gain a finer control of the format of input and
output statementes the so called _format descriptors_ are
introduced. We have make use of the default options or free
format up to now, indicated with the symbol `*' as in
`READ(*,*)', `READ*', and `PRINT*'.
To specify a particular format for the input and output in the
above mentioned commands the syntax used is `PRINT' ,
, or `READ' , ; where is a
label driving to a `FORMAT' statement with the necessary
descriptors and are the constant and variables that
will be read or written. It is possible to include directly the
descriptors in the statement.
The format descriptors in `FORTRAN', due to historical reasons
(line printers), treated the first character as a control
character. If the first character is
1. `0' : double spacing.
2. `1' : new page.
3. `+' : no spacing. Print over the previous line.
4. `blank' : simple spacing.
But this is not anymore true unless you are using a line printer
(quite bizarre situation in the XXI century).
The format descriptors can fix the vertical position in a line of
text, alter the horizontal position of characters in a line,
control the display of integers (`I'), floats (`F' and `E'),
strings `A' and logical variables (`L').
The following symbols are used
1. : column number
2. : number of digits after decimal point (real values)
3. : minimum number of digits displayed
4. : number of spaces
5. : times a descriptor is repeated
6. : number of characters affected by a descriptor
Descriptors in I/O operations
1. Integers: `I': General form `I'
This descriptor indicates that integer values will be
read or written, and they occupy characters or columns.
The number is right justified and if the number of digits is
less than the number of spacings the rest of the space is
filled with space characters. The example
PRINT 100, I, I*I
100 FORMAT(' ',I3, ' squared is ', I6)
outputs a space, a three-digit integer, the string '`squared
is'' and finish with the square of the variable `I', with a
maximum number of six digits. More examples can be found in
Section 6.3.1, ``excode_6_1.f90'', where the reader can see
the effect of having a number with more digits than the
allocated space in the format. In this example we also
include the `X' descriptor, such that `X' includes
space characters in the output, or skip characters from
the input.
Format descriptors can be also included directly in the
`PRINT' statement, though the resulting code is generally
less readable.
PRINT "(' ',I3, ' squared is ', I6)", I, I*I
As can be seen in code example Section 6.3.1,
``excode_6_1.f90'' we can have an arithmetic overflow in a
variable and the solution is shown in example Section 6.3.2,
``excode_6_2.f90''.
2. Real values descriptor `F': General form `F'
Where is the total number of columns used to fit the
number, the number of figures after the decimal point,
and the number of times this descriptor is applied.
For example if the descriptor is `F7.3' the number will be
displayed with three figures after the decimal point and
occupies seven spaces. This implies that this format
descriptor is valid for numbers between `-99.999' and
`999.999'. The truncated decimal part of the number is
properly rounded. It may happens that as a result of the
truncation the number has more digits than expected. The
output will be changed for asterisk characters (`*').
In source code Section 6.3.3, ``excode_6_3.f90'' we face
such kind of problems.
3. Real descriptor `E': General form `E'
Introduces scientific notation. The number that multiplies
the power of ten takes values between 0.1 to 1.0. This case
differs from the previous one that some space should be
devoted to the exponent. In fact, apart from the
multiplier, it is needed one character for the sign of the
number if it is negative, another character for the decimal
point, another one for the `E' symbol (stands for Exponent),
and the magnitude and sign of the exponent. Therefore the
minimum size in this case is . Example code
Section 6.3.4, ``excode_6_4.f90'' is identical to example
Section 6.3.3, ``excode_6_3.f90'' changing the `F'
descriptors to `E'. This change facilitates to work with
numbers whose value vary into a big range.
4. Real data descriptor `ES': general format `ES'
It allows the use of the standard scientific notation, with
the factor that multiplies the power of ten taking velues in
the range 1.0 to 10.0. Apart from this it is similar to the
previous float descriptor.
5. Logical data descriptor `L': general format `L'
Logical or boolean data only take the values `TRUE' or
`FALSE' and the output of this descriptor will be a right
justified `T' or `F'.
6. Character descriptor `A': general format `A' or `A'
This format implies that there are string fields
character wide. If is missing the string is taken with
the same length of the character variable. The example
Section 6.3.5, ``excode_6_5.f90'' shows how this descriptor
is used.
7. `X' descriptor: general format `X'
The `X' descriptor controls horizontal displacement, and it
implies that spaces should be included in the output.
You can find an example of this descriptor in source code
Section 6.3.5, ``excode_6_5.f90''.
8. Descriptor `T':
El descriptor `T' controla el desplazamiento horizontal e
indica que se salte directamente a la columna .
9. `/' descriptor:
The `/'descriptor flush the output buffered and feeds a new
line. It does not need to be included between commas.
10. The repetition of a set of descriptors can be easily
indicated combining them between parentheses. For example
100 FORMAT(1X, I6, I6, F9.3, F9.3, F9.3)
can be simplified to
100 FORMAT(1X, 3(I6, F9.3))
* `Fortran' allows file manipulation with the commands `OPEN',
`WRITE' and `CLOSE'. Other, more advanced, commands are `REWIND'
and `BACKSPACE'.
The `OPEN' command allows to initiate a file. The simplest
instance of this command is
OPEN(UNIT=,FILE='')
where the file name and the integer number of the associated unit
are indicated. The file is therefore associated to this number
for any Read/Write operation. We can write something in this
file as follows
OPEN(UNIT=33, FILE='program_OUT.dat')
WRITE(UNIT=33,FMT=100)
which indicates that the data included in will be
written in the file associated with unit number 33, following the
format specified in line labeled `100'. It is possible to
abbreviate the command to `WRITE(33,100)' or `WRITE(33,*)' if
free format is required. In order to send the data to `STDOUT',
`WRITE(UNIT=6,)', `WRITE(6,*)', `WRITE(*,*)', or `PRINT*'
are all valid and equivalent commands. Standard input `STDIN' is
associated with unit number `5' or the `*' symbol[1].
Once the write process takes place the unit should be closed
using the statement `CLOSE(UNIT=)'. In our case
CLOSE(UNIT=33)
Example Section 6.3.6, ``excode_6_6.f90'' shows how data are sent
to a file and introduces the intrinsic function `CPU_TIME' that
allows to estimate the cpu time spent in a program and its
different sections.
The `OPEN' command can be more specific, adding the following
arguments:
OPEN(UNIT=,FILE=,STATUS=, ACTION=, IOSTAT=)
These options control the following aspects:
1. `STATUS='
The constant or variable is of character type
and can take the following values:
* 'OLD'
* 'NEW'
* 'REPLACE'
* 'SCRATCH'
* 'UNKNOWN'
2. `ACTION='
The constant or variable is of character type
and can have the following forms:
* 'READ'
* 'WRITE'
* 'READWRITE'
By default. archives are opened with both read and write
permissions active.
3. `IOSTAT='
The variable is of integer type and gives
feedback about the success of the opening of the file. If
the final value is `0' the file has been correctly opened.
Any other value indicates a problem.
A complete example will be
INTEGER ierr
OPEN(UNIT=33, FILE='input_program.dat', STATUS='OLD', ACTION='READ', IOSTAT=ierr)
If we want to create a file to store some data:
INTEGER ierr
OPEN(UNIT=33, FILE='output_program.dat', STATUS='NEW', ACTION='WRITE', IOSTAT=ierr)
* It is possible some degree of control on the access to the
elements stored sequentially using the commands
BACKSPACE(UNIT = )
REWIND(UNIT = )
The `BACKSPACE' statement set the register one line back in the
associated file while `REWIND' move back to the first register of
the file.
* The default is to open formatted files. Thus, the following two
statements are equivalent
OPEN(UNIT=33,FILE='')
OPEN(UNIT=33,FILE='',FORM='FORMATTED')
Formatted files can be edited and read by the user, but they have
a couple of cons. Data storage and reading in formatted files
takes longer than in unformatted files and there may be some
precision loss in float numbers. In order to write data without
format files should be opened including the `FORM='UNFORMATTED''
option:
OPEN(UNIT=33,FILE='',FORM='UNFORMATTED')
To write in a file declared unformatted the `WRITE' command takes
the form
WRITE(UNIT=33)
The combination of fortran descriptors and different kinds of loop in
a code can be found in the example Section 6.3.7, ``excode_6_7.f90''.
This program reads a data file (a template of this fila can be found
under the program, and can be saved removing the trailing `!'
symbols). When the program opens the datafile with `OPEN' it uses the
`STATUS = 'OLD'' and `ACTION='READ'' options. It reads the file,
skipping some files making use of a `REPEAT UNTIL' loop, until it
arrives to a line that provides the number of data pairs in the
file[2]. Knowing the number of data pairs the appropriate matrices
are allocated and the points are read and saved into vectors `data_X'
and `data_Y', and computes the maximum (minimum) value of `data_X'
(`data_Y') making use of the intrinsic functions `MAXVAL' and `MINVAL'
(see Section 8.1, `Objectives').
[1] `STDERR' is associated with unit `0'.
[2] This is achieved making use of the `IERR ='