source: XIOS/dev/dev_trunk_omp/src/node/field.cpp @ 1678

Last change on this file since 1678 was 1677, checked in by yushan, 5 years ago

MARK: Dynamic workflow graph developement. Branch up to date with trunk @1663.

  • 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
  • Property svn:executable set to *
File size: 64.2 KB
Line 
1#include "field.hpp"
2
3#include "attribute_template.hpp"
4#include "object_template.hpp"
5#include "group_template.hpp"
6
7#include "node_type.hpp"
8#include "calendar_util.hpp"
9#include "message.hpp"
10#include "xios_spl.hpp"
11#include "type.hpp"
12#include "timer.hpp"
13#include "context_client.hpp"
14#include "context_server.hpp"
15#include <set>
16#include "garbage_collector.hpp"
17#include "source_filter.hpp"
18#include "store_filter.hpp"
19#include "file_writer_filter.hpp"
20#include "pass_through_filter.hpp"
21#include "filter_expr_node.hpp"
22#include "lex_parser.hpp"
23#include "temporal_filter.hpp"
24#include "spatial_transform_filter.hpp"
25#include "file_server_writer_filter.hpp"
26#include "workflow_graph.hpp"
27
28namespace xios{
29
30   /// ////////////////////// Définitions ////////////////////// ///
31
32   CField::CField(void)
33      : CObjectTemplate<CField>(), CFieldAttributes()
34      , grid(), file()
35      , written(false)
36      , nstep(0), nstepMax(0)
37      , hasOutputFile(false)
38      , domAxisScalarIds_(vector<StdString>(3,""))
39      , areAllReferenceSolved(false), isReferenceSolved(false), isReferenceSolvedAndTransformed(false)
40      , isGridChecked(false)
41      , useCompressedOutput(false)
42      , hasTimeInstant(false)
43      , hasTimeCentered(false)
44      , wasDataRequestedFromServer(false)
45      , wasDataAlreadyReceivedFromServer(false)
46      , mustAutoTrigger(false)
47      , isEOF(false), nstepMaxRead(false)
48   { setVirtualVariableGroup(CVariableGroup::create(getId() + "_virtual_variable_group")); }
49
50   CField::CField(const StdString& id)
51      : CObjectTemplate<CField>(id), CFieldAttributes()
52      , grid(), file()
53      , written(false)
54      , nstep(0), nstepMax(0)
55      , hasOutputFile(false)
56      , domAxisScalarIds_(vector<StdString>(3,""))
57      , areAllReferenceSolved(false), isReferenceSolved(false), isReferenceSolvedAndTransformed(false)
58      , isGridChecked(false)
59      , useCompressedOutput(false)
60      , hasTimeInstant(false)
61      , hasTimeCentered(false)
62      , wasDataRequestedFromServer(false)
63      , wasDataAlreadyReceivedFromServer(false)
64      , mustAutoTrigger(false)
65      , isEOF(false), nstepMaxRead(false)
66   { setVirtualVariableGroup(CVariableGroup::create(getId() + "_virtual_variable_group")); }
67
68   CField::~CField(void)
69   {}
70
71  //----------------------------------------------------------------
72
73
74   const StdString& CField::getOId(void)
75   {
76     if (this->getId().compare(2,5,"field") == 0 && this->getId().compare(8,5,"undef") == 0)
77     {
78       if(!name.isEmpty()) return name;
79       else return CField::get(field_ref)->getOId();
80     }
81     else if(!name.isEmpty()) return name;
82     else return this->getId();
83   }
84
85   void CField::setVirtualVariableGroup(CVariableGroup* newVVariableGroup)
86   TRY
87   {
88      this->vVariableGroup = newVVariableGroup;
89   }
90   CATCH
91
92   CVariableGroup* CField::getVirtualVariableGroup(void) const
93   TRY
94   {
95      return this->vVariableGroup;
96   }
97   CATCH
98
99   std::vector<CVariable*> CField::getAllVariables(void) const
100   TRY
101   {
102      return this->vVariableGroup->getAllChildren();
103   }
104   CATCH
105
106   void CField::solveDescInheritance(bool apply, const CAttributeMap* const parent)
107   TRY
108   {
109      SuperClassAttribute::setAttributes(parent, apply);
110      this->getVirtualVariableGroup()->solveDescInheritance(apply, NULL);
111   }
112   CATCH_DUMP_ATTR
113
114  //----------------------------------------------------------------
115
116  bool CField::dispatchEvent(CEventServer& event)
117  TRY
118  {
119    if (SuperClass::dispatchEvent(event)) return true;
120    else
121    {
122      switch(event.type)
123      {
124        case EVENT_ID_UPDATE_DATA :
125          recvUpdateData(event);
126          return true;
127          break;
128
129        case EVENT_ID_READ_DATA :
130          recvReadDataRequest(event);
131          return true;
132          break;
133
134        case EVENT_ID_READ_DATA_READY :
135          recvReadDataReady(event);
136          return true;
137          break;
138
139        case EVENT_ID_ADD_VARIABLE :
140          recvAddVariable(event);
141          return true;
142          break;
143
144        case EVENT_ID_ADD_VARIABLE_GROUP :
145          recvAddVariableGroup(event);
146          return true;
147          break;
148
149        default :
150          ERROR("bool CField::dispatchEvent(CEventServer& event)", << "Unknown Event");
151          return false;
152      }
153    }
154  }
155  CATCH
156
157  void CField::sendUpdateData(const CArray<double,1>& data)
158  TRY
159  {
160    CTimer::get("Field : send data").resume();
161
162    CContext* context = CContext::getCurrent();
163    CContextClient* client = (!context->hasServer) ? context->client : this->file->getContextClient();
164    int receiverSize = client->serverSize;
165
166    CEventClient event(getType(), EVENT_ID_UPDATE_DATA);
167
168    map<int, CArray<int,1> >::iterator it;
169    list<CMessage> list_msg;
170    list<CArray<double,1> > list_data;
171
172    if (!grid->doGridHaveDataDistributed(client))
173    {
174       if (client->isServerLeader())
175       {
176          for (it = grid->storeIndex_toSrv[client].begin(); it != grid->storeIndex_toSrv[client].end(); it++)
177          {
178            int rank = it->first;
179            CArray<int,1>& index = it->second;
180
181            list_msg.push_back(CMessage());
182            list_data.push_back(CArray<double,1>(index.numElements()));
183
184            CArray<double,1>& data_tmp = list_data.back();
185            for (int n = 0; n < data_tmp.numElements(); n++) data_tmp(n) = data(index(n));
186
187            list_msg.back() << getId() << data_tmp;
188            event.push(rank, 1, list_msg.back());
189          }
190          client->sendEvent(event);
191        }
192      else client->sendEvent(event);
193    }
194    else
195    {
196      for (it = grid->storeIndex_toSrv[client].begin(); it != grid->storeIndex_toSrv[client].end(); it++)
197      {
198        int rank = it->first;
199        CArray<int,1>& index = it->second;
200
201        list_msg.push_back(CMessage());
202        list_data.push_back(CArray<double,1>(index.numElements()));
203
204        CArray<double,1>& data_tmp = list_data.back();
205        for (int n = 0; n < data_tmp.numElements(); n++) data_tmp(n) = data(index(n));
206
207        list_msg.back() << getId() << data_tmp;
208        event.push(rank, grid->nbSenders[receiverSize][rank], list_msg.back());
209      }
210      client->sendEvent(event);
211    }
212
213    CTimer::get("Field : send data").suspend();
214  }
215  CATCH_DUMP_ATTR
216
217  void CField::recvUpdateData(CEventServer& event)
218  TRY
219  {
220    std::map<int,CBufferIn*> rankBuffers;
221
222    list<CEventServer::SSubEvent>::iterator it;
223    string fieldId;
224    CTimer::get("Field : recv data").resume();
225    for (it = event.subEvents.begin(); it != event.subEvents.end(); ++it)
226    {
227      int rank = it->rank;
228      CBufferIn* buffer = it->buffer;
229      *buffer >> fieldId;
230      rankBuffers[rank] = buffer;
231    }
232    get(fieldId)->recvUpdateData(rankBuffers);
233    CTimer::get("Field : recv data").suspend();
234  }
235  CATCH
236
237  void  CField::recvUpdateData(std::map<int,CBufferIn*>& rankBuffers)
238  TRY
239  {
240    CContext* context = CContext::getCurrent();
241
242    size_t sizeData = 0;
243    if (0 == recvDataSrv.numElements())
244    {           
245      CArray<int,1>& storeClient = grid->storeIndex_client;
246
247      // Gather all data from different clients     
248      recvDataSrv.resize(storeClient.numElements());
249      recvFoperationSrv = std::shared_ptr<func::CFunctor>(new func::CInstant(recvDataSrv));
250    }
251
252    CArray<double,1> recv_data_tmp(recvDataSrv.numElements());   
253    const CDate& currDate = context->getCalendar()->getCurrentDate();
254    CDuration offsetAllButMonth (freq_offset.getValue().year, 0 , freq_offset.getValue().day,
255                                   freq_offset.getValue().hour, freq_offset.getValue().minute,
256                                   freq_offset.getValue().second, freq_offset.getValue().timestep);
257    const CDate opeDate   = (last_operation_srv - offsetAllButMonth + context->getCalendar()->getTimeStep())
258                              + freq_op + freq_operation_srv - freq_op - context->getCalendar()->getTimeStep() + offsetAllButMonth;
259
260    if (opeDate <= currDate)
261    {
262      for (map<int, CArray<size_t, 1> >::iterator it = grid->outLocalIndexStoreOnClient.begin(); it != grid->outLocalIndexStoreOnClient.end(); ++it)
263      {
264        CArray<double,1> tmp;
265        CArray<size_t,1>& indexTmp = it->second;
266        *(rankBuffers[it->first]) >> tmp;
267        for (int idx = 0; idx < indexTmp.numElements(); ++idx)
268        {
269          recv_data_tmp(indexTmp(idx)) = tmp(idx);
270        }     
271      }
272    }
273
274    this->setData(recv_data_tmp);
275    // delete incomming flux for server only
276    recvFoperationSrv.reset() ;
277    recvDataSrv.reset() ;
278  }
279  CATCH_DUMP_ATTR
280
281  void CField::writeUpdateData(const CArray<double,1>& data)
282  TRY
283  {
284    CContext* context = CContext::getCurrent();
285
286    const CDate& currDate = context->getCalendar()->getCurrentDate();
287    CDuration offsetAllButMonth (freq_offset.getValue().year, 0 , freq_offset.getValue().day,
288                                   freq_offset.getValue().hour, freq_offset.getValue().minute,
289                                   freq_offset.getValue().second, freq_offset.getValue().timestep);
290    const CDate opeDate   = (last_operation_srv - offsetAllButMonth + context->getCalendar()->getTimeStep())
291                              + freq_op + freq_operation_srv - freq_op - context->getCalendar()->getTimeStep() + offsetAllButMonth;
292    const CDate writeDate = last_Write_srv + freq_write_srv;
293
294    if (opeDate <= currDate)
295    {
296      (*recvFoperationSrv)(data);
297      last_operation_srv = currDate;
298    }
299
300    if (writeDate < (currDate + freq_operation_srv))
301    {
302      recvFoperationSrv->final();
303      last_Write_srv = writeDate;
304      grid->computeWrittenIndex();
305      writeField();
306      lastlast_Write_srv = last_Write_srv;
307    }
308  }
309  CATCH_DUMP_ATTR
310
311  void CField::writeField(void)
312  TRY
313  {
314    if (!getRelFile()->isEmptyZone())
315    {
316      if (grid->doGridHaveDataToWrite() || getRelFile()->type == CFile::type_attr::one_file)
317      {
318        getRelFile()->checkWriteFile();
319        this->incrementNStep();
320        getRelFile()->getDataOutput()->writeFieldData(CField::get(this));
321      }
322    }
323  }
324  CATCH_DUMP_ATTR
325
326  /*
327    Send a request for reading data.
328    Client sends a request to server for demanding server to read data and send back to it.
329    For now, this function is called only by client
330    In the future, it can be called by level-1 servers
331    \param [in] tsDataRequested timestamp when the call is made
332  */
333  bool CField::sendReadDataRequest(const CDate& tsDataRequested)
334  TRY
335  {
336    CContext* context = CContext::getCurrent();
337    // CContextClient* client = context->client;
338
339    // This code is for future: If we want to read file with level-2 servers
340    CContextClient* client = (!context->hasServer) ? context->client : this->file->getContextClient();
341
342    lastDataRequestedFromServer = tsDataRequested;
343
344    // No need to send the request if we are sure that we are already at EOF
345    if (!isEOF || context->getCalendar()->getCurrentDate() <= dateEOF)
346    {
347      CEventClient event(getType(), EVENT_ID_READ_DATA);
348      if (client->isServerLeader())
349      {
350        CMessage msg;
351        msg << getId();
352        const std::list<int>& ranks = client->getRanksServerLeader();
353        for (std::list<int>::const_iterator itRank = ranks.begin(), itRankEnd = ranks.end(); itRank != itRankEnd; ++itRank)
354          event.push(*itRank, 1, msg);
355        client->sendEvent(event);
356      }
357      else client->sendEvent(event);
358    }
359    else
360      serverSourceFilter->signalEndOfStream(tsDataRequested);
361
362    wasDataRequestedFromServer = true;
363
364    return !isEOF;
365  }
366  CATCH_DUMP_ATTR
367
368  /*!
369  Send request new data read from file if need be, that is the current data is out-of-date.
370  \return true if and only if some data was requested
371  */
372  bool CField::sendReadDataRequestIfNeeded(void)
373  TRY
374  {
375    const CDate& currentDate = CContext::getCurrent()->getCalendar()->getCurrentDate();
376
377    bool dataRequested = false;
378
379    while (currentDate >= lastDataRequestedFromServer)
380    {
381      #pragma omp critical (_output)
382      {
383        info(20) << "currentDate : " << currentDate << endl ;
384        info(20) << "lastDataRequestedFromServer : " << lastDataRequestedFromServer << endl ;
385        info(20) << "file->output_freq.getValue() : " << file->output_freq.getValue() << endl ;
386        info(20) << "lastDataRequestedFromServer + file->output_freq.getValue() : " << lastDataRequestedFromServer + file->output_freq << endl ;
387      }
388
389      dataRequested |= sendReadDataRequest(lastDataRequestedFromServer + file->output_freq);
390    }
391
392    return dataRequested;
393  }
394  CATCH_DUMP_ATTR
395
396  void CField::recvReadDataRequest(CEventServer& event)
397  TRY
398  {
399    CBufferIn* buffer = event.subEvents.begin()->buffer;
400    StdString fieldId;
401    *buffer >> fieldId;
402    get(fieldId)->recvReadDataRequest();
403  }
404  CATCH
405
406  /*!
407    Receive data request sent from client and process it
408    Every time server receives this request, it will try to read data and sent read data back to client
409    At the moment, this function is called by server level 1
410    In the future, this should (only) be done by the last level servers.
411  */
412  void CField::recvReadDataRequest(void)
413  TRY
414  {
415    CContext* context = CContext::getCurrent();
416    CContextClient* client = context->client;
417
418    CEventClient event(getType(), EVENT_ID_READ_DATA_READY);
419    std::list<CMessage> msgs;
420
421    EReadField hasData = readField();
422
423    map<int, CArray<double,1> >::iterator it;
424    if (!grid->doGridHaveDataDistributed(client))
425    {
426       if (client->isServerLeader())
427       {
428          if (0 != recvDataSrv.numElements())
429          {           
430            const std::list<int>& ranks = client->getRanksServerLeader();
431            for (std::list<int>::const_iterator itRank = ranks.begin(), itRankEnd = ranks.end(); itRank != itRankEnd; ++itRank)
432            {
433              msgs.push_back(CMessage());
434              CMessage& msg = msgs.back();
435              msg << getId();
436              switch (hasData)
437              {
438                case RF_DATA:
439                  msg << getNStep() - 1 << recvDataSrv;
440                  break;
441                case RF_NODATA:
442                  msg << int(-2) << recvDataSrv;
443                  break;
444                case RF_EOF:                 
445                default:
446                  msg << int(-1);
447                  break;
448              }
449
450              event.push(*itRank, 1, msg);
451            }
452          }
453          client->sendEvent(event);
454       }
455       else
456       {
457          client->sendEvent(event);
458       }
459    }
460    else
461    {
462      for (map<int, CArray<size_t, 1> >::iterator it = grid->outLocalIndexStoreOnClient.begin(); 
463                                                  it != grid->outLocalIndexStoreOnClient.end(); ++it)
464      {
465        CArray<size_t,1>& indexTmp = it->second;
466        CArray<double,1> tmp(indexTmp.numElements());
467        for (int idx = 0; idx < indexTmp.numElements(); ++idx)
468        {
469          tmp(idx) = recvDataSrv(indexTmp(idx));
470        } 
471
472        msgs.push_back(CMessage());
473        CMessage& msg = msgs.back();
474        msg << getId();
475        switch (hasData)
476        {
477          case RF_DATA:
478            msg << getNStep() - 1 << tmp;
479            break;
480          case RF_NODATA:
481            msg << int(-2) << tmp;
482            break;
483          case RF_EOF:                 
484          default:
485            msg << int(-1);
486            break;
487        }
488
489        event.push(it->first, grid->nbReadSenders[client][it->first], msg);
490      }
491      client->sendEvent(event);
492    }
493  }
494  CATCH_DUMP_ATTR
495
496  /*!
497    Read field from a file.
498    A field is read with the distribution of data on the server side
499    \return State of field can be read from a file
500  */
501  CField::EReadField CField::readField(void)
502  TRY
503  {
504    CContext* context = CContext::getCurrent();
505    grid->computeWrittenIndex();
506    getRelFile()->initRead();
507    EReadField readState = RF_DATA;
508
509    if (!getRelFile()->isEmptyZone())
510    {     
511      if (grid->doGridHaveDataToWrite() || getRelFile()->type == CFile::type_attr::one_file)     
512      {
513        if (0 == recvDataSrv.numElements())
514        {           
515          CArray<int,1>& storeClient = grid->storeIndex_client;         
516          recvDataSrv.resize(storeClient.numElements());         
517        }
518       
519        getRelFile()->checkReadFile();
520
521        if (!nstepMax)
522        {
523          nstepMax = getRelFile()->getDataInput()->getFieldNbRecords(CField::get(this));
524        }
525
526        this->incrementNStep();
527
528        if (getNStep() > nstepMax && (getRelFile()->cyclic.isEmpty() || !getRelFile()->cyclic) )
529          readState = RF_EOF;
530
531        if (RF_EOF != readState)
532          getRelFile()->getDataInput()->readFieldData(CField::get(this));
533      }
534    }
535    else
536    {
537      this->incrementNStep();
538      if (getNStep() > nstepMax && (getRelFile()->cyclic.isEmpty() || !getRelFile()->cyclic) )
539        readState = RF_EOF;
540      else
541        readState = RF_NODATA;
542
543      if (!nstepMaxRead) // This can be a bug if we try to read field from zero time record
544        readState = RF_NODATA;
545    }
546
547    if (!nstepMaxRead)
548    {
549       #ifdef _usingEP
550       MPI_Allreduce(&nstepMax, &nstepMax, 1, MPI_INT, MPI_MAX, context->server->intraComm);
551       #else
552       MPI_Allreduce(MPI_IN_PLACE, &nstepMax, 1, MPI_INT, MPI_MAX, context->server->intraComm);
553       #endif
554       nstepMaxRead = true;
555    }
556
557    return readState;
558  }
559  CATCH_DUMP_ATTR
560
561  /*
562    Receive read data from server.
563    At the moment, this function is called in the client side.
564    In the future, this function can be called hiearachically (server n-1, server n -2, ..., client)
565    \param event event containing read data
566  */
567  void CField::recvReadDataReady(CEventServer& event)
568  TRY
569  {
570    string fieldId;
571    vector<int> ranks;
572    vector<CBufferIn*> buffers;
573
574    list<CEventServer::SSubEvent>::iterator it;
575    for (it = event.subEvents.begin(); it != event.subEvents.end(); ++it)
576    {
577      ranks.push_back(it->rank);
578      CBufferIn* buffer = it->buffer;
579      *buffer >> fieldId;
580      buffers.push_back(buffer);
581    }
582    get(fieldId)->recvReadDataReady(ranks, buffers);
583  }
584  CATCH
585
586  /*!
587    Receive read data from server
588    \param [in] ranks Ranks of sending processes
589    \param [in] buffers buffers containing read data
590  */
591  void CField::recvReadDataReady(vector<int> ranks, vector<CBufferIn*> buffers)
592  TRY
593  {
594    CContext* context = CContext::getCurrent();
595    std::map<int, CArray<double,1> > data;
596    const bool wasEOF = isEOF;
597
598    for (int i = 0; i < ranks.size(); i++)
599    {
600      int rank = ranks[i];
601      int record;
602      *buffers[i] >> record;
603      isEOF = (record == int(-1));
604
605      if (!isEOF)
606        *buffers[i] >> data[rank];
607      else
608        break;
609    }
610
611    if (wasDataAlreadyReceivedFromServer)
612      lastDataReceivedFromServer = lastDataReceivedFromServer + file->output_freq;
613    else
614    {
615      lastDataReceivedFromServer = context->getCalendar()->getInitDate();
616      wasDataAlreadyReceivedFromServer = true;
617    }
618
619    if (isEOF)
620    {
621      if (!wasEOF)
622        dateEOF = lastDataReceivedFromServer;
623
624      serverSourceFilter->signalEndOfStream(lastDataReceivedFromServer);
625    }
626    else
627      serverSourceFilter->streamDataFromServer(lastDataReceivedFromServer, data);
628  }
629  CATCH_DUMP_ATTR
630
631  void CField::checkForLateDataFromServer(void)
632  TRY
633  {
634    CContext* context = CContext::getCurrent();
635    const CDate& currentDate = context->getCalendar()->getCurrentDate();
636
637    // Check if data previously requested has been received as expected
638    if (wasDataRequestedFromServer && !isEOF)
639    {
640      CTimer timer("CField::checkForLateDataFromServer");
641
642      bool isDataLate;
643      do
644      {
645        const CDate nextDataDue = wasDataAlreadyReceivedFromServer ? (lastDataReceivedFromServer + file->output_freq) : context->getCalendar()->getInitDate();
646        isDataLate = (nextDataDue <= currentDate);
647
648        if (isDataLate)
649        {
650          timer.resume();
651
652          context->checkBuffersAndListen();
653
654          timer.suspend();
655        }
656      }
657      while (isDataLate && timer.getCumulatedTime() < CXios::recvFieldTimeout);
658
659      if (isDataLate)
660        ERROR("void CField::checkForLateDataFromServer(void)",
661              << "Late data at timestep = " << currentDate);
662    }
663  }
664  CATCH_DUMP_ATTR
665
666  void CField::checkIfMustAutoTrigger(void)
667  TRY
668  {
669    mustAutoTrigger = serverSourceFilter ? serverSourceFilter->mustAutoTrigger() : false;
670  }
671  CATCH_DUMP_ATTR
672
673  void CField::autoTriggerIfNeeded(void)
674  TRY
675  {
676    if (mustAutoTrigger)
677      serverSourceFilter->trigger(CContext::getCurrent()->getCalendar()->getCurrentDate());
678  }
679  CATCH_DUMP_ATTR
680
681   //----------------------------------------------------------------
682
683   void CField::setRelFile(CFile* _file)
684   TRY
685   {
686      this->file = _file;
687      hasOutputFile = true;
688   }
689   CATCH_DUMP_ATTR
690
691   //----------------------------------------------------------------
692
693   StdString CField::GetName(void)    { return StdString("field"); }
694   StdString CField::GetDefName(void) { return CField::GetName(); }
695   ENodeType CField::GetType(void)    { return eField; }
696
697   //----------------------------------------------------------------
698
699   CGrid* CField::getRelGrid(void) const
700   TRY
701   {
702      return this->grid;
703   }
704   CATCH
705
706   //----------------------------------------------------------------
707
708   CFile* CField::getRelFile(void) const
709   TRY
710   {
711      return this->file;
712   }
713   CATCH
714
715   int CField::getNStep(void) const
716   TRY
717   {
718      return this->nstep;
719   }
720   CATCH
721
722   func::CFunctor::ETimeType CField::getOperationTimeType() const
723   TRY
724   {
725     return operationTimeType;
726   }
727   CATCH
728
729   //----------------------------------------------------------------
730
731   void CField::incrementNStep(void)
732   TRY
733   {
734      this->nstep++;
735   }
736   CATCH_DUMP_ATTR
737
738   void CField::resetNStep(int nstep /*= 0*/)
739   TRY
740   {
741      this->nstep = nstep;
742   }
743   CATCH_DUMP_ATTR
744
745   void CField::resetNStepMax(void)
746   TRY
747   {
748      this->nstepMax = 0;
749      nstepMaxRead = false;
750   }
751   CATCH_DUMP_ATTR
752
753   //----------------------------------------------------------------
754
755   bool CField::isActive(bool atCurrentTimestep /*= false*/) const
756   TRY
757   {
758      if (clientSourceFilter)
759        return atCurrentTimestep ? clientSourceFilter->isDataExpected(CContext::getCurrent()->getCalendar()->getCurrentDate()) : true;
760      else if (storeFilter)
761        return true;
762      else if (instantDataFilter)
763        ERROR("bool CField::isActive(bool atCurrentTimestep)",
764              << "Impossible to check if field [ id = " << getId() << " ] is active as it cannot be used to receive nor send data.");
765
766      return false;
767   }
768   CATCH
769
770   //----------------------------------------------------------------
771
772   bool CField::wasWritten() const
773   TRY
774   {
775     return written;
776   }
777   CATCH
778
779   void CField::setWritten()
780   TRY
781   {
782     written = true;
783   }
784   CATCH_DUMP_ATTR
785
786   //----------------------------------------------------------------
787
788   bool CField::getUseCompressedOutput() const
789   TRY
790   {
791     return useCompressedOutput;
792   }
793   CATCH
794
795   void CField::setUseCompressedOutput()
796   TRY
797   {
798     useCompressedOutput = true;
799   }
800   CATCH_DUMP_ATTR
801
802   //----------------------------------------------------------------
803
804   std::shared_ptr<COutputPin> CField::getInstantDataFilter()
805   TRY
806   {
807     return instantDataFilter;
808   }
809   CATCH_DUMP_ATTR
810
811   //----------------------------------------------------------------
812
813   /*!
814     Build up graph of grids which plays role of destination and source in grid transformation
815     This function should be called before \func solveGridReference()
816   */
817   void CField::buildGridTransformationGraph()
818   TRY
819   {
820     CContext* context = CContext::getCurrent();
821     if (context->hasClient && !context->hasServer)
822     {
823       if (grid && !grid->isTransformed() && hasDirectFieldReference() && grid != getDirectFieldReference()->grid)
824       {
825         grid->addTransGridSource(getDirectFieldReference()->grid);
826       }
827     }
828   }
829   CATCH_DUMP_ATTR
830
831   /*!
832     Generate a new grid destination if there are more than one grid source pointing to a same grid destination
833   */
834   void CField::generateNewTransformationGridDest()
835   TRY
836   {
837     CContext* context = CContext::getCurrent();
838     if (context->hasClient && !context->hasServer)
839     {
840       std::map<CGrid*,std::pair<bool,StdString> >& gridSrcMap = grid->getTransGridSource();
841       if (1 < gridSrcMap.size())
842       {
843         // Search for grid source
844         CGrid* gridSrc = grid;
845         CField* currField = this;
846         std::vector<CField*> hieraField;
847
848         while (currField->hasDirectFieldReference() && (gridSrc == grid))
849         {
850           hieraField.push_back(currField);
851           CField* tmp = currField->getDirectFieldReference();
852           currField = tmp;
853           gridSrc = currField->grid;
854         }
855
856         if (gridSrcMap.end() != gridSrcMap.find(gridSrc))
857         {
858           CGrid* gridTmp;
859           std::pair<bool,StdString> newGridDest = gridSrcMap[gridSrc];
860           if (newGridDest.first)
861           {
862             StdString newIdGridDest = newGridDest.second;
863             if (!CGrid::has(newIdGridDest))
864             {
865                ERROR("CGrid* CGrid::generateNewTransformationGridDest()",
866                  << " Something wrong happened! Grid whose id " << newIdGridDest
867                  << "should exist ");
868             }
869             gridTmp = CGrid::get(newIdGridDest);
870           }
871           else
872           {
873             StdString newIdGridDest = CGrid::generateId(gridSrc, grid);
874             gridTmp = CGrid::cloneGrid(newIdGridDest, grid);
875
876             (gridSrcMap[gridSrc]).first = true;
877             (gridSrcMap[gridSrc]).second = newIdGridDest;
878           }
879
880           // Update all descendants
881           for (std::vector<CField*>::iterator it = hieraField.begin(); it != hieraField.end(); ++it)
882           {
883             (*it)->grid = gridTmp;
884             (*it)->updateRef((*it)->grid);
885           }
886         }
887       }
888     }
889   }
890   CATCH_DUMP_ATTR
891
892   void CField::updateRef(CGrid* grid)
893   TRY
894   {
895     if (!grid_ref.isEmpty()) grid_ref.setValue(grid->getId());
896     else
897     {
898       std::vector<CAxis*> axisTmp = grid->getAxis();
899       std::vector<CDomain*> domainTmp = grid->getDomains();
900       if ((1<axisTmp.size()) || (1<domainTmp.size()))
901         ERROR("void CField::updateRef(CGrid* grid)",
902           << "More than one domain or axis is available for domain_ref/axis_ref of field " << this->getId());
903
904       if ((!domain_ref.isEmpty()) && (domainTmp.empty()))
905         ERROR("void CField::updateRef(CGrid* grid)",
906           << "Incoherent between available domain and domain_ref of field " << this->getId());
907       if ((!axis_ref.isEmpty()) && (axisTmp.empty()))
908         ERROR("void CField::updateRef(CGrid* grid)",
909           << "Incoherent between available axis and axis_ref of field " << this->getId());
910
911       if (!domain_ref.isEmpty()) domain_ref.setValue(domainTmp[0]->getId());
912       if (!axis_ref.isEmpty()) axis_ref.setValue(axisTmp[0]->getId());
913     }
914   }
915   CATCH_DUMP_ATTR
916   
917   /*!
918     Solve reference of all enabled fields even the source fields .
919     In this step, we do transformations.
920   */
921   void CField::solveAllEnabledFieldsAndTransform()
922   TRY
923   {
924     CContext* context = CContext::getCurrent();
925     bool hasClient = context->hasClient;
926     bool hasServer = context->hasServer;
927
928     if (!isReferenceSolvedAndTransformed)
929     {
930        isReferenceSolvedAndTransformed = true;
931
932        if (hasClient && !hasServer)
933        {
934          solveRefInheritance(true);
935          if (hasDirectFieldReference()) getDirectFieldReference()->solveAllEnabledFieldsAndTransform();
936        }
937
938        if (hasServer)
939          solveServerOperation();
940
941        solveGridReference();
942
943        if (hasClient && !hasServer)
944       {
945         solveGenerateGrid();
946         buildGridTransformationGraph();
947       }
948
949       solveGridDomainAxisRef(false);
950
951       if (hasClient && !hasServer)
952       {
953         solveTransformedGrid();
954       }
955
956       solveGridDomainAxisRef(false);
957     }
958   }
959   CATCH_DUMP_ATTR
960
961   void CField::checkGridOfEnabledFields()
962   TRY
963   {
964     if (!isGridChecked)
965     {
966       isGridChecked = true;
967       solveCheckMaskIndex(false);
968     }
969   }
970   CATCH_DUMP_ATTR
971
972   void CField::sendGridComponentOfEnabledFields()
973   TRY
974   {
975      solveGridDomainAxisRef(true);
976      // solveCheckMaskIndex(true);
977   }
978   CATCH_DUMP_ATTR
979
980   void CField::sendGridOfEnabledFields()
981   TRY
982   {
983      // solveGridDomainAxisRef(true);
984      solveCheckMaskIndex(true);
985   }   
986   CATCH_DUMP_ATTR
987
988   void CField::solveOnlyReferenceEnabledField(bool doSending2Server)
989   TRY
990   {
991     CContext* context = CContext::getCurrent();
992     if (!isReferenceSolved)
993     {
994        isReferenceSolved = true;
995
996        if (context->hasClient && !context->hasServer)
997        {
998          solveRefInheritance(true);
999          if (hasDirectFieldReference()) getDirectFieldReference()->solveOnlyReferenceEnabledField(false);
1000        }
1001
1002        if (context->hasServer)
1003          solveServerOperation();
1004
1005        solveGridReference();
1006        grid->solveDomainAxisRefInheritance(true); // make it again to solve grid reading from file
1007
1008        if (context->hasClient && !context->hasServer)
1009       {
1010         solveGenerateGrid();
1011         buildGridTransformationGraph();
1012       }
1013     }
1014   }
1015   CATCH_DUMP_ATTR
1016
1017   void CField::solveAllReferenceEnabledField(bool doSending2Server)
1018   TRY
1019   {
1020     CContext* context = CContext::getCurrent();
1021     solveOnlyReferenceEnabledField(doSending2Server);
1022
1023     if (!areAllReferenceSolved)
1024     {
1025        areAllReferenceSolved = true;
1026       
1027        if (context->hasClient && !context->hasServer)
1028        {
1029          solveRefInheritance(true);
1030          if (hasDirectFieldReference()) getDirectFieldReference()->solveAllReferenceEnabledField(false);
1031        }
1032        else if (context->hasServer)
1033          solveServerOperation();
1034
1035        solveGridReference();
1036     }
1037
1038     solveGridDomainAxisRef(doSending2Server);
1039
1040     if (context->hasClient && !context->hasServer)
1041     {
1042       solveTransformedGrid();
1043     }
1044
1045     solveCheckMaskIndex(doSending2Server);
1046   }
1047   CATCH_DUMP_ATTR
1048
1049   std::map<int, StdSize> CField::getGridAttributesBufferSize(CContextClient* client, bool bufferForWriting /*= "false"*/)
1050   TRY
1051   {
1052     return grid->getAttributesBufferSize(client, bufferForWriting);
1053   }
1054   CATCH_DUMP_ATTR
1055
1056   std::map<int, StdSize> CField::getGridDataBufferSize(CContextClient* client, bool bufferForWriting /*= "false"*/)
1057   TRY
1058   {
1059     return grid->getDataBufferSize(client, getId(), bufferForWriting);
1060   }
1061   CATCH_DUMP_ATTR
1062
1063   size_t CField::getGlobalWrittenSize()
1064   TRY
1065   {
1066     return grid->getGlobalWrittenSize();
1067   }
1068   CATCH_DUMP_ATTR
1069
1070   //----------------------------------------------------------------
1071
1072   void CField::solveServerOperation(void)
1073   TRY
1074   {
1075      CContext* context = CContext::getCurrent();
1076
1077      if (!context->hasServer || !hasOutputFile) return;
1078
1079      if (freq_op.isEmpty())
1080        freq_op.setValue(TimeStep);
1081
1082      if (freq_offset.isEmpty())
1083        freq_offset.setValue(NoneDu);
1084
1085      freq_operation_srv = file->output_freq.getValue();
1086      freq_write_srv     = file->output_freq.getValue();
1087
1088      lastlast_Write_srv = context->getCalendar()->getInitDate();
1089      last_Write_srv     = context->getCalendar()->getInitDate();
1090      last_operation_srv = context->getCalendar()->getInitDate();
1091
1092      const CDuration toffset = freq_operation_srv - freq_offset.getValue() - context->getCalendar()->getTimeStep();
1093      last_operation_srv     = last_operation_srv - toffset;
1094
1095      if (operation.isEmpty())
1096        ERROR("void CField::solveServerOperation(void)",
1097              << "An operation must be defined for field \"" << getId() << "\".");
1098
1099      std::shared_ptr<func::CFunctor> functor;
1100      CArray<double, 1> dummyData;
1101
1102#define DECLARE_FUNCTOR(MType, mtype) \
1103      if (operation.getValue().compare(#mtype) == 0) \
1104      { \
1105        functor.reset(new func::C##MType(dummyData)); \
1106      }
1107
1108#include "functor_type.conf"
1109
1110      if (!functor)
1111        ERROR("void CField::solveServerOperation(void)",
1112              << "\"" << operation << "\" is not a valid operation.");
1113
1114      operationTimeType = functor->timeType();
1115   }
1116   CATCH_DUMP_ATTR
1117
1118   //----------------------------------------------------------------
1119
1120   /*!
1121    * Constructs the graph filter for the field, enabling or not the data output.
1122    * This method should not be called more than once with enableOutput equal to true.
1123    *
1124    * \param gc the garbage collector to use when building the filter graph
1125    * \param enableOutput must be true when the field data is to be
1126    *                     read by the client or/and written to a file
1127    */
1128   void CField::buildFilterGraph(CGarbageCollector& gc, bool enableOutput)
1129   TRY
1130   {     
1131     if (!isReferenceSolvedAndTransformed) solveAllEnabledFieldsAndTransform();
1132     if (!isGridChecked) checkGridOfEnabledFields();
1133
1134     const bool detectMissingValues = (!detect_missing_value.isEmpty() && !default_value.isEmpty() && detect_missing_value == true);
1135     const bool buildWorkflowGraph = (!build_workflow_graph.isEmpty() && build_workflow_graph == true);
1136     const double defaultValue  = detectMissingValues ? default_value : (!default_value.isEmpty() ? default_value : 0.0);
1137
1138     CContext* context = CContext::getCurrent();
1139     bool hasWriterServer = context->hasServer && !context->hasClient;
1140     bool hasIntermediateServer = context->hasServer && context->hasClient;
1141
1142     if (hasWriterServer)
1143     {
1144        if (!instantDataFilter)
1145          instantDataFilter = clientSourceFilter = std::shared_ptr<CSourceFilter>(new CSourceFilter(gc, grid, true, false));
1146
1147
1148       // If the field data is to be read by the client or/and written to a file
1149       if (enableOutput && !storeFilter && !fileWriterFilter)
1150       {
1151         if (file && (file->mode.isEmpty() || file->mode == CFile::mode_attr::write))
1152         {
1153           fileServerWriterFilter = std::shared_ptr<CFileServerWriterFilter>(new CFileServerWriterFilter(gc, this));
1154           instantDataFilter->connectOutput(fileServerWriterFilter, 0);
1155         }
1156       }
1157     }
1158     else if (hasIntermediateServer)
1159     {
1160       if (!instantDataFilter)
1161         instantDataFilter = clientSourceFilter = std::shared_ptr<CSourceFilter>(new CSourceFilter(gc, grid, false, false));
1162
1163             // If the field data is to be read by the client or/and written to a file
1164       if (enableOutput && !storeFilter && !fileWriterFilter)
1165       {
1166         if (file && (file->mode.isEmpty() || file->mode == CFile::mode_attr::write))
1167         {
1168           fileWriterFilter = std::shared_ptr<CFileWriterFilter>(new CFileWriterFilter(gc, this));
1169           instantDataFilter->connectOutput(fileWriterFilter, 0);       
1170         }
1171       }
1172     }
1173     else
1174     {
1175       // Start by building a filter which can provide the field's instant data
1176       if (!instantDataFilter)
1177       {
1178         // Check if we have an expression to parse
1179         if (hasExpression())
1180         {
1181           boost::scoped_ptr<IFilterExprNode> expr(parseExpr(getExpression() + '\0'));
1182           std::shared_ptr<COutputPin> filter = expr->reduce(gc, *this);
1183           filter->output_field_id = this->getId();
1184
1185           // Check if a spatial transformation is needed
1186           if (!field_ref.isEmpty())
1187           {
1188             CField* fieldRef = CField::get(field_ref);
1189             fieldRef->build_workflow_graph.setValue(buildWorkflowGraph);
1190             CGrid* gridRef = fieldRef->grid;
1191
1192             if (grid && grid != gridRef && grid->hasTransform())
1193             {
1194               std::pair<std::shared_ptr<CFilter>, std::shared_ptr<CFilter> > filters = CSpatialTransformFilter::buildFilterGraph(gc, gridRef, grid,
1195                                                                                         detectMissingValues, defaultValue, buildWorkflowGraph);
1196
1197               filter->connectOutput(filters.first, 0);
1198               filters.second->output_field_id = this->getId();
1199
1200
1201               filter = filters.second;
1202               
1203             }
1204           }
1205
1206           instantDataFilter = filter;
1207           instantDataFilter->output_field_id = this->getId();
1208           filter->output_field_id = this->getId();
1209           filter->tag = buildWorkflowGraph;
1210           // std::cout<<"=====1============= output_field_id = "<<this->getId()<<" =========== tag="<<filter->tag<<std::endl;
1211           for(int i=0; i<filter->parent_filters.size(); i++)
1212           {
1213             // std::cout<<"transforme filter cout= "<<std::shared_ptr<COutputPin>(filter)<<" parent filter = "<<filter->parent_filters[i]<<std::endl;
1214             filter->tag = filter->tag || filter->parent_filters[i]->tag;
1215           }
1216           
1217           
1218
1219
1220
1221         }
1222         // Check if we have a reference on another field
1223         else if (!field_ref.isEmpty())
1224         {
1225           CField::get(field_ref)->build_workflow_graph.setValue(buildWorkflowGraph);
1226           instantDataFilter = getFieldReference(gc);
1227           instantDataFilter->tag = buildWorkflowGraph;
1228         }
1229         // Check if the data is to be read from a file
1230         else if (file && !file->mode.isEmpty() && file->mode == CFile::mode_attr::read)
1231         {
1232           checkTimeAttributes();
1233           instantDataFilter = serverSourceFilter = std::shared_ptr<CSourceFilter>(new CSourceFilter(gc, grid, true, false, freq_offset, true,
1234                                                                                                       detectMissingValues, defaultValue, buildWorkflowGraph));
1235           instantDataFilter->tag = buildWorkflowGraph;
1236         }
1237         else // The data might be passed from the model
1238         {
1239            if (check_if_active.isEmpty()) check_if_active = false; 
1240            instantDataFilter = clientSourceFilter = std::shared_ptr<CSourceFilter>(new CSourceFilter(gc, grid, false, true, NoneDu, false,
1241                                                                                                      detectMissingValues, defaultValue, buildWorkflowGraph)); 
1242            instantDataFilter->tag = buildWorkflowGraph;
1243            // std::cout<<"=====4============= output_field_id = "<<this->getId()<<" =========== tag="<<instantDataFilter->tag<<std::endl;
1244            // std::cout<<"Source filter cout = "<<std::shared_ptr<COutputPin>(clientSourceFilter)<<std::endl;
1245         }
1246       }
1247
1248       // If the field data is to be read by the client or/and written to a file
1249       if (enableOutput && !storeFilter && !fileWriterFilter)
1250       {
1251         if (!read_access.isEmpty() && read_access)
1252         {
1253           storeFilter = std::shared_ptr<CStoreFilter>(new CStoreFilter(gc, CContext::getCurrent(), grid,
1254                                                                          detectMissingValues, defaultValue));
1255           instantDataFilter->connectOutput(storeFilter, 0);
1256
1257           storeFilter->tag = (instantDataFilter->tag || buildWorkflowGraph);
1258           instantDataFilter->setParentFiltersTag();
1259         }
1260
1261         if (file && (file->mode.isEmpty() || file->mode == CFile::mode_attr::write))
1262         {
1263           fileWriterFilter = std::shared_ptr<CFileWriterFilter>(new CFileWriterFilter(gc, this, buildWorkflowGraph));
1264           getTemporalDataFilter(gc, file->output_freq)->connectOutput(fileWriterFilter, 0);
1265           std::cout<<"=====10============= output_field_id = "<<this->name<<std::endl;
1266           // std::cout<<"CFileWriterFilter filter = "<<fileWriterFilter<<" parent filter = "<<std::shared_ptr<COutputPin>(getTemporalDataFilter(gc, file->output_freq))<<std::endl;
1267           
1268           fileWriterFilter->tag = (getTemporalDataFilter(gc, file->output_freq)->tag || buildWorkflowGraph);
1269           fileWriterFilter->output_field_id = this->name;
1270           getTemporalDataFilter(gc, file->output_freq)->setParentFiltersTag();
1271         }
1272       }
1273     }
1274   }
1275   CATCH_DUMP_ATTR
1276
1277   /*!
1278    * Returns the filter needed to handle the field reference.
1279    * This method should only be called when building the filter graph corresponding to the field.
1280    *
1281    * \param gc the garbage collector to use
1282    * \return the output pin corresponding to the field reference
1283    */
1284   std::shared_ptr<COutputPin> CField::getFieldReference(CGarbageCollector& gc)
1285   TRY
1286   {
1287     if (instantDataFilter || field_ref.isEmpty())
1288       ERROR("COutputPin* CField::getFieldReference(CGarbageCollector& gc)",
1289             "Impossible to get the field reference for a field which has already been parsed or which does not have a field_ref.");
1290
1291     CField* fieldRef = CField::get(field_ref);
1292     fieldRef->buildFilterGraph(gc, false);
1293     bool buildWorkflowGraph = (!build_workflow_graph.isEmpty() && build_workflow_graph == true);
1294
1295     std::pair<std::shared_ptr<CFilter>, std::shared_ptr<CFilter> > filters;
1296     // Check if a spatial transformation is needed
1297     if (grid && grid != fieldRef->grid && grid->hasTransform())
1298     {       
1299       bool hasMissingValue = (!detect_missing_value.isEmpty() && !default_value.isEmpty() && detect_missing_value == true);
1300       double defaultValue  = hasMissingValue ? default_value : (!default_value.isEmpty() ? default_value : 0.0);                               
1301       filters = CSpatialTransformFilter::buildFilterGraph(gc, fieldRef->grid, grid, hasMissingValue, defaultValue, buildWorkflowGraph);
1302
1303       fieldRef->getInstantDataFilter()->connectOutput(filters.first, 0);
1304
1305       filters.second->output_field_id = this->getId();
1306     
1307
1308       filters.second->parent_filters.resize(1);
1309       filters.second->parent_filters[0]= fieldRef->getInstantDataFilter();
1310
1311       filters.second->tag = (buildWorkflowGraph || filters.second->parent_filters[0]->tag);
1312
1313       // std::cout<<"=====5============= output_field_id = "<<this->getId()<<std::endl;
1314       // std::cout<<"CSpatialTransformFilter first filter cout = "<<std::shared_ptr<COutputPin>(filters.first)<<"  second filter cout = "<<std::shared_ptr<COutputPin>(filters.second)<< " parent filter = "<<fieldRef->getInstantDataFilter()<<std::endl;
1315       
1316     }
1317     else
1318     {
1319       filters.first = filters.second = std::shared_ptr<CFilter>(new CPassThroughFilter(gc, buildWorkflowGraph));
1320
1321       fieldRef->getInstantDataFilter()->connectOutput(filters.first, 0);
1322       filters.second->output_field_id = this->getId();
1323     
1324
1325       filters.second->parent_filters.resize(1);
1326       filters.second->parent_filters[0]= fieldRef->getInstantDataFilter();
1327
1328       filters.second->tag = buildWorkflowGraph || filters.second->parent_filters[0]->tag;
1329
1330       // std::cout<<"=====6============= output_field_id = "<<this->getId()<<" =========== tag="<<filters.second->tag<<std::endl;
1331       // std::cout<<"Pass through filter cout = "<<std::shared_ptr<COutputPin>(filters.second)<<" parent filter = "<<filters.second->parent_filters[0]<<std::endl;
1332
1333     }
1334
1335     return filters.second;
1336   }
1337   CATCH_DUMP_ATTR
1338
1339   /*!
1340    * Returns the filter needed to handle a self reference in the field's expression.
1341    * If the needed filter does not exist, it is created, otherwise it is reused.
1342    * This method should only be called when building the filter graph corresponding
1343    * to the field's expression.
1344    *
1345    * \param gc the garbage collector to use
1346    * \return the output pin corresponding to a self reference
1347    */
1348   std::shared_ptr<COutputPin> CField::getSelfReference(CGarbageCollector& gc)
1349   TRY
1350   {
1351     if (instantDataFilter || !hasExpression())
1352       ERROR("COutputPin* CField::getSelfReference(CGarbageCollector& gc)",
1353             "Impossible to add a self reference to a field which has already been parsed or which does not have an expression.");
1354     
1355     bool buildWorkflowGraph = (!build_workflow_graph.isEmpty() && build_workflow_graph == true);
1356
1357     if (!selfReferenceFilter)
1358     {
1359       const bool detectMissingValues = (!detect_missing_value.isEmpty() && !default_value.isEmpty() && detect_missing_value == true);
1360       const double defaultValue  = detectMissingValues ? default_value : (!default_value.isEmpty() ? default_value : 0.0);
1361
1362       if (file && !file->mode.isEmpty() && file->mode == CFile::mode_attr::read)
1363       {
1364         if (!serverSourceFilter)
1365         {
1366           checkTimeAttributes();
1367           serverSourceFilter = std::shared_ptr<CSourceFilter>(new CSourceFilter(gc, grid, true, false, freq_offset, true,
1368                                                                                   detectMissingValues, defaultValue));
1369         }
1370
1371         selfReferenceFilter = serverSourceFilter;
1372       }
1373       else if (!field_ref.isEmpty())
1374       {
1375         CField* fieldRef = CField::get(field_ref);
1376         fieldRef->buildFilterGraph(gc, false);
1377         selfReferenceFilter = fieldRef->getInstantDataFilter();
1378       }
1379       else
1380       {
1381         if (!clientSourceFilter)
1382         {
1383           if (check_if_active.isEmpty()) check_if_active = false;
1384           clientSourceFilter = std::shared_ptr<CSourceFilter>(new CSourceFilter(gc, grid, true, true, NoneDu, false,
1385                                                                                   detectMissingValues, defaultValue));
1386         }
1387
1388         selfReferenceFilter = clientSourceFilter;
1389       }   
1390     }
1391
1392     selfReferenceFilter->tag = buildWorkflowGraph;
1393     return selfReferenceFilter;
1394   }
1395   CATCH_DUMP_ATTR
1396
1397   /*!
1398    * Returns the temporal filter corresponding to the field's temporal operation
1399    * for the specified operation frequency. The filter is created if it does not
1400    * exist, otherwise it is reused.
1401    *
1402    * \param gc the garbage collector to use
1403    * \param outFreq the operation frequency, i.e. the frequency at which the output data will be computed
1404    * \return the output pin corresponding to the requested temporal filter
1405    */
1406   std::shared_ptr<COutputPin> CField::getTemporalDataFilter(CGarbageCollector& gc, CDuration outFreq)
1407   TRY
1408   {
1409     std::map<CDuration, std::shared_ptr<COutputPin> >::iterator it = temporalDataFilters.find(outFreq);
1410     bool buildWorkflowGraph = buildWorkflowGraph = (!build_workflow_graph.isEmpty() && build_workflow_graph == true);
1411
1412     if (it == temporalDataFilters.end())
1413     {
1414       if (operation.isEmpty())
1415         ERROR("void CField::getTemporalDataFilter(CGarbageCollector& gc, CDuration outFreq)",
1416               << "An operation must be defined for field \"" << getId() << "\".");
1417
1418       checkTimeAttributes(&outFreq);
1419
1420       const bool detectMissingValues = (!detect_missing_value.isEmpty()  && detect_missing_value == true);
1421       std::shared_ptr<CTemporalFilter> temporalFilter(new CTemporalFilter(gc, operation, CContext::getCurrent()->getCalendar()->getInitDate(),
1422                                                                             freq_op, freq_offset, outFreq,
1423                                                                             detectMissingValues, buildWorkflowGraph));
1424
1425       instantDataFilter->connectOutput(temporalFilter, 0);
1426       temporalFilter->output_field_id = this->getId();
1427       temporalFilter->tag = buildWorkflowGraph;
1428       
1429       temporalFilter->parent_filters.resize(1);
1430       temporalFilter->parent_filters[0] = instantDataFilter;
1431       
1432
1433       // std::cout<<"=====8============= output_field_id = "<<this->getId()<<" =========== tag="<<temporalFilter->tag<<std::endl;
1434       // std::cout<<"Temporal filter cout = "<<std::shared_ptr<COutputPin>(temporalFilter)<<" parent filter = "<<temporalFilter->parent_filters[0]<<std::endl;
1435       if(temporalFilter->parent_filters[0]->tag) temporalFilter->tag=true;
1436
1437       it = temporalDataFilters.insert(std::make_pair(outFreq, temporalFilter)).first;
1438     }
1439
1440     return it->second;
1441   }
1442   CATCH_DUMP_ATTR
1443
1444  /*!
1445    * Returns the temporal filter corresponding to the field's temporal operation
1446    * for the specified operation frequency.
1447    *
1448    * \param gc the garbage collector to use
1449    * \param outFreq the operation frequency, i.e. the frequency at which the output data will be computed
1450    * \return the output pin corresponding to the requested temporal filter
1451    */
1452   
1453   std::shared_ptr<COutputPin> CField::getSelfTemporalDataFilter(CGarbageCollector& gc, CDuration outFreq)
1454   TRY
1455   {
1456     if (instantDataFilter || !hasExpression())
1457       ERROR("COutputPin* CField::getSelfTemporalDataFilter(CGarbageCollector& gc)",
1458             "Impossible to add a self reference to a field which has already been parsed or which does not have an expression.");
1459
1460     if (!selfReferenceFilter) getSelfReference(gc) ;
1461
1462     if (serverSourceFilter || clientSourceFilter)
1463     {
1464       if (operation.isEmpty())
1465         ERROR("void CField::getSelfTemporalDataFilter(CGarbageCollector& gc, CDuration outFreq)",
1466               << "An operation must be defined for field \"" << getId() << "\".");
1467
1468       checkTimeAttributes(&outFreq);
1469
1470       const bool detectMissingValues = (!detect_missing_value.isEmpty() && detect_missing_value == true);
1471       bool buildWorkflowGraph = buildWorkflowGraph = (!build_workflow_graph.isEmpty() && build_workflow_graph == true);
1472       std::shared_ptr<CTemporalFilter> temporalFilter(new CTemporalFilter(gc, operation,
1473                                                                           CContext::getCurrent()->getCalendar()->getInitDate(),
1474                                                                           freq_op, freq_offset, outFreq,
1475                                                                           detectMissingValues, buildWorkflowGraph));
1476
1477       selfReferenceFilter->connectOutput(temporalFilter, 0);
1478       temporalFilter->output_field_id = this->getId();
1479       temporalFilter->tag = buildWorkflowGraph;
1480       // std::cout<<"=====9============= output_field_id = "<<this->getId()<<" =========== tag="<<temporalFilter->tag<<std::endl;
1481
1482       return temporalFilter ;
1483     }
1484     else if (!field_ref.isEmpty())
1485     {
1486       CField* fieldRef = CField::get(field_ref);
1487       fieldRef->buildFilterGraph(gc, false); 
1488       return fieldRef->getTemporalDataFilter(gc, outFreq) ;
1489     }
1490  }
1491   CATCH_DUMP_ATTR
1492
1493   //----------------------------------------------------------------
1494/*
1495   void CField::fromBinary(StdIStream& is)
1496   {
1497      SuperClass::fromBinary(is);
1498#define CLEAR_ATT(name_)\
1499      SuperClassAttribute::operator[](#name_)->reset()
1500
1501         CLEAR_ATT(domain_ref);
1502         CLEAR_ATT(axis_ref);
1503#undef CLEAR_ATT
1504
1505   }
1506*/
1507   //----------------------------------------------------------------
1508
1509   void CField::solveGridReference(void)
1510   TRY
1511   {
1512      if (grid_ref.isEmpty() && domain_ref.isEmpty() && axis_ref.isEmpty() && scalar_ref.isEmpty())
1513      {
1514        ERROR("CField::solveGridReference(void)",
1515              << "A grid must be defined for field '" << getFieldOutputName() << "' .");
1516      }
1517      else if (!grid_ref.isEmpty() && (!domain_ref.isEmpty() || !axis_ref.isEmpty() || !scalar_ref.isEmpty()))
1518      {
1519        ERROR("CField::solveGridReference(void)",
1520              << "Field '" << getFieldOutputName() << "' has both a grid and a domain/axis/scalar." << std::endl
1521              << "Please define either 'grid_ref' or 'domain_ref'/'axis_ref'/'scalar_ref'.");
1522      }
1523
1524      if (grid_ref.isEmpty())
1525      {
1526        std::vector<CDomain*> vecDom;
1527        std::vector<CAxis*> vecAxis;
1528        std::vector<CScalar*> vecScalar;
1529        std::vector<int> axisDomainOrderTmp;
1530
1531        std::vector<CDomain*> vecDomRef;
1532        std::vector<CAxis*> vecAxisRef;
1533        std::vector<CScalar*> vecScalarRef;
1534
1535       
1536        if (!domain_ref.isEmpty())
1537        {
1538          StdString tmp = domain_ref.getValue();
1539          if (CDomain::has(domain_ref))
1540          {
1541            vecDom.push_back(CDomain::get(domain_ref));
1542            vecDomRef.push_back(CDomain::createDomain());
1543            vecDomRef.back()->domain_ref=domain_ref;
1544            axisDomainOrderTmp.push_back(2);
1545          }
1546          else  ERROR("CField::solveGridReference(void)",
1547                      << "Invalid reference to domain '" << domain_ref.getValue() << "'.");
1548        }
1549
1550        if (!axis_ref.isEmpty())
1551        {
1552          if (CAxis::has(axis_ref))
1553          {
1554            vecAxis.push_back(CAxis::get(axis_ref));
1555            vecAxisRef.push_back(CAxis::createAxis());
1556            vecAxisRef.back()->axis_ref=axis_ref;
1557            axisDomainOrderTmp.push_back(1);
1558          }
1559          else  ERROR("CField::solveGridReference(void)",
1560                      << "Invalid reference to axis '" << axis_ref.getValue() << "'.");
1561        }
1562
1563        if (!scalar_ref.isEmpty())
1564        {
1565          if (CScalar::has(scalar_ref))
1566          {
1567            vecScalar.push_back(CScalar::get(scalar_ref));
1568            vecScalarRef.push_back(CScalar::createScalar());
1569            vecScalarRef.back()->scalar_ref=scalar_ref;
1570            axisDomainOrderTmp.push_back(0);
1571          }
1572          else ERROR("CField::solveGridReference(void)",
1573                     << "Invalid reference to scalar '" << scalar_ref.getValue() << "'.");
1574        }
1575       
1576        CArray<int,1> axisDomainOrder(axisDomainOrderTmp.size());
1577        for (int idx = 0; idx < axisDomainOrderTmp.size(); ++idx)
1578        {
1579          axisDomainOrder(idx) = axisDomainOrderTmp[idx];
1580        }
1581
1582        // Warning: the gridId shouldn't be set as the grid_ref since it could be inherited
1583        StdString gridId = CGrid::generateId(vecDom, vecAxis, vecScalar,axisDomainOrder);
1584        if (CGrid::has(gridId)) this->grid = CGrid::get(gridId);
1585        else  this->grid = CGrid::createGrid(gridId, vecDomRef, vecAxisRef, vecScalarRef,axisDomainOrder);
1586      }
1587      else
1588      {
1589        if (CGrid::has(grid_ref)) this->grid = CGrid::get(grid_ref);
1590        else  ERROR("CField::solveGridReference(void)",
1591                     << "Invalid reference to grid '" << grid_ref.getValue() << "'.");
1592      }
1593   }
1594   CATCH_DUMP_ATTR
1595
1596   void CField::solveGridDomainAxisRef(bool checkAtt)
1597   TRY
1598   {
1599     grid->solveDomainAxisRef(checkAtt);
1600   }
1601   CATCH_DUMP_ATTR
1602
1603   void CField::solveCheckMaskIndex(bool doSendingIndex)
1604   TRY
1605   {
1606     grid->checkMaskIndex(doSendingIndex);
1607   }
1608   CATCH_DUMP_ATTR
1609
1610   void CField::solveTransformedGrid()
1611   TRY
1612   {
1613     if (grid && !grid->isTransformed() && hasDirectFieldReference() && grid != getDirectFieldReference()->grid)
1614     {
1615       std::vector<CGrid*> grids;
1616       // Source grid
1617       grids.push_back(getDirectFieldReference()->grid);
1618       // Intermediate grids
1619       if (!grid_path.isEmpty())
1620       {
1621         std::string gridId;
1622         size_t start = 0, end;
1623
1624         do
1625         {
1626           end = grid_path.getValue().find(',', start);
1627           if (end != std::string::npos)
1628           {
1629             gridId = grid_path.getValue().substr(start, end - start);
1630             start = end + 1;
1631           }
1632           else
1633             gridId = grid_path.getValue().substr(start);
1634
1635           if (!CGrid::has(gridId))
1636             ERROR("void CField::solveTransformedGrid()",
1637                   << "Invalid grid_path, the grid '" << gridId << "' does not exist.");
1638
1639           grids.push_back(CGrid::get(gridId));
1640         }
1641         while (end != std::string::npos);
1642       }
1643       // Destination grid
1644       grids.push_back(grid);
1645
1646       for (size_t i = 0, count = grids.size() - 1; i < count; ++i)
1647       {
1648         CGrid *gridSrc  = grids[i];
1649         CGrid *gridDest = grids[i + 1];
1650         if (!gridDest->isTransformed())
1651           gridDest->transformGrid(gridSrc);
1652       }
1653     }
1654     else if (grid && grid->hasTransform() && !grid->isTransformed())
1655     {
1656       // Temporarily deactivate the self-transformation of grid
1657       // grid->transformGrid(grid);
1658     }
1659   }
1660   CATCH_DUMP_ATTR
1661
1662   void CField::solveGenerateGrid()
1663   TRY
1664   {
1665     if (grid && !grid->isTransformed() && hasDirectFieldReference() && grid != getDirectFieldReference()->grid)
1666       grid->completeGrid(getDirectFieldReference()->grid);
1667     else
1668       grid->completeGrid();
1669   }
1670   CATCH_DUMP_ATTR
1671
1672   void CField::solveGridDomainAxisBaseRef()
1673   TRY
1674   {
1675     grid->solveDomainAxisRef(false);
1676     grid->solveDomainAxisBaseRef();
1677   }
1678   CATCH_DUMP_ATTR
1679
1680   ///-------------------------------------------------------------------
1681
1682   template <>
1683   void CGroupTemplate<CField, CFieldGroup, CFieldAttributes>::solveRefInheritance(void)
1684   TRY
1685   {
1686      if (this->group_ref.isEmpty()) return;
1687      StdString gref = this->group_ref.getValue();
1688
1689      if (!CFieldGroup::has(gref))
1690         ERROR("CGroupTemplate<CField, CFieldGroup, CFieldAttributes>::solveRefInheritance(void)",
1691               << "[ gref = " << gref << "]"
1692               << " invalid group name !");
1693
1694      CFieldGroup* group = CFieldGroup::get(gref);
1695      CFieldGroup* owner = CFieldGroup::get(boost::polymorphic_downcast<CFieldGroup*>(this));
1696      owner->setAttributes(group); // inherite of attributes of group reference
1697     
1698      std::vector<CField*> allChildren  = group->getAllChildren();
1699      std::vector<CField*>::iterator it = allChildren.begin(), end = allChildren.end();
1700
1701      for (; it != end; it++)
1702      {
1703         CField* child = *it;
1704         if (child->hasId()) owner->createChild()->field_ref.setValue(child->getId());
1705
1706      }
1707   }
1708   CATCH_DUMP_ATTR
1709
1710   void CField::scaleFactorAddOffset(double scaleFactor, double addOffset)
1711   TRY
1712   {
1713     recvDataSrv = (recvDataSrv - addOffset) / scaleFactor;
1714   }
1715   CATCH_DUMP_ATTR
1716
1717   void CField::invertScaleFactorAddOffset(double scaleFactor, double addOffset)
1718   TRY
1719   {
1720     recvDataSrv = recvDataSrv * scaleFactor + addOffset;
1721   }
1722   CATCH_DUMP_ATTR
1723
1724   void CField::outputField(CArray<double,1>& fieldOut)
1725   TRY
1726   { 
1727      CArray<size_t,1>& outIndexClient = grid->localIndexToWriteOnClient;
1728      CArray<size_t,1>& outIndexServer = grid->localIndexToWriteOnServer;
1729      for (size_t idx = 0; idx < outIndexServer.numElements(); ++idx)
1730      {
1731        fieldOut(outIndexServer(idx)) = recvDataSrv(outIndexClient(idx));
1732      }
1733   }
1734   CATCH_DUMP_ATTR
1735
1736   void CField::inputField(CArray<double,1>& fieldIn)
1737   TRY
1738   {
1739      CArray<size_t,1>& outIndexClient = grid->localIndexToWriteOnClient;
1740      CArray<size_t,1>& outIndexServer = grid->localIndexToWriteOnServer;
1741      for (size_t idx = 0; idx < outIndexServer.numElements(); ++idx)
1742      {
1743        recvDataSrv(outIndexClient(idx)) = fieldIn(outIndexServer(idx));
1744      }
1745   }
1746   CATCH_DUMP_ATTR
1747
1748   void CField::outputCompressedField(CArray<double,1>& fieldOut)
1749   TRY
1750   {
1751      CArray<size_t,1>& outIndexClient = grid->localIndexToWriteOnClient;
1752      CArray<size_t,1>& outIndexServer = grid->localIndexToWriteOnServer;
1753      for (size_t idx = 0; idx < outIndexServer.numElements(); ++idx)
1754      {
1755        fieldOut((idx)) = recvDataSrv(outIndexClient(idx));
1756      }
1757   }
1758   CATCH_DUMP_ATTR
1759
1760   ///-------------------------------------------------------------------
1761
1762   void CField::parse(xml::CXMLNode& node)
1763   TRY
1764   {
1765      string newContent ;
1766      SuperClass::parse(node);
1767      if (node.goToChildElement())
1768      {
1769        do
1770        {
1771          if (node.getElementName() == "variable" || node.getElementName() == "variable_group") this->getVirtualVariableGroup()->parseChild(node);
1772          else if (node.getElementName() == "expr") if (node.getContent(newContent)) content+=newContent ;
1773        } while (node.goToNextElement());
1774        node.goToParentElement();
1775      }
1776      if (node.getContent(newContent)) content=newContent ;
1777    }
1778   CATCH_DUMP_ATTR
1779
1780   /*!
1781     This function retrieves Id of corresponding domain_ref and axis_ref (if any)
1782   of a field. In some cases, only domain exists but axis doesn't
1783   \return pair of Domain and Axis id
1784   */
1785   const std::vector<StdString>& CField::getRefDomainAxisIds()
1786   TRY
1787   {
1788     CGrid* cgPtr = getRelGrid();
1789     if (NULL != cgPtr)
1790     {
1791       std::vector<StdString>::iterator it;
1792       if (!domain_ref.isEmpty())
1793       {
1794         std::vector<StdString> domainList = cgPtr->getDomainList();
1795         it = std::find(domainList.begin(), domainList.end(), domain_ref.getValue());
1796         if (domainList.end() != it) domAxisScalarIds_[0] = *it;
1797       }
1798
1799       if (!axis_ref.isEmpty())
1800       {
1801         std::vector<StdString> axisList = cgPtr->getAxisList();
1802         it = std::find(axisList.begin(), axisList.end(), axis_ref.getValue());
1803         if (axisList.end() != it) domAxisScalarIds_[1] = *it;
1804       }
1805
1806       if (!scalar_ref.isEmpty())
1807       {
1808         std::vector<StdString> scalarList = cgPtr->getScalarList();
1809         it = std::find(scalarList.begin(), scalarList.end(), scalar_ref.getValue());
1810         if (scalarList.end() != it) domAxisScalarIds_[2] = *it;
1811       }
1812     }
1813     return (domAxisScalarIds_);
1814   }
1815   CATCH_DUMP_ATTR
1816
1817   CVariable* CField::addVariable(const string& id)
1818   TRY
1819   {
1820     return vVariableGroup->createChild(id);
1821   }
1822   CATCH
1823
1824   CVariableGroup* CField::addVariableGroup(const string& id)
1825   TRY
1826   {
1827     return vVariableGroup->createChildGroup(id);
1828   }
1829   CATCH
1830
1831   void CField::setContextClient(CContextClient* contextClient)
1832   TRY
1833   {
1834     CContext* context = CContext::getCurrent();
1835     client = contextClient;
1836     if (context->hasClient)
1837     {
1838       // A grid is sent by a client (both for read or write) or by primary server (write only)
1839       if (context->hasServer)
1840       {
1841         if (file->mode.isEmpty() || (!file->mode.isEmpty() && file->mode == CFile::mode_attr::write))
1842           grid->setContextClient(contextClient);
1843       }
1844       else
1845           grid->setContextClient(contextClient);
1846     }
1847   }
1848   CATCH_DUMP_ATTR
1849
1850   CContextClient* CField::getContextClient()
1851   TRY
1852   {
1853     return client;
1854   }
1855   CATCH
1856
1857   void CField::sendAddAllVariables(CContextClient* client)
1858   TRY
1859   {
1860     std::vector<CVariable*> allVar = getAllVariables();
1861     std::vector<CVariable*>::const_iterator it = allVar.begin();
1862     std::vector<CVariable*>::const_iterator itE = allVar.end();
1863
1864     for (; it != itE; ++it)
1865     {
1866       this->sendAddVariable((*it)->getId(), client);
1867       (*it)->sendAllAttributesToServer(client);
1868       (*it)->sendValue(client);
1869     }
1870   }
1871   CATCH_DUMP_ATTR
1872
1873   /*!
1874    * Send all Attributes to server. This method is overloaded, since only grid_ref attribute
1875    * must be sent to server and not domain_ref/axis_ref/scalar_ref.
1876    */
1877   
1878   void CField::sendAllAttributesToServer(CContextClient* client)
1879   TRY
1880   {
1881     if (grid_ref.isEmpty())
1882     {
1883       grid_ref=grid->getId() ;
1884       SuperClass::sendAllAttributesToServer(client) ;
1885       grid_ref.reset();
1886     }
1887     else SuperClass::sendAllAttributesToServer(client) ;
1888   }
1889   CATCH_DUMP_ATTR
1890   
1891   void CField::sendAddVariable(const string& id, CContextClient* client)
1892   TRY
1893   {
1894      sendAddItem(id, (int)EVENT_ID_ADD_VARIABLE, client);
1895   }
1896   CATCH_DUMP_ATTR
1897
1898   void CField::sendAddVariableGroup(const string& id, CContextClient* client)
1899   TRY
1900   {
1901      sendAddItem(id, (int)EVENT_ID_ADD_VARIABLE_GROUP, client);
1902   }
1903   CATCH_DUMP_ATTR
1904
1905   void CField::recvAddVariable(CEventServer& event)
1906   TRY
1907   {
1908
1909      CBufferIn* buffer = event.subEvents.begin()->buffer;
1910      string id;
1911      *buffer >> id;
1912      get(id)->recvAddVariable(*buffer);
1913   }
1914   CATCH
1915
1916   void CField::recvAddVariable(CBufferIn& buffer)
1917   TRY
1918   {
1919      string id;
1920      buffer >> id;
1921      addVariable(id);
1922   }
1923   CATCH_DUMP_ATTR
1924
1925   void CField::recvAddVariableGroup(CEventServer& event)
1926   TRY
1927   {
1928
1929      CBufferIn* buffer = event.subEvents.begin()->buffer;
1930      string id;
1931      *buffer >> id;
1932      get(id)->recvAddVariableGroup(*buffer);
1933   }
1934   CATCH
1935
1936   void CField::recvAddVariableGroup(CBufferIn& buffer)
1937   TRY
1938   {
1939      string id;
1940      buffer >> id;
1941      addVariableGroup(id);
1942   }
1943   CATCH_DUMP_ATTR
1944
1945   /*!
1946    * Check on freq_off and freq_op attributes.
1947    */
1948   void CField::checkTimeAttributes(CDuration* freqOp)
1949   TRY
1950   {
1951     bool isFieldRead  = file && !file->mode.isEmpty() && file->mode == CFile::mode_attr::read;
1952     bool isFieldWrite = file && ( file->mode.isEmpty() ||  file->mode == CFile::mode_attr::write);
1953     if (isFieldRead && !(operation.getValue() == "instant" || operation.getValue() == "once") )     
1954       ERROR("void CField::checkTimeAttributes(void)",
1955             << "Unsupported operation for field '" << getFieldOutputName() << "'." << std::endl
1956             << "Currently only \"instant\" is supported for fields read from file.")
1957
1958     if (freq_op.isEmpty())
1959     {
1960       if (operation.getValue() == "instant")
1961       {
1962         if (isFieldRead || isFieldWrite) freq_op.setValue(file->output_freq.getValue());
1963         else freq_op=*freqOp ;
1964       }
1965       else
1966         freq_op.setValue(TimeStep);
1967     }
1968     if (freq_offset.isEmpty())
1969       freq_offset.setValue(isFieldRead ? NoneDu : (freq_op.getValue() - TimeStep));
1970   }
1971   CATCH_DUMP_ATTR
1972
1973   /*!
1974    * Returns string arithmetic expression associated to the field.
1975    * \return if content is defined return content string, otherwise, if "expr" attribute is defined, return expr string.
1976    */
1977   const string& CField::getExpression(void)
1978   TRY
1979   {
1980     if (!expr.isEmpty() && content.empty())
1981     {
1982       content = expr;
1983       expr.reset();
1984     }
1985
1986     return content;
1987   }
1988   CATCH_DUMP_ATTR
1989
1990   bool CField::hasExpression(void) const
1991   TRY
1992   {
1993     return (!expr.isEmpty() || !content.empty());
1994   }
1995   CATCH
1996
1997   bool CField::hasGridMask(void) const
1998   TRY
1999   {
2000     return (this->grid->hasMask());
2001   }
2002   CATCH
2003
2004   DEFINE_REF_FUNC(Field,field)
2005} // namespace xios
Note: See TracBrowser for help on using the repository browser.