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