You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
263 lines
9.0 KiB
263 lines
9.0 KiB
2 years ago
|
// Copyright(C) 2021, 2022, 2023 National Technology & Engineering Solutions
|
||
|
// of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 with
|
||
|
// NTESS, the U.S. Government retains certain rights in this software.
|
||
|
//
|
||
|
// See packages/seacas/LICENSE for details
|
||
|
|
||
|
#ifdef USE_ZOLTAN
|
||
|
#include "zoltan.h" // for Zoltan_Set_Param, etc
|
||
|
#include "zoltan_types.h" // for ZOLTAN_ID_PTR*, ZOLTAN_OK, etc
|
||
|
#include <mpi.h> // for MPI_Finalize, etc
|
||
|
#endif
|
||
|
|
||
|
#include <algorithm>
|
||
|
#include <fmt/format.h>
|
||
|
#include <random>
|
||
|
#include <vector>
|
||
|
|
||
|
#include "Cell.h"
|
||
|
#include "Grid.h"
|
||
|
#include "Ioss_ElementBlock.h"
|
||
|
#include "Ioss_ParallelUtils.h"
|
||
|
#include "Ioss_Sort.h"
|
||
|
|
||
|
extern unsigned int debug_level;
|
||
|
|
||
|
namespace {
|
||
|
#ifdef USE_ZOLTAN
|
||
|
#define STRINGIFY(x) #x
|
||
|
#define TOSTRING(x) STRINGIFY(x)
|
||
|
#define ZCHECK(funcall) \
|
||
|
do { \
|
||
|
ierr = (funcall); \
|
||
|
if (ierr == ZOLTAN_FATAL) { \
|
||
|
fmt::print(stderr, "Error returned from {} ({}:{})\n", TOSTRING(funcall), __FILE__, \
|
||
|
__LINE__); \
|
||
|
goto End; \
|
||
|
} \
|
||
|
} while (0)
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
/***** Global data structure used by Zoltan callbacks. *****/
|
||
|
/***** Could implement Zoltan callbacks without global data structure, *****/
|
||
|
/***** but using the global data structure makes implementation quick. *****/
|
||
|
struct
|
||
|
{
|
||
|
size_t ndot; /* Length of x, y, z, and part (== # of elements) */
|
||
|
int *vwgt; /* vertex weights */
|
||
|
float *x; /* x-coordinates */
|
||
|
float *y; /* y-coordinates */
|
||
|
float *z; /* z-coordinates */
|
||
|
} Zoltan_Data;
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
/***** ZOLTAN CALLBACK FUNCTIONS *****/
|
||
|
int zoltan_num_dim(void * /*data*/, int *ierr)
|
||
|
{
|
||
|
/* Return dimensionality of coordinate data.
|
||
|
* Using global data structure Zoltan_Data, initialized in ZOLTAN_RCB_assign.
|
||
|
*/
|
||
|
*ierr = ZOLTAN_OK;
|
||
|
if (Zoltan_Data.z != nullptr) {
|
||
|
return 3;
|
||
|
}
|
||
|
if (Zoltan_Data.y != nullptr) {
|
||
|
return 2;
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
int zoltan_num_obj(void * /*data*/, int *ierr)
|
||
|
{
|
||
|
/* Return number of objects.
|
||
|
* Using global data structure Zoltan_Data, initialized in ZOLTAN_RCB_assign.
|
||
|
*/
|
||
|
*ierr = ZOLTAN_OK;
|
||
|
return Zoltan_Data.ndot;
|
||
|
}
|
||
|
|
||
|
void zoltan_obj_list(void * /*data*/, int /*ngid_ent*/, int /*nlid_ent*/, ZOLTAN_ID_PTR gids,
|
||
|
ZOLTAN_ID_PTR /*lids*/, int wdim, float *wgts, int *ierr)
|
||
|
{
|
||
|
/* Return list of object IDs.
|
||
|
* Return only global IDs; don't need local IDs since running in serial.
|
||
|
* gids are array indices for coordinate and vwgts arrays.
|
||
|
* Using global data structure Zoltan_Data, initialized in ZOLTAN_RCB_assign.
|
||
|
*/
|
||
|
for (size_t i = 0; i < Zoltan_Data.ndot; i++) {
|
||
|
gids[i] = i;
|
||
|
if (wdim != 0) {
|
||
|
wgts[i] = static_cast<float>(Zoltan_Data.vwgt[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
*ierr = ZOLTAN_OK;
|
||
|
}
|
||
|
|
||
|
void zoltan_geom(void * /*data*/, int /*ngid_ent*/, int /*nlid_ent*/, int nobj,
|
||
|
const ZOLTAN_ID_PTR gids, ZOLTAN_ID_PTR /*lids*/, int ndim, double *geom,
|
||
|
int *ierr)
|
||
|
{
|
||
|
/* Return coordinates for objects.
|
||
|
* gids are array indices for coordinate arrays.
|
||
|
* Using global data structure Zoltan_Data, initialized in ZOLTAN_RCB_assign.
|
||
|
*/
|
||
|
|
||
|
for (int i = 0; i < nobj; i++) {
|
||
|
size_t j = gids[i];
|
||
|
geom[i * ndim] = Zoltan_Data.x[j];
|
||
|
if (ndim > 1) {
|
||
|
geom[i * ndim + 1] = Zoltan_Data.y[j];
|
||
|
}
|
||
|
if (ndim > 2) {
|
||
|
geom[i * ndim + 2] = Zoltan_Data.z[j];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
*ierr = ZOLTAN_OK;
|
||
|
}
|
||
|
#endif
|
||
|
} // namespace
|
||
|
|
||
|
void decompose_grid(Grid &grid, int ranks, const std::string &method)
|
||
|
{
|
||
|
if (ranks == 1) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (method == "CYCLIC" || method == "LINEAR" || method == "RANDOM") {
|
||
|
std::vector<int> rank_vec(grid.size());
|
||
|
|
||
|
// Generate a "cyclic" vector 0, 1, 2, ... ranks-1, 0, 1, ...
|
||
|
std::generate(rank_vec.begin(), rank_vec.end(),
|
||
|
[n = 0, ranks]() mutable { return n++ % ranks; });
|
||
|
|
||
|
if (method == "LINEAR") {
|
||
|
Ioss::sort(rank_vec.begin(), rank_vec.end());
|
||
|
}
|
||
|
else if (method == "RANDOM") {
|
||
|
unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
|
||
|
std::shuffle(rank_vec.begin(), rank_vec.end(), std::default_random_engine(seed));
|
||
|
}
|
||
|
|
||
|
size_t k = 0;
|
||
|
for (size_t j = 0; j < grid.JJ(); j++) {
|
||
|
for (size_t i = 0; i < grid.II(); i++) {
|
||
|
auto &cell = grid.get_cell(i, j);
|
||
|
cell.set_rank(Loc::C, rank_vec[k++]);
|
||
|
}
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
#ifdef USE_ZOLTAN
|
||
|
// Below here are Zoltan decompositions...
|
||
|
std::vector<float> x(grid.size());
|
||
|
std::vector<float> y(grid.size());
|
||
|
std::vector<int> w(grid.size());
|
||
|
|
||
|
// Get coordinates of each cell centroid... (actually origin of cell)
|
||
|
size_t idx = 0;
|
||
|
for (size_t j = 0; j < grid.JJ(); j++) {
|
||
|
for (size_t i = 0; i < grid.II(); i++) {
|
||
|
const auto &cell = grid.get_cell(i, j);
|
||
|
x[idx] = cell.m_offX;
|
||
|
y[idx] = cell.m_offY;
|
||
|
|
||
|
const auto &element_blocks = cell.region()->get_element_blocks();
|
||
|
for (const auto *block : element_blocks) {
|
||
|
w[idx] += block->entity_count();
|
||
|
}
|
||
|
idx++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Copy mesh data and pointers into structure accessible from callback fns. */
|
||
|
Zoltan_Data.ndot = grid.size();
|
||
|
Zoltan_Data.vwgt = w.data();
|
||
|
Zoltan_Data.x = x.data();
|
||
|
Zoltan_Data.y = y.data();
|
||
|
Zoltan_Data.z = nullptr;
|
||
|
|
||
|
/* Initialize Zoltan */
|
||
|
int argc = 0;
|
||
|
char **argv = nullptr;
|
||
|
ZOLTAN_ID_PTR zgids = nullptr;
|
||
|
ZOLTAN_ID_PTR zlids = nullptr; /* Useful output from Zoltan_LB_Partition */
|
||
|
int *zprocs = nullptr; /* Useful output from Zoltan_LB_Partition */
|
||
|
int *zparts = nullptr; /* Useful output from Zoltan_LB_Partition */
|
||
|
|
||
|
int ierr = 0;
|
||
|
float ver = 0.0;
|
||
|
Zoltan_Initialize(argc, argv, &ver);
|
||
|
struct Zoltan_Struct *zz = Zoltan_Create(Ioss::ParallelUtils::comm_world());
|
||
|
|
||
|
/* Register Callback functions */
|
||
|
/* Using global Zoltan_Data; could register it here instead as data field. */
|
||
|
ZCHECK(Zoltan_Set_Fn(zz, ZOLTAN_NUM_GEOM_FN_TYPE,
|
||
|
reinterpret_cast<ZOLTAN_VOID_FN *>(zoltan_num_dim), nullptr));
|
||
|
ZCHECK(Zoltan_Set_Fn(zz, ZOLTAN_NUM_OBJ_FN_TYPE,
|
||
|
reinterpret_cast<ZOLTAN_VOID_FN *>(zoltan_num_obj), nullptr));
|
||
|
ZCHECK(Zoltan_Set_Fn(zz, ZOLTAN_OBJ_LIST_FN_TYPE,
|
||
|
reinterpret_cast<ZOLTAN_VOID_FN *>(zoltan_obj_list), nullptr));
|
||
|
ZCHECK(Zoltan_Set_Fn(zz, ZOLTAN_GEOM_MULTI_FN_TYPE,
|
||
|
reinterpret_cast<ZOLTAN_VOID_FN *>(zoltan_geom), nullptr));
|
||
|
|
||
|
/* Set parameters for Zoltan */
|
||
|
ZCHECK(Zoltan_Set_Param(zz, "DEBUG_LEVEL", "0"));
|
||
|
{
|
||
|
std::string str = fmt::format("{}", ranks);
|
||
|
ZCHECK(Zoltan_Set_Param(zz, "NUM_GLOBAL_PARTITIONS", str.c_str()));
|
||
|
ZCHECK(Zoltan_Set_Param(zz, "LB_METHOD", method.c_str()));
|
||
|
}
|
||
|
ZCHECK(Zoltan_Set_Param(zz, "NUM_LID_ENTRIES", "0"));
|
||
|
ZCHECK(Zoltan_Set_Param(zz, "REMAP", "0"));
|
||
|
ZCHECK(Zoltan_Set_Param(zz, "RETURN_LISTS", "PARTITION_ASSIGNMENTS"));
|
||
|
if (Zoltan_Data.vwgt != nullptr) {
|
||
|
ZCHECK(Zoltan_Set_Param(zz, "OBJ_WEIGHT_DIM", "1"));
|
||
|
}
|
||
|
ZCHECK(Zoltan_Set_Param(zz, "RCB_RECTILINEAR_BLOCKS", "1"));
|
||
|
|
||
|
/* Call partitioner */
|
||
|
{
|
||
|
fmt::print(" Using Zoltan version {:.2}, method {}\n", static_cast<double>(ver), method);
|
||
|
int zngid_ent = 0;
|
||
|
int znlid_ent = 0; /* Useful output from Zoltan_LB_Partition */
|
||
|
int znobj = 0;
|
||
|
ZOLTAN_ID_PTR dummy1 = nullptr;
|
||
|
ZOLTAN_ID_PTR dummy2 = nullptr; /* Empty output from Zoltan_LB_Partition */
|
||
|
int dummy0 = 0;
|
||
|
int *dummy3 = nullptr;
|
||
|
int *dummy4 = nullptr;
|
||
|
int changes = 0;
|
||
|
|
||
|
ZCHECK(Zoltan_LB_Partition(zz, &changes, &zngid_ent, &znlid_ent, &dummy0, &dummy1, &dummy2,
|
||
|
&dummy3, &dummy4, &znobj, &zgids, &zlids, &zprocs, &zparts));
|
||
|
|
||
|
/* Sanity check */
|
||
|
if (grid.size() != static_cast<size_t>(znobj)) {
|
||
|
fmt::print(stderr, "Sanity check failed; ndot {} != znobj {}.\n", grid.size(),
|
||
|
static_cast<size_t>(znobj));
|
||
|
goto End;
|
||
|
}
|
||
|
|
||
|
idx = 0;
|
||
|
for (size_t j = 0; j < grid.JJ(); j++) {
|
||
|
for (size_t i = 0; i < grid.II(); i++) {
|
||
|
auto &cell = grid.get_cell(i, j);
|
||
|
cell.set_rank(Loc::C, zparts[idx++]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
End:
|
||
|
/* Clean up */
|
||
|
Zoltan_LB_Free_Part(&zgids, &zlids, &zprocs, &zparts);
|
||
|
Zoltan_Destroy(&zz);
|
||
|
if (ierr != 0) {
|
||
|
MPI_Finalize();
|
||
|
exit(-1);
|
||
|
}
|
||
|
#endif
|
||
|
}
|