Salome HOME
Fix problem of make distcheck
[modules/med.git] / src / MEDSPLITTER / MEDSPLITTER_ParaDomainSelector.cxx
1 // Copyright (C) 2007-2012  CEA/DEN, EDF R&D
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19 // File      : MEDSPLITTER_ParaDomainSelector.cxx
20 // Created   : Wed Jun 24 12:39:59 2009
21 // Author    : Edward AGAPOV (eap)
22
23 #include "MEDSPLITTER_ParaDomainSelector.hxx"
24
25 #include "MEDSPLITTER_UserGraph.hxx"
26 #include "MEDSPLITTER_JointExchangeData.hxx"
27
28 #include <MEDMEM_Meshing.hxx>
29 #include <MEDMEM_DriversDef.hxx>
30
31 #include <iostream>
32 #include <numeric>
33
34 #ifdef HAVE_MPI2
35 #include <mpi.h>
36 #endif
37
38 #ifndef WIN32
39 #include <sys/sysinfo.h>
40 #endif
41
42 using namespace MEDSPLITTER;
43 using namespace MED_EN;
44 using namespace std;
45
46 //================================================================================
47 /*!
48  * \brief Constructor. Find out my rank and world size
49  */
50 //================================================================================
51
52 ParaDomainSelector::ParaDomainSelector(bool mesure_memory)
53   :_rank(0),_world_size(1), _nb_result_domains(-1), _mesure_memory(mesure_memory),
54    _init_time(0.0), _init_memory(0), _max_memory(0)
55 {
56 #ifdef HAVE_MPI2
57   MPI_Comm_size(MPI_COMM_WORLD,&_world_size) ;
58   MPI_Comm_rank(MPI_COMM_WORLD,&_rank) ;
59   _init_time = MPI_Wtime();
60 #endif
61   evaluateMemory();
62 }
63
64 //================================================================================
65 /*!
66  * \brief Destructor
67  */
68 //================================================================================
69
70 ParaDomainSelector::~ParaDomainSelector()
71 {
72 }
73
74 //================================================================================
75 /*!
76  * \brief Return true if is running on different hosts
77  */
78 //================================================================================
79
80 bool ParaDomainSelector::isOnDifferentHosts() const
81 {
82   evaluateMemory();
83   if ( _world_size < 2 )
84     return false;
85
86 #ifdef HAVE_MPI2
87   char name_here[ MPI_MAX_PROCESSOR_NAME+1 ], name_there[ MPI_MAX_PROCESSOR_NAME+1 ];
88   int size;
89   MPI_Get_processor_name( name_here, &size);
90
91   int next_proc = (rank() + 1) % nbProcs();
92   int prev_proc = (rank() - 1 + nbProcs()) % nbProcs();
93   int tag  = 1111111;
94
95   MPI_Status status;
96   MPI_Sendrecv((void*)&name_here[0],  MPI_MAX_PROCESSOR_NAME, MPI_CHAR, next_proc, tag,
97                (void*)&name_there[0], MPI_MAX_PROCESSOR_NAME, MPI_CHAR, prev_proc, tag,
98                MPI_COMM_WORLD, &status);
99   return string(name_here) != string(name_there);
100 #else
101   return false;
102 #endif
103 }
104
105 //================================================================================
106 /*!
107  * \brief Return true if the domain with domainIndex is to be loaded on this proc
108  *  \param domainIndex - index of mesh domain
109  *  \retval bool - to load or not
110  */
111 //================================================================================
112
113 bool ParaDomainSelector::isMyDomain(int domainIndex) const
114 {
115   evaluateMemory();
116   return (_rank == getProccessorID( domainIndex ));
117 }
118
119 //================================================================================
120 /*!
121  * \brief Return processor id where the domain with domainIndex resides
122  *  \param domainIndex - index of mesh domain
123  *  \retval int - processor id
124  */
125 //================================================================================
126
127 int ParaDomainSelector::getProccessorID(int domainIndex) const
128 {
129   evaluateMemory();
130   return ( domainIndex % _world_size );
131 }
132
133 //================================================================================
134 /*!
135  * \brief Gather info on nb of entities on each processor and return total nb.
136  *
137  * Is called
138  * 1) for MED_CELL to know global id shift for domains at graph construction;
139  * 2) for sub-entity to know total nb of sub-entities before creating those of joints
140  */
141 //================================================================================
142
143 int ParaDomainSelector::gatherNbOf(MED_EN::medEntityMesh        entity,
144                                    const vector<MEDMEM::MESH*>& domain_meshes)
145 {
146   evaluateMemory();
147
148   // get nb of elems of each domain mesh
149   int nb_domains = domain_meshes.size();
150   vector<int> nb_elems( nb_domains, 0 );
151   for ( int i = 0; i < nb_domains; ++i )
152     if ( domain_meshes[i] )
153       nb_elems[i] = domain_meshes[i]->getNumberOfElements(entity, MED_ALL_ELEMENTS);
154
155   // receive nb of elems from other procs
156   vector<int> all_nb_elems( nb_domains );
157 #ifdef HAVE_MPI2
158   MPI_Allreduce((void*)&nb_elems[0], (void*)&all_nb_elems[0], nb_domains,
159                 MPI_INT, MPI_SUM, MPI_COMM_WORLD);
160 #endif
161   int total_nb = std::accumulate( all_nb_elems.begin(), all_nb_elems.end(), 0 );
162
163   vector<int>& elem_shift_by_domain
164     = (entity == MED_CELL) ? _cell_shift_by_domain : _face_shift_by_domain;
165
166   // fill elem_shift_by_domain
167
168   vector< int > ordered_nbs, domain_order( nb_domains );
169   ordered_nbs.push_back(0);
170   for ( int iproc = 0; iproc < nbProcs(); ++iproc )
171     for ( int idomain = 0; idomain < nb_domains; ++idomain )
172       if ( getProccessorID( idomain ) == iproc )
173       {
174         domain_order[ idomain ] = ordered_nbs.size() - 1;
175         ordered_nbs.push_back( ordered_nbs.back() + all_nb_elems[idomain] );
176       }
177   elem_shift_by_domain.resize( nb_domains+1, 0 );
178   for ( int idomain = 0; idomain < nb_domains; ++idomain )
179     elem_shift_by_domain[ idomain ] = ordered_nbs[ domain_order[ idomain ]];
180
181   elem_shift_by_domain.back() = ordered_nbs.back(); // to know total nb of elements
182
183   if ( entity == MED_CELL )
184   {
185     // fill _nb_vert_of_procs
186     _nb_vert_of_procs.resize( _world_size+1, 0 );
187     for ( int i = 0; i < nb_domains; ++i )
188     {
189       int rank = getProccessorID( i );
190       _nb_vert_of_procs[ rank+1 ] += all_nb_elems[ i ];
191     }
192     _nb_vert_of_procs[0] = 1; // base = 1
193     for ( int i = 1; i < _nb_vert_of_procs.size(); ++i )
194       _nb_vert_of_procs[ i ] += _nb_vert_of_procs[ i-1 ]; // to CSR format
195   }
196   else
197   {
198     // to compute global ids of faces in joints
199     //_total_nb_faces = total_nb;
200   }
201
202 //   if ( !_rank) {
203 //     MEDMEM::STRING out("_nb_vert_of_procs :");
204 //     for ( int i = 0; i < _nb_vert_of_procs.size(); ++i )
205 //       out << _nb_vert_of_procs[i] << " ";
206 //     std::cout << out << std::endl;
207 //   }
208 //   if ( !_rank) {
209 //     MEDMEM::STRING out("elem_shift_by_domain :");
210 //     for ( int i = 0; i < elem_shift_by_domain.size(); ++i )
211 //       out << elem_shift_by_domain[i] << " ";
212 //     std::cout << out << std::endl;
213 //   }
214
215   evaluateMemory();
216
217   return total_nb;
218 }
219
220 //================================================================================
221 /*!
222  * \brief Return distribution of the graph vertices among the processors
223  *  \retval int* - array conatining nb of vertices on all processors
224  *
225  * gatherNbOf( MED_CELL ) must be called before.
226  * The result array is to be used as the first arg of ParMETIS_V3_PartKway() and
227  * is freed by ParaDomainSelector.
228  */
229 //================================================================================
230
231 #define gatherNbOf_NOT_CALLED(meth) throw MED_EXCEPTION \
232 ("ParaDomainSelector::" #meth "(): gatherNbOf( MED_CELL ) must be called before")
233
234 int* ParaDomainSelector::getNbVertOfProcs() const
235 {
236   evaluateMemory();
237   if ( _nb_vert_of_procs.empty() )
238     gatherNbOf_NOT_CALLED(getNbVertOfProcs);
239
240   return (int*) & _nb_vert_of_procs[0];
241 }
242 //================================================================================
243 /*!
244  * \brief Return nb of cells in domains with lower index.
245  *
246  * gatherNbOf( MED_CELL ) must be called before.
247  * Result added to local id on given domain gives id in the whole distributed mesh
248  */
249 //================================================================================
250
251 int ParaDomainSelector::getDomainShift(int domainIndex) const
252 {
253   evaluateMemory();
254   if ( _cell_shift_by_domain.empty() )
255     gatherNbOf_NOT_CALLED(getDomainShift);
256
257   return _cell_shift_by_domain[ domainIndex ];
258 }
259
260 //================================================================================
261 /*!
262  * \brief Return nb of cells on processors with lower rank.
263  *
264  * gatherNbOf( MED_CELL ) must be called before.
265  * Result added to global id on this processor gives id in the whole distributed mesh
266  */
267 //================================================================================
268
269 int ParaDomainSelector::getProcShift() const
270 {
271   evaluateMemory();
272   if ( _nb_vert_of_procs.empty() )
273     gatherNbOf_NOT_CALLED(getProcShift);
274
275   return _nb_vert_of_procs[_rank]-1;
276 }
277
278 //================================================================================
279 /*!
280  * \brief Gather graphs from all processors into one
281  */
282 //================================================================================
283
284 auto_ptr<Graph> ParaDomainSelector::gatherGraph(const Graph* graph) const
285 {
286   Graph* glob_graph = 0;
287
288   evaluateMemory();
289 #ifdef HAVE_MPI2
290
291   // ---------------
292   // Gather indices
293   // ---------------
294
295   vector<int> index_size_of_proc( nbProcs() ); // index sizes - 1
296   for ( int i = 1; i < _nb_vert_of_procs.size(); ++i )
297     index_size_of_proc[i-1] = _nb_vert_of_procs[ i ] - _nb_vert_of_procs[ i-1 ];
298
299   int index_size = 1 + _cell_shift_by_domain.back();
300   int* graph_index = new int[ index_size ];
301   const int* index = graph->getGraph()->getIndex();
302   int* proc_index_displacement = (int*) & _nb_vert_of_procs[0];
303
304   MPI_Allgatherv((void*) (index+1),         // send local index except first 0 (or 1)
305                  index_size_of_proc[_rank], // index size on this proc
306                  MPI_INT,
307                  (void*) graph_index,       // receive indices
308                  & index_size_of_proc[0],   // index size on each proc
309                  proc_index_displacement,   // displacement of each proc index
310                  MPI_INT,
311                  MPI_COMM_WORLD);
312   graph_index[0] = index[0]; // it is not overwritten thanks to proc_index_displacement[0]==1
313
314   // get sizes of graph values on each proc by the got indices of graphs
315   vector< int > value_size_of_proc( nbProcs() ), proc_value_displacement(1,0);
316   for ( int i = 0; i < nbProcs(); ++i )
317   {
318     if ( index_size_of_proc[i] > 0 )
319       value_size_of_proc[i] = graph_index[ proc_index_displacement[ i+1 ]-1 ] - graph_index[0];
320     else
321       value_size_of_proc[i] = 0;
322     proc_value_displacement.push_back( proc_value_displacement.back() + value_size_of_proc[i] );
323   }
324   
325   // update graph_index
326   for ( int i = 1; i < nbProcs(); ++i )
327   {
328     int shift = graph_index[ proc_index_displacement[i]-1 ]-graph_index[0];
329     for ( int j = proc_index_displacement[i]; j < proc_index_displacement[i+1]; ++j )
330       graph_index[ j ] += shift;
331   }
332   
333   // --------------
334   // Gather values
335   // --------------
336
337   int value_size = graph_index[ index_size-1 ] - graph_index[ 0 ];
338   int* graph_value = new int[ value_size ];
339   const int* value = graph->getGraph()->getValue();
340
341   MPI_Allgatherv((void*) value,                // send local value
342                  value_size_of_proc[_rank],    // value size on this proc
343                  MPI_INT,
344                  (void*) graph_value,          // receive values
345                  & value_size_of_proc[0],      // value size on each proc
346                  & proc_value_displacement[0], // displacement of each proc value
347                  MPI_INT,
348                  MPI_COMM_WORLD);
349
350   // -----------------
351   // Gather partition
352   // -----------------
353
354   int * partition = new int[ _cell_shift_by_domain.back() ];
355   const int* part = graph->getPart();
356   
357   MPI_Allgatherv((void*) part,              // send local partition
358                  index_size_of_proc[_rank], // index size on this proc
359                  MPI_INT,
360                  (void*)(partition-1),      // -1 compensates proc_index_displacement[0]==1
361                  & index_size_of_proc[0],   // index size on each proc
362                  proc_index_displacement,   // displacement of each proc index
363                  MPI_INT,
364                  MPI_COMM_WORLD);
365
366   // -----------
367   // Make graph
368   // -----------
369
370   MEDMEM::MEDSKYLINEARRAY* array =
371     new MEDMEM::MEDSKYLINEARRAY( index_size-1, value_size, graph_index, graph_value, true );
372
373   glob_graph = new UserGraph( array, partition, index_size-1 );
374
375   evaluateMemory();
376
377   delete [] partition;
378
379 #endif // HAVE_MPI2
380
381   return auto_ptr<Graph>( glob_graph );
382 }
383
384 //================================================================================
385 /*!
386  * \brief Sets global numbering for the entity.
387  *
388  * This method must be once called for MED_CELL before exchangeJoint() calls
389  */
390 //================================================================================
391
392 void ParaDomainSelector::gatherEntityTypesInfo(vector<MEDMEM::MESH*>& domain_meshes,
393                                                MED_EN::medEntityMesh  entity)
394 {
395   const list<medGeometryElement> & all_types = meshEntities[ entity ];
396
397   evaluateMemory();
398
399   // Put nb of elements of the entity of all domains in one vector
400   // and by the way find out mesh dimension
401
402   vector<int> nb_of_type( domain_meshes.size() * all_types.size(), 0 );
403   int mesh_dim = -1, space_dim = -1;
404
405   for ( int idomain = 0; idomain < domain_meshes.size(); ++idomain )
406   {
407     if ( !isMyDomain(idomain)) continue;
408
409     int* domain_nbs = & nb_of_type[ idomain * all_types.size()];
410
411     list<medGeometryElement>::const_iterator type = all_types.begin();
412     for ( int t = 0; type != all_types.end(); ++t,++type )
413       domain_nbs[t] = domain_meshes[idomain]->getNumberOfElements(entity,*type);
414
415     int i_mesh_dim = domain_meshes[idomain]->getMeshDimension();
416     int i_space_dim = domain_meshes[idomain]->getSpaceDimension();
417     if ( mesh_dim < i_mesh_dim && i_mesh_dim <= 3 )
418       mesh_dim = i_mesh_dim;
419     if ( space_dim < i_space_dim && i_space_dim <= 3 )
420       space_dim = i_space_dim;
421   }
422
423   // Receive nbs from other procs
424
425   vector< int > nb_recv( nb_of_type.size() );
426 #ifdef HAVE_MPI2
427   MPI_Allreduce((void*)&nb_of_type[0], (void*)&nb_recv[0], nb_of_type.size(),
428                 MPI_INT, MPI_SUM, MPI_COMM_WORLD);
429 #endif
430
431   // Set info to meshes of distant domains
432
433   for ( int idomain = 0; idomain < domain_meshes.size(); ++idomain )
434   {
435     if ( isMyDomain(idomain)) continue;
436
437     MEDMEM::MESHING* meshing = (MEDMEM::MESHING*) domain_meshes[idomain];
438     if ( meshing->getMeshDimension() < mesh_dim )
439     {
440       //meshing->setSpaceDimension( space_dim );
441       meshing->setCoordinates( space_dim, /*NumberOfNodes=*/0, 0, "", 0);
442     }
443
444     vector< medGeometryElement > types;
445     vector< int >                nb_elems;
446
447     int* domain_nbs = & nb_recv[ idomain * all_types.size()];
448
449     list<medGeometryElement>::const_iterator type = all_types.begin();
450     for ( int t = 0; type != all_types.end(); ++t,++type )
451     {
452       if ( domain_nbs[t]==0 ) continue;
453       types.push_back( *type );
454       nb_elems.push_back( domain_nbs[t] );
455     }
456     meshing->setNumberOfTypes( types.size(), entity );
457     if ( !types.empty() )
458     {
459       meshing->setTypes           ( & types[0], entity );
460       meshing->setNumberOfElements( & nb_elems[0], entity );
461     }
462   }
463   evaluateMemory();
464 }
465
466 //================================================================================
467 /*!
468  * \brief Set nb of cell/cell pairs in a joint between domains
469  */
470 //================================================================================
471
472 void ParaDomainSelector::setNbCellPairs( int nb_cell_pairs, int dist_domain, int loc_domain )
473 {
474   // This method is needed for further computing global numbers of faces in joint.
475   // Store if both domains are on this proc else on one of procs only
476   if ( isMyDomain( dist_domain ) || dist_domain < loc_domain )
477   {
478     if ( _nb_cell_pairs_by_joint.empty() )
479       _nb_cell_pairs_by_joint.resize( _nb_result_domains*(_nb_result_domains+1), 0);
480
481     int joint_id = jointId( loc_domain, dist_domain );
482     _nb_cell_pairs_by_joint[ joint_id ] = nb_cell_pairs;
483   }
484   evaluateMemory();
485 }
486
487 //================================================================================
488 /*!
489  * \brief Return nb of cell/cell pairs in a joint between domains on different procs
490  */
491 //================================================================================
492
493 int ParaDomainSelector::getNbCellPairs( int dist_domain, int loc_domain ) const
494 {
495   evaluateMemory();
496
497   int joint_id = jointId( loc_domain, dist_domain );
498   return _nb_cell_pairs_by_joint[ joint_id ];
499 }
500
501 //================================================================================
502 /*!
503  * \brief Gather size of each joint
504  */
505 //================================================================================
506
507 void ParaDomainSelector::gatherNbCellPairs()
508 {
509   const char* LOC = "MEDSPLITTER::ParaDomainSelector::gatherNbCellPairs(): ";
510   if ( _nb_cell_pairs_by_joint.empty() )
511     _nb_cell_pairs_by_joint.resize( _nb_result_domains*(_nb_result_domains+1), 0);
512   evaluateMemory();
513
514   vector< int > send_buf = _nb_cell_pairs_by_joint;
515 #ifdef HAVE_MPI2
516   MPI_Allreduce((void*)&send_buf[0],
517                 (void*)&_nb_cell_pairs_by_joint[0],
518                 _nb_cell_pairs_by_joint.size(),
519                 MPI_INT, MPI_SUM, MPI_COMM_WORLD);
520 #endif
521   // check that the set nbs of cell pairs are correct,
522   // namely that each joint is treated on one proc only
523   for ( int j = 0; j < _nb_cell_pairs_by_joint.size(); ++j )
524     if ( _nb_cell_pairs_by_joint[j] != send_buf[j] && send_buf[j]>0 )
525       throw MED_EXCEPTION(MEDMEM::STRING(LOC) << "invalid nb of cell pairs");
526 }
527
528 //================================================================================
529 /*!
530  * \brief Send-receive joint data
531  */
532 //================================================================================
533
534 void ParaDomainSelector::exchangeJoint( JointExchangeData* joint ) const
535 {
536 #ifdef HAVE_MPI2
537   vector<int> send_data, recv_data( joint->serialize( send_data ));
538
539   int dest = getProccessorID( joint->distantDomain() );
540   int tag  = 1001 + jointId( joint->localDomain(), joint->distantDomain() );
541   
542   MPI_Status status;
543   MPI_Sendrecv((void*)&send_data[0], send_data.size(), MPI_INT, dest, tag,
544                (void*)&recv_data[0], recv_data.size(), MPI_INT, dest, tag,
545                MPI_COMM_WORLD, &status);  
546
547   joint->deserialize( recv_data );
548 #endif
549 }
550
551 //================================================================================
552 /*!
553  * \brief Return the first global id of sub-entity for the joint
554  */
555 //================================================================================
556
557 int ParaDomainSelector::getFisrtGlobalIdOfSubentity( int loc_domain, int dist_domain ) const
558 {
559   // total_nb_faces includes faces existing before creation of joint faces
560   // (got in gatherNbOf( MED_FACE )).
561   evaluateMemory();
562
563   int total_nb_faces = _face_shift_by_domain.empty() ? 0 : _face_shift_by_domain.back();
564   int id = total_nb_faces + 1;
565
566   if ( _nb_cell_pairs_by_joint.empty() )
567     throw MED_EXCEPTION("MEDSPLITTER::ParaDomainSelector::getFisrtGlobalIdOfSubentity(), "
568                         "gatherNbCellPairs() must be called before");
569   int joint_id = jointId( loc_domain, dist_domain );
570   for ( int j = 0; j < joint_id; ++j )
571     id += _nb_cell_pairs_by_joint[ j ];
572
573   return id;
574 }
575
576 //================================================================================
577 /*!
578  * \brief Send-receive local ids of joint faces
579  */
580 //================================================================================
581
582 int* ParaDomainSelector::exchangeSubentityIds( int loc_domain, int dist_domain,
583                                                const vector<int>& loc_ids_here ) const
584 {
585   int* loc_ids_dist = new int[ loc_ids_here.size()];
586 #ifdef HAVE_MPI2
587   int dest = getProccessorID( dist_domain );
588   int tag  = 2002 + jointId( loc_domain, dist_domain );
589   MPI_Status status;
590   MPI_Sendrecv((void*)&loc_ids_here[0], loc_ids_here.size(), MPI_INT, dest, tag,
591                (void*) loc_ids_dist,    loc_ids_here.size(), MPI_INT, dest, tag,
592                MPI_COMM_WORLD, &status);  
593   evaluateMemory();
594 #endif
595
596   return loc_ids_dist;
597 }
598
599 //================================================================================
600 /*!
601  * \brief Return identifier for a joint
602  */
603 //================================================================================
604
605 int ParaDomainSelector::jointId( int local_domain, int distant_domain ) const
606 {
607   evaluateMemory();
608   if (_nb_result_domains < 0)
609     throw MED_EXCEPTION("ParaDomainSelector::jointId(): setNbDomains() must be called before()");
610
611   if ( local_domain < distant_domain )
612     swap( local_domain, distant_domain );
613   return local_domain * _nb_result_domains + distant_domain;
614 }
615
616 //================================================================================
617 /*!
618  * \brief Return domain order so that first go domains on proc 0 and so n
619  */
620 //================================================================================
621
622 // int ParaDomainSelector::getDomianOrder(int idomain, int nb_domains) const
623 // {
624 //   return nb_domains / nbProcs() * getProccessorID( idomain ) + idomain / nbProcs();
625 // }
626
627 //================================================================================
628 /*!
629  * \brief Return time passed from construction in seconds
630  */
631 //================================================================================
632
633 double ParaDomainSelector::getPassedTime() const
634 {
635 #ifdef HAVE_MPI2
636   return MPI_Wtime() - _init_time;
637 #else
638   return 0.0;
639 #endif
640 }
641
642 //================================================================================
643 /*!
644  * \brief Evaluate current memory usage and return the maximal one in KB
645  */
646 //================================================================================
647
648 int ParaDomainSelector::evaluateMemory() const
649 {
650   if ( _mesure_memory )
651   {
652     int used_memory = 0;
653 #ifndef WIN32
654     struct sysinfo si;
655     int err = sysinfo( &si );
656     if ( !err )
657       used_memory =
658         (( si.totalram - si.freeram + si.totalswap - si.freeswap ) * si.mem_unit ) / 1024;
659 #endif
660     if ( used_memory > _max_memory )
661       ((ParaDomainSelector*) this)->_max_memory = used_memory;
662
663     if ( !_init_memory )
664       ((ParaDomainSelector*) this)->_init_memory = used_memory;
665   }
666   return _max_memory - _init_memory;
667 }