Lecciones de `Fortran 90' para la asignatura Química Computacional ------------------------------------------------------------------ Curro Pérez Bernal $Id: clases_fortran.sgml,v 1.24 2013/07/02 09:38:58 curro Exp curro $ ------------------------------------------------------------------------------- Resumen ------- El presente documento está basado en diversas referencias (ver Capítulo 13, `Referencias') y es el esquema básico de las nociones de programación `Fortran' que se imparten en la asignatura optativa _Química Computacional_ de cuarto curso del grado en Química en la Universidad de Huelva. ------------------------------------------------------------------------------- Contenidos ---------- 1. Introducción 1.1. Objetivos 1.2. Puntos destacables. 1.3. Programas usados como ejemplo. 2. Primeras operaciones básicas 2.1. Objetivos 2.2. Puntos destacables. 2.3. Programas usados como ejemplo. 3. Matrices o _Arrays_ (Básico) 3.1. Objetivos 3.2. Puntos destacables. 3.3. Programas usados como ejemplo. 4. Matrices o _Arrays_ (Avanzado) 4.1. Objetivos 4.2. Puntos destacables. 4.3. Programas usados como ejemplo. 5. Estructuras de control 5.1. Objetivos 5.2. Puntos destacables. 5.3. Programas usados como ejemplo. 6. Operaciones de Entrada/Salida (`I/O') (I) 6.1. Objetivos 6.2. Puntos destacables. 6.3. Programas usados como ejemplo. 7. Operaciones `I/O' (II) 7.1. Objetivos 7.2. Puntos destacables. 7.3. Programas usados como ejemplo. 8. Subprogramas (I): funciones 8.1. Objetivos 8.2. Puntos destacables. 8.3. Programas usados como ejemplo. 9. Subprogramas (II): subrutinas 9.1. Objetivos 9.2. Puntos destacables. 9.3. Programas usados como ejemplo. 10. Subprogramas (III): módulos 10.1. Objetivos 10.2. Puntos destacables. 10.3. Programas usados como ejemplo. 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. Introducción --------------- 1.1. Objetivos -------------- Los objetivos de esta clase son los siguientes: 1. dar una breve introducción a la programación y a las características de los lenguajes de programación. 2. indicar la importancia de poseer una idea clara y un esquema (diagrama de flujo) del algoritmo que se quiere programar. 3. dar una breve introducción a las características del lenguaje Fortran. 4. instalar el compilador Fortran de GNU (`gfortran') en el ordenador. Indicar que unas opciones útiles para la compilación son "`gfortran -std=f95 -Wextra -Wall -pedantic'". 5. estudiar dos programas muy simples. 6. presentar posibles fuentes de información. 1.2. Puntos destacables. ------------------------ Se trabaja con el editor `emacs' y con dos programas simples como ejemplo, Sección 1.3.1, `Programa ejemplo_1_1.f90' y Sección 1.3.2, `Programa ejemplo_1_2.f90'. Se destaca, usando los ejemplos, las partes en las que se dividen un programa simple: 1. comienzo con la orden `PROGRAM' . 2. definición de variables. 3. cuerpo del programa con diferentes instrucciones y operaciones de `I/O' (entrada/salida). 4. fin del programa con la orden `END PROGRAM' . Es importante hacer énfasis al explicar esto en los siguientes puntos * la importancia de los comentarios (todo aquello en una línea tras el carácter `!') y de un correcto sangrado para una fácil comprensión del programa. Indicar como ayuda en este apartado el uso del editor `emacs'. * el porqué de la orden `IMPLICIT NONE'. * Señalar las declaraciones de variables y, en su caso, la asignación inicial dada a las mismas. * Señalar las operaciones de `I/O'. * En el ejemplo Sección 1.3.2, `Programa ejemplo_1_2.f90' llamar la atención sobre la definición de la variable `N' y su asignación inicial `N = 3' como buena práctica de programación. 1.3. Programas usados como ejemplo. ----------------------------------- 1.3.1. Programa ejemplo_1_1.f90 ------------------------------- PROGRAM ej_1_1 ! ! Este programa lee e imprime un nombre (cadena de caracteres) ! IMPLICIT NONE CHARACTER(LEN=50) :: Nombre ! PRINT *,' Escribe tu nombre entre comillas:' PRINT *,' (max 50 caracteres)' READ(*,*),Nombre PRINT *,Nombre ! END PROGRAM ej_1_1 1.3.2. Programa ejemplo_1_2.f90 ------------------------------- PROGRAM ej_1_2 ! ! Este programa lee tres números y calcula su suma y su media ! IMPLICIT NONE REAL :: N1, N2, N3, Average = 0.0, Total = 0.0 INTEGER :: N = 3 PRINT *,' Introduce tres numeros (separados por espacio o coma).' PRINT *,' ' READ(*,*),N1,N2,N3 Total = N1 + N2 + N3 Average = Total/N PRINT *,'La suma de los tres numeros es ',Total PRINT *,'Y su promedio es ',Average END PROGRAM ej_1_2 ------------------------------------------------------------------------------- 2. Primeras operaciones básicas ------------------------------- 2.1. Objetivos -------------- Los objetivos de esta clase son los siguientes: 1. Enumerar las reglas básicas de la sintaxis de `Fortran' y el juego de caracteres usado en los programas. 2. La ejecución de operaciones básicas aritméticas y el orden en que se ejecutan los operadores aritméticos. 3. El uso de la orden `PARAMETER'. 4. Explicar los diferentes tipos que existen de variables numéricas y cómo definirlas. 2.2. Puntos destacables. ------------------------ Reglas básicas de sintaxis: * Número máximo de caracteres por línea de código: 132. * Número máximo de caracteres en la definición de una variable: 31. * '&' es el carácter de continuación de línea. Solo se coloca al final de la línea.[1] * '!' es el carácter que marca inicio de comentario. Los comentarios tras el comando están permitidos. * ';' es el carácter que permite separar dos (o más) comandos en una misma línea. En la definición de variables puede usarse el guión bajo ('`_'') y mezclar números y letras, aunque el nombre de la variable no puede comenzar por un número. Juego de caracteres usados en códigos `Fortran' 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 Jerarquía de operadores aritméticos: * Operadores aritméticos: {`+',`-',`*',`/',`**'}. * Jerarquía: (1) `**' (dcha. a izqda.); (2) `*',`/' (depende del compilador); (3) `+',`-' (depende del compilador). * Atención al redondeo, a la mezcla de tipos diferentes de variables en funciones aritméticas. Se deben realizar las operaciones de modo que la pérdida de precisión se minimice. Cuando se realizan operaciones aritméticas, el procesador utiliza variables del mismo tipo, por lo que si las variables utilizadas son de diferente tipo una de ellas cambia de tipo para que sean ambos iguales. El orden de prioridad, de más bajo a más alto, es: `INTEGER', `REAL', `DOUBLE PRECISION' y `COMPLEX', por lo que si se combina un número entero con uno real de doble precisión, el entero promociona a doble precisión y el resultado se da en doble precisión. Una vez terminada la operación u operaciones aritméticas, el tipo del resultado final se determina de acuerdo con el tipo de la variable a la que se asignará. * Tamaño de enteros 1. Entero 32 bits ::`(2**31)-1 = 2,147,483,647' (~ 10**9) 2. Entero 64 bits :: `(2**63)-1 = 9,223,372,036,854,774,807' (~ 10**19) * Tamaño y precisión de números reales 1. Real 32 bits :: precisión = 6-9 `~ 0.3E­38 - 1.7E38' 2. Real 64 bits :: precisión = 15-18 `~ 0.5E­308 - 0.8E308' * Usando `PARAMETER' al definir una variable podemos definir constantes en los programas. En la definición pueden llevarse a cabo operaciones aritméticas simples. Ver Sección 2.3.4, `Programa ejemplo_2_4.f90'. * (*) Presentar los diferentes tipos de enteros y de reales que existen en `Fortran' y las funciones intrínsecas [2] `KIND', `EPSILON', `PRECISION' y `HUGE' etc., así como la manera de definir variables dentro de los diferentes tipos que existen. En el caso de variables enteras, si queremos definir una variable llamada `i0' que pueda tomar, como mínimo, todos los valores posibles entre -999999 y 999999 debemos establecer una constante, llamada por ejemplo `ki', usando la función intrínseca `SELECTED_INT_KIND()' y utilizarla en la definición de la variable INTEGER, PARAMETER :: ki = SELECTED_INT_KIND(6) INTEGER(KIND=ki) :: i0 La función intrínseca[3] `SELECTED_INT_KIND(X)' tiene como salida un entero que nos indica el tipo (_kind_) de una variable entera que es capaz de reproducir todos los números enteros en el rango `(-10E+X, 10E+X)' donde `X' es un escalar entero. De este modo si queremos que una constante entera en nuestro programa tenga esta precisión debemos hacer referencia a ella del siguiente modo: -1234_ki 2354_ki 2_ki Si hubiera algún error al compilar con las órdenes anteriores y definir un entero, entonces la función `SELECTED_INT_KIND(X)' tendrá como salida `-1'. En el caso de variables reales hemos de usar una representación del número con decimales (_floating point representation_). Todas las que siguen son representaciones válidas de constantes reales en `Fortran' -10.66E-20 0.66E10 1. -0.4 1.32D-44 2E-12 3.141592653 En este caso la orden para alterar el modo de almacenamiento de reales es usando la función `SELECTED_REAL_KIND(p=X,r=Y)', con dos parámetros. Proporciona como salida un número entero asociado con el tipo (_kind_) de la variable definida. Permite definir a una variable real que cumpla con las siguientes condiciones: * Que tenga una precisión que sea _al menos_ `X' y con un rango de exponentes decimales que viene dado _al menos_ por `Y'. Las etiquetas de los argumentos son opcionales. * Si varios resultados son posibles se escogerá aquel con la menor precisión decimal. * Al menos uno de los dos parámetros de entrada, `X' o `Y', debe expresarse en la definición de la variable. Tanto `X' como `Y' son enteros. Si no existe una variable que se acomode a las condiciones exigidas la salida de `SELECTED_REAL_KIND(X,Y)' será `-1' si la precisión no alcanza el nivel impuesto, `-2' si es el exponente el que no lo alcanza y `-3' si ambos no son posibles. Si por ejemplo queremos definir una variable real llamada `a0' que tenga una precisión de 15 decimales y con exponentes entre -306 y 307 lo podemos hacer con las ordenes INTEGER, PARAMETER :: kr = SELECTED_REAL_KIND(15,307) REAL(KIND=kr) :: a0 Si queremos que los escalares reales que hemos mencionado anteriormente sean de este tipo haremos referencia a ellos como -10.66E-20_kr 0.66E10_kr 142857._kr -0.4_kr 2E-12_kr 3.141592653_kr El programa Sección 2.3.5, `Programa ejemplo_2_5.f90' permite ver cómo usar la orden `KIND', que nos proporciona el valor de `KIND' asociado a un escalar o una variable dada. En este programa corto podemos ver el resultado por defecto de `KIND' obtenido para diferentes variables. El programa Sección 2.3.6, `Programa ejemplo_2_6.f90' se pueden encontrar ejemplos de los diferentes tipos, cómo definirlos y de las funciones que proporcionan información acerca de sus límites presentando las útiles funciones intrínsecas `KIND', `DIGITS', `EPSILON', `TINY', `HUGE', `EXPONENT', `MAXEXPONENT', `MINEXPONENT', `PRECISION' , `RADIX' y `RANGE'. En este programa se definen las variables usando las funciones `SELECTED_INT_KIND' y `SELECTED_REAL_KIND' directamente en la definición. Es correcto aunque es mejor cuando se definen varias variables usar el procedimiento descrito en las notas. Las funciones usadas dan la siguiente información: 1. `KIND(x)': su salida es de tipo entero y proporciona el parámetro de tipo de la variable `x'. 2. `DIGITS(x)': su salida es de tipo entero y proporciona el número de cifras significativas de `x'. 3. `EPSILON(x)': si el argumento `x' es real su salida es otro real, con el mismo de tipo (_kind_) que `x', y que es el número más pequeño de este tipo tal que `1.0 + EPSILON(X) > 1'. 4. `TINY(x)': si el argumento `x' es real su salida es un valor del mismo tipo que `x', siendo el mínimo valor positivo definible para dicho tipo de variables. 5. `HUGE(x)': si el argumento `x' es real o entero su salida es un valor del mismo tipo que `x', siendo el máximo valor definible en dicho tipo. 6. `EXPONENT(x)': exponente del valor `x'. Si `x = 0' entonces `EXPONENT(x)=0'. 7. `MAXEXPONENT(x)': máximo exponente posible en el tipo del valor `x'. 8. `MINEXPONENT(x)': mínimo exponente posible en el tipo del valor `x'. 9. `PRECISION(x)': si el argumento `x' es real o complejo su salida es un valor entero que informa de la precisión decimal en el tipo de variable de `x'. 10. `RADIX(x)': resultado entero e igual a la base del tipo de `x'. 11. `RANGE(x)': resultado entero e igual al rango del exponente para el tipo de `x'. * (*) Presentar como se pierde precisión al ejecutar cálculos y como indicar que una constante es de un tipo determinado para mejorar la precisión en los cálculos. [1] Salvo cuando se rompe en dos líneas una secuencia alfanumérica, tipo `CHARACTER', en ese caso se coloca al final de la línea y al comienzo de la siguiente. [2] Ver Sección 2.3.4, `Programa ejemplo_2_4.f90'. [3] La información acerca de la definición de funciones y las posibles funciones intrínsecas en `Fortran' puede encontrarse en Sección 8.1, `Objetivos'. 2.3. Programas usados como ejemplo. ----------------------------------- 2.3.1. Programa ejemplo_2_1.f90 ------------------------------- PROGRAM ej_2_1 IMPLICIT NONE ! ! Ejemplo de programa que permite calcular la energía ! de un modo normal dados el número de cuantos (v), la ! frecuencia (w) y la anarmonicidad (wx) como ! ! 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 ! Unidades: cm-1 INTEGER :: v = 0 CHARACTER(LEN=60) :: for_mol ! Operaciones I/O PRINT *,'Formula de la molecula : ' READ(*,*), for_mol PRINT *,'Num. de quanta de excitacion : ' READ(*,*), v ! Calculos energ = we*(v+0.5) - wexe*(v+0.5)**2 energ_0 = we*(0.5) - wexe*(0.5)**2 delta_e = energ - energ_0 ! Operaciones 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 ej_2_1 2.3.2. Programa ejemplo_2_2.f90 ------------------------------- PROGRAM ej_2_2 IMPLICIT NONE REAL :: A,B,C INTEGER :: I A = 1.5 B = 2.0 C = A / B I = A / B PRINT * PRINT *, 'Caso (1), variable real' PRINT *,A,'/',B,' = ',C PRINT *, 'Caso (2), variable entera' PRINT *,A,'/',B,' = ',I END PROGRAM ej_2_2 2.3.3. Programa ejemplo_2_3.f90 ------------------------------- PROGRAM ej_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 ej_2_3 2.3.4. Programa ejemplo_2_4.f90 ------------------------------- PROGRAM ej_2_4 ! Programa para calcular el tiempo que tarda la luz en ! recorrer una distancia dada en unidades astronómicas. ! 1 UA = 1,50E11 m ! !Definicion de variables IMPLICIT NONE ! u_a : unidad astronomica en km REAL , PARAMETER :: u_a=1.50*10.0**8 ! a_luz : año luz --> distancia en km recorrida por la luz en un año. REAL , PARAMETER :: a_luz=9.46*10.0**12 ! m_luz : minuto luz --> distancia en km recorrida por la luz en un minuto. REAL :: m_luz ! dist : distancia recorrida en UAs (INPUT) REAL :: dist ! t_min : tiempo en minutos necesario para recorrer la distancia Distance REAL :: t_min ! ! min : parte entera de t_min ! seg : número de segundos (parte decimal de t_min) INTEGER :: min, seg ! m_luz = a_luz/(365.25 * 24.0 * 60.0) ! Calculo del minuto-luz ! PRINT * PRINT *,'Distancia en UAs' READ(*,*), dist PRINT * ! t_min = (dist*u_a)/m_luz min = t_min; seg = (t_min - min) * 60 ! PRINT *,' La luz tarda ' , min,' minutos y ', seg,' segundos' Print *,' en recorrer una distancia de ',dist,' UA.' END PROGRAM ej_2_4 2.3.5. Programa ejemplo_2_5.f90 ------------------------------- PROGRAM ej_2_5 INTEGER :: i REAL :: r CHARACTER :: 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 ej_2_5 2.3.6. Programa ejemplo_2_6.f90 ------------------------------- PROGRAM ej_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 ej_2_6 ------------------------------------------------------------------------------- 3. Matrices o _Arrays_ (Básico) ------------------------------- 3.1. Objetivos -------------- Los objetivos de esta clase son los siguientes: 1. presentar las matrices o arreglos de una y varias dimensiones como estructuras de datos en `Fortran'. 2. presentar cómo se define una matriz y cómo se accede a sus elementos. 3. presentar la sintaxis de los bucles `DO' como estructura de control. `DO' implícito. Uso de esta estructura con matrices. 4. Definición dinámica del tamaño de arreglos. 5. Arreglos multidimensionales. 3.2. Puntos destacables. ------------------------ Conceptos previos: 1. rango _(rank)_: número de índices necesarios para indicar un elemento de la matriz. 2. límites _(bounds)_: valor superior e inferior del índice que indica los elementos de la matriz en cada dimensión. 3. extensión _(extent)_: número de elementos de la matriz para cada dimensión. 4. tamaño _(size)_: número total de elementos de la matriz. 5. Se dice que dos arreglos son "conformables" si tienen idéntico rango y extensión. Es importante al explicar este material hacer énfasis en los siguientes puntos * definición de una matriz monodimensional (vector) y uso de la estructura de control `DO' presentado en Sección 3.3.1, `Programa ejemplo_3_1.f90'. (ejercicio 2_1) * es recomendable definir las dimensiones de las matrices (en caso de dimensionamiento estático) como variables (o parámetros) lo que facilita posteriores cambios. * es importante siempre inicializar las matrices que se definan. La inicialización puede llevarse a cabo en el momento en el que la matriz se defina, mediante `READ' o usando asignaciones. Es muy simple inicializar todos los elementos de una matriz a un mismo valor: `vec = '. Una matriz también puede definirse e inicializarse usando los llamados _array constructors_. Por ejemplo, para definir una matriz entera `vec_int'con seis elementos puede usarse cualquiera de las tres opciones siguientes 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/) Las dos últimas opciones implican _array constructors_ y pueden llevarse a cabo al definir la variable. * como se define el tamaño de una matriz definida con el atributo `ALLOCATABLE' al correr el programa usando la función `ALLOCATE' tal y como se aplica en el ejemplo Sección 3.3.2, `Programa ejemplo_3_2.f90'. En el caso de `ALLOCATE' es posible añadir una opción `STAT = ' tal que si la definición del arreglo ha tenido éxito entonces ` = 0'. Un ejemplo de esta opción se muestra en el Sección 9.3.3, `Programa ejemplo_9_3.f90'. * presentar arreglos de datos con varias dimensiones (hasta un máximo de siete son posibles) y la forma del `DO' implícito. Ver ejemplo Sección 3.3.3, `Programa ejemplo_3_3.f90'. * explicar la forma más general de la estructura `DO' y de la definición de matrices utilizando índices negativos. Ver ejemplo Sección 3.3.4, `Programa ejemplo_3_4.f90'. * explicar como combinar el redireccionado de la `bash shell' con programas `Fortran'. Necesario para ejercicio 2, se explica en Capítulo 4, `Matrices o _Arrays_ (Avanzado)'. 3.3. Programas usados como ejemplo. ----------------------------------- 3.3.1. Programa ejemplo_3_1.f90 ------------------------------- PROGRAM ej_3_1 ! ! DEFINICIÓN DE VARIABLES IMPLICIT NONE REAL :: Total=0.0, Promedio=0.0 INTEGER, PARAMETER :: semana=7 REAL , DIMENSION(1:semana) :: Horas_trab INTEGER :: dia ! PRINT *,' Introduzca las horas trabajadas' PRINT *,' por dia en una semana' DO dia=1,semana READ(*,*), Horas_trab(dia) ENDDO ! DO dia=1,semana Total = Total + Horas_trab(dia) ENDDO Promedio = Total / semana ! PRINT *,' Promedio de horas de trabajo semanales: ' PRINT *, Promedio END PROGRAM ej_3_1 3.3.2. Programa ejemplo_3_2.f90 ------------------------------- PROGRAM ej_3_2 ! ! DEFINICION DE VARIABLES IMPLICIT NONE REAL :: Total=0.0, Promedio=0.0 REAL , DIMENSION(:), ALLOCATABLE :: Horas_trab INTEGER :: dia, num_dias ! PRINT *,' Introduzca el número de dias para los que ' PRINT *,' se va a calcular el promedio de horas trabajadas.' READ(*,*), num_dias ! ALLOCATE(Horas_trab(1:num_dias)) ! PRINT *,' Introduzca las horas trabajadas' PRINT *,' por dia en ', num_dias, 'dias.' DO dia=1, num_dias READ(*,*), Horas_trab(dia) ENDDO ! DO dia=1,num_dias Total = Total + Horas_trab(dia) ENDDO Promedio = Total / num_dias ! PRINT *,' Promedio de horas de trabajo en ',num_dias, ' dias : ' PRINT *, Promedio ! END PROGRAM ej_3_2 3.3.3. Programa ejemplo_3_3.f90 ------------------------------- PROGRAM asistencia IMPLICIT NONE INTEGER , PARAMETER :: N_alum=4 INTEGER , PARAMETER :: N_asig=3 INTEGER , PARAMETER :: N_prac=3 INTEGER :: alumno, asignatura, pract CHARACTER(LEN = 2) , DIMENSION(1:N_prac,1:N_asig,1:N_alum) :: asiste='NO' DO alumno = 1,N_alum DO asignatura = 1,N_asig READ(*,*),(asiste(pract,asignatura,alumno),pract = 1, N_prac) ENDDO ENDDO PRINT *,' Asistencia a practicas : ' DO alumno=1, N_alum PRINT *,' Alumno = ', alumno DO asignatura = 1,N_asig PRINT *,' Asignatura = ', asignatura, ' : ', (asiste(pract,asignatura,alumno),pract=1,N_prac) ENDDO ENDDO END PROGRAM asistencia 3.3.4. Programa ejemplo_3_4.f90 ------------------------------- PROGRAM longitude 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 longitude ------------------------------------------------------------------------------- 4. Matrices o _Arrays_ (Avanzado) --------------------------------- 4.1. Objetivos -------------- Los objetivos de esta clase son los siguientes: 1. presentar el orden natural en el que se almacenan los elementos en matrices multidimensionales. 2. presentar como se pueden manipular matrices completas y secciones de matrices en `Fortran'. 3. ver como se puede trabajar con parte de una matriz. 4. explicar la definición de matrices con la orden `WHERE'. 4.2. Puntos destacables. ------------------------ * Orden de almacenamiento El orden en el que se almacenan los elementos de una matriz multidimensional en `Fortran' es tal que el primer subíndice varía más rápidamente, a continuación el segundo, a continuación el tercero y así sucesivamente. Es lo que se conoce como _column major order_. Por ejemplo, si definimos una matriz con dos subíndices y de dimensión 4x2 con la orden `REAL , DIMENSION(1:4,1:2) :: A', Esta matriz `A' tiene ocho elementos que se guardan en memoria siguiendo el orden A(1,1), A(2,1), A(3,1), A(4,1), A(1,2), A(2,2), A(3,2), A(4,2) Por tanto para inicializar la matriz `A' podemos hacerlo de diferentes maneras. Supongamos que queremos inicializar la matriz de modo que cada elemento sea igual al número de su fila. Podemos usar dos bucles[1] DO I_col = 1, 2 DO I_row = 1, 4 A(I_row, I_col) = I_row ENDDO ENDDO Podemos también hacer uso de un _array constructor_, aunque la solución que parece más lógica, haciendo A = (/ 1, 2, 3, 4, 1, 2, 3, 4 /) no funciona. El _array constructor_ produce un vector de dimensión ocho, y no una matriz 4x2. Este vector y la matriz `A' tiene idéntico tamaño, pero no son conformables. Para resolver este problema es preciso hacer uso de la orden `RESHAPE'. La sintaxis de esta orden es la siguiente = RESHAPE(, ) Donde contiene es una matriz los datos que van a sufrir el cambio, es un vector que describe las dimensiones de la nueva matriz . El número de elementos, esto es, el tamaño, de las matrices y debe ser idéntico. En el caso anterior podemos por tanto hacer A = RESHAPE( (/ 1, 2, 3, 4, 1, 2, 3, 4 /), (/ 4, 2 /) ) En Sección 4.3.3, `Programa ejemplo_4_3.f90' se puede ver otro ejemplo. El comando `RESHAPE' puede usarse en la declaración del arreglo INTEGER, DIMENSION(1:4,1:2) :: A = & RESHAPE( (/ 1, 2, 3, 4, 1, 2, 3, 4 /), (/ 4, 2 /) ) El orden seguido en el almacenamiento de los datos es especialmente importante en las operaciones de entrada y salida. Si ejecutamos PRINT*, A El resultado será A(1,1), A(2,1), A(3,1), A(4,1), A(1,2), A(2,2), A(3,2), A(4,2) Es preciso tener esto en cuenta al utilizar `READ' para leer los elementos de un arreglo en un fichero, haciendo `READ(,*) A'. Para cambiar el orden de lectura es posible hacer uso de _implicit_ `DO''s READ(unit,*) ( ( A(row,col), col = 1, 2 ), row = 1, 4 ) * En `FORTRAN' es posible definir arreglos multidimensionales más allá de las matrices, siendo 7 el límite del número de índices y corriendo más rápidamente el primero, luego el segundo, y así sucesivamente hasta el último. En el ejemplo Sección 4.3.2, `Programa ejemplo_4_2.f90' se caracteriza de forma completa un arreglo haciendo uso de varias de funciones de tipo. _inquiry_ (ver Sección 8.3.2, `Programa ejemplo_8_2.f90'). * Es importante al explicar el material de esta clase hacer énfasis en los siguientes puntos que afectan al manejo de matrices completas. Si por ejemplo definimos los vectores reales `V1', `V2', `V3' y `V4' como REAL , DIMENSION(1:21) :: V1, V2 REAL , DIMENSION(-10:10) :: V3 REAL , DIMENSION(0:10) :: V4 Podemos trabajar de forma natural con estos vectores completos como en los siguientes ejemplos[2]. 1. Asignación de un valor: V1 = 0.5 Hacemos que todos los elementos de `V1' sean iguales a `0.5'. 2. Igualar matrices: V1 = V2 Hacemos que cada elemento de `V1' sea igual al elemento equivalente de `V2'. Esta función sólo es aplicable a matrices _conformables_[3]. Por ejemplo, también es válido hacer V3 = V2 pero _no_ es válido V1 = V4 3. Todas las operaciones aritméticas aplicables a escalares se pueden asimismo aplicar a matrices, siempre que estas sean conformables, aunque hay que tener claro qué implican pues es posible que matemáticamente no tengan sentido. Usando los mismos vectores que en el punto anterior podemos hacer V1 = V2 + V3 V1 = V2*V3 En el primer caso `V1' es la suma de los dos vectores, pero en el segundo `V1' es un vector, cada uno de sus elementos es el producto de los elementos correspondientes de `V2' y `V3'. Esto _no_ es el producto escalar de dos vectores. Como hemos indicado, este caso de nuevo las matrices han de ser conformables. Para matrices bidimensionales, por ejemplo, si definimos las matrices REAL , DIMENSION(1:4,1:4) :: A, B, C Pueden hacerse operaciones como las siguientes A = A**0.5 C = A + B C = A * B Es importante tener en cuenta que la última operación no es el producto de dos matrices, sino otra matriz, de idéntica dimensión a las de `A' y `B', y cada uno de sus elementos es el producto de los correspondientes elementos de `A' y `B'. 4. También pueden leerse todos los elementos de una matriz sin necesidad de un bucle `DO', como en el ejemplo Sección 4.3.1, `Programa ejemplo_4_1.f90'. En este ejemplo también se presenta la función `SUM' que suma los elementos de un vector en la aplicación presentada, aunque tiene una aplicación más general. * También es posible referirnos a una sección de una matriz, por ejemplo, usando las matrices definidas arriba podemos hacer V1(1:10) = 0.5 B (1,1:4) = 100.0 En el primer caso se hacen iguales a `0.5' los diez primeros elementos de `V1', mientras que en el segundo se hacen iguales a `100.0' los elementos de la primer fila de `A'. Puede verse una aplicación de este punto en el ejemplo Sección 4.3.1, `Programa ejemplo_4_1.f90'. La forma más general como se define una sección de un arreglo de datos es como `::': la sección comienza con el índice , termina en y es el incremento en la variable que marca el índice. Cuando no está presente, como en los casos anteriores, toma por defecto el valor 1. Algunos ejemplos: V1(:) ! todo el vector V1(3:10) ! elementos V1(3), V1(4), ... , V1(10) V1(3:10:1) ! "" "" "" "" V1(3:10:2) ! "" V1(3), V1(5), ... , V1(9) V1(m:n) ! elementos V1(m), V1(m+1), ... , V1(n) V1(9:4:-2) ! "" V1(9), V1(7), V1(5) V1(m:n:-k) ! elementos V1(m), V1(m-k), ... , V1(n) V1(::2) ! "" V1(1), V1(3), ... , V1(21) V1(m:m) ! Arreglo 1x1 V1(m) ! Escalar Es importante diferenciar los dos últimos casos. * Al asignar valores a un arreglo puede utilizarse una _máscara lógica_, haciendo uso de la orden `WHERE'. El uso de una máscara lógica permite seleccionar los elementos del arreglo sobre los que se llevará a cabo una operación. Si, por ejemplo, queremos calcular la raíz cuadrada de los elementos de un arreglo real llamado `data_mat' y almacenarlos en el arreglo `sq_data_mat', debemos evitar tomar raíces cuadradas de números negativos. Para ello podemos usar una combinación de bucles y condicionales 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 El uso de `WHERE' simplifica esta tarea. La forma general de esta orden es [:] WHERE () .... Array assignment block 1 .... ELSEWHERE () [] .... Array assignment block 2 .... ELSEWHERE .... Array assignment block 3 .... ENDWHERE [] Donde y son arreglos lógicos conformables con la matriz que se asigna. El ejemplo anterior se simplifica usando `WHERE' WHERE ( data_mat >= 0.0 ) sq_data_mat = SQRT( data_mat ) ELSEWHERE sq_data_mat = -99999.0 ENDWHERE * Estos puntos se tratan en los diferentes ejemplos proporcionados. En el ejemplo Sección 4.3.3, `Programa ejemplo_4_3.f90' se puede ver como se inicializan vectores y para matrices. En este último caso haciendo uso de la orden `RESHAPE'. Este ejemplo presenta también las funciones `Fortran' `DOT_PRODUCT' (producto escalar) y `MATMUL' (multiplicación de matrices). En el ejemplo Sección 4.3.4, `Programa ejemplo_4_4.f90' se presenta como puede asignarse una matrix usando `WHERE', y una "máscara" lógica. Como se muestra en el ejemplo Sección 4.3.5, `Programa ejemplo_4_5.f90' es importante no confundir los resultados esperados al vectorizar un programa eliminando los bucles `DO' presentes en él. En el ejemplo Sección 4.3.6, `Programa ejemplo_4_6.f90' vemos como usar la orden `RESHAPE' en la definición de una matriz y como acceder a diferentes partes de la misma. [1] Debido al almacenamiento en memoria de acuerdo con el _column major order_ el orden de los bucles haciendo que las columnas corran antes que las filas puede mejorar el rendimiento del código en matrices de tamaño considerable. [2] Esta es una de las grandes ventajas de `Fortran90' frente a `Fortran77'. [3] Se dice que dos matrices son "conformables" si tienen idéntico número de dimensiones (subíndices) y cada una de ellas tiene la misma longitud. 4.3. Programas usados como ejemplo. ----------------------------------- 4.3.1. Programa ejemplo_4_1.f90 ------------------------------- PROGRAM ej_4_1 ! ! DEFINICION DE VARIABLES IMPLICIT NONE REAL :: Total=0.0, Promedio=0.0 REAL , DIMENSION(:), ALLOCATABLE :: t_trab ! Factor que corrige el tiempo trabajado los dos últimos días REAL :: Fac_correc=1.05 INTEGER :: dia, num_dias ! PRINT *,' Introduzca el no. de dias para los que se va ' PRINT *,' a calcular el promedio de horas y minutos trabajados.' READ(*,*), num_dias ! Dimensiona dinámicamente la matriz ALLOCATE(t_trab(1:num_dias)) ! PRINT *,' Introduzca las horas trabajadas' PRINT *,' por dia en ', num_dias, 'dias.' ! Lectura de datos READ(*,*), t_trab ! t_trab(num_dias-1:num_dias) = Fac_correc*t_trab(num_dias-1:num_dias) ! ! Total = SUM(t_trab) ! Promedio = Total / num_dias ! PRINT *,' Horas de trabajo en ',num_dias, ' dias : ' PRINT *, Promedio ! END PROGRAM ej_4_1 4.3.2. Programa ejemplo_4_2.f90 ------------------------------- PROGRAM EJEMPLO_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 EJEMPLO_4_2 4.3.3. Programa ejemplo_4_3.f90 ------------------------------- PROGRAM ej_4_3 ! ! DEFINICION DE VARIABLES IMPLICIT NONE REAL, DIMENSION(1:5) :: VA = (/1.2,2.3,3.4,4.5,5.6/), 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 /) ! Producto escalar VA.VB PE = DOT_PRODUCT(VA,VB) ! PRINT *, 'PRODUCTO ESCALAR (VA,VB) = ', PE ! ! Producto de matrices VAxMC ! Haciendo RESHAPE de VC hacemos que sea una matriz 5 x 5 MC = RESHAPE(VC,(/ 5, 5 /)) PMAT = MATMUL(VA,MC) ! PRINT *, 'PRODUCTO VAxMC = ', PMAT(1:5) ! END PROGRAM ej_4_3 4.3.4. Programa ejemplo_4_4.f90 ------------------------------- PROGRAM long2 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 long2 4.3.5. Programa ejemplo_4_5.f90 ------------------------------- PROGRAM ej_4_5 ! ! DEFINICION DE VARIABLES 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) ! ! The 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 ej_4_5 4.3.6. Programa ejemplo_4_6.f90 ------------------------------- PROGRAM ej_4_6 ! ! DEFINICION DE 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*, "Elemento de la matriz", A(2,3) PRINT*, "Submatriz", A(1:2,2:3) PRINT*, "Submatriz", A(::2,::2) PRINT*, "Columna de la matriz", A(:,3) PRINT*, "Fila de la matriz", A(2,:) PRINT*, "Matriz completa", A PRINT*, "Matriz traspuesta", TRANSPOSE(A) END PROGRAM ej_4_6 ------------------------------------------------------------------------------- 5. Estructuras de control ------------------------- 5.1. Objetivos -------------- Los objetivos de esta clase son los siguientes: 1. presentar las diferentes estructuras de control condicionales en `Fortran' (_branching_). 2. presentar las diferentes formas de ejecutar bucles en programas `Fortran' (_loops_). Estas estructuras de control son básicas para dominar el flujo de un programa, permitiendo que la ejecución del programa pueda depender de los datos implicados o de la decisiones del usuario que lo corra. Es importante tener en cuenta que para cualquier proyecto medianamente complejo es preciso tener una idea clara del problema, de los inputs y outputs del mismo y del algoritmo que se utilizará, así como de la estructura del programa que se llevará a cabo como un diagrama de flujo o como pseudocódigo antes de comenzar a escribir el código `Fortran'. Los problemas complejos deben dividirse en tareas más simples, y si es necesario estas se subdividirán en otras tareas aún más simples (_top-down design_) y cada una de estas tareas debe codificarse y comprobarse de forma independiente. 5.2. Puntos destacables. ------------------------ Daremos de forma esquemática el formato que tienen las estructuras de control. En primer lugar las condicionales y en segundo lugar los bucles. * Estructuras condicionales. Todas estas estructuras dependen de la evaluación de condiciones lógicas. Estas condiciones se establecen mediante los operadores relacionales siguientes: * `==' Ser igual a. * `/=' No ser igual a. * `>' Mayor que. * `<' Menor que. * `>=' Mayor o igual que. * `<=' Menor o igual que. También es posible utilizar operadores lógicos para combinar diferentes condiciones. Los operadores lógicos son * `.AND.' Operador Y lógico. * `.OR.' Operador O lógico. * `.NOT.' Operador NO lógico. * `.EQV.' Operador '==' lógico. * `.NEQV.' Operador '/=' lógico. Los diferentes condicionales posibles son los siguientes. 1. `IF THEN ENDIF' La forma de este condicional es la siguiente . . codigo . IF (cond logica) THEN . . bloque_1 . ENDIF . . codigo . Solamente si se cumple la condición lógica impuesta se ejecutan las instrucciones en `bloque_1', si no se cumple son ignoradas. En caso de que sea únicamente una la instrucción ejecutada si se cumple la condición lógica se puede simplificar la orden eliminando `THEN' y `ENDIF' del siguiente modo . . codigo . IF (cond logica) instruccion . . codigo . 2. `IF THEN ELSE ENDIF' La forma de este condicional es la siguiente . . codigo . IF (cond logica) THEN . . bloque_1 . ELSE . . bloque_2 . ENDIF . . codigo . Si se cumple la condición lógica impuesta se ejecutan las instrucciones en `bloque_1', si no se cumple se ejecuta el bloque `bloque_2'. 3. `IF THEN ELSE IF ENDIF' La forma de este condicional es la siguiente . . codigo . IF (cond logica_1) THEN . . bloque_1 . ELSE IF (cond logica_2) THEN . . bloque_2 . ENDIF . . codigo . Si se cumple la condición lógica `cond logica_1' se ejecutan las instrucciones en `bloque_1'; si no se cumple, pero sí se cumple la condición lógica `cond logica_2', entonces se ejecuta el bloque `bloque_2'. 4. `IF THEN ELSE IF ELSE ENDIF' La forma de este condicional es la siguiente . . codigo . IF (cond logica_1) THEN . . bloque_1 . ELSE IF (cond logica_2) THEN . . bloque_2 . ELSE . . bloque_3 . ENDIF . . codigo . Si se cumple la condición lógica `cond logica_1' se ejecutan las instrucciones del `bloque_1'; si no se cumple, pero sí se cumple la condición lógica `cond logica_2', entonces se ejecuta el bloque `bloque_2'. Si ninguna de las dos condiciones lógicas son ciertas, entonces se ejecuta el código en el bloque `bloque_3'. 5. `SELECT CASE' La orden `CASE' permite escoger una opción entre diferentes posibilidades de forma más eficiente, clara y elegante que las estructuras de control anteriores. La forma de este condicional es la siguiente SELECT CASE (selector) CASE (label-1) bloque-1 CASE (label-2) bloque-2 CASE (label-3) bloque-3 ............. CASE (label-n) bloque-n CASE DEFAULT bloque-default END SELECT En esta orden `selector' es una variable o una expresión cuyo resultado es del tipo _entero_, _lógico_ o _carácter_. En concreto no puede ser una variable ni una expresión que dé como resultado un número real. En esta orden `label-1' hasta `label-n' son listas de etiquetas, separadas por comas, que deben tener una de las siguientes formas: valor valor-1 : valor-2 valor-1 : : valor-2 La primera indica el valor `valor', la segunda un valor comprendido entre `valor-1' y `valor-2', la tercer un valor mayor que `valor-1'y la última un valor menor que `valor-2'. Tanto `valor' como `valor-1' y `valor-2' deben ser constantes o alias definidos por una orden `PARAMETER'. Como se ejecuta esta orden es evaluando `selector'. Se compara el resultado con los valores establecidos en cada una de las listas de etiquetas, ejecutando el bloque correspondiente a la primera de las comparaciones que resulte exitosa. Si ninguna comparación es exitosa se ejecuta -caso de que exista- el `bloque-default'. Tras ello se prosigue ejecutando las órdenes que existan tras el `END SELECT'. Por ejemplo 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 La orden `SELECT CASE' es más eficiente que una combinación de `IF''s porque una sola expresión controla las diferentes alternativas. Los condicionales `==' y `/=' no deben usarse con expresiones del tipo real, ya que estas son aproximadas. Así, si `A' y `B' son variables reales, no debe usarse ... IF (A==B) same = .TRUE. ... Para evitar esto debe definirse una tolerancia y utilizar algo como REAL :: TOL = 0.00001 ... IF (ABS(A-B) < TOL) same = .TRUE. ... Estas estructuras de control condicionales pueden anidarse en varios niveles. En este caso es interesante a veces etiquetar los bucles para lograr una mayor claridad en el código, como en el siguiente caso primerif: IF (a == 0) THEN PRINT*, "a is zero" IF (c /= 0) THEN PRINT*, "a is zero and c is not zero" ELSE PRINT*, "a and c are zero" ENDIF ELSEIF (a > 0) THEN primerif PRINT*, "a is positive" ELSE primerif PRINT*, "a is negative" ENDIF primerif La etiqueta `primerif' simplemente trata de hacer el código más claro, sin jugar ningún otro papel. Si la etiqueta se define en el `IF', entonces ha de estar presente en el `ENDIF', aunque en las instrucciones `ELSE' y `ELSEIF' sea opcional. Puede haber tantos condicionales anidados como se desee. En el ejemplo Sección 5.3.1, `Programa ejemplo_5_1.f90' puede verse una aplicación de la estructura `IF THEN ELSE IF ELSE ENDIF' y en el ejemplo Sección 5.3.2, `Programa ejemplo_5_2.f90' como se lleva a cabo, aparentemente, la misma tarea con una aplicación de la estructura `CASE'. * Bucles y comandos de utilidad en bucles 1. Primera forma de bucle (_loop_) con el comando `DO' Ya conocemos una forma de formar bucles con la orden `DO'. DO Var = vinicial, vfinal, incremento bloque de instrucciones END DO Con esta forma la variable `Var' toma el valor inicial y se va incrementando en `inc' hasta que alcanza el valor `vfinal'. Cada vez se ejecuta el bloque de instrucciones limitado por el final del bloque marcado por el `ENDDO'. Por ejemplo, en el ejemplo Sección 5.3.3, `Programa ejemplo_5_3.f90' se imprime el seno y coseno para una circunferencia completa dividida en octantes. 2. Segunda forma de bucle (_loop_) con el comando `DO' Esta segunda forma, con el esquema siguiente, se conoce como un bucle `WHILE' DO WHILE (expresion logica) bloque de instrucciones ENDDO En este caso el bloque de instrucciones se ejecuta mientras que la expresión lógica en la cabecera del bucle sea cierta. Por ejemplo, en el programa Sección 5.3.4, `Programa ejemplo_5_4.f90' se calculan aproximaciones sucesivas al `SIN(X)' usando la fórmula del desarrollo de Taylor de esta función, avanzando un grado en el desarrollo hasta que el usuario introduzca un cierto valor (cero en este caso). 3. Tercera forma de bucle (_loop_) con el comando `DO' Esta forma es la conocida como un bucle del tipo `REPEAT UNTIL'. El esquema es el siguiente. DO bloque de instrucciones IF (expresion logica) EXIT END DO El bloque de instrucciones se repite hasta que se cumpla la expresión lógica en el `IF'. En este caso, a diferencia de los dos anteriores, siempre se ejecuta al menos una vez el bloque de instrucciones. En los otros dos casos puede no llegar a ejecutarse el bloque de instrucciones ni una sola vez. En este caso se hace uso de la orden `EXIT'. Cuando se ejecuta esta orden el flujo del programa abandona el bucle y continúa en la orden siguiente al `ENDDO' que marca el final del bucle. Otra orden interesante cuando se trabaja con bucles es la orden `CYCLE'. Cuando se ejecuta esta orden se vuelve al principio del bucle, y no se ejecutan las órdenes que se hallen entre su posición y el final del bucle en esa iteración. Como en el caso de los condicionales, los diferentes bucles anidados pueden etiquetarse, lo que permite indicar a qué bucle en concreto se refiere la orden `EXIT' o `CYCLE'. Por defecto, sin usar etiquetas, se refieren al último bucle. En el Sección 5.3.5, `Programa ejemplo_5_5.f90' se combinan varias de las instrucciones de `FORTRAN' descritas en esta sección. Este programa lee un fichero de datos (que se encuentra comentado más abajo del programa, basta con cortar, pegar y eliminar los '`!''). Al abrir el fichero con `OPEN' el programa se asegura de que sea un fichero existente y solo de lectura (`STATUS = 'OLD'' y `ACTION='READ''). Comienza a leer el fichero y salta una serie de lineas, en un bucle del tipo `REPEAT UNTIL', hasta llegar a una línea en la que se proporciona el número de puntos en el fichero[1].Tras ello lee los puntos y los almacena en los vectores `X' e `Y', calculando el máximo valor de `X' y el mínimo valor de `Y'. Para llevar a cabo este cálculo usa las funciones intrínsecas `MAXVAL' y `MINVAL' (ver Sección 8.1, `Objetivos'). Existe una última orden, cuyo uso no se fomenta ya que no está de acuerdo con los principios de programación estructurada, la orden `GOTO'. [1] Para conseguir esto se hace uso de la opción `IERR ='