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

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