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

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