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