`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.3E­38 - 1.7E38' 2. Real 64 bits :: precision = 15-18 `~ 0.5E­308 - 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 ='