source: XIOS/dev/dev_ym/XIOS_COUPLING/src/node/file.cpp @ 1961

Last change on this file since 1961 was 1961, checked in by ymipsl, 4 years ago

Rewrite file writing filter.
YM

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