FAQ Fortran
FAQ FortranConsultez toutes les FAQ
Nombre d'auteurs : 1, nombre de questions : 70, dernière mise à jour : 23 juillet 2021
- Quelle est la différence entre une FUNCTION et une SUBROUTINE ?
- Comment récupérer un paramètre/argument en ligne de commande ?
- Qu'est ce qu'une variable locale/globale ?
- Comment passer des variables en arguments vers/depuis une subroutine ?
- Comment utiliser un common pour partager des variables entre sous programmes (F77) ?
- Comment utiliser un module pour partager des variables entre sous programmes (F90) ?
Les function et les subroutine sont littéralement traduits de l'anglais par fonction et sous-programme. Ce sont les deux types de sous programmes disponibles en Fortran.
- Une function est également une variable qui contient le résultat de la fonction alors que le ou les résultats d'une subroutine sont en paramètres. Il s'ensuit qu'une fonction doit être déclarée.
- Pour appeler une subroutine on utilise la commande call alors qu'une function n'en a pas besoin.
- Une function doit forcément renvoyer un résultat stocké dans une variable (qui peut être un tableau). Une subroutine peut ne renvoyer aucun résultat et juste réaliser une action (écrire dans un fichier, nettoyer l'écran ...).
Les fonctions
Voyons un exemple simple d'une fonction et de son appel. Une fonction est, par exemple, de la forme f(x) = a*x + b. Voici un programme qui utilise cette fonction :
PROGRAM
test_function
! programme principal
implicit
none
integer
::i
real
::f,a,b,x,y
a = 1.d0
b = 2.d0
x=0.d0
Do
i=1,100
x = x + 0.1d0
write
(*,*)x,f(a,b,x) ! appel de la fonction
Enddo
! autre exemple
x=0.d0
Do
i=1,100
x = x + 0.1d0
y = f(a,b,x) ! appel de la fonction
write
(*,*)x,y
Enddo
END
PROGRAM
test_function
! * * * * * * * * * * * *
FUNCTION
f(a,b,x)
implicit
none
real
,intent(in)::a,b,x
real
::f ! dans une fonction son nom est déclaré
f = a*x + b ! quelque part dans une fonction on doit assigner une valeur à f
END
FUNCTION
f
Les subroutines
Pour continuer l'exemple précédent, on peut programmer une sous-routine qui va lire les paramètres a et b de la fonction f(a,b,x).
PROGRAM
test_function
! programme principal
! la subroutine n'est pas déclarée
implicit
none
integer
::i
real
::a,b,f,x
call
lec(a,b) ! on utilise call pour appeler une subroutine
x = 0.d0
Do
i=1,100
x = x + 0.1d0
write
(*,*)x,f(a,b,x)
Enddo
END
PROGRAM
test_function
! * * * * * * * * * * * * * *
SUBROUTINE
lec(a,b)
implicit
none
real
,intent(out)::a,b ! on précise que a et b sont des argument de sortie
write
(*,*)"entrer a et b"
read
(*,*)a,b
write
(*,*)"a = "
,a
write
(*,*)"b = "
,b
END
SUBROUTINE
lec
! * * * * * * * * * * * * * *
FUNCTION
f(a,b,x)
implicit
none
real
,intent(in)::a,b,x
real
::f ! dans une fonction son nom est déclaré
f = a*x + b ! quelque part dans une fonction on doit assigner une valeur à f
END
FUNCTION
f
- argument sortant : integer,intent(out)::p1
- argument entrant : integer,intent(in)::p2
- argument entrant/sortant : integer,intent(inout)::p3
Les arguments entrants ne peuvent pas être modifiés dans la subroutine. Une subroutine doit assigner une valeur à un argument sortant.
Quand on travaille dans un terminal il arrive souvant qu'on rajoute à la suite d'un programme des paramètres. par exemple : mon_programme.x p1 p2 p3. Pour récupérer p1, p2 et p3 on utilise la subroutine getarg.
call getarg( entier , chaîne de caractère)
L'entier correspond à la place de l'argument. Par exemple, dans notre cas, 1 correspond à p1, 2 à p2 et 3 à p3. La valeur 0 permet de récupérer le nom du programme. Ceci peut être mis à profit pour si l'exécutable est enregistré sous des noms différents afin d'avoir une exécution différente selon le nom du programme. Les paramètres p1, p2 ou p3 sont enregistrés dans une variable de type chaîne de caractères. Voici un exemple d'utilisation :
PROGRAM
test_getarg
implicit
none
integer
::i,p1,p2,p3
integer
,dimension
(3)::p
character
(len
=10)::parm,nom
! on récupère le nom du programme
call
getarg(0,nom)
! on récupère les valeurs des paramètres
call
getarg(1,parm)
read
(parm,*)p1
call
getarg(2,parm)
read
(parm,*)p2
call
getarg(3,parm)
read
(parm,*)p3
! on peut faire une boucle
Do
i=1,3
call
getarg(i,parm)
read
(parm,*)p(i)
Enddo
END
PROGRAM
test_getarg
Une variable est une zone mémoire à laquelle on associe un nom pour pouvoir écire ou lire ce qu'elle contient. Lorsqu'on utilise des sous programmes il faut savoir si les variables qu'on utilise sont accessibles par tous les sous programmes (variables globales) ou juste dans le programme ou sous programme dans lequel elles sont déclarées.
En Fortran toutes les variables déclarées au début d'un programme principal, d'une fonction ou d'une subroutine sont des variables locales. Ainsi si l'on déclare un entier i dans 3 sous programme, ces 3 variables i seront associées à 3 zones mémoires différentes et indépendantes.
Pour utiliser des variables globales, communes à plusieurs sous programmes, on utilise les common (FORTRAN77) ou des module (Fortran90).
La méthode la plus simple pour qu'une subroutine et son programme appelant se passent des variables est de les mettre comme arguments de la subroutine. Il faut ensuite préciser s'ils sont entrant (ils viennent du programme appelant et ne doivent pas être modifié), sortant (ils viennent de la subroutine vers le programme appelant et doivent être assignés) ou les deux.
PROGRAM
test_arg
implicit
none
integer
::a,b,c
a = 2
c = 1
call
sub(a,b,c)
END
PROGRAM
test_arg
! * * * * * * *
SUBROUTINE
sub(a,b,c)
! il n'est pas nécessaire que les arguments aient le même nom, seule la position est importante
! mais ça facilite la lecture du programme
implicit
none
integer
,intent(in)::a ! a n'est pas modifiable
integer
,intent(out)::b ! b doit être assigné
integer
,intent(inout)::c ! c peut être modifié
b = a + c
c = c + 1
END
SUBROUTINE
sub
Il n'est pas obligatoire de préciser l'intent, cela peut cependant éviter certaines erreurs et facilite la lecture du programme.
Le common permet de déclarer un zone mémoire commune dans laquelle seront enregistrées des variables. Elles seront accessibles par tous les sous programmes dans lesquels le common est déclaré. Reprenons l'exemple utilisé par les arguments pour comparer.
! * * * *
! syntaxe d'un common
common /nom de la zone commune/ liste des variables
! * * * *
PROGRAM
test_arg
implicit
none
integer
::a,b,c
common /arg/ a,b,c ! je déclare le common
a = 2
c = 1
call
sub
END
PROGRAM
test_arg
! * * * * * * *
SUBROUTINE
sub
implicit
none
integer
::a,b,c
common /arg/ a,b,c ! j'utilise le common
b = a + c
c = c + 1
END
SUBROUTINE
sub
Il n'est plus nécessaire de donner les variables a,b et c en argument de la subroutine sub car elles sont accessibles par l'intermédiaire du common.
Dans ce cas les variables de la zone commune sont toutes modifiables par la subroutine, pour préciser un intent il faut utiliser les paramètres.
Pour ne pas avoir à reécrire dans chaque sous programme le common, il était courant d'inclure un fichier qui contenait les variables du common. Ceci avait également l'avantage de ne pas avoir à modifier tous les common dans tous les sous programmes à chaque modification. Voici un exemple :
PROGRAM
test_arg
implicit
none
include
"arg"
a = 2
c = 1
call
sub
END
PROGRAM
test_arg
! * * * * * * *
SUBROUTINE
sub
implicit
none
include
"arg"
b = a + c
c = c + 1
END
SUBROUTINE
sub
! * * * * * * * * * * * * * * *
!contenu du fichier arg
integer
::a,b,c
common /arg/ a,b,c
Les modules sont la grande nouveauté du Fortran 90 et apporte au Fortran un soupçon de programmation orientée objet. L'utilisation des modules pour partager des variables entre des sous programmes n'est qu'une infime partie de ce qu'ils apportent. Dans ce cas, leur utilisation est similaire à celle du common. On va créer un module, dans lequel seront déclarées des variables qui seront disponibles dans tous les sous programmes qui utilisent le module.
MODULE
arg
implicit
none
integer
::a,b,c
END
MODULE
arg
! * * * * * * *
PROGRAM
test_arg
USE
arg
implicit
none
a = 2
c = 1
call
sub
END
PROGRAM
test_arg
! * * * * * * *
SUBROUTINE
sub
USE
arg
implicit
none
b = a + c
c = c + 1
END
SUBROUTINE
sub
L'utilisation d'un module apporte cependant une contrainte. Ils doivent être compilés avant le programme principal. Plusieurs solutions sont possibles :
- Lors de la compilation placer le nom du module en premier, par exemple : ifort module.f90 program.f90
- Si le programme est court et que tous les sous programmes et modules sont dans un même fichier, placer les modules en premier, en tête du fichier dans l'ordre qu'ils apparaissent. Puis compiler simplement le fichier comme un programme normal.
- Compilez à l'avance vos modules (s'arréter à la compilation, création de l'objet). Puis lors de la compilation du programme ajouter les .o des modules.
Pour les compilateurs ifort, g95 ou gFortran, la compilation (création de l'objet) se fait en ajoutant l'option -c.
Il est possible de n'utiliser qu'une partie d'un module avec l'instruction only. On va ainsi sélectionner les variables qui seront accessibles et celles qui ne le seront pas. Voici un exemple :
MODULE
arg
implicit
none
integer
::a,b,c
double
precision
::x
END
MODULE
arg
! * * * * * * *
PROGRAM
test_arg
USE
arg, only : a,c ! seuls a et c sont utiles
implicit
none
a = 2
c = 1
call
sub
END
PROGRAM
test_arg
! * * * * * * *
SUBROUTINE
sub
USE
arg, only : a,b,c ! seuls a b et c sont utiles
implicit
none
b = a + c
c = c + 1
END
SUBROUTINE
sub