source: XIOS/trunk/src/node/domain.cpp @ 687

Last change on this file since 687 was 687, checked in by mhnguyen, 7 years ago

Implementing auto-generate rectilinear domain

+) Add a new special transformation to generate (complete) rectilinear domain

Test
+) On Curie
+) test_new_feature passed

  • 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.0 KB
Line 
1#include "domain.hpp"
2
3#include "attribute_template.hpp"
4#include "object_template.hpp"
5#include "group_template.hpp"
6
7#include "xios_spl.hpp"
8#include "event_client.hpp"
9#include "event_server.hpp"
10#include "buffer_in.hpp"
11#include "message.hpp"
12#include "type.hpp"
13#include "context.hpp"
14#include "context_client.hpp"
15#include "context_server.hpp"
16#include "array_new.hpp"
17#include "distribution_client.hpp"
18#include "server_distribution_description.hpp"
19#include "client_server_mapping_distributed.hpp"
20#include "zoom_domain.hpp"
21#include "interpolate_from_file_domain.hpp"
22#include "generate_rectilinear_domain.hpp"
23
24#include <algorithm>
25
26namespace xios {
27
28   /// ////////////////////// Définitions ////////////////////// ///
29
30   CDomain::CDomain(void)
31      : CObjectTemplate<CDomain>(), CDomainAttributes()
32      , isChecked(false), relFiles(), isClientChecked(false), nbConnectedClients_(), indSrv_(), connectedServerRank_()
33      , hasBounds(false), hasArea(false), isDistributed_(false), nGlobDomain_(), isCompressible_(false), isUnstructed_(false)
34      , global_zoom_ni(0), global_zoom_ibegin(0), global_zoom_nj(0), global_zoom_jbegin(0)
35      , isClientAfterTransformationChecked(false), hasLonLat(false)
36      , lonvalue_client(), latvalue_client(), bounds_lon_client(), bounds_lat_client()
37      , srcObject_(0)
38   { /* Ne rien faire de plus */ }
39
40   CDomain::CDomain(const StdString & id)
41      : CObjectTemplate<CDomain>(id), CDomainAttributes()
42      , isChecked(false), relFiles(), isClientChecked(false), nbConnectedClients_(), indSrv_(), connectedServerRank_()
43      , hasBounds(false), hasArea(false), isDistributed_(false), nGlobDomain_(), isCompressible_(false), isUnstructed_(false)
44      , global_zoom_ni(0), global_zoom_ibegin(0), global_zoom_nj(0), global_zoom_jbegin(0)
45      , isClientAfterTransformationChecked(false), hasLonLat(false)
46      , lonvalue_client(), latvalue_client(), bounds_lon_client(), bounds_lat_client()
47      , srcObject_(0)
48   { /* Ne rien faire de plus */ }
49
50   CDomain::~CDomain(void)
51   {
52   }
53
54   ///---------------------------------------------------------------
55
56   CDomain* CDomain::createDomain()
57   {
58     CDomain* domain = CDomainGroup::get("domain_definition")->createChild();
59     return domain;
60   }
61
62   void CDomain::duplicateAttributes(CDomain* domain)
63   {
64     domain->setAttributes(this);
65   }
66
67   const std::set<StdString> & CDomain::getRelFiles(void) const
68   {
69      return (this->relFiles);
70   }
71
72   //----------------------------------------------------------------
73
74   const std::vector<int>& CDomain::getIndexesToWrite(void) const
75   {
76     return indexesToWrite;
77   }
78
79   /*!
80     Returns the number of indexes written by each server.
81     \return the number of indexes written by each server
82   */
83   int CDomain::getNumberWrittenIndexes() const
84   {
85     return numberWrittenIndexes_;
86   }
87
88   /*!
89     Returns the total number of indexes written by the servers.
90     \return the total number of indexes written by the servers
91   */
92   int CDomain::getTotalNumberWrittenIndexes() const
93   {
94     return totalNumberWrittenIndexes_;
95   }
96
97   /*!
98     Returns the offset of indexes written by each server.
99     \return the offset of indexes written by each server
100   */
101   int CDomain::getOffsetWrittenIndexes() const
102   {
103     return offsetWrittenIndexes_;
104   }
105
106   //----------------------------------------------------------------
107
108   bool CDomain::isEmpty(void) const
109   {
110      return ((this->zoom_ni_srv == 0) ||
111              (this->zoom_nj_srv == 0));
112   }
113
114   //----------------------------------------------------------------
115
116   bool CDomain::IsWritten(const StdString & filename) const
117   {
118      return (this->relFiles.find(filename) != this->relFiles.end());
119   }
120
121   bool CDomain::isWrittenCompressed(const StdString& filename) const
122   {
123      return (this->relFilesCompressed.find(filename) != this->relFilesCompressed.end());
124   }
125
126   //----------------------------------------------------------------
127
128   bool CDomain::isDistributed(void) const
129   {
130      return isDistributed_;
131   }
132
133   //----------------------------------------------------------------
134
135   /*!
136    * Test whether the data defined on the domain can be outputted in a compressed way.
137    *
138    * \return true if and only if a mask was defined for this domain
139    */
140   bool CDomain::isCompressible(void) const
141   {
142      return isCompressible_;
143   }
144
145   //----------------------------------------------------------------
146
147   void CDomain::addRelFile(const StdString & filename)
148   {
149      this->relFiles.insert(filename);
150   }
151
152   void CDomain::addRelFileCompressed(const StdString& filename)
153   {
154      this->relFilesCompressed.insert(filename);
155   }
156
157
158   StdString CDomain::GetName(void)   { return (StdString("domain")); }
159   StdString CDomain::GetDefName(void){ return (CDomain::GetName()); }
160   ENodeType CDomain::GetType(void)   { return (eDomain); }
161
162   //----------------------------------------------------------------
163
164   /*!
165     Redistribute RECTILINEAR domain with a number of local domains.
166   All attributes ni,nj,ibegin,jbegin (if defined) will be rewritten
167   The optional attributes lonvalue, latvalue will be added. Because this function only serves (for now)
168   for interpolation from unstructured domain to rectilinear one, range of latvalue is 0-360 and lonvalue is -90 - +90
169    \param [in] nbLocalDomain number of local domain on the domain destination
170   */
171   void CDomain::redistribute(int nbLocalDomain)
172   {
173     if (type_attr::rectilinear == type)
174     {
175        CContext* context = CContext::getCurrent();
176        CContextClient* client = context->client;
177        int rankClient = client->clientRank;
178        int rankOnDomain = rankClient%nbLocalDomain;
179
180        if (ni_glo.isEmpty() || ni_glo <= 0 )
181        {
182           ERROR("CDomain::redistribute(int nbLocalDomain)",
183              << "[ Id = " << this->getId() << " ] "
184              << "The global domain is badly defined,"
185              << " check the \'ni_glo\'  value !")
186        }
187
188        if (nj_glo.isEmpty() || nj_glo <= 0 )
189        {
190           ERROR("CDomain::redistribute(int nbLocalDomain)",
191              << "[ Id = " << this->getId() << " ] "
192              << "The global domain is badly defined,"
193              << " check the \'nj_glo\'  value !")
194        }
195
196        int globalDomainSize = ni_glo * nj_glo;
197        if (globalDomainSize <= nbLocalDomain)
198        {
199          for (int idx = 0; idx < nbLocalDomain; ++idx)
200          {
201            if (rankOnDomain < globalDomainSize)
202            {
203              int iIdx = rankOnDomain % ni_glo;
204              int jIdx = rankOnDomain / ni_glo;
205              ibegin.setValue(iIdx); jbegin.setValue(jIdx);
206              ni.setValue(1); nj.setValue(1);
207            }
208            else
209            {
210              ibegin.setValue(0); jbegin.setValue(0);
211              ni.setValue(0); nj.setValue(0);
212            }
213          }
214        }
215        else
216        {
217          // Compute (approximately) number of segment on x and y axis
218          float yOverXRatio = (nj_glo.getValue())/(ni_glo.getValue());
219          int nbProcOnX, nbProcOnY, range;
220          nbProcOnX = std::ceil(std::sqrt(nbLocalDomain/yOverXRatio));
221          nbProcOnY = std::ceil(((float)nbLocalDomain)/nbProcOnX);
222
223          // Simple distribution: Sweep from top to bottom, left to right
224          // Calculate local begin on x
225          std::vector<int> ibeginVec(nbProcOnX,0), jbeginVec(nbProcOnY,0);
226          std::vector<int> niVec(nbProcOnX), njVec(nbProcOnY);
227          for (int i = 1; i < nbProcOnX; ++i)
228          {
229            range = ni_glo / nbProcOnX;
230            if (i < (ni_glo%nbProcOnX)) ++range;
231            niVec[i-1] = range;
232            ibeginVec[i] = ibeginVec[i-1] + niVec[i-1];
233          }
234          niVec[nbProcOnX-1] = ni_glo - ibeginVec[nbProcOnX-1];
235
236          // Calculate local begin on y
237          for (int j = 1; j < nbProcOnY; ++j)
238          {
239            range = nj_glo / nbProcOnY;
240            if (j < (nj_glo%nbProcOnY)) ++range;
241            njVec[j-1] = range;
242            jbeginVec[j] = jbeginVec[j-1] + njVec[j-1];
243          }
244          njVec[nbProcOnY-1] = nj_glo - jbeginVec[nbProcOnY-1];
245
246          // Now assign value to ni, ibegin, nj, jbegin
247          int iIdx = rankOnDomain % nbProcOnX;
248          int jIdx = rankOnDomain / nbProcOnX;
249
250          if (rankOnDomain != (nbLocalDomain-1))
251          {
252            ibegin.setValue(ibeginVec[iIdx]);
253            jbegin.setValue(jbeginVec[jIdx]);
254            nj.setValue(njVec[jIdx]);
255            ni.setValue(niVec[iIdx]);
256          }
257          else // just merge all the remaining rectangle into the last one
258          {
259            ibegin.setValue(ibeginVec[iIdx]);
260            jbegin.setValue(jbeginVec[jIdx]);
261            nj.setValue(njVec[jIdx]);
262            ni.setValue(ni_glo - ibeginVec[iIdx]);
263          }
264        }
265
266        // Now fill other attributes
267        fillInRectilinearLonLat();
268     }
269   }
270
271   /*!
272     Fill in the values for lonvalue_1d and latvalue_1d of rectilinear domain
273     Range of longitude value from 0 - 360
274     Range of latitude value from -90 - +90
275   */
276   void CDomain::fillInRectilinearLonLat()
277   {
278     if (!lonvalue_2d.isEmpty()) lonvalue_2d.free();
279     if (!latvalue_2d.isEmpty()) latvalue_1d.free();
280     lonvalue_1d.resize(ni);
281     latvalue_1d.resize(nj);
282     double lonStep = double(360/ni_glo.getValue());
283     double latStep = double(180/nj_glo.getValue());
284
285     // Assign lon value
286     for (int i = 0; i < ni; ++i)
287     {
288       lonvalue_1d(i) = static_cast<double>(ibegin + i) * lonStep;
289     }
290
291     for (int j = 0; j < nj; ++j)
292     {
293       latvalue_1d(j) = static_cast<double>(jbegin + j) * latStep - 90;
294     }
295   }
296
297   void CDomain::checkDomain(void)
298   {
299     if (type.isEmpty())
300     {
301       ERROR("CDomain::checkDomain(void)",
302             << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
303             << "The domain type is mandatory, "
304             << "please define the 'type' attribute.")
305     }
306
307     if (type == type_attr::unstructured)
308     {
309        if (ni_glo.isEmpty())
310        {
311          ERROR("CDomain::checkDomain(void)",
312                << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
313                << "The global domain is badly defined, "
314                << "the mandatory 'ni_glo' attribute is missing.")
315        }
316        else if (ni_glo <= 0)
317        {
318          ERROR("CDomain::checkDomain(void)",
319                << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
320                << "The global domain is badly defined, "
321                << "'ni_glo' attribute should be strictly positive so 'ni_glo = " << ni_glo.getValue() << "' is invalid.")
322        }
323        isUnstructed_ = true;
324        nj_glo = 1;
325        nj = 1;
326        jbegin = 0;
327        if (ni.isEmpty()) ni = i_index.numElements();
328        j_index.resize(ni);
329        for(int i=0;i<ni;++i) j_index(i)=0;
330
331        if (!area.isEmpty())
332          area.transposeSelf(1, 0);
333     }
334
335     if (ni_glo.isEmpty())
336     {
337       ERROR("CDomain::checkDomain(void)",
338             << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
339             << "The global domain is badly defined, "
340             << "the mandatory 'ni_glo' attribute is missing.")
341     }
342     else if (ni_glo <= 0)
343     {
344       ERROR("CDomain::checkDomain(void)",
345             << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
346             << "The global domain is badly defined, "
347             << "'ni_glo' attribute should be strictly positive so 'ni_glo = " << ni_glo.getValue() << "' is invalid.")
348     }
349
350     if (nj_glo.isEmpty())
351     {
352       ERROR("CDomain::checkDomain(void)",
353             << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
354             << "The global domain is badly defined, "
355             << "the mandatory 'nj_glo' attribute is missing.")
356     }
357     else if (nj_glo <= 0)
358     {
359       ERROR("CDomain::checkDomain(void)",
360             << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
361             << "The global domain is badly defined, "
362             << "'nj_glo' attribute should be strictly positive so 'nj_glo = " << nj_glo.getValue() << "' is invalid.")
363     }
364
365     isDistributed_ = !ibegin.isEmpty() || !ni.isEmpty() || !jbegin.isEmpty() || !nj.isEmpty();
366
367     checkLocalIDomain();
368     checkLocalJDomain();
369
370     if (i_index.isEmpty())
371     {
372       i_index.resize(ni*nj);
373       for (int j = 0; j < nj; ++j)
374         for (int i = 0; i < ni; ++i) i_index(i+j*ni) = i+ibegin;
375     }
376
377     if (j_index.isEmpty())
378     {
379       j_index.resize(ni*nj);
380       for (int j = 0; j < nj; ++j)
381         for (int i = 0; i < ni; ++i) j_index(i+j*ni) = j+jbegin;
382     }
383     computeNGlobDomain();
384
385     if (0 == global_zoom_ni) global_zoom_ni = ni_glo;
386     if (0 == global_zoom_nj) global_zoom_nj = nj_glo;
387   }
388
389   //----------------------------------------------------------------
390
391   void CDomain::checkLocalIDomain(void)
392   {
393      if (ibegin.isEmpty() && ni.isEmpty())
394      {
395        ibegin = 0;
396        ni = ni_glo;
397      }
398      else if (!i_index.isEmpty())
399      {
400        if (ibegin.isEmpty()) ibegin = i_index(0);
401      }
402
403      if (ni.getValue() < 0 || ibegin.getValue() < 0 ||
404         (ibegin.getValue() + ni.getValue()) > ni_glo.getValue())
405      {
406        ERROR("CDomain::checkLocalIDomain(void)",
407              << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
408              << "The local domain is wrongly defined,"
409              << " check the attributes 'ni_glo' (" << ni_glo.getValue() << "), 'ni' (" << ni.getValue() << ") and 'ibegin' (" << ibegin.getValue() << ")");
410      }
411   }
412
413   void CDomain::checkLocalJDomain(void)
414   {
415     if (jbegin.isEmpty() && nj.isEmpty())
416     {
417       jbegin = 0;
418       nj = nj_glo;
419     }
420     else if (!j_index.isEmpty())
421     {
422       if (jbegin.isEmpty()) jbegin = j_index(0);
423     }
424
425      if (nj.getValue() < 0 || jbegin.getValue() < 0 ||
426         (jbegin.getValue() + nj.getValue()) > nj_glo.getValue())
427      {
428        ERROR("CDomain::checkLocalJDomain(void)",
429              << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
430              << "The local domain is wrongly defined,"
431              << " check the attributes 'nj_glo' (" << nj_glo.getValue() << "), 'nj' (" << nj.getValue() << ") and 'jbegin' (" << jbegin.getValue() << ")");
432      }
433   }
434
435   //----------------------------------------------------------------
436
437   void CDomain::checkMask(void)
438   {
439      if (!mask_1d.isEmpty() && !mask_2d.isEmpty())
440        ERROR("CDomain::checkMask(void)",
441              << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
442              << "Both mask_1d and mask_2d are defined but only one can be used at the same time." << std::endl
443              << "Please define only one mask: 'mask_1d' or 'mask_2d'.");
444
445      if (!mask_1d.isEmpty() && mask_2d.isEmpty())
446      {
447        if (mask_1d.numElements() != i_index.numElements())
448          ERROR("CDomain::checkMask(void)",
449                << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
450                << "'mask_1d' does not have the same size as the local domain." << std::endl
451                << "Local size is " << i_index.numElements() << "." << std::endl
452                << "Mask size is " << mask_1d.numElements() << ".");
453      }
454
455      if (mask_1d.isEmpty() && !mask_2d.isEmpty())
456      {
457        if (mask_2d.extent(0) != ni || mask_2d.extent(1) != nj)
458          ERROR("CDomain::checkMask(void)",
459                << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
460                << "The mask does not have the same size as the local domain." << std::endl
461                << "Local size is " << ni.getValue() << " x " << nj.getValue() << "." << std::endl
462                << "Mask size is " << mask_2d.extent(0) << " x " << mask_2d.extent(1) << ".");
463      }
464
465      if (!mask_2d.isEmpty())
466      {
467        mask_1d.resize(mask_2d.extent(0) * mask_2d.extent(1));
468        for (int j = 0; j < nj; ++j)
469          for (int i = 0; i < ni; ++i) mask_1d(i+j*ni) = mask_2d(i,j);
470        mask_2d.free();
471      }
472      else if (mask_1d.isEmpty())
473      {
474        mask_1d.resize(i_index.numElements());
475        for (int i = 0; i < i_index.numElements(); ++i) mask_1d(i) = true;
476      }
477   }
478
479   //----------------------------------------------------------------
480
481   void CDomain::checkDomainData(void)
482   {
483      if (data_dim.isEmpty())
484      {
485        data_dim.setValue(1);
486      }
487      else if (!(data_dim.getValue() == 1 || data_dim.getValue() == 2))
488      {
489        ERROR("CDomain::checkDomainData(void)",
490              << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
491              << "The data dimension is invalid, 'data_dim' must be 1 or 2 not << " << data_dim.getValue() << ".");
492      }
493
494      if (data_ibegin.isEmpty())
495         data_ibegin.setValue(0);
496      if (data_jbegin.isEmpty())
497         data_jbegin.setValue(0);
498
499      if (data_ni.isEmpty())
500      {
501        data_ni.setValue((data_dim == 1) ? (ni.getValue() * nj.getValue()) : ni.getValue());
502      }
503      else if (data_ni.getValue() < 0)
504      {
505        ERROR("CDomain::checkDomainData(void)",
506              << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
507              << "The data size cannot be negative ('data_ni' = " << data_ni.getValue() << ").");
508      }
509
510      if (data_nj.isEmpty())
511      {
512        data_nj.setValue((data_dim.getValue() == 1) ? (ni.getValue() * nj.getValue()) : nj.getValue());
513      }
514      else if (data_nj.getValue() < 0)
515      {
516        ERROR("CDomain::checkDomainData(void)",
517              << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
518              << "The data size cannot be negative ('data_nj' = " << data_nj.getValue() << ").");
519      }
520   }
521
522   //----------------------------------------------------------------
523
524   void CDomain::checkCompression(void)
525   {
526      if (!data_i_index.isEmpty())
527      {
528        if (!data_j_index.isEmpty() &&
529            data_j_index.numElements() != data_i_index.numElements())
530        {
531           ERROR("CDomain::checkCompression(void)",
532                 << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
533                 << "'data_i_index' and 'data_j_index' arrays must have the same size." << std::endl
534                 << "'data_i_index' size = " << data_i_index.numElements() << std::endl
535                 << "'data_j_index' size = " << data_j_index.numElements());
536        }
537
538        if (2 == data_dim)
539        {
540          if (data_j_index.isEmpty())
541          {
542             ERROR("CDomain::checkCompression(void)",
543                   << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
544                   << "'data_j_index' must be defined when 'data_i_index' is set and 'data_dim' is 2.");
545          }
546        }
547        else // (1 == data_dim)
548        {
549          if (data_j_index.isEmpty())
550          {
551            data_j_index.resize(data_ni);
552            for (int j = 0; j < data_ni; ++j) data_j_index(j) = 0;
553          }
554        }
555      }
556      else
557      {
558        if (data_dim == 2 && !data_j_index.isEmpty())
559          ERROR("CDomain::checkCompression(void)",
560                << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
561                << "'data_i_index' must be defined when 'data_j_index' is set and 'data_dim' is 2.");
562
563        if (1 == data_dim)
564        {
565          data_i_index.resize(data_ni);
566          data_j_index.resize(data_ni);
567
568          for (int i = 0; i < data_ni; ++i)
569          {
570            data_i_index(i) = i;
571            data_j_index(i) = 0;
572          }
573        }
574        else // (data_dim == 2)
575        {
576          const int dsize = data_ni * data_nj;
577          data_i_index.resize(dsize);
578          data_j_index.resize(dsize);
579
580          for(int count = 0, j = 0; j < data_nj; ++j)
581          {
582            for(int i = 0; i < data_ni; ++i, ++count)
583            {
584              data_i_index(count) = i;
585              data_j_index(count) = j;
586            }
587          }
588        }
589      }
590   }
591
592   //----------------------------------------------------------------
593
594   void CDomain::checkEligibilityForCompressedOutput(void)
595   {
596     // We don't check if the mask or the indexes are valid here, just if they have been defined at this point.
597     isCompressible_ = !mask_1d.isEmpty() || !mask_2d.isEmpty() || !data_i_index.isEmpty();
598   }
599
600   //----------------------------------------------------------------
601
602   void CDomain::completeLonLatClient(void)
603   {
604     int i,j,k ;
605     CArray<double,1> lonvalue_temp(ni * nj);
606     CArray<double,2> bounds_lon_temp(nvertex, ni * nj);
607     CArray<double,1> latvalue_temp(ni * nj);
608     CArray<double,2> bounds_lat_temp(nvertex, ni * nj);
609
610     if (!lonvalue_2d.isEmpty())
611     {
612        for (j = 0; j < nj; ++j)
613          for (i = 0; i < ni; ++i)
614          {
615            lonvalue_temp(i + j * ni) = lonvalue_2d(i,j);
616            latvalue_temp(i + j * ni) = latvalue_2d(i,j);
617            if (hasBounds)
618            {
619              k = j * ni + i;
620              for (int n = 0; n < nvertex; ++n)
621              {
622                bounds_lon_temp(n,k) = bounds_lon_2d(n,i,j);
623                bounds_lat_temp(n,k) = bounds_lat_2d(n,i,j);
624              }
625            }
626          }
627     }
628
629     if (!lonvalue_1d.isEmpty())
630     {
631       if (type_attr::rectilinear == type)
632       {
633         if (ni == lonvalue_1d.numElements() && nj == latvalue_1d.numElements())
634         {
635           for (j = 0; j < nj; ++j)
636             for (i = 0; i < ni; ++i)
637             {
638               k = j * ni + i;
639               lonvalue_temp(k) = lonvalue_1d(i);
640               latvalue_temp(k) = latvalue_1d(j);
641               if (hasBounds)
642               {
643                 for (int n = 0; n < nvertex; ++n)
644                 {
645                   bounds_lon_temp(n,k) = bounds_lon_1d(n,i);
646                   bounds_lat_temp(n,k) = bounds_lat_1d(n,j);
647                 }
648               }
649             }
650          }
651          else
652            ERROR("CDomain::completeLonClient(void)",
653                  << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
654                  << "'lonvalue_1d' and 'latvalue_1d' does not have the same size as the local domain." << std::endl
655                  << "'lonvalue_1d' size is " << lonvalue_1d.numElements() << " but it should be " << ni.getValue() << '.' << std::endl
656                  << "'latvalue_1d' size is " << latvalue_1d.numElements() << " but it should be " << nj.getValue() << '.');
657       }
658       else if (type == type_attr::curvilinear || type == type_attr::unstructured)
659       {
660         lonvalue_temp = lonvalue_1d;
661         latvalue_temp = latvalue_1d;
662         if (hasBounds)
663         {
664           bounds_lon_temp = bounds_lon_1d;
665           bounds_lat_temp = bounds_lat_1d;
666         }
667       }
668     }
669
670    int i_ind,j_ind;
671    int global_zoom_iend=global_zoom_ibegin+global_zoom_ni-1;
672    int global_zoom_jend=global_zoom_jbegin+global_zoom_nj-1;
673
674    int globalIndexCountZoom = 0;
675    int nbIndex = i_index.numElements();
676    for (i = 0; i < nbIndex; ++i)
677    {
678      i_ind=i_index(i);
679      j_ind=j_index(i);
680
681      if (i_ind >= global_zoom_ibegin && i_ind <= global_zoom_iend && j_ind >= global_zoom_jbegin && j_ind <= global_zoom_jend)
682      {
683        ++globalIndexCountZoom;
684      }
685    }
686
687      // Make sure that this attribute is non-empty for every client.
688     if (0 != globalIndexCountZoom)
689     {
690       lonvalue_client.resize(globalIndexCountZoom);
691       latvalue_client.resize(globalIndexCountZoom);
692       if (hasBounds)
693       {
694         bounds_lon_client.resize(nvertex,globalIndexCountZoom);
695         bounds_lat_client.resize(nvertex,globalIndexCountZoom);
696       }
697     }
698
699     int nCountZoom = 0;
700     for (i = 0; i < nbIndex; ++i)
701     {
702       i_ind=i_index(i);
703       j_ind=j_index(i);
704
705       if (i_ind >= global_zoom_ibegin && i_ind <= global_zoom_iend && j_ind >= global_zoom_jbegin && j_ind <= global_zoom_jend)
706       {
707         lonvalue_client(nCountZoom) = lonvalue_temp(i);
708         latvalue_client(nCountZoom) = latvalue_temp(i);
709         if (hasBounds)
710         {
711           for (int n = 0; n < nvertex; ++n)
712           {
713             bounds_lon_client(n,nCountZoom) = bounds_lon_temp(n,i);
714             bounds_lat_client(n,nCountZoom) = bounds_lat_temp(n,i);
715           }
716         }
717         ++nCountZoom;
718       }
719     }
720   }
721
722   void CDomain::checkBounds(void)
723   {
724     if (!nvertex.isEmpty() && nvertex > 0)
725     {
726       if (!bounds_lon_1d.isEmpty() && !bounds_lon_2d.isEmpty())
727         ERROR("CDomain::checkBounds(void)",
728               << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
729               << "Only one longitude boundary attribute can be used but both 'bounds_lon_1d' and 'bounds_lon_2d' are defined." << std::endl
730               << "Define only one longitude boundary attribute: 'bounds_lon_1d' or 'bounds_lon_2d'.");
731
732       if (!bounds_lat_1d.isEmpty() && !bounds_lat_2d.isEmpty())
733         ERROR("CDomain::checkBounds(void)",
734               << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
735               << "Only one latitude boundary attribute can be used but both 'bounds_lat_1d' and 'bounds_lat_2d' are defined." << std::endl
736               << "Define only one latitude boundary attribute: 'bounds_lat_1d' or 'bounds_lat_2d'.");
737
738       if ((!bounds_lon_1d.isEmpty() && bounds_lat_1d.isEmpty()) || (bounds_lon_1d.isEmpty() && !bounds_lat_1d.isEmpty()))
739       {
740         ERROR("CDomain::checkBounds(void)",
741               << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
742               << "Only 'bounds_lon_1d' or 'bounds_lat_1d' is defined." << std::endl
743               << "Please define either both attributes or none.");
744       }
745
746       if ((!bounds_lon_2d.isEmpty() && bounds_lat_2d.isEmpty()) || (bounds_lon_2d.isEmpty() && !bounds_lat_2d.isEmpty()))
747       {
748         ERROR("CDomain::checkBounds(void)",
749               << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
750               << "Only 'bounds_lon_2d' or 'bounds_lat_2d' is defined." << std::endl
751               << "Please define either both attributes or none.");
752       }
753
754       if (!bounds_lon_1d.isEmpty() && nvertex.getValue() != bounds_lon_1d.extent(1))
755         ERROR("CDomain::checkBounds(void)",
756               << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
757               << "'bounds_lon_1d' dimension is not compatible with 'nvertex'." << std::endl
758               << "'bounds_lon_1d' dimension is " << bounds_lon_1d.extent(1)
759               << " but nvertex is " << nvertex.getValue() << ".");
760
761       if (!bounds_lon_2d.isEmpty() && nvertex.getValue() != bounds_lon_2d.extent(2))
762         ERROR("CDomain::checkBounds(void)",
763               << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
764               << "'bounds_lon_2d' dimension is not compatible with 'nvertex'." << std::endl
765               << "'bounds_lon_2d' dimension is " << bounds_lon_2d.extent(2)
766               << " but nvertex is " << nvertex.getValue() << ".");
767
768       if (!bounds_lon_1d.isEmpty() && lonvalue_1d.isEmpty())
769         ERROR("CDomain::checkBounds(void)",
770               << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
771               << "Since 'bounds_lon_1d' is defined, 'lonvalue_1d' must be defined too." << std::endl);
772
773       if (!bounds_lon_2d.isEmpty() && lonvalue_2d.isEmpty())
774         ERROR("CDomain::checkBounds(void)",
775               << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
776               << "Since 'bounds_lon_2d' is defined, 'lonvalue_2d' must be defined too." << std::endl);
777
778       if (!bounds_lat_1d.isEmpty() && nvertex.getValue() != bounds_lat_1d.extent(1))
779         ERROR("CDomain::checkBounds(void)",
780               << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
781               << "'bounds_lat_1d' dimension is not compatible with 'nvertex'." << std::endl
782               << "'bounds_lat_1d' dimension is " << bounds_lat_1d.extent(1)
783               << " but nvertex is " << nvertex.getValue() << ".");
784
785       if (!bounds_lat_2d.isEmpty() && nvertex.getValue() != bounds_lat_2d.extent(2))
786         ERROR("CDomain::checkBounds(void)",
787               << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
788               << "'bounds_lat_2d' dimension is not compatible with 'nvertex'." << std::endl
789               << "'bounds_lat_2d' dimension is " << bounds_lat_2d.extent(2)
790               << " but nvertex is " << nvertex.getValue() << ".");
791
792       if (!bounds_lat_1d.isEmpty() && latvalue_1d.isEmpty())
793         ERROR("CDomain::checkBounds(void)",
794               << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
795               << "Since 'bounds_lat_1d' is defined, 'latvalue_1d' must be defined too." << std::endl);
796
797       if (!bounds_lat_2d.isEmpty() && latvalue_2d.isEmpty())
798         ERROR("CDomain::checkBounds(void)",
799               << "Since 'bounds_lat_2d' is defined, 'latvalue_2d' must be defined too." << std::endl);
800
801       hasBounds = true;
802     }
803     else
804     {
805       hasBounds = false;
806       nvertex = 0;
807     }
808   }
809
810   void CDomain::checkArea(void)
811   {
812     hasArea = !area.isEmpty();
813     if (hasArea)
814     {
815       if (area.extent(0) != ni || area.extent(1) != nj)
816       {
817         ERROR("CDomain::checkArea(void)",
818               << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
819               << "The area does not have the same size as the local domain." << std::endl
820               << "Local size is " << ni.getValue() << " x " << nj.getValue() << "." << std::endl
821               << "Area size is " << area.extent(0) << " x " << area.extent(1) << ".");
822       }
823     }
824   }
825
826   void CDomain::checkLonLat()
827   {
828     hasLonLat = (!latvalue_1d.isEmpty() && !lonvalue_1d.isEmpty()) ||
829                 (!latvalue_2d.isEmpty() && !lonvalue_2d.isEmpty());
830     if (hasLonLat)
831     {
832       if (!lonvalue_1d.isEmpty() && !lonvalue_2d.isEmpty())
833         ERROR("CDomain::completeLonLatClient(void)",
834               << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
835               << "Only one longitude attribute can be used but both 'lonvalue_1d' and 'lonvalue_2d' are defined." << std::endl
836               << "Define only one longitude attribute: 'lonvalue_1d' or 'lonvalue_2d'.");
837
838       if (!lonvalue_1d.isEmpty() && lonvalue_2d.isEmpty())
839       {
840         if ((type_attr::rectilinear != type) && (lonvalue_1d.numElements() != i_index.numElements()))
841           ERROR("CDomain::completeLonLatClient(void)",
842                 << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
843                 << "'lonvalue_1d' does not have the same size as the local domain." << std::endl
844                 << "Local size is " << i_index.numElements() << "." << std::endl
845                 << "'lonvalue_1d' size is " << lonvalue_1d.numElements() << ".");
846       }
847
848       if (lonvalue_1d.isEmpty() && !lonvalue_2d.isEmpty())
849       {
850         if (lonvalue_2d.extent(0) != ni || lonvalue_2d.extent(1) != nj)
851           ERROR("CDomain::completeLonLatClient(void)",
852                 << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
853                 << "'lonvalue_2d' does not have the same size as the local domain." << std::endl
854                 << "Local size is " << ni.getValue() << " x " << nj.getValue() << "." << std::endl
855                 << "'lonvalue_2d' size is " << lonvalue_2d.extent(0) << " x " << lonvalue_2d.extent(1) << ".");
856       }
857
858       if (!latvalue_1d.isEmpty() && !latvalue_2d.isEmpty())
859         ERROR("CDomain::completeLonLatClient(void)",
860               << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
861               << "Only one latitude attribute can be used but both 'latvalue_1d' and 'latvalue_2d' are defined." << std::endl
862               << "Define only one latitude attribute: 'latvalue_1d' or 'latvalue_2d'.");
863
864       if (!latvalue_1d.isEmpty() && latvalue_2d.isEmpty())
865       {
866         if ((type_attr::rectilinear != type) && (latvalue_1d.numElements() != i_index.numElements()))
867           ERROR("CDomain::completeLonLatClient(void)",
868                 << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
869                 << "'latvalue_1d' does not have the same size as the local domain." << std::endl
870                 << "Local size is " << i_index.numElements() << "." << std::endl
871                 << "'latvalue_1d' size is " << latvalue_1d.numElements() << ".");
872       }
873
874       if (latvalue_1d.isEmpty() && !latvalue_2d.isEmpty())
875       {
876         if (latvalue_2d.extent(0) != ni || latvalue_2d.extent(1) != nj)
877           ERROR("CDomain::completeLonLatClient(void)",
878                 << "[ id = " << this->getId() << " , context = '" << CObjectFactory::GetCurrentContextId() << " ] "
879                 << "'latvalue_2d' does not have the same size as the local domain." << std::endl
880                 << "Local size is " << ni.getValue() << " x " << nj.getValue() << "." << std::endl
881                 << "'latvalue_2d' size is " << latvalue_2d.extent(0) << " x " << latvalue_2d.extent(1) << ".");
882       }
883     }
884   }
885
886   void CDomain::checkAttributesOnClientAfterTransformation()
887   {
888     CContext* context=CContext::getCurrent() ;
889
890     if (this->isClientAfterTransformationChecked) return;
891     if (context->hasClient)
892     {
893       this->checkMask();
894       if (hasLonLat || hasArea) this->computeConnectedServer();
895       if (hasLonLat) this->completeLonLatClient();
896     }
897
898     this->isClientAfterTransformationChecked = true;
899   }
900
901   //----------------------------------------------------------------
902   // Divide function checkAttributes into 2 seperate ones
903   // This function only checks all attributes of current domain
904   void CDomain::checkAttributesOnClient()
905   {
906     if (this->isClientChecked) return;
907     CContext* context=CContext::getCurrent();
908
909      this->checkDomain();
910      this->checkBounds();
911      this->checkArea();
912      this->checkLonLat();
913
914      if (context->hasClient)
915      { // CÃŽté client uniquement
916         this->checkMask();
917         this->checkDomainData();
918         this->checkCompression();
919      }
920      else
921      { // CÃŽté serveur uniquement
922      }
923
924      this->isClientChecked = true;
925   }
926
927   // Send all checked attributes to server
928   void CDomain::sendCheckedAttributes()
929   {
930     if (!this->isClientChecked) checkAttributesOnClient();
931     if (!this->isClientAfterTransformationChecked) checkAttributesOnClientAfterTransformation();
932     CContext* context=CContext::getCurrent() ;
933
934     if (this->isChecked) return;
935     if (context->hasClient)
936     {
937       sendServerAttribut();
938       if (hasLonLat || hasArea) sendLonLatArea();
939     }
940     this->isChecked = true;
941   }
942
943   void CDomain::checkAttributes(void)
944   {
945      if (this->isChecked) return;
946      CContext* context=CContext::getCurrent() ;
947
948      this->checkDomain();
949      this->checkLonLat();
950      this->checkBounds();
951      this->checkArea();
952
953      if (context->hasClient)
954      { // CÃŽté client uniquement
955         this->checkMask();
956         this->checkDomainData();
957         this->checkCompression();
958
959      }
960      else
961      { // CÃŽté serveur uniquement
962      }
963
964      if (context->hasClient)
965      {
966        this->computeConnectedServer();
967        this->completeLonLatClient();
968        this->sendServerAttribut();
969        this->sendLonLatArea();
970      }
971
972      this->isChecked = true;
973   }
974
975  void CDomain::sendServerAttribut(void)
976  {
977    CServerDistributionDescription serverDescription(nGlobDomain_);
978
979    CContext* context = CContext::getCurrent();
980    CContextClient* client = context->client;
981    int nbServer = client->serverSize;
982
983    if (isUnstructed_) serverDescription.computeServerDistribution(nbServer,0);
984    else serverDescription.computeServerDistribution(nbServer,1);
985
986    std::vector<std::vector<int> > serverIndexBegin = serverDescription.getServerIndexBegin();
987    std::vector<std::vector<int> > serverDimensionSizes = serverDescription.getServerDimensionSizes();
988
989    CEventClient event(getType(),EVENT_ID_SERVER_ATTRIBUT);
990    if (client->isServerLeader())
991    {
992      std::list<CMessage> msgs;
993
994      const std::list<int>& ranks = client->getRanksServerLeader();
995      for (std::list<int>::const_iterator itRank = ranks.begin(), itRankEnd = ranks.end(); itRank != itRankEnd; ++itRank)
996      {
997        // Use const int to ensure CMessage holds a copy of the value instead of just a reference
998        const int ibegin_srv = serverIndexBegin[*itRank][0];
999        const int jbegin_srv = serverIndexBegin[*itRank][1];
1000        const int ni_srv = serverDimensionSizes[*itRank][0];
1001        const int nj_srv = serverDimensionSizes[*itRank][1];
1002        const int iend_srv = ibegin_srv + ni_srv - 1;
1003        const int jend_srv = jbegin_srv + nj_srv - 1;
1004
1005        msgs.push_back(CMessage());
1006        CMessage& msg = msgs.back();
1007        msg << this->getId() ;
1008        msg << ni_srv << ibegin_srv << iend_srv << nj_srv << jbegin_srv << jend_srv;
1009        msg << global_zoom_ni << global_zoom_ibegin << global_zoom_nj << global_zoom_jbegin;
1010        msg << isCompressible_;
1011
1012        event.push(*itRank,1,msg);
1013      }
1014      client->sendEvent(event);
1015    }
1016    else client->sendEvent(event);
1017  }
1018
1019  void CDomain::computeNGlobDomain()
1020  {
1021    nGlobDomain_.resize(2);
1022    nGlobDomain_[0] = ni_glo.getValue();
1023    nGlobDomain_[1] = nj_glo.getValue();
1024  }
1025
1026  void CDomain::computeConnectedServer(void)
1027  {
1028    CContext* context=CContext::getCurrent() ;
1029    CContextClient* client=context->client ;
1030    int nbServer=client->serverSize;
1031    bool doComputeGlobalIndexServer = true;
1032
1033    int i,j,i_ind,j_ind, nbIndex;
1034    int global_zoom_iend=global_zoom_ibegin+global_zoom_ni-1 ;
1035    int global_zoom_jend=global_zoom_jbegin+global_zoom_nj-1 ;
1036
1037    // Precompute number of index
1038    int globalIndexCountZoom = 0;
1039    nbIndex = i_index.numElements();
1040    for (i = 0; i < nbIndex; ++i)
1041    {
1042      i_ind=i_index(i);
1043      j_ind=j_index(i);
1044
1045      if (i_ind >= global_zoom_ibegin && i_ind <= global_zoom_iend && j_ind >= global_zoom_jbegin && j_ind <= global_zoom_jend)
1046      {
1047        ++globalIndexCountZoom;
1048      }
1049    }
1050
1051    int globalIndexWrittenCount = 0;
1052    if (isCompressible_)
1053    {
1054      for (i = 0; i < data_i_index.numElements(); ++i)
1055      {
1056        i_ind = CDistributionClient::getDomainIndex(data_i_index(i), data_j_index(i),
1057                                                    data_ibegin, data_jbegin, data_dim, ni,
1058                                                    j_ind);
1059        if (i_ind >= 0 && i_ind < ni && j_ind >= 0 && j_ind < nj && mask_1d(i_ind + j_ind * ni))
1060        {
1061          i_ind += ibegin;
1062          j_ind += jbegin;
1063          if (i_ind >= global_zoom_ibegin && i_ind <= global_zoom_iend && j_ind >= global_zoom_jbegin && j_ind <= global_zoom_jend)
1064            ++globalIndexWrittenCount;
1065        }
1066      }
1067    }
1068
1069    // Fill in index
1070    CArray<size_t,1> globalIndexDomainZoom(globalIndexCountZoom);
1071    CArray<size_t,1> localIndexDomainZoom(globalIndexCountZoom);
1072    CArray<size_t,1> globalIndexDomain(nbIndex);
1073    size_t globalIndex;
1074    int globalIndexCount = 0;
1075    globalIndexCountZoom = 0;
1076
1077    for (i = 0; i < nbIndex; ++i)
1078    {
1079      i_ind=i_index(i);
1080      j_ind=j_index(i);
1081      globalIndex = i_ind + j_ind * ni_glo;
1082      globalIndexDomain(globalIndexCount) = globalIndex;
1083      ++globalIndexCount;
1084      if (i_ind >= global_zoom_ibegin && i_ind <= global_zoom_iend && j_ind >= global_zoom_jbegin && j_ind <= global_zoom_jend)
1085      {
1086        globalIndexDomainZoom(globalIndexCountZoom) = globalIndex;
1087        localIndexDomainZoom(globalIndexCountZoom) = i;
1088        ++globalIndexCountZoom;
1089      }
1090    }
1091
1092    CArray<int,1> globalIndexWrittenDomain(globalIndexWrittenCount);
1093    if (isCompressible_)
1094    {
1095      globalIndexWrittenCount = 0;
1096      for (i = 0; i < data_i_index.numElements(); ++i)
1097      {
1098        i_ind = CDistributionClient::getDomainIndex(data_i_index(i), data_j_index(i),
1099                                                    data_ibegin, data_jbegin, data_dim, ni,
1100                                                    j_ind);
1101        if (i_ind >= 0 && i_ind < ni && j_ind >= 0 && j_ind < nj && mask_1d(i_ind + j_ind * ni))
1102        {
1103          i_ind += ibegin;
1104          j_ind += jbegin;
1105          if (i_ind >= global_zoom_ibegin && i_ind <= global_zoom_iend && j_ind >= global_zoom_jbegin && j_ind <= global_zoom_jend)
1106          {
1107            globalIndexWrittenDomain(globalIndexWrittenCount) = i_ind + j_ind * ni_glo;
1108            ++globalIndexWrittenCount;
1109          }
1110        }
1111      }
1112    }
1113
1114    size_t globalSizeIndex = 1, indexBegin, indexEnd;
1115    int range, clientSize = client->clientSize;
1116    for (int i = 0; i < nGlobDomain_.size(); ++i) globalSizeIndex *= nGlobDomain_[i];
1117    indexBegin = 0;
1118    for (int i = 0; i < clientSize; ++i)
1119    {
1120      range = globalSizeIndex / clientSize;
1121      if (i < (globalSizeIndex%clientSize)) ++range;
1122      if (i == client->clientRank) break;
1123      indexBegin += range;
1124    }
1125    indexEnd = indexBegin + range - 1;
1126
1127    CServerDistributionDescription serverDescription(nGlobDomain_);
1128    if (isUnstructed_) serverDescription.computeServerGlobalIndexInRange(nbServer, std::make_pair<size_t,size_t>(indexBegin, indexEnd), 0);
1129    else serverDescription.computeServerGlobalIndexInRange(nbServer, std::make_pair<size_t,size_t>(indexBegin, indexEnd), 1);
1130
1131    CClientServerMapping* clientServerMap = new CClientServerMappingDistributed(serverDescription.getGlobalIndexRange(),
1132                                                                                client->intraComm);
1133    clientServerMap->computeServerIndexMapping(globalIndexDomain);
1134    const std::map<int, std::vector<size_t> >& globalIndexDomainOnServer = clientServerMap->getGlobalIndexOnServer();
1135
1136    std::map<int, std::vector<size_t> >::const_iterator it = globalIndexDomainOnServer.begin(),
1137                                                       ite = globalIndexDomainOnServer.end();
1138    typedef XIOSBinarySearchWithIndex<size_t> BinarySearch;
1139    std::vector<int>::iterator itVec;
1140
1141    indSrv_.clear();
1142    indWrittenSrv_.clear();
1143    for (; it != ite; ++it)
1144    {
1145      int rank = it->first;
1146      int indexSize = it->second.size();
1147      std::vector<int> permutIndex(indexSize);
1148      XIOSAlgorithms::fillInIndex(indexSize, permutIndex);
1149      XIOSAlgorithms::sortWithIndex<size_t, CVectorStorage>(it->second, permutIndex);
1150      BinarySearch binSearch(it->second);
1151      int nb = globalIndexDomainZoom.numElements();
1152      for (int i = 0; i < nb; ++i)
1153      {
1154        if (binSearch.search(permutIndex.begin(), permutIndex.end(), globalIndexDomainZoom(i), itVec))
1155        {
1156          indSrv_[rank].push_back(localIndexDomainZoom(i));
1157        }
1158      }
1159      for (int i = 0; i < globalIndexWrittenDomain.numElements(); ++i)
1160      {
1161        if (binSearch.search(permutIndex.begin(), permutIndex.end(), globalIndexWrittenDomain(i), itVec))
1162        {
1163          indWrittenSrv_[rank].push_back(globalIndexWrittenDomain(i));
1164        }
1165      }
1166    }
1167
1168    connectedServerRank_.clear();
1169    for (it = globalIndexDomainOnServer.begin(); it != ite; ++it) {
1170      connectedServerRank_.push_back(it->first);
1171    }
1172
1173    if (!indSrv_.empty())
1174    {
1175      connectedServerRank_.clear();
1176      for (it = indSrv_.begin(); it != indSrv_.end(); ++it)
1177        connectedServerRank_.push_back(it->first);
1178    }
1179    nbConnectedClients_ = clientServerMap->computeConnectedClients(client->serverSize, client->clientSize, client->intraComm, connectedServerRank_);
1180
1181    delete clientServerMap;
1182  }
1183
1184  const std::map<int, vector<size_t> >& CDomain::getIndexServer() const
1185  {
1186    return indSrv_;
1187  }
1188
1189  /*!
1190    Send index from client to server(s)
1191  */
1192  void CDomain::sendIndex()
1193  {
1194    int ns, n, i, j, ind, nv, idx;
1195    CContext* context = CContext::getCurrent();
1196    CContextClient* client=context->client;
1197
1198    CEventClient eventIndex(getType(), EVENT_ID_INDEX);
1199
1200    list<CMessage> list_msgsIndex;
1201    list<CArray<int,1> > list_indi, list_indj, list_writtenInd;
1202
1203    std::map<int, std::vector<size_t> >::const_iterator it, iteMap;
1204    iteMap = indSrv_.end();
1205    for (int k = 0; k < connectedServerRank_.size(); ++k)
1206    {
1207      int nbData = 0;
1208      int rank = connectedServerRank_[k];
1209      it = indSrv_.find(rank);
1210      if (iteMap != it)
1211        nbData = it->second.size();
1212
1213      list_indi.push_back(CArray<int,1>(nbData));
1214      list_indj.push_back(CArray<int,1>(nbData));
1215
1216      CArray<int,1>& indi = list_indi.back();
1217      CArray<int,1>& indj = list_indj.back();
1218      const std::vector<size_t>& temp = it->second;
1219      for (n = 0; n < nbData; ++n)
1220      {
1221        idx = static_cast<int>(it->second[n]);
1222        indi(n) = i_index(idx);
1223        indj(n) = j_index(idx);
1224      }
1225
1226      list_msgsIndex.push_back(CMessage());
1227
1228      list_msgsIndex.back() << this->getId() << (int)type; // enum ne fonctionne pour les message => ToFix
1229      list_msgsIndex.back() << isCurvilinear;
1230      list_msgsIndex.back() << list_indi.back() << list_indj.back();
1231
1232      if (isCompressible_)
1233      {
1234        std::vector<int>& writtenIndSrc = indWrittenSrv_[rank];
1235        list_writtenInd.push_back(CArray<int,1>(writtenIndSrc.size()));
1236        CArray<int,1>& writtenInd = list_writtenInd.back();
1237
1238        for (n = 0; n < writtenInd.numElements(); ++n)
1239          writtenInd(n) = writtenIndSrc[n];
1240
1241        list_msgsIndex.back() << writtenInd;
1242      }
1243
1244      eventIndex.push(rank, nbConnectedClients_[rank], list_msgsIndex.back());
1245    }
1246
1247    client->sendEvent(eventIndex);
1248  }
1249
1250  /*!
1251    Send area from client to server(s)
1252  */
1253  void CDomain::sendArea()
1254  {
1255    if (!hasArea) return;
1256
1257    int ns, n, i, j, ind, nv, idx;
1258    CContext* context = CContext::getCurrent();
1259    CContextClient* client=context->client;
1260
1261    // send area for each connected server
1262    CEventClient eventArea(getType(), EVENT_ID_AREA);
1263
1264    list<CMessage> list_msgsArea;
1265    list<CArray<double,1> > list_area;
1266
1267    std::map<int, std::vector<size_t> >::const_iterator it, iteMap;
1268    iteMap = indSrv_.end();
1269    for (int k = 0; k < connectedServerRank_.size(); ++k)
1270    {
1271      int nbData = 0;
1272      int rank = connectedServerRank_[k];
1273      it = indSrv_.find(rank);
1274      if (iteMap != it)
1275        nbData = it->second.size();
1276      list_area.push_back(CArray<double,1>(nbData));
1277
1278      const std::vector<size_t>& temp = it->second;
1279      for (n = 0; n < nbData; ++n)
1280      {
1281        idx = static_cast<int>(it->second[n]);
1282        i = i_index(idx);
1283        j = j_index(idx);
1284        if (hasArea)
1285          list_area.back()(n) = area(i - ibegin, j - jbegin);
1286      }
1287
1288      list_msgsArea.push_back(CMessage());
1289      list_msgsArea.back() << this->getId() << list_area.back();
1290      eventArea.push(rank, nbConnectedClients_[rank], list_msgsArea.back());
1291    }
1292    client->sendEvent(eventArea);
1293  }
1294
1295  /*!
1296    Send longitude and latitude from client to servers
1297    Each client send long and lat information to corresponding connected server(s).
1298    Because longitude and latitude are optional, this function only called if latitude and longitude exist
1299  */
1300  void CDomain::sendLonLat()
1301  {
1302    if (!hasLonLat) return;
1303
1304    int ns, n, i, j, ind, nv, idx;
1305    CContext* context = CContext::getCurrent();
1306    CContextClient* client=context->client;
1307
1308    // send lon lat for each connected server
1309    CEventClient eventLon(getType(), EVENT_ID_LON);
1310    CEventClient eventLat(getType(), EVENT_ID_LAT);
1311
1312    list<CMessage> list_msgsLon, list_msgsLat;
1313    list<CArray<double,1> > list_lon, list_lat;
1314    list<CArray<double,2> > list_boundslon, list_boundslat;
1315
1316    std::map<int, std::vector<size_t> >::const_iterator it, iteMap;
1317    iteMap = indSrv_.end();
1318    for (int k = 0; k < connectedServerRank_.size(); ++k)
1319    {
1320      int nbData = 0;
1321      int rank = connectedServerRank_[k];
1322      it = indSrv_.find(rank);
1323      if (iteMap != it)
1324        nbData = it->second.size();
1325
1326      list_lon.push_back(CArray<double,1>(nbData));
1327      list_lat.push_back(CArray<double,1>(nbData));
1328
1329      if (hasBounds)
1330      {
1331        list_boundslon.push_back(CArray<double,2>(nvertex, nbData));
1332        list_boundslat.push_back(CArray<double,2>(nvertex, nbData));
1333      }
1334
1335      CArray<double,1>& lon = list_lon.back();
1336      CArray<double,1>& lat = list_lat.back();
1337      const std::vector<size_t>& temp = it->second;
1338      for (n = 0; n < nbData; ++n)
1339      {
1340        idx = static_cast<int>(it->second[n]);
1341        lon(n) = lonvalue_client(idx);
1342        lat(n) = latvalue_client(idx);
1343
1344        if (hasBounds)
1345        {
1346          CArray<double,2>& boundslon = list_boundslon.back();
1347          CArray<double,2>& boundslat = list_boundslat.back();
1348
1349          for (nv = 0; nv < nvertex; ++nv)
1350          {
1351            boundslon(nv, n) = bounds_lon_client(nv, idx);
1352            boundslat(nv, n) = bounds_lat_client(nv, idx);
1353          }
1354        }
1355      }
1356
1357      list_msgsLon.push_back(CMessage());
1358      list_msgsLat.push_back(CMessage());
1359
1360      list_msgsLon.back() << this->getId() << list_lon.back();
1361      list_msgsLat.back() << this->getId() << list_lat.back();
1362
1363      if (hasBounds)
1364      {
1365        list_msgsLon.back() << list_boundslon.back();
1366        list_msgsLat.back() << list_boundslat.back();
1367      }
1368
1369      eventLon.push(rank, nbConnectedClients_[rank], list_msgsLon.back());
1370      eventLat.push(rank, nbConnectedClients_[rank], list_msgsLat.back());
1371    }
1372
1373    client->sendEvent(eventLon);
1374    client->sendEvent(eventLat);
1375  }
1376
1377  /*!
1378    Send some optional information to server(s)
1379    In the future, this function can be extended with more optional information to send
1380  */
1381  void CDomain::sendLonLatArea(void)
1382  {
1383    sendIndex();
1384    sendLonLat();
1385    sendArea();
1386  }
1387
1388  bool CDomain::dispatchEvent(CEventServer& event)
1389  {
1390    if (SuperClass::dispatchEvent(event)) return true;
1391    else
1392    {
1393      switch(event.type)
1394      {
1395        case EVENT_ID_SERVER_ATTRIBUT:
1396          recvServerAttribut(event);
1397          return true;
1398          break;
1399        case EVENT_ID_INDEX:
1400          recvIndex(event);
1401          return true;
1402          break;
1403        case EVENT_ID_LON:
1404          recvLon(event);
1405          return true;
1406          break;
1407        case EVENT_ID_LAT:
1408          recvLat(event);
1409          return true;
1410          break;
1411        case EVENT_ID_AREA:
1412          recvArea(event);
1413          return true;
1414          break;
1415        default:
1416          ERROR("bool CContext::dispatchEvent(CEventServer& event)",
1417                << "Unknown Event");
1418          return false;
1419       }
1420    }
1421  }
1422
1423  /*!
1424    Receive attributes event from clients(s)
1425    \param[in] event event contain info about rank and associated attributes
1426  */
1427  void CDomain::recvServerAttribut(CEventServer& event)
1428  {
1429    CBufferIn* buffer=event.subEvents.begin()->buffer;
1430    string domainId ;
1431    *buffer>>domainId ;
1432    get(domainId)->recvServerAttribut(*buffer) ;
1433  }
1434
1435  /*!
1436    Receive attributes from client(s): zoom info and begin and n of each server
1437    \param[in] rank rank of client source
1438    \param[in] buffer message containing attributes info
1439  */
1440  void CDomain::recvServerAttribut(CBufferIn& buffer)
1441  {
1442    buffer >> ni_srv >> ibegin_srv >> iend_srv >> nj_srv >> jbegin_srv >> jend_srv
1443           >> global_zoom_ni >> global_zoom_ibegin >> global_zoom_nj >> global_zoom_jbegin
1444           >> isCompressible_;
1445
1446    int zoom_iend = global_zoom_ibegin + global_zoom_ni - 1;
1447    int zoom_jend = global_zoom_jbegin + global_zoom_nj - 1;
1448
1449    zoom_ibegin_srv = global_zoom_ibegin > ibegin_srv ? global_zoom_ibegin : ibegin_srv ;
1450    zoom_iend_srv = zoom_iend < iend_srv ? zoom_iend : iend_srv ;
1451    zoom_ni_srv=zoom_iend_srv-zoom_ibegin_srv+1 ;
1452
1453    zoom_jbegin_srv = global_zoom_jbegin > jbegin_srv ? global_zoom_jbegin : jbegin_srv ;
1454    zoom_jend_srv = zoom_jend < jend_srv ? zoom_jend : jend_srv ;
1455    zoom_nj_srv=zoom_jend_srv-zoom_jbegin_srv+1 ;
1456
1457    if (zoom_ni_srv<=0 || zoom_nj_srv<=0)
1458    {
1459      zoom_ibegin_srv=0 ; zoom_iend_srv=0 ; zoom_ni_srv=0 ;
1460      zoom_jbegin_srv=0 ; zoom_jend_srv=0 ; zoom_nj_srv=0 ;
1461    }
1462    lonvalue_srv.resize(zoom_ni_srv*zoom_nj_srv) ;
1463    lonvalue_srv = 0. ;
1464    latvalue_srv.resize(zoom_ni_srv*zoom_nj_srv) ;
1465    latvalue_srv = 0. ;
1466    if (hasBounds)
1467    {
1468      bounds_lon_srv.resize(nvertex,zoom_ni_srv*zoom_nj_srv) ;
1469      bounds_lon_srv = 0. ;
1470      bounds_lat_srv.resize(nvertex,zoom_ni_srv*zoom_nj_srv) ;
1471      bounds_lat_srv = 0. ;
1472    }
1473
1474    if (hasArea)
1475      area_srv.resize(zoom_ni_srv * zoom_nj_srv);
1476  }
1477
1478  /*!
1479    Receive index event from clients(s)
1480    \param[in] event event contain info about rank and associated index
1481  */
1482  void CDomain::recvIndex(CEventServer& event)
1483  {
1484    CDomain* domain;
1485
1486    list<CEventServer::SSubEvent>::iterator it;
1487    for (it = event.subEvents.begin(); it != event.subEvents.end(); ++it)
1488    {
1489      CBufferIn* buffer = it->buffer;
1490      string domainId;
1491      *buffer >> domainId;
1492      domain = get(domainId);
1493      domain->recvIndex(it->rank, *buffer);
1494    }
1495
1496    if (domain->isCompressible_)
1497    {
1498      std::sort(domain->indexesToWrite.begin(), domain->indexesToWrite.end());
1499
1500      CContextServer* server = CContext::getCurrent()->server;
1501      domain->numberWrittenIndexes_ = domain->indexesToWrite.size();
1502      MPI_Allreduce(&domain->numberWrittenIndexes_, &domain->totalNumberWrittenIndexes_, 1, MPI_INT, MPI_SUM, server->intraComm);
1503      MPI_Scan(&domain->numberWrittenIndexes_, &domain->offsetWrittenIndexes_, 1, MPI_INT, MPI_SUM, server->intraComm);
1504      domain->offsetWrittenIndexes_ -= domain->numberWrittenIndexes_;
1505    }
1506  }
1507
1508  /*!
1509    Receive index information from client(s)
1510    \param[in] rank rank of client source
1511    \param[in] buffer message containing index info
1512  */
1513  void CDomain::recvIndex(int rank, CBufferIn& buffer)
1514  {
1515    int type_int;
1516    buffer >> type_int >> isCurvilinear >> indiSrv[rank] >> indjSrv[rank];
1517    type.setValue((type_attr::t_enum)type_int); // probleme des type enum avec les buffers : ToFix
1518
1519    if (isCompressible_)
1520    {
1521      CArray<int, 1> writtenIndexes;
1522      buffer >> writtenIndexes;
1523      indexesToWrite.reserve(indexesToWrite.size() + writtenIndexes.numElements());
1524      for (int i = 0; i < writtenIndexes.numElements(); ++i)
1525        indexesToWrite.push_back(writtenIndexes(i));
1526    }
1527  }
1528
1529  /*!
1530    Receive longitude event from clients(s)
1531    \param[in] event event contain info about rank and associated longitude
1532  */
1533  void CDomain::recvLon(CEventServer& event)
1534  {
1535    list<CEventServer::SSubEvent>::iterator it;
1536    for (it = event.subEvents.begin(); it != event.subEvents.end(); ++it)
1537    {
1538      CBufferIn* buffer = it->buffer;
1539      string domainId;
1540      *buffer >> domainId;
1541      get(domainId)->recvLon(it->rank, *buffer);
1542    }
1543  }
1544
1545  /*!
1546    Receive longitude information from client(s)
1547    \param[in] rank rank of client source
1548    \param[in] buffer message containing longitude info
1549  */
1550  void CDomain::recvLon(int rank, CBufferIn& buffer)
1551  {
1552    CArray<int,1> &indi = indiSrv[rank], &indj = indjSrv[rank];
1553    CArray<double,1> lon;
1554    CArray<double,2> boundslon;
1555
1556    buffer >> lon;
1557    if (hasBounds) buffer >> boundslon;
1558
1559    int i, j, ind_srv;
1560    for (int ind = 0; ind < indi.numElements(); ind++)
1561    {
1562      i = indi(ind); j = indj(ind);
1563      ind_srv = (i - zoom_ibegin_srv) + (j - zoom_jbegin_srv) * zoom_ni_srv;
1564      lonvalue_srv(ind_srv) = lon(ind);
1565      if (hasBounds)
1566      {
1567        for (int nv = 0; nv < nvertex; ++nv)
1568          bounds_lon_srv(nv, ind_srv) = boundslon(nv, ind);
1569      }
1570    }
1571  }
1572
1573  /*!
1574    Receive latitude event from clients(s)
1575    \param[in] event event contain info about rank and associated latitude
1576  */
1577  void CDomain::recvLat(CEventServer& event)
1578  {
1579    list<CEventServer::SSubEvent>::iterator it;
1580    for (it = event.subEvents.begin(); it != event.subEvents.end(); ++it)
1581    {
1582      CBufferIn* buffer = it->buffer;
1583      string domainId;
1584      *buffer >> domainId;
1585      get(domainId)->recvLat(it->rank, *buffer);
1586    }
1587  }
1588
1589  /*!
1590    Receive latitude information from client(s)
1591    \param[in] rank rank of client source
1592    \param[in] buffer message containing latitude info
1593  */
1594  void CDomain::recvLat(int rank, CBufferIn& buffer)
1595  {
1596    CArray<int,1> &indi = indiSrv[rank], &indj = indjSrv[rank];
1597    CArray<double,1> lat;
1598    CArray<double,2> boundslat;
1599
1600    buffer >> lat;
1601    if (hasBounds) buffer >> boundslat;
1602
1603    int i, j, ind_srv;
1604    for (int ind = 0; ind < indi.numElements(); ind++)
1605    {
1606      i = indi(ind); j = indj(ind);
1607      ind_srv = (i - zoom_ibegin_srv) + (j - zoom_jbegin_srv) * zoom_ni_srv;
1608      latvalue_srv(ind_srv) = lat(ind);
1609      if (hasBounds)
1610      {
1611        for (int nv = 0; nv < nvertex; nv++)
1612          bounds_lat_srv(nv, ind_srv) = boundslat(nv, ind);
1613      }
1614    }
1615  }
1616
1617  /*!
1618    Receive area event from clients(s)
1619    \param[in] event event contain info about rank and associated area
1620  */
1621  void CDomain::recvArea(CEventServer& event)
1622  {
1623    list<CEventServer::SSubEvent>::iterator it;
1624    for (it = event.subEvents.begin(); it != event.subEvents.end(); ++it)
1625    {
1626      CBufferIn* buffer = it->buffer;
1627      string domainId;
1628      *buffer >> domainId;
1629      get(domainId)->recvArea(it->rank, *buffer);
1630    }
1631  }
1632
1633  /*!
1634    Receive area information from client(s)
1635    \param[in] rank rank of client source
1636    \param[in] buffer message containing area info
1637  */
1638  void CDomain::recvArea(int rank, CBufferIn& buffer)
1639  {
1640    CArray<int,1> &indi = indiSrv[rank], &indj = indjSrv[rank];
1641    CArray<double,1> clientArea;
1642
1643    buffer >> clientArea;
1644
1645    int i, j, ind_srv;
1646    for (int ind = 0; ind < indi.numElements(); ind++)
1647    {
1648      i = indi(ind); j = indj(ind);
1649      ind_srv = (i - zoom_ibegin_srv) + (j - zoom_jbegin_srv) * zoom_ni_srv;
1650      area_srv(ind_srv) = clientArea(ind);
1651    }
1652  }
1653
1654  /*!
1655    Check whether a domain has transformation
1656    \return true if domain has transformation
1657  */
1658  bool CDomain::hasTransformation()
1659  {
1660    return (!transformationMap_.empty());
1661  }
1662
1663  /*!
1664    Set transformation for current domain. It's the method to move transformation in hierarchy
1665    \param [in] domTrans transformation on domain
1666  */
1667  void CDomain::setTransformations(const TransMapTypes& domTrans)
1668  {
1669    transformationMap_ = domTrans;
1670  }
1671
1672  /*!
1673    Get all transformation current domain has
1674    \return all transformation
1675  */
1676  CDomain::TransMapTypes CDomain::getAllTransformations(void)
1677  {
1678    return transformationMap_;
1679  }
1680
1681  /*!
1682    Check the validity of all transformations applied on domain
1683  This functions is called AFTER all inherited attributes are solved
1684  */
1685  void CDomain::checkTransformations()
1686  {
1687    TransMapTypes::const_iterator itb = transformationMap_.begin(), it,
1688                                  ite = transformationMap_.end();
1689    for (it = itb; it != ite; ++it)
1690    {
1691      (it->second)->checkValid(this);
1692    }
1693  }
1694
1695  /*!
1696    A current domain will go up the hierarchy to find out the domain from which it has transformation
1697  */
1698  void CDomain::solveInheritanceTransformation()
1699  {
1700    if (this->hasTransformation()) return;
1701
1702    std::vector<CDomain*> refDomain;
1703    CDomain* refer_sptr;
1704    CDomain* refer_ptr = this;
1705    while (refer_ptr->hasDirectDomainReference())
1706    {
1707      refDomain.push_back(refer_ptr);
1708      refer_sptr = refer_ptr->getDirectDomainReference();
1709      refer_ptr  = refer_sptr;
1710      if (refer_ptr->hasTransformation()) break;
1711    }
1712
1713    if (refer_ptr->hasTransformation())
1714      for (int idx = 0; idx < refDomain.size(); ++idx)
1715        refDomain[idx]->setTransformations(refer_ptr->getAllTransformations());
1716  }
1717
1718  void CDomain::solveSrcInheritance()
1719  {
1720    if (!domain_src.isEmpty())
1721    {
1722       if (!CDomain::has(this->domain_src.getValue()))                                   \
1723          ERROR("CDomain::solveSrcInheritance()",                                \
1724             << "[ src_name = " << this->domain_src.getValue() << "]"                 \
1725             << " invalid domain name !");
1726
1727       srcObject_ = CDomain::get(this->domain_src.getValue());
1728    }
1729  }
1730
1731  CDomain* CDomain::getDomainSrc()
1732  {
1733    return srcObject_;
1734  }
1735
1736  /*!
1737    Parse children nodes of a domain in xml file.
1738    Whenver there is a new transformation, its type and name should be added into this function
1739    \param node child node to process
1740  */
1741  void CDomain::parse(xml::CXMLNode & node)
1742  {
1743    SuperClass::parse(node);
1744
1745    if (node.goToChildElement())
1746    {
1747      StdString zoomDomainDefRoot("zoom_domain_definition");
1748      StdString zoom("zoom_domain");
1749      StdString interpFromFileDomainDefRoot("interpolate_from_file_domain_definition");
1750      StdString interpFromFile("interpolate_from_file_domain");
1751      StdString generateRectilinearDefRoot("generate_rectilinear_domain_definition");
1752      StdString generateRectilinear("generate_rectilinear_domain");
1753      do
1754      {
1755        if (node.getElementName() == zoom) {
1756          CZoomDomain* tmp = (CZoomDomainGroup::get(zoomDomainDefRoot))->createChild();
1757          tmp->parse(node);
1758          transformationMap_.push_back(std::make_pair(TRANS_ZOOM_DOMAIN,tmp));
1759        }
1760        else if (node.getElementName() == interpFromFile)
1761        {
1762          CInterpolateFromFileDomain* tmp = (CInterpolateFromFileDomainGroup::get(interpFromFileDomainDefRoot))->createChild();
1763          tmp->parse(node);
1764          transformationMap_.push_back(std::make_pair(TRANS_INTERPOLATE_DOMAIN_FROM_FILE,tmp));
1765        }
1766        else if (node.getElementName() == generateRectilinear)
1767        {
1768          CGenerateRectilinearDomain* tmp = (CGenerateRectilinearDomainGroup::get(generateRectilinearDefRoot))->createChild();
1769          tmp->parse(node);
1770          transformationMap_.push_back(std::make_pair(TRANS_GENERATE_RECTILINEAR_DOMAIN,tmp));
1771        }
1772      } while (node.goToNextElement()) ;
1773      node.goToParentElement();
1774    }
1775  }
1776   //----------------------------------------------------------------
1777
1778   DEFINE_REF_FUNC(Domain,domain)
1779
1780   ///---------------------------------------------------------------
1781
1782} // namespace xios
Note: See TracBrowser for help on using the repository browser.