source: XIOS/trunk/src/node/file.cpp @ 1638

Last change on this file since 1638 was 1638, checked in by yushan, 5 years ago

dev on ADA

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