source: XIOS/dev/dev_olga/src/node/field.cpp @ 1653

Last change on this file since 1653 was 1653, checked in by oabramkina, 5 years ago

Developments for visualization of XIOS workflow.

Branch is spawned from trunk r1649.

Boost library is used for producing Graphviz DOT files. Current results: a DOT file representing a static workflow. For a complete proof of concept, DOT files for each timestamp should be generated. The necessary information has been collected by XIOS, it only requires rearranging the information for graphing (changes in classes CWorkflowGraph and CGraphviz).

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