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

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

MARK: branch merged with trunk @1663. static graph OK with EP

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