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