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