Salome HOME
mergefrom branch BR_V511_PR tag mergeto_trunk_03feb09
[modules/yacs.git] / doc / calculator.rst
1
2 .. _calculator:
3
4 MED Calculator Component
5 =========================
6 This example is the following of the HELLO component. It's purpose is to show how to quickly create a new Salome module, to introduce the use of MED objects within  Salome/Corba context, to demonstrate the mechanism of exceptions, and to introduce the use of  Salome supervisor.
7
8 The MED libraries
9 -----------------
10
11 Let's go back a little bit on the different MED libraries (a web site giving general information and documentation about MED project is accessible from the Salome2 web site, links section).
12
13 The first level (called “Med File”) is a C and Fortran API that implements mesh and field persistence. The definition of the mesh format (the definition of the implemented entities, the connectivity references ...) is done in [R3]_. The use of the API is documented by [R4]_ and [R5]_. The produced files are based on hdf format, and have the “.med” extension.
14
15 The second level library is called “Med Memory”, which is a  C++ API that allows  creating mesh and field objects in memory. Mesh creation can be done from scratch using set functions, or by loading a file with a driver (see html documentation and [R6]_). Fields are also created using drivers, or by initialization with a user-defined analytic function.  Supported  file format are .med, .sauv (persistent format of the CASTEM code), vtk (only in write mode), and porflow (fluid flow and heat transfer code). Med Memory was developed to allow  the exchange of  mesh and field objects in memory between solvers. It can also be used as an internal data structure for new solvers. A last purpose was to gather in the same place existing algorithms and services around meshes and fields :
16
17 * computation of sub-elements nodal connectivities (faces, edges),
18
19
20
21 * geometric computation like volumes, barycenters,
22
23
24
25 * arithmetic operations on fields, and different kind of norms (integral or discrete, 1 or 2 morms)
26
27
28
29 * logical operations on field's supports such as union, intersection,
30
31
32
33 * entity location functionalities, and interpolation toolkit
34
35
36
37 * building of boundary support of mesh (nodes, elements or faces).
38
39
40
41 There are two libraries on the third level: a python API generated by SWIG, and a CORBA API :
42
43 * The python API wraps the complete Med Memory API, and allows a python user to manipulate Med Memory C++ objects within python.
44
45
46
47 * The CORBA API was written to facilitate distributed computation inside Salome. The API defines interfaces for the main MED objects (FIELD, SUPPORT, MESH). These interfaces are defined in the file MED.idl, they propose methods that allow distant users to access data. They are implemented by CORBA servants that encapsulate C++ Med Memory objects.  This last library is the one we are using here to implement the CALCULATOR component on the server side.
48
49
50
51 Finally, on the client side, we will demonstrate the use of the MEDClient classes. These classes are proxy classes designed to facilitate and optimize interaction of distant MED CORBA objects with local solvers. The Corba API is completely hidden to clients.
52
53 Creation of a new Salome module - Compilation
54 ---------------------------------------------
55
56 The first step when developing a new Salome module is to create a directories tree with makefiles that allow you to compile a Salome component. This directories tree must follow Salome2 rules, which are described in [R1]_ and [R2]_. Create a complete tree from scratch is complicated, and error prone. The easiest way consist to find an existing module that “looks like the one you need”, and copy it. A shell script was written to facilitate this step :  renameSalomeModule . This utility replace, after copying a module, any occurrence of the old name by the new one, thus avoiding to forget one of them. In the following example, we create a  CALCULATOR_SRC   module by copying  HELLO_SRC  module, then  renameSalomeModule  replace any occurrence of HELLO by  CALCULATOR  in  CALCULATOR_SRC  module:
57
58
59
60 ::
61
62     cp -r HELLO_SRC CALCULATOR_SRC
63     renameSalomeModule  HELLO CALCULATOR CALCULATOR_SRC
64
65
66
67
68 The remaining charge for the developer is to define the module interface (by writing a CORBA IDL file), and to implement it. But before, you may want to check that your  duplicated module still compiles :
69
70
71
72 ::
73
74     CALCULATOR_SRC/build_configure
75     mkdir CALCULATOR_BUILD
76     mkdir CALCULATOR_INSTALL
77     cd CALCULATOR_BUILD
78     ../CALCULATOR_SRC/configure --prefix=installDir
79     make && make install
80
81
82
83
84
85 Modification of interface (IDL)
86 -------------------------------
87
88 The chosen methods demonstrate the use of MED fields ( FIELDDOUBLE  interface) as in/out parameters and return value.
89
90
91
92 ::
93
94     #ifndef __CALCULATOR_GEN__
95     #define __CALCULATOR_GEN__
96     
97     #include "SALOME_Component.idl"
98     #include "SALOME_Exception.idl"
99     #include "MED.idl"
100     
101     module CALCULATOR_ORB
102     {
103       /*! \brief Interface of the %CALCULATOR component
104        */
105       interface CALCULATOR_Gen : Engines::Component
106       {
107          /*!
108              Calculate the maximum relative difference of field with the previous one.
109              At first call, store passed field and return 1.
110           */
111          double convergenceCriteria(
112         in SALOME_MED::FIELDDOUBLE field);
113          /*!
114              Apply to each (scalar) field component the linear function x -> ax+b.
115              Release field1 after use.
116           */
117          SALOME_MED::FIELDDOUBLE applyLin(
118         in SALOME_MED::FIELDDOUBLE field1, 
119         in double a1, 
120         in double a2);
121     
122          /*!
123              Addition of fields.
124              Return exception if fields are not compatible.
125              Release field1 and field2 after use.
126          */
127          SALOME_MED::FIELDDOUBLE add(
128         in SALOME_MED::FIELDDOUBLE field1, 
129         in SALOME_MED::FIELDDOUBLE field2)
130              raises (SALOME::SALOME_Exception);
131     
132          /*!
133              return euclidian norm of field
134              Release field after use.
135           */
136          double norm2(in SALOME_MED::FIELDDOUBLE field);
137         /*!
138              return L2 norm of field
139              Release field after use.
140           */
141          double normL2(in SALOME_MED::FIELDDOUBLE field);
142     
143          /*!
144              return L1 norm of field
145              Release field after use.
146           */
147          double normL1(in SALOME_MED::FIELDDOUBLE field);
148     
149          /*!
150              return max norm of field
151              Release field after use.
152           */
153          double normMax(in SALOME_MED::FIELDDOUBLE field);
154     
155          /*!
156              This utility method print in standard output the coordinates & field values
157              Release field after use.
158           */
159          void printField(in SALOME_MED::FIELDDOUBLE field);
160     
161          /*!
162              This method clones field in four examples.
163              Release field after use.
164           */
165          void cloneField(
166         in SALOME_MED::FIELDDOUBLE field, 
167         out SALOME_MED::FIELDDOUBLE clone1,
168         out SALOME_MED::FIELDDOUBLE clone2, 
169         out SALOME_MED::FIELDDOUBLE clone3,
170         out SALOME_MED::FIELDDOUBLE clone4 );
171       };
172     };
173     
174     #endif
175
176
177
178
179 The main points to note are:
180
181 * the protection against multiple inclusion (ifndef instruction),
182
183
184
185 * the inclusion of   SALOME_Component.idl   and   SALOME_Exception.idl  files, necessary for each Salome component (the CALCULATOR interface inherit from  Engines::Component  to benefit common services),
186
187
188
189 * the inclusion of MED.idl, because we are using the  FIELDDOUBLE  interface defined in  SALOME_MED  module.
190
191
192
193 * The use of “doxygen like” comments, to allow automatic generation of inline documentation.
194
195
196
197
198
199
200 Component implementation
201 ------------------------
202
203 After defining the interface of our component, we have to implement it by modifying the C++ implementation class ( CALCULATOR.hxx  and  CALCULATOR.cxx  in  src/CALCULATOR  directory) and adapt it to the new IDL. In our case, this means to replace the HELLO method “ char* makeBanner(const char* name) ” with new methods that extends the IDL-generated implementation base class (as explained in the HELLO documentation, when compiling the IDL, CORBA generates an abstract base class, that the developer of the component has to derive and write code for the abstract methods). For the CALCULATOR component, the   IDL-generated  base class is called  POA_CALCULATOR_ORB::CALCULATOR_Gen  and is defined in generated header  CALCULATOR_Gen.hh .
204
205 The IDL attributes are mapped to C++ methods. This operation is normalized by CORBA. Here, we give the mapping for the types involved in our example:
206
207 ====================== ================= 
208  **IDL Type**          **C++ type**    
209 ====================== ================= 
210  **double**            CORBA::DOUBLE   
211  **in FIELDDOUBLE**    FIELDDOUBLE_ptr 
212  **out  FIELDDOUBLE**  FIELDDOUBLE_out 
213  **FIELDDOUBLE**       FIELDDOUBLE_ptr 
214 ====================== ================= 
215
216
217 FIELDDOUBLE_ptr  and  FIELDDOUBLE_out  are C++ classes generated by the IDL compiler to map the MED CORBA interface   FIELDDOUBLE . We will see below how to create such classes. But before, let's have a look on the new header of the user-defined derived class   CALCULATOR.hxx  :
218
219
220
221 ::
222
223     #ifndef _CALCULATOR_HXX_
224     #define _CALCULATOR_HXX_
225     
226     #include <SALOMEconfig.h>
227     #include CORBA_SERVER_HEADER(CALCULATOR_Gen)
228     #include CORBA_CLIENT_HEADER(MED)
229     #include "SALOME_Component_i.hxx"
230     
231     class CALCULATOR:
232       public POA_CALCULATOR_ORB::CALCULATOR_Gen,
233       public Engines_Component_i
234     {
235     
236     public:
237         CALCULATOR(CORBA::ORB_ptr orb,
238                 PortableServer::POA_ptr poa,
239                 PortableServer::ObjectId * contId,
240                 const char *instanceName,
241                 const char *interfaceName);
242         virtual ~CALCULATOR();
243     
244         CORBA::Double convergenceCriteria(
245         SALOME_MED::FIELDDOUBLE_ptr field);
246         CORBA::Double normMax(
247         SALOME_MED::FIELDDOUBLE_ptr field1);
248         CORBA::Double normL2(
249         SALOME_MED::FIELDDOUBLE_ptr field1);
250         CORBA::Double norm2(SALOME_MED::FIELDDOUBLE_ptr field1);
251         CORBA::Double normL1(
252         SALOME_MED::FIELDDOUBLE_ptr field1);
253         SALOME_MED::FIELDDOUBLE_ptr applyLin(
254         SALOME_MED::FIELDDOUBLE_ptr field1,
255         CORBA::Double a,CORBA::Double b);
256         SALOME_MED::FIELDDOUBLE_ptr add(
257         SALOME_MED::FIELDDOUBLE_ptr field1,
258         SALOME_MED::FIELDDOUBLE_ptr field2)
259             throw ( SALOME::SALOME_Exception );
260         void printField(SALOME_MED::FIELDDOUBLE_ptr field);
261         void cloneField(
262         SALOME_MED::FIELDDOUBLE_ptr field,
263         SALOME_MED::FIELDDOUBLE_out clone1,
264         SALOME_MED::FIELDDOUBLE_out clone2,
265         SALOME_MED::FIELDDOUBLE_out clone3,
266         SALOME_MED::FIELDDOUBLE_out clone4);
267     };
268     
269     
270     extern "C"
271         PortableServer::ObjectId * CALCULATOREngine_factory(
272                 CORBA::ORB_ptr orb,
273                 PortableServer::POA_ptr poa,
274                 PortableServer::ObjectId * contId,
275                 const char *instanceName,
276                 const char *interfaceName);
277     
278     
279     #endif
280
281
282
283
284 The main points to note are:
285
286 * the inclusion of  CORBA_SERVER_HEADER(CALCULATOR_Gen)  : this macro includes the header of the base class generated by CORBA
287
288
289
290 * the inclusion of  CORBA_CLIENT_HEADER(MED)  : this macro includes the header we needs to use CORBA MED interfaces (here, to use  FIELDDOUBLE  interface).
291
292
293
294
295 The implementation of the methods is very simple, thanks to the use of MEDClient library, which create an automatic link between CORBA and C++ objects. As a first example, let's consider the implementation of the  norm2  method. For being more concise, we do not explicit here the namespace  SALOME_MED::  .
296
297
298
299 ::
300
301     CORBA::Double CALCULATOR::norm2(FIELDDOUBLE_ptr field1)
302     {
303         beginService( "CALCULATOR::norm2");
304         BEGIN_OF("CALCULATOR::Norm2(FIELDDOUBLE_ptr field1)");
305     
306         // Create a local field from corba field
307         // apply method normMax on it. When exiting the function 
308         // f1 is deleted, and with it the remote corba field.
309         FIELDClient<double> f1(field1);
310         CORBA::Double norme = f1.norm2();
311         END_OF("CALCULATOR::Norm2(FIELDDOUBLE_ptr field1)");
312         endService( "CALCULATOR::norm2");
313         return norme;
314     }
315
316
317
318
319 The  norm2  method receives as an input parameter a reference to a distant MED CORBA field (named  field1 ). It plays the role of the client toward the distant field  field1 . As a client, we could directly call the methods of the  FIELDDOUBLE  CORBA API, for example call the  getValue()  method to retrieve the field values as an array. Doing this has some drawbacks. The transfer is not optimized because values are duplicated on server side. On the client side, we retrieve an array, but if we want to use existing solver or a function that takes an MedMemory C++ field, we need to rebuild a C++ field from the array, which is fastidious. That's why we are using here FIELDClient class :  FIELDClient<double>.  This is a proxy C++ template class (also available for int type), that inherit the interface of the MedMemory C++  FIELD<double>  class. Therefore, it can be used anywhere in place where a  FIELD<double>  is expected. The characteristics of this class are :
320
321 * it holds the CORBA reference of the distant field – and release it when object get out of scope (done in the class destructor),
322
323
324
325 * on creation, only the general information are retrieved from distant field (like size, number of component), not the complete array,
326
327
328
329 * complete array is transfered only  on demand ,
330
331
332
333 * the transfer is optimized : duplication is avoided on server side, and transfer protocol may be switched at compile time (for example to MPI on a parallel machine), without any modification of client code,
334
335
336
337 * the memory is automatically managed : when deleted, the FIELDClient release the  CORBA reference it holds.
338
339
340
341 * and   as already said, it can be used anywhere in state of a FIELD<double>, thus facilitating re-use of existing C++ API.
342
343
344
345 In our example, we simply create a  FIELDClient , and then call on it the norm2 method of the MedMemory C++ API :
346
347 ::
348
349         FIELDClient<double> f1(field1);
350         CORBA::Double norme = f1.norm2();
351
352
353 A client class was also created for MESH, called  MESHClient , with the same  characteristics. For meshes, all the arrays (connectivities, coordinates) are transferred on demand, which is generally more interesting than for fields (where we usually need to retrieve values soon or later).
354
355 BEGIN_OF  et  END_OF  macros are used to send traces to standard output when working on debug mode.   BeginService  and  endService  macros are used to send signals to the Supervisor to let him know the state of computation.
356
357 As a second example, let consider the applyLin method, which plays both the role of client and server:
358
359
360
361 ::
362
363     FIELDDOUBLE_ptr CALCULATOR::applyLin(
364         FIELDDOUBLE_ptr field1,
365         CORBA::Double a,CORBA::Double b)
366     {
367         beginService( "CALCULATOR::applyLin");
368         BEGIN_OF("CALCULATOR::applyLin");
369         // create a local field on the heap, 
370         // because it has to remain after exiting the function
371         FIELD<double> * f1 = new FIELDClient<double>(field1);
372         f1->applyLin(a,b);
373         
374         // create servant from f1, give it the property of c++ 
375         // field (parameter true).  This imply that when the 
376         // client will release it's field, it will delete
377         // NewField,and f1.
378         FIELDDOUBLE_i * NewField = new FIELDDOUBLE_i(f1,true) ;
379         // activate object
380         FIELDDOUBLE_ptr myFieldIOR = NewField->_this() ;
381     
382         END_OF("CALCULATOR::applyLin");
383         endService( "CALCULATOR::applyLin");
384         return myFieldIOR;
385
386
387
388
389 The method is client for the parameter field  field1 , and server for the returned field  NewField . The client part (treatment of  field1 ) is similar to the first example : we create with  field1  a FIELDClient  f1  and apply on it  C++ method applyLin. The difference is that creation is done on the heap, not on the stack (we will explain why later) :
390
391 ::
392
393     FIELDDOUBLE_i * NewField = new FIELDDOUBLE_i(f1,true) ;
394     f1->applyLin(a,b);
395
396
397 For the server part, we create a CORBA field (class  FIELDDOUBLE_i ), activate it and return a reference on it :
398
399 ::
400
401     FIELDDOUBLE_i * NewField = new FIELDDOUBLE_i(f1,true) ;
402     FIELDDOUBLE_ptr myFieldIOR = NewField->_this() ;
403     return myFieldIOR;
404
405
406 The parameters passed to the   FIELDDOUBLE_i  constructor are the C++ field f1 that is wrapped and used to give the services declared in IDL, and a boolean that indicates if ownership of wrapped field is transferred or not. If ownership is transferred, this means that when the CORBA field will be released by a client (for example by a  FIELDClient  created with a reference on it), it will delete the C++ field it holds. For example, the following code a hypothetic client could write would cause deletion of C++ field  f1  :
407
408
409
410 ::
411
412     FIELDDOUBLE_ptr distant_f = CALCULATOR::applyLin(f,a,b);
413     FIELD<double>* local_f = new FIELDClient<double>(distant_f);
414     //  .. Use  local_f 
415     delete  local_f; // causes release of distant_f and deletion
416                      // of the C++ field it holds  
417
418
419
420
421 This is why  f1  is created on the heap and is not deleted : we want it to survive the end of the method! It will be deleted when client will release it reference.
422
423
424
425
426
427
428
429
430
431 .. [R1] Guide pour le développement d'un module Salome 2 en Python (C. Caremoli) (voir :ref:`pysalome`).
432
433 .. [R2] Guide pour le développement d'un module Salome 2 en C++ (N. Crouzet) (voir :ref:`cppsalome`).
434
435 .. [R3]  Définition du modèle d'échange de données MED V2.2 (V. Lefebvre, E. Fayolle).
436
437 .. [R4]  Guide de référence de la bibliothèque MED V2.2 (V. Lefebvre, E. Fayolle).
438
439 .. [R5]  Guide d'utilisation de la bibliothèque MED V2.2 (V. Lefebvre, E. Fayolle).
440
441 .. [R6]  User's guide of Med Memory (P. Goldbronn, E. Fayolle, N. Bouhamou).
442
443
444
445
446