Example #1
0
int Zoltan_Set_Key_Param(
ZZ *zz,                         /* Zoltan structure */
const char *name,		/* name of variable */
const char *val,		/* value of variable */
int  idx 			/* index of vector param, -1 if scalar */
)
{
    char *yo = "Zoltan_Set_Key_Param";
    char msg[256];
    int status;			/* return code */
    PARAM_UTYPE result;		/* value returned from Check_Param */
    int index;			/* index returned from Check_Param */
    int tmp;
    int export, import;

    status = Zoltan_Check_Param(name, val, Key_params, &result, &index);

    if (status == 0) {

      switch (index) {

      case 0:  		/* Imbalance_Tol */
        if (result.def) 
            result.fval = ZOLTAN_LB_IMBALANCE_TOL_DEF;
	if (result.fval < 1.0) {
	    sprintf(msg, "Invalid Imbalance_Tol value (%g) "
		"being set to %g.", result.fval, ZOLTAN_LB_IMBALANCE_TOL_DEF);
            ZOLTAN_PRINT_WARN(zz->Proc, yo, msg);
	    result.fval = ZOLTAN_LB_IMBALANCE_TOL_DEF;
	}
        if (idx > zz->Obj_Weight_Dim){
          sprintf(msg, "Imbalance_Tol index %d > Obj_Weight_Dim = %d\n",
            idx, zz->Obj_Weight_Dim);
          ZOLTAN_PRINT_WARN(zz->Proc, yo, msg);
        }
        else if (idx < -1){
          sprintf(msg, "Invalid Imbalance_Tol index %d\n", idx);
          ZOLTAN_PRINT_WARN(zz->Proc, yo, msg);
        }
        else if (idx == -1){
          /* Set all entries to the same value. */
          for (idx=0; idx<zz->LB.Imb_Tol_Len; idx++)
	    zz->LB.Imbalance_Tol[idx] = result.fval;
        }
        else
	  zz->LB.Imbalance_Tol[idx] = result.fval;
	status = 3;		/* Don't add to Params field of ZZ */
        break;

      case 1:		/* Help_Migrate */
        if (result.def)
            result.ival = ZOLTAN_AUTO_MIGRATE_DEF;
	zz->Migrate.Auto_Migrate = result.ival;
	status = 3;		/* Don't add to Params field of ZZ */
        break;

      case 2:		/* Object weight dim.  */
        if (result.def)
            result.ival = ZOLTAN_OBJ_WEIGHT_DEF;
	if (result.ival < 0) {
	    sprintf(msg, "Invalid Obj_Weight_Dim value (%d) "
		"being set to %d.", result.ival, ZOLTAN_OBJ_WEIGHT_DEF);
            ZOLTAN_PRINT_WARN(zz->Proc, yo, msg);
	    result.ival = ZOLTAN_OBJ_WEIGHT_DEF;
	}
	zz->Obj_Weight_Dim = result.ival;
        if (zz->Obj_Weight_Dim > zz->LB.Imb_Tol_Len){
          /* Resize and reallocate Imb_Tol. */
          zz->LB.Imb_Tol_Len += 10;
          zz->LB.Imbalance_Tol = (float *) ZOLTAN_REALLOC(zz->LB.Imbalance_Tol, zz->LB.Imb_Tol_Len * sizeof(float));
        }
	status = 3;		/* Don't add to Params field of ZZ */
        break;

      case 3: 		/* Edge weight dim.  */
      case 13:
        if (result.def)
            result.ival = ZOLTAN_EDGE_WEIGHT_DEF;
	if (result.ival < 0) {
	    sprintf(msg, "Invalid Edge_Weight_Dim value (%d) "
		"being set to %d.", result.ival, ZOLTAN_EDGE_WEIGHT_DEF);
            ZOLTAN_PRINT_WARN(zz->Proc, yo, msg);
	    result.ival = ZOLTAN_EDGE_WEIGHT_DEF;
	}
	zz->Edge_Weight_Dim = result.ival;
	status = 3;		/* Don't add to Params field of ZZ */
        break;

      case 4: 		/* Debug level  */
        if (result.def)
            result.ival = ZOLTAN_DEBUG_LEVEL_DEF;
	if (result.ival < 0) {
	    sprintf(msg, "Invalid Debug_Level value (%d) "
		"being set to %d.", result.ival, ZOLTAN_DEBUG_LEVEL_DEF);
            ZOLTAN_PRINT_WARN(zz->Proc, yo, msg);
	    result.ival = ZOLTAN_DEBUG_LEVEL_DEF;
	}
	zz->Debug_Level = result.ival;
	status = 3;		/* Don't add to Params field of ZZ */
        break;

      case 5: 		/* Debug processor  */
        if (result.def)
            result.ival = ZOLTAN_DEBUG_PROC_DEF;
	if (result.ival < 0 || result.ival > zz->Num_Proc) {
	    sprintf(msg, "Invalid Debug_Processor value (%d) "
		"being set to %d.", result.ival, ZOLTAN_DEBUG_PROC_DEF);
            ZOLTAN_PRINT_WARN(zz->Proc, yo, msg);
	    result.ival = ZOLTAN_DEBUG_PROC_DEF;
	}
	zz->Debug_Proc = result.ival;
	status = 3;		/* Don't add to Params field of ZZ */
        break;
       
      case 6: 		/* Deterministic flag */
        if (result.def)
            result.ival = ZOLTAN_DETERMINISTIC_DEF;
	if (result.ival < 0) {
	    sprintf(msg, "Invalid Deterministic value (%d) "
		"being set to %d.", result.ival, ZOLTAN_DETERMINISTIC_DEF);
            ZOLTAN_PRINT_WARN(zz->Proc, yo, msg);
	    result.ival = ZOLTAN_DETERMINISTIC_DEF;
	}
	zz->Deterministic = result.ival;
	status = 3;		/* Don't add to Params field of ZZ */
        break;

      case 7: 		/* Timer */
	status = Zoltan_Set_Timer_Param(name, val, &tmp);
        zz->Timer = tmp;
        Zoltan_Timer_ChangeFlag(zz->ZTime, zz->Timer);

	if (status==0) status = 3;	/* Don't add to Params field of ZZ */
        break;

      case 8:           /* Num_GID_Entries */
        if (result.def)
            result.ival = ZOLTAN_NUM_ID_ENTRIES_DEF;
        if (result.ival < 1) {
	    sprintf(msg, "Invalid Num_GID_Entries value (%d); "
		"being set to %d.", result.ival, ZOLTAN_NUM_ID_ENTRIES_DEF);
            ZOLTAN_PRINT_WARN(zz->Proc, yo, msg);
            result.ival = ZOLTAN_NUM_ID_ENTRIES_DEF;
        }
        zz->Num_GID = result.ival;
        status = 3;
        break;

      case 9:           /* Num_LID_Entries */
        if (result.def)
            result.ival = ZOLTAN_NUM_ID_ENTRIES_DEF;
        if (result.ival < 0) {
	    sprintf(msg, "Invalid Num_LID_Entries value (%d); "
		"being set to %d.", result.ival, ZOLTAN_NUM_ID_ENTRIES_DEF);
            ZOLTAN_PRINT_WARN(zz->Proc, yo, msg);
            result.ival = ZOLTAN_NUM_ID_ENTRIES_DEF;
        }
        zz->Num_LID = result.ival;
        status = 3;
        break;

      case 10:          /* LB.Return_Lists */
        export = (strstr(result.sval, "EXPORT") != NULL);
        import = (strstr(result.sval, "IMPORT") != NULL);
        if ((export && import) || (strcmp(result.sval, "ALL") == 0)) {
          tmp = ZOLTAN_LB_ALL_LISTS;  /* export AND import lists */
          status = 3;
        }
        else if (import){
          tmp = ZOLTAN_LB_IMPORT_LISTS;  /* import lists */
          status = 3;
        }
        else if (export){
          tmp = ZOLTAN_LB_EXPORT_LISTS;  /* export lists */
          status = 3;
        }
        else if (strstr(result.sval, "PART")!=NULL) {
          /* list of every object's part assignment */
          tmp = ZOLTAN_LB_COMPLETE_EXPORT_LISTS; 
          status = 3;
        }
        else if (strcmp(result.sval, "NONE")==0) {
          tmp = ZOLTAN_LB_NO_LISTS;      /* no lists */
          status = 3;
        }
        else if (strcmp(result.sval, "CANDIDATE_LISTS")==0) {
          tmp = ZOLTAN_LB_CANDIDATE_LISTS;   /* candidates needed in matching */
          status = 3;
        }
        else{
          tmp = ZOLTAN_LB_RETURN_LISTS_DEF;
          sprintf(msg, "Unknown return_lists option %s.", result.sval);
          ZOLTAN_PRINT_WARN(zz->Proc, yo, msg);
          status = 2; /* Illegal parameter */
        }
	zz->LB.Return_Lists = tmp;
        break;

      case 11:          /* LB_Method */
        if (result.def) 
          strcpy(result.sval, "RCB");
        status = Zoltan_LB_Set_LB_Method(zz,result.sval);
        if (status == ZOLTAN_OK)
          status = 3;
        break;

      case 12: 		/* Tflops Special flag */
        if (result.def)
            result.ival = ZOLTAN_TFLOPS_SPECIAL_DEF;
	if (result.ival < 0) {
	    sprintf(msg, "Invalid Tflops Special value (%d) "
		"being set to %d.", result.ival, ZOLTAN_TFLOPS_SPECIAL_DEF);
            ZOLTAN_PRINT_WARN(zz->Proc, yo, msg);
	    result.ival = ZOLTAN_TFLOPS_SPECIAL_DEF;
	}
	zz->Tflops_Special = result.ival;
	status = 3;		/* Don't add to Params field of ZZ */
        break;

      case 14:          /* Num_Global_Parts */
      case 15:
        if (result.def)
            result.ival = zz->Num_Proc;
        if (result.ival < 1) {
	    sprintf(msg, "Invalid Num_Global_Parts value (%d); "
		"being set to %d.", result.ival,zz->Num_Proc);
            ZOLTAN_PRINT_WARN(zz->Proc, yo, msg);
            result.ival = zz->Num_Proc;
        }
        zz->LB.Num_Global_Parts_Param = result.ival;
        status = 3;
        break;

      case 16:          /* Num_Local_Parts */
      case 17:
        if (result.def)
            result.ival = -1;
        if (result.ival < -1) {
	    sprintf(msg, "Invalid Num_Local_Parts value (%d); "
		"being set to %d.", result.ival,-1);
            ZOLTAN_PRINT_WARN(zz->Proc, yo, msg);
            result.ival = -1;
        }
        zz->LB.Num_Local_Parts_Param = result.ival;
        status = 3;
        break;

      case 18:		/* Migrate_Only_Proc_Changes */
        if (result.def)
            result.ival = ZOLTAN_MIGRATE_ONLY_PROC_CHANGES_DEF;
	zz->Migrate.Only_Proc_Changes = result.ival;
	status = 3;		/* Don't add to Params field of ZZ */
        break;

      case 19:		/* LB.Remap */
        if (result.def)
            result.ival = 0;
	zz->LB.Remap_Flag = result.ival;
	status = 3;		/* Don't add to Params field of ZZ */
        break;

      case 20:          /* Seed */
        if (result.def)
            result.ival = Zoltan_Seed();
        zz->Seed = result.ival;
        Zoltan_Srand(result.ival, NULL);
        status = 3;
        break;

      case 21:          /* LB_APPROACH */
        if (result.def)
          strcpy(result.sval, ZOLTAN_LB_APPROACH_DEF);
        strcpy(zz->LB.Approach, result.sval);
        status = 3;
        break;

      }  /* end switch (index) */
Example #2
0
int Zoltan_Random(
  ZZ *zz,                       /* The Zoltan structure.                     */
  float *part_sizes,            /* Input:  Array of size zz->LB.Num_Global_Parts
                                   * zz->Obj_Weight_Dim
                                   containing the percentage of work to be
                                   assigned to each partition.               */
  int *num_import,              /* Return -1. Random uses only export lists. */
  ZOLTAN_ID_PTR *import_global_ids, /* Not used. */
  ZOLTAN_ID_PTR *import_local_ids,  /* Not used. */
  int **import_procs,           /* Not used. */
  int **import_to_part,         /* Not used. */
  int *num_export,              /* Output: Number of objects to export. */
  ZOLTAN_ID_PTR *export_global_ids, /* Output: GIDs to export. */
  ZOLTAN_ID_PTR *export_local_ids,  /* Output: LIDs to export. */
  int **export_procs,           /* Output: Processsors to export to. */
  int **export_to_part          /* Output: Partitions to export to. */
)
{
  int ierr = ZOLTAN_OK;
  int i, count, num_obj;
  int max_export;
  double rand_frac = 1.0;       /* Default is to move all objects. */
  ZOLTAN_ID_PTR global_ids = NULL;
  ZOLTAN_ID_PTR local_ids = NULL; 
  int *parts = NULL;
  float *dummy = NULL;
  static char *yo = "Zoltan_Random";
  static int first_time = 1;

  ZOLTAN_TRACE_ENTER(zz, yo);

  /* Synchronize the random number generator. 
   * This synchronization is needed only for sanity in our nightly testing. 
   * If some other operation (eg., Zoltan_LB_Eval) changes the status of 
   * the random number generator, the answers here will change.  They won't
   * be wrong, but they will be different from our accepted answers.
   */
  if (first_time) {
    Zoltan_Srand(zz->Seed, NULL);
    Zoltan_Rand(NULL);
    first_time=0;
  }

  /* No import lists computed. */
  *num_import = -1;

  /* Get parameter values. */
  Zoltan_Bind_Param(Random_params, "RANDOM_MOVE_FRACTION", (void *) &rand_frac);
  Zoltan_Assign_Param_Vals(zz->Params, Random_params, zz->Debug_Level, 
                           zz->Proc, zz->Debug_Proc);

  /* Get list of local objects. */
  ierr = Zoltan_Get_Obj_List(zz, &num_obj, &global_ids, &local_ids, 0,
                             &dummy, &parts);

  /* Bound number of objects to export. */
  max_export = 1.5*rand_frac*num_obj;

  /* Allocate export lists. */
  *export_global_ids = *export_local_ids = NULL;
  *export_procs = *export_to_part = NULL;
  if (max_export > 0) {
    if (!Zoltan_Special_Malloc(zz, (void **)export_global_ids, max_export,
                               ZOLTAN_SPECIAL_MALLOC_GID)
     || !Zoltan_Special_Malloc(zz, (void **)export_local_ids, max_export,
                               ZOLTAN_SPECIAL_MALLOC_LID)
     || !Zoltan_Special_Malloc(zz, (void **)export_procs, max_export,
                               ZOLTAN_SPECIAL_MALLOC_INT)
     || !Zoltan_Special_Malloc(zz, (void **)export_to_part, max_export,
                               ZOLTAN_SPECIAL_MALLOC_INT)) {
      ZOLTAN_PRINT_ERROR(zz->Proc, yo, "Memory error.");
      ierr = ZOLTAN_MEMERR;
      goto End;
    }
  }

  /* Randomly assign ids to procs. */
  count=0;
  for (i=0; i<num_obj; i++){
    /* Randomly select some objects to move (export) */
    if ((count<max_export) && (Zoltan_Rand(NULL)<rand_frac*ZOLTAN_RAND_MAX)){
      /* export_global_ids[count] = global_ids[i]; */
      ZOLTAN_SET_GID(zz, &((*export_global_ids)[count*zz->Num_GID]),
                     &global_ids[i*zz->Num_GID]);
      if (local_ids)
        /* export_local_ids[count] = local_ids[i]; */
        ZOLTAN_SET_LID(zz, &((*export_local_ids)[count*zz->Num_LID]),
                       &local_ids[i*zz->Num_LID]);
      /* Randomly pick new partition number. */
      (*export_to_part)[count] = Zoltan_Rand_InRange(NULL, zz->LB.Num_Global_Parts);
      /* Processor number is derived from partition number. */
      (*export_procs)[count] = Zoltan_LB_Part_To_Proc(zz, 
                     (*export_to_part)[count], &global_ids[i*zz->Num_GID]);

      /* printf("Debug: Export gid %u to part %d and proc %d.\n", (*export_global_ids)[count], (*export_to_part)[count], (*export_procs)[count]); */

      ++count;
    }
  }
  (*num_export) = count;

End:
  /* Free local memory, but not export lists. */
  ZOLTAN_FREE(&global_ids);
  ZOLTAN_FREE(&local_ids);
  ZOLTAN_FREE(&parts);

  ZOLTAN_TRACE_EXIT(zz, yo);
  return ierr;
}
Example #3
0
int Zoltan_PHG_CoarsePartition(
  ZZ *zz, 
  HGraph *phg,         /* Input:  coarse hypergraph -- distributed! */
  int numPart,         /* Input:  number of partitions to generate. */
  float *part_sizes,   /* Input:  array of size numPart listing target sizes
                                  (% of work) for the partitions */
  Partition part,      /* Input:  array of initial partition assignments.
                          Output: array of computed partition assignments.   */
  PHGPartParams *hgp   /* Input:  parameters to use.  */
)
{
/* 
 * Zoltan_PHG_CoarsePartition computes a partitioning of a hypergraph.
 * Typically, this routine is called at the bottom level in a
 * multilevel scheme (V-cycle).
 * It gathers the distributed hypergraph to each processor and computes
 * a decomposition of the serial hypergraph.  
 * It computes a different partition on each processor
 * using different random numbers (and possibly also
 * different algorithms) and selects the best.
 */
char *yo = "Zoltan_PHG_CoarsePartition";
int ierr = ZOLTAN_OK;
int i, si, j;
static PHGComm scomm;          /* Serial communicator info */
static int first_time = 1;
HGraph *shg = NULL;            /* Serial hypergraph gathered from phg */
int *spart = NULL;             /* Partition vectors for shg. */
int *new_part = NULL;          /* Ptr to new partition vector. */
float *bestvals = NULL;        /* Best cut values found so far */
int worst, new_cand;
float bal, cut, worst_cut;
int fine_timing = (hgp->use_timers > 2);
struct phg_timer_indices *timer = Zoltan_PHG_LB_Data_timers(zz);
int local_coarse_part = hgp->LocalCoarsePartition;

/* Number of iterations to try coarse partitioning on each proc. */
/* 10 when p=1, and 1 when p is large. */
const int num_coarse_iter = 1 + 9/zz->Num_Proc; 


  ZOLTAN_TRACE_ENTER(zz, yo);

  if (fine_timing) {
    if (timer->cpgather < 0)
      timer->cpgather = Zoltan_Timer_Init(zz->ZTime, 1, "CP Gather");
    if (timer->cprefine < 0)
      timer->cprefine = Zoltan_Timer_Init(zz->ZTime, 0, "CP Refine");
    if (timer->cpart < 0)
      timer->cpart = Zoltan_Timer_Init(zz->ZTime, 0, "CP Part");

    ZOLTAN_TIMER_START(zz->ZTime, timer->cpart, phg->comm->Communicator);
  }


  /* Force LocalCoarsePartition if large global graph */
#define LARGE_GRAPH_VTX   64000
#define LARGE_GRAPH_PINS 256000
  if (phg->dist_x[phg->comm->nProc_x] > LARGE_GRAPH_VTX){
    /* TODO: || (global_nPins > LARGE_GRAPH_PINS) */
    local_coarse_part = 1;
  }

  /* take care of all special cases first */

  if (!strcasecmp(hgp->coarsepartition_str, "no")
      || !strcasecmp(hgp->coarsepartition_str, "none")) {
    /* Do no coarse partitioning. */
    /* Do a sanity test and  mapping to parts [0,...,numPart-1] */
    int first = 1;
    PHGComm *hgc=phg->comm;    

    Zoltan_Srand_Sync (Zoltan_Rand(NULL), &(hgc->RNGState_col), hgc->col_comm);
    if (hgp->UsePrefPart) {
        for (i = 0; i < phg->nVtx; i++) {
            /* Impose fixed vertex/preferred part constraints. */
            if (phg->pref_part[i] < 0) { /* Free vertex in fixedvertex partitioning or repart */
                /* randomly assigned to a part */
                part[i] = Zoltan_Rand_InRange(&(hgc->RNGState_col), numPart);
            } else {
                if (phg->bisec_split < 0)
                    /* direct k-way, use part numbers directly */
                    part[i] = phg->pref_part[i];
                else
                    /* recursive bisection, map to 0-1 part numbers */
                    part[i] = (phg->pref_part[i] < phg->bisec_split ? 0 : 1);
            }            
        }
    } else {
        for (i = 0; i < phg->nVtx; i++) {
            if (part[i] >= numPart || part[i]<0) {
                if (first) {
                    ZOLTAN_PRINT_WARN(zz->Proc, yo, "Initial part number > numParts.");
                    first = 0;
                    ierr = ZOLTAN_WARN;
                }
                part[i] = ((part[i]<0) ? -part[i] : part[i]) % numPart;
            }        
        }
    }
  }
  else if (numPart == 1) {            
    /* everything goes in the one partition */
    for (i =  0; i < phg->nVtx; i++)
      part[i] = 0;
  }
  else if (!hgp->UsePrefPart && numPart >= phg->dist_x[phg->comm->nProc_x]) { 
    /* more partitions than vertices, trivial answer */
    for (i = 0; i < phg->nVtx; i++)
      part[i] = phg->dist_x[phg->comm->myProc_x]+i;
  }
  else if (local_coarse_part) {
    /* Apply local partitioner to each column */
    ierr = local_coarse_partitioner(zz, phg, numPart, part_sizes, part, hgp,
                                    hgp->CoarsePartition);
  }
  else {
    /* Normal case:
     * Gather distributed HG to each processor;
     * compute different partitioning on each processor;
     * select the "best" result.
     */
    ZOLTAN_PHG_COARSEPARTITION_FN *CoarsePartition;

    /* Select different coarse partitioners for processors here. */

    CoarsePartition = hgp->CoarsePartition;
    if (CoarsePartition == NULL) { /* auto */
      /* Select a coarse partitioner from the array of coarse partitioners */
      CoarsePartition = CoarsePartitionFns[phg->comm->myProc % 
                                           NUM_COARSEPARTITION_FNS];
    }


    if (phg->comm->nProc == 1) {
      /* Serial and parallel hgraph are the same. */
      shg = phg;
    }
    else {
      /* Set up a serial communication struct for gathered HG */

      if (first_time) {
        scomm.nProc_x = scomm.nProc_y = 1;
        scomm.myProc_x = scomm.myProc_y = 0;
        scomm.Communicator = MPI_COMM_SELF;
        scomm.row_comm = MPI_COMM_SELF;
        scomm.col_comm = MPI_COMM_SELF;
        scomm.myProc = 0;
        scomm.nProc = 1;
        first_time = 0;
      }
      scomm.RNGState = Zoltan_Rand(NULL);
      scomm.RNGState_row = Zoltan_Rand(NULL);
      scomm.RNGState_col = Zoltan_Rand(NULL);
      scomm.zz = zz;

      /* 
       * Gather parallel hypergraph phg to each processor, creating
       * serial hypergraph shg.
       */
      if (fine_timing) {
        ZOLTAN_TIMER_STOP(zz->ZTime, timer->cpart, phg->comm->Communicator);
        ZOLTAN_TIMER_START(zz->ZTime, timer->cpgather, phg->comm->Communicator);
      }

      ierr = Zoltan_PHG_Gather_To_All_Procs(zz, phg, hgp, &scomm, &shg);
      if (ierr < 0) {
        ZOLTAN_PRINT_ERROR(zz->Proc, yo, "Error returned from gather.");
        goto End;
      }

      if (fine_timing) {
        ZOLTAN_TIMER_STOP(zz->ZTime, timer->cpgather, phg->comm->Communicator);
        ZOLTAN_TIMER_START(zz->ZTime, timer->cpart, phg->comm->Communicator);
      }

    }

    /* 
     * Allocate partition array spart for the serial hypergraph shg
     * and partition shg.
     */
    spart = (int *) ZOLTAN_CALLOC(shg->nVtx * (NUM_PART_KEEP+1),
                                    sizeof(int));
    bestvals = (float *) ZOLTAN_MALLOC((NUM_PART_KEEP+1)*sizeof(int)); 
    if ((!spart) || (!bestvals)) {
      ZOLTAN_PRINT_ERROR(zz->Proc, yo, "Out of memory.");
      ierr = ZOLTAN_MEMERR;
      goto End;
    }
    
    /* Compute several coarse partitionings. */
    /* Keep the NUM_PART_KEEP best ones around. */
    /* Currently, only the best one is used. */

    /* Set RNG so different procs compute different parts. */
    Zoltan_Srand(Zoltan_Rand(NULL) + zz->Proc, NULL);

    new_cand = 0;
    new_part = spart;

    for (i=0; i< num_coarse_iter; i++){
      int savefmlooplimit=hgp->fm_loop_limit;
        
      /* Overwrite worst partition with new candidate. */
      ierr = CoarsePartition(zz, shg, numPart, part_sizes, 
               new_part, hgp);
      if (ierr < 0) {
        ZOLTAN_PRINT_ERROR(zz->Proc, yo, 
                         "Error returned from CoarsePartition.");
        goto End;
      }

      /* time refinement step in coarse partitioner */
      if (fine_timing) {
        ZOLTAN_TIMER_STOP(zz->ZTime, timer->cpart, phg->comm->Communicator);
        ZOLTAN_TIMER_START(zz->ZTime, timer->cprefine, phg->comm->Communicator);
      }

      /* UVCUVC: Refine new candidate: only one pass is enough. */
      hgp->fm_loop_limit = 1;
      Zoltan_PHG_Refinement(zz, shg, numPart, part_sizes, new_part, hgp);
      hgp->fm_loop_limit = savefmlooplimit;
      
      /* stop refinement timer */
      if (fine_timing) {
        ZOLTAN_TIMER_STOP(zz->ZTime, timer->cprefine, phg->comm->Communicator);
        ZOLTAN_TIMER_START(zz->ZTime, timer->cpart, phg->comm->Communicator);
      }

      /* Decide if candidate is in the top tier or not. */
      /* Our objective is a combination of cuts and balance */

      bal = Zoltan_PHG_Compute_Balance(zz, shg, part_sizes, 0, 
                                       numPart, new_part); 
      cut = Zoltan_PHG_Compute_ConCut(shg->comm, shg, new_part, numPart, &ierr);
      
      /* Use ratio-cut as our objective. There are many other options! */
      bestvals[new_cand] = cut/(MAX(2.-bal, 0.0001)); /* avoid divide-by-0 */

      if (ierr < 0) {
        ZOLTAN_PRINT_ERROR(zz->Proc, yo, 
                         "Error returned from Zoltan_PHG_Compute_ConCut.");
        goto End;
      }
      if (i<NUM_PART_KEEP)
        new_cand = i+1;
      else {
        /* find worst partition vector, to overwrite it */
        /* future optimization: keep bestvals sorted */
        worst = 0;
        worst_cut = bestvals[0];
        for (j=1; j<NUM_PART_KEEP+1; j++){
          if (worst_cut < bestvals[j]){
            worst_cut = bestvals[j];
            worst = j;
          }
        }
        new_cand = worst;
      }
      new_part = spart+new_cand*(shg->nVtx);
    }
    /* Copy last partition vector such that all the best ones
       are contiguous starting at spart.                     */
    for (i=0; i<shg->nVtx; i++){
      new_part[i] = spart[NUM_PART_KEEP*(shg->nVtx)+i];
    }
    /* Also update bestvals */
    bestvals[new_cand] = bestvals[NUM_PART_KEEP];

    /* Evaluate and select the best. */
    /* For now, only pick the best one, in the future we pick the k best. */

    ierr = pick_best(zz, hgp, phg->comm, shg, numPart, 
              MIN(NUM_PART_KEEP, num_coarse_iter), spart,
              bestvals);
    if (ierr < 0) {
      ZOLTAN_PRINT_ERROR(zz->Proc, yo, 
                        "Error returned from pick_best.");
      goto End;
    }
  
    if (phg->comm->nProc > 1) {
      /* Map gathered partition back to 2D distribution */
      for (i = 0; i < phg->nVtx; i++) {
        /* KDDKDD  Assume vertices in serial HG are ordered by GNO of phg */
        si = VTX_LNO_TO_GNO(phg, i);
        part[i] = spart[si];
      }

      Zoltan_HG_HGraph_Free(shg);
      ZOLTAN_FREE(&shg);
    } 
    else { /* single processor */
      for (i = 0; i < phg->nVtx; i++)
        part[i] = spart[i];
    }
    ZOLTAN_FREE(&spart);
    ZOLTAN_FREE(&bestvals);
  }
  
End:
  if (fine_timing) 
    ZOLTAN_TIMER_STOP(zz->ZTime, timer->cpart, phg->comm->Communicator);

  ZOLTAN_TRACE_EXIT(zz, yo);
  return ierr;
}