7 \g@addto@macro\@verbatim\small
13 YACSGEN : Générateur automatique de module SALOME
14 ==================================================
16 YACSGEN est un module python (module_generator.py) qui permet de fabriquer un module
17 SALOME automatiquement à partir d'une description synthétique des composants
18 qu'il contiendra. Cette description est faite en langage Python.
20 Les caractéristiques de ces composants ne sont pas générales mais devraient
21 faciliter l'intégration de nombreux composants de calcul scientifique.
23 Ce générateur ne prend pas en charge l'intégration d'une IHM graphique mais seulement
24 la partie calcul. L'objectif principal est l'intégration d'une bibliothèque Fortran dans laquelle
25 on peut faire des appels aux ports datastream (Calcium, en particulier).
27 Obtenir module_generator.py
28 ------------------------------------------------------------
29 Voir site PAL : http://pal.der.edf.fr/pal/projets/pal/superv/modulegenerator
31 Versions et architectures supportées
32 -----------------------------------------------------------------
33 Module_generator.py utilise des fonctionnalités de python 2.4 mais a un mode de compatibilité avec python 2.3.
34 Il fonctionne sur architecture 32 bits et 64 bits (testé sur machine Aster).
37 ----------------------------
38 Il n'y a pas de procédure d'installation particulière. Il suffit de décompresser et détarrer l'archive
39 obtenue à partir du site PAL (YACSGEN-x.y.tar.gz) et d'ajouter le répertoire ainsi créé au PYTHONPATH.
41 Description d'un module SALOME
42 --------------------------------------------------------
43 Un module SALOME est décrit au moyen d'instructions Python et en utilisant des définitions contenues
44 dans le module Python module_generator.py.
46 La première action à réaliser est d'importer ces définitions::
48 from module_generator import Generator,Module,PYComponent
49 from module_generator import CPPComponent,Service,F77Component
51 Pour décrire un module SALOME, on donne son nom <nommodule>, la liste de ses composants (<liste des composants>)
52 et le nom du répertoire dans lequel il sera installé (<prefix>).
54 Sa description prend la forme suivante ::
56 m=Module(<nommodule>,components=<liste des composants>,prefix=<prefix>)
58 Pour un module de nom "toto" avec un composant c1 (voir ci-dessous pour la description des composants) qui sera installé dans le
59 répertoire "Install", on aura ::
61 m=Module("toto",components=[c1],prefix="Install")
65 Description des composants
66 ------------------------------------------------
68 Il est possible de créer plusieurs types de composants :
75 Tous ces types ont une description semblable. On commencera par le type C++ puis
76 on décrira les différences principales pour les autres types.
79 ++++++++++++++++++++++++++++++++++++++++
80 Tout d'abord, un composant C++ a un nom. Ce nom sera utilisé par la suite quand on voudra créer
81 des instances de ce composant. Le module SALOME, une fois compilé et installé contiendra une
82 librairie dynamique de nom lib<compo>Engine.so, où <compo> est le nom du composant.
83 Un composant C++ est implémenté comme un objet C++ exécutable à distance.
85 Un composant C++ a un ou plusieurs services. Chaque service a un nom qui est le nom de
86 la méthode de l'objet C++ qui correspond au composant.
87 Chaque service a, éventuellement, des ports dataflow d'entrée et de sortie et des ports datastream
88 d'entrée et de sortie.
90 Un premier service avec des ports dataflow
91 """""""""""""""""""""""""""""""""""""""""""""""""""""""
92 Pour le moment, les seuls types possibles pour les ports dataflow sont :
94 - double : scalaire équivalent à un double C
95 - long : scalaire équivalent à un long C
96 - string : équivalent à un char* C (chaine de caractères de longueur quelconque)
97 - dblevec : vecteur de doubles
98 - stringvec : vecteur de string
99 - intvec : vecteur de longs
100 - pyobj : objet python serialisé avec pickle (ne marche qu'avec des composants implémentés en Python)
102 Un port est décrit par un tuple python de longueur 2 dont la première valeur est le nom du port
103 et la deuxième le nom du type. Les ports d'entrée sont décrits par une liste de ces tuples
104 ainsi que les ports de sortie.
106 Un petit exemple vaut mieux qu'un long discours. Un composant de nom "moncompo" avec un service
107 de nom "monservice" qui a un port dataflow d'entrée de nom "portentrant" de type double
108 et un port dataflow de sortie de nom "portsortant" de type double aura la description suivante ::
110 c1=CPPComponent("moncompo",
112 Service("monservice",
113 inport=[("portentrant","double"),],
114 outport=[("portsortant","double")],
119 c1 est une variable intermédiaire Python qui sera utilisée pour décrire la liste des composants d'un module : (components=[c1])
120 pour un module avec un seul composant.
123 En fait ce composant n'a pas grand intérêt car lors de l'exécution, il va prendre un double en entrée
124 d'exécution et fournir un double en sortie d'exécution mais il ne fait rien entre temps.
125 Il faut donc lui ajouter un contenu. Ce contenu sera spécifié dans 2 attributs du service : defs et body.
126 defs contiendra du code C++ de définition (par exemple, #include <iostream>) et body contiendra le code C++
127 qui sera exécuté entre l'entrée et la sortie (par exemple, portsortant=2*portentrant;).
128 Au final notre description devient ::
130 c1=CPPComponent("moncompo",
132 Service("monservice",
133 inport=[("portentrant","double"),],
134 outport=[("portsortant","double")],
135 defs="#include <iostream>",
136 body="portsortant=2*portentrant;",
141 Ajouter des ports datastream au service
142 """"""""""""""""""""""""""""""""""""""""""""""
143 Pour ajouter des ports datastream au service "monservice", on ajoute à la description les attributs instream et outstream.
144 Ces attributs doivent être des listes de triplets dont les éléments sont :
148 3. le mode de dépendance temporelle ("T") ou itérative ("I") (se référer à la documentation Calcium pour plus de détails)
150 Les types possibles sont "CALCIUM_double", "CALCIUM_integer", "CALCIUM_real", "CALCIUM_string", "CALCIUM_logical"
151 et "CALCIUM_complex".
153 Avec un port datastream entrant et un port sortant en dépendance temporelle, la description devient ::
155 c1=CPPComponent("moncompo",
157 Service("monservice",
158 inport=[("portentrant","double"),],
159 outport=[("portsortant","double")],
160 instream=[("porta","CALCIUM_double","T")],
161 outstream=[("portb","CALCIUM_double","T")],
162 defs="#include <iostream>",
163 body="portsortant=2*portentrant;",
168 Il faudrait bien sûr ajouter dans body des appels à la bibliothèque CALCIUM pour que le service soit vraiment fonctionnel.
170 Ajouter un deuxième service au composant
171 """""""""""""""""""""""""""""""""""""""""""""""""
172 Si on veut un deuxième service pour le composant il suffit d'ajouter une autre description de service ::
174 c1=CPPComponent("moncompo",
176 Service("monservice",
177 inport=[("portentrant","double"),],
178 outport=[("portsortant","double")],
179 instream=[("porta","CALCIUM_double","T")],
180 outstream=[("portb","CALCIUM_double","T")],
181 defs="#include <iostream>",
182 body="portsortant=2*portentrant;",
185 inport=[("a","double"),("b","long")],
186 outport=[("c","double")],
192 Ici, on a ajouté un deuxième service de nom "serv2" avec 2 ports dataflow d'entrée (a et b) et un port dataflow de sortie (c).
193 Le service est réduit à sa plus simple expression : il retourne le produit de ses 2 entrées.
195 Assembler avec des bibliothèques externes
196 """"""""""""""""""""""""""""""""""""""""""""""""""""
197 On a vu que les attributs *defs* et *body* permettent de définir le corps du service mais il est souvent plus pratique d'utiliser des bibliothèques
198 externes plutôt que de tout mettre dans ces 2 attributs.
199 Ceci est possible à condition d'indiquer dans les attributs *libs* et *rlibs* du composant, tout ce qui est nécessaire pour l'étape de link
202 On pourra avoir, par exemple::
204 c1=CPPComponent("moncompo",
206 Service("monservice",
207 inport=[("portentrant","double"),],
208 outport=[("portsortant","double")],
209 defs="extern double myfunc(double);",
210 body="portsortant=myfunc(portentrant);",
213 libs="-L/usr/local/mysoft -lmybib",
214 rlibs="-Wl,--rpath -Wl,/usr/local/mysoft"
217 L'attribut *rlibs* n'est pas obligatoire mais peut être utilisé pour indiquer un path de recherche pour des bibliothèques
218 dynamiques à l'exécution.
219 *libs* est utilisé pendant la phase de link. *rlibs* est utilisé uniquement à l'exécution, il évite d'avoir à positionner
220 la variable d'environnement LD_LIBRARY_PATH pour trouver la librairie dynamique.
223 """"""""""""""""""""""""""""""""""""""""""""""""""""
224 Les includes seront ajoutés au moyen de l'attribut *defs*. Par exemple ::
226 defs="#include "moninclude.h"
228 Le chemin des includes sera spécifié dans l'attribut *includes* du composant, sous la forme suivante ::
230 defs="""#include "moninclude.h"
231 extern double myfunc(double);
233 c1=CPPComponent("moncompo",
235 Service("monservice",
236 inport=[("portentrant","double"),],
237 outport=[("portsortant","double")],
239 body="portsortant=myfunc(portentrant);",
242 libs="-L/usr/local/mysoft -lmybib",
243 rlibs="-Wl,--rpath -Wl,/usr/local/mysoft",
244 includes="-I/usr/local/mysoft/include",
249 ++++++++++++++++++++++++++++++++++++++++
250 Un composant Fortran se décrit comme un composant C++ à quelques différences près.
251 Tout d'abord, on utilise l'objet de définition F77Component au lieu de CPPComponent.
252 Ensuite, un interfaçage supplémentaire spécial au Fortran est réalisé. On suppose que les fonctionnalités Fortran
253 sont implémentées dans une librairie (dynamique ou statique) qui sera linkée avec le composant et qui dispose de
254 plusieurs points d'entrée de mêmes noms que les services du composant. L'appel à ce point d'entrée sera ajouté
255 automatiquement après le code C++ fourni par l'utilisateur dans l'attribut body.
257 Ceci permet de découpler presque totalement l'implémentation du composant Fortran qui sera dans la bibliothèque
258 externe, de l'implémentation du composant SALOME qui ne sert que pour l'encapsulation.
260 L'exemple suivant permettra de préciser ces dernières notions ::
262 c3=F77Component("compo3",
265 inport=[("a","double"),("b","long"),("c","string")],
266 outport=[("d","double"),("e","long"),("f","string")],
267 instream=[("a","CALCIUM_double","T"),
268 ("b","CALCIUM_double","I")],
269 outstream=[("ba","CALCIUM_double","T"),
270 ("bb","CALCIUM_double","I")],
271 defs="#include <unistd.h>",
275 libs="-L/usr/local/fcompo -lfcompo",
276 rlibs="-Wl,--rpath -Wl,/usr/local/fcompo"
279 Le composant Fortran "compo3" a des ports dataflow et datastream comme le composant C++. La bibliothèque dynamique Fortran
280 qui contient le point d'entrée Fortran s1 sera linkée grâce aux attributs libs et rlibs de la description. Le composant
281 Fortran supporte également l'attribut *includes*.
283 Il est possible d'ajouter un bout de code C++ avant l'appel au point d'entrée Fortan. Ce bout de code doit être mis
284 dans l'attribut body avec des définitions éventuelles dans defs. Ici, on utilise la variable dataflow entrante "c"
285 pour faire un changement de répertoire avec l'appel à chdir.
288 ++++++++++++++++++++++++++++++++++++++++
289 Un composant Python se décrit également comme un composant C++. Les seules différences portent sur l'objet Python
290 à utiliser pour le définir : PYComponent au lieu de CPPComponent et sur le contenu des attributs *defs* et *body*
291 qui doivent contenir du code Python et non C++ (attention à l'indentation, elle n'est pas prise en charge automatiquement).
293 Exemple de composant Python ::
295 pyc1=PYComponent("moncompo",
297 Service("monservice",
298 inport=[("portentrant","double"),],
299 outport=[("portsortant","double")],
301 body=" portsortant=2*portentrant;",
306 L'équivalent de l'assemblage avec des bibliothèques externes est réalisé ici avec la possibilité d'importer des modules
307 Python externes. Il suffit d'ajouter l'attribut *python_path* à la description du composant pour avoir cette possibilité. La valeur
308 à donner est une liste de répertoires susceptibles de contenir des modules à importer.
312 pyc1=PYComponent("moncompo",
314 Service("monservice",
315 inport=[("portentrant","double"),],
316 outport=[("portsortant","double")],
319 python_path=["/usr/local/mysoft","/home/chris/monsoft"],
323 ++++++++++++++++++++++++++++++++++++++++
324 Un composant Aster est un composant un peu particulier car les fonctionnalités du logiciel sont implémentées en Fortran mais elles
325 sont activées par un superviseur de commandes écrit en Python. Au final ce superviseur exécute un script Python mais il faut gérer le transfert
326 des données entre Python et Fortran et l'intégration du superviseur de commandes dans un composant SALOME.
328 Le point de départ est le suivant : on suppose que l'on dispose d'une installation d'Aster qui fournit un module python aster
329 sous la forme d'une bibliothèque dynamique importable (astermodule.so) et non comme c'est le cas dans l'installation actuelle
330 d'un interpréteur Python spécifique linké avec ce même module.
332 Un composant Aster se décrit comme un composant Python auquel il faut ajouter plusieurs attributs
335 - l'attribut *python_path* : il indique le chemin du répertoire contenant le module aster (astermodule.so)
336 - l'attribut *aster_dir* : il indique le chemin du répertoire d'installation d'Aster
337 - l'attribut *argv* : il initialise les paramètres de la ligne de commande. On y mettra, par exemple la valeur
338 de memjeveux (``argv=["-memjeveux","10"]``) ou de rep_outils.
340 Voici un petit exemple de description de composant Aster avec un seul service doté de 3 ports dataflow d'entrée,
341 d'un port dataflow de sortie, de 7 ports datastream d'entrée et d'un port datastream de sortie::
343 c1=ASTERComponent("caster",
346 inport=[("a","double"),("b","long"),("c","string")],
347 outport=[("d","double")],
348 instream=[("aa","CALCIUM_double","T"),
349 ("ab","CALCIUM_double","I"),
350 ("ac","CALCIUM_integer","I"),
351 ("ad","CALCIUM_real","I"),
352 ("ae","CALCIUM_string","I"),
353 ("af","CALCIUM_complex","I"),
354 ("ag","CALCIUM_logical","I"),
356 outstream=[("ba","CALCIUM_double","T"),
357 ("bb","CALCIUM_double","I")],
360 aster_dir="/local/chris/ASTER/instals/NEW9",
361 python_path=["/local/chris/modulegen/YACSGEN/aster/bibpyt"],
362 argv=["-memjeveux","10",
363 "-rep_outils","/local/chris/ASTER/instals/outils"],
366 Attention à ne pas appeler le composant "aster" car ce nom est réservé au module python de Code_Aster. En cas
367 d'utilisation du nom "aster", le comportement est complètement erratique.
369 Bien que sa description soit très semblable à celle d'un composant Python, il y a une différence importante à l'utilisation.
370 En effet, le composant Aster a besoin de la description d'un jeu de commandes pour fonctionner. Ce jeu de commandes
371 est passé sous la forme d'un texte à chaque service du composant dans un port dataflow d'entrée de nom "jdc" et
373 Après génération, ce composant Aster aura donc 4 ports dataflow d'entrée ("jdc","a","b","c") et non 3 comme indiqué
374 dans la description. Il ne faut pas oublier d'initialiser le port "jdc" dans le fichier de couplage avec un jeu de commandes.
376 Le superviseur de commandes a été intégré dans un composant SALOME et les variables reçues dans les ports dataflow
377 sont disponibles lors de l'exécution du jeu de commandes.
378 De même pour les ports dataflow de sortie, ils sont alimentés par les valeurs des variables issues de l'exécution
381 **Attention au mode d'exécution**. Le superviseur de commandes a 2 modes d'exécution (PAR_LOT="OUI" ou PAR_LOT="NON"
382 que l'on spécifie dans la commande DEBUT). En mode PAR_LOT="OUI", il est obligatoire de terminer le jeu de commandes
383 par une commande FIN ce qui a pour effet d'interrompre l'exécution. Ce n'est pas le fonctionnement à privilégier
384 avec YACS. Il est préférable d'utiliser le mode PAR_LOT="NON" sans mettre de commande FIN ce qui évite d'interrompre
385 l'exécution prématurément.
387 Module Aster dynamiquement importable et lien avec YACS
388 """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
389 Ces deux points ne sont pas pris en charge par *module_generator.py*. Ils doivent être traités séparément dans un contexte
390 proche de celui d'un développeur Aster.
392 On suppose que l'on dispose d'une installation d'Aster, que l'on veut créer un module Python aster dynamiquement importable
393 et que l'on veut ajouter à Aster quelques commandes pour échanger des données via des ports datastream de YACS.
395 Pour rester simple, on ajoute 3 commandes : YACS_INIT, ECRIRE_MAILLAGE et LECTURE_FORCE dont les catalogues sont::
397 YACS_INIT=PROC(nom="YACS_INIT",op=181, fr="Initialisation YACS",
398 COMPO=SIMP(statut='o',typ='I'),
400 ECRIRE_MAILLAGE=PROC(nom="ECRIRE_MAILLAGE",op=78, fr="Ecriture du maillage")
401 LECTURE_FORCE=PROC(nom="LECTURE_FORCE",op=189, fr="Lecture de la force")
403 La première commande YACS_INIT sert à initialiser Aster dans le contexte YACS. Elle a un seul mot-clé simple COMPO
404 (de type entier) qui sera utilisé pour passer aux autres commandes l'identificateur du composant SALOME. Cet
405 identificateur sera stocké dans un COMMON fortran. Il est indispensable pour les appels aux sous programmes
406 CPLxx et CPExx qui seront utilisés dans les 2 autres commandes ECRIRE_MAILLAGE et LECTURE_FORCE.
408 Les 2 autres commandes ne prennent n'ont aucun mot-clé et récupèrent l'identificateur dans le COMMON.
410 Les opérateurs seront écrits comme suit (sans les déclarations)::
412 SUBROUTINE OP0189 ( IER )
413 C COMMANDE: LECTURE_FORCE
416 CALL cpldb(ICOMPO,CP_TEMPS,t0,t1,iter,'aa',1,n,ss,info)
417 CALL cpldb(ICOMPO,CP_ITERATION,t0,t1,iter,'ab',1,n,zz,info)
418 CALL cplen(ICOMPO,CP_ITERATION,t0,t1,iter,'ac',1,n,zn,info)
419 CALL cplre(ICOMPO,CP_ITERATION,t0,t1,iter,'ad',1,n,yr,info)
420 CALL cplch(ICOMPO,CP_ITERATION,t0,t1,iter,'ae',1,n,tch,info)
421 CALL cplcp(ICOMPO,CP_ITERATION,t0,t1,iter,'af',1,n,tcp,info)
422 CALL cpllo(ICOMPO,CP_ITERATION,t0,t1,iter,'ag',3,n,tlo,info)
425 SUBROUTINE OP0078 ( IER )
426 C COMMANDE: ECRIRE_MAILLAGE
429 CALL cpeDB(ICOMPO,CP_TEMPS,t0,1,'ba',1,tt,info)
430 CALL cpeDB(ICOMPO,CP_ITERATION,t0,1,'bb',1,tp,info)
433 Enfin, il faut construire une bibliothèque dynamique astermodule.so, et placer tous les modules Python
434 nécessaires dans un répertoire que l'on indiquera dans l'attribut *python_path*.
435 On peut utiliser différentes méthodes pour obtenir ce résultat. Le Makefile suivant en est une::
440 KERNEL_ROOT_DIR=/local/chris/SALOME2/RELEASES/Install/KERNEL_V4_0
441 KERNEL_INCLUDES=-I$(KERNEL_ROOT_DIR)/include/salome
442 KERNEL_LIBS= -L$(KERNEL_ROOT_DIR)/lib/salome -lCalciumC -lSalomeDSCSuperv \
443 -lSalomeDSCContainer -lSalomeDatastream -lSalomeDSCSupervBasic \
444 -Wl,--rpath -Wl,$(KERNEL_ROOT_DIR)/lib/salome
446 ASTER_ROOT=/local/chris/ASTER/instals
447 ASTER_INSTALL=$(ASTER_ROOT)/NEW9
448 ASTER_PUB=$(ASTER_ROOT)/public
449 ASTER_LIBS = -L$(ASTER_INSTALL)/lib -laster \
450 -L$(ASTER_PUB)/scotch_4.0/bin -lscotch -lscotcherr \
451 -lferm -llapack -lhdf5
452 SOURCES=src/op0078.f src/op0189.f
453 CATAPY=catalo/ecrire_maillage.capy catalo/lecture_force.capy
455 all:pyth cata astermodule
457 cp -rf $(ASTER_INSTALL)/bibpyt .
458 cata: commande/cata.py
459 cp -rf commande/cata.py* bibpyt/Cata
460 commande/cata.py:$(CATAPY)
461 $(ASTER_ROOT)/ASTK/ASTK_SERV/bin/as_run make-cmd
462 astermodule:astermodule.so pyth
463 cp -rf astermodule.so bibpyt
464 astermodule.so: $(SOURCES)
465 $(FC) -shared -o $@ $(SOURCES) $(KERNEL_INCLUDES) $(ASTER_LIBS) $(KERNEL_LIBS)
467 Modifier les paramètres de la ligne de commande à l'exécution
468 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
469 L'attribut *argv* permet de donner des valeurs initiales aux arguments comme "memjeveux" mais
470 ces valeurs sont utilisées par le générateur pour construire le composant et restent donc constantes
471 par la suite, à l'exécution.
473 Pour modifier ces valeurs à l'exécution, il faut ajouter un port d'entrée de nom "argv" et de type "string".
474 La chaine de caractère qui sera donnée comme valeur de ce port sera utilisée par le composant pour
475 modifier les arguments de la ligne de commande (voir `Exemple d'exécution de composant Aster`_
476 pour un exemple d'utilisation).
478 Gestion du fichier elements
479 """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
480 Le fichier des éléments finis est copié automatiquement dans le répertoire de travail sous le
481 nom elem.1. Le composant utilise l'attribut *aster_dir* pour localiser le fichier d'origine.
483 Version d'Aster supportées
484 """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
485 Module_generator.py peut fonctionner avec Aster 9.0 et 9.2 (probablement avec 9.1 mais
488 Générateur de module SALOME
489 -----------------------------------------------------------
490 Le module SALOME est créé par un générateur construit à partir de la description du module SALOME (m) vue
491 précédemment et d'un dictionnaire Python (context) qui fournit quelques paramètres d'environnement ::
493 g=Generator(m,context)
495 Les paramètres indispensables pour context sont :
497 - "prerequisites" : indique le chemin d'un shell script qui positionne les variables d'environnement
498 des prérequis de SALOME
499 - "kernel" : indique le chemin d'installation du module KERNEL de SALOME
500 - "update" : mettre à 1 pour le moment (futurs développements)
502 Exemple de création de générateur ::
505 "prerequisites":"/local/cchris/.packages.d/envSalome40",
506 "kernel":"/local/chris/SALOME2/RELEASES/Install/KERNEL_V4_0"
508 g=Generator(m,context)
510 Une fois ce générateur créé, il suffit d'appeler ses commandes pour réaliser les opérations nécessaires.
512 - génération du module SALOME : ``g.generate()``
513 - initialisation d'automake : ``g.bootstrap()``
514 - exécution du script configure : ``g.configure()``
515 - compilation : ``g.make()``
516 - installation dans le répertoire <prefix> : ``g.install()``
517 - création d'une application SALOME dans le répertoire appli_dir ::
519 g.make_appli(appli_dir,restrict=<liste de modules>,
520 altmodules=<dictionnaire de modules>)
522 Ces commandes ne prennent pas de paramètres sauf make_appli qui en prend 3 :
524 - **appliname** : le nom du répertoire qui contiendra l'application SALOME
525 - **restrict** : une liste de noms de modules SALOME à mettre dans l'application. Par défaut, make_appli
526 met dans l'application tous les modules SALOME qu'il est capable de détecter (répertoire voisins de
527 KERNEL avec le même suffixe que KERNEL. Si le répertoire du module KERNEL s'appelle KERNEL_V41, il
528 retiendra GUI_V41, GEOM_V41, etc.). Si restrict est fourni, make_appli ne retiendra que les modules listés.
529 - **altmodules** : un dictionnaire de modules autres. La clé donne le nom du module. La valeur correspondante
530 donne le chemin du répertoire d'installation du module. Exemple : ``altmodules={"monmodule":"/local/chris/unmodule"}``
533 Fabrication du module SALOME
534 -----------------------------------------------------
535 Le module sera fabriqué en exécutant un fichier Python qui contient sa description, la mise en données du
536 générateur et les commandes du générateur.
538 Ce qui donne quelque chose comme ce qui suit pour un module avec un seul composant Fortran::
540 from module_generator import Generator,Module
541 from module_generator import PYComponent,CPPComponent,Service,F77Component
544 "prerequisites":"/local/cchris/.packages.d/envSalome40",
545 "kernel":"/local/chris/SALOME2/RELEASES/Install/KERNEL_V4_0"
547 c3=F77Component("compo",
550 inport=[("a","double"),("b","long"),("c","string")],
551 outport=[("d","double"),("e","long"),("f","string")],
552 instream=[("a","CALCIUM_double","T"),
553 ("b","CALCIUM_double","I")],
554 outstream=[("ba","CALCIUM_double","T"),
555 ("bb","CALCIUM_double","I")],
556 defs="#include <unistd.h>",
560 libs="-L/local/chris/modulegen/YACSGEN/fcompo -lfcompo"
561 rlibs="-Wl,--rpath -Wl,/local/chris/modulegen/YACSGEN/fcompo")
563 m=Module("toto",components=[c1],prefix="Install")
564 g=Generator(m,context)
570 g.make_appli("appli",restrict=["KERNEL","GUI","YACS"])
572 Si cette description est dans le fichier monmodule.py, il suffit d'exécuter::
576 ce qui a pour effet de créer le répertoire source du module (toto_SRC), le répertoire d'installation du module (Instal)
577 et un répertoire d'application SALOME (appli).
579 Il faut bien sûr que le module module_generator.py puisse être importé soit en étant dans le répertoire courant soit en étant
582 Il est toujours préférable (bien que non indispensable) de faire le ménage dans le répertoire de travail avant d'exécuter
585 Mise en oeuvre du composant dans un couplage
586 -----------------------------------------------------------------------------------------
587 Creation du fichier de couplage YACS
588 ++++++++++++++++++++++++++++++++++++++++
589 Un fichier de couplage YACS est un fichier XML qui décrit la façon dont des composants SALOME préalablement
590 installés dans une application SALOME sont couplés et exécutés.
592 Pour une documentation sur la façon d'écrire un fichier XML YACS, voir :ref:`schemaxml`.
594 Voici un exemple de fichier YACS mettant en oeuvre le composant Fortran défini ci-dessus ::
597 <container name="A"> </container>
598 <container name="B"> </container>
600 <service name="pipo1" >
601 <component>compo</component>
603 <load container="A"/>
604 <inport name="a" type="double"/>
605 <inport name="b" type="int"/>
606 <inport name="c" type="string"/>
607 <outport name="d" type="double"/>
608 <outport name="e" type="int"/>
609 <outport name="f" type="string"/>
610 <instream name="a" type="CALCIUM_double"/>
611 <instream name="b" type="CALCIUM_double"/>
612 <outstream name="ba" type="CALCIUM_double"/>
613 <outstream name="bb" type="CALCIUM_double"/>
615 <service name="pipo2" >
616 <component>compo</component>
618 <load container="B"/>
619 <inport name="a" type="double"/>
620 <inport name="b" type="int"/>
621 <inport name="c" type="string"/>
622 <outport name="d" type="double"/>
623 <outport name="e" type="int"/>
624 <outport name="f" type="string"/>
625 <instream name="a" type="CALCIUM_double"/>
626 <instream name="b" type="CALCIUM_double"/>
627 <outstream name="ba" type="CALCIUM_double"/>
628 <outstream name="bb" type="CALCIUM_double"/>
632 <fromnode>pipo1</fromnode><fromport>ba</fromport>
633 <tonode>pipo2</tonode><toport>a</toport>
636 <fromnode>pipo1</fromnode><fromport>bb</fromport>
637 <tonode>pipo2</tonode><toport>b</toport>
640 <fromnode>pipo2</fromnode><fromport>ba</fromport>
641 <tonode>pipo1</tonode><toport>a</toport>
644 <fromnode>pipo2</fromnode><fromport>bb</fromport>
645 <tonode>pipo1</tonode><toport>b</toport>
648 <tonode>pipo1</tonode> <toport>a</toport>
649 <value><double>23</double> </value>
652 <tonode>pipo1</tonode> <toport>b</toport>
653 <value><int>23</int> </value>
656 <tonode>pipo1</tonode> <toport>c</toport>
657 <value><string>/local/cchris/SALOME2/SUPERV/YACS/modulegen/data1</string> </value>
660 <tonode>pipo2</tonode> <toport>a</toport>
661 <value><double>23</double> </value>
664 <tonode>pipo2</tonode> <toport>b</toport>
665 <value><int>23</int> </value>
668 <tonode>pipo2</tonode> <toport>c</toport>
669 <value><string>/local/cchris/SALOME2/SUPERV/YACS/modulegen/data2</string> </value>
674 Dans les grandes lignes, le couplage fait intervenir 2 instances du composant compo (pipo1 et pipo2) dont on exécute
675 le service s1. Les ports datastream de ces services sont connectés au moyen des informations fromnode, fromport, tonode, toport
676 dans les sections stream.
677 Les ports dataflow sont initialisés par les sections parameter. En particulier, le répertoire de travail de chaque instance
678 de composant est initialisé à travers le port d'entrée "c" de chaque instance de composant.
679 Chaque instance de composant est exécuté dans un container différent (A et B). Ces noms sont virtuels. C'est SALOME qui au
680 moment du lancement décidera du nom effectif des containers. On ne donne ici que des contraintes sur les containers à utiliser.
681 En l'occurrence, il n'y en a qu'une : containers différents.
683 Exécution du couplage
684 +++++++++++++++++++++++++++++++++++++++++++++
685 Une fois le fichier de couplage écrit au moyen d'un éditeur classique ou de l'éditeur graphique YACS, il est possible
686 de lancer l'exécution.
688 Elle se passe en plusieurs temps :
690 - le lancement de SALOME : exécution du script runAppli de l'application SALOME (``./appli/runAppli -t``). L'application tourne
691 en tâche de fond jusqu'à ce qu'elle soit arrêtée.
692 - le lancement du couplage : exécution du coupleur YACS dans l'environnement de l'application SALOME
693 lancée (``./appli/runSession driver test.xml``) avec test.xml le fichier de couplage.
694 - l'arrêt de l'application : ``./appli/runSession killSalome.py``
696 Les sorties du couplage sont multiples :
698 - la sortie du coupleur lui-même. Si aucune erreur d'exécution ne remonte jusqu'à lui, elle ne contient qu'une information
699 utile : le nom des containers lancé par SALOME pour exécuter les composants. Si des erreurs d'exécution sont remontées
700 jusqu'au coupleur elles sont listées en fin d'exécution.
701 - les sorties des containers. Elles se retrouvent dans le répertoire /tmp avec un nom construit sur la base du nom du container
702 lu dans la sortie du coupleur.
704 Attention : lors de l'arrêt de l'application les containers sont arrêtés brutalement ce qui peut provoquer des pertes d'informations
705 dans leurs fichiers de sortie.
707 La question du répertoire de travail
708 ++++++++++++++++++++++++++++++++++++++
709 Chaque instance de composant est hébergée par un container. Toutes les instances hébergées par un container s'exécutent donc
710 dans un même répertoire qui est celui du container. A partir de la version 4.1.1 de SALOME il est possible de spécifier le répertoire
711 de travail d'un container dans le fichier de couplage. Il suffit d'ajouter la propriété *workingdir* au container. Voici quelques
715 <property name="workingdir" value="/home/user/w1"/>
718 <property name="workingdir" value="$TEMPDIR"/>
721 <property name="workingdir" value="a/b"/>
724 Le container A s'exécutera dans le répertoire "/home/user/w1". S'il n'existe pas il sera créé.
725 Le container B s'exécutera dans un nouveau répertoire temporaire.
726 Le container C s'exécutera dans le répertoire relatif "a/b" (par rapport au répertoire de l'application utilisée pour l'exécution).
727 S'il n'existe pas il sera créé.
729 La question des fichiers
730 ++++++++++++++++++++++++++++
731 Les composants sont des bibliothèques dynamiques ou des modules Python, il n'est pas possible de les lancer dans des scripts shell.
732 Pour les composants qui utilisent des fichiers en entrée et en sortie, il est possible de spécifier dans le fichier de couplage
733 des ports "fichiers" qui effectueront le transfert des fichiers et le nommage local adéquat.
734 Par exemple, un service qui utilise un fichier d'entrée a et produit un fichier de sortie b sera déclaré comme suit::
736 <service name="pipo1">
737 <component>caster</component>
739 <inport name="a" type="file"/>
740 <outport name="b" type="file"/>
743 Ces ports pourront être initialisés ou connectés à d'autres ports "fichiers" comme des ports ordinaires.
744 Par exemple, l'initialisation pour le fichier d'entrée prendra la forme suivante::
747 <tonode>pipo1</tonode> <toport>a</toport>
748 <value><objref>/local/chris/tmp/unfichier</objref> </value>
751 Il n'est pas possible d'initialiser directement un port fichier de sortie. Il faut passer par un noeud spécial qui
752 collecte les sorties. On créera un noeud "dataout" et un lien entre le noeud "pipo1" et le noeud "dataout"::
754 <outnode name="dataout" >
755 <parameter name="f1" type="file" ref="monfichier"/>
758 <fromnode>pipo1</fromnode><fromport>b</fromport>
759 <tonode>dataout</tonode> <toport>f1</toport>
762 ATTENTION: il n'est pas possible d'utiliser le caractère '.' dans les noms des ports. Ceci interdit l'utilisation de noms
763 tels que fort.8 qui sont assez fréquents. Un contournement existe : il suffit de remplacer le '.' par le caractère ':' (donc
764 fort:8 dans notre exemple) pour obtenir le résultat attendu. Bien entendu, les noms contenant des caractères ':' ne sont pas
765 utilisables. Ils devraient être très rares.
767 Exemple d'exécution de composant Aster
768 +++++++++++++++++++++++++++++++++++++++++++
769 L'exécution d'un composant Aster présente quelques particularités qui sont exposées ici.
771 - prise en charge du jeu de commande
772 - spécification des paramètres de la ligne de commande
773 - spécification d'un fichier maillage (.mail)
774 - spécification de variables d'environnement (également valable pour les autres types de composant)
776 Voici un exemple simplifié de schéma YACS comportant un noeud de calcul devant exécuter le service s1 du
777 composant caster (de type Aster) avec une variable d'environnement, un fichier mail un fichier comm
778 et des paramètres de la ligne de commande. Pour un exemple plus complet, voir les fichiers aster.xml et f.comm
781 <service name="pipo1" >
782 <component>caster</component>
783 <property name="MYENVAR" value="25"/>
785 <load container="A"/>
786 <inport name="jdc" type="string"/>
787 <inport name="argv" type="string"/>
788 <inport name="a" type="double"/>
789 <inport name="fort:20" type="file"/>
790 <outport name="d" type="double"/>
791 <instream name="aa" type="CALCIUM_double"/>
792 <outstream name="ba" type="CALCIUM_double"/>
795 <inline name="ljdc" >
797 <code>f=open(comm)</code>
798 <code>jdc=f.read()</code>
799 <code>f.close()</code>
801 <inport name="comm" type="string"/>
802 <outport name="jdc" type="string"/>
806 <tonode>ljdc</tonode> <toport>comm</toport>
807 <value><string>/home/chris/jdc.comm</string> </value>
811 <fromnode>ljdc</fromnode><fromport>jdc</fromport>
812 <tonode>pipo1</tonode> <toport>jdc</toport>
816 <tonode>pipo1</tonode> <toport>argv</toport>
817 <value><string>-rep_outils /aster/outils</string> </value>
821 <tonode>pipo1</tonode> <toport>fort:20</toport>
822 <value><objref>/local/chris/ASTER/instals/NEW9/astest/forma01a.mmed</objref> </value>
825 Tout d'abord, il faut spécifier le jeu de commande. Comme indiqué ci-dessus (`Composant Aster`_), il faut
826 déclarer un port supplémentaire "jdc" de type "string" et l'initialiser ou le connecter. Ici, le port jdc est connecté à
827 un port de sortie d'un noeud python (ljdc) qui lira le fichier .comm dont le chemin lui est donné par
828 son port d'entrée comm. Le transfert de l'identificateur du composant à la commande YACS_INIT est réalisé au moyen
829 de la variable "component" qui est ajoutée automatiquement par le générateur et est disponible
830 pour écrire le fichier .comm.
832 Exemple succinct de .comm ::
835 YACS_INIT(COMPO=component)
839 Pour spécifier des valeurs de paramètres de la ligne de commande, il faut avoir créer un composant
840 avec un port de nom "argv" de type "string". Il suffit alors de donner une valeur à ce port. Ici, on modifie
841 le chemin du répertoire des outils avec le paramètre rep_outils.
843 Pour spécifier un fichier de maillage (.mail) à un composant Aster, il faut ajouter un port fichier au noeud de
846 <inport name="fort:20" type="file"/>
848 Ce port fichier doit avoir comme nom le nom local du fichier tel qu'attendu par Aster. En général Aster utilise
849 le fichier fort.20 comme entrée de LIRE_MAILLAGE. Comme indiqué plus haut, le point de fort.20 ne peut pas être
850 utilisé dans un nom de port, on donnera donc comme nom fort:20. Il faut ensuite donner une valeur à ce port
851 qui correspond au chemin du fichier à utiliser. Ceci est réalisé par une directive parameter::
854 <tonode>pipo1</tonode> <toport>fort:20</toport>
855 <value><objref>/local/chris/ASTER/instals/NEW9/astest/forma01a.mmed</objref> </value>
858 Pour spécifier des variables d'environnement, on passe par les properties du noeud de calcul. Ici, on
859 définit la variable d'environnement MYENVAR de valeur 25.
861 Composants standalone
862 --------------------------------------------------
863 Jusqu'à la version 4.1 de Salome, la seule méthode pour intégrer un composant était de produire
864 une bibliothèque dynamique (\*.so) ou un module python (\*.py). Ce composant est chargé par un
865 exécutable Salome nommé Container soit par dlopen dans le cas de la bibliothèque soit par
866 import dans le cas du module python. Cette méthode est un peu contraignante pour les codes de
867 calcul comme Code_Aster ou Code_Saturne qui sont exécutés dans un environnement particulier
868 de préférence à partir d'un shell script.
870 A partir de la version 4.1.3, il est possible d'intégrer un composant en tant qu'exécutable ou shell
871 script. Cette nouvelle fonctionnalité est pour le moment expérimentale et demande à être testée
872 plus complètement. Elle est cependant utilisable et module_generator a été adapté (à partir de
873 la version 0.3) pour générer des composants standalone. On décrit ci-après les opérations à réaliser
874 pour passer au mode standalone pour chaque type de composant (C/C++, Python, Fortran ou Aster).
877 ++++++++++++++++++++++++++++++++++++++++
878 Pour transformer un composant C/C++ qui se présente de façon standard sous forme d'une
879 bibliothèque dynamique en composant standalone, il suffit d'ajouter deux attributs à sa
882 - l'attribut **kind** : en lui donnant la valeur "exe"
883 - l'attribut **exe_path** : en lui donnant comme valeur le chemin de l'exécutable ou du script shell
884 qui sera utilisé au lancement du composant
886 Voici un exemple de composant C++ modifié pour en faire un composant standalone::
888 c1=CPPComponent("compo1",services=[
889 Service("monservice",inport=[("portentrant","double"),],
890 outport=[("portsortant","double")],
894 exe_path="/local/chris/SALOME2/SUPERV/YACS/modulegen/execpp_essai/prog",
897 Le chemin donné pour exe_path correspond à un exécutable dont le source est le suivant::
899 #include "compo1.hxx"
901 int main(int argc, char* argv[])
907 Il doit être compilé et linké en utilisant l'include compo1.hxx et la librairie libcompo1Exelib.so
908 que l'on trouvera dans l'installation du module généré respectivement dans include/salome
909 et dans lib/salome. On pourra consulter un exemple plus complet dans les sources de la distribution
910 dans le répertoire cppcompo.
912 Il est possible de remplacer l'exécutable par un script shell intermédiaire mais il est bon de savoir que l'appel
913 à yacsinit récupère dans 3 variables d'environnement ( *SALOME_CONTAINERNAME*, *SALOME_INSTANCE*,
914 *SALOME_CONTAINER*), les informations nécessaires à l'initialisation du composant.
917 ++++++++++++++++++++++++++++++++++++++++
918 Pour un composant Fortran, la méthode est identique. On ajoute les deux mêmes attributs :
920 - l'attribut **kind** : en lui donnant la valeur "exe"
921 - l'attribut **exe_path** : en lui donnant comme valeur le chemin de l'exécutable ou du script shell
922 qui sera utilisé au lancement du composant
924 Voici un exemple de composant Fortran standalone::
926 c3=F77Component("compo3",services=[
927 Service("s1",inport=[("a","double"),("b","long"),
929 outport=[("d","double"),("e","long"),
931 instream=[("a","CALCIUM_double","T"),
932 ("b","CALCIUM_double","I")],
933 outstream=[("ba","CALCIUM_double","T"),
934 ("bb","CALCIUM_double","I")],
938 exe_path="/local/chris/SALOME2/SUPERV/YACS/modulegen/YACSGEN/fcompo/prog",
941 Le chemin donné pour exe_path correspond à un exécutable dont le source est le suivant::
947 Il doit être compilé et linké en utilisant la librairie libcompo3Exelib.so que l'on trouvera dans l'installation
948 du module généré dans lib/salome ainsi qu'avec le source Fortran contenant la subroutine S1.
949 On pourra consulter un exemple plus complet dans les sources de la distribution
950 dans le répertoire fcompo.
953 ++++++++++++++++++++++++++++++++++++++++
954 Pour un composant Python, un générateur très rudimentaire a été codé. Il n'est possible que
955 d'ajouter l'attribut **kind** avec la valeur "exe". L'exécutable est automatiquement généré dans l'installation
956 du module. Il n'est pas possible, sauf à modifier l'installation, de le remplacer par un script.
958 Composant Aster standalone
959 ++++++++++++++++++++++++++++++++++++++++
960 Pour un composant Aster, il faut un peu plus de travail. Il faut spécifier 4 attributs :
962 - l'attribut **aster_dir** : qui donne le chemin de l'installation de Code_Aster
963 - l'attribut **kind** : avec la valeur "exe"
964 - l'attribut **asrun** : qui donne le chemin d'accès au lanceur as_run
965 - l'attribut **exe_path** : qui donne le chemin d'un REPERTOIRE dans lequel le générateur va
966 produire plusieurs fichiers qui serviront au lancement de l'exécution de Code_Aster.
968 Voici un exemple de description d'un composant Aster standalone::
970 c1=ASTERComponent("caster",services=[
971 Service("s1",inport=[("argv","string"),("a","double"),
972 ("b","long"),("c","string")],
973 outport=[("d","double")],
974 instream=[("aa","CALCIUM_double","T"),
975 ("ab","CALCIUM_double","I"),
976 ("ac","CALCIUM_integer","I"),
977 ("ad","CALCIUM_real","I"),
978 ("ae","CALCIUM_string","I"),
979 ("af","CALCIUM_complex","I"),
980 ("ag","CALCIUM_logical","I"),
982 outstream=[("ba","CALCIUM_double","T"),
983 ("bb","CALCIUM_double","I")],
986 aster_dir="/aster/NEW9",
987 exe_path="/home/pora/CCAR/SALOME4/exeaster_essai",
988 asrun="/aster/ASTK/ASTK_SERV/bin/as_run",
992 Le générateur produit les fichiers suivants, dans le répertoire **exe_path** :
994 - **aster_component.py** : qui est l'exécutable python qui remplace l'exécutable standard
995 E_SUPERV.py. Il n'a pas à être modifié.
996 - **E_SUPERV.py** : une modification du fichier original contenu dans
997 ``bibpyt/Execution``. Il n'a pas à être modifié.
998 - **config.txt** : le fichier config.txt de l'installation de Code_Aster modifié pour changer
999 l'exécutable python (ARGPYT). Il peut être modifié en dehors de ARGPYT.
1000 - **profile.sh** : une copie du fichier profile.sh de l'installation de Code_Aster (pour que çà marche).
1001 - **caster.comm** : un fichier de commande d'amorçage qui ne contient que la commande DEBUT
1002 en mode PAR_LOT="NON". Il n'a pas à être modifié.
1003 - **make_etude.export** : un fichier de commande pour as_run simplifié. Il est complété
1004 dynamiquement au lancement pour rediriger les fichiers 6,8 et 9 dans REP/messages, REP/resu
1005 et REP/erre. REP est le répertoire d'exécution du composant standalone qui a pour
1006 nom : <composant>_inst_<N>. <N> est un numéro d'exécution qui démarre à 1.
1007 <composant> est le nom du composant (caster, dans notre exemple). Ce fichier peut être
1008 modifié en particulier si on a modifié ou ajouté des commandes Aster.
1010 Bien que l'exécution soit lancée avec un fichier de commandes (caster.comm), il est toujours
1011 nécessaire de spécifier le fichier de commandes "effectif" dans le fichier de couplage xml.
1012 La seule différence avec un composant sous forme de bibliothèque est que ce dernier fichier de
1013 commandes NE DOIT PAS contenir de commande DEBUT (sinon, plantage inexplicable).
1015 Exemple de couplage avec composants standalone
1016 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1017 En rassemblant tous les éléments ci-dessus, le couplage d'un composant Aster standalone avec un composant
1018 Fortran standalone s'écrit comme suit (fichier astexe.py dans la distribution) ::
1020 from module_generator import Generator,Module
1021 from module_generator import ASTERComponent,Service,F77Component
1023 context={'update':1,"prerequisites":"/home/caremoli/pkg/env.sh",
1024 "kernel":"/home/pora/CCAR/SALOME4/Install/KERNEL_V4_1"}
1026 install_prefix="./exe_install"
1027 appli_dir="exe_appli"
1029 c1=ASTERComponent("caster",services=[
1030 Service("s1",inport=[("a","double"),("b","long"),
1032 outport=[("d","double")],
1033 instream=[("aa","CALCIUM_double","T"),
1034 ("ab","CALCIUM_double","I"),
1035 ("ac","CALCIUM_integer","I"),
1036 ("ad","CALCIUM_real","I"),
1037 ("ae","CALCIUM_string","I"),
1038 ("af","CALCIUM_complex","I"),
1039 ("ag","CALCIUM_logical","I"),
1041 outstream=[("ba","CALCIUM_double","T"),
1042 ("bb","CALCIUM_double","I")],
1045 aster_dir="/aster/NEW9",
1046 exe_path="/home/pora/CCAR/SALOME4/exeaster_essai",
1047 asrun="/aster/ASTK/ASTK_SERV/bin/as_run",
1051 c2=F77Component("cedyos",services=[
1052 Service("s1",inport=[("a","double"),("b","long"),
1054 outport=[("d","double"),("e","long"),
1056 instream=[("a","CALCIUM_double","T"),
1057 ("b","CALCIUM_double","I")],
1058 outstream=[("ba","CALCIUM_double","T"),
1059 ("bb","CALCIUM_double","I"),
1060 ("bc","CALCIUM_integer","I"),
1061 ("bd","CALCIUM_real","I"),
1062 ("be","CALCIUM_string","I"),
1063 ("bf","CALCIUM_complex","I"),
1064 ("bg","CALCIUM_logical","I"),
1069 exe_path="/home/pora/CCAR/SALOME4/exeedyos_essai/prog",
1072 g=Generator(Module("titi",components=[c1,c2],prefix=install_prefix),context)
1078 g.make_appli(appli_dir,restrict=["KERNEL","YACS"])
1080 Le fichier de couplage xml et le fichier de commandes Aster correspondants peuvent être consultés
1081 dans la distribution (asterexe.xml et fexe.xml).
1082 On trouvera les éléments complémentaires d'implantation dans le répertoire fcompo (composant cedyos)
1083 et dans le répertoire aster (composant caster).