Mostrando entradas con la etiqueta Fortran. Mostrar todas las entradas
Mostrando entradas con la etiqueta Fortran. Mostrar todas las entradas

sábado, 17 de julio de 2010

Interprogramación de Fortran y C (II)

Hace relativamente poco tiempo, un mes o dos, buscando cómo hacer funcionar la biblioteca libdl en Fortran, me tropecé con este artículo de Aleksandar Donev, en el que explica muy rápidamente cómo hacerlo, y además de un modo que facilita la interprogramación entre Fortran y C en general.

Básicamente, comienza creando las rutinas que usaremos para pasar cadenas de caracteres de C (terminadas en \0) a Fortran (con indicación de longitud) y viceversa. A continuación crea las interfaces Fortran para las principales funciones de libdl y luego muestra un ejemplo.

Sencillo, conciso y correcto.

Claro que no funciona «nada más sacarlo de la caja», por dos razones. Por un lado, utiliza punteros a funciones, algo que entró en Fortran 2003 (en C lo tenemos «de toda la vida»). Y por otro, los valores RTLD_LAZY, RTLD_NOW, RTLD_GLOBAL y RTLD_LOCAL hay que hallarlos a mano antes de empezar. Pero eso no cuesta nada.

Finalmente, lo mejor de todo es que el Sr. Donev me ha autorizado a usar no solamente su método sino el código que presenta en la página. En sus propias palabras:

The code is available with absolutely no restrictions: feel free to incorporate it into your software.

El código está disponible absolutamente sin restricciones. Siéntase libre de usarlo en su propio programa.

lunes, 29 de marzo de 2010

Makefile para Fortran 95 cuando se usan módulos

En mi trabajo tengo que usar módulos de Fortran 95, y no solamente usarlos, sino a veces crearlos. Vienen a ser como las bibliotecas compartidas de C, pero su gestión es distinta. Desde unos puntos de vista, más fea y engorrosa, y desde otros, más simple y clara.

En este caso, los ficheros .mod vienen a hacer el papel de los ficheros .h de C, solo que no se pueden escribir a mano, ya que no son ficheros de texto. Se generan al compilar los ficheros fuente de Fortran que contienen módulos.

Por esa razón me ha costado un poco hacerme a trabajar con ellos, en particular en los Makefiles, pero finalmente creo que ya tengo cómo tratarlos:


testprogram : testprogram.o testmodule.o
gfortran -J. -o testprogram testprogram.o testmodule.o

%.o : %.F95
gfortran -J. -c -o $@ $<

testprogram.o : testmodule.mod

%.mod : %.o
@


Esa simple arroba hace el papel de línea de órdenes vacía que no causa errores, y, al depender el módulo del objeto, depende transitivamente de su fichero fuente, con lo que cambiar éste hace saltar la regla, se detecta que el objeto no está actualizado y se recompila, creando el módulo en el proceso. Poco importa que después la regla para el módulo vaya a ser invocada cuando ya no sea necesaria por tener ya el módulo, porque no hará nada.

martes, 1 de septiembre de 2009

RESHAPE es ineficiente

Fortran es un lenguaje pensado para escribir en el ordenador fórmulas matemáticas, o más en general, cálculos científicos. Y es muy bueno trabajando con matrices multidimensionales: se puede escribir


real(4) :: a(64,64,64)
real(4) :: b(10,10,10)

[...]

b=a(1:10,1:10,1:10)


y así, sin necesidad de bucles, copiamos un trozo de una matriz en la otra. O también podemos, para rellenar de datos una matriz:


real(4) :: a(1:64,1:64,1:64)
integer :: i,j,k

a=RESHAPE( (/ ( (/ ( (/ ( REAL((i-1+(64/2))/64)+1.0 ,k=1,64) /) ,j=1,64) /) ,i=1,64) /) , (/64,64,64/) )


para rellenar un cubo de datos de 64*64*64 con los valores 1 y 2. Esto es lo mismo que


real(4) :: a(1:64,1:64,1:64)
integer :: i,j,k

do k=1,64
do j=1,64
do i=1,64
a(i,j,k)=REAL((i-1+(64/2))/64)+1.0
end do
end do
end do


aunque la filosofía no es la misma. En el primer caso, creamos una ristra de 64*64*64 valores que después adaptamos a un cubo de lado 64. En el segundo caso calculamos los valores uno por uno y los vamos colocando en sus lugares de memoria. El segundo código parece más ineficiente, y puede que el compilador no lo sepa optimizar bien... pero el primero es el realmente ineficiente: compilarlo puede tardar muchísimo más, incluso hacerse eterno con valores mayores, mientras que el segundo se compila en un instante y se ejecuta en poco tiempo.

