Salome HOME
Some documentation about Containers.
[modules/yacs.git] / doc / schemapy.rst
1
2 .. _schemapy:
3
4 Defining a calculation scheme with the Python programming interface
5 ============================================================================
6 A YACS calculation scheme can be defined from a program written in the Python language (http://www.python.org/).  
7 Refer to the `Python tutorial <http://docs.python.org/tut/tut.html>`_ for an introduction to the language.
8
9 The programming interface (API) is carried on three Python modules:  pilot, SALOMERuntime and loader.
10
11 The SALOMERuntime module is used to initialise YACS for SALOME.
12
13 The loader module is used to create calculation schemes by loading files in the XML format.
14
15 The pilot module is used to create calculation schemes.
16
17 These modules must be imported at the beginning of the Python program and YACS must be initialised::
18
19     import sys
20     import pilot
21     import SALOMERuntime
22     import loader
23     SALOMERuntime.RuntimeSALOME_setRuntime()
24
25 Before YACS modules can be imported, the environment must be correctly configured, as it will be if the 
26 SALOME application is used.  Otherwise, the PYTHONPATH environment variable has to be set to 
27 <YACS_ROOT_DIR>/lib/pythonX.Y/site-packages/salome.
28
29 .. _loadxml:
30
31 Create a calculation scheme by loading an XML file
32 --------------------------------------------------------------
33 This is the easiest way of creating a calculation scheme.  If there is a file conforming with the YACS syntax (see :ref:`schemaxml`), 
34 then all that is necessary is to create an XML file loader and then to use its load method to obtain a calculation scheme object in Python.
35
36 The following shows the sufficient Python code to load an XML file::
37
38   xmlLoader = loader.YACSLoader()
39   try:
40     p = xmlLoader.load("simple1.xml")
41   except IOError,ex:
42     print "IO exception:",ex
43     sys.exit(1)
44
45 Then, if the initialisation code and the loading code are put into a file named testLoader.py, proceed as follows::
46  
47   python testLoader.py
48
49 to execute the program.  The IOError exception can be raised by the loading operation principally if the file does not exist 
50 or if it cannot be read.  If no exception has been raised, it is necessary to make sure that the file analysis took place correctly.  
51 This is done using the Logger object associated with the calculation scheme::
52
53    logger=p.getLogger("parser")
54    if not logger.isEmpty():
55      print "The imported file has errors :"
56      print logger.getStr()
57      sys.exit(1)
58
59 Finally, if the file analysis took place correctly, the validity of the scheme (completeness of connections, no unconnected 
60 input port, etc.) has to be checked.  This is done using the isValid method of the calculation scheme object, and 
61 then the p.checkConsistency method of this object as below::
62
63    if not p.isValid():
64      print "The schema is not valid and can not be executed"
65      print p.getErrorReport()
66      sys.exit(1)
67
68    info=pilot.LinkInfo(pilot.LinkInfo.ALL_DONT_STOP)
69    p.checkConsistency(info)
70    if info.areWarningsOrErrors():
71      print "The schema is not consistent and can not be executed"
72      print info.getGlobalRepr()
73      sys.exit(1)
74
75
76 If all these tests took place correctly, the scheme is ready to be executed (see :ref:`execpy`).
77
78 Create a calculation scheme from scratch
79 -------------------------------------------
80 We will use the same sequence as in :ref:`schemaxml`.
81 The first step is to obtain the runtime object that will be used for creation of objects making up the scheme, before they are created::
82
83   r = pilot.getRuntime()
84
85 Creating an empty scheme
86 ''''''''''''''''''''''''''''
87 An empty scheme is obtained using the createProc method of the runtime object with the name of the scheme as an argument::
88
89   p=r.createProc("pr")
90
91 The scheme object named “pr” was created.  It is represented by the Python variable p.
92
93 Definition of data types
94 '''''''''''''''''''''''''''''''''
95
96 .. _basictypes:
97
98 Basic types
99 ++++++++++++++++
100 A basic type cannot be defined.  These types are defined by YACS.  However, it must be possible to retrieve a Python object 
101 equivalent to a basic type so as to be able to subsequently create ports.
102  
103 A basic data type is recovered using the getTypeCode method in the calculation scheme with the name of the type as an argument.  
104 For example::
105
106    td=p.getTypeCode("double")
107
108 will obtain a double type (Python td object).  Other basic types are obtained by::
109
110    ti=p.getTypeCode("int")
111    ts=p.getTypeCode("string")
112    tb=p.getTypeCode("bool")
113    tf=p.getTypeCode("file")
114
115 Object reference
116 +++++++++++++++++++++
117 The createInterfaceTc method in the calculation scheme is used to define an object reference type.  
118 This method accepts three arguments:  the repository id of the corresponding SALOME object, the name of the type, and a 
119 list of types that will be basic types of this type.  If the repository id is equal to “”, the default value will be used.
120
121 The following is a minimal example for a reference definition of an object name Obj (default repository id, no basic type)::
122
123   tc1=p.createInterfaceTc("","Obj",[])
124
125 The same Obj type can be defined giving the repository id::
126
127   tc1=p.createInterfaceTc("IDL:GEOM/GEOM_Object","Obj",[])
128
129 A list of basic types is also provided so as to define a reference object type derived from another type.
130
131 The following gives a definition of the MyObj type derived from the Obj type::
132
133   tc2=p.createInterfaceTc("","MyObj",[tc1])
134
135 Sequence
136 +++++++++++++++++++++
137 The createSequenceTc method in the calculation scheme is used to define a sequence type.   
138 This method accepts three arguments, namely the repository id, the type name, and the type of elements in the sequence.  
139 There is generally no point in specifying the repository id.  The value “” will be given.
140
141 The following gives an example definition of the seqdbl double sequence type::
142
143   tc3=p.createSequenceTc("","seqdbl",td)
144
145 td is the double type that is obtained as above in the section on :ref:`basictypes`.
146
147 A sequence type of sequence is defined as follows::
148
149   tc4=p.createSequenceTc("","seqseqdbl",tc3)
150
151 A reference sequence type is defined as follows::
152
153   tc5=p.createSequenceTc("","seqobj",tc1)
154
155 Structure
156 ++++++++++++
157 A structure type is defined using the createStructTc method in the calculation scheme.  
158 This method accepts two arguments, namely the repository id and the type name.  For standard use, the repository id is 
159 equal to the value “”.  The structure type is the only type that is defined in two steps.  It is created empty after 
160 calling the createStructTc method.  Its members are then defined by adding them with the addMember method.
161
162 The following shows an example definition of an s1 type structure with 2 members (m1 and m2) of the double and double sequence types::
163
164   ts1=p.createStructTc("","s1")
165   ts1.addMember("m1",td)
166   ts1.addMember("m2",tc3)
167
168 Retrieve predefined types
169 +++++++++++++++++++++++++++++++++
170 By default, YACS only defines the basic types.  If more predefined types are required, they must be requested from SALOME.  
171 These other predefined types are contained in module catalogs such as GEOM or SMESH.
172
173 The following code sequence is used to obtain an image of SALOME catalogs in YACS::
174
175   try:
176     cata=r.loadCatalog("session",
177            "corbaname::localhost:2810/NameService#Kernel.dir/ModulCatalog.object")
178   except CORBA.TRANSIENT,ex:
179     print "Unable to contact server:",ex
180   except CORBA.SystemException,ex:
181     print ex,CORBA.id(ex)
182
183 The SALOME application must be running before the catalog is accessible.  
184 Predefined types are then accessible in the cata._typeMap dictionary.  
185 If the name of the required type is known (for example ‘GEOM_Shape’), it is obtained as follows::
186
187   tgeom=cata._typeMap['GEOM_Shape']
188
189 .. _typedict:
190
191 Add a type into the scheme types dictionary
192 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
193 Some operations require that types are defined in the scheme dictionary.  Proceed as follows if you want to add a type 
194 into the dictionary, for example for the seqobj type defined above::
195
196   p.typeMap["seqobj"]=tc5
197
198 where the type name is the dictionary key and the type is the value.
199
200 Definition of elementary calculation nodes
201 ''''''''''''''''''''''''''''''''''''''''''''''
202
203 .. _pyscript:
204
205 Python script node
206 +++++++++++++++++++++
207 Several steps are used to define a script node in a given context (for example the calculation scheme).  
208 The first step consists of creating the node object by calling the runtime createScriptNode method.  
209 This method uses 2 arguments, the first of which in standard use must be equal to “” and the second is the node name.  
210 The following is an example to create node node1::
211
212   n=r.createScriptNode("","node1")
213  
214 The second step consists of attaching the node to its definition context by calling the edAddChild method for the context object.  
215 This method has one argument, namely the node to be attached.  The following is an example of the attachment of the node node1 
216 to the calculation scheme::
217
218   p.edAddChild(n)
219
220 Warning: the name of the method to be used depends on the type of context node.  We will see which method should be used for other 
221 node types later.
222
223 The third step consists of defining the Python script associated with the node.  This is done using the setScript method for the node 
224 with a character string argument that contains the Python code.  The following shows an example definition of the associated code::
225
226   n.setScript("p1=p1+2.5")
227
228 The fourth step consists of defining input and output data ports.  An input port is created by calling the edAddInputPort method 
229 for the node.  An output port is created by calling the edAddOutputPort method for the node.  
230 These two methods have two arguments:  the port name and the port data type.  The following is an example creating a double 
231 type input port p1 and a double type output port p1::
232
233   n.edAddInputPort("p1",td)
234   n.edAddOutputPort("p1",td)
235
236 Our node is now fully defined with its name, script, ports and context.  It retrieves the double in the input port p1, adds 2.5 to it 
237 and puts the result into the output port p1.
238
239 If you want to execute your script node on a remote container, you have to set the execution mode of the node to **remote**
240 and to assign a container (see :ref:`py_container` to define a container) to the node as in the following example::
241
242   n.setExecutionMode("remote")
243   n.setContainer(cont1)
244
245 .. _pyfunc:
246
247 Python function node
248 ++++++++++++++++++++++
249 The same procedure is used to define a function node.  The only differences apply to creation, in using the createFuncNode 
250 method and defining the function:  the setFname method must also be called to give the name of the function to be executed.  
251 The following is a complete example for the definition of a function node that is functionally identical to the previous script node::
252
253   n2=r.createFuncNode("","node2")
254   p.edAddChild(n2)
255   n2.setScript("""
256   def f(p1):
257     p1=p1+2.5
258     return p1
259   """)
260   n2.setFname("f")
261   n2.edAddInputPort("p1",td)
262   n2.edAddOutputPort("p1",td)
263
264 If you want to execute your function node on a remote container, you have to set the execution mode of the node to **remote**
265 and to assign a container (see :ref:`py_container` to define a container) to the node as in the following example::
266
267   n2.setExecutionMode("remote")
268   n2.setContainer(cont1)
269
270 .. _pyservice:
271
272 SALOME service node
273 ++++++++++++++++++++++++++
274 There are two definition forms for a SALOME service node.
275
276 The first form in which the component name is given, uses the createCompoNode method to create the node.  The name of the 
277 component is given as an argument to the setRef method for the node.  The service name is given as an argument for the 
278 setMethod method for the node.  The remainder of the definition is exactly the same as for the previous Python nodes.
279
280 The following is an example of a node that calls the makeBanner service for a PYHELLO component::
281
282   n3=r.createCompoNode("","node3")
283   p.edAddChild(n3)
284   n3.setRef("PYHELLO")
285   n3.setMethod("makeBanner")
286   n3.edAddInputPort("p1",ts)
287   n3.edAddOutputPort("p1",ts)
288
289 The second form is used to define a node that uses the same component as another node uses the createNode method of this other node.  
290 This method only has one argument, which is the node name.  
291 The remainder of the definition is identical to the definition for the previous form.
292
293 The following gives an example of a service node that makes a second call to the makeBanner service for the same component 
294 instance as the previous node::
295
296   n4=n3.createNode("node4")
297   p.edAddChild(n4)
298   n4.setMethod("makeBanner")
299   n4.edAddInputPort("p1",ts)
300   n4.edAddOutputPort("p1",ts)
301
302 Definition of connections
303 ''''''''''''''''''''''''''''
304 Obtaining a node port 
305 ++++++++++++++++++++++++++++
306 Before links can be defined, it is almost always necessary to have Python objects representing the output port to be 
307 connected to the input port.  There are two ways of obtaining this object.
308
309 The first way is to retrieve the port when it is created using the edAddInputPort and edAddOutputPort methods.  
310 For example, we can then write::
311
312   pin=n4.edAddInputPort("p1",ts)
313   pout=n4.edAddOutputPort("p1",ts)
314
315 pin and pout are then the objects necessary to define links.
316
317 The second way is to interrogate the node and ask it for one of its ports by its name.  
318 This is done using the getInputPort and getOutputPort methods.
319 pin and pout can then be obtained as follows::
320
321   pin=n4.getInputPort("p1")
322   pout=n4.getOutputPort("p1")
323
324 Control link
325 ++++++++++++++++++++++++++++
326 The edAddCFLink method for the context is used to define a control link between two nodes, transferring the two nodes to be 
327 connected to it as arguments.  For example, a control link between nodes n3 and n4 will be written::
328
329   p.edAddCFLink(n3,n4)
330
331 Node n3 will be executed before node n4.
332
333 Dataflow link
334 ++++++++++++++++++++++++++++
335 The first step in defining a dataflow link is to obtain port objects using one of the methods described above.  
336 The edAddDFLink method for the context node is then used, transferring the two ports to be connected to it.
337 The following gives an example of a dataflow link between the output port p1 of node n3 and the input port of node n4::
338
339   pout=n3.getOutputPort("p1")
340   pin=n4.getInputPort("p1")
341   p.edAddDFLink(pout,pin)
342
343 Data link
344 ++++++++++++++++++++++++++++
345 A data link is defined as being a dataflow link using the edAddLink method instead of edAddDFLink.  
346 The same example as above with a data link::
347
348   pout=n3.getOutputPort("p1")
349   pin=n4.getInputPort("p1")
350   p.edAddLink(pout,pin)
351
352 Initialising an input data port
353 '''''''''''''''''''''''''''''''''''''''''''''''
354 An input data port is initialised firstly by obtaining the corresponding port object.  There are then two methods of initialising it.
355
356 The first method initialises the port with a value encoded in XML-RPC.  The edInitXML method for the port is then used.  
357 The following is an example that initialises the port with the integer value 5::
358
359   pin.edInitXML("<value><int>5</int></value>")
360
361 The second method initialises the port with a Python value.  The edInitPy method is then used.  
362 The following is an example that initialises this port with the same value::
363
364   pin.edInitPy(5)
365
366 Specific methods can also be used for basic types:
367
368 - ``edInitInt`` for the int type
369 - ``edInitDbl`` for the double type
370 - ``edInitBool`` for the bool type
371 - ``edInitString`` for the string type
372
373 First example starting from the previous elements
374 '''''''''''''''''''''''''''''''''''''''''''''''''''
375 By collecting all previous definition elements, a complete calculation scheme identical to that given in the :ref:`schemaxml` chapter 
376 will appear as follows::
377
378   import sys
379   import pilot
380   import SALOMERuntime
381   import loader
382   SALOMERuntime.RuntimeSALOME_setRuntime()
383   r = pilot.getRuntime()
384   p=r.createProc("pr")
385   ti=p.getTypeCode("int")
386   #node1
387   n1=r.createScriptNode("","node1")
388   p.edAddChild(n1)
389   n1.setScript("p1=p1+10")
390   n1.edAddInputPort("p1",ti)
391   n1.edAddOutputPort("p1",ti)
392   #node2
393   n2=r.createScriptNode("","node2")
394   p.edAddChild(n2)
395   n2.setScript("p1=2*p1")
396   n2.edAddInputPort("p1",ti)
397   n2.edAddOutputPort("p1",ti)
398   #node4
399   n4=r.createCompoNode("","node4")
400   p.edAddChild(n4)
401   n4.setRef("ECHO")
402   n4.setMethod("echoDouble")
403   n4.edAddInputPort("p1",td)
404   n4.edAddOutputPort("p1",td)
405   #control links
406   p.edAddCFLink(n1,n2)
407   p.edAddCFLink(n1,n4)
408   #dataflow links
409   pout=n3.getOutputPort("p1")
410   pin=n4.getInputPort("p1")
411   #dataflow links
412   p.edAddDFLink(n1.getOutputPort("p1"),n2.getInputPort("p1"))
413   p.edAddDFLink(n1.getOutputPort("p1"),n4.getInputPort("p1"))
414   #initialisation ports
415   n1.getInputPort("p1").edInitPy(5)
416
417 Definition of composite nodes
418 '''''''''''''''''''''''''''''''''
419
420 .. _py_block:
421
422 Block
423 +++++++
424 A block is defined using the runtime createBloc method transferring the Block name to it as an argument.  The node is then 
425 attached to its definition context as an elementary node.  The following is an example Block definition in a calculation scheme::
426
427   b=r.createBloc("b1")
428   p.edAddChild(b)
429
430 Once the block has been created, all nodes and links possible in its context can be added.  
431 Repeating a part of the example above, we will get::
432
433   n1=r.createScriptNode("","node1")
434   b.edAddChild(n1)
435   n1.setScript("p1=p1+10")
436   n1.edAddInputPort("p1",ti)
437   n1.edAddOutputPort("p1",ti)
438   n2=r.createScriptNode("","node2")
439   b.edAddChild(n2)
440   n2.setScript("p1=2*p1")
441   n2.edAddInputPort("p1",ti)
442   n2.edAddOutputPort("p1",ti)
443   b.edAddCFLink(n1,n2)
444   b.edAddDFLink(n1.getOutputPort("p1"),n2.getInputPort("p1"))
445
446 .. _py_forloop:
447
448 ForLoop
449 ++++++++
450 A Forloop is defined using the runtime createForLoop method, transferring the node name to it as an argument.  
451 The node is then attached to its definition context.  The following is an example ForLoop definition in a calculation scheme::
452
453   l=r.createForLoop("l1")
454   p.edAddChild(l)
455
456 The number of iterations in the loop to be executed will be initialised using the “nsteps” port that is initialised 
457 with an integer.  For example::
458
459   ip=l.getInputPort("nsteps") 
460   ip.edInitPy(3)
461
462 There is a special method for obtaining the “nsteps” port for the loop, namely edGetNbOfTimesInputPort.  Therefore, it can also be 
463 written as follows::
464
465   ip=l.edGetNbOfTimesInputPort()
466   ip.edInitPy(3)
467
468 Finally, a method called edSetNode will be used in the context of a loop, instead of the edAddChild method, so as to add one (and only one) node.  
469 The following is a small example definition of a node inside a loop::
470
471   n1=r.createScriptNode("","node1")
472   l.edSetNode(n1)
473   n1.setScript("p1=p1+10")
474   n1.edAddInputPort("p1",ti)
475   n1.edAddOutputPort("p1",ti)
476
477 .. _py_whileloop:
478
479 WhileLoop
480 ++++++++++
481 WhileLoop node is defined in practically the same way as a ForLoop node.  The only differences apply to creation and assignment 
482 of the end of loop condition.  The createWhileLoop method is used for creation.  The “condition” port is used for the condition.  
483 If looping takes place on a node, it is important to use a data link instead of a dataflow link.  
484 The following is an example of WhileLoop node definition with a Python script internal node.  
485 The condition is initialised to True and is then changed to False by the internal node.  This results in a link loop::
486
487   wh=r.createWhileLoop("w1")
488   p.edAddChild(wh)
489   n=r.createScriptNode("","node3")
490   n.setScript("p1=0")
491   n.edAddOutputPort("p1",ti)
492   wh.edSetNode(n)
493   cport=wh.getInputPort("condition")
494   cport.edInitBool(True)
495   p.edAddLink(n.getOutputPort("p1"),cport)
496
497 There is a special method for obtaining the loop “condition” port:  edGetConditionPort.
498
499 .. _py_foreachloop:
500
501 ForEach loop
502 ++++++++++++++++
503 A ForEach node is basically defined in the same way as any other loop node.  There are several differences.  
504 The node is created with the createForEachLoop method that has an additional argument, namely the data type managed by the ForEach.  
505 The number of ForEach branches is specified with the “nbBranches” port.  The collection on which the ForEach iterates is managed by 
506 connection of the “evalSamples” and “SmplsCollection” ports.
507
508 The following is an example definition of the ForEach node with a Python script internal node that increments 
509 the element of the collection by 3::
510
511   fe=r.createForEachLoop("fe1",td)
512   p.edAddChild(fe)
513   n=r.createScriptNode("","node3")
514   n.setScript("p1=p1+3.")
515   n.edAddInputPort("p1",td)
516   n.edAddOutputPort("p1",td)
517   fe.edSetNode(n)
518   p.edAddLink(fe.getOutputPort("evalSamples"),n.getInputPort("p1"))
519   fe.getInputPort("nbBranches").edInitPy(3)
520   fe.getInputPort("SmplsCollection").edInitPy([2.,3.,4.])
521
522 Special ports for the ForEach can be obtained using the following methods instead of getInputPort and getOutputPort:
523
524 - edGetNbOfBranchesPort for the “nbBranches” port
525 - edGetSamplePort for the “evalSamples” port
526 - edGetSeqOfSamplesPort for the “SmplsCollection” port
527
528 .. _py_switch:
529
530 Switch
531 ++++++++
532 A switch node is defined in several steps.  The first two steps are creation and attachment to the context node.  
533 The node is created by calling the runtime createSwitch method with the name of the node as an argument.  The node is attached 
534 to the context node by calling the edAddChild method for a scheme or a block or edSetNode for a loop node.
535
536 The following is an example of a creation followed by an attachment::
537
538   sw=r.createSwitch("sw1")
539   p.edAddChild(sw)
540
541 The next step is to create an internal elementary or composite node by case.  The node for the default case is attached to 
542 the switch using the edSetDefaultNode method.  Nodes for other cases are attached to the switch using the edSetNode method, in 
543 which the first argument is equal to the value of the case (integer) and the second argument is equal to the internal node.
544
545 The following is an example of a switch with one script node for case “1” and another script node for the “default” case 
546 and a script node to initialise an exchanged variable::
547
548   #init
549   n=r.createScriptNode("","node3")
550   n.setScript("p1=3.5")
551   n.edAddOutputPort("p1",td)
552   p.edAddChild(n)
553   #switch
554   sw=r.createSwitch("sw1")
555   p.edAddChild(sw)
556   nk1=r.createScriptNode("","ncas1")
557   nk1.setScript("p1=p1+3.")
558   nk1.edAddInputPort("p1",td)
559   nk1.edAddOutputPort("p1",td)
560   sw.edSetNode(1,nk1)
561   ndef=r.createScriptNode("","ndefault")
562   ndef.setScript("p1=p1+5.")
563   ndef.edAddInputPort("p1",td)
564   ndef.edAddOutputPort("p1",td)
565   sw.edSetDefaultNode(ndef)
566   #initialise the select port
567   sw.getInputPort("select").edInitPy(1)
568   #connection of internal nodes
569   p.edAddDFLink(n.getOutputPort("p1"),nk1.getInputPort("p1"))
570   p.edAddDFLink(n.getOutputPort("p1"),ndef.getInputPort("p1"))
571
572 The edGetConditionPort method can be used instead of getInputPort, to obtain the special “select” port for the Switch.
573
574 .. _py_optimizerloop:
575
576 OptimizerLoop
577 +++++++++++++++++++
578
579 The following is an example of OptimizerLoop with one python script as internal node. The algorithm
580 is defined by the class async in the python module myalgo2.py::
581
582   ol=r.createOptimizerLoop("ol1","myalgo2.py","async",True)
583   p.edAddChild(ol)
584   n=r.createScriptNode("","node3")
585   n.setScript("p1=3")
586   n.edAddInputPort("p1",td)
587   n.edAddOutputPort("p1",ti)
588   ol.edSetNode(n)
589   ol.getInputPort("nbBranches").edInitPy(3)
590   ol.getInputPort("algoInit").edInitPy("coucou")
591   p.edAddLink(ol.getOutputPort("evalSamples"),n.getInputPort("p1"))
592   p.edAddLink(n.getOutputPort("p1"),ol.getInputPort("evalResults"))
593
594 .. _py_container_creation:
595
596 Creation of the three type of containers
597 ''''''''''''''''''''''''''''''''''''''''
598
599 To create a mono YACS container simply invoke::
600
601    my_mono_cont=p.createContainer("MyMonoCont","Salome")
602    my_mono_cont.setProperty("type","mono")
603
604 To create a multi YACS container simply invoke::
605
606    my_multi_cont=p.createContainer("MyMultiCont","Salome")
607    my_multi_cont.setProperty("type","multi")
608
609 To create a HP YACS container simply invoke::
610
611    my_hp_cont=p.createContainer("MyHPCont","HPSalome")
612
613 .. _py_container:
614
615 Definition of containers
616 ''''''''''''''''''''''''''''
617 A container is defined using the runtime createContainer method and it is then given a name using its setName method.  
618 The next step is to assign constraints to it by adding properties.  
619 The following is an example creation of a container named “A”::
620
621   c1=r.createContainer()
622   c1.setName("A")
623
624 A property is added to a container using its setProperty method that uses 2 arguments (character strings).  
625 The first is the property name.  The second is its value.  
626 The following is an example of this container “A” with constraints::
627
628   c1=r.createContainer()
629   c1.setName("A")
630   c1.setProperty("container_name","FactoryServer")
631   c1.setProperty("hostname","localhost")
632   c1.setProperty("mem_mb","1000")
633
634 Once the containers have been defined, SALOME components can be placed on this container.  The first step to place the component 
635 of a SALOME service node is to obtain the component instance of this service node using the getComponent method for this node.  
636 The previously defined container is then assigned to this component instance using the setContainer method of the component instance.
637
638 If it is required to place the SALOME service defined above (node “node3”) on container “A”, we will write::
639
640   n3.getComponent().setContainer(c1)
641
642 Node properties
643 '''''''''''''''''''''''''''
644 A property is added to an elementary or composite node (or is modified) using its setProperty method that has two 
645 arguments (character strings).  The first is the name of the property.  The second is its value.
646 The following is an example for the previous node “node3”::
647
648   n3.setProperty("VERBOSE","2")
649
650 Datastream connections
651 '''''''''''''''''''''''''''
652 Datastream connections are only possible for SALOME service nodes as we have seen in :ref:`principes`.  
653 We firstly need to define the datastream ports in the service node.  An input datastream port is defined using 
654 the edAddInputDataStreamPort method.  An output datastream port is defined using the edAddOutputDataStreamPort method.  
655 These methods use the port name and the datastream type as arguments.
656
657 Some datastream ports (for example CALCIUM ports) must be configured with properties.  The port setProperty method will 
658 be used to configure them.
659 The following is an example definition of the SALOME service node with datastream ports.  This is the DSCCODC component 
660 located in the DSCCODES module in the EXAMPLES base.  The datastream ports are of the “CALCIUM_integer” type 
661 with time dependency::
662
663   calcium_int=cata._typeMap['CALCIUM_integer']
664   n5=r.createCompoNode("","node5")
665   p.edAddChild(n5)
666   n5.setRef("DSCCODC")
667   n5.setMethod("prun")
668   pin=n5.edAddInputDataStreamPort("ETP_EN",calcium_int)
669   pin.setProperty("DependencyType","TIME_DEPENDENCY")
670   pout=n5.edAddOutputDataStreamPort("STP_EN",calcium_int)
671   pout.setProperty("DependencyType","TIME_DEPENDENCY")
672
673 Once the service nodes have been provided with datastream ports, all that remains is to connect them.  
674 This connection is made using the edAddLink method for the context node in the same way as for data links.  
675 The only difference is the type of ports transferred as arguments.
676
677 To complete our example, we will define a second service node and connect the datastream ports for these services::
678
679   n6=r.createCompoNode("","node6")
680   p.edAddChild(n6)
681   n6.setRef("DSCCODD")
682   n6.setMethod("prun")
683   pin=n6.edAddInputDataStreamPort("ETP_EN",calcium_int)
684   pin.setProperty("DependencyType","TIME_DEPENDENCY")
685   pout=n6.edAddOutputDataStreamPort("STP_EN",calcium_int)
686   pout.setProperty("DependencyType","TIME_DEPENDENCY")
687   p.edAddLink(n5.getOutputDataStreamPort("STP_EN"),n6.getInputDataStreamPort("ETP_EN"))
688   p.edAddLink(n6.getOutputDataStreamPort("STP_EN"),n5.getInputDataStreamPort("ETP_EN"))
689
690 Other elementary nodes
691 '''''''''''''''''''''''''''''''
692 SalomePython node
693 +++++++++++++++++++
694 A SalomePython node is defined in practically exactly the same way as a :ref:`pyfunc`.  The runtime createSInlineNode method is used 
695 instead of the createFuncNode and information about placement on a container is added in the same way as for a 
696 SALOME service node (setContainer method).
697
698 The following is an example similar to that given in :ref:`schemaxml`::
699
700   n2=r.createSInlineNode("","node2")
701   p.edAddChild(n2)
702   n2.setScript("""
703   import salome
704   salome.salome_init()
705   import PYHELLO_ORB
706   def f(p1):
707     print __container__from__YACS__
708     machine,container=__container__from__YACS__.split('/')
709     param={'hostname':machine,'container_name':container}
710     compo=salome.lcc.LoadComponent(param, "PYHELLO")
711     print compo.makeBanner(p1)
712     print p1
713   """)
714   n2.setFname("f")
715   n2.edAddInputPort("p1",ts)
716   n2.getComponent().setContainer(c1)
717
718 .. _py_datain:
719
720 DataIn node
721 +++++++++++++++
722 A DataIn node is defined using the runtime createInDataNode method.  It uses two arguments, the first of which must be “” and 
723 the second the node name.  Node data are defined by adding output data ports to it using the edAddOutputPort method 
724 and transferring the data name and its type to it as arguments.  
725 The value of the data is initialised using the port setData method thus created by transferring the value encoded in 
726 XML-RPC to it (see :ref:`initialisation`).
727
728 The following is an example of the DataIn node that defines 2 double type data (b and c) and one file type data (f)::
729
730   n=r.createInDataNode("","data1")
731   p.edAddChild(n)
732   pout=n.edAddOutputPort('a',td)
733   pout.setData("<value><double>-1.</double></value>")
734   pout=n.edAddOutputPort('b',td)
735   pout.setData("<value><double>5.</double></value>")
736   pout=n.edAddOutputPort('f',tf)
737   pout.setData("<value><objref>f.data</objref></value>")
738   
739 A value can be directly assigned to a data with a Python object, using the setDataPy method.  Example for a sequence::
740
741   pout.setDataPy([1.,5.])
742
743 .. _py_dataout:
744
745 DataOut node
746 +++++++++++++++++
747 A DataOut node is defined using the runtime createOutDataNode method.  It uses two arguments, the first of which 
748 must be “” and the second the node name .  Node results are defined by adding input data ports to it using the edAddInputPort 
749 method with the result name and its type as arguments.  The results are saved in a file using the node setRef method with the 
750 file name as an argument.  
751 A result file is copied into a local file using the setData method for the port corresponding to the result with the 
752 file name as an argument.
753
754 The following is an example of the DataOut node that defines different types (double, int, string, doubles vector, file) of 
755 results (a, b, c, d, f) and writes the corresponding values in the g.data file.  
756 The result file will be copied into the local file myfile::
757
758   n=r.createOutDataNode("","data2")
759   n.setRef("g.data")
760   p.edAddChild(n)
761   n.edAddInputPort('a',td)
762   n.edAddInputPort('b',ti)
763   n.edAddInputPort('c',ts)
764   n.edAddInputPort('d',tc3)
765   pin=n.edAddInputPort('f',tf)
766   pin.setData("monfich")
767
768 .. _py_studyin:
769
770 StudyIn node
771 ++++++++++++++
772 A StudyIn node is defined using the runtime createInDataNode method.  It uses two arguments, the first of which must be “study” 
773 and the second the node name.  The associated study is specified by adding the “StudyID” property to the node using 
774 its setProperty method.  Node data are defined by adding output data ports using the edAddOutputPOrt method, transferring 
775 the name of the data and its type as arguments.  The data is initialised with the reference in the study, using the setData method 
776 for the port thus created, transferring a character string to it containing either the SALOME Entry or the path in the study 
777 tree structure.
778
779 The following is an example of the StudyIn node that defines 2 GEOM_Object type data (a and b).  The study is assumed to be 
780 loaded into memory by SALOME as StudyID 1.  Data a is referenced by one SALOME Entry.  Data b is referenced by a path in the 
781 study tree structure::
782
783   n=r.createInDataNode("study","study1")
784   p.edAddChild(n)
785   n.setProperty("StudyID","1")
786   pout=n.edAddOutputPort('a',tgeom)
787   pout.setData("0:1:1:1")
788   pout=n.edAddOutputPort('b',tgeom)
789   pout.setData("/Geometry/Sphere_1")
790
791 .. _py_studyout:
792
793 StudyOut node
794 ++++++++++++++
795 A StudyOut node is defined using the runtime createOutDataNode method.  It uses two arguments, the first of 
796 which must be “study” and the second the node name.  The associated study is specified by adding 
797 the “StudyID” property to the node using its setProperty method.  The name of the file in which the study will be 
798 saved is specified using the node SetRef method with the file name as an argument.  
799 The node results are defined by adding input data ports to it using the edAddInputPort method, transferring the data name 
800 and type as arguments.  The setData method for the port is used to associate the entry into the study to the result, transferring 
801 a character string to it that contains either the SALOME Entry or the path in the study tree structure.
802
803 The following contains an example of the StudyOut node that defines two GEOM_Object type results (a and b).  
804 The studyId of the study used is 1.  Result a is referenced by a SALOME Entry.  The result b is referenced by a path.  
805 The complete study is saved in the study1.hdf file at the end of the calculation::
806
807   n=r.createOutDataNode("study","study2")
808   n.setRef("study1.hdf")
809   p.edAddChild(n)
810   n.setProperty("StudyID","1")
811   pout=n.edAddInputPort('a',tgeom)
812   pout.setData("0:1:2:1")
813   pout=n.edAddInputPort('b',tgeom)
814   pout.setData("/Save/Sphere_2")
815
816 Save a calculation scheme in an XML file
817 ------------------------------------------------------
818 A calculation scheme is saved in a file in the XML format using the saveSchema method for the calculation 
819 scheme, transferring the file name to it as an argument.  Before a calculation scheme constructed under Python 
820 can be saved in a consistent form in an XML file, all types defined in Python have to be added to the scheme types 
821 dictionary (see :ref:`typedict`).  The save will not do this automatically.
822
823 Proceed as follows to save the scheme p constructed above in the myscheme.xml file::
824
825   p.saveSchema("monschema.xml")
826
827 The file thus obtained can then be loaded as in :ref:`loadxml`.
828
829 Several useful operations
830 ------------------------------
831
832 Finding a node by its name
833 '''''''''''''''''''''''''''''''''''
834 A node (Python object) can be found, when all that is available is the calculation scheme object and 
835 the absolute name of the node, by calling the scheme getChildByName method, transferring the absolute name to it.
836
837 To find the Python script node defined in :ref:`pyscript`::
838
839   n=p.getChildByName("node1")
840
841 To find node “node1” node in block “b1”::
842
843   n=p.getChildByName("b1.node1")
844
845 This operation can also be used starting from a composite node provided that the relative node name is used.  
846 The previous example can be rewritten::
847
848   n=b.getChildByName("node1")
849
850 Finding a port by its name
851 '''''''''''''''''''''''''''''''''''
852 The first step to find a node port by its name is to retrieve the node by its name.  An input data port is then found 
853 using the getInputPort method, and an output data port is found using the getOutputPort method.
854
855 The following is an example starting from the previous node n::
856
857   pin=n.getOutputPort("p1")
858   pout=n.getInputPort("p2")
859
860 Obtaining a port value
861 '''''''''''''''''''''''''''''''''''
862 The value of a port is obtained using its getPyObj method.  For example::
863
864   print pin.getPyObj()
865   print pout.getPyObj()
866
867 Obtaining the state of a node
868 '''''''''''''''''''''''''''''''''''
869 The state of a node is obtained using its getEffectiveState method (see possible values in :ref:`etats`).
870
871 Removing a node from its context
872 '''''''''''''''''''''''''''''''''''
873 A node can be removed from its context node using a context method.  The method name will be different 
874 depending on the context type.
875
876 - For a block or a calculation scheme, the edRemoveChild method will be used with the node to be removed as an argument::
877
878     p.edRemoveChild(n)
879
880 - For a loop (ForLoop, WhileLoop or ForEachLoop) the edRemoveNode method will be used without any argument::
881  
882     l.edRemoveNode()
883
884 - The edRemoveChild method will be used for a Switch, with the internal node concerned as an argument::
885
886     sw.edRemoveChild(nk1)
887