Line data Source code
1 : // The libMesh Finite Element Library.
2 : // Copyright (C) 2002-2025 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 :
4 : // This library is free software; you can redistribute it and/or
5 : // modify it under the terms of the GNU Lesser General Public
6 : // License as published by the Free Software Foundation; either
7 : // version 2.1 of the License, or (at your option) any later version.
8 :
9 : // This library is distributed in the hope that it will be useful,
10 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : // Lesser General Public License for more details.
13 :
14 : // You should have received a copy of the GNU Lesser General Public
15 : // License along with this library; if not, write to the Free Software
16 : // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 :
18 :
19 : #ifndef LIBMESH_UTILITY_H
20 : #define LIBMESH_UTILITY_H
21 :
22 : // LibMesh includes
23 : #include "libmesh/libmesh_common.h" // for Real
24 :
25 : // C++ includes
26 : #include <string>
27 : #include <vector>
28 : #include <algorithm> // is_sorted, lower_bound
29 : #include <memory> // unique_ptr
30 :
31 : namespace libMesh
32 : {
33 :
34 : /**
35 : * Encapsulates the common "get value from map, otherwise error"
36 : * idiom, which is similar to calling map.at(), but gives a more
37 : * useful error message with a line number.
38 : */
39 : #define libmesh_map_find(map, key) libMesh::Utility::map_find((map), (key), __FILE__, __LINE__)
40 :
41 : /**
42 : * Encapsulates the common "get value from vector, otherwise error"
43 : * idiom, which is similar to calling vec.at(), but gives a more
44 : * useful error message with a line number.
45 : */
46 : #define libmesh_vector_at(vec, idx) libMesh::Utility::vector_at((vec), (idx), __FILE__, __LINE__)
47 :
48 : // ------------------------------------------------------------
49 : // The Utility namespace is for functions
50 : // which are useful but don't necessarily belong anywhere else.
51 :
52 : namespace Utility
53 : {
54 :
55 : /**
56 : * \returns A string containing information about the system you are
57 : * running on.
58 : */
59 : std::string system_info();
60 :
61 : /**
62 : * Helper struct for enabling template metaprogramming/SFINAE.
63 : */
64 : template <typename T>
65 : class is_streamable
66 : {
67 : template <typename U> // must be template to get SFINAE fall-through...
68 : static auto test(const U* u) -> decltype(std::cout << *u);
69 :
70 : static auto test(...) -> std::false_type;
71 :
72 : public:
73 : enum { value = !std::is_same<decltype(test((T*)0)), std::false_type>::value };
74 : };
75 :
76 : /**
77 : * -Wdangling-reference was nowhere *near* ready to add to -Wall in
78 : * gcc 13. It's been moved to -Wextra, but we use that too. :-)
79 : *
80 : * See e.g. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109642
81 : *
82 : * Our map_find functions trigger it.
83 : */
84 :
85 : #if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined(__clang__)
86 : #if (__GNUC__ > 12)
87 : #pragma GCC diagnostic push
88 : #pragma GCC diagnostic ignored "-Wdangling-reference"
89 : #endif
90 : #endif
91 :
92 : /**
93 : * This function should not be called directly (although it can be),
94 : * instead see the libmesh_map_find() macro.
95 : *
96 : * Calls find(key), and checks the result against end(). Returns the
97 : * corresponding value if found, throws an error otherwise. Templated
98 : * on the type of map, so this will work with both std::map and
99 : * std::unordered_map.
100 : */
101 : template<typename Map, typename Key,
102 : typename std::enable_if<!is_streamable<Key>::value, Key>::type* = nullptr>
103 : inline
104 : typename Map::mapped_type &
105 316664 : map_find(Map & map,
106 : const Key & key,
107 : const char * filename,
108 : int line_number)
109 : {
110 205880 : auto it = map.find(key);
111 316664 : libmesh_error_msg_if(it == map.end(),
112 : "map_find() error: key not found in file "
113 : << filename << " on line " << line_number);
114 316664 : return it->second;
115 : }
116 :
117 : /**
118 : * A version of the function above that works for const objects.
119 : */
120 : template<typename Map, typename Key,
121 : typename std::enable_if<!is_streamable<Key>::value, Key>::type* = nullptr>
122 : inline
123 : const typename Map::mapped_type &
124 8736 : map_find(const Map & map,
125 : const Key & key,
126 : const char * filename,
127 : int line_number)
128 : {
129 684 : auto it = map.find(key);
130 8736 : libmesh_error_msg_if(it == map.end(),
131 : "map_find() error: key not found in file "
132 : << filename << " on line " << line_number);
133 8736 : return it->second;
134 : }
135 :
136 : /**
137 : * A version of the map_find() utility which can only be used if
138 : * the map key is printable via std::stream.
139 : */
140 : template<typename Map, typename Key,
141 : typename std::enable_if<is_streamable<Key>::value, Key>::type* = nullptr>
142 : inline
143 : typename Map::mapped_type &
144 29520989 : map_find(Map & map,
145 : const Key & key,
146 : const char * filename,
147 : int line_number)
148 : {
149 19673053 : auto it = map.find(key);
150 29520989 : libmesh_error_msg_if(it == map.end(),
151 : "map_find() error: key \"" << key << "\" not found in file "
152 : << filename << " on line " << line_number);
153 29520989 : return it->second;
154 : }
155 :
156 : /**
157 : * A version of the function above that works for const objects.
158 : */
159 : template<typename Map, typename Key,
160 : typename std::enable_if<is_streamable<Key>::value, Key>::type* = nullptr>
161 : inline
162 : const typename Map::mapped_type &
163 59730078 : map_find(const Map & map,
164 : const Key & key,
165 : const char * filename,
166 : int line_number)
167 : {
168 4564172 : auto it = map.find(key);
169 59730504 : libmesh_error_msg_if(it == map.end(),
170 : "map_find() error: key \"" << key << "\" not found in file "
171 : << filename << " on line " << line_number);
172 59729865 : return it->second;
173 : }
174 :
175 : // The map_find functions are our only dangling-reference false positives
176 :
177 : #if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined(__clang__)
178 : #if (__GNUC__ > 12)
179 : #pragma GCC diagnostic pop
180 : #endif
181 : #endif
182 :
183 :
184 : /**
185 : * A replacement for std::vector::at(i) which is meant to be used with
186 : * a macro, and, unlike at(), gives a proper line number and useful
187 : * error message when the index is past the end.
188 : */
189 : template<typename Vector>
190 : inline
191 : typename Vector::reference &
192 4623480 : vector_at(Vector & vec,
193 : typename Vector::size_type i,
194 : const char * filename,
195 : int line_number)
196 : {
197 5008770 : libmesh_error_msg_if(i >= vec.size(),
198 : "vec_at() error: Index " << i <<
199 : " past end of vector in file " << filename <<
200 : " on line " << line_number);
201 4623480 : return vec[i];
202 : }
203 :
204 : /**
205 : * Same as above, but for const inputs.
206 : */
207 : template<typename Vector>
208 : inline
209 : typename Vector::const_reference &
210 33237 : vector_at(const Vector & vec,
211 : typename Vector::size_type i,
212 : const char * filename,
213 : int line_number)
214 : {
215 33237 : libmesh_error_msg_if(i >= vec.size(),
216 : "vec_at() error: Index " << i <<
217 : " past end of vector in file " << filename <<
218 : " on line " << line_number);
219 33237 : return vec[i];
220 : }
221 :
222 :
223 : #ifdef LIBMESH_ENABLE_DEPRECATED
224 : /**
225 : * Utility::iota was created back when std::iota was just an
226 : * SGI STL extension.
227 : */
228 : template <typename ForwardIter, typename T>
229 : void iota (ForwardIter first, ForwardIter last, T value)
230 : {
231 : // Use std::iota instead!
232 : libmesh_deprecated();
233 :
234 : while (first != last)
235 : {
236 : *first = value++;
237 : ++first;
238 : }
239 : }
240 :
241 :
242 : /**
243 : * Utility::is_sorted was created back when std::is_sorted was just an
244 : * SGI STL extension.
245 : */
246 : template<class InputIterator >
247 : bool is_sorted(InputIterator first, InputIterator last)
248 : {
249 : libmesh_deprecated();
250 : return std::is_sorted(first, last);
251 : }
252 : #endif // LIBMESH_ENABLE_DEPRECATED
253 :
254 :
255 : /**
256 : * The STL provides \p std::binary_search() which returns \p true or
257 : * \p false depending on whether the searched-for value is found. In
258 : * contrast, Utility::binary_find() uses a std::lower_bound() based
259 : * search on a sorted range to find the required value.
260 : *
261 : * \returns An iterator to the searched-for element, or "last" if the
262 : * element is not found.
263 : */
264 : template<class ForwardIterator, class T>
265 572 : ForwardIterator binary_find(ForwardIterator first, ForwardIterator last, const T & value)
266 : {
267 572 : ForwardIterator it = std::lower_bound(first, last, value);
268 572 : return (it == last || value < *it) ? last : it;
269 : }
270 :
271 : /**
272 : * As above, but takes a custom comparison object.
273 : */
274 : template<class ForwardIterator, class T, class Compare>
275 : ForwardIterator binary_find(ForwardIterator first, ForwardIterator last, const T & value, Compare comp)
276 : {
277 : ForwardIterator it = std::lower_bound(first, last, value, comp);
278 : return (it == last || comp(value,*it)) ? last : it;
279 : }
280 :
281 :
282 : /**
283 : * An efficient template instantiation for raising
284 : * to an arbitrary integer power.
285 : */
286 : template <int N, typename T>
287 : struct do_pow {
288 147958473 : static inline T apply (const T & x)
289 : {
290 : libmesh_assert(N>1);
291 :
292 : if (N%2) // odd exponent
293 3765234 : return x * do_pow<N-1,T>::apply(x);
294 :
295 147882656 : const T xNover2 = do_pow<N/2,T>::apply(x);
296 :
297 7240791008 : return xNover2*xNover2;
298 : }
299 : };
300 :
301 : // An efficient compiler would distill N=6 down to 3
302 : // multiplications, but an inefficient one (or a complicated
303 : // T::operator*) might do worse, so we'll specialize here.
304 : template <typename T>
305 : struct do_pow<6,T> {
306 289 : static inline T apply (const T & x)
307 : {
308 3179 : const T x2 = x*x,
309 3179 : x4 = x2*x2;
310 :
311 3179 : return x4*x2;
312 : }
313 : };
314 :
315 : template <typename T>
316 : struct do_pow<1,T> {
317 59113157 : static inline T apply (const T & x) { return x; }
318 : };
319 :
320 : template <typename T>
321 : struct do_pow<0,T> {
322 : static inline T apply (const T &) { return 1; }
323 : };
324 :
325 :
326 : template <int N, typename T>
327 : inline
328 598789643 : T pow(const T & x)
329 : {
330 598789643 : return do_pow<N,T>::apply(x);
331 : }
332 :
333 : /**
334 : * A simple implementation of the factorial.
335 : */
336 : inline
337 : unsigned int factorial(unsigned int n)
338 : {
339 :
340 : unsigned int factorial_n = 1;
341 :
342 : if (n==0)
343 : return factorial_n;
344 :
345 : for (unsigned int i=1; i<n; i++)
346 : factorial_n *= i+1;
347 :
348 : return factorial_n;
349 : }
350 :
351 :
352 : // Simple function to compute "n choose k", aka the binomial coefficient.
353 : template <typename T>
354 0 : T binomial(T n, T k)
355 : {
356 0 : T ret = 1;
357 :
358 : // Binomial function is "symmetric" in k, C(n, k) = C(n, n-k).
359 0 : if (k > n - k)
360 0 : k = n - k;
361 :
362 : // Compute n * (n-1) * ... * (n-k+1) / (k * (k-1) * ... * 1)
363 0 : for (T i = 0; i < k; ++i)
364 : {
365 0 : ret *= (n - i);
366 0 : ret /= (i + 1);
367 : }
368 :
369 0 : return ret;
370 : }
371 :
372 :
373 : /**
374 : * A convenient method to truly empty a vector using the "swap trick"
375 : */
376 : template <typename T>
377 9912 : void deallocate (std::vector<T> & vec)
378 : {
379 9960 : std::vector<T>().swap(vec);
380 9912 : }
381 :
382 :
383 : // When looking for a complicated suffix to a filename, we only want
384 : // to consider the base name, without any preceding path name.
385 : // "/tmp/foo.e25ad0/mesh.msh" is not ExodusII.
386 : std::string_view basename_of(const std::string & fullname);
387 :
388 :
389 : /**
390 : * Look for a substring within a string.
391 : */
392 : bool contains(std::string_view superstring,
393 : std::string_view substring);
394 :
395 :
396 : /**
397 : * Look for a substring at the very end of a string.
398 : */
399 : bool ends_with(std::string_view superstring,
400 : std::string_view suffix);
401 :
402 :
403 : // Utility functions useful when dealing with complex numbers.
404 :
405 : #ifdef LIBMESH_USE_COMPLEX_NUMBERS
406 :
407 : /**
408 : * \returns For \p r_o_c = 0 the filename for output of the real part
409 : * of complex data, and for \p r_o_c = 1 the filename for the imaginary
410 : * part.
411 : */
412 : std::string complex_filename (std::string basename,
413 : unsigned int r_o_c=0);
414 :
415 : /**
416 : * Prepare complex data for writing.
417 : */
418 : void prepare_complex_data (const std::vector<Complex> & source,
419 : std::vector<Real> & real_part,
420 : std::vector<Real> & imag_part);
421 :
422 : #endif // #ifdef LIBMESH_USE_COMPLEX_NUMBERS
423 :
424 :
425 : /**
426 : * Create a directory.
427 : */
428 : int mkdir(const char* pathname);
429 :
430 :
431 : /**
432 : * Create an unzipped copy of a bz2 or xz file, returning the name of
433 : * the now-unzipped file that can be directly opened.
434 : *
435 : * This is a hack because we don't have a neat bz2/xz equivalent to
436 : * gzstreams.
437 : */
438 : std::string unzip_file (std::string_view name);
439 :
440 :
441 : /**
442 : * This Functor simply takes an object and reverses its byte
443 : * representation. This is useful for changing endian-ness
444 : * for file IO. This class has been tested on x86 architectures
445 : * with 4-byte words.
446 : *
447 : *
448 : */
449 : class ReverseBytes
450 : {
451 : public:
452 :
453 : /**
454 : * Constructor. Takes a bool, determines if we will actually
455 : * do byte reversing.
456 : */
457 : explicit
458 : ReverseBytes (const bool dr);
459 :
460 : /**
461 : * Functor. Takes the data to reverse and performs the
462 : * byte-ordering reversal.
463 : */
464 : template <typename T>
465 : T operator () (T & data) const;
466 :
467 : private:
468 :
469 : /**
470 : * \returns The value of the reverse flag.
471 : */
472 42185 : bool reverse () const { return _do_reverse; }
473 :
474 : /**
475 : * flag
476 : */
477 : const bool _do_reverse;
478 : };
479 :
480 :
481 :
482 : // ReverseBytes inline members
483 : inline
484 11 : ReverseBytes::ReverseBytes (const bool rb) :
485 11 : _do_reverse (rb)
486 1 : {}
487 :
488 :
489 : template <typename T>
490 : inline
491 42185 : T ReverseBytes::operator() (T & data) const
492 : {
493 : // Possibly reverse the byte ordering
494 42185 : if (this->reverse())
495 : {
496 0 : unsigned char * b = (unsigned char *) &data;
497 :
498 0 : int i=0;
499 0 : int j=(sizeof(T) - 1);
500 :
501 0 : while (i < j)
502 : {
503 0 : std::swap (b[i], b[j]);
504 0 : i++; j--;
505 : }
506 : }
507 :
508 43464 : return data;
509 : }
510 :
511 :
512 :
513 : /**
514 : * Struct which defines a custom comparison object that
515 : * can be used with std::sets of std::unique_ptrs
516 : */
517 : struct CompareUnderlying
518 : {
519 : /**
520 : * As of C++14, std::set::find() can be a templated overload.
521 : * https://en.cppreference.com/w/cpp/container/set/find
522 : * We enable this by defining is_transparent as a type.
523 : */
524 : using is_transparent = void;
525 :
526 : /**
527 : * This is already what the default operator< comparison for std::unique_ptrs does,
528 : * we are not adding anything here.
529 : */
530 : template <class T>
531 0 : bool operator()(const std::unique_ptr<T> & a, const std::unique_ptr<T> & b) const
532 : {
533 0 : return a.get() < b.get();
534 : }
535 :
536 : /**
537 : * operator< comparison when rhs is a dumb pointer
538 : */
539 : template <class T>
540 0 : bool operator()(const std::unique_ptr<T> & a, const T * const & b) const
541 : {
542 0 : return a.get() < b;
543 : }
544 :
545 : /**
546 : * operator< comparison when lhs is a dumb pointer
547 : */
548 : template <class T>
549 0 : bool operator()(const T * const & a, const std::unique_ptr<T> & b) const
550 : {
551 0 : return a < b.get();
552 : }
553 : };
554 :
555 : } // namespace Utility
556 :
557 : } // namespace libMesh
558 :
559 : #endif // LIBMESH_UTILITY_H
|