source: XIOS/dev/XIOS_DEV_CMIP6/src/node/file.cpp @ 1485

Last change on this file since 1485 was 1485, checked in by oabramkina, 6 years ago

Enforcing sequential I/O on the client side during reading of metadata by client processes. Parallel I/O can still be used in this case via setting file attribute "read_metadata_par" to true.

  • 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: 39.7 KB
Line 
1#include "file.hpp"
2
3#include "attribute_template.hpp"
4#include "object_template.hpp"
5#include "group_template.hpp"
6#include "object_factory.hpp"
7#include "context.hpp"
8#include "context_server.hpp"
9#include "nc4_data_output.hpp"
10#include "nc4_data_input.hpp"
11#include "calendar_util.hpp"
12#include "date.hpp"
13#include "message.hpp"
14#include "type.hpp"
15#include "xios_spl.hpp"
16#include "context_client.hpp"
17#include "mpi.hpp"
18#include "timer.hpp"
19#include "server.hpp"
20
21namespace xios {
22
23   /// ////////////////////// Dfinitions ////////////////////// ///
24
25   CFile::CFile(void)
26      : CObjectTemplate<CFile>(), CFileAttributes()
27      , vFieldGroup(), data_out(), enabledFields(), fileComm(MPI_COMM_NULL)
28      , isOpen(false), read_client(0), checkRead(false), allZoneEmpty(false)
29   {
30     setVirtualFieldGroup(CFieldGroup::create(getId() + "_virtual_field_group"));
31     setVirtualVariableGroup(CVariableGroup::create(getId() + "_virtual_variable_group"));
32   }
33
34   CFile::CFile(const StdString & id)
35      : CObjectTemplate<CFile>(id), CFileAttributes()
36      , vFieldGroup(), data_out(), enabledFields(), fileComm(MPI_COMM_NULL)
37      , isOpen(false), read_client(0), checkRead(false), allZoneEmpty(false)
38    {
39      setVirtualFieldGroup(CFieldGroup::create(getId() + "_virtual_field_group"));
40      setVirtualVariableGroup(CVariableGroup::create(getId() + "_virtual_variable_group"));
41    }
42
43   CFile::~CFile(void)
44   { /* Ne rien faire de plus */ }
45
46   ///---------------------------------------------------------------
47  //! Get name of file
48   StdString CFile::GetName(void)   { return (StdString("file")); }
49   StdString CFile::GetDefName(void){ return (CFile::GetName()); }
50   ENodeType CFile::GetType(void)   { return (eFile); }
51
52   //----------------------------------------------------------------
53
54   const StdString CFile::getFileOutputName(void) const
55   {
56     return (name.isEmpty() ? getId() : name) + (name_suffix.isEmpty() ? StdString("") :  name_suffix.getValue());
57   }
58
59   //----------------------------------------------------------------
60   /*!
61   \brief Get data writer object.
62   Each enabled file in xml represents a physical netcdf file.
63   This function allows to access the data writer object.
64   \return data writer object.
65   */
66   boost::shared_ptr<CDataOutput> CFile::getDataOutput(void) const
67   {
68      return data_out;
69   }
70
71   /*!
72   \brief Get data reader object.
73   Each enabled file in xml represents a physical netcdf file.
74   This function allows to access the data reader object.
75   \return data reader object.
76   */
77   boost::shared_ptr<CDataInput> CFile::getDataInput(void) const
78   {
79      return data_in;
80   }
81
82   /*!
83   \brief Get virtual field group
84      In each file, there always exists a field group which is the ancestor of all
85   fields in the file. This is considered be virtual because it is created automatically during
86   file initialization and it normally doesn't appear on xml file
87   \return Pointer to field group
88   */
89   CFieldGroup* CFile::getVirtualFieldGroup(void) const
90   {
91      return (this->vFieldGroup);
92   }
93
94   /*!
95   \brief Get virtual variable group
96      In each file, there always exists a variable group which is the ancestor of all
97   variable in the file. This is considered be virtual because it is created automatically during
98   file initialization and it normally doesn't appear on xml file
99   \return Pointer to variable group
100   */
101   CVariableGroup* CFile::getVirtualVariableGroup(void) const
102   {
103      return (this->vVariableGroup);
104   }
105
106   //! Get all fields of a file
107   std::vector<CField*> CFile::getAllFields(void) const
108   {
109      return (this->vFieldGroup->getAllChildren());
110   }
111
112   //! Get all variables of a file
113   std::vector<CVariable*> CFile::getAllVariables(void) const
114   {
115      return (this->vVariableGroup->getAllChildren());
116   }
117
118   //----------------------------------------------------------------
119   /*!
120   \brief Get all enabled fields of file
121      A field is considered to be enabled if it fullfil these conditions: it is enabled, inside a enabled file
122   and its own level is not larger than file output level.
123   \param [in] default_outputlevel default value output level of file
124   \param [in] default_level default value level of field
125   \param [in] default_enabled flag determine by default if field is enabled
126   \return Vector of pointers of enabled fields
127   */
128   std::vector<CField*> CFile::getEnabledFields(int default_outputlevel,
129                                                int default_level,
130                                                bool default_enabled)
131   {
132      if (!this->enabledFields.empty())
133         return (this->enabledFields);
134
135      const int _outputlevel =
136         (!output_level.isEmpty()) ? output_level.getValue() : default_outputlevel;
137      std::vector<CField*>::iterator it;
138      this->enabledFields = this->getAllFields();
139
140      std::vector<CField*> newEnabledFields;
141
142      for ( it = this->enabledFields.begin(); it != this->enabledFields.end(); it++ )
143      {
144         if (!(*it)->enabled.isEmpty()) // Si l'attribut 'enabled' est dfini ...
145         {
146            if (! (*it)->enabled.getValue()) continue;
147//            { it--; this->enabledFields.erase(it+1); continue; }
148         }
149         else // Si l'attribut 'enabled' n'est pas dfini ...
150         {
151            if (!default_enabled) continue;
152//            { it--; this->enabledFields.erase(it+1); continue; }
153         }
154
155         if (!(*it)->level.isEmpty()) // Si l'attribut 'level' est dfini ...
156         {
157            if ((*it)->level.getValue() > _outputlevel) continue;
158//            { it--; this->enabledFields.erase(it+1); continue; }
159         }
160         else // Si l'attribut 'level' n'est pas dfini ...
161         {
162            if (default_level > _outputlevel) continue;
163//            { it--; this->enabledFields.erase(it+1); continue; }
164         }
165
166//         CField* field_tmp=(*it).get();
167//         shared_ptr<CField> sptfield=*it;
168//         field_tmp->refObject.push_back(sptfield);
169         newEnabledFields.push_back(*it);
170         // Le champ est finalement actif, on y ajoute sa propre reference.
171//         (*it)->refObject.push_back(*it);
172         // Le champ est finalement actif, on y ajoute la rfrence au champ de base.
173         (*it)->setRelFile(CFile::get(this));
174      }
175      enabledFields = newEnabledFields;
176
177      return (this->enabledFields);
178   }
179
180   //----------------------------------------------------------------
181   //! Change virtual field group to a new one
182   void CFile::setVirtualFieldGroup(CFieldGroup* newVFieldGroup)
183   {
184      this->vFieldGroup = newVFieldGroup;
185   }
186
187   //! Change virtual variable group to new one
188   void CFile::setVirtualVariableGroup(CVariableGroup* newVVariableGroup)
189   {
190      this->vVariableGroup = newVVariableGroup;
191   }
192
193   //----------------------------------------------------------------
194   bool CFile::isSyncTime(void)
195   {
196     CContext* context = CContext::getCurrent();
197     const CDate& currentDate = context->calendar->getCurrentDate();
198     if (!sync_freq.isEmpty())
199     {
200       if (lastSync + sync_freq.getValue() < currentDate)
201       {
202         lastSync = currentDate;
203         return true;
204        }
205      }
206      return false;
207    }
208
209   //! Initialize a file in order to write into it
210   void CFile::initWrite(void)
211   {
212      CContext* context = CContext::getCurrent();
213      const CDate& currentDate = context->calendar->getCurrentDate();
214      CContextServer* server = context->server;
215
216      lastSync  = currentDate;
217      lastSplit = currentDate;
218      if (!split_freq.isEmpty())
219      {
220        StdString keySuffix("CFile::"+getFileOutputName()+"::") ; 
221        if (context->registryIn->foundKey(keySuffix+"splitStart") && context->registryIn->foundKey(keySuffix+"splitEnd"))
222        {
223          CDate savedSplitStart(*context->getCalendar()), savedSplitEnd(*context->getCalendar());
224          context->registryIn->getKey(keySuffix+"splitStart", savedSplitStart);
225          context->registryIn->getKey(keySuffix+"splitEnd",   savedSplitEnd);
226
227          if (savedSplitStart <= lastSplit && lastSplit <= savedSplitEnd)
228            lastSplit = savedSplitStart;
229        }
230      }
231      isOpen = false;     
232
233//      if (!record_offset.isEmpty() && record_offset < 0)
234//        ERROR("void CFile::initFile(void)",
235//              "Invalid 'record_offset', this attribute cannot be negative.");
236      const int recordOffset = record_offset.isEmpty() ? 0 : record_offset;
237
238      set<StdString> setAxis;
239      set<StdString> setDomains;
240
241      std::vector<CField*>::iterator it, end = this->enabledFields.end();
242      for (it = this->enabledFields.begin(); it != end; it++)
243      {
244         CField* field = *it;         
245         std::vector<CAxis*> vecAxis = field->grid->getAxis();
246         for (size_t i = 0; i < vecAxis.size(); ++i)
247           setAxis.insert(vecAxis[i]->getAxisOutputName());
248         std::vector<CDomain*> vecDomains = field->grid->getDomains();
249         for (size_t i = 0; i < vecDomains.size(); ++i)
250           setDomains.insert(vecDomains[i]->getDomainOutputName());
251
252         field->resetNStep(recordOffset);
253      }
254      nbAxis = setAxis.size();
255      nbDomains = setDomains.size();
256
257      // create sub communicator for file
258      createSubComFile();
259
260      // if (time_counter.isEmpty()) time_counter.setValue(time_counter_attr::centered);
261      if (time_counter_name.isEmpty()) time_counter_name = "time_counter";
262    }
263
264    //! Initialize a file in order to write into it
265    void CFile::initRead(void)
266    {
267      if (checkRead) return;
268      createSubComFile();
269      checkRead = true;
270    }
271
272    /*!
273      Create a sub communicator in which processes participate in reading/opening file
274    */
275    void CFile::createSubComFile()
276    {
277      CContext* context = CContext::getCurrent();
278      CContextServer* server = context->server;
279
280      // create sub communicator for file
281      allZoneEmpty = true;     
282      std::vector<CField*>::iterator it, end = this->enabledFields.end();
283      for (it = this->enabledFields.begin(); it != end; it++)
284      {
285         CField* field = *it;
286         bool nullGrid = (0 == field->grid);
287         allZoneEmpty &= nullGrid ? false : !field->grid->doGridHaveDataToWrite();
288      }
289
290      int color = allZoneEmpty ? 0 : 1;
291      MPI_Comm_split(server->intraComm, color, server->intraCommRank, &fileComm);
292      if (allZoneEmpty) MPI_Comm_free(&fileComm);
293    }
294
295    /*
296       Check condition to write into a file
297       For now, we only use the level-2 server to write files (if this mode is activated)
298       or classical server to do this job.
299    */
300    void CFile::checkWriteFile(void)
301    {
302      CContext* context = CContext::getCurrent();
303      // Done by classical server or secondary server
304      // This condition should be changed soon
305      if (CServer::serverLevel == 0 || CServer::serverLevel == 2)
306      {
307        if (mode.isEmpty() || mode.getValue() == mode_attr::write)
308        {
309          CTimer::get("Files : create headers").resume();
310          if (!isOpen) createHeader();
311          CTimer::get("Files : create headers").suspend();
312          checkSync();
313        }       
314        checkSplit(); // REally need this?
315      }
316    }
317
318    /*
319       Check condition to read from a file
320       For now, we only use the level-1 server to write files (if this mode is activated)
321       or classical server to do this job.
322       This function can be used by client for reading metadata
323    */
324    void CFile::checkReadFile(void)
325    {
326      CContext* context = CContext::getCurrent();
327      // Done by classical server or secondary server
328      // TODO: This condition should be changed soon. It only works with maximum number of level as 2
329      if (CServer::serverLevel == 0 || CServer::serverLevel == 1)
330      {
331        if (!mode.isEmpty() && mode.getValue() == mode_attr::read)
332        {
333          CTimer::get("Files : open headers").resume();
334         
335          if (!isOpen) openInReadMode();
336
337          CTimer::get("Files : open headers").suspend();
338        }
339        //checkSplit(); // Really need for reading?
340      }
341    }
342
343    /*!
344      Verify if a process participates in an opening-file communicator
345      \return true if the process doesn't participate in opening file
346    */
347    bool CFile::isEmptyZone()
348    {
349      return allZoneEmpty;
350    }
351
352    /*!
353    \brief Verify if synchronisation should be done
354        If syn option is enabled, syn frequence and current time will be used to
355    calculate the moment to syn file(s)
356    \return True if it is the moment to synchronize file, otherwise false
357    */
358   bool CFile::checkSync(void)
359   {
360     CContext* context = CContext::getCurrent();
361     const CDate& currentDate = context->calendar->getCurrentDate();
362     if (!sync_freq.isEmpty())
363     {
364       if (lastSync + sync_freq.getValue() <= currentDate)
365       {
366         lastSync = currentDate;
367         data_out->syncFile();
368         return true;
369        }
370      }
371      return false;
372    }
373
374    /*!
375    \brief Verify if splitting should be done
376        If split option is enabled, split frequence and current time will be used to
377    calculate the moment to split file
378    \return True if it is the moment to split file, otherwise false
379    */
380    bool CFile::checkSplit(void)
381    {
382      CContext* context = CContext::getCurrent();
383      const CDate& currentDate = context->calendar->getCurrentDate();
384      if (!split_freq.isEmpty())
385      {
386        if (currentDate > lastSplit + split_freq.getValue())
387        {
388          lastSplit = lastSplit + split_freq.getValue();
389          std::vector<CField*>::iterator it, end = this->enabledFields.end();
390          for (it = this->enabledFields.begin(); it != end; it++)
391          {
392            (*it)->resetNStep();
393            (*it)->resetNStepMax();
394          }
395          if (mode.isEmpty() || mode.getValue() == mode_attr::write)
396            createHeader();
397          else
398            openInReadMode();
399          return true;
400        }
401      }
402      return false;
403    }
404
405   /*!
406   \brief Create header of netcdf file
407   There are some information to fill in header of each netcdf.
408   */
409   void CFile::createHeader(void)
410   {
411      CContext* context = CContext::getCurrent();
412      CContextServer* server = context->server;
413
414      if (!allZoneEmpty)
415      {
416         StdString filename = getFileOutputName();
417
418// determine splitting format in the file name  : firstPart%start_date%middlePart%end_date%lastPart
419
420         std::string strStartDate="%start_date%" ;
421         std::string strEndDate="%end_date%" ;
422
423         std::string firstPart ;
424         std::string middlePart ;
425         std::string lastPart ;
426         size_t pos1, pos2 ;
427         bool hasStartDate=false ;
428         bool hasEndDate=false ;
429         bool hasSplit = (!split_freq.isEmpty());
430                 
431         pos1=filename.find(strStartDate) ;
432         if (pos1!=std::string::npos)
433         {
434           firstPart=filename.substr(0,pos1) ;
435           pos1+=strStartDate.size() ;
436           hasStartDate=true ;
437         }
438         else pos1=0 ;
439
440         pos2=filename.find(strEndDate,pos1) ;
441         if (pos2!=std::string::npos)
442         {
443           middlePart=filename.substr(pos1,pos2-pos1) ;
444           pos2+=strEndDate.size() ;
445           lastPart=filename.substr(pos2,filename.size()-pos2) ;
446           hasEndDate=true ;
447         }
448         else middlePart=filename.substr(pos1,filename.size()) ;
449
450         if (!hasStartDate && !hasEndDate)
451         {
452           hasStartDate=true ;
453           hasEndDate=true;
454           firstPart=middlePart ;
455           if (hasSplit) firstPart +="_";
456           middlePart="-" ;
457         }
458   
459         StdOStringStream oss;
460
461         if (!split_freq.isEmpty())
462         {
463           CDate split_start ;
464           CDate splitEnd ;
465           if (!split_start_offset.isEmpty()) split_start=lastSplit + split_start_offset ;
466           else split_start=lastSplit ;
467
468           splitEnd = lastSplit + split_freq ;
469           if (!split_last_date.isEmpty())
470           {
471             CDate splitLastDate=CDate::FromString(split_last_date,*CContext::getCurrent()->getCalendar()) ;
472             if( splitLastDate < splitEnd)  splitEnd=splitLastDate ;
473           }
474           
475           if (!split_end_offset.isEmpty()) splitEnd = splitEnd + split_end_offset;
476           else splitEnd = splitEnd - 1 * Second;
477
478           string splitFormat;
479           if (split_freq_format.isEmpty())
480           {
481             if (split_freq.getValue().second != 0) splitFormat = "%y%mo%d%h%mi%s";
482             else if (split_freq.getValue().minute != 0) splitFormat = "%y%mo%d%h%mi";
483             else if (split_freq.getValue().hour != 0) splitFormat = "%y%mo%d%h";
484             else if (split_freq.getValue().day != 0) splitFormat = "%y%mo%d";
485             else if (split_freq.getValue().month != 0) splitFormat = "%y%mo";
486             else splitFormat = "%y";
487           }
488           else splitFormat = split_freq_format;
489
490           oss << firstPart ;
491           if (hasStartDate) oss << split_start.getStr(splitFormat) ;
492           oss << middlePart ;
493           if (hasEndDate) oss << splitEnd.getStr(splitFormat);
494           oss << lastPart ;
495
496           StdString keySuffix("CFile::"+getFileOutputName()+"::") ; 
497           context->registryOut->setKey(keySuffix+"splitStart", lastSplit);
498           context->registryOut->setKey(keySuffix+"splitEnd",   splitEnd);
499         }
500         else oss<<firstPart<<lastPart ;
501
502        bool append = !this->append.isEmpty() && this->append.getValue();
503
504         bool useClassicFormat = !format.isEmpty() && format == format_attr::netcdf4_classic;
505         bool useCFConvention = convention.isEmpty() || convention == convention_attr::CF;
506
507         bool multifile = true;
508         if (!type.isEmpty())
509         {
510           if (type == type_attr::one_file) multifile = false;
511           else if (type == type_attr::multiple_file) multifile = true;
512
513         }
514#ifndef USING_NETCDF_PAR
515         if (!multifile)
516         {
517            info(0) << "!!! Warning -> Using non parallel version of netcdf, switching in multiple_file mode for file : " << filename << " ..." << endl;
518            multifile = true;
519          }
520#endif
521         if (multifile)
522         {
523            int commSize, commRank;
524            MPI_Comm_size(fileComm, &commSize);
525            MPI_Comm_rank(fileComm, &commRank);
526
527            if (server->intraCommSize > 1)
528            {
529              oss << "_" ;
530              int width=0; int n = commSize-1;
531              while (n != 0) { n = n / 10; width++;}
532              if (!min_digits.isEmpty())
533                if (width < min_digits) width = min_digits;
534              oss.width(width);
535              oss.fill('0');
536              oss << right << commRank;
537            }
538         }
539         oss << ".nc";
540
541         bool isCollective = par_access.isEmpty() ||  par_access == par_access_attr::collective;
542
543         if (isOpen) data_out->closeFile();
544
545        data_out = shared_ptr<CDataOutput>(new CNc4DataOutput(this, oss.str(), append, useClassicFormat, useCFConvention,
546                                                              fileComm, multifile, isCollective, time_counter_name));
547        isOpen = true;
548
549        data_out->writeFile(CFile::get(this));
550
551        // Do not recreate the file structure if opening an existing file
552        if (!data_out->IsInAppendMode())
553        {
554          std::vector<CField*>::iterator it, end = this->enabledFields.end();
555          for (it = this->enabledFields.begin(); it != end; it++)
556          {
557            CField* field = *it;
558            this->data_out->writeFieldGrid(field);
559          }
560          this->data_out->writeTimeDimension();
561
562          for (it = this->enabledFields.begin(); it != end; it++)
563          {
564            CField* field = *it;
565            this->data_out->writeFieldTimeAxis(field);
566          }
567         
568          for (it = this->enabledFields.begin(); it != end; it++)
569          {
570            CField* field = *it;
571            this->data_out->writeField(field);
572          }
573
574          vector<CVariable*> listVars = getAllVariables();
575          for (vector<CVariable*>::iterator it = listVars.begin(); it != listVars.end(); it++)
576            this->data_out->writeAttribute(*it);
577
578          this->data_out->definition_end();
579        }
580        else
581        {
582          // check time axis even in append mode
583          std::vector<CField*>::iterator it, end = this->enabledFields.end();
584          for (it = this->enabledFields.begin(); it != end; it++)
585          {
586            CField* field = *it;
587            this->data_out->writeFieldTimeAxis(field);
588          }
589        }
590      }
591   }
592
593  /*!
594  \brief Open an existing NetCDF file in read-only mode
595  */
596  void CFile::openInReadMode()
597  {
598    CContext* context = CContext::getCurrent();
599    CContextServer* server = context->server;
600    MPI_Comm readComm = this->fileComm;
601
602    if (!allZoneEmpty)
603    {
604      StdString filename = getFileOutputName();
605      StdOStringStream oss;
606      oss << filename;
607
608      if (!split_freq.isEmpty())
609      {
610        string splitFormat;
611        if (split_freq_format.isEmpty())
612        {
613          if (split_freq.getValue().second != 0) splitFormat = "%y%mo%d%h%mi%s";
614          else if (split_freq.getValue().minute != 0) splitFormat = "%y%mo%d%h%mi";
615          else if (split_freq.getValue().hour != 0) splitFormat = "%y%mo%d%h";
616          else if (split_freq.getValue().day != 0) splitFormat = "%y%mo%d";
617          else if (split_freq.getValue().month != 0) splitFormat = "%y%mo";
618          else splitFormat = "%y";
619        }
620        else splitFormat = split_freq_format;
621        oss << "_" << lastSplit.getStr(splitFormat)
622        << "-" << (lastSplit + split_freq.getValue() - 1 * Second).getStr(splitFormat);
623      }
624
625      bool multifile = true;
626      if (!type.isEmpty())
627      {
628        if (type == type_attr::one_file) multifile = false;
629        else if (type == type_attr::multiple_file) multifile = true;
630      }
631  #ifndef USING_NETCDF_PAR
632      if (!multifile)
633      {
634        info(0) << "!!! Warning -> Using non parallel version of netcdf, switching in multiple_file mode for file : " << filename << " ..." << endl;
635        multifile = true;
636      }
637  #endif
638      if (multifile)
639      {
640        int commSize, commRank;
641        MPI_Comm_size(readComm, &commSize);
642        MPI_Comm_rank(readComm, &commRank);
643
644        if (server->intraCommSize > 1)
645        {
646          oss << "_";
647          int width = 0, n = commSize - 1;
648          while (n != 0) { n = n / 10; width++; }
649          if (!min_digits.isEmpty() && width < min_digits)
650            width = min_digits;
651          oss.width(width);
652          oss.fill('0');
653          oss << right << commRank;
654        }
655      }
656      oss << ".nc";
657
658      bool isCollective = par_access.isEmpty() || par_access == par_access_attr::collective;
659      bool readMetaDataPar = true;
660      if (!context->hasServer) readMetaDataPar = (read_metadata_par.isEmpty()) ? false : read_metadata_par;
661
662      if (isOpen) data_out->closeFile();
663      if (time_counter_name.isEmpty()) data_in = shared_ptr<CDataInput>(new CNc4DataInput(oss.str(), readComm, multifile, isCollective, readMetaDataPar));
664      else data_in = shared_ptr<CDataInput>(new CNc4DataInput(oss.str(), readComm, multifile, isCollective, readMetaDataPar, time_counter_name));
665      isOpen = true;
666    }
667  }
668
669   //! Close file
670   void CFile::close(void)
671   {
672     if (!allZoneEmpty)
673       if (isOpen)
674       {
675         if (mode.isEmpty() || mode.getValue() == mode_attr::write)
676          this->data_out->closeFile();
677         else
678          this->data_in->closeFile();
679        isOpen = false;
680       }
681      if (fileComm != MPI_COMM_NULL) MPI_Comm_free(&fileComm);
682   }
683   //----------------------------------------------------------------
684
685   void CFile::readAttributesOfEnabledFieldsInReadMode()
686   {
687     if (enabledFields.empty()) return;
688
689     // Just check file and try to open it
690     if (time_counter_name.isEmpty()) time_counter_name = "time_counter";
691
692     checkReadFile();
693
694     for (int idx = 0; idx < enabledFields.size(); ++idx)
695     {
696        // First of all, find out which domain and axis associated with this field
697        enabledFields[idx]->solveGridReference();
698
699        // Read attributes of domain and axis from this file
700        this->data_in->readFieldAttributesMetaData(enabledFields[idx]);
701
702        // Now complete domain and axis associated with this field
703        enabledFields[idx]->solveGenerateGrid();
704
705        // Read necessary value from file
706        this->data_in->readFieldAttributesValues(enabledFields[idx]);
707
708        // Fill attributes for base reference
709        enabledFields[idx]->solveGridDomainAxisBaseRef();
710     }
711
712     // Now everything is ok, close it
713     close();
714   }
715
716
717   /*!
718   \brief Parse xml file and write information into file object
719   \param [in] node xmld node corresponding in xml file
720   */
721   void CFile::parse(xml::CXMLNode & node)
722   {
723      SuperClass::parse(node);
724
725      if (node.goToChildElement())
726      {
727        do
728        {
729           if (node.getElementName()=="field" || node.getElementName()=="field_group") this->getVirtualFieldGroup()->parseChild(node);
730           else if (node.getElementName()=="variable" || node.getElementName()=="variable_group") this->getVirtualVariableGroup()->parseChild(node);
731        } while (node.goToNextElement());
732        node.goToParentElement();
733      }
734
735   }
736   //----------------------------------------------------------------
737
738   /*!
739   \brief Represent a file in form of string with all its info
740   \return String
741   */
742   StdString CFile::toString(void) const
743   {
744      StdOStringStream oss;
745
746      oss << "<" << CFile::GetName() << " ";
747      if (this->hasId())
748         oss << " id=\"" << this->getId() << "\" ";
749      oss << SuperClassAttribute::toString() << ">" << std::endl;
750      if (this->getVirtualFieldGroup() != NULL)
751         oss << *this->getVirtualFieldGroup() << std::endl;
752      oss << "</" << CFile::GetName() << " >";
753      return (oss.str());
754   }
755
756   //----------------------------------------------------------------
757
758   /*!
759   \brief Find all inheritace among objects in a file.
760   \param [in] apply (true) write attributes of parent into ones of child if they are empty
761                     (false) write attributes of parent into a new container of child
762   \param [in] parent
763   */
764   void CFile::solveDescInheritance(bool apply, const CAttributeMap * const parent)
765   {
766      SuperClassAttribute::setAttributes(parent,apply);
767      this->getVirtualFieldGroup()->solveDescInheritance(apply, NULL);
768      this->getVirtualVariableGroup()->solveDescInheritance(apply, NULL);
769   }
770
771   //----------------------------------------------------------------
772
773   /*!
774   \brief Resolve all reference of active fields.
775      In order to know exactly which data each active field has, a search for all its
776   reference to find its parents or/and its base reference object must be done. Moreover
777   during this search, there are some information that can only be sent to server AFTER
778   all information of active fields are created on server side, e.g: checking mask or index
779   \param [in] sendToServer: Send all info to server (true) or only a part of it (false)
780   */
781   void CFile::solveOnlyRefOfEnabledFields(bool sendToServer)
782   {
783     int size = this->enabledFields.size();
784     for (int i = 0; i < size; ++i)
785     {
786       this->enabledFields[i]->solveOnlyReferenceEnabledField(sendToServer);
787     }
788   }
789
790   void CFile::checkGridOfEnabledFields()
791   { 
792     int size = this->enabledFields.size();
793     for (int i = 0; i < size; ++i)
794     {
795       this->enabledFields[i]->checkGridOfEnabledFields();
796     }
797   }
798
799   void CFile::sendGridComponentOfEnabledFields()
800   { 
801     int size = this->enabledFields.size();
802     for (int i = 0; i < size; ++i)
803     {
804       this->enabledFields[i]->sendGridComponentOfEnabledFields();
805     }
806   }
807
808   void CFile::sendGridOfEnabledFields()
809   { 
810     int size = this->enabledFields.size();
811     for (int i = 0; i < size; ++i)
812     {
813       this->enabledFields[i]->sendGridOfEnabledFields();
814     }
815   }
816
817   void CFile::generateNewTransformationGridDest()
818   {
819     int size = this->enabledFields.size();
820     for (int i = 0; i < size; ++i)
821     {
822       this->enabledFields[i]->generateNewTransformationGridDest();
823     }
824   }
825
826   /*!
827   \brief Resolve all reference of active fields.
828      In order to know exactly which data each active field has, a search for all its
829   reference to find its parents or/and its base reference object must be done. Moreover
830   during this search, there are some information that can only be sent to server AFTER
831   all information of active fields are created on server side, e.g: checking mask or index
832   \param [in] sendToServer: Send all info to server (true) or only a part of it (false)
833   */
834   void CFile::solveAllRefOfEnabledFieldsAndTransform(bool sendToServer)
835   {
836     int size = this->enabledFields.size();
837     for (int i = 0; i < size; ++i)
838     {       
839      this->enabledFields[i]->solveAllEnabledFieldsAndTransform();
840     }
841   }
842
843   /*!
844    * Constructs the filter graph for each active field.
845    *
846    * \param gc the garbage collector to use when building the filter graph
847    */
848   void CFile::buildFilterGraphOfEnabledFields(CGarbageCollector& gc)
849   {
850     int size = this->enabledFields.size();
851     for (int i = 0; i < size; ++i)
852     {
853       this->enabledFields[i]->buildFilterGraph(gc, true);
854     }
855   }
856
857   /*!
858    * Post-process the filter graph for each active field.
859    */
860   void CFile::postProcessFilterGraph()
861   {
862     int size = this->enabledFields.size();
863     for (int i = 0; i < size; ++i)
864     {
865       this->enabledFields[i]->checkIfMustAutoTrigger();
866     }
867   }
868
869   /*!
870     Prefetching the data for enabled fields read from file.
871   */
872   void CFile::prefetchEnabledReadModeFields(void)
873   {
874     if (mode.isEmpty() || mode.getValue() != mode_attr::read)
875       return;
876
877     int size = this->enabledFields.size();
878     for (int i = 0; i < size; ++i)
879       this->enabledFields[i]->sendReadDataRequest(CContext::getCurrent()->getCalendar()->getCurrentDate());
880   }
881
882   /*!
883     Do all pre timestep operations for enabled fields in read mode:
884      - Check that the data excepted from server has been received
885      - Check if some filters must auto-trigger
886   */
887   void CFile::doPreTimestepOperationsForEnabledReadModeFields(void)
888   {
889     if (mode.isEmpty() || mode.getValue() != mode_attr::read)
890       return;
891
892     int size = this->enabledFields.size();
893     for (int i = 0; i < size; ++i)
894     {
895       this->enabledFields[i]->checkForLateDataFromServer();
896       this->enabledFields[i]->autoTriggerIfNeeded();
897     }
898   }
899
900   /*!
901     Do all post timestep operations for enabled fields in read mode:
902      - Prefetch the data read from file when needed
903   */
904   void CFile::doPostTimestepOperationsForEnabledReadModeFields(void)
905   {
906     if (mode.isEmpty() || mode.getValue() != mode_attr::read)
907       return;
908
909     int size = this->enabledFields.size();
910     for (int i = 0; i < size; ++i)
911     {
912       this->enabledFields[i]->sendReadDataRequestIfNeeded();
913     }
914   }
915
916   void CFile::solveFieldRefInheritance(bool apply)
917   {
918      // Rsolution des hritages par rfrence de chacun des champs contenus dans le fichier.
919      std::vector<CField*> allF = this->getAllFields();
920      for (unsigned int i = 0; i < allF.size(); i++)
921         allF[i]->solveRefInheritance(apply);
922   }
923
924   //----------------------------------------------------------------
925
926   /*!
927   \brief Add a field into file.
928      A field is added into file and it will be written out if the file is enabled and
929   level of this field is smaller than level_output. A new field won't be created if one
930   with id has already existed
931   \param [in] id String identity of new field
932   \return Pointer to added (or already existed) field
933   */
934   CField* CFile::addField(const string& id)
935   {
936     return vFieldGroup->createChild(id);
937   }
938
939   /*!
940   \brief Add a field group into file.
941      A field group is added into file and it will play a role as parents for fields.
942   A new field group won't be created if one with id has already existed
943   \param [in] id String identity of new field group
944   \return Pointer to added (or already existed) field group
945   */
946   CFieldGroup* CFile::addFieldGroup(const string& id)
947   {
948     return vFieldGroup->createChildGroup(id);
949   }
950
951   /*!
952   \brief Add a variable into file.
953      A variable is added into file and if one with id has already existed, pointer to
954   it will be returned.
955      Variable as long as attributes are information container of file.
956   However, whereas attributes are "fixed" information, variables provides a more flexible way to user
957   to fill in (extra) information for a file.
958   \param [in] id String identity of new variable
959   \return Pointer to added (or already existed) variable
960   */
961   CVariable* CFile::addVariable(const string& id)
962   {
963     return vVariableGroup->createChild(id);
964   }
965
966   /*!
967   \brief Add a variable group into file.
968      A variable group is added into file and it will play a role as parents for variables.
969   A new variable group won't be created if one with id has already existed
970   \param [in] id String identity of new variable group
971   \return Pointer to added (or already existed) variable group
972   */
973   CVariableGroup* CFile::addVariableGroup(const string& id)
974   {
975     return vVariableGroup->createChildGroup(id);
976   }
977
978   void CFile::setContextClient(CContextClient* newContextClient)
979   {
980     client = newContextClient;
981     size_t size = this->enabledFields.size();
982     for (size_t i = 0; i < size; ++i)
983     {
984       this->enabledFields[i]->setContextClient(newContextClient);
985     }
986   }
987
988   CContextClient* CFile::getContextClient()
989   {
990     return client;
991   }
992
993   void CFile::setReadContextClient(CContextClient* readContextclient)
994   {
995     read_client = readContextclient;
996   }
997
998   CContextClient* CFile::getReadContextClient()
999   {
1000     return read_client;
1001   }
1002
1003   /*!
1004   \brief Send a message to create a field on server side
1005   \param[in] id String identity of field that will be created on server
1006   */
1007   void CFile::sendAddField(const string& id, CContextClient* client)
1008   {
1009      sendAddItem(id, EVENT_ID_ADD_FIELD, client);
1010   }
1011
1012   /*!
1013   \brief Send a message to create a field group on server side
1014   \param[in] id String identity of field group that will be created on server
1015   */
1016   void CFile::sendAddFieldGroup(const string& id, CContextClient* client)
1017   {
1018      sendAddItem(id, (int)EVENT_ID_ADD_FIELD_GROUP, client);
1019   }
1020
1021   /*!
1022   \brief Receive a message annoucing the creation of a field on server side
1023   \param[in] event Received event
1024   */
1025   void CFile::recvAddField(CEventServer& event)
1026   {
1027
1028      CBufferIn* buffer = event.subEvents.begin()->buffer;
1029      string id;
1030      *buffer>>id;
1031      get(id)->recvAddField(*buffer);
1032   }
1033
1034   /*!
1035   \brief Receive a message annoucing the creation of a field on server side
1036   \param[in] buffer Buffer containing message
1037   */
1038   void CFile::recvAddField(CBufferIn& buffer)
1039   {
1040      string id;
1041      buffer>>id;
1042      addField(id);
1043   }
1044
1045   /*!
1046   \brief Receive a message annoucing the creation of a field group on server side
1047   \param[in] event Received event
1048   */
1049   void CFile::recvAddFieldGroup(CEventServer& event)
1050   {
1051
1052      CBufferIn* buffer = event.subEvents.begin()->buffer;
1053      string id;
1054      *buffer>>id;
1055      get(id)->recvAddFieldGroup(*buffer);
1056   }
1057
1058   /*!
1059   \brief Receive a message annoucing the creation of a field group on server side
1060   \param[in] buffer Buffer containing message
1061   */
1062   void CFile::recvAddFieldGroup(CBufferIn& buffer)
1063   {
1064      string id;
1065      buffer>>id;
1066      addFieldGroup(id);
1067   }
1068
1069   /*!
1070   \brief Send messages to duplicate all variables on server side
1071      Because each variable has also its attributes. So first thing to do is replicate
1072   all these attributes on server side. Because variable can have a value, the second thing
1073   is to duplicate this value on server, too.
1074   */
1075   void CFile::sendAddAllVariables(CContextClient* client)
1076   {
1077     std::vector<CVariable*> allVar = getAllVariables();
1078     std::vector<CVariable*>::const_iterator it = allVar.begin();
1079     std::vector<CVariable*>::const_iterator itE = allVar.end();
1080
1081     for (; it != itE; ++it)
1082     {
1083       this->sendAddVariable((*it)->getId(), client);
1084       (*it)->sendAllAttributesToServer(client);
1085       (*it)->sendValue(client);
1086     }
1087   }
1088
1089   /*!
1090   \brief Send a message to create a variable group on server side
1091   \param[in] id String identity of variable group that will be created on server
1092   \param [in] client client to which we will send this adding action
1093   */
1094   void CFile::sendAddVariableGroup(const string& id, CContextClient* client)
1095   {
1096      sendAddItem(id, (int)EVENT_ID_ADD_VARIABLE_GROUP, client);
1097   }
1098
1099   /*
1100     Send message to add a variable into a file within a certain client
1101     \param [in] id String identity of a variable
1102     \param [in] client client to which we will send this adding action
1103   */
1104   void CFile::sendAddVariable(const string& id, CContextClient* client)
1105   {
1106      sendAddItem(id, (int)EVENT_ID_ADD_VARIABLE, client);
1107   }
1108
1109   /*!
1110   \brief Receive a message annoucing the creation of a variable on server side
1111   \param[in] event Received event
1112   */
1113   void CFile::recvAddVariable(CEventServer& event)
1114   {
1115
1116      CBufferIn* buffer = event.subEvents.begin()->buffer;
1117      string id;
1118      *buffer>>id;
1119      get(id)->recvAddVariable(*buffer);
1120   }
1121
1122   /*!
1123   \brief Receive a message annoucing the creation of a variable on server side
1124   \param[in] buffer Buffer containing message
1125   */
1126   void CFile::recvAddVariable(CBufferIn& buffer)
1127   {
1128      string id;
1129      buffer>>id;
1130      addVariable(id);
1131   }
1132
1133   /*!
1134   \brief Receive a message annoucing the creation of a variable group on server side
1135   \param[in] event Received event
1136   */
1137   void CFile::recvAddVariableGroup(CEventServer& event)
1138   {
1139
1140      CBufferIn* buffer = event.subEvents.begin()->buffer;
1141      string id;
1142      *buffer>>id;
1143      get(id)->recvAddVariableGroup(*buffer);
1144   }
1145
1146   /*!
1147   \brief Receive a message annoucing the creation of a variable group on server side
1148   \param[in] buffer Buffer containing message
1149   */
1150   void CFile::recvAddVariableGroup(CBufferIn& buffer)
1151   {
1152      string id;
1153      buffer>>id;
1154      addVariableGroup(id);
1155   }
1156
1157   /*!
1158     \brief Sending all active (enabled) fields from client to server.
1159   Each field is identified uniquely by its string identity. Not only should we
1160   send the id to server but also we need to send ids of reference domain and reference axis.
1161   With these two id, it's easier to make reference to grid where all data should be written.
1162   Remark: This function must be called AFTER all active (enabled) files have been created on the server side
1163   */
1164   void CFile::sendEnabledFields(CContextClient* client)
1165   {
1166     size_t size = this->enabledFields.size();
1167     for (size_t i = 0; i < size; ++i)
1168     {
1169       CField* field = this->enabledFields[i];
1170       this->sendAddField(field->getId(), client);
1171       field->checkTimeAttributes();
1172       field->sendAllAttributesToServer(client);
1173       field->sendAddAllVariables(client);
1174     }
1175   }
1176
1177
1178   /*!
1179   \brief Dispatch event received from client
1180      Whenever a message is received in buffer of server, it will be processed depending on
1181   its event type. A new event type should be added in the switch list to make sure
1182   it processed on server side.
1183   \param [in] event: Received message
1184   */
1185   bool CFile::dispatchEvent(CEventServer& event)
1186   {
1187      if (SuperClass::dispatchEvent(event)) return true;
1188      else
1189      {
1190        switch(event.type)
1191        {
1192           case EVENT_ID_ADD_FIELD :
1193             recvAddField(event);
1194             return true;
1195             break;
1196
1197           case EVENT_ID_ADD_FIELD_GROUP :
1198             recvAddFieldGroup(event);
1199             return true;
1200             break;
1201
1202            case EVENT_ID_ADD_VARIABLE :
1203             recvAddVariable(event);
1204             return true;
1205             break;
1206
1207           case EVENT_ID_ADD_VARIABLE_GROUP :
1208             recvAddVariableGroup(event);
1209             return true;
1210             break;
1211           default :
1212              ERROR("bool CFile::dispatchEvent(CEventServer& event)", << "Unknown Event");
1213           return false;
1214        }
1215      }
1216   }
1217
1218
1219
1220
1221   ///---------------------------------------------------------------
1222
1223} // namespace xios
Note: See TracBrowser for help on using the repository browser.