Salome HOME
Merge 'master' branch into 'V9_dev' branch.
[modules/med.git] / doc / dev / sphinx / fr / medop-prototype-develguide.rst
1 .. meta::
2    :keywords: maillage, champ, manipulation, XMED
3    :author: Guillaume Boulant
4
5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6 Démonstrateur XMED, documentation technique
7 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8
9 Cette note fait la synthèse des développements effectués pour le
10 maquettage des fonctions de manipulation de champs dans SALOME. Elle
11 présente les principes retenus en matière de conception, c'est-à-dire
12 concernant les mécanismes techniques sous-jacents, et en matière
13 d'ergonomie, c'est-à-dire concernant les modalités d'utilisation dans
14 l'environnement SALOME.
15
16 Ces principes sont illustrés par des développements implantés dans le
17 module XMED, développé pour les besoins de l'analyse, et dans le
18 module MED distribué avec la plateforme SALOME.
19
20 .. note:: la lecture de ce chapitre demande une connaissance de la
21    structure de classes du module MED, en particulier la distinction
22    entre les classes ``MEDMEM::*`` et les servants CORBA associés
23    (classe ``SALOME_MED::*``).
24
25 .. contents:: Sommaire
26    :local:
27    :backlinks: none
28
29 Principes directeurs
30 ====================
31
32 Objectif et motivation
33 ----------------------
34
35 L'objectif de maquettage est de trouver une architecture technique qui
36 permet d'exécuter le cas d'utilisation suivant:
37
38 * Chargement d'un fichier med dans SALOME (a priori dans le module MED)
39 * Sélection graphique des champs de l'étude à mettre à disposition
40   dans la console utilisateur ("calculette" en mode texte qui
41   concraitement correspond à l'interface python de SALOME).
42 * Dans la calculette, exécution d'opérations algébriques (+,-,*,/)
43   entre champs avec possibilité d'utiliser des scalaires dans des
44   opérations de type transformation linéaire (y=ax+b ou y et x sont
45   des champs et a et b des scalaires). Opérations pow, sqrt.
46 * Possibilité de visualiser les champs produits avec VISU pour
47   contrôle des résultats.
48 * Possibilité d'exporter des champs produits dans un fichier med.
49
50 Eléments de contexte
51 --------------------
52
53 Les opérations de manipulation de champs sont en grande partie
54 implémentées dans la bibliothèque MEDMEM. Pour illustration, le
55 fragment de code ci-dessous montre comment une addition de champ peut
56 être opérée en python:
57
58 .. code-block:: python
59
60     from libMEDMEM_Swig import MedDataManager
61     from xmed.helper import readMed, writeMed
62
63     # Load the medmem data structure from a med file
64     med = readMed("/tmp/input.med")
65     # Then create a med data manager to deal with the fields data
66     dm  = MedDataManager(med)
67     # Get the timestamps (dt,it)=(-1,-1) of the fields "testfield1" and "testfield2"
68     f1 = dm.getFieldDouble("testfield1",-1,-1)
69     f2 = dm.getFieldDouble("testfield2",-1,-1)
70
71     # Create a new field as the sum of f1 and f2
72     r  = f1 + f2
73     # And add this new field to the med data structure
74     med.addField(r)
75
76     # Finally, write the whole data in an output med file
77     writeMed(med,"/tmp/output.med")
78
79 Ceci montre que les champs peuvent être manipulés avec une interface
80 relativement ergonomique (une addition de deux champs f1 et f2 s'écrit
81 f1+f2) tant que l'on manoeuvre des objets MEDMEM purs (classes C++ du
82 package MEDMEM et wrapping python du package MEDMEM_SWIG).
83
84 Par ailleurs, le fonctionnement actuel des modules SALOME qui
85 manoeuvrent des données MED est d'instancier les structures de données
86 MEDMEM au niveau de la partie serveur, c'est-à-dire au niveau des
87 servants CORBA hébergés dans le processus ``SALOME_Container``, et de
88 donner accés à ces données depuis l'étude SALOME au travers de
89 pointeurs CORBA. Ce choix d'architecture présente l'avantage de
90 centraliser au niveau serveur la gestion du cycle de vie des données
91 informatiques et de pouvoir distribuer des "poignées" pour manipuler
92 ces données depuis chaque point de l'application qui sait accéder au
93 bus CORBA, l'interface graphique en particulier.
94
95
96 Hypothèse de travail
97 --------------------
98
99 Compte-tenu de l'objectif de maquettage et des éléments de contexte
100 existant, on cherche une solution dans le cadre des hypothèses
101 de travail suivantes:
102
103 * La manipulation des champs se fait dans l'environement graphique de
104   SALOME.
105 * Dans cet environnement, on souhaite pouvoir sélectionner
106   graphiquement les champs à considérer, puis manipuler ces champs
107   dans l'interface texte au moyen de variables python avec une syntaxe
108   aussi simple que celle définie dans le wrapping python de MEDMEM,
109   c'est-à-dire que pour faire l'addition de 2 champs f1 et f2, on veut
110   pouvoir écrire f1+f2.
111 * Les données MED sont physiquement dans la partie serveur de SALOME
112   et accessibles via des pointeurs CORBA (interface spécifiée dans
113   MED.idl). On exclu la recopie de données au niveau du client
114   graphique.
115
116 Dans le cadre de ces hypothèses, la difficulté technique réside dans
117 la mise au point d'une interface de communication entre des variables
118 manipulées par l'utilisateur dans l'interface graphique (c'est-à-dire
119 dans le processus ``SALOME_SessionServer``) et des objets MEDMEM
120 instanciés dans le containeur des servants CORBA (c'est-à-dire dans le
121 processus ``SALOME_Container``).
122
123
124 Eléments de conception
125 ======================
126
127
128 Implantation technique
129 ----------------------
130
131 Le diagramme ci-dessous représente l'organisation des principaux
132 paquets logiciels du module MED:
133
134 .. image:: ../images/medmem-layers.png
135    :align: center
136
137 Les cadres bleus représentent le lieu d'implantation des
138 développements effectués dans le module MED pour les besoins du
139 maquettage. On notera en particulier les interventions aux niveaux
140 suivants:
141
142 * interfaces idl: ajout de l'interface MEDOP.idl
143 * package MEDMEM_I: ajout du servant SALOME_MED::MEDOP qui implémente
144   l'interface MEDOP.idl
145
146 Architecture technique
147 ----------------------
148
149 Les schéma ci-dessous représente les objets informatiques qui sont à
150 l'oeuvre pour la réalisation des opérations sur les champs:
151
152 .. image:: ../images/xmed-architecture.png
153    :align: center
154    :alt: Objets mis en oeuvre dans l'interface de manipulation de champs
155
156 On distingue les objets suivants:
157
158 * Une instance de ``MEDMEM::MED``, correspondant à la structure de donnée
159   MED chargée en mémoire.
160 * Des instances de ``MEDMEM::FIELD`` qui représentent les champs med
161   chargés en mémoire.
162 * Une instances de ``SALOME_MED::MED`` et des instances de
163   ``SALOME_MED::FIELD`` qui sont les servants CORBA respectivement de la
164   structure med et des champs qui lui sont associés et chargés en
165   mémoire.
166 * Une instance de ``SALOME_MED::MEDOP`` qui est le servant CORBA qui
167   centralise la mise en oeuvre des opérations de champs sur le serveur
168   ``SALOME_Container``. Le servant MEDOP détient en attribut une référence
169   sur la structure ``MEDMEM::MED``, ce qui lui permet d'accéder
170   directement aux champs ``MEDMEM::FIELD`` à partir de leur nom et du pas
171   de temps.
172 * Des instances de ``FieldProxy`` qui correspondent aux variables
173   manipulées au niveau de l'interface graphique et qui représentent
174   les champs. Une instance de FieldProxy possède détient les
175   références des servants ``SALOME_MED::MEDOP`` et
176   ``SALOME_MED::FIELD`` sous la forme de pointeurs CORBA de noms
177   ``medop_ptr`` et ``field_ptr`` respectivement.
178 * Il existe également une instance de ``MedProxy`` non représentée
179   dans ce diagramme. Cette instance correspond à une variable qui
180   permet de manipuler la structure med.
181
182 .. note:: Les éléments apportés par la maquette sont les classes
183    ``SALOME_MED::MEDOP``, ``MedProxy`` et ``FieldProxy``. Les autres
184    éléments ont pu être modifiés légèrement pour les besoins de
185    l'intégration ou pour la correction de quelques bugs.
186
187 Le cycle de vie de ces objets est le suivant.
188
189 Pour ce qui concerne les instances de la structure ``MEDMEM::MED`` et
190 des champs ``MEDMEM::FIELD``, la création est faite au moment du
191 chargement du fichier med dans SALOME au moyen du module MED. A cette
192 occasion, les servants CORBA associés ``SALOME_MED::MED`` et
193 ``SALOME_MED::FIELD`` sont créés et des références vers ces servants
194 sont publiés dans l'étude. Ils peuvent donc être sélectionnés par
195 l'utilisateur dans l'interface graphique. L'ensemble de ces données
196 préexiste à la manipulation de champs.
197
198 Les objets ``SALOME_MED::MEDOP`` sont instanciés au sein du servant
199 ``SALOME_MED::MED`` auquel ils sont associées. Le servant
200 ``SALOME_MED::MED`` possède une référence sur la structure
201 ``MEDMEM::MED`` et il la transmet à l'instance du servant
202 ``SALOME_MED::MEDOP`` qu'il construit. L'opérateur MEDOP est donc
203 autonome par la suite pour manipuler les données MED, et les champs en
204 particulier. Le code python ci-dessous montre comment un opérateur med
205 ``SALOME_MED::MEDOP`` peut être créé puis utilisé pour réaliser
206 l'addition de deux champs:
207
208 .. code-block:: python
209
210    import salome
211    salome.salome_init()
212    import SALOME_MED
213
214    medComp = salome.lcc.FindOrLoadComponent("FactoryServer", "MED")
215    medObj  = medComp.readStructFile("myfile.med",salome.myStudyName)
216    medOp   = medObj.createMedOperator()
217
218    f1 = medObj.getField("testfield1",-1,-1)
219    f2 = medObj.getField("testfield2",-1,-1)
220
221    somme = medOp.add(f1,f2)
222
223 Il est à noter qu'une instance de ``SALOME_MED::MEDOP`` est associé à
224 une instance unique de ``SALOME_MED::MED`` (et donc indirectement de
225 ``MEDMED::MED``) pour toute la durée de son cycle de vie. Par contre,
226 un servant ``SALOME_MED::MED`` peut être associé à plusieurs servants
227 ``SALOME_MED::MEDOP`` différents. Un servant ``SALOME_MED::MEDOP`` a
228 une référence directe sur la structure ``MEDMEM::MED`` et peut la
229 manoeuvrer pour demander des champs, faire des opérations avec ces
230 champs, ajouter le champs résultat à la structure et enfin retourner
231 un servant ``SALOME_MED::FIELD`` qui encapsule le champ résultat.
232
233 Enfin, quelques éléments concernant la classe ``FieldProxy``. Une
234 instance de ``FieldProxy`` est un objet python qui peut être
235 manoeuvrée dans l'interpréteur SALOME et qui référence un champ MED
236 localisé sur le serveur ``SALOME_Container`` (par le mécanisme décrit
237 ci-dessus). C'est à ce niveau qu'on règle les détails d'ergonomie
238 d'usage (cf. paragraphe ci-après). La création d'un objet
239 ``FieldProxy`` déclenche la création d'un opérateur med (instance de
240 ``SALOME_MED::MEDOP``) qui lui est associé et dont il conserve la
241 référence CORBA en attribut (noté ``medop_ptr`` sur le diagramme). Cet
242 opérateur ``medop_ptr`` peut être requêter pour exécuter toutes les
243 opérations possibles sur ce champ, comme illustrer sur l'exemple
244 ci-dessus.
245
246
247 Rôle des objets proxy
248 ---------------------
249
250 Dans le modèle d'architecture présenté ci-dessus, on introduit deux
251 types d'objets proxy:
252
253 * Les objets de classe ``FieldProxy`` qui représentent des poignées de
254   manipulation des champs ``MEDMEM::FIELD`` physiquement instanciés
255   dans le container SALOME.
256 * Les objets de classe ``MedProxy`` qui représentent des poignées de
257   manipulation des structures ``MEDMEM::MED`` physiquement instanciées
258   dans le container SALOME.
259
260 Elles sont instanciées dans l'interpréteur python SALOME pour
261 manipulation dans l'interface textuelle à partir de la donnée du
262 pointeur vers le servant ``SALOME_MED::MED`` et de l'identifiant du
263 champ (le nom du champ et le pas de temps défini par le numéro d'ordre
264 et le numéro d'iteration:
265
266 .. code-block:: python
267
268    import salome
269    salome.salome_init()
270    import SALOME_MED
271
272    medComp = salome.lcc.FindOrLoadComponent("FactoryServer", "MED")
273    medObj  = medComp.readStructFile("myfile.med",salome.myStudyName)
274
275    from xmed import fieldproxy
276    from xmed import medproxy
277
278    f1 = fieldproxy.getFieldFromMed(medObj, "testfield1", -1, -1)
279    f2 = fieldproxy.getFieldFromMed(medObj, "testfield2", -1, -1)
280
281    field_somme  = f1 + f2
282    field_offset = f1 + 5.3
283
284 Dans cet exemple, les variables ``f1``, ``f2``, ``field_somme`` et
285 ``field_offset`` sont des objets de classe ``FieldProxy``. Ils
286 correspondent aux variables physiquement manipulées par
287 l'utilisateur pour désigner les champs dans les opérations.
288
289 Ces classes proxy sont conçues pour être le lieu d'implémentation de
290 l'interprétation des commandes utilisateur et donc de l'ergonomie
291 de manipulation des champs au niveau l'interface textuelle. Ce point
292 est développé :ref:`plus bas <develguide_execFieldOperation-fr>`.
293
294 Programmation de l'interface textuelle
295 --------------------------------------
296
297 Dans le cadre de la maquette, l'interface de manipulation des champs
298 est l'interface textuelle python intégrée à SALOME. Dans la pratique,
299 l'utilisateur manipule des variables python qui correspondent à des
300 objets de classe ``FieldProxy`` équipées des fonctions requises et de
301 l'ergonomie nécessaire à la mise en oeuvre des opérations (voir
302 ci-dessus).
303
304 Or, l'hypothèse de travail est que les données MED sont chargées dans
305 SALOME et publiées dans l'étude pour point d'accés depuis l'interface
306 graphique. L'utilisateur choisi un champs directement dans l'arbre
307 d'étude (ou dans une interface graphique dédiée) puis demande qu'il
308 soit mis à disposition dans l'interface python sous un nom de variable
309 à choisir. Les captures d'écran ci-dessous montre la séquence
310 graphique en images:
311
312 .. |IMG_SELECT| image:: ../images/medop-gui-selectfield_scale.png
313 .. |IMG_ALIAS| image:: ../images/medop-gui-aliasfield_scale.png
314
315 +--------------+-------------+
316 | |IMG_SELECT| | |IMG_ALIAS| |
317 +--------------+-------------+
318
319 L'image de gauche montre la sélection du pas de temps, l'image de
320 droite la boîte de dialogue qui permet la saisie de l'alias avec
321 lequel le champs sera manipulé dans l'interface textuelle. La
322 validation de cette fenêtre doit mettre automatiquement le champ à
323 disposition dans l'interface python SALOME et sous le nom de variable
324 spécifié par l'alias saisi.
325
326 Pour cela, il y a un couplage technique à programmer entre l'interface
327 graphique et l'interface textuelle python, avec en particulier la
328 transmission des pointeurs vers les servants CORBA mis en jeu dans la
329 sélection.
330
331 Ce couplage est implanté au niveau de la classe MEDGUI.cxx du module
332 MED (où de la classe XMEDGUI.cxx du module XMED pour la maquette) qui
333 implémente l'interface graphique du module. Pour rappel, l'interface
334 graphique d'un module SALOME se présente sous la forme d'une classe
335 centrale de nom ``<MODULE_NAME>GUI`` et qui spécialise la classe
336 ``SalomeApp_Module``. Cette classe possède une méthode ``getApp()``
337 par laquelle on peut récupérer une instance de la console python
338 embarquée (this->getApp()->pythonConsole()).
339
340 Le code suivant illustre l'envoie d'une commande python par ce
341 mécanisme. Dans cet example, on cherche à reconstituer dans le
342 contexte de la console python un pointer vers un objet med instancié
343 dans le contexte C++ de l'application graphique. Pour cela, on
344 communique la référence de l'objet sous la forme sérialisé (IOR pour
345 un objet CORBA):
346
347 .. code-block:: cpp
348
349    #include <PyConsole_Console.h>
350    #include <QString>
351    #include <QStringList>
352    #include <SalomeApp_Application.h>
353
354    // We suppose here that we have a CORBA object reference (object of
355    // type *_ptr or *_var), for example a SALOME_MED::MED object.
356    SALOME_MED::MED_ptr medObj = ... // anything to get this object
357
358    // Get the IOR of this object
359    QString medIOR = SalomeApp_Application::orb()->object_to_string(medObj);
360
361    PyConsole_Console * pyConsole = getApp()->pythonConsole();
362
363    QStringList commands;
364    commands+="import salome";
365    commands+=QString("med=salome.orb.string_to_object(\"%1\")").arg(medIOR);
366
367    QStringListIterator it(commands);
368    while (it.hasNext()) {
369        pyConsole->exec(it.next());
370    }
371
372 Le code réel de la maquette est basé sur ce principe et transmet à la
373 console python des lignes de commandes qui permettent de reconstruire:
374
375 * un pointeur CORBA vers le servant ``SALOME_MED::MED`` associé au
376   champ sélectionné;
377 * une instance de ``FieldProxy`` qui correspond au champ sélectionné
378   et avec pour nom de variable la valeur de l'alias saisi dans
379   l'interface graphique.
380
381 Au niveau du code C++ de la classe ``XMEDGUI.cxx``, cela se traduit
382 par la fabrication de la liste de commandes suivante pour envoie à la
383 console python par le mécanisme illustré plus haut:
384
385 .. code-block:: cpp
386
387    QStringList commands;
388    commands+="from xmed.fieldproxy import getFieldFromMed";
389    commands+="from xmed.medproxy import getMedProxy";
390    commands+=QString("if not dir().__contains__('med'): med = getMedProxy(\"%1\")").arg(medIOR);
391    commands+=QString("%1=getFieldFromMed(med,\"%3\",%4,%5)").arg(*alias).arg(fieldName).arg(orderIndex).arg(iterationIndex);
392
393 Les variables ``medIOR``, ``fieldName``, ``orderIndex`` et
394 ``iterationIndex`` sont construites à partir du champ sélectionné par
395 des techniques de programmation standard dans SALOME qu'on peut
396 examiner en détail dans la classe ``XMEDGUI`` (voir méthode
397 ``XMEDGUI::LoadIntoPythonConsole()``). La variable ``alias`` est la
398 chaîne saisie par l'utilisateur dans la fenêtre de dialogue.
399
400 Le point important à noter ici est que les données à transmettre
401 doivent être fournies sous forme de chaînes de caractères ou de types
402 simples. C'est pourquoi la référence au servant CORBA
403 ``SALOME_MED::MED`` est transmise ici sous la forme de son IOR,
404 c'est-à-dire une chaîne de caractères qui permet l'identification de
405 l'objet au niveau du bus CORBA.
406
407 Au niveau de la console python cela correspond à l'exécution des
408 commandes suivantes:
409
410 .. code-block:: python
411
412    from xmed.fieldproxy import getFieldFromMed
413    from xmed.medproxy import getMedProxy
414
415    med = getMedProxy("IOR:010000001700000049444c3a53414c4f4d455f4d45442f4d45443a312e300000010000000000000064000000010102000e0000003133302e39382e37372e313733009e0a0e000000feadc4ca4c00003169000000001100000200000000000000080000000100000000545441010000001c00000001000000010001000100000001000105090101000100000009010100")
416
417    f1=getFieldFromMed(med,"testfield1",-1,-1)
418
419 Ce jeu d'instructions reconstitue un pointeur vers le servant CORBA
420 ``SALOME_MED::MED`` à partir de son identifiant IOR (voir la fonction
421 ``getMedProxy(...)``, puis crée une instance de ``FieldProxy``
422 associée à ce servant (en fait associée au servant
423 ``SALOME_MED::MEDOP`` créé sur demande par le servant
424 ``SALOME_MED::MED``, voir la fonction ``getFieldFromMed(...)``).
425
426 .. _develguide_execFieldOperation-fr:
427
428 Exécution des opérations sur le champs
429 --------------------------------------
430
431 Les variables définies dans l'interface textuelle pour désigner les
432 champs à manipuler sont des objets de classe ``FieldProxy``.
433
434 Cette classe a une propriété remarquable, elle est construite sur un
435 design pattern de type "Proxy" qui pointe vers un servant
436 ``SALOME_MED::FIELD``. Cela signifie que l'on ne peut pas accéder
437 directement au servant vers lequel il pointe, mais que l'on passe
438 systématiquement par une procédure de l'objet proxy qui fait "boîte
439 aux lettres":
440
441 .. code-block:: python
442
443    class FieldProxy:
444
445      def __getattr__( self, name ):
446         """
447         This method realizes the proxy pattern toward the servant
448         SALOME_MED::FIELD.
449         """
450         return getattr( self.__field_ptr, name )
451
452 Ce pattern permet l'implémentation de pré-traitement et/ou de
453 post-traitement suivant le type d'accés que l'on cherche à faire.
454
455 Il permet aussi et surtout de fournir un objet python qui présente
456 l'interface de ``SALOME_MED::FIELD`` dotée d'extentions adhoc pour les
457 operations de champs. Ici, python est ton ami, car il s'agit pour cela
458 d'équiper la classe ``FieldProxy`` des automatismes prévus nativement
459 par python pour les operations entre objets. En particulier, la
460 re-définition des fonctions internes ``__add__`` (opérateur addition),
461 ``__sub__`` (opérateur soustraction), ``__mul__`` (opérateur
462 multiplication) et ``__div__`` (opérateur division) au sein de la
463 classe ``FieldProxy``, permet de prendre la main sur le comportement
464 des opérations algébriques et de définir une ergonomie sur mesure. Par
465 exemple, la méthode ``__add__`` peut gérer les variantes "f1+f2"
466 (ajout de deux variables de type FieldProxy) et "f1+5.3" (ajout d'un
467 réel à une variable de type FieldProxy):
468
469 .. code-block:: python
470
471    class FieldProxy:
472
473      def __add__(self, operande):
474         """
475         This can process the addition of two fields or the addition of
476         a scalar to a field. It depends weither the operande is a
477         FieldProxy or a simple scalar numerical value.
478         """
479         if isinstance(operande, FieldProxy):
480             # The operande is an other field
481             otherField_ptr = operande.__field_ptr
482             rfield_ptr = self.__medOp_ptr.add(self.__field_ptr, otherField_ptr)
483         else:
484             # The operande is a scalar numerical value that must be
485             # considered as an offset in a linear transformation
486             factor = 1
487             offset = operande
488             rfield_ptr = self.__medOp_ptr.lin(self.__field_ptr, factor, offset)
489         return FieldProxy(self.__med_ptr, rfield_ptr)
490
491 Il est à noter que dans les deux cas de figure (opérande=champ ou
492 opérande=scalaire), la fonction délègue la réalisation concrète de
493 l'opération au servant ``SALOME_MED::MEDOP`` (identifié ici par
494 l'attribut ``self.__medOp_ptr`` et que l'on appelera l'*opérateur
495 MEDOP* dans la suite pour simplifier), mais n'appelle pas le même
496 service de calcul (l'addition entre champs dans le premier cas,
497 l'application d'une transformation linéaire de type y=factor*x+offset
498 dans le deuxième cas).
499
500 Pour couvrir le cas des opérations algébriques, l'opérateur MEDOP
501 présentre l'interface suivante (cf. fichier ``MEDOP.idl`` qui définie
502 l'interface du servant ``SALOME_MED_MEDOP``):
503
504 .. code-block:: cpp
505
506     /*! Addition of the fields f1 and f2 ( f1+f2) */
507     FIELD add(in FIELD f1, in FIELD f2) raises (SALOME::SALOME_Exception);
508     /*! Substraction of the fields f1 and f2 (f1-f2) */
509     FIELD sub(in FIELD f1, in FIELD f2) raises (SALOME::SALOME_Exception);
510     /*! Multiplication of the fields f1 by f2 (f1*f2) */
511     FIELD mul(in FIELD f1, in FIELD f2) raises (SALOME::SALOME_Exception);
512     /*! Division of the fields f1 by f2 (f1/f2) */
513     FIELD div(in FIELD f1, in FIELD f2) raises (SALOME::SALOME_Exception);
514     /*! Power of the field f (f^power) */
515     FIELD pow(in FIELD f, in long power) raises (SALOME::SALOME_Exception);
516     /*! Linear transformation of the field f (factor*f+offset) */
517     FIELD lin(in FIELD f, in double factor, in double offset) raises (SALOME::SALOME_Exception);
518     /*! Duplication of the field f */
519     FIELD dup(in FIELD f) raises (SALOME::SALOME_Exception);
520
521 Cette interface est implémentée dans la classe C++ ``MEDOP_i`` du
522 module MED (voir fichier ``MEDMEM_MedOp_i.hxx`` du package
523 ``MEDMEM_I``). C'est au sein des instances de cette classe que sont
524 réalisées les opérations et que sont produites physiquement les
525 données. Typiquement, les opérations présentées ici produisent un
526 champ ``MEDMEM::FIELD`` sur la base duquel elle fabrique un servant
527 ``SALOME_MED::FIELD`` pour finalement retourner un pointeur CORBA sur
528 ce servant.
529
530 Ce mécanisme global peut être étendu sans limitation à tout les types
531 d'opération qui sont envisagés dans les spécifications de manipulation
532 des champs dans SALOME.
533
534
535 Contrôle visuel des champs
536 --------------------------
537
538 Les illustrations ci-dessous montrent qu'une fonction de visalisation
539 est implémentée dans la maquette pour permettre le contrôle visuel
540 d'un champ au moyen d'une représentation 3D (une carte spatiale du
541 module du champ dans l'exemple implémenté par défaut):
542
543 .. |IMG_VISU| image:: ../images/medop-gui-visufield_scale.png
544 .. |IMG_RESULT| image:: ../images/medop-gui-result_scale.png
545
546 +------------+--------------+
547 | |IMG_VISU| | |IMG_RESULT| |
548 +------------+--------------+
549
550 Cette fonction répond au besoin de contrôle interactif des résultats
551 produits par les opérations de manipulation de champs.
552
553 Il s'agit là d'un usage classique de SALOME, dans lequel on demande au
554 module VISU de faire une représentation 3D d'un champ spécifié par la
555 donnée du servant ``SALOME_MED::FIELD`` qui lui est associé
556 (représenté par la variable ``field_ptr`` dans l'exemple ci-dessous):
557
558 .. code-block:: python
559
560    import salome
561    import VISU
562
563    visuComp = salome.lcc.FindOrLoadComponent("FactoryServer", "VISU")
564
565    # Then we can import the specified field in the VISU module. This
566    # creates an study entry in the VISU folder.
567    result = visuComp.ImportMedField(field_ptr)
568
569    meshName   = field_ptr.getSupport().getMesh().getName()
570    fieldName  = field_ptr.getName()
571    iterNumber = field_ptr.getIterationNumber()
572    scalarmap = visuComp.ScalarMapOnField(result,
573                                          meshName,
574                                          visuEntityType,
575                                          fieldName,
576                                          iterNumber)
577
578 Dans ce jeu d'instructions donné pour exemple (non fonctionnel, en
579 particulier à cause de la non définition de la variable
580 ``visuEntityType``, voir remarque plus bas), le composant VISU
581 désigné ici par la variable ``visuComp`` va chercher les données du
582 champ en interrogeant le servant ``SALOME_MED::FIELD`` transmis en
583 argument de la fonction ``ImportMedField``, puis produit une
584 représentation de type "scalarmap".
585
586 .. note:: Compte-tenu des propriétés de la classe FieldProxy décrites
587    plus haut conférées par le pattern "Proxy", on peut transmettre ici
588    aussi bien le servant CORBA que l'instance du proxy (la fonction
589    ``ImportMedField`` n'y verra que du feu).
590
591 Le code complet et fonctionnel de la fonction d'affichage est dans le
592 corps du module python ``fieldproxy.py`` sous la forme d'une fonction
593 de nom ``visuField``. Il convient de noter que cette fonction doit
594 établir une correspondance entre le type des entités tel que défini
595 dans MED et dans VISU:
596
597 .. code-block:: python
598
599     medEntityType = field_ptr.getSupport().getEntity()
600     if (medEntityType == SALOME_MED.MED_CELL):
601         visuEntityType = VISU.CELL
602     elif (medEntityType == SALOME_MED.MED_NODE):
603         visuEntityType = VISU.NODE
604
605
606 Export des résultats de calcul
607 ------------------------------
608
609 Tous les champs produits à l'occasion des opérations entre objets
610 ``FieldProxy`` sont automatiquement ajoutés à la structure med à
611 laquelle is sont associés. Une convention d'attribution des noms est
612 implémentée de sorte que par défaut aucune précision n'est demandée à
613 l'utilisateur.
614
615 La structure med peut être manipulée au moyen de la variable ``med``
616 créée dans l'interface textuelle comme une instance de la classe
617 ``MedProxy``. La classe ``MedProxy`` fournit un objet qui présente
618 l'interface du servant ``SALOME_MED::MED`` étendue de quelques
619 fonctions utilitaires pour la gestion et le contrôle des données.
620
621 En particulier, la sauvegarde de la structure dans un fichier est
622 automatisée par la méthode ``save(medfilename)``:
623
624 .. code-block:: python
625
626    med = medproxy.MedProxy(medObj)
627    med.save("/tmp/output.med")
628
629 Cette méthode s'occupe de définir un driver d'écriture et de procéder
630 à l'enregistrement des données de la structure med (les maillages, les
631 champs présents au départ et tous les champs produits depuis la
632 lecture initiale).
633
634 Limitations
635 ===========
636
637 L'implémentation de la maquette limite l'usage des opérations aux cas
638 de figure suivants:
639
640 * Seules les operations entre champs qui partagent le même support med
641   sont possibles. Ceci est une contrainte imposé par la conception
642   actuelle de MEDMEM.
643 * Le résultat d'une opérations est calculé sur toutes les composantes
644   et tout le domaine de définition des champs en opérande. Cette
645   deuxième contrainte est juste parce que les usages plus fin,
646   notemment avec la notion de domaine de définition, n'a pas encore
647   été exéminée à ce jour.
648 * Le nom d'un champ produit par une opération ne correspond pas au nom
649   de la variable python par laquelle on le réceptionne et on le
650   manipule. Le nom est attribué par une convention (ceci n'est pas
651   vraiment une limitation mais une caractéristique à connaître).
652
653 On note également les restriction techniques suivantes:
654
655 * Les données MEDMEM sont supposées être chargées par le composant MED
656   puis référencées dans l'étude SALOME (comme c'est fait aujourd'hui
657   par le module MED).
658 * Dans certain cas, python n'est pas ton ami. Pour que les opérateur
659   de la classe ``FieldProxy`` soient pris en considération dans les
660   opérations sur les champs, il est indispensable que le premier
661   opérande d'une opération unitaire soit un champ (objet de classe
662   ``FieldProxy``). Par exemple: "field_offset = field + 5.3"
663   fonctionne alors que "field_offset = 5.3 + field" ne fonctionne pas
664   car python tente de traiter la situation au moyen de la fonction
665   ``__add__`` de la classe ``float`` (qui n'est pas modifiable).
666
667
668 Notice informatique
669 ===================
670
671 Gestion de configuration
672 ------------------------
673
674 Les développements décrits dans ce chapitre sont répartis entre les
675 modules MED et XMED (développé pour l'occasion). Cette séparation est
676 faite par soucis de clarté et d'efficacité de développement, mais les
677 éléménts du module XMED ont vocation à intégrer le module MED dans la
678 mesure où les propositions techniques sont retenues pour le
679 développement à venir.
680
681 Le code source du module XMED peut être récupérés par la commande
682 suivante::
683
684  $ svn co svn://nepal.der.edf.fr/FIELD/XMED_SRC/trunk XMED_SRC
685
686 Le pré-requis est la plate-forme SALOME version 5.1.4 (ou plus)
687 équipée au minimum des modules KERNEL, GUI, MED (branche BR_medop) et
688 VISU. Pour récupérer la branche BR_medop du module MED, taper la
689 commande::
690
691  $ cvs -d :pserver:anonymous@cvs.opencascade.com:2401/home/server/cvs/MED co -r BR_medop MED_SRC
692
693 La configuration de référence est:
694
695 * XMED: révision svn 41
696 * MED: tag cvs BR_medop_20101025
697
698 Moyens de tests
699 ---------------
700
701 Plusieurs types de tests unitaires sont définis (reste à les
702 automatiser proprement):
703
704 * Test des servants et utilitaires de manipulation python:
705
706   - Dans XMED, package xmed/tests, utiliser le script
707     ``test_medoperation.py`` dans un interpréteur python lancé dans
708     une session shell SALOME. Ce script prépare des variables de test
709     et fournit des fonctions de test unitaire (à exécuter ou pour s'en
710     inspirer). Après avoir lancé SALOME via une application virtuelle,
711     on peut taper::
712
713       $ <APPLI_ROOT>/runSession
714       [NS=venus:2810] $ python -i test_medoperation.py
715       >>>
716
717   - Ceci permet de tester en particulier l'interface ``MedOp`` et son
718     utilisation dans le module python ``fieldproxy.py``.
719
720 * Test des classes MEDMEM:
721
722   - Test de MEDMEM::MedDataManager dans ``MEDMEM_MedDataManager_test.cxx``
723
724 Un fichier de test basique (mais néanmoins suffisant) de nom
725 ``tesfield.med`` est fourni avec les sources dans le répertoire
726 ``<XMED_SRC>/resources/datafiles`` et dans l'installation au niveau du
727 répertoire ``<INSTALLDIR>/share/salome/resources/xmed/datadir``. Il
728 contient deux champs ``testfield1`` et ``testfield2`` définis sur un
729 pas de temps unique (dt,it=-1,-1). Ces champs définissent des valeurs
730 par éléments (MED_CELL).