Index: XIOS/dev/dev_oa/src/node/domain.cpp
===================================================================
--- XIOS/dev/dev_oa/src/node/domain.cpp	(revision 1963)
+++ XIOS/dev/dev_oa/src/node/domain.cpp	(revision 1966)
@@ -35,4 +35,5 @@
       , clients(), hasLatInReadFile_(false), hasBoundsLatInReadFile_(false)
       , hasLonInReadFile_(false), hasBoundsLonInReadFile_(false)
+      , isTiled_(false)
    {
    }
@@ -48,4 +49,5 @@
       , clients(), hasLatInReadFile_(false), hasBoundsLatInReadFile_(false)
       , hasLonInReadFile_(false), hasBoundsLonInReadFile_(false)
+      , isTiled_(false)
    {
     }
@@ -246,4 +248,27 @@
    {
       return isCompressible_;
+   }
+   CATCH
+
+   bool CDomain::isTiled(void) const
+   TRY
+   {
+      return isTiled_;
+   }
+   CATCH
+
+   int CDomain::getTileId(int i, int j) const
+   TRY
+   {
+     int tileId = 0;
+     bool stop = false;
+     while (!stop)
+     {
+       if ((i >= tile_ibegin(tileId) ) && (i < tile_ni(tileId)+tile_ibegin(tileId) )
+           && (j >= tile_jbegin(tileId) ) && (j < tile_nj(tileId)+tile_jbegin(tileId) ) )
+         stop = true;
+       ++tileId;
+     }
+     return (tileId - 1);
    }
    CATCH
@@ -1723,4 +1748,81 @@
    CATCH_DUMP_ATTR
 
+   //----------------------------------------------------------------
+   void CDomain::checkTiles()
+   TRY
+   {
+     if (!ntiles.isEmpty() && ntiles.getValue() >1)  isTiled_ = true;
+     if (isTiled_)
+     {
+
+       // Attributes tile_ni/nj and tile_i/jbegin are mandatory for tiled domains
+       if (tile_ni.numElements() != ntiles || tile_ibegin.numElements() != ntiles)
+           ERROR("CDomain::checkTiles()",
+                 << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
+                 << "'tile_ni' or 'tile_ibegin' are ill defined: these attributes must be specified for tiled domains and be of the size 'ntiles'." << std::endl
+                 << "The number of tiles is  " << ntiles.getValue() << " while the size of 'tile_ni' is " << tile_ni.numElements() 
+                 << " and the size of 'tile_ibegin' is " << tile_ibegin.numElements() << ".");
+
+       if (tile_nj.numElements() != ntiles || tile_jbegin.numElements() != ntiles)
+           ERROR("CDomain::checkTiles()",
+                 << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
+                 << "'tile_nj' or 'tile_jbegin' are ill defined: these attributes must be specified for tiled domains and be of the size 'ntiles'." << std::endl
+                 << "The number of tiles is  " << ntiles.getValue() << " while the size of 'tile_nj' is " << tile_nj.numElements()
+                 << " and the size of 'tile_jbegin' is " << tile_jbegin.numElements() << ".");
+
+       // Check on consistency of individual tile sizes and local domain size
+       int sizeTiles = 0;
+       for (int iTile = 0; iTile < ntiles.getValue(); ++iTile)
+       {
+         sizeTiles += tile_ni(iTile) * tile_nj(iTile);
+       }
+       if (sizeTiles != (ni*nj))
+           ERROR("CDomain::checkTiles()",
+                 << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
+                 << "tiles should cover the entire local domain and cannot overlap." << std::endl << ".");
+
+
+       // Fill in tile_data_ni/nj and tile_data_i/jbegin if they are not specified
+       if (tile_data_ni.numElements() == 0 || tile_data_nj.numElements() == 0)
+       {
+         tile_data_ni.resize(ntiles);
+         tile_data_nj.resize(ntiles);
+         for (int iTile = 0; iTile < ntiles.getValue(); ++iTile)
+         {
+           tile_data_ni(iTile) = tile_ni(iTile);
+           tile_data_nj(iTile) = tile_nj(iTile);
+         }
+       }
+
+       if (tile_data_ibegin.numElements() == 0 || tile_data_jbegin.numElements() == 0)
+       {
+         tile_data_ibegin.resize(ntiles);
+         tile_data_jbegin.resize(ntiles);
+         tile_data_ibegin = 0;
+         tile_data_jbegin = 0;
+       }
+
+     } // isTiled_
+   }
+   CATCH_DUMP_ATTR
+
+   //----------------------------------------------------------------
+   int CDomain::getTileDataISize(int tileId) const
+   TRY
+   {
+     int retvalue = (tile_data_ni(tileId) > tile_ni(tileId)) ? tile_data_ni(tileId) : tile_ni(tileId);
+     return retvalue;
+   }
+   CATCH_DUMP_ATTR
+
+   //----------------------------------------------------------------
+   int CDomain::getTileDataJSize(int tileId) const
+   TRY
+   {
+     int retvalue = (tile_data_nj(tileId) > tile_nj(tileId)) ? tile_data_nj(tileId) : tile_nj(tileId);
+     return retvalue;
+   }
+   CATCH_DUMP_ATTR
+
    void CDomain::checkAttributesOnClientAfterTransformation()
    TRY