Recordemos, pues, que salvo en determinadas ocasiones, explícito es mejor que implícito, por mucho que les duela a los fortranistas de toda la vida.

viernes, 2 de noviembre de 2007

Interprogramación de Fortran y C

Estoy en Valencia encargado de mantener código.

Código en Fortran.

El problema de los códigos heredados, en general, es que son heredados. Uno no cuenta con sus propias costumbres de programación (indentado, nombrado de variables, comentarios en el código...) sino que tiene que adivinar sobre código ajeno.

Pero bueno, una cosa es eso, y otra Fortran. Fortran como lenguaje, tal y como se utiliza en Ciencia (códigos FORTRAN 77 heredados de estudiantes de doctorado en estudiantes de doctorado) es algo realmente asqueroso. A veces da la tentación de reprogramar en C, o de modernizar el código a Fortran 95.

La almendra de la cuestión es hacer esa reprogramación en C de rutinas que seguirán siendo llamadas desde Fortran, al estilo Fortran.

¿Funciones o subrutinas?



En C tenemos funciones. En Fortran tenemos funciones y subrutinas. Si tenemos que llamar a Fortran desde C, ¿cómo escribir en Fortran el código llamado?

Básicamente, si nuestra función en C retorna un valor, en Fortran usaremos una función. Si la función en C no retorna (void), en Fortran usaremos una subrutina.

Y viceversa, si en Fortran tenemos una llamada CALL, en C usaremos una función void, mientras que si en Fortran tenemos una llamada a función, en C usaremos una función del mismo tipo.

Nombres de funciones



Normalmente los compiladores de Fortran, en las tablas de símbolos de los ficheros objeto, cambian el nombre de los objetos a minúsculas (recordemos que en Fortran las mayúsculas y las minúsculas son equivalentes) y añaden un guión bajo al final del nombre.

Así, si desde C queremos llamar a una función o subrutina Fortran, debemos llamarla con su nombre escrito completamente en minúsculas y añadir un guión bajo al final.

Y si desde Fortran queremos llamar a una función C, ésta tiene que tener un nombre completamente en minúsculas y un guión al final, que en el código Fortran no pondremos. Si no es así, nos veremos obligados a programar en C un wrapper que siga esta convención y llame a la función C real.

Paso de variables



Es importante recordar que en Fortran las variables siempre se pasan por referencia, nunca por valor. Incluso si llamamos a una función con argumentos constantes.

Por ello, las funciones C que sean llamadas desde Fortran deben esperar que todos sus argumentos sean punteros, y las funciones o subrutinas Fortran que sean llamadas desde C deben ser llamadas proporcionándoles argumentos que sean punteros.

Hay que tener en cuenta, cuando se pasen matrices, que el nombre de la matriz, a secas, ya es un puntero en C, así que no hay que añadirle el asterisco delante.

Almacenamiento de matrices



En C las matrices se almacenan "por filas", o mejor expresado, el índice más a la derecha, el último, recorre secuencialmente los elementos de la matriz. En Fortran, en cambio, las matrices se almacenan "por columnas", es decir, el índice más a la izquierda, el primero, es el que recorre secuencialmente los elementos.

Por ello, cuando pasamos una matriz de Fortran a C o viceversa, debemos invertir el orden de todos sus índices.

También hay que tener en cuenta que mientras que en Fortran una matriz puede declararse con índices arbitrarios, que por defecto comienzan en 1, en C las matrices siempre comienzan con el índice 0.

Por la misma razón, todo programador decente de Fortran escribe bucles anidados en los que el bucle más interno recorre el primer índice, mientras que todo programador decente de C escribe bucles anidados en los que el bucle más interno recorre el último índice.

Bloques COMMON



En Fortran es común utilizar bloques COMMON como un modo de que todas las subrutinas y funciones puedan acceder a determinadas variables. Es posible hacer uso desde C de estos bloques COMMON, si tenemos en cuenta que se presentan como estructuras de datos cuyo contenido es el conjunto de variables que en C correspondan a las variables Fortran.

Es necesario tener en cuenta que lo mencionado acerca de los identificadores de funciones Fortran en C también se aplica a los nombres de estas estructuras y a los de las variables que las componen.

Es necesario fijarse con cuidado, si tenemos problemas de acceso a estas variables, en las direcciones resultantes para las variables flotantes de doble precisión. Los compiladores de C pueden añadir relleno en las estructuras de datos para que los límites de las variables se alineen correctamente, y los de Fortran no pueden hacerlo en sus bloques COMMON.