Exemplo n.º 1
0
//-----------------------------------------------------------------------------
std::vector<std::size_t> GraphOrdering::compute_local_reordering_map()
{
  // Initialise Zoltan
  float version;
  int argc = 0;
  char** argv = NULL;
  Zoltan_Initialize(argc, argv, &version);

  // Create Zoltan object
  Zoltan zoltan;

  // Set parameters
  //zoltan.Set_Param( "ORDER_METHOD", "METIS");
  zoltan.Set_Param( "ORDER_METHOD", "SCOTCH");
  zoltan.Set_Param( "NUM_GID_ENTRIES", "1");  // global ID is 1 integer
  zoltan.Set_Param( "NUM_LID_ENTRIES", "1");  // local ID is 1 integer
  zoltan.Set_Param( "OBJ_WEIGHT_DIM", "0");   // omit object weights

  // Set call-back functions
  zoltan.Set_Num_Obj_Fn(GraphOrdering::get_number_of_objects, this);
  zoltan.Set_Obj_List_Fn(GraphOrdering::get_object_list, this);
  zoltan.Set_Num_Edges_Multi_Fn(GraphOrdering::get_number_edges, this);
  zoltan.Set_Edge_List_Multi_Fn(GraphOrdering::get_all_edges, this);

  // Create array for global ids that should be re-ordered
  std::vector<ZOLTAN_ID_TYPE> global_ids(num_global_objects());
  for (std::size_t i = 0; i < global_ids.size(); ++i)
    global_ids[i] = i;

  // Create array for re-ordered vertices
  std::vector<ZOLTAN_ID_TYPE> new_id(num_global_objects());

  // Compute re-ordering
  int rc = zoltan.Order(1, num_global_objects(), &global_ids[0], &new_id[0]);

  // Check for errors
  if (rc != ZOLTAN_OK)
  {
    dolfin_error("GraphOrdering.cpp",
                 "compute matrix re-ordering",
                 "Zoltan partitioning failed");
  }

  // Copy re-ordering into a vector (in case Zoltan uses something other than std::size_t)
  std::vector<std::size_t> map(new_id.begin(), new_id.end());

  return map;
}
Exemplo n.º 2
0
int migrate_elements(
  int Proc,
  MESH_INFO_PTR mesh,
  Zoltan &zz,
  int num_gid_entries, 
  int num_lid_entries,
  int num_imp,
  ZOLTAN_ID_PTR imp_gids,
  ZOLTAN_ID_PTR imp_lids,
  int *imp_procs,
  int *imp_to_part,
  int num_exp,
  ZOLTAN_ID_PTR exp_gids,
  ZOLTAN_ID_PTR exp_lids,
  int *exp_procs,
  int *exp_to_part)
{
/* Local declarations. */
const char *yo = "migrate_elements";

/***************************** BEGIN EXECUTION ******************************/
  DEBUG_TRACE_START(Proc, yo);

  /*
   * register migration functions
   */
  if (!Test.Null_Lists) {
    /* If not passing NULL lists, let Help_Migrate call the
     * pre-processing and post-processing routines.
     */
    if (zz.Set_Pre_Migrate_PP_Fn(migrate_pre_process,
                      (void *) mesh) == ZOLTAN_FATAL) {
      Gen_Error(0, "fatal:  error returned from Set_Pre_Migrate_PP_Fn()\n");
      return 0;
    }

    if (zz.Set_Post_Migrate_PP_Fn(migrate_post_process,
                      (void *) mesh) == ZOLTAN_FATAL) {
      Gen_Error(0, "fatal:  error returned from Set_Post_Migrate_PP_Fn()\n");
      return 0;
    }
  }

  if (Test.Multi_Callbacks) {
    if (zz.Set_Obj_Size_Multi_Fn(migrate_elem_size_multi,
                      (void *) mesh) == ZOLTAN_FATAL) {
      Gen_Error(0, "fatal:  error returned from Set_Obj_Size_Multi_Fn()\n");
      return 0;
    }

    if (zz.Set_Pack_Obj_Multi_Fn(migrate_pack_elem_multi,
                      (void *) mesh) == ZOLTAN_FATAL) {
      Gen_Error(0, "fatal:  error returned from Set_Pack_Obj_Multi_Fn()\n");
      return 0;
    }
  
    if (zz.Set_Unpack_Obj_Multi_Fn(migrate_unpack_elem_multi,
                      (void *) mesh) == ZOLTAN_FATAL) {
      Gen_Error(0, "fatal:  error returned from Set_Unpack_Obj_Multi_Fn()\n");
      return 0;
    }
  }
  else {
    if (zz.Set_Obj_Size_Fn(migrate_elem_size,
                      (void *) mesh) == ZOLTAN_FATAL) {
      Gen_Error(0, "fatal:  error returned from Set_Obj_Size_Fn()\n");
      return 0;
    }

    if (zz.Set_Pack_Obj_Fn(migrate_pack_elem,
                      (void *) mesh) == ZOLTAN_FATAL) {
      Gen_Error(0, "fatal:  error returned from Set_Pack_Obj_Fn()\n");
      return 0;
    }

    if (zz.Set_Unpack_Obj_Fn(migrate_unpack_elem,
                      (void *) mesh) == ZOLTAN_FATAL) {
      Gen_Error(0, "fatal:  error returned from Set_Unpack_Obj_Fn()\n");
      return 0;
    }
  }


  if (Test.Null_Lists == NONE) {
    if (zz.Migrate(num_imp, imp_gids, imp_lids, imp_procs, imp_to_part,
                   num_exp, exp_gids, exp_lids, exp_procs, exp_to_part)
                   == ZOLTAN_FATAL) {
      Gen_Error(0, "fatal:  error returned from Migrate()\n");
      return 0;
    }
  }
  else {
    /* Call zz.Help_Migrate with empty import lists. */
    /* Have to "manually" call migrate_pre_process and migrate_post_process. */
    int ierr = 0;
    migrate_pre_process((void *) mesh, 1, 1,
                        num_imp, imp_gids, imp_lids, imp_procs, imp_to_part,
                        num_exp, exp_gids, exp_lids, exp_procs, exp_to_part,
                        &ierr);
    if (Test.Null_Lists == IMPORT_LISTS) {
      if (zz.Migrate(-1, NULL, NULL, NULL, NULL,
                     num_exp, exp_gids, exp_lids, exp_procs, exp_to_part)
                     == ZOLTAN_FATAL) {
        Gen_Error(0, "fatal:  error returned from Migrate()\n");
        return 0;
      }
    }
    else {
      if (zz.Migrate(num_imp, imp_gids, imp_lids, imp_procs, imp_to_part,
                    -1, NULL, NULL, NULL, NULL)
                    == ZOLTAN_FATAL) {
        Gen_Error(0, "fatal:  error returned from Migrate()\n");
        return 0;
      }
    }
    migrate_post_process((void *) mesh, 1, 1,  
                         num_imp, imp_gids, imp_lids, imp_procs, imp_to_part,
                         num_exp, exp_gids, exp_lids, exp_procs, exp_to_part,
                         &ierr);
  }

  DEBUG_TRACE_END(Proc, yo);
  return 1;

}
Exemplo n.º 3
0
//-----------------------------------------------------------------------------
void
ZoltanPartition::compute_partition_phg(const MPI_Comm mpi_comm,
                                       std::vector<std::size_t>& cell_partition,
                                       const LocalMeshData& mesh_data)
{
  Timer timer0("Partition graph (calling Zoltan PHG)");

  // Create data structures to hold graph
  std::vector<std::set<std::size_t>> local_graph;
  std::set<std::size_t> ghost_vertices;

  // Compute local dual graph
  GraphBuilder::compute_dual_graph(mpi_comm, mesh_data, local_graph,
                                   ghost_vertices);

  // Initialise Zoltan
  float version;
  int argc = 0;
  char** argv = NULL;
  Zoltan_Initialize(argc, argv, &version);

  // Create Zoltan object
  Zoltan zoltan;

  // Set Zoltan parameters
  zoltan.Set_Param("NUM_GID_ENTRIES", "1");
  zoltan.Set_Param("NUM_LID_ENTRIES", "0");

  zoltan.Set_Param("NUM_GLOBAL_PARTS",
                   std::to_string(MPI::size(mpi_comm)));

  zoltan.Set_Param("NUM_LOCAL_PARTS", "1");
  zoltan.Set_Param("LB_METHOD", "GRAPH");

  // Get partition method: 'PARTITION', 'REPARTITION' or 'REFINE'
  std::string lb_approach = parameters["partitioning_approach"];
  zoltan.Set_Param("LB_APPROACH", lb_approach.c_str());

  // Repartitioning weighting
  double phg_repart_multiplier = parameters["Zoltan_PHG_REPART_MULTIPLIER"];
  zoltan.Set_Param("PHG_REPART_MULTIPLIER",
                   std::to_string(phg_repart_multiplier));


  // Set call-back functions
  void *mesh_data_ptr = (void *)&mesh_data;
  zoltan.Set_Num_Obj_Fn(get_number_of_objects, mesh_data_ptr);
  zoltan.Set_Obj_List_Fn(get_object_list, mesh_data_ptr);

  void *graph_data_ptr = (void *)&local_graph;
  zoltan.Set_Num_Edges_Multi_Fn(get_number_edges, graph_data_ptr);
  zoltan.Set_Edge_List_Multi_Fn(get_all_edges, graph_data_ptr);

  // Call Zoltan function to compute partitions
  int changes = 0;
  int num_gids = 0;
  int num_lids = 0;
  int num_import, num_export;
  ZOLTAN_ID_PTR import_lids;
  ZOLTAN_ID_PTR export_lids;
  ZOLTAN_ID_PTR import_gids;
  ZOLTAN_ID_PTR export_gids;
  int* import_procs;
  int* export_procs;
  int* import_parts;
  int* export_parts;

  int rc = zoltan.LB_Partition(changes, num_gids, num_lids,
           num_import, import_gids, import_lids, import_procs, import_parts,
           num_export, export_gids, export_lids, export_procs, export_parts);

  dolfin_assert(num_gids == 1);
  dolfin_assert(num_lids == 0);

  std::size_t proc = MPI::rank(mpi_comm);

  if (rc != ZOLTAN_OK)
  {
    dolfin_error("ZoltanPartition.cpp",
                 "partition mesh using Zoltan",
                 "Call to Zoltan failed");
  }

  cell_partition.assign(local_graph.size(), proc);

  std::size_t offset = MPI::global_offset(mpi_comm, local_graph.size(), true);
  for(int i = 0; i < num_export; ++i)
  {
    const std::size_t idx = export_gids[i] - offset;
    cell_partition[idx] = (std::size_t)export_procs[i];
  }

  // Free data structures allocated by Zoltan::LB_Partition
  zoltan.LB_Free_Part(&import_gids, &import_lids, &import_procs, &import_parts);
  zoltan.LB_Free_Part(&export_gids, &export_lids, &export_procs, &export_parts);
}
Exemplo n.º 4
0
//-----------------------------------------------------------------------------
void
ZoltanPartition::compute_partition_rcb(const MPI_Comm mpi_comm,
                                       std::vector<std::size_t>& cell_partition,
                                       const LocalMeshData& mesh_data)
{
  Timer timer0("Partition graph (calling Zoltan RCB)");

  // Get number of local graph vertices
  const std::size_t nlocal = mesh_data.cell_vertices.shape()[0];

  // Initialise Zoltan
  float version;
  int argc = 0;
  char** argv = NULL;
  Zoltan_Initialize(argc, argv, &version);

  // Create Zoltan object
  Zoltan zoltan;

  // Set Zoltan parameters
  zoltan.Set_Param("NUM_GID_ENTRIES", "1");
  zoltan.Set_Param("NUM_LID_ENTRIES", "0");

  zoltan.Set_Param("NUM_GLOBAL_PARTS",
                   std::to_string(MPI::size(mpi_comm)));

  zoltan.Set_Param("NUM_LOCAL_PARTS", "1");
  zoltan.Set_Param("LB_METHOD", "RCB");

  // Set call-back functions
  void *mesh_data_ptr = (void *)&mesh_data;

  zoltan.Set_Num_Obj_Fn(get_number_of_objects, mesh_data_ptr);
  zoltan.Set_Obj_List_Fn(get_object_list, mesh_data_ptr);
  zoltan.Set_Num_Geom_Fn(get_geom, mesh_data_ptr);
  zoltan.Set_Geom_Multi_Fn(get_all_geom, mesh_data_ptr);

  // Call Zoltan function to compute partitions
  int changes = 0;
  int num_gids = 0;
  int num_lids = 0;
  int num_import, num_export;
  ZOLTAN_ID_PTR import_lids;
  ZOLTAN_ID_PTR export_lids;
  ZOLTAN_ID_PTR import_gids;
  ZOLTAN_ID_PTR export_gids;
  int* import_procs;
  int* export_procs;
  int* import_parts;
  int* export_parts;

  int rc = zoltan.LB_Partition(changes, num_gids, num_lids,
           num_import, import_gids, import_lids, import_procs, import_parts,
           num_export, export_gids, export_lids, export_procs, export_parts);

  dolfin_assert(num_gids == 1);
  dolfin_assert(num_lids == 0);


  // Get my process rank
  const std::size_t my_rank = MPI::rank(mpi_comm);

  if (rc != ZOLTAN_OK)
  {
    dolfin_error("ZoltanPartition.cpp",
                 "partition mesh using Zoltan",
                 "Call to Zoltan failed");
  }

  // Assign all nodes to this processor
  cell_partition.assign(nlocal, my_rank);
  std::size_t offset = MPI::global_offset(mpi_comm, nlocal, true);

  // Change nodes to be exported to the appropriate remote processor
  for(int i = 0; i < num_export; ++i)
  {
    const std::size_t idx = export_gids[i] - offset;
    cell_partition[idx] = export_procs[i];
  }

  // Free data structures allocated by Zoltan::LB_Partition
  zoltan.LB_Free_Part(&import_gids, &import_lids, &import_procs, &import_parts);
  zoltan.LB_Free_Part(&export_gids, &export_lids, &export_procs, &export_parts);
}
Exemplo n.º 5
0
int main(int argc, char *argv[])
{
/* Local declarations. */
  const char  *cmd_file;
  char   cmesg[256]; /* for error messages */
  int    error, gerror;
  int    print_output = 1;

/***************************** BEGIN EXECUTION ******************************/

  /* Initialize MPI */

  // We must use the C bindings to MPI because the C++ bindings are
  // are not available or not complete on some of our platforms.
 
  MPI_Init(&argc, &argv);

  /* get some machine information */
  int Proc = 0, Num_Proc = 0;
  MPI_Comm_rank(MPI_COMM_WORLD, &Proc);
  MPI_Comm_size(MPI_COMM_WORLD, &Num_Proc);

  /* Initialize flags */
  Test.DDirectory = 0;
  Test.Local_Parts = 0;
  Test.Drops = 0;
  Test.RCB_Box = 0;
  Test.Multi_Callbacks = 0;
  Test.Gen_Files = 0;
  Test.Fixed_Objects = 0;
  Test.Null_Lists = NONE;

  Output.Text = 1;
  Output.Gnuplot = 0;
  Output.Nemesis = 0;
  Output.Plot_Partition = 0;
  Output.Mesh_Info_File = 0;

  /* Interpret the command line */
  switch(argc)
  {
  case 1:
    cmd_file = "zdrive.inp";
    break;

  case 2:
    cmd_file = argv[1];
    break;

  default:
    cerr << "MAIN: ERROR in command line " ;

    if (Proc == 0)
    {
      cerr << "usage:" << endl;
      cerr << "\t" << DRIVER_NAME << " [command file]";
    }
    cerr << endl;

    exit(1);
    break;
  }

  /* Initialize Zoltan 
   *  Not part of C++ interface at this time. (C++ wishlist)
   */

  float version;
  
  if ((error = Zoltan_Initialize(argc, argv, &version)) != ZOLTAN_OK) {
    sprintf(cmesg, "fatal: Zoltan_Initialize returned error code, %d", error);
    Gen_Error(0, cmesg);
    error_report(Proc);
    MPI_Finalize();
    return 1;
  }

  /*
   *  Create a Zoltan structure.  
   *  No exception handling at this time. (C++ wishlist)
   *  We must dynamically create the object so that we can delete it
   *  before MPI_Finalize().  (If zz is created on the stack, it will
   *  be deleted atexit, after MPI_Finalize().  Zoltan_Destroy calls
   *  MPI functions.)
   */

  Zoltan *zz = NULL;
  zz = new Zoltan();

  /* initialize some variables */
  MESH_INFO  mesh;
  initialize_mesh(&mesh);

  PARIO_INFO pio_info;
  pio_info.dsk_list_cnt		= -1;
  pio_info.num_dsk_ctrlrs	= -1;
  pio_info.pdsk_add_fact	= -1;
  pio_info.zeros		= -1;
  pio_info.file_type		= -1;
  pio_info.init_dist_type	= -1;
  pio_info.init_size		= ZOLTAN_ID_INVALID;
  pio_info.init_dim 		= -1;
  pio_info.init_vwgt_dim 	= -1;
  pio_info.init_dist_pins       = -1;
  pio_info.pdsk_root[0]		= '\0';
  pio_info.pdsk_subdir[0]	= '\0';
  pio_info.pexo_fname[0]	= '\0';
  pio_info.file_comp            = STANDARD;

  PROB_INFO  prob;
  prob.method[0]		= '\0';
  prob.num_params		= 0;
  prob.params			= NULL;

  /* Read in the ascii input file */
  error = 0;
  if (Proc == 0) {
    cout << "\n\nReading the command file, " << cmd_file << endl;
    if (!read_cmd_file(cmd_file, &prob, &pio_info, NULL)) {
      sprintf(cmesg,"fatal: Could not read in the command file"
              " \"%s\"!\n", cmd_file);
      Gen_Error(0, cmesg);
      error_report(Proc);
      print_output = 0;
      error = 1;
    }

    if (!check_inp(&prob, &pio_info)) {
      Gen_Error(0, "fatal: Error in user specified parameters.\n");
      error_report(Proc);
      print_output = 0;
      error = 1;
    }

    print_input_info(cout, Num_Proc, &prob, &pio_info, version);
  }

  MPI_Allreduce(&error, &gerror, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD);

  if (gerror) goto End;

  /* broadcast the command info to all of the processor */
  brdcst_cmd_info(Proc, &prob, &pio_info, &mesh);

  zz->Set_Param("DEBUG_MEMORY", "1");
  print_output = Output.Text;

  if (!setup_zoltan(*zz, Proc, &prob, &mesh, &pio_info)) {
    Gen_Error(0, "fatal: Error returned from setup_zoltan\n");
    error_report(Proc);
    print_output = 0;
    goto End;
  }

  srand(Proc);

  /* Loop over read and balance for a number of iterations */
  /* (Useful for testing REUSE parameters in Zoltan.) */
  for (int iteration = 1; iteration <= Number_Iterations; iteration++) {

    /*
     * now read in the mesh and element information.
     * This is the only function call to do this. Upon return,
     * the mesh struct and the elements array should be filled.
     */
    if (iteration == 1) {
      if (!read_mesh(Proc, Num_Proc, &prob, &pio_info, &mesh)) {
        Gen_Error(0, "fatal: Error returned from read_mesh\n");
        error_report(Proc);
        print_output = 0;
        goto End;
      }
      /* 
       *  Create a Zoltan DD for tracking elements during repartitioning.
       */

      if (mesh.data_type == HYPERGRAPH && !build_elem_dd(&mesh)) {
        Gen_Error(0, "fatal: Error returned from build_elem_dd\n");
        error_report(Proc);
        print_output = 0;
        goto End;
      }
    }


#ifdef KDDKDD_COOL_TEST
/* KDD Cool test of changing number of partitions  */
    sprintf(cmesg, "%d", Num_Proc * iteration);
    zz->Set_Param("NUM_GLOBAL_PARTS", cmesg);
#endif

    /*
     * Produce files to verify input.
     */
    if (iteration == 1)
      if (Debug_Driver > 2) {
        if (!output_results(cmd_file,"in",Proc,Num_Proc,&prob,&pio_info,&mesh)){
          Gen_Error(0, "fatal: Error returned from output_results\n");
          error_report(Proc);
        }
        if (Output.Gnuplot)
          if (!output_gnu(cmd_file,"in",Proc,Num_Proc,&prob,&pio_info,&mesh)) {
            Gen_Error(0, "warning: Error returned from output_gnu\n");
            error_report(Proc);
          }
      }

    /*
     * now run Zoltan to get a new load balance and perform
     * the migration
     */
    if (!run_zoltan(*zz, Proc, &prob, &mesh, &pio_info)) {
      Gen_Error(0, "fatal: Error returned from run_zoltan\n");
      error_report(Proc);
      print_output = 0;
      goto End;
    }

    /* Reset the mesh data structure for next iteration. */
    if (iteration < Number_Iterations) {
      float twiddle = 0.01;
      char str[4];
      /* Perturb coordinates of mesh */
      if (mesh.data_type == ZOLTAN_GRAPH)
        for (int i = 0; i < mesh.num_elems; i++) {
          for (int j = 0; j < mesh.num_dims; j++) {
            /* tmp = ((float) rand())/RAND_MAX; *//* Equiv. to sjplimp's test */
            float tmp = (float) (i % 10) / 10.;
            mesh.elements[i].coord[0][j] += twiddle * (2.0*tmp-1.0);
            mesh.elements[i].avg_coord[j] = mesh.elements[i].coord[0][j];
          }
        }
      /* change the ParMETIS Seed */
      sprintf(str, "%d", iteration%10000);
#ifdef ZOLTAN_PARMETIS      
      zz->Set_Param("PARMETIS_SEED", str);
#endif
    }

  } /* End of loop over read and balance */

  if (Proc == 0) {
    cout << "FILE " << cmd_file << ":  Total:  " ;
    cout << Total_Partition_Time << " seconds in Partitioning" << endl;

    cout << "FILE " << cmd_file << ":  Average:  ";
    cout << Total_Partition_Time/Number_Iterations << " seconds per Iteration";
    cout << endl;
  }

End:
  
  if (mesh.data_type == HYPERGRAPH)
  {
    destroy_elem_dd();
  }

  delete zz;

  Zoltan_Memory_Stats();

  /*
   * output the results
   */
  if (print_output) {
    if (!output_results(cmd_file,"out",Proc,Num_Proc,&prob,&pio_info,&mesh)) {
      Gen_Error(0, "fatal: Error returned from output_results\n");
      error_report(Proc);
    }

    if (Output.Gnuplot) {
      if (!output_gnu(cmd_file,"out",Proc,Num_Proc,&prob,&pio_info,&mesh)) {
        Gen_Error(0, "warning: Error returned from output_gnu\n");
        error_report(Proc);
      }
    }
  }

  free_mesh_arrays(&mesh);
  if (prob.params != NULL) free(prob.params);

  MPI_Finalize();

  return 0;
}
Exemplo n.º 6
0
int run(
  const RCP<const Comm<int> > &comm,
  int numGlobalParts,
  int testCnt,
  std::string *thisTest
)
{
#ifdef HAVE_ZOLTAN2_MPI
  // Zoltan needs an MPI comm
  const Teuchos::MpiComm<int> *tmpicomm =
               dynamic_cast<const Teuchos::MpiComm<int> *>(comm.getRawPtr());
  MPI_Comm mpiComm = *(tmpicomm->getRawMpiComm());
#endif

  int me = comm->getRank();
  int np = comm->getSize();
  double tolerance = 1.05;

  //////////////////////////////////////////////
  // Read test data from Zoltan's test directory
  //////////////////////////////////////////////

  UserInputForTests *uinput;
  try{
    uinput = new UserInputForTests(zoltanTestDirectory,
                                   thisTest[TESTNAMEOFFSET],
                                   comm, true);
  }
  catch(std::exception &e){
    if (me == 0)
      cout << "Test " << testCnt << ":  FAIL: UserInputForTests "
           << e.what() << endl;
    return 1;
  }

  RCP<tMatrix_t> matrix;
  try{
    matrix = uinput->getUITpetraCrsMatrix();
  }
  catch(std::exception &e){
    if (me == 0)
      cout << "Test " << testCnt << ":  FAIL: get matrix "
           << e.what() << endl;
    return 1;
  }

  RCP<const tMatrix_t> matrixConst = rcp_const_cast<const tMatrix_t>(matrix);

  RCP<tMVector_t> coords;
  try{
   coords = uinput->getUICoordinates();
  }
  catch(std::exception &e){
    if (me == 0)
      cout << "Test " << testCnt << ":  FAIL: get coordinates "
           << e.what() << endl;
    return 1;
  }

  RCP<tMVector_t> weights;
  try{
   weights = uinput->getUIWeights();
  }
  catch(std::exception &e){
    if (me == 0)
      cout << "Test " << testCnt << ":  FAIL: get weights "
           << e.what() << endl;
    return 1;
  }
  int nWeights = atoi(thisTest[TESTOBJWGTOFFSET].c_str());

  if (me == 0) {
    cout << "Test " << testCnt << " filename            = "
         << thisTest[TESTNAMEOFFSET] << endl;
    cout << "Test " << testCnt << " num processors      = "
         << np << endl;
    cout << "Test " << testCnt << " zoltan method       = "
         << thisTest[TESTMETHODOFFSET] << endl;
    cout << "Test " << testCnt << " num_global_parts    = "
         << numGlobalParts << endl;
    cout << "Test " << testCnt << " imbalance_tolerance = "
         << tolerance << endl;
    cout << "Test " << testCnt << " num weights per ID  = "
         << nWeights << endl;
  }

  /////////////////////////////////////////
  // PARTITION USING ZOLTAN DIRECTLY
  /////////////////////////////////////////

  if (me == 0) cout << "Calling Zoltan directly" << endl;

# ifdef HAVE_ZOLTAN2_MPI
    Zoltan zz(mpiComm);
# else
    Zoltan zz;
# endif

  char tmp[56];
  zz.Set_Param("LB_METHOD", thisTest[TESTMETHODOFFSET]);
  
  sprintf(tmp, "%d", numGlobalParts);
  zz.Set_Param("NUM_GLOBAL_PARTS", tmp);
  sprintf(tmp, "%d", nWeights);
  zz.Set_Param("OBJ_WEIGHT_DIM", tmp);
  sprintf(tmp, "%f", tolerance);
  zz.Set_Param("IMBALANCE_TOL", tmp);
  zz.Set_Param("RETURN_LISTS", "PART");
  zz.Set_Param("FINAL_OUTPUT", "1");

  zz.Set_Num_Obj_Fn(znumobj, (void *) coords.getRawPtr());
  if (nWeights)
    zz.Set_Obj_List_Fn(zobjlist, (void *) weights.getRawPtr());
  else
    zz.Set_Obj_List_Fn(zobjlist, (void *) coords.getRawPtr());
  zz.Set_Num_Geom_Fn(znumgeom, (void *) coords.getRawPtr());
  zz.Set_Geom_Multi_Fn(zgeom, (void *) coords.getRawPtr());

  int changes, ngid, nlid;
  int numd, nump;
  ZOLTAN_ID_PTR dgid = NULL, dlid = NULL, pgid = NULL, plid = NULL;
  int *dproc = NULL, *dpart = NULL, *pproc = NULL, *ppart = NULL;

  int ierr = zz.LB_Partition(changes, ngid, nlid,
                             numd, dgid, dlid, dproc, dpart,
                             nump, pgid, plid, pproc, ppart);
  if (ierr != ZOLTAN_OK && ierr != ZOLTAN_WARN) {
    if (me == 0)
      cout << "Test " << testCnt << ":  FAIL: direct Zoltan call" << endl;
    zz.LB_Free_Part(&pgid, &plid, &pproc, &ppart);
    return 1;
  }

  /////////////////////////////////////////
  // PARTITION USING ZOLTAN THROUGH ZOLTAN2
  /////////////////////////////////////////

  if (me == 0) cout << "Calling Zoltan through Zoltan2" << endl;

  matrixAdapter_t *ia;
  try{
    ia = new matrixAdapter_t(matrixConst, nWeights);
  }
  catch(std::exception &e){
    if (me == 0)
      cout << "Test " << testCnt << ":  FAIL: matrix adapter "
           << e.what() << endl;
    return 1;
  }
  for (int idx=0; idx < nWeights; idx++)
    ia->setRowWeights(weights->getData(idx).getRawPtr(), 1, idx);

  vectorAdapter_t *ca = NULL;
  try{
    ca = new vectorAdapter_t(coords);
  }
  catch(std::exception &e){
    if (me == 0)
      cout << "Test " << testCnt << ":  FAIL: vector adapter "
           << e.what() << endl;
    return 1;
  }
  ia->setCoordinateInput(ca);
  
  Teuchos::ParameterList params;
  params.set("timer_output_stream" , "std::cout");
  params.set("compute_metrics", "true");
  // params.set("debug_level" , "verbose_detailed_status");

  params.set("algorithm", "zoltan");
  params.set("imbalance_tolerance", tolerance );
  params.set("num_global_parts", numGlobalParts);

  if (thisTest[TESTMETHODOFFSET] != "default") {
    // "default" tests case of no Zoltan parameter sublist
    Teuchos::ParameterList &zparams = params.sublist("zoltan_parameters",false);
    zparams.set("LB_METHOD",thisTest[TESTMETHODOFFSET]);
  }

  Zoltan2::PartitioningProblem<matrixAdapter_t> *problem;
# ifdef HAVE_ZOLTAN2_MPI
    try{
      problem = new Zoltan2::PartitioningProblem<matrixAdapter_t>(ia, &params,
                                                                  mpiComm);
    }
# else
    try{
      problem = new Zoltan2::PartitioningProblem<matrixAdapter_t>(ia, &params);
    }
# endif
  catch(std::exception &e){
    cout << "Test " << testCnt << " FAIL: problem " << e.what() << endl;
    return 1;
  }

  try {
    problem->solve();
  }
  catch(std::exception &e){
    cout << "Test " << testCnt << " FAIL: solve " << e.what() << endl;
    return 1;
  }

  if (me == 0){
    problem->printMetrics(cout);
  }
  problem->printTimers();

  /////////////////////////////////////////
  // COMPARE RESULTS
  /////////////////////////////////////////
  size_t nObj = coords->getLocalLength();
  const int *z2parts = problem->getSolution().getPartListView();
  int diffcnt = 0, gdiffcnt = 0;
  for (size_t i = 0; i < nObj; i++) {
    if (z2parts[plid[i]] != ppart[i]) {
      diffcnt++;
      cout << me << " DIFF for " << i << " (" 
           << coords->getMap()->getGlobalElement(i) << "):  "
           << "Z2 = " << z2parts[i] << "; Z1 = " << ppart[plid[i]] << endl;
    }
  }

  /////////////////////////////////////////
  // CLEAN UP
  /////////////////////////////////////////
  zz.LB_Free_Part(&pgid, &plid, &pproc, &ppart);
  delete ia;
  delete ca;
  delete problem;
  delete uinput;

  Teuchos::reduceAll(*comm, Teuchos::REDUCE_SUM, 1, &diffcnt, &gdiffcnt);
  if (gdiffcnt > 0) {
    if (me == 0) 
      cout << "Test " << testCnt << " "
           << thisTest[TESTNAMEOFFSET] << " "
           << thisTest[TESTMETHODOFFSET] << " "
           << thisTest[TESTOBJWGTOFFSET] << " "
           << " FAIL: comparison " << endl;
    return 1;
  }

  return 0;
}