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

Last change on this file since 1278 was 1278, checked in by oabramkina, 4 years ago

Adding a check on field attributes. For now it only concerns freq_op and freq_offset.
It is done so to avoid changing its values on client after the attributes have already been sent to servers.

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