Salome HOME
Merge branch V7_3_1_BR
[modules/yacs.git] / doc / yacsgen.rst
1
2 :tocdepth: 3
3
4 .. _yacsgen:
5
6 YACSGEN: SALOME module generator
7 ==================================================
8
9 YACSGEN is a python package (module_generator) that automatically fabricates a SALOME module starting 
10 from a synthetic description of the components that it will contain.  This description is made in the python language.
11
12 YACSGEN includes since version 6.5 the HXX2SALOME functionalities, and is therefore able to also generate the 
13 implementation of C++ dataflow components.
14
15 The characteristics of these components are not general but they should facilitate integration of many scientific 
16 calculation components.
17
18 How to get it
19 -----------------------------------------------------------------
20 It is a module (named YACSGEN) in the SALOME CVS TOOLS base that is distributed with main SALOME modules.
21
22 Supported versions and architectures
23 -----------------------------------------------------------------
24 YACSGEN needs a Python version  >= 2.4  and < 3.0.  It runs on a 32-bit and 64-bit 
25 architecture.
26
27 Installation
28 ----------------------------
29 If you get the source archive, simply decompress and untar the archive (YACSGEN-x.y.tar.gz) 
30 and add the directory thus created to PYTHONPATH. YACSGEN is also provided with binaries installation of SALOME.
31
32 Description of a SALOME module
33 --------------------------------------------------------
34 A SALOME module is described using Python instructions and definitions contained in the Python module_generator package.
35
36 The first action to be done is to import these definitions::
37
38      from module_generator import Generator,Module,PYComponent
39      from module_generator import CPPComponent,Service,F77Component
40
41 If you want to import all definitions, you can do that::
42
43      from module_generator import *
44
45 A SALOME module is described by giving its name <modulename> together with the list of its 
46 components (<components list>) and the name of the directory in which it will be installed (<prefix>).
47
48 Its description is in the following form::
49
50   m=Module(<modulename>,components=<components list>,prefix=<prefix>)
51
52 The statement for a module named "mymodule" with a component c1 (see below for a description of components) that 
53 will be installed in the "Install" directory will be::
54
55   m=Module("mymodule",components=[c1],prefix="Install")
56
57 Description of components
58 ------------------------------------------------
59 Several types of components can be created:
60
61 - the C / C++ type
62 - the Fortran 77 type
63 - the Python type
64 - the Aster type.
65
66 All these types have a similar description.  We will begin with the C++ type and then describe the main differences for the other types.
67
68 C / C++ component
69 ++++++++++++++++++++++++++++++++++++++++
70 Firstly, a C++ component has a name.  This name will be used subsequently when it is required to create instances of this component.  
71 Once compiled and installed, the SALOME module will contain a dynamic library named lib<compo>Engine.so, in which <compo> is the component name.  
72 A C++ component is implemented as a remote executable C++ object.
73
74 A C++ component has one or several services.  Each service has a name that is the name of the method of the C++ object 
75 that corresponds to the component.  Each service may have input and output dataflow ports and input and output datastream ports.
76
77 A first service with dataflow ports
78 """""""""""""""""""""""""""""""""""""""""""""""""""""""
79 The only possible types for dataflow ports for the moment are:
80
81 - double:  scalar equivalent to a C double
82 - long:  scalar equivalent to a C long
83 - string:  equivalent to a C char* (character string with arbitrary length)
84 - file: a file object
85 - dblevec:  doubles vector
86 - stringvec:  strings vector
87 - intvec:  longs vector
88 - pyobj:  python object serialised with pickle (only works with components implemented in Python).
89
90 A port is described by a python tuple with length 2, the first value is the name of the port and the second value is the type name.  
91 Input ports are described by a list of these tuples as well as the output ports.
92
93 A small example is better than a long description.  A component named “mycompo” with a service named “myservice” which has 
94 an input dataflow port named “inputport” of the double type and an output data flow port with name “outputport” of the double 
95 type will have the following description::
96
97
98       c1=CPPComponent("mycompo",
99                       services=[
100                                 Service("myservice",
101                                         inport=[("inputport","double"),],
102                                         outport=[("outputport","double")],
103                                        ),
104                                ]
105                      )
106
107 c1 is an intermediate Python variable that will be used to describe the list of components of a 
108 module: (components=[c1]) for a module with a single component.
109
110 In fact, this component is not particularly useful because during execution, it will take a double at the input to the 
111 execution and will provide a double at the output from the execution, but it does nothing in the meantime.  
112 Therefore, a content has to be added to it.  This content will be specified in two service attributes,  **defs** and **body**.  
113
114 - defs will contain the C++ definition code (for example, #include<iostream>)
115 - body will contain the C++ code that will be executed between the input and the output (for example, outputport=2*inputport).  
116
117 The final description becomes::
118
119       c1=CPPComponent("mycompo",
120                        services=[
121                                  Service("myservice",
122                                          inport=[("inputport","double"),],
123                                          outport=[("outputport","double")],
124                                          defs="#include <iostream>",
125                                          body="outputport=2*inputport;",
126                                         ),
127                                 ]
128                      )
129
130 Adding datastream ports to the service
131 """"""""""""""""""""""""""""""""""""""""""""""
132 Datastream ports are added to the “myservice” service by adding **instream** and **outstream** attributes to the description.  
133 These attributes must be lists of triplets with the following elements:
134
135 1.  the port name
136 2.  the port type
137 3.  the time (“T”) or iteration (“I”) dependency mode (refer to :ref:`calcium` for further details)
138
139 Possible types are “CALCIUM_double”, “CALCIUM_integer”, "CALCIUM_long", “CALCIUM_real”, “CALCIUM_string”, “CALCIUM_logical” and “CALCIUM_complex”.
140
141 The description for an input datastream port and an output port in time dependency becomes::
142
143       c1=CPPComponent("mycompo",
144                       services=[
145                                 Service("myservice",
146                                         inport=[("inputport","double"),],
147                                         outport=[("outputport","double")],
148                                         instream=[("porta","CALCIUM_double","T")],
149                                         outstream=[("portb","CALCIUM_double","T")],
150                                         defs="#include <iostream>",
151                                         body="ouputport=2*inputport;",
152                                         ),
153                                ]
154                      )
155
156 Obviously, calls to the CALCIUM library have to be added into body to make the service genuinely functional.
157
158 Adding a second service to the component
159 """""""""""""""""""""""""""""""""""""""""""""""""
160 If a second service has to be added for the component, simply add another service description::
161
162       c1=CPPComponent("mycompo",
163                       services=[
164                                 Service("myservice",
165                                         inport=[("inputport","double"),],
166                                         outport=[("outputport","double")],
167                                         instream=[("porta","CALCIUM_double","T")],
168                                         outstream=[("portb","CALCIUM_double","T")],
169                                         defs="#include <iostream>",
170                                         body="ouputport=2*inputport;",
171                                         ),
172                                 Service("serv2",
173                                         inport=[("a","double"),("b","long")],
174                                         outport=[("c","double")],
175                                         body="c=b*a",
176                                        ),
177                                ]
178                      )
179
180 In this description, a second service name “serv2” has been added with 2 input dataflow ports (a and b) and an output dataflow port (c).  
181 The service is reduced to its simplest expression:  it returns the product of its 2 inputs.
182
183 Link with external libraries
184 """"""""""""""""""""""""""""""""""""""""""""""""""""
185 We have seen that the **defs** and **body** attributes are sufficient to define the body of the service but it is often more practical 
186 to use external libraries rather than put everything into these 2 attributes. This is possible provided that everything necessary 
187 for the component link step is indicated in the **libs** and **rlibs** attributes of the component.
188
189 For example, we can have::
190
191
192       c1=CPPComponent("mycompo",
193                       services=[
194                                 Service("myservice",
195                                         inport=[("inputport","double"),],
196                                         outport=[("outputport","double")],
197                                         defs="extern double myfunc(double);",
198                                         body="outputport=myfunc(inputport);",
199                                        ),
200                                ],
201                       libs="-L/usr/local/mysoft -lmybib",
202                       rlibs="-Wl,--rpath -Wl,/usr/local/mysoft"
203                       )
204
205 The **rlibs** attribute is not compulsory but it can be used to indicate a search path for dynamic libraries in execution.  
206 **libs** is used during the link phase.  **rlibs** is only used during execution, it avoids the need to set the LD_LIBRARY_PATH 
207 environment variable to find the dynamic library.
208
209 Adding includes
210 """"""""""""""""""""""""""""""""""""""""""""""""""""
211 Includes will be added using the **defs** attribute.  For example::
212
213    defs="""#include "myinclude.h" """
214
215 The includes path will be specified in the **includes** attribute of the component in the following form::
216
217
218    defs="""#include "myinclude.h"
219    extern double myfunc(double);
220    """
221    c1=CPPComponent("mycompo",
222                    services=[
223                              Service("myservice",
224                                      inport=[("inputport","double"),],
225                                      outport=[("outputport","double")],
226                                      defs=defs,
227                                      body="outputport=myfunc(inputport);",
228                                     ),
229                             ],
230                    libs="-L/usr/local/mysoft -lmybib",
231                    rlibs="-Wl,--rpath -Wl,/usr/local/mysoft",
232                    includes="-I/usr/local/mysoft/include",
233                   )
234
235 Adding sources
236 """"""""""""""""""""""""""""""""""""""""""""""""""""
237 It is possible to add some source files with the **sources** attribute (a list of source files will be given).
238
239 For example, instead of using an external library, we could implement the function myfunc in a file 
240 named myfunc.cpp. The description will be::
241
242    defs="""#include "myinclude.h"
243    extern double myfunc(double);
244    """
245    c1=CPPComponent("mycompo",
246                    services=[
247                              Service("myservice",
248                                      inport=[("inputport","double"),],
249                                      outport=[("outputport","double")],
250                                      defs=defs,
251                                      body="outputport=myfunc(inputport);",
252                                     ),
253                             ],
254                    sources=["myfunc.cpp"],
255                    includes="-I/usr/local/mysoft/include",
256                   )
257
258
259 HXX2SALOME components
260 +++++++++++++++++++++
261
262 For computational codes which exchange arrays, MED meshes and fields, the implementation of the SALOME component is more complex.
263 hxx2salome is a Salome generation tool for dataflow C++ components, which is available in SALOME since version 4.
264  
265 Its principle is to start the integration of a code (written in Fortran/C/C++ or any C-compatible language) 
266 by writing a C++ layer (a class), which purpose is to drive the underlying code, and exchange data at C++ standard 
267 (c++ integral types, STL strings and vectors) and MED types for numerical meshes and fields. 
268
269 Then the Salome CORBA layer (a SALOME C++ component) is generated automatically by the tool.
270 The implementation of the component, which has to be provided in standard YACSGEN through the defs and body attributes, 
271 is generated here through analysing the interface of the c++ layer written above the code.
272
273 hxx2salome tool, although still available in Salome 6 as a standalone tool, was merged within YACSGEN.
274 For the general principles of HXX2SALOME, and the detailed documentation, please refer to 
275 the HXX2SALOME chapter of this documentation (:ref:`hxx2salome`). We will only present here the embedded use of HXX2SALOME within YACSGEN.
276
277
278 The tool can be used in two different ways:
279
280   - within a YACSGEN python script, by using the **HXX2SALOMEComponent** class combined with other YACSGEN CLASSES.
281   - with the **hxx2salome.py** script, a python executable which use YACSGEN classes to reproduce the interface of the former former hxx2salome bash script.
282
283
284 using the **HXX2SALOMEComponent** class 
285 """""""""""""""""""""""""""""""""""""""
286
287 The merge of hxx2salome within YACSGEN was done by adding a new class, called **HXX2SALOMEComponent**, to the YACSGEN package. 
288 Given a C++ component (a C++ layer which wraps a computational code), HXX2SALOMEComponent class parses its interface 
289 (as defined in its .hxx header), extracts the public methods, analyses the types of these methods, 
290 and uses this type information to generate the implementation. All the information is then given to YACSGEN which generate a ready-to-use component.
291
292 As an example, let's suppose we have a code called mycode, wrapped by a C++ layer 
293 (a dynamic library libmycodeCXX.so, and its interface "mycode.hxx", both located in directory mycodecpp_root_dir).
294 To generate the SALOME C++ component, one should add in his YACS script: ::
295
296         from module_generator HXX2SALOMEComponent
297         c1=HXX2SALOMEComponent("mycode.hxx", 
298                                "libmycodeCXX.so", 
299                                 mycodecpp_root_dir ) )
300
301 The HXX2SALOMEComponent takes three arguments : the C++ header, the C++ library, and the path where to find them. The class does the parsing of c++ and generate all the necessary information for YACSGEN to generate the SALOME module.
302
303 Finally, if the code is parallel (mpi), one has to use instead the **HXX2SALOMEParaComponent**. This class work exactly in the same way, but generates also 
304 the mpi code necessary for a parallel SALOME component.
305
306
307 Using **hxx2salome.py** executable
308 """"""""""""""""""""""""""""""""""
309
310 **hxx2salome.py** script is a python executable which use YACSGEN classes to reproduce the interface of the former hxx2salome bash script.
311 The script takes optional arguments, followed by four mandatory arguments: ::
312
313         hxx2salome.py --help
314         
315         usage:
316         hxx2salome.py [options] <CPPCOMPO>_root_dir lib<CPPCOMPO>.hxx <CPPCOMPO>.so installDir
317
318         generate a SALOME component that wrapps given the C++ component
319
320         Mandatory arguments:
321
322           - <CPPCOMPO>_root_dir   : install directory (absolute path) of the c++ component
323           - <CPPCOMPO>.hxx        : header of the c++ component"
324           - lib<CPPCOMPO>.so      : the shared library containing the c++ component
325           - installDir            : directory where the generated files and the build should be installed
326
327           Note that <CPPCOMPO>.hxx and lib<CPPCOMPO>.so should be found in <CPPCOMPO>_root_dir)
328
329
330
331         options:
332           -h, --help       show this help message and exit
333           -e ENVIRON_FILE  specify the name of a environment file (bash/sh) that will
334                            be updated
335           -g               to create a generic gui in your component building tree
336           -c               to compile after generation
337
338
339 The mandatory argument are respectively : 
340  - the path where the C++ component was installed,
341  - within this path the name of the interface header, 
342  - the name of the dynamic library,
343  - and finally the location where to generate and compile the Salome component. 
344
345 As an example, the command to generate the mycode component would be: ::
346
347         hxx2salome.py -c -g -e salome.sh 
348               mycodecpp_root_dir mycode.hxx 
349               libmycodeCXX.so   <absolute path where to install generated component>
350
351
352
353
354 Fortran component
355 ++++++++++++++++++++++++++++++++++++++++
356 A Fortran component is described like a C++ component, except that there are a few differences.  Firstly, the F77Component 
357 definition object is used instead of the CPPComponent.  Then, a special additional interface is made in Fortran.  
358 It is assumed that Fortran functions are implemented in a library (dynamic or static) that will be linked with the component and 
359 that will have several entry points with the same names as the component services.  The call to this entry point will be added 
360 automatically after the C++ code supplied by the user in the **body** attribute.
361
362 This makes it possible to decouple practically the entire implementation of the Fortran component that will be in 
363 the external library or sources, from the implementation of the SALOME component that will only be used for encapsulation.
364
365 The following example will be used to specify these final concepts::
366
367      c3=F77Component("compo3",
368                      services=[
369                                Service("s1",
370                                        inport=[("a","double"),("b","long"),
371                                                ("c","string")],
372                                        outport=[("d","double"),("e","long"),
373                                                 ("f","string")],
374                                        instream=[("a","CALCIUM_double","T"),
375                                                  ("b","CALCIUM_double","I")],
376                                        outstream=[("ba","CALCIUM_double","T"),
377                                                   ("bb","CALCIUM_double","I")],
378                                        defs="#include <unistd.h>",
379                                        body="chdir(c);"
380                                       ),
381                               ],
382                      libs="-L/usr/local/fcompo -lfcompo",
383                      rlibs="-Wl,--rpath -Wl,/usr/local/fcompo"
384                     )
385
386 The Fortran “compo3” component has dataflow and datastream ports like the C++ component.  The Fortran dynamic library 
387 that contains the Fortran entry point *s1* will be linked by means of the **libs** and **rlibs** attributes of the description.  
388 The Fortran component also supports the **includes** and **sources** attributes.
389  
390 The Fortran subroutine with name **s1** must have a signature with a first argument that is used to transmit the address of
391 the component and all following arguments that are used to transmit the values of the inport and outport ports. The instream and 
392 outstream ports are managed internally to the subroutine through calls to the CALCIUM API with the address of the component
393 as first argument.
394
395 An example of subroutine for the above definition follows:
396
397 .. code-block:: fortran
398
399        SUBROUTINE S1(compo,A,B,C,D,E,F)
400    C  implementation of service s1 with inport a,b,c and outport d,e,f and stream ports
401        include 'calcium.hf'
402        integer compo
403        real*8 a,d
404        integer b,e
405        character*(*) c,f
406
407        CALL cpldb(COMPO,CP_TEMPS,t0,t1,iter,'aa',1,n,ss,info)
408        CALL cpldb(COMPO,CP_ITERATION,t0,t1,iter,'ab',1,n,zz,info)
409        CALL cplen(COMPO,CP_ITERATION,t0,t1,iter,'ac',1,n,zn,info)
410        CALL cplre(COMPO,CP_ITERATION,t0,t1,iter,'ad',1,n,yr,info)
411        CALL cplch(COMPO,CP_ITERATION,t0,t1,iter,'ae',1,n,tch,info)
412        CALL cplcp(COMPO,CP_ITERATION,t0,t1,iter,'af',1,n,tcp,info)
413        CALL cpllo(COMPO,CP_ITERATION,t0,t1,iter,'ag',3,n,tlo,info)
414
415        CALL cpeDB(COMPO,CP_TEMPS,t0,1,'ba',1,tt,info)
416        CALL cpeDB(COMPO,CP_ITERATION,t0,1,'bb',1,tp,info)
417
418        d=4.5
419        e=3
420        f="zzzzzzzzzzzzzzz"
421
422        END
423
424 As a special case, since version 5.1.4, the first argument (address of the component) is not included, if there is no
425 instream and outstream ports.
426
427 Same example without stream ports:
428
429 .. code-block:: fortran
430
431        SUBROUTINE S1(A,B,C,D,E,F)
432    C  implementation of service s1 with inport a,b,c and outport d,e,f
433        real*8 a,d
434        integer b,e
435        character*(*) c,f
436        d=4.5
437        e=3
438        f="zzzzzzzzzzzzzzz"
439        END
440
441 A piece of C++ code can be added before the call to the Fortran entry point.  This piece of code must be put into the **body** 
442 attribute with any definitions in **defs**.  In this case, we use the “c” input dataflow variable to change the directory with the call to chdir.
443
444 Python component
445 ++++++++++++++++++++++++++++++++++++++++
446 A Python component is also described like a C++ component.  The only differences are in the Python object to be used to 
447 define it:  PYComponent instead of CPPComponent and in the content of the **defs** and **body** attributes that must contain 
448 Python code and not C++.
449
450 .. warning::
451    The indentation of the complete block of code is automatically handled but not the internal indentation of the block.
452
453 Example Python component::
454
455       pyc1=PYComponent("mycompo",
456                        services=[
457                                  Service("myservice",
458                                          inport=[("inputport","double"),],
459                                          outport=[("outputport","double")],
460                                          defs="import sys",
461                                          body="      outputport=2*inputport;",
462                                         ),
463                                 ]
464                       )
465
466 The equivalent of the assembly with external libraries is done in this case with the possibility of importing external 
467 Python modules.  Simply add the **python_path** attribute to the description of the component to obtain this possibility.  
468 The value to be given is a list of directories that might contain modules to be imported.
469
470 Example::
471
472      pyc1=PYComponent("mycompo",
473                       services=[
474                                 Service("myservice",
475                                         inport=[("inputport","double"),],
476                                         outport=[("outputport","double")],
477                                        ),
478                                ],
479                       python_path=["/usr/local/mysoft","/home/chris/monsoft"],
480                      )
481
482 .. _aster:
483
484 Aster component
485 ++++++++++++++++++++++++++++++++++++++++
486 *Code_Aster* is a software package for finite element analysis and numeric simulation in structural mechanics developed by EDF.
487
488 An Aster component is a component that is a little bit special because the software functions are implemented in Fortran but
489 they are activated by a command supervisor written in Python.  Finally, this supervisor executes a Python script but the data 
490 transfer between Python and Fortran and the integration of the command supervisor into a SALOME component have to be managed.
491
492 The start point is that it is assumed that there is an Aster installation that provides an aster python module in the form of 
493 an importable dynamic library (astermodule.so) and not a specific Python interpreter linked with this module, as is the case 
494 in the existing installation.
495
496 An Aster component is described as a Python component to which several important attributes have to be added.
497
498 - the **python_path** attribute:  this indicates the path of the directory containing the aster module (astermodule.so)
499 - the **aster_dir** attribute:  this indicates the path of the Aster installation directory
500 - the **argv** attribute:  this initialises command line parameters.  For example, it will be set equal to the value 
501   of memjeveux (``argv=[“-memjeveux”,”10”]``) or rep_outils.
502
503 The following shows a small example description of an Aster component with a single service provided with 3 input dataflow 
504 ports, one output dataflow port, 7 input datastream ports and one output datastream port::
505
506     c1=ASTERComponent("caster",
507                       services=[
508                                 Service("s1",
509                                         inport=[("a","double"),("b","long"),
510                                                 ("c","string")],
511                                         outport=[("d","double")],
512                                         instream=[("aa","CALCIUM_double","T"),
513                                                   ("ab","CALCIUM_double","I"),
514                                                   ("ac","CALCIUM_integer","I"),
515                                                   ("ad","CALCIUM_real","I"),
516                                                   ("ae","CALCIUM_string","I"),
517                                                   ("af","CALCIUM_complex","I"),
518                                                   ("ag","CALCIUM_logical","I"),
519                                                  ],
520                                        outstream=[("ba","CALCIUM_double","T"),
521                                                   ("bb","CALCIUM_double","I")],
522                                       ),
523                                ],
524                       aster_dir="/local/chris/ASTER/instals/NEW9",
525                       python_path=["/local/chris/modulegen/YACSGEN/aster/bibpyt"],
526                       argv=["-memjeveux","10",
527                             "-rep_outils","/local/chris/ASTER/instals/outils"],
528                      )
529
530 .. warning::
531    Do not use the name “aster” for the component because this name is reserved for the *Code_Aster* python module.  
532    If the name “aster” is used, the behaviour will be completely erratic.
533
534 Although its description is very similar to the behaviour of a Python component, there is an important difference in use.  
535 The Aster component needs the description of a command set to run.  This command set is transferred to each service of the 
536 component in the form of a text in an input dataflow port named “jdc” with type “string”.  Therefore after generation, this 
537 Aster component will have four input dataflow ports (“jdc”, “a”, “b”, “c”) and not three as indicated in the description.  
538 It is important not to forget to initialise the “jdc” port in the coupling file with a command set.
539
540 The command supervisor has been integrated into a SALOME component and the variables received in the dataflow ports are available 
541 during execution of the command set.  Similarly, values for output dataflow ports are defined by values of variables derived 
542 from execution of the command set.
543
544 .. warning::
545    **Beware with the execution mode**.  The command supervisor has 2 execution modes (PAR_LOT=”OUI” or PAR_LOT=”NON” that are 
546    specified in the DEBUT command) (PAR_LOT = BY_BATCH).  In PAR_LOT=”OUI” mode, it is compulsory to terminate the command set 
547    with a FIN (END) command which has the effect of interrupting execution.  This is not the preferred method of operation with YACS.  
548    It is preferable to use PAR_LOT=”NON” mode without adding the FIN command, which avoids interrupting the execution prematurely.
549
550 Dynamically importable Aster module and link with YACS
551 """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
552 These two points are not handled by YACSGEN.  They must be processed separately in a context similar to the context of an Aster developer.
553
554 It is assumed that there is an Aster installation, that it is required to create a dynamically importable Python Aster module, and 
555 that a few commands are to be added to Aster to exchange data through YACS datastream ports.
556
557 To remain simple, three commands:  YACS_INIT, ECRIRE_MAILLAGE and LECTURE_FORCE are added, for which the catalogs are::
558
559              YACS_INIT=PROC(nom="YACS_INIT",op=181, fr="YACS initialisation",
560                                   COMPO=SIMP(statut='o',typ='I'),
561                            )
562              ECRIRE_MAILLAGE=PROC(nom="ECRIRE_MAILLAGE",op=78, fr="write mesh")
563              LECTURE_FORCE=PROC(nom="LECTURE_FORCE",op=189, fr="read force")
564
565 The first YACS_INIT command initialises Aster in the YACS context.  It has a single simple keyword COMPO (integer type) that 
566 will be used to transfer the SALOME component identifier to other commands.  This identifier will be stored in a Fortran COMMON. 
567 It is essential for calls to subprograms CPLxx and CPExx that will be used in the other two ECRIRE_MAILLAGE and LECTURE_FORCE commands.
568
569 The other two commands do not have any keyword and they retrieve the identifier from the COMMON.
570
571 The operators will be written as follows (without the declarations):
572
573 .. code-block:: fortran
574
575           SUBROUTINE OP0189 ( IER )
576     C     COMMANDE:  LECTURE_FORCE
577           include 'calcium.hf'
578           COMMON/YACS/ICOMPO
579           CALL cpldb(ICOMPO,CP_TEMPS,t0,t1,iter,'aa',1,n,ss,info)
580           CALL cpldb(ICOMPO,CP_ITERATION,t0,t1,iter,'ab',1,n,zz,info)
581           CALL cplen(ICOMPO,CP_ITERATION,t0,t1,iter,'ac',1,n,zn,info)
582           CALL cplre(ICOMPO,CP_ITERATION,t0,t1,iter,'ad',1,n,yr,info)
583           CALL cplch(ICOMPO,CP_ITERATION,t0,t1,iter,'ae',1,n,tch,info)
584           CALL cplcp(ICOMPO,CP_ITERATION,t0,t1,iter,'af',1,n,tcp,info)
585           CALL cpllo(ICOMPO,CP_ITERATION,t0,t1,iter,'ag',3,n,tlo,info)
586           END
587
588           SUBROUTINE OP0078 ( IER )
589     C     COMMANDE:  ECRIRE_MAILLAGE
590           include 'calcium.hf'
591           COMMON/YACS/ICOMPO
592           CALL cpeDB(ICOMPO,CP_TEMPS,t0,1,'ba',1,tt,info)
593           CALL cpeDB(ICOMPO,CP_ITERATION,t0,1,'bb',1,tp,info)
594           END
595
596 Finally, an astermodule.so dynamic library must be constructed, and all necessary Python modules must be placed in a directory 
597 that will be indicated in the **python_path** attribute.  Different methods can be used to obtain this result.  
598 The following Makefile is one of them:
599
600 .. code-block:: make
601
602      #compiler
603      FC=gfortran
604      #SALOME
605      KERNEL_ROOT_DIR=/local/chris/SALOME/RELEASES/Install/KERNEL_V5
606      KERNEL_INCLUDES=-I$(KERNEL_ROOT_DIR)/include/salome
607      KERNEL_LIBS= -L$(KERNEL_ROOT_DIR)/lib/salome -lCalciumC -lSalomeDSCSuperv \
608                   -lSalomeDSCContainer -lSalomeDatastream -lSalomeDSCSupervBasic \
609                   -Wl,--rpath -Wl,$(KERNEL_ROOT_DIR)/lib/salome
610      #ASTER
611      ASTER_ROOT=/local/chris/ASTER/instals
612      ASTER_INSTALL=$(ASTER_ROOT)/NEW9
613      ASTER_PUB=$(ASTER_ROOT)/public
614      ASTER_LIBS = -L$(ASTER_INSTALL)/lib -laster \
615              -L$(ASTER_PUB)/scotch_4.0/bin -lscotch -lscotcherr \
616              -lferm -llapack -lhdf5
617      SOURCES=src/op0078.f src/op0189.f
618      CATAPY=catalo/ecrire_maillage.capy  catalo/lecture_force.capy
619
620      all:pyth cata astermodule
621      pyth:
622        cp -rf $(ASTER_INSTALL)/bibpyt .
623      cata: commande/cata.py
624        cp -rf commande/cata.py* bibpyt/Cata
625      commande/cata.py:$(CATAPY)
626        $(ASTER_ROOT)/ASTK/ASTK_SERV/bin/as_run make-cmd
627      astermodule:astermodule.so pyth
628        cp -rf astermodule.so bibpyt
629      astermodule.so: $(SOURCES)
630        $(FC) -shared -o $@ $(SOURCES) $(KERNEL_INCLUDES) $(ASTER_LIBS) $(KERNEL_LIBS)
631
632 Modify command line parameters during execution
633 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
634 The **argv** attribute gives initial values to arguments such as “memjeveux” but these values are used by the generator to 
635 build the component and therefore remain constant afterwards during execution.
636
637 If you want to modify these values during execution, you need to add an input port named “argv” with type “string”.  The character 
638 string that will be given as the value of this port will be used by the component to modify the arguments of the command 
639 line (see :ref:`execaster` for an example use).
640
641 Management of the elements file
642 """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
643 The finite elements file is automatically copied into the working directory under the name elem.1.  
644 The component uses the **aster_dir** attribute to locate the origin file.
645
646 Supported Aster versions
647 """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
648 YACSGEN can function with Aster 9 and higher versions.
649
650 SALOME module generator
651 -----------------------------------------------------------
652 The SALOME module is created by a generator constructed from the description of the SALOME module (m) seen previously 
653 and a Python dictionary (context) that provides some environment parameters::
654
655    g=Generator(m,context)
656
657 The following parameters are mandatory for this context:
658
659 - **prerequisites**:  indicates the path of a shell script that sets the environment variables of SALOME prerequisites
660 - **kernel**:  indicates the installation path of the SALOME KERNEL module
661 - **update**:  set equal to 1 at the moment (future development)
662
663 Example creation of generator:: 
664
665      context={'update':1,
666               "prerequisites":"/local/cchris/.packages.d/envSalome",
667               "kernel":"/local/chris/SALOME/RELEASES/Install/KERNEL_V5"
668               }
669      g=Generator(m,context)
670
671 Once this generator has been created, simply call its commands to perform the necessary operations.
672
673 - SALOME module generation:  ``g.generate()``
674 - initialise automake:  ``g.bootstrap()``
675 - execute the configure script:  ``g.configure()``
676 - compilation:  ``g.make()``
677 - installation in the directory <prefix>:  ``g.install()``
678 - create a SALOME application in the directory **appli_dir**::
679
680         g.make_appli(appli_dir,restrict=<liste de modules>,
681                                altmodules=<dictionnaire de modules>)
682
683 These commands do not use any parameters except for make_appli that uses 3 parameters:
684
685 - **appliname**:  the name of the directory that will contain the SALOME application
686 - **restrict**:  a list of SALOME module names to put into the application.  By default, make_appli puts all SALOME modules 
687   that it can detect into the application (neighbour directories of KERNEL with the same suffix as KERNEL.  If the directory 
688   of the KERNEL module is called KERNEL_V5, then it will use GUI_V5, GEOM_V5, etc.). If restrict is provided, make_appli will 
689   only use the modules listed.
690 - **altmodules**:  a dictionary of other modules.  The key gives the name of the module.  The corresponding value gives the path 
691   of the module installation directory.  For example ``altmodules={"mymodule":"/local/chris/amodule"}``
692
693 Fabrication of the SALOME module
694 -----------------------------------------------------
695 The module will be fabricated by executing a Python file that contains its description, by inputting data into the generator  
696 and generator commands.
697
698 This gives something like the following for a module with a single Fortran component:
699
700 .. code-block:: python
701
702   from module_generator import Generator,Module
703   from module_generator import PYComponent,CPPComponent,Service,F77Component
704
705   context={"update":1,
706            "prerequisites":"/local/cchris/.packages.d/envSalome",
707            "kernel":"/local/chris/SALOME/RELEASES/Install/KERNEL_V5"
708           }
709
710
711   c1=F77Component("compo",
712                   services=[
713                             Service("s1",
714                                     inport=[("a","double"),
715                                             ("b","long"),
716                                             ("c","string")],
717                                     outport=[("d","double"),("e","long"),
718                                              ("f","string")],
719                                     instream=[("a","CALCIUM_double","T"),
720                                               ("b","CALCIUM_double","I")],
721                                     outstream=[("ba","CALCIUM_double","T"),
722                                                ("bb","CALCIUM_double","I")],
723                                     defs="#include <unistd.h>",
724                                     body="chdir(c);"
725                                    ),
726                            ],
727                   libs="-L/local/chris/modulegen/YACSGEN/fcompo -lfcompo",
728                   rlibs="-Wl,--rpath -Wl,/local/chris/modulegen/YACSGEN/fcompo")
729
730   m=Module("mymodule",components=[c1],prefix="Install")
731   g=Generator(m,context)
732   g.generate()
733   g.bootstrap()
734   g.configure()
735   g.make()
736   g.install()
737   g.make_appli("appli",restrict=["KERNEL","GUI","YACS"])
738
739 If this description is in the mymodule.py file, all that is required is to execute::
740
741    python mymodule.py
742
743 which has the effect of creating the module source directory (mymodule_SRC), the module installation directory (Install) and a 
744 SALOME application directory (appli).
745
746 Obviously, it must be possible to import the **module_generator** package either while being in the current directory or in the PYTHONPATH.
747
748 It is always preferable (although not essential) to clean up the working directory before executing the generator.
749
750 Using the component in a coupling
751 -----------------------------------------------------------------------------------------
752 Create the YACS coupling file
753 ++++++++++++++++++++++++++++++++++++++++
754 A YACS coupling file is an XML file that describes how SALOME components previously installed in a SALOME application are coupled and executed.
755
756 See :ref:`schemaxml` for documentation about how to write a YACS XML file.
757
758 The following is an example of a YACS file using the Fortran component defined above:
759
760 .. code-block:: xml
761
762   <proc>
763   <container name="A"> </container>
764   <container name="B"> </container>
765
766   <service name="pipo1" >
767     <component>compo</component>
768     <method>s1</method>
769     <load container="A"/>
770     <inport name="a" type="double"/>
771     <inport name="b" type="int"/>
772     <inport name="c" type="string"/>
773     <outport name="d" type="double"/>
774     <outport name="e" type="int"/>
775     <outport name="f" type="string"/>
776     <instream name="a" type="CALCIUM_double"/>
777     <instream name="b" type="CALCIUM_double"/>
778     <outstream name="ba" type="CALCIUM_double"/>
779     <outstream name="bb" type="CALCIUM_double"/>
780   </service>
781   <service name="pipo2" >
782     <component>compo</component>
783     <method>s1</method>
784     <load container="B"/>
785     <inport name="a" type="double"/>
786     <inport name="b" type="int"/>
787     <inport name="c" type="string"/>
788     <outport name="d" type="double"/>
789     <outport name="e" type="int"/>
790     <outport name="f" type="string"/>
791     <instream name="a" type="CALCIUM_double"/>
792     <instream name="b" type="CALCIUM_double"/>
793     <outstream name="ba" type="CALCIUM_double"/>
794     <outstream name="bb" type="CALCIUM_double"/>
795   </service>
796
797   <stream>
798     <fromnode>pipo1</fromnode><fromport>ba</fromport>
799     <tonode>pipo2</tonode><toport>a</toport>
800   </stream>
801   <stream>
802     <fromnode>pipo1</fromnode><fromport>bb</fromport>
803     <tonode>pipo2</tonode><toport>b</toport>
804   </stream>
805   <stream>
806     <fromnode>pipo2</fromnode><fromport>ba</fromport>
807     <tonode>pipo1</tonode><toport>a</toport>
808   </stream>
809   <stream>
810     <fromnode>pipo2</fromnode><fromport>bb</fromport>
811     <tonode>pipo1</tonode><toport>b</toport>
812   </stream>
813   <parameter>
814     <tonode>pipo1</tonode> <toport>a</toport>
815     <value><double>23</double> </value>
816   </parameter>
817   <parameter>
818     <tonode>pipo1</tonode> <toport>b</toport>
819     <value><int>23</int> </value>
820   </parameter>
821   <parameter>
822     <tonode>pipo1</tonode> <toport>c</toport>
823     <value><string>/local/cchris/SALOME/SUPERV/YACS/modulegen/data1</string> </value>
824   </parameter>
825   <parameter>
826     <tonode>pipo2</tonode> <toport>a</toport>
827     <value><double>23</double> </value>
828   </parameter>
829   <parameter>
830     <tonode>pipo2</tonode> <toport>b</toport>
831     <value><int>23</int> </value>
832   </parameter>
833   <parameter>
834     <tonode>pipo2</tonode> <toport>c</toport>
835     <value><string>/local/cchris/SALOME/SUPERV/YACS/modulegen/data2</string> </value>
836   </parameter>
837
838   </proc>
839
840 In general terms, coupling uses two instances of the component compo (pipo1 and pipo2) of which the service s1 is executed.  
841 The datastream ports of these services are connected using fromnode, fromport, tonode, toport information in the stream sections.  
842 The dataflow ports are initialised by the parameter sections.  In particular, the working directory of each component instance 
843 is initialised through input port “c” of each component instance.  Each component instance is executed in a different container (A and B).  
844 These names are virtual.  SALOME will decide on the effective name of the containers at the time of the startup.  The following simply 
845 describes constraints on containers to be used.  In fact, there is only one constraint, which is that the containers have to be different.
846
847 Executing coupling
848 +++++++++++++++++++++++++++++++++++++++++++++
849 Once the coupling file has been written using a classical editor or the YACS graphic editor, execution can be started.
850
851 It takes place in several steps:
852
853 - start SALOME:  execute the runAppli script of the SALOME application (``./appli/runAppli –t``).  The application runs 
854   as a background task until it is stopped.
855 - start coupling:  execute the YACS coupler in the environment of the running SALOME application (``./appli/runSession driver test.xml``) 
856   with test.xml as the coupling file.
857 - stop the application:  ``./appli/runSession killSalome.py``
858
859 There are many coupling outputs:
860
861 - the output from the coupler itself.  If no execution error is returned to the coupler, the output will only contain one useful 
862   item of information:  the name of containers started by SALOME to execute the components.  If execution errors are returned to 
863   the coupler, they will be listed at the end of execution.
864 - container outputs:  these outputs are located in the /tmp directory with a name constructed based on the container name read 
865   in the coupler output.
866
867 .. warning::
868    When the application is stopped, the containers are killed, and this can cause information losses in their output files.
869
870 The working directory
871 ++++++++++++++++++++++++++++++++++++++
872 Each component instance is hosted in a container.  Therefore all instances hosted in a container are executed in the same 
873 directory, which is the container directory.  Starting from version 4.1.1 of SALOME, the working directory of a container 
874 can be specified in the coupling file.  All that is necessary is to add the **workingdir** property to the container.  
875 The following gives a few examples:
876
877 .. code-block:: xml
878
879    <container name="A">
880      <property name="workingdir" value="/home/user/w1"/>
881    </container>
882    <container name="B">
883      <property name="workingdir" value="$TEMPDIR"/>
884    </container>
885    <container name="C">
886      <property name="workingdir" value="a/b"/>
887    </container>
888
889 The container A is executed in directory “/home/user/w1”.  This directory will be created if it does not exist.  
890 The container B will be executed in a new temporary directory.  
891 Container C will be executed in the relative directory “a/b” (starting from the directory of the application used 
892 for the execution).  This directory will be created if it does not already exist.
893
894 Files management
895 ++++++++++++++++++++++++++++
896 Components are dynamic libraries or Python modules, and they cannot be run in shell scripts.  For components that use input and 
897 output files, “files” ports can be specified in the coupling file through which file transfers will be made and appropriate 
898 local names will be given.  For example, a service that uses an input file a and produces an output file b will be declared as follows:
899
900 .. code-block:: xml
901
902     <service name="pipo1">
903       <component>caster</component>
904       <method>s1</method>
905       <inport name="a" type="file"/>
906       <outport name="b" type="file"/>
907     </service>
908
909 These ports can be initialised or connected to other “files” ports like ordinary ports.  For example, initialisation for the input 
910 file will be in the following form:
911
912 .. code-block:: xml
913
914     <parameter>
915       <tonode>pipo1</tonode> <toport>a</toport>
916       <value><objref>/local/chris/tmp/unfichier</objref> </value>
917     </parameter>
918
919 It is impossible to initialise an output file port directly.  A special node has to be used that collects outputs.  
920 A “dataout” node and the link between node “pipo1” and node “dataout” will be created:
921
922 .. code-block:: xml
923
924     <outnode name="dataout" >
925       <parameter name="f1" type="file" ref="myfile"/>
926     </outnode>
927     <datalink>
928        <fromnode>pipo1</fromnode><fromport>b</fromport>
929        <tonode>dataout</tonode> <toport>f1</toport>
930     </datalink>
931
932 .. warning::
933    It is impossible to use the “.” character in port names.  This prevents the use of names such as fort.8 that are 
934    fairly frequent.  There is a simple workaround solution, which is to replace the “.” by the “:”character (therefore fort:8 in 
935    our example) to obtain the expected result.  
936    Obviously, names containing the “:” characters cannot be used.  They must be very rare.
937
938 .. _execaster:
939
940 Example execution of an Aster component
941 +++++++++++++++++++++++++++++++++++++++++++
942 There are a few unusual features when executing an Aster component that are presented below:
943
944 - handling the command set
945 - specification of parameters in the command line
946 - specification of a mesh file (.mail)
947 - specification of environment variables (also valid for other component types).
948
949 The following is a simplified example of a YACS scheme comprising a calculation node that should execute service s1 of 
950 the caster component (type Aster) with an environment variable, a mail file, a comm file and command line parameters.  
951 A more complete example is given in the directory Examples/ast1 in the distribution:
952
953 .. code-block:: xml
954
955     <service name="pipo1" >
956       <component>caster</component>
957       <property name="MYENVAR" value="25"/>
958       <method>s1</method>
959       <load container="A"/>
960       <inport name="jdc" type="string"/>
961       <inport name="argv" type="string"/>
962       <inport name="a" type="double"/>
963       <inport name="fort:20" type="file"/>
964       <outport name="d" type="double"/>
965       <instream name="aa" type="CALCIUM_double"/>
966       <outstream name="ba" type="CALCIUM_double"/>
967     </service>
968
969     <inline name="ljdc" >
970        <script>
971        <code>f=open(comm)</code>
972        <code>jdc=f.read()</code>
973        <code>f.close()</code>
974        </script>
975        <inport name="comm" type="string"/>
976        <outport name="jdc" type="string"/>
977     </inline>
978
979     <parameter>
980       <tonode>ljdc</tonode> <toport>comm</toport>
981       <value><string>/home/chris/jdc.comm</string> </value>
982     </parameter>
983
984     <datalink>
985        <fromnode>ljdc</fromnode><fromport>jdc</fromport>
986        <tonode>pipo1</tonode> <toport>jdc</toport>
987     </datalink>
988
989     <parameter>
990       <tonode>pipo1</tonode> <toport>argv</toport>
991       <value><string>-rep_outils /aster/outils</string> </value>
992     </parameter>
993
994     <parameter>
995        <tonode>pipo1</tonode> <toport>fort:20</toport>
996        <value>
997          <objref>/local/chris/ASTER/instals/NEW9/astest/forma01a.mmed</objref>
998        </value>
999     </parameter>
1000
1001 Firstly, the command set has to be specified.  As mentioned above (:ref:`aster`), an additional “jdc” “string” type port 
1002 has to be declared and it has to be initialised or connected.  In this case, the jdc port is connected to an output port 
1003 from a python node (ljdc) that will read the .comm file, for which the path is given to it by its comm input port.  
1004 The component identifier is transferred to the YACS_INIT command by means of the “component” variable that is 
1005 automatically added by the generator and is available to write the .comm file.
1006
1007 Brief example of .comm::
1008
1009    DEBUT(PAR_LOT="NON")
1010    YACS_INIT(COMPO=component)
1011    ECRIRE_MAILLAGE()
1012    LECTURE_FORCE()
1013
1014 Before values of command line parameters can be specified, a component must have been created with a “string” type port named “argv”.  
1015 A value then has to be given to this port.  In this case, we modify the tools directory path using the **rep_outils** parameter.
1016
1017 A mesh file (.mail) is specified to an Aster component by adding a file port to the calculation node:
1018
1019 .. code-block:: xml
1020
1021       <inport name="fort:20" type="file"/>
1022
1023 The name of this file port must be the same as the local file name as expected by Aster.  Usually, Aster uses 
1024 the fort.20 file as an input to LIRE_MAILLAGE.  As mentioned above, the dot in fort.20 cannot be used in a port 
1025 name, and therefore it will be given the name fort:20.  A value will then have to be given to this port that will 
1026 correspond to the path of the file to be used.  This is done by a parameter directive:
1027
1028 .. code-block:: xml
1029
1030     <parameter>
1031        <tonode>pipo1</tonode> <toport>fort:20</toport>
1032        <value>
1033          <objref>/local/chris/ASTER/instals/NEW9/astest/forma01a.mmed</objref>
1034        </value>
1035     </parameter>
1036
1037 Environment variables are specified by using properties of the calculation node.  In this case, we define 
1038 the MYENVAR environment variable with value 25.
1039
1040 Standalone components
1041 --------------------------------------------------
1042 Before SALOME version 4.1, the only method for integrating a component was to produce a dynamic library (\*.so) or a python 
1043 module (\*.py).  This component is loaded by a SALOME executable named Container, either by dlopen in the case of the 
1044 library or by import in the case of the Python module.  This method is a little constraining for calculation codes 
1045 like *Code_Aster* or *Code_Saturne* that are executed in a particular environment, and preferably from a shell script.
1046  
1047 Starting from version 4.1.3, a component can be integrated as an executable or shell script.  This new function is 
1048 experimental at the moment and it will have to be tested more completely.  However, it can be used and module_generator 
1049 was adapted (starting from version 0.3) to generate standalone components.  The following describes operations to be carried out 
1050 to change to standalone mode for each type of component (C/C++, Python, Fortran or Aster).
1051
1052 C/C++ component
1053 ++++++++++++++++++++++++++++++++++++++++
1054 All that is necessary to transform a standard C/C++ component in the form of a dynamic library into a standalone component, is 
1055 to add two attributes to its description:
1056
1057 - the **kind** attribute:  by setting the value “exe”
1058 - the **exe_path** attribute:  by setting its value equal to the path of the executable or the shell script that will be used 
1059   when the component is started
1060
1061 The following is an example of a C++ component modified to make it a standalone component::
1062
1063       c1=CPPComponent("compo1",services=[
1064                       Service("myservice",inport=[("inputport","double"),],
1065                                outport=[("outputport","double")],
1066                              ),
1067                             ],
1068          kind="exe",
1069          exe_path="/local/SALOME/execpp/prog",
1070                      )
1071
1072 The path given for **exe_path** corresponds to an executable with the following source:
1073
1074 .. code-block:: cpp
1075
1076    #include "compo1.hxx"
1077
1078    int main(int argc, char* argv[])
1079    {
1080      yacsinit();
1081      return 0;
1082    }
1083
1084 It must be compiled and linked using the compo1.hxx include and the libcompo1Exelib.so library that are given 
1085 in the installation of the module generated in include/salome and in lib/salome respectively.  
1086
1087 .. note::
1088
1089    the SALOME module must be generated before compiling and linking the standalone component.
1090  
1091 A more complete example is given in the distribution sources in the Examples/cpp2 directory.
1092
1093 The executable can be replaced by an intermediary shell script, but it is good to know that the call to yacsinit 
1094 retrieves information necessary to initialise the component in the three environment variables (*SALOME_CONTAINERNAME*, 
1095 *SALOME_INSTANCE*, *SALOME_CONTAINER*).
1096
1097 Fortran component
1098 ++++++++++++++++++++++++++++++++++++++++
1099 The method for a Fortran component is exactly the same.  The same two attributes are added:
1100
1101 - The **kind** attribute:  by setting the value “exe”
1102 - The **exe_path** attribute:  by setting its value equal to the path of the executable or the shell script that will 
1103   be used when the component is started
1104
1105 The following is an example of a standalone Fortran component::
1106
1107      c3=F77Component("compo3",services=[
1108           Service("s1",inport=[("a","double"),("b","long"),
1109                                ("c","string")],
1110                        outport=[("d","double"),("e","long"),
1111                                 ("f","string")],
1112                        instream=[("a","CALCIUM_double","T"),
1113                                  ("b","CALCIUM_double","I")],
1114                        outstream=[("ba","CALCIUM_double","T"),
1115                                   ("bb","CALCIUM_double","I")],
1116                              ),
1117                              ],
1118          kind="exe",
1119          exe_path="/local/SALOME/fcompo/prog",
1120                                      )
1121
1122 The path given for **exe_path** corresponds to an executable with the following source:
1123
1124 .. code-block:: fortran
1125
1126        PROGRAM P
1127        CALL YACSINIT()
1128        END
1129
1130 It must be compiled and linked using the libcompo3Exelib.so library that is located in the installation of the module 
1131 generated in lib/salome, and with the Fortran source containing subroutine S1.  
1132 Refer to a more complete example in distribution sources in the Examples/fort2 directory.
1133
1134 Python component
1135 ++++++++++++++++++++++++++++++++++++++++
1136 A very rudimentary generator has been coded for a Python component.  The only possible action is to add the **kind** 
1137 attribute (with the value "exe"). The executable is automatically generated in the module installation.  
1138 It cannot be replaced by a script, unless the installation is modified.
1139
1140 Standalone Aster component
1141 ++++++++++++++++++++++++++++++++++++++++
1142 Slightly more work is necessary for an Aster component.  Three attributes have to be specified:
1143
1144 - the **aster_dir** attribute:  that gives the path of the *Code_Aster* installation
1145 - the **kind** attribute:  with the “exe” value
1146 - the **exe_path** attribute:  that gives the path of the shell script that will be used when the component is started
1147
1148 The following is an example description of a standalone Aster component::
1149
1150       c1=ASTERComponent("caster",services=[
1151                   Service("s1",inport=[("argv","string"),("a","double"),
1152                                        ("b","long"),("c","string")],
1153                                outport=[("d","double")],
1154                                instream=[("aa","CALCIUM_double","T"),
1155                                          ("ab","CALCIUM_double","I"),
1156                                          ("ac","CALCIUM_integer","I"),
1157                                          ("ad","CALCIUM_real","I"),
1158                                          ("ae","CALCIUM_string","I"),
1159                                          ("af","CALCIUM_complex","I"),
1160                                          ("ag","CALCIUM_logical","I"),
1161                                        ],
1162                                outstream=[("ba","CALCIUM_double","T"),
1163                                           ("bb","CALCIUM_double","I")],
1164                  ),
1165          ],
1166          aster_dir="/aster/NEW9",
1167          kind="exe",
1168          exe_path="/home/SALOME5/exeaster",
1169          )
1170
1171 The “effective” command file always has to be specified in the XML coupling file. 
1172
1173 Example coupling with standalone components
1174 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1175 By collecting all the above elements, coupling of a standalone Aster component with a standalone Fortran component is 
1176 written as follows::
1177
1178   from module_generator import Generator,Module
1179   from module_generator import ASTERComponent,Service,F77Component
1180
1181   context={'update':1,"prerequisites":"/home/SALOME5/env.sh",
1182           "kernel":"/home/SALOME5/Install/KERNEL_V5"}
1183
1184   install_prefix="./exe_install"
1185   appli_dir="exe_appli"
1186
1187   c1=ASTERComponent("caster",services=[
1188           Service("s1",inport=[("a","double"),("b","long"),
1189                                ("c","string")],
1190                        outport=[("d","double")],
1191                    instream=[("aa","CALCIUM_double","T"),
1192                              ("ab","CALCIUM_double","I"),
1193                              ("ac","CALCIUM_integer","I"),
1194                              ("ad","CALCIUM_real","I"),
1195                              ("ae","CALCIUM_string","I"),
1196                              ("af","CALCIUM_complex","I"),
1197                              ("ag","CALCIUM_logical","I"),
1198                          ],
1199                    outstream=[("ba","CALCIUM_double","T"),
1200                               ("bb","CALCIUM_double","I")],
1201                  ),
1202          ],
1203          kind="exe",
1204          aster_dir="/aster/NEW9",
1205          exe_path="/home/SALOME5/exeaster",
1206          )
1207
1208   c2=F77Component("cfort",services=[
1209           Service("s1",inport=[("a","double"),("b","long"),
1210                                ("c","string")],
1211                        outport=[("d","double"),("e","long"),
1212                                 ("f","string")],
1213                   instream=[("a","CALCIUM_double","T"),
1214                             ("b","CALCIUM_double","I")],
1215                   outstream=[("ba","CALCIUM_double","T"),
1216                              ("bb","CALCIUM_double","I"),
1217                              ("bc","CALCIUM_integer","I"),
1218                              ("bd","CALCIUM_real","I"),
1219                              ("be","CALCIUM_string","I"),
1220                              ("bf","CALCIUM_complex","I"),
1221                              ("bg","CALCIUM_logical","I"),
1222                          ],
1223                        defs="",body="",
1224                  ),
1225          ],
1226            exe_path="/home/SALOME5/fcompo/prog",
1227            kind="exe")
1228
1229   g=Generator(Module("astmod",components=[c1,c2],prefix=install_prefix),context)
1230   g.generate()
1231   g.bootstrap()
1232   g.configure()
1233   g.make()
1234   g.install()
1235   g.make_appli(appli_dir,restrict=["KERNEL","YACS"])
1236
1237 The corresponding xml coupling file and Aster command file may be viewed in the distribution (Examples/ast2 directory).  
1238 The complementary implantation elements are located in the fcompo directory (cfort component) and in the myaster directory (caster component).
1239
1240 Miscellaneous
1241 -----------------------------------------------------------------
1242 YACSGEN is mainly targeted to the integration of Python, C++ or Fortran calculation codes.
1243 Nevertheless, if you want to generate a complete module with GUI, documentation and persistence,
1244 there are some minimal options to do that.
1245
1246 Add a GUI
1247 ++++++++++++++++++++++++++++++++++++++++
1248 It is possible to add a C++ or a Python GUI to the module with the *gui* parameter of the module.
1249 This parameter must be a list of file names. These files can be source files (\*.cxx, \*.hxx or \*.h for C++, \*.py for python),
1250 image files (\*.png, ...) and qt designer files (\*.ui). You can't mix python and C++ source files.
1251 In C++, include files with .h extension are processed with the moc qt tool.
1252
1253 Here is an excerpt from pygui1 example that shows how to add a python GUI to a module::
1254
1255   modul=Module("pycompos",components=[c1],prefix="./install",
1256                           gui=["pycomposGUI.py","demo.ui","*.png"],
1257               )
1258
1259 The GUI is implemented in the pycomposGUI.py (that must be named <module name>GUI.py) python module. It uses a qt designer
1260 file demo.ui that is dynamically loaded and several images in PNG files.
1261
1262 Here is an excerpt from cppgui1 example that shows how to add a C++ GUI to a module::
1263
1264   modul=Module("cppcompos",components=[c1],prefix="./install",
1265                            gui=["cppcomposGUI.cxx","cppcomposGUI.h","demo.ui","*.png"],
1266               )
1267
1268 The C++ GUI is very similar to the python GUI except that the cppcomposGUI.h file is processed by the moc and the demo.ui
1269 is processed by the uic qt tool.
1270
1271 By default, a Makefile.am and a SalomeApp.xml files are generated but you can put your own Makefile.am or SalomeApp.xml
1272 in the list to override this default.
1273
1274 Add an online documentation
1275 ++++++++++++++++++++++++++++++++++++++++
1276 It is possible to add an online documentation that is made with the sphinx tool (http://sphinx.pocoo.org). You need a well installed
1277 sphinx tool (1.0.x or 0.6.x).
1278 To add a documentation use the *doc* parameter of the module. It must be a list of file names. These files can be text files
1279 (name with extension .rst) in the reStructured format (see http://docutils.sourceforge.net/) and image files (\*.png, ...).
1280 The main file must be named index.rst.
1281
1282 By default, a sphinx configuration file conf.py and a Makefile.am are generated but you can put your own Makefile.am or conf.py
1283 in the list to override this default.
1284
1285 Here is an excerpt from pygui1 example that shows how to add a documentation to a module::
1286
1287   modul=Module("pycompos",components=[c1],prefix="./install",
1288                           doc=["*.rst","*.png"],
1289               )
1290
1291 .. warning::
1292    The online documentation will only appear in the SALOME GUI, if your module has a minimal GUI but not if it has no GUI.
1293
1294 Add extra methods to your components
1295 ++++++++++++++++++++++++++++++++++++++++
1296 If you have a C++ or Python class or some methods that you want to add to your components, it is possible to do that by
1297 using the *compodefs* and *inheritedclass* parameters of the component (:class:`module_generator.CPPComponent` or
1298 :class:`module_generator.PYComponent`).
1299
1300 The *inheritedclass* parameter gives the name of the class that will be included in the parent classes of the component and
1301 the *compodefs* parameter is a fragment of code that will be inserted in the definition section of the component. It can be used
1302 to add definitions such as include or even a complete class.
1303
1304 Here is an excerpt from pygui1 example that shows how to add a method named createObject to the component pycompos::
1305
1306   compodefs=r"""
1307   class A:
1308     def createObject( self, study, name ):
1309       "Create object.  "
1310       builder = study.NewBuilder()
1311       father = study.FindComponent( "pycompos" )
1312       if father is None:
1313         father = builder.NewComponent( "pycompos" )
1314       attr = builder.FindOrCreateAttribute( father, "AttributeName" )
1315       attr.SetValue( "pycompos" )
1316       object  = builder.NewObject( father )
1317       attr    = builder.FindOrCreateAttribute( object, "AttributeName" )
1318       attr.SetValue( name )
1319   """
1320
1321   c1=PYComponent("pycompos",services=[
1322                  Service("s1",inport=[("a","double"),("b","double")],
1323                               outport=[("c","double"),("d","double")],
1324                         ),
1325                                      ],
1326                  compodefs=compodefs,
1327                  inheritedclass="A",
1328                 )
1329
1330 .. note::
1331
1332    If you have special characters in your code fragments such as backslash, think about using python raw strings (r"...")
1333
1334 For a C++ component, the method is exactly the same. There is only one case that can be handled in Python with this method and not in C++.
1335 It's when you want to redefine one of the component methods (DumpPython, for example). In this case, adding a class in the inheritance tree
1336 does not override the default implementation. So, for this special case, there is another parameter (*addmethods*) that is a code
1337 fragment that will be included in the component class to effectively redefine the method.
1338
1339 Here is an excerpt from cppgui1 example that shows how to redefine the DumpPython method in a C++ component::
1340
1341   compomethods=r"""
1342   Engines::TMPFile* DumpPython(CORBA::Object_ptr theStudy, CORBA::Boolean isPublished,
1343                                CORBA::Boolean& isValidScript)
1344   {
1345     SALOMEDS::Study_var aStudy = SALOMEDS::Study::_narrow(theStudy);
1346     if(CORBA::is_nil(aStudy))
1347       return new Engines::TMPFile(0);
1348     SALOMEDS::SObject_var aSO = aStudy->FindComponent("cppcompos");
1349     if(CORBA::is_nil(aSO))
1350        return new Engines::TMPFile(0);
1351     std::string Script = "import cppcompos_ORB\n";
1352     Script += "import salome\n";
1353     Script += "compo = salome.lcc.FindOrLoadComponent('FactoryServer','cppcompos')\n";
1354     Script += "def RebuildData(theStudy):\n";
1355     Script += "  compo.SetCurrentStudy(theStudy)\n";
1356     const char* aScript=Script.c_str();
1357     char* aBuffer = new char[strlen(aScript)+1];
1358     strcpy(aBuffer, aScript);
1359     CORBA::Octet* anOctetBuf =  (CORBA::Octet*)aBuffer;
1360     int aBufferSize = strlen(aBuffer)+1;
1361     Engines::TMPFile_var aStreamFile = new Engines::TMPFile(aBufferSize, aBufferSize, anOctetBuf, 1);
1362     isValidScript = true;
1363     return aStreamFile._retn();
1364   }
1365   """
1366
1367   c1=CPPComponent("cppcompos",services=[ Service("s1",
1368                                                  inport=[("a","double"),("b","double")],
1369                                                  outport=[("c","double")],
1370                                                 ),
1371                                        ],
1372                   addedmethods=compomethods,
1373                  )
1374
1375
1376 Add extra idl corba interfaces to your components
1377 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1378 If you want to add pure CORBA methods (not SALOME services) to your components or even complete IDL interface (SALOMEDS::Driver, for
1379 example), you can do that by using the *idls*, *interfacedefs* and *inheritedinterface* parameters of the component.
1380
1381 The *idls* parameter must be a list of CORBA idl file names. The *inheritedinterface* parameter gives the name of the CORBA
1382 interface that will be included in the parent interfaces of the component interface. The *interfacedefs* parameter is a fragment
1383 of code that will be inserted in the idl file of the module. It can be used to add definitions such as include or even a complete interface.
1384
1385 Here is an excerpt from pygui1 example that shows how to add the SALOMEDS::Driver interface (with its default
1386 implementation from SALOME KERNEL) and an extra method (createObject) to a python component::
1387
1388   idldefs="""
1389   #include "myinterface.idl"
1390   """
1391
1392   compodefs=r"""
1393   import SALOME_DriverPy
1394
1395   class A(SALOME_DriverPy.SALOME_DriverPy_i):
1396     def __init__(self):
1397       SALOME_DriverPy.SALOME_DriverPy_i.__init__(self,"pycompos")
1398       return
1399
1400     def createObject( self, study, name ):
1401       "Create object.  "
1402       builder = study.NewBuilder()
1403       father = study.FindComponent( "pycompos" )
1404       if father is None:
1405         father = builder.NewComponent( "pycompos" )
1406         attr = builder.FindOrCreateAttribute( father, "AttributeName" )
1407         attr.SetValue( "pycompos" )
1408
1409       object  = builder.NewObject( father )
1410       attr    = builder.FindOrCreateAttribute( object, "AttributeName" )
1411       attr.SetValue( name )
1412   """
1413
1414   c1=PYComponent("pycompos",services=[ Service("s1",
1415                                                inport=[("a","double"),("b","double")],
1416                                                outport=[("c","double"),("d","double")],
1417                                               ),
1418                                      ],
1419               idls=["*.idl"],
1420               interfacedefs=idldefs,
1421               inheritedinterface="Idl_A",
1422               compodefs=compodefs,
1423               inheritedclass="A",
1424          )
1425
1426 The idl file names can contain shell-style wildcards that are accepted by the python glob module. Here, there is only
1427 one file (myinterface.idl) that contains the definition of interface Idl_A::
1428
1429   #include "SALOMEDS.idl"
1430   #include "SALOME_Exception.idl"
1431
1432   interface Idl_A : SALOMEDS::Driver
1433   {
1434     void createObject(in SALOMEDS::Study theStudy, in string name) raises (SALOME::SALOME_Exception);
1435   };
1436
1437 In this simple case, it is also possible to include directly the content of the file with the *interfacedefs* parameter.
1438
1439 For a C++ component, the method is exactly the same, except that there is no default implementation of the Driver interface
1440 so you have to implement it.
1441
1442 Add YACS type definition to YACSGEN
1443 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1444 If you define a port, you need to give a type name. YACSGEN knows about a limited set of types (see :ref:`yacstypes`).
1445 If you want to add more types either because they have been forgotten or you want to use one from a new module, it is possible
1446 to add them with the function :func:`module_generator.add_type`. This function can also overload an existing type.
1447
1448 For example, to overload the definition of type GEOM_Object in GEOM module::
1449
1450     from module_generator import add_type
1451     add_type("GEOM_Object", "GEOM::GEOM_Object_ptr", "GEOM::GEOM_Object_out", "GEOM", "GEOM::GEOM_Object","GEOM::GEOM_Object_ptr")
1452
1453 Add YACS module definition to YACSGEN
1454 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1455 Now if you want to add a new type from a new module (unknown to YACSGEN), you need to add a module definition to YACSGEN.
1456 You can add it with the function :func:`module_generator.add_module`. This function can also overload the definition
1457 of an existing module.
1458
1459 For example, to overload the definition of module GEOM::
1460
1461     from module_generator import add_module
1462
1463     idldefs="""
1464     #include "GEOM_Gen.idl"
1465     """
1466
1467     makefiledefs="""
1468     #module GEOM
1469     GEOM_IDL_INCLUDES = -I$(GEOM_ROOT_DIR)/idl/salome
1470     GEOM_INCLUDES= -I$(GEOM_ROOT_DIR)/include/salome
1471     GEOM_IDL_LIBS= -L$(GEOM_ROOT_DIR)/lib/salome -lSalomeIDLGEOM
1472     GEOM_LIBS= -L$(GEOM_ROOT_DIR)/lib/salome
1473     SALOME_LIBS += ${GEOM_LIBS}
1474     SALOME_IDL_LIBS += ${GEOM_IDL_LIBS}
1475     SALOME_INCLUDES += ${GEOM_INCLUDES}
1476     IDL_INCLUDES += ${GEOM_IDL_INCLUDES}
1477     """
1478
1479     configdefs="""
1480     if test "x${GEOM_ROOT_DIR}" != "x" && test -d ${GEOM_ROOT_DIR} ; then
1481       AC_MSG_RESULT(Using GEOM installation in ${GEOM_ROOT_DIR})
1482     else
1483       AC_MSG_ERROR([Cannot find module GEOM. Have you set GEOM_ROOT_DIR ?],1)
1484     fi
1485     """
1486
1487     add_module("GEOM",idldefs,makefiledefs,configdefs)
1488
1489
1490 Reference guide 
1491 -----------------------------------------------------------------
1492
1493 .. automodule:: module_generator
1494    :synopsis: YACSGEN interface 
1495
1496 The module provides the following classes:
1497
1498 .. autoclass:: Service
1499
1500 .. autoclass:: CPPComponent
1501
1502 .. autoclass:: PYComponent
1503
1504 .. autoclass:: F77Component
1505
1506 .. autoclass:: ASTERComponent
1507
1508 .. autoclass:: Module
1509
1510 .. autoclass:: Generator
1511     :members: generate, bootstrap, configure, make, install, make_appli
1512
1513 .. autofunction:: add_type
1514
1515 .. autofunction:: add_module
1516
1517 .. _yacstypes:
1518
1519 Supported SALOME types
1520 ----------------------------
1521
1522 ======================= =============================== ================================ ===================== ==========================
1523    SALOME module            YACS type name                    IDL type name                  Implementation          Comment
1524 ======================= =============================== ================================ ===================== ==========================
1525    GEOM                  GEOM_Object                     GEOM::GEOM_Object                C++, Python
1526    SMESH                 SMESH_Mesh                      SMESH::SMESH_Mesh                C++, Python
1527    SMESH                 SMESH_Hypothesis                SMESH::SMESH_Hypothesis          C++, Python
1528    MED                   SALOME_MED/MED                  SALOME_MED::MED                  C++, Python
1529    MED                   SALOME_MED/MESH                 SALOME_MED::MESH                 C++, Python
1530    MED                   SALOME_MED/SUPPORT              SALOME_MED::SUPPORT              C++, Python
1531    MED                   SALOME_MED/FIELD                SALOME_MED::FIELD                C++, Python
1532    MED                   SALOME_MED/FIELDDOUBLE          SALOME_MED::FIELDDOUBLE          C++, Python
1533    MED                   SALOME_MED/FIELDINT             SALOME_MED::FIELDINT             C++, Python
1534    KERNEL                double                          double                           C++, Python, F77
1535    KERNEL                long                            long                             C++, Python, F77
1536    KERNEL                string                          string                           C++, Python, F77
1537    KERNEL                dblevec                         dblevec                          C++, Python, F77       list of double
1538    KERNEL                stringvec                       stringvec                        C++, Python, F77       list of string
1539    KERNEL                intvec                          intvec                           C++, Python, F77       list of long  
1540    KERNEL                pyobj                                                            Python                 a pickled python object   
1541    KERNEL                file                                                             C++, Python, F77       to transfer a file
1542    KERNEL                SALOME_TYPES/Parameter          SALOME_TYPES::Parameter          C++, Python
1543    KERNEL                SALOME_TYPES/ParameterList      SALOME_TYPES::ParameterList      C++, Python
1544    KERNEL                SALOME_TYPES/Variable           SALOME_TYPES::Variable           C++, Python
1545    KERNEL                SALOME_TYPES/VariableSequence   SALOME_TYPES::VariableSequence   C++, Python
1546    KERNEL                SALOME_TYPES/StateSequence      SALOME_TYPES::StateSequence      C++, Python
1547    KERNEL                SALOME_TYPES/TimeSequence       SALOME_TYPES::TimeSequence       C++, Python
1548    KERNEL                SALOME_TYPES/VarList            SALOME_TYPES::VarList            C++, Python
1549    KERNEL                SALOME_TYPES/ParametricInput    SALOME_TYPES::ParametricInput    C++, Python
1550    KERNEL                SALOME_TYPES/ParametricOutput   SALOME_TYPES::ParametricOutput   C++, Python
1551 ======================= =============================== ================================ ===================== ==========================