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

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

MARK: Dynamic workflow graph developement. Branch up to date with trunk @1676. Arithmetic filter unified

  • 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: 65.1 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
925     CContext* context = CContext::getCurrent();
926     bool hasClient = context->hasClient;
927     bool hasServer = context->hasServer;
928
929     if (!isReferenceSolvedAndTransformed)
930     {
931        isReferenceSolvedAndTransformed = true;
932
933        if (hasClient && !hasServer)
934        {
935          solveRefInheritance(true);
936          if (hasDirectFieldReference()) getDirectFieldReference()->solveAllEnabledFieldsAndTransform();
937        }
938
939        if (hasServer)
940          solveServerOperation();
941
942        solveGridReference();
943
944        if (hasClient && !hasServer)
945       {
946         solveGenerateGrid();
947         buildGridTransformationGraph();
948       }
949
950       solveGridDomainAxisRef(false);
951
952       if (hasClient && !hasServer)
953       {
954         solveTransformedGrid();
955       }
956
957       solveGridDomainAxisRef(false);
958     }
959   }
960   CATCH_DUMP_ATTR
961
962   void CField::checkGridOfEnabledFields()
963   TRY
964   {
965     if (!isGridChecked)
966     {
967       isGridChecked = true;
968       solveCheckMaskIndex(false);
969     }
970   }
971   CATCH_DUMP_ATTR
972
973   void CField::sendGridComponentOfEnabledFields()
974   TRY
975   {
976      solveGridDomainAxisRef(true);
977      // solveCheckMaskIndex(true);
978   }
979   CATCH_DUMP_ATTR
980
981   void CField::sendGridOfEnabledFields()
982   TRY
983   {
984      // solveGridDomainAxisRef(true);
985      solveCheckMaskIndex(true);
986   }   
987   CATCH_DUMP_ATTR
988
989   void CField::solveOnlyReferenceEnabledField(bool doSending2Server)
990   TRY
991   {
992     CContext* context = CContext::getCurrent();
993     if (!isReferenceSolved)
994     {
995        isReferenceSolved = true;
996
997        if (context->hasClient && !context->hasServer)
998        {
999          solveRefInheritance(true);
1000          if (hasDirectFieldReference()) getDirectFieldReference()->solveOnlyReferenceEnabledField(false);
1001        }
1002
1003        if (context->hasServer)
1004          solveServerOperation();
1005
1006        solveGridReference();
1007        grid->solveDomainAxisRefInheritance(true); // make it again to solve grid reading from file
1008
1009        if (context->hasClient && !context->hasServer)
1010       {
1011         solveGenerateGrid();
1012         buildGridTransformationGraph();
1013       }
1014     }
1015   }
1016   CATCH_DUMP_ATTR
1017
1018   void CField::solveAllReferenceEnabledField(bool doSending2Server)
1019   TRY
1020   {
1021     CContext* context = CContext::getCurrent();
1022     solveOnlyReferenceEnabledField(doSending2Server);
1023
1024     if (!areAllReferenceSolved)
1025     {
1026        areAllReferenceSolved = true;
1027       
1028        if (context->hasClient && !context->hasServer)
1029        {
1030          solveRefInheritance(true);
1031          if (hasDirectFieldReference()) getDirectFieldReference()->solveAllReferenceEnabledField(false);
1032        }
1033        else if (context->hasServer)
1034          solveServerOperation();
1035
1036        solveGridReference();
1037     }
1038
1039     solveGridDomainAxisRef(doSending2Server);
1040
1041     if (context->hasClient && !context->hasServer)
1042     {
1043       solveTransformedGrid();
1044     }
1045
1046     solveCheckMaskIndex(doSending2Server);
1047   }
1048   CATCH_DUMP_ATTR
1049
1050   std::map<int, StdSize> CField::getGridAttributesBufferSize(CContextClient* client, bool bufferForWriting /*= "false"*/)
1051   TRY
1052   {
1053     return grid->getAttributesBufferSize(client, bufferForWriting);
1054   }
1055   CATCH_DUMP_ATTR
1056
1057   std::map<int, StdSize> CField::getGridDataBufferSize(CContextClient* client, bool bufferForWriting /*= "false"*/)
1058   TRY
1059   {
1060     return grid->getDataBufferSize(client, getId(), bufferForWriting);
1061   }
1062   CATCH_DUMP_ATTR
1063
1064   size_t CField::getGlobalWrittenSize()
1065   TRY
1066   {
1067     return grid->getGlobalWrittenSize();
1068   }
1069   CATCH_DUMP_ATTR
1070
1071   //----------------------------------------------------------------
1072
1073   void CField::solveServerOperation(void)
1074   TRY
1075   {
1076      CContext* context = CContext::getCurrent();
1077
1078      if (!context->hasServer || !hasOutputFile) return;
1079
1080      if (freq_op.isEmpty())
1081        freq_op.setValue(TimeStep);
1082
1083      if (freq_offset.isEmpty())
1084        freq_offset.setValue(NoneDu);
1085
1086      freq_operation_srv = file->output_freq.getValue();
1087      freq_write_srv     = file->output_freq.getValue();
1088
1089      lastlast_Write_srv = context->getCalendar()->getInitDate();
1090      last_Write_srv     = context->getCalendar()->getInitDate();
1091      last_operation_srv = context->getCalendar()->getInitDate();
1092
1093      const CDuration toffset = freq_operation_srv - freq_offset.getValue() - context->getCalendar()->getTimeStep();
1094      last_operation_srv     = last_operation_srv - toffset;
1095
1096      if (operation.isEmpty())
1097        ERROR("void CField::solveServerOperation(void)",
1098              << "An operation must be defined for field \"" << getId() << "\".");
1099
1100      std::shared_ptr<func::CFunctor> functor;
1101      CArray<double, 1> dummyData;
1102
1103#define DECLARE_FUNCTOR(MType, mtype) \
1104      if (operation.getValue().compare(#mtype) == 0) \
1105      { \
1106        functor.reset(new func::C##MType(dummyData)); \
1107      }
1108
1109#include "functor_type.conf"
1110
1111      if (!functor)
1112        ERROR("void CField::solveServerOperation(void)",
1113              << "\"" << operation << "\" is not a valid operation.");
1114
1115      operationTimeType = functor->timeType();
1116   }
1117   CATCH_DUMP_ATTR
1118
1119   //----------------------------------------------------------------
1120
1121   /*!
1122    * Constructs the graph filter for the field, enabling or not the data output.
1123    * This method should not be called more than once with enableOutput equal to true.
1124    *
1125    * \param gc the garbage collector to use when building the filter graph
1126    * \param enableOutput must be true when the field data is to be
1127    *                     read by the client or/and written to a file
1128    */
1129   void CField::buildFilterGraph(CGarbageCollector& gc, bool enableOutput)
1130   TRY
1131   {     
1132     if (!isReferenceSolvedAndTransformed) solveAllEnabledFieldsAndTransform();
1133     if (!isGridChecked) checkGridOfEnabledFields();
1134
1135     const bool detectMissingValues = (!detect_missing_value.isEmpty() && !default_value.isEmpty() && detect_missing_value == true);
1136     
1137     const bool buildWorkflowGraph = (!build_workflow_graph.isEmpty() && build_workflow_graph == true);
1138
1139     
1140     const double defaultValue  = detectMissingValues ? default_value : (!default_value.isEmpty() ? default_value : 0.0);
1141
1142     CContext* context = CContext::getCurrent();
1143     Time filter_start;
1144     if(!build_workflow_graph_start.isEmpty() && buildWorkflowGraph) filter_start = context->calendar->getInitDate()+build_workflow_graph_start;
1145     else if(build_workflow_graph_start.isEmpty() && buildWorkflowGraph) filter_start = 0;
1146     else filter_start = -1;
1147
1148     Time filter_end;
1149     if(!build_workflow_graph_end.isEmpty() && buildWorkflowGraph) filter_end = context->calendar->getInitDate()+build_workflow_graph_end;
1150     else if(build_workflow_graph_end.isEmpty() && buildWorkflowGraph) filter_end = 9223372036854775807;
1151     else filter_end = -1;
1152     
1153
1154     bool hasWriterServer = context->hasServer && !context->hasClient;
1155     bool hasIntermediateServer = context->hasServer && context->hasClient;
1156
1157     if (hasWriterServer)
1158     {
1159        if (!instantDataFilter)
1160          instantDataFilter = clientSourceFilter = std::shared_ptr<CSourceFilter>(new CSourceFilter(gc, grid, true, false));
1161
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           fileServerWriterFilter = std::shared_ptr<CFileServerWriterFilter>(new CFileServerWriterFilter(gc, this));
1169           instantDataFilter->connectOutput(fileServerWriterFilter, 0);
1170         }
1171       }
1172     }
1173     else if (hasIntermediateServer)
1174     {
1175       if (!instantDataFilter)
1176         instantDataFilter = clientSourceFilter = std::shared_ptr<CSourceFilter>(new CSourceFilter(gc, grid, false, false));
1177
1178             // If the field data is to be read by the client or/and written to a file
1179       if (enableOutput && !storeFilter && !fileWriterFilter)
1180       {
1181         if (file && (file->mode.isEmpty() || file->mode == CFile::mode_attr::write))
1182         {
1183           fileWriterFilter = std::shared_ptr<CFileWriterFilter>(new CFileWriterFilter(gc, this));
1184           instantDataFilter->connectOutput(fileWriterFilter, 0);       
1185         }
1186       }
1187     }
1188     else
1189     {
1190       // Start by building a filter which can provide the field's instant data
1191       if (!instantDataFilter)
1192       {
1193         // Check if we have an expression to parse
1194         if (hasExpression())
1195         {
1196           boost::scoped_ptr<IFilterExprNode> expr(parseExpr(getExpression() + '\0'));
1197           std::shared_ptr<COutputPin> filter = expr->reduce(gc, *this);
1198
1199           // Check if a spatial transformation is needed
1200           if (!field_ref.isEmpty())
1201           {
1202             CField* fieldRef = CField::get(field_ref);
1203             CGrid* gridRef = fieldRef->grid;
1204
1205             if (grid && grid != gridRef && grid->hasTransform())
1206             {
1207               std::pair<std::shared_ptr<CFilter>, std::shared_ptr<CFilter> > filters = CSpatialTransformFilter::buildFilterGraph(gc, gridRef, grid,
1208                                                                                         detectMissingValues, defaultValue);
1209
1210               filter->connectOutput(filters.first, 0);
1211
1212
1213               filter = filters.second;
1214               
1215             }
1216           }
1217
1218           instantDataFilter = filter;
1219           instantDataFilter->field = this;
1220           filter->tag = buildWorkflowGraph;
1221           
1222           filter->start_graph = filter_start;
1223           filter->end_graph = filter_end;
1224
1225           for(int i=0; i<filter->parent_filters.size(); i++)
1226           {
1227             filter->tag = filter->tag || filter->parent_filters[i]->tag;
1228           }
1229           
1230           
1231
1232
1233
1234         }
1235         // Check if we have a reference on another field
1236         else if (!field_ref.isEmpty())
1237         {
1238           instantDataFilter = getFieldReference(gc);
1239           instantDataFilter->tag = buildWorkflowGraph;
1240           instantDataFilter->start_graph = filter_start;
1241           instantDataFilter->end_graph = filter_end;
1242         }
1243         // Check if the data is to be read from a file
1244         else if (file && !file->mode.isEmpty() && file->mode == CFile::mode_attr::read)
1245         {
1246           checkTimeAttributes();
1247           instantDataFilter = serverSourceFilter = std::shared_ptr<CSourceFilter>(new CSourceFilter(gc, grid, true, false, freq_offset, true,
1248                                                                                                       detectMissingValues, defaultValue));
1249           instantDataFilter->tag = buildWorkflowGraph;
1250           instantDataFilter->start_graph = filter_start;
1251           instantDataFilter->end_graph = filter_end;
1252           instantDataFilter->field = this;
1253
1254         }
1255         else // The data might be passed from the model
1256         {
1257            if (check_if_active.isEmpty()) check_if_active = false; 
1258            instantDataFilter = clientSourceFilter = std::shared_ptr<CSourceFilter>(new CSourceFilter(gc, grid, false, true, NoneDu, false,
1259                                                                                                      detectMissingValues, defaultValue)); 
1260            instantDataFilter->tag = buildWorkflowGraph;
1261            instantDataFilter->start_graph = filter_start;
1262            instantDataFilter->end_graph = filter_end;
1263            instantDataFilter->field = this;
1264         }
1265       }
1266
1267       // If the field data is to be read by the client or/and written to a file
1268       if (enableOutput && !storeFilter && !fileWriterFilter)
1269       {
1270         if (!read_access.isEmpty() && read_access)
1271         {
1272           storeFilter = std::shared_ptr<CStoreFilter>(new CStoreFilter(gc, CContext::getCurrent(), grid,
1273                                                                          detectMissingValues, defaultValue));
1274           instantDataFilter->connectOutput(storeFilter, 0);
1275
1276           storeFilter->tag = (instantDataFilter->tag || buildWorkflowGraph);
1277           instantDataFilter->start_graph = filter_start;
1278           instantDataFilter->end_graph = filter_end;
1279
1280           instantDataFilter->setParentFiltersTag();
1281           storeFilter->start_graph = filter_start;
1282           storeFilter->end_graph = filter_end;
1283           storeFilter->field = this;
1284         }
1285
1286         if (file && (file->mode.isEmpty() || file->mode == CFile::mode_attr::write))
1287         {
1288           fileWriterFilter = std::shared_ptr<CFileWriterFilter>(new CFileWriterFilter(gc, this));
1289           getTemporalDataFilter(gc, file->output_freq)->connectOutput(fileWriterFilter, 0);
1290           
1291           fileWriterFilter->tag = (getTemporalDataFilter(gc, file->output_freq)->tag || buildWorkflowGraph);
1292           getTemporalDataFilter(gc, file->output_freq)->start_graph = filter_start;
1293           getTemporalDataFilter(gc, file->output_freq)->end_graph = filter_end;
1294           getTemporalDataFilter(gc, file->output_freq)->setParentFiltersTag();
1295           fileWriterFilter->start_graph = filter_start;
1296           fileWriterFilter->end_graph = filter_end;
1297
1298           // std::cout<<"CFileWriterFilter filter start = "<<filter_start<<" end = "<<filter_end<<" field = "<<this->getId()<<std::endl;
1299
1300         }
1301       }
1302     }
1303   }
1304   CATCH_DUMP_ATTR
1305
1306   /*!
1307    * Returns the filter needed to handle the field reference.
1308    * This method should only be called when building the filter graph corresponding to the field.
1309    *
1310    * \param gc the garbage collector to use
1311    * \return the output pin corresponding to the field reference
1312    */
1313   std::shared_ptr<COutputPin> CField::getFieldReference(CGarbageCollector& gc)
1314   TRY
1315   {
1316     if (instantDataFilter || field_ref.isEmpty())
1317       ERROR("COutputPin* CField::getFieldReference(CGarbageCollector& gc)",
1318             "Impossible to get the field reference for a field which has already been parsed or which does not have a field_ref.");
1319
1320     CField* fieldRef = CField::get(field_ref);
1321     fieldRef->buildFilterGraph(gc, false);
1322     const bool buildWorkflowGraph = (!build_workflow_graph.isEmpty() && build_workflow_graph == true);
1323
1324     CContext* context = CContext::getCurrent();
1325
1326     Time filter_start;
1327     if(!build_workflow_graph_start.isEmpty() && buildWorkflowGraph) filter_start = context->calendar->getInitDate()+build_workflow_graph_start;
1328     else if(build_workflow_graph_start.isEmpty() && buildWorkflowGraph) filter_start = 0;
1329     else filter_start = -1;
1330
1331     Time filter_end;
1332     if(!build_workflow_graph_end.isEmpty() && buildWorkflowGraph) filter_end = context->calendar->getInitDate()+build_workflow_graph_end;
1333     else if(build_workflow_graph_end.isEmpty() && buildWorkflowGraph) filter_end = 9223372036854775807;
1334     else filter_end = -1;
1335
1336
1337     std::pair<std::shared_ptr<CFilter>, std::shared_ptr<CFilter> > filters;
1338     // Check if a spatial transformation is needed
1339     if (grid && grid != fieldRef->grid && grid->hasTransform())
1340     {       
1341       bool hasMissingValue = (!detect_missing_value.isEmpty() && !default_value.isEmpty() && detect_missing_value == true);
1342       double defaultValue  = hasMissingValue ? default_value : (!default_value.isEmpty() ? default_value : 0.0);                               
1343       filters = CSpatialTransformFilter::buildFilterGraph(gc, fieldRef->grid, grid, hasMissingValue, defaultValue);
1344
1345       fieldRef->getInstantDataFilter()->connectOutput(filters.first, 0);
1346
1347     
1348
1349       filters.second->parent_filters.resize(1);
1350       filters.second->parent_filters[0]= fieldRef->getInstantDataFilter();
1351
1352       filters.second->tag = (buildWorkflowGraph || filters.second->parent_filters[0]->tag);
1353       
1354       filters.second->start_graph = filter_start;
1355       filters.second->end_graph = filter_end;
1356       filters.second->field = this;
1357       
1358     }
1359     else
1360     {
1361       filters.first = filters.second = std::shared_ptr<CFilter>(new CPassThroughFilter(gc));
1362
1363       fieldRef->getInstantDataFilter()->connectOutput(filters.first, 0);
1364     
1365
1366       filters.second->parent_filters.resize(1);
1367       filters.second->parent_filters[0]= fieldRef->getInstantDataFilter();
1368
1369       filters.second->tag = (buildWorkflowGraph || filters.second->parent_filters[0]->tag);
1370
1371       filters.second->start_graph = filter_start;
1372       filters.second->end_graph = filter_end;
1373       filters.second->field = this;
1374
1375     }
1376
1377     return filters.second;
1378   }
1379   CATCH_DUMP_ATTR
1380
1381   /*!
1382    * Returns the filter needed to handle a self reference in the field's expression.
1383    * If the needed filter does not exist, it is created, otherwise it is reused.
1384    * This method should only be called when building the filter graph corresponding
1385    * to the field's expression.
1386    *
1387    * \param gc the garbage collector to use
1388    * \return the output pin corresponding to a self reference
1389    */
1390   std::shared_ptr<COutputPin> CField::getSelfReference(CGarbageCollector& gc)
1391   TRY
1392   {
1393     if (instantDataFilter || !hasExpression())
1394       ERROR("COutputPin* CField::getSelfReference(CGarbageCollector& gc)",
1395             "Impossible to add a self reference to a field which has already been parsed or which does not have an expression.");
1396     
1397     bool buildWorkflowGraph = (!build_workflow_graph.isEmpty() && build_workflow_graph == true);
1398
1399     if (!selfReferenceFilter)
1400     {
1401       const bool detectMissingValues = (!detect_missing_value.isEmpty() && !default_value.isEmpty() && detect_missing_value == true);
1402       const double defaultValue  = detectMissingValues ? default_value : (!default_value.isEmpty() ? default_value : 0.0);
1403
1404       if (file && !file->mode.isEmpty() && file->mode == CFile::mode_attr::read)
1405       {
1406         if (!serverSourceFilter)
1407         {
1408           checkTimeAttributes();
1409           serverSourceFilter = std::shared_ptr<CSourceFilter>(new CSourceFilter(gc, grid, true, false, freq_offset, true,
1410                                                                                   detectMissingValues, defaultValue));
1411         }
1412
1413         selfReferenceFilter = serverSourceFilter;
1414       }
1415       else if (!field_ref.isEmpty())
1416       {
1417         CField* fieldRef = CField::get(field_ref);
1418         fieldRef->buildFilterGraph(gc, false);
1419         selfReferenceFilter = fieldRef->getInstantDataFilter();
1420       }
1421       else
1422       {
1423         if (!clientSourceFilter)
1424         {
1425           if (check_if_active.isEmpty()) check_if_active = false;
1426           clientSourceFilter = std::shared_ptr<CSourceFilter>(new CSourceFilter(gc, grid, true, true, NoneDu, false,
1427                                                                                   detectMissingValues, defaultValue));
1428         }
1429
1430         selfReferenceFilter = clientSourceFilter;
1431       }   
1432     }
1433
1434     selfReferenceFilter->tag = buildWorkflowGraph;
1435     selfReferenceFilter->field = this;
1436     return selfReferenceFilter;
1437   }
1438   CATCH_DUMP_ATTR
1439
1440   /*!
1441    * Returns the temporal filter corresponding to the field's temporal operation
1442    * for the specified operation frequency. The filter is created if it does not
1443    * exist, otherwise it is reused.
1444    *
1445    * \param gc the garbage collector to use
1446    * \param outFreq the operation frequency, i.e. the frequency at which the output data will be computed
1447    * \return the output pin corresponding to the requested temporal filter
1448    */
1449   std::shared_ptr<COutputPin> CField::getTemporalDataFilter(CGarbageCollector& gc, CDuration outFreq)
1450   TRY
1451   {
1452     std::map<CDuration, std::shared_ptr<COutputPin> >::iterator it = temporalDataFilters.find(outFreq);
1453     const bool buildWorkflowGraph = (!build_workflow_graph.isEmpty() && build_workflow_graph == true);
1454
1455     CContext* context = CContext::getCurrent();
1456
1457     Time filter_start;
1458     if(!build_workflow_graph_start.isEmpty() && buildWorkflowGraph) filter_start = context->calendar->getInitDate()+build_workflow_graph_start;
1459     else if(build_workflow_graph_start.isEmpty() && buildWorkflowGraph) filter_start = 0;
1460     else filter_start = -1;
1461
1462     Time filter_end;
1463     if(!build_workflow_graph_end.isEmpty() && buildWorkflowGraph) filter_end = context->calendar->getInitDate()+build_workflow_graph_end;
1464     else if(build_workflow_graph_end.isEmpty() && buildWorkflowGraph) filter_end = 9223372036854775807;
1465     else filter_end = -1;
1466
1467
1468
1469     if (it == temporalDataFilters.end())
1470     {
1471       if (operation.isEmpty())
1472         ERROR("void CField::getTemporalDataFilter(CGarbageCollector& gc, CDuration outFreq)",
1473               << "An operation must be defined for field \"" << getId() << "\".");
1474
1475       checkTimeAttributes(&outFreq);
1476
1477       const bool detectMissingValues = (!detect_missing_value.isEmpty()  && detect_missing_value == true);
1478       std::shared_ptr<CTemporalFilter> temporalFilter(new CTemporalFilter(gc, operation, CContext::getCurrent()->getCalendar()->getInitDate(),
1479                                                                             freq_op, freq_offset, outFreq,
1480                                                                             detectMissingValues));
1481
1482       instantDataFilter->connectOutput(temporalFilter, 0);
1483       temporalFilter->tag = buildWorkflowGraph;
1484       
1485       temporalFilter->parent_filters.resize(1);
1486       temporalFilter->parent_filters[0] = instantDataFilter;
1487       
1488
1489       if(temporalFilter->parent_filters[0]->tag) temporalFilter->tag=true;
1490
1491       temporalFilter->start_graph = filter_start;
1492       temporalFilter->end_graph = filter_end;
1493       temporalFilter->field = this;
1494
1495       it = temporalDataFilters.insert(std::make_pair(outFreq, temporalFilter)).first;
1496     }
1497
1498     return it->second;
1499   }
1500   CATCH_DUMP_ATTR
1501
1502  /*!
1503    * Returns the temporal filter corresponding to the field's temporal operation
1504    * for the specified operation frequency.
1505    *
1506    * \param gc the garbage collector to use
1507    * \param outFreq the operation frequency, i.e. the frequency at which the output data will be computed
1508    * \return the output pin corresponding to the requested temporal filter
1509    */
1510   
1511   std::shared_ptr<COutputPin> CField::getSelfTemporalDataFilter(CGarbageCollector& gc, CDuration outFreq)
1512   TRY
1513   {
1514     if (instantDataFilter || !hasExpression())
1515       ERROR("COutputPin* CField::getSelfTemporalDataFilter(CGarbageCollector& gc)",
1516             "Impossible to add a self reference to a field which has already been parsed or which does not have an expression.");
1517
1518     if (!selfReferenceFilter) getSelfReference(gc) ;
1519
1520     if (serverSourceFilter || clientSourceFilter)
1521     {
1522       if (operation.isEmpty())
1523         ERROR("void CField::getSelfTemporalDataFilter(CGarbageCollector& gc, CDuration outFreq)",
1524               << "An operation must be defined for field \"" << getId() << "\".");
1525
1526       checkTimeAttributes(&outFreq);
1527
1528       const bool detectMissingValues = (!detect_missing_value.isEmpty() && detect_missing_value == true);
1529       bool buildWorkflowGraph = (!build_workflow_graph.isEmpty() && build_workflow_graph == true);
1530       std::shared_ptr<CTemporalFilter> temporalFilter(new CTemporalFilter(gc, operation,
1531                                                                           CContext::getCurrent()->getCalendar()->getInitDate(),
1532                                                                           freq_op, freq_offset, outFreq,
1533                                                                           detectMissingValues));
1534
1535       selfReferenceFilter->connectOutput(temporalFilter, 0);
1536       temporalFilter->tag = buildWorkflowGraph;
1537       temporalFilter->field = this;
1538
1539       return temporalFilter ;
1540     }
1541     else if (!field_ref.isEmpty())
1542     {
1543       CField* fieldRef = CField::get(field_ref);
1544       fieldRef->buildFilterGraph(gc, false); 
1545       return fieldRef->getTemporalDataFilter(gc, outFreq) ;
1546     }
1547  }
1548   CATCH_DUMP_ATTR
1549
1550   //----------------------------------------------------------------
1551/*
1552   void CField::fromBinary(StdIStream& is)
1553   {
1554      SuperClass::fromBinary(is);
1555#define CLEAR_ATT(name_)\
1556      SuperClassAttribute::operator[](#name_)->reset()
1557
1558         CLEAR_ATT(domain_ref);
1559         CLEAR_ATT(axis_ref);
1560#undef CLEAR_ATT
1561
1562   }
1563*/
1564   //----------------------------------------------------------------
1565
1566   void CField::solveGridReference(void)
1567   TRY
1568   {
1569      if (grid_ref.isEmpty() && domain_ref.isEmpty() && axis_ref.isEmpty() && scalar_ref.isEmpty())
1570      {
1571        ERROR("CField::solveGridReference(void)",
1572              << "A grid must be defined for field '" << getFieldOutputName() << "' .");
1573      }
1574      else if (!grid_ref.isEmpty() && (!domain_ref.isEmpty() || !axis_ref.isEmpty() || !scalar_ref.isEmpty()))
1575      {
1576        ERROR("CField::solveGridReference(void)",
1577              << "Field '" << getFieldOutputName() << "' has both a grid and a domain/axis/scalar." << std::endl
1578              << "Please define either 'grid_ref' or 'domain_ref'/'axis_ref'/'scalar_ref'.");
1579      }
1580
1581      if (grid_ref.isEmpty())
1582      {
1583        std::vector<CDomain*> vecDom;
1584        std::vector<CAxis*> vecAxis;
1585        std::vector<CScalar*> vecScalar;
1586        std::vector<int> axisDomainOrderTmp;
1587
1588        std::vector<CDomain*> vecDomRef;
1589        std::vector<CAxis*> vecAxisRef;
1590        std::vector<CScalar*> vecScalarRef;
1591
1592       
1593        if (!domain_ref.isEmpty())
1594        {
1595          StdString tmp = domain_ref.getValue();
1596          if (CDomain::has(domain_ref))
1597          {
1598            vecDom.push_back(CDomain::get(domain_ref));
1599            vecDomRef.push_back(CDomain::createDomain());
1600            vecDomRef.back()->domain_ref=domain_ref;
1601            axisDomainOrderTmp.push_back(2);
1602          }
1603          else  ERROR("CField::solveGridReference(void)",
1604                      << "Invalid reference to domain '" << domain_ref.getValue() << "'.");
1605        }
1606
1607        if (!axis_ref.isEmpty())
1608        {
1609          if (CAxis::has(axis_ref))
1610          {
1611            vecAxis.push_back(CAxis::get(axis_ref));
1612            vecAxisRef.push_back(CAxis::createAxis());
1613            vecAxisRef.back()->axis_ref=axis_ref;
1614            axisDomainOrderTmp.push_back(1);
1615          }
1616          else  ERROR("CField::solveGridReference(void)",
1617                      << "Invalid reference to axis '" << axis_ref.getValue() << "'.");
1618        }
1619
1620        if (!scalar_ref.isEmpty())
1621        {
1622          if (CScalar::has(scalar_ref))
1623          {
1624            vecScalar.push_back(CScalar::get(scalar_ref));
1625            vecScalarRef.push_back(CScalar::createScalar());
1626            vecScalarRef.back()->scalar_ref=scalar_ref;
1627            axisDomainOrderTmp.push_back(0);
1628          }
1629          else ERROR("CField::solveGridReference(void)",
1630                     << "Invalid reference to scalar '" << scalar_ref.getValue() << "'.");
1631        }
1632       
1633        CArray<int,1> axisDomainOrder(axisDomainOrderTmp.size());
1634        for (int idx = 0; idx < axisDomainOrderTmp.size(); ++idx)
1635        {
1636          axisDomainOrder(idx) = axisDomainOrderTmp[idx];
1637        }
1638
1639        // Warning: the gridId shouldn't be set as the grid_ref since it could be inherited
1640        StdString gridId = CGrid::generateId(vecDom, vecAxis, vecScalar,axisDomainOrder);
1641        if (CGrid::has(gridId)) this->grid = CGrid::get(gridId);
1642        else  this->grid = CGrid::createGrid(gridId, vecDomRef, vecAxisRef, vecScalarRef,axisDomainOrder);
1643      }
1644      else
1645      {
1646        if (CGrid::has(grid_ref)) this->grid = CGrid::get(grid_ref);
1647        else  ERROR("CField::solveGridReference(void)",
1648                     << "Invalid reference to grid '" << grid_ref.getValue() << "'.");
1649      }
1650   }
1651   CATCH_DUMP_ATTR
1652
1653   void CField::solveGridDomainAxisRef(bool checkAtt)
1654   TRY
1655   {
1656     grid->solveDomainAxisRef(checkAtt);
1657   }
1658   CATCH_DUMP_ATTR
1659
1660   void CField::solveCheckMaskIndex(bool doSendingIndex)
1661   TRY
1662   {
1663     grid->checkMaskIndex(doSendingIndex);
1664   }
1665   CATCH_DUMP_ATTR
1666
1667   void CField::solveTransformedGrid()
1668   TRY
1669   {
1670     if (grid && !grid->isTransformed() && hasDirectFieldReference() && grid != getDirectFieldReference()->grid)
1671     {
1672       std::vector<CGrid*> grids;
1673       // Source grid
1674       grids.push_back(getDirectFieldReference()->grid);
1675       // Intermediate grids
1676       if (!grid_path.isEmpty())
1677       {
1678         std::string gridId;
1679         size_t start = 0, end;
1680
1681         do
1682         {
1683           end = grid_path.getValue().find(',', start);
1684           if (end != std::string::npos)
1685           {
1686             gridId = grid_path.getValue().substr(start, end - start);
1687             start = end + 1;
1688           }
1689           else
1690             gridId = grid_path.getValue().substr(start);
1691
1692           if (!CGrid::has(gridId))
1693             ERROR("void CField::solveTransformedGrid()",
1694                   << "Invalid grid_path, the grid '" << gridId << "' does not exist.");
1695
1696           grids.push_back(CGrid::get(gridId));
1697         }
1698         while (end != std::string::npos);
1699       }
1700       // Destination grid
1701       grids.push_back(grid);
1702
1703       for (size_t i = 0, count = grids.size() - 1; i < count; ++i)
1704       {
1705         CGrid *gridSrc  = grids[i];
1706         CGrid *gridDest = grids[i + 1];
1707         if (!gridDest->isTransformed())
1708           gridDest->transformGrid(gridSrc);
1709       }
1710     }
1711     else if (grid && grid->hasTransform() && !grid->isTransformed())
1712     {
1713       // Temporarily deactivate the self-transformation of grid
1714       // grid->transformGrid(grid);
1715     }
1716   }
1717   CATCH_DUMP_ATTR
1718
1719   void CField::solveGenerateGrid()
1720   TRY
1721   {
1722     if (grid && !grid->isTransformed() && hasDirectFieldReference() && grid != getDirectFieldReference()->grid)
1723       grid->completeGrid(getDirectFieldReference()->grid);
1724     else
1725       grid->completeGrid();
1726   }
1727   CATCH_DUMP_ATTR
1728
1729   void CField::solveGridDomainAxisBaseRef()
1730   TRY
1731   {
1732     grid->solveDomainAxisRef(false);
1733     grid->solveDomainAxisBaseRef();
1734   }
1735   CATCH_DUMP_ATTR
1736
1737   ///-------------------------------------------------------------------
1738
1739   template <>
1740   void CGroupTemplate<CField, CFieldGroup, CFieldAttributes>::solveRefInheritance(void)
1741   TRY
1742   {
1743      if (this->group_ref.isEmpty()) return;
1744      StdString gref = this->group_ref.getValue();
1745
1746      if (!CFieldGroup::has(gref))
1747         ERROR("CGroupTemplate<CField, CFieldGroup, CFieldAttributes>::solveRefInheritance(void)",
1748               << "[ gref = " << gref << "]"
1749               << " invalid group name !");
1750
1751      CFieldGroup* group = CFieldGroup::get(gref);
1752      CFieldGroup* owner = CFieldGroup::get(boost::polymorphic_downcast<CFieldGroup*>(this));
1753      owner->setAttributes(group); // inherite of attributes of group reference
1754     
1755      std::vector<CField*> allChildren  = group->getAllChildren();
1756      std::vector<CField*>::iterator it = allChildren.begin(), end = allChildren.end();
1757
1758      for (; it != end; it++)
1759      {
1760         CField* child = *it;
1761         if (child->hasId()) owner->createChild()->field_ref.setValue(child->getId());
1762
1763      }
1764   }
1765   CATCH_DUMP_ATTR
1766
1767   void CField::scaleFactorAddOffset(double scaleFactor, double addOffset)
1768   TRY
1769   {
1770     recvDataSrv = (recvDataSrv - addOffset) / scaleFactor;
1771   }
1772   CATCH_DUMP_ATTR
1773
1774   void CField::invertScaleFactorAddOffset(double scaleFactor, double addOffset)
1775   TRY
1776   {
1777     recvDataSrv = recvDataSrv * scaleFactor + addOffset;
1778   }
1779   CATCH_DUMP_ATTR
1780
1781   void CField::outputField(CArray<double,1>& fieldOut)
1782   TRY
1783   { 
1784      CArray<size_t,1>& outIndexClient = grid->localIndexToWriteOnClient;
1785      CArray<size_t,1>& outIndexServer = grid->localIndexToWriteOnServer;
1786      for (size_t idx = 0; idx < outIndexServer.numElements(); ++idx)
1787      {
1788        fieldOut(outIndexServer(idx)) = recvDataSrv(outIndexClient(idx));
1789      }
1790   }
1791   CATCH_DUMP_ATTR
1792
1793   void CField::inputField(CArray<double,1>& fieldIn)
1794   TRY
1795   {
1796      CArray<size_t,1>& outIndexClient = grid->localIndexToWriteOnClient;
1797      CArray<size_t,1>& outIndexServer = grid->localIndexToWriteOnServer;
1798      for (size_t idx = 0; idx < outIndexServer.numElements(); ++idx)
1799      {
1800        recvDataSrv(outIndexClient(idx)) = fieldIn(outIndexServer(idx));
1801      }
1802   }
1803   CATCH_DUMP_ATTR
1804
1805   void CField::outputCompressedField(CArray<double,1>& fieldOut)
1806   TRY
1807   {
1808      CArray<size_t,1>& outIndexClient = grid->localIndexToWriteOnClient;
1809      CArray<size_t,1>& outIndexServer = grid->localIndexToWriteOnServer;
1810      for (size_t idx = 0; idx < outIndexServer.numElements(); ++idx)
1811      {
1812        fieldOut((idx)) = recvDataSrv(outIndexClient(idx));
1813      }
1814   }
1815   CATCH_DUMP_ATTR
1816
1817   ///-------------------------------------------------------------------
1818
1819   void CField::parse(xml::CXMLNode& node)
1820   TRY
1821   {
1822      string newContent ;
1823      SuperClass::parse(node);
1824      if (node.goToChildElement())
1825      {
1826        do
1827        {
1828          if (node.getElementName() == "variable" || node.getElementName() == "variable_group") this->getVirtualVariableGroup()->parseChild(node);
1829          else if (node.getElementName() == "expr") if (node.getContent(newContent)) content+=newContent ;
1830        } while (node.goToNextElement());
1831        node.goToParentElement();
1832      }
1833      if (node.getContent(newContent)) content=newContent ;
1834    }
1835   CATCH_DUMP_ATTR
1836
1837   /*!
1838     This function retrieves Id of corresponding domain_ref and axis_ref (if any)
1839   of a field. In some cases, only domain exists but axis doesn't
1840   \return pair of Domain and Axis id
1841   */
1842   const std::vector<StdString>& CField::getRefDomainAxisIds()
1843   TRY
1844   {
1845     CGrid* cgPtr = getRelGrid();
1846     if (NULL != cgPtr)
1847     {
1848       std::vector<StdString>::iterator it;
1849       if (!domain_ref.isEmpty())
1850       {
1851         std::vector<StdString> domainList = cgPtr->getDomainList();
1852         it = std::find(domainList.begin(), domainList.end(), domain_ref.getValue());
1853         if (domainList.end() != it) domAxisScalarIds_[0] = *it;
1854       }
1855
1856       if (!axis_ref.isEmpty())
1857       {
1858         std::vector<StdString> axisList = cgPtr->getAxisList();
1859         it = std::find(axisList.begin(), axisList.end(), axis_ref.getValue());
1860         if (axisList.end() != it) domAxisScalarIds_[1] = *it;
1861       }
1862
1863       if (!scalar_ref.isEmpty())
1864       {
1865         std::vector<StdString> scalarList = cgPtr->getScalarList();
1866         it = std::find(scalarList.begin(), scalarList.end(), scalar_ref.getValue());
1867         if (scalarList.end() != it) domAxisScalarIds_[2] = *it;
1868       }
1869     }
1870     return (domAxisScalarIds_);
1871   }
1872   CATCH_DUMP_ATTR
1873
1874   CVariable* CField::addVariable(const string& id)
1875   TRY
1876   {
1877     return vVariableGroup->createChild(id);
1878   }
1879   CATCH
1880
1881   CVariableGroup* CField::addVariableGroup(const string& id)
1882   TRY
1883   {
1884     return vVariableGroup->createChildGroup(id);
1885   }
1886   CATCH
1887
1888   void CField::setContextClient(CContextClient* contextClient)
1889   TRY
1890   {
1891     CContext* context = CContext::getCurrent();
1892     client = contextClient;
1893     if (context->hasClient)
1894     {
1895       // A grid is sent by a client (both for read or write) or by primary server (write only)
1896       if (context->hasServer)
1897       {
1898         if (file->mode.isEmpty() || (!file->mode.isEmpty() && file->mode == CFile::mode_attr::write))
1899           grid->setContextClient(contextClient);
1900       }
1901       else
1902           grid->setContextClient(contextClient);
1903     }
1904   }
1905   CATCH_DUMP_ATTR
1906
1907   CContextClient* CField::getContextClient()
1908   TRY
1909   {
1910     return client;
1911   }
1912   CATCH
1913
1914   void CField::sendAddAllVariables(CContextClient* client)
1915   TRY
1916   {
1917     std::vector<CVariable*> allVar = getAllVariables();
1918     std::vector<CVariable*>::const_iterator it = allVar.begin();
1919     std::vector<CVariable*>::const_iterator itE = allVar.end();
1920
1921     for (; it != itE; ++it)
1922     {
1923       this->sendAddVariable((*it)->getId(), client);
1924       (*it)->sendAllAttributesToServer(client);
1925       (*it)->sendValue(client);
1926     }
1927   }
1928   CATCH_DUMP_ATTR
1929
1930   /*!
1931    * Send all Attributes to server. This method is overloaded, since only grid_ref attribute
1932    * must be sent to server and not domain_ref/axis_ref/scalar_ref.
1933    */
1934   
1935   void CField::sendAllAttributesToServer(CContextClient* client)
1936   TRY
1937   {
1938     if (grid_ref.isEmpty())
1939     {
1940       grid_ref=grid->getId() ;
1941       SuperClass::sendAllAttributesToServer(client) ;
1942       grid_ref.reset();
1943     }
1944     else SuperClass::sendAllAttributesToServer(client) ;
1945   }
1946   CATCH_DUMP_ATTR
1947   
1948   void CField::sendAddVariable(const string& id, CContextClient* client)
1949   TRY
1950   {
1951      sendAddItem(id, (int)EVENT_ID_ADD_VARIABLE, client);
1952   }
1953   CATCH_DUMP_ATTR
1954
1955   void CField::sendAddVariableGroup(const string& id, CContextClient* client)
1956   TRY
1957   {
1958      sendAddItem(id, (int)EVENT_ID_ADD_VARIABLE_GROUP, client);
1959   }
1960   CATCH_DUMP_ATTR
1961
1962   void CField::recvAddVariable(CEventServer& event)
1963   TRY
1964   {
1965
1966      CBufferIn* buffer = event.subEvents.begin()->buffer;
1967      string id;
1968      *buffer >> id;
1969      get(id)->recvAddVariable(*buffer);
1970   }
1971   CATCH
1972
1973   void CField::recvAddVariable(CBufferIn& buffer)
1974   TRY
1975   {
1976      string id;
1977      buffer >> id;
1978      addVariable(id);
1979   }
1980   CATCH_DUMP_ATTR
1981
1982   void CField::recvAddVariableGroup(CEventServer& event)
1983   TRY
1984   {
1985
1986      CBufferIn* buffer = event.subEvents.begin()->buffer;
1987      string id;
1988      *buffer >> id;
1989      get(id)->recvAddVariableGroup(*buffer);
1990   }
1991   CATCH
1992
1993   void CField::recvAddVariableGroup(CBufferIn& buffer)
1994   TRY
1995   {
1996      string id;
1997      buffer >> id;
1998      addVariableGroup(id);
1999   }
2000   CATCH_DUMP_ATTR
2001
2002   /*!
2003    * Check on freq_off and freq_op attributes.
2004    */
2005   void CField::checkTimeAttributes(CDuration* freqOp)
2006   TRY
2007   {
2008     bool isFieldRead  = file && !file->mode.isEmpty() && file->mode == CFile::mode_attr::read;
2009     bool isFieldWrite = file && ( file->mode.isEmpty() ||  file->mode == CFile::mode_attr::write);
2010     if (isFieldRead && !(operation.getValue() == "instant" || operation.getValue() == "once") )     
2011       ERROR("void CField::checkTimeAttributes(void)",
2012             << "Unsupported operation for field '" << getFieldOutputName() << "'." << std::endl
2013             << "Currently only \"instant\" is supported for fields read from file.")
2014
2015     if (freq_op.isEmpty())
2016     {
2017       if (operation.getValue() == "instant")
2018       {
2019         if (isFieldRead || isFieldWrite) freq_op.setValue(file->output_freq.getValue());
2020         else freq_op=*freqOp ;
2021       }
2022       else
2023         freq_op.setValue(TimeStep);
2024     }
2025     if (freq_offset.isEmpty())
2026       freq_offset.setValue(isFieldRead ? NoneDu : (freq_op.getValue() - TimeStep));
2027   }
2028   CATCH_DUMP_ATTR
2029
2030   /*!
2031    * Returns string arithmetic expression associated to the field.
2032    * \return if content is defined return content string, otherwise, if "expr" attribute is defined, return expr string.
2033    */
2034   const string& CField::getExpression(void)
2035   TRY
2036   {
2037     if (!expr.isEmpty() && content.empty())
2038     {
2039       content = expr;
2040       expr.reset();
2041     }
2042
2043     return content;
2044   }
2045   CATCH_DUMP_ATTR
2046
2047   bool CField::hasExpression(void) const
2048   TRY
2049   {
2050     return (!expr.isEmpty() || !content.empty());
2051   }
2052   CATCH
2053
2054   bool CField::hasGridMask(void) const
2055   TRY
2056   {
2057     return (this->grid->hasMask());
2058   }
2059   CATCH
2060
2061   DEFINE_REF_FUNC(Field,field)
2062} // namespace xios
Note: See TracBrowser for help on using the repository browser.