1 /**
2 * MOAB, a Mesh-Oriented datABase, is a software component for creating,
3 * storing and accessing finite element mesh data.
4 *
5 * Copyright 2004 Sandia Corporation. Under the terms of Contract
6 * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government
7 * retains certain rights in this software.
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 */
15
16 // Contributed by Lorenzo Alessio Botti (SpaFEDTe)
17 // This implementation is mostly borrowed from the mbzoltan MOAB partitioning tool
18
19 #include <iostream>
20 #include <assert.h>
21 #include <sstream>
22 #include <map>
23 #include <ctime>
24
25 #include "moab/MetisPartitioner.hpp"
26 #include "moab/Interface.hpp"
27 #include "Internals.hpp"
28 #include "moab/Range.hpp"
29 #include "moab/WriteUtilIface.hpp"
30 #include "moab/MeshTopoUtil.hpp"
31 #include "moab/Skinner.hpp"
32 #include "MBTagConventions.hpp"
33 #include "moab/CN.hpp"
34
35 using namespace moab;
36
37 const bool debug = false;
38
MetisPartitioner(Interface * impl,const bool use_coords)39 MetisPartitioner::MetisPartitioner( Interface *impl,
40 const bool use_coords)
41 : PartitionerBase<idx_t>(impl,use_coords)
42
43 {
44 }
45
~MetisPartitioner()46 MetisPartitioner::~MetisPartitioner()
47 {
48 }
49
partition_mesh(const idx_t nparts,const char * method,const int part_dim,const bool write_as_sets,const bool write_as_tags,const bool partition_tagged_sets,const bool partition_tagged_ents,const char * aggregating_tag,const bool print_time)50 ErrorCode MetisPartitioner::partition_mesh(const idx_t nparts,
51 const char *method,
52 const int part_dim,
53 const bool write_as_sets,
54 const bool write_as_tags,
55 const bool partition_tagged_sets,
56 const bool partition_tagged_ents,
57 const char *aggregating_tag,
58 const bool print_time)
59 {
60 #ifdef MOAB_HAVE_MPI
61 // should only be called in serial
62 if (mbpc->proc_config().proc_size() != 1) {
63 std::cout << "MetisPartitioner::partition_mesh_and_geometry must be called in serial."
64 << std::endl;
65 return MB_FAILURE;
66 }
67 #endif
68
69 if (NULL != method && strcmp(method, "ML_RB") && strcmp(method, "ML_KWAY"))
70 {
71 std::cout << "ERROR: Method must be "
72 << "ML_RB or ML_KWAY"
73 << std::endl;
74 return MB_FAILURE;
75 }
76
77 std::vector<double> pts; // x[0], y[0], z[0], ... from MOAB
78 std::vector<idx_t> ids; // poidx_t ids from MOAB
79 std::vector<idx_t> adjs, parts;
80 std::vector<idx_t> length;
81 Range elems;
82 // Get a mesh from MOAB and diide it across processors.
83
84 clock_t t = clock();
85
86 ErrorCode result;
87 if (!partition_tagged_sets && !partition_tagged_ents)
88 {
89 result = assemble_graph(part_dim, pts, ids, adjs, length, elems);MB_CHK_ERR(result);
90 }
91 else if (partition_tagged_sets)
92 {
93 result = assemble_taggedsets_graph(part_dim, pts, ids, adjs, length, elems, &(*aggregating_tag));MB_CHK_ERR(result);
94 }
95 else if (partition_tagged_ents)
96 {
97 result = assemble_taggedents_graph(part_dim, pts, ids, adjs, length, elems, &(*aggregating_tag));MB_CHK_ERR(result);
98 }
99 else {
100 MB_SET_ERR(MB_FAILURE, "Either partition tags or sets for Metis partitoner");
101 }
102
103 if (print_time)
104 {
105 std::cout << " time to assemble graph: " << (clock() - t) / (double) CLOCKS_PER_SEC << "s. \n";
106 t = clock();
107 }
108
109 std::cout << "Computing partition using " << method
110 <<" method for " << nparts << " processors..." << std::endl;
111
112 idx_t nelems = length.size()-1;
113 idx_t *assign_parts;
114 assign_parts = (idx_t *)malloc(sizeof(idx_t) * nelems);
115 idx_t nconstraidx_ts = 1;
116 idx_t edgeCut = 0;
117 idx_t nOfPartitions = static_cast<idx_t>(nparts);
118 idx_t metis_RESULT;
119
120 if (strcmp(method, "ML_KWAY") == 0)
121 {
122 idx_t options[METIS_NOPTIONS];
123 METIS_SetDefaultOptions(options);
124 options[METIS_OPTION_CONTIG] = 1;
125 metis_RESULT = METIS_PartGraphKway(&nelems, &nconstraidx_ts, &length[0], &adjs[0], NULL, NULL, NULL, &nOfPartitions, NULL, NULL, options, &edgeCut, assign_parts);
126 }
127 else if (strcmp(method, "ML_RB") == 0)
128 {
129 idx_t options[METIS_NOPTIONS];
130 METIS_SetDefaultOptions(options);
131 options[METIS_OPTION_OBJTYPE] = METIS_OBJTYPE_CUT; // CUT
132 options[METIS_OPTION_IPTYPE] = METIS_IPTYPE_GROW; // GROW or RANDOM
133 options[METIS_OPTION_CTYPE] = METIS_CTYPE_RM; // RM or SHEM
134 options[METIS_OPTION_RTYPE] = METIS_RTYPE_FM; // FM
135 options[METIS_OPTION_NCUTS] = 10; // Number of different partitionings to compute, then chooses the best one, default = 1
136 options[METIS_OPTION_NITER] = 10; // Number of refinements steps, default = 10
137 options[METIS_OPTION_UFACTOR] = 30; // Imabalance, default = 1
138 options[METIS_OPTION_DBGLVL] = METIS_DBG_INFO;
139 metis_RESULT = METIS_PartGraphRecursive(&nelems, &nconstraidx_ts, &length[0], &adjs[0], NULL, NULL, NULL, &nOfPartitions, NULL, NULL, options, &edgeCut, assign_parts);
140 }
141 else
142 MB_SET_ERR(MB_FAILURE, "Either ML_KWAY or ML_RB needs to be specified for Metis partitioner");
143
144 if (print_time)
145 {
146 std::cout << " time to partition: " << (clock() - t) / (double) CLOCKS_PER_SEC << "s. \n";
147 t = clock();
148 }
149
150 #ifdef MOAB_HAVE_MPI
151 // assign global node ids, starting from one! TODO
152 result = mbpc->assign_global_ids(0, 0, 1);MB_CHK_ERR(result);
153 #endif
154
155 if (metis_RESULT != METIS_OK)
156 return MB_FAILURE;
157
158 // take results & write onto MOAB partition sets
159 std::cout << "Saving partition information to MOAB..." << std::endl;
160 {
161 if (partition_tagged_sets || partition_tagged_ents) {
162 result = write_aggregationtag_partition(nparts, elems, assign_parts,
163 write_as_sets, write_as_tags);MB_CHK_ERR(result);
164 }
165 else {
166 result = write_partition(nparts, elems, assign_parts,
167 write_as_sets, write_as_tags);MB_CHK_ERR(result);
168 }
169 }
170
171 if (print_time)
172 {
173 std::cout << " time to write partition in memory " <<(clock() - t) / (double) CLOCKS_PER_SEC << "s. \n";
174 t = clock();
175 }
176 free(assign_parts);
177
178 return MB_SUCCESS;
179 }
180
assemble_taggedents_graph(const int dimension,std::vector<double> & coords,std::vector<idx_t> & moab_ids,std::vector<idx_t> & adjacencies,std::vector<idx_t> & length,Range & elems,const char * aggregating_tag)181 ErrorCode MetisPartitioner::assemble_taggedents_graph(const int dimension,
182 std::vector<double> &coords,
183 std::vector<idx_t> &moab_ids,
184 std::vector<idx_t> &adjacencies,
185 std::vector<idx_t> &length,
186 Range &elems,
187 const char *aggregating_tag)
188 {
189 Tag partSetTag;
190 ErrorCode result = mbImpl->tag_get_handle(aggregating_tag, 1, MB_TYPE_INTEGER, partSetTag);
191 if (MB_SUCCESS != result) return result;
192
193 Range allSubElems;
194 result = mbImpl->get_entities_by_dimension(0, dimension, allSubElems);
195 if (MB_SUCCESS != result || allSubElems.empty()) return result;
196 idx_t partSet;
197 std::map<idx_t, Range> aggloElems;
198 for (Range::iterator rit = allSubElems.begin(); rit != allSubElems.end(); rit++)
199 {
200 EntityHandle entity = *rit;
201 result = mbImpl->tag_get_data(partSetTag,&entity,1,&partSet);
202 if (MB_SUCCESS != result) return result;
203 if (partSet >= 0)
204 aggloElems[partSet].insert(entity);
205 }
206 // clear aggregating tag data
207 TagType type;
208 result = mbImpl->tag_get_type(partSetTag, type);
209 if (type == MB_TAG_DENSE)
210 {
211 // clear tag on ents and sets
212 result = mbImpl->tag_delete(partSetTag);
213 if (MB_SUCCESS != result) return result;
214 }
215 if (type == MB_TAG_SPARSE)
216 {
217 // clear tag on ents
218 result = mbImpl->tag_delete_data(partSetTag, allSubElems);
219 if (MB_SUCCESS != result) return result;
220 // clear tag on sets
221 result = mbImpl->get_entities_by_type_and_tag(0 , MBENTITYSET, &partSetTag, 0, 1, elems);
222 if (MB_SUCCESS != result) return result;
223 result = mbImpl->tag_delete_data(partSetTag, elems);
224 if (MB_SUCCESS != result) return result;
225 elems.clear();
226 }
227 result = mbImpl->tag_get_handle("PARALLEL_PARTITION", 1, MB_TYPE_INTEGER,
228 partSetTag, MB_TAG_SPARSE|MB_TAG_CREAT);
229 if (MB_SUCCESS != result) return result;
230
231 for (std::map<idx_t, Range>::iterator mit = aggloElems.begin(); mit != aggloElems.end(); mit++)
232 {
233 EntityHandle new_set;
234 result = mbImpl->create_meshset(MESHSET_SET, new_set);
235 if (MB_SUCCESS != result) return result;
236 result = mbImpl->add_entities(new_set, mit->second);
237 if (MB_SUCCESS != result) return result;
238 result = mbImpl->tag_set_data (partSetTag, &new_set, 1, &mit->first);
239 if (MB_SUCCESS != result) return result;
240 }
241
242 result = assemble_taggedsets_graph(dimension, coords, moab_ids, adjacencies, length, elems, &(*aggregating_tag));
243 return MB_SUCCESS;
244 }
245
assemble_taggedsets_graph(const int dimension,std::vector<double> & coords,std::vector<idx_t> & moab_ids,std::vector<idx_t> & adjacencies,std::vector<idx_t> & length,Range & elems,const char * aggregating_tag)246 ErrorCode MetisPartitioner::assemble_taggedsets_graph(const int dimension,
247 std::vector<double> &coords,
248 std::vector<idx_t> &moab_ids,
249 std::vector<idx_t> &adjacencies,
250 std::vector<idx_t> &length,
251 Range &elems,
252 const char *aggregating_tag)
253 {
254 length.push_back(0);
255 // assemble a graph with vertices equal to elements of specified dimension, edges
256 // signified by list of other elements to which an element is connected
257
258 // get the tagged elements
259 Tag partSetTag;
260 ErrorCode result = mbImpl->tag_get_handle(aggregating_tag, 1, MB_TYPE_INTEGER, partSetTag);MB_CHK_ERR(result);
261 //ErrorCode result = mbImpl->tag_get_handle("PARALLEL_PARTITION_SET", 1, MB_TYPE_INTEGER, partSetTag);MB_CHK_ERR(result);
262
263 result = mbImpl->get_entities_by_type_and_tag(0 , MBENTITYSET, &partSetTag, 0, 1, elems);
264 if (MB_SUCCESS != result || elems.empty()) return result;
265
266 //assign globla ids to elem sets based on aggregating_tag data
267 Tag gid_tag;
268 idx_t zero1 = -1;
269 result = mbImpl->tag_get_handle("GLOBAL_ID_AGGLO", 1, MB_TYPE_INTEGER, gid_tag, MB_TAG_SPARSE|MB_TAG_CREAT, &zero1);MB_CHK_ERR(result);
270 for (Range::iterator rit = elems.begin(); rit != elems.end(); rit++)
271 {
272 idx_t partSet;
273 result = mbImpl->tag_get_data(partSetTag,&(*rit),1,&partSet);MB_CHK_ERR(result);
274 result = mbImpl->tag_set_data(gid_tag, &(*rit), 1, &partSet);MB_CHK_ERR(result);
275 }
276 // clear aggregating tag data
277 TagType type;
278 result = mbImpl->tag_get_type(partSetTag, type);MB_CHK_ERR(result);
279 if (type == MB_TAG_DENSE)
280 {
281 result = mbImpl->tag_delete(partSetTag);MB_CHK_ERR(result);
282 }
283 if (type == MB_TAG_SPARSE)
284 {
285 result = mbImpl->tag_delete_data(partSetTag, elems);MB_CHK_ERR(result);
286 }
287
288 // assemble the graph, using Skinner to get d-1 dimensional neighbors and then idx_tersecting to get adjacencies
289 std::vector<Range> skin_subFaces(elems.size());
290 unsigned int i = 0;
291 for (Range::iterator rit = elems.begin(); rit != elems.end(); rit++)
292 {
293 Range part_ents;
294 result = mbImpl->get_entities_by_handle(*rit, part_ents, false);
295 if (mbImpl->dimension_from_handle(*part_ents.rbegin()) != mbImpl->dimension_from_handle(*part_ents.begin()))
296 {
297 Range::iterator lower = part_ents.lower_bound(CN::TypeDimensionMap[0].first),
298 upper = part_ents.upper_bound(CN::TypeDimensionMap[dimension-1].second);
299 part_ents.erase(lower, upper);
300 }
301 Skinner skinner(mbImpl);
302 result = skinner.find_skin(0, part_ents, false, skin_subFaces[i], NULL, false, true, false);MB_CHK_ERR(result);
303 i++;
304 }
305 std::vector<EntityHandle> adjs;
306 std::vector<idx_t> neighbors;
307 double avg_position[3];
308 idx_t moab_id;
309 MeshTopoUtil mtu(mbImpl);
310 for (unsigned int k = 0; k < i; k++)
311 {
312 // get bridge adjacencies for element k
313 adjs.clear();
314 for (unsigned int t = 0; t < i; t++)
315 {
316 if (t != k)
317 {
318 Range subFaces = intersect(skin_subFaces[k],skin_subFaces[t]);
319 if (subFaces.size() > 0)
320 adjs.push_back(elems[t]);
321 }
322 }
323 if (!adjs.empty())
324 {
325 neighbors.resize(adjs.size());
326 result = mbImpl->tag_get_data(gid_tag, &adjs[0], adjs.size(), &neighbors[0]);MB_CHK_ERR(result);
327 }
328 // copy those idx_to adjacencies vector
329 length.push_back(length.back()+(idx_t)adjs.size());
330 std::copy(neighbors.begin(), neighbors.end(), std::back_inserter(adjacencies));
331 // get the graph vertex id for this element
332 const EntityHandle& setk = elems[k];
333 result = mbImpl->tag_get_data(gid_tag, &setk, 1, &moab_id);
334 moab_ids.push_back(moab_id);
335 // get average position of vertices
336 Range part_ents;
337 result = mbImpl->get_entities_by_handle(elems[k], part_ents, false);MB_CHK_ERR(result);
338 result = mtu.get_average_position(part_ents, avg_position);MB_CHK_ERR(result);
339 std::copy(avg_position, avg_position+3, std::back_inserter(coords));
340 }
341 for (unsigned int k = 0; k < i; k++)
342 {
343 for (unsigned int t = 0; t < k; t++)
344 {
345 Range subFaces = intersect(skin_subFaces[k],skin_subFaces[t]);
346 if (subFaces.size() > 0)
347 mbImpl->delete_entities(subFaces);
348 }
349 }
350
351 if (debug) {
352 std::cout << "Length vector: " << std::endl;
353 std::copy(length.begin(), length.end(), std::ostream_iterator<idx_t>(std::cout, ", "));
354 std::cout << std::endl;
355 std::cout << "Adjacencies vector: " << std::endl;
356 std::copy(adjacencies.begin(), adjacencies.end(), std::ostream_iterator<idx_t>(std::cout, ", "));
357 std::cout << std::endl;
358 std::cout << "Moab_ids vector: " << std::endl;
359 std::copy(moab_ids.begin(), moab_ids.end(), std::ostream_iterator<idx_t>(std::cout, ", "));
360 std::cout << std::endl;
361 std::cout << "Coords vector: " << std::endl;
362 std::copy(coords.begin(), coords.end(), std::ostream_iterator<double>(std::cout, ", "));
363 std::cout << std::endl;
364 }
365 return MB_SUCCESS;
366 }
367
assemble_graph(const int dimension,std::vector<double> & coords,std::vector<idx_t> & moab_ids,std::vector<idx_t> & adjacencies,std::vector<idx_t> & length,Range & elems)368 ErrorCode MetisPartitioner::assemble_graph(const int dimension,
369 std::vector<double> &coords,
370 std::vector<idx_t> &moab_ids,
371 std::vector<idx_t> &adjacencies,
372 std::vector<idx_t> &length,
373 Range &elems)
374 {
375 length.push_back(0);
376 // assemble a graph with vertices equal to elements of specified dimension, edges
377 // signified by list of other elements to which an element is connected
378
379 // get the elements of that dimension
380 ErrorCode result = mbImpl->get_entities_by_dimension(0, dimension, elems);
381 if (MB_SUCCESS != result || elems.empty()) return result;
382
383 #ifdef MOAB_HAVE_MPI
384 // assign global ids
385 result = mbpc->assign_global_ids(0, dimension, 0);
386 #endif
387
388 // now assemble the graph, calling MeshTopoUtil to get bridge adjacencies through d-1 dimensional
389 // neighbors
390 MeshTopoUtil mtu(mbImpl);
391 Range adjs;
392 // can use a fixed-size array 'cuz the number of lower-dimensional neighbors is limited
393 // by MBCN
394 int neighbors[5*MAX_SUB_ENTITIES]; // these are global ids, they will be int
395
396 double avg_position[3];
397 int moab_id;
398
399 // get the global id tag hanlde
400 Tag gid;
401 result = mbImpl->tag_get_handle(GLOBAL_ID_TAG_NAME, 1, MB_TYPE_INTEGER,
402 gid, MB_TAG_DENSE|MB_TAG_CREAT);MB_CHK_ERR(result);
403
404 for (Range::iterator rit = elems.begin(); rit != elems.end(); rit++) {
405
406 // get bridge adjacencies
407 adjs.clear();
408 result = mtu.get_bridge_adjacencies(*rit, (dimension > 0 ? dimension-1 : 3),
409 dimension, adjs);MB_CHK_ERR(result);
410
411 // get the graph vertex ids of those
412 if (!adjs.empty()) {
413 assert(adjs.size() < 5*MAX_SUB_ENTITIES);
414 result = mbImpl->tag_get_data(gid, adjs, neighbors);MB_CHK_ERR(result);
415 }
416
417 // copy those idx_to adjacencies vector
418 length.push_back(length.back()+(idx_t)adjs.size());
419 // conversion made to idx_t
420 std::copy(neighbors, neighbors+adjs.size(), std::back_inserter(adjacencies));
421
422 // get average position of vertices
423 result = mtu.get_average_position(*rit, avg_position);MB_CHK_ERR(result);
424
425 // get the graph vertex id for this element
426 result = mbImpl->tag_get_data(gid, &(*rit), 1, &moab_id);MB_CHK_ERR(result);
427
428 // copy those idx_to coords vector
429 moab_ids.push_back(moab_id); // conversion made to idx_t
430 std::copy(avg_position, avg_position+3, std::back_inserter(coords));
431 }
432
433 if (debug) {
434 std::cout << "Length vector: " << std::endl;
435 std::copy(length.begin(), length.end(), std::ostream_iterator<idx_t>(std::cout, ", "));
436 std::cout << std::endl;
437 std::cout << "Adjacencies vector: " << std::endl;
438 std::copy(adjacencies.begin(), adjacencies.end(), std::ostream_iterator<idx_t>(std::cout, ", "));
439 std::cout << std::endl;
440 std::cout << "Moab_ids vector: " << std::endl;
441 std::copy(moab_ids.begin(), moab_ids.end(), std::ostream_iterator<idx_t>(std::cout, ", "));
442 std::cout << std::endl;
443 std::cout << "Coords vector: " << std::endl;
444 std::copy(coords.begin(), coords.end(), std::ostream_iterator<double>(std::cout, ", "));
445 std::cout << std::endl;
446 }
447
448 return MB_SUCCESS;
449 }
450
write_aggregationtag_partition(const idx_t nparts,Range & elems,const idx_t * assignment,const bool write_as_sets,const bool write_as_tags)451 ErrorCode MetisPartitioner::write_aggregationtag_partition(const idx_t nparts,
452 Range &elems,
453 const idx_t *assignment,
454 const bool write_as_sets,
455 const bool write_as_tags)
456 {
457 ErrorCode result;
458
459 // get the partition set tag
460 Tag part_set_tag;
461 result = mbImpl->tag_get_handle("PARALLEL_PARTITION", 1, MB_TYPE_INTEGER,
462 part_set_tag, MB_TAG_SPARSE|MB_TAG_CREAT);MB_CHK_ERR(result);
463
464 // get any sets already with this tag, and clear them
465 Range tagged_sets;
466 result = mbImpl->get_entities_by_type_and_tag(0, MBENTITYSET, &part_set_tag, NULL, 1,
467 tagged_sets, Interface::UNION);MB_CHK_ERR(result);
468 if (!tagged_sets.empty()) {
469 result = mbImpl->clear_meshset(tagged_sets);
470 if (!write_as_sets) {
471 result = mbImpl->tag_delete_data(part_set_tag, tagged_sets);MB_CHK_ERR(result);
472 }
473 }
474
475 if (write_as_sets) {
476 // first, create partition sets and store in vector
477 partSets.clear();
478
479 if (nparts > (idx_t) tagged_sets.size()) {
480 // too few partition sets - create missing ones
481 idx_t num_new = nparts - tagged_sets.size();
482 for (idx_t i = 0; i < num_new; i++) {
483 EntityHandle new_set;
484 result = mbImpl->create_meshset(MESHSET_SET, new_set);MB_CHK_ERR(result);
485 tagged_sets.insert(new_set);
486 }
487 }
488 else if (nparts < (idx_t) tagged_sets.size()) {
489 // too many partition sets - delete extras
490 idx_t num_del = tagged_sets.size() - nparts;
491 for (idx_t i = 0; i < num_del; i++) {
492 EntityHandle old_set = tagged_sets.pop_back();
493 result = mbImpl->delete_entities(&old_set, 1);MB_CHK_ERR(result);
494 }
495 }
496
497 // assign partition sets to vector
498 partSets.swap(tagged_sets);
499
500 // write a tag to those sets denoting they're partition sets, with a value of the
501 // proc number
502 idx_t *dum_ids = new idx_t[nparts];
503 for (idx_t i = 0; i < nparts; i++) dum_ids[i] = i;
504
505 result = mbImpl->tag_set_data(part_set_tag, partSets, dum_ids);MB_CHK_ERR(result);
506
507 // assign entities to the relevant sets
508 std::vector<EntityHandle> tmp_part_sets;
509 std::copy(partSets.begin(), partSets.end(), std::back_inserter(tmp_part_sets));
510 Range::iterator rit;
511 unsigned j=0;
512 for (rit = elems.begin(); rit != elems.end(); rit++, j++) {
513 result = mbImpl->add_entities(tmp_part_sets[assignment[j]], &(*rit), 1);MB_CHK_ERR(result);
514 }
515
516 // check for empty sets, warn if there are any
517 Range empty_sets;
518 for (rit = partSets.begin(); rit != partSets.end(); rit++) {
519 int num_ents = 0;
520 result = mbImpl->get_number_entities_by_handle(*rit, num_ents);
521 if (MB_SUCCESS != result || !num_ents) empty_sets.insert(*rit);
522 }
523 if (!empty_sets.empty()) {
524 std::cout << "WARNING: " << empty_sets.size() << " empty sets in partition: ";
525 for (rit = empty_sets.begin(); rit != empty_sets.end(); rit++)
526 std::cout << *rit << " ";
527 std::cout << std::endl;
528 }
529 }
530
531 if (write_as_tags) {
532 Tag gid_tag;
533 result = mbImpl->tag_get_handle("GLOBAL_ID_AGGLO", 1, MB_TYPE_INTEGER, gid_tag, MB_TAG_SPARSE);MB_CHK_ERR(result);
534
535 // allocate idx_teger-size partitions
536 unsigned int i = 0;
537 idx_t gid;
538 for (Range::iterator rit = elems.begin(); rit != elems.end(); rit++)
539 {
540 result = mbImpl->tag_get_data(gid_tag, &(*rit), 1, &gid);
541 Range part_ents;
542 // std::cout<<"part ents "<<part_ents.size()<<std::endl;
543 result = mbImpl->get_entities_by_handle(*rit, part_ents, false);MB_CHK_ERR(result);
544
545 for (Range::iterator eit = part_ents.begin(); eit != part_ents.end(); eit++)
546 {
547 result = mbImpl->tag_set_data(part_set_tag, &(*eit), 1, &assignment[i]);MB_CHK_ERR(result);
548
549 result = mbImpl->tag_set_data(gid_tag, &(*eit), 1, &gid);MB_CHK_ERR(result);
550 }
551 i++;
552 }
553 }
554 return MB_SUCCESS;
555 }
556
write_partition(const idx_t nparts,Range & elems,const idx_t * assignment,const bool write_as_sets,const bool write_as_tags)557 ErrorCode MetisPartitioner::write_partition(const idx_t nparts,
558 Range &elems,
559 const idx_t *assignment,
560 const bool write_as_sets,
561 const bool write_as_tags)
562 {
563 ErrorCode result;
564
565 // get the partition set tag
566 Tag part_set_tag;
567 idx_t dum_id = -1, i;
568 result = mbImpl->tag_get_handle("PARALLEL_PARTITION", 1, MB_TYPE_INTEGER,
569 part_set_tag, MB_TAG_SPARSE|MB_TAG_CREAT, &dum_id);MB_CHK_ERR(result);
570
571 // get any sets already with this tag, and clear them
572 Range tagged_sets;
573 result = mbImpl->get_entities_by_type_and_tag(0, MBENTITYSET, &part_set_tag, NULL, 1,
574 tagged_sets, Interface::UNION);MB_CHK_ERR(result);
575 if (!tagged_sets.empty()) {
576 result = mbImpl->clear_meshset(tagged_sets);
577 if (!write_as_sets) {
578 result = mbImpl->tag_delete_data(part_set_tag, tagged_sets);MB_CHK_ERR(result);
579 }
580 }
581
582 if (write_as_sets) {
583 // first, create partition sets and store in vector
584 partSets.clear();
585
586 if (nparts > (int) tagged_sets.size()) {
587 // too few partition sets - create missing ones
588 idx_t num_new = nparts - tagged_sets.size();
589 for (i = 0; i < num_new; i++) {
590 EntityHandle new_set;
591 result = mbImpl->create_meshset(MESHSET_SET, new_set);MB_CHK_ERR(result);
592 tagged_sets.insert(new_set);
593 }
594 }
595 else if (nparts < (idx_t) tagged_sets.size()) {
596 // too many partition sets - delete extras
597 idx_t num_del = tagged_sets.size() - nparts;
598 for (i = 0; i < num_del; i++) {
599 EntityHandle old_set = tagged_sets.pop_back();
600 result = mbImpl->delete_entities(&old_set, 1);MB_CHK_ERR(result);
601 }
602 }
603
604 // assign partition sets to vector
605 partSets.swap(tagged_sets);
606
607 // write a tag to those sets denoting they're partition sets, with a value of the
608 // proc number
609 int *dum_ids = new int[nparts]; // this remains integer
610 for (i = 0; i < nparts; i++) dum_ids[i] = i;
611
612 result = mbImpl->tag_set_data(part_set_tag, partSets, dum_ids);
613 delete [] dum_ids;
614
615 // assign entities to the relevant sets
616 std::vector<EntityHandle> tmp_part_sets;
617 std::copy(partSets.begin(), partSets.end(), std::back_inserter(tmp_part_sets));
618 Range::iterator rit;
619 for (i = 0, rit = elems.begin(); rit != elems.end(); rit++, i++) {
620 result = mbImpl->add_entities(tmp_part_sets[assignment[i]], &(*rit), 1);MB_CHK_ERR(result);
621 }
622
623 // check for empty sets, warn if there are any
624 Range empty_sets;
625 for (rit = partSets.begin(); rit != partSets.end(); rit++) {
626 int num_ents = 0;
627 result = mbImpl->get_number_entities_by_handle(*rit, num_ents);
628 if (MB_SUCCESS != result || !num_ents) empty_sets.insert(*rit);
629 }
630 if (!empty_sets.empty()) {
631 std::cout << "WARNING: " << empty_sets.size() << " empty sets in partition: ";
632 for (rit = empty_sets.begin(); rit != empty_sets.end(); rit++)
633 std::cout << *rit << " ";
634 std::cout << std::endl;
635 }
636 }
637
638 if (write_as_tags) {
639 if (sizeof(int) != sizeof(idx_t))
640 {
641 // allocate idx_teger-size partitions
642 // first we have to copy to int, then assign
643 int * assg_int = new int [elems.size()];
644 for (int k=0; k<(int)elems.size(); k++)
645 assg_int [k] = assignment[k];
646 result = mbImpl->tag_set_data(part_set_tag, elems, assg_int); MB_CHK_ERR(result);
647 delete [] assg_int;
648 }
649 else
650 result = mbImpl->tag_set_data(part_set_tag, elems, assignment);MB_CHK_ERR(result);
651 }
652
653 return MB_SUCCESS;
654 }
655
656