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 #ifndef IOSS_Ioss_Utils_h 8 #define IOSS_Ioss_Utils_h 9 10 #include "vtk_ioss_mangle.h" 11 12 #include <Ioss_CodeTypes.h> 13 #include <Ioss_Field.h> 14 #include <Ioss_Property.h> 15 #include <Ioss_Sort.h> 16 #include <algorithm> // for sort, lower_bound, copy, etc 17 #include <cassert> 18 #include <cmath> 19 #include <cstddef> // for size_t 20 #include <cstdint> // for int64_t 21 #include <cstdlib> // for nullptrr 22 #include <iostream> // for ostringstream, etcstream, etc 23 #include <stdexcept> // for runtime_error 24 #include <string> // for string 25 #include <vector> // for vector 26 namespace Ioss { 27 class Field; 28 class GroupingEntity; 29 class Region; 30 class SideBlock; 31 class PropertyManager; 32 } // namespace Ioss 33 34 #define IOSS_ERROR(errmsg) throw std::runtime_error((errmsg).str()) 35 36 namespace { 37 // SEE: http://lemire.me/blog/2017/04/10/removing-duplicates-from-lists-quickly unique(std::vector<T> & out,bool skip_first)38 template <typename T> size_t unique(std::vector<T> &out, bool skip_first) 39 { 40 if (out.empty()) 41 return 0; 42 size_t i = 1; 43 size_t pos = 1; 44 T oldv = out[0]; 45 if (skip_first) { 46 i = 2; 47 pos = 2; 48 oldv = out[1]; 49 } 50 for (; i < out.size(); ++i) { 51 T newv = out[i]; 52 out[pos] = newv; 53 pos += (newv != oldv); 54 oldv = newv; 55 } 56 return pos; 57 } 58 } // namespace 59 60 namespace Ioss { 61 /* \brief Utility methods. 62 */ 63 class Utils 64 { 65 public: 66 Utils() = default; 67 ~Utils() = default; 68 69 /** 70 * \defgroup IossStreams Streams used for IOSS output 71 *@{ 72 */ 73 static std::ostream 74 *m_outputStream; ///< general informational output (very rare). Default std::cerr 75 static std::ostream *m_debugStream; ///< debug output when requested. Default std::cerr 76 static std::ostream *m_warningStream; ///< IOSS warning output. Default std::cerr 77 static std::string m_preWarningText; ///< is a string that prepends all warning message output. 78 ///< Default is "\nIOSS WARNING: " 79 80 /** \brief set the stream for all streams (output, debug, and warning) to the specified 81 * `out_stream` 82 */ set_all_streams(std::ostream & out_stream)83 static void set_all_streams(std::ostream &out_stream) 84 { 85 m_outputStream = &out_stream; 86 m_debugStream = &out_stream; 87 m_warningStream = &out_stream; 88 } 89 90 /** \brief set the output stream to the specified `output_stream` 91 */ set_output_stream(std::ostream & output_stream)92 static void set_output_stream(std::ostream &output_stream) { m_outputStream = &output_stream; } 93 94 /** \brief set the debug stream to the specified `debug_stream` 95 */ set_debug_stream(std::ostream & debug_stream)96 static void set_debug_stream(std::ostream &debug_stream) { m_debugStream = &debug_stream; } 97 98 /** \brief set the warning stream to the specified `warning_stream` 99 */ set_warning_stream(std::ostream & warning_stream)100 static void set_warning_stream(std::ostream &warning_stream) 101 { 102 m_warningStream = &warning_stream; 103 } 104 105 /** \brief set the pre-warning text 106 * Sets the text output prior to a warning to the specified text. 107 * Pass an empty string to disable this. Default is `"\nIOSS WARNING: "` 108 */ set_pre_warning_text(const std::string & text)109 static void set_pre_warning_text(const std::string &text) { m_preWarningText = text; } 110 /** @}*/ 111 check_dynamic_cast(const void * ptr)112 static void check_dynamic_cast(const void *ptr) 113 { 114 if (ptr == nullptr) { 115 std::ostringstream errmsg; 116 errmsg << "INTERNAL ERROR: Invalid dynamic cast returned nullptr\n"; 117 IOSS_ERROR(errmsg); 118 } 119 } 120 121 template <typename T> static void uniquify(std::vector<T> &vec, bool skip_first = false) 122 { 123 auto it = vec.begin(); 124 if (skip_first) { 125 it++; 126 } 127 Ioss::sort(it, vec.end()); 128 vec.resize(unique(vec, skip_first)); 129 vec.shrink_to_fit(); 130 } 131 generate_index(std::vector<T> & index)132 template <typename T> static void generate_index(std::vector<T> &index) 133 { 134 T sum = 0; 135 for (size_t i = 0; i < index.size() - 1; i++) { 136 T cnt = index[i]; 137 index[i] = sum; 138 sum += cnt; 139 } 140 index.back() = sum; 141 } 142 find_index_location(T node,const std::vector<T> & index)143 template <typename T> static T find_index_location(T node, const std::vector<T> &index) 144 { 145 // 0-based node numbering 146 // index[p] = first node (0-based) on processor p 147 148 #if 1 149 // Assume data coherence. I.e., a new search will be close to the 150 // previous search. 151 static size_t prev = 1; 152 153 size_t nproc = index.size(); 154 if (prev < nproc && index[prev - 1] <= node && index[prev] > node) { 155 return prev - 1; 156 } 157 158 for (size_t p = 1; p < nproc; p++) { 159 if (index[p] > node) { 160 prev = p; 161 return p - 1; 162 } 163 } 164 std::ostringstream errmsg; 165 errmsg << "FATAL ERROR: find_index_location. Searching for " << node << " in:\n"; 166 for (auto idx : index) { 167 errmsg << idx << ", "; 168 } 169 errmsg << "\n"; 170 IOSS_ERROR(errmsg); 171 #else 172 return std::distance(index.begin(), std::upper_bound(index.begin(), index.end(), node)) - 1; 173 #endif 174 } 175 176 static void copy_string(char *dest, char const *source, size_t elements); 177 copy_string(char * dest,const std::string & source,size_t elements)178 static void copy_string(char *dest, const std::string &source, size_t elements) 179 { 180 copy_string(dest, source.c_str(), elements); 181 } 182 copy_string(char (& output)[size],const std::string & source)183 template <size_t size> static void copy_string(char (&output)[size], const std::string &source) 184 { 185 copy_string(output, source.c_str(), size); 186 } 187 copy_string(char (& output)[size],const char * source)188 template <size_t size> static void copy_string(char (&output)[size], const char *source) 189 { 190 // Copy the string — don’t copy too many bytes. 191 copy_string(output, source, size); 192 } 193 clear(std::vector<T> & vec)194 template <typename T> static void clear(std::vector<T> &vec) 195 { 196 vec.clear(); 197 vec.shrink_to_fit(); 198 assert(vec.capacity() == 0); 199 } 200 201 /** 202 * Returns the number of digits required to print the number. 203 * If `use_commas` is specified, then the width will be adjusted 204 * to account for the comma used every 3 digits. 205 * (1,234,567,890 would return 13) 206 * Typically used with the `fmt::print()` functions as: 207 * ``` 208 * fmt::print("{:{}L}", number, number_width(number,true)) 209 * fmt::print("{:{}d}", number, number_width(number,false)) 210 * ``` 211 */ 212 inline static int number_width(const size_t number, bool use_commas = false) 213 { 214 if (number == 0) { 215 return 1; 216 } 217 int width = int(std::floor(std::log10(number))) + 1; 218 if (use_commas) { 219 width += ((width - 1) / 3); 220 } 221 return width; 222 } 223 power_2(int count)224 inline static int power_2(int count) 225 { 226 // Return the power of two which is equal to or greater than `count` 227 // count = 15 -> returns 16 228 // count = 16 -> returns 16 229 // count = 17 -> returns 32 230 231 // Use brute force... 232 int pow2 = 1; 233 while (pow2 < count) { 234 pow2 *= 2; 235 } 236 return pow2; 237 } 238 check_block_order(const std::vector<T * > & blocks)239 template <typename T> static bool check_block_order(const std::vector<T *> &blocks) 240 { 241 #ifndef NDEBUG 242 // Verify that element blocks are defined in sorted offset order... 243 typename std::vector<T *>::const_iterator I; 244 245 int64_t eb_offset = -1; 246 for (I = blocks.begin(); I != blocks.end(); ++I) { 247 int64_t this_off = (*I)->get_offset(); 248 if (this_off < eb_offset) { 249 { 250 { 251 return false; 252 } 253 } 254 } 255 eb_offset = this_off; 256 } 257 #endif 258 return true; 259 } 260 261 static int term_width(); 262 263 static int log_power_2(uint64_t value); 264 265 static char **get_name_array(size_t count, int size); 266 static void delete_name_array(char **names, int count); 267 268 /** \brief Get formatted time and date strings. 269 * 270 * Fill time_string and date_string with current time and date 271 * formatted as "HH:MM:SS" for time and "yy/mm/dd" or "yyyy/mm/dd" 272 * for date. 273 * 274 * \param[out] time_string The formatted time string. 275 * \param[out] date_string The formatted date string. 276 * \param[in] length Use 8 for short-year date format, or 10 for long-year date format. 277 */ 278 static void time_and_date(char *time_string, char *date_string, size_t length); 279 280 static std::string decode_filename(const std::string &filename, int processor, 281 int num_processors); 282 static size_t get_number(const std::string &suffix); 283 static int64_t extract_id(const std::string &name_id); 284 static std::string encode_entity_name(const std::string &entity_type, int64_t id); 285 286 /** \brief create a string that describes the list of input `ids` collapsing ranges if possible. 287 * 288 * Traverse the sorted input vector `ids` and return a string that has all sequential ranges 289 * collapsed and separated by `rng_sep` and all individual ids or ranges separated by `seq_sep`. 290 * Will throw an exception if `ids` is not sorted. An empty list returns an empty string. 291 * The sequence of ids `1, 2, 3, 5, 6, 7` with `rng_sep=".."` will return the default 292 * string `1..3, 5..8` 293 */ 294 static std::string format_id_list(const std::vector<size_t> &ids, 295 const std::string & rng_sep = " to ", 296 const std::string & seq_sep = ", "); 297 298 /** \brief Convert a string to lower case, and convert spaces to `_`. 299 * 300 * The conversion is performed in place. 301 * 302 * \param[in,out] name On input, the string to convert. On output, the converted string. 303 * 304 */ 305 static void fixup_name(char *name); 306 307 /** \brief Convert a string to lower case, and convert spaces to `_`. 308 * 309 * The conversion is performed in place. 310 * 311 * \param[in,out] name On input, the string to convert. On output, the converted string. 312 * 313 */ 314 static void fixup_name(std::string &name); 315 316 /** \brief Check whether property `prop_name` exists and if so, set `prop_value` 317 * 318 * based on the property value. Either "TRUE", "YES", "ON", or nonzero for true; 319 * or "FALSE", "NO", "OFF", or 0 for false. 320 * \param[in] properties the Ioss::PropertyManager containing the properties to be checked. 321 * \param[in] prop_name the name of the property to check whether it exists and if so, set its 322 * value. 323 * \param[out] prop_value if `prop_name` exists and has a valid value, set prop_value 324 * accordingly. Does not modify if `prop_name` does not exist. \returns true/false depending on 325 * whether property found and value set. 326 */ 327 328 static bool check_set_bool_property(const Ioss::PropertyManager &properties, 329 const std::string &prop_name, bool &prop_value); 330 331 /** \brief Determine whether an entity has the property `omitted`. 332 * 333 * \param[in] block The entity. 334 * \returns True if the entity has the property `omitted`. 335 */ 336 static bool block_is_omitted(Ioss::GroupingEntity *block); 337 338 /** \brief Process the base element type `base` which has 339 * `nodes_per_element` nodes and a spatial dimension of `spatial` 340 * into a form that the IO system can (hopefully) recognize. 341 * 342 * Lowercases the name; converts spaces to `_`, adds 343 * nodes_per_element at end of name (if not already there), and 344 * does some other transformations to remove some exodusII ambiguity. 345 * 346 * \param[in] base The element base name. 347 * \param[in] nodes_per_element The number of nodes per element. 348 * \param[in] spatial The spatial dimension of the element. 349 * \returns The Ioss-formatted element name. 350 */ 351 static std::string fixup_type(const std::string &base, int nodes_per_element, int spatial); 352 353 /** \brief Convert a string to upper case. 354 * 355 * \param[in] name The string to convert. 356 * \returns The converted string. 357 */ 358 static std::string uppercase(std::string name); 359 360 /** \brief Convert a string to lower case. 361 * 362 * \param[in] name The string to convert. 363 * \returns The converted string. 364 */ 365 static std::string lowercase(std::string name); 366 367 static void check_non_null(void *ptr, const char *type, const std::string &name, 368 const std::string &func); 369 370 /** \brief Case-insensitive string comparison. 371 * 372 * \param[in] s1 First string 373 * \param[in] s2 Second string 374 * \returns `true` if strings are equal 375 */ 376 static bool str_equal(const std::string &s1, const std::string &s2); 377 378 /** \brief Case-insensitive substring comparison. 379 * 380 * \param[in] prefix 381 * \param[in] str 382 * \returns `true` if `str` begins with `prefix` or `prefix` is empty 383 */ 384 static bool substr_equal(const std::string &prefix, const std::string &str); 385 386 /** \brief Get a string containing `uname` output. 387 * 388 * This output contains information about the current computing platform. 389 * This is used as information data in the created results file to help 390 * in tracking when/where/... the file was created. 391 * 392 * \returns The platform information string. 393 */ 394 static std::string platform_information(); 395 396 /** \brief Return amount of memory being used on this processor */ 397 static size_t get_memory_info(); 398 static size_t get_hwm_memory_info(); 399 400 /** \brief Get a filename relative to the specified working directory (if any) 401 * of the current execution. 402 * 403 * Working_directory must end with `/` or be empty. 404 * 405 * \param[in] relative_filename The file path to be appended to the working directory path. 406 * \param[in] type The file type. "generated" file types are treated differently. 407 * \param[in] working_directory the path to which the relative_filename path is appended. 408 * \returns The full path (working_directory + relative_filename) 409 */ 410 static std::string local_filename(const std::string &relative_filename, const std::string &type, 411 const std::string &working_directory); 412 413 static void get_fields(int64_t entity_count, char **names, size_t num_names, 414 Ioss::Field::RoleType fld_role, bool enable_field_recognition, 415 char suffix_separator, int *local_truth, 416 std::vector<Ioss::Field> &fields); 417 418 static int field_warning(const Ioss::GroupingEntity *ge, const Ioss::Field &field, 419 const std::string &inout); 420 421 static void calculate_sideblock_membership(IntVector &face_is_member, const SideBlock *ef_blk, 422 size_t int_byte_size, const void *element, 423 const void *sides, int64_t number_sides, 424 const Region *region); 425 426 /** \brief Get the appropriate index offset for the sides of elements in a SideBlock. 427 * 428 * And yet another idiosyncrasy of sidesets... 429 * The side of an element (especially shells) can be 430 * either a face or an edge in the same sideset. The 431 * ordinal of an edge is (local_edge_number+numfaces) on the 432 * database, but needs to be (local_edge_number) for Sierra... 433 * 434 * If the sideblock has a "parent_element_topology" and a 435 * "topology", then we can determine whether to offset the 436 * side ordinals... 437 * 438 * \param[in] sb Compute the offset for element sides in this SideBlock 439 * \returns The offset. 440 */ 441 static int64_t get_side_offset(const Ioss::SideBlock *sb); 442 443 static unsigned int hash(const std::string &name); 444 445 static double timer(); 446 447 /** \brief Convert an input file to a vector of strings containing one string for each line of 448 * the file. 449 * 450 * Should only be called by a single processor or each processor will be accessing the file 451 * at the same time... 452 * 453 * \param[in] file_name The name of the file. 454 * \param[out] lines The vector of strings containing the lines of the file 455 * \param[in] max_line_length The maximum number of characters in any line of the file. 456 */ 457 static void input_file(const std::string &file_name, std::vector<std::string> *lines, 458 size_t max_line_length = 0); 459 to_string(const T & t)460 template <class T> static std::string to_string(const T &t) { return std::to_string(t); } 461 462 //! \brief Tries to shorten long variable names to an acceptable 463 //! length, and converts to lowercase and spaces to `_` 464 //! 465 //! Many databases have a maximum length for variable names which can 466 //! cause a problem with variable name length. 467 //! 468 469 //! This routine tries to shorten long variable names to an 470 //! acceptable length (`max_var_len` characters max). If the name 471 //! is already less than this length, it is returned unchanged... 472 //! 473 //! Since there is a (good) chance that two shortened names will match, 474 //! a 2-letter `hash` code is appended to the end of the variable name. 475 //! 476 //! So, we shorten the name to a maximum of `max_var_len`-3 477 //! characters and append a 2 character hash+separator. 478 //! 479 //! It also converts name to lowercase and converts spaces to `_` 480 static std::string variable_name_kluge(const std::string &name, size_t component_count, 481 size_t copies, size_t max_var_len); 482 483 /** \brief Create a nominal mesh for use in history databases. 484 * 485 * The model for a history file is a single sphere element (1 node, 1 element). 486 * This is needed for some applications that read this file that require a 487 * "mesh" even though a history file is just a collection of global variables 488 * with no real mesh. This routine will add the mesh portion to a history file. 489 * 490 * \param[in,out] region The region on which the nominal mesh is to be defined. 491 */ 492 static void generate_history_mesh(Ioss::Region *region); 493 494 static void info_fields(const Ioss::GroupingEntity *ige, Ioss::Field::RoleType role, 495 const std::string &header, const std::string &suffix = "\n\t"); 496 497 static void info_property(const Ioss::GroupingEntity *ige, Ioss::Property::Origin origin, 498 const std::string &header, const std::string &suffix = "\n\t", 499 bool print_empty = false); 500 }; 501 OUTPUT()502 inline std::ostream &OUTPUT() { return *Utils::m_outputStream; } 503 DEBUG()504 inline std::ostream &DEBUG() { return *Utils::m_debugStream; } 505 WARNING()506 inline std::ostream &WARNING() 507 { 508 *Utils::m_warningStream << Utils::m_preWarningText; 509 return *Utils::m_warningStream; 510 } 511 512 } // namespace Ioss 513 #endif 514