Cours avancé Fortran : Module et Types dérivés


Télécharger Cours avancé Fortran : Module et Types dérivés
3.53.5 étoiles sur 5 a partir de 1 votes.
Votez ce document:

Télécharger aussi :


FORTRAN

1 Historique

FORTRAN=mathematical FORmula TRANslating system.

Pourquoi un langage Fortran ? 

=>Volonté de créer un langage quasi naturel pour les scientifiques et conciliant Efficacité / Performance du code généré.

Initialement, un compilateur Fortran a été créé sur les systèmes IBM. La généralisation d'un compilateur Fortran à d'autres systèmes a nécessité la création en 1966 d'un comité chargé du développement d'une norme (ANSI . American Nat. Standards Institute).

•    Première norme apparue : Fortran 66

•    Seconde norme : Fortran 77

•    Nécessité de moderniser le Fortran 77 en respectant les critères suivants : o Introduire des nouveaux concepts exploités par des langages plus récents. o Améliorer les performances en calcul scientifique o Cette extension doit être totalement compatible avec Fortran 77

•    En 1991/1992, les comités ISO et ANSI créent la norme Fortran 90

(avec F77 inclus dans F90)

2 Les apports de la norme 90

•    Le "format libre", identificateurs, déclarations, !, &, ;

•    Précision des nombres. KIND, destiné à assurer une meilleure portabilité

•    Objets de type dérivés

•    DO END DO, SELECT CASE, WHERE

•    Extensions sur les tableaux : profil, manipulation, fonctions prédéfinies

•    Allocation dynamique de mémoire (ALLOCATABLE)

•    Pointeurs

•    Procédures et fonctions récursives

•    Arguments de fonctions et de procédures : OPTIONAL, INTENT, PRESENT.

Passage par mot-clé

•    Bloc interface, interface générique, surcharge d'opérateurs

•    Les entrées / sorties

? De nouvelles fonctions intrinsèques 3 Identificateurs et formats du Fortran

Identificateurs en Fortran 77 et 90 :

Un identificateur est une suite de caractères choisis parmi les lettres non accentuées, les chiffres, le blanc souligné. Le premier caractère est obligatoirement une lettre. Aucune distinction n'est faite entre les minuscules et les majuscules. Les identificateurs sont utilisés pour nommer le programme, les variables, les procédures et fonctions et en Fortran 90 les types dérivés, modules et interfaces. 

La longueur d'un identificateur est limitée à 6 caractères pour le Fortran 77 et à 31 caractères pour le Fortran 90. 

Exemples :

fin_de_fichier, Compteur sont des identificateurs du Fortran 90, contrairement à 8Q et FIVE.4

Les caractéristiques du format fixe Fortran 77 :

1.    Une ligne de code a la structure suivante :

o    Colonnes 1 à 5 : zone étiquette. Ces colonnes peuvent contenir des espaces ou une étiquette (nombre entier compris entre 1 et 99999) permettant de se référer en cours de programme à l'instruction qui débute sur la même ligne. o Colonne 6 : colonne de continuation. Lorsqu'une instruction ne peut être placée sur une seule ligne, cette colonne contient un caractère différent de "0". Les caractères les plus couramment utilisés sont "+", "$". Le nombre de lignes de continuation est limité à 19.

o    Colonnes 7 à 72 : zone instruction. A partir de la 73ème colonne, les caractères sont ignorés.

2.    Toute ligne commenant par l'un des caractères "C", "c" ou "*" est une ligne de commentaire.

3.    Le caractère blanc n'est pas sugnificatif.

Les Caractéristiques du format fixe Fortran 90 :

Il s'agit du format fixe Fortran 77 avec 2 extensions :

1.    Plusieurs instructions peuvent être codées sur une même ligne en les séparant avec le caractère ";"

2.    Le caractère "!" après une ou des instructions indique le début d'un commentaire. Toute ligne dont le premier caractère non blanc commence par le caractère "!" en une colonne différente de la 6ème est une ligne de commentaire.

Ce format est principalement destiné à assurer la compatibilité entre le Fortran 77 et le Fortran 90. Les fichiers au "format fixe" porteront l'un des suffixes .f (le plus courant), .for ou .FOR pour le compilateur fortran 90 DEC-Unix.

Les caractéristiques du format libre Fortran 90 :

1.    Les colonnes 1 à 6 ne sont plus réservées. Les étiquettes peuvent être placées à partir de n'importe quelle colonne.

2.    Plusieurs instructions peuvent être codées sur une même ligne en les séparant avec le caractère ";" La première instruction peut être placée à partir de n'importe quelle colonne.

3.    Un commentaire peut être indiqué n'importe où sur une ligne à condition qu'il soit précédé du caractère "!".

Attention . C-----, c-----, *----- ne sont pas des commentaires Fortran 90 en "format libre" et génèrent des erreurs de compilation. 

Exemple :

i=0 ! initialisation

4.    Les lignes peuvent être de longueur quelconque à concurrence de 132 caractères.

5.    Les blancs sont significatifs. Les commandes ENDIF, ENDDO et

GOTO peuvent également être écrite sous la forme END IF, END DO, GO TO (mais EN  DIF est interdit alors que c'est autorisé en "format fixe").

6.    Une ligne se terminant par le caractère "&" indique que la ligne suivante est une ligne de continuation. Si la ligne de continuation commence par le caractère "&", seuls les caractères situés juste après le "&" font partie de la ligne de continuation (pratique pour écrire des chaînes de caractères sur plusieurs lignes). Sans le caractère "&" au début de la ligne de continuation, tous les caractères, y compris les blancs et la tabulation, font partie de la ligne de continuation.

Exemple :

print *, 'Ceci est une phrase sur & 

            &sur deux lignes'

Les fichiers au format libre porteront l'extension .f90.

4 Les déclarations

Forme générale d'une déclaration :

type [, liste_attributs ::] liste_objets

Liste des différents types :

•    integer

•    real

•    double precision

•    complex

•    character

•    logical (valeurs prises : .true. ou .false. )

•    type (type_dérivé_défini_par_le_développeur)

Liste des différents attributs :

•    parameter : utilisé pour une constante

•    dimension : pour indiquer la (les) taille(s) d'un tableau

•    allocatable : objet dynamique

•    pointer : variable définie comme pointeur

•    target : variable cible d'un pointeur

•    save : variable statique

•    intent : vocation d'un argument muet

•    optional : argument muet facultatif

•    external / intrinsic : nature d'une procédure

Nouveauté :

Il est possible d'initialiser une variable au moment de sa déclaration (c'est d'ailleurs obligatoire si la variable possède l'attribut parameter). En Fortran 77, seule une variable de type parameter peut être initialisée au moment de sa déclaration.

Exemples de déclarations :

•    real x, y, z

•    integer, parameter :: n=5

•    double precision a(100)

•    double precision, dimension(100) :: a

•    integer, dimension( -1:1), parameter :: p=(/ 5, 7, 13 /)

Remarque :

Les règles relatives au typage par défaut à l'intérieur d'un programme, d'une procédure ou fonction sont identiques au Fortran 77 :

•    déclaration implicite : INTEGER (I-N), REAL(A-H,O-Z)

•    déclaration non implicite : IMPLICIT NONE   =>  toutes les variables doivent être déclarées (vivement recommandé).

5 Syntaxe des opérateurs logiques

Certains opérateurs logiques possèdent deux syntaxes : 

.LE.

<=

.LT.

<

.GE.

>=

.GT.

>

.EQ.

==

.NE.

/=

En revanche, les opérateurs .AND., .OR., .NOT. n'ont pas d'équivalents nouveaux.

6 Les structures de contrôle

Les instructions conditionnelles (IF) Syntaxe 1 :

IF (expression_logique) instruction Syntaxe 2 :

