Accueil - Informatique - Programmation - Langages

Matlab et NetCDF (2)

Publié le mardi 17 mars 2009.


Dernière mise à jour : 12/01/2010


Descriptif

Le paquetage us191.netcdf facilite et simplifie l’écriture de fichier NetCDF sous Matlab. Utilisant la gestion native des fichiers NetCDF disponible depuis la dernière version R2008b de Matlab, il permet par une méthode originale, de générer simplement des fichiers NetCDF sans avoir à ouvrir le "capot" ni mettre ses mains dans la mécanique interne de ces fichiers.

Le portage vers des versions plus anciennes de Matlab (R2007) sera réalisé dès que les principales fonctionnalités de cette boite à outils seront figées et suffisamment testées. Cela est déjà le cas pour us191.hashtable et us191.dynaload. Reste encore à écrire le code pour convertir us191.netcdf.


Préambule

NetCDF est une bibliothèque permettant de créer, manipuler et diffuser des données scientifiques avec un format auto-descriptif. Écrite à l’origine par Charles R. Denham, la boite à outils NetCDF pour Matlab permet de manipuler simplement et efficacement des fichiers de données au format NetCDF à l’aide des opérateurs Matlab. Malheureusement, cette boite à outils n’est plus maintenue par son successeur John Evans, qui propose sa propre boite à outils SNCTOOLS.

Heureusement, depuis la version R2008b, Matlab offre une gestion native des fichiers NetCDF avec des fonctions proches de la bibliothèques C et Fortran.
Si il facilite l’utilisation de la bibliothèque NetCDF sous Matlab, le jeu de fonctions implémentées par SNCTOOLS présente toutefois quelques inconvénient qui m’ont pousser à développer mes propres fonctions de lecture/écriture de fichiers NetCDF..


Historique

Jusqu’à la version R2008a, pour lire ou écrire des fichiers NetCDF sous Matlab, il fallait installer une version spécifique suivant son système d’exploitation de mexnc, puis suivant ses préférences, soit la boite à outils NetCDF, soit SNCTOOLS .
Depuis la version R2008b, Matlab utilise nativement NetCDF au travers d’une librairie, netcdflib, installée sous $MATLABROOT/toolbox/matlab/imagesci/+netcdf/private/

Malheureusement, le paquetage NetCDF natif de Matlab n’est qu’une compilation d’un mex file appelant la librairie C netdcf (.dll ou .so). Utiliser tel quel les fonctions de ce paquetage devient rapidement une opération fastidieuse, répétitive et source d’erreurs.

En effet, les fonctions Matlab NetCDF ne travaillent qu’avec des identifiants numériques. L’utilisateur va rapidement se retrouver avec un code obscure, codé en dur, et qu’il devra modifier lorsque des changement (ajout/suppression) de dimensions ou de variables seront nécessaires.

Mon premier réflexe en utilisant Matlab R2008b fut d’installer la dernière version de SNCTOOLS (2.9.4), et de lire la documentation conseillant fortement d’utiliser SNCTOOLS à la place de l’ancienne toolbox de Charles R. Denham qui n’est plus maintenue.

Si la doc est claire et les premiers exemples faciles à mettre en œuvre, je suis quelque peu réservé sur son utilisation pour remplacer la toolbox de Denham en ce qui concerne les points suivants :


Rappel

NetCDF Users Guide

Description de NetCDF, voir lien sur Wikipédia :
http://fr.wikipedia.org/wiki/NetCDF


Convention d’écriture des matrices

Matlab utilise la convention Fortran d’écriture des matrices, il faut donc être vigilant lorsque les fichiers générés doivent être lus par des applications tierces.
La documentation de l’API native n’est pas très explicite sur le sujet, voir l’aide sur la fonction netcdf.defvar

