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