IF (expression_logique_1) THEN bloc1

ELSE IF (expression_logique_2) THEN bloc2 ELSE

bloc3

END IF

Les boucles (DO)

Il existe trois nouvelles formes de boucles.

Syntaxe 1 :

[ref:] DO bloc1

IF (condition) instruction_de_sortie_de_boucle bloc2

END DO [ref]

Il s'agit d'une boucle DO dont la sortie s'effectue à l'aide d'une instruction de sortie de boucle EXIT ou GOTO.

Syntaxe 2 :

[ref:] DO variable=integer1, integer2 [,integer3] bloc

END DO [ref] ou également

[ref:] DO etiquette variable=integer1, integer2 [,integer3] bloc etiquette CONTINUE

Syntaxe 3 :

[ref:] DO WHILE (expression_logique) bloc END DO [ref]

Remarques :

•    L'instruction CYCLE permet d'interrompre l'exécution d'instructions à l'intérieur d'une boucle et de continuer à l'itération suivante de la boucle.

•    L'instruction EXIT permet de sortir du corps d'une boucle et indique qu'il faut exécuter les instructions juste après la boucle.

•    Il est désormais possible de référencer des boucles par une chaîne de caractères. Ceci est particulièrement utile lorsque l'on souhaite utiliser les instructions EXIT et CYCLE à l'intérieur de boucles imbriquées.

•    Les instructions CYCLE et EXIT ne sont utilisables qu'à l'intérieur de boucles DO.

•    Les deux dernières formes de boucles, sans les références, existaient déjà en Fortran 77.

Exemple :

program boucle implicit none

integer :: i, j, n, m, nb, som integer, parameter :: som_max=150 n=10; som=0; b1 : do i=1,n read *, m do j=1,m read *, nb if (nb < 0) cycle som = som + nb if (som > som_max) exit b1 end do end do b1 end Aiguillage (SELECT CASE) Syntaxe :

[ref:] SELECT CASE (expression) CASE cas_1 bloc_1

CASE cas_n

bloc_n

CASE DEFAULT bloc_defaut END SELECT [ref] Exemple :

integer :: flag, res

SELECT CASE(flag) CASE (1, 2, 3) res=1 CASE (4:6) res=2

CASE DEFAULT

res=0

END SELECT

7 Types dérivés

En Fortran 90, il est désormais possible de définir des types dérivés (ou strucures de données) permettant de regrouper un ensemble de données de types hétérognes.

Exemple :

Déclaration d'un type dérivé couleur :

type COULEUR

character(len=16) :: nom real, dimension(3) :: compos end type COULEUR

Dans le programme principal, la déclaration et l'initialisation d'une variable coul de type couleur se fera de la manière suivante :

type(COULEUR), parameter :: coul = couleur('rouge', (/ 1.,0.,0. /))

Pour accéder aux champs nom et compos de la variable coul, on utilisera l'opérateur % :

print *,'Composantes RGB de la couleur ',coul%nom,' : ',coul%compos

Caractéristiques :

•    chaque champ est constitué d'éléments de type intrinsèque ou de type dérivé.

•    un champ ne peut avoir aucun des attributs PARAMETER, ALLOCATABLE, TARGET.

•    un champ ne peut pas recevoir d'initialisation par défaut lors de la déclaration du type dérivé (c'est par contre possible en Fortran 95).

8 Les modules

Un module est une unité de programme permettant principalement d'encapsuler :

•    un ensemble de données (permet d'avoir l'équivalent des fichiers .h du

C)

•    des définitions de types dérivés

•    des procédures internes et des blocs interfaces (cf chapitre suivant)

Exemple :    Création d'un module de données équivalent au COMMON

MODULE varGlobales

integer, save :: vari real, save :: varr

END MODULE varGlobales

Les données de ce module sont accessibles dans toute unité de programme commençant par l'instruction USE varGlobales. Cet usage du module fournit une substitution aux blocs COMMON utilisés en Fortran 77.

Dans une unité de programme, il est également possible de faire appel qu'à certains objets du module à l'aide du qualificateur ONLY. Par exemple, l'accès unique à la variable vari du module varGlobales est spcifié grâce à l'instruction suivante :

USE varGlobales, ONLY : vari

Lors de l'appel d'un module, il est également possible de renommer des données de ce même module. Le renommage s'effectue à l'aide de l'opérateur =>. Dans l'exemple précédent, on aurait ainsi pu renommer la variable vari par ma_vari de la manière suivante :

USE varGlobales, ONLY : ma_vari => vari

Quelques règles sur les modules :

•    Un module peut faire appel un autre module autre que lui même.

•    Les modules figureront dans des fichiers à part du programme principal, des procédures et des fonctions. Plusieurs modules peuvent être définis dans un même fichier.

Compilation :

Les modules "source" doivent être compilés avant les modules, procédures et fonctions utilisant d'autres modules. Supposons que l'on ait à compiler un programme prog.f90 utilisant trois modules mod1, mod2 et mod3 regroupés dans les fichiers mod1et2.f90 et mod3.f90, où mod3 utilise mod2. La compilation doit alors être effectuée dans l'ordre suivant :

1.    Compilation préalable des sources contenant les modules :

f90 -c mod1et2.f90 f90 -c mod3.f90

Ces commandes créent un fichier objet .o par fichier source et un fichier par module. 

2.    Compilation de prog.f :

f90 -o prog pro.g mod1et2.o mod3.o              

9 Précision des nombres (kind)

En Fortran 90, les types prédéfinis sont en fait des noms génériques regroupant un ensemble de sous-types(appels aussi variantes). Ces soustypes sont accessibles à l'aide du paramètre KIND et sont à valeur entière. Ce paramètre, utilisé lors de la déclaration d'une variable, prend un ensemble de valeurs qui sont propres au système utilisé. Ces valeurs correspondent au nombre d'octets désirés pour le codage de la variable déclarée.

Exemples :

•    real(kind=4) x

Sur alger, cela revient à déclarer x comme un réel simple précision (ce qui est aussi équivalent à déclarer x en tant que real*4).

•    real(kind=8) y

Sur alger, cela revient à déclarer x comme un réel double précision (ce qui est aussi équivalent à déclarer  x en tant que real*8).

Remarque :

A chaque type correspond un sous-type par défaut, sélectionné en l'abscence du paramètre KIND (pour le type réel, il s'agit du sous-type simple précision).

Pour les constantes, il est possible d'indiquer le sous-type désiré pour leur écriture. Pour cela, la constante sera suffixée par la valeur du sous-type, séparé par le caractère blanc souligné "_".

Exemples :

•    173_4

•    25.678_8

La fonction intrinsèque KIND :

Cette fonction renvoie la valeur entière correspondant au sous-type de l'argument spécifié.

Exemples :

•    kind(1.0d0) renvoit 8 sur alger

•    real(kind=kind(1.d0)) x permet de déclarer x comme un réel double précision quelle que soit la machine utilisée.

Les fonctions intrinsèques SELECTED_INT_KIND(r) et SELECTED_REAL_KIND(p, r) :

•    La fonction SELECTED_INT_KIND reçoit un nombre entier r en argument et retourne l'entier du sous-type integer permettant de représenter les entiers n vérifiant |n| < 10r. La fonction renvoit 1 si aucun sous-type ne convient.

•    La fonction SELECTED_REAL_KIND reçoit deux arguments

entiers p et r (ils sont optionnels mais au moins l'un d'entre eux doit être mentionné). Elle retourne l'entier du sous-type real permettant de représenter les entiers x avec une précision au moins égale à p chiffres décimaux et vérifiant 10-rx < 10r (r s'appelle l'étendue (range) de x ). Cette fonction retourne 1 si la précision demandée n'est pas disponible, 2 si l'étendue souhaitée n'est pas disponible, 3 si ni la précision ni l'étendue ne sont disponibles.

