source: XIOS3/trunk/src/node/file.cpp @ 2400

Last change on this file since 2400 was 2400, checked in by jderouillat, 22 months ago

Fix for the multiple_file mode, cases with no data to write on some servers

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