Flow123d  release_3.0.0-1106-ga3b2e4c
partitioning.cc
Go to the documentation of this file.
1 /*!
2  *
3  * Copyright (C) 2015 Technical University of Liberec. All rights reserved.
4  *
5  * This program is free software; you can redistribute it and/or modify it under
6  * the terms of the GNU General Public License version 3 as published by the
7  * Free Software Foundation. (http://www.gnu.org/licenses/gpl-3.0.en.html)
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12  *
13  *
14  * @file partitioning.cc
15  * @brief
16  */
17 
18 #include "mesh/partitioning.hh"
19 #include "la/sparse_graph.hh"
20 #include "la/distribution.hh"
21 #include "mesh/side_impl.hh"
22 #include "mesh/long_idx.hh"
23 #include "mesh/mesh.h"
24 #include "mesh/accessors.hh"
25 #include "mesh/range_wrapper.hh"
26 #include "mesh/neighbours.h"
27 
28 #include "petscao.h"
29 
30 
31 namespace IT = Input::Type;
33  return IT::Selection("GraphType",
34  "Different algorithms to make the sparse graph with weighted edges\n"
35  "from the multidimensional mesh. Main difference is dealing with \n"
36  "neighboring of elements of different dimension.")
37  .add_value(any_neighboring, "any_neighboring", "Add an edge for any pair of neighboring elements.")
38  .add_value(any_weight_lower_dim_cuts, "any_weight_lower_dim_cuts", "Same as before and assign higher weight to cuts of lower dimension in order to make them stick to one face.")
39  .add_value(same_dimension_neighboring, "same_dimension_neighboring", "Add an edge for any pair of neighboring elements of the same dimension (bad for matrix multiply).")
40  .close();
41 }
42 
44  return IT::Selection("PartTool", "Select the partitioning tool to use.")
45  .add_value(PETSc, "PETSc", "Use PETSc interface to various partitioning tools.")
46  .add_value(METIS, "METIS", "Use direct interface to Metis.")
47  .close();
48 }
49 
51  static IT::Record input_type = IT::Record("Partition","Setting for various types of mesh partitioning." )
52  .declare_key("tool", Partitioning::get_tool_sel(), IT::Default("\"METIS\""), "Software package used for partitioning. See corresponding selection.")
53  .declare_key("graph_type", Partitioning::get_graph_type_sel(), IT::Default("\"any_neighboring\""), "Algorithm for generating graph and its weights from a multidimensional mesh.")
54  .allow_auto_conversion("graph_type") // mainly in order to allow Default value for the whole record Partition
55  .close();
56  input_type.finish();
57 
58  return input_type;
59 }
60 
61 
63 : mesh_(mesh), in_(in), graph_(NULL), loc_part_(NULL), init_el_ds_(NULL)
64 {
66 }
67 
68 
69 
71  if (loc_part_) delete [] loc_part_;
72  loc_part_ = NULL;
73  if (init_el_ds_) delete init_el_ds_;
74  init_el_ds_ = NULL;
75  if (graph_) delete graph_;
76  graph_ = NULL;
77 }
78 
80  OLD_ASSERT(init_el_ds_, "NULL initial distribution.");
81  return init_el_ds_;
82 }
83 
84 
85 
87  OLD_ASSERT(loc_part_, "NULL local partitioning.");
88  return loc_part_;
89 }
90 
91 
92 
94 
95  Distribution edistr = graph_->get_distr();
96 
97  const Edge *edg;
98  int e_idx;
99  unsigned int i_neigh;
100  int i_s, n_s;
101 
102  // Add nigbouring edges only for "any_*" graph types
103  bool neigh_on = ( in_.val<PartitionGraphType>("graph_type") != same_dimension_neighboring );
104 
105  for (auto ele : mesh_->elements_range()) {
106  // skip non-local elements
107  if ( !edistr.is_local( ele.idx() ) )
108  continue;
109 
110  // for all connected elements
111  for (unsigned int si=0; si<ele->n_sides(); si++) {
112  edg = ele.side(si)->edge();
113 
114  for (unsigned int li=0; li<edg->n_sides; li++) {
115  ASSERT(edg->side(li)->valid()).error("NULL side of edge.");
116  e_idx = edg->side(li)->element().idx();
117 
118  // for elements of connected elements, excluding element itself
119  if ( e_idx != ele.idx() ) {
120  graph_->set_edge( ele.idx(), e_idx );
121  }
122  }
123  }
124 
125  // include connections from lower dim. edge
126  // to the higher dimension
127  if (neigh_on) {
128  for (i_neigh = 0; i_neigh < ele->n_neighs_vb(); i_neigh++) {
129  n_s = ele->neigh_vb[i_neigh]->edge()->n_sides;
130  for (i_s = 0; i_s < n_s; i_s++) {
131  e_idx = ele->neigh_vb[i_neigh]->edge()->side(i_s)->element().idx();
132  graph_->set_edge( ele.idx(), e_idx );
133  graph_->set_edge( e_idx, ele.idx() );
134  }
135  }
136  }
137  }
138  graph_->finalize();
139 }
140 
141 
142 
144 
145 
146  // prepare dual graph
147  switch ( in_.val<PartitionTool>("tool")) {
148  case PETSc:
149  init_el_ds_ = new Distribution(DistributionBlock(), mesh_->n_elements(), mesh_->get_comm() ); // initial distr.
151 
152  break;
153  case METIS:
154  init_el_ds_ = new Distribution(DistributionLocalized(), mesh_->n_elements(), mesh_->get_comm() ); // initial distr.
155  graph_ = new SparseGraphMETIS(*init_el_ds_);
156  break;
157  }
158  int mesh_size = mesh_->n_elements();
159  int num_of_procs = init_el_ds_->np();
160  if (mesh_size < num_of_procs) { // check if decomposing is possible
161  THROW( ExcDecomposeMesh() << EI_NElems( mesh_size ) << EI_NProcs( num_of_procs ) );
162  }
163 
165 
166  // compute partitioning
169  delete graph_; graph_ = NULL;
170 }
171 
172 
173 
174 
175 /**
176  * Old UGLY, PETSC dependent method for getting new numbering after partitioning.
177  *
178  * n_ids - given maximal ID used in id_4_old
179  * id_4_old - given array of size init_el_ds_.size() - assign ID to an old index
180  *
181  * new_ds - new distribution of elements according to current distributed partitioning loc_part_
182  * id_4_loc - IDs for local elements in new distribution, has size new_ds->lsize()
183  * new_4_id - for given ID, the new index, -1 for unknown IDs
184  *
185  */
186 void Partitioning::id_maps(int n_ids, LongIdx *id_4_old,
187  const Distribution &old_ds, LongIdx *loc_part,
188  Distribution * &new_ds, LongIdx * &id_4_loc, LongIdx * &new_4_id) {
189 
190  IS part, new_numbering;
191  unsigned int size = old_ds.size(); // whole size of distr. array
192  int new_counts[old_ds.np()];
193  AO new_old_ao;
194  int *old_4_new;
195  int i_loc;
196 
197  // make distribution and numbering
198  ISCreateGeneral(PETSC_COMM_WORLD, old_ds.lsize(), loc_part, PETSC_COPY_VALUES, &part); // global IS part.
199  ISPartitioningCount(part, old_ds.np(), new_counts); // new size of each proc
200 
201  new_ds = new Distribution((unsigned int *) new_counts, PETSC_COMM_WORLD); // new distribution
202  ISPartitioningToNumbering(part, &new_numbering); // new numbering
203 
204  old_4_new = new int [size];
205  id_4_loc = new LongIdx [ new_ds->lsize() ];
206  new_4_id = new LongIdx [ n_ids + 1 ];
207 
208  // create whole new->old mapping on each proc
209  AOCreateBasicIS(new_numbering, PETSC_NULL, &new_old_ao); // app ordering= new; petsc ordering = old
210  for (unsigned int i = 0; i < size; i++)
211  old_4_new[i] = i;
212  AOApplicationToPetsc(new_old_ao, size, old_4_new);
213  AODestroy(&(new_old_ao));
214 
215  // compute id_4_loc
216  i_loc = 0;
217 
218  for (unsigned int i_new = new_ds->begin(); i_new < new_ds->end(); i_new++) {
219  id_4_loc[i_loc++] = id_4_old[old_4_new[i_new]];
220  }
221  // compute row_4_id
222  for (i_loc = 0; i_loc <= n_ids; i_loc++)
223  new_4_id[i_loc] = -1; // ensure that all ids are initialized
224  for (unsigned int i_new = 0; i_new < size; i_new++)
225  new_4_id[id_4_old[old_4_new[i_new]]] = i_new;
226 
227  delete [] old_4_new;
228 }
229 
230 
231 void Partitioning::id_maps(int n_ids, LongIdx *id_4_old, Distribution * &new_ds, LongIdx * &id_4_loc, LongIdx * &new_4_id) {
232  Partitioning::id_maps(n_ids, id_4_old, *init_el_ds_, loc_part_, new_ds, id_4_loc, new_4_id);
233 }
234 
235 
236 
237 shared_ptr< vector<int> > Partitioning::subdomain_id_field_data() {
238  OLD_ASSERT(loc_part_, "Partition is not yet computed.\n");
239  if (!seq_part_) {
240  unsigned int seq_size=(init_el_ds_->myp() == 0) ? init_el_ds_->size() : 1;
241  //seq_part_.resize(seq_size);
242  seq_part_ = make_shared< vector<int> >(seq_size);
243  std::vector<int> &vec = *( seq_part_.get() );
244 
246  &vec[0],
247  (int *)(init_el_ds_->get_lsizes_array()),
248  (int *)(init_el_ds_->get_starts_array()),
249  MPI_INT, 0,init_el_ds_->get_comm() );
250 
251  }
252  return seq_part_;
253 
254 }
int LongIdx
Define type that represents indices of large arrays (elements, nodes, dofs etc.)
Definition: long_idx.hh:22
const Edge * edge() const
Definition: side_impl.hh:66
unsigned int size() const
get global size
Mesh * mesh_
The input mesh.
~Partitioning()
Destructor.
Definition: partitioning.cc:70
shared_ptr< vector< int > > subdomain_id_field_data()
void make_element_connection_graph()
Definition: partitioning.cc:93
Class Input::Type::Default specifies default value of keys of a Input::Type::Record.
Definition: type_record.hh:61
static const Input::Type::Record & get_input_type()
Definition: partitioning.cc:50
Add edge for any pair of neighboring elements.
shared_ptr< vector< int > > seq_part_
Sequential partitioning for output.
Definition: mesh.h:76
#define MPI_Gatherv(sendbuf, sendcount, sendtype, recvbuf, recvcounts, displs, recvtype, root, comm)
Definition: mpi.h:549
int n_sides
Definition: edges.h:36
Definition: edges.h:26
bool valid() const
Definition: side_impl.hh:92
#define ASSERT(expr)
Allow use shorter versions of macro names if these names is not used with external library...
Definition: asserts.hh:346
ElementAccessor< 3 > element() const
Definition: side_impl.hh:53
Record & close() const
Close the Record for further declarations of keys.
Definition: type_record.cc:303
const unsigned int * get_lsizes_array()
get local sizes array
LongIdx * loc_part_
Partition numbers for local elements in original distribution of elements given be init_el_ds_...
#define OLD_ASSERT(...)
Definition: global_defs.h:131
virtual Record & allow_auto_conversion(const string &from_key)
Allows shorter input of the Record providing only value of the from_key given as the parameter...
Definition: type_record.cc:132
bool is_local(unsigned int idx) const
identify local index
Use PETSc interface to various partitioing tools.
static const Input::Type::Selection & get_tool_sel()
Definition: partitioning.cc:43
Input::Record in_
Input Record accessor.
const unsigned int * get_starts_array() const
get local starts array
MPI_Comm get_comm() const
Definition: mesh.h:185
void finalize()
Make sparse graph structures: rows, adj.
Definition: sparse_graph.cc:98
virtual void partition(int *loc_part)=0
Accessor to the data with type Type::Record.
Definition: accessors.hh:292
const Ret val(const string &key) const
Use direct interface to Metis.
Selection & add_value(const int value, const std::string &key, const std::string &description="", TypeBase::attribute_map attributes=TypeBase::attribute_map())
Adds one new value with name given by key to the Selection.
Record & declare_key(const string &key, std::shared_ptr< TypeBase > type, const Default &default_value, const string &description, TypeBase::attribute_map key_attributes=TypeBase::attribute_map())
Declares a new key of the Record.
Definition: type_record.cc:501
virtual Range< ElementAccessor< 3 > > elements_range() const
Returns range of bulk elements.
Definition: mesh.cc:1040
unsigned int np() const
get num of processors
Distribution get_distr()
Distribution * init_el_ds_
Original distribution of elements. Depends on type of partitioner.
unsigned int myp() const
get my processor
Support classes for parallel programing.
void id_maps(int n_ids, LongIdx *id_4_old, Distribution *&new_ds, LongIdx *&id_4_loc, LongIdx *&new_4_id)
FinishStatus finish(FinishStatus finish_type=FinishStatus::regular_) override
Finish declaration of the Record type.
Definition: type_record.cc:242
virtual unsigned int n_elements(bool boundary=false) const
Returns count of boundary or bulk elements.
Definition: mesh.h:346
const Selection & close() const
Close the Selection, no more values can be added.
#define MPI_INT
Definition: mpi.h:160
MPI_Comm get_comm() const
Returns communicator.
Same as before and assign higher weight to cuts of lower dimension in order to make them stick to one...
Distributed sparse graphs, partitioning.
const Distribution * get_init_distr() const
Definition: partitioning.cc:79
void make_partition()
const LongIdx * get_loc_part() const
Definition: partitioning.cc:86
Record type proxy class.
Definition: type_record.hh:182
SparseGraph * graph_
Graph used to partitioning the mesh.
static const Input::Type::Selection & get_graph_type_sel()
Input specification objects.
Definition: partitioning.cc:32
Add edge for any pair of neighboring elements of same dimension (bad for matrix multiply) ...
Partitioning(Mesh *mesh, Input::Record in)
Definition: partitioning.cc:62
unsigned int idx() const
Return local idx of element in boundary / bulk part of element vector.
Definition: accessors.hh:111
#define THROW(whole_exception_expr)
Wrapper for throw. Saves the throwing point.
Definition: exceptions.hh:53
SideIter side(const unsigned int i) const
Definition: edges.h:31
Template for classes storing finite set of named values.
Implementation of range helper class.
void set_edge(const int a, const int b, int weight=1)
Definition: sparse_graph.cc:66
unsigned int lsize(int proc) const
get local size