2 :keywords: maillage, champ, manipulation, XMED
3 :author: Guillaume Boulant
5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6 Démonstrateur XMED, documentation technique
7 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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.
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.
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::*``).
25 .. contents:: Sommaire
32 Objectif et motivation
33 ----------------------
35 L'objectif de maquettage est de trouver une architecture technique qui
36 permet d'exécuter le cas d'utilisation suivant:
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.
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:
58 .. code-block:: python
60 from libMEDMEM_Swig import MedDataManager
61 from xmed.helper import readMed, writeMed
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)
71 # Create a new field as the sum of f1 and f2
73 # And add this new field to the med data structure
76 # Finally, write the whole data in an output med file
77 writeMed(med,"/tmp/output.med")
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).
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.
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:
103 * La manipulation des champs se fait dans l'environement graphique de
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
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``).
124 Eléments de conception
125 ======================
128 Implantation technique
129 ----------------------
131 Le diagramme ci-dessous représente l'organisation des principaux
132 paquets logiciels du module MED:
134 .. image:: images/medmem-layers.png
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
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
146 Architecture technique
147 ----------------------
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:
152 .. image:: /images/xmed-architecture.png
154 :alt: Objets mis en oeuvre dans l'interface de manipulation de champs
156 On distingue les objets suivants:
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
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
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
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.
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.
187 Le cycle de vie de ces objets est le suivant.
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.
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:
208 .. code-block:: python
214 medComp = salome.lcc.FindOrLoadComponent("FactoryServer", "MED")
215 medObj = medComp.readStructFile("myfile.med",salome.myStudyName)
216 medOp = medObj.createMedOperator()
218 f1 = medObj.getField("testfield1",-1,-1)
219 f2 = medObj.getField("testfield2",-1,-1)
221 somme = medOp.add(f1,f2)
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.
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
247 Rôle des objets proxy
248 ---------------------
250 Dans le modèle d'architecture présenté ci-dessus, on introduit deux
251 types d'objets proxy:
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.
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:
266 .. code-block:: python
272 medComp = salome.lcc.FindOrLoadComponent("FactoryServer", "MED")
273 medObj = medComp.readStructFile("myfile.med",salome.myStudyName)
275 from xmed import fieldproxy
276 from xmed import medproxy
278 f1 = fieldproxy.getFieldFromMed(medObj, "testfield1", -1, -1)
279 f2 = fieldproxy.getFieldFromMed(medObj, "testfield2", -1, -1)
281 field_somme = f1 + f2
282 field_offset = f1 + 5.3
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.
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>`.
294 Programmation de l'interface textuelle
295 --------------------------------------
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
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
312 .. |IMG_SELECT| image:: images/medop-gui-selectfield_scale.png
313 .. |IMG_ALIAS| image:: images/medop-gui-aliasfield_scale.png
315 +---------------+---------------+
316 | |IMG_SELECT| | |IMG_ALIAS| |
317 +---------------+---------------+
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.
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
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()).
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
349 #include <PyConsole_Console.h>
351 #include <QStringList>
352 #include <SalomeApp_Application.h>
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
358 // Get the IOR of this object
359 QString medIOR = SalomeApp_Application::orb()->object_to_string(medObj);
361 PyConsole_Console * pyConsole = getApp()->pythonConsole();
363 QStringList commands;
364 commands+="import salome";
365 commands+=QString("med=salome.orb.string_to_object(\"%1\")").arg(medIOR);
367 QStringListIterator it(commands);
368 while (it.hasNext()) {
369 pyConsole->exec(it.next());
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:
375 * un pointeur CORBA vers le servant ``SALOME_MED::MED`` associé au
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.
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:
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);
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.
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.
407 Au niveau de la console python cela correspond à l'exécution des
410 .. code-block:: python
412 from xmed.fieldproxy import getFieldFromMed
413 from xmed.medproxy import getMedProxy
415 med = getMedProxy("IOR:010000001700000049444c3a53414c4f4d455f4d45442f4d45443a312e300000010000000000000064000000010102000e0000003133302e39382e37372e313733009e0a0e000000feadc4ca4c00003169000000001100000200000000000000080000000100000000545441010000001c00000001000000010001000100000001000105090101000100000009010100")
417 f1=getFieldFromMed(med,"testfield1",-1,-1)
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(...)``).
426 .. _develguide_execFieldOperation:
428 Exécution des opérations sur le champs
429 --------------------------------------
431 Les variables définies dans l'interface textuelle pour désigner les
432 champs à manipuler sont des objets de classe ``FieldProxy``.
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
441 .. code-block:: python
445 def __getattr__( self, name ):
447 This method realizes the proxy pattern toward the servant
450 return getattr( self.__field_ptr, name )
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.
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):
469 .. code-block:: python
473 def __add__(self, operande):
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.
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)
484 # The operande is a scalar numerical value that must be
485 # considered as an offset in a linear transformation
488 rfield_ptr = self.__medOp_ptr.lin(self.__field_ptr, factor, offset)
489 return FieldProxy(self.__med_ptr, rfield_ptr)
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).
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``):
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 /*! Dublication of the field f */
519 FIELD dup(in FIELD f) raises (SALOME::SALOME_Exception);
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
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.
535 Contrôle visuel des champs
536 --------------------------
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):
543 .. |IMG_VISU| image:: images/medop-gui-visufield_scale.png
544 .. |IMG_RESULT| image:: images/medop-gui-result_scale.png
546 +---------------+---------------+
547 | |IMG_VISU| | |IMG_RESULT| |
548 +---------------+---------------+
550 Cette fonction répond au besoin de contrôle interactif des résultats
551 produits par les opérations de manipulation de champs.
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):
558 .. code-block:: python
563 visuComp = salome.lcc.FindOrLoadComponent("FactoryServer", "VISU")
564 visuComp.SetCurrentStudy(salome.myStudy)
566 # Then we can import the specified field in the VISU module. This
567 # creates an study entry in the VISU folder.
568 result = visuComp.ImportMedField(field_ptr)
570 meshName = field_ptr.getSupport().getMesh().getName()
571 fieldName = field_ptr.getName()
572 iterNumber = field_ptr.getIterationNumber()
573 scalarmap = visuComp.ScalarMapOnField(result,
579 Dans ce jeu d'instructions donné pour exemple (non fonctionnel, en
580 particulier à cause de la non définition de la variable
581 ``visuEntityType``, voir remarque plus bas), le composant VISU
582 désigné ici par la variable ``visuComp`` va chercher les données du
583 champ en interrogeant le servant ``SALOME_MED::FIELD`` transmis en
584 argument de la fonction ``ImportMedField``, puis produit une
585 représentation de type "scalarmap".
587 .. note:: Compte-tenu des propriétés de la classe FieldProxy décrites
588 plus haut conférées par le pattern "Proxy", on peut transmettre ici
589 aussi bien le servant CORBA que l'instance du proxy (la fonction
590 ``ImportMedField`` n'y verra que du feu).
592 Le code complet et fonctionnel de la fonction d'affichage est dans le
593 corps du module python ``fieldproxy.py`` sous la forme d'une fonction
594 de nom ``visuField``. Il convient de noter que cette fonction doit
595 établir une correspondance entre le type des entités tel que défini
596 dans MED et dans VISU:
598 .. code-block:: python
600 medEntityType = field_ptr.getSupport().getEntity()
601 if (medEntityType == SALOME_MED.MED_CELL):
602 visuEntityType = VISU.CELL
603 elif (medEntityType == SALOME_MED.MED_NODE):
604 visuEntityType = VISU.NODE
607 Export des résultats de calcul
608 ------------------------------
610 Tous les champs produits à l'occasion des opérations entre objets
611 ``FieldProxy`` sont automatiquement ajoutés à la structure med à
612 laquelle is sont associés. Une convention d'attribution des noms est
613 implémentée de sorte que par défaut aucune précision n'est demandée à
616 La structure med peut être manipulée au moyen de la variable ``med``
617 créée dans l'interface textuelle comme une instance de la classe
618 ``MedProxy``. La classe ``MedProxy`` fournit un objet qui présente
619 l'interface du servant ``SALOME_MED::MED`` étendue de quelques
620 fonctions utilitaires pour la gestion et le contrôle des données.
622 En particulier, la sauvegarde de la structure dans un fichier est
623 automatisée par la méthode ``save(medfilename)``:
625 .. code-block:: python
627 med = medproxy.MedProxy(medObj)
628 med.save("/tmp/output.med")
630 Cette méthode s'occupe de définir un driver d'écriture et de procéder
631 à l'enregistrement des données de la structure med (les maillages, les
632 champs présents au départ et tous les champs produits depuis la
638 L'implémentation de la maquette limite l'usage des opérations aux cas
641 * Seules les operations entre champs qui partagent le même support med
642 sont possibles. Ceci est une contrainte imposé par la conception
644 * Le résultat d'une opérations est calculé sur toutes les composantes
645 et tout le domaine de définition des champs en opérande. Cette
646 deuxième contrainte est juste parce que les usages plus fin,
647 notemment avec la notion de domaine de définition, n'a pas encore
648 été exéminée à ce jour.
649 * Le nom d'un champ produit par une opération ne correspond pas au nom
650 de la variable python par laquelle on le réceptionne et on le
651 manipule. Le nom est attribué par une convention (ceci n'est pas
652 vraiment une limitation mais une caractéristique à connaître).
654 On note également les restriction techniques suivantes:
656 * Les données MEDMEM sont supposées être chargées par le composant MED
657 puis référencées dans l'étude SALOME (comme c'est fait aujourd'hui
659 * Dans certain cas, python n'est pas ton ami. Pour que les opérateur
660 de la classe ``FieldProxy`` soient pris en considération dans les
661 opérations sur les champs, il est indispensable que le premier
662 opérande d'une opération unitaire soit un champ (objet de classe
663 ``FieldProxy``). Par exemple: "field_offset = field + 5.3"
664 fonctionne alors que "field_offset = 5.3 + field" ne fonctionne pas
665 car python tente de traiter la situation au moyen de la fonction
666 ``__add__`` de la classe ``float`` (qui n'est pas modifiable).
672 Gestion de configuration
673 ------------------------
675 Les développements décrits dans ce chapitre sont répartis entre les
676 modules MED et XMED (développé pour l'occasion). Cette séparation est
677 faite par soucis de clarté et d'efficacité de développement, mais les
678 éléménts du module XMED ont vocation à intégrer le module MED dans la
679 mesure où les propositions techniques sont retenues pour le
680 développement à venir.
682 Le code source du module XMED peut être récupérés par la commande
685 $ svn co svn://nepal.der.edf.fr/FIELD/XMED_SRC/trunk XMED_SRC
687 Le pré-requis est la plate-forme SALOME version 5.1.4 (ou plus)
688 équipée au minimum des modules KERNEL, GUI, MED (branche BR_medop) et
689 VISU. Pour récupérer la branche BR_medop du module MED, taper la
692 $ cvs -d :pserver:anonymous@cvs.opencascade.com:2401/home/server/cvs/MED co -r BR_medop MED_SRC
694 La configuration de référence est:
696 * XMED: révision svn 41
697 * MED: tag cvs BR_medop_20101025
702 Plusieurs types de tests unitaires sont définis (reste à les
703 automatiser proprement):
705 * Test des servants et utilitaires de manipulation python:
707 - Dans XMED, package xmed/tests, utiliser le script
708 ``test_medoperation.py`` dans un interpréteur python lancé dans
709 une session shell SALOME. Ce script prépare des variables de test
710 et fournit des fonctions de test unitaire (à exécuter ou pour s'en
711 inspirer). Après avoir lancé SALOME via une application virtuelle,
714 $ <APPLI_ROOT>/runSession
715 [NS=venus:2810] $ python -i test_medoperation.py
718 - Ceci permet de tester en particulier l'interface ``MedOp`` et son
719 utilisation dans le module python ``fieldproxy.py``.
721 * Test des classes MEDMEM:
723 - Test de MEDMEM::MedDataManager dans ``MEDMEM_MedDataManager_test.cxx``
725 Un fichier de test basique (mais néanmoins suffisant) de nom
726 ``tesfield.med`` est fourni avec les sources dans le répertoire
727 ``<XMED_SRC>/resources/datafiles`` et dans l'installation au niveau du
728 répertoire ``<INSTALLDIR>/share/salome/resources/xmed/datadir``. Il
729 contient deux champs ``testfield1`` et ``testfield2`` définis sur un
730 pas de temps unique (dt,it=-1,-1). Ces champs définissent des valeurs
731 par éléments (MED_CELL).