Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/** \file
*
* \brief Implement the functionality that provides a globally unique index for
* all kinds of entities of a Dune grid. Such functionality is relevant
* for a number of cases:
* e.g. to map a degree of freedom associated wit an entity to its
* location in a global matrix or global vector; e.g. to associate data
* with entities in an adaptive mesh refinement scheme.
*
* Nota bene: the functionality to provide a globally unique index for an entity, over all
* processes in a distributed memory parallel compute environment is at present
* not available in the Dune framework; therefore, we implement such functionality in a class
* that is templatized by the GridView Type and the codimension of the entity for which the
* globaly unique index shall be computed.
*
* Method: (1) we use the UniqueEntityPartition class that reports if a specific entity belongs
* to a specific process;
*
* (2) we compute the number of entities that are owned by the specific process;
*
* (3) we communicate the index of entities that are owned by the process to processes
* that also contain these entities but do not own them, so that on a non-owner process
* we have information on the index of the entity that it got from the owner-process;
*
* Thus, we can access the same element in a global, distributed matrix that is managed
* by all processes; a typical case would be using matrix and vector routines from the PETSc
* or trilinos parallel linear algebra packages for distributed memory parallel computers.
*
* Copyright by Benedikt Oswald and Patrick Leidenberger, 2002-2010. All rights reserved.
*
* \author Benedikt Oswald, Patrick Leidenberger
*
* \warning None
*
* \attention globally unique indices are ONLY provided for entities of the
* InteriorBorder_Partition type, NOT for the Ghost_Partition type !!!
*/
#ifndef GLOBALUNIQUEINDEX_HH_
#define GLOBALUNIQUEINDEX_HH_
/** \brief Include standard header files. */
#include <vector>
#include <iostream>
#include <fstream>
#include <map>
#include <utility>
/** include base class functionality for the communication interface */
#include <dune/grid/common/datahandleif.hh>
// Include proprietary header files.
#include "uniqueentitypartition.hh"
/** include parallel capability */
#if HAVE_MPI
#include <dune/common/parallel/mpihelper.hh>
#endif
/*********************************************************************************************/
/* calculate globally unique index over all processes in a Dune grid that is distributed */
/* over all the processes in a distributed memory parallel environment, for all entities */
/* of a given codim in a given GridView, assuming they all have the same geometry, */
/* i.e. codim, type */
/*********************************************************************************************/
template<class GridView, int CODIM>
class GlobalUniqueIndex
{
private:
/** define data types */
typedef typename GridView::Grid Grid;
typedef typename GridView::Grid::GlobalIdSet GlobalIdSet;
typedef typename GridView::Grid::GlobalIdSet::IdType IdType;
typedef typename GridView::Traits::template Codim<0>::Iterator Iterator;
typedef typename GridView::Traits::template Codim<CODIM>::Entity Entity;
#if HAVE_MPI
/** define data type related to collective communication */
typedef typename Dune::template CollectiveCommunication<MPI_Comm> CollectiveCommunication;
#else
typedef typename Grid::CollectiveCommunication CollectiveCommunication;
#endif
typedef std::map<int,int> IndexMap;
typedef UniqueEntityPartition<GridView,CODIM> UniqueEntityPartitionType;
private:
/* A DataHandle class to communicate the global index from the
* owning to the non-owning entity; the class is based on the MinimumExchange
* class in the parallelsolver.hh header file.
*/
template<class GlobalIdSet, class IdType, class Map, typename ValueType>
class IndexExchange : public Dune::CommDataHandleIF<IndexExchange<GlobalIdSet,IdType,Map,ValueType>,ValueType>
{
public:
//! returns true if data for this codim should be communicated
bool contains (int dim, int codim) const
{
return(codim==CODIM);
}
//! returns true if size per entity of given dim and codim is a constant
bool fixedsize (int dim, int codim) const
{
return(true);
}
/*! how many objects of type DataType have to be sent for a given entity
*
* Note: Only the sender side needs to know this size. */
template<class EntityType>
size_t size (EntityType& e) const
{
return(1);
}
/*! pack data from user to message buffer */
template<class MessageBuffer, class EntityType>
void gather (MessageBuffer& buff, const EntityType& e) const
{
IdType id=globalidset_.id(e);
buff.write((*mapid2entity_.find(id)).second);
}
/*! unpack data from message buffer to user
n is the number of objects sent by the sender
*/
template<class MessageBuffer, class EntityType>
void scatter (MessageBuffer& buff, const EntityType& entity, size_t n)
{
ValueType x;
buff.read(x);
/** only if the incoming index is a valid one,
i.e. if it is greater than zero, will it be
inserted as the global index; it is made
sure in the upper class, i.e. GlobalUniqueIndex,
that non-owning processes use -1 to mark an entity
that they do not own.
*/
if(x >= 0)
{
IdType id = globalidset_.id(entity);
mapid2entity_.erase(id);
mapid2entity_.insert(std::make_pair(id,x));
const int lindex = indexSet_.index(entity);
localGlobalMap_[lindex] = x;
globalLocalMap_[x] = lindex;
}
}
//! constructor
IndexExchange (const GlobalIdSet& globalidset, Map& mapid2entity, int &rank,
const typename GridView::IndexSet& localIndexSet, IndexMap& localGlobal, IndexMap& globalLocal)
: globalidset_(globalidset),
mapid2entity_(mapid2entity),
rank_(rank),
indexSet_(localIndexSet),
localGlobalMap_(localGlobal),
globalLocalMap_(globalLocal)
{
}
private:
const GlobalIdSet& globalidset_;
Map& mapid2entity_;
int& rank_;
const typename GridView::IndexSet& indexSet_;
IndexMap& localGlobalMap_;
IndexMap& globalLocalMap_;
};
public:
/**********************************************************************************************************************/
/* \brief class constructor - when the class is instantiated by passing a const reference to a GridView object, */
/* we calculate the complete set of global unique indices so that we can then */
/* later query the global index, by directly passing the entity in question: then the respective global */
/* index is returned. */
/**********************************************************************************************************************/
GlobalUniqueIndex(const GridView& gridview)
: gridview_(gridview),
int rank = gridview.comm().rank();
int size = gridview.comm().size();
nLocalEntity_=0;
nGlobalEntity_=0;
nLocalEntity_ = uniqueEntityPartition_.numOwners();
/** compute the global, non-redundant number of entities, i.e. the number of entities in the set
* without double, aka. redundant entities, on the interprocessor boundary via global reduce. */
const CollectiveCommunication& collective = gridview_.comm();
nGlobalEntity_ = collective.template sum<int>(nLocalEntity_);
/** communicate the number of locally owned entities to all other processes so that the respective offset
* can be calculated on the respective processor; we use the Dune mpi collective communication facility
* for this; first, we gather the number of locally owned entities on the root process and, second, we
* broadcast the array to all processes where the respective offset can be calculated. */
std::vector<int> offset(size);
std::fill(offset.begin(), offset.end(), 0);
/** gather number of locally owned entities on root process */
collective.template gather<int>(&nLocalEntity_,offset.data(),1,0);
/** broadcast the array containing the number of locally owned entities to all processes */
indexOffset_.clear();
for(unsigned int ii=0; ii<indexOffset_.size(); ++ii)
for(unsigned int jj=0; jj < ii; ++jj)
indexOffset_[ii] += offset[jj];
/** compute globally unique index over all processes; the idea of the algorithm is as follows: if
* an entity is owned by the process, it is assigned an index that is the addition of the offset
* specific for this process and a consecutively incremented counter; if the entity is not owned
* by the process, it is assigned -1, which signals that this specific entity will get its global
* unique index through communication afterwards;
*
* thus, the calculation of the globally unique index is divided into 2 stages:
*
* (1) we calculate the global index independently;
*
* (2) we achieve parallel adjustment by communicating the index
* from the owning entity to the non-owning entity.
*
*/
/** 1st stage of global index calculation: calculate global index for owned entities */
/** intialize map that stores an entity's global index via it's globally unique id as key */
MapId2Index globalIndex;
const typename GridView::IndexSet& indexSet = gridview.indexSet();
const GlobalIdSet &globalIdSet=gridview_.grid().globalIdSet(); /** retrieve globally unique Id set */
int globalcontrib=0; /** initialize contribution for the global index */
std::vector<bool> firstTime(gridview_.size(CODIM));
std::fill(firstTime.begin(), firstTime.end(), true);
for(Iterator iter = gridview_.template begin<0>();iter!=gridview_.template end<0>(); ++iter)
{
#if DUNE_VERSION_NEWER(DUNE_GRID,2,4)
for (size_t i=0; i<iter->subEntities(CODIM); i++)
#else
for (size_t i=0; i<iter->template count<CODIM>(); i++)
#endif
IdType id=globalIdSet.subId(*iter,i,CODIM); /** retrieve the entity's id */
int idx = gridview_.indexSet().subIndex(*iter,i,CODIM);
if (! firstTime[idx] )
continue;
firstTime[idx] = false;
if(uniqueEntityPartition_.owner(idx) == true) /** if the entity is owned by the process, go ahead with computing the global index */
{
const int gindex = myoffset + globalcontrib; /** compute global index */
globalIndex.insert(std::make_pair(id,gindex)); /** insert pair (key, value) into the map */
const int lindex = idx;
localGlobalMap_[lindex] = gindex;
globalLocalMap_[gindex] = lindex;
globalcontrib++; /** increment contribution to global index */
}
else /** if entity is not owned, insert -1 to signal not yet calculated global index */
{
globalIndex.insert(std::make_pair(id,-1));
}
}
/** 2nd stage of global index calculation: communicate global index for non-owned entities */
// Create the data handle and communicate.
IndexExchange<GlobalIdSet,IdType,MapId2Index,int> dataHandle(globalIdSet,globalIndex,rank,indexSet,localGlobalMap_,globalLocalMap_);
gridview_.communicate(dataHandle, Dune::All_All_Interface, Dune::ForwardCommunication);
}
/** \brief Given a local index, retrieve its index globally unique over all processes. */
int globalIndex(const int& localIndex) const {
return localGlobalMap_.find(localIndex)->second;
}
int localIndex(const int& globalIndex) const {
return globalLocalMap_.find(globalIndex)->second;
}
int globalIndex(const typename GridView::template Codim<CODIM>::Entity& entity) const
{
return localGlobalMap_.find(gridview_.indexSet().index(entity))->second;
}
int localIndex(const typename GridView::template Codim<CODIM>::Entity& entity) const {
return gridview_.indexSet().index(entity);
}
inline unsigned int nGlobalEntity() const
{
return(nGlobalEntity_);
}
inline unsigned int nOwnedLocalEntity() const
{
return(nLocalEntity_);
}
const GridView& getGridView() const {
return gridview_;
}
protected:
/** store data members */
const GridView gridview_; /** store a const reference to a gridview */
UniqueEntityPartitionType uniqueEntityPartition_;
int nLocalEntity_; /** store number of entities that are owned by the local */
int nGlobalEntity_; /** store global number of entities, i.e. number of entities without rendundant entities on interprocessor boundaries */
std::vector<int> indexOffset_; /** store offset of entity index on every process */
IndexMap localGlobalMap_;
IndexMap globalLocalMap_;
};
#endif /* GLOBALUNIQUEINDEX_HH_ */