1
/* Basic example of using Zoltan to compute a quick partitioning
2
** of a set of objects.
3
***************************************************************/
11
/* Name of file containing objects to be partitioned */
13
static char *fname="objects.txt";
15
/* Structure to hold object data */
23
static int get_number_of_objects(void *data, int *ierr);
25
static void get_object_list(void *data, int sizeGID, int sizeLID,
26
ZOLTAN_ID_PTR globalID, ZOLTAN_ID_PTR localID,
27
int wgt_dim, float *obj_wgts, int *ierr);
29
static int get_next_line(FILE *fp, char *buf, int bufsize);
31
static void input_file_error(int numProcs, int tag, int startProc);
33
static void showSimpleMeshPartitions(int myProc, int numIDs, int *GIDs, int *parts);
35
static void read_input_objects(int myRank, int numProcs, char *fname, OBJECT_DATA *myData);
37
int main(int argc, char *argv[])
42
struct Zoltan_Struct *zz;
43
int changes, numGidEntries, numLidEntries, numImport, numExport;
44
ZOLTAN_ID_PTR importGlobalGids, importLocalGids;
45
ZOLTAN_ID_PTR exportGlobalGids, exportLocalGids;
46
int *importProcs, *importToPart, *exportProcs, *exportToPart;
52
/******************************************************************
53
** Initialize MPI and Zoltan
54
******************************************************************/
56
MPI_Init(&argc, &argv);
57
MPI_Comm_rank(MPI_COMM_WORLD, &myRank);
58
MPI_Comm_size(MPI_COMM_WORLD, &numProcs);
60
rc = Zoltan_Initialize(argc, argv, &ver);
63
printf("Error initializing Zoltan\n");
68
/******************************************************************
69
** Read objects from input file and distribute them unevenly
70
******************************************************************/
72
fp = fopen(fname, "r");
74
if (myRank == 0) fprintf(stderr,"ERROR: Can not open %s\n",fname);
80
read_input_objects(myRank, numProcs, fname, &myData);
82
/******************************************************************
83
** Create a Zoltan library structure for this instance of load
84
** balancing. Set the parameters and query functions.
85
******************************************************************/
87
zz = Zoltan_Create(MPI_COMM_WORLD);
89
/* General parameters */
91
Zoltan_Set_Param(zz, "LB_METHOD", "BLOCK"); /* Zoltan method: "BLOCK" */
92
Zoltan_Set_Param(zz, "NUM_GID_ENTRIES", "1"); /* global ID is 1 integer */
93
Zoltan_Set_Param(zz, "NUM_LID_ENTRIES", "1"); /* local ID is 1 integer */
94
Zoltan_Set_Param(zz, "OBJ_WEIGHT_DIM", "0"); /* we omit object weights */
98
Zoltan_Set_Num_Obj_Fn(zz, get_number_of_objects, &myData);
99
Zoltan_Set_Obj_List_Fn(zz, get_object_list, &myData);
101
/******************************************************************
102
** Call Zoltan to partition the objects.
103
******************************************************************/
105
rc = Zoltan_LB_Partition(zz, /* input (all remaining fields are output) */
106
&changes, /* 1 if partitioning was changed, 0 otherwise */
107
&numGidEntries, /* Number of integers used for a global ID */
108
&numLidEntries, /* Number of integers used for a local ID */
109
&numImport, /* Number of objects to be sent to me */
110
&importGlobalGids, /* Global IDs of objects to be sent to me */
111
&importLocalGids, /* Local IDs of objects to be sent to me */
112
&importProcs, /* Process rank for source of each incoming object */
113
&importToPart, /* New partition for each incoming object */
114
&numExport, /* Number of objects I must send to other processes*/
115
&exportGlobalGids, /* Global IDs of the objects I must send */
116
&exportLocalGids, /* Local IDs of the objects I must send */
117
&exportProcs, /* Process to which I send each of the objects */
118
&exportToPart); /* Partition to which each object will belong */
120
if (rc != ZOLTAN_OK){
121
printf("Error in Zoltan library\n");
127
/******************************************************************
128
** Visualize the object partitioning before and after calling Zoltan.
130
** In this example, partition number equals process rank.
131
******************************************************************/
133
parts = (int *)malloc(sizeof(int) * myData.numMyObjects);
135
for (i=0; i < myData.numMyObjects; i++){
140
printf("\nObject partition assignments before calling Zoltan\n");
143
showSimpleMeshPartitions(myRank, myData.numMyObjects, myData.myGlobalIDs, parts);
145
for (i=0; i < numExport; i++){
146
parts[exportLocalGids[i]] = exportToPart[i];
150
printf("Object partition assignments after calling Zoltan\n");
153
showSimpleMeshPartitions(myRank, myData.numMyObjects, myData.myGlobalIDs, parts);
155
/******************************************************************
156
** Free the arrays allocated by Zoltan_LB_Partition, and free
157
** the storage allocated for the Zoltan structure.
158
******************************************************************/
160
Zoltan_LB_Free_Part(&importGlobalGids, &importLocalGids,
161
&importProcs, &importToPart);
162
Zoltan_LB_Free_Part(&exportGlobalGids, &exportLocalGids,
163
&exportProcs, &exportToPart);
171
/* Application defined query functions */
173
static int get_number_of_objects(void *data, int *ierr)
176
OBJECT_DATA *objects = (OBJECT_DATA *)data;
177
return objects->numMyObjects;
180
static void get_object_list(void *data, int sizeGID, int sizeLID,
181
ZOLTAN_ID_PTR globalID, ZOLTAN_ID_PTR localID,
182
int wgt_dim, float *obj_wgts, int *ierr)
186
OBJECT_DATA *objects = (OBJECT_DATA *)data;
188
/* In this example, return the IDs of our objects, but no weights.
189
* Zoltan will assume equally weighted objects.
192
for (i=0; i<objects->numMyObjects; i++){
193
globalID[i] = objects->myGlobalIDs[i];
198
/* Function to find next line of information in input file */
200
static int get_next_line(FILE *fp, char *buf, int bufsize)
207
c = fgets(buf, bufsize, fp);
210
return 0; /* end of file */
214
for (i=0, c=buf; i < len; i++, c++){
216
if (isspace(cval) == 0) break;
218
if (i == len) continue; /* blank line */
219
if (*c == '#') continue; /* comment */
227
return strlen(buf); /* number of characters */
230
/* Proc 0 notifies others of error and exits */
232
static void input_file_error(int numProcs, int tag, int startProc)
238
fprintf(stderr,"ERROR in input file.\n");
240
for (i=startProc; i < numProcs; i++){
241
/* these procs have posted receive for "tag" */
242
MPI_Send(&val, 1, MPI_INT, i, tag, MPI_COMM_WORLD);
244
for (i=1; i < startProc; i++){
245
/* these procs are done */
246
MPI_Send(&val, 1, MPI_INT, i, 0, MPI_COMM_WORLD);
253
/* Draw the partition assignments of the objects */
255
void showSimpleMeshPartitions(int myProc, int numIDs, int *GIDs, int *parts)
257
int partAssign[25], allPartAssign[25];
260
memset(partAssign, 0, sizeof(int) * 25);
262
for (i=0; i < numIDs; i++){
263
partAssign[GIDs[i]-1] = parts[i];
266
MPI_Reduce(partAssign, allPartAssign, 25, MPI_INT, MPI_MAX, 0, MPI_COMM_WORLD);
270
for (i=20; i >= 0; i-=5){
271
for (j=0; j < 5; j++){
272
part = allPartAssign[i + j];
274
printf("%d-----",part);
279
printf("| | | | |\n");
285
/* Proc 0 reads the objects in the input file and divides them across processes */
287
void read_input_objects(int myRank, int numProcs, char *fname, OBJECT_DATA *myData)
291
int num, nobj, remainingObj, ack=0;
296
int obj_ack_tag = 5, obj_count_tag = 10, obj_id_tag = 15;
300
buf = (char *)malloc(sizeof(char) * bufsize);
301
fp = fopen(fname, "r");
303
num = get_next_line(fp, buf, bufsize);
304
if (num == 0) input_file_error(numProcs, obj_count_tag, 1);
305
num = sscanf(buf, "%d", &myData->numGlobalObjects);
306
if (num != 1) input_file_error(numProcs, obj_count_tag, 1);
309
nobj = myData->numGlobalObjects / 2;
310
remainingObj = myData->numGlobalObjects - nobj;
313
nobj = myData->numGlobalObjects;
317
myData->myGlobalIDs = (int *)malloc(sizeof(int) * nobj);
318
myData->numMyObjects = nobj;
320
for (i=0; i < nobj; i++){
322
num = get_next_line(fp, buf, bufsize);
323
if (num == 0) input_file_error(numProcs, obj_count_tag, 1);
324
num = sscanf(buf, "%d", myData->myGlobalIDs + i);
325
if (num != 1) input_file_error(numProcs, obj_count_tag, 1);
329
gids = (int *)malloc(sizeof(int) * (nobj + 1));
331
for (i=1; i < numProcs; i++){
333
if (remainingObj > 1){
334
nobj = remainingObj / 2;
335
remainingObj -= nobj;
337
else if (remainingObj == 1){
345
if ((i == numProcs - 1) && (remainingObj > 0))
346
nobj += remainingObj;
349
for (j=0; j < nobj; j++){
350
num = get_next_line(fp, buf, bufsize);
351
if (num == 0) input_file_error(numProcs, obj_count_tag, i);
352
num = sscanf(buf, "%d", gids + j);
353
if (num != 1) input_file_error(numProcs, obj_count_tag, i);
357
MPI_Send(&nobj, 1, MPI_INT, i, obj_count_tag, MPI_COMM_WORLD);
358
MPI_Recv(&ack, 1, MPI_INT, i, obj_ack_tag, MPI_COMM_WORLD, &status);
361
MPI_Send(gids, nobj, MPI_INT, i, obj_id_tag, MPI_COMM_WORLD);
369
/* signal all procs it is OK to go on */
371
for (i=1; i < numProcs; i++){
372
MPI_Send(&ack, 1, MPI_INT, i, 0, MPI_COMM_WORLD);
377
MPI_Recv(&myData->numMyObjects, 1, MPI_INT, 0, obj_count_tag, MPI_COMM_WORLD, &status);
379
if (myData->numMyObjects > 0){
380
myData->myGlobalIDs = (int *)malloc(sizeof(int) * myData->numMyObjects);
381
MPI_Send(&ack, 1, MPI_INT, 0, obj_ack_tag, MPI_COMM_WORLD);
382
MPI_Recv(myData->myGlobalIDs, myData->numMyObjects, MPI_INT, 0,
383
obj_id_tag, MPI_COMM_WORLD, &status);
385
else if (myData->numMyObjects == 0){
386
MPI_Send(&ack, 1, MPI_INT, 0, obj_ack_tag, MPI_COMM_WORLD);
393
MPI_Recv(&ack, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, &status);