source: XIOS/dev/branch_openmp/src/node/context.cpp @ 1287

Last change on this file since 1287 was 1287, checked in by yushan, 7 years ago

EP updated

  • Property copyright set to
    Software name : XIOS (Xml I/O Server)
    http://forge.ipsl.jussieu.fr/ioserver
    Creation date : January 2009
    Licence : CeCCIL version2
    see license file in root directory : Licence_CeCILL_V2-en.txt
    or http://www.cecill.info/licences/Licence_CeCILL_V2-en.html
    Holder : CEA/LSCE (Laboratoire des Sciences du CLimat et de l'Environnement)
    CNRS/IPSL (Institut Pierre Simon Laplace)
    Project Manager : Yann Meurdesoif
    yann.meurdesoif@cea.fr
File size: 44.5 KB
Line 
1#include "context.hpp"
2#include "attribute_template.hpp"
3#include "object_template.hpp"
4#include "group_template.hpp"
5
6#include "calendar_type.hpp"
7#include "duration.hpp"
8
9#include "context_client.hpp"
10#include "context_server.hpp"
11#include "nc4_data_output.hpp"
12#include "node_type.hpp"
13#include "message.hpp"
14#include "type.hpp"
15#include "xios_spl.hpp"
16#include "timer.hpp"
17#include "memtrack.hpp"
18
19
20namespace xios {
21
22  //shared_ptr<CContextGroup> CContext::root;
23  boost::shared_ptr<CContextGroup> * CContext::root_ptr = 0;
24
25   /// ////////////////////// Dfinitions ////////////////////// ///
26
27   CContext::CContext(void)
28      : CObjectTemplate<CContext>(), CContextAttributes()
29      , calendar(), hasClient(false), hasServer(false), isPostProcessed(false), finalized(false)
30      , idServer_(), client(0), server(0)
31   { /* Ne rien faire de plus */ }
32
33   CContext::CContext(const StdString & id)
34      : CObjectTemplate<CContext>(id), CContextAttributes()
35      , calendar(), hasClient(false), hasServer(false), isPostProcessed(false), finalized(false)
36      , idServer_(), client(0), server(0)
37   { /* Ne rien faire de plus */ }
38
39   CContext::~CContext(void)
40   {
41     delete client;
42     delete server;
43   }
44
45   //----------------------------------------------------------------
46   //! Get name of context
47   StdString CContext::GetName(void)   { return (StdString("context")); }
48   StdString CContext::GetDefName(void){ return (CContext::GetName()); }
49   ENodeType CContext::GetType(void)   { return (eContext); }
50
51   //----------------------------------------------------------------
52
53   /*!
54   \brief Get context group (context root)
55   \return Context root
56   */
57   CContextGroup* CContext::getRoot(void)
58   {
59      //if (root.get()==NULL) root=shared_ptr<CContextGroup>(new CContextGroup(xml::CXMLNode::GetRootName()));
60      //return root.get();
61
62      //static shared_ptr<CContextGroup> *root_ptr;
63      if(root_ptr == 0) //root_ptr = new shared_ptr<CContextGroup>;
64      // if (root_ptr->get()==NULL)
65      root_ptr = new boost::shared_ptr<CContextGroup>(new CContextGroup(xml::CXMLNode::GetRootName()));
66      return root_ptr->get();
67   }
68
69   //----------------------------------------------------------------
70
71   /*!
72   \brief Get calendar of a context
73   \return Calendar
74   */
75   boost::shared_ptr<CCalendar> CContext::getCalendar(void) const
76   {
77      return (this->calendar);
78   }
79
80   //----------------------------------------------------------------
81
82   /*!
83   \brief Set a context with a calendar
84   \param[in] newCalendar new calendar
85   */
86   void CContext::setCalendar(boost::shared_ptr<CCalendar> newCalendar)
87   {
88      this->calendar = newCalendar;
89   }
90
91   //----------------------------------------------------------------
92   /*!
93   \brief Parse xml file and write information into context object
94   \param [in] node xmld node corresponding in xml file
95   */
96   void CContext::parse(xml::CXMLNode & node)
97   {
98      CContext::SuperClass::parse(node);
99
100      // PARSING POUR GESTION DES ENFANTS
101      xml::THashAttributes attributes = node.getAttributes();
102
103      if (attributes.end() != attributes.find("src"))
104      {
105         StdIFStream ifs ( attributes["src"].c_str() , StdIFStream::in );
106         if ( (ifs.rdstate() & std::ifstream::failbit ) != 0 )
107            ERROR("void CContext::parse(xml::CXMLNode & node)",
108                  <<endl<< "Can not open <"<<attributes["src"].c_str()<<"> file" );
109         if (!ifs.good())
110            ERROR("CContext::parse(xml::CXMLNode & node)",
111                  << "[ filename = " << attributes["src"] << " ] Bad xml stream !");
112         xml::CXMLParser::ParseInclude(ifs, attributes["src"], *this);
113      }
114
115      if (node.getElementName().compare(CContext::GetName()))
116         DEBUG("Le noeud is wrong defined but will be considered as a context !");
117
118      if (!(node.goToChildElement()))
119      {
120         DEBUG("Le context ne contient pas d'enfant !");
121      }
122      else
123      {
124         do { // Parcours des contextes pour traitement.
125
126            StdString name = node.getElementName();
127            attributes.clear();
128            attributes = node.getAttributes();
129
130            if (attributes.end() != attributes.find("id"))
131            {
132              DEBUG(<< "Definition node has an id,"
133                    << "it will not be taking account !");
134            }
135
136#define DECLARE_NODE(Name_, name_)    \
137   if (name.compare(C##Name_##Definition::GetDefName()) == 0) \
138   { C##Name_##Definition::create(C##Name_##Definition::GetDefName()) -> parse(node); continue; }
139#define DECLARE_NODE_PAR(Name_, name_)
140#include "node_type.conf"
141
142            DEBUG(<< "The element \'"     << name
143                  << "\' in the context \'" << CContext::getCurrent()->getId()
144                  << "\' is not a definition !");
145
146         } while (node.goToNextElement());
147
148         node.goToParentElement(); // Retour au parent
149      }
150   }
151
152   //----------------------------------------------------------------
153   //! Show tree structure of context
154   void CContext::ShowTree(StdOStream & out)
155   {
156      StdString currentContextId = CContext::getCurrent() -> getId();
157      std::vector<CContext*> def_vector =
158         CContext::getRoot()->getChildList();
159      std::vector<CContext*>::iterator
160         it = def_vector.begin(), end = def_vector.end();
161
162      out << "<? xml version=\"1.0\" ?>" << std::endl;
163      out << "<"  << xml::CXMLNode::GetRootName() << " >" << std::endl;
164
165      for (; it != end; it++)
166      {
167         CContext* context = *it;
168         CContext::setCurrent(context->getId());
169         out << *context << std::endl;
170      }
171
172      out << "</" << xml::CXMLNode::GetRootName() << " >" << std::endl;
173      CContext::setCurrent(currentContextId);
174   }
175
176
177   //----------------------------------------------------------------
178
179   //! Convert context object into string (to print)
180   StdString CContext::toString(void) const
181   {
182      StdOStringStream oss;
183      oss << "<" << CContext::GetName()
184          << " id=\"" << this->getId() << "\" "
185          << SuperClassAttribute::toString() << ">" << std::endl;
186      if (!this->hasChild())
187      {
188         //oss << "<!-- No definition -->" << std::endl; // fait planter l'incrmentation
189      }
190      else
191      {
192
193#define DECLARE_NODE(Name_, name_)    \
194   if (C##Name_##Definition::has(C##Name_##Definition::GetDefName())) \
195   oss << * C##Name_##Definition::get(C##Name_##Definition::GetDefName()) << std::endl;
196#define DECLARE_NODE_PAR(Name_, name_)
197#include "node_type.conf"
198
199      }
200
201      oss << "</" << CContext::GetName() << " >";
202
203      return (oss.str());
204   }
205
206   //----------------------------------------------------------------
207
208   /*!
209   \brief Find all inheritace among objects in a context.
210   \param [in] apply (true) write attributes of parent into ones of child if they are empty
211                     (false) write attributes of parent into a new container of child
212   \param [in] parent unused
213   */
214   void CContext::solveDescInheritance(bool apply, const CAttributeMap * const UNUSED(parent))
215   {
216#define DECLARE_NODE(Name_, name_)    \
217   if (C##Name_##Definition::has(C##Name_##Definition::GetDefName())) \
218     C##Name_##Definition::get(C##Name_##Definition::GetDefName())->solveDescInheritance(apply);
219#define DECLARE_NODE_PAR(Name_, name_)
220#include "node_type.conf"
221   }
222
223   //----------------------------------------------------------------
224
225   //! Verify if all root definition in the context have child.
226   bool CContext::hasChild(void) const
227   {
228      return (
229#define DECLARE_NODE(Name_, name_)    \
230   C##Name_##Definition::has(C##Name_##Definition::GetDefName())   ||
231#define DECLARE_NODE_PAR(Name_, name_)
232#include "node_type.conf"
233      false);
234}
235
236   //----------------------------------------------------------------
237
238   void CContext::CleanTree(void)
239   {
240#define DECLARE_NODE(Name_, name_) C##Name_##Definition::ClearAllAttributes();
241#define DECLARE_NODE_PAR(Name_, name_)
242#include "node_type.conf"
243   }
244   ///---------------------------------------------------------------
245
246   //! Initialize client side
247   void CContext::initClient(ep_lib::MPI_Comm intraComm, ep_lib::MPI_Comm interComm, CContext* cxtServer /*= 0*/)
248   {
249     hasClient=true;
250     client = new CContextClient(this, intraComm, interComm, cxtServer);
251
252     int tmp_rank;
253     MPI_Comm_rank(intraComm, &tmp_rank);
254     MPI_Barrier(intraComm);
255     
256
257     registryIn=new CRegistry(intraComm);
258     registryIn->setPath(getId()) ;
259     if (client->clientRank==0) registryIn->fromFile("xios_registry.bin") ;
260     registryIn->bcastRegistry() ;
261
262     registryOut=new CRegistry(intraComm) ;
263     registryOut->setPath(getId()) ;
264
265     ep_lib::MPI_Comm intraCommServer, interCommServer;
266     if (cxtServer) // Attached mode
267     {
268       intraCommServer = intraComm;
269       interCommServer = interComm;
270     }
271     else
272     {
273       MPI_Comm_dup(intraComm, &intraCommServer);
274       comms.push_back(intraCommServer);
275       MPI_Comm_dup(interComm, &interCommServer);
276       comms.push_back(interCommServer);
277     }
278     server = new CContextServer(this,intraCommServer,interCommServer);
279   }
280
281   void CContext::setClientServerBuffer()
282   {
283     // Estimated minimum event size for small events (10 is an arbitrary constant just for safety)
284     const size_t minEventSize = CEventClient::headerSize + getIdServer().size() + 10 * sizeof(int);
285     // Ensure there is at least some room for 20 of such events in the buffers
286     size_t minBufferSize = std::max(CXios::minBufferSize, 20 * minEventSize);
287#define DECLARE_NODE(Name_, name_)    \
288     if (minBufferSize < sizeof(C##Name_##Definition)) minBufferSize = sizeof(C##Name_##Definition);
289#define DECLARE_NODE_PAR(Name_, name_)
290#include "node_type.conf"
291#undef DECLARE_NODE
292#undef DECLARE_NODE_PAR
293
294     // Compute the buffer sizes needed to send the attributes and data corresponding to fields
295     std::map<int, StdSize> maxEventSize;
296     std::map<int, StdSize> bufferSize = getAttributesBufferSize(maxEventSize);
297     std::map<int, StdSize> dataBufferSize = getDataBufferSize(maxEventSize);
298
299     std::map<int, StdSize>::iterator it, ite = dataBufferSize.end();
300     for (it = dataBufferSize.begin(); it != ite; ++it)
301       if (it->second > bufferSize[it->first]) bufferSize[it->first] = it->second;
302
303     // Apply the buffer size factor and check that we are above the minimum buffer size
304     ite = bufferSize.end();
305     for (it = bufferSize.begin(); it != ite; ++it)
306     {
307       it->second *= CXios::bufferSizeFactor;
308       if (it->second < minBufferSize) it->second = minBufferSize;
309     }
310
311     // Leaders will have to send some control events so ensure there is some room for those in the buffers
312     if (client->isServerLeader())
313     {
314       const std::list<int>& ranks = client->getRanksServerLeader();
315       for (std::list<int>::const_iterator itRank = ranks.begin(), itRankEnd = ranks.end(); itRank != itRankEnd; ++itRank)
316       {
317         if (!bufferSize.count(*itRank))
318         {
319           bufferSize[*itRank] = minBufferSize;
320           maxEventSize[*itRank] = minEventSize;
321         }
322       }
323     }
324
325     client->setBufferSize(bufferSize, maxEventSize);
326   }
327
328   //! Verify whether a context is initialized
329   bool CContext::isInitialized(void)
330   {
331     return hasClient;
332   }
333
334   //! Initialize server
335   void CContext::initServer(ep_lib::MPI_Comm intraComm, ep_lib::MPI_Comm interComm, CContext* cxtClient /*= 0*/)
336   {
337     hasServer=true;
338     server = new CContextServer(this,intraComm,interComm);
339
340     registryIn=new CRegistry(intraComm);
341     registryIn->setPath(getId()) ;
342     if (server->intraCommRank==0) registryIn->fromFile("xios_registry.bin") ;
343     registryIn->bcastRegistry() ;
344     registryOut=new CRegistry(intraComm) ;
345     registryOut->setPath(getId()) ;
346
347     ep_lib::MPI_Comm intraCommClient, interCommClient;
348     if (cxtClient) // Attached mode
349     {
350       intraCommClient = intraComm;
351       interCommClient = interComm;
352     }
353     else
354     {
355       MPI_Comm_dup(intraComm, &intraCommClient);
356       comms.push_back(intraCommClient);
357       MPI_Comm_dup(interComm, &interCommClient);
358       comms.push_back(interCommClient);
359     }
360     client = new CContextClient(this,intraCommClient,interCommClient, cxtClient);
361   }
362
363   //! Try to send the buffers and receive possible answers
364   bool CContext::checkBuffersAndListen(void)
365   {
366     client->checkBuffers();
367
368     bool hasTmpBufferedEvent = client->hasTemporarilyBufferedEvent();
369     if (hasTmpBufferedEvent)
370       hasTmpBufferedEvent = !client->sendTemporarilyBufferedEvent();
371
372     // Don't process events if there is a temporarily buffered event
373     return server->eventLoop(!hasTmpBufferedEvent);
374   }
375
376   //! Terminate a context
377   void CContext::finalize(void)
378   {
379      if (!finalized)
380      {
381        finalized = true;
382        if (hasClient) sendRegistry() ;
383        client->finalize();
384        while (!server->hasFinished())
385        {
386          server->eventLoop();
387        }
388
389        if (hasServer)
390        {
391          closeAllFile();
392          registryOut->hierarchicalGatherRegistry() ;
393          if (server->intraCommRank==0) CXios::globalRegistry->mergeRegistry(*registryOut) ;
394        }
395
396        for (std::list<ep_lib::MPI_Comm>::iterator it = comms.begin(); it != comms.end(); ++it)
397          MPI_Comm_free(&(*it));
398        comms.clear();
399      }
400   }
401
402   /*!
403   \brief Close all the context defintion and do processing data
404      After everything is well defined on client side, they will be processed and sent to server
405   From the version 2.0, sever and client work no more on the same database. Moreover, client(s) will send
406   all necessary information to server, from which each server can build its own database.
407   Because the role of server is to write out field data on a specific netcdf file,
408   the only information that it needs is the enabled files
409   and the active fields (fields will be written onto active files)
410   */
411   void CContext::closeDefinition(void)
412   {
413     CTimer::get("Context : close definition").resume() ;
414     // There is nothing client need to send to server
415     if (hasClient)
416     {
417       // After xml is parsed, there are some more works with post processing
418       postProcessing();
419     }
420     setClientServerBuffer();
421
422     if (hasClient && !hasServer)
423     {
424      // Send all attributes of current context to server
425      this->sendAllAttributesToServer();
426
427      // Send all attributes of current calendar
428      CCalendarWrapper::get(CCalendarWrapper::GetDefName())->sendAllAttributesToServer();
429
430      // We have enough information to send to server
431      // First of all, send all enabled files
432       sendEnabledFiles();
433
434      // Then, send all enabled fields
435       sendEnabledFields();
436
437      // At last, we have all info of domain and axis, then send them
438       sendRefDomainsAxis();
439
440      // After that, send all grid (if any)
441       sendRefGrid();
442    }
443
444    // We have a xml tree on the server side and now, it should be also processed
445    if (hasClient && !hasServer) sendPostProcessing();
446
447    // There are some processings that should be done after all of above. For example: check mask or index
448    if (hasClient)
449    {
450      this->buildFilterGraphOfEnabledFields();
451      buildFilterGraphOfFieldsWithReadAccess();
452      this->solveAllRefOfEnabledFields(true);
453    }
454
455    // Now tell server that it can process all messages from client
456    if (hasClient && !hasServer) this->sendCloseDefinition();
457
458    // Nettoyage de l'arborescence
459    if (hasClient && !hasServer) CleanTree(); // Only on client side??
460
461    if (hasClient)
462    {
463      sendCreateFileHeader();
464
465      startPrefetchingOfEnabledReadModeFiles();
466    }
467    CTimer::get("Context : close definition").suspend() ;
468   }
469
470   void CContext::findAllEnabledFields(void)
471   {
472     for (unsigned int i = 0; i < this->enabledFiles.size(); i++)
473     (void)this->enabledFiles[i]->getEnabledFields();
474   }
475
476   void CContext::findAllEnabledFieldsInReadModeFiles(void)
477   {
478     for (unsigned int i = 0; i < this->enabledReadModeFiles.size(); ++i)
479     (void)this->enabledReadModeFiles[i]->getEnabledFields();
480   }
481
482   void CContext::readAttributesOfEnabledFieldsInReadModeFiles()
483   {
484      for (unsigned int i = 0; i < this->enabledReadModeFiles.size(); ++i)
485        (void)this->enabledReadModeFiles[i]->readAttributesOfEnabledFieldsInReadMode();
486   }
487
488   void CContext::solveOnlyRefOfEnabledFields(bool sendToServer)
489   {
490     int size = this->enabledFiles.size();
491     for (int i = 0; i < size; ++i)
492     {
493       this->enabledFiles[i]->solveOnlyRefOfEnabledFields(sendToServer);
494     }
495
496     for (int i = 0; i < size; ++i)
497     {
498       this->enabledFiles[i]->generateNewTransformationGridDest();
499     }
500   }
501
502   void CContext::solveAllRefOfEnabledFields(bool sendToServer)
503   {
504     int size = this->enabledFiles.size();
505     for (int i = 0; i < size; ++i)
506     {
507       this->enabledFiles[i]->solveAllRefOfEnabledFields(sendToServer);
508     }
509   }
510
511   void CContext::buildFilterGraphOfEnabledFields()
512   {
513     int size = this->enabledFiles.size();
514     for (int i = 0; i < size; ++i)
515     {
516       this->enabledFiles[i]->buildFilterGraphOfEnabledFields(garbageCollector);
517     }
518   }
519
520   void CContext::startPrefetchingOfEnabledReadModeFiles()
521   {
522     int size = enabledReadModeFiles.size();
523     for (int i = 0; i < size; ++i)
524     {
525        enabledReadModeFiles[i]->prefetchEnabledReadModeFields();
526     }
527   }
528
529   void CContext::checkPrefetchingOfEnabledReadModeFiles()
530   {
531     int size = enabledReadModeFiles.size();
532     for (int i = 0; i < size; ++i)
533     {
534        enabledReadModeFiles[i]->prefetchEnabledReadModeFieldsIfNeeded();
535     }
536   }
537
538  void CContext::findFieldsWithReadAccess(void)
539  {
540    fieldsWithReadAccess.clear();
541    const vector<CField*> allFields = CField::getAll();
542    for (size_t i = 0; i < allFields.size(); ++i)
543    {
544      CField* field = allFields[i];
545
546      if (field->file && !field->file->mode.isEmpty() && field->file->mode == CFile::mode_attr::read)
547        field->read_access = true;
548      else if (!field->read_access.isEmpty() && field->read_access && (field->enabled.isEmpty() || field->enabled))
549        fieldsWithReadAccess.push_back(field);
550    }
551  }
552
553  void CContext::solveAllRefOfFieldsWithReadAccess()
554  {
555    for (size_t i = 0; i < fieldsWithReadAccess.size(); ++i)
556      fieldsWithReadAccess[i]->solveAllReferenceEnabledField(false);
557  }
558
559  void CContext::buildFilterGraphOfFieldsWithReadAccess()
560  {
561    for (size_t i = 0; i < fieldsWithReadAccess.size(); ++i)
562      fieldsWithReadAccess[i]->buildFilterGraph(garbageCollector, true);
563  }
564
565   void CContext::solveAllInheritance(bool apply)
566   {
567     // Rsolution des hritages descendants (cd des hritages de groupes)
568     // pour chacun des contextes.
569      solveDescInheritance(apply);
570
571     // Rsolution des hritages par rfrence au niveau des fichiers.
572      const vector<CFile*> allFiles=CFile::getAll();
573      const vector<CGrid*> allGrids= CGrid::getAll();
574
575     //if (hasClient && !hasServer)
576      if (hasClient)
577      {
578        for (unsigned int i = 0; i < allFiles.size(); i++)
579          allFiles[i]->solveFieldRefInheritance(apply);
580      }
581
582      unsigned int vecSize = allGrids.size();
583      unsigned int i = 0;
584      for (i = 0; i < vecSize; ++i)
585        allGrids[i]->solveDomainAxisRefInheritance(apply);
586
587   }
588
589   void CContext::findEnabledFiles(void)
590   {
591      const std::vector<CFile*> allFiles = CFile::getAll();
592      const CDate& initDate = calendar->getInitDate();
593
594      for (unsigned int i = 0; i < allFiles.size(); i++)
595         if (!allFiles[i]->enabled.isEmpty()) // Si l'attribut 'enabled' est dfini.
596         {
597            if (allFiles[i]->enabled.getValue()) // Si l'attribut 'enabled' est fix  vrai.
598            {
599              if ((initDate + allFiles[i]->output_freq.getValue()) < (initDate + this->getCalendar()->getTimeStep()))
600              {
601                error(0)<<"WARNING: void CContext::findEnabledFiles()"<<endl
602                    << "Output frequency in file \""<<allFiles[i]->getFileOutputName()
603                    <<"\" is less than the time step. File will not be written."<<endl;
604              }
605              else
606               enabledFiles.push_back(allFiles[i]);
607            }
608         }
609         else
610         {
611           if ( (initDate + allFiles[i]->output_freq.getValue()) < (initDate + this->getCalendar()->getTimeStep()))
612           {
613             error(0)<<"WARNING: void CContext::findEnabledFiles()"<<endl
614                 << "Output frequency in file \""<<allFiles[i]->getFileOutputName()
615                 <<"\" is less than the time step. File will not be written."<<endl;
616           }
617           else
618             enabledFiles.push_back(allFiles[i]); // otherwise true by default
619         }
620
621      if (enabledFiles.size() == 0)
622         DEBUG(<<"Aucun fichier ne va tre sorti dans le contexte nomm \""
623               << getId() << "\" !");
624   }
625
626   void CContext::findEnabledReadModeFiles(void)
627   {
628     int size = this->enabledFiles.size();
629     for (int i = 0; i < size; ++i)
630     {
631       if (!enabledFiles[i]->mode.isEmpty() && enabledFiles[i]->mode.getValue() == CFile::mode_attr::read)
632        enabledReadModeFiles.push_back(enabledFiles[i]);
633     }
634   }
635
636   void CContext::closeAllFile(void)
637   {
638     std::vector<CFile*>::const_iterator
639            it = this->enabledFiles.begin(), end = this->enabledFiles.end();
640
641     for (; it != end; it++)
642     {
643       info(30)<<"Closing File : "<<(*it)->getId()<<endl;
644       (*it)->close();
645     }
646   }
647
648   /*!
649   \brief Dispatch event received from client
650      Whenever a message is received in buffer of server, it will be processed depending on
651   its event type. A new event type should be added in the switch list to make sure
652   it processed on server side.
653   \param [in] event: Received message
654   */
655   bool CContext::dispatchEvent(CEventServer& event)
656   {
657
658      if (SuperClass::dispatchEvent(event)) return true;
659      else
660      {
661        switch(event.type)
662        {
663           case EVENT_ID_CLOSE_DEFINITION :
664             recvCloseDefinition(event);
665             return true;
666             break;
667           case EVENT_ID_UPDATE_CALENDAR:
668             recvUpdateCalendar(event);
669             return true;
670             break;
671           case EVENT_ID_CREATE_FILE_HEADER :
672             recvCreateFileHeader(event);
673             return true;
674             break;
675           case EVENT_ID_POST_PROCESS:
676             recvPostProcessing(event);
677             return true;
678            case EVENT_ID_SEND_REGISTRY:
679             recvRegistry(event);
680             return true;
681            break;
682
683           default :
684             ERROR("bool CContext::dispatchEvent(CEventServer& event)",
685                    <<"Unknown Event");
686           return false;
687         }
688      }
689   }
690
691   //! Client side: Send a message to server to make it close
692   void CContext::sendCloseDefinition(void)
693   {
694     CEventClient event(getType(),EVENT_ID_CLOSE_DEFINITION);
695     if (client->isServerLeader())
696     {
697       CMessage msg;
698       msg<<this->getIdServer();
699       const std::list<int>& ranks = client->getRanksServerLeader();
700       for (std::list<int>::const_iterator itRank = ranks.begin(), itRankEnd = ranks.end(); itRank != itRankEnd; ++itRank)
701         event.push(*itRank,1,msg);
702       client->sendEvent(event);
703     }
704     else client->sendEvent(event);
705   }
706
707   //! Server side: Receive a message of client announcing a context close
708   void CContext::recvCloseDefinition(CEventServer& event)
709   {
710
711      CBufferIn* buffer=event.subEvents.begin()->buffer;
712      string id;
713      *buffer>>id;
714      get(id)->closeDefinition();
715   }
716
717   //! Client side: Send a message to update calendar in each time step
718   void CContext::sendUpdateCalendar(int step)
719   {
720     if (!hasServer)
721     {
722       CEventClient event(getType(),EVENT_ID_UPDATE_CALENDAR);
723       if (client->isServerLeader())
724       {
725         CMessage msg;
726         msg<<this->getIdServer()<<step;
727         const std::list<int>& ranks = client->getRanksServerLeader();
728         for (std::list<int>::const_iterator itRank = ranks.begin(), itRankEnd = ranks.end(); itRank != itRankEnd; ++itRank)
729           event.push(*itRank,1,msg);
730         client->sendEvent(event);
731       }
732       else client->sendEvent(event);
733     }
734   }
735
736   //! Server side: Receive a message of client annoucing calendar update
737   void CContext::recvUpdateCalendar(CEventServer& event)
738   {
739      CBufferIn* buffer=event.subEvents.begin()->buffer;
740      string id;
741      *buffer>>id;
742      get(id)->recvUpdateCalendar(*buffer);
743   }
744
745   //! Server side: Receive a message of client annoucing calendar update
746   void CContext::recvUpdateCalendar(CBufferIn& buffer)
747   {
748      int step;
749      buffer>>step;
750      updateCalendar(step);
751   }
752
753   //! Client side: Send a message to create header part of netcdf file
754   void CContext::sendCreateFileHeader(void)
755   {
756     CEventClient event(getType(),EVENT_ID_CREATE_FILE_HEADER);
757     if (client->isServerLeader())
758     {
759       CMessage msg;
760       msg<<this->getIdServer();
761       const std::list<int>& ranks = client->getRanksServerLeader();
762       for (std::list<int>::const_iterator itRank = ranks.begin(), itRankEnd = ranks.end(); itRank != itRankEnd; ++itRank)
763         event.push(*itRank,1,msg) ;
764       client->sendEvent(event);
765     }
766     else client->sendEvent(event);
767   }
768
769   //! Server side: Receive a message of client annoucing the creation of header part of netcdf file
770   void CContext::recvCreateFileHeader(CEventServer& event)
771   {
772      CBufferIn* buffer=event.subEvents.begin()->buffer;
773      string id;
774      *buffer>>id;
775      get(id)->recvCreateFileHeader(*buffer);
776   }
777
778   //! Server side: Receive a message of client annoucing the creation of header part of netcdf file
779   void CContext::recvCreateFileHeader(CBufferIn& buffer)
780   {
781      createFileHeader();
782   }
783
784   //! Client side: Send a message to do some post processing on server
785   void CContext::sendPostProcessing()
786   {
787     if (!hasServer)
788     {
789       CEventClient event(getType(),EVENT_ID_POST_PROCESS);
790       if (client->isServerLeader())
791       {
792         CMessage msg;
793         msg<<this->getIdServer();
794         const std::list<int>& ranks = client->getRanksServerLeader();
795         for (std::list<int>::const_iterator itRank = ranks.begin(), itRankEnd = ranks.end(); itRank != itRankEnd; ++itRank)
796           event.push(*itRank,1,msg);
797         client->sendEvent(event);
798       }
799       else client->sendEvent(event);
800     }
801   }
802
803   //! Server side: Receive a message to do some post processing
804   void CContext::recvPostProcessing(CEventServer& event)
805   {
806      CBufferIn* buffer=event.subEvents.begin()->buffer;
807      string id;
808      *buffer>>id;
809      get(id)->recvPostProcessing(*buffer);
810   }
811
812   //! Server side: Receive a message to do some post processing
813   void CContext::recvPostProcessing(CBufferIn& buffer)
814   {
815      CCalendarWrapper::get(CCalendarWrapper::GetDefName())->createCalendar();
816      postProcessing();
817   }
818
819   const StdString& CContext::getIdServer()
820   {
821      if (hasClient)
822      {
823        idServer_ = this->getId();
824        idServer_ += "_server";
825        return idServer_;
826      }
827      if (hasServer) return (this->getId());
828   }
829
830   /*!
831   \brief Do some simple post processings after parsing xml file
832      After the xml file (iodef.xml) is parsed, it is necessary to build all relations among
833   created object, e.g: inhertance among fields, domain, axis. After that, all fiels as well as their parents (reference fields),
834   which will be written out into netcdf files, are processed
835   */
836   void CContext::postProcessing()
837   {
838     int myRank;
839     MPI_Comm_rank(MPI_COMM_WORLD, &myRank);
840
841     if (isPostProcessed) return;
842
843      // Make sure the calendar was correctly created
844      if (!calendar)
845        ERROR("CContext::postProcessing()", << "A calendar must be defined for the context \"" << getId() << "!\"")
846      else if (calendar->getTimeStep() == NoneDu)
847        ERROR("CContext::postProcessing()", << "A timestep must be defined for the context \"" << getId() << "!\"")
848      // Calendar first update to set the current date equals to the start date
849      calendar->update(0);
850
851      // Find all inheritance in xml structure
852      this->solveAllInheritance();
853
854      // Check if some axis, domains or grids are eligible to for compressed indexed output.
855      // Warning: This must be done after solving the inheritance and before the rest of post-processing
856      checkAxisDomainsGridsEligibilityForCompressedOutput();
857
858      // Check if some automatic time series should be generated
859      // Warning: This must be done after solving the inheritance and before the rest of post-processing
860      prepareTimeseries();
861
862      //Initialisation du vecteur 'enabledFiles' contenant la liste des fichiers  sortir.
863      this->findEnabledFiles();
864      this->findEnabledReadModeFiles();
865
866      // Find all enabled fields of each file
867      this->findAllEnabledFields();
868      this->findAllEnabledFieldsInReadModeFiles();
869
870     if (hasClient && !hasServer)
871     {
872      // Try to read attributes of fields in file then fill in corresponding grid (or domain, axis)
873      this->readAttributesOfEnabledFieldsInReadModeFiles();
874     }
875
876      // Only search and rebuild all reference objects of enable fields, don't transform
877      this->solveOnlyRefOfEnabledFields(false);
878
879      // Search and rebuild all reference object of enabled fields
880      this->solveAllRefOfEnabledFields(false);
881
882      // Find all fields with read access from the public API
883      findFieldsWithReadAccess();
884      // and solve the all reference for them
885      solveAllRefOfFieldsWithReadAccess();
886
887      isPostProcessed = true;
888   }
889
890   /*!
891    * Compute the required buffer size to send the attributes (mostly those grid related).
892    *
893    * \param maxEventSize [in/out] the size of the bigger event for each connected server
894    */
895   std::map<int, StdSize> CContext::getAttributesBufferSize(std::map<int, StdSize>& maxEventSize)
896   {
897     std::map<int, StdSize> attributesSize;
898
899     if (hasClient)
900     {
901       size_t numEnabledFiles = this->enabledFiles.size();
902       for (size_t i = 0; i < numEnabledFiles; ++i)
903       {
904         CFile* file = this->enabledFiles[i];
905
906         std::vector<CField*> enabledFields = file->getEnabledFields();
907         size_t numEnabledFields = enabledFields.size();
908         for (size_t j = 0; j < numEnabledFields; ++j)
909         {
910           const std::map<int, StdSize> mapSize = enabledFields[j]->getGridAttributesBufferSize();
911           std::map<int, StdSize>::const_iterator it = mapSize.begin(), itE = mapSize.end();
912           for (; it != itE; ++it)
913           {
914             // If attributesSize[it->first] does not exist, it will be zero-initialized
915             // so we can use it safely without checking for its existance
916             if (attributesSize[it->first] < it->second)
917               attributesSize[it->first] = it->second;
918
919             if (maxEventSize[it->first] < it->second)
920               maxEventSize[it->first] = it->second;
921           }
922         }
923       }
924     }
925
926     return attributesSize;
927   }
928
929   /*!
930    * Compute the required buffer size to send the fields data.
931    *
932    * \param maxEventSize [in/out] the size of the bigger event for each connected server
933    */
934   std::map<int, StdSize> CContext::getDataBufferSize(std::map<int, StdSize>& maxEventSize)
935   {
936     CFile::mode_attr::t_enum mode = hasClient ? CFile::mode_attr::write : CFile::mode_attr::read;
937
938     std::map<int, StdSize> dataSize;
939
940     // Find all reference domain and axis of all active fields
941     size_t numEnabledFiles = this->enabledFiles.size();
942     for (size_t i = 0; i < numEnabledFiles; ++i)
943     {
944       CFile* file = this->enabledFiles[i];
945       CFile::mode_attr::t_enum fileMode = file->mode.isEmpty() ? CFile::mode_attr::write : file->mode.getValue();
946
947       if (fileMode == mode)
948       {
949         std::vector<CField*> enabledFields = file->getEnabledFields();
950         size_t numEnabledFields = enabledFields.size();
951         for (size_t j = 0; j < numEnabledFields; ++j)
952         {
953           const std::map<int, StdSize> mapSize = enabledFields[j]->getGridDataBufferSize();
954           std::map<int, StdSize>::const_iterator it = mapSize.begin(), itE = mapSize.end();
955           for (; it != itE; ++it)
956           {
957             // If dataSize[it->first] does not exist, it will be zero-initialized
958             // so we can use it safely without checking for its existance
959             if (CXios::isOptPerformance)
960               dataSize[it->first] += it->second;
961             else if (dataSize[it->first] < it->second)
962               dataSize[it->first] = it->second;
963
964             if (maxEventSize[it->first] < it->second)
965               maxEventSize[it->first] = it->second;
966           }
967         }
968       }
969     }
970
971     return dataSize;
972   }
973
974   //! Client side: Send infomation of active files (files are enabled to write out)
975   void CContext::sendEnabledFiles()
976   {
977     int size = this->enabledFiles.size();
978
979     // In a context, each type has a root definition, e.g: axis, domain, field.
980     // Every object must be a child of one of these root definition. In this case
981     // all new file objects created on server must be children of the root "file_definition"
982     StdString fileDefRoot("file_definition");
983     CFileGroup* cfgrpPtr = CFileGroup::get(fileDefRoot);
984
985     for (int i = 0; i < size; ++i)
986     {
987       cfgrpPtr->sendCreateChild(this->enabledFiles[i]->getId());
988       this->enabledFiles[i]->sendAllAttributesToServer();
989       this->enabledFiles[i]->sendAddAllVariables();
990     }
991   }
992
993   //! Client side: Send information of active fields (ones are written onto files)
994   void CContext::sendEnabledFields()
995   {
996     int size = this->enabledFiles.size();
997     for (int i = 0; i < size; ++i)
998     {
999       this->enabledFiles[i]->sendEnabledFields();
1000     }
1001   }
1002
1003   //! Client side: Check if the defined axis, domains and grids are eligible for compressed indexed output
1004   void CContext::checkAxisDomainsGridsEligibilityForCompressedOutput()
1005   {
1006     if (!hasClient) return;
1007
1008     const vector<CAxis*> allAxis = CAxis::getAll();
1009     for (vector<CAxis*>::const_iterator it = allAxis.begin(); it != allAxis.end(); it++)
1010       (*it)->checkEligibilityForCompressedOutput();
1011
1012     const vector<CDomain*> allDomains = CDomain::getAll();
1013     for (vector<CDomain*>::const_iterator it = allDomains.begin(); it != allDomains.end(); it++)
1014       (*it)->checkEligibilityForCompressedOutput();
1015
1016     const vector<CGrid*> allGrids = CGrid::getAll();
1017     for (vector<CGrid*>::const_iterator it = allGrids.begin(); it != allGrids.end(); it++)
1018       (*it)->checkEligibilityForCompressedOutput();
1019   }
1020
1021   //! Client side: Prepare the timeseries by adding the necessary files
1022   void CContext::prepareTimeseries()
1023   {
1024     if (!hasClient) return;
1025
1026     const std::vector<CFile*> allFiles = CFile::getAll();
1027     for (size_t i = 0; i < allFiles.size(); i++)
1028     {
1029       CFile* file = allFiles[i];
1030
1031       std::vector<CVariable*> fileVars, fieldVars, vars = file->getAllVariables();
1032       for (size_t k = 0; k < vars.size(); k++)
1033       {
1034         CVariable* var = vars[k];
1035
1036         if (var->ts_target.isEmpty()
1037              || var->ts_target == CVariable::ts_target_attr::file || var->ts_target == CVariable::ts_target_attr::both)
1038           fileVars.push_back(var);
1039
1040         if (!var->ts_target.isEmpty()
1041              && (var->ts_target == CVariable::ts_target_attr::field || var->ts_target == CVariable::ts_target_attr::both))
1042           fieldVars.push_back(var);
1043       }
1044
1045       if (!file->timeseries.isEmpty() && file->timeseries != CFile::timeseries_attr::none)
1046       {
1047         StdString fileNameStr("%file_name%") ;
1048         StdString tsPrefix = !file->ts_prefix.isEmpty() ? file->ts_prefix : fileNameStr ;
1049         
1050         StdString fileName=file->getFileOutputName();
1051         size_t pos=tsPrefix.find(fileNameStr) ;
1052         while (pos!=std::string::npos)
1053         {
1054           tsPrefix=tsPrefix.replace(pos,fileNameStr.size(),fileName) ;
1055           pos=tsPrefix.find(fileNameStr) ;
1056         }
1057       
1058         const std::vector<CField*> allFields = file->getAllFields();
1059         for (size_t j = 0; j < allFields.size(); j++)
1060         {
1061           CField* field = allFields[j];
1062
1063           if (!field->ts_enabled.isEmpty() && field->ts_enabled)
1064           {
1065             CFile* tsFile = CFile::create();
1066             tsFile->duplicateAttributes(file);
1067
1068             // Add variables originating from file and targeted to timeserie file
1069             for (size_t k = 0; k < fileVars.size(); k++)
1070               tsFile->getVirtualVariableGroup()->addChild(fileVars[k]);
1071
1072           
1073             tsFile->name = tsPrefix + "_";
1074             if (!field->name.isEmpty())
1075               tsFile->name.get() += field->name;
1076             else if (field->hasDirectFieldReference()) // We cannot use getBaseFieldReference() just yet
1077               tsFile->name.get() += field->field_ref;
1078             else
1079               tsFile->name.get() += field->getId();
1080
1081             if (!field->ts_split_freq.isEmpty())
1082               tsFile->split_freq = field->ts_split_freq;
1083
1084             CField* tsField = tsFile->addField();
1085             tsField->field_ref = field->getId();
1086
1087             // Add variables originating from file and targeted to timeserie field
1088             for (size_t k = 0; k < fieldVars.size(); k++)
1089               tsField->getVirtualVariableGroup()->addChild(fieldVars[k]);
1090
1091             vars = field->getAllVariables();
1092             for (size_t k = 0; k < vars.size(); k++)
1093             {
1094               CVariable* var = vars[k];
1095
1096               // Add variables originating from field and targeted to timeserie field
1097               if (var->ts_target.isEmpty()
1098                    || var->ts_target == CVariable::ts_target_attr::field || var->ts_target == CVariable::ts_target_attr::both)
1099                 tsField->getVirtualVariableGroup()->addChild(var);
1100
1101               // Add variables originating from field and targeted to timeserie file
1102               if (!var->ts_target.isEmpty()
1103                    && (var->ts_target == CVariable::ts_target_attr::file || var->ts_target == CVariable::ts_target_attr::both))
1104                 tsFile->getVirtualVariableGroup()->addChild(var);
1105             }
1106
1107             tsFile->solveFieldRefInheritance(true);
1108
1109             if (file->timeseries == CFile::timeseries_attr::exclusive)
1110               field->enabled = false;
1111           }
1112         }
1113
1114         // Finally disable the original file is need be
1115         if (file->timeseries == CFile::timeseries_attr::only)
1116          file->enabled = false;
1117       }
1118     }
1119   }
1120
1121   //! Client side: Send information of reference grid of active fields
1122   void CContext::sendRefGrid()
1123   {
1124     std::set<StdString> gridIds;
1125     int sizeFile = this->enabledFiles.size();
1126     CFile* filePtr(NULL);
1127
1128     // Firstly, find all reference grids of all active fields
1129     for (int i = 0; i < sizeFile; ++i)
1130     {
1131       filePtr = this->enabledFiles[i];
1132       std::vector<CField*> enabledFields = filePtr->getEnabledFields();
1133       int sizeField = enabledFields.size();
1134       for (int numField = 0; numField < sizeField; ++numField)
1135       {
1136         if (0 != enabledFields[numField]->getRelGrid())
1137           gridIds.insert(CGrid::get(enabledFields[numField]->getRelGrid())->getId());
1138       }
1139     }
1140
1141     // Create all reference grids on server side
1142     StdString gridDefRoot("grid_definition");
1143     CGridGroup* gridPtr = CGridGroup::get(gridDefRoot);
1144     std::set<StdString>::const_iterator it, itE = gridIds.end();
1145     for (it = gridIds.begin(); it != itE; ++it)
1146     {
1147       gridPtr->sendCreateChild(*it);
1148       CGrid::get(*it)->sendAllAttributesToServer();
1149       CGrid::get(*it)->sendAllDomains();
1150       CGrid::get(*it)->sendAllAxis();
1151       CGrid::get(*it)->sendAllScalars();
1152     }
1153   }
1154
1155
1156   //! Client side: Send information of reference domain and axis of active fields
1157   void CContext::sendRefDomainsAxis()
1158   {
1159     std::set<StdString> domainIds, axisIds, scalarIds;
1160
1161     // Find all reference domain and axis of all active fields
1162     int numEnabledFiles = this->enabledFiles.size();
1163     for (int i = 0; i < numEnabledFiles; ++i)
1164     {
1165       std::vector<CField*> enabledFields = this->enabledFiles[i]->getEnabledFields();
1166       int numEnabledFields = enabledFields.size();
1167       for (int j = 0; j < numEnabledFields; ++j)
1168       {
1169         const std::vector<StdString>& prDomAxisScalarId = enabledFields[j]->getRefDomainAxisIds();
1170         if ("" != prDomAxisScalarId[0]) domainIds.insert(prDomAxisScalarId[0]);
1171         if ("" != prDomAxisScalarId[1]) axisIds.insert(prDomAxisScalarId[1]);
1172         if ("" != prDomAxisScalarId[2]) scalarIds.insert(prDomAxisScalarId[2]);
1173       }
1174     }
1175
1176     // Create all reference axis on server side
1177     std::set<StdString>::iterator itDom, itAxis, itScalar;
1178     std::set<StdString>::const_iterator itE;
1179
1180     StdString scalarDefRoot("scalar_definition");
1181     CScalarGroup* scalarPtr = CScalarGroup::get(scalarDefRoot);
1182     itE = scalarIds.end();
1183     for (itScalar = scalarIds.begin(); itScalar != itE; ++itScalar)
1184     {
1185       if (!itScalar->empty())
1186       {
1187         scalarPtr->sendCreateChild(*itScalar);
1188         CScalar::get(*itScalar)->sendAllAttributesToServer();
1189       }
1190     }
1191
1192     StdString axiDefRoot("axis_definition");
1193     CAxisGroup* axisPtr = CAxisGroup::get(axiDefRoot);
1194     itE = axisIds.end();
1195     for (itAxis = axisIds.begin(); itAxis != itE; ++itAxis)
1196     {
1197       if (!itAxis->empty())
1198       {
1199         axisPtr->sendCreateChild(*itAxis);
1200         CAxis::get(*itAxis)->sendAllAttributesToServer();
1201       }
1202     }
1203
1204     // Create all reference domains on server side
1205     StdString domDefRoot("domain_definition");
1206     CDomainGroup* domPtr = CDomainGroup::get(domDefRoot);
1207     itE = domainIds.end();
1208     for (itDom = domainIds.begin(); itDom != itE; ++itDom)
1209     {
1210       if (!itDom->empty()) {
1211          domPtr->sendCreateChild(*itDom);
1212          CDomain::get(*itDom)->sendAllAttributesToServer();
1213       }
1214     }
1215   }
1216
1217   //! Update calendar in each time step
1218   void CContext::updateCalendar(int step)
1219   {
1220      #pragma omp critical (_output)
1221      {info(50) << "updateCalendar : before : " << calendar->getCurrentDate() << endl;}
1222      calendar->update(step);
1223      #pragma omp critical (_output)
1224      {info(50) << "updateCalendar : after : " << calendar->getCurrentDate() << endl;}
1225#ifdef XIOS_MEMTRACK_LIGHT
1226      #pragma omp critical (_output)
1227      {info(50) << " Current memory used by XIOS : "<<  MemTrack::getCurrentMemorySize()*1.0/(1024*1024)<<" Mbyte, at timestep "<<step<<" of context "<<this->getId()<<endl ;}
1228#endif
1229      if (hasClient)
1230      {
1231        checkPrefetchingOfEnabledReadModeFiles();
1232        garbageCollector.invalidate(calendar->getCurrentDate());
1233      }
1234   }
1235
1236   //! Server side: Create header of netcdf file
1237   void CContext::createFileHeader(void )
1238   {
1239      vector<CFile*>::const_iterator it;
1240
1241      for (it=enabledFiles.begin(); it != enabledFiles.end(); it++)
1242      {
1243         (*it)->initFile();
1244      }
1245   }
1246
1247   //! Get current context
1248   CContext* CContext::getCurrent(void)
1249   {
1250     return CObjectFactory::GetObject<CContext>(CObjectFactory::GetCurrentContextId()).get();
1251   }
1252
1253   /*!
1254   \brief Set context with an id be the current context
1255   \param [in] id identity of context to be set to current
1256   */
1257   void CContext::setCurrent(const string& id)
1258   {
1259     CObjectFactory::SetCurrentContextId(id);
1260     CGroupFactory::SetCurrentContextId(id);
1261   }
1262
1263  /*!
1264  \brief Create a context with specific id
1265  \param [in] id identity of new context
1266  \return pointer to the new context or already-existed one with identity id
1267  */
1268  CContext* CContext::create(const StdString& id)
1269  {
1270    CContext::setCurrent(id);
1271
1272    bool hasctxt = CContext::has(id);
1273    CContext* context = CObjectFactory::CreateObject<CContext>(id).get();
1274    getRoot();
1275    if (!hasctxt) CGroupFactory::AddChild(*root_ptr, context->getShared());
1276
1277#define DECLARE_NODE(Name_, name_) \
1278    C##Name_##Definition::create(C##Name_##Definition::GetDefName());
1279#define DECLARE_NODE_PAR(Name_, name_)
1280#include "node_type.conf"
1281
1282    return (context);
1283  }
1284
1285
1286
1287     //! Server side: Receive a message to do some post processing
1288  void CContext::recvRegistry(CEventServer& event)
1289  {
1290    CBufferIn* buffer=event.subEvents.begin()->buffer;
1291    string id;
1292    *buffer>>id;
1293    get(id)->recvRegistry(*buffer);
1294  }
1295
1296  void CContext::recvRegistry(CBufferIn& buffer)
1297  {
1298    if (server->intraCommRank==0)
1299    {
1300      CRegistry registry(server->intraComm) ;
1301      registry.fromBuffer(buffer) ;
1302      registryOut->mergeRegistry(registry) ;
1303    }
1304  }
1305
1306  void CContext::sendRegistry(void)
1307  {
1308    registryOut->hierarchicalGatherRegistry() ;
1309
1310    CEventClient event(CContext::GetType(), CContext::EVENT_ID_SEND_REGISTRY);
1311    if (client->isServerLeader())
1312    {
1313       CMessage msg ;
1314       msg<<this->getIdServer();
1315       if (client->clientRank==0) msg<<*registryOut ;
1316       const std::list<int>& ranks = client->getRanksServerLeader();
1317       for (std::list<int>::const_iterator itRank = ranks.begin(), itRankEnd = ranks.end(); itRank != itRankEnd; ++itRank)
1318         event.push(*itRank,1,msg);
1319       client->sendEvent(event);
1320     }
1321     else client->sendEvent(event);
1322  }
1323
1324} // namespace xios
Note: See TracBrowser for help on using the repository browser.