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

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

Some update on XIOS_COUPLING branch...

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.1 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      if (checkRead) return;
279      createSubComFile();
280      checkRead = true;
281    }
282    CATCH_DUMP_ATTR
283
284    /*!
285      Create a sub communicator in which processes participate in reading/opening file
286    */
287    void CFile::createSubComFile()
288    TRY
289    {
290      CContext* context = CContext::getCurrent();
291
292      // create sub communicator for file
293      allZoneEmpty = true;     
294      std::vector<CField*>::iterator it, end = this->enabledFields.end();
295      for (it = this->enabledFields.begin(); it != end; it++)
296      {
297         CField* field = *it;
298         bool nullGrid = (nullptr == field->getGrid());
299         allZoneEmpty &= nullGrid ? false : !field->getGrid()->doGridHaveDataToWrite();
300      }
301
302      int color = allZoneEmpty ? 0 : 1;
303      MPI_Comm_split(context->intraComm_, color, context->intraCommRank_, &fileComm);
304      if (allZoneEmpty) MPI_Comm_free(&fileComm);
305    }
306    CATCH_DUMP_ATTR
307
308    /*
309       Check condition to write into a file
310       For now, we only use the level-2 server to write files (if this mode is activated)
311       or classical server to do this job.
312    */
313    void CFile::checkWriteFile(void)
314    TRY
315    {
316      CContext* context = CContext::getCurrent();
317      // Done by classical server or secondary server
318      // This condition should be changed soon
319//ym      if (CServer::serverLevel == 0 || CServer::serverLevel == 2)
320       if (context->getServiceType()==CServicesManager::IO_SERVER || context->getServiceType()==CServicesManager::OUT_SERVER)
321       {
322        if (mode.isEmpty() || mode.getValue() == mode_attr::write)
323        {
324          CTimer::get("Files : create headers").resume();
325          if (!isOpen) createHeader();
326          CTimer::get("Files : create headers").suspend();
327          checkSync();
328        }       
329        checkSplit(); // REally need this?
330      }
331    }
332    CATCH_DUMP_ATTR
333
334    /*
335       Check condition to read from a file
336       For now, we only use the level-1 server to write files (if this mode is activated)
337       or classical server to do this job.
338       This function can be used by client for reading metadata
339    */
340    void CFile::checkReadFile(void)
341    TRY
342    {
343      CContext* context = CContext::getCurrent();
344      // Done by classical server or secondary server
345      // TODO: This condition should be changed soon. It only works with maximum number of level as 2
346
347//ym      if (CServer::serverLevel == 0 || CServer::serverLevel == 1)
348      if (context->getServiceType()==CServicesManager::IO_SERVER || context->getServiceType()==CServicesManager::GATHERER)
349      {
350        if (!mode.isEmpty() && mode.getValue() == mode_attr::read)
351        {
352          CTimer::get("Files : open headers").resume();
353         
354          if (!isOpen) openInReadMode();
355
356          CTimer::get("Files : open headers").suspend();
357        }
358        //checkSplit(); // Really need for reading?
359      }
360    }
361    CATCH_DUMP_ATTR
362
363    /*!
364      Verify if a process participates in an opening-file communicator
365      \return true if the process doesn't participate in opening file
366    */
367    bool CFile::isEmptyZone()
368    TRY
369    {
370      return allZoneEmpty;
371    }
372    CATCH_DUMP_ATTR
373
374    /*!
375    \brief Verify if synchronisation should be done
376        If syn option is enabled, syn frequence and current time will be used to
377    calculate the moment to syn file(s)
378    \return True if it is the moment to synchronize file, otherwise false
379    */
380   bool CFile::checkSync(void)
381   TRY
382   {
383     CContext* context = CContext::getCurrent();
384     const CDate& currentDate = context->calendar->getCurrentDate();
385     if (!sync_freq.isEmpty())
386     {
387       if (lastSync + sync_freq.getValue() <= currentDate)
388       {
389         lastSync = currentDate;
390         data_out->syncFile();
391         return true;
392        }
393      }
394      return false;
395    }
396   CATCH_DUMP_ATTR
397
398    /*!
399    \brief Verify if splitting should be done
400        If split option is enabled, split frequence and current time will be used to
401    calculate the moment to split file
402    \return True if it is the moment to split file, otherwise false
403    */
404    bool CFile::checkSplit(void)
405    TRY
406    {
407      CContext* context = CContext::getCurrent();
408      const CDate& currentDate = context->calendar->getCurrentDate();
409      if (!split_freq.isEmpty())
410      {
411        if (currentDate > lastSplit + split_freq.getValue())
412        {
413          lastSplit = lastSplit + split_freq.getValue();
414          std::vector<CField*>::iterator it, end = this->enabledFields.end();
415          for (it = this->enabledFields.begin(); it != end; it++)
416          {
417            (*it)->resetNStep();
418            (*it)->resetNStepMax();
419          }
420          if (mode.isEmpty() || mode.getValue() == mode_attr::write)
421            createHeader();
422          else
423            openInReadMode();
424          return true;
425        }
426      }
427      return false;
428    }
429    CATCH_DUMP_ATTR
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   /*!
759   \brief Parse xml file and write information into file object
760   \param [in] node xmld node corresponding in xml file
761   */
762   void CFile::parse(xml::CXMLNode & node)
763   TRY
764   {
765      SuperClass::parse(node);
766
767      if (node.goToChildElement())
768      {
769        do
770        {
771           if (node.getElementName()=="field" || node.getElementName()=="field_group") this->getVirtualFieldGroup()->parseChild(node);
772           else if (node.getElementName()=="variable" || node.getElementName()=="variable_group") this->getVirtualVariableGroup()->parseChild(node);
773        } while (node.goToNextElement());
774        node.goToParentElement();
775      }
776   }
777   CATCH_DUMP_ATTR
778
779   //----------------------------------------------------------------
780
781   /*!
782   \brief Represent a file in form of string with all its info
783   \return String
784   */
785   StdString CFile::toString(void) const
786   TRY
787   {
788      StdOStringStream oss;
789
790      oss << "<" << CFile::GetName() << " ";
791      if (this->hasId())
792         oss << " id=\"" << this->getId() << "\" ";
793      oss << SuperClassAttribute::toString() << ">" << std::endl;
794      if (this->getVirtualFieldGroup() != NULL)
795         oss << *this->getVirtualFieldGroup() << std::endl;
796      oss << "</" << CFile::GetName() << " >";
797      return (oss.str());
798   }
799   CATCH
800
801   //----------------------------------------------------------------
802
803   /*!
804   \brief Find all inheritace among objects in a file.
805   \param [in] apply (true) write attributes of parent into ones of child if they are empty
806                     (false) write attributes of parent into a new container of child
807   \param [in] parent
808   */
809   void CFile::solveDescInheritance(bool apply, const CAttributeMap * const parent)
810   TRY
811   {
812      SuperClassAttribute::setAttributes(parent,apply);
813      this->getVirtualFieldGroup()->solveDescInheritance(apply, NULL);
814      this->getVirtualVariableGroup()->solveDescInheritance(apply, NULL);
815   }
816   CATCH_DUMP_ATTR
817
818   //----------------------------------------------------------------
819
820   /*!
821   \brief Resolve all reference of active fields.
822      In order to know exactly which data each active field has, a search for all its
823   reference to find its parents or/and its base reference object must be done. Moreover
824   during this search, there are some information that can only be sent to server AFTER
825   all information of active fields are created on server side, e.g: checking mask or index
826   \param [in] sendToServer: Send all info to server (true) or only a part of it (false)
827   */
828   void CFile::solveOnlyRefOfEnabledFields(void)
829   TRY
830   {
831     int size = this->enabledFields.size();
832     for (int i = 0; i < size; ++i)
833     {
834       this->enabledFields[i]->solveOnlyReferenceEnabledField();
835     }
836   }
837   CATCH_DUMP_ATTR
838
839   void CFile::checkGridOfEnabledFields()
840   TRY
841   { 
842     int size = this->enabledFields.size();
843     for (int i = 0; i < size; ++i)
844     {
845       this->enabledFields[i]->checkGridOfEnabledFields();
846     }
847   }
848   CATCH_DUMP_ATTR
849
850   void CFile::sendGridComponentOfEnabledFields()
851   TRY
852   { 
853     int size = this->enabledFields.size();
854     for (int i = 0; i < size; ++i)
855     {
856       this->enabledFields[i]->sendGridComponentOfEnabledFields();
857     }
858   }
859   CATCH_DUMP_ATTR
860
861   /*!
862   \brief Sorting domains with the same name (= describing the same mesh) in the decreasing order of nvertex for UGRID files.
863   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.
864   */
865   void CFile::sortEnabledFieldsForUgrid()
866   TRY
867   {
868     int size = this->enabledFields.size();
869     std::vector<int> domainNvertices;
870     std::vector<StdString> domainNames;
871
872     for (int i = 0; i < size; ++i)
873     {
874       std::vector<CDomain*> domain = this->enabledFields[i]->getRelGrid()->getDomains();
875       if (domain.size() != 1)
876       {
877         ERROR("void CFile::sortEnabledFieldsForUgrid()",
878               "A domain, and only one, should be defined for grid "<< this->enabledFields[i]->getRelGrid()->getId() << ".");
879       }
880       StdString domainName = domain[0]->getDomainOutputName();
881       int nvertex;
882       if (domain[0]->nvertex.isEmpty())
883       {
884         ERROR("void CFile::sortEnabledFieldsForUgrid()",
885               "Attributes nvertex must be defined for domain "<< domain[0]->getDomainOutputName() << ".");
886       }
887       else
888         nvertex = domain[0]->nvertex;
889
890       for (int j = 0; j < i; ++j)
891       {
892         if (domainName == domainNames[j] && nvertex > domainNvertices[j])
893         {
894           CField* tmpSwap = this->enabledFields[j];
895           this->enabledFields[j] = this->enabledFields[i];
896           this->enabledFields[i] = tmpSwap;
897           domainNames.push_back(domainNames[j]);
898           domainNames[j] = domainName;
899           domainNvertices.push_back(domainNvertices[j]);
900           domainNvertices[j] = nvertex;
901         }
902         else
903         {
904           domainNames.push_back(domainName);
905           domainNvertices.push_back(nvertex);
906         }
907       }
908       if (i==0)
909       {
910         domainNames.push_back(domainName);
911         domainNvertices.push_back(nvertex);
912       }
913     }
914   }
915   CATCH_DUMP_ATTR
916
917   void CFile::sendGridOfEnabledFields()
918   TRY
919   { 
920     int size = this->enabledFields.size();
921     for (int i = 0; i < size; ++i)
922     {
923       this->enabledFields[i]->sendGridOfEnabledFields();
924     }
925   }
926   CATCH_DUMP_ATTR
927
928   void CFile::generateNewTransformationGridDest()
929   TRY
930   {
931     int size = this->enabledFields.size();
932     for (int i = 0; i < size; ++i)
933     {
934       this->enabledFields[i]->generateNewTransformationGridDest();
935     }
936   }
937   CATCH_DUMP_ATTR
938
939   /*!
940   \brief Resolve all reference of active fields.
941      In order to know exactly which data each active field has, a search for all its
942   reference to find its parents or/and its base reference object must be done. Moreover
943   during this search, there are some information that can only be sent to server AFTER
944   all information of active fields are created on server side, e.g: checking mask or index
945   \param [in] sendToServer: Send all info to server (true) or only a part of it (false)
946   */
947   void CFile::solveAllRefOfEnabledFieldsAndTransform(void)
948   TRY
949   {
950     int size = this->enabledFields.size();
951     for (int i = 0; i < size; ++i)
952     {       
953      this->enabledFields[i]->solveAllEnabledFieldsAndTransform();
954     }
955   }
956   CATCH_DUMP_ATTR
957
958   /*!
959    * Constructs the filter graph for each active field.
960    *
961    * \param gc the garbage collector to use when building the filter graph
962    */
963   void CFile::buildFilterGraphOfEnabledFields(CGarbageCollector& gc)
964   TRY
965   {
966     int size = this->enabledFields.size();
967     for (int i = 0; i < size; ++i)
968     {
969       this->enabledFields[i]->buildFilterGraph(gc, true);
970     }
971   }
972   CATCH_DUMP_ATTR
973
974   /*!
975    * Post-process the filter graph for each active field.
976    */
977   void CFile::postProcessFilterGraph()
978   TRY
979   {
980     int size = this->enabledFields.size();
981     for (int i = 0; i < size; ++i)
982     {
983       this->enabledFields[i]->checkIfMustAutoTrigger();
984     }
985   }
986   CATCH_DUMP_ATTR
987
988   /*!
989     Prefetching the data for enabled fields read from file.
990   */
991   void CFile::prefetchEnabledReadModeFields(void)
992   TRY
993   {
994     if (mode.isEmpty() || mode.getValue() != mode_attr::read)
995       return;
996
997     int size = this->enabledFields.size();
998     for (int i = 0; i < size; ++i)
999       this->enabledFields[i]->sendReadDataRequest(CContext::getCurrent()->getCalendar()->getCurrentDate(), getContextClient());
1000   }
1001   CATCH_DUMP_ATTR
1002
1003   /*!
1004     Do all pre timestep operations for enabled fields in read mode:
1005      - Check that the data excepted from server has been received
1006      - Check if some filters must auto-trigger
1007   */
1008   void CFile::doPreTimestepOperationsForEnabledReadModeFields(void)
1009   TRY
1010   {
1011     if (mode.isEmpty() || mode.getValue() != mode_attr::read)
1012       return;
1013
1014     int size = this->enabledFields.size();
1015     for (int i = 0; i < size; ++i)
1016     {
1017       this->enabledFields[i]->checkForLateDataFromServer();
1018       this->enabledFields[i]->autoTriggerIfNeeded();
1019     }
1020   }
1021   CATCH_DUMP_ATTR
1022
1023   /*!
1024     Do all post timestep operations for enabled fields in read mode:
1025      - Prefetch the data read from file when needed
1026   */
1027   void CFile::doPostTimestepOperationsForEnabledReadModeFields(void)
1028   TRY
1029   {
1030     if (mode.isEmpty() || mode.getValue() != mode_attr::read)
1031       return;
1032
1033     int size = this->enabledFields.size();
1034     for (int i = 0; i < size; ++i)
1035     {
1036       this->enabledFields[i]->sendReadDataRequestIfNeeded();
1037     }
1038   }
1039   CATCH_DUMP_ATTR
1040
1041   void CFile::solveFieldRefInheritance(bool apply)
1042   TRY
1043   {
1044      // Rsolution des hritages par rfrence de chacun des champs contenus dans le fichier.
1045      std::vector<CField*> allF = this->getAllFields();
1046      for (unsigned int i = 0; i < allF.size(); i++)
1047         allF[i]->solveRefInheritance(apply);
1048   }
1049   CATCH_DUMP_ATTR
1050
1051   //----------------------------------------------------------------
1052
1053   /*!
1054   \brief Add a field into file.
1055      A field is added into file and it will be written out if the file is enabled and
1056   level of this field is smaller than level_output. A new field won't be created if one
1057   with id has already existed
1058   \param [in] id String identity of new field
1059   \return Pointer to added (or already existed) field
1060   */
1061   CField* CFile::addField(const string& id)
1062   TRY
1063   {
1064     return vFieldGroup->createChild(id);
1065   }
1066   CATCH_DUMP_ATTR
1067
1068   /*!
1069   \brief Add a field group into file.
1070      A field group is added into file and it will play a role as parents for fields.
1071   A new field group won't be created if one with id has already existed
1072   \param [in] id String identity of new field group
1073   \return Pointer to added (or already existed) field group
1074   */
1075   CFieldGroup* CFile::addFieldGroup(const string& id)
1076   TRY
1077   {
1078     return vFieldGroup->createChildGroup(id);
1079   }
1080   CATCH_DUMP_ATTR
1081
1082   /*!
1083   \brief Add a variable into file.
1084      A variable is added into file and if one with id has already existed, pointer to
1085   it will be returned.
1086      Variable as long as attributes are information container of file.
1087   However, whereas attributes are "fixed" information, variables provides a more flexible way to user
1088   to fill in (extra) information for a file.
1089   \param [in] id String identity of new variable
1090   \return Pointer to added (or already existed) variable
1091   */
1092   CVariable* CFile::addVariable(const string& id)
1093   TRY
1094   {
1095     return vVariableGroup->createChild(id);
1096   }
1097   CATCH_DUMP_ATTR
1098
1099   /*!
1100   \brief Add a variable group into file.
1101      A variable group is added into file and it will play a role as parents for variables.
1102   A new variable group won't be created if one with id has already existed
1103   \param [in] id String identity of new variable group
1104   \return Pointer to added (or already existed) variable group
1105   */
1106   CVariableGroup* CFile::addVariableGroup(const string& id)
1107   TRY
1108   {
1109     return vVariableGroup->createChildGroup(id);
1110   }
1111   CATCH_DUMP_ATTR
1112
1113   void CFile::setContextClient(CContextClient* newContextClient)
1114   TRY
1115   {
1116     client = newContextClient;
1117     size_t size = this->enabledFields.size();
1118     for (size_t i = 0; i < size; ++i)
1119     {
1120       this->enabledFields[i]->setContextClient(newContextClient);
1121     }
1122   }
1123   CATCH_DUMP_ATTR
1124
1125   CContextClient* CFile::getContextClient()
1126   TRY
1127   {
1128     return client;
1129   }
1130   CATCH_DUMP_ATTR
1131
1132   void CFile::setReadContextClient(CContextClient* readContextclient)
1133   TRY
1134   {
1135     read_client = readContextclient;
1136   }
1137   CATCH_DUMP_ATTR
1138
1139   CContextClient* CFile::getReadContextClient()
1140   TRY
1141   {
1142     return read_client;
1143   }
1144   CATCH_DUMP_ATTR
1145
1146   /*!
1147   \brief Send a message to create a field on server side
1148   \param[in] id String identity of field that will be created on server
1149   */
1150   void CFile::sendAddField(const string& id, CContextClient* client)
1151   TRY
1152   {
1153      sendAddItem(id, EVENT_ID_ADD_FIELD, client);
1154   }
1155   CATCH_DUMP_ATTR
1156
1157   /*!
1158   \brief Send a message to create a field group on server side
1159   \param[in] id String identity of field group that will be created on server
1160   */
1161   void CFile::sendAddFieldGroup(const string& id, CContextClient* client)
1162   TRY
1163   {
1164      sendAddItem(id, (int)EVENT_ID_ADD_FIELD_GROUP, client);
1165   }
1166   CATCH_DUMP_ATTR
1167
1168   /*!
1169   \brief Receive a message annoucing the creation of a field on server side
1170   \param[in] event Received event
1171   */
1172   void CFile::recvAddField(CEventServer& event)
1173   TRY
1174   {
1175
1176      CBufferIn* buffer = event.subEvents.begin()->buffer;
1177      string id;
1178      *buffer>>id;
1179      get(id)->recvAddField(*buffer);
1180   }
1181   CATCH
1182
1183   /*!
1184   \brief Receive a message annoucing the creation of a field on server side
1185   \param[in] buffer Buffer containing message
1186   */
1187   void CFile::recvAddField(CBufferIn& buffer)
1188   TRY
1189   {
1190      string id;
1191      buffer>>id;
1192      addField(id);
1193   }
1194   CATCH_DUMP_ATTR
1195
1196   /*!
1197   \brief Receive a message annoucing the creation of a field group on server side
1198   \param[in] event Received event
1199   */
1200   void CFile::recvAddFieldGroup(CEventServer& event)
1201   TRY
1202   {
1203
1204      CBufferIn* buffer = event.subEvents.begin()->buffer;
1205      string id;
1206      *buffer>>id;
1207      get(id)->recvAddFieldGroup(*buffer);
1208   }
1209   CATCH
1210
1211   /*!
1212   \brief Receive a message annoucing the creation of a field group on server side
1213   \param[in] buffer Buffer containing message
1214   */
1215   void CFile::recvAddFieldGroup(CBufferIn& buffer)
1216   TRY
1217   {
1218      string id;
1219      buffer>>id;
1220      addFieldGroup(id);
1221   }
1222   CATCH_DUMP_ATTR
1223
1224   /*!
1225   \brief Send messages to duplicate all variables on server side
1226      Because each variable has also its attributes. So first thing to do is replicate
1227   all these attributes on server side. Because variable can have a value, the second thing
1228   is to duplicate this value on server, too.
1229   */
1230   void CFile::sendAddAllVariables(CContextClient* client)
1231   TRY
1232   {
1233     std::vector<CVariable*> allVar = getAllVariables();
1234     std::vector<CVariable*>::const_iterator it = allVar.begin();
1235     std::vector<CVariable*>::const_iterator itE = allVar.end();
1236
1237     for (; it != itE; ++it)
1238     {
1239       this->sendAddVariable((*it)->getId(), client);
1240       (*it)->sendAllAttributesToServer(client);
1241       (*it)->sendValue(client);
1242     }
1243   }
1244   CATCH_DUMP_ATTR
1245
1246   /*!
1247   \brief Send a message to create a variable group on server side
1248   \param[in] id String identity of variable group that will be created on server
1249   \param [in] client client to which we will send this adding action
1250   */
1251   void CFile::sendAddVariableGroup(const string& id, CContextClient* client)
1252   TRY
1253   {
1254      sendAddItem(id, (int)EVENT_ID_ADD_VARIABLE_GROUP, client);
1255   }
1256   CATCH_DUMP_ATTR
1257
1258   /*
1259     Send message to add a variable into a file within a certain client
1260     \param [in] id String identity of a variable
1261     \param [in] client client to which we will send this adding action
1262   */
1263   void CFile::sendAddVariable(const string& id, CContextClient* client)
1264   TRY
1265   {
1266      sendAddItem(id, (int)EVENT_ID_ADD_VARIABLE, client);
1267   }
1268   CATCH_DUMP_ATTR
1269
1270   /*!
1271   \brief Receive a message annoucing the creation of a variable on server side
1272   \param[in] event Received event
1273   */
1274   void CFile::recvAddVariable(CEventServer& event)
1275   TRY
1276   {
1277      CBufferIn* buffer = event.subEvents.begin()->buffer;
1278      string id;
1279      *buffer>>id;
1280      get(id)->recvAddVariable(*buffer);
1281   }
1282   CATCH
1283
1284   /*!
1285   \brief Receive a message annoucing the creation of a variable on server side
1286   \param[in] buffer Buffer containing message
1287   */
1288   void CFile::recvAddVariable(CBufferIn& buffer)
1289   TRY
1290   {
1291      string id;
1292      buffer>>id;
1293      addVariable(id);
1294   }
1295   CATCH_DUMP_ATTR
1296
1297   /*!
1298   \brief Receive a message annoucing the creation of a variable group on server side
1299   \param[in] event Received event
1300   */
1301   void CFile::recvAddVariableGroup(CEventServer& event)
1302   TRY
1303   {
1304
1305      CBufferIn* buffer = event.subEvents.begin()->buffer;
1306      string id;
1307      *buffer>>id;
1308      get(id)->recvAddVariableGroup(*buffer);
1309   }
1310   CATCH
1311
1312   /*!
1313   \brief Receive a message annoucing the creation of a variable group on server side
1314   \param[in] buffer Buffer containing message
1315   */
1316   void CFile::recvAddVariableGroup(CBufferIn& buffer)
1317   TRY
1318   {
1319      string id;
1320      buffer>>id;
1321      addVariableGroup(id);
1322   }
1323   CATCH_DUMP_ATTR
1324
1325   /*!
1326     \brief Sending all active (enabled) fields from client to server.
1327   Each field is identified uniquely by its string identity. Not only should we
1328   send the id to server but also we need to send ids of reference domain and reference axis.
1329   With these two id, it's easier to make reference to grid where all data should be written.
1330   Remark: This function must be called AFTER all active (enabled) files have been created on the server side
1331   */
1332   void CFile::sendEnabledFields(CContextClient* client)
1333   TRY
1334   {
1335     size_t size = this->enabledFields.size();
1336     for (size_t i = 0; i < size; ++i)
1337     {
1338       CField* field = this->enabledFields[i];
1339       this->sendAddField(field->getId(), client);
1340       field->checkTimeAttributes();
1341       field->sendAllAttributesToServer(client);
1342       field->sendAddAllVariables(client);
1343     }
1344   }
1345   CATCH_DUMP_ATTR
1346
1347
1348 
1349   /*!
1350    * Send file attribute, related variable and chield field tree to a given file server.
1351    * \param[in] client : the context client where to send file
1352    */
1353   void CFile::sendFileToFileServer(CContextClient* client)
1354   TRY
1355   {
1356     if (sendFileToFileServer_done_.count(client)!=0) return ;
1357     else sendFileToFileServer_done_.insert(client) ;
1358     
1359     StdString fileDefRoot("file_definition");
1360     CFileGroup* cfgrpPtr = CFileGroup::get(fileDefRoot);
1361     cfgrpPtr->sendCreateChild(this->getId(), client);
1362     this->sendAllAttributesToServer(client);
1363     this->sendAddAllVariables(client);
1364     for(auto field : enabledFields) this->sendAddField(field->getId(), client);
1365   }
1366   CATCH_DUMP_ATTR
1367   /*!
1368   \brief Dispatch event received from client
1369      Whenever a message is received in buffer of server, it will be processed depending on
1370   its event type. A new event type should be added in the switch list to make sure
1371   it processed on server side.
1372   \param [in] event: Received message
1373   */
1374   bool CFile::dispatchEvent(CEventServer& event)
1375   TRY
1376   {
1377      if (SuperClass::dispatchEvent(event)) return true;
1378      else
1379      {
1380        switch(event.type)
1381        {
1382           case EVENT_ID_ADD_FIELD :
1383             recvAddField(event);
1384             return true;
1385             break;
1386
1387           case EVENT_ID_ADD_FIELD_GROUP :
1388             recvAddFieldGroup(event);
1389             return true;
1390             break;
1391
1392            case EVENT_ID_ADD_VARIABLE :
1393             recvAddVariable(event);
1394             return true;
1395             break;
1396
1397           case EVENT_ID_ADD_VARIABLE_GROUP :
1398             recvAddVariableGroup(event);
1399             return true;
1400             break;
1401           default :
1402              ERROR("bool CFile::dispatchEvent(CEventServer& event)", << "Unknown Event");
1403           return false;
1404        }
1405      }
1406   }
1407   CATCH
1408
1409   ///--------------------------------------------------------------
1410   /*!
1411   */
1412   StdString CFile::dumpClassAttributes(void)
1413   {
1414     StdString str;
1415     CContext* context = CContext::getCurrent();
1416     str.append("context=\"");
1417     str.append(context->getId());
1418     str.append("\"");
1419     str.append(" enabled fields=\"");
1420     int size = this->enabledFields.size();
1421     for (int i = 0; i < size; ++i)
1422     {
1423       str.append(this->enabledFields[i]->getId());
1424       str.append(" ");
1425     }
1426     str.append("\"");
1427     return str;
1428   }
1429
1430   ///---------------------------------------------------------------
1431
1432} // namespace xios
Note: See TracBrowser for help on using the repository browser.