1 // Copyright(C) 1999-2021 National Technology & Engineering Solutions
2 // of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 with
3 // NTESS, the U.S. Government retains certain rights in this software.
4 //
5 // See packages/seacas/LICENSE for details
6
7 #include <Ioss_CodeTypes.h>
8 #include <Ioss_SubSystem.h>
9 #include <Ioss_Utils.h>
10
11 #include <algorithm>
12 #include <cassert>
13 #include <cctype>
14 #include <chrono>
15 #include <cstdint>
16 #include <cstdlib>
17 #include <cstring>
18 #include <fmt/chrono.h>
19 #include <fmt/format.h>
20 #include <fmt/ostream.h>
21 #include <fstream>
22 #include <sstream>
23 #include <string>
24 #include <tokenize.h>
25 #include <vector>
26
27 #ifndef _WIN32
28 #include <sys/ioctl.h>
29 #include <sys/utsname.h>
30 #endif
31
32 #ifndef _POSIX_SOURCE
33 #define _POSIX_SOURCE
34 #endif
35 #include <cstdio>
36
37 #if defined(_MSC_VER)
38 #include <io.h>
39 #define isatty _isatty
40 #endif
41
42 // For memory utilities...
43 #if defined(_WIN32)
44 #if 0
45 #define WIN32_LEAN_AND_MEAN
46 #define NOMINMAX
47 #include <psapi.h>
48 #include <windows.h>
49 #endif
50
51 #elif defined(__unix__) || defined(__unix) || defined(unix) || \
52 (defined(__APPLE__) && defined(__MACH__))
53 #include <sys/resource.h>
54 #include <unistd.h>
55
56 #if defined(__APPLE__) && defined(__MACH__)
57 #include <mach/mach.h>
58
59 #elif (defined(_AIX) || defined(__TOS__AIX__)) || \
60 (defined(__sun__) || defined(__sun) || defined(sun) && (defined(__SVR4) || defined(__svr4__)))
61 #include <fcntl.h>
62 #include <procfs.h>
63
64 #elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__)
65 #include <stdio.h>
66 #endif
67 #endif
68
69 #if defined(BGQ_LWK) && defined(__linux__)
70 #include <spi/include/kernel/location.h>
71 #include <spi/include/kernel/memory.h>
72 #endif
73
74 std::ostream *Ioss::Utils::m_warningStream = &std::cerr;
75 std::ostream *Ioss::Utils::m_debugStream = &std::cerr;
76 std::ostream *Ioss::Utils::m_outputStream = &std::cerr;
77 std::string Ioss::Utils::m_preWarningText = "\nIOSS WARNING: ";
78
79 namespace {
80 auto initial_time = std::chrono::steady_clock::now();
81
82 template <typename INT>
83 void set_owned_node_count(Ioss::Region ®ion, int my_processor, INT dummy);
84
85 ////////////////////////////////////////////////////////////////////////
is_separator(const char separator,const char value)86 bool is_separator(const char separator, const char value) { return separator == value; }
87
match(const char * name1,const char * name2)88 size_t match(const char *name1, const char *name2)
89 {
90 size_t l1 = std::strlen(name1);
91 size_t l2 = std::strlen(name2);
92 size_t len = l1 < l2 ? l1 : l2;
93 for (size_t i = 0; i < len; i++) {
94 if (name1[i] != name2[i]) {
95 while (i > 0 && (isdigit(name1[i - 1]) != 0) && (isdigit(name2[i - 1]) != 0)) {
96 i--;
97 // Back up to first non-digit so to handle "evar0000, evar0001, ..., evar 1123"
98 }
99 return i;
100 }
101 }
102 return len;
103 }
104
105 // Split 'str' into 'tokens' based on the 'separator' character.
106 // If 'str' starts with 1 or more 'separator', they are part of the
107 // first token and not used for splitting. If there are multiple
108 // 'separator' characters in a row, then the first is used to split
109 // and the subsequent 'separator' characters are put as leading
110 // characters of the next token.
111 // `__this___is_a_string__for_tokens` will split to 6 tokens:
112 // `__this` `__is` `a` `string` `_for` `tokens`
field_tokenize(const std::string & str,const char separator,std::vector<std::string> & tokens)113 void field_tokenize(const std::string &str, const char separator,
114 std::vector<std::string> &tokens)
115 {
116 std::string curr_token;
117 // Skip leading separators...
118 size_t i = 0;
119 while (i < str.length() && is_separator(separator, str[i])) {
120 curr_token += str[i++];
121 }
122 for (; i < str.length(); ++i) {
123 char curr_char = str[i];
124
125 // determine if current character is a separator
126 bool is_sep = is_separator(separator, curr_char);
127 if (is_sep && curr_token != "") {
128 // we just completed a token
129 tokens.push_back(curr_token);
130 curr_token.clear();
131 while (i++ < str.length() && is_separator(separator, str[i])) {
132 curr_token += str[i];
133 }
134 i--;
135 }
136 else if (!is_sep) {
137 curr_token += curr_char;
138 }
139 }
140 if (curr_token != "") {
141 tokens.push_back(curr_token);
142 }
143 }
144 } // namespace
145
time_and_date(char * time_string,char * date_string,size_t length)146 void Ioss::Utils::time_and_date(char *time_string, char *date_string, size_t length)
147 {
148 std::time_t t = std::time(nullptr);
149 std::string time = fmt::format("{:%H:%M:%S}", fmt::localtime(t));
150 std::string date;
151 if (length >= 10) {
152 date = fmt::format("{:%Y/%m/%d}", fmt::localtime(t));
153 }
154 else {
155 date = fmt::format("{:%y/%m/%d}", fmt::localtime(t));
156 }
157 copy_string(time_string, time, 9);
158 copy_string(date_string, date, length + 1);
159 }
160
check_non_null(void * ptr,const char * type,const std::string & name,const std::string & func)161 void Ioss::Utils::check_non_null(void *ptr, const char *type, const std::string &name,
162 const std::string &func)
163 {
164 if (ptr == nullptr) {
165 std::ostringstream errmsg;
166 fmt::print(errmsg,
167 "INTERNAL ERROR: Could not find {} '{}'. Something is wrong in {}. Please report.\n",
168 type, name, func);
169 IOSS_ERROR(errmsg);
170 }
171 }
172
decode_filename(const std::string & filename,int processor,int num_processors)173 std::string Ioss::Utils::decode_filename(const std::string &filename, int processor,
174 int num_processors)
175 {
176 // Current format for per-processor file names is:
177 // PREFIX/basename.num_proc.cur_proc
178 // the 'cur_proc' field is padded to be the same width as
179 // the 'num_proc' field
180 // Examples: basename.8.1, basename.64.03, basename.128.001
181
182 // Create a std::string containing the total number of processors
183 if (num_processors > 1) {
184 size_t proc_width = number_width(num_processors);
185
186 std::string decoded_filename =
187 fmt::format("{}.{}.{:0{}}", filename, num_processors, processor, proc_width);
188 return decoded_filename;
189 }
190 return filename;
191 }
192
get_number(const std::string & suffix)193 size_t Ioss::Utils::get_number(const std::string &suffix)
194 {
195 int N = 0;
196 bool all_dig = suffix.find_first_not_of("0123456789") == std::string::npos;
197 if (all_dig) {
198 N = std::stoi(suffix);
199 }
200 return N;
201 }
202
extract_id(const std::string & name_id)203 int64_t Ioss::Utils::extract_id(const std::string &name_id)
204 {
205 int64_t id = 0;
206
207 std::vector<std::string> tokens = Ioss::tokenize(name_id, "_");
208 if (tokens.size() > 1) {
209 // Check whether last token is an integer...
210 std::string str_id = tokens.back();
211 id = get_number(str_id);
212 }
213 return id;
214 }
215
format_id_list(const std::vector<size_t> & ids,const std::string & rng_sep,const std::string & seq_sep)216 std::string Ioss::Utils::format_id_list(const std::vector<size_t> &ids, const std::string &rng_sep,
217 const std::string &seq_sep)
218 {
219 // Based on function from cubit (but I wrote original cubit version long time ago... ;-)
220 if (ids.empty()) {
221 return "";
222 }
223
224 // PRECONDITION: `ids` is monotonically increasing -- will throw IOSS_ERROR if violated.
225 if (!std::is_sorted(ids.begin(), ids.end(), [](size_t a, size_t b) { return a <= b; })) {
226 std::ostringstream errmsg;
227 fmt::print(errmsg,
228 "INTERNAL ERROR: ({}) The `ids` vector is not in monotonically increasing order as "
229 "required.\n",
230 __func__);
231 IOSS_ERROR(errmsg);
232 }
233
234 size_t num = 0;
235 std::ostringstream ret_str;
236 while (num < ids.size()) {
237 fmt::print(ret_str, "{}{}", num == 0 ? "" : seq_sep, ids[num]);
238 size_t begin = ids[num]; // first id in range of 1 or more ids
239 size_t previous = ids[num]; // last id in range of 1 or more ids
240 // Gather a range or single value... (begin .. previous)
241 while (previous == ids[num] && ++num < ids.size() && ids[num] == previous + 1) {
242 previous++;
243 }
244
245 if (begin != previous) {
246 fmt::print(ret_str, "{}{}", previous == begin + 1 ? seq_sep : rng_sep, previous);
247 }
248 }
249 return ret_str.str();
250 }
251
encode_entity_name(const std::string & entity_type,int64_t id)252 std::string Ioss::Utils::encode_entity_name(const std::string &entity_type, int64_t id)
253 {
254 // ExodusII stores block, nodeset, and sideset ids as integers
255 // Sierra stores these as std::strings. The string is created by
256 // concatenating the type, the character '_' and the id.
257
258 return fmt::format("{}_{}", entity_type, id);
259 }
260
get_name_array(size_t count,int size)261 char **Ioss::Utils::get_name_array(size_t count, int size)
262 {
263 auto names = new char *[count];
264 for (size_t i = 0; i < count; i++) {
265 names[i] = new char[size + 1];
266 std::memset(names[i], '\0', size + 1);
267 }
268 return names;
269 }
270
delete_name_array(char ** names,int count)271 void Ioss::Utils::delete_name_array(char **names, int count)
272 {
273 for (int i = 0; i < count; i++) {
274 delete[] names[i];
275 }
276 delete[] names;
277 }
278
fixup_type(const std::string & base,int nodes_per_element,int spatial)279 std::string Ioss::Utils::fixup_type(const std::string &base, int nodes_per_element, int spatial)
280 {
281 std::string type = base;
282 Ioss::Utils::fixup_name(type); // Convert to lowercase; replace spaces with '_'
283
284 // Fixup an exodusII kluge/ambiguity.
285 // The element block type does not fully define the element. For
286 // example, a block of type 'triangle' may have either 3 or 6
287 // nodes. To fix this, check the block type name and see if it
288 // ends with a number. If it does, assume it is OK; if not, append
289 // the 'nodes_per_element'.
290 if (isdigit(*(type.rbegin())) == 0) {
291 if (nodes_per_element > 1) {
292 type += std::to_string(nodes_per_element);
293 }
294 }
295
296 // Fixup an exodusII kludge. For triangular elements, the same
297 // name is used for 2D elements and 3D shell elements. Convert
298 // to unambiguous names for the IO Subsystem. The 2D name
299 // stays the same, the 3D name becomes 'trishell#'
300 if (spatial == 3) {
301 if (type == "triangle3") {
302 type = "trishell3";
303 }
304 else if (type == "triangle4") {
305 type = "trishell4";
306 }
307 else if (type == "triangle6") {
308 type = "trishell6";
309 }
310 else if (type == "tri3") {
311 type = "trishell3";
312 }
313 else if (type == "tri4") {
314 type = "trishell4";
315 }
316 else if (type == "tri6") {
317 type = "trishell6";
318 }
319 }
320
321 if (spatial == 2) {
322 if (type == "shell2") {
323 type = "shellline2d2";
324 }
325 else if (type == "rod2" || type == "bar2" || type == "truss2") {
326 type = "rod2d2";
327 }
328 else if (type == "shell3") {
329 type = "shellline2d3";
330 }
331 else if (type == "bar3" || type == "rod3" || type == "truss3") {
332 type = "rod2d3";
333 }
334 else if (type == "bar4" || type == "rod4" || type == "truss4") {
335 type = "rod2d4";
336 }
337 }
338
339 if (Ioss::Utils::substr_equal("super", type)) {
340 // A super element can have a varying number of nodes. Create
341 // an IO element type for this super element just so the IO
342 // system can read a mesh containing super elements. This
343 // allows the "omit volume" command to be used in the Sierra
344 // applications to skip creating a corresponding element block
345 // in the application.
346 type = "super" + std::to_string(nodes_per_element);
347 }
348 return type;
349 }
350
local_filename(const std::string & relative_filename,const std::string & type,const std::string & working_directory)351 std::string Ioss::Utils::local_filename(const std::string &relative_filename,
352 const std::string &type,
353 const std::string &working_directory)
354 {
355 if (relative_filename[0] == '/' || type == "generated" || working_directory.empty()) {
356 return relative_filename;
357 }
358 std::string filename = working_directory;
359 filename += relative_filename;
360 return filename;
361 }
362
field_warning(const Ioss::GroupingEntity * ge,const Ioss::Field & field,const std::string & inout)363 int Ioss::Utils::field_warning(const Ioss::GroupingEntity *ge, const Ioss::Field &field,
364 const std::string &inout)
365 {
366 fmt::print(Ioss::WARNING(), "{} '{}'. Unknown {} field '{}'\n", ge->type_string(), ge->name(),
367 inout, field.get_name());
368 return -4;
369 }
370
371 namespace {
match_composite_field(char ** names,Ioss::IntVector & which_names,const char suffix_separator)372 const Ioss::VariableType *match_composite_field(char **names, Ioss::IntVector &which_names,
373 const char suffix_separator)
374 {
375 // ASSUME: Fields are in order...
376 // The field we are trying to match will be a composite field of
377 // type base_x_1, base_y_1, base_z_1, ...., base_y_3, base_z_3.
378 // The composite field type currently always has a numeric Real[N]
379 // type field as the last suffix and the other field as the first
380 // suffix.
381 // If we take the last suffix of the last name, it should give us
382 // the 'N' in the Real[N] field. Dividing 'which_names.size()' by
383 // 'N' will give the number of components in the inner field.
384
385 char suffix[2] = {suffix_separator, '\0'};
386
387 std::vector<std::string> tokens =
388 Ioss::tokenize(names[which_names[which_names.size() - 1]], suffix);
389
390 if (tokens.size() <= 2) {
391 return nullptr;
392 }
393
394 assert(tokens.size() > 2);
395
396 // Check that suffix is a number -- all digits
397 size_t N = Ioss::Utils::get_number(tokens[tokens.size() - 1]);
398
399 if (N == 0) {
400 return nullptr;
401 }
402
403 if (which_names.size() % N != 0) {
404 return nullptr;
405 }
406
407 size_t inner_token = tokens.size() - 2;
408 size_t inner_comp = which_names.size() / N;
409
410 // Gather the first 'inner_ccomp' inner field suffices...
411 std::vector<Ioss::Suffix> suffices;
412 for (size_t i = 0; i < inner_comp; i++) {
413 std::vector<std::string> ltokens = Ioss::tokenize(names[which_names[i]], suffix);
414 // The second-last token is the suffix for this component...
415 Ioss::Suffix tmp(ltokens[inner_token]);
416 suffices.push_back(tmp);
417 }
418
419 // check that the suffices on the next copies of the inner field
420 // match the first copy...
421 size_t j = inner_comp;
422 for (size_t copy = 1; copy < N; copy++) {
423 for (size_t i = 0; i < inner_comp; i++) {
424 std::vector<std::string> ltokens = Ioss::tokenize(names[which_names[j++]], suffix);
425 // The second-last token is the suffix for this component...
426 if (suffices[i] != ltokens[inner_token]) {
427 return nullptr;
428 }
429 }
430 }
431
432 // All 'N' copies of the inner field match, now see the
433 // suffices actually defines a field...
434 const Ioss::VariableType *type = Ioss::VariableType::factory(suffices);
435 if (type != nullptr) {
436 type = Ioss::VariableType::factory(type->name(), static_cast<int>(N));
437 }
438 return type;
439 }
440
match_single_field(char ** names,Ioss::IntVector & which_names,const char suffix_separator)441 const Ioss::VariableType *match_single_field(char **names, Ioss::IntVector &which_names,
442 const char suffix_separator)
443 {
444 // Strip off the suffix from each name indexed in 'which_names'
445 // and see if it defines a valid type...
446 std::vector<Ioss::Suffix> suffices;
447
448 char suffix[2] = {suffix_separator, '\0'};
449
450 for (int which_name : which_names) {
451 std::vector<std::string> tokens = Ioss::tokenize(names[which_name], suffix);
452 size_t num_tokens = tokens.size();
453
454 // The last token is the suffix for this component...
455 Ioss::Suffix tmp(tokens[num_tokens - 1]);
456 suffices.push_back(tmp);
457 }
458 const Ioss::VariableType *type = Ioss::VariableType::factory(suffices);
459 return type;
460 }
461
get_next_field(char ** names,int num_names,size_t count,Ioss::Field::RoleType fld_role,const char suffix_separator,const int * truth_table)462 Ioss::Field get_next_field(char **names, int num_names, size_t count,
463 Ioss::Field::RoleType fld_role, const char suffix_separator,
464 const int *truth_table)
465 {
466 // NOTE: 'names' are all lowercase at this point.
467
468 // Assumption 1: To convert to a non-SCALAR type, the variable name
469 // must have an field_suffix_sep in the name separating the suffixes from
470 // the main name.
471
472 // Find first unused name (used names have '\0' as first character...
473 int index = 0;
474 bool found_valid = false;
475 for (index = 0; index < num_names; index++) {
476 assert(truth_table == nullptr || truth_table[index] == 1 || truth_table[index] == 0);
477 if ((truth_table == nullptr || truth_table[index] == 1) && names[index][0] != '\0') {
478 found_valid = true;
479 break;
480 }
481 }
482
483 if (!found_valid) {
484 // Return an invalid field...
485 return Ioss::Field("", Ioss::Field::INVALID, IOSS_SCALAR(), fld_role, 1);
486 }
487
488 // At this point, name[index] should be a valid potential field
489 // name and all names[i] with i < index are either already used or
490 // not valid for this grouping entity (truth_table entry == 0).
491 assert(index < num_names && names[index][0] != '\0' &&
492 (truth_table == nullptr || truth_table[index] == 1));
493 char *name = names[index];
494
495 // Split the name up into tokens separated by the
496 // 'suffix_separator'. Note that the basename itself could
497 // contain a suffix_separator (back_stress_xx or
498 // back_stress_xx_01). Need to ignore embedded separators
499 // (back_stress) and also recognize composite variable types
500 // (back_stress_xx_01). At the current time, a composite variable
501 // type can only contain two non-composite variable types, so we
502 // only need to look to be concerned with the last 1 or 2 tokens...
503 std::vector<std::string> tokens;
504 field_tokenize(name, suffix_separator, tokens);
505 size_t num_tokens = tokens.size();
506
507 // Check that tokenizer did not return empty tokens...
508 bool invalid = tokens[0].empty() || tokens[num_tokens - 1].empty();
509 if (num_tokens == 1 || invalid) {
510 // It is not a (Sierra-generated) name for a non-SCALAR variable
511 // Return a SCALAR field
512 Ioss::Field field(name, Ioss::Field::REAL, IOSS_SCALAR(), fld_role, count);
513 field.set_index(index);
514 names[index][0] = '\0';
515 return field;
516 }
517
518 // KNOW: num_tokens > 1 at this point. Possible that we still
519 // just have a scalar with one or more embedded separator characters...
520 int suffix_size = 1;
521 if (num_tokens > 2) {
522 suffix_size = 2;
523 }
524
525 // If num_tokens > 2, then we can potentially have a composite
526 // variable type which would have a double suffix (_xx_01).
527
528 // Gather all names which match in the first
529 // (num_tokens-suffix_size) tokens and see if their suffices form
530 // a valid variable type...
531 while (suffix_size > 0) {
532 Ioss::IntVector which_names; // Contains index of names that
533 // potentially match as components
534 // of a higher-order type.
535
536 std::string base_name = tokens[0];
537 for (size_t i = 1; i < num_tokens - suffix_size; i++) {
538 base_name += suffix_separator;
539 base_name += tokens[i];
540 }
541 base_name += suffix_separator;
542 size_t bn_len = base_name.length(); // Length of basename portion only
543 size_t length = std::strlen(name); // Length of total name (with suffix)
544
545 // Add the current name...
546 which_names.push_back(index);
547
548 // Gather all other names that are valid for this entity, and
549 // have the same overall length and match in the first 'bn_len'
550 // characters.
551 //
552 // Check that they have the same number of tokens,
553 // It is possible that the first name(s) that match with two
554 // suffices have a basename that match other names with only a
555 // single suffix lc_cam_x, lc_cam_y, lc_sfarea.
556 for (int i = index + 1; i < num_names; i++) {
557 char * tst_name = names[i];
558 std::vector<std::string> subtokens;
559 field_tokenize(tst_name, suffix_separator, subtokens);
560 if ((truth_table == nullptr || truth_table[i] == 1) && // Defined on this entity
561 std::strlen(tst_name) == length && // names must be same length
562 std::strncmp(name, tst_name, bn_len) == 0 && // base portion must match
563 subtokens.size() == num_tokens) {
564 which_names.push_back(i);
565 }
566 }
567
568 const Ioss::VariableType *type = nullptr;
569 if (suffix_size == 2) {
570 if (which_names.size() > 1) {
571 type = match_composite_field(names, which_names, suffix_separator);
572 }
573 }
574 else {
575 assert(suffix_size == 1);
576 type = match_single_field(names, which_names, suffix_separator);
577 }
578
579 if (type != nullptr) {
580 // A valid variable type was recognized.
581 // Mark the names which were used so they aren't used for another field on this entity.
582 // Create a field of that variable type.
583 assert(type->component_count() == static_cast<int>(which_names.size()));
584 Ioss::Field field(base_name.substr(0, bn_len - 1), Ioss::Field::REAL, type, fld_role,
585 count);
586 field.set_index(index);
587 for (const auto &which_name : which_names) {
588 names[which_name][0] = '\0';
589 }
590 return field;
591 }
592 if (suffix_size == 1) {
593 Ioss::Field field(name, Ioss::Field::REAL, IOSS_SCALAR(), fld_role, count);
594 field.set_index(index);
595 names[index][0] = '\0';
596 return field;
597 }
598
599 suffix_size--;
600 }
601 return Ioss::Field("", Ioss::Field::INVALID, IOSS_SCALAR(), fld_role, 1);
602 }
603
604 // common
define_field(size_t nmatch,size_t match_length,char ** names,std::vector<Ioss::Suffix> & suffices,size_t entity_count,Ioss::Field::RoleType fld_role,std::vector<Ioss::Field> & fields)605 bool define_field(size_t nmatch, size_t match_length, char **names,
606 std::vector<Ioss::Suffix> &suffices, size_t entity_count,
607 Ioss::Field::RoleType fld_role, std::vector<Ioss::Field> &fields)
608 {
609 // Try to define a field of size 'nmatch' with the suffices in 'suffices'.
610 // If this doesn't define a known field, then assume it is a scalar instead
611 // and return false.
612 if (nmatch > 1) {
613 const Ioss::VariableType *type = Ioss::VariableType::factory(suffices);
614 if (type == nullptr) {
615 nmatch = 1;
616 }
617 else {
618 char *name = names[0];
619 name[match_length] = '\0';
620 Ioss::Field field(name, Ioss::Field::REAL, type, fld_role, entity_count);
621 if (field.is_valid()) {
622 fields.push_back(field);
623 }
624 for (size_t j = 0; j < nmatch; j++) {
625 names[j][0] = '\0';
626 }
627 return true;
628 }
629 }
630
631 // NOTE: nmatch could be reset inside previous if block.
632 // This is not an 'else' block, it is a new if block.
633 if (nmatch == 1) {
634 Ioss::Field field(names[0], Ioss::Field::REAL, IOSS_SCALAR(), fld_role, entity_count);
635 if (field.is_valid()) {
636 fields.push_back(field);
637 }
638 names[0][0] = '\0';
639 return false;
640 }
641 return false; // Can't get here... Quiet the compiler
642 }
643 } // namespace
644 // Read scalar fields off an input database and determine whether
645 // they are components of a higher order type (vector, tensor, ...).
646 // This routine is used if there is no field component separator. E.g.,
647 // fieldx, fieldy, fieldz instead of field_x field_y field_z
648
get_fields(int64_t entity_count,char ** names,size_t num_names,Ioss::Field::RoleType fld_role,bool enable_field_recognition,const char suffix_separator,int * local_truth,std::vector<Ioss::Field> & fields)649 void Ioss::Utils::get_fields(int64_t entity_count, // The number of objects in this entity.
650 char ** names, // Raw list of field names from exodus
651 size_t num_names, // Number of names in list
652 Ioss::Field::RoleType fld_role, // Role of field
653 bool enable_field_recognition, const char suffix_separator,
654 int *local_truth, // Truth table for this entity;
655 // null if not applicable.
656 std::vector<Ioss::Field> &fields) // The fields that were found.
657 {
658 if (!enable_field_recognition) {
659 // Create a separate field for each name.
660 for (size_t i = 0; i < num_names; i++) {
661 if (local_truth == nullptr || local_truth[i] == 1) {
662 Ioss::Field field(names[i], Ioss::Field::REAL, IOSS_SCALAR(), fld_role, entity_count);
663 fields.push_back(field);
664 names[i][0] = '\0';
665 }
666 }
667 }
668 else if (suffix_separator != 0) {
669 while (true) {
670 // NOTE: 'get_next_field' determines storage type (vector, tensor,...)
671 Ioss::Field field =
672 get_next_field(names, static_cast<int>(num_names), entity_count, fld_role, suffix_separator, local_truth);
673 if (field.is_valid()) {
674 fields.push_back(field);
675 }
676 else {
677 break;
678 }
679 }
680 }
681 else {
682 size_t nmatch = 1;
683 size_t ibeg = 0;
684 size_t pmat = 0;
685 std::vector<Ioss::Suffix> suffices;
686 top:
687
688 while (ibeg + nmatch < num_names) {
689 if (local_truth != nullptr) {
690 while (ibeg < num_names && local_truth[ibeg] == 0) {
691 ibeg++;
692 }
693 }
694 for (size_t i = ibeg + 1; i < num_names; i++) {
695 size_t mat = match(names[ibeg], names[i]);
696 if (local_truth != nullptr && local_truth[i] == 0) {
697 mat = 0;
698 }
699
700 // For all fields, the total length of the name is the same
701 // for all components of that field. The 'basename' of the
702 // field will also be the same for all cases.
703 //
704 // It is possible that the length of the match won't be the
705 // same for all components of a field since the match may
706 // include a portion of the suffix; (sigxx, sigxy, sigyy
707 // should match only 3 characters of the basename (sig), but
708 // sigxx and sigxy will match 4 characters) so consider a
709 // valid match if the match length is >= previous match length.
710 if ((std::strlen(names[ibeg]) == std::strlen(names[i])) && mat > 0 &&
711 (pmat == 0 || mat >= pmat)) {
712 nmatch++;
713 if (nmatch == 2) {
714 // Get suffix for first field in the match
715 pmat = mat;
716 Ioss::Suffix tmp(&names[ibeg][pmat]);
717 suffices.push_back(tmp);
718 }
719 // Get suffix for next fields in the match
720 Ioss::Suffix tmp(&names[i][pmat]);
721 suffices.push_back(tmp);
722 }
723 else {
724
725 bool multi_component =
726 define_field(nmatch, pmat, &names[ibeg], suffices, entity_count, fld_role, fields);
727 if (!multi_component) {
728 // Although we matched multiple suffices, it wasn't a
729 // higher-order field, so we only used 1 name instead of
730 // the 'nmatch' we thought we might use.
731 i = ibeg + 1;
732 }
733
734 // Cleanout the suffices vector.
735 clear(suffices);
736
737 // Reset for the next time through the while loop...
738 nmatch = 1;
739 pmat = 0;
740 ibeg = i;
741 break;
742 }
743 }
744 }
745 // We've gone through the entire list of names; see if what we
746 // have forms a multi-component field; if not, then define a
747 // scalar field and jump up to the loop again to handle the others
748 // that had been gathered.
749 if (ibeg < num_names) {
750 if (local_truth == nullptr || local_truth[ibeg] == 1) {
751 bool multi_component =
752 define_field(nmatch, pmat, &names[ibeg], suffices, entity_count, fld_role, fields);
753 clear(suffices);
754 if (nmatch > 1 && !multi_component) {
755 ibeg++;
756 goto top;
757 }
758 }
759 else {
760 ibeg++;
761 goto top;
762 }
763 }
764 }
765 }
766
platform_information()767 std::string Ioss::Utils::platform_information()
768 {
769 #ifndef _WIN32
770 struct utsname sys_info
771 {
772 };
773 uname(&sys_info);
774 std::string info =
775 fmt::format("Node: {0}, OS: {1} {2}, {3}, Machine: {4}", sys_info.nodename, sys_info.sysname,
776 sys_info.release, sys_info.version, sys_info.machine);
777 #else
778 std::string info = "Node: Unknown, OS: Unknown, Machine: Unknown";
779 #endif
780 return info;
781 }
782
get_memory_info()783 size_t Ioss::Utils::get_memory_info()
784 {
785 // Code from http://nadeausoftware.com/sites/NadeauSoftware.com/files/getRSS.c
786 size_t memory_usage = 0;
787 #if defined(_WIN32)
788 #if 0
789 /* Windows -------------------------------------------------- */
790 PROCESS_MEMORY_COUNTERS info;
791 GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info));
792 memory_usage = (size_t)info.WorkingSetSize;
793 #else
794 memory_usage = 0;
795 #endif
796
797 #elif defined(__APPLE__) && defined(__MACH__)
798 kern_return_t error;
799 mach_msg_type_number_t outCount;
800 mach_task_basic_info_data_t taskinfo{};
801
802 taskinfo.virtual_size = 0;
803 outCount = MACH_TASK_BASIC_INFO_COUNT;
804 error = task_info(mach_task_self(), MACH_TASK_BASIC_INFO,
805 reinterpret_cast<task_info_t>(&taskinfo), &outCount);
806 if (error == KERN_SUCCESS) {
807 memory_usage = taskinfo.resident_size;
808 }
809 #elif __linux__
810 #if defined(BGQ_LWK)
811 uint64_t heap;
812 Kernel_GetMemorySize(KERNEL_MEMSIZE_HEAP, &heap);
813 memory_usage = heap;
814 #else
815 // On Linux, the /proc pseudo-file system contains a directory for
816 // each running or zombie process. The /proc/[pid]/stat,
817 // /proc/[pid]/statm, and /proc/[pid]/status pseudo-files for the
818 // process with id [pid] all contain a process's current resident
819 // set size, among other things. But the /proc/[pid]/statm
820 // pseudo-file is the easiest to read since it contains a single
821 // line of text with white-space delimited values:
822 //
823 // * total program size
824 // * resident set size
825 // * shared pages
826 // * text (code) size
827 // * library size
828 // * data size (heap + stack)
829 // * dirty pages
830 //
831 // The second value provides the process's current resident set size
832 // in pages. To get the field for the current process, open
833 // /proc/self/statm and parse the second integer value. Multiply the
834 // field by the page size from sysconf( ).
835
836 long rss = 0L;
837 FILE *fp = NULL;
838 if ((fp = fopen("/proc/self/statm", "r")) == NULL)
839 return (size_t)0L; // Can't open? */
840 if (fscanf(fp, "%*s%ld", &rss) != 1) {
841 fclose(fp);
842 return (size_t)0L; // Can't read? */
843 }
844 fclose(fp);
845 memory_usage = (size_t)rss * (size_t)sysconf(_SC_PAGESIZE);
846 #endif
847 #endif
848 return memory_usage;
849 }
850
get_hwm_memory_info()851 size_t Ioss::Utils::get_hwm_memory_info()
852 {
853 // Code from http://nadeausoftware.com/sites/NadeauSoftware.com/files/getRSS.c
854 size_t memory_usage = 0;
855 #if defined(_WIN32)
856 #if 0
857 /* Windows -------------------------------------------------- */
858 PROCESS_MEMORY_COUNTERS info;
859 GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info));
860 memory_usage = (size_t)info.PeakWorkingSetSize;
861 #else
862 memory_usage = 0;
863 #endif
864
865 #elif (defined(_AIX) || defined(__TOS__AIX__)) || \
866 (defined(__sun__) || defined(__sun) || defined(sun) && (defined(__SVR4) || defined(__svr4__)))
867 /* AIX and Solaris ------------------------------------------ */
868 struct psinfo psinfo;
869 int fd = -1;
870 if ((fd = open("/proc/self/psinfo", O_RDONLY)) == -1)
871 return (size_t)0L; /* Can't open? */
872 if (read(fd, &psinfo, sizeof(psinfo)) != sizeof(psinfo)) {
873 close(fd);
874 return (size_t)0L; /* Can't read? */
875 }
876 close(fd);
877 memory_usage = (size_t)(psinfo.pr_rssize * 1024L);
878
879 #elif (defined(__APPLE__) && defined(__MACH__)) || (defined(__linux__) && !defined(BGQ_LWK))
880 /* BSD, Linux, and OSX -------------------------------------- */
881 struct rusage rusage;
882 getrusage(RUSAGE_SELF, &rusage);
883 #if defined(__APPLE__) && defined(__MACH__)
884 memory_usage = (size_t)rusage.ru_maxrss;
885 #else
886 memory_usage = (size_t)(rusage.ru_maxrss * 1024L);
887 #endif
888 #endif
889 return memory_usage;
890 }
891
block_is_omitted(Ioss::GroupingEntity * block)892 bool Ioss::Utils::block_is_omitted(Ioss::GroupingEntity *block)
893 {
894 bool omitted = block->get_optional_property("omitted", 0) == 1;
895 return omitted;
896 }
897
calculate_sideblock_membership(IntVector & face_is_member,const Ioss::SideBlock * ef_blk,size_t int_byte_size,const void * element,const void * sides,int64_t number_sides,const Ioss::Region * region)898 void Ioss::Utils::calculate_sideblock_membership(IntVector & face_is_member,
899 const Ioss::SideBlock *ef_blk,
900 size_t int_byte_size, const void *element,
901 const void *sides, int64_t number_sides,
902 const Ioss::Region *region)
903 {
904 assert(ef_blk != nullptr);
905
906 face_is_member.reserve(number_sides);
907
908 const ElementTopology *unknown = Ioss::ElementTopology::factory("unknown");
909
910 // Topology of faces in this face block...
911 const ElementTopology *ftopo = ef_blk->topology();
912
913 // Topology of parent element for faces in this face block
914 const ElementTopology *parent_topo = ef_blk->parent_element_topology();
915
916 // If split by element block then parent_block will be non-nullptr
917 const ElementBlock *parent_block = ef_blk->parent_element_block();
918
919 // The element block containing the face we are working on...
920 Ioss::ElementBlock *block = nullptr;
921
922 // Topology of face/edge in current element block
923 const ElementTopology *common_ftopo = nullptr;
924
925 // Topology of elements in the element block containing this element
926 const ElementTopology *block_topo = nullptr;
927
928 // Topology of the face we are currently working with...
929 const ElementTopology *topo = nullptr;
930
931 // The element side that the current face is on the element...
932 int64_t current_side = -1;
933
934 if (number_sides > 0 && (element == nullptr || sides == nullptr)) {
935 std::ostringstream errmsg;
936 fmt::print(errmsg, "INTERNAL ERROR: null element or sides pointer passed to {}.", __func__);
937 IOSS_ERROR(errmsg);
938 }
939
940 for (int64_t iel = 0; iel < number_sides; iel++) {
941 int64_t elem_id = 0;
942 int64_t side_id = 0;
943 if (int_byte_size == 4) {
944 elem_id = ((int *)element)[iel];
945 side_id = ((int *)sides)[iel];
946 }
947 else {
948 elem_id = ((int64_t *)element)[iel];
949 side_id = ((int64_t *)sides)[iel];
950 }
951
952 // Get the element block containing this face...
953 if (block == nullptr || !block->contains(elem_id)) {
954 block = region->get_element_block(elem_id);
955 block_topo = block->topology();
956 // nullptr if hetero face/edge on element
957 common_ftopo = block->topology()->boundary_type(0);
958 if (common_ftopo != nullptr) {
959 topo = common_ftopo;
960 }
961 current_side = -1;
962 }
963
964 // If the element topology of the element block containing this
965 // face has heterogeneous topology (eg. wedge), then determine the
966 // topology corresponding to the current side..
967 if (common_ftopo == nullptr && side_id != current_side) {
968 current_side = side_id;
969 topo = block->topology()->boundary_type(static_cast<int>(side_id));
970 }
971
972 bool face_topo_match = ftopo == unknown || topo == ftopo;
973 bool block_topo_match = parent_topo == unknown || block_topo == parent_topo;
974 // See if the face topology and the parent element topology for
975 // the current face match the topology associated with this face block.
976 if (face_topo_match && block_topo_match && (parent_block == nullptr || parent_block == block) &&
977 !block_is_omitted(block)) {
978 // This face/edge belongs in the face/edge block
979 face_is_member.push_back(1);
980 }
981 else {
982 face_is_member.push_back(0);
983 }
984 }
985 }
986
get_side_offset(const Ioss::SideBlock * sb)987 int64_t Ioss::Utils::get_side_offset(const Ioss::SideBlock *sb)
988 {
989
990 const Ioss::ElementTopology *side_topo = sb->topology();
991 const Ioss::ElementTopology *parent_topo = sb->parent_element_topology();
992 int64_t side_offset = 0;
993 if ((side_topo != nullptr) && (parent_topo != nullptr)) {
994 int side_topo_dim = side_topo->parametric_dimension();
995 int elem_topo_dim = parent_topo->parametric_dimension();
996 int elem_spat_dim = parent_topo->spatial_dimension();
997
998 if (side_topo_dim + 1 < elem_spat_dim && side_topo_dim < elem_topo_dim) {
999 side_offset = parent_topo->number_faces();
1000 }
1001 }
1002 return side_offset;
1003 }
1004
hash(const std::string & name)1005 unsigned int Ioss::Utils::hash(const std::string &name)
1006 {
1007 // Hash function from Aho, Sethi, Ullman "Compilers: Principles,
1008 // Techniques, and Tools. Page 436
1009
1010 const char * symbol = name.c_str();
1011 unsigned int hashval;
1012 unsigned int g;
1013 for (hashval = 0; *symbol != '\0'; symbol++) {
1014 hashval = (hashval << 4) + *symbol;
1015 g = hashval & 0xf0000000;
1016 if (g != 0) {
1017 hashval = hashval ^ (g >> 24);
1018 hashval = hashval ^ g;
1019 }
1020 }
1021 return hashval;
1022 }
1023
timer()1024 double Ioss::Utils::timer()
1025 {
1026 auto now = std::chrono::steady_clock::now();
1027 return std::chrono::duration<double>(now - initial_time).count();
1028 }
1029
input_file(const std::string & file_name,std::vector<std::string> * lines,size_t max_line_length)1030 void Ioss::Utils::input_file(const std::string &file_name, std::vector<std::string> *lines,
1031 size_t max_line_length)
1032 {
1033 // Create an ifstream for the input file. This does almost the same
1034 // function as sierra::Env::input() except this is for a single
1035 // processor and the sierra::Env::input() is for parallel...
1036
1037 if (!file_name.empty()) {
1038 // Open the file and read into the vector...
1039 std::string input_line;
1040 std::ifstream infile(file_name);
1041 lines->push_back(file_name.substr(0, max_line_length));
1042 while (!std::getline(infile, input_line).fail()) {
1043 if (max_line_length == 0 || input_line.length() <= max_line_length) {
1044 lines->push_back(input_line);
1045 }
1046 else {
1047 // Split the line into pieces of length "max_line_length-1"
1048 // and append a "\" to all but the last. Don't worry about
1049 // splitting at whitespace...
1050 size_t ibeg = 0;
1051 do {
1052 std::string sub = input_line.substr(ibeg, max_line_length - 1);
1053 if (ibeg + max_line_length - 1 < input_line.length()) {
1054 sub += "\\";
1055 }
1056 lines->push_back(sub);
1057 ibeg += max_line_length - 1;
1058 } while (ibeg < input_line.length());
1059 }
1060 }
1061 }
1062 }
1063
str_equal(const std::string & s1,const std::string & s2)1064 bool Ioss::Utils::str_equal(const std::string &s1, const std::string &s2)
1065 {
1066 return (s1.size() == s2.size()) &&
1067 std::equal(s1.begin(), s1.end(), s2.begin(),
1068 [](char a, char b) { return std::tolower(a) == std::tolower(b); });
1069 }
1070
substr_equal(const std::string & prefix,const std::string & str)1071 bool Ioss::Utils::substr_equal(const std::string &prefix, const std::string &str)
1072 {
1073 return (str.size() >= prefix.size()) && str_equal(prefix, str.substr(0, prefix.size()));
1074 }
1075
uppercase(std::string name)1076 std::string Ioss::Utils::uppercase(std::string name)
1077 {
1078 std::transform(name.begin(), name.end(), name.begin(), [](char c) { return static_cast<char>(std::toupper(c)); });
1079 return name;
1080 }
1081
lowercase(std::string name)1082 std::string Ioss::Utils::lowercase(std::string name)
1083 {
1084 std::transform(name.begin(), name.end(), name.begin(), [](char c) { return static_cast<char>(std::tolower(c)); });
1085 return name;
1086 }
1087
check_set_bool_property(const Ioss::PropertyManager & properties,const std::string & prop_name,bool & prop_value)1088 bool Ioss::Utils::check_set_bool_property(const Ioss::PropertyManager &properties,
1089 const std::string &prop_name, bool &prop_value)
1090 {
1091 bool found_property = false;
1092 if (properties.exists(prop_name)) {
1093 found_property = true;
1094 if (properties.get(prop_name).get_type() == Ioss::Property::INTEGER) {
1095 prop_value = properties.get(prop_name).get_int() != 0;
1096 }
1097 else {
1098 std::string yesno = Ioss::Utils::uppercase(properties.get(prop_name).get_string());
1099 if (yesno == "TRUE" || yesno == "YES" || yesno == "ON") {
1100 prop_value = true;
1101 }
1102 else if (yesno == "FALSE" || yesno == "NO" || yesno == "OFF") {
1103 prop_value = false;
1104 }
1105 else {
1106 std::ostringstream errmsg;
1107 fmt::print(errmsg,
1108 "ERROR: Unrecognized value found for {}. "
1109 "Found '{}' which is not one of TRUE|FALSE|YES|NO|ON|OFF",
1110 prop_name, yesno);
1111 IOSS_ERROR(errmsg);
1112 }
1113 }
1114 }
1115 return found_property;
1116 }
1117
fixup_name(char * name)1118 void Ioss::Utils::fixup_name(char *name)
1119 {
1120 assert(name != nullptr);
1121
1122 size_t len = std::strlen(name);
1123 for (size_t i = 0; i < len; i++) {
1124 name[i] = static_cast<char>(tolower(name[i])); // guaranteed(?) to be ascii...
1125 if (name[i] == ' ') {
1126 name[i] = '_';
1127 }
1128 }
1129 }
1130
fixup_name(std::string & name)1131 void Ioss::Utils::fixup_name(std::string &name)
1132 {
1133 name = Ioss::Utils::lowercase(name);
1134
1135 size_t len = name.length();
1136 for (size_t i = 0; i < len; i++) {
1137 if (name[i] == ' ') {
1138 name[i] = '_';
1139 }
1140 }
1141 }
1142
1143 namespace {
1144
1145 /** \brief Hash function from Aho, Sethi, Ullman "Compilers: Principles,
1146 * Techniques, and Tools. Page 436
1147 */
two_letter_hash(const char * symbol)1148 std::string two_letter_hash(const char *symbol)
1149 {
1150 const int HASHSIZE = 673; // Largest prime less than 676 (26*26)
1151 unsigned int hashval;
1152 for (hashval = 0; *symbol != '\0'; symbol++) {
1153 hashval = (hashval << 4) + *symbol;
1154 unsigned int g = hashval & 0xf0000000;
1155 if (g != 0) {
1156 hashval = hashval ^ (g >> 24);
1157 hashval = hashval ^ g;
1158 }
1159 }
1160
1161 // Convert to base-26 'number'
1162 hashval %= HASHSIZE;
1163 char word[3] = {char(hashval / 26 + 'a'), char(hashval % 26 + 'a'), '\0'};
1164 return (std::string(word));
1165 }
1166 } // namespace
1167
variable_name_kluge(const std::string & name,size_t component_count,size_t copies,size_t max_var_len)1168 std::string Ioss::Utils::variable_name_kluge(const std::string &name, size_t component_count,
1169 size_t copies, size_t max_var_len)
1170 {
1171
1172 // Width = 'max_var_len'.
1173 // Reserve space for suffix '_00...'
1174 // Reserve 3 for hash '.xx'
1175 int hash_len = 3;
1176 int comp_len = 3; // _00
1177 int copy_len = 0;
1178
1179 if (copies > 1) {
1180 assert(component_count % copies == 0);
1181 component_count /= copies;
1182 }
1183
1184 if (component_count <= 1) {
1185 comp_len = 0;
1186 }
1187 else {
1188 comp_len = number_width(component_count) + 1; // _00000
1189 }
1190
1191 if (copies <= 1) {
1192 copy_len = 0;
1193 }
1194 else {
1195 copy_len = number_width(copies) + 1; // _00000
1196 }
1197
1198 size_t maxlen = max_var_len - comp_len - copy_len;
1199
1200 std::string new_str = name;
1201 if (name.length() <= maxlen) {
1202 // If name fits without kluging, then just use name as it is
1203 // without adding on the hash...
1204 return lowercase(new_str);
1205 }
1206 // Know that the name is too long, try to shorten. Need room for
1207 // hash now.
1208 maxlen -= hash_len;
1209 int len = static_cast<int>(name.length());
1210
1211 // Take last 'maxlen' characters. Motivation for this is that the
1212 // beginning of the composed (or generated) variable name is the
1213 // names of the mechanics and mechanics instances in which this
1214 // variable is nested, so they will be similar for all variables at
1215 // the same scope and the differences will occur at the variable
1216 // name level...
1217 //
1218 // However, there will likely be variables at the
1219 // same level but in different scope that have the same name which
1220 // would cause a clash, so we *hope* that the hash will make those
1221 // scope names unique...
1222 std::string s = std::string(name).substr(len - maxlen, len);
1223 assert(s.length() <= maxlen);
1224 new_str = s;
1225
1226 // NOTE: The hash is not added if the name is not shortened.
1227 std::string hash_string = two_letter_hash(name.c_str());
1228 new_str += std::string(".");
1229 new_str += hash_string;
1230 return lowercase(new_str);
1231 }
1232
generate_history_mesh(Ioss::Region * region)1233 void Ioss::Utils::generate_history_mesh(Ioss::Region *region)
1234 {
1235 Ioss::DatabaseIO *db = region->get_database();
1236 if (db->parallel_rank() == 0) {
1237 region->begin_mode(Ioss::STATE_DEFINE_MODEL);
1238
1239 // Node Block
1240 auto *nb = new Ioss::NodeBlock(db, "nodeblock_1", 1, 3);
1241 region->add(nb);
1242
1243 // Element Block
1244 auto *eb = new Ioss::ElementBlock(db, "e1", "sphere", 1);
1245 eb->property_add(Ioss::Property("id", 1));
1246 eb->property_add(Ioss::Property("guid", 1));
1247 region->add(eb);
1248 region->end_mode(Ioss::STATE_DEFINE_MODEL);
1249
1250 region->begin_mode(Ioss::STATE_MODEL);
1251 static double coord[3] = {1.1, 2.2, 3.3};
1252 static int ids[1] = {1};
1253 nb->put_field_data("ids", ids, sizeof(int));
1254 nb->put_field_data("mesh_model_coordinates", coord, 3 * sizeof(double));
1255
1256 static int connect[1] = {1};
1257 eb->put_field_data("ids", ids, sizeof(int));
1258 eb->put_field_data("connectivity", connect, 1 * sizeof(int));
1259
1260 region->end_mode(Ioss::STATE_MODEL);
1261 }
1262 }
1263
1264 // Safer than Ioss::Utils::copy_string -- guarantees null termination
copy_string(char * dest,char const * source,size_t elements)1265 void Ioss::Utils::copy_string(char *dest, char const *source, size_t elements)
1266 {
1267 char *d;
1268 for (d = dest; d + 1 < dest + elements && *source; d++, source++) {
1269 *d = *source;
1270 }
1271 *d = '\0';
1272 }
1273
1274 namespace {
1275 const int tab64[64] = {63, 0, 58, 1, 59, 47, 53, 2, 60, 39, 48, 27, 54, 33, 42, 3,
1276 61, 51, 37, 40, 49, 18, 28, 20, 55, 30, 34, 11, 43, 14, 22, 4,
1277 62, 57, 46, 52, 38, 26, 32, 41, 50, 36, 17, 19, 29, 10, 13, 21,
1278 56, 45, 25, 31, 35, 16, 9, 12, 44, 24, 15, 8, 23, 7, 6, 5};
1279 } // namespace
1280
log_power_2(uint64_t value)1281 int Ioss::Utils::log_power_2(uint64_t value)
1282 {
1283 assert(value > 0);
1284 value = (value << 1) - 1;
1285 value |= value >> 1;
1286 value |= value >> 2;
1287 value |= value >> 4;
1288 value |= value >> 8;
1289 value |= value >> 16;
1290 value |= value >> 32;
1291 return tab64[(((value - (value >> 1)) * 0x07EDD5E59A4E28C2)) >> 58];
1292 }
1293
term_width()1294 int Ioss::Utils::term_width()
1295 {
1296 int cols = 0;
1297 if (isatty(fileno(stdout))) {
1298 #if defined(TIOCGWINSZ)
1299 struct winsize ts;
1300 ioctl(STDOUT_FILENO, TIOCGWINSZ, &ts);
1301 cols = ts.ws_col;
1302 #elif TIOCGSIZE
1303 struct ttysize ts;
1304 ioctl(STDOUT_FILENO, TIOCGSIZE, &ts);
1305 cols = ts.ts_cols;
1306 #endif /* TIOCGSIZE */
1307 }
1308 return cols != 0 ? cols : 100;
1309 }
1310
info_fields(const Ioss::GroupingEntity * ige,Ioss::Field::RoleType role,const std::string & header,const std::string & suffix)1311 void Ioss::Utils::info_fields(const Ioss::GroupingEntity *ige, Ioss::Field::RoleType role,
1312 const std::string &header, const std::string &suffix)
1313 {
1314 Ioss::NameList fields;
1315 ige->field_describe(role, &fields);
1316
1317 if (fields.empty()) {
1318 return;
1319 }
1320
1321 if (!header.empty()) {
1322 fmt::print("{}{}", header, suffix);
1323 }
1324 // Iterate through results fields and transfer to output
1325 // database...
1326 // Get max width of a name...
1327 int max_width = 0;
1328 for (const auto &field_name : fields) {
1329 max_width = max_width > static_cast<int>(field_name.length()) ? max_width : static_cast<int>(field_name.length());
1330 }
1331
1332 auto width = Ioss::Utils::term_width();
1333 if (width == 0) {
1334 width = 80;
1335 }
1336 int cur_out = 8; // Tab width...
1337 if (!header.empty()) {
1338 cur_out = static_cast<int>(header.size() + suffix.size() + 16); // Assume 2 tabs...
1339 }
1340 for (const auto &field_name : fields) {
1341 const Ioss::VariableType *var_type = ige->get_field(field_name).raw_storage();
1342 int comp_count = var_type->component_count();
1343 fmt::print("{1:>{0}s}:{2} ", max_width, field_name, comp_count);
1344 cur_out += max_width + 4;
1345 if (cur_out + max_width >= width) {
1346 fmt::print("\n\t");
1347 cur_out = 8;
1348 }
1349 }
1350 if (!header.empty()) {
1351 fmt::print("\n");
1352 }
1353 }
1354
info_property(const Ioss::GroupingEntity * ige,Ioss::Property::Origin origin,const std::string & header,const std::string & suffix,bool print_empty)1355 void Ioss::Utils::info_property(const Ioss::GroupingEntity *ige, Ioss::Property::Origin origin,
1356 const std::string &header, const std::string &suffix,
1357 bool print_empty)
1358 {
1359 Ioss::NameList properties;
1360 ige->property_describe(origin, &properties);
1361
1362 if (properties.empty()) {
1363 if (print_empty && !header.empty()) {
1364 fmt::print("{}{} *** No attributes ***\n", header, suffix);
1365 }
1366 return;
1367 }
1368
1369 if (!header.empty()) {
1370 fmt::print("{}{}", header, suffix);
1371 }
1372
1373 int num_out = 0;
1374 for (const auto &property_name : properties) {
1375 fmt::print("{:>s}: ", property_name);
1376 auto prop = ige->get_property(property_name);
1377 switch (prop.get_type()) {
1378 case Ioss::Property::BasicType::REAL: fmt::print("{}\t", prop.get_real()); break;
1379 case Ioss::Property::BasicType::INTEGER: fmt::print("{}\t", prop.get_int()); break;
1380 case Ioss::Property::BasicType::STRING: fmt::print("'{}'\t", prop.get_string()); break;
1381 case Ioss::Property::BasicType::VEC_INTEGER:
1382 fmt::print("{}\t", fmt::join(prop.get_vec_int(), " "));
1383 break;
1384 case Ioss::Property::BasicType::VEC_DOUBLE:
1385 fmt::print("{}\t", fmt::join(prop.get_vec_double(), " "));
1386 break;
1387 default:; // Do nothing
1388 }
1389 num_out++;
1390 if (num_out >= 3) {
1391 fmt::print("\n\t");
1392 num_out = 0;
1393 }
1394 }
1395 if (!header.empty()) {
1396 fmt::print("\n");
1397 }
1398 }
1399