Si vous avez l’habitude d’écrire vos matrices 2D sous la forme ligne x colonne, il est nécessaire de les permuter avant écriture car Matlab stocke les matrices en mémoire suivant l’indexation linéaire et non comme ils sont représentés dans l’espace de travail. Et c’est suivant cette représentation en mémoire que les tableaux seront stockés dans le fichier par l’API NetCDF de Matlab.
Voir le lien sur Linear indexing

Si pour des matrices 2D, une simple transposition et une inversion de l’ordre des dimensions suffit à la création de la variable NetCDF, cette méthode ne fonctionne pas avec des matrices à N-D dimensions.

Dans ce cas, il faudra effectuer les conversions suivantes :

% ecriture
dim1 = 5;
dim2 = 4;

% creation du fichier
ncid = netcdf.create('test.nc', 'nc_write');

%  variable pour l'exemple
var =reshape(1:20, [dim1 dim2])

% creation des dimensions et de la variable Netcdf
dimid1 = netcdf.defDim(ncid, 'dim1', dim1);
dimid2 = netcdf.defDim(ncid, 'dim2', dim2);
varid = netcdf.defVar(ncid, 'var','int', fliplr([dimid1 dimid2]));
netcdf.endDef(ncid);

% permutation de la matrice pour l'ecriture
netcdf.putVar(ncid, varid, permute(var, fliplr(1:ndims(var))));

% lecture de la variable NetCDF
[varname xtype dimid natts ] = netcdf.inqVar(ncid, varid);
var = netcdf.getVar(ncid, varid);

% permutation de la matrice pour la lecture
var = permute(var, fliplr(1:length(dimid)))
netcdf.close(ncid);

Avec les vecteurs, qu’ils soient en ligne (1,N) ou en colonne (N,1), Matlab les représentes toujours en mémoire sous la forme N,1, et c’est sous cette forme qu’ils seront enregistrés dans le fichier NetCDF. C’est sous cette forme qu’ils seront lus et renvoyer par la fonction <codevarget. On ne fera pas de transformation à la lecture car il n’y a aucun moyen de savoir si le vecteur d’origine était en 1,N (ligne) ou (N,1) colonne et ceci a peu d’importance car une recherche par indice renverra toujours la même valeur.

Par contre, il n’en va pas de même avec chaines de caractères que l’on représente habituellement sous la forme de vecteur 1,N. La chaine sera transformée en vecteur N,1 à l’écriture, vecteur qu’il faudra transformer à la lecture avec l’instruction suivante :

