Salome HOME
bos #26458 Versioning of sources via git commit id (sha1)
[modules/yacs.git] / doc / calculator.rst
1
2 .. _calculator:
3
4 MEDCoupling 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 MEDCoupling 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 / MEDCoupling project is accessible from the SALOME 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 “MEDCoupling”, which is a  C++ API that allows to create mesh and field objects in memory and manipulate them. 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 (MEDLoader), or by initialization with a user-defined analytic function.  Supported  file format are .med, .sauv (persistent format of the CASTEM code) and vtk (only in write mode). MEDCoupling 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 interface to exchange meshes, fields objects between different processes and SALOME containers without using files:
42
43 * The python API wraps the complete MEDCoupling API, and allows a python user to manipulate MEDCoupling C++ objects within python.
44
45
46
47 * The CORBA interface defined in MEDCouplingCorbaInterface.idl, propose methods that allow distant users to retrieve remote meshes, fields objects and to make available objects for others components. They are implemented by CORBA servants that encapsulate C++ MEDCoupling 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 MEDCoupling 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 SALOME 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     mkdir CALCULATOR_BUILD
71     mkdir CALCULATOR_INSTALL
72     cd CALCULATOR_BUILD
73     cmake -DCMAKE_BUILD_TYPE=<Mode> -DCMAKE_INSTALL_PREFIX=CALCULATOR_INSTALL ../CALCULATOR_SRC
74     make && make install
75
76
77 Where <Mode> is build mode (Release or Debug)
78
79
80 Modification of interface (IDL)
81 -------------------------------
82
83 The chosen methods demonstrate the use of MED fields ( FIELDDOUBLE  interface) as in/out parameters and return value.
84
85
86
87 ::
88
89     #ifndef __CALCULATOR_GEN__
90     #define __CALCULATOR_GEN__
91     
92     #include "SALOME_Component.idl"
93     #include "SALOME_Exception.idl"
94     #include "MEDCouplingCorbaServant.idl"
95     
96     module CALCULATOR_ORB
97     {
98       /*! \brief Interface of the %CALCULATOR component
99        */
100       interface CALCULATOR_Gen : Engines::EngineComponent
101       {
102          /*!
103              Calculate the maximum relative difference of field with the previous one.
104              At first call, store passed field and return 1.
105           */
106          double convergenceCriteria(
107         in SALOME_MED::MEDCouplingFieldDoubleCorbaInterface field);
108          /*!
109              Apply to each (scalar) field component the linear function x -> ax+b.
110              Release field1 after use.
111           */
112          SALOME_MED::MEDCouplingFieldDoubleCorbaInterface applyLin(
113         in SALOME_MED::MEDCouplingFieldDoubleCorbaInterface field1, 
114         in double a1, 
115         in double a2);
116     
117          /*!
118              Addition of fields.
119              Return exception if fields are not compatible.
120              Release field1 and field2 after use.
121          */
122          SALOME_MED::MEDCouplingFieldDoubleCorbaInterface add(
123         in SALOME_MED::MEDCouplingFieldDoubleCorbaInterface field1, 
124         in SALOME_MED::MEDCouplingFieldDoubleCorbaInterface field2)
125              raises (SALOME::SALOME_Exception);
126     
127          /*!
128              return euclidian norm of field
129              Release field after use.
130           */
131          double norm2(in SALOME_MED::MEDCouplingFieldDoubleCorbaInterface field);
132         /*!
133              return L2 norm of field
134              Release field after use.
135           */
136          double normL2(in SALOME_MED::MEDCouplingFieldDoubleCorbaInterface field);
137     
138          /*!
139              return L1 norm of field
140              Release field after use.
141           */
142          double normL1(in SALOME_MED::MEDCouplingFieldDoubleCorbaInterface field);
143     
144          /*!
145              return max norm of field
146              Release field after use.
147           */
148          double normMax(in SALOME_MED::MEDCouplingFieldDoubleCorbaInterface field);
149     
150          /*!
151              This utility method print in standard output the coordinates & field values
152              Release field after use.
153           */
154          void printField(in SALOME_MED::MEDCouplingFieldDoubleCorbaInterface field);
155     
156          /*!
157              This method clones field in four examples.
158              Release field after use.
159           */
160          void cloneField(
161         in SALOME_MED::MEDCouplingFieldDoubleCorbaInterface field, 
162         out SALOME_MED::MEDCouplingFieldDoubleCorbaInterface clone1,
163         out SALOME_MED::MEDCouplingFieldDoubleCorbaInterface clone2, 
164         out SALOME_MED::MEDCouplingFieldDoubleCorbaInterface clone3,
165         out SALOME_MED::MEDCouplingFieldDoubleCorbaInterface clone4 );
166       };
167     };
168     
169     #endif
170
171
172
173
174 The main points to note are:
175
176 * the protection against multiple inclusion (ifndef instruction),
177
178
179
180 * the inclusion of   SALOME_Component.idl   and   SALOME_Exception.idl  files, necessary for each SALOME component (the CALCULATOR interface inherit from  Engines::EngineComponent  to benefit common services),
181
182
183
184 * the inclusion of MEDCouplingCorbaServant.idl, because we are using the  MEDCouplingFieldDoubleCorbaInterface  interface defined in  SALOME_MED  module.
185
186
187
188 * The use of “doxygen like” comments, to allow automatic generation of inline documentation.
189
190
191
192
193
194
195 Component implementation
196 ------------------------
197
198 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 .
199
200 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:
201
202 =============================================== =========================================
203  **IDL Type**                                   **C++ type**    
204 =============================================== =========================================
205  **double**                                     CORBA::DOUBLE   
206  **in MEDCouplingFieldDoubleCorbaInterface**    MEDCouplingFieldDoubleCorbaInterface_ptr 
207  **out  MEDCouplingFieldDoubleCorbaInterface**  MEDCouplingFieldDoubleCorbaInterface_out 
208  **MEDCouplingFieldDoubleCorbaInterface**       MEDCouplingFieldDoubleCorbaInterface_ptr 
209 =============================================== =========================================
210
211
212 MEDCouplingFieldDoubleCorbaInterface_ptr  and  MEDCouplingFieldDoubleCorbaInterface_out  are C++ classes generated by the IDL compiler to map the MEDCoupling CORBA interface  MEDCouplingFieldDoubleCorbaInterface . 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  :
213
214
215
216 ::
217
218     #ifndef _CALCULATOR_HXX_
219     #define _CALCULATOR_HXX_
220     
221     #include <SALOMEconfig.h>
222     #include CORBA_SERVER_HEADER(CALCULATOR_Gen)
223     #include CORBA_CLIENT_HEADER(MEDCouplingCorbaServant)
224     #include "SALOME_Component_i.hxx"
225     
226     class CALCULATOR:
227       public POA_CALCULATOR_ORB::CALCULATOR_Gen,
228       public Engines_Component_i
229     {
230     
231     public:
232         CALCULATOR(CORBA::ORB_ptr orb,
233                 PortableServer::POA_ptr poa,
234                 PortableServer::ObjectId * contId,
235                 const char *instanceName,
236                 const char *interfaceName);
237         virtual ~CALCULATOR();
238     
239         CORBA::Double convergenceCriteria(
240         SALOME_MED::MEDCouplingFieldDoubleCorbaInterface_ptr field);
241         CORBA::Double normMax(
242         SALOME_MED::MEDCouplingFieldDoubleCorbaInterface_ptr field1);
243         CORBA::Double normL2(
244         SALOME_MED::MEDCouplingFieldDoubleCorbaInterface_ptr field1);
245         CORBA::Double norm2(SALOME_MED::MEDCouplingFieldDoubleCorbaInterface_ptr field1);
246         CORBA::Double normL1(
247         SALOME_MED::MEDCouplingFieldDoubleCorbaInterface_ptr field1);
248         SALOME_MED::MEDCouplingFieldDoubleCorbaInterface_ptr applyLin(
249         SALOME_MED::MEDCouplingFieldDoubleCorbaInterface_ptr field1,
250         CORBA::Double a,CORBA::Double b);
251         SALOME_MED::MEDCouplingFieldDoubleCorbaInterface_ptr add(
252         SALOME_MED::MEDCouplingFieldDoubleCorbaInterface_ptr field1,
253         SALOME_MED::MEDCouplingFieldDoubleCorbaInterface_ptr field2)
254             throw ( SALOME::SALOME_Exception );
255         void printField(SALOME_MED::MEDCouplingFieldDoubleCorbaInterface_ptr field);
256         void cloneField(
257         SALOME_MED::MEDCouplingFieldDoubleCorbaInterface_ptr field,
258         SALOME_MED::FIELDDOUBLE_out clone1,
259         SALOME_MED::FIELDDOUBLE_out clone2,
260         SALOME_MED::FIELDDOUBLE_out clone3,
261         SALOME_MED::FIELDDOUBLE_out clone4);
262     };
263     
264     
265     extern "C"
266         PortableServer::ObjectId * CALCULATOREngine_factory(
267                 CORBA::ORB_ptr orb,
268                 PortableServer::POA_ptr poa,
269                 PortableServer::ObjectId * contId,
270                 const char *instanceName,
271                 const char *interfaceName);
272     
273     
274     #endif
275
276
277
278
279 The main points to note are:
280
281 * the inclusion of  CORBA_SERVER_HEADER(CALCULATOR_Gen)  : this macro includes the header of the base class generated by CORBA
282
283
284
285 * the inclusion of  CORBA_CLIENT_HEADER(MEDCouplingCorbaServant)  : this macro includes the header we needs to use CORBA MEDCoupling interfaces (here, to use  MEDCouplingFieldDoubleCorbaInterface  interface).
286
287
288
289
290 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::  .
291
292
293
294 ::
295
296     CORBA::Double CALCULATOR::norm2(SALOME_MED::MEDCouplingFieldDoubleCorbaInterface_ptr field1)
297     {
298         beginService( "CALCULATOR::norm2");
299         BEGIN_OF("CALCULATOR::Norm2(SALOME_MED::MEDCouplingFieldDoubleCorbaInterface_ptr field1)");
300     
301         // Create a local field from corba field
302         // apply method normMax on it. When exiting the function 
303         // f1 is deleted, and with it the remote corba field.
304         ParaMEDMEM::MEDCouplingAutoRefCountObjectPtr<ParaMEDMEM::MEDCouplingFieldDouble> f1=ParaMEDMEM::MEDCouplingFieldDoubleClient::New(field1);
305         CORBA::Double norme = f1->norm2();
306         END_OF("CALCULATOR::Norm2(SALOME_MED::MEDCouplingFieldDoubleCorbaInterface_ptr field1)");
307         endService( "CALCULATOR::norm2");
308         return norme;
309     }
310
311
312
313
314 The  norm2  method receives as an input parameter a reference to a distant MEDCoupling 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  MEDCouplingFieldDouble  CORBA interface, 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 MEDCoupling C++ field, we need to rebuild a C++ field from the array, which is fastidious. That's why we are using here MEDCouplingFieldDoubleClient class :  MEDCouplingFieldDouble.  This is a proxy C++ template class (also available for int type), that inherit the interface of the MEDCoupling C++  MEDCouplingFieldDouble  class. Therefore, it can be used anywhere in place where a  MEDCouplingFieldDouble  is expected. The characteristics of this class are :
315
316 * it holds the CORBA reference of the distant field – and release it when object get out of scope (done in the class destructor),
317
318
319
320 * on creation, only the general information are retrieved from distant field (like size, number of component), not the complete array,
321
322
323
324 * complete array is transfered only  on demand ,
325
326
327
328 * 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,
329
330
331
332 * the memory is automatically managed : when deleted, the MEDCouplingFieldDoubleClient release the  CORBA reference it holds.
333
334
335
336 * and   as already said, it can be used anywhere in state of a MEDCouplingFieldDouble, thus facilitating re-use of existing C++ API.
337
338
339
340 In our example, we simply create a  MEDCouplingFieldDoubleClient , and then call on it the norm2 method of the MEDCoupling C++ API :
341
342 ::
343
344         ParaMEDMEM::MEDCouplingAutoRefCountObjectPtr<ParaMEDMEM::MEDCouplingFieldDouble> f1=ParaMEDMEM::MEDCouplingFieldDoubleClient::New(field1);
345         CORBA::Double norme = f1->norm2();
346
347
348 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).
349
350 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.
351
352 As a second example, let consider the applyLin method, which plays both the role of client and server:
353
354
355
356 ::
357
358     SALOME_MED::MEDCouplingFieldDoubleCorbaInterface_ptr CALCULATOR::applyLin(
359         SALOME_MED::MEDCouplingFieldDoubleCorbaInterface_ptr field1,
360         CORBA::Double a,CORBA::Double b)
361     {
362         beginService( "CALCULATOR::applyLin");
363         BEGIN_OF("CALCULATOR::applyLin");
364         // create a local field on the heap, 
365         // because it has to remain after exiting the function
366         ParaMEDMEM::MEDCouplingAutoRefCountObjectPtr<ParaMEDMEM::MEDCouplingFieldDouble> f1=ParaMEDMEM::MEDCouplingFieldDoubleClient::New(field1);
367         int nbOfCompo=f1->getArray()->getNumberOfComponents();
368         f1->getArray()->rearrange(1);
369         ParaMEDMEM::MEDCouplingFieldDoubleServant *NewField=NULL;
370         SALOME_MED::MEDCouplingFieldDoubleCorbaInterface_ptr myFieldIOR = NULL;
371         f1->applyLin(a,b);
372         f1->getArray()->rearrange(nbOfCompo);
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         NewField = new ParaMEDMEM::MEDCouplingFieldDoubleServant(f1);
379         // activate object
380         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 MEDCouplingFieldDoubleClient  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     ParaMEDMEM::MEDCouplingFieldDoubleServant * NewField = new ParaMEDMEM::MEDCouplingFieldDoubleServant(f1);
394     f1->applyLin(a,b);
395
396
397 For the server part, we create a CORBA field (class  ParaMEDMEM::MEDCouplingFieldDoubleCorbaInterface ), activate it and return a reference on it :
398
399 ::
400
401     ParaMEDMEM::MEDCouplingFieldDoubleServant * NewField = new ParaMEDMEM::MEDCouplingFieldDoubleServant(f1);
402     SALOME_MED::MEDCouplingFieldDoubleCorbaInterface_ptr myFieldIOR = NewField->_this() ;
403     return myFieldIOR;
404
405
406 The parameters passed to the   ParaMEDMEM::MEDCouplingFieldDoubleServant  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  MEDCouplingFieldDoubleClient  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     SALOME_MED::MEDCouplingFieldDoubleCorbaInterface_ptr distant_f = CALCULATOR::applyLin(f,a,b);
413     ParaMEDMEM::MEDCouplingAutoRefCountObjectPtr<ParaMEDMEM::MEDCouplingFieldDouble> local_f=ParaMEDMEM::MEDCouplingFieldDoubleClient::New(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 for the development of a SALOME module in Python (C. Caremoli) (see :ref:`pysalome`).
432
433 .. [R2] Guide for the development of a SALOME module in C++ (N. Crouzet) (see :ref:`cppsalome`).
434
435 .. [R3]  Définition du modèle d'échange de données MED V3 (V. Lefebvre, E. Fayolle).
436
437 .. [R4]  Guide de référence de la bibliothèque MED V3 (V. Lefebvre, E. Fayolle).
438
439 .. [R5]  Guide d'utilisation de la bibliothèque MED V3 (V. Lefebvre, E. Fayolle).
440
441 .. [R6]  User's guide of MEDCoupling (Doc HTML MED).
442
443
444
445
446