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

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

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