Cloned SEACAS for EXODUS library with extra build files for internal package management.
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.

390 lines
14 KiB

2 years ago
/*
* Copyright(C) 1999-2020, 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
*/
#include "defs.h" // for TRUE, FALSE, max, min
#include "params.h" // for MAXSETS
#include "structs.h" // for vtx_data
/* Enact a single move from largest (smallest) set in such a way
that some small (large) set gets bigger. */
int N_VTX_CHECKS; /* number of considered moves */
int N_VTX_MOVES; /* number of actual moves */
static void nextmove(int nvtxs, /* number of vertices in graph */
int nsets, /* how many sets am I dividing into? */
double *vals[][MAXSETS], /* values in sorted lists */
int *indices[][MAXSETS], /* indices that define order in sorted lists */
int startvtx[][MAXSETS], /* index values corresponding to splitter */
double *dist, /* distances defining splitter */
int *sets, /* set assignment for each vertex */
int toobig, /* is bad set too big or too small? */
int *active, /* flags sets trying to change size */
int *next_vtx, /* vertex selected to move next */
int *next_to, /* set vertex should be moved to */
double *next_delta /* size of change in distances */
);
static void undo_coupling(struct vtx_data **graph, /* data structure with vertex weights */
int *sets, /* sets each vertex is in */
int nsets, /* number of sets being divided into */
int from, int to, /* set final vertex moved from and to */
int toobig, /* are we shrinking or enlarging a set? */
int badset, /* the set number being shrunk or enlarged */
double *size /* sizes of the different sets */
);
static void couple(int nsets, /* number of sets being divided into */
int from, int to, /* sets to be coupled */
int vtx /* vertex that they share */
);
void movevtxs(struct vtx_data **graph, /* data structure with vertex weights */
int nvtxs, /* number of vertices in graph */
int nsets, /* how many sets am I dividing into? */
double *dist, /* distances defining splitter */
int *indices[][MAXSETS], /* indices that define order in sorted lists */
double *vals[][MAXSETS], /* values in sorted lists */
int startvtx[][MAXSETS], /* index values corresponding to splitter */
int *sets, /* set assignment for each vertex */
double *size, /* sizes of the different sets */
double *goal, /* desired set sizes */
int vwgt_max /* largest vertex weight */
)
{
double largest; /* largest overshoot from desired size */
double smallest; /* largest undershoot from desired size */
int active[MAXSETS]; /* flags sets trying to change size */
double delta; /* amount distances must change */
int vtx; /* vertex being moved */
int to, from; /* set vertex is being moved to/from */
int weight; /* weight of vertex being moved */
int done; /* have I successfully move a vertex? */
/* int npass=0; */ /* counts passes through main loop */
int badset = -1; /* most unbalanced set */
int toobig = 0; /* badset too large or too small? */
int balanced; /* is balance attained? */
double imbalance; /* amount of imbalance in badset */
int i; /* loop counter */
/* Find most unbalanced set. */
imbalance = largest = smallest = 0;
for (i = 0; i < nsets; i++) {
if (size[i] - goal[i] > largest) {
largest = size[i] - goal[i];
if (largest > imbalance) {
imbalance = largest;
badset = i;
toobig = 1;
}
}
else if (goal[i] - size[i] > smallest) {
smallest = goal[i] - size[i];
if (smallest > imbalance) {
imbalance = smallest;
badset = i;
toobig = -1;
}
}
}
if (largest + smallest <= vwgt_max) {
balanced = TRUE;
}
else {
balanced = FALSE;
}
/* If not balanced, change distances to move vertices between sets. */
while (!balanced) {
/* npass++; */
for (i = 0; i < nsets; i++) {
active[i] = FALSE;
}
active[badset] = TRUE;
done = FALSE;
while (!done) {
nextmove(nvtxs, nsets, vals, indices, startvtx, dist, sets, toobig, active, &vtx, &to,
&delta);
from = sets[vtx];
weight = graph[vtx]->vwgt;
/* Now adjust all active dists to reflect this move so far. */
for (i = 0; i < nsets; i++) {
if (active[i]) {
dist[i] -= toobig * delta;
}
}
if (toobig > 0) {
if (size[to] + weight - goal[to] < largest) {
done = TRUE;
size[from] -= graph[vtx]->vwgt;
size[to] += graph[vtx]->vwgt;
sets[vtx] = to;
undo_coupling(graph, sets, nsets, from, to, toobig, badset, size);
}
else {
couple(nsets, from, to, vtx);
active[to] = TRUE;
}
}
else {
if (goal[from] - (size[from] - weight) < smallest) {
done = TRUE;
size[from] -= graph[vtx]->vwgt;
size[to] += graph[vtx]->vwgt;
sets[vtx] = to;
undo_coupling(graph, sets, nsets, from, to, toobig, badset, size);
}
else {
couple(nsets, from, to, vtx);
active[from] = TRUE;
}
}
}
/* Find most unbalanced set. */
imbalance = largest = smallest = 0;
for (i = 0; i < nsets; i++) {
if (size[i] - goal[i] > largest) {
largest = size[i] - goal[i];
if (largest > imbalance) {
imbalance = largest;
badset = i;
toobig = 1;
}
}
else if (goal[i] - size[i] > smallest) {
smallest = goal[i] - size[i];
if (smallest > imbalance) {
imbalance = smallest;
badset = i;
toobig = -1;
}
}
}
if (largest + smallest <= vwgt_max) {
balanced = TRUE;
}
else {
balanced = FALSE;
}
}
}
/* Find the next move from an active to an inactive set, returning */
/* next_vtx, next_to, and distance. */
static void nextmove(int nvtxs, /* number of vertices in graph */
int nsets, /* how many sets am I dividing into? */
double *vals[][MAXSETS], /* values in sorted lists */
int *indices[][MAXSETS], /* indices that define order in sorted lists */
int startvtx[][MAXSETS], /* index values corresponding to splitter */
double *dist, /* distances defining splitter */
int *sets, /* set assignment for each vertex */
int toobig, /* is bad set too big or too small? */
int *active, /* flags sets trying to change size */
int *next_vtx, /* vertex selected to move next */
int *next_to, /* set vertex should be moved to */
double *next_delta /* size of change in distances */
)
{
double delta; /* amount distance must change */
double bestdelta; /* best value see so far */
int good; /* is this the delta OK? */
int first; /* is this the first OK delta I've seen? */
int maxset, minset; /* larger/smaller of two sets */
int bestfrom = 0, bestto = 0; /* sets best move comes from and goes to */
int bestdir = 0; /* direction to step in list for best move */
int bestvtx = 0; /* vertex being moved between sets */
int from, to; /* sets vertex wants to move from and to */
int index; /* offset into indices array */
int dir = 0; /* direction to step through list */
int i, j; /* loop counter */
bestdelta = 0;
first = TRUE;
while (first) {
for (i = 0; i < nsets; i++) {
if (active[i]) {
for (j = 0; j < nsets; j++) {
if (!active[j]) {
/* Look for next move from active set i to inactive set j. */
if (toobig > 0) {
from = i;
to = j;
}
else {
from = j;
to = i;
}
minset = min(to, from);
maxset = max(to, from);
index = startvtx[minset][maxset];
if (index >= nvtxs || index < 0) {
good = FALSE;
}
else {
if (j > i) {
dir = -1;
}
else {
dir = 1;
}
good = TRUE;
delta = -toobig * dir * vals[minset][maxset][indices[minset][maxset][index]] -
(dist[to] - dist[from]);
}
if (good && (first || delta < bestdelta)) {
/* Is this the best so far? */
first = FALSE;
bestdelta = delta;
bestfrom = from;
bestto = to;
bestdir = dir;
bestvtx = indices[minset][maxset][index] + 1;
}
}
}
}
}
/* Only accept a vertex if it's from the right set. */
if (sets[bestvtx] != bestfrom || (toobig > 0 && !active[bestfrom]) ||
(toobig < 0 && !active[bestto])) {
/* Set rejection flag, and increment startvtx pointer. */
first = TRUE;
minset = min(bestto, bestfrom);
maxset = max(bestto, bestfrom);
startvtx[minset][maxset] -= toobig * bestdir;
}
++N_VTX_CHECKS;
}
*next_vtx = bestvtx;
*next_to = bestto;
*next_delta = bestdelta;
++N_VTX_MOVES;
}
static int ncoupled = 0;
static int coupled_vtxs[MAXSETS];
static int coupled_sets[MAXSETS][MAXSETS];
static void couple(int nsets, /* number of sets being divided into */
int from, int to, /* sets to be coupled */
int vtx /* vertex that they share */
)
{
int i; /* loop counter */
/* Check for degenerate case of vertex shared by more than two sets. */
for (i = 0; i < ncoupled; i++) {
if (coupled_vtxs[i] == vtx) {
coupled_sets[i][from] = -1;
coupled_sets[i][to] = 1;
return;
}
}
coupled_vtxs[ncoupled] = vtx;
for (i = 0; i < nsets; i++) {
coupled_sets[ncoupled][i] = 0;
}
coupled_sets[ncoupled][from] = -1;
coupled_sets[ncoupled][to] = 1;
++ncoupled;
}
static void undo_coupling(struct vtx_data **graph, /* data structure with vertex weights */
int *sets, /* sets each vertex is in */
int nsets, /* number of sets being divided into */
int from, int to, /* set final vertex moved from and to */
int toobig, /* are we shrinking or enlarging a set? */
int badset, /* the set number being shrunk or enlarged */
double *size /* sizes of the different sets */
)
{
int done; /* have enough vertices been moved? */
int found; /* have I found the right set? */
int vtx; /* vertex being moved between sets */
int i, j; /* loop counter */
if (ncoupled == 0) {
return;
}
if (toobig > 0) {
done = FALSE;
if (from == badset) {
done = TRUE;
}
while (!done) {
found = FALSE;
to = from;
for (i = 0; i < ncoupled && !found; i++) {
if (coupled_sets[i][from] == 1) { /* Found an edge into set. */
found = TRUE;
/* And find other end of edge. */
for (j = 0; j < nsets; j++) {
if (coupled_sets[i][j] == -1) {
from = j;
}
}
/* Switch the set for this vertex. */
vtx = coupled_vtxs[i];
size[from] -= graph[vtx]->vwgt;
size[to] += graph[vtx]->vwgt;
sets[vtx] = to;
coupled_sets[i][from] = 0;
if (from == badset) {
done = TRUE;
}
}
}
}
}
else {
done = FALSE;
if (to == badset) {
done = TRUE;
}
while (!done) {
found = FALSE;
from = to;
for (i = 0; i < ncoupled && !found; i++) {
if (coupled_sets[i][from] == -1) { /* Found an edge from set. */
found = TRUE;
/* And find other end of edge. */
for (j = 0; j < nsets; j++) {
if (coupled_sets[i][j] == 1) {
to = j;
}
}
/* Switch the set for this vertex. */
vtx = coupled_vtxs[i];
size[from] -= graph[vtx]->vwgt;
size[to] += graph[vtx]->vwgt;
sets[vtx] = to;
coupled_sets[i][to] = 0;
if (to == badset) {
done = TRUE;
}
}
}
}
}
ncoupled = 0;
}