Exemple :

•    Sur alger, la fonction selected_int_kind retourne les valeurs suivantes

:

Valeur de r

Valeur retournée par selected_int_kind

1

1

3

2

5

4

10

8

19

1

10 Les tableaux

Quelques définitions

1.    Le rang d'un tableau est son nombre de dimensions.



2.    L'étendue d'un tableau dans une dimension donnée désigne le nombre d'éléments du tableau dans cette même dimension.

3.    Le profil (shape) d'un tableau est un vecteur dont le ième élément est le nombre d'éléments du tableau dans la ième dimension.

4.    Deux tableaux sont dits conformants s'ils ont le même profil.

5.    La taille d'un tableau est le produit des éléments de son vecteur profil.

Exemple :

integer, dimension ( -5:4, 0:2) :: x integer, dimension (0:9, -1:1) :: y

Les tableaux x et y, tous deux de rang 2, ont pour profil le vecteur (/ 10, 3 /). Ils sont donc conformants. Leur taille vaut 30.

Premières caractéristiques

1.    Les tableaux de rang strictement supérieur à 7 sont interdits.

2.    Tout scalaire est supposé conformant à tout tableau. Par conséquent, si a est un tableau, l'instruction a=1a un sens et signifie que tous les éléments de a sont affectés à 1.

3.    Si a est un tableau de dimension 2, écrire a=1 est équivalent à écrire a(:,:)=1. De la même façon, a(i,:) désigne la ième ligne de a.

4.    Tout tableau de rang 1 peut être initialisé à sa déclaration en encadrant ses valeurs d'affectation par (/ et /).

Pour les tableaux de rang au moins égal à 2, on utilisera la fonction RESHAPE. Exemples :

character(len=1), dimension(3) :: tabc=(/ 'a', 'b', 'c' /) integer, dimension (4) :: tabi1, tabi2

tabi1 = (/ 8, 45, 6, 1 /) tabi2 = (/ (i*2, i=1,4) /)

5.    La fonction intrinsèque SHAPE(array) permet d'obtenir le profil d'un tableau.

6.    La fonction intrinsèque SIZE(array [,dim]) retourne la taille d'un tableau si seul ce tableau est spécifié en argument. Si un second argument, dim, est précisé, cette fonction retourne l'étendue du tableau relativement à la dimension dim.

Exemple :

integer, dimension ( 5:4, 0:2) :: x

print *, size(x) ! retourne 30 print *, size(x,1) ! retourne 10 print *, size(x,2) ! retourne 3

Sections de tableaux

Une section de tableau désigne une partie de tableau.

1.    Sections régulières de tableaux :

Lorsque les indices des éléments varient en suivant une suite arithmétique, on parlera de section régulière de tableau. Pour chaque indice du tableau, cette progression arithmétique sera notée sous la

forme valeur_initiale : valeur_finale : pasExemple :

integer, dimension (9,9) :: a integer, dimension (3,3) :: b integer, dimension (4,9) :: c

b   = a (1:5:2, 1:9:3) ! affectation possible car les tableaux sont conformants c = a (1:7:2, :) ! affectation possible car les tableaux sont conformants

1 1 1 1 1 1 1 1 1

                      4 4 4 4 4 4 4 4 4

1 2 3 4 5 6 7 8 9

1 1 1

1 4 7

1

1

1  1 1 1 1 1 1 1

2  3 4 5 6 7 8 9

                 Ainsi                                    , b             et c

                                      5 5 5 5 5 5 5 5 5              3 3 3              3 3 3 3 3 3 3 3 3

                , si a                                     vau            vau

vaut1 2 3 4 5 6 7 8 9                t1 4 7                t1 2 3 4 5 6 7 8 9

6 6 6 6 6 6 6 6 6

5 5 5

1 4 7

5

5 5 5 5 5 5 5 5

1 2 3 4 5 6 7 8 9

1 2 3 4 5 6 7 8 9

2.    Sections non régulières de tableaux :

Lorsque l'on souhaite extraire une section non régulière d'un tableau, on accède aux éléments du tableau par l'intermédiaire de vecteur d'indices.  Exemple :

integer, dimension (9,9) :: a integer, dimension (10,10) :: b

b   = 0

b ((/ 3, 5, 8 /), (/ 1, 5, 7 /)) = a (1:5:2, (/ 1, 5, 9 /)) b ((/ 1, 2 /), (/ 2, 3, 4 /)) = 1

                11 12 13 14 15 16 17 18 19 0 1 1 1 0 0 0 0 0 0

21 22 23 24 25 26 27 28 29 0 1 1 1 0 0 0 0 0 0

31 32 33 34 35 36 37 38 39

41 42 43 44 45 46 47 48 49 0 0 0 0 0 0 0 0 0 0

Ainsi, si a vaut

51 52 53 54 55 56 57 58 59 , b vaut 31 0 0 0 35 0 39 0 0 0

         0 0 0 0 0 0 0 0 0 0

81 82 83 84 85 86 87 88 89 51 0 0 0 55 0 59 0 0 0

91 92 93 94 95 96 97 98 99

                                                                                                 0 0 0 0 0 0 0 0 0 0

Opérateurs définis sur les tableaux

Les opérateurs d'affectation (=), de comparaison (==, /=, < , < =, > , > =), arithmétiques (+, , *, /, **) et logiques (.and., .or., .not.) ont été définis sur les tableaux. Ces opérateurs s'appliquent sur des tableaux de profil identique. Ces opérateurs s'appliquent terme terme.

Remarque :

Les opérateurs arithmétiques *, ** n'opèrent pas comme des opérateurs de produit matriciel sur les vecteurs et matrices mais comme des opérateurs de produit terme terme.

Les principales fonctions intrinsèques définies sur les tableaux

•    RESHAPE(source, shape [,pad ] [,order]) permet de construire un tableau de profil égal à shape à partir des éléments du vecteur source (qui est obligatoirement un vecteur de rang 1). Si des éléments sont manquants dans source, le tableau est complété à l'aide des éléments figurant dans pad. order spécifie sous la forme d'un tableau de rang 1, l'ordre dans lequel les indices commencent à varier pour le remplissage du tableau résultat.

Exemple :

L'instruction reshape((/ (i, i=1,6) /), (/ 2, 3 /), order=(/ 2, 1 /)) permet de générer la matrice

1 2 3

4 5 6

•    TRANSPOSE(matrix ), où matrix est un tableau de dimension 2, retourne la transposée de matrix.

•    DOT_PRODUCT(vector a, vector b) retourne le produit scalaire de deux vecteurs, c'est à dire at.b si a et b sont de type entier ou réel, conjugué(a)t.b si a et b sont complexes, et Somme(ai .and. bi) si a et b sont de type logique.

•    MATMUL(matrix a, matrix b) retourne le produit matriciel de a et b, sous réserve de compatibilité des dimensions.

Soit a et b sont tous deux des matrices, soit a est un vecteur et b est une matrice, soit a est une matrice et b est un vecteur. a et b peuvent être de type quelconque.

•    LBOUND(array [,dim]) et UBOUND(array [,dim]) retournent les bornes inférieures / supérieures de chacune des dimensions (ou seulement de la dimension dim) du tableau array.

Exemple :

integer, dimension( 21:2, 45:49) :: tab  lbound(tab) vaut (/ 21, 45 /) et ubound(tab) vaut (/ 2, 49 /)  lbound(tab, dim=2) vaut 45 et ubound(tab, dim=1) vaut 2  


ALL(mask [,dim]) applique un masque de type logique sur les éléments du tableau et renvoit vrai si pour tous les éléments le résultat du masque est vrai. Si dim est précisé, la fonction travaille sur cet indice pour chaque valeur des autres dimensions. Exemple :

Si A =123et B =135

