source: XIOS/dev/dev_ym/XIOS_COUPLING/src/node/field.cpp @ 1986

Last change on this file since 1986 was 1984, checked in by ymipsl, 4 years ago

intermediate commit for new tranformation engine?
YM

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