source: XIOS/trunk/src/node/grid.cpp @ 624

Last change on this file since 624 was 624, checked in by mhnguyen, 9 years ago

Final tests of zoom and inverse on axis

+) Modify test_client and test_complete to work with new grid definition
+) Correct some bugs causing memory leak
+) Clean abundant code
+) Add more comments to new files

Test
+) On Curie
+) test_client and test_complete pass with correct results

  • 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: 41.6 KB
Line 
1
2#include "grid.hpp"
3
4#include "attribute_template.hpp"
5#include "object_template.hpp"
6#include "group_template.hpp"
7#include "message.hpp"
8#include <iostream>
9#include "xios_spl.hpp"
10#include "type.hpp"
11#include "context.hpp"
12#include "context_client.hpp"
13#include "context_server.hpp"
14#include "array_new.hpp"
15#include "client_server_mapping_distributed.hpp"
16
17namespace xios {
18
19   /// ////////////////////// Définitions ////////////////////// ///
20
21   CGrid::CGrid(void)
22      : CObjectTemplate<CGrid>(), CGridAttributes()
23      , isChecked(false), isDomainAxisChecked(false), storeIndex(1)
24      , vDomainGroup_(), vAxisGroup_(), axisList_(), isAxisListSet(false), isDomListSet(false), clientDistribution_(0), isIndexSent(false)
25      , serverDistribution_(0), serverDistributionDescription_(0), clientServerMap_(0), writtenDataSize_(0), globalDim_()
26      , connectedDataSize_(), connectedServerRank_(), isDataDistributed_(true), transformations_(0), isTransformed_(false)
27      , axisPositionInGrid_()
28   {
29     setVirtualDomainGroup();
30     setVirtualAxisGroup();
31   }
32
33   CGrid::CGrid(const StdString & id)
34      : CObjectTemplate<CGrid>(id), CGridAttributes()
35      , isChecked(false), isDomainAxisChecked(false), storeIndex(1)
36      , vDomainGroup_(), vAxisGroup_(), axisList_(), isAxisListSet(false), isDomListSet(false), clientDistribution_(0), isIndexSent(false)
37      , serverDistribution_(0), serverDistributionDescription_(0), clientServerMap_(0), writtenDataSize_(0), globalDim_()
38      , connectedDataSize_(), connectedServerRank_(), isDataDistributed_(true), transformations_(0), isTransformed_(false)
39      , axisPositionInGrid_()
40   {
41     setVirtualDomainGroup();
42     setVirtualAxisGroup();
43   }
44
45   CGrid::~CGrid(void)
46   {
47    deque< CArray<int, 1>* >::iterator it ;
48
49    for(deque< CArray<int,1>* >::iterator it=storeIndex.begin(); it!=storeIndex.end();it++)  delete *it ;
50    for(map<int,CArray<size_t,1>* >::iterator it=outIndexFromClient.begin();it!=outIndexFromClient.end();++it) delete (it->second);
51
52    if (0 != clientDistribution_) delete clientDistribution_;
53    if (0 != serverDistribution_) delete serverDistribution_;
54    if (0 != serverDistributionDescription_) delete serverDistributionDescription_;
55    if (0 != transformations_) delete transformations_;
56   }
57
58   ///---------------------------------------------------------------
59
60   StdString CGrid::GetName(void)    { return (StdString("grid")); }
61   StdString CGrid::GetDefName(void) { return (CGrid::GetName()); }
62   ENodeType CGrid::GetType(void)    { return (eGrid); }
63
64   //----------------------------------------------------------------
65
66   const std::deque< CArray<int,1>* > & CGrid::getStoreIndex(void) const
67   {
68      return (this->storeIndex );
69   }
70
71   //---------------------------------------------------------------
72//
73//   const std::deque< CArray<int,1>* > & CGrid::getOutIIndex(void)  const
74//   {
75//      return (this->out_i_index );
76//   }
77//
78//   //---------------------------------------------------------------
79//
80//   const std::deque< CArray<int,1>* > & CGrid::getOutJIndex(void)  const
81//   {
82//      return (this->out_j_index );
83//   }
84//
85//   //---------------------------------------------------------------
86//
87//   const std::deque< CArray<int,1>* > & CGrid::getOutLIndex(void)  const
88//   {
89//      return (this->out_l_index );
90//   }
91//
92//   //---------------------------------------------------------------
93//
94//   const CAxis*   CGrid::getRelAxis  (void) const
95//   {
96//      return (this->axis );
97//   }
98
99//   //---------------------------------------------------------------
100//
101//   const CDomain* CGrid::getRelDomain(void) const
102//   {
103//      return (this->domain );
104//   }
105
106   //---------------------------------------------------------------
107
108//   bool CGrid::hasAxis(void) const
109//   {
110//      return (this->withAxis);
111//   }
112
113   //---------------------------------------------------------------
114
115   StdSize CGrid::getDimension(void) const
116   {
117      return (globalDim_.size());
118   }
119
120   //---------------------------------------------------------------
121
122/*
123   std::vector<StdSize> CGrid::getLocalShape(void) const
124   {
125      std::vector<StdSize> retvalue;
126      retvalue.push_back(domain->zoom_ni_loc.getValue());
127      retvalue.push_back(domain->zoom_nj_loc.getValue());
128      if (this->withAxis)
129         retvalue.push_back(this->axis->zoom_size.getValue());
130      return (retvalue);
131   }
132*/
133   //---------------------------------------------------------------
134
135/*
136   StdSize CGrid::getLocalSize(void) const
137   {
138      StdSize retvalue = 1;
139      std::vector<StdSize> shape_ = this->getLocalShape();
140      for (StdSize s = 0; s < shape_.size(); s++)
141         retvalue *= shape_[s];
142      return (retvalue);
143   }
144*/
145   //---------------------------------------------------------------
146/*
147   std::vector<StdSize> CGrid::getGlobalShape(void) const
148   {
149      std::vector<StdSize> retvalue;
150      retvalue.push_back(domain->ni.getValue());
151      retvalue.push_back(domain->nj.getValue());
152      if (this->withAxis)
153         retvalue.push_back(this->axis->size.getValue());
154      return (retvalue);
155   }
156*/
157   //---------------------------------------------------------------
158
159/*
160   StdSize CGrid::getGlobalSize(void) const
161   {
162      StdSize retvalue = 1;
163      std::vector<StdSize> shape_ = this->getGlobalShape();
164      for (StdSize s = 0; s < shape_.size(); s++)
165         retvalue *= shape_[s];
166      return (retvalue);
167   }
168*/
169   StdSize CGrid::getDataSize(void) const
170   {
171     StdSize retvalue = 1;
172     if (!isScalarGrid())
173     {
174       std::vector<int> dataNindex = clientDistribution_->getDataNIndex();
175       for (int i = 0; i < dataNindex.size(); ++i) retvalue *= dataNindex[i];
176     }
177     return retvalue;
178   }
179
180   std::map<int, StdSize> CGrid::getConnectedServerDataSize()
181   {
182     double secureFactor = 2.5 * sizeof(double) * CXios::bufferServerFactorSize;
183     StdSize retVal = 1;
184     std::map<int, StdSize> ret;
185     std::map<int, size_t >::const_iterator itb = connectedDataSize_.begin(), it, itE = connectedDataSize_.end();
186
187     if (isScalarGrid())
188     {
189       for (it = itb; it != itE; ++it)
190       {
191         retVal *= secureFactor;
192         ret.insert(std::make_pair(it->first, retVal));
193       }
194       return ret;
195     }
196
197     for (it = itb; it != itE; ++it)
198     {
199        retVal = it->second;
200        retVal *= secureFactor;
201        ret.insert(std::make_pair<int,StdSize>(it->first, retVal));
202     }
203
204     if (connectedDataSize_.empty())
205     {
206       for (int i = 0; i < connectedServerRank_.size(); ++i)
207       {
208         retVal = 1;
209         retVal *= secureFactor;
210         ret.insert(std::make_pair<int,StdSize>(connectedServerRank_[i], retVal));
211       }
212     }
213
214     return ret;
215   }
216
217
218   void CGrid::solveDomainAxisRef(bool areAttributesChecked)
219   {
220     if (this->isDomainAxisChecked) return;
221
222     this->solveAxisRef(areAttributesChecked);
223     this->solveDomainRef(areAttributesChecked);
224
225     this->isDomainAxisChecked = areAttributesChecked;
226   }
227
228   void CGrid::checkMaskIndex(bool doSendingIndex)
229   {
230     CContext* context = CContext::getCurrent() ;
231     CContextClient* client=context->client ;
232
233     if (isScalarGrid())
234     {
235       if (context->hasClient)
236          if (this->isChecked && doSendingIndex && !isIndexSent) { sendIndexScalarGrid(); this->isIndexSent = true; }
237
238       if (this->isChecked) return;
239       if (context->hasClient)
240       {
241          this->computeIndexScalarGrid();
242       }
243
244       this->isChecked = true;
245       return;
246     }
247
248     if (context->hasClient)
249      if (this->isChecked && doSendingIndex && !isIndexSent) { sendIndex(); this->isIndexSent = true; }
250
251     if (this->isChecked) return;
252
253     if (context->hasClient)
254     {
255        checkMask() ;
256        this->computeIndex() ;
257        this->storeIndex.push_front(new CArray<int,1>() );
258     }
259     this->isChecked = true;
260   }
261
262   void CGrid::checkMask(void)
263   {
264      using namespace std;
265      std::vector<CDomain*> domainP = this->getDomains();
266      std::vector<CAxis*> axisP = this->getAxis();
267      int dim = domainP.size() * 2 + axisP.size();
268
269      std::vector<CArray<bool,2>* > domainMasks(domainP.size());
270      for (int i = 0; i < domainMasks.size(); ++i) domainMasks[i] = &(domainP[i]->mask);
271      std::vector<CArray<bool,1>* > axisMasks(axisP.size());
272      for (int i = 0; i < axisMasks.size(); ++i) axisMasks[i] = &(axisP[i]->mask);
273
274      switch (dim) {
275        case 1:
276          checkGridMask(mask1, domainMasks, axisMasks, axis_domain_order);
277          break;
278        case 2:
279          checkGridMask(mask2, domainMasks, axisMasks, axis_domain_order);
280          break;
281        case 3:
282          checkGridMask(mask3, domainMasks, axisMasks, axis_domain_order);
283          break;
284//        case 4:
285//          checkGridMask(mask4, domainMasks, axisMasks, axis_domain_order);
286//          break;
287//        case 5:
288//          checkGridMask(mask5, domainMasks, axisMasks, axis_domain_order);
289//          break;
290//        case 6:
291//          checkGridMask(mask6, domainMasks, axisMasks, axis_domain_order);
292//          break;
293//        case 7:
294//          checkGridMask(mask7, domainMasks, axisMasks, axis_domain_order);
295//          break;
296        default:
297          break;
298      }
299   }
300
301   void CGrid::modifyMask(const CArray<int,1>& indexToModify)
302   {
303      using namespace std;
304      std::vector<CDomain*> domainP = this->getDomains();
305      std::vector<CAxis*> axisP = this->getAxis();
306      int dim = domainP.size() * 2 + axisP.size();
307
308      switch (dim) {
309        case 1:
310          modifyGridMask(mask1, indexToModify);
311          break;
312        case 2:
313          modifyGridMask(mask2, indexToModify);
314          break;
315        case 3:
316          modifyGridMask(mask3, indexToModify);
317          break;
318
319        default:
320          break;
321      }
322   }
323
324   //---------------------------------------------------------------
325
326   void CGrid::solveDomainRef(bool sendAtt)
327   {
328      setDomainList();
329      std::vector<CDomain*> domListP = this->getDomains();
330      if (!domListP.empty())
331      {
332        computeGridGlobalDimension(getDomains(), getAxis(), axis_domain_order);
333        for (int i = 0; i < domListP.size(); ++i)
334        {
335          if (sendAtt) domListP[i]->sendCheckedAttributes();
336          else domListP[i]->checkAttributesOnClient();
337        }
338      }
339   }
340
341   //---------------------------------------------------------------
342
343   void CGrid::solveAxisRef(bool sendAtt)
344   {
345      setAxisList();
346      std::vector<CAxis*> axisListP = this->getAxis();
347      if (!axisListP.empty())
348      {
349        int idx = 0;
350        axisPositionInGrid_.resize(0);
351        for (int i = 0; i < axis_domain_order.numElements(); ++i)
352        {
353          if (false == axis_domain_order(i))
354          {
355            axisPositionInGrid_.push_back(idx);
356            ++idx;
357          }
358          else idx += 2;
359        }
360
361        computeGridGlobalDimension(getDomains(), getAxis(), axis_domain_order);
362        for (int i = 0; i < axisListP.size(); ++i)
363        {
364          if (sendAtt)
365            axisListP[i]->sendCheckedAttributes(globalDim_,axisPositionInGrid_[i]);
366          else
367            axisListP[i]->checkAttributesOnClient(globalDim_,axisPositionInGrid_[i]);
368          ++idx;
369        }
370
371      }
372   }
373
374   std::vector<int> CGrid::getAxisPositionInGrid() const
375   {
376     return axisPositionInGrid_;
377   }
378
379
380   //---------------------------------------------------------------
381
382   void CGrid::computeIndex(void)
383   {
384     CContext* context = CContext::getCurrent() ;
385     CContextClient* client=context->client ;
386
387     // First of all, compute distribution on client side
388     clientDistribution_ = new CDistributionClient(client->clientRank, this);
389     // Get local data index on client
390     storeIndex_client.resize(clientDistribution_->getLocalDataIndexOnClient().numElements());
391     storeIndex_client = (clientDistribution_->getLocalDataIndexOnClient());
392     isDataDistributed_= clientDistribution_->isDataDistributed();
393
394     if (!doGridHaveDataDistributed())
395     {
396        if (0 == client->clientRank)
397        {
398          size_t ssize = clientDistribution_->getLocalDataIndexOnClient().numElements();
399          for (int rank = 0; rank < client->serverSize; ++rank)
400            connectedDataSize_[rank] = ssize;
401        }
402        return;
403     }
404
405     // Compute mapping between client and server
406     size_t globalSizeIndex = 1, indexBegin, indexEnd;
407     int range, clientSize = client->clientSize;
408     for (int i = 0; i < globalDim_.size(); ++i) globalSizeIndex *= globalDim_[i];
409     indexBegin = 0;
410     for (int i = 0; i < clientSize; ++i)
411     {
412       range = globalSizeIndex / clientSize;
413       if (i < (globalSizeIndex%clientSize)) ++range;
414       if (i == client->clientRank) break;
415       indexBegin += range;
416     }
417     indexEnd = indexBegin + range - 1;
418
419     // Then compute distribution on server side
420     serverDistributionDescription_ = new CServerDistributionDescription(clientDistribution_->getNGlob());
421     serverDistributionDescription_->computeServerDistribution(client->serverSize, true);
422     serverDistributionDescription_->computeServerGlobalIndexInRange(client->serverSize,
423                                                                     std::make_pair<size_t,size_t>(indexBegin, indexEnd));
424
425     // Finally, compute index mapping between client(s) and server(s)
426     clientServerMap_ = new CClientServerMappingDistributed(serverDistributionDescription_->getGlobalIndexRange(),
427                                                            client->intraComm,
428                                                            clientDistribution_->isDataDistributed());
429
430     clientServerMap_->computeServerIndexMapping(clientDistribution_->getGlobalIndex());
431     const std::map<int, std::vector<size_t> >& globalIndexOnServer = clientServerMap_->getGlobalIndexOnServer();
432     const CArray<size_t,1>& globalIndexSendToServer = clientDistribution_->getGlobalDataIndexSendToServer();
433
434     std::map<int, std::vector<size_t> >::const_iterator iteGlobalMap, itbGlobalMap, itGlobalMap;
435     itbGlobalMap = itGlobalMap = globalIndexOnServer.begin();
436     iteGlobalMap = globalIndexOnServer.end();
437
438     int nbGlobalIndex = globalIndexSendToServer.numElements();
439    for (; itGlobalMap != iteGlobalMap; ++itGlobalMap)
440    {
441      int serverRank = itGlobalMap->first;
442      std::vector<size_t>::const_iterator itbVecGlobal = (itGlobalMap->second).begin(), itVecGlobal,
443                                          iteVecGlobal = (itGlobalMap->second).end();
444      for (int i = 0; i < nbGlobalIndex; ++i)
445      {
446        if (iteVecGlobal != std::find(itbVecGlobal, iteVecGlobal, globalIndexSendToServer(i)))
447        {
448          if (connectedDataSize_.end() == connectedDataSize_.find(serverRank))
449            connectedDataSize_[serverRank] = 1;
450          else
451            ++connectedDataSize_[serverRank];
452        }
453      }
454    }
455
456   connectedServerRank_.clear();
457   for (std::map<int, std::vector<size_t> >::const_iterator it = globalIndexOnServer.begin(); it != globalIndexOnServer.end(); ++it) {
458     connectedServerRank_.push_back(it->first);
459   }
460   if (!connectedDataSize_.empty())
461   {
462     connectedServerRank_.clear();
463     for (std::map<int,size_t>::const_iterator it = connectedDataSize_.begin(); it != connectedDataSize_.end(); ++it)
464       connectedServerRank_.push_back(it->first);
465   }
466
467    nbSenders = clientServerMap_->computeConnectedClients(client->serverSize, client->clientSize, client->intraComm, connectedServerRank_);
468   }
469
470   //----------------------------------------------------------------
471
472   CGrid* CGrid::createGrid(CDomain* domain)
473   {
474      std::vector<CDomain*> vecDom(1,domain);
475      std::vector<CAxis*> vecAxis;
476      CArray<bool,1> axisDomainOrder;
477      CGrid* grid = createGrid(vecDom, vecAxis, axisDomainOrder);
478
479      return (grid);
480   }
481
482   CGrid* CGrid::createGrid(CDomain* domain, CAxis* axis)
483   {
484      std::vector<CDomain*> vecDom(1,domain);
485      std::vector<CAxis*> vecAxis(1,axis);
486      CArray<bool,1> axisDomainOrder;
487      CGrid* grid = createGrid(vecDom, vecAxis, axisDomainOrder);
488
489      return (grid);
490   }
491
492   CGrid* CGrid::createGrid(std::vector<CDomain*> domains, std::vector<CAxis*> axis, CArray<bool,1> axisDomainOrder)
493   {
494      StdString new_id = StdString("__");
495      if (!domains.empty()) for (int i = 0; i < domains.size(); ++i) new_id += domains[i]->getId() + StdString("_");
496      if (!axis.empty()) for (int i = 0; i < axis.size(); ++i) new_id += axis[i]->getId() + StdString("_") ;
497      if (domains.empty() && axis.empty()) new_id += StdString("scalar_grid");
498      new_id += StdString("_");
499
500      CGrid* grid = CGridGroup::get("grid_definition")->createChild(new_id) ;
501      grid->setDomainList(domains);
502      grid->setAxisList(axis);
503
504      //By default, domains are always the first ones of a grid
505      if (0 == axisDomainOrder.numElements())
506      {
507        int size = domains.size()+axis.size();
508        grid->axis_domain_order.resize(size);
509        for (int i = 0; i < size; ++i)
510        {
511          if (i < domains.size()) grid->axis_domain_order(i) = true;
512          else grid->axis_domain_order(i) = false;
513        }
514      }
515      else
516      {
517        grid->axis_domain_order.resize(axisDomainOrder.numElements());
518        grid->axis_domain_order = axisDomainOrder;
519      }
520
521      grid->computeGridGlobalDimension(domains, axis, grid->axis_domain_order);
522
523      return (grid);
524   }
525
526   CDomainGroup* CGrid::getVirtualDomainGroup() const
527   {
528     return (this->vDomainGroup_);
529   }
530
531   CAxisGroup* CGrid::getVirtualAxisGroup() const
532   {
533     return (this->vAxisGroup_);
534   }
535
536   void CGrid::outputField(int rank, const CArray<double, 1>& stored, double* field)
537   {
538     CArray<size_t,1>& out_i=*outIndexFromClient[rank];
539     StdSize numElements = stored.numElements();
540     for (StdSize n = 0; n < numElements; ++n)
541     {
542       *(field+out_i(n)) = stored(n);
543     }
544   }
545
546   void CGrid::inputField(int rank, const double* const field, CArray<double,1>& stored)
547   {
548     CArray<size_t,1>& out_i = *outIndexFromClient[rank];
549     StdSize numElements = stored.numElements();
550     for (StdSize n = 0; n < numElements; ++n)
551     {
552       stored(n) = *(field+out_i(n));
553     }
554   }
555
556   //----------------------------------------------------------------
557
558
559   void CGrid::storeField_arr
560      (const double * const data, CArray<double, 1>& stored) const
561   {
562      const StdSize size = storeIndex_client.numElements() ;
563
564      stored.resize(size) ;
565      for(StdSize i = 0; i < size; i++) stored(i) = data[storeIndex_client(i)] ;
566   }
567
568   void CGrid::restoreField_arr
569      (const CArray<double, 1>& stored, double * const data) const
570   {
571      const StdSize size = storeIndex_client.numElements() ;
572
573      for(StdSize i = 0; i < size; i++) data[storeIndex_client(i)] = stored(i) ;
574   }
575
576  void CGrid::computeIndexScalarGrid()
577  {
578    CContext* context = CContext::getCurrent();
579    CContextClient* client=context->client;
580
581    storeIndex_client.resize(1);
582    storeIndex_client[0] = 0;
583    if (0 == client->clientRank)
584    {
585      for (int rank = 0; rank < client->serverSize; ++rank)
586        connectedDataSize_[rank] = 1;
587    }
588    isDataDistributed_ = false;
589  }
590
591  void CGrid::sendIndexScalarGrid()
592  {
593    CContext* context = CContext::getCurrent() ;
594    CContextClient* client=context->client ;
595
596    CEventClient event(getType(),EVENT_ID_INDEX);
597    list<shared_ptr<CMessage> > list_msg ;
598    list< CArray<size_t,1>* > listOutIndex;
599    if (0 == client->clientRank)
600    {
601      for (int rank = 0; rank < client->serverSize; ++rank)
602      {
603        int nb = 1;
604        CArray<size_t, 1> outGlobalIndexOnServer(nb);
605        CArray<int, 1> outLocalIndexToServer(nb);
606        for (int k = 0; k < nb; ++k)
607        {
608          outGlobalIndexOnServer(k) = 0;
609          outLocalIndexToServer(k)  = 0;
610        }
611
612        storeIndex_toSrv.insert( pair<int,CArray<int,1>* >(rank,new CArray<int,1>(outLocalIndexToServer) ));
613        listOutIndex.push_back(new CArray<size_t,1>(outGlobalIndexOnServer));
614
615        list_msg.push_back(shared_ptr<CMessage>(new CMessage));
616        *list_msg.back()<<getId()<<isDataDistributed_<<*listOutIndex.back();
617
618        event.push(rank, 1, *list_msg.back());
619      }
620      client->sendEvent(event);
621    }
622    else
623      client->sendEvent(event);
624
625    for(list<CArray<size_t,1>* >::iterator it=listOutIndex.begin();it!=listOutIndex.end();++it) delete *it ;
626  }
627
628  void CGrid::sendIndex(void)
629  {
630    CContext* context = CContext::getCurrent() ;
631    CContextClient* client=context->client ;
632
633    CEventClient event(getType(),EVENT_ID_INDEX) ;
634    int rank ;
635    list<shared_ptr<CMessage> > list_msg ;
636    list< CArray<size_t,1>* > listOutIndex;
637    const std::map<int, std::vector<size_t> >& globalIndexOnServer = clientServerMap_->getGlobalIndexOnServer();
638    const CArray<int,1>& localIndexSendToServer = clientDistribution_->getLocalDataIndexSendToServer();
639    const CArray<size_t,1>& globalIndexSendToServer = clientDistribution_->getGlobalDataIndexSendToServer();
640
641    if (!doGridHaveDataDistributed())
642    {
643      if (0 == client->clientRank)
644      {
645        CArray<size_t, 1> outGlobalIndexOnServer = globalIndexSendToServer;
646        CArray<int,1> outLocalIndexToServer = localIndexSendToServer;
647        for (rank = 0; rank < client->serverSize; ++rank)
648        {
649          storeIndex_toSrv.insert( pair<int,CArray<int,1>* >(rank,new CArray<int,1>(outLocalIndexToServer)));
650          listOutIndex.push_back(new CArray<size_t,1>(outGlobalIndexOnServer));
651
652          list_msg.push_back(shared_ptr<CMessage>(new CMessage));
653          *list_msg.back()<<getId()<<isDataDistributed_<<*listOutIndex.back();
654
655          event.push(rank, 1, *list_msg.back());
656        }
657        client->sendEvent(event);
658      }
659      else
660        client->sendEvent(event);
661    }
662    else
663    {
664      std::map<int, std::vector<size_t> >::const_iterator iteGlobalMap, itbGlobalMap, itGlobalMap;
665      itbGlobalMap = itGlobalMap = globalIndexOnServer.begin();
666      iteGlobalMap = globalIndexOnServer.end();
667
668      int nbGlobalIndex = globalIndexSendToServer.numElements();
669      std::map<int,std::vector<int> >localIndexTmp;
670      std::map<int,std::vector<size_t> > globalIndexTmp;
671      for (; itGlobalMap != iteGlobalMap; ++itGlobalMap)
672      {
673        int serverRank = itGlobalMap->first;
674        std::vector<size_t>::const_iterator itbVecGlobal = (itGlobalMap->second).begin(),
675                                            iteVecGlobal = (itGlobalMap->second).end();
676        for (int i = 0; i < nbGlobalIndex; ++i)
677        {
678          if (iteVecGlobal != std::find(itbVecGlobal, iteVecGlobal, globalIndexSendToServer(i)))
679          {
680            globalIndexTmp[serverRank].push_back(globalIndexSendToServer(i));
681            localIndexTmp[serverRank].push_back(localIndexSendToServer(i));
682          }
683        }
684      }
685
686      for (int ns = 0; ns < connectedServerRank_.size(); ++ns)
687      {
688        rank = connectedServerRank_[ns];
689        int nb = 0;
690        if (globalIndexTmp.end() != globalIndexTmp.find(rank))
691          nb = globalIndexTmp[rank].size();
692
693        CArray<size_t, 1> outGlobalIndexOnServer(nb);
694        CArray<int, 1> outLocalIndexToServer(nb);
695        for (int k = 0; k < nb; ++k)
696        {
697          outGlobalIndexOnServer(k) = globalIndexTmp[rank].at(k);
698          outLocalIndexToServer(k)  = localIndexTmp[rank].at(k);
699        }
700
701        storeIndex_toSrv.insert( pair<int,CArray<int,1>* >(rank,new CArray<int,1>(outLocalIndexToServer) ));
702        listOutIndex.push_back(new CArray<size_t,1>(outGlobalIndexOnServer));
703
704        list_msg.push_back(shared_ptr<CMessage>(new CMessage));
705        *list_msg.back()<<getId()<<isDataDistributed_<<*listOutIndex.back();
706
707        event.push(rank, nbSenders[rank], *list_msg.back());
708      }
709
710      client->sendEvent(event);
711    }
712
713    for(list<CArray<size_t,1>* >::iterator it=listOutIndex.begin();it!=listOutIndex.end();++it) delete *it ;
714  }
715
716  void CGrid::recvIndex(CEventServer& event)
717  {
718    string gridId;
719    vector<int> ranks;
720    vector<CBufferIn*> buffers;
721
722    list<CEventServer::SSubEvent>::iterator it;
723    for (it=event.subEvents.begin();it!=event.subEvents.end();++it)
724    {
725      ranks.push_back(it->rank);
726      CBufferIn* buffer = it->buffer;
727      *buffer >> gridId;
728      buffers.push_back(buffer);
729    }
730    get(gridId)->recvIndex(ranks, buffers) ;
731  }
732
733  void CGrid::computeGridGlobalDimension(const std::vector<CDomain*>& domains,
734                                         const std::vector<CAxis*>& axis,
735                                         const CArray<bool,1>& axisDomainOrder)
736  {
737    globalDim_.resize(domains.size()*2+axis.size());
738    int idx = 0, idxDomain = 0, idxAxis = 0;
739    for (int i = 0; i < axisDomainOrder.numElements(); ++i)
740    {
741      if (axisDomainOrder(i))
742      {
743        globalDim_[idx]   = domains[idxDomain]->ni_glo.getValue();
744        globalDim_[idx+1] = domains[idxDomain]->nj_glo.getValue();
745        ++idxDomain;
746        idx += 2;
747      }
748      else
749      {
750        globalDim_[idx] = axis[idxAxis]->size.getValue();
751        ++idxAxis;
752        ++idx;
753      }
754    }
755  }
756
757  std::vector<int> CGrid::getGlobalDimension()
758  {
759    return globalDim_;
760  }
761
762  bool CGrid::isScalarGrid() const
763  {
764    return (axisList_.empty() && domList_.empty());
765  }
766
767  /*!
768    Verify whether one server need to write data
769    There are some cases on which one server has nodata to write. For example, when we
770  just only want to zoom on a domain.
771  */
772  bool CGrid::doGridHaveDataToWrite()
773  {
774//    size_t ssize = 0;
775//    for (map<int, CArray<size_t, 1>* >::const_iterator it = outIndexFromClient.begin();
776//                                                       it != outIndexFromClient.end(); ++it)
777//    {
778//      ssize += (it->second)->numElements();
779//    }
780//    return (0 != ssize);
781     return (0 != writtenDataSize_);
782  }
783
784  /*!
785    Return size of data which is written on each server
786    Whatever dimension of a grid, data which are written on server must be presented as
787  an one dimension array.
788  \return size of data written on server
789  */
790  size_t CGrid::getWrittenDataSize() const
791  {
792    return writtenDataSize_;
793  }
794
795  const CDistributionServer* CGrid::getDistributionServer() const
796  {
797    return serverDistribution_;
798  }
799
800  const CDistributionClient* CGrid::getDistributionClient() const
801  {
802    return clientDistribution_;
803  }
804
805  bool CGrid::doGridHaveDataDistributed()
806  {
807    if (isScalarGrid()) return false;
808    else
809      return isDataDistributed_;
810  }
811
812  void CGrid::recvIndex(vector<int> ranks, vector<CBufferIn*> buffers)
813  {
814    CContext* context = CContext::getCurrent();
815    CContextServer* server = context->server;
816
817    for (int n = 0; n < ranks.size(); n++)
818    {
819      int rank = ranks[n];
820      CBufferIn& buffer = *buffers[n];
821
822      buffer >> isDataDistributed_;
823      size_t dataSize = 0;
824
825      if (isScalarGrid())
826      {
827        writtenDataSize_ = 1;
828        CArray<size_t,1> outIndex;
829        buffer >> outIndex;
830        outIndexFromClient.insert(std::pair<int, CArray<size_t,1>* >(rank, new CArray<size_t,1>(outIndex)));
831        std::vector<int> nZoomBegin(1,0), nZoomSize(1,1), nGlob(1,1), nZoomBeginGlobal(1,0);
832        serverDistribution_ = new CDistributionServer(server->intraCommRank, nZoomBegin, nZoomSize,
833                                                      nZoomBeginGlobal, nGlob);
834        return;
835      }
836
837      if (0 == serverDistribution_)
838      {
839        int idx = 0, numElement = axis_domain_order.numElements();
840        int ssize = numElement;
841        std::vector<int> indexMap(numElement);
842        for (int i = 0; i < numElement; ++i)
843        {
844          indexMap[i] = idx;
845          if (true == axis_domain_order(i))
846          {
847            ++ssize;
848            idx += 2;
849          }
850          else
851            ++idx;
852        }
853
854        int axisId = 0, domainId = 0;
855        std::vector<CDomain*> domainList = getDomains();
856        std::vector<CAxis*> axisList = getAxis();
857        std::vector<int> nZoomBegin(ssize), nZoomSize(ssize), nGlob(ssize), nZoomBeginGlobal(ssize);
858        for (int i = 0; i < numElement; ++i)
859        {
860          if (axis_domain_order(i))
861          {
862            nZoomBegin[indexMap[i]] = domainList[domainId]->zoom_ibegin_srv;
863            nZoomSize[indexMap[i]]  = domainList[domainId]->zoom_ni_srv;
864            nZoomBeginGlobal[indexMap[i]] = domainList[domainId]->zoom_ibegin;
865            nGlob[indexMap[i]] = domainList[domainId]->ni_glo;
866
867            nZoomBegin[indexMap[i] + 1] = domainList[domainId]->zoom_jbegin_srv;
868            nZoomSize[indexMap[i] + 1] = domainList[domainId]->zoom_nj_srv;
869            nZoomBeginGlobal[indexMap[i] + 1] = domainList[domainId]->zoom_jbegin;
870            nGlob[indexMap[i] + 1] = domainList[domainId]->nj_glo;
871            ++domainId;
872          }
873          else
874          {
875            nZoomBegin[indexMap[i]] = axisList[axisId]->zoom_begin_srv;
876            nZoomSize[indexMap[i]]  = axisList[axisId]->zoom_size_srv;
877            nZoomBeginGlobal[indexMap[i]] = axisList[axisId]->global_zoom_begin;
878            nGlob[indexMap[i]] = axisList[axisId]->size;
879            ++axisId;
880          }
881        }
882        dataSize = 1;
883        for (int i = 0; i < nZoomSize.size(); ++i)
884          dataSize *= nZoomSize[i];
885
886        serverDistribution_ = new CDistributionServer(server->intraCommRank, nZoomBegin, nZoomSize,
887                                                      nZoomBeginGlobal, nGlob);
888      }
889
890      CArray<size_t,1> outIndex;
891      buffer >> outIndex;
892      if (isDataDistributed_)
893        serverDistribution_->computeLocalIndex(outIndex);
894      else
895      {
896        dataSize = outIndex.numElements();
897        for (int i = 0; i < outIndex.numElements(); ++i) outIndex(i) = i;
898      }
899      writtenDataSize_ += dataSize;
900
901      outIndexFromClient.insert(std::pair<int, CArray<size_t,1>* >(rank, new CArray<size_t,1>(outIndex)));
902      connectedDataSize_[rank] = outIndex.numElements();
903    }
904
905    nbSenders = CClientServerMappingDistributed::computeConnectedClients(context->client->serverSize, context->client->clientSize, context->client->intraComm, ranks);
906  }
907
908   /*!
909   \brief Dispatch event received from client
910      Whenever a message is received in buffer of server, it will be processed depending on
911   its event type. A new event type should be added in the switch list to make sure
912   it processed on server side.
913   \param [in] event: Received message
914   */
915  bool CGrid::dispatchEvent(CEventServer& event)
916  {
917
918    if (SuperClass::dispatchEvent(event)) return true ;
919    else
920    {
921      switch(event.type)
922      {
923        case EVENT_ID_INDEX :
924          recvIndex(event) ;
925          return true ;
926          break ;
927
928         case EVENT_ID_ADD_DOMAIN :
929           recvAddDomain(event) ;
930           return true ;
931           break ;
932
933         case EVENT_ID_ADD_AXIS :
934           recvAddAxis(event) ;
935           return true ;
936           break ;
937        default :
938          ERROR("bool CDomain::dispatchEvent(CEventServer& event)",
939                <<"Unknown Event") ;
940          return false ;
941      }
942    }
943  }
944
945   void CGrid::inputFieldServer(const std::deque< CArray<double, 1>* > storedClient, CArray<double, 1>&  storedServer) const
946   {
947      if ((this->storeIndex.size()-1 ) != storedClient.size())
948         ERROR("void CGrid::inputFieldServer(const std::deque< CArray<double, 1>* > storedClient, CArray<double, 1>&  storedServer) const",
949                << "[ Expected received field = " << (this->storeIndex.size()-1) << ", "
950                << "[ received fiedl = "    << storedClient.size() << "] "
951                << "Data from clients are missing!") ;
952      storedServer.resize(storeIndex[0]->numElements());
953
954      for (StdSize i = 0, n = 0; i < storedClient.size(); i++)
955         for (StdSize j = 0; j < storedClient[i]->numElements(); j++)
956            storedServer(n++) = (*storedClient[i])(j);
957   }
958
959   void CGrid::outputFieldToServer(CArray<double,1>& fieldIn, int rank, CArray<double,1>& fieldOut)
960   {
961     CArray<int,1>& index = *storeIndex_toSrv[rank] ;
962     int nb=index.numElements() ;
963     fieldOut.resize(nb) ;
964
965     for(int k=0;k<nb;k++) fieldOut(k)=fieldIn(index(k)) ;
966    }
967   ///---------------------------------------------------------------
968
969   CDomain* CGrid::addDomain(const std::string& id)
970   {
971     return vDomainGroup_->createChild(id) ;
972   }
973
974   CAxis* CGrid::addAxis(const std::string& id)
975   {
976     return vAxisGroup_->createChild(id) ;
977   }
978
979   //! Change virtual field group to a new one
980   void CGrid::setVirtualDomainGroup(CDomainGroup* newVDomainGroup)
981   {
982      this->vDomainGroup_ = newVDomainGroup;
983   }
984
985   //! Change virtual variable group to new one
986   void CGrid::setVirtualAxisGroup(CAxisGroup* newVAxisGroup)
987   {
988      this->vAxisGroup_ = newVAxisGroup;
989   }
990
991   //----------------------------------------------------------------
992   //! Create virtual field group, which is done normally on initializing file
993   void CGrid::setVirtualDomainGroup(void)
994   {
995      this->setVirtualDomainGroup(CDomainGroup::create());
996   }
997
998   //! Create virtual variable group, which is done normally on initializing file
999   void CGrid::setVirtualAxisGroup(void)
1000   {
1001      this->setVirtualAxisGroup(CAxisGroup::create());
1002   }
1003
1004   /*!
1005   \brief Send a message to create a domain on server side
1006   \param[in] id String identity of domain that will be created on server
1007   */
1008   void CGrid::sendAddDomain(const string& id)
1009   {
1010    CContext* context=CContext::getCurrent() ;
1011
1012    if (! context->hasServer )
1013    {
1014       CContextClient* client=context->client ;
1015
1016       CEventClient event(this->getType(),EVENT_ID_ADD_DOMAIN) ;
1017       if (client->isServerLeader())
1018       {
1019         CMessage msg ;
1020         msg<<this->getId() ;
1021         msg<<id ;
1022         const std::list<int>& ranks = client->getRanksServerLeader();
1023         for (std::list<int>::const_iterator itRank = ranks.begin(), itRankEnd = ranks.end(); itRank != itRankEnd; ++itRank)
1024           event.push(*itRank,1,msg);
1025         client->sendEvent(event) ;
1026       }
1027       else client->sendEvent(event) ;
1028    }
1029   }
1030
1031   /*!
1032   \brief Send a message to create an axis on server side
1033   \param[in] id String identity of axis that will be created on server
1034   */
1035   void CGrid::sendAddAxis(const string& id)
1036   {
1037    CContext* context=CContext::getCurrent() ;
1038
1039    if (! context->hasServer )
1040    {
1041       CContextClient* client=context->client ;
1042
1043       CEventClient event(this->getType(),EVENT_ID_ADD_AXIS) ;
1044       if (client->isServerLeader())
1045       {
1046         CMessage msg ;
1047         msg<<this->getId() ;
1048         msg<<id ;
1049         const std::list<int>& ranks = client->getRanksServerLeader();
1050         for (std::list<int>::const_iterator itRank = ranks.begin(), itRankEnd = ranks.end(); itRank != itRankEnd; ++itRank)
1051           event.push(*itRank,1,msg);
1052         client->sendEvent(event) ;
1053       }
1054       else client->sendEvent(event) ;
1055    }
1056   }
1057
1058   /*!
1059   \brief Receive a message annoucing the creation of a domain on server side
1060   \param[in] event Received event
1061   */
1062   void CGrid::recvAddDomain(CEventServer& event)
1063   {
1064
1065      CBufferIn* buffer=event.subEvents.begin()->buffer;
1066      string id;
1067      *buffer>>id ;
1068      get(id)->recvAddDomain(*buffer) ;
1069   }
1070
1071   /*!
1072   \brief Receive a message annoucing the creation of a domain on server side
1073   \param[in] buffer Buffer containing message
1074   */
1075   void CGrid::recvAddDomain(CBufferIn& buffer)
1076   {
1077      string id ;
1078      buffer>>id ;
1079      addDomain(id) ;
1080   }
1081
1082   /*!
1083   \brief Receive a message annoucing the creation of an axis on server side
1084   \param[in] event Received event
1085   */
1086   void CGrid::recvAddAxis(CEventServer& event)
1087   {
1088
1089      CBufferIn* buffer=event.subEvents.begin()->buffer;
1090      string id;
1091      *buffer>>id ;
1092      get(id)->recvAddAxis(*buffer) ;
1093   }
1094
1095   /*!
1096   \brief Receive a message annoucing the creation of an axis on server side
1097   \param[in] buffer Buffer containing message
1098   */
1099   void CGrid::recvAddAxis(CBufferIn& buffer)
1100   {
1101      string id ;
1102      buffer>>id ;
1103      addAxis(id) ;
1104   }
1105
1106  /*!
1107  \brief Solve domain and axis references
1108  As field, domain and axis can refer to other domains or axis. In order to inherit correctly
1109  all attributes from their parents, they should be processed with this function
1110  \param[in] apply inherit all attributes of parents (true)
1111  */
1112  void CGrid::solveDomainAxisRefInheritance(bool apply)
1113  {
1114    CContext* context = CContext::getCurrent();
1115    unsigned int vecSize, i;
1116    std::vector<StdString>::iterator it, itE;
1117    setDomainList();
1118    it = domList_.begin(); itE = domList_.end();
1119    for (; it != itE; ++it)
1120    {
1121      CDomain* pDom = CDomain::get(*it);
1122      if (context->hasClient)
1123      {
1124        pDom->solveRefInheritance(apply);
1125        pDom->solveBaseReference();
1126        if ((!pDom->domain_ref.isEmpty()) && (pDom->name.isEmpty()))
1127          pDom->name.setValue(pDom->getBaseDomainReference()->getId());
1128      }
1129    }
1130
1131    setAxisList();
1132    it = axisList_.begin(); itE = axisList_.end();
1133    for (; it != itE; ++it)
1134    {
1135      CAxis* pAxis = CAxis::get(*it);
1136      if (context->hasClient)
1137      {
1138        pAxis->solveRefInheritance(apply);
1139        pAxis->solveBaseReference();
1140        pAxis->solveInheritanceTransformation();
1141        if ((!pAxis->axis_ref.isEmpty()) && (pAxis->name.isEmpty()))
1142          pAxis->name.setValue(pAxis->getBaseAxisReference()->getId());
1143      }
1144    }
1145  }
1146
1147  bool CGrid::isTransformed()
1148  {
1149    return isTransformed_;
1150  }
1151
1152  void CGrid::setTransformed()
1153  {
1154    isTransformed_ = true;
1155  }
1156
1157  CGridTransformation* CGrid::getTransformations()
1158  {
1159    return transformations_;
1160  }
1161
1162  void CGrid::transformGrid(CGrid* transformedGrid)
1163  {
1164    if (transformedGrid->isTransformed()) return;
1165    transformedGrid->setTransformed();
1166    if (axis_domain_order.numElements() != transformedGrid->axis_domain_order.numElements())
1167    {
1168      ERROR("CGrid::transformGrid(CGrid* transformedGrid)",
1169           << "Two grids have different dimension size"
1170           << "Dimension of grid source " <<this->getId() << " is " << axis_domain_order.numElements() << std::endl
1171           << "Dimension of grid destination " <<transformedGrid->getId() << " is " << transformedGrid->axis_domain_order.numElements());
1172    }
1173    else
1174    {
1175      int ssize = axis_domain_order.numElements();
1176      for (int i = 0; i < ssize; ++i)
1177        if (axis_domain_order(i) != (transformedGrid->axis_domain_order)(i))
1178          ERROR("CGrid::transformGrid(CGrid* transformedGrid)",
1179                << "Grids " <<this->getId() <<" and " << transformedGrid->getId()
1180                << " don't have elements in the same order");
1181    }
1182
1183    transformations_ = new CGridTransformation(transformedGrid, this);
1184    transformations_->computeAll();
1185
1186    // Ok, now need to compute index of grid source
1187    checkMaskIndex(false);
1188  }
1189
1190  /*!
1191  \brief Get the list of domain pointers
1192  \return list of domain pointers
1193  */
1194  std::vector<CDomain*> CGrid::getDomains()
1195  {
1196    std::vector<CDomain*> domList;
1197    if (!domList_.empty())
1198    {
1199      for (int i = 0; i < domList_.size(); ++i) domList.push_back(CDomain::get(domList_[i]));
1200    }
1201    return domList;
1202  }
1203
1204  /*!
1205  \brief Get the list of  axis pointers
1206  \return list of axis pointers
1207  */
1208  std::vector<CAxis*> CGrid::getAxis()
1209  {
1210    std::vector<CAxis*> aList;
1211    if (!axisList_.empty())
1212      for (int i =0; i < axisList_.size(); ++i) aList.push_back(CAxis::get(axisList_[i]));
1213
1214    return aList;
1215  }
1216
1217  /*!
1218  \brief Set domain(s) of a grid from a list
1219  \param[in] domains list of domains
1220  */
1221  void CGrid::setDomainList(const std::vector<CDomain*> domains)
1222  {
1223    if (isDomListSet) return;
1224    std::vector<CDomain*> domList = this->getVirtualDomainGroup()->getAllChildren();
1225    if (!domains.empty() && domList.empty()) domList = domains;
1226    if (!domList.empty())
1227    {
1228      int sizeDom = domList.size();
1229      domList_.resize(sizeDom);
1230      for (int i = 0 ; i < sizeDom; ++i)
1231      {
1232        domList_[i] = domList[i]->getId();
1233      }
1234      isDomListSet = true;
1235    }
1236
1237  }
1238
1239  /*!
1240  \brief Set axis(s) of a grid from a list
1241  \param[in] axis list of axis
1242  */
1243  void CGrid::setAxisList(const std::vector<CAxis*> axis)
1244  {
1245    if (isAxisListSet) return;
1246    std::vector<CAxis*> aList = this->getVirtualAxisGroup()->getAllChildren();
1247    if (!axis.empty() && aList.empty()) aList = axis;
1248    if (!aList.empty())
1249    {
1250      int sizeAxis = aList.size();
1251      axisList_.resize(sizeAxis);
1252      for (int i = 0; i < sizeAxis; ++i)
1253      {
1254        axisList_[i] = aList[i]->getId();
1255      }
1256      isAxisListSet = true;
1257    }
1258  }
1259
1260  /*!
1261  \brief Get list of id of domains
1262  \return id list of domains
1263  */
1264  std::vector<StdString> CGrid::getDomainList()
1265  {
1266    setDomainList();
1267    return domList_;
1268  }
1269
1270  /*!
1271  \brief Get list of id of axis
1272  \return id list of axis
1273  */
1274  std::vector<StdString> CGrid::getAxisList()
1275  {
1276    setAxisList();
1277    return axisList_;
1278  }
1279
1280  void CGrid::sendAllDomains()
1281  {
1282    std::vector<CDomain*> domList = this->getVirtualDomainGroup()->getAllChildren();
1283    int dSize = domList.size();
1284    for (int i = 0; i < dSize; ++i)
1285    {
1286      sendAddDomain(domList[i]->getId());
1287      domList[i]->sendAllAttributesToServer();
1288    }
1289  }
1290
1291  void CGrid::sendAllAxis()
1292  {
1293    std::vector<CAxis*> aList = this->getVirtualAxisGroup()->getAllChildren();
1294    int aSize = aList.size();
1295
1296    for (int i = 0; i < aSize; ++i)
1297    {
1298      sendAddAxis(aList[i]->getId());
1299      aList[i]->sendAllAttributesToServer();
1300    }
1301  }
1302
1303  void CGrid::parse(xml::CXMLNode & node)
1304  {
1305    SuperClass::parse(node);
1306
1307    // List order of axis and domain in a grid, if there is a domain, it will take value 1 (true), axis 0 (false)
1308    std::vector<bool> order;
1309
1310    if (node.goToChildElement())
1311    {
1312      StdString domainName("domain");
1313      StdString axisName("axis");
1314      do
1315      {
1316        if (node.getElementName() == domainName) {
1317          order.push_back(true);
1318          this->getVirtualDomainGroup()->parseChild(node);
1319        }
1320        if (node.getElementName() == axisName) {
1321          order.push_back(false);
1322          this->getVirtualAxisGroup()->parseChild(node);
1323        }
1324      } while (node.goToNextElement()) ;
1325      node.goToParentElement();
1326    }
1327
1328    if (!order.empty())
1329    {
1330      int sizeOrd = order.size();
1331      axis_domain_order.resize(sizeOrd);
1332      for (int i = 0; i < sizeOrd; ++i)
1333      {
1334        axis_domain_order(i) = order[i];
1335      }
1336    }
1337
1338    setDomainList();
1339    setAxisList();
1340   }
1341
1342} // namespace xios
Note: See TracBrowser for help on using the repository browser.