4 5 6

426

all(A==B) vaut .false.  all(A==B, dim=1) vaut (/ .true., .false., .false. /) 

•    ANY(mask [,dim]) fonctionne de la même façon que la fonction ALL à l'exception qu'elle renvoit vrai si l'un des résultats du masque est vrai.

Exemple :

En reprenant A et B de l'exemple précédent, on obtient :  any(A==B) et any(A/=B) valent .true.  any(A/=B, dim=1) vaut (/ .false., .true., .true. /) 

•    COUNT(mask [,dim]) comptabilise le nombre d'éléments pour lesquels le résultat du masque est vrai.

Exemple :  count(A==B) vaut 3  count (A/=B, dim=1) vaut (/ 0, 2, 1 /) 

•    MINVAL(array [,dim][,mask]) et MAXVAL(array

[,dim][,mask]) retournent la plus petite / plus grande valeur du tableau array (après un éventuel filtrage par dim et / ou mask).

Exemple :  maxval(A) vaut 6 

minval(A, dim=1) vaut (/ 1, 2, 3 /)  minval(A,dim=2,A > 2) vaut (/ 3, 4 /) 

•    PRODUCT(array [,dim][,mask]) et SUM(array

[,dim][,mask]) retournent le produit / la somme des valeurs des éléments du tableau array (après un éventuel filtrage par dim et / ou mask). Exemple :

product(A) retourne 720  sum(A,dim=1,A > 2) vaut (/ 4, 5, 9 /)

.

L'instruction WHERE

L'instruction WHERE permet d'effectuer des opérations sur des éléments d'un tableau sélectionnés via un filtre de type logique.  Syntaxe :

WHERE (filtre) bloc1

ELSEWHERE

bloc2

END WHERE

Lorsque bloc2 n'existe pas et que bloc1 se résume à une seule instruction, on peut utiliser également la forme contractée suivante :

WHERE (filtre) instruction

Exemples :

real, dimension(10) :: a

where (a > 0.) a = sqrt(a) where (a > 0.) a = log(a) elsewhere a = 1. end where

11 Les tableaux dynamiques

Fortran 90 permet de créer des tableaux de manière dynamique. Pour cela, il faut tout d'abord spécifier l'attribut ALLOCATABLE lors de la dclaration d'un tableau. Les instructions ALLOCATE et DEALLOCATE, ainsi que la fonction intrinsèque ALLOCATED permettent alors de gérer les tableaux dynamiques :

•    ALLOCATE(array, stat=err) permet de faire l'allocation mémoire du tableau. Si le mot clé stat= est spécifié, la variable err (de type integer) vaut 0 si l'allocation s'est déroulée sans problème et 1 sinon.

•    DEALLOCATE(array, stat=err) permet de libérer l'espace mémoire réservée à un tableau.

ALLOCATED(array) est une fonction intrinsèque renvoyant 0 ou 1 suivant que le tableau spécifié en argument est alloué ou non.

Exemple :

integer, dimension (:,:), allocatable :: a integer :: n, m, err

read *, n, m if (.not. allocated(a)) then allocate(a(n,m),stat=err)

if (err /= 0) print *,'Erreur d''allocation dynamique du tableau a'; stop end if

deallocate(a)

Remarques :

•    Un tableau ayant l'attribut ALLOCATABLE doit avoir été alloué avant d'être passé en argument d'une procédure.