@@ -1756,4 +1858,5 @@
         this->checkArea();
         this->checkLonLat();
+        this->checkTiles();
       }
 
Index: XIOS/dev/dev_oa/src/node/domain.hpp
===================================================================
--- XIOS/dev/dev_oa/src/node/domain.hpp	(revision 1963)
+++ XIOS/dev/dev_oa/src/node/domain.hpp	(revision 1966)
@@ -104,5 +104,10 @@
          bool isDistributed(void) const;
          bool isCompressible(void) const; 
- 
+
+         bool isTiled(void) const;
+         int getTileId(int i, int j) const;
+         int getTileDataISize(int tileId) const;
+         int getTileDataJSize(int tileId) const;
+
          std::vector<int> getNbGlob();
          bool isEqual(CDomain* domain);
@@ -169,4 +174,5 @@
          void checkArea(void);
          void checkLonLat();
+         void checkTiles();
 
          void setTransformations(const TransMapTypes&);         
@@ -228,5 +234,7 @@
          bool isUnstructed_;
          std::unordered_map<size_t,size_t> globalLocalIndexMap_;
-       
+
+         bool isTiled_;
+
        private:
          static bool initializeTransformationMap(std::map<StdString, ETranformationType>& m);
Index: XIOS/dev/dev_oa/src/node/grid.cpp
===================================================================
--- XIOS/dev/dev_oa/src/node/grid.cpp	(revision 1963)
+++ XIOS/dev/dev_oa/src/node/grid.cpp	(revision 1966)
@@ -33,5 +33,5 @@
       , writtenDataSize_(0), numberWrittenIndexes_(0), totalNumberWrittenIndexes_(0), offsetWrittenIndexes_(0)
       , connectedDataSize_(), connectedServerRank_(), connectedServerRankRead_(), connectedDataSizeRead_()
-	  , isDataDistributed_(true), isCompressible_(false)
+      , isDataDistributed_(true), isCompressible_(false)
       , transformations_(0), isTransformed_(false)
       , axisPositionInGrid_(), hasDomainAxisBaseRef_(false)
@@ -39,4 +39,7 @@
       , computedWrittenIndex_(false)
       , clients()