if xtype == netcdf.getConstant('NC_CHAR') && (size(var', 1) == 1
 var = var';
elseif length(dimid) > 1
 % permutation de la matrice pour la lecture
 var = permute(var, fliplr(1:length(dimid)));
end

...


Principe

Le paquetage us191.netcdf hérite de la classe us191.dynaload. Cette classe permet de lire, parcourir et modifier des structures de données complexes (hash de hash) :

On accèdera au contenu d’un attribut très simplement comme suit :

>> lg = nc.VARIABLES.DATE.long_name;

Et on modifiera tout aussi simplement sa valeur comme suit :

>> nc.VARIABLES.DATE.long_name = 'Température en degrés Celcius °C';

Par rapport à une structure dynaload classique, la classe us191.netcdf utilise 4 variables spécifiques dans sa structure interne (variables suffixées avec un double underscore qui sont) :

key__ la clé contenant le nom ou code de la variable
dimension__ cellule contenant la liste des dimensions d’une variable
type__ définit le type NetCDF de la variable (char, byte, float, etc)
data__ variable qui contiendra les données, vecteur ou matrice.

Pour plus de détail sur la façon de créer ces fichiers, voir l’article précédent sur la classe dynaload.

La structure des données NetCDF peut être lue depuis un fichier "descripteur" csv ou générée en dur dans le programme Matlab.
Pour assurer la compatibilité avec les toolbox précédentes, le paquetage us191 implémente les très utiles fonctions ncload et ncdump.

Exemple d’utilisation :


Installation

Le code de ces classes est disponible sous subversion :
+us191

Il suffit que le répertoire +us191 (paquetage ou espace de nom Matlab) soit copié dans un répertoire qui est dans le PATH Matlab (variable MATLABPATH ou pathtool).

Pour obtenir de l’aide :

>> help us191.netcdf
>> help us191.dynaload

Utilisé sans argument, la classe invoque uigetfile pour choisir un fichier netcdf à charger en mémoire.
ex :

>> nc = us191.netcdf

Utilisé avec un argument, celui ci peut être soit un fichier NetcDF, soit un fichier de type dynaload (Excel ou CSV). Prenons le cas d’un fichier NetCDF chargé en mémoire, voyons plus en détail comment l’instance est organisée :

Nous retrouvons dans notre instance us191.netcdf notre objet us191.dynaload (par héritage) qui est un tableau associatif particulier contenant les 3 éléments accessibles par leurs clés DIMENSIONS, VARIABLES et ATTRIBUTES.

Chacun des ces éléments contenant de nouveau un tableau associatif avec les données contenues dans le fichier NetCDF lu.

En surchargeant les opérateurs subsref et subsasgn, Matlab nous permet d’accéder ou de modifier les membres de la structure contenue dans un des tableaux associatifs, comme par exemple nc.VARIABLES.DATE

On retrouve bien les variables spéciales utilisées par us191.netcdf pour définir nos variables NetCDF (eg : data__, type__ et dimension__). On remarquera également que la variable spéciale key__ n’est plus dans la structure mais est devenue une des clés de notre tableau associatif VARIABLES (DATE dans cet exemple).

Voyons maintenant comment modifier une variable ou un attribut de variable :

Nous allons maintenant créer une instance us191.netcdf à partir d’un fichier CSV, ajouter ou créer les données de notre variables DATE puis sauvegarder cela dans un fichier NetCDF :

Le fichier CSV est un fichier ASCII contenant la description de notre fichier NetCDF telle que nous l’avons conçue. Il a été créé avec la classe us191.dynaload à partir d’un fichier Excel puis sauvegardé en CSV, voir l’article précédent sur Dynaload.

Remarque : Seul les membres non vides de la structure deviendront des attributs de la variable.

Il est a noté que pour l’instant, la classe us191.netcdf ne permet pas de transformer un fichier Excel en CSV mais à priori, cela ne devrait pas poser de problème.

Dans la structure DATE, le membre spécial data__ n’existe pas, il est du ressort de l’application de créer cette variable puis de l’initialiser lors de la lecture du fichier contenant vos données. C’est la seule chose que l’utilisateur devra coder dans son programme avant la sauvegarde du fichier NetCDF.

Remarque : Dans le fichier Excel décrivant la structure du fichier NetCDF que l’on veut créer, certaines dimensions ne sont pas forcément définies, puisque le fichier de données n’est pas encore lu à cette étape. Il est possible, avec le membre ulimited, qui est de type booleen, de définir si une des variables sera de type unimited ou non.

Important : Si la dimension n’est pas connue et n’est pas définie comme ulimited, us191.netcdf assume par défaut qu’il existe une variable ayant le même nom qu’une de ses dimensions.

dimensions:
        DAYD = 10 ;
       ...

variables:
        char DATE(DAYD,STRING14), shape = [10 x 14] ;
       ...
        double DAYD(DAYD), shape = [10] ;
       ...

La création du fichier NetCDF est réalisée avec la commande write, prenant comme arguments, le nom du fichier NetCDF et le mode d’écriture, ’NC_CLOBBER’ dans ce cas. CLOBBER voulant dire écrabouiller ou écraser. On pourra utiliser le mode "NC_WRITE" pour mettre à jour un fichier existant.


Exemples

Sous le paquetage +us191, vous trouverez un répertoire demos/netcdf qui contient quelques scripts Matlab et fichiers csv qui vous permettront de tester l’utilisation de cette toolbox pour générer des fichiers NetCDF.


Liens utiles