•    Un tableau alloué dynamiquement dans une unité de programme et n'ayant pas l'attribut SAVE (sert pour les variables locales d'une procédure. Avant chaque sortie de procédure, la valeur courante d'une telle variable est sauvegardée) a un état indéterminé en sortie de cette unité (c'est à dire après un RETURN / END). Il ne sera pas non plus libéré automatiquement.

12 Les pointeurs

En Fortran 90 (contrairement à un langage comme le C), un pointeur désigne un alias et non une adresse sur une case mémoire. Un pointeur possède trois états :

•    l'état indéfini : c'est l'état qui lui est attribué à sa dclaration

•    l'état nul : cela signifie que le pointeur n'est alias d'aucun objet

•    l'état associé : le pointeur est alias d'un objet cible

Quelques règles

•    Lors de la déclaration, on spécifie qu'une variable est un pointeur en indiquant l'attribut pointer.


Pour qu'un pointeur ait pour cible un objet c, c doit avoir l'attribut target (inutile lorsque la cible est également un pointeur).

•    On indique qu'un pointeur p est alias d'une cible c à l'aide de l'opérateur => : p => c

•    Lorsque les opérandes de l'opérateur = sont des pointeurs, l'opération s'effectue sur les cibles et non sur les pointeurs.

•    Il n'est pas nécessaire qu'un pointeur soit associé avant d'être passé en argument d'une procédure (contrairement à allocatable).

Les fonctions intrinsèques NULLIFY et ASSOCIATED

•    NULLIFY(pointer_1 [,pointer_2] [pointer_n]) permet de forcer l'état d'un ou de plusieurs pointeurs à nul.

•    ASSOCIATED(pointer_1 [,pointer_2] [,cible]) indique si un pointeur est associé à une cible lorsqu'un seul pointeur est passé en argument, indique si deux pointeurs ont même cible lorsque deux pointeurs sont passés en argument et indique si un pointeur est alias d'une cible lorsqu'un pointeur et une cible sont passés en argument.

Exemple :

real, pointer :: p1, p2 real, target :: c nullify (p1, p2)

print *, associated(p1), associated (p2), associated(p1, p2) ! retourne F F F c=1.5

p1 = > c ! p1 est donc alias de c p2 = > c print *, associated(p1), associated(p1, c), associated(p1, p2) ! retourne T T T Remarques :

•    Si p1 et p2 sont deux pointeurs, l'instruction p1 = > p2 est licite et signifie que p1 prend l'état de p2 (de plus il est inutile de spécifier l'attribut target à p2).

•    Dans l'exemple précédent, l'instruction p1 => 1.5 aurait été illicite (1.5 n'étant pas une variable).

Les pointeurs de type tableau

Deux cas peuvent se présenter : soit le pointeur est alias d'un tableau existant (ou d'une partie d'un tableau existant), soit le pointeur est alias d'une zone mémoire allouable.

Lorsque l'on souhaite qu'un pointeur soit alias d'un tableau existant, il faut lui spécifier les attributs pointer et dimension. Il n'est pas nécessaire de préciser les bornes min et max pour chacune des dimensions; ceci est fait automatiquement lors de l'attribution du tableau cible au pointeur (cf exemple suivant).

•    Un pointeur de type tableau peut être alias d'une zone mémoire gérée via les commandes ALLOCATE, DEALLOCATE, ALLOCATED.

Dans ce cas, l'attribut pointer est suffisant dans la liste des attributs (il est donc inutile de spécifier l'attribut ALLOCATABLE).

Exemple :

integer, dimension(10,10), target :: c integer, dimension(:), pointer :: p1 integer, dimension(:,:), pointer :: p2 c=reshape((/ (i, i=1,100) /), (/ 10, 10 /)) p1 = > c(4, 1:10:3) ! p1 est donc un vecteur de profil égal à (/ 4 /) allocate (p2(5, 10)) ! p2 est donc un tableau de profil égal à (/ 5, 10 /) p2 = reshape((/ (2*i, i=1,50) /), (/ 5, 10 /)) print *, p2 deallocate (p2)

Remarques importantes :

•    Une variable de type pointeur n'a pas besoin d'etre associée pour etre passée en argument d'une procédure / fonction (ceci permet d'allouer un argument de type tableau à l'intérieur d'une procédure, avantage par rapport à ALLOCATABLE).

•    Pour que le compilateur sache que l'argument muet est de type pointeur, l'interface de la procédure / fonction doit etre explicite (voir chapitre suivant).

13 Interfaces de procédures

Définitions :

Une interface de procédure spécifie le nom et les caractéristiques d'une procédure, à savoir le nom, le type et les attributs de chaque argument muet. Si toutes ces propriétés sont connues du programme appelant, l'interface est dite explicite. Dans le cas contraire, elle est dite implicite.

Les défauts de l'interfaçage implicite

Considérons une procédure appelée somprod permettant de calculer la somme et le produit des termes d'un vecteur et éventuellement le nombre d'éléments nuls de ce vecteur. A première vue, l'interface de cette procédure pourrait s'écrire comme suit :

subroutine somprod(vect, dim, som, prod, nbnuls) integer, dimension (dim) :: vect integer :: dim, som, prod, nbnuls

Des erreurs non détectées à la compilation peuvent se produire si, par exemple, dans le corps de la procédure figure une instruction comme dim=8. De même, lors de l'appel de la procédure dans le programme principal, les instructions suivantes sont erronées et ne sont pourtant pas détectées à la compilation :

real, dimension (n) :: v

real :: x

call somprod (x, n, s, p, nb) ! Erreur de typage pour le premier argument call somprod (v, n, s, p, nb, x) ! Le dernier argument est de trop call somprod (v, n, p, s, nb) ! Intervertion de deux arguments call somprod (v, 10, p, s, nb) ! Argument de type constante numerique :



DANGER

Ces erreurs sont dues à un interfaçage de procédure implicite ne permettant pas au compilateur de contrôler rigoureusement la cohérence des arguments d'appel avec les arguments muets.

L'interfaçage explicite

Fortran 90 permet au compilateur de mieux contrôler la cohérence des arguments. La norme prévoit en effet les possibilités suivantes :

1.    Il est possible de préciser la vocation des arguments à l'aide de l'attribut INTENT. Dans la déclaration d'un argument, on indiquera alors o intent(in) si l'argument ne doit pas être réaffecté dans la procédure,

o    intent(out) si l'argument doit être affecté dans la procédure pour être utilisé dans le programme appelant,

o    intent(inout) si la valeur de l'argument doit être modifiée dans la procédure.


L'interface de la procédure somprod serait alors la suivante :

subroutine somprod(vect, dim, som, prod, nbnuls) integer, dimension (dim), intent(in) :: vect integer, intent(in) :: dim

integer, intent(out) :: som, prod, nbnuls

2.    Certains arguments peuvent ne pas être utilisés lors de l'appel de la procédure à condition que cet argument ait l'attribut OPTIONAL.

Dans la procédure, il est possible de tester si cet argument est utilisé à l'appel grâce à la fonction intrinsèque PRESENT qui retourne un booléen. 

Par convention, les arguments optionnels figureront en dernier dans la liste des arguments. 

Pour l'interface de la procédure somprod, nbnuls recevrait donc l'attribut optional.

3.    Lors de l'appel d'une procédure, il est possible de passer un argument à l'aide d'un mot-clé. Ce mot-clé est en fait le nom donné à l'argument muet de la procédure. Le passage par mot-clé est particulièrement utilisé pour les arguments optionnels. Un appel à la fonction somprod pourrait se formuler ainsi :

call somprod(v, n, s, p, nbnuls=nb)

1. L'interface d'une procédure peut être rendue explicite pour le programme appelant en créant un module avec bloc interface ou un module avec procédure. Ces deux notions sont explicitées ci-après.

Interfaçage explicite : création d'un module avec bloc interface

La première solution permettant à un programme d'avoir une visibilité complète sur l'interface d'une procédure est de créer dans ce programme ce qu'on appelle un bloc interface. Ce bloc interface regroupe l'interface complète de la procédure entre les qualificatifs interface et end interface. Exemple : Création d'un bloc interface pour la procédure somprod

interface subroutine somprod(vect, dim, som, prod, nbnuls) integer, dimension (dim), intent(in) :: vect integer, intent(in) :: dim integer, intent(out) :: som, prod

integer, intent(out), optional :: nbnuls end subroutine somprod end interface

Les procédures externes étant susceptibles d'être appelées dans plusieurs unités de programmes, il est fortement recommandé d'inclure un bloc interface dans un module. Il suffira alors de faire appel à ce module (instruction USE) dans chacune de ces unités de programme afin que l'interface de procédure soit parfaitement connue.

Interfaçage explicite : création d'un module avec procédure

La seconde solution consiste à créer un module contenant la déclaration complète de la procédure. Ce module étant appelé dans une unité de programme, cela revient quasiment à déclarer la procédure à l'intérieur de l'unité (on parle alors de procédure interne). Ceci est possible en Fortran 90, à condition d'ajouter le mot clé CONTAINS avant la déclaration de la procédure.

Exemple :  Création d'un module avec la procédure somprod

module mod_somprod contains subroutine somprod(vect, dim, som, prod, nbnuls) integer, dimension (dim), intent(in) :: vect integer, intent(in) :: dim integer, intent(out) :: som, prod integer, intent(out), optional :: nbnuls

som = sum(vect) prod = product(vect)

if (present(nbnuls)) nbnuls = count(vect==0) end subroutine somprod end module mod_somprod

Tableau en argument d'une procédure à interface explicite

Fortran 90 permet de transmettre le profil et la taille d'un tableau passé en argument d'une procédure à condition que son interface soit explicite. Dans la déclaration de la procédure et de son interface, on précise alors que la taille est inconnue (à l'aide de l'attribut dimension(:,:) pour un tableau à 2 dimensions par exemple).

Ainsi, la procédure somprod et son bloc interface pourrait être déclarées comme suit :

! Declaration de la procedure somprod subroutine somprod(vect, som, prod, nbnuls) integer, dimension (:), intent(in) :: vect integer, intent(out) :: som, prod integer, intent(out), optional :: nbnuls

som = sum(vect) prod = product(vect) if (present(nbnuls)) nbnuls = count(vect==0) print *,'taille du vecteur : ', size(vect) end subroutine somprod

! Declaration du bloc interface de la procedure somprod interface subroutine somprod(vect, som, prod, nbnuls) integer, dimension (:), intent(in) :: vect integer, intent(out) :: som, prod integer, intent(out), optional :: nbnuls end subroutine somprod end interface

Cas des fonctions intrinsèques

Les procédures intrinsèques du Fortran 90 possèdent toutes une interface explicite.

14 Les procédures et fonctions récursives

Le Fortran 90 permet d'écrire des procédures et fonctions récursives en les définissant sous l'une des formes suivantes :

recursive subroutine sub(arg_1, , arg _n)recursive function f(n) result(res)recursive type function f(n) result(res)

Remarques :

•    Le type de la variable résultat est celui de la fonction doivent être identiques.

•    La clause RESULT peut également être utilisée pour les fonctions non récursives.

Exemple :    Ecriture de la fonction factorielle

recursive integer function factorielle(n) result (fact) integer, intent(in) :: n integer :: fact if (n==0) then fact = 1 else

fact = n * factorielle(n - 1) end if

15 Interface générique

Plusieurs fonctions mathématiques peuvent être utilisées à la fois sur des réels simple précision, des réels double précision et des complexes. Dans le cas de la fonction exponentielle, par exemple, exp(a) retourne une valeur de type identique à celui de a. Pratiquement, trois fonctions exponentielle sont définies et regroupées sous un nom générique (ici exp) par l'intermédiaire d'un bloc interface. Le bloc interface portera le nom de la fonction générique et regroupera l'interface de chacune des trois fonctions. A la compiltion, le compilateur choisit :-

•    la fonction exp si a est un réel simple précision

•    la fonction dexp si a est un réel double précision

•    la fonction cexp si a est un complexe

Cette notion existe déjà en Fortran 77 mais reste limitée aux fonctions intrinsèques. Fortran 90 permet de définir ses propres fonctions et procédures génériques.

Exercice : Créer une procédure somprod générique s'appliquant aux vecteurs entiers ou réels.

Méthode :

1.    Créer une procédure isomprod pour vect de type entier et une procédure rsomprod pour vect de type réel. Placer ces deux procédures dans un même fichier.

2.    Créer un bloc interface appelé somprod regroupant les interfaces de isomprod et de rsomprod. Placer ce bloc interface dans un module mod_somprod.

3.    Ecrire un programme faisant appel au module mod_somprod et appelant somprod pour un vecteur entier puis pour un vecteur réel.

4.    Compiler les différents fichiers et éxecuter le programme.

16 Surcharge et création d'opérateurs

Fortran 90 permet de surcharger des opérateurs comme le font certains langages orientés objets tels que C++ et Java. La définition de nouveaux opérateurs est également possible. On distinguera deux sortes d'opérateurs :

les opérateurs s'appliquant sur des expressions et retournant une valeur (comme +, *) et l'opérateur d'affectation (=) qui ne retourne aucune valeur.

Les opérateurs retournant une valeur

Pour la première série d'opérateurs, la surcharge s'effectue grâce à un bloc interface commençant par INTERFACE OPERATOR, suivi du symbole  de l'opérateur entre parenthèses.

Il reste ensuite à spécifier l'interface complète de la fonction effectuant l'opération. 

Pour la définition d'un nouvel opérateur, le principe est identique mis à part que c'est le nom de l'opérateur encadré par le caractère "." qu'il faut mettre entre parenthèses.

Exemple 1 :  Interface de l'opérateur + sur les nombres entiers, réels et complexes.

interface operator(+) integer function iadd(a, b) integer, intent(in) :: a, b end function iadd real function radd(a, b) real, intent(in) :: a, b end integer radd complex function cadd(a, b) complex, intent(in) :: a, b end function cadd end interface

Exemple 2 : Interface de l'opérateur .inv. d'inversion des nombres entiers

interface operator (.inv.) integer function iinv(i) integer, intent(in) :: i end function iinv

end interface

Remarque :

Lors de la surcharge ou de la définition d'un tel opérateur, les arguments de la fonction doivent tous avoir l'attribut intent(in).

Surcharge de l'opérateur d'affectation =

La surcharge de l'opérateur d'affectation s'effectuera à l'aide d'une interface du type INTERFACE ASSIGNMENT(=)

L'interface de la procédure associée doit spécifier l'attribut intent(out) pour le premier argument (opérande de gauche) et l'attribut intent(in) pour le second argument (opérande de droite).

Exemple :   Interface de l'opérateur = sur des données de type matrice

interface assignment(=) subroutine affect(a, b) type(matrice), intent(out) :: a type(matrice), intent(in) :: b end subroutine affect end interface

Exercice :

1.    Définir un type matrice triangulaire sur les entiers. On s'attachera à limiter autant que possible la taille mémoire occupée par une telle matrice.

2.    Créer des procédures d'allocation / désallocation et d'affichage de matrices triangulaires.

3.    Surcharger les opérateurs +, =, == et définir l'opérateur de transposition .tr. sur ce type de matrices.

4.    Tester ces opérateurs sur des matrices triangulaires inférieures de votre choix.

5.    Pour l'ensemble de cet exercice, on s'attachera à définir une interface explicite pour toutes les procédures et fonctions.

Solution

Remarques :

•    La définition d'un type dérivé sur les matrices triangulaires nécessite la création d'un type dérivé ligne (la commande allocate n'étant

possible que sur les tableaux). Les tableaux dynamiques étant définis à l'intérieur d'un type dérivé, ils ne peuvent avoir que l'attribut pointer (et non allocatable).

•    L'ordre de compilation est important. Pour compiler un fichier F2 faisant appel un module défini dans un fichier F1, il faut compiler F1 puis F2. Ainsi, dans le cas présent, l'ordre de compilation est .

f90 -c mod_mattri.f90 f90 -c mod_matutil.f90 f90 -c mod_matop.f90 f90 -c matutil.f90 f90 -c matop.f90

f90 -o TestMatTri TestMatTri.f90 mod_mattri.o mod_matutil.o mod_matop.o matutil.o matop.o

17 Rappels et nouveautés sur les entrées / sorties

Définitions

•    Un enregistrement désigne une suite ordonnée de valeurs ou de caractères.

•    Un fichier est constitué d'une suite ordonnée d'enregistrements tous de même type (c'est à dire composé d'enregistrements soit tous formattés soit tous non formattés).

•    Les fichiers formattés sont des fichiers lisibles par un éditeur de textes.

•    Les fichiers non formattés sont des fichiers écrits en binaire pur.

•    La façon dont on accède aux enregistrements d'un fichier détermine s'il s'agit d'un fichier à accès séquentiel ou d'un fichier à accès direct.

•    Pour un fichier séquentiel, l'ordre des enregistrements est l'ordre dans lequel ils ont été écrits.

La longueur des enregistrements du fichier peut être de taille variable. 

Pour accéder à un enregistrement du fichier, il est nécessaire d'accéder à tous les enregistrements qui le précèdent. 

Un tel fichier se termine par un enregistrement de fin de fichier.  En Fortran, un fichier séquentiel est par défaut formatté.

•    Pour un fichier à accès direct, l'accès à un enregistrement du fichier se fait par l'intermédiaire du numéro d'ordre de l'enregistrement.

Tous les enregistrements doivent avoir la même longueur. 

Un tel fichier ne contient pas d'enregistrement de fin de fichier.  En Fortran, un tel fichier est par défaut non formatté.

Principe des entrées / sorties

•    En Fortran, une correspondance logique est établie entre un numéro et les périphériques clavier / écran pour la gestion des entrées / sorties sur un terminal. Pour une saisie de données (périphérique clavier), le numéro 5 est réservé. Pour l'affichage de données (périphérique écran), le numéro 6 est réservé.

•    La gestion des entrées / sorties dans un fichier se fait également à l'aide d'une correspondance logique entre un nombre entier positif (choisi par l'utilisateur et différent de 5 et 6) et le fichier. Ce numéro logique sera indiqué lors de l'ouverture et de la fermeture du fichier ainsi que lors de la lecture et de l'écriture de données dans le fichier.

Remarque :  En Fortran, on utilisera le plus souvent des fichiers séquentiels et formattés.

Les formats

De manière générale, un format indique la façon de représenter un ensemble de données. La notion de format est utilisée pour la lecture et l'écriture de nombres ou de chaînes de caractères.

Il est possible de ne pas se soucier de la représentation de ces données, auquel cas le format sera dit libre. 

Dans le cas contraire, le format sera dit imposé. 

Voici les différents descripteurs de format de données en Fortran 90 :

•    A[n] : descripteur pour les chaînes de caractères. Le descripteur A permet de traiter les chaînes de caractères sans se soucier de leur longueur.

•    L[n] : descripteur pour les données de type logique. En entrée, un champ ne contenant que des caractères blancs est considéré comme étant la valeur .false.. En sortie, la lettre T (ou F) est affichée, précédée de n-1 caractères blancs.

•    In,Bn, On, Zn : représentation d'un entier sur n positions (signe - à prendre en compte pour les entiers négatifs) respectivement en base décimal, binaire, octale ou hexadécimale.

•    En.p : descripteur pour nombres réels sous forme de puissance de 10 (avec n  >= p + 8) .

[+][0].x1x2 xpE+c1c2 ou [+][0].x1x2 xpE+c1c2c3

•    En.pEe: représentation identique à celle de En.p sauf que e positions sont réservées pour l'exposant ( n >= p + e + 5) .

•    ENn.p : ce descripteur représente un nombre sous forme de puissance de 10 de telle sorte que la puissance soit divisible par 3 et que la partie entière de sa valeur absolue (y1y2y3) soit comprise entre 1 et 999, exception faite pour la valeur 0 ( n >= p + 10) .

[+]y1y2y3.x1x2 xpE+c1c2 ou [+]y1y2y3.x1x2 xpE+c1c2c3

•    ENn.pEe : représentation identique à celle de ENn.p sauf que e positions sont réservées pour l'exposant ( n >= p + e + 7) .

•    ESn.p : ce descripteur représente un nombre sous forme de puissance de 10 de telle sorte que la partie entière de sa valeur absolue (y) soit comprise entre 1 et 9, exception faite pour la valeur 0 ( n >= p + 8) .

[+]y.x1x2 xpE+c1c2 ou [+]y.x1x2 xpE+c1c2c3

•    ESn.pEe : représentation identique à celle de ESn.p sauf que e positions sont réservées pour l'exposant ( n >= p + e + 5) .

•    Fn.p :descripteur pour nombres réels sous forme fixe (p positions après la virgule et n positions au total). Pour les nombres négatifs, on vérifiera que la relation n >= p+3 est bien respectée (une position pour le signe , une autre pour le . et au moins une pour le chiffre précédent le .).

•    Gn.p[Ee] : le format le plus approprié entre E et F est choisi. Plus précisément, si la donnée d à représenter vérifie 10p <= d < 0.1, d sera représentée au format En.p[Ee], sinon une notation avec exposant sera utilisée.



Remarques :

•    En écriture, si les spécifications du format ne correspondent pas, une série de caractères * est affichée.

•    Pour les réels, les valeurs affichées sont les valeurs arrondies.

Plusieurs descripteurs de mise en page ont également été prévus :

•    / : Saut d'enregistrement.

•    nX : Saut de colonne. A la lecture, les n positions sont ignorées. A l'écriture, les n positions sont remplies de caractères blancs.

•    Tn : Tabulation. Ce descripteur est utilisé afin d'indiquer qu'une donnée sera lue ou écrite à partir de la position n depuis le début de l'enregistrement.

Exemple :

write(6, 1000) X, Y

1000 format(T10, F10.2, T60, F10.2)

=> La valeur de X sera écrite à partir de la colonne 10 et celle de Y à partir de la colonne 60.

•    TLn : Tabulation arrière. Ce descripteur indique que le prochain caractère sera lu ou écrit n positions avant la position courante du curseur.

Exemple : print "(I2, 5X, TL4, I2)" 20, 24

=>20 24

•    TRn : Tabulation avant. Ce descripteur indique que le prochain caractère sera lu ou écrit n positions après la position courante du curseur. Descripteur équivalent à nX.

•    $ : Descripteur de suppression de retour à la ligne (pas standard, donc non reconnu par certains compilateurs).

•    'texte' ou nHtexte : Descripteur d'insertion de texte. Pour la seconde forme, n désigne le nombre de caractères à insérer après le symbole H.

•    n(descripteurs) : répétition d'un ou de plusieurs descripteurs n fois.

Exemple : 3(F8.2, 3X, 2I3)

•    : Descripteur d'interruption de contrôle du format. Ce descipteur permet d'interrompre l'exploitation de la liste des formats qui le succèdent lorsque le nombre d'entrées / sorties est inférieur à ce qui est attendu. Il est utilisé avec les descripteurs de répétition.

Exemple :

write (6, 1000) 10, 12

1000 format(5(:,'valeur : ', I2, 1X)) => Le résultat en sortie sera :  valeur : 10 valeur : 12 

=>Sans le descripteur :, le résultat serait :  valeur : 10 valeur : 12 valeur :

Les namelist

L'instruction NAMELIST permet d'associer un nom à une liste de variables. L'intéret est d'utiliser ce nom pour les opérations d'entrées/sorties sur ces variables.

Syntaxe de déclaration d'une namelist :

NAMELIST /nom_groupe/ liste_variables [[,] /nom_groupe/ liste_variables]

Seules les variables ayant l'attribut allocatable ou pointer ne sont pas autorisées à figurer dans une namelist.

Syntaxe de saisie des valeurs d'une namelist :

&nom_groupe var=valeur [,var=valeur] /

Le caractère / permet d'interrompre l'instruction de lecture. Pendant l'exécution, il est possible d'interroger le système sur la nature de la namelist à saisir :

•    le caractère ? permet l'affichage du nom de la namelist et de ses variables

•    les caractères =? permettent l'affichage du nom de la namelist à saisir, de ses variables et de leur valeur courante

Exemple :

character (len=20), dimension(2) :: nom int, dimension(3) :: mensurations

real :: poids

namelist /personne/ nom, mensurations, poids read(*, nml=personne) open(unit=6, delim="apostrohe")

write(6, nml=personne)

Supposons qu'à la saisie, on entre :

&personne nom(2)='dupond' mensurations=85, , 90 poids=50.2 /

En sortie du programme, on aura alors : 

&PERSONNE

NOM = '', 'dupond              ',

MENSURATIONS = 85, 0, 90,

POIDS = 50.20000 /

Les entrées / sorties sur un terminal

1. Les entrées au clavier

Syntaxe au format libre :

read(5, *) liste_variables ou read *, liste_variables (forme condensée) 

read(5, nml=nom groupe) ou read(*, nml=nom groupe) (pour les namelist)

Syntaxe au format imposé :

read(5, num fmt) liste_variables

num_fmt format(liste_descripteurs_de_ format)

OU

read format, liste_variables

Dans ce dernier cas, le format devra figuré entre les caractères "( et )"  Exemple :

read "(I2,F10.3)", i, x

2. Les sorties sur écran

Syntaxe au format libre :

write(6, *) liste_variables ou print *, liste_variableswrite(6, nml=nom_groupe) (pour les namelist)

Syntaxe au format imposé :

write(6, num_fmt) liste_variables

num_fmt format(liste_descripteurs_de_format)

OU

print format, liste_variables

Les entrées / sorties dans un fichier

1.    L'instruction OPEN

L'instruction OPEN établit la correspondance entre un numéro logique (choisi par l'utilisateur) et un fichier (référencé par son nom).  Syntaxe :

OPEN([unit=] u [liste_specifications]) où liste_specifications peut contenir les spécifications suivantes :

o    ACCESS = acc : acc spécifie le mode d'accès aux enregistrements et vaut 'DIRECT' ou 'SEQUENTIAL' (valeur par défaut).

o    ACTION = act : act peut valoir 'READ' (toute tentative d'écriture est interdite), 'WRITE' (toute tentative de lecture est autorisée), ou 'READWRITE' (les opérations de lecture et écriture sont autorisées, valeur par dfaut).

o    BLANK = blnk : blnk vaut 'NULL' ou 'ZERO' suivant que les caractères blancs doivent être ignorés ou interprétés par des 0. La valeur par défaut est 'NULL'. Cette option n'est licite que pour les fichiers formattés.

o    DELIM = del_char : del_char vaut 'APOSTROPHE', 'QUOTE' ou 'NONE' (valeur par défaut) et spécifie le caractère utilisé pour délimiter des chaînes de caractères ou des namelist.

o    ERR= errs : si une erreur survient lors de l'ouverture du fichier, l'instruction spécifiée par l'étiquette errs est exécutée.

o    FILE = fln : fln est le nom du fichier à ouvrir (chaîne de caractères).

o    FORM = fm : fm est égal à 'FORMATTED' ou 'UNFORMAT TED'. o IOSTAT = ios : ios est une valeur de retour prenant la valeur 0 si aucune erreur ne s'est produite et une valeur strictement positive si une erreur est survenue pendant l'ouverture du fichier.

o    PAD = pad_char : pad_char doit être affecté à 'YES' (valeur par défaut) ou 'NO'. Si la valeur 'YES' est choisie, les enregistrements de longueur inférieure à celle indiquée par l'option RECL seront complétés avec des blancs. o POSITION = pos : pos indique la position du pointeur de fichier à l'ouverture. 'REWIND' : position en début de fichier, 'APPEND' : position en fin de fichier. o RECL = rl : rl est une expression entière égale à la longueur d'un enregistrement pour un fichier à accès direct. o STATUS = sta : sta peut prendre les valeurs 'NEW' (le fichier est créé et s'il existe déjà, une erreur survient), 'OLD' (le fichier existe déjà, sinon erreur), 'REPLACE' (si le fichier n'existe pas, il sera créé, sinon il sera détruit et un fichier de même nom sera créé), 'SCRATCH' (le fichier est effacé à sa fermeture, l'option FILE ne doit pas être utilisée) ou 'UNKNOWN' (l'existence du fichier est inconnue). 'UNKNOWN' était en fait utilisé en Fortran 77 afin d'éviter les messages d'erreur, d'où la possibilité d'utiliser la valeur 'REPLACE' en Fortran 90.-

2.    L'instruction CLOSE

L'instruction CLOSE ferme la connexion entre le numéro logique indiqué et le fichier qui lui est associé.  Syntaxe :

CLOSE([UNIT=] u [,ERR = errs] [,IOSTAT = ios]) avec la même signification des options que pour l'instruction OPEN. 

3.    L'instruction READ

L'instruction READ permet la lecture de données dans un fichier.  Syntaxes :

READ([unit=] u, [liste_specifications]) liste_variables

READ([unit=] u, [NML=] namelist [liste_specifications]) (pour les namelist)

Liste des options de spécification de lecture :

o    ADVANCE = spec, spec valant 'YES' (valeur par défaut) ou 'NO' suivant que l'on souhaite passer à l'enregistrement suivant ou non. Cette option ne peut pas être spécifiée pour les namelist et n'existe que pour les fichiers formattés séquentiels.

o    END = s, où s est l'étiquette d'une instruction qui sera exécutée en fin de lecture du fichier. o EOR = s, où s est l'étiquette d'une instruction qui sera exécutée en fin de lecture d'un enregistrement. o ERR = s, où s est l'étiquette d'une instruction qui sera exécutée en cas d'erreur.

o    FMT = format,format désigne soit un format, soit l'étiquette d'un format.

o    IOSTAT = ios, ios prenant la valeur 0 si aucune erreur n'intervient et une valeur strictement positive sinon. o REC = rn, rn étant le numéro de l'enregistrement lire. o SIZE = n,n étant affecté au nombre de caractères lus.

4.    L'instruction WRITE

L'instruction WRITE permet l'écriture de données dans un fichier.  Syntaxes :

WRITE([unit=] u, [liste_specifications]) liste_variables

WRITE([unit=] u, [NML=] namelist [liste_specifications]) (pour les namelist)

Liste des options de spécification d'écriture :

o    ADVANCE = spec,spec valant 'YES' (valeur par défaut) ou 'NO' suivant que l'on souhaite passer à l'enregistrement suivant ou non. Cette option ne peut pas être spécifiée pour les namelist et n'existe que pour les fichiers formattés séquentiels. o ERR = s, où s est l'étiquette d'une instruction qui sera exécutée en cas d'erreur.

o    FMT = format,format désigne soit un format, soit l'étiquette d'un format.

o    IOSTAT = ios,ios prenant la valeur 0 si aucune erreur n'intervient et une valeur strictement positive sinon. o REC = rn,rn étant le numéro de l'enregistrement à lire.

5.    L'instruction REWIND

REWIND est une instruction de positionnement du curseur au début

d'un fichier séquentiel.  Syntaxes :

REWIND([unit=] u [,ERR = s] [,IOSTAT = ios])

REWIND u

6.    L'instruction BACKSPACE

L'instruction BACKSPACE positionne le curseur d'un fichier séquentiel au début de l'enregistrement précédent.  Syntaxes :

BACKSPACE([unit=] u [,ERR = s] [,IOSTAT = ios])

BACKSPACE u

7.    L'instruction INQUIRE

L'instruction INQUIRE permet de récupérer des caractéristiques d'un fichier ou d'une unité. Elle admet trois formes :

inquire([unit=] u [,liste_proprietes])inquire([file=] nom_fichier [,liste_proprietes])inquire(iolength=len) liste_variables

Les propriétés pouvant être récupérées pour les deux premières formes sont les suivantes :

o    ACCESS = acc : acc désignera le mode d'accès du fichier ou de l'unité ('DIRECT', 'SEQUENTIAL' ou 'UNDEFINED' si la connection n'est pas établie)

o    ACTION = acc : acc vaudra 'READ', 'WRITE', 'READWRITE' ou 'UNDEFINED'.

o    BLANK = blnk : blnk vaudra 'NULL', 'ZERO' ou 'UNDEFINED'.

o    DELIM = del_char : del_char prendra la valeur 'QUOTE', 'APOSTROPHE', 'NONE' ou 'UNDEFINED'.

o    DIRECT = dir_char : dir_char prendra la valeur 'YES' si l'accès est direct, et 'NO' sinon.

o    ERR = errs : retourne l'étiquette renvoyant aux instructions d'erreur.

o    EXIST = val : retourne .true. ou .false. suivant que le fichier ou l'unité spécifiée existe ou non.

o    FORM = fm : retourne 'FORMATTED', 'UNFORMATTED' ou 'UNDEFINED'. o FORMATTED = fmt : retourne 'YES' ou 'NO' suivant que le fichier est formatté ou non. o IOSTAT = ios : retourne la valeur associée à une erreur de traitement du fichier.

o    NAME = nm : retourne le nom du fichier ou 'UNDEFINED' si le fichier ou l'unité n'a pas de nom.

o    NAMED = nmd : retourne 'YES ou 'NO' suivant que le fichier possède un nom ou pas. o NEXTREC = nr : retourne n+1, où n est le numéro du dernier enregistrement lu ou écrit d'un fichier à accès direct. Si le fichier n'est pas connecté ou si la position est inconnue suite à une erreur, nrdevient undéfini.

o    NUMBER = num : retourne le numéro de l'unité connectée. Si l'unité n'est pas connectée, num est indéfini.

o    OPENED = od : retourne .true. ou .false. suivant que le fichier est ouvert ou pas. o PAD = pad_char : retourne 'NO' si cette valeur a été spécifiée lors de l'ouverture de fichier et 'YES' sinon.

o    POSITION = pos_char : retourne 'APPEND', 'REWIND' ou 'UNDEFINED'.

o    READ = rl : retourne 'YES' si le fichier est ouvert en lecture, 'NO' sinon et 'UNKNOWN' si le système ne parvient pas à déterminer le type d'accès aux données du fichier. o READWRITE = rl : retourne 'YES' si le fichier est ouvert en lecture/écriture, 'NO' sinon et 'UNKNOWN' si le système ne parvient pas à déterminer le type d'accès aux données du fichier.

o    RECL = rl : retourne la longueur d'un enregistrement pour un fichier à accès direct et la valeur maximum d'un enregistrement pour un fichier à accès séquentiel.

o    SEQUENTIAL = seq : renvoit 'YES' si le mode d'accès du fichier est séquentiel, 'NO' sinon et 'UNKNOWN' si le système ne peut pas déterminer le mode d'accès.

o    UNFORMATTED = unf : renvoit 'YES', 'NO' ou

'UNKNOWN' suivant que le fichier est formatté, non formatté ou que le système ne parvient pas déterminer le formattage du fichier.

o    WRITE = rl : retourne 'YES 'si le fichier est ouvert en écriture, 'NO' sinon et 'UNKNOWN' si le système ne parvient pas à déterminer le type d'accès aux données du fichier.



162