+      , nTiles_(0)
+      , isTiled_(false)
+      , storeTileIndex()
    {
      setVirtualDomainGroup(CDomainGroup::create(getId() + "_virtual_domain_group"));
@@ -54,5 +57,5 @@
       , writtenDataSize_(0), numberWrittenIndexes_(0), totalNumberWrittenIndexes_(0), offsetWrittenIndexes_(0)
       , connectedDataSize_(), connectedServerRank_(), connectedServerRankRead_(), connectedDataSizeRead_()
-	  , isDataDistributed_(true), isCompressible_(false)
+      , isDataDistributed_(true), isCompressible_(false)
       , transformations_(0), isTransformed_(false)
       , axisPositionInGrid_(), hasDomainAxisBaseRef_(false)
@@ -60,4 +63,7 @@
       , computedWrittenIndex_(false)
       , clients()
+      , nTiles_(0)
+      , isTiled_(false)
+      , storeTileIndex()
    {
      setVirtualDomainGroup(CDomainGroup::create(getId() + "_virtual_domain_group"));
@@ -102,4 +108,74 @@
    }
    CATCH
+
+   //---------------------------------------------------------------
+   /*!
+    * Returns size of tile data
+    */
+
+   StdSize CGrid::getTileDataSize(int tileId)
+   TRY
+   {
+     StdSize tileGridSize =1 ;
+     int numElement = axis_domain_order.numElements();
+
+     std::vector<CAxis*> axisListP = this->getAxis();
+     std::vector<CDomain*> domainListP = this->getDomains();
+
+     int axisIndex = 0, domIndex = 0;
+     for (int idx = 0; idx < numElement; ++idx)
+     {
+       int eleDim = axis_domain_order(idx);
+       if (2 == eleDim)
+       {
+         tileGridSize *= domainListP[domIndex]->tile_data_ni(tileId);
+         tileGridSize *= domainListP[domIndex]->tile_data_nj(tileId);
+         ++domIndex;
+          }
+       else if (1 == eleDim)
+       {
+         tileGridSize *= axisListP[axisIndex]->n.getValue();
+         ++axisIndex;
+       }
+     } // loop over grid elements
+     return tileGridSize;
+   }
+   CATCH
+
+   //---------------------------------------------------------------
+   /*!
+    * Returns tile size
+    */
+
+   StdSize CGrid::getTileSize(int tileId)
+   TRY
+   {
+     StdSize tileGridSize =1 ;
+     int numElement = axis_domain_order.numElements();
+
+     std::vector<CAxis*> axisListP = this->getAxis();
+     std::vector<CDomain*> domainListP = this->getDomains();
+
+     int axisIndex = 0, domIndex = 0;
+     for (int idx = 0; idx < numElement; ++idx)
+     {
+       int eleDim = axis_domain_order(idx);
+       if (2 == eleDim)
+       {
+         tileGridSize *= domainListP[domIndex]->tile_ni(tileId);
+         tileGridSize *= domainListP[domIndex]->tile_nj(tileId);
+         ++domIndex;
+          }
+       else if (1 == eleDim)// So it's an axis
+       {
+         tileGridSize *= axisListP[axisIndex]->n.getValue();
+         ++axisIndex;
+       }
+     } // loop over grid elements
+     return tileGridSize;
+   }
+   CATCH
+
+   //---------------------------------------------------------------
 
    /*!
@@ -561,4 +637,5 @@
           if (sendAtt) domListP[i]->sendCheckedAttributes();
           else domListP[i]->checkAttributesOnClient();
+          if (domListP[i]->isTiled()) this->isTiled_ = true;
         }
       }
@@ -902,4 +979,5 @@
      {
        computeClientIndex();
+       if (this->isTiled_) computeTileIndex();
        if (context->hasClient)
        {
@@ -1097,4 +1175,111 @@
    }
    CATCH_DUMP_ATTR
+
+   //---------------------------------------------------------------
+
+   /*
+   */
+   void CGrid::computeTileIndex()
+   TRY
+   {
+     int numElement = axis_domain_order.numElements();
+     storeTileIndex.resize(nTiles_);
+
+     std::vector<CAxis*> axisListP = this->getAxis();
+     std::vector<CDomain*> domainListP = this->getDomains();
+
+     // First, allocate storeTileIndex[0..ntiles]
+     for (int iTile = 0; iTile < nTiles_; ++iTile)
+     {
+       int tileGridSize = 1;
+       int axisIndex = 0, domIndex = 0;
+       for (int idx = 0; idx < numElement; ++idx)
+       {
+         int eleDim = axis_domain_order(idx);
+         if (2 == eleDim)
+         {
+           tileGridSize *= domainListP[domIndex]->getTileDataISize(iTile);
+           tileGridSize *= domainListP[domIndex]->getTileDataJSize(iTile);
+           ++domIndex;
+         }
+         else if (1 == eleDim)// So it's an axis
+         {
+           tileGridSize *= axisListP[axisIndex]->n.getValue();
+           ++axisIndex;
+         }
+       } // loop over grid elements
+       storeTileIndex[iTile].resize(tileGridSize);
+       storeTileIndex[iTile] = -1;
+     } // loop over tiles
+
+     // Now fill in storeTileIndex
+     // Currently assuming two possible situations : (1) domain x axis or (2) domain
+     std::vector<int> tileIndexCount (nTiles_,0);
+     int axisSize = 1;
+     if (axisListP.size() != 0) axisSize = axisListP[0]->n.getValue();
+     int ni = domainListP[0]->ni.getValue();
+     int nj = domainListP[0]->nj.getValue();
+
+     for (int idxAxis = 0; idxAxis < axisSize; ++idxAxis)
+     {
+       for (int jIdxDom = 0; jIdxDom < nj; ++jIdxDom)
+       {
+         for (int iIdxDom = 0; iIdxDom < ni; ++iIdxDom)
+         {
+           int tile = domainListP[0]->getTileId(iIdxDom, jIdxDom);
+           int tileOffset =  domainListP[0]->tile_data_ibegin(tile);  // only sign of offset matters
+
+           // case 1: data size corresponds to tile size
+           if (tileOffset == 0)
+           {
+             storeTileIndex[tile](tileIndexCount[tile]) = idxAxis*nj*ni + jIdxDom * ni + iIdxDom;
+             ++tileIndexCount[tile];
+           }
+           // case 2: masked data
+           else if (tileOffset > 0)
+           {
+             int iBegin = domainListP[0]->tile_ibegin(tile) + domainListP[0]->tile_data_ibegin(tile); // tile data relative to domain
+             int jBegin = domainListP[0]->tile_jbegin(tile) + domainListP[0]->tile_data_jbegin(tile); // tile data relative to domain
+             int iEnd = iBegin + domainListP[0]->tile_data_ni(tile);
+             int jEnd = jBegin + domainListP[0]->tile_data_nj(tile);
+             if ((jIdxDom >= jBegin) && (jIdxDom < jEnd) && (iIdxDom >= iBegin) && (iIdxDom < iEnd))
+             {
+               storeTileIndex[tile](tileIndexCount[tile]) = idxAxis*nj*ni + jIdxDom * ni + iIdxDom;
+             }
+             ++tileIndexCount[tile];
+           }
+           // case 3: ghost zones
+           else
+           {
+             int tileDataNi = domainListP[0]->tile_data_ni(tile);
+             int tileDataNj = domainListP[0]->tile_data_nj(tile);
+             int tileDomSize = tileDataNi * tileDataNj;
+             int tileNi = domainListP[0]->tile_ni(tile);
+             int iBegin = domainListP[0]->tile_data_ibegin(tile);
+             int jBegin = domainListP[0]->tile_data_jbegin(tile);
+
+             // add the ghost zone at the beginning of a domain tile
+             if (tileIndexCount[tile] % tileDomSize == 0)
+               tileIndexCount[tile] += (abs(jBegin)*tileDataNi + abs(iBegin));
+
+             storeTileIndex[tile](tileIndexCount[tile]) = idxAxis*nj*ni + jIdxDom*ni + iIdxDom;
+             
+             // add two ghost zones at the right end of a tile
+             if ( (iIdxDom+1) % tileNi == 0 )
+               tileIndexCount[tile] += (2*abs(iBegin));
+
+             // add ghost zone at the end of a domain tile
+             if ((tileIndexCount[tile] + abs(jBegin)*tileDataNi-abs(iBegin) + 1) % tileDomSize == 0)
+               tileIndexCount[tile] += (abs(jBegin)*tileDataNi -abs(iBegin));
+
+             ++tileIndexCount[tile];
+           }
+         } // loop over domain first dimension
+       } // loop over domain second dimension
+     } // loop over axis dimension
+
+   }
+   CATCH_DUMP_ATTR
+
 //----------------------------------------------------------------
 
@@ -1359,4 +1544,5 @@
 
    void CGrid::maskField_arr(const double* const data, CArray<double, 1>& stored) const
+   TRY
    {
       const StdSize size = storeIndex_client.numElements();
@@ -1369,4 +1555,41 @@
         for(StdSize i = 0; i < size; i++) stored(i) = data[storeIndex_client(i)];
    }
+   CATCH
+
+   void CGrid::copyTile_arr(const double* const tileData, CArray<double, 1>& stored, int tileId)
+   TRY
+   {
+     StdSize tileSize = this->getTileSize(tileId);
+     const StdSize tileDataSize = this->getTileDataSize(tileId);
+
+     // case 1: data correspond in size to a tile
+     if (tileSize == tileDataSize)
+     {
+       for(StdSize i = 0; i < tileDataSize; i++)
+         stored(storeTileIndex[tileId](i)) = tileData[i];
+     }
+     // case 2: masked data
+     else if (tileSize > tileDataSize)
+     {
+       int tileDataCount = 0;
+       for(StdSize i = 0; i < tileSize; i++)
+         if (storeTileIndex[tileId](i) >= 0)
+         {
+           stored(storeTileIndex[tileId](i)) = tileData[tileDataCount];
+           ++tileDataCount;
+         }
+     }
+     // case 3: ghost zones
+     else
+     {
+       for(StdSize i = 0; i < tileDataSize; i++)
+         if (storeTileIndex[tileId](i) >= 0)
+         {
+           stored(storeTileIndex[tileId](i)) = tileData[i];
+         }
+
+     }
+   }
+   CATCH
 
    void CGrid::uncompressField_arr(const double* const data, CArray<double, 1>& out) const
@@ -2253,4 +2476,5 @@
         pDom->solveRefInheritance(apply);
         pDom->solveInheritanceTransformation();
+        if (!pDom->ntiles.isEmpty() && pDom->ntiles.getValue()>0) nTiles_=pDom->ntiles.getValue();
       }
     }
