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

Last change on this file since 1784 was 1784, checked in by ymipsl, 23 months ago
  • Preparing coupling functionalities.
  • Make some cleaner things

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