source: XIOS3/branches/xios-3.0-beta/src/node/file.cpp

Last change on this file was 2417, checked in by jderouillat, 21 months ago

Backport commits [1977, 2181, 2200-2202, 2250, 2252] related to UGRID in XIOS3 beta

  • 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: 44.8 KB
Line 
1#include "file.hpp"
2
3#include "attribute_template.hpp"
4#include "object_template.hpp"
5#include "group_template.hpp"
6#include "object_factory.hpp"
7#include "context.hpp"
8#include "context_server.hpp"
9#include "nc4_data_output.hpp"
10#include "nc4_data_input.hpp"
11#include "calendar_util.hpp"
12#include "date.hpp"
13#include "message.hpp"
14#include "type.hpp"
15#include "xios_spl.hpp"
16#include "context_client.hpp"
17#include "mpi.hpp"
18#include "timer.hpp"
19#include "server.hpp"
20
21namespace xios {
22
23   /// ////////////////////// Dfinitions ////////////////////// ///
24
25   CFile::CFile(void)
26      : CObjectTemplate<CFile>(), CFileAttributes()
27      , vFieldGroup(), data_out(), enabledFields(), fileComm(MPI_COMM_NULL)
28      , isOpen(false), read_client(0), checkRead(false), allZoneEmpty(false)
29   {
30     setVirtualFieldGroup(CFieldGroup::create(getId() + "_virtual_field_group"));
31     setVirtualVariableGroup(CVariableGroup::create(getId() + "_virtual_variable_group"));
32   }
33
34   CFile::CFile(const StdString & id)
35      : CObjectTemplate<CFile>(id), CFileAttributes()
36      , vFieldGroup(), data_out(), enabledFields(), fileComm(MPI_COMM_NULL)
37      , isOpen(false), read_client(0), checkRead(false), allZoneEmpty(false)
38    {
39      setVirtualFieldGroup(CFieldGroup::create(getId() + "_virtual_field_group"));
40      setVirtualVariableGroup(CVariableGroup::create(getId() + "_virtual_variable_group"));
41    }
42
43   CFile::~CFile(void)
44   { /* Ne rien faire de plus */ }
45
46   ///---------------------------------------------------------------
47  //! Get name of file
48   StdString CFile::GetName(void)   { return (StdString("file")); }
49   StdString CFile::GetDefName(void){ return (CFile::GetName()); }
50   ENodeType CFile::GetType(void)   { return (eFile); }
51
52   //----------------------------------------------------------------
53
54   const StdString CFile::getFileOutputName(void) const
55   TRY
56   {
57     return (name.isEmpty() ? getId() : name) + (name_suffix.isEmpty() ? StdString("") :  name_suffix.getValue());
58   }
59   CATCH
60
61   //----------------------------------------------------------------
62   /*!
63   \brief Get data writer object.
64   Each enabled file in xml represents a physical netcdf file.
65   This function allows to access the data writer object.
66   \return data writer object.
67   */
68   std::shared_ptr<CDataOutput> CFile::getDataOutput(void) const
69   TRY
70   {
71      return data_out;
72   }
73   CATCH
74
75   /*!
76   \brief Get data reader object.
77   Each enabled file in xml represents a physical netcdf file.
78   This function allows to access the data reader object.
79   \return data reader object.
80   */
81   std::shared_ptr<CDataInput> CFile::getDataInput(void) const
82   TRY
83   {
84      return data_in;
85   }
86   CATCH
87
88   /*!
89   \brief Get virtual field group
90      In each file, there always exists a field group which is the ancestor of all
91   fields in the file. This is considered be virtual because it is created automatically during
92   file initialization and it normally doesn't appear on xml file
93   \return Pointer to field group
94   */
95   CFieldGroup* CFile::getVirtualFieldGroup(void) const
96   TRY
97   {
98      return (this->vFieldGroup);
99   }
100   CATCH
101
102   /*!
103   \brief Get virtual variable group
104      In each file, there always exists a variable group which is the ancestor of all
105   variable in the file. This is considered be virtual because it is created automatically during
106   file initialization and it normally doesn't appear on xml file
107   \return Pointer to variable group
108   */
109   CVariableGroup* CFile::getVirtualVariableGroup(void) const
110   TRY
111   {
112      return (this->vVariableGroup);
113   }
114   CATCH
115
116   //! Get all fields of a file
117   std::vector<CField*> CFile::getAllFields(void) const
118   TRY
119   {
120      return (this->vFieldGroup->getAllChildren());
121   }
122   CATCH
123
124   //! Get all variables of a file
125   std::vector<CVariable*> CFile::getAllVariables(void) const
126   TRY
127   {
128      return (this->vVariableGroup->getAllChildren());
129   }
130   CATCH
131
132   //----------------------------------------------------------------
133   /*!
134   \brief Get all enabled fields of file
135      A field is considered to be enabled if it fullfil these conditions: it is enabled, inside a enabled file
136   and its own level is not larger than file output level.
137   \param [in] default_outputlevel default value output level of file
138   \param [in] default_level default value level of field
139   \param [in] default_enabled flag determine by default if field is enabled
140   \return Vector of pointers of enabled fields
141   */
142   std::vector<CField*> CFile::getEnabledFields(int default_outputlevel,
143                                                int default_level,
144                                                bool default_enabled)
145   TRY
146   {
147      if (!this->enabledFields.empty()) return (this->enabledFields);
148
149      const int _outputlevel = (!output_level.isEmpty()) ? output_level.getValue() : default_outputlevel;
150      std::vector<CField*>::iterator it;
151      this->enabledFields = this->getAllFields();
152
153      std::vector<CField*> newEnabledFields;
154
155      for ( it = this->enabledFields.begin(); it != this->enabledFields.end(); it++ )
156      {
157         if (!(*it)->enabled.isEmpty()) // Si l'attribut 'enabled' est dfini ...
158         {
159            if (! (*it)->enabled.getValue()) continue;
160         }
161         else // Si l'attribut 'enabled' n'est pas dfini ...
162         {
163            if (!default_enabled) continue;
164         }
165
166         if (!(*it)->level.isEmpty()) // Si l'attribut 'level' est dfini ...
167         {
168            if ((*it)->level.getValue() > _outputlevel) continue;
169         }
170         else // Si l'attribut 'level' n'est pas dfini ...
171         {
172            if (default_level > _outputlevel) continue;
173         }
174
175         newEnabledFields.push_back(*it);
176      }
177      enabledFields = newEnabledFields;
178
179      return (this->enabledFields);
180   }
181   CATCH_DUMP_ATTR
182
183   //----------------------------------------------------------------
184   //! Change virtual field group to a new one
185   void CFile::setVirtualFieldGroup(CFieldGroup* newVFieldGroup)
186   TRY
187   {
188      this->vFieldGroup = newVFieldGroup;
189   }
190   CATCH_DUMP_ATTR
191
192   //! Change virtual variable group to new one
193   void CFile::setVirtualVariableGroup(CVariableGroup* newVVariableGroup)
194   TRY
195   {
196      this->vVariableGroup = newVVariableGroup;
197   }
198   CATCH_DUMP_ATTR
199
200   //----------------------------------------------------------------
201   bool CFile::isSyncTime(void)
202   TRY
203   {
204     CContext* context = CContext::getCurrent();
205     const CDate& currentDate = context->calendar->getCurrentDate();
206     if (!sync_freq.isEmpty())
207     {
208       if (lastSync + sync_freq.getValue() < currentDate)
209       {
210         lastSync = currentDate;
211         return true;
212        }
213      }
214      return false;
215    }
216    CATCH_DUMP_ATTR
217
218   //! Initialize a file in order to write into it
219   void CFile::initWrite(void)
220   TRY
221   {
222      CContext* context = CContext::getCurrent();
223      const CDate& currentDate = context->calendar->getCurrentDate();
224
225      lastSync  = currentDate;
226      lastSplit = currentDate;
227      if (!split_freq.isEmpty())
228      {
229        StdString keySuffix("CFile::"+getFileOutputName()+"::") ; 
230        if (context->registryIn->foundKey(keySuffix+"splitStart") && context->registryIn->foundKey(keySuffix+"splitEnd"))
231        {
232          CDate savedSplitStart(*context->getCalendar()), savedSplitEnd(*context->getCalendar());
233          context->registryIn->getKey(keySuffix+"splitStart", savedSplitStart);
234          context->registryIn->getKey(keySuffix+"splitEnd",   savedSplitEnd);
235
236          if (savedSplitStart <= lastSplit && lastSplit <= savedSplitEnd)
237            lastSplit = savedSplitStart;
238        }
239      }
240      isOpen = false;     
241
242      set<StdString> setAxis;
243      set<StdString> setDomains;
244
245      std::vector<CField*>::iterator it, end = this->enabledFields.end();
246      for (it = this->enabledFields.begin(); it != end; it++)
247      {
248         CField* field = *it;         
249         std::vector<CAxis*> vecAxis = field->getGrid()->getAxis();
250         for (size_t i = 0; i < vecAxis.size(); ++i)
251           setAxis.insert(vecAxis[i]->getAxisOutputName());
252         std::vector<CDomain*> vecDomains = field->getGrid()->getDomains();
253         for (size_t i = 0; i < vecDomains.size(); ++i)
254           setDomains.insert(vecDomains[i]->getDomainOutputName());
255      }
256      nbAxis = setAxis.size();
257      nbDomains = setDomains.size();
258
259      // create sub communicator for file
260      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     std::map<string, tuple<set<CDomain*>,set<CDomain*>,set<CDomain*>>> registeredDomains ;
866
867     for (int i = 0; i < size; ++i)
868     {
869       std::vector<CDomain*> domain = this->enabledFields[i]->getRelGrid()->getDomains();
870       if (domain.size() != 1)
871       {
872         ERROR("void CFile::sortEnabledFieldsForUgrid()",
873               "A domain, and only one, should be defined for grid "<< this->enabledFields[i]->getRelGrid()->getId() << ".");
874       }
875       StdString domainName = domain[0]->getDomainOutputName();
876       int nvertex;
877       if (domain[0]->nvertex.isEmpty())
878       {
879         ERROR("void CFile::sortEnabledFieldsForUgrid()",
880               "Attributes nvertex must be defined for domain "<< domain[0]->getDomainOutputName() << ".");
881       }
882       else
883         nvertex = domain[0]->nvertex;
884
885       for (int j = 0; j < i; ++j)
886       {
887         if (domainName == domainNames[j] && nvertex > domainNvertices[j])
888         {
889           CField* tmpSwap = this->enabledFields[j];
890           this->enabledFields[j] = this->enabledFields[i];
891           this->enabledFields[i] = tmpSwap;
892           domainNames.push_back(domainNames[j]);
893           domainNames[j] = domainName;
894           domainNvertices.push_back(domainNvertices[j]);
895           domainNvertices[j] = nvertex;
896         }
897         else
898         {
899           domainNames.push_back(domainName);
900           domainNvertices.push_back(nvertex);
901         }
902       }
903       if (i==0)
904       {
905         domainNames.push_back(domainName);
906         domainNvertices.push_back(nvertex);
907       }
908
909       if (nvertex==1)  std::get<0>(registeredDomains[domainName]).insert(domain[0]) ;
910       else if (nvertex==2) std::get<1>(registeredDomains[domainName]).insert(domain[0]) ;
911       else  std::get<2>(registeredDomains[domainName]).insert(domain[0]) ;
912     }
913
914     for(auto& it:registeredDomains)
915     {
916       list<CDomain*> domains ;
917       string domainName=it.first ;
918
919       for(auto& domain : std::get<0>(it.second) ) domains.push_back(domain) ;
920       for(auto& domain : std::get<1>(it.second) ) domains.push_back(domain) ;
921       for(auto& domain : std::get<2>(it.second) ) domains.push_back(domain) ;
922       
923       // for each component of a given mesh (i.e. domains with same name but different number of vertices)
924       // associate the UGRID mesh in increasing order
925       for(auto& domain : domains )
926       {
927         //domain-> computeWrittenIndex();
928         //CArray<int, 1>& indexToWrite = domain->localIndexToWriteOnServer;
929         CArray<int, 1> indexToWrite = domain->getLocalView(CElementView::WORKFLOW)->getIndex();
930         int nbWritten = indexToWrite.numElements();
931
932         CArray<double,1> writtenLat, writtenLon;
933         CArray<double,2> writtenBndsLat, writtenBndsLon;
934
935         writtenLat.resize(nbWritten);
936         writtenLon.resize(nbWritten);
937         for (int idx = 0; idx < nbWritten; ++idx)
938         {
939           writtenLat(idx) = domain->latvalue(indexToWrite(idx));
940           writtenLon(idx) = domain->lonvalue(indexToWrite(idx));
941         }
942   
943         int nvertex = domain->nvertex, idx;
944        if (nvertex>1)
945         {
946           writtenBndsLat.resize(nvertex, nbWritten);
947           writtenBndsLon.resize(nvertex, nbWritten);
948           CArray<double,2>& boundslat = domain->bounds_latvalue;
949           CArray<double,2>& boundslon = domain->bounds_lonvalue;
950           for (idx = 0; idx < nbWritten; ++idx)
951             for (int nv = 0; nv < nvertex; ++nv)
952             {
953                writtenBndsLat(nv, idx) = boundslat(nv, int(indexToWrite(idx)));
954                writtenBndsLon(nv, idx) = boundslon(nv, int(indexToWrite(idx)));
955             }
956         }
957         domain->assignMesh(domainName, domain->nvertex);
958         //CContextServer* server=CContext::getCurrent()->server ;
959         //domain->mesh->createMeshEpsilon(server->intraComm, writtenLon, writtenLat, writtenBndsLon, writtenBndsLat);
960         MPI_Comm intraComm =CContext::getCurrent()->getIntraComm() ;
961         domain->mesh->createMeshEpsilon(intraComm, writtenLon, writtenLat, writtenBndsLon, writtenBndsLat);
962       }
963
964     }
965   }
966   CATCH_DUMP_ATTR
967
968   void CFile::sendGridOfEnabledFields()
969   TRY
970   { 
971     int size = this->enabledFields.size();
972     for (int i = 0; i < size; ++i)
973     {
974       this->enabledFields[i]->sendGridOfEnabledFields();
975     }
976   }
977   CATCH_DUMP_ATTR
978
979
980   /*!
981    * Post-process the filter graph for each active field.
982    */
983   void CFile::postProcessFilterGraph()
984   TRY
985   {
986     int size = this->enabledFields.size();
987     for (int i = 0; i < size; ++i)
988     {
989       this->enabledFields[i]->checkIfMustAutoTrigger();
990     }
991   }
992   CATCH_DUMP_ATTR
993
994   /*!
995     Prefetching the data for enabled fields read from file.
996   */
997   void CFile::prefetchEnabledReadModeFields(void)
998   TRY
999   {
1000     if (mode.isEmpty() || mode.getValue() != mode_attr::read)
1001       return;
1002
1003     int size = this->enabledFields.size();
1004     for (int i = 0; i < size; ++i)
1005       this->enabledFields[i]->sendReadDataRequest(CContext::getCurrent()->getCalendar()->getCurrentDate());
1006   }
1007   CATCH_DUMP_ATTR
1008
1009   /*!
1010     Do all pre timestep operations for enabled fields in read mode:
1011      - Check that the data excepted from server has been received
1012      - Check if some filters must auto-trigger
1013   */
1014   void CFile::doPreTimestepOperationsForEnabledReadModeFields(void)
1015   TRY
1016   {
1017     if (mode.isEmpty() || mode.getValue() != mode_attr::read)
1018       return;
1019
1020     int size = this->enabledFields.size();
1021     for (int i = 0; i < size; ++i)
1022     {
1023       this->enabledFields[i]->checkForLateDataFromServer();
1024       this->enabledFields[i]->autoTriggerIfNeeded();
1025     }
1026   }
1027   CATCH_DUMP_ATTR
1028
1029   /*!
1030     Do all post timestep operations for enabled fields in read mode:
1031      - Prefetch the data read from file when needed
1032   */
1033   void CFile::doPostTimestepOperationsForEnabledReadModeFields(void)
1034   TRY
1035   {
1036     if (mode.isEmpty() || mode.getValue() != mode_attr::read)
1037       return;
1038
1039     int size = this->enabledFields.size();
1040     for (int i = 0; i < size; ++i)
1041     {
1042       this->enabledFields[i]->sendReadDataRequestIfNeeded();
1043     }
1044   }
1045   CATCH_DUMP_ATTR
1046
1047   void CFile::solveFieldRefInheritance(bool apply)
1048   TRY
1049   {
1050      // Rsolution des hritages par rfrence de chacun des champs contenus dans le fichier.
1051      std::vector<CField*> allF = this->getAllFields();
1052      for (unsigned int i = 0; i < allF.size(); i++)
1053         allF[i]->solveRefInheritance(apply);
1054   }
1055   CATCH_DUMP_ATTR
1056
1057   //----------------------------------------------------------------
1058
1059   /*!
1060   \brief Add a field into file.
1061      A field is added into file and it will be written out if the file is enabled and
1062   level of this field is smaller than level_output. A new field won't be created if one
1063   with id has already existed
1064   \param [in] id String identity of new field
1065   \return Pointer to added (or already existed) field
1066   */
1067   CField* CFile::addField(const string& id)
1068   TRY
1069   {
1070     return vFieldGroup->createChild(id);
1071   }
1072   CATCH_DUMP_ATTR
1073
1074   /*!
1075   \brief Add a field group into file.
1076      A field group is added into file and it will play a role as parents for fields.
1077   A new field group won't be created if one with id has already existed
1078   \param [in] id String identity of new field group
1079   \return Pointer to added (or already existed) field group
1080   */
1081   CFieldGroup* CFile::addFieldGroup(const string& id)
1082   TRY
1083   {
1084     return vFieldGroup->createChildGroup(id);
1085   }
1086   CATCH_DUMP_ATTR
1087
1088   /*!
1089   \brief Add a variable into file.
1090      A variable is added into file and if one with id has already existed, pointer to
1091   it will be returned.
1092      Variable as long as attributes are information container of file.
1093   However, whereas attributes are "fixed" information, variables provides a more flexible way to user
1094   to fill in (extra) information for a file.
1095   \param [in] id String identity of new variable
1096   \return Pointer to added (or already existed) variable
1097   */
1098   CVariable* CFile::addVariable(const string& id)
1099   TRY
1100   {
1101     return vVariableGroup->createChild(id);
1102   }
1103   CATCH_DUMP_ATTR
1104
1105   /*!
1106   \brief Add a variable group into file.
1107      A variable group is added into file and it will play a role as parents for variables.
1108   A new variable group won't be created if one with id has already existed
1109   \param [in] id String identity of new variable group
1110   \return Pointer to added (or already existed) variable group
1111   */
1112   CVariableGroup* CFile::addVariableGroup(const string& id)
1113   TRY
1114   {
1115     return vVariableGroup->createChildGroup(id);
1116   }
1117   CATCH_DUMP_ATTR
1118
1119   void CFile::setContextClient(CContextClient* newContextClient)
1120   TRY
1121   {
1122     client = newContextClient;
1123     size_t size = this->enabledFields.size();
1124     for (size_t i = 0; i < size; ++i)
1125     {
1126       this->enabledFields[i]->setContextClient(newContextClient);
1127     }
1128   }
1129   CATCH_DUMP_ATTR
1130
1131   CContextClient* CFile::getContextClient()
1132   TRY
1133   {
1134     return client;
1135   }
1136   CATCH_DUMP_ATTR
1137
1138   void CFile::setReadContextClient(CContextClient* readContextclient)
1139   TRY
1140   {
1141     read_client = readContextclient;
1142   }
1143   CATCH_DUMP_ATTR
1144
1145   CContextClient* CFile::getReadContextClient()
1146   TRY
1147   {
1148     return read_client;
1149   }
1150   CATCH_DUMP_ATTR
1151
1152   /*!
1153   \brief Send a message to create a field on server side
1154   \param[in] id String identity of field that will be created on server
1155   */
1156   void CFile::sendAddField(const string& id, CContextClient* client)
1157   TRY
1158   {
1159      sendAddItem(id, EVENT_ID_ADD_FIELD, client);
1160   }
1161   CATCH_DUMP_ATTR
1162
1163   /*!
1164   \brief Send a message to create a field group on server side
1165   \param[in] id String identity of field group that will be created on server
1166   */
1167   void CFile::sendAddFieldGroup(const string& id, CContextClient* client)
1168   TRY
1169   {
1170      sendAddItem(id, (int)EVENT_ID_ADD_FIELD_GROUP, client);
1171   }
1172   CATCH_DUMP_ATTR
1173
1174   /*!
1175   \brief Receive a message annoucing the creation of a field on server side
1176   \param[in] event Received event
1177   */
1178   void CFile::recvAddField(CEventServer& event)
1179   TRY
1180   {
1181
1182      CBufferIn* buffer = event.subEvents.begin()->buffer;
1183      string id;
1184      *buffer>>id;
1185      get(id)->recvAddField(*buffer);
1186   }
1187   CATCH
1188
1189   /*!
1190   \brief Receive a message annoucing the creation of a field on server side
1191   \param[in] buffer Buffer containing message
1192   */
1193   void CFile::recvAddField(CBufferIn& buffer)
1194   TRY
1195   {
1196      string id;
1197      buffer>>id;
1198      addField(id);
1199   }
1200   CATCH_DUMP_ATTR
1201
1202   /*!
1203   \brief Receive a message annoucing the creation of a field group on server side
1204   \param[in] event Received event
1205   */
1206   void CFile::recvAddFieldGroup(CEventServer& event)
1207   TRY
1208   {
1209
1210      CBufferIn* buffer = event.subEvents.begin()->buffer;
1211      string id;
1212      *buffer>>id;
1213      get(id)->recvAddFieldGroup(*buffer);
1214   }
1215   CATCH
1216
1217   /*!
1218   \brief Receive a message annoucing the creation of a field group on server side
1219   \param[in] buffer Buffer containing message
1220   */
1221   void CFile::recvAddFieldGroup(CBufferIn& buffer)
1222   TRY
1223   {
1224      string id;
1225      buffer>>id;
1226      addFieldGroup(id);
1227   }
1228   CATCH_DUMP_ATTR
1229
1230   /*!
1231   \brief Send messages to duplicate all variables on server side
1232      Because each variable has also its attributes. So first thing to do is replicate
1233   all these attributes on server side. Because variable can have a value, the second thing
1234   is to duplicate this value on server, too.
1235   */
1236   void CFile::sendAddAllVariables(CContextClient* client)
1237   TRY
1238   {
1239     std::vector<CVariable*> allVar = getAllVariables();
1240     std::vector<CVariable*>::const_iterator it = allVar.begin();
1241     std::vector<CVariable*>::const_iterator itE = allVar.end();
1242
1243     for (; it != itE; ++it)
1244     {
1245       this->sendAddVariable((*it)->getId(), client);
1246       (*it)->sendAllAttributesToServer(client);
1247       (*it)->sendValue(client);
1248     }
1249   }
1250   CATCH_DUMP_ATTR
1251
1252   /*!
1253   \brief Send a message to create a variable group on server side
1254   \param[in] id String identity of variable group that will be created on server
1255   \param [in] client client to which we will send this adding action
1256   */
1257   void CFile::sendAddVariableGroup(const string& id, CContextClient* client)
1258   TRY
1259   {
1260      sendAddItem(id, (int)EVENT_ID_ADD_VARIABLE_GROUP, client);
1261   }
1262   CATCH_DUMP_ATTR
1263
1264   /*
1265     Send message to add a variable into a file within a certain client
1266     \param [in] id String identity of a variable
1267     \param [in] client client to which we will send this adding action
1268   */
1269   void CFile::sendAddVariable(const string& id, CContextClient* client)
1270   TRY
1271   {
1272      sendAddItem(id, (int)EVENT_ID_ADD_VARIABLE, client);
1273   }
1274   CATCH_DUMP_ATTR
1275
1276   /*!
1277   \brief Receive a message annoucing the creation of a variable on server side
1278   \param[in] event Received event
1279   */
1280   void CFile::recvAddVariable(CEventServer& event)
1281   TRY
1282   {
1283      CBufferIn* buffer = event.subEvents.begin()->buffer;
1284      string id;
1285      *buffer>>id;
1286      get(id)->recvAddVariable(*buffer);
1287   }
1288   CATCH
1289
1290   /*!
1291   \brief Receive a message annoucing the creation of a variable on server side
1292   \param[in] buffer Buffer containing message
1293   */
1294   void CFile::recvAddVariable(CBufferIn& buffer)
1295   TRY
1296   {
1297      string id;
1298      buffer>>id;
1299      addVariable(id);
1300   }
1301   CATCH_DUMP_ATTR
1302
1303   /*!
1304   \brief Receive a message annoucing the creation of a variable group on server side
1305   \param[in] event Received event
1306   */
1307   void CFile::recvAddVariableGroup(CEventServer& event)
1308   TRY
1309   {
1310
1311      CBufferIn* buffer = event.subEvents.begin()->buffer;
1312      string id;
1313      *buffer>>id;
1314      get(id)->recvAddVariableGroup(*buffer);
1315   }
1316   CATCH
1317
1318   /*!
1319   \brief Receive a message annoucing the creation of a variable group on server side
1320   \param[in] buffer Buffer containing message
1321   */
1322   void CFile::recvAddVariableGroup(CBufferIn& buffer)
1323   TRY
1324   {
1325      string id;
1326      buffer>>id;
1327      addVariableGroup(id);
1328   }
1329   CATCH_DUMP_ATTR
1330
1331   /*!
1332     \brief Sending all active (enabled) fields from client to server.
1333   Each field is identified uniquely by its string identity. Not only should we
1334   send the id to server but also we need to send ids of reference domain and reference axis.
1335   With these two id, it's easier to make reference to grid where all data should be written.
1336   Remark: This function must be called AFTER all active (enabled) files have been created on the server side
1337   */
1338   void CFile::sendEnabledFields(CContextClient* client)
1339   TRY
1340   {
1341     size_t size = this->enabledFields.size();
1342     for (size_t i = 0; i < size; ++i)
1343     {
1344       CField* field = this->enabledFields[i];
1345       this->sendAddField(field->getId(), client);
1346       field->checkTimeAttributes();
1347       field->sendAllAttributesToServer(client);
1348       field->sendAddAllVariables(client);
1349     }
1350   }
1351   CATCH_DUMP_ATTR
1352
1353
1354 
1355   /*!
1356    * Send file attribute, related variable and chield field tree to a given file server.
1357    * \param[in] client : the context client where to send file
1358    */
1359   void CFile::sendFileToFileServer(CContextClient* client)
1360   TRY
1361   {
1362     if (sendFileToFileServer_done_.count(client)!=0) return ;
1363     else sendFileToFileServer_done_.insert(client) ;
1364     
1365     StdString fileDefRoot("file_definition");
1366     CFileGroup* cfgrpPtr = CFileGroup::get(fileDefRoot);
1367     cfgrpPtr->sendCreateChild(this->getId(), client);
1368     this->sendAllAttributesToServer(client);
1369     this->sendAddAllVariables(client);
1370     for(auto field : enabledFields) this->sendAddField(field->getId(), client);
1371   }
1372   CATCH_DUMP_ATTR
1373   /*!
1374   \brief Dispatch event received from client
1375      Whenever a message is received in buffer of server, it will be processed depending on
1376   its event type. A new event type should be added in the switch list to make sure
1377   it processed on server side.
1378   \param [in] event: Received message
1379   */
1380   bool CFile::dispatchEvent(CEventServer& event)
1381   TRY
1382   {
1383      if (SuperClass::dispatchEvent(event)) return true;
1384      else
1385      {
1386        switch(event.type)
1387        {
1388           case EVENT_ID_ADD_FIELD :
1389             recvAddField(event);
1390             return true;
1391             break;
1392
1393           case EVENT_ID_ADD_FIELD_GROUP :
1394             recvAddFieldGroup(event);
1395             return true;
1396             break;
1397
1398            case EVENT_ID_ADD_VARIABLE :
1399             recvAddVariable(event);
1400             return true;
1401             break;
1402
1403           case EVENT_ID_ADD_VARIABLE_GROUP :
1404             recvAddVariableGroup(event);
1405             return true;
1406             break;
1407           default :
1408              ERROR("bool CFile::dispatchEvent(CEventServer& event)", << "Unknown Event");
1409           return false;
1410        }
1411      }
1412   }
1413   CATCH
1414
1415   ///--------------------------------------------------------------
1416   /*!
1417   */
1418   StdString CFile::dumpClassAttributes(void)
1419   {
1420     StdString str;
1421     CContext* context = CContext::getCurrent();
1422     str.append("context=\"");
1423     str.append(context->getId());
1424     str.append("\"");
1425     str.append(" enabled fields=\"");
1426     int size = this->enabledFields.size();
1427     for (int i = 0; i < size; ++i)
1428     {
1429       str.append(this->enabledFields[i]->getId());
1430       str.append(" ");
1431     }
1432     str.append("\"");
1433     return str;
1434   }
1435
1436   ///---------------------------------------------------------------
1437
1438} // namespace xios
Note: See TracBrowser for help on using the repository browser.