Salome HOME
MERGE stage 2: update doc/dev then remove src/MEDCalc/doc
[modules/med.git] / src / MEDCalc / doc / sphinx / medop-prototype-medmem.rst
1 .. meta::
2    :keywords: maillage, champ, MED, MEDMEM
3    :author: Guillaume Boulant
4
5 .. include:: medcalc-definitions.rst
6
7 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8 Note de travail concernant l'utilisation de MEDMEM
9 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10
11 Le module MED de SALOME comporte plusieurs composants d'intérêt pour
12 la manipulation de champs:
13
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
20
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::
24
25  $ svn export svn://nepal.der.edf.fr/OM/manifield/trunk manifield
26  $ svn export svn://nepal.der.edf.fr/FIELD/demofield/trunk demofield
27
28 .. contents:: Sommaire
29    :local:
30    :backlinks: none
31
32 Présentation synthétique de MED
33 ===============================
34
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:
40
41 * |LINK_EDF_MEDDOC|_ (version 2.3).
42
43 On distingue deux implémentations informatiques de ce modèle:
44
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
51   globales.
52
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:
57
58 * |LINK_EDF_MEDFICHIERDOC|_
59
60 La bibliothèque MEDMEM
61 ======================
62
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:
67
68 .. image:: images/med-uml-main_60pc.png
69    :align: center
70
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.
78
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, ...)::
84
85   field1->support1->mesh
86   field2->support2->mesh
87   field3->support3->mesh
88
89 On observe:
90
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.
100
101 Les objets champs (FIELD) et maillage (MESH)
102 --------------------------------------------
103
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.
110
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
115 l'exemple):
116
117 .. code-block:: cpp
118
119    MED *myMed = new MED;
120    MED_MED_RDONLY_DRIVER *driverIn = new MED_MED_RDONLY_DRIVER(testfilename, myMed);
121    driverIn->open();
122    driverIn->readFileStruct();
123    driverIn->close();
124
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):
134
135 .. code-block:: cpp
136
137    FIELD<double> *field = (FIELD<double> *)myMed->getField(fieldname, dt, it);
138
139 Puis le champ qui lui est associé doit être physiquement chargé pour
140 permettre la mise à jour du support:
141
142 .. code-block:: cpp
143
144    MESH * mesh = myMed->getMesh(field);
145    mesh->read();
146    myMed->updateSupport();
147
148 Pour enfin charger les valeurs des composantes du champ:
149
150 .. code-block:: cpp
151
152    field->read();
153
154 La numérotation des éléments de maillage
155 ----------------------------------------
156
157 Les éléments qui composent un maillage sont caractérisés par:
158
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``.
165
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.
176
177 .. note:: Des exemples de code sont disponibles dans le package ``demofield``, fichier ``python/pybasicfields/MEDMEM_tester.py``.
178
179
180 Binding python de MEDMEM
181 ------------------------
182
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``).
190
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).
195
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
200 SWIG.
201
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:
205
206 .. code-block:: cpp
207
208    #include "MEDMEM_MedDataManager.hxx"
209
210    class MedDataManager
211    {
212      public:
213       ~MedDataManager();
214       void printFieldDouble(FIELD<double,FullInterlace> * field);
215
216       %extend {
217         MedDataManager(char * fileName)
218         {
219           return new MedDataManager(string(fileName));
220         }
221         MedDataManager(MED * med)
222         {
223           return new MedDataManager(med);
224         }
225
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)
228         {
229           return (FIELD<double, FullInterlace> *) self->getFieldDouble(string(fieldName), dt, it);
230         }
231       }
232
233    };
234
235
236 Utilisation de MEDMEM pour la manipulation de champs
237 ----------------------------------------------------
238
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é:
244
245 .. code-block:: python
246
247     from libMEDMEM_Swig import MedDataManager
248     from xmed.helper import readMed, writeMed
249
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)
257
258     # Create a new field as the sum of f1 and f2
259     r  = f1 + f2
260     # And add this new field to the med data structure
261     med.addField(r)
262
263     # Finally, write the whole data in an output med file
264     writeMed(med,"/tmp/output.med")
265
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).
272
273 Des limitations existent aujourd'hui pour ce type de manipulations:
274
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
277   maillage.
278 * ...
279
280
281 Remarque sur l'implémentation C++
282 ---------------------------------
283
284 A noter l'usage de plusieurs formes d'arguments pour les fonctions:
285
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);``
289
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.
293
294 .. _xmed-medmem_corbainterface:
295
296 L'interface CORBA SALOME_MED
297 ============================
298
299 Implémentation du composant MED et des servants SALOME_MED::\*
300 --------------------------------------------------------------
301
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
307 données MEDMEM.
308
309 Le schéma ci-dessous représente les éléments informatiques qui
310 composent l'architecture CORBA du module MED:
311
312 .. image:: images/medmem-corba-layers.png
313    :align: center
314
315 Les structures MEDMEM (données physiques) et SALOME_MED (wrapping
316 CORBA) fonctionnent différement en ce qui concerne le chargement des
317 données:
318
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
323   type de gestion.
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).
333
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).
340
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
345 suivants:
346
347 * le fichier
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
351   et FIELD.
352 * le fichier
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:
358
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.
365
366 L'implémentation de ces interfaces est faites au niveau de différents
367 packages des sources du module MED:
368
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
376   ``MED``
377
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:
381
382 .. code-block:: python
383
384    import salome
385    salome.salome_init()
386
387    import SALOME_MED
388    from libSALOME_Swig import *
389
390 On peut charger le composant SALOME MED:
391
392 .. code-block:: python
393
394    medComp=salome.lcc.FindOrLoadComponent("FactoryServer", "MED")
395
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:
399
400 .. code-block:: python
401
402    filePathName = "myfile.med"
403    medComp.readStructFileWithFieldType(filePathName,salome.myStudyName)
404
405 Ce deuxième exemple charge la structure MED mais ne place pas le résultat dans l'étude:
406
407 .. code-block:: python
408
409    filePathName = "myfile.med"
410    medObj = medComp.readStructFile(filePathName,salome.myStudyName)
411
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:
415
416 .. code-block:: python
417
418    fieldIdx     = 1 # WRN maybe there is no field of idx=1
419    iterationIdx = 0
420    fieldName = medObj.getFieldNames()[fieldIdx]
421    dtitfield = medObj.getFieldIteration(fieldName,iterationIdx)
422    it = dtitfield[0]
423    dt = dtitfield[1]
424    fieldObj = medObj.getField(fieldName,it,dt)
425    nbOfFields = medObj.getNumberOfFields()
426    fieldNames = medObj.getFieldNames()
427
428    mesh = fieldObj.getSupport().getMesh()
429
430 .. note::
431    Observations en vrac:
432
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é
438      corbaIndex.
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
449      de MED)
450
451 Interface avec le module VISU
452 =============================
453
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.
461
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):
470
471 .. code-block:: python
472
473    # Load the med structure using MED
474    medComp=salome.lcc.FindOrLoadComponent("FactoryServer", "MED")
475    filePathName = "myfile.med"
476    medComp.readStructFileWithFieldType(filePathName,salome.myStudyName)
477
478    # Get the VISU component
479    import VISU
480    visuComp = salome.lcc.FindOrLoadComponent("FactoryServer", "VISU")
481    visuComp.SetCurrentStudy(salome.myStudy)
482
483    # Get the sobject associated to the med object named "Med"
484    aSObject = salome.myStudy.FindObject("Med")
485    isPresent, medSObj = aSObject.FindSubObject(1)
486
487    # Finally, import the med sobject in VISU
488    result = visuComp.ImportMed(medSObj)
489
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).
495
496 Liens complémentaires:
497
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
499
500
501 Notes en vrac
502 =============
503
504 Questions:
505
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?
509
510 Remarques:
511
512 * A part, les opérations arithmétiques (+,-,*,/), aucune opération
513   n'est définie.