diff --git a/src/partition.c b/src/partition.c index dcded26043b0c71fbde039c869052c1e37a30bb6..5ac70205a77998aff59b24df2aaafc9406b5aa99 100644 --- a/src/partition.c +++ b/src/partition.c @@ -274,6 +274,10 @@ static void accumulate_counts(struct space *s, int *counts) { static void split_metis(struct space *s, int nregions, int *celllist) { for (int i = 0; i < s->nr_cells; i++) s->cells_top[i].nodeID = celllist[i]; + + /* To check or visualise the partition dump all the cells. + * dumpCellRanks("metis_partition", s->cells_top, s->nr_cells); + */ } #endif @@ -483,13 +487,16 @@ static void repart_edge_metis(int partweights, int bothweights, int nodeID, int nr_tasks) { /* Create weight arrays using task ticks for vertices and edges (edges - * assume the same graph structure as used in the part_ calls). */ + * assume the same graph structure as used in the part_ calls). Note that + * we scale edges and vertices independently as they will have very + * different ranges of weights. */ int nr_cells = s->nr_cells; struct cell *cells = s->cells_top; - float wscale = 1.f, wscale_buff = 0.0; - int wtot = 0; - int wmax = 1e9 / nr_nodes; - int wmin; + float wscale[2] = {1.f, 1.f}; + float wscale_buff[2] = {0.0, 0.0}; + int wmax[2] = {1e9 / nr_nodes, 1e9 / nr_nodes}; + int wmin[2] = {0, 0}; + int wtot[2] = {0, 0}; /* Allocate and fill the adjncy indexing array defining the graph of * cells. */ @@ -521,18 +528,24 @@ static void repart_edge_metis(int partweights, int bothweights, int nodeID, /* Skip un-interesting tasks. */ if (t->cost == 0) continue; - /* Get the task weight. */ - int w = t->cost * wscale; + /* Get the task weight based on costs. */ + int w = t->cost * wscale[0]; + wtot[0] += w; /* Do we need to re-scale? */ - wtot += w; - while (wtot > wmax) { - wscale /= 2; - wtot /= 2; - w /= 2; - for (int k = 0; k < 26 * nr_cells; k++) weights_e[k] *= 0.5; - if (taskvweights) + if (taskvweights) { + while (wtot[0] > wmax[0]) { + wscale[0] /= 2; + wtot[0] /= 2; + w /= 2; for (int k = 0; k < nr_cells; k++) weights_v[k] *= 0.5; + } + } + + while (wtot[1] > wmax[1]) { + wscale[1] /= 2; + wtot[1] /= 2; + for (int k = 0; k < 26 * nr_cells; k++) weights_e[k] *= 0.5; } /* Get the top-level cells involved. */ @@ -575,26 +588,53 @@ static void repart_edge_metis(int partweights, int bothweights, int nodeID, } - /* Distinct cells with local ci? */ - else if (ci->nodeID == nodeID) { + /* Distinct cells. */ + else { /* Index of the jth cell. */ int cjd = cj - cells; - /* Add half of weight to each cell. */ - if (taskvweights) { - if (ci->nodeID == nodeID) weights_v[cid] += 0.5 * w; + /* Local cells add weight to vertices. */ + if (taskvweights && ci->nodeID == nodeID) { + weights_v[cid] += 0.5 * w; if (cj->nodeID == nodeID) weights_v[cjd] += 0.5 * w; } - /* Add weights to edge, reduced by some weight from the expected time - * of next interaction -- we want active cells to be clustered. */ + /* Add weights to edge for all cells based on the expected interaction + * time. We want to avoid having active cells on the edges, so we cut + * for that. */ + int dti = num_time_bins - get_time_bin(ci->ti_end_min); + int dtj = num_time_bins - get_time_bin(cj->ti_end_min); + int kk; for (kk = 26 * cid; inds[kk] != cjd; kk++) ; - weights_e[kk] += w / get_time_bin(ci->ti_end_max); + + /* If a foreign cell we repeat the interaction remotely, so these get + * 1/2 of the local time bin weight. */ + int dt; + if (ci->nodeID == nodeID) + dt = dti + dtj/2; + else + dt = dtj/2; + dt = dti + dtj; + dt = (1<<dt); + + wtot[1] += dt; + weights_e[kk] += dt; + + /* cj */ for (kk = 26 * cjd; inds[kk] != cid; kk++) ; - weights_e[kk] += w / get_time_bin(ci->ti_end_max); + if (cj->nodeID == nodeID) + dt = dtj + dti/2; + else + dt = dti/2; + + dt = dti + dtj; + dt = (1<<dt); + + wtot[1] += dt; + weights_e[kk] += dt; } } } @@ -604,7 +644,7 @@ static void repart_edge_metis(int partweights, int bothweights, int nodeID, accumulate_counts(s, weights_v); /* Rescale to balance times. */ - float vwscale = (float)wtot / (float)nr_tasks; + float vwscale = (float)wtot[0] / (float)nr_tasks; for (int k = 0; k < nr_cells; k++) { weights_v[k] *= vwscale; } @@ -612,15 +652,19 @@ static void repart_edge_metis(int partweights, int bothweights, int nodeID, /* Get the minimum scaling and re-scale if necessary. */ int res; - if ((res = MPI_Allreduce(&wscale, &wscale_buff, 1, MPI_FLOAT, MPI_MIN, + if ((res = MPI_Allreduce(&wscale, &wscale_buff, 2, MPI_FLOAT, MPI_MIN, MPI_COMM_WORLD)) != MPI_SUCCESS) mpi_error(res, "Failed to allreduce the weight scales."); - if (wscale_buff != wscale) { - float scale = wscale_buff / wscale; - for (int k = 0; k < 26 * nr_cells; k++) weights_e[k] *= scale; - if (bothweights) + if (bothweights) { + if (wscale_buff[0] != wscale[0]) { + float scale = wscale_buff[0] / wscale[0]; for (int k = 0; k < nr_cells; k++) weights_v[k] *= scale; + } + } + if (wscale_buff[1] != wscale[1]) { + float scale = wscale_buff[1] / wscale[1]; + for (int k = 0; k < 26 * nr_cells; k++) weights_e[k] *= scale; } /* Merge the weights arrays across all nodes. */ @@ -642,33 +686,39 @@ static void repart_edge_metis(int partweights, int bothweights, int nodeID, /* As of here, only one node needs to compute the partition. */ if (nodeID == 0) { + /* Final rescale of all weights to avoid a large range. Large ranges * have been seen to cause an incomplete graph. */ - wmin = wmax; - wmax = 0; - for (int k = 0; k < 26 * nr_cells; k++) { - wmax = weights_e[k] > wmax ? weights_e[k] : wmax; - wmin = weights_e[k] < wmin ? weights_e[k] : wmin; - } if (bothweights) { + wmin[0] = wmax[0]; + wmax[0] = 0; for (int k = 0; k < nr_cells; k++) { - wmax = weights_v[k] > wmax ? weights_v[k] : wmax; - wmin = weights_v[k] < wmin ? weights_v[k] : wmin; + wmax[0] = weights_v[k] > wmax[0] ? weights_v[k] : wmax[0]; + wmin[0] = weights_v[k] < wmin[0] ? weights_v[k] : wmin[0]; } - } - if ((wmax - wmin) > metis_maxweight) { - wscale = metis_maxweight / (wmax - wmin); - for (int k = 0; k < 26 * nr_cells; k++) { - weights_e[k] = (weights_e[k] - wmin) * wscale + 1; - } - if (bothweights) { + if ((wmax[0] - wmin[0]) > metis_maxweight) { + wscale[0] = metis_maxweight / (wmax[0] - wmin[0]); for (int k = 0; k < nr_cells; k++) { - weights_v[k] = (weights_v[k] - wmin) * wscale + 1; + weights_v[k] = (weights_v[k] - wmin[0]) * wscale[0] + 1; } } } + /* Now edge weights. Unlikely these will ever need scaling. */ + wmin[1] = wmax[1]; + wmax[1] = 0; + for (int k = 0; k < nr_cells; k++) { + wmax[1] = weights_e[k] > wmax[1] ? weights_e[k] : wmax[1]; + wmin[1] = weights_e[k] < wmin[1] ? weights_e[k] : wmin[1]; + } + if ((wmax[1] - wmin[1]) > metis_maxweight) { + wscale[1] = metis_maxweight / (wmax[1] - wmin[1]); + for (int k = 0; k < 26 * nr_cells; k++) { + weights_e[k] = (weights_e[k] - wmin[0]) * wscale[1] + 1; + } + } + /* Make sure there are no zero weights. */ for (int k = 0; k < 26 * nr_cells; k++) if (weights_e[k] == 0) weights_e[k] = 1;