source: XIOS/dev/dev_trunk_omp/src/node/file.cpp @ 1744

Last change on this file since 1744 was 1671, checked in by yushan, 5 years ago

MARK: branch merged with trunk @1663. One output graph file with output file names in file writer filter.

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