Code owners
Assign users and groups as approvers for specific file changes. Learn more.
aluzoltan.hh 14.86 KiB
#ifndef ALUGRID_ZOLTAN_H_INCLUDED
#define ALUGRID_ZOLTAN_H_INCLUDED
#include <iostream>
#include <cmath>
#include <dune/alugrid/common/alugrid_assert.hh>
#include <sstream>
#include "mpAccess_MPI.h"
// Warning: Zoltan defines HAVE_MPI and HAVE_PARMETIS itself. However, their definition will
// not match ours. The following complicated preprocessor code tries
// to cope with this problem.
#if HAVE_ZOLTAN
// if DUNE was built with MPI
#if HAVE_MPI
// undefine our definition of HAVE_MPI before including zoltan_cpp.h
#undef HAVE_MPI
#define HAVE_MPI_WAS_UNDEFED_HERE
#endif
#if HAVE_PARMETIS
#undef HAVE_PARMETIS
#define HAVE_PARMETIS_WAS_UNDEFED_HERE
#endif
// include Zoltan's C++ header
#include <zoltan_cpp.h>
// undefine any definition of HAVE_MPI made by Zoltan
#ifdef HAVE_MPI
#undef HAVE_MPI
#endif // #ifdef HAVE_MPI
// undefine any definition of HAVE_PARMETIS made by Zoltan
#ifdef HAVE_PARMETIS
#undef HAVE_PARMETIS
#endif
#ifdef HAVE_MPI_WAS_UNDEFED_HERE
#ifdef ENABLE_MPI
// redefine our definition of HAVE_MPI if it was undef'd before
#define HAVE_MPI ENABLE_MPI
#else
#define HAVE_MPI 1
#endif
#undef HAVE_MPI_WAS_UNDEFED_HERE
#endif // #ifdef HAVE_MPI_WAS_UNDEFED_HERE
#ifdef HAVE_PARMETIS_WAS_UNDEFED_HERE
// redefine our definition of HAVE_PARMETIS if it was undef'd before
#define HAVE_PARMETIS ENABLE_PARMETIS
#undef HAVE_PARMETIS_WAS_UNDEFED_HERE
#endif // #ifdef HAVE_PARMETIS_WAS_UNDEFED_HERE
#endif // #if HAVE_ZOLTAN
namespace ALUGridZoltan
{
#if HAVE_ZOLTAN
template < class ldb_vertex_map_t, class ldb_edge_set_t >
class ObjectCollection
{
int _rank;
ldb_vertex_map_t& _vertexMap ;
ldb_edge_set_t& _edgeMap ;
typedef typename ldb_edge_set_t::const_iterator edgeType;
std::vector< std::vector< std::pair<edgeType,bool> > > _edges;
static const int dimension = 3 ;
public:
// constructor
ObjectCollection( int rank, ldb_vertex_map_t& vertexMap, ldb_edge_set_t& edgeMap )
: _rank( rank ),
_vertexMap( vertexMap ),
_edgeMap( edgeMap ),
_edges(0)
{}
int rank() { return _rank; }
ldb_vertex_map_t& vertexMap() { return _vertexMap; }
ldb_edge_set_t& edgeMap() { return _edgeMap; }
std::vector< std::vector<std::pair<edgeType,bool> > >& edges() { return _edges; }
int edgeIdx(int i,int k) { alugrid_assert ( i < (int)_edges.size() && k < (int)_edges[i].size() );
return ( (_edges[i][k].second) ?
_edges[i][k].first->rightNode() :
_edges[i][k].first->leftNode() ) ; }
int edgeMaster(int i,int k) { alugrid_assert ( i < (int)_edges.size() && k < (int)_edges[i].size() );
return ( (_edges[i][k].second) ?
_edges[i][k].first->rightMaster() :
_edges[i][k].first->leftMaster() ) ; }
int edgeWeight(int i,int k) { alugrid_assert ( i < (int)_edges.size() && k < (int)_edges[i].size() );
return _edges[i][k].first->weight(); }
// query functions that respond to requests from Zoltan
static int get_number_of_objects(void *data, int *ierr)
{
ObjectCollection *objs = static_cast<ObjectCollection *> (data);
alugrid_assert ( objs );
*ierr = ZOLTAN_OK;
return objs->vertexMap().size();
}
static void get_object_list(void *data, int sizeGID, int sizeLID,
ZOLTAN_ID_PTR globalID, ZOLTAN_ID_PTR localID,
int wgt_dim, float *obj_wgts, int *ierr)
{
ObjectCollection *objs = static_cast<ObjectCollection *> (data);
alugrid_assert ( objs );
alugrid_assert (wgt_dim==1);
*ierr = ZOLTAN_OK;
ldb_vertex_map_t& vertexMap = objs->vertexMap();
// In this example, return the IDs of our objects,
int i = 0;
typedef typename ldb_vertex_map_t :: iterator iterator ;
const iterator end = vertexMap.end();
for ( iterator it = vertexMap.begin(); it != end; ++ it, ++i )
{
// std::cout << "[" << objs->rank() << "]: ";
// std::cout << "vertex(" << i << ") = " << (*it).first.index() << std::endl;
globalID[ i ] = (*it).first.index() ;
localID [ i ] = i;
if (wgt_dim == 1)
obj_wgts[ i ] = (*it).first.weight();
}
}
static void get_num_edges_list(void *data, int sizeGID, int sizeLID,
int num_obj,
ZOLTAN_ID_PTR globalID, ZOLTAN_ID_PTR localID,
int *numEdges, int *ierr)
{
ObjectCollection *objs = static_cast<ObjectCollection *> (data);
alugrid_assert ( num_obj == (int)objs->vertexMap().size() );
if (num_obj == 0)
{
*ierr = ZOLTAN_OK;
return;
}
for (int i=0;i<num_obj;++i)
{
numEdges[i] = 0;
}
objs->edges().resize(num_obj);
const int rank = objs->rank();
typename ldb_edge_set_t::const_iterator iEnd = objs->edgeMap().end();
for (typename ldb_edge_set_t::const_iterator it = objs->edgeMap().begin (); it != iEnd; ++it )
{
if ( it->leftMaster() == rank )
{
int node = it->leftNode();
int i=0;
// std::cout << "[" << objs->rank() << "]: ";
// std::cout << "edge(" << node << ") = " << it->rightNode() << " " << it->rightMaster() << std::endl;
for (;i<num_obj;++i)
if ((int)globalID[i] == node) break;
alugrid_assert ( i<num_obj );
alugrid_assert (it->rightMaster() >= 0);
alugrid_assert (it->weight() >= 0);
++numEdges[i];
objs->edges()[i].push_back( std::make_pair(it,true) );
}
if ( it->rightMaster() == rank )
{
int node = it->rightNode();
// std::cout << "[" << objs->rank() << "]: ";
// std::cout << "edge(" << node << ") = " << it->leftNode() << " " << it->leftMaster() << std::endl;
int i=0;
for (;i<num_obj;++i)
if ((int)globalID[i] == node) break;
alugrid_assert ( i<num_obj );
alugrid_assert (it->leftMaster() >= 0);
alugrid_assert (it->weight() >= 0);
++numEdges[i];
objs->edges()[i].push_back( std::make_pair(it,false) );
}
}
*ierr = ZOLTAN_OK;
}
static void get_edge_list(void *data, int sizeGID, int sizeLID,
int num_obj, ZOLTAN_ID_PTR globalID, ZOLTAN_ID_PTR localID,
int *num_edges,
ZOLTAN_ID_PTR nborGID, int *nborProc,
int wgt_dim, float *ewgts, int *ierr)
{
ObjectCollection *objs = static_cast<ObjectCollection *> (data);
if (num_obj == 0)
{
*ierr = ZOLTAN_OK;
return;
}
int k=0;
for (int j=0;j<num_obj;++j)
{
for (int l=0;l<num_edges[j];++l)
{
// std::cout << "[" << objs->rank() << "]: ";
// std::cout << "v(" << j << ")(" << l << ")=" << objs->edgeIdx(j,l) << " " << objs->edgeMaster(j,l) << std::endl;
nborGID[k] = objs->edgeIdx(j,l);
nborProc[k] = objs->edgeMaster(j,l);
alugrid_assert ( nborProc[k] >= 0 );
if (wgt_dim==1)
{
ewgts[k]=objs->edgeWeight(j,l);
alugrid_assert ( ewgts[k] >= 0 );
}
++k;
}
}
*ierr = ZOLTAN_OK;
}
// return dimension of coordinates
static int get_num_geometry(void *data, int *ierr)
{
*ierr = ZOLTAN_OK;
return dimension;
}
static void get_geometry_list(void *data, int sizeGID, int sizeLID,
int num_obj,
ZOLTAN_ID_PTR globalID, ZOLTAN_ID_PTR localID,
int num_dim, double *geom_vec, int *ierr)
{
ObjectCollection *objs = static_cast<ObjectCollection *> (data);
alugrid_assert ( objs );
if ( (sizeGID != 1) || (sizeLID != 1) || (num_dim != dimension))
{
*ierr = ZOLTAN_FATAL;
return;
}
*ierr = ZOLTAN_OK;
ldb_vertex_map_t& vertexMap = objs->vertexMap();
int idx = 0;
typedef typename ldb_vertex_map_t :: iterator iterator ;
double coord[ 3 ];
const iterator end = vertexMap.end();
for ( iterator it = vertexMap.begin(); it != end; ++ it )
{
(*it).first.computeBaryCenter( coord );
for( int d=0; d<dimension; ++d, ++idx )
geom_vec[ idx ] = coord[ d ];
}
}
};
#endif // #if HAVE_ZOLTAN
enum method_t {HSFC, PHG, PARMETIS};
template< class ldb_vertex_map_t, class ldb_edge_set_t, class ldb_connect_set_t >
bool CALL_Zoltan_LB_Partition( method_t method,
ALUGrid::MpAccessGlobal &mpa,
ldb_vertex_map_t& vertexMap,
ldb_edge_set_t& edgeSet,
ldb_connect_set_t& connect,
const double givenTolerance,
const bool verbose )
{
#if HAVE_ZOLTAN && HAVE_MPI
ALUGrid::MpAccessMPI* mpaMPI = dynamic_cast<ALUGrid::MpAccessMPI *> (&mpa);
if( mpaMPI == 0 )
{
std::cerr << "ERROR: wrong mpAccess object, couldn't convert to MpAccessMPI!! in: " << __FILE__ << " line : " << __LINE__ << std::endl;
std::abort();
}
// get communincator (see mpAccess_MPI.cc
MPI_Comm comm = mpaMPI->communicator();
int rank = mpaMPI->myrank();
typedef ObjectCollection< ldb_vertex_map_t, ldb_edge_set_t > ObjectCollectionType;
ObjectCollectionType objects( rank, vertexMap, edgeSet );
Zoltan *zz = new Zoltan( comm );
alugrid_assert ( zz );
// General parameters
const char* debug = ( verbose ) ? "1" : "0";
zz->Set_Param( "DEBUG_LEVEL", debug );
zz->Set_Param( "OBJ_WEIGHT_DIM", "1");
zz->Set_Param( "NUM_GID_ENTRIES", "1");
zz->Set_Param( "NUM_LID_ENTRIES", "1");
zz->Set_Param( "RETURN_LISTS", "ALL");
if ( method == HSFC ) // edgeSet.size() == 0 )
{
zz->Set_Param( "LB_METHOD", "HSFC");
//zz->Set_Param( "KEEP_CUTS", "1" );
zz->Set_Param( "RCB_OUTPUT_LEVEL", "0");
zz->Set_Param( "RCB_RECTILINEAR_BLOCKS", "1");
zz->Set_Param( "LB_APPROACH","REPARTITION");
}
else
{
zz->Set_Param( "LB_METHOD", "GRAPH");
zz->Set_Param( "LB_APPROACH", "REPARTITION");
// zz->Set_Param( "LB_APPROACH","PARTITION"); // give an error in PARMETIS with an
// empty partitioning - no idea why...
// zz->Set_Param( "LB_APPROACH", "REFINE"); // gives bad loadbalance with
// PARMETOS and the rest of the setting - no idea why
zz->Set_Param( "EDGE_WEIGHT_DIM","1");
zz->Set_Param( "GRAPH_SYMMETRIZE","NONE" );
// zz->Set_Param( "GRAPH_SYMMETRIZE","TRANSPOSE");
zz->Set_Param( "GRAPH_SYM_WEIGHT","MAX");
// zz->Set_Param( "GRAPH_BUILD_TYPE","FAST_NO_DUP");
#if HAVE_PARMETIS
if (method == PARMETIS)
zz->Set_Param( "GRAPH_PACKAGE","PARMETIS");
#elif defined(HAVE_SCOTCH)
zz->Set_Param( "GRAPH_PACKAGE","SCOTCH");
#endif
zz->Set_Param( "CHECK_GRAPH", "0");
zz->Set_Param( "PHG_EDGE_SIZE_THRESHOLD", ".25");
}
zz->Set_Num_Obj_Fn ( ObjectCollectionType::get_number_of_objects, &objects);
zz->Set_Obj_List_Fn( ObjectCollectionType::get_object_list, &objects);
zz->Set_Num_Geom_Fn( ObjectCollectionType::get_num_geometry, &objects);
zz->Set_Geom_Multi_Fn( ObjectCollectionType::get_geometry_list, &objects);
zz->Set_Num_Edges_Multi_Fn(ObjectCollectionType::get_num_edges_list, &objects);
zz->Set_Edge_List_Multi_Fn(ObjectCollectionType::get_edge_list, &objects);
int changes = 0;
int numGidEntries = 0;
int numLidEntries = 0;
int numImport = 0;
ZOLTAN_ID_PTR importGlobalIds = 0;
ZOLTAN_ID_PTR importLocalIds = 0;
int *importProcs = 0;
int *importToPart = 0;
int numExport = 0;
ZOLTAN_ID_PTR exportGlobalIds = 0;
ZOLTAN_ID_PTR exportLocalIds = 0;
int *exportProcs = 0;
int *exportToPart = 0;
double tolerance = givenTolerance ;
int rc = ZOLTAN_OK + 1 ;
int count = 0 ;
while( rc != ZOLTAN_OK )
{
// std::cout << "[" << rank << "]: ";
// std::cout << "zoltan partitioning tolerance: " << tolerance << std::endl;
// tolerance for load imbalance
{
std::stringstream tol;
tol << tolerance ;
zz->Set_Param( "IMBALANCE_TOL", tol.str() );
}
rc = zz->LB_Partition(changes, numGidEntries, numLidEntries,
numImport, importGlobalIds, importLocalIds, importProcs, importToPart,
numExport, exportGlobalIds, exportLocalIds, exportProcs, exportToPart);
// increase imbalance tolerance and try again
if( rc != ZOLTAN_OK )
{
tolerance *= 1.05 ;
++ count ;
if( count > 3 ) break ;
}
}
// if new partitioning has been calculated
if (rc == ZOLTAN_OK)
{
typedef typename ldb_vertex_map_t::iterator iterator;
for (int i=0; i < numExport; ++i)
{
iterator vertex = vertexMap.find( exportGlobalIds[ i ] );
alugrid_assert ( vertex != vertexMap.end () );
(*vertex).second = exportProcs[ i ];
}
const iterator iEnd = vertexMap.end ();
const int myrank = mpaMPI->myrank();
for ( iterator i = vertexMap.begin (); i != iEnd; ++i )
{
int& moveTo = i->second;
// insert and also set partition number new (including own number)
if ( moveTo == -1 ) moveTo = myrank ;
connect.insert( ALUGrid::MpAccessLocal::sendRank( moveTo ) );
}
// insert also process number that I will receive objects from
for (int i=0; i < numImport; ++i)
{
connect.insert( ALUGrid::MpAccessLocal::recvRank( importProcs[ i ] ) );
}
}
else
{
if( verbose && mpa.myrank() == 0 )
std::cerr << "ERROR: Zoltan partitioning failed, partitioning won't change! " << std::endl;
// no changes
changes = 0;
}
////////////////////////////////////////////////////////////////
// Free the arrays allocated by LB_Partition, and free
// the storage allocated for the Zoltan structure and the mesh.
////////////////////////////////////////////////////////////////
Zoltan::LB_Free_Part(&importGlobalIds, &importLocalIds, &importProcs, &importToPart);
Zoltan::LB_Free_Part(&exportGlobalIds, &exportLocalIds, &exportProcs, &exportToPart);
// delete zoltan structure
delete zz;
// return true if partition changed
return (changes > 0);
#else
std::cerr << "ERROR: Zoltan library not found, cannot use Zoltan partitioning! " << std::endl;
std::abort();
return false ;
#endif // #if HAVE_ZOLTAN && HAVE_MPI
} // CALL_Zoltan_LB_Partition
} // namespace ALUGridZoltan
#endif // #ifndef ALUGRID_ZOLTAN_H_INCLUDED