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.
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).
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.
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 :
17 * computation of sub-elements nodal connectivities (faces, edges),
21 * geometric computation like volumes, barycenters,
25 * arithmetic operations on fields, and different kind of norms (integral or discrete, 1 or 2 morms)
29 * logical operations on field's supports such as union, intersection,
33 * entity location functionalities, and interpolation toolkit
37 * building of boundary support of mesh (nodes, elements or faces).
41 There are two libraries on the third level: a python API generated by SWIG, and a CORBA API :
43 * The python API wraps the complete Med Memory API, and allows a python user to manipulate Med Memory C++ objects within python.
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.
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.
53 Creation of a new Salome module - Compilation
54 ---------------------------------------------
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:
62 cp -r HELLO_SRC CALCULATOR_SRC
63 renameSalomeModule HELLO CALCULATOR CALCULATOR_SRC
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 :
74 CALCULATOR_SRC/build_configure
75 mkdir CALCULATOR_BUILD
76 mkdir CALCULATOR_INSTALL
78 ../CALCULATOR_SRC/configure --prefix=installDir
85 Modification of interface (IDL)
86 -------------------------------
88 The chosen methods demonstrate the use of MED fields ( FIELDDOUBLE interface) as in/out parameters and return value.
94 #ifndef __CALCULATOR_GEN__
95 #define __CALCULATOR_GEN__
97 #include "SALOME_Component.idl"
98 #include "SALOME_Exception.idl"
101 module CALCULATOR_ORB
103 /*! \brief Interface of the %CALCULATOR component
105 interface CALCULATOR_Gen : Engines::Component
108 Calculate the maximum relative difference of field with the previous one.
109 At first call, store passed field and return 1.
111 double convergenceCriteria(
112 in SALOME_MED::FIELDDOUBLE field);
114 Apply to each (scalar) field component the linear function x -> ax+b.
115 Release field1 after use.
117 SALOME_MED::FIELDDOUBLE applyLin(
118 in SALOME_MED::FIELDDOUBLE field1,
124 Return exception if fields are not compatible.
125 Release field1 and field2 after use.
127 SALOME_MED::FIELDDOUBLE add(
128 in SALOME_MED::FIELDDOUBLE field1,
129 in SALOME_MED::FIELDDOUBLE field2)
130 raises (SALOME::SALOME_Exception);
133 return euclidian norm of field
134 Release field after use.
136 double norm2(in SALOME_MED::FIELDDOUBLE field);
138 return L2 norm of field
139 Release field after use.
141 double normL2(in SALOME_MED::FIELDDOUBLE field);
144 return L1 norm of field
145 Release field after use.
147 double normL1(in SALOME_MED::FIELDDOUBLE field);
150 return max norm of field
151 Release field after use.
153 double normMax(in SALOME_MED::FIELDDOUBLE field);
156 This utility method print in standard output the coordinates & field values
157 Release field after use.
159 void printField(in SALOME_MED::FIELDDOUBLE field);
162 This method clones field in four examples.
163 Release field after use.
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 );
179 The main points to note are:
181 * the protection against multiple inclusion (ifndef instruction),
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),
189 * the inclusion of MED.idl, because we are using the FIELDDOUBLE interface defined in SALOME_MED module.
193 * The use of “doxygen like” comments, to allow automatic generation of inline documentation.
200 Component implementation
201 ------------------------
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 .
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:
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 ====================== =================
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 :
223 #ifndef _CALCULATOR_HXX_
224 #define _CALCULATOR_HXX_
226 #include <SALOMEconfig.h>
227 #include CORBA_SERVER_HEADER(CALCULATOR_Gen)
228 #include CORBA_CLIENT_HEADER(MED)
229 #include "SALOME_Component_i.hxx"
232 public POA_CALCULATOR_ORB::CALCULATOR_Gen,
233 public Engines_Component_i
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();
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);
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);
271 PortableServer::ObjectId * CALCULATOREngine_factory(
273 PortableServer::POA_ptr poa,
274 PortableServer::ObjectId * contId,
275 const char *instanceName,
276 const char *interfaceName);
284 The main points to note are:
286 * the inclusion of CORBA_SERVER_HEADER(CALCULATOR_Gen) : this macro includes the header of the base class generated by CORBA
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).
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:: .
301 CORBA::Double CALCULATOR::norm2(FIELDDOUBLE_ptr field1)
303 beginService( "CALCULATOR::norm2");
304 BEGIN_OF("CALCULATOR::Norm2(FIELDDOUBLE_ptr field1)");
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");
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 :
321 * it holds the CORBA reference of the distant field – and release it when object get out of scope (done in the class destructor),
325 * on creation, only the general information are retrieved from distant field (like size, number of component), not the complete array,
329 * complete array is transfered only on demand ,
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,
337 * the memory is automatically managed : when deleted, the FIELDClient release the CORBA reference it holds.
341 * and as already said, it can be used anywhere in state of a FIELD<double>, thus facilitating re-use of existing C++ API.
345 In our example, we simply create a FIELDClient , and then call on it the norm2 method of the MedMemory C++ API :
349 FIELDClient<double> f1(field1);
350 CORBA::Double norme = f1.norm2();
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).
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.
357 As a second example, let consider the applyLin method, which plays both the role of client and server:
363 FIELDDOUBLE_ptr CALCULATOR::applyLin(
364 FIELDDOUBLE_ptr field1,
365 CORBA::Double a,CORBA::Double b)
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);
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
378 FIELDDOUBLE_i * NewField = new FIELDDOUBLE_i(f1,true) ;
380 FIELDDOUBLE_ptr myFieldIOR = NewField->_this() ;
382 END_OF("CALCULATOR::applyLin");
383 endService( "CALCULATOR::applyLin");
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) :
393 FIELDDOUBLE_i * NewField = new FIELDDOUBLE_i(f1,true) ;
397 For the server part, we create a CORBA field (class FIELDDOUBLE_i ), activate it and return a reference on it :
401 FIELDDOUBLE_i * NewField = new FIELDDOUBLE_i(f1,true) ;
402 FIELDDOUBLE_ptr myFieldIOR = NewField->_this() ;
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 :
412 FIELDDOUBLE_ptr distant_f = CALCULATOR::applyLin(f,a,b);
413 FIELD<double>* local_f = new FIELDClient<double>(distant_f);
415 delete local_f; // causes release of distant_f and deletion
416 // of the C++ field it holds
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.
431 .. [R1] Guide pour le développement d'un module Salome 2 en Python (C. Caremoli) (voir :ref:`pysalome`).
433 .. [R2] Guide pour le développement d'un module Salome 2 en C++ (N. Crouzet) (voir :ref:`cppsalome`).
435 .. [R3] Définition du modèle d'échange de données MED V2.2 (V. Lefebvre, E. Fayolle).
437 .. [R4] Guide de référence de la bibliothèque MED V2.2 (V. Lefebvre, E. Fayolle).
439 .. [R5] Guide d'utilisation de la bibliothèque MED V2.2 (V. Lefebvre, E. Fayolle).
441 .. [R6] User's guide of Med Memory (P. Goldbronn, E. Fayolle, N. Bouhamou).