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

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

Bigfix for UGRID: fields describing the same mesh (= fields defined on domains with the same name) are sorted in the decreasing order of nvertex. The first, and the only, domain to be written is that with the highest value of nvertex. The entire mesh connectivity will be generated according to this domain.

  • 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: 41.8 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        if (!useCFConvention) sortEnabledFieldsForUgrid();
552
553        // Do not recreate the file structure if opening an existing file
554        if (!data_out->IsInAppendMode())
555        {
556          std::vector<CField*>::iterator it, end = this->enabledFields.end();
557          for (it = this->enabledFields.begin(); it != end; it++)
558          {
559            CField* field = *it;
560            this->data_out->writeFieldGrid(field);
561          }
562          this->data_out->writeTimeDimension();
563
564          for (it = this->enabledFields.begin(); it != end; it++)
565          {
566            CField* field = *it;
567            this->data_out->writeFieldTimeAxis(field);
568          }
569         
570          for (it = this->enabledFields.begin(); it != end; it++)
571          {
572            CField* field = *it;
573            this->data_out->writeField(field);
574          }
575
576          vector<CVariable*> listVars = getAllVariables();
577          for (vector<CVariable*>::iterator it = listVars.begin(); it != listVars.end(); it++)
578            this->data_out->writeAttribute(*it);
579
580          this->data_out->definition_end();
581        }
582        else
583        {
584          // check time axis even in append mode
585          std::vector<CField*>::iterator it, end = this->enabledFields.end();
586          for (it = this->enabledFields.begin(); it != end; it++)
587          {
588            CField* field = *it;
589            this->data_out->writeFieldTimeAxis(field);
590          }
591        }
592      }
593   }
594
595  /*!
596  \brief Open an existing NetCDF file in read-only mode
597  */
598  void CFile::openInReadMode()
599  {
600    CContext* context = CContext::getCurrent();
601    CContextServer* server = context->server;
602    MPI_Comm readComm = this->fileComm;
603
604    if (!allZoneEmpty)
605    {
606      StdString filename = getFileOutputName();
607      StdOStringStream oss;
608      oss << filename;
609
610      if (!split_freq.isEmpty())
611      {
612        string splitFormat;
613        if (split_freq_format.isEmpty())
614        {
615          if (split_freq.getValue().second != 0) splitFormat = "%y%mo%d%h%mi%s";
616          else if (split_freq.getValue().minute != 0) splitFormat = "%y%mo%d%h%mi";
617          else if (split_freq.getValue().hour != 0) splitFormat = "%y%mo%d%h";
618          else if (split_freq.getValue().day != 0) splitFormat = "%y%mo%d";
619          else if (split_freq.getValue().month != 0) splitFormat = "%y%mo";
620          else splitFormat = "%y";
621        }
622        else splitFormat = split_freq_format;
623        oss << "_" << lastSplit.getStr(splitFormat)
624        << "-" << (lastSplit + split_freq.getValue() - 1 * Second).getStr(splitFormat);
625      }
626
627      bool multifile = true;
628      if (!type.isEmpty())
629      {
630        if (type == type_attr::one_file) multifile = false;
631        else if (type == type_attr::multiple_file) multifile = true;
632      }
633  #ifndef USING_NETCDF_PAR
634      if (!multifile)
635      {
636        info(0) << "!!! Warning -> Using non parallel version of netcdf, switching in multiple_file mode for file : " << filename << " ..." << endl;
637        multifile = true;
638      }
639  #endif
640      if (multifile)
641      {
642        int commSize, commRank;
643        MPI_Comm_size(readComm, &commSize);
644        MPI_Comm_rank(readComm, &commRank);
645
646        if (server->intraCommSize > 1)
647        {
648          oss << "_";
649          int width = 0, n = commSize - 1;
650          while (n != 0) { n = n / 10; width++; }
651          if (!min_digits.isEmpty() && width < min_digits)
652            width = min_digits;
653          oss.width(width);
654          oss.fill('0');
655          oss << right << commRank;
656        }
657      }
658      oss << ".nc";
659
660      bool isCollective = par_access.isEmpty() || par_access == par_access_attr::collective;
661      bool readMetaDataPar = true;
662      if (!context->hasServer) readMetaDataPar = (read_metadata_par.isEmpty()) ? false : read_metadata_par;
663
664      if (isOpen) data_out->closeFile();
665      bool ugridConvention = !convention.isEmpty() ? (convention == convention_attr::UGRID) : false;
666      if (time_counter_name.isEmpty())
667        data_in = shared_ptr<CDataInput>(new CNc4DataInput(oss.str(), readComm, multifile, isCollective, readMetaDataPar, ugridConvention));
668      else
669        data_in = shared_ptr<CDataInput>(new CNc4DataInput(oss.str(), readComm, multifile, isCollective, readMetaDataPar, ugridConvention, time_counter_name));
670      isOpen = true;
671    }
672  }
673
674   //! Close file
675   void CFile::close(void)
676   {
677     if (!allZoneEmpty)
678       if (isOpen)
679       {
680         if (mode.isEmpty() || mode.getValue() == mode_attr::write)
681          this->data_out->closeFile();
682         else
683          this->data_in->closeFile();
684        isOpen = false;
685       }
686      if (fileComm != MPI_COMM_NULL) MPI_Comm_free(&fileComm);
687   }
688   //----------------------------------------------------------------
689
690   void CFile::readAttributesOfEnabledFieldsInReadMode()
691   {
692     if (enabledFields.empty()) return;
693
694     // Just check file and try to open it
695     if (time_counter_name.isEmpty()) time_counter_name = "time_counter";
696
697     checkReadFile();
698
699     for (int idx = 0; idx < enabledFields.size(); ++idx)
700     {
701        // First of all, find out which domain and axis associated with this field
702        enabledFields[idx]->solveGridReference();
703
704        // Read attributes of domain and axis from this file
705        this->data_in->readFieldAttributesMetaData(enabledFields[idx]);
706
707        // Now complete domain and axis associated with this field
708        enabledFields[idx]->solveGenerateGrid();
709
710        // Read necessary value from file
711        this->data_in->readFieldAttributesValues(enabledFields[idx]);
712
713        // Fill attributes for base reference
714        enabledFields[idx]->solveGridDomainAxisBaseRef();
715     }
716
717     // Now everything is ok, close it
718     close();
719   }
720
721
722   /*!
723   \brief Parse xml file and write information into file object
724   \param [in] node xmld node corresponding in xml file
725   */
726   void CFile::parse(xml::CXMLNode & node)
727   {
728      SuperClass::parse(node);
729
730      if (node.goToChildElement())
731      {
732        do
733        {
734           if (node.getElementName()=="field" || node.getElementName()=="field_group") this->getVirtualFieldGroup()->parseChild(node);
735           else if (node.getElementName()=="variable" || node.getElementName()=="variable_group") this->getVirtualVariableGroup()->parseChild(node);
736        } while (node.goToNextElement());
737        node.goToParentElement();
738      }
739
740   }
741   //----------------------------------------------------------------
742
743   /*!
744   \brief Represent a file in form of string with all its info
745   \return String
746   */
747   StdString CFile::toString(void) const
748   {
749      StdOStringStream oss;
750
751      oss << "<" << CFile::GetName() << " ";
752      if (this->hasId())
753         oss << " id=\"" << this->getId() << "\" ";
754      oss << SuperClassAttribute::toString() << ">" << std::endl;
755      if (this->getVirtualFieldGroup() != NULL)
756         oss << *this->getVirtualFieldGroup() << std::endl;
757      oss << "</" << CFile::GetName() << " >";
758      return (oss.str());
759   }
760
761   //----------------------------------------------------------------
762
763   /*!
764   \brief Find all inheritace among objects in a file.
765   \param [in] apply (true) write attributes of parent into ones of child if they are empty
766                     (false) write attributes of parent into a new container of child
767   \param [in] parent
768   */
769   void CFile::solveDescInheritance(bool apply, const CAttributeMap * const parent)
770   {
771      SuperClassAttribute::setAttributes(parent,apply);
772      this->getVirtualFieldGroup()->solveDescInheritance(apply, NULL);
773      this->getVirtualVariableGroup()->solveDescInheritance(apply, NULL);
774   }
775
776   //----------------------------------------------------------------
777
778   /*!
779   \brief Resolve all reference of active fields.
780      In order to know exactly which data each active field has, a search for all its
781   reference to find its parents or/and its base reference object must be done. Moreover
782   during this search, there are some information that can only be sent to server AFTER
783   all information of active fields are created on server side, e.g: checking mask or index
784   \param [in] sendToServer: Send all info to server (true) or only a part of it (false)
785   */
786   void CFile::solveOnlyRefOfEnabledFields(bool sendToServer)
787   {
788     int size = this->enabledFields.size();
789     for (int i = 0; i < size; ++i)
790     {
791       this->enabledFields[i]->solveOnlyReferenceEnabledField(sendToServer);
792     }
793   }
794
795   void CFile::checkGridOfEnabledFields()
796   { 
797     int size = this->enabledFields.size();
798     for (int i = 0; i < size; ++i)
799     {
800       this->enabledFields[i]->checkGridOfEnabledFields();
801     }
802   }
803
804   void CFile::sendGridComponentOfEnabledFields()
805   { 
806     int size = this->enabledFields.size();
807     for (int i = 0; i < size; ++i)
808     {
809       this->enabledFields[i]->sendGridComponentOfEnabledFields();
810     }
811   }
812
813   /*!
814   \brief Sorting domains with the same name (= describing the same mesh) in the decreasing order of nvertex for UGRID files.
815   This insures that the domain with the highest nvertex is written first and thus all known mesh connectivity is generated at once by this domain.
816   */
817   void CFile::sortEnabledFieldsForUgrid()
818   {
819     int size = this->enabledFields.size();
820     std::vector<int> domainNvertices;
821     std::vector<StdString> domainNames;
822
823     for (int i = 0; i < size; ++i)
824     {
825       std::vector<CDomain*> domain = this->enabledFields[i]->getRelGrid()->getDomains();
826       if (domain.size() != 1)
827       {
828         ERROR("void CFile::sortEnabledFieldsForUgrid()",
829               "A domain, and only one, should be defined for grid "<< this->enabledFields[i]->getRelGrid()->getId() << ".");
830       }
831       StdString domainName = domain[0]->getDomainOutputName();
832       int nvertex;
833       if (domain[0]->nvertex.isEmpty())
834       {
835         ERROR("void CFile::sortEnabledFieldsForUgrid()",
836               "Attributes nvertex must be defined for domain "<< domain[0]->getDomainOutputName() << ".");
837       }
838       else
839         nvertex = domain[0]->nvertex;
840
841       for (int j = 0; j < i; ++j)
842       {
843         if (domainName == domainNames[j] && nvertex > domainNvertices[j])
844         {
845           CField* tmpSwap = this->enabledFields[j];
846           this->enabledFields[j] = this->enabledFields[i];
847           this->enabledFields[i] = tmpSwap;
848           domainNames.push_back(domainNames[j]);
849           domainNames[j] = domainName;
850           domainNvertices.push_back(domainNvertices[j]);
851           domainNvertices[j] = nvertex;
852         }
853         else
854         {
855           domainNames.push_back(domainName);
856           domainNvertices.push_back(nvertex);
857         }
858       }
859       if (i==0)
860       {
861         domainNames.push_back(domainName);
862         domainNvertices.push_back(nvertex);
863       }
864     }
865   }
866
867   void CFile::sendGridOfEnabledFields()
868   { 
869     int size = this->enabledFields.size();
870     for (int i = 0; i < size; ++i)
871     {
872       this->enabledFields[i]->sendGridOfEnabledFields();
873     }
874   }
875
876   void CFile::generateNewTransformationGridDest()
877   {
878     int size = this->enabledFields.size();
879     for (int i = 0; i < size; ++i)
880     {
881       this->enabledFields[i]->generateNewTransformationGridDest();
882     }
883   }
884
885   /*!
886   \brief Resolve all reference of active fields.
887      In order to know exactly which data each active field has, a search for all its
888   reference to find its parents or/and its base reference object must be done. Moreover
889   during this search, there are some information that can only be sent to server AFTER
890   all information of active fields are created on server side, e.g: checking mask or index
891   \param [in] sendToServer: Send all info to server (true) or only a part of it (false)
892   */
893   void CFile::solveAllRefOfEnabledFieldsAndTransform(bool sendToServer)
894   {
895     int size = this->enabledFields.size();
896     for (int i = 0; i < size; ++i)
897     {       
898      this->enabledFields[i]->solveAllEnabledFieldsAndTransform();
899     }
900   }
901
902   /*!
903    * Constructs the filter graph for each active field.
904    *
905    * \param gc the garbage collector to use when building the filter graph
906    */
907   void CFile::buildFilterGraphOfEnabledFields(CGarbageCollector& gc)
908   {
909     int size = this->enabledFields.size();
910     for (int i = 0; i < size; ++i)
911     {
912       this->enabledFields[i]->buildFilterGraph(gc, true);
913     }
914   }
915
916   /*!
917    * Post-process the filter graph for each active field.
918    */
919   void CFile::postProcessFilterGraph()
920   {
921     int size = this->enabledFields.size();
922     for (int i = 0; i < size; ++i)
923     {
924       this->enabledFields[i]->checkIfMustAutoTrigger();
925     }
926   }
927
928   /*!
929     Prefetching the data for enabled fields read from file.
930   */
931   void CFile::prefetchEnabledReadModeFields(void)
932   {
933     if (mode.isEmpty() || mode.getValue() != mode_attr::read)
934       return;
935
936     int size = this->enabledFields.size();
937     for (int i = 0; i < size; ++i)
938       this->enabledFields[i]->sendReadDataRequest(CContext::getCurrent()->getCalendar()->getCurrentDate());
939   }
940
941   /*!
942     Do all pre timestep operations for enabled fields in read mode:
943      - Check that the data excepted from server has been received
944      - Check if some filters must auto-trigger
945   */
946   void CFile::doPreTimestepOperationsForEnabledReadModeFields(void)
947   {
948     if (mode.isEmpty() || mode.getValue() != mode_attr::read)
949       return;
950
951     int size = this->enabledFields.size();
952     for (int i = 0; i < size; ++i)
953     {
954       this->enabledFields[i]->checkForLateDataFromServer();
955       this->enabledFields[i]->autoTriggerIfNeeded();
956     }
957   }
958
959   /*!
960     Do all post timestep operations for enabled fields in read mode:
961      - Prefetch the data read from file when needed
962   */
963   void CFile::doPostTimestepOperationsForEnabledReadModeFields(void)
964   {
965     if (mode.isEmpty() || mode.getValue() != mode_attr::read)
966       return;
967
968     int size = this->enabledFields.size();
969     for (int i = 0; i < size; ++i)
970     {
971       this->enabledFields[i]->sendReadDataRequestIfNeeded();
972     }
973   }
974
975   void CFile::solveFieldRefInheritance(bool apply)
976   {
977      // Rsolution des hritages par rfrence de chacun des champs contenus dans le fichier.
978      std::vector<CField*> allF = this->getAllFields();
979      for (unsigned int i = 0; i < allF.size(); i++)
980         allF[i]->solveRefInheritance(apply);
981   }
982
983   //----------------------------------------------------------------
984
985   /*!
986   \brief Add a field into file.
987      A field is added into file and it will be written out if the file is enabled and
988   level of this field is smaller than level_output. A new field won't be created if one
989   with id has already existed
990   \param [in] id String identity of new field
991   \return Pointer to added (or already existed) field
992   */
993   CField* CFile::addField(const string& id)
994   {
995     return vFieldGroup->createChild(id);
996   }
997
998   /*!
999   \brief Add a field group into file.
1000      A field group is added into file and it will play a role as parents for fields.
1001   A new field group won't be created if one with id has already existed
1002   \param [in] id String identity of new field group
1003   \return Pointer to added (or already existed) field group
1004   */
1005   CFieldGroup* CFile::addFieldGroup(const string& id)
1006   {
1007     return vFieldGroup->createChildGroup(id);
1008   }
1009
1010   /*!
1011   \brief Add a variable into file.
1012      A variable is added into file and if one with id has already existed, pointer to
1013   it will be returned.
1014      Variable as long as attributes are information container of file.
1015   However, whereas attributes are "fixed" information, variables provides a more flexible way to user
1016   to fill in (extra) information for a file.
1017   \param [in] id String identity of new variable
1018   \return Pointer to added (or already existed) variable
1019   */
1020   CVariable* CFile::addVariable(const string& id)
1021   {
1022     return vVariableGroup->createChild(id);
1023   }
1024
1025   /*!
1026   \brief Add a variable group into file.
1027      A variable group is added into file and it will play a role as parents for variables.
1028   A new variable group won't be created if one with id has already existed
1029   \param [in] id String identity of new variable group
1030   \return Pointer to added (or already existed) variable group
1031   */
1032   CVariableGroup* CFile::addVariableGroup(const string& id)
1033   {
1034     return vVariableGroup->createChildGroup(id);
1035   }
1036
1037   void CFile::setContextClient(CContextClient* newContextClient)
1038   {
1039     client = newContextClient;
1040     size_t size = this->enabledFields.size();
1041     for (size_t i = 0; i < size; ++i)
1042     {
1043       this->enabledFields[i]->setContextClient(newContextClient);
1044     }
1045   }
1046
1047   CContextClient* CFile::getContextClient()
1048   {
1049     return client;
1050   }
1051
1052   void CFile::setReadContextClient(CContextClient* readContextclient)
1053   {
1054     read_client = readContextclient;
1055   }
1056
1057   CContextClient* CFile::getReadContextClient()
1058   {
1059     return read_client;
1060   }
1061
1062   /*!
1063   \brief Send a message to create a field on server side
1064   \param[in] id String identity of field that will be created on server
1065   */
1066   void CFile::sendAddField(const string& id, CContextClient* client)
1067   {
1068      sendAddItem(id, EVENT_ID_ADD_FIELD, client);
1069   }
1070
1071   /*!
1072   \brief Send a message to create a field group on server side
1073   \param[in] id String identity of field group that will be created on server
1074   */
1075   void CFile::sendAddFieldGroup(const string& id, CContextClient* client)
1076   {
1077      sendAddItem(id, (int)EVENT_ID_ADD_FIELD_GROUP, client);
1078   }
1079
1080   /*!
1081   \brief Receive a message annoucing the creation of a field on server side
1082   \param[in] event Received event
1083   */
1084   void CFile::recvAddField(CEventServer& event)
1085   {
1086
1087      CBufferIn* buffer = event.subEvents.begin()->buffer;
1088      string id;
1089      *buffer>>id;
1090      get(id)->recvAddField(*buffer);
1091   }
1092
1093   /*!
1094   \brief Receive a message annoucing the creation of a field on server side
1095   \param[in] buffer Buffer containing message
1096   */
1097   void CFile::recvAddField(CBufferIn& buffer)
1098   {
1099      string id;
1100      buffer>>id;
1101      addField(id);
1102   }
1103
1104   /*!
1105   \brief Receive a message annoucing the creation of a field group on server side
1106   \param[in] event Received event
1107   */
1108   void CFile::recvAddFieldGroup(CEventServer& event)
1109   {
1110
1111      CBufferIn* buffer = event.subEvents.begin()->buffer;
1112      string id;
1113      *buffer>>id;
1114      get(id)->recvAddFieldGroup(*buffer);
1115   }
1116
1117   /*!
1118   \brief Receive a message annoucing the creation of a field group on server side
1119   \param[in] buffer Buffer containing message
1120   */
1121   void CFile::recvAddFieldGroup(CBufferIn& buffer)
1122   {
1123      string id;
1124      buffer>>id;
1125      addFieldGroup(id);
1126   }
1127
1128   /*!
1129   \brief Send messages to duplicate all variables on server side
1130      Because each variable has also its attributes. So first thing to do is replicate
1131   all these attributes on server side. Because variable can have a value, the second thing
1132   is to duplicate this value on server, too.
1133   */
1134   void CFile::sendAddAllVariables(CContextClient* client)
1135   {
1136     std::vector<CVariable*> allVar = getAllVariables();
1137     std::vector<CVariable*>::const_iterator it = allVar.begin();
1138     std::vector<CVariable*>::const_iterator itE = allVar.end();
1139
1140     for (; it != itE; ++it)
1141     {
1142       this->sendAddVariable((*it)->getId(), client);
1143       (*it)->sendAllAttributesToServer(client);
1144       (*it)->sendValue(client);
1145     }
1146   }
1147
1148   /*!
1149   \brief Send a message to create a variable group on server side
1150   \param[in] id String identity of variable group that will be created on server
1151   \param [in] client client to which we will send this adding action
1152   */
1153   void CFile::sendAddVariableGroup(const string& id, CContextClient* client)
1154   {
1155      sendAddItem(id, (int)EVENT_ID_ADD_VARIABLE_GROUP, client);
1156   }
1157
1158   /*
1159     Send message to add a variable into a file within a certain client
1160     \param [in] id String identity of a variable
1161     \param [in] client client to which we will send this adding action
1162   */
1163   void CFile::sendAddVariable(const string& id, CContextClient* client)
1164   {
1165      sendAddItem(id, (int)EVENT_ID_ADD_VARIABLE, client);
1166   }
1167
1168   /*!
1169   \brief Receive a message annoucing the creation of a variable on server side
1170   \param[in] event Received event
1171   */
1172   void CFile::recvAddVariable(CEventServer& event)
1173   {
1174
1175      CBufferIn* buffer = event.subEvents.begin()->buffer;
1176      string id;
1177      *buffer>>id;
1178      get(id)->recvAddVariable(*buffer);
1179   }
1180
1181   /*!
1182   \brief Receive a message annoucing the creation of a variable on server side
1183   \param[in] buffer Buffer containing message
1184   */
1185   void CFile::recvAddVariable(CBufferIn& buffer)
1186   {
1187      string id;
1188      buffer>>id;
1189      addVariable(id);
1190   }
1191
1192   /*!
1193   \brief Receive a message annoucing the creation of a variable group on server side
1194   \param[in] event Received event
1195   */
1196   void CFile::recvAddVariableGroup(CEventServer& event)
1197   {
1198
1199      CBufferIn* buffer = event.subEvents.begin()->buffer;
1200      string id;
1201      *buffer>>id;
1202      get(id)->recvAddVariableGroup(*buffer);
1203   }
1204
1205   /*!
1206   \brief Receive a message annoucing the creation of a variable group on server side
1207   \param[in] buffer Buffer containing message
1208   */
1209   void CFile::recvAddVariableGroup(CBufferIn& buffer)
1210   {
1211      string id;
1212      buffer>>id;
1213      addVariableGroup(id);
1214   }
1215
1216   /*!
1217     \brief Sending all active (enabled) fields from client to server.
1218   Each field is identified uniquely by its string identity. Not only should we
1219   send the id to server but also we need to send ids of reference domain and reference axis.
1220   With these two id, it's easier to make reference to grid where all data should be written.
1221   Remark: This function must be called AFTER all active (enabled) files have been created on the server side
1222   */
1223   void CFile::sendEnabledFields(CContextClient* client)
1224   {
1225     size_t size = this->enabledFields.size();
1226     for (size_t i = 0; i < size; ++i)
1227     {
1228       CField* field = this->enabledFields[i];
1229       this->sendAddField(field->getId(), client);
1230       field->checkTimeAttributes();
1231       field->sendAllAttributesToServer(client);
1232       field->sendAddAllVariables(client);
1233     }
1234   }
1235
1236
1237   /*!
1238   \brief Dispatch event received from client
1239      Whenever a message is received in buffer of server, it will be processed depending on
1240   its event type. A new event type should be added in the switch list to make sure
1241   it processed on server side.
1242   \param [in] event: Received message
1243   */
1244   bool CFile::dispatchEvent(CEventServer& event)
1245   {
1246      if (SuperClass::dispatchEvent(event)) return true;
1247      else
1248      {
1249        switch(event.type)
1250        {
1251           case EVENT_ID_ADD_FIELD :
1252             recvAddField(event);
1253             return true;
1254             break;
1255
1256           case EVENT_ID_ADD_FIELD_GROUP :
1257             recvAddFieldGroup(event);
1258             return true;
1259             break;
1260
1261            case EVENT_ID_ADD_VARIABLE :
1262             recvAddVariable(event);
1263             return true;
1264             break;
1265
1266           case EVENT_ID_ADD_VARIABLE_GROUP :
1267             recvAddVariableGroup(event);
1268             return true;
1269             break;
1270           default :
1271              ERROR("bool CFile::dispatchEvent(CEventServer& event)", << "Unknown Event");
1272           return false;
1273        }
1274      }
1275   }
1276
1277
1278
1279
1280   ///---------------------------------------------------------------
1281
1282} // namespace xios
Note: See TracBrowser for help on using the repository browser.