[ anterior ] [ Contenidos ] [ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] [ 6 ] [ 7 ] [ 8 ] [ 9 ] [ 10 ] [ 11 ] [ 12 ] [ 13 ] [ siguiente ]
Fortran 90 para la asignatura Química Computacional
Los objetivos de esta clase son los siguientes:
presentar las diferentes estructuras de control condicionales en
Fortran (branching).
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.
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.
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
.
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.
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.
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.
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-1y 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 Programa ejemplo_5_1.f90, Sección 5.3.1 puede verse una aplicación de la estructura IF THEN ELSE IF ELSE ENDIF y en el ejemplo Programa ejemplo_5_2.f90, Sección 5.3.2 como se lleva a cabo, aparentemente, la misma tarea con una aplicación de la estructura CASE.
Bucles y comandos de utilidad en bucles
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 Programa ejemplo_5_3.f90, Sección 5.3.3 se imprime el seno y coseno para una circunferencia completa dividida en octantes.
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 Programa ejemplo_5_4.f90, Sección 5.3.4 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).
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 Programa ejemplo_5_5.f90, Sección 5.3.5 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[7].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 Objetivos, Sección
8.1).
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.
PROGRAM EJEMPLO_5_1
!
IMPLICIT NONE
!
REAL :: NOTA
CHARACTER (3), DIMENSION(1:5) :: NT, LISTNT=(/'S','A','N','Sob','MH'/)
INTEGER :: IN
! READ NOTE
PRINT *, "Nota del estudiante?"
READ 50, Nota
50 FORMAT(F4.1)
!
IF (Nota>=0.0.AND.Nota<5.0) THEN
IN=1
ELSE IF (Nota>=5.0.AND.Nota<7.0) THEN
IN=2
ELSE IF (Nota>=7.0.AND.Nota<9.0) THEN
IN=3
ELSE IF (Nota>=9.0.AND.Nota<10.0) THEN
IN=4
ELSE IF (Nota==10.0) THEN
IN=5
ELSE
IN=0
ENDIF
!
IF (IN==0) THEN
PRINT *, "LA NOTA : ", Nota," NO ES UNA NOTA ACEPTABLE."
ELSE
PRINT 100, Nota, LISTNT(IN)
ENDIF
!
100 FORMAT(1X,'LA NOTA DEL ALUMNO ES ',F4.1,' (',A3,')')
!
END PROGRAM EJEMPLO_5_1
PROGRAM EJEMPLO_5_2
IMPLICIT NONE
REAL :: Nota
INTEGER :: IN, Inota
CHARACTER (3), DIMENSION(1:5) :: LISTNT=(/'S','A','N','Sob','MH'/)
! READ NOTE
PRINT *, "Nota del estudiante?"
READ(*,*), Nota
!
Inota = NINT(Nota)
!
SELECT CASE (Inota)
CASE (0:4)
IN = 1
CASE (5,6)
IN = 2
CASE (7,8)
IN = 3
CASE (9)
IN = 4
CASE (10)
IN = 5
CASE DEFAULT
IN = 0
END SELECT
!
IF (IN==0) THEN
PRINT *, "LA NOTA : ", Nota," NO ES UNA NOTA ACEPTABLE."
ELSE
PRINT 100, Nota, LISTNT(IN)
ENDIF
!
100 FORMAT(1X,'LA NOTA DEL ALUMNO ES ',F4.1,' (',A3,')')
!
END PROGRAM EJEMPLO_5_2
PROGRAM EJEMPLO_5_3
!
IMPLICIT NONE
!
REAL :: Pio2 = ASIN(1.0)
REAL :: Angle1 = 0.0, Angle2 = 0.0
INTEGER :: index
!
DO index = 0, 16, 2
Angle1 = index*Pio2/4.0
!
WRITE(*,*)
WRITE(*,*) 'Cos(',index/2,'Pi/4) = ',COS(Angle1),'; Cos(',index/2,'Pi/4) = ',COS(Angle2)
WRITE(*,*) 'Sin(',index/2,'Pi/4) = ',SIN(Angle1),'; Sin(',index/2,'Pi/4) = ',SIN(Angle2)
WRITE(*,*)
!
Angle2 = Angle2 + Pio2/2.0
!
ENDDO
END PROGRAM ejemplo_5_3
PROGRAM ejemplo_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
!
! APROXIMACIONES SUCESIVAS A SIN(X) = X - X^3/3! + X^5/5! - X^7/7! + ...
WRITE(*,*) "INTRODUZCA EL VALOR DEL ANGULO X (RAD) :"
READ(*,*) X_val
!
I_count = 1
X_app = X_val
X_sum = X_val
!
WRITE(*,*) ' ORDEN APROX SIN(X) APPROX-SIN(X)'
DO WHILE (I_flag == 1)
WRITE(*,*) 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 SI, 1 NO)"
READ(*,*) I_flag
IF (I_flag /= 1 .AND. I_flag /= 0) I_flag = 1
!
ENDDO
END PROGRAM ejemplo_5_4
PROGRAM ej_5_5
!
IMPLICIT NONE
REAL , DIMENSION(:), ALLOCATABLE :: X_vec, Y_vec
INTEGER :: Index, Ierr, Numpoints = 0
REAL :: Max_x, Min_y
CHARACTER(LEN=64) :: Filename
!
! READ FILENAME
READ(5,*) Filename
! OPEN FILE
OPEN( UNIT=10, FILE=Filename, STATUS='OLD', ACTION='READ' )
!
DO
READ(UNIT=10, FMT=100, ERR=10) Numpoints
IF (Numpoints /= 0) EXIT
10 READ (UNIT=10, FMT=*) ! JUMP ONE LINE
CYCLE
ENDDO
!
PRINT*, 'NUMPOINTS = ', Numpoints
!
! ALLOCATE X, Y VECTORS
ALLOCATE(X_vec(1:NUMPOINTS), STAT = IERR)
IF (Ierr /= 0) STOP 'X_vec MEM ALLOCATION FAILED'
ALLOCATE(Y_vec(1:NUMPOINTS), STAT = IERR)
IF (Ierr /= 0) STOP 'Y_vec MEM ALLOCATION FAILED'
!
DO I = 1, Numpoints
!
READ(UNIT=10, FMT=110) X_vec(I), Y_vec(I)
!
ENDDO
!
Max_x = MAXVAL(X_vec)
Min_y = MINVAL(Y_vec)
!
PRINT*, "MAXIMUM X VALUE = ", Max_x
PRINT*, "MINIMUM Y VALUE = ", Min_y
! DEALLOCATE AND CLOSE FILE
DEALLOCATE(X_vec, STAT = IERR)
IF (Ierr /= 0) STOP 'X_vec MEM DEALLOCATION FAILED'
DEALLOCATE(Y_vec, STAT = IERR)
IF (Ierr /= 0) STOP 'Y_vec MEM DEALLOCATION FAILED'
!
CLOSE(10)
! FORMAT STATEMENTS
100 FORMAT(19X,I3)
110 FORMAT(F6.3,1X,F6.3)
!
END PROGRAM ej_5_5
!# Remark 1
!# Remark 2
!Useless line 1
!Useless line 2
!Number of points = 4
!+1.300;-2.443
!+1.265;-1.453
!+1.345;-8.437
!+1.566;+4.455
[ anterior ] [ Contenidos ] [ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] [ 6 ] [ 7 ] [ 8 ] [ 9 ] [ 10 ] [ 11 ] [ 12 ] [ 13 ] [ siguiente ]
Lecciones de Fortran 90 para la asignatura Química Computacional
mailto:francisco.perez@dfaie.uhu.es