FEAT 3
Finite Element Analysis Toolbox
Loading...
Searching...
No Matches
parti_zoltan.cpp
1// FEAT3: Finite Element Analysis Toolbox, Version 3
2// Copyright (C) 2010 by Stefan Turek & the FEAT group
3// FEAT3 is released under the GNU General Public License version 3,
4// see the file 'copyright.txt' in the top level directory for details.
5
6#include <kernel/geometry/parti_zoltan.hpp>
7
8#ifdef FEAT_HAVE_ZOLTAN
9#include <zoltan.h>
10
11using namespace FEAT;
12using namespace Geometry;
13
27extern "C" int feat_zoltan_num_elems(void* data, int* ierr)
28{
29 *ierr = ZOLTAN_OK;
30 const Adjacency::Graph& grp = reinterpret_cast<const PartiZoltan::Hypergraph*>(data)->sub_graph;
31 return int(grp.get_num_nodes_domain());
32}
33
45extern "C" void feat_zoltan_elem_list(void* data, int size_gid, int size_lid,
46 ZOLTAN_ID_PTR global_id, ZOLTAN_ID_PTR DOXY(local_id), int wgt_dim, float* obj_wgts, int* ierr)
47{
48 XASSERT(size_gid == 1);
49 XASSERT(size_lid == 0);
50 //XASSERT(wgt_dim == 0);
51 const PartiZoltan::Hypergraph& hyg = *reinterpret_cast<const PartiZoltan::Hypergraph*>(data);
52 const Index n = hyg.sub_graph.get_num_nodes_domain();
53 for(Index i(0); i < n; ++i)
54 global_id[i] = ZOLTAN_ID_TYPE(hyg.first_elem + i);
55 if(wgt_dim > 0)
56 {
57 for(Index i(0); i < n; ++i)
58 obj_wgts[i] = hyg.weights[i];
59 }
60 *ierr = ZOLTAN_OK;
61}
62
72extern "C" void feat_zoltan_hypergraph_size(void* data, int* num_lists, int* num_nonzeroes, int* format, int* ierr)
73{
74 *ierr = ZOLTAN_OK;
75 const Adjacency::Graph& grp = reinterpret_cast<const PartiZoltan::Hypergraph*>(data)->sub_graph;
76 *num_lists = int(grp.get_num_nodes_domain());
77 *num_nonzeroes = int(grp.get_num_indices());
78 *format = ZOLTAN_COMPRESSED_EDGE;
79 *ierr = ZOLTAN_OK;
80}
81
91extern "C" void feat_zoltan_hypergraph_data(void* data, int size_gid, int num_edges, int num_nonzeroes,
92 int format, ZOLTAN_ID_PTR edgeGID, int* vtxPtr, ZOLTAN_ID_PTR vtxGID, int* ierr)
93{
94 const Adjacency::Graph& grp = reinterpret_cast<const PartiZoltan::Hypergraph*>(data)->sub_graph;
95 XASSERT(size_gid == 1);
96 XASSERT(num_edges == int(grp.get_num_nodes_domain()));
97 XASSERT(num_nonzeroes == int(grp.get_num_indices()));
98 XASSERT(format == ZOLTAN_COMPRESSED_EDGE);
99
100 const Index* dom_ptr = grp.get_domain_ptr();
101 const Index* img_idx = grp.get_image_idx();
102
103 // copy domain ptr = hyperedge ptr
104 for(int i(0); i < num_edges; ++i)
105 {
106 edgeGID[i] = ZOLTAN_ID_TYPE(i);
107 vtxPtr[i] = int(dom_ptr[i]);
108 }
109
110 // copy image idx = vertex idx
111 for(int i(0); i < num_nonzeroes; ++i)
112 {
113 vtxGID[i] = ZOLTAN_ID_TYPE(img_idx[i]);
114 }
115 *ierr = ZOLTAN_OK;
116}
117
118namespace FEAT
119{
120 namespace Geometry
121 {
122 PartiZoltan::PartiZoltan(const Dist::Comm& comm, Index min_elems, int max_procs) :
123 _comm(comm),
124 _zoltan_comm(Dist::Comm::null()),
125 _max_procs(max_procs),
126 _min_elems(min_elems),
127 _num_elems(0u),
128 _num_parts(0u),
129 _hypergraph(),
130 _coloring(),
131 _zz(nullptr)
132 {
133 }
134
135 PartiZoltan::~PartiZoltan()
136 {
137 if(_zz != nullptr)
138 {
139 Zoltan_Destroy(reinterpret_cast<Zoltan_Struct**>(&_zz));
140 _zz = nullptr;
141 }
142 }
143
144 bool PartiZoltan::execute(const Adjacency::Graph& faces_at_elem, const Index num_parts, const std::vector<Real>& weights)
145 {
146 // set number of desired partitions and total number of elements
147 this->_num_parts = num_parts;
148 this->_num_elems = faces_at_elem.get_num_nodes_domain();
149
150 // first of all, determine how many processes we want to use for Zoltan
151 int num_procs = Math::max(1, int(faces_at_elem.get_num_nodes_domain() / _min_elems));
152 if(_max_procs > 0)
153 Math::mini(num_procs, _max_procs);
154 Math::mini(num_procs, int(num_parts));
155 Math::mini(num_procs, _comm.size());
156
157 // do we need to create a sub-communicator for Zoltan?
158 if(num_procs < _comm.size())
159 _zoltan_comm = _comm.comm_create_range_incl(num_procs);
160 else
161 _zoltan_comm = _comm.comm_dup();
162
163 // get my rank and size
164 //const int z_rank = _zoltan_comm.rank();
165 const int z_size = _zoltan_comm.size();
166
167 // make sure that the number of desired parts is not smaller than the size of the communicator
168 XASSERTM(Index(z_size) <= num_parts, "thou shall not create less partitions than thou hast ranks");
169
170 // if the size of the Zoltan communicator is zero, then this process does not participate
171 // in the actual partitioning process and it only needs to participate in the broadcast
172 bool is_ok = true;
173 if(z_size > 0)
174 {
175 // create our sub-hypergraph
176 this->_create_hypergraph(faces_at_elem, weights);
177
178 // apply Zoltan partitioner
179 is_ok = this->_apply_zoltan();
180 }
181
182 // broadcast coloring from root rank and return
183 return this->_broadcast_coloring(is_ok);
184 }
185
186 void PartiZoltan::_create_hypergraph(const Adjacency::Graph& faces_at_elem, const std::vector<Real>& weights)
187 {
188 // get number of elements and faces
189 const Index num_elems = faces_at_elem.get_num_nodes_domain();
190 const Index num_faces = faces_at_elem.get_num_nodes_image();
191
192 // get my rank and size
193 const int z_rank = _zoltan_comm.rank();
194 const int z_size = _zoltan_comm.size();
195
196 // this should not happen
197 XASSERT(z_size > 0);
198
199 // determine the first and the last element of this rank
200 Index first_elem = (Index(z_rank) * num_elems) / Index(z_size);
201 Index end_elem = (Index(z_rank+1) * num_elems) / Index(z_size);
202 Index my_num_elems = end_elem - first_elem;
203
204 const Index* dom_ptr = faces_at_elem.get_domain_ptr();
205 const Index* img_idx = faces_at_elem.get_image_idx();
206
207 // create a sub-graph for this rank
208 const Index first_ptr = dom_ptr[first_elem];
209 const Index my_num_idx = dom_ptr[end_elem] - first_ptr;
210 Adjacency::Graph faces_at_my_elem(my_num_elems, num_faces, my_num_idx);
211
212 Index* my_dom_ptr = faces_at_my_elem.get_domain_ptr();
213 Index* my_img_idx = faces_at_my_elem.get_image_idx();
214 for(Index i(0); i <= my_num_elems; ++i)
215 my_dom_ptr[i] = dom_ptr[first_elem + i] - first_ptr;
216 for(Index i(0); i < my_num_idx; ++i)
217 my_img_idx[i] = img_idx[first_ptr + i];
218
219 // transpose graph for this rank
220 Adjacency::Graph elems_at_faces(Adjacency::RenderType::transpose, faces_at_elem);
221
222 // combine graph into our sub-hypergraph
223 this->_hypergraph.first_elem = first_elem;
224 this->_hypergraph.sub_graph = Adjacency::Graph(Adjacency::RenderType::injectify_sorted, faces_at_my_elem, elems_at_faces);
225
226 // extract weights if desired
227 if(!weights.empty())
228 {
229 XASSERT(end_elem <= weights.size());
230 this->_hypergraph.weights.resize(my_num_elems);
231 for(Index i(0); i < my_num_elems; ++i)
232 this->_hypergraph.weights[i] = float(weights[first_elem + i]);
233 }
234 }
235
236 bool PartiZoltan::_apply_zoltan()
237 {
238 // create Zoltan structure
239 Zoltan_Struct* zz = Zoltan_Create(_zoltan_comm.mpi_comm());
240 this->_zz = zz;
241
242 // define Zoltan parameters
243 Zoltan_Set_Param(zz, "DEBUG_LEVEL", "0");
244 Zoltan_Set_Param(zz, "LB_METHOD", "HYPERGRAPH");
245 Zoltan_Set_Param(zz, "HYPERGRAPH_PACKAGE", "PHG");
246 Zoltan_Set_Param(zz, "LB_APPROACH", "PARTITION");
247 Zoltan_Set_Param(zz, "NUM_GID_ENTRIES", "1");
248 Zoltan_Set_Param(zz, "NUM_LID_ENTRIES", "0");
249 Zoltan_Set_Param(zz, "RETURN_LISTS", "PARTS");
250 Zoltan_Set_Param(zz, "NUM_GLOBAL_PARTS", stringify(this->_num_parts).c_str());
251 Zoltan_Set_Param(zz, "OBJ_WEIGHT_DIM", this->_hypergraph.weights.empty() ? "0" : "1");
252 Zoltan_Set_Param(zz, "EDGE_WEIGHT_DIM", "0");
253 //Zoltan_Set_Param(zz, "CHECK_GRAPH", "2");
254 Zoltan_Set_Param(zz, "PHG_EDGE_SIZE_THRESHOLD", "1.0"); // Zoltan keeps complaining for values < 1
255
256 // set hypergraph interface callbacks
257 Zoltan_Set_Num_Obj_Fn(zz, feat_zoltan_num_elems, &this->_hypergraph);
258 Zoltan_Set_Obj_List_Fn(zz, feat_zoltan_elem_list, &this->_hypergraph);
259 Zoltan_Set_HG_Size_CS_Fn(zz, feat_zoltan_hypergraph_size, &this->_hypergraph);
260 Zoltan_Set_HG_CS_Fn(zz, feat_zoltan_hypergraph_data, &this->_hypergraph);
261
262 int changes(0), num_gid_entries(0), num_lid_entries(0), num_import(0), num_export(0);
263 ZOLTAN_ID_PTR import_global_ids(nullptr);
264 ZOLTAN_ID_PTR import_local_ids(nullptr);
265 int* import_procs(nullptr);
266 int* import_parts(nullptr);
267 ZOLTAN_ID_PTR export_global_ids(nullptr);
268 ZOLTAN_ID_PTR export_local_ids(nullptr);
269 int* export_procs(nullptr);
270 int* export_parts(nullptr);
271
272 int rtn = Zoltan_LB_Partition(zz, &changes, &num_gid_entries, &num_lid_entries,
273 &num_import, &import_global_ids, &import_local_ids, &import_procs, &import_parts,
274 &num_export, &export_global_ids, &export_local_ids, &export_procs, &export_parts);
275
276 // create coloring from Zoltan partitioning
277 bool gather_ok = true;
278 if(rtn == ZOLTAN_OK)
279 gather_ok = this->_gather_coloring(num_export, export_parts);
280
281 Zoltan_LB_Free_Part(&import_global_ids, &import_local_ids, &import_procs, &import_parts);
282 Zoltan_LB_Free_Part(&export_global_ids, &export_local_ids, &export_procs, &export_parts);
283
284 Zoltan_Destroy(&zz);
285 this->_zz = nullptr;
286
287 return gather_ok && (rtn == ZOLTAN_OK);
288 }
289
290 bool PartiZoltan::_gather_coloring(const int num_export, const int* export_parts)
291 {
292 // get my rank and size
293 const Index z_rank = Index(_zoltan_comm.rank());
294 const Index z_size = Index(_zoltan_comm.size());
295
296 // determine maximum export size
297 Index max_export = Index(num_export);
298 _zoltan_comm.allreduce(&max_export, &max_export, 1u, Dist::op_max);
299 max_export += 1; // for actual size
300
301 // allocate send buffer
302 std::vector<int> send_buf(max_export);
303 send_buf[0] = num_export;
304 for(Index i(0); i < Index(num_export); ++i)
305 send_buf[i+1] = export_parts[i];
306
307 // allocate receive buffer and gather coloring at rank 0
308 std::vector<int> recv_buf(z_rank == 0 ? max_export * z_size : Index(0));
309 _zoltan_comm.gather(send_buf.data(), max_export, recv_buf.data(), max_export, 0);
310
311 // all ranks > 0 are done here
312 if(z_rank > 0)
313 return true;
314
315 // compute total element count
316 Index num_elems(0);
317 for(Index i(0); i < z_size; ++i)
318 num_elems += Index(recv_buf[i*max_export]);
319
320 // allocate ranks vector
321 this->_coloring = Adjacency::Coloring(num_elems, this->_num_parts);
322 Index* col = this->_coloring.get_coloring();
323
324 // build ranks vector
325 for(Index i(0), k(0); i < z_size; ++i)
326 {
327 Index off = i*max_export;
328 Index n = Index(recv_buf[off]);
329 for(Index j(1); j <= n; ++j, ++k)
330 col[k] = Index(recv_buf[off+j]);
331 }
332
333 // build color counts
334 std::vector<int> color_counts(this->_num_parts, 0);
335 for(Index i(0); i < this->_num_elems; ++i)
336 ++color_counts[col[i]];
337
338 // make sure we have at least 1 element per color/partition
339 for(Index i(0); i < this->_num_parts; ++i)
340 {
341 if(color_counts[i] == 0)
342 return false;
343 }
344
345 // okay
346 return true;
347 }
348
349 bool PartiZoltan::_broadcast_coloring(bool zoltan_ok)
350 {
351 // first of all, let's compare our Zoltan return codes;
352 // all processes which did not participate in the partitioning have a 'true' status
353 int was_ok(zoltan_ok ? 0 : 1), was_all_ok(0);
354 this->_comm.allreduce(&was_ok, &was_all_ok, std::size_t(1), Dist::op_sum);
355 if(was_all_ok > 0)
356 return false; // at least 1 process failed
357
358 // allocate coloring if not on root
359 if(this->_comm.rank() > 0)
360 this->_coloring = Adjacency::Coloring(this->_num_elems, this->_num_parts);
361
362 // broadcast coloring
363 this->_comm.bcast(this->_coloring.get_coloring(), this->_num_elems, 0);
364
365 // okay
366 return true;
367 }
368 } // namespace Geometry
369} // namespace FEAT
370
371#else // no FEAT_HAVE_ZOLTAN
372
373// dummy function to suppress linker warnings
374void zoltan_did_not_make_it_to_the_feat_parti() {}
375
376#endif // FEAT_HAVE_ZOLTAN
#define XASSERT(expr)
Assertion macro definition.
Definition: assertion.hpp:262
#define XASSERTM(expr, msg)
Assertion macro definition with custom message.
Definition: assertion.hpp:263
Coloring object implementation.
Definition: coloring.hpp:37
Adjacency Graph implementation.
Definition: graph.hpp:34
Index * get_domain_ptr()
Returns the domain pointer array.
Definition: graph.hpp:359
Index * get_image_idx()
Returns the image node index array.
Definition: graph.hpp:374
Index get_num_indices() const
Returns the total number indices.
Definition: graph.hpp:390
Communicator class.
Definition: dist.hpp:1349
PartiZoltan(const Dist::Comm &comm, Index min_elems=1000u, int max_procs=1000)
Constructor.
FEAT namespace.
Definition: adjactor.hpp:12
String stringify(const T_ &item)
Converts an item into a String.
Definition: string.hpp:944
std::uint64_t Index
Index data type.