@@ -2279,4 +2503,11 @@
       }
     }
+  }
+  CATCH_DUMP_ATTR
+
+  int CGrid::getNTiles()
+  TRY
+  {
+    return nTiles_;
   }
   CATCH_DUMP_ATTR
Index: XIOS/dev/dev_oa/src/node/grid.hpp
===================================================================
--- XIOS/dev/dev_oa/src/node/grid.hpp	(revision 1963)
+++ XIOS/dev/dev_oa/src/node/grid.hpp	(revision 1966)
@@ -91,4 +91,8 @@
          StdSize  getDataSize(void) const;
 
+         StdSize  getTileDataSize(int tileId);
+
+         StdSize  getTileSize(int tileId);
+
          /// Entrees-sorties de champs
          template <int n>
@@ -100,4 +104,6 @@
          template <int n>
          void uncompressField(const CArray<double,n>& data, CArray<double,1>& outData) const; 
+         template <int n>
+         void copyTile(const CArray<double,n>& tileData, CArray<double, 1>& stored, int tileId);
 
          virtual void parse(xml::CXMLNode& node);
@@ -256,4 +262,6 @@
                                          const CArray<int,1>& axisDomainOrder);
 
+         int getNTiles();
+
       private:
        template<int N>
@@ -273,4 +281,5 @@
         void uncompressField_arr(const double* const data, CArray<double, 1>& outData) const;
         void maskField_arr(const double* const data, CArray<double, 1>& stored) const;
