Path: blob/devel/elmergrid/src/metis-5.1.0/libmetis/minconn.c
3206 views
/*!1\file2\brief Functions that deal with prunning the number of adjacent subdomains in kmetis34\date Started 7/15/985\author George6\author Copyright 1997-2009, Regents of the University of Minnesota7\version $Id: minconn.c 10513 2011-07-07 22:06:03Z karypis $8*/910#include "metislib.h"111213/*************************************************************************/14/*! This function computes the subdomain graph storing the result in the15pre-allocated worspace arrays */16/*************************************************************************/17void ComputeSubDomainGraph(ctrl_t *ctrl, graph_t *graph)18{19idx_t i, ii, j, pid, other, nparts, nvtxs, nnbrs;20idx_t *xadj, *adjncy, *adjwgt, *where;21idx_t *pptr, *pind;22idx_t nads=0, *vadids, *vadwgts;2324WCOREPUSH;2526nvtxs = graph->nvtxs;27xadj = graph->xadj;28adjncy = graph->adjncy;29adjwgt = graph->adjwgt;30where = graph->where;3132nparts = ctrl->nparts;3334vadids = ctrl->pvec1;35vadwgts = iset(nparts, 0, ctrl->pvec2);3637pptr = iwspacemalloc(ctrl, nparts+1);38pind = iwspacemalloc(ctrl, nvtxs);39iarray2csr(nvtxs, nparts, where, pptr, pind);4041for (pid=0; pid<nparts; pid++) {42switch (ctrl->objtype) {43case METIS_OBJTYPE_CUT:44{45ckrinfo_t *rinfo;46cnbr_t *nbrs;4748rinfo = graph->ckrinfo;49for (nads=0, ii=pptr[pid]; ii<pptr[pid+1]; ii++) {50i = pind[ii];51ASSERT(pid == where[i]);5253if (rinfo[i].ed > 0) {54nnbrs = rinfo[i].nnbrs;55nbrs = ctrl->cnbrpool + rinfo[i].inbr;5657for (j=0; j<nnbrs; j++) {58other = nbrs[j].pid;59if (vadwgts[other] == 0)60vadids[nads++] = other;61vadwgts[other] += nbrs[j].ed;62}63}64}65}66break;6768case METIS_OBJTYPE_VOL:69{70vkrinfo_t *rinfo;71vnbr_t *nbrs;7273rinfo = graph->vkrinfo;74for (nads=0, ii=pptr[pid]; ii<pptr[pid+1]; ii++) {75i = pind[ii];76ASSERT(pid == where[i]);7778if (rinfo[i].ned > 0) {79nnbrs = rinfo[i].nnbrs;80nbrs = ctrl->vnbrpool + rinfo[i].inbr;8182for (j=0; j<nnbrs; j++) {83other = nbrs[j].pid;84if (vadwgts[other] == 0)85vadids[nads++] = other;86vadwgts[other] += nbrs[j].ned;87}88}89}90}91break;9293default:94gk_errexit(SIGERR, "Unknown objtype: %d\n", ctrl->objtype);95}9697/* See if you have enough memory to store the adjacent info for that subdomain */98if (ctrl->maxnads[pid] < nads) {99ctrl->maxnads[pid] = 2*nads;100ctrl->adids[pid] = irealloc(ctrl->adids[pid], ctrl->maxnads[pid],101"ComputeSubDomainGraph: adids[pid]");102ctrl->adwgts[pid] = irealloc(ctrl->adwgts[pid], ctrl->maxnads[pid],103"ComputeSubDomainGraph: adids[pid]");104}105106ctrl->nads[pid] = nads;107for (j=0; j<nads; j++) {108ctrl->adids[pid][j] = vadids[j];109ctrl->adwgts[pid][j] = vadwgts[vadids[j]];110111vadwgts[vadids[j]] = 0;112}113}114115WCOREPOP;116}117118119/*************************************************************************/120/*! This function updates the weight of an edge in the subdomain graph by121adding to it the value of ewgt. The update can either increase or122decrease the weight of the subdomain edge based on the value of ewgt.123124\param u is the ID of one of the incident subdomains to the edge125\param v is the ID of the other incident subdomains to the edge126\param ewgt is the weight to be added to the subdomain edge127\param nparts is the number of subdomains128\param r_maxndoms is the maximum number of adjacent subdomains and is129updated as necessary. The update is skipped if a NULL value is130supplied.131*/132/*************************************************************************/133void UpdateEdgeSubDomainGraph(ctrl_t *ctrl, idx_t u, idx_t v, idx_t ewgt,134idx_t *r_maxndoms)135{136idx_t i, j, nads;137138if (ewgt == 0)139return;140141for (i=0; i<2; i++) {142nads = ctrl->nads[u];143/* Find the edge */144for (j=0; j<nads; j++) {145if (ctrl->adids[u][j] == v) {146ctrl->adwgts[u][j] += ewgt;147break;148}149}150151if (j == nads) {152/* Deal with the case in which the edge was not found */153ASSERT(ewgt > 0);154if (ctrl->maxnads[u] == nads) {155ctrl->maxnads[u] = 2*(nads+1);156ctrl->adids[u] = irealloc(ctrl->adids[u], ctrl->maxnads[u],157"IncreaseEdgeSubDomainGraph: adids[pid]");158ctrl->adwgts[u] = irealloc(ctrl->adwgts[u], ctrl->maxnads[u],159"IncreaseEdgeSubDomainGraph: adids[pid]");160}161ctrl->adids[u][nads] = v;162ctrl->adwgts[u][nads] = ewgt;163nads++;164if (r_maxndoms != NULL && nads > *r_maxndoms) {165printf("You just increased the maxndoms: %"PRIDX" %"PRIDX"\n",166nads, *r_maxndoms);167*r_maxndoms = nads;168}169}170else {171/* See if the updated edge becomes 0 */172ASSERT(ctrl->adwgts[u][j] >= 0);173if (ctrl->adwgts[u][j] == 0) {174ctrl->adids[u][j] = ctrl->adids[u][nads-1];175ctrl->adwgts[u][j] = ctrl->adwgts[u][nads-1];176nads--;177if (r_maxndoms != NULL && nads+1 == *r_maxndoms)178*r_maxndoms = ctrl->nads[iargmax(ctrl->nparts, ctrl->nads)];179}180}181ctrl->nads[u] = nads;182183SWAP(u, v, j);184}185}186187188/*************************************************************************/189/*! This function computes the subdomain graph */190/*************************************************************************/191void EliminateSubDomainEdges(ctrl_t *ctrl, graph_t *graph)192{193idx_t i, ii, j, k, ncon, nparts, scheme, pid_from, pid_to, me, other, nvtxs,194total, max, avg, totalout, nind=0, ncand=0, ncand2, target, target2,195nadd, bestnadd=0;196idx_t min, move, *cpwgt;197idx_t *xadj, *adjncy, *vwgt, *adjwgt, *pwgts, *where, *maxpwgt,198*mypmat, *otherpmat, *kpmat, *ind;199idx_t *nads, **adids, **adwgts;200ikv_t *cand, *cand2;201ipq_t queue;202real_t *tpwgts, badfactor=1.4;203idx_t *pptr, *pind;204idx_t *vmarker=NULL, *pmarker=NULL, *modind=NULL; /* volume specific work arrays */205206WCOREPUSH;207208nvtxs = graph->nvtxs;209ncon = graph->ncon;210xadj = graph->xadj;211adjncy = graph->adjncy;212vwgt = graph->vwgt;213adjwgt = (ctrl->objtype == METIS_OBJTYPE_VOL ? NULL : graph->adjwgt);214215where = graph->where;216pwgts = graph->pwgts; /* We assume that this is properly initialized */217218nparts = ctrl->nparts;219tpwgts = ctrl->tpwgts;220221cpwgt = iwspacemalloc(ctrl, ncon);222maxpwgt = iwspacemalloc(ctrl, nparts*ncon);223ind = iwspacemalloc(ctrl, nvtxs);224otherpmat = iset(nparts, 0, iwspacemalloc(ctrl, nparts));225226cand = ikvwspacemalloc(ctrl, nparts);227cand2 = ikvwspacemalloc(ctrl, nparts);228229pptr = iwspacemalloc(ctrl, nparts+1);230pind = iwspacemalloc(ctrl, nvtxs);231iarray2csr(nvtxs, nparts, where, pptr, pind);232233if (ctrl->objtype == METIS_OBJTYPE_VOL) {234/* Vol-refinement specific working arrays */235modind = iwspacemalloc(ctrl, nvtxs);236vmarker = iset(nvtxs, 0, iwspacemalloc(ctrl, nvtxs));237pmarker = iset(nparts, -1, iwspacemalloc(ctrl, nparts));238}239240241/* Compute the pmat matrix and ndoms */242ComputeSubDomainGraph(ctrl, graph);243244nads = ctrl->nads;245adids = ctrl->adids;246adwgts = ctrl->adwgts;247248mypmat = iset(nparts, 0, ctrl->pvec1);249kpmat = iset(nparts, 0, ctrl->pvec2);250251/* Compute the maximum allowed weight for each domain */252for (i=0; i<nparts; i++) {253for (j=0; j<ncon; j++)254maxpwgt[i*ncon+j] =255(ncon == 1 ? 1.25 : 1.025)*tpwgts[i]*graph->tvwgt[j]*ctrl->ubfactors[j];256}257258ipqInit(&queue, nparts);259260/* Get into the loop eliminating subdomain connections */261while (1) {262total = isum(nparts, nads, 1);263avg = total/nparts;264max = nads[iargmax(nparts, nads)];265266IFSET(ctrl->dbglvl, METIS_DBG_CONNINFO,267printf("Adjacent Subdomain Stats: Total: %3"PRIDX", "268"Max: %3"PRIDX"[%zu], Avg: %3"PRIDX"\n",269total, max, iargmax(nparts, nads), avg));270271if (max < badfactor*avg)272break;273274/* Add the subdomains that you will try to reduce their connectivity */275ipqReset(&queue);276for (i=0; i<nparts; i++) {277if (nads[i] >= avg + (max-avg)/2)278ipqInsert(&queue, i, nads[i]);279}280281move = 0;282while ((me = ipqGetTop(&queue)) != -1) {283totalout = isum(nads[me], adwgts[me], 1);284285for (ncand2=0, i=0; i<nads[me]; i++) {286mypmat[adids[me][i]] = adwgts[me][i];287288/* keep track of the weakly connected adjacent subdomains */289if (2*nads[me]*adwgts[me][i] < totalout) {290cand2[ncand2].val = adids[me][i];291cand2[ncand2++].key = adwgts[me][i];292}293}294295IFSET(ctrl->dbglvl, METIS_DBG_CONNINFO,296printf("Me: %"PRIDX", Degree: %4"PRIDX", TotalOut: %"PRIDX",\n",297me, nads[me], totalout));298299/* Sort the connections according to their cut */300ikvsorti(ncand2, cand2);301302/* Two schemes are used for eliminating subdomain edges.303The first, tries to eliminate subdomain edges by moving remote groups304of vertices to subdomains that 'me' is already connected to.305The second, tries to eliminate subdomain edges by moving entire sets of306my vertices that connect to the 'other' subdomain to a subdomain that307I'm already connected to.308These two schemes are applied in sequence. */309target = target2 = -1;310for (scheme=0; scheme<2; scheme++) {311for (min=0; min<ncand2; min++) {312other = cand2[min].val;313314/* pid_from is the subdomain from where the vertices will be removed.315pid_to is the adjacent subdomain to pid_from that defines the316(me, other) subdomain edge that needs to be removed */317if (scheme == 0) {318pid_from = other;319pid_to = me;320}321else {322pid_from = me;323pid_to = other;324}325326/* Go and find the vertices in 'other' that are connected in 'me' */327for (nind=0, ii=pptr[pid_from]; ii<pptr[pid_from+1]; ii++) {328i = pind[ii];329ASSERT(where[i] == pid_from);330for (j=xadj[i]; j<xadj[i+1]; j++) {331if (where[adjncy[j]] == pid_to) {332ind[nind++] = i;333break;334}335}336}337338/* Go and construct the otherpmat to see where these nind vertices are339connected to */340iset(ncon, 0, cpwgt);341for (ncand=0, ii=0; ii<nind; ii++) {342i = ind[ii];343iaxpy(ncon, 1, vwgt+i*ncon, 1, cpwgt, 1);344345for (j=xadj[i]; j<xadj[i+1]; j++) {346if ((k = where[adjncy[j]]) == pid_from)347continue;348if (otherpmat[k] == 0)349cand[ncand++].val = k;350otherpmat[k] += (adjwgt ? adjwgt[j] : 1);351}352}353354for (i=0; i<ncand; i++) {355cand[i].key = otherpmat[cand[i].val];356ASSERT(cand[i].key > 0);357}358359ikvsortd(ncand, cand);360361IFSET(ctrl->dbglvl, METIS_DBG_CONNINFO,362printf("\tMinOut: %4"PRIDX", to: %3"PRIDX", TtlWgt: %5"PRIDX"[#:%"PRIDX"]\n",363mypmat[other], other, isum(ncon, cpwgt, 1), nind));364365/* Go through and select the first domain that is common with 'me', and does366not increase the nads[target] higher than nads[me], subject to the maxpwgt367constraint. Traversal is done from the mostly connected to the least. */368for (i=0; i<ncand; i++) {369k = cand[i].val;370371if (mypmat[k] > 0) {372/* Check if balance will go off */373if (!ivecaxpylez(ncon, 1, cpwgt, pwgts+k*ncon, maxpwgt+k*ncon))374continue;375376/* get a dense vector out of k's connectivity */377for (j=0; j<nads[k]; j++)378kpmat[adids[k][j]] = adwgts[k][j];379380/* Check if the move to domain k will increase the nads of another381subdomain j that the set of vertices being moved are connected382to but domain k is not connected to. */383for (j=0; j<nparts; j++) {384if (otherpmat[j] > 0 && kpmat[j] == 0 && nads[j]+1 >= nads[me])385break;386}387388/* There were no bad second level effects. See if you can find a389subdomain to move to. */390if (j == nparts) {391for (nadd=0, j=0; j<nparts; j++) {392if (otherpmat[j] > 0 && kpmat[j] == 0)393nadd++;394}395396IFSET(ctrl->dbglvl, METIS_DBG_CONNINFO,397printf("\t\tto=%"PRIDX", nadd=%"PRIDX", %"PRIDX"\n", k, nadd, nads[k]));398399if (nads[k]+nadd < nads[me]) {400if (target2 == -1 || nads[target2]+bestnadd > nads[k]+nadd ||401(nads[target2]+bestnadd == nads[k]+nadd && bestnadd > nadd)) {402target2 = k;403bestnadd = nadd;404}405}406407if (nadd == 0)408target = k;409}410411/* reset kpmat for the next iteration */412for (j=0; j<nads[k]; j++)413kpmat[adids[k][j]] = 0;414}415416if (target != -1)417break;418}419420/* reset the otherpmat for the next iteration */421for (i=0; i<ncand; i++)422otherpmat[cand[i].val] = 0;423424if (target == -1 && target2 != -1)425target = target2;426427if (target != -1) {428IFSET(ctrl->dbglvl, METIS_DBG_CONNINFO,429printf("\t\tScheme: %"PRIDX". Moving to %"PRIDX"\n", scheme, target));430move = 1;431break;432}433}434435if (target != -1)436break; /* A move was found. No need to try the other scheme */437}438439/* reset the mypmat for next iteration */440for (i=0; i<nads[me]; i++)441mypmat[adids[me][i]] = 0;442443/* Note that once a target is found the above loops exit right away. So the444following variables are valid */445if (target != -1) {446switch (ctrl->objtype) {447case METIS_OBJTYPE_CUT:448MoveGroupMinConnForCut(ctrl, graph, target, nind, ind);449break;450case METIS_OBJTYPE_VOL:451MoveGroupMinConnForVol(ctrl, graph, target, nind, ind, vmarker,452pmarker, modind);453break;454default:455gk_errexit(SIGERR, "Unknown objtype of %d\n", ctrl->objtype);456}457458/* Update the csr representation of the partitioning vector */459iarray2csr(nvtxs, nparts, where, pptr, pind);460}461}462463if (move == 0)464break;465}466467ipqFree(&queue);468469WCOREPOP;470}471472473/*************************************************************************/474/*! This function moves a collection of vertices and updates their rinfo */475/*************************************************************************/476void MoveGroupMinConnForCut(ctrl_t *ctrl, graph_t *graph, idx_t to, idx_t nind,477idx_t *ind)478{479idx_t i, ii, j, jj, k, l, nvtxs, nbnd, from, me;480idx_t *xadj, *adjncy, *adjwgt, *where, *bndptr, *bndind;481ckrinfo_t *myrinfo;482cnbr_t *mynbrs;483484nvtxs = graph->nvtxs;485xadj = graph->xadj;486adjncy = graph->adjncy;487adjwgt = graph->adjwgt;488489where = graph->where;490bndptr = graph->bndptr;491bndind = graph->bndind;492493nbnd = graph->nbnd;494495while (--nind>=0) {496i = ind[nind];497from = where[i];498499myrinfo = graph->ckrinfo+i;500if (myrinfo->inbr == -1) {501myrinfo->inbr = cnbrpoolGetNext(ctrl, xadj[i+1]-xadj[i]+1);502myrinfo->nnbrs = 0;503}504mynbrs = ctrl->cnbrpool + myrinfo->inbr;505506/* find the location of 'to' in myrinfo or create it if it is not there */507for (k=0; k<myrinfo->nnbrs; k++) {508if (mynbrs[k].pid == to)509break;510}511if (k == myrinfo->nnbrs) {512ASSERT(k < xadj[i+1]-xadj[i]);513mynbrs[k].pid = to;514mynbrs[k].ed = 0;515myrinfo->nnbrs++;516}517518/* Update pwgts */519iaxpy(graph->ncon, 1, graph->vwgt+i*graph->ncon, 1, graph->pwgts+to*graph->ncon, 1);520iaxpy(graph->ncon, -1, graph->vwgt+i*graph->ncon, 1, graph->pwgts+from*graph->ncon, 1);521522/* Update mincut */523graph->mincut -= mynbrs[k].ed-myrinfo->id;524525/* Update subdomain connectivity graph to reflect the move of 'i' */526UpdateEdgeSubDomainGraph(ctrl, from, to, myrinfo->id-mynbrs[k].ed, NULL);527528/* Update ID/ED and BND related information for the moved vertex */529UpdateMovedVertexInfoAndBND(i, from, k, to, myrinfo, mynbrs, where, nbnd,530bndptr, bndind, BNDTYPE_REFINE);531532/* Update the degrees of adjacent vertices */533for (j=xadj[i]; j<xadj[i+1]; j++) {534ii = adjncy[j];535me = where[ii];536myrinfo = graph->ckrinfo+ii;537538UpdateAdjacentVertexInfoAndBND(ctrl, ii, xadj[ii+1]-xadj[ii], me,539from, to, myrinfo, adjwgt[j], nbnd, bndptr, bndind, BNDTYPE_REFINE);540541/* Update subdomain graph to reflect the move of 'i' for domains other542than 'from' and 'to' */543if (me != from && me != to) {544UpdateEdgeSubDomainGraph(ctrl, from, me, -adjwgt[j], NULL);545UpdateEdgeSubDomainGraph(ctrl, to, me, adjwgt[j], NULL);546}547}548}549550ASSERT(ComputeCut(graph, where) == graph->mincut);551552graph->nbnd = nbnd;553554}555556557/*************************************************************************/558/*! This function moves a collection of vertices and updates their rinfo */559/*************************************************************************/560void MoveGroupMinConnForVol(ctrl_t *ctrl, graph_t *graph, idx_t to, idx_t nind,561idx_t *ind, idx_t *vmarker, idx_t *pmarker, idx_t *modind)562{563idx_t i, ii, j, jj, k, l, nvtxs, from, me, other, xgain, ewgt;564idx_t *xadj, *vsize, *adjncy, *where;565vkrinfo_t *myrinfo, *orinfo;566vnbr_t *mynbrs, *onbrs;567568nvtxs = graph->nvtxs;569xadj = graph->xadj;570vsize = graph->vsize;571adjncy = graph->adjncy;572where = graph->where;573574while (--nind>=0) {575i = ind[nind];576from = where[i];577578myrinfo = graph->vkrinfo+i;579if (myrinfo->inbr == -1) {580myrinfo->inbr = vnbrpoolGetNext(ctrl, xadj[i+1]-xadj[i]+1);581myrinfo->nnbrs = 0;582}583mynbrs = ctrl->vnbrpool + myrinfo->inbr;584585xgain = (myrinfo->nid == 0 && myrinfo->ned > 0 ? vsize[i] : 0);586587//printf("Moving %"PRIDX" from %"PRIDX" to %"PRIDX" [vsize: %"PRIDX"] [xgain: %"PRIDX"]\n",588// i, from, to, vsize[i], xgain);589590/* find the location of 'to' in myrinfo or create it if it is not there */591for (k=0; k<myrinfo->nnbrs; k++) {592if (mynbrs[k].pid == to)593break;594}595596if (k == myrinfo->nnbrs) {597//printf("Missing neighbor\n");598599if (myrinfo->nid > 0)600xgain -= vsize[i];601602/* determine the volume gain resulting from that move */603for (j=xadj[i]; j<xadj[i+1]; j++) {604ii = adjncy[j];605other = where[ii];606orinfo = graph->vkrinfo+ii;607onbrs = ctrl->vnbrpool + orinfo->inbr;608ASSERT(other != to)609610//printf(" %8d %8d %3d\n", (int)ii, (int)vsize[ii], (int)other);611612if (from == other) {613/* Same subdomain vertex: Decrease the gain if 'to' is a new neighbor. */614for (l=0; l<orinfo->nnbrs; l++) {615if (onbrs[l].pid == to)616break;617}618if (l == orinfo->nnbrs)619xgain -= vsize[ii];620}621else {622/* Remote vertex: increase if 'to' is a new subdomain */623for (l=0; l<orinfo->nnbrs; l++) {624if (onbrs[l].pid == to)625break;626}627if (l == orinfo->nnbrs)628xgain -= vsize[ii];629630/* Remote vertex: decrease if i is the only connection to 'from' */631for (l=0; l<orinfo->nnbrs; l++) {632if (onbrs[l].pid == from && onbrs[l].ned == 1) {633xgain += vsize[ii];634break;635}636}637}638}639graph->minvol -= xgain;640graph->mincut -= -myrinfo->nid;641ewgt = myrinfo->nid;642}643else {644graph->minvol -= (xgain + mynbrs[k].gv);645graph->mincut -= mynbrs[k].ned-myrinfo->nid;646ewgt = myrinfo->nid-mynbrs[k].ned;647}648649/* Update where and pwgts */650where[i] = to;651iaxpy(graph->ncon, 1, graph->vwgt+i*graph->ncon, 1, graph->pwgts+to*graph->ncon, 1);652iaxpy(graph->ncon, -1, graph->vwgt+i*graph->ncon, 1, graph->pwgts+from*graph->ncon, 1);653654/* Update subdomain connectivity graph to reflect the move of 'i' */655UpdateEdgeSubDomainGraph(ctrl, from, to, ewgt, NULL);656657/* Update the subdomain connectivity of the adjacent vertices */658for (j=xadj[i]; j<xadj[i+1]; j++) {659me = where[adjncy[j]];660if (me != from && me != to) {661UpdateEdgeSubDomainGraph(ctrl, from, me, -1, NULL);662UpdateEdgeSubDomainGraph(ctrl, to, me, 1, NULL);663}664}665666/* Update the id/ed/gains/bnd of potentially affected nodes */667KWayVolUpdate(ctrl, graph, i, from, to, NULL, NULL, NULL, NULL,668NULL, BNDTYPE_REFINE, vmarker, pmarker, modind);669670/*CheckKWayVolPartitionParams(ctrl, graph);*/671}672ASSERT(ComputeCut(graph, where) == graph->mincut);673ASSERTP(ComputeVolume(graph, where) == graph->minvol,674("%"PRIDX" %"PRIDX"\n", ComputeVolume(graph, where), graph->minvol));675676}677678679/*************************************************************************/680/*! This function computes the subdomain graph. For deubuging purposes. */681/*************************************************************************/682void PrintSubDomainGraph(graph_t *graph, idx_t nparts, idx_t *where)683{684idx_t i, j, k, me, nvtxs, total, max;685idx_t *xadj, *adjncy, *adjwgt, *pmat;686687nvtxs = graph->nvtxs;688xadj = graph->xadj;689adjncy = graph->adjncy;690adjwgt = graph->adjwgt;691692pmat = ismalloc(nparts*nparts, 0, "ComputeSubDomainGraph: pmat");693694for (i=0; i<nvtxs; i++) {695me = where[i];696for (j=xadj[i]; j<xadj[i+1]; j++) {697k = adjncy[j];698if (where[k] != me)699pmat[me*nparts+where[k]] += adjwgt[j];700}701}702703/* printf("Subdomain Info\n"); */704total = max = 0;705for (i=0; i<nparts; i++) {706for (k=0, j=0; j<nparts; j++) {707if (pmat[i*nparts+j] > 0)708k++;709}710total += k;711712if (k > max)713max = k;714/*715printf("%2"PRIDX" -> %2"PRIDX" ", i, k);716for (j=0; j<nparts; j++) {717if (pmat[i*nparts+j] > 0)718printf("[%2"PRIDX" %4"PRIDX"] ", j, pmat[i*nparts+j]);719}720printf("\n");721*/722}723printf("Total adjacent subdomains: %"PRIDX", Max: %"PRIDX"\n", total, max);724725gk_free((void **)&pmat, LTERM);726}727728729730731