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 // If Microsoft compiler, then WIN32
17 #if defined(_MSC_VER) || defined(__MINGW32__)
18 #  ifndef WIN32
19 #    define WIN32
20 #  endif
21 #endif
22 
23 #include "moab/Core.hpp"
24 #include "moab/Range.hpp"
25 #include "MBTagConventions.hpp"
26 #include "moab/ReaderWriterSet.hpp"
27 #include <iostream>
28 #include <fstream>
29 #include <sstream>
30 #include <iomanip>
31 #include <set>
32 #include <list>
33 #include <cstdlib>
34 #include <algorithm>
35 #ifndef WIN32
36 #  include <sys/times.h>
37 #  include <limits.h>
38 #  include <unistd.h>
39 #endif
40 #include <time.h>
41 #ifdef MOAB_HAVE_MPI
42 #  include "moab_mpi.h"
43 #endif
44 
45 #ifdef MOAB_HAVE_TEMPESTREMAP
46 #include "moab/Remapping/TempestRemapper.hpp"
47 #endif
48 
49 #include <stdio.h>
50 
51 /* Exit values */
52 #define USAGE_ERROR 1
53 #define READ_ERROR 2
54 #define WRITE_ERROR 3
55 #define OTHER_ERROR 4
56 #define ENT_NOT_FOUND 5
57 
58 using namespace moab;
59 
print_usage(const char * name,std::ostream & stream)60 static void print_usage( const char* name, std::ostream& stream )
61 {
62   stream << "Usage: " << name <<
63     " [-a <sat_file>|-A] [-t] [subset options] [-f format] <input_file> [<input_file2> ...] <output_file>" << std::endl
64     << "\t-f <format>    - Specify output file format" << std::endl
65     << "\t-a <acis_file> - ACIS SAT file dumped by .cub reader (same as \"-o SAT_FILE=acis_file\"" << std::endl
66     << "\t-A             - .cub file reader should not dump a SAT file (depricated default)" << std::endl
67     << "\t-o option      - Specify write option." << std::endl
68     << "\t-O option      - Specify read option." << std::endl
69     << "\t-t             - Time read and write of files." << std::endl
70     << "\t-g             - Enable verbose/debug output." << std::endl
71     << "\t-h             - Print this help text and exit." << std::endl
72     << "\t-l             - List available file formats and exit." << std::endl
73     << "\t-I <dim>       - Generate internal entities of specified dimension." << std::endl
74 #ifdef MOAB_HAVE_MPI
75     << "\t-P             - Append processor ID to output file name" << std::endl
76     << "\t-p             - Replace '%' with processor ID in input and output file name" << std::endl
77     << "\t-M[0|1|2]      - Read/write in parallel, optionally also doing resolve_shared_ents (1) and exchange_ghosts (2)" << std::endl
78     << "\t-z <file>      - Read metis partition information corresponding to an MPAS grid file and create h5m partition file" << std::endl
79 #endif
80 
81 #ifdef MOAB_HAVE_TEMPESTREMAP
82     << "\t-T             - Use tempest exodus file reader" << std::endl
83 #endif
84     << "\t--             - treat all subsequent options as file names" << std::endl
85     << "\t                 (allows file names beginning with '-')" << std::endl
86     << "  subset options: " << std::endl
87     << "\tEach of the following options should be followed by " << std::endl
88     << "\ta list of ids.  IDs may be separated with commas.  " << std::endl
89     << "\tRanges of IDs may be specified with a '-' between " << std::endl
90     << "\ttwo values.  The list may not contain spaces." << std::endl
91     << "\t-v  - Volume" << std::endl
92     << "\t-s  - Surface" << std::endl
93     << "\t-c  - Curve" << std::endl
94     << "\t-V  - Vertex" << std::endl
95     << "\t-m  - Material set (block)" << std::endl
96     << "\t-d  - Dirichlet set (nodeset)" << std::endl
97     << "\t-n  - Neumann set (sideset)" << std::endl
98     << "\t-D  - Parallel partitioning set (PARALLEL_PARTITION)" << std::endl
99     << "\tThe presence of one or more of the following flags limits " << std::endl
100     << "\tthe exported mesh to only elements of the corresponding " << std::endl
101     << "\tdimension.  Vertices are always exported." << std::endl
102     << "\t-1  - Edges " << std::endl
103     << "\t-2  - Tri, Quad, Polygon " << std::endl
104     << "\t-3  - Tet, Hex, Prism, etc. " << std::endl
105     ;
106 }
107 
print_help(const char * name)108 static void print_help( const char* name )
109 {
110   std::cout <<
111   " This program can be used to convert between mesh file\n"
112   " formats, extract a subset of a mesh file to a separate\n"
113   " file, or both.  The type of file to write is determined\n"
114   " from the file extension (e.g. \".vtk\") portion of the\n"
115   " output file name.\n"
116   " \n"
117   " While MOAB strives to export and import all data from\n"
118   " each supported file format, most file formats do\n"
119   " not support MOAB's entire data model.  Thus MOAB cannot\n"
120   " guarantee lossless conversion for any file formats\n"
121   " other than the native HDF5 representation.\n"
122   "\n";
123 
124   print_usage( name, std::cout );
125   exit(0);
126 }
127 
usage_error(const char * name)128 static void usage_error( const char* name )
129 {
130   print_usage( name, std::cerr );
131 #ifdef MOAB_HAVE_MPI
132   MPI_Finalize();
133 #endif
134   exit(USAGE_ERROR);
135 }
136 
137 
138 static void list_formats( Interface* );
139 static bool parse_id_list( const char* string, std::set<int>&  );
140 static void print_id_list( const char*, std::ostream& stream, const std::set<int>& list );
141 static void reset_times();
142 static void write_times( std::ostream& stream );
143 static void remove_entities_from_sets( Interface* gMB, Range& dead_entities, Range& empty_sets );
144 static void remove_from_vector( std::vector<EntityHandle>& vect, const Range& ents_to_remove );
145 static bool make_opts_string( std::vector<std::string> options, std::string& result );
146 static std::string percent_subst( const std::string& s, int val );
147 
148 static int process_partition_file(Interface * gMB, std::string & metis_partition_file);
149 
main(int argc,char * argv[])150 int main(int argc, char* argv[])
151 {
152   int proc_id = 0;
153 #ifdef MOAB_HAVE_MPI
154   MPI_Init(&argc,&argv);
155   MPI_Comm_rank( MPI_COMM_WORLD, &proc_id );
156 #endif
157 
158 #ifdef MOAB_HAVE_TEMPESTREMAP
159   bool tempest=false;
160 #endif
161 
162   Core core;
163   Interface* gMB = &core;
164   ErrorCode result;
165   Range range;
166 
167   bool append_rank = false;
168   bool percent_rank_subst = false;
169   int i, dim;
170   std::list< std::string >::iterator j;
171   bool dims[4] = {false, false, false, false};
172   const char* format = NULL; // output file format
173   std::list< std::string > in; // input file name list
174   std::string out;   // output file name
175   bool verbose = false;
176   std::set<int> geom[4], mesh[4];       // user-specified IDs
177   std::vector<EntityHandle> set_list; // list of user-specified sets to write
178   std::vector<std::string> write_opts, read_opts;
179   std::string metis_partition_file;
180 
181   const char* const mesh_tag_names[] = { DIRICHLET_SET_TAG_NAME,
182                                          NEUMANN_SET_TAG_NAME,
183                                          MATERIAL_SET_TAG_NAME,
184                                          PARALLEL_PARTITION_TAG_NAME };
185   const char* const geom_names[] = { "VERTEX",
186                                      "CURVE",
187                                      "SURFACE",
188                                      "VOLUME" };
189 
190     // scan arguments
191   bool do_flag = true;
192   bool print_times = false;
193   bool generate[] = { false, false, false };
194   bool pval;
195   bool parallel = false, resolve_shared = false, exchange_ghosts = false;
196   for (i = 1; i < argc; i++)
197   {
198     if (!argv[i][0])
199       usage_error(argv[0]);
200 
201     if (do_flag && argv[i][0] == '-')
202     {
203       if (!argv[i][1] || (argv[i][1] != 'M' && argv[i][2]))
204         usage_error(argv[0]);
205 
206       switch ( argv[i][1] )
207       {
208           // do flag arguments:
209         case '-': do_flag = false;       break;
210         case 'g': verbose = true;        break;
211         case 't': print_times = true;    break;
212         case 'A':                        break;
213         case 'h':
214         case 'H': print_help( argv[0] ); break;
215         case 'l': list_formats( gMB );   break;
216 #ifdef MOAB_HAVE_MPI
217         case 'P': append_rank = true;    break;
218         case 'p': percent_rank_subst = true; break;
219         case 'M':
220             parallel = true;
221             if (argv[i][2] == '1' || argv[i][2] == '2') resolve_shared = true;
222             if (argv[i][2] == '2') exchange_ghosts = true;
223 #endif
224 #ifdef MOAB_HAVE_TEMPESTREMAP
225         case 'T':      tempest=true;    break;
226 #endif
227         case '1': case '2': case '3':
228           dims[argv[i][1] - '0'] = true; break;
229           // do options that require additional args:
230         default:
231           ++i;
232           if (i == argc || argv[i][0] == '-') {
233             std::cerr << "Expected argument following " << argv[i-1] << std::endl;
234             usage_error(argv[0]);
235           }
236           if (argv[i-1][1] == 'I') {
237             dim = atoi( argv[i] );
238             if (dim < 1 || dim > 2) {
239               std::cerr << "Invalid dimension value following -I" << std::endl;
240               usage_error(argv[0]);
241             }
242             generate[dim] = true;
243             continue;
244           }
245           pval = false;
246           switch ( argv[i-1][1] )
247           {
248             case 'a':
249               read_opts.push_back( std::string("SAT_FILE=") + argv[i] );
250               pval = true;
251               break;
252             case 'f': format = argv[i]; pval = true;              break;
253             case 'o': write_opts.push_back(argv[i]); pval = true; break;
254             case 'O':  read_opts.push_back(argv[i]); pval = true; break;
255             case 'v': pval = parse_id_list( argv[i], geom[3] ); break;
256             case 's': pval = parse_id_list( argv[i], geom[2] ); break;
257             case 'c': pval = parse_id_list( argv[i], geom[1] ); break;
258             case 'V': pval = parse_id_list( argv[i], geom[0] ); break;
259             case 'D': pval = parse_id_list( argv[i], mesh[3] ); break;
260             case 'm': pval = parse_id_list( argv[i], mesh[2] ); break;
261             case 'n': pval = parse_id_list( argv[i], mesh[1] ); break;
262             case 'd': pval = parse_id_list( argv[i], mesh[0] ); break;
263             case 'z':
264               metis_partition_file = argv[i];
265               pval = true;
266               break;
267             default: std::cerr << "Invalid option: " << argv[i] << std::endl;
268           }
269 
270           if (!pval) {
271             std::cerr << "Invalid flag or flag value: " << argv[i-1] << " " << argv[i] << std::endl;
272             usage_error(argv[0]);
273           }
274       }
275     }
276       // do file names
277     else {
278       in.push_back( argv[i] );
279     }
280   }
281   if (in.size() < 2) {
282     std::cerr << "No output file name specified." << std::endl;
283     usage_error(argv[0]);
284   }
285     // output file name is the last one specified
286   out = in.back();
287   in.pop_back();
288 
289   if (append_rank) {
290     std::ostringstream mod;
291     mod << out << "." << proc_id;
292     out = mod.str();
293   }
294 
295   if (percent_rank_subst) {
296     for (j = in.begin(); j != in.end(); ++j)
297       *j = percent_subst( *j , proc_id );
298     out = percent_subst( out, proc_id );
299   }
300 
301     // construct options string from individual options
302   std::string read_options, write_options;
303   if (parallel) {
304     read_opts.push_back("PARALLEL=READ_PART");
305     read_opts.push_back("PARTITION=PARALLEL_PARTITION");
306     if (!append_rank && !percent_rank_subst)
307       write_opts.push_back("PARALLEL=WRITE_PART");
308   }
309   if (resolve_shared) read_opts.push_back("PARALLEL_RESOLVE_SHARED_ENTS");
310   if (exchange_ghosts) read_opts.push_back("PARALLEL_GHOSTS=3.0.1");
311 
312   if (!make_opts_string(  read_opts,  read_options ) ||
313       !make_opts_string( write_opts, write_options ))
314   {
315 #ifdef MOAB_HAVE_MPI
316     MPI_Finalize();
317 #endif
318     return USAGE_ERROR;
319   }
320 
321   if (!metis_partition_file.empty())
322   {
323     if ( (in.size()!=1) || (proc_id!=0) )
324     {
325       std::cerr<<" mpas partition allows only one input file, in serial conversion\n";
326 #ifdef MOAB_HAVE_MPI
327       MPI_Finalize();
328 #endif
329       return USAGE_ERROR;
330     }
331   }
332     // Read the input file.
333 #ifdef MOAB_HAVE_TEMPESTREMAP
334   if (tempest && in.size()>1)
335   {
336     std::cerr<<" we can read only one tempest files at a time\n";
337 #ifdef MOAB_HAVE_MPI
338     MPI_Finalize();
339 #endif
340     return USAGE_ERROR;
341   }
342 #endif
343   for (j = in.begin(); j != in.end(); ++j) {
344     reset_times();
345 #ifdef MOAB_HAVE_TEMPESTREMAP
346     if (tempest)
347     {
348       // convert
349       TempestRemapper *remapper = new moab::TempestRemapper(gMB);
350       remapper->meshValidate = true;
351       //remapper->constructEdgeMap = true;
352       remapper->initialize();
353 
354 
355       result = remapper->LoadMesh(moab::Remapper::SourceMesh, *j, moab::TempestRemapper::DEFAULT);MB_CHK_ERR(result);
356 
357         // Load the meshes and validate
358       result = remapper->ConvertTempestMesh(moab::Remapper::SourceMesh);
359       delete remapper;
360     }
361     else
362       result = gMB->load_file( j->c_str(), 0, read_options.c_str() );
363 #else
364     result = gMB->load_file( j->c_str(), 0, read_options.c_str() );
365 #endif
366     if (MB_SUCCESS != result)
367     {
368       std::cerr << "Failed to load \"" << *j << "\"." << std::endl;
369       std::cerr  << "Error code: " << gMB->get_error_string(result) << " (" << result << ")" << std::endl;
370       std::string message;
371       if (MB_SUCCESS == gMB->get_last_error(message) && !message.empty())
372         std::cerr << "Error message: " << message << std::endl;
373   #ifdef MOAB_HAVE_MPI
374       MPI_Finalize();
375   #endif
376       return READ_ERROR;
377     }
378     if (!proc_id) std::cerr << "Read \"" << *j << "\"" << std::endl;
379     if (print_times && !proc_id) write_times( std::cout );
380   }
381 
382     // Determine if the user has specified any geometry sets to write
383   bool have_geom = false;
384   for (dim = 0; dim <= 3; ++dim)
385   {
386     if (!geom[dim].empty())
387       have_geom = true;
388     if (verbose)
389       print_id_list( geom_names[dim], std::cout, geom[dim] );
390   }
391 
392     // True if the user has specified any sets to write
393   bool have_sets = have_geom;
394 
395     // Get geometry tags
396   Tag dim_tag, id_tag;
397   if (have_geom)
398   {
399     result = gMB->tag_get_handle( GLOBAL_ID_TAG_NAME, 1, MB_TYPE_INTEGER, id_tag );
400     if (MB_SUCCESS != result)
401     {
402       std::cerr << "No ID tag defined."  << std::endl;
403       have_geom = false;
404     }
405     result = gMB->tag_get_handle( GEOM_DIMENSION_TAG_NAME, 1, MB_TYPE_INTEGER, dim_tag );
406     if (MB_SUCCESS != result)
407     {
408       std::cerr << "No geometry tag defined."  << std::endl;
409       have_geom = false;
410     }
411   }
412 
413     // Get geometry sets
414   if ( have_geom )
415   {
416     int id_val;
417     Tag tags[] = { id_tag, dim_tag };
418     const void* vals[] = { &id_val, &dim };
419     for (dim = 0; dim <= 3; ++dim)
420     {
421       int init_count = set_list.size();
422       for (std::set<int>::iterator iter = geom[dim].begin(); iter != geom[dim].end(); ++iter)
423       {
424         id_val = *iter;
425         range.clear();
426         result = gMB->get_entities_by_type_and_tag( 0, MBENTITYSET, tags, vals, 2, range );
427         if (MB_SUCCESS != result || range.empty())
428         {
429           range.clear();
430           std::cerr << geom_names[dim] << " " << id_val << " not found.\n";
431         }
432         std::copy( range.begin(), range.end(), std::back_inserter(set_list) );
433       }
434 
435       if (verbose)
436         std::cout << "Found " << (set_list.size()-init_count) << ' '
437                   << geom_names[dim] << " sets" << std::endl;
438     }
439   }
440 
441     // Get mesh groupings
442   for (i = 0; i < 4; ++i)
443   {
444     if (verbose)
445       print_id_list( mesh_tag_names[i], std::cout, mesh[i] );
446 
447     if (mesh[i].empty())
448       continue;
449     have_sets = true;
450 
451       // Get tag
452     Tag tag;
453     result = gMB->tag_get_handle( mesh_tag_names[i], 1, MB_TYPE_INTEGER, tag );
454     if (MB_SUCCESS != result)
455     {
456       std::cerr << "Tag not found: " << mesh_tag_names[i] << std::endl;
457       continue;
458     }
459 
460       // get entity sets
461     int init_count = set_list.size();
462     for (std::set<int>::iterator iter = mesh[i].begin(); iter != mesh[i].end(); ++iter)
463     {
464       range.clear();
465       const void* vals[] = { &*iter };
466       result = gMB->get_entities_by_type_and_tag( 0, MBENTITYSET, &tag, vals, 1, range );
467       if (MB_SUCCESS != result || range.empty())
468       {
469         range.clear();
470         std::cerr << mesh_tag_names[i] << " " << *iter << " not found.\n";
471       }
472       std::copy( range.begin(), range.end(), std::back_inserter(set_list) );
473     }
474 
475     if (verbose)
476       std::cout << "Found " << (set_list.size()-init_count) << ' '
477                 << mesh_tag_names[i] << " sets" << std::endl;
478   }
479 
480     // Check if output is limited to certain dimensions of elements
481   bool bydim = false;
482   for (dim = 1; dim < 4; ++dim)
483     if (dims[dim])
484       bydim = true;
485 
486     // Check conflicting input
487   if (bydim) {
488     if (generate[1] && !dims[1]) {
489       std::cerr << "Warning: Request to generate 1D internal entities but not export them." << std::endl;
490       generate[1] = false;
491     }
492      if (generate[2] && !dims[2]) {
493       std::cerr << "Warning: Request to generate 2D internal entities but not export them." << std::endl;
494       generate[2] = false;
495     }
496   }
497 
498     // Generate any internal entities
499   if (generate[1] || generate[2]) {
500     EntityHandle all_mesh = 0;
501     const EntityHandle* sets = &all_mesh;
502     int num_sets = 1;
503     if (have_sets) {
504       num_sets = set_list.size();
505       sets = &set_list[0];
506     }
507     for (i = 0; i < num_sets; ++i) {
508       Range dim3, dim2, adj;
509       gMB->get_entities_by_dimension( sets[i], 3, dim3, true );
510       if (generate[1]) {
511         gMB->get_entities_by_dimension( sets[i], 2, dim2, true );
512         gMB->get_adjacencies( dim3, 1, true, adj, Interface::UNION );
513         gMB->get_adjacencies( dim2, 1, true, adj, Interface::UNION );
514       }
515       if (generate[2]) {
516         gMB->get_adjacencies( dim3, 2, true, adj, Interface::UNION );
517       }
518       if (sets[i])
519         gMB->add_entities( sets[i], adj );
520     }
521   }
522 
523     // Delete any entities not of the dimensions to be exported
524   if (bydim) {
525       // Get list of dead elements
526     Range dead_entities , tmp_range;
527     for (dim = 1; dim <= 3; ++dim) {
528       if (dims[dim])
529         continue;
530       gMB->get_entities_by_dimension(0, dim, tmp_range );
531       dead_entities.merge( tmp_range );
532     }
533       // Remove dead entities from all sets, and add all
534       // empty sets to list of dead entities.
535     Range empty_sets;
536     remove_entities_from_sets( gMB, dead_entities, empty_sets );
537     while (!empty_sets.empty()) {
538       if (!set_list.empty())
539         remove_from_vector( set_list, empty_sets );
540       dead_entities.merge( empty_sets );
541       tmp_range.clear();
542       remove_entities_from_sets( gMB, empty_sets, tmp_range );
543       empty_sets = subtract( tmp_range,  dead_entities );
544     }
545       // Destroy dead entities
546     gMB->delete_entities( dead_entities );
547   }
548 
549     // If user specified sets to write, but none were found, exit.
550   if (have_sets && set_list.empty())
551   {
552     std::cerr << "Nothing to write." << std::endl;
553 #ifdef MOAB_HAVE_MPI
554     MPI_Finalize();
555 #endif
556     return ENT_NOT_FOUND;
557   }
558 
559   // interpret the mpas partition file created by gpmetis
560   if (!metis_partition_file.empty())
561   {
562     int err = process_partition_file(gMB, metis_partition_file);
563     if (err)
564     {
565       std::cerr << "Failed to process partition file \"" << metis_partition_file << "\"." << std::endl;
566 #ifdef MOAB_HAVE_MPI
567       MPI_Finalize();
568 #endif
569       return WRITE_ERROR;
570     }
571   }
572   if (verbose)
573   {
574     if (have_sets)
575       std::cout << "Found " << set_list.size()
576               << " specified sets to write (total)." << std::endl;
577     else
578       std::cout << "No sets specifed.  Writing entire mesh." << std::endl;
579   }
580 
581     // Write the output file
582   reset_times();
583   if (have_sets)
584     result = gMB->write_file( out.c_str(), format, write_options.c_str(), &set_list[0], set_list.size() );
585   else
586     result = gMB->write_file( out.c_str(), format, write_options.c_str() );
587   if (MB_SUCCESS != result)
588   {
589     std::cerr << "Failed to write \"" << out << "\"." << std::endl;
590     std::cerr  << "Error code: " << gMB->get_error_string(result) << " (" << result << ")" << std::endl;
591     std::string message;
592     if (MB_SUCCESS == gMB->get_last_error(message) && !message.empty())
593       std::cerr << "Error message: " << message << std::endl;
594 #ifdef MOAB_HAVE_MPI
595     MPI_Finalize();
596 #endif
597     return WRITE_ERROR;
598   }
599 
600   if (!proc_id) std::cerr << "Wrote \"" << out << "\"" << std::endl;
601   if (print_times && !proc_id) write_times( std::cout );
602 
603 #ifdef MOAB_HAVE_MPI
604   MPI_Finalize();
605 #endif
606   return 0;
607 }
608 
parse_id_list(const char * string,std::set<int> & results)609 bool parse_id_list( const char* string, std::set<int>& results )
610 {
611   bool okay = true;
612   char* mystr = strdup( string );
613   for (const char* ptr = strtok(mystr, ","); ptr; ptr = strtok(0,","))
614   {
615     char* endptr;
616     long val = strtol( ptr, &endptr, 0 );
617     if (endptr == ptr || val <= 0) {
618       std::cerr << "Not a valid id: " << ptr << std::endl;
619       okay = false;
620       break;
621     }
622 
623     long val2 = val;
624     if (*endptr == '-') {
625       const char* sptr = endptr+1;
626       val2 = strtol( sptr, &endptr, 0 );
627       if (endptr == sptr || val2 <= 0) {
628         std::cerr << "Not a valid id: " << sptr << std::endl;
629         okay = false;
630         break;
631       }
632       if (val2 < val) {
633         std::cerr << "Invalid id range: " << ptr << std::endl;
634         okay = false;
635         break;
636       }
637     }
638 
639     if (*endptr) {
640       std::cerr << "Unexpected character: " << *endptr << std::endl;
641       okay = false;
642       break;
643     }
644 
645     for (; val <= val2; ++val)
646       if (!results.insert( (int)val ).second)
647         std::cerr << "Warning: duplicate Id: " << val << std::endl;
648 
649   }
650 
651   free( mystr );
652   return okay;
653 }
654 
print_id_list(const char * head,std::ostream & stream,const std::set<int> & list)655 void print_id_list( const char* head, std::ostream& stream, const std::set<int>& list )
656 {
657   stream << head << ": ";
658 
659   if (list.empty())
660   {
661     stream << "(none)" << std::endl;
662     return;
663   }
664 
665   int start, prev;
666   std::set<int>::const_iterator iter = list.begin();
667   start = prev = *(iter++);
668   for (;;)
669   {
670     if (iter == list.end() || *iter != 1+prev) {
671       stream << start;
672       if (prev != start)
673         stream << '-' << prev;
674       if (iter == list.end())
675         break;
676       stream << ", ";
677       start = *iter;
678     }
679     prev = *(iter++);
680   }
681 
682   stream << std::endl;
683 }
684 
685 
686 
687 
print_time(int clk_per_sec,const char * prefix,clock_t ticks,std::ostream & stream)688 static void print_time( int clk_per_sec, const char* prefix, clock_t ticks, std::ostream& stream )
689 {
690   ticks *= clk_per_sec/100;
691   clock_t centi = ticks % 100;
692   clock_t seconds = ticks / 100;
693   stream << prefix;
694   if (seconds < 120)
695   {
696     stream << (ticks / 100) << "." << centi << "s" << std::endl;
697   }
698   else
699   {
700     clock_t minutes = (seconds / 60) % 60;
701     clock_t hours = (seconds / 3600);
702     seconds %= 60;
703     if (hours)
704       stream << hours << "h";
705     if (minutes)
706       stream << minutes << "m";
707     if (seconds || centi)
708       stream << seconds << "." << centi << "s";
709     stream << " (" << (ticks/100) << "." << centi << "s)" << std::endl;
710   }
711 }
712 
713 clock_t usr_time, sys_time, abs_time;
714 
715 #ifdef WIN32
716 
reset_times()717 void reset_times()
718 {
719   abs_time = clock();
720 }
721 
722 
write_times(std::ostream & stream)723 void write_times( std::ostream& stream )
724 {
725   clock_t abs_tm = clock();
726   print_time( CLOCKS_PER_SEC, "  ", abs_tm - abs_time, stream );
727   abs_time = abs_tm;
728 }
729 
730 #else
731 
reset_times()732 void reset_times()
733 {
734   tms timebuf;
735   abs_time = times( &timebuf );
736   usr_time = timebuf.tms_utime;
737   sys_time = timebuf.tms_stime;
738 }
739 
write_times(std::ostream & stream)740 void write_times( std::ostream& stream )
741 {
742   clock_t usr_tm, sys_tm, abs_tm;
743   tms timebuf;
744   abs_tm = times( &timebuf );
745   usr_tm = timebuf.tms_utime;
746   sys_tm = timebuf.tms_stime;
747   print_time( sysconf(_SC_CLK_TCK), "  real:   ", abs_tm - abs_time, stream );
748   print_time( sysconf(_SC_CLK_TCK), "  user:   ", usr_tm - usr_time, stream );
749   print_time( sysconf(_SC_CLK_TCK), "  system: ", sys_tm - sys_time, stream );
750   abs_time = abs_tm;
751   usr_time = usr_tm;
752   sys_time = sys_tm;
753 }
754 
755 #endif
756 
make_opts_string(std::vector<std::string> options,std::string & opts)757 bool make_opts_string( std::vector<std::string> options, std::string& opts )
758 {
759   opts.clear();
760   if (options.empty())
761     return true;
762 
763     // choose a separator character
764   std::vector<std::string>::const_iterator i;
765   char separator = '\0';
766   const char* alt_separators = ";+,:\t\n";
767   for (const char* sep_ptr = alt_separators; *sep_ptr; ++sep_ptr) {
768     bool seen = false;
769     for (i = options.begin(); i != options.end(); ++i)
770       if (i->find( *sep_ptr, 0 ) != std::string::npos) {
771         seen = true;
772         break;
773       }
774     if (!seen) {
775       separator = *sep_ptr;
776       break;
777     }
778   }
779   if (!separator) {
780     std::cerr << "Error: cannot find separator character for options string" << std::endl;
781     return false;
782   }
783   if (separator != ';') {
784     opts = ";";
785     opts += separator;
786   }
787 
788     // concatenate options
789   i = options.begin();
790   opts += *i;
791   for (++i; i != options.end(); ++i) {
792     opts += separator;
793     opts += *i;
794   }
795 
796   return true;
797 }
798 
799 
list_formats(Interface * gMB)800 void list_formats( Interface* gMB )
801 {
802   const char iface_name[] = "ReaderWriterSet";
803   ErrorCode err;
804   ReaderWriterSet* set = 0;
805   ReaderWriterSet::iterator i;
806   std::ostream& str = std::cout;
807 
808     // get ReaderWriterSet
809   err = gMB->query_interface( set );
810   if (err != MB_SUCCESS || !set) {
811     std::cerr << "Internal error:  Interface \"" << iface_name
812               << "\" not available.\n";
813     exit(OTHER_ERROR);
814   }
815 
816     // get field width for format description
817   size_t w = 0;
818   for (i = set->begin(); i != set->end(); ++i)
819     if (i->description().length() > w)
820       w = i->description().length();
821 
822     // write table header
823   str << "Format  " << std::setw(w) << std::left << "Description"
824       << "  Read  Write  File Name Suffixes\n"
825       << "------  " << std::setw(w) << std::setfill('-') << "" << std::setfill(' ')
826       << "  ----  -----  ------------------\n";
827 
828     // write table data
829   for (i = set->begin(); i != set->end(); ++i)
830   {
831     std::vector<std::string> ext;
832     i->get_extensions( ext );
833     str << std::setw(6) << i->name() << "  "
834         << std::setw(w) << std::left << i->description() << "  "
835         << (i->have_reader() ?  " yes" :  "  no") << "  "
836         << (i->have_writer() ? "  yes" : "   no") << " ";
837     for (std::vector<std::string>::iterator j = ext.begin(); j != ext.end(); ++j)
838       str << " " << *j;
839     str << std::endl;
840   }
841   str << std::endl;
842 
843   gMB->release_interface( set );
844   exit(0);
845 }
846 
remove_entities_from_sets(Interface * gMB,Range & dead_entities,Range & empty_sets)847 void remove_entities_from_sets( Interface* gMB, Range& dead_entities, Range& empty_sets )
848 {
849   empty_sets.clear();
850   Range sets;
851   gMB->get_entities_by_type( 0, MBENTITYSET, sets );
852   for (Range::iterator i = sets.begin(); i != sets.end(); ++i) {
853     Range set_contents;
854     gMB->get_entities_by_handle( *i, set_contents, false );
855     set_contents = intersect( set_contents, dead_entities );
856     gMB->remove_entities( *i, set_contents );
857     set_contents.clear();
858     gMB->get_entities_by_handle( *i, set_contents, false );
859     if (set_contents.empty())
860       empty_sets.insert( *i );
861   }
862 }
863 
remove_from_vector(std::vector<EntityHandle> & vect,const Range & ents_to_remove)864 void remove_from_vector( std::vector<EntityHandle>& vect, const Range& ents_to_remove )
865 {
866   Range::const_iterator i;
867   std::vector<EntityHandle>::iterator j;
868   for (i = ents_to_remove.begin(); i != ents_to_remove.end(); ++i) {
869     j = std::find( vect.begin(), vect.end(), *i );
870     if (j != vect.end())
871       vect.erase( j );
872   }
873 }
874 
percent_subst(const std::string & s,int val)875 std::string percent_subst( const std::string& s, int val )
876 {
877   if (s.empty())
878     return s;
879 
880   size_t j = s.find( '%' );
881   if (j == std::string::npos)
882     return s;
883 
884   std::ostringstream st;
885   st << s.substr( 0, j );
886   st << val;
887 
888   size_t i;
889   while ((i = s.find( '%', j+1)) != std::string::npos) {
890     st << s.substr( j, i - j );
891     st << val;
892     j = i;
893   }
894   st << s.substr( j+1 );
895   return st.str();
896 }
897 
process_partition_file(Interface * mb,std::string & metis_partition_file)898 int process_partition_file(Interface * mb, std::string & metis_partition_file)
899 {
900   // how many faces in the file ? how do we make sure it is an mpas file?
901   // mpas atmosphere files can be downloaded from here
902   // https://mpas-dev.github.io/atmosphere/atmosphere_meshes.html
903   Range faces;
904   ErrorCode rval = mb->get_entities_by_dimension(0, 2, faces); MB_CHK_ERR(rval);
905   std::cout << " MPAS model has " << faces.size() << " polygons\n";
906 
907   // read the partition file
908   std::ifstream partfile;
909   partfile.open(metis_partition_file.c_str());
910   std::string line;
911   std::vector<int> parts;
912   parts.resize(faces.size(), -1);
913   int i=0;
914   if (partfile.is_open())
915   {
916     while ( getline (partfile,line) )
917     {
918       //cout << line << '\n';
919       parts[i++] = atoi(line.c_str());
920       if (i>(int)faces.size())
921       {
922          std::cerr<< " too many lines in partition file \n. bail out \n";
923          return 1;
924       }
925     }
926     partfile.close();
927   }
928   std::vector<int>::iterator pmax = max_element(parts.begin(), parts.end());
929   std::vector<int>::iterator pmin = min_element(parts.begin(), parts.end());
930   if (*pmin <=-1)
931   {
932     std::cerr<<" partition file is incomplete, *pmin is -1 !! \n";
933     return 1;
934   }
935   std::cout << " partitions range: " << *pmin << " " << *pmax << "\n";
936   Tag part_set_tag;
937   int dum_id = -1;
938   rval = mb->tag_get_handle("PARALLEL_PARTITION", 1, MB_TYPE_INTEGER,
939                                   part_set_tag, MB_TAG_SPARSE|MB_TAG_CREAT, &dum_id);  MB_CHK_ERR(rval);
940 
941     // get any sets already with this tag, and clear them
942   // remove the parallel partition sets if they exist
943   Range tagged_sets;
944   rval = mb->get_entities_by_type_and_tag(0, MBENTITYSET, &part_set_tag, NULL, 1,
945                                                 tagged_sets, Interface::UNION);  MB_CHK_ERR(rval);
946   if (!tagged_sets.empty()) {
947     rval = mb->clear_meshset(tagged_sets); MB_CHK_ERR(rval);
948     rval = mb->tag_delete_data(part_set_tag, tagged_sets); MB_CHK_ERR(rval);
949   }
950   Tag gid;
951   rval = mb->tag_get_handle("GLOBAL_ID", gid); MB_CHK_ERR(rval);
952   int num_sets =  *pmax + 1;
953   if (*pmin!=0)
954   {
955      std::cout << " problem reading parts; min is not 0 \n";
956      return 1;
957   }
958   for (i = 0; i < num_sets; i++) {
959     EntityHandle new_set;
960     rval = mb->create_meshset(MESHSET_SET, new_set); MB_CHK_ERR(rval);
961     tagged_sets.insert(new_set);
962   }
963   int *dum_ids = new int[num_sets];
964     for (i = 0; i < num_sets; i++) dum_ids[i] = i;
965 
966   rval = mb->tag_set_data(part_set_tag, tagged_sets, dum_ids); MB_CHK_ERR(rval);
967   delete [] dum_ids;
968 
969   std::vector<int> gids;
970   int num_faces = (int)faces.size();
971   gids.resize(num_faces);
972   rval = mb->tag_get_data(gid, faces, &gids[0]); MB_CHK_ERR(rval);
973 
974   for (int j=0; j<num_faces; j++)
975   {
976     int eid = gids[j];
977     EntityHandle eh = faces[j];
978     int partition = parts[eid-1];
979     if (partition <0 || partition >= num_sets)
980     {
981        std::cout << " wrong partition number \n";
982        return 1;
983     }
984     rval = mb->add_entities(tagged_sets[partition], &eh, 1); MB_CHK_ERR(rval);
985   }
986   return 0;
987 }
988