2 :keywords: maillage, champ, MED, MEDMEM
3 :author: Guillaume Boulant
5 .. include:: medop-definitions.rst
7 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8 Note de travail concernant l'utilisation de MEDMEM
9 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11 Le module MED de SALOME comporte plusieurs composants d'intérêt pour
12 la manipulation de champs:
14 * la bibliothèque MEDMEM qui fournit une interface de programmation
15 pour manoeuvrer une structure MED
16 * le module CORBA SALOME_MED qui matérialise le composant SALOME
17 (serveur corba) du module MED
18 * l'interopérabilité avec le module VISU qui permet la visualisation
19 des champs manipulés dans MED
21 Les sections ci-après donnent quelques éclairages techniques sur ces
22 différents aspects. Les sources de démonstration peuvent être
23 récupérés depuis le dépôt svn::
25 $ svn export svn://nepal.der.edf.fr/OM/manifield/trunk manifield
26 $ svn export svn://nepal.der.edf.fr/FIELD/demofield/trunk demofield
28 .. contents:: Sommaire
32 Présentation synthétique de MED
33 ===============================
35 MED désigne un modèle conceptuel pour décrire des données de type
36 éléments finis (éléments finis, volumes finis et éléments
37 discrets). Dans l'usage courant, il permet la description et l'échange
38 des données de calcul de type maillages et champs. La documentation
39 complète peut être trouvée à l'URL suivantes:
41 * |LINK_EDF_MEDDOC|_ (version 2.3).
43 On distingue deux implémentations informatiques de ce modèle:
45 * MED fichier: qui permet la lecture et l'écriture de données depuis
46 un fichier au format med. Les opérations de lecture/écriture sont
47 atomiques (pas de chargement de la structure de données globale).
48 * MED mémoire (noté MEDMEM): qui permet le chargement en mémoire d'une
49 image de la structure de données MED contenue dans un fichier au
50 format med. Les opérations peuvent être atomiques ou
53 On notera simplement ici que MEDMEM utilise MED fichier pour les
54 opérations de lecture/écriture et que MED fichier est indépendant de
55 MED mémoire. La documentation complète de MED fichier peut être
56 trouvée à l'URL suivante:
58 * |LINK_EDF_MEDFICHIERDOC|_
60 La bibliothèque MEDMEM
61 ======================
63 Le modèle de classes MEDMEM est structuré autour des notions de MESH
64 (les maillages), de SUPPORT (le profil des entités) et de FIELD (les
65 champs). Ces notions reprennent en partie des concepts du modèle
66 MED. Le diagramme ci-dessous présente les classes principales:
68 .. image:: images/med-uml-main_60pc.png
71 Le conteneur de plus haut niveau dans MEDMEM est la classe MED. La
72 figure ci-dessous indique qu'une instance MED peut être associée à
73 plusieurs maillage et plusieurs champs. Par contre un champ donné ne
74 peut être associé qu'à un seul maillage (par l'intermédiaire du
75 support). Plusieurs champs peuvent être associés au même maillage. La
76 forme la plus courante est d'ailleurs une instance composé d'un
77 maillage unique sur lequel sont définis plusieurs champs.
79 On peut avoir également des configurations plus complexes, comme par
80 exemple un maillage unique, plusieurs champs définis sur ce maillage
81 mais avec des supports différents, par exemple parce que les valeurs
82 sont définies sur des entités de maillage différentes (les éléments
83 pour un champ, les noeuds pour un autre, ...)::
85 field1->support1->mesh
86 field2->support2->mesh
87 field3->support3->mesh
91 * 2 champs U et V doivent avoir le même support (au sens informatique
92 du terme) pour pouvoir être en argument d'une opération (sinon
93 exception). Il faudrait accepter qu'il soit informatiquement
94 différent et vérifier la conformité conceptuelle.
95 * Cette contrainte peut se comprendre car physiquement les données
96 sont stockées dans un vecteur qui couvre toutes les mailles. Le
97 support est le seul masque de lecture pour établir la correspondance
98 avec les positions dans le maillage et il est donc important qu'une
99 cohérence soit assurée.
101 Les objets champs (FIELD) et maillage (MESH)
102 --------------------------------------------
104 Un objet MED permet d'accéder aux différentes informations concernant
105 les objets MESH, SUPPORT et FIELD, mais il ne permet pas d'accéder aux
106 données physiques associées à ces objets (les valeurs des composantes
107 pour les champs, les mailles et leur connectivité pour les
108 maillages). L'accès aux données physiques est du ressort des objets
109 spécifiques MESH, SUPPORT et FIELD.
111 Un objet MED peut être créé intégralement en mémoire. L'usage plus
112 fréquent est de l'initialiser à partir de la donnée d'un fichier
113 med. Pour cela, l'objet MED doit être associé à un driver
114 d'entrée/sortie branché sur le fichier (``testfilename`` dans
119 MED *myMed = new MED;
120 MED_MED_RDONLY_DRIVER *driverIn = new MED_MED_RDONLY_DRIVER(testfilename, myMed);
122 driverIn->readFileStruct();
125 A l'occasion de la fonction readFileStruct, la structure interne de
126 l'objet MED est enrichie des informations concernant les objets MESH,
127 SUPPORT et FIELD contenu dans le fichier. En particulier un
128 dictionnaire des champs (variable map interne) est initialisé est
129 contient l'ensemble des objets ``FIELD_`` préchargés (i.e. avec les
130 méta-données uniquement). Chaque objet ``FIELD_`` ainsi préchargé est
131 autonome pour être chargé sur demande. On peut alors requêter l'objet
132 MED pour obtenir un champ particulier (spécifié par son nom
133 ``fieldname`` dans l'exemple):
137 FIELD<double> *field = (FIELD<double> *)myMed->getField(fieldname, dt, it);
139 Puis le champ qui lui est associé doit être physiquement chargé pour
140 permettre la mise à jour du support:
144 MESH * mesh = myMed->getMesh(field);
146 myMed->updateSupport();
148 Pour enfin charger les valeurs des composantes du champ:
154 La numérotation des éléments de maillage
155 ----------------------------------------
157 Les éléments qui composent un maillage sont caractérisés par:
159 * Le type d'entité de l'élément, à choisir dans la liste
160 ``MED_EN::medEntityMesh``, qui contient en particulier ``MED_NODE``,
161 ``MED_FACE``, ``MED_CELL``.
162 * Le type de géométrie de l'élément, à choisir dans la liste
163 ``MED_EN::medGeometryElement``, qui contient en particulier
164 ``MED_NONE``, ``MED_TRIA3``, ..., ``MED_ALL_ELEMENTS``.
166 Les éléments sont numérotés par un indice relatif à la catégorie
167 géométrique à laquelle ils appartiennent. Ainsi, si le modèle est
168 composé de Na arrêtes et Nf faces de type géométrique MED_QUAD4, alors
169 ces faces sont numérotées de 1 à Nf dans le modèle MED (et de manière
170 persistente dans le fichier med). De même, les arrêtes sont numérotées
171 de 1 à Na. Une numérotion globale implicite existe sur les éléments,
172 elle consiste à parcourir l'ensemble des types géométriques dans
173 l'ordre de définition du modèle de données. Ainsi, si le modèle
174 contient uniquement les Na arrêtes et les Nf faces, alors l'indice
175 global de la première face est Na+1.
177 .. note:: Des exemples de code sont disponibles dans le package ``demofield``, fichier ``python/pybasicfields/MEDMEM_tester.py``.
180 Binding python de MEDMEM
181 ------------------------
183 Les classes du package ``MEDMEM`` (package du module ``MED`` qui
184 implémentent les structures de données C++ de MED mémoire) produisent
185 la bibliothèque ``libmedmem.so``. Cette ensemble de classes est en
186 partie mis à disposition de l'interface python grace à une couche de
187 liaison (binding Python-C++) générée par le logiciel SWIG à partir
188 d'un fichier de description d'interface ``libMEDMEM_Swig.i`` (dans le
189 package source ``MEDMEM_SWIG``).
191 Ce fichier d'interface doit être mis à jour dés lors qu'une évolution
192 des interfaces publiques des classes C++ MEDMEM est faite ou qu'une
193 nouvelle classe est créée (du moins si l'on souhaite profiter de ces
194 évolutions dans l'interface python).
196 Cette mise à jour nécessite de prendre soin au transfert des
197 structures de données entre les espaces python et C++. En particulier,
198 l'utilisation des template de classe pour décrire les champs typés en
199 C++ appelle une précaution de codage particulière de l'interface
202 Pour exemple, le fragment de code ci-dessous, extrait du fichier
203 ``libMEDMEM_Swig.i``, montre comment déclarer la nouvelle classe
204 ``MedDataManager`` dans l'interface:
208 #include "MEDMEM_MedDataManager.hxx"
214 void printFieldDouble(FIELD<double,FullInterlace> * field);
217 MedDataManager(char * fileName)
219 return new MedDataManager(string(fileName));
221 MedDataManager(MED * med)
223 return new MedDataManager(med);
226 %newobject getFieldDouble(const char * fieldName, const int dt, const int it);
227 FIELD<double, FullInterlace> * getFieldDouble(const char * fieldName, const int dt, const int it)
229 return (FIELD<double, FullInterlace> *) self->getFieldDouble(string(fieldName), dt, it);
236 Utilisation de MEDMEM pour la manipulation de champs
237 ----------------------------------------------------
239 Des opérations de manipulation de champs sont disponibles dans la
240 bibliothèque MEDMEM standard est peuvent être utilisées dans
241 l'interface python. Les quelques lignes suivantes illustrent l'usage
242 qu'on peut en faire pour exécuter l'addition de deux champs sur tout
243 leur espace de définition et pour un pas de temps donné:
245 .. code-block:: python
247 from libMEDMEM_Swig import MedDataManager
248 from xmed.helper import readMed, writeMed
250 # Load the medmem data structure from a med file
251 med = readMed("/tmp/input.med")
252 # Then create a med data manager to deal with the fields data
253 dm = MedDataManager(med)
254 # Get the timestamps (dt,it)=(-1,-1) of the fields "testfield1" and "testfield2"
255 f1 = dm.getFieldDouble("testfield1",-1,-1)
256 f2 = dm.getFieldDouble("testfield2",-1,-1)
258 # Create a new field as the sum of f1 and f2
260 # And add this new field to the med data structure
263 # Finally, write the whole data in an output med file
264 writeMed(med,"/tmp/output.med")
266 .. note:: Cet exemple de code requiert les évolutions de MEDMEM
267 opérées dans la branche BR_medop (pour disposer de la classe
268 MedDataManager en particulier) et le package python ``xmed`` qui
269 fournit quelques fonctions utilitaires pour manoeuvrer les données
270 med (ce package est dans le module XMED et sera probablement à
271 terme intégré au module MED).
273 Des limitations existent aujourd'hui pour ce type de manipulations:
275 * les champs doivent partager le même support MED, c'est-à-dire être
276 décrit sur le même maillage et sur les mêmes entités de ce
281 Remarque sur l'implémentation C++
282 ---------------------------------
284 A noter l'usage de plusieurs formes d'arguments pour les fonctions:
286 * passage des arguments par valeur ``myfunction(A a);``
287 * passage des arguments par référence ``myfunction(A& a);``
288 * passage des arguments par pointeur ``myfunction(A* a);``
290 Le passage des arguments par référence est une facilité d'écriture
291 pour éviter de passer un pointeur tout en évitant la récopie des
292 données de la variable.
294 .. _xmed-medmem_corbainterface:
296 L'interface CORBA SALOME_MED
297 ============================
299 Implémentation du composant MED et des servants SALOME_MED::\*
300 --------------------------------------------------------------
302 Le composant MED est un servant CORBA qui permet la manipulation de
303 données MEDMEM dans l'environnement SALOME. Le composant peut fournir
304 des pointeurs vers des instances de l'interface SALOME_MED (objets
305 SALOMEMED::MED, SALOME_MED_FIELD, ...). Ces instances sont des
306 servants CORBA qui résident dans le container et qui encapsulent les
309 Le schéma ci-dessous représente les éléments informatiques qui
310 composent l'architecture CORBA du module MED:
312 .. image:: images/medmem-corba-layers.png
315 Les structures MEDMEM (données physiques) et SALOME_MED (wrapping
316 CORBA) fonctionnent différement en ce qui concerne le chargement des
319 * Dans MEDMEM, les données sont chargées à la demande (fonctions read
320 des objets) et aucune gestion n'est assurée. En particulier l'appel
321 à read alors que la donnée est déjà chargée conduit à une levée
322 d'exception. C'est à l'utilisateur de MEDMEM de prendre en charge ce
324 * Dans SALOME_MED, les données sont chargées à la création de
325 l'instance SALOME_MED::MED. Les maillages ainsi que les champs et
326 leurs données sont chargés à ce moment là et gérés dans une table de
327 type HashMap au niveau de la structure SALOME_MED::MED. Cette
328 structure remplie dés lors des fonction de gestion. L'appel à
329 SALOME_MED::MED.getField(...) ne charge pas les données mais renvoie
330 un pointeur SALOME_MED::FIELD_ptr sur les données chargées à
331 l'initialisation (ATTENTION, cette fonction est bugguée dans la
332 branche principale -> Fix dans la branche BR_medop).
334 Une gestion intermédiaire peut être envisagée: le chargement à la
335 demande géré dans une ou plusieurs tables de champs (une pour chaque
336 type de valeur numérique). Une implémentation de ce type de gestion
337 est illustré dans la classe ``MedDataManager`` du package MEDMEM qui prend
338 en charge ce comportement pour les structures de données MED (en
339 particulier les champs).
341 Utilisation du composant MED
342 ----------------------------
343 Le module SALOME MED fournit un module CORBA appelé SALOME_MED. Les
344 interfaces de ce module CORBA sont spécifiées par les fichiers idl
348 [http://nepal.der.edf.fr/pub/SALOME_userguide/MED5/doc/salome/tui/MED/MED_8idl.html
349 ``MED.idl``] qui décrit les interfaces des objets manipulés par le
350 module SALOME_MED. On trouve en particulier les objets MESH, SUPPORT
353 [http://nepal.der.edf.fr/pub/SALOME_userguide/MED5/doc/salome/tui/MED/MED__Gen_8idl.html
354 ``MED_Gen.idl``] qui décrit les interfaces du composant SALOME
355 (c'est-à-dire le composant chargé par la commande
356 ``FindOrLoadComponent("FactoryServer", "MED")`` du
357 lyfeCycleCorba). On trouve:
359 - l'interface ``MED_Gen_Driver`` qui hérite de SALOMEDS::Driver
360 pour l'implémentation des services généraux des composants SALOME
361 (persistance hdf, dump)
362 - l'interface ``MED_Gen`` qui hérite des interfaces
363 ``Engines::Component`` et ``MED_Gen_Driver`` pour
364 l'implémentation des services spécifiques du composant MED.
366 L'implémentation de ces interfaces est faites au niveau de différents
367 packages des sources du module MED:
369 * Le package ``MEDMEM_I`` qui fournit l'implémentation C++ des
370 interfaces décrites par le fichier ``MED.idl``;
371 * Le package ``MED`` qui fournit l'implémentation C++ des interfaces
372 décrites par le fichier ``MED_Gen.idl``, et qui correspond à la
373 partie composant classique d'un module SALOME.
374 * Le package ``MedCorba_Swig`` qui fournit une interface swig
375 générée à partir de l'implémentation C++ de ``MEDMEM_I`` et
378 L'utilisation peut être illustrée au moyen d'exemples python (i.e. qui
379 utilise l'interface swig fournie par MedCorba_Swig). Après l'import
380 d'amorce systématique:
382 .. code-block:: python
388 from libSALOME_Swig import *
390 On peut charger le composant SALOME MED:
392 .. code-block:: python
394 medComp=salome.lcc.FindOrLoadComponent("FactoryServer", "MED")
396 grâce auquel les services de chargement de la structure MED peuvent
397 être invoqués. Par exemple, les commandes suivantes chargent toute la
398 structure MED dans l'étude salome passée en argument:
400 .. code-block:: python
402 filePathName = "myfile.med"
403 medComp.readStructFileWithFieldType(filePathName,salome.myStudyName)
405 Ce deuxième exemple charge la structure MED mais ne place pas le résultat dans l'étude:
407 .. code-block:: python
409 filePathName = "myfile.med"
410 medObj = medComp.readStructFile(filePathName,salome.myStudyName)
412 On récupère à la place un objet de classe |LINK_EDF_SALOME_MED__MED|_
413 qui permet une utilisation assez semblable (mais différente on le
414 verra plus bas) à MEDMEM:
416 .. code-block:: python
418 fieldIdx = 1 # WRN maybe there is no field of idx=1
420 fieldName = medObj.getFieldNames()[fieldIdx]
421 dtitfield = medObj.getFieldIteration(fieldName,iterationIdx)
424 fieldObj = medObj.getField(fieldName,it,dt)
425 nbOfFields = medObj.getNumberOfFields()
426 fieldNames = medObj.getFieldNames()
428 mesh = fieldObj.getSupport().getMesh()
431 Observations en vrac:
433 * Un FIELD_i possède un champ de type ``MEDMEM::FIELD_`` qui représente
434 le champ informatique réel (objet MEDMEM).
435 * FIELD_i::fieldMap variable static de type map qui semble gérer
436 les différentes instances de FIELD_i (~pattern factory). Cette
437 map peut être requétée au moyen d'un indice de type long appelé
439 * Quand on crée un FIELD_i par le constructeur d'argument
440 ``MEDMEM::FIELD_``, le ``MEDMEM::FIELD_`` est ajouté dans la map avec
441 incrément du corbaIndex
442 * La fonction FIELD_i::read(i) redirige vers la fonction read(i) du
443 ``MEDMEM::FIELD_`` associé
444 * A CONFIRMER: Il semble que les fonctions de chargement
445 ``readStructFile*()`` charge toutes les données du fichier med,
446 alors qu'en MEDMEM seules les meta-données sont chargées.
447 * A CONFIRMER: il semble que le chargement d'une structure MED
448 CORBA peut se faire sans passer par le composant (cf. l'interface
451 Interface avec le module VISU
452 =============================
454 Des interactions sont possibles entre MED et VISU à partir du moment
455 où les données med sont gérées dans l'étude, c'est-à-dire sous la
456 forme d'objets SALOME_MED (voir ci-dessus) publiés dans l'étude. Les
457 deux conditions sont aujourd'hui nécessaires (objet corba + publié
458 dans l'étude) mais il semble que ce ne soit lié qu'à un choix
459 d'interface VISU (la fonction ``ImportMed`` en particulier) qui peut
460 a priori être modifié. A CONFIRMER.
462 L'exemple de code ci-dessous (en python, mais il peut être transposé à
463 une implémentation C++) montre par exemple comment envoyer au module
464 VISU une requête de visualisation d'un champs hébergé par le module
465 MED (en fait, les données sont gérées au travers d'un objet corba
466 SALOME_MED "délocalisé" et qui a été référencé dans l'étude dans la
467 catégorie du composant MED). Les importations standard (salome,
468 SALOME_MED, ...) sont supposées avoir été faites au préalable (voir
469 les exemples précédents):
471 .. code-block:: python
473 # Load the med structure using MED
474 medComp=salome.lcc.FindOrLoadComponent("FactoryServer", "MED")
475 filePathName = "myfile.med"
476 medComp.readStructFileWithFieldType(filePathName,salome.myStudyName)
478 # Get the VISU component
480 visuComp = salome.lcc.FindOrLoadComponent("FactoryServer", "VISU")
481 visuComp.SetCurrentStudy(salome.myStudy)
483 # Get the sobject associated to the med object named "Med"
484 aSObject = salome.myStudy.FindObject("Med")
485 isPresent, medSObj = aSObject.FindSubObject(1)
487 # Finally, import the med sobject in VISU
488 result = visuComp.ImportMed(medSObj)
490 Il est possible de d'aller plus loin et par exemple de déclencher
491 l'affichage d'une scalarmap d'un champ spécifique pour une itération
492 particulière (voir la fonction
493 ``TEST_SALOMEMED_requestToVisu_scalarmap`` du fichier
494 ``SALOMEMED_tester.py`` fourni dans les sources d'exemple).
496 Liens complémentaires:
498 * http://nepal.der.edf.fr/pub/SALOME_userguide/VISU_V5_1_3/doc/salome/gui/VISU La documentation utilisateur en ligne du module VISU
506 * Comment obtenir le nom du fichier med à partir d'une structure med?
507 * Peut-on imaginer un moyen de fournir l'objet MEDMEM::MED à partir de
508 la donnée de l'objet CORBA SALOME_MED::MED?
512 * A part, les opérations arithmétiques (+,-,*,/), aucune opération