+        void copyTile_arr(const double* const tileData, CArray<double, 1>& stored, int tileId);
 
         void setVirtualDomainGroup(CDomainGroup* newVDomainGroup);
@@ -303,4 +312,6 @@
         void computeConnectedClientsScalarGrid(); 
 
+        void computeTileIndex();
+
       private:
 
@@ -312,4 +323,9 @@
         bool isDomainAxisChecked;
         bool isIndexSent;
+
+        int nTiles_;
+        bool isTiled_;
+/** Vector containing local domain indexes for each tile */
+        std::vector<CArray<int,1> > storeTileIndex;
 
         CDomainGroup* vDomainGroup_;
@@ -387,9 +403,15 @@
 
    template <int n>
+   void CGrid::copyTile(const CArray<double,n>& tileData, CArray<double,1>& storedData, int tileId)
+   {
+      this->copyTile_arr(tileData.dataFirst(), storedData, tileId);
+   }
+
+   template <int n>
    void CGrid::maskField(const CArray<double,n>& field, CArray<double,1>& stored) const
    {
 //#ifdef __XIOS_DEBUG
       if (this->getDataSize() != field.numElements())
-         ERROR("void CGrid::inputField(const  CArray<double,n>& field, CArray<double,1>& stored) const",
+         ERROR("void CGrid::maskField(const  CArray<double,n>& field, CArray<double,1>& stored) const",
                 << "[ Awaiting data of size = " << this->getDataSize() << ", "
                 << "Received data size = "      << field.numElements() << " ] "
