//-----------------------------------------------------------------------------
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;
}
//-----------------------------------------------------------------------------
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);
}