Line data Source code
1 : //* This file is part of the MOOSE framework
2 : //* https://mooseframework.inl.gov
3 : //*
4 : //* All rights reserved, see COPYRIGHT for full restrictions
5 : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 : //*
7 : //* Licensed under LGPL 2.1, please see LICENSE for details
8 : //* https://www.gnu.org/licenses/lgpl-2.1.html
9 :
10 : #pragma once
11 :
12 : // MOOSE includes
13 : #include "MooseTypes.h"
14 : #include "HashMap.h"
15 : #include "InfixIterator.h"
16 : #include "MooseEnumItem.h"
17 : #include "MooseError.h"
18 : #include "Moose.h"
19 : #include "ExecutablePath.h"
20 : #include "ConsoleUtils.h"
21 : #include "MooseStringUtils.h"
22 :
23 : #include "libmesh/compare_types.h"
24 : #include "libmesh/bounding_box.h"
25 : #include "libmesh/int_range.h"
26 : #include "libmesh/tensor_tools.h"
27 : #include "metaphysicl/raw_type.h"
28 : #include "metaphysicl/metaphysicl_version.h"
29 : #include "metaphysicl/dualnumber_decl.h"
30 : #include "metaphysicl/dynamic_std_array_wrapper.h"
31 : #include "timpi/standard_type.h"
32 :
33 : // C++ includes
34 : #include <string>
35 : #include <vector>
36 : #include <map>
37 : #include <list>
38 : #include <filesystem>
39 : #include <deque>
40 : #include <regex>
41 :
42 : // Forward Declarations
43 : class InputParameters;
44 : class ExecFlagEnum;
45 : class MaterialProperties;
46 : class MaterialBase;
47 :
48 : namespace libMesh
49 : {
50 : class Elem;
51 : namespace Parallel
52 : {
53 : class Communicator;
54 : }
55 : }
56 : class MultiMooseEnum;
57 :
58 : namespace MooseUtils
59 : {
60 :
61 : std::filesystem::path pathjoin(const std::filesystem::path & p);
62 :
63 : template <typename... Args>
64 : std::filesystem::path
65 356355 : pathjoin(const std::filesystem::path & p, Args... args)
66 : {
67 356355 : return p / pathjoin(args...);
68 : }
69 :
70 : /// Check if the input string can be parsed into a Real
71 : /// @param input input to check / parse
72 : /// @param parsed_real pointer to a Real that gets set to the parsed real if it does parse to Real
73 : bool parsesToReal(const std::string & input, Real * parsed_real = nullptr);
74 :
75 : /// Returns the location of either a local repo run_tests script - or an
76 : /// installed test executor script if run_tests isn't found.
77 : std::string runTestsExecutable();
78 :
79 : /// Searches in the current working directory and then recursively up in each
80 : /// parent directory looking for a "testroot" file. Returns the full path to
81 : /// the first testroot file found.
82 : std::string findTestRoot();
83 :
84 : /// Returns the directory of any installed inputs or the empty string if none are found.
85 : std::string installedInputsDir(const std::string & app_name,
86 : const std::string & dir_name,
87 : const std::string & extra_error_msg = "");
88 :
89 : /// Returns the directory of any installed docs/site.
90 : std::string docsDir(const std::string & app_name);
91 :
92 : /**
93 : * Returns the URL of a page located on the MOOSE documentation site.
94 : *
95 : * @param[in] path URL path following the domain name. For example, in the
96 : * URL "www.example.com/folder1/folder2/file.html", this
97 : * would be "folder1/folder2/file.html".
98 : */
99 : std::string mooseDocsURL(const std::string & path);
100 :
101 : /// Replaces all occurrences of from in str with to and returns the result.
102 : std::string replaceAll(std::string str, const std::string & from, const std::string & to);
103 :
104 : /**
105 : * Replaces "LATEST" placeholders with the latest checkpoint file name.
106 : */
107 : std::string convertLatestCheckpoint(std::string orig);
108 :
109 : /// Computes and returns the Levenshtein distance between strings s1 and s2.
110 : int levenshteinDist(const std::string & s1, const std::string & s2);
111 :
112 : /**
113 : * This function will escape all of the standard C++ escape characters so that they can be printed.
114 : * The
115 : * passed in parameter is modified in place
116 : */
117 : void escape(std::string & str);
118 :
119 : /**
120 : * Removes additional whitespace from a string
121 : *
122 : * Removes beginning whitespace, end whitespace, and repeated whitespace into a single space
123 : */
124 : std::string removeExtraWhitespace(const std::string & str);
125 :
126 : /**
127 : * Python like split functions for strings.
128 : *
129 : * NOTE: This is similar to the tokenize function, but it maintains empty items, which tokenize does
130 : * not. For example, "foo;bar;;" becomes {"foo", "bar", "", ""}.
131 : */
132 : std::vector<std::string> split(const std::string & str,
133 : const std::string & delimiter,
134 : std::size_t max_count = std::numeric_limits<std::size_t>::max());
135 : std::vector<std::string> rsplit(const std::string & str,
136 : const std::string & delimiter,
137 : std::size_t max_count = std::numeric_limits<std::size_t>::max());
138 :
139 : /**
140 : * Python-like join function for strings over an iterator range.
141 : */
142 : template <typename Iterator>
143 : std::string
144 104231 : join(Iterator begin, Iterator end, const std::string & delimiter)
145 : {
146 104231 : std::ostringstream oss;
147 104231 : std::copy(begin, end, infix_ostream_iterator<std::string>(oss, delimiter.c_str()));
148 208462 : return oss.str();
149 104231 : }
150 :
151 : /**
152 : * Python-like join function for strings over a container.
153 : */
154 : template <typename T>
155 : std::string
156 104079 : join(const T & strings, const std::string & delimiter)
157 : {
158 104079 : return join(strings.begin(), strings.end(), delimiter);
159 : }
160 :
161 : /**
162 : * Check the file size.
163 : */
164 : std::size_t fileSize(const std::string & filename);
165 :
166 : /**
167 : * This function tokenizes a path and checks to see if it contains the string to look for
168 : */
169 : bool pathContains(const std::string & expression,
170 : const std::string & string_to_find,
171 : const std::string & delims = "/");
172 :
173 : /**
174 : * Checks to see if a file is readable (exists and permissions)
175 : * @param filename The filename to check
176 : * @param check_line_endings Whether or not to see if the file contains DOS line endings.
177 : * @param throw_on_unreadable Whether or not to throw a MOOSE error if the file doesn't exist
178 : * @param check_for_git_lfs_pointer Whether or not to call a subroutine utility to make sure that
179 : * the file in question is not actually a git-lfs pointer.
180 : * @return a Boolean indicating whether the file exists and is readable
181 : */
182 : bool checkFileReadable(const std::string & filename,
183 : bool check_line_endings = false,
184 : bool throw_on_unreadable = true,
185 : bool check_for_git_lfs_pointer = true);
186 :
187 : /**
188 : * Check if the file is writable (path exists and permissions)
189 : * @param filename The filename you want to see if you can write to.
190 : * @param throw_on_unwritable Whether or not to throw a MOOSE error if the file is not writable
191 : * return a Boolean indicating whether the file exists and is writable
192 : */
193 : bool checkFileWriteable(const std::string & filename, bool throw_on_unwritable = true);
194 :
195 : /**
196 : * Check if the file is a Git-LFS pointer. When using a repository that utilizes Git-LFS,
197 : * it's possible that the client may not have the right packages installed in which case
198 : * the clone will contain plain-text files with key information for retrieving the actual
199 : * (large) files. This can cause odd errors since the file technically exists, is readable,
200 : * and even has the right name/extension. However, the content of the file will not match
201 : * the expected content.
202 : * @param file A pointer to the open filestream.
203 : */
204 : bool checkForGitLFSPointer(std::ifstream & file);
205 :
206 : /**
207 : * This function implements a parallel barrier function but writes progress
208 : * to stdout.
209 : */
210 : void parallelBarrierNotify(const libMesh::Parallel::Communicator & comm, bool messaging = true);
211 :
212 : /**
213 : * This function marks the begin of a section of code that is executed in serial
214 : * rank by rank. The section must be closed with a call to serialEnd.
215 : * These functions are intended for debugging use to obtain clean terminal output
216 : * from multiple ranks (use --keep-cout).
217 : * @param comm The communicator to use
218 : * @param warn Whether or not to warn that something is being serialized
219 : */
220 : void serialBegin(const libMesh::Parallel::Communicator & comm, bool warn = true);
221 :
222 : /**
223 : * Closes a section of code that is executed in serial rank by rank, and that was
224 : * opened with a call to serialBegin. No MPI communication can happen in this block.
225 : * @param comm The communicator to use
226 : * @param warn Whether or not to warn that something is being serialized
227 : */
228 : void serialEnd(const libMesh::Parallel::Communicator & comm, bool warn = true);
229 :
230 : /**
231 : * Function tests if the supplied filename as the desired extension
232 : * @param filename The filename to test the extension
233 : * @param ext The extension to test for (do not include the .)
234 : * @param strip_exodus_ext When true, this function ignores -s* from the end of the extension
235 : * @return True if the filename has the supplied extension
236 : */
237 : bool hasExtension(const std::string & filename, std::string ext, bool strip_exodus_ext = false);
238 :
239 : /**
240 : * Gets the extension of the passed file name.
241 : * @param filename The filename of which to get the extension
242 : * @param rfind When true, searches for last "." in filename. Otherwise, searches for first "."
243 : * @return file_ext The extension of filename (does not include the leading "."). If filename has no
244 : * extension, returns "".
245 : */
246 : std::string getExtension(const std::string & filename, const bool rfind = false);
247 :
248 : /**
249 : * Removes any file extension from the given string s (i.e. any ".[extension]" suffix of s) and
250 : * returns the result.
251 : */
252 : std::string stripExtension(const std::string & s, const bool rfind = false);
253 :
254 : /**
255 : * Function for splitting path and filename
256 : * @param full_file A complete filename and path
257 : * @return A std::pair<std::string, std::string> containing the path and filename
258 : *
259 : * If the supplied filename does not contain a path, it returns "." as the path
260 : */
261 : template <typename T>
262 : std::pair<std::filesystem::path, std::filesystem::path>
263 71100 : splitFileName(const T & full_file)
264 : {
265 71100 : const auto p = std::filesystem::path(std::string(full_file));
266 : // Error if path ends with /
267 71100 : if (!p.has_filename())
268 2 : mooseError("Invalid full file name: ", p);
269 :
270 71098 : const auto d = p.parent_path();
271 142196 : return {d.empty() ? "." : d, p.filename()};
272 71100 : }
273 :
274 : /**
275 : * Returns the current working directory as a string. If there's a problem
276 : * obtaining the current working directory, this function just returns an
277 : * empty string. It doesn't not throw.
278 : */
279 : std::string getCurrentWorkingDir();
280 :
281 : /**
282 : * Recursively make directories
283 : * @param dir_name A complete path
284 : * @param throw_on_failure True to throw instead of error out when creating a directory is failed.
285 : *
286 : * The path can be relative like 'a/b/c' or absolute like '/a/b/c'.
287 : * The path is allowed to contain '.' or '..'.
288 : */
289 : void makedirs(const std::string & dir_name, bool throw_on_failure = false);
290 :
291 : /**
292 : * Recursively remove directories from inner-most when the directories are empty
293 : * @param dir_name A complete path
294 : * @param throw_on_failure True to throw instead of error out when deleting a directory is failed.
295 : *
296 : * The path can be relative like 'a/b/c' or absolute like '/a/b/c'.
297 : * The path is allowed to contain '.' or '..'.
298 : */
299 : void removedirs(const std::string & dir_name, bool throw_on_failure = false);
300 :
301 : /**
302 : * Function for converting a camel case name to a name containing underscores.
303 : * @param camel_case_name A string containing camel casing
304 : * @return a string containing no capital letters with underscores as appropriate
305 : */
306 : std::string camelCaseToUnderscore(const std::string & camel_case_name);
307 :
308 : /**
309 : * Function for converting an underscore name to a camel case name.
310 : * @param underscore_name A string containing underscores
311 : * @return a string containing camel casing
312 : */
313 : std::string underscoreToCamelCase(const std::string & underscore_name, bool leading_upper_case);
314 :
315 : /**
316 : * Function for stripping name after the file / in parser block
317 : */
318 : std::string shortName(const std::string & name);
319 :
320 : /**
321 : * Function for string the information before the final / in a parser block
322 : */
323 : std::string baseName(const std::string & name);
324 :
325 : /**
326 : * Get the hostname the current process is running on
327 : */
328 : std::string hostname();
329 :
330 : /**
331 : * Returns the width of the terminal using sys/ioctl
332 : */
333 : unsigned short getTermWidth(bool use_environment);
334 :
335 : /**
336 : * @returns A cleaner representation of the c++ type \p cpp_type.
337 : */
338 : std::string prettyCppType(const std::string & cpp_type);
339 :
340 : /**
341 : * @returns A cleaner representation of the type for the given object
342 : */
343 : template <typename T>
344 : std::string
345 166289 : prettyCppType(const T * obj = nullptr)
346 : {
347 166289 : if (obj)
348 12205 : return prettyCppType(libMesh::demangle(typeid(*obj).name()));
349 : else
350 154084 : return prettyCppType(libMesh::demangle(typeid(T).name()));
351 : }
352 :
353 : /**
354 : * This routine is a simple helper function for searching a map by values instead of keys
355 : */
356 : template <typename T1, typename T2>
357 : bool
358 120748 : doesMapContainValue(const std::map<T1, T2> & the_map, const T2 & value)
359 : {
360 408364 : for (typename std::map<T1, T2>::const_iterator iter = the_map.begin(); iter != the_map.end();
361 287616 : ++iter)
362 390277 : if (iter->second == value)
363 102661 : return true;
364 18087 : return false;
365 : }
366 :
367 : /**
368 : * Function to check whether two variables are equal within an absolute tolerance
369 : * @param var1 The first variable to be checked
370 : * @param var2 The second variable to be checked
371 : * @param tol The tolerance to be used
372 : * @return true if var1 and var2 are equal within tol
373 : */
374 : template <
375 : typename T,
376 : typename T2,
377 : typename T3 = T,
378 : typename std::enable_if<libMesh::ScalarTraits<T>::value && libMesh::ScalarTraits<T2>::value &&
379 : libMesh::ScalarTraits<T3>::value,
380 : int>::type = 0>
381 : bool
382 50939833 : absoluteFuzzyEqual(const T & var1,
383 : const T2 & var2,
384 88352748 : const T3 & tol = libMesh::TOLERANCE * libMesh::TOLERANCE)
385 : {
386 50939833 : return (std::abs(MetaPhysicL::raw_value(var1) - MetaPhysicL::raw_value(var2)) <=
387 50939833 : MetaPhysicL::raw_value(tol));
388 : }
389 :
390 : /**
391 : * Function to check whether a variable is greater than or equal to another variable within an
392 : * absolute tolerance
393 : * @param var1 The first variable to be checked
394 : * @param var2 The second variable to be checked
395 : * @param tol The tolerance to be used
396 : * @return true if var1 > var2 or var1 == var2 within tol
397 : */
398 : template <
399 : typename T,
400 : typename T2,
401 : typename T3 = T,
402 : typename std::enable_if<libMesh::ScalarTraits<T>::value && libMesh::ScalarTraits<T2>::value &&
403 : libMesh::ScalarTraits<T3>::value,
404 : int>::type = 0>
405 : bool
406 288700 : absoluteFuzzyGreaterEqual(const T & var1,
407 : const T2 & var2,
408 389344 : const T3 & tol = libMesh::TOLERANCE * libMesh::TOLERANCE)
409 : {
410 288700 : return (MetaPhysicL::raw_value(var1) >=
411 288700 : (MetaPhysicL::raw_value(var2) - MetaPhysicL::raw_value(tol)));
412 : }
413 :
414 : /**
415 : * Function to check whether a variable is greater than another variable within an absolute
416 : * tolerance
417 : * @param var1 The first variable to be checked
418 : * @param var2 The second variable to be checked
419 : * @param tol The tolerance to be used
420 : * @return true if var1 > var2 and var1 != var2 within tol
421 : */
422 : template <
423 : typename T,
424 : typename T2,
425 : typename T3 = T,
426 : typename std::enable_if<libMesh::ScalarTraits<T>::value && libMesh::ScalarTraits<T2>::value &&
427 : libMesh::ScalarTraits<T3>::value,
428 : int>::type = 0>
429 : bool
430 6235170 : absoluteFuzzyGreaterThan(const T & var1,
431 : const T2 & var2,
432 8619431 : const T3 & tol = libMesh::TOLERANCE * libMesh::TOLERANCE)
433 : {
434 6235170 : return (MetaPhysicL::raw_value(var1) >
435 6235170 : (MetaPhysicL::raw_value(var2) + MetaPhysicL::raw_value(tol)));
436 : }
437 :
438 : /**
439 : * Function to check whether a variable is less than or equal to another variable within an absolute
440 : * tolerance
441 : * @param var1 The first variable to be checked
442 : * @param var2 The second variable to be checked
443 : * @param tol The tolerance to be used
444 : * @return true if var1 < var2 or var1 == var2 within tol
445 : */
446 : template <
447 : typename T,
448 : typename T2,
449 : typename T3 = T,
450 : typename std::enable_if<libMesh::ScalarTraits<T>::value && libMesh::ScalarTraits<T2>::value &&
451 : libMesh::ScalarTraits<T3>::value,
452 : int>::type = 0>
453 : bool
454 307 : absoluteFuzzyLessEqual(const T & var1,
455 : const T2 & var2,
456 271 : const T3 & tol = libMesh::TOLERANCE * libMesh::TOLERANCE)
457 : {
458 307 : return (MetaPhysicL::raw_value(var1) <=
459 307 : (MetaPhysicL::raw_value(var2) + MetaPhysicL::raw_value(tol)));
460 : }
461 :
462 : /**
463 : * Function to check whether a variable is less than another variable within an absolute tolerance
464 : * @param var1 The first variable to be checked
465 : * @param var2 The second variable to be checked
466 : * @param tol The tolerance to be used
467 : * @return true if var1 < var2 and var1 != var2 within tol
468 : */
469 : template <
470 : typename T,
471 : typename T2,
472 : typename T3 = T,
473 : typename std::enable_if<libMesh::ScalarTraits<T>::value && libMesh::ScalarTraits<T2>::value &&
474 : libMesh::ScalarTraits<T3>::value,
475 : int>::type = 0>
476 : bool
477 770993 : absoluteFuzzyLessThan(const T & var1,
478 : const T2 & var2,
479 1000524 : const T3 & tol = libMesh::TOLERANCE * libMesh::TOLERANCE)
480 : {
481 770993 : return (MetaPhysicL::raw_value(var1) <
482 770993 : (MetaPhysicL::raw_value(var2) - MetaPhysicL::raw_value(tol)));
483 : }
484 :
485 : /**
486 : * Function to check whether two variables are equal within a relative tolerance
487 : * @param var1 The first variable to be checked
488 : * @param var2 The second variable to be checked
489 : * @param tol The relative tolerance to be used
490 : * @return true if var1 and var2 are equal within relative tol
491 : */
492 : template <typename T, typename T2, typename T3 = Real>
493 : bool
494 6212320 : relativeFuzzyEqual(const T & var1,
495 : const T2 & var2,
496 747 : const T3 & tol = libMesh::TOLERANCE * libMesh::TOLERANCE)
497 : {
498 : if constexpr (libMesh::ScalarTraits<T>::value ||
499 : libMesh::TensorTools::MathWrapperTraits<T>::value)
500 : {
501 : static_assert(libMesh::TensorTools::TensorTraits<T>::rank ==
502 : libMesh::TensorTools::TensorTraits<T2>::rank,
503 : "Mathematical types must be same for arguments to relativelyFuzzEqual");
504 : if constexpr (libMesh::TensorTools::TensorTraits<T>::rank == 0)
505 12424640 : return absoluteFuzzyEqual(
506 : var1,
507 : var2,
508 6212320 : tol * (std::abs(MetaPhysicL::raw_value(var1)) + std::abs(MetaPhysicL::raw_value(var2))));
509 : else if constexpr (libMesh::TensorTools::TensorTraits<T>::rank == 1)
510 : {
511 : for (const auto i : make_range(Moose::dim))
512 : if (!relativeFuzzyEqual(var1(i), var2(i), tol))
513 : return false;
514 :
515 : return true;
516 : }
517 : else if constexpr (libMesh::TensorTools::TensorTraits<T>::rank == 2)
518 : {
519 : for (const auto i : make_range(Moose::dim))
520 : for (const auto j : make_range(Moose::dim))
521 : if (!relativeFuzzyEqual(var1(i, j), var2(i, j), tol))
522 : return false;
523 :
524 : return true;
525 : }
526 : }
527 : else
528 : {
529 : // We dare to dream
530 : mooseAssert(var1.size() == var2.size(), "These must be the same size");
531 : for (const auto i : index_range(var1))
532 : if (!relativeFuzzyEqual(var1(i), var2(i), tol))
533 : return false;
534 :
535 : return true;
536 : }
537 : }
538 :
539 : /**
540 : * Function to check whether a variable is greater than or equal to another variable within a
541 : * relative tolerance
542 : * @param var1 The first variable to be checked
543 : * @param var2 The second variable to be checked
544 : * @param tol The tolerance to be used
545 : * @return true if var1 > var2 or var1 == var2 within relative tol
546 : */
547 : template <
548 : typename T,
549 : typename T2,
550 : typename T3 = T,
551 : typename std::enable_if<libMesh::ScalarTraits<T>::value && libMesh::ScalarTraits<T2>::value &&
552 : libMesh::ScalarTraits<T3>::value,
553 : int>::type = 0>
554 : bool
555 8 : relativeFuzzyGreaterEqual(const T & var1,
556 : const T2 & var2,
557 16 : const T3 & tol = libMesh::TOLERANCE * libMesh::TOLERANCE)
558 : {
559 16 : return (absoluteFuzzyGreaterEqual(
560 : var1,
561 : var2,
562 8 : tol * (std::abs(MetaPhysicL::raw_value(var1)) + std::abs(MetaPhysicL::raw_value(var2)))));
563 : }
564 :
565 : /**
566 : * Function to check whether a variable is greater than another variable within a relative tolerance
567 : * @param var1 The first variable to be checked
568 : * @param var2 The second variable to be checked
569 : * @param tol The tolerance to be used
570 : * @return true if var1 > var2 and var1 != var2 within relative tol
571 : */
572 : template <
573 : typename T,
574 : typename T2,
575 : typename T3 = T,
576 : typename std::enable_if<libMesh::ScalarTraits<T>::value && libMesh::ScalarTraits<T2>::value &&
577 : libMesh::ScalarTraits<T3>::value,
578 : int>::type = 0>
579 : bool
580 4 : relativeFuzzyGreaterThan(const T & var1,
581 : const T2 & var2,
582 8 : const T3 & tol = libMesh::TOLERANCE * libMesh::TOLERANCE)
583 : {
584 8 : return (absoluteFuzzyGreaterThan(
585 : var1,
586 : var2,
587 4 : tol * (std::abs(MetaPhysicL::raw_value(var1)) + std::abs(MetaPhysicL::raw_value(var2)))));
588 : }
589 :
590 : /**
591 : * Function to check whether a variable is less than or equal to another variable within a relative
592 : * tolerance
593 : * @param var1 The first variable to be checked
594 : * @param var2 The second variable to be checked
595 : * @param tol The tolerance to be used
596 : * @return true if var1 < var2 or var1 == var2 within relative tol
597 : */
598 : template <
599 : typename T,
600 : typename T2,
601 : typename T3 = T,
602 : typename std::enable_if<libMesh::ScalarTraits<T>::value && libMesh::ScalarTraits<T2>::value &&
603 : libMesh::ScalarTraits<T3>::value,
604 : int>::type = 0>
605 : bool
606 8 : relativeFuzzyLessEqual(const T & var1,
607 : const T2 & var2,
608 16 : const T3 & tol = libMesh::TOLERANCE * libMesh::TOLERANCE)
609 : {
610 16 : return (absoluteFuzzyLessEqual(
611 : var1,
612 : var2,
613 8 : tol * (std::abs(MetaPhysicL::raw_value(var1)) + std::abs(MetaPhysicL::raw_value(var2)))));
614 : }
615 :
616 : /**
617 : * Function to check whether a variable is less than another variable within a relative tolerance
618 : * @param var1 The first variable to be checked
619 : * @param var2 The second variable to be checked
620 : * @param tol The tolerance to be used
621 : * @return true if var1 < var2 and var1 != var2 within relative tol
622 : */
623 : template <
624 : typename T,
625 : typename T2,
626 : typename T3 = T,
627 : typename std::enable_if<libMesh::ScalarTraits<T>::value && libMesh::ScalarTraits<T2>::value &&
628 : libMesh::ScalarTraits<T3>::value,
629 : int>::type = 0>
630 : bool
631 5262 : relativeFuzzyLessThan(const T & var1,
632 : const T2 & var2,
633 7895 : const T3 & tol = libMesh::TOLERANCE * libMesh::TOLERANCE)
634 : {
635 10524 : return (absoluteFuzzyLessThan(
636 : var1,
637 : var2,
638 5262 : tol * (std::abs(MetaPhysicL::raw_value(var1)) + std::abs(MetaPhysicL::raw_value(var2)))));
639 : }
640 :
641 : /**
642 : * Function which takes the union of \p vector1 and \p vector2 and copies them
643 : * to \p common . Depending on the vector size and data type this can be very expensive!
644 : */
645 : template <typename T>
646 : void
647 43332 : getUnion(const std::vector<T> & vector1, const std::vector<T> & vector2, std::vector<T> & common)
648 : {
649 43332 : std::unordered_set<T> unique_elements;
650 43332 : unique_elements.reserve(vector1.size() + vector2.size());
651 :
652 82647 : for (const T & entry : vector1)
653 39315 : unique_elements.insert(entry);
654 82647 : for (const T & entry : vector2)
655 39315 : unique_elements.insert(entry);
656 :
657 : // Now populate the common vector with the union
658 43332 : common.clear();
659 43332 : common.assign(unique_elements.begin(), unique_elements.end());
660 43332 : }
661 :
662 : /**
663 : * Taken from https://stackoverflow.com/a/257382
664 : * Evaluating constexpr (Has_size<T>::value) in a templated method over class T will
665 : * return whether T is a standard container or a singleton
666 : */
667 : template <typename T>
668 : class Has_size
669 : {
670 : using Yes = char;
671 : struct No
672 : {
673 : char x[2];
674 : };
675 :
676 : template <typename C>
677 : static Yes test(decltype(&C::size));
678 : template <typename C>
679 : static No test(...);
680 :
681 : public:
682 : static constexpr bool value = sizeof(test<T>(0)) == sizeof(Yes);
683 : };
684 :
685 : /**
686 : * @param value The quantity to test for zero-ness
687 : * @param tolerance The tolerance for testing zero-ness. The default is 1e-18 for double precision
688 : * configurations of libMesh/MOOSE
689 : * @return whether the L_infty norm of the value is (close enough to) zero
690 : */
691 : template <typename T>
692 : bool
693 208 : isZero(const T & value, const Real tolerance = TOLERANCE * TOLERANCE * TOLERANCE)
694 : {
695 : if constexpr (Has_size<T>::value)
696 : {
697 12 : for (const auto & element : value)
698 8 : if (!isZero(element, tolerance))
699 4 : return false;
700 :
701 4 : return true;
702 : }
703 : else if constexpr (libMesh::TensorTools::TensorTraits<T>::rank == 0)
704 184 : return MooseUtils::absoluteFuzzyEqual(MetaPhysicL::raw_value(value), 0, tolerance);
705 : else if constexpr (libMesh::TensorTools::TensorTraits<T>::rank == 1)
706 : {
707 20 : for (const auto i : make_range(Moose::dim))
708 16 : if (!MooseUtils::absoluteFuzzyEqual(MetaPhysicL::raw_value(value(i)), 0, tolerance))
709 4 : return false;
710 :
711 4 : return true;
712 : }
713 : else if constexpr (libMesh::TensorTools::TensorTraits<T>::rank == 2)
714 : {
715 20 : for (const auto i : make_range(Moose::dim))
716 52 : for (const auto j : make_range(Moose::dim))
717 40 : if (!MooseUtils::absoluteFuzzyEqual(MetaPhysicL::raw_value(value(i, j)), 0, tolerance))
718 4 : return false;
719 :
720 4 : return true;
721 : }
722 : }
723 :
724 : /**
725 : * Function to dump the contents of MaterialPropertyStorage for debugging purposes
726 : * @param props The storage item to dump, this should be
727 : * MaterialPropertyStorage.props(state)
728 : *
729 : * Currently this only words for scalar material properties. Something to do as needed would be to
730 : * create a method in MaterialProperty
731 : * that may be overloaded to dump the type using template specialization.
732 : */
733 : void MaterialPropertyStorageDump(
734 : const HashMap<const libMesh::Elem *, HashMap<unsigned int, MaterialProperties>> & props);
735 :
736 : /**
737 : * Indents the supplied message given the prefix and color
738 : * @param prefix The prefix to use for indenting
739 : * @param message The message that will be indented
740 : * @param color The color to apply to the prefix (default CYAN)
741 : * @param indent_first_line If true this will indent the first line too (default)
742 : * @param post_prefix A string to append right after the prefix, defaults to a column and a space
743 : *
744 : * Takes a message like the following and indents it with another color code (see below)
745 : *
746 : * Input message:
747 : * COLOR_YELLOW
748 : * *** Warning ***
749 : * Something bad has happened and we want to draw attention to it with color
750 : * COLOR_DEFAULT
751 : *
752 : * Output message:
753 : * COLOR_CYAN sub_app: COLOR_YELLOW
754 : * COLOR_CYAN sub_app: COLOR_YELLOW *** Warning ***
755 : * COLOR_CYAN sub_app: COLOR_YELLOW Something bad has happened and we want to draw attention to it
756 : * with color
757 : * COLOR_DEFAULT
758 : *
759 : * Also handles single line color codes
760 : * COLOR_CYAN sub_app: 0 Nonlinear |R| = COLOR_GREEN 1.0e-10 COLOR_DEFAULT
761 : *
762 : * Not indenting the first line is useful in the case where the first line is actually finishing
763 : * the line before it.
764 : */
765 : void indentMessage(const std::string & prefix,
766 : std::string & message,
767 : const char * color = COLOR_CYAN,
768 : bool dont_indent_first_line = true,
769 : const std::string & post_prefix = ": ");
770 :
771 : /**
772 : * remove ANSI escape sequences for terminal color from msg
773 : */
774 : std::string & removeColor(std::string & msg);
775 :
776 : std::list<std::string> listDir(const std::string path, bool files_only = false);
777 :
778 : bool pathExists(const std::string & path);
779 :
780 : /**
781 : * Retrieves the names of all of the files contained within the list of directories passed into
782 : * the routine. The names returned will be the paths to the files relative to the current
783 : * directory.
784 : * @param directory_list The list of directories to retrieve files from.
785 : * @param file_only Whether or not to list only files
786 : */
787 : std::list<std::string> getFilesInDirs(const std::list<std::string> & directory_list,
788 : const bool files_only = true);
789 :
790 : /**
791 : * Returns the most recent checkpoint prefix (the four numbers at the beginning)
792 : * If a suitable file isn't found the empty string is returned
793 : * @param checkpoint_files the list of files to analyze
794 : */
795 : std::string getLatestCheckpointFilePrefix(const std::list<std::string> & checkpoint_files);
796 :
797 : /*
798 : * Checks to see if a string matches a search string
799 : * @param name The name to check
800 : * @param search_string The search string to check name against
801 : */
802 : bool wildCardMatch(std::string name, std::string search_string);
803 :
804 : /*
805 : * Checks to see if a candidate string matches a pattern string, permitting glob
806 : * wildcards (* and ?) anywhere in the pattern.
807 : * @param candidate The name to check
808 : * @param pattern The search string to check name candidate
809 : */
810 : bool globCompare(const std::string & candidate,
811 : const std::string & pattern,
812 : std::size_t c = 0,
813 : std::size_t p = 0);
814 :
815 : template <typename T>
816 : void
817 26 : expandAllMatches(const std::vector<T> & candidates, std::vector<T> & patterns)
818 : {
819 26 : std::set<T> expanded;
820 78 : for (const auto & p : patterns)
821 : {
822 52 : unsigned int found = 0;
823 260 : for (const auto & c : candidates)
824 208 : if (globCompare(c, p))
825 : {
826 52 : expanded.insert(c);
827 52 : found++;
828 : }
829 52 : if (!found)
830 0 : throw std::invalid_argument(p);
831 : }
832 26 : patterns.assign(expanded.begin(), expanded.end());
833 26 : }
834 :
835 : /**
836 : * Takes the string representation of a value and converts it to the value.
837 : *
838 : * See the convert method in MooseStringUtils.h for more information on
839 : * handling of each case.
840 : *
841 : * @param str The string to convert from
842 : * @param throw_on_failure If true, throw on a failure to convert, otherwise use mooseError
843 : * @return The converted value on success
844 : */
845 : template <typename T>
846 : T
847 6773 : convert(const std::string & str, bool throw_on_failure = false)
848 : {
849 : T val;
850 : try
851 : {
852 6773 : convert(str, val, true);
853 : }
854 1094 : catch (std::exception const & e)
855 : {
856 549 : if (throw_on_failure)
857 541 : throw;
858 8 : mooseError(e.what());
859 : }
860 6224 : return val;
861 : }
862 :
863 : /**
864 : * Create a symbolic link, if the link already exists it is replaced.
865 : */
866 : void createSymlink(const std::string & target, const std::string & link);
867 :
868 : /**
869 : * Remove a symbolic link, if the given filename is a link.
870 : */
871 : void clearSymlink(const std::string & link);
872 :
873 : /**
874 : * Returns a container that contains the content of second passed in container
875 : * inserted into the first passed in container (set or map union).
876 : */
877 : template <typename T>
878 : T
879 : concatenate(T c1, const T & c2)
880 : {
881 : c1.insert(c2.begin(), c2.end());
882 : return c1;
883 : }
884 :
885 : /**
886 : * Returns a vector that contains is the concatenation of the two passed in vectors.
887 : */
888 : template <typename T>
889 : std::vector<T>
890 : concatenate(std::vector<T> c1, const std::vector<T> & c2)
891 : {
892 : c1.insert(c1.end(), c2.begin(), c2.end());
893 : return c1;
894 : }
895 :
896 : /**
897 : * Returns the passed in vector with the item appended to it.
898 : */
899 : template <typename T>
900 : std::vector<T>
901 33 : concatenate(std::vector<T> c1, const T & item)
902 : {
903 33 : c1.push_back(item);
904 33 : return c1;
905 : }
906 :
907 : /**
908 : * Concatenates \p value into a single string separated by \p separator
909 : */
910 : std::string stringJoin(const std::vector<std::string> & values,
911 : const std::string & separator = " ");
912 :
913 : /**
914 : * @return Whether or not \p value begins with \p begin_value
915 : */
916 : bool beginsWith(const std::string & value, const std::string & begin_value);
917 :
918 : /**
919 : * Return the number of digits for a number.
920 : *
921 : * This can foster quite a large discussion:
922 : * https://stackoverflow.com/questions/1489830/efficient-way-to-determine-number-of-digits-in-an-integer
923 : *
924 : * For our purposes I like the following algorithm.
925 : */
926 : template <typename T>
927 : int
928 1942 : numDigits(const T & num)
929 : {
930 1942 : return num > 9 ? static_cast<int>(std::log10(static_cast<double>(num))) + 1 : 1;
931 : }
932 :
933 : /**
934 : * Return the default ExecFlagEnum for MOOSE.
935 : */
936 : ExecFlagEnum getDefaultExecFlagEnum();
937 :
938 : /**
939 : * Robust string to integer conversion that fails for cases such at "1foo".
940 : * @param input The string to convert.
941 : * @param throw_on_failure Throw an invalid_argument exception instead of mooseError.
942 : */
943 : int stringToInteger(const std::string & input, bool throw_on_failure = false);
944 :
945 : /**
946 : * Linearly partition a number of items
947 : *
948 : * @param num_items The number of items to partition
949 : * @param num_chunks The number of chunks to partition into
950 : * @param chunk_id The ID of the chunk you are trying to get information about (typically the
951 : * current MPI rank)
952 : * @param num_local_items Output: The number of items for this chunk_id
953 : * @param local_items_begin Output: The first item for this chunk_id
954 : * @param local_items_end Output: One past the final item for this chunk_id
955 : */
956 : void linearPartitionItems(dof_id_type num_items,
957 : dof_id_type num_chunks,
958 : dof_id_type chunk_id,
959 : dof_id_type & num_local_items,
960 : dof_id_type & local_items_begin,
961 : dof_id_type & local_items_end);
962 :
963 : /**
964 : * Return the chunk_id that is assigned to handle item_id
965 : *
966 : * @param num_items Global number of items to partition
967 : * @param num_chunks Total number of chunks to split into
968 : * @param item_id The item to find the chunk_id for
969 : * @return The chunk_id of the chunk that contains item_id
970 : */
971 : processor_id_type
972 : linearPartitionChunk(dof_id_type num_items, dof_id_type num_chunks, dof_id_type item_id);
973 :
974 : /**
975 : * Wrapper around PetscGetRealPath, which is a cross-platform replacement for realpath
976 : */
977 : std::string realpath(const std::string & path);
978 :
979 : /**
980 : * Custom type trait that has a ::value of true for types that cam be use interchangeably
981 : * with Real. Most notably it is false for complex numbers, which do not have a
982 : * strict ordering (and therefore no <,>,<=,>= operators).
983 : */
984 : template <typename T>
985 : struct IsLikeReal
986 : {
987 : static constexpr bool value = false;
988 : };
989 : template <>
990 : struct IsLikeReal<Real>
991 : {
992 : static constexpr bool value = true;
993 : };
994 : template <>
995 : struct IsLikeReal<ADReal>
996 : {
997 : static constexpr bool value = true;
998 : };
999 :
1000 : /**
1001 : * Custom type trait that has a ::value of true for types that can be broadcasted
1002 : */
1003 : template <typename T>
1004 : struct canBroadcast
1005 : {
1006 : static constexpr bool value = std::is_base_of<TIMPI::DataType, TIMPI::StandardType<T>>::value ||
1007 : TIMPI::Has_buffer_type<TIMPI::Packing<T>>::value;
1008 : };
1009 :
1010 : ///@{ Comparison helpers that support the MooseUtils::Any wildcard which will match any value
1011 : const static struct AnyType
1012 : {
1013 : } Any;
1014 :
1015 : template <typename T1, typename T2>
1016 : bool
1017 49557 : wildcardEqual(const T1 & a, const T2 & b)
1018 : {
1019 49557 : return a == b;
1020 : }
1021 :
1022 : template <typename T>
1023 : bool
1024 : wildcardEqual(const T &, AnyType)
1025 : {
1026 : return true;
1027 : }
1028 : template <typename T>
1029 : bool
1030 5448 : wildcardEqual(AnyType, const T &)
1031 : {
1032 5448 : return true;
1033 : }
1034 : ///@}
1035 :
1036 : /**
1037 : * Find a specific pair in a container matching on first, second or both pair components
1038 : */
1039 : template <typename C, typename It, typename M1, typename M2>
1040 : auto
1041 58978 : findPair(C & container, It start_iterator, const M1 & first, const M2 & second)
1042 : {
1043 58978 : return std::find_if(start_iterator,
1044 : container.end(),
1045 49557 : [&](auto & item) {
1046 55005 : return wildcardEqual(first, item.first) &&
1047 55005 : wildcardEqual(second, item.second);
1048 58978 : });
1049 : }
1050 :
1051 : /**
1052 : * Construct a valid bounding box from 2 arbitrary points
1053 : *
1054 : * If you have 2 points in space and you wish to construct a bounding box, you should use
1055 : * this method to avoid unexpected behavior of the underlying BoundingBox class in libMesh.
1056 : * BoundingBox class expect 2 points whose coordinates are "sorted" (i.e., x-, y- and -z
1057 : * coordinates of the first point are smaller then the corresponding coordinates of the second
1058 : * point). If this "sorting" is not present, the BoundingBox class will build an empty box and
1059 : * any further testing of points inside the box will fail. This method will allow you to obtain
1060 : * the correct bounding box for any valid combination of 2 corner points of a box.
1061 : *
1062 : * @param p1 First corner of the constructed bounding box
1063 : * @param p2 Second corner of the constructed bounding box
1064 : * @return Valid bounding box
1065 : */
1066 : libMesh::BoundingBox buildBoundingBox(const Point & p1, const Point & p2);
1067 :
1068 : /**
1069 : * Utility class template for a semidynamic vector with a maximum size N
1070 : * and a chosen dynamic size. This container avoids heap allocation and
1071 : * is meant as a replacement for small local std::vector variables.
1072 : * By default this class uses `value initialization`. This can be disabled
1073 : * using the third template parameter if uninitialized storage is acceptable,
1074 : */
1075 : template <typename T, std::size_t N, bool value_init = true>
1076 : #if METAPHYSICL_MAJOR_VERSION < 2
1077 : class SemidynamicVector : public MetaPhysicL::DynamicStdArrayWrapper<T, MetaPhysicL::NWrapper<N>>
1078 : {
1079 : typedef MetaPhysicL::DynamicStdArrayWrapper<T, MetaPhysicL::NWrapper<N>> Parent;
1080 : #else
1081 : class SemidynamicVector : public MetaPhysicL::DynamicStdArrayWrapper<T, N>
1082 : {
1083 : typedef MetaPhysicL::DynamicStdArrayWrapper<T, N> Parent;
1084 : #endif
1085 :
1086 : public:
1087 21764038 : SemidynamicVector(std::size_t size) : Parent()
1088 : {
1089 5441014 : Parent::resize(size);
1090 : if constexpr (value_init)
1091 21728244 : for (const auto i : make_range(size))
1092 16287232 : _data[i] = T{};
1093 5441014 : }
1094 :
1095 2 : void resize(std::size_t new_size)
1096 : {
1097 2 : [[maybe_unused]] const auto old_dynamic_n = Parent::size();
1098 :
1099 2 : Parent::resize(new_size);
1100 :
1101 : if constexpr (value_init)
1102 : for (const auto i : make_range(old_dynamic_n, _dynamic_n))
1103 : _data[i] = T{};
1104 2 : }
1105 :
1106 2 : void push_back(const T & v)
1107 : {
1108 2 : const auto old_dynamic_n = Parent::size();
1109 2 : Parent::resize(old_dynamic_n + 1);
1110 2 : _data[old_dynamic_n] = v;
1111 2 : }
1112 :
1113 : template <typename... Args>
1114 2 : void emplace_back(Args &&... args)
1115 : {
1116 2 : const auto old_dynamic_n = Parent::size();
1117 2 : Parent::resize(old_dynamic_n + 1);
1118 2 : (::new (&_data[old_dynamic_n]) T(std::forward<Args>(args)...));
1119 2 : }
1120 :
1121 2 : std::size_t max_size() const { return N; }
1122 :
1123 : using Parent::_data;
1124 : using Parent::_dynamic_n;
1125 : };
1126 :
1127 : /**
1128 : * The MooseUtils::get() specializations are used to support making
1129 : * forwards-compatible code changes from dumb pointers to smart pointers.
1130 : * The same line of code, e.g.
1131 : *
1132 : * libMesh::Parameters::Value * value = MooseUtils::get(map_iter->second);
1133 : *
1134 : * will then work regardless of whether map_iter->second is a dumb pointer
1135 : * or a smart pointer. Note that the smart pointer get() functions are const
1136 : * so they can be (ab)used to get a non-const pointer to the underlying
1137 : * resource. We are simply following this convention here.
1138 : */
1139 : template <typename T>
1140 : T *
1141 147669734 : get(const std::unique_ptr<T> & u)
1142 : {
1143 147669734 : return u.get();
1144 : }
1145 :
1146 : template <typename T>
1147 : T *
1148 : get(T * p)
1149 : {
1150 : return p;
1151 : }
1152 :
1153 : template <typename T>
1154 : T *
1155 : get(const std::shared_ptr<T> & s)
1156 : {
1157 : return s.get();
1158 : }
1159 :
1160 : /**
1161 : * This method detects whether two sets intersect without building a result set.
1162 : * It exits as soon as any intersection is detected.
1163 : */
1164 : template <class InputIterator>
1165 : bool
1166 37 : setsIntersect(InputIterator first1, InputIterator last1, InputIterator first2, InputIterator last2)
1167 : {
1168 51 : while (first1 != last1 && first2 != last2)
1169 : {
1170 18 : if (*first1 == *first2)
1171 4 : return true;
1172 :
1173 14 : if (*first1 < *first2)
1174 0 : ++first1;
1175 14 : else if (*first1 > *first2)
1176 14 : ++first2;
1177 : }
1178 33 : return false;
1179 : }
1180 :
1181 : template <class T>
1182 : bool
1183 37 : setsIntersect(const T & s1, const T & s2)
1184 : {
1185 37 : return setsIntersect(s1.begin(), s1.end(), s2.begin(), s2.end());
1186 : }
1187 :
1188 : /**
1189 : * Courtesy https://stackoverflow.com/a/8889045 and
1190 : * https://en.cppreference.com/w/cpp/string/byte/isdigit
1191 : * @return Whether every character in the string is a digit
1192 : */
1193 : inline bool
1194 1953848 : isDigits(const std::string & str)
1195 : {
1196 4466371 : return std::all_of(str.begin(), str.end(), [](unsigned char c) { return std::isdigit(c); });
1197 : }
1198 :
1199 : /**
1200 : * Courtesy https://stackoverflow.com/a/57163016 and
1201 : * https://stackoverflow.com/questions/447206/c-isfloat-function
1202 : * @return Whether the string is convertible to a float
1203 : */
1204 : inline bool
1205 17 : isFloat(const std::string & str)
1206 : {
1207 17 : if (str.empty())
1208 0 : return false;
1209 : char * ptr;
1210 17 : strtof(str.c_str(), &ptr);
1211 17 : return (*ptr) == '\0';
1212 : }
1213 :
1214 : /**
1215 : * Gets the canonical path of the given path
1216 : */
1217 : std::string canonicalPath(const std::string & path);
1218 :
1219 : /**
1220 : * @returns Whether the \p string1 starts with \p string2
1221 : */
1222 : bool startsWith(const std::string & string1, const std::string & string2);
1223 :
1224 : /**
1225 : * Replace the starting string \p string2 of \p string1 with \p string3. A user should have checked
1226 : * that \p string1 \p startsWith \p string2
1227 : */
1228 : void replaceStart(std::string & string1, const std::string & string2, const std::string & string3);
1229 :
1230 : /**
1231 : * @returns whether every alphabetic character in a string is lower-case
1232 : */
1233 : bool isAllLowercase(const std::string & str);
1234 : } // MooseUtils namespace
1235 :
1236 : namespace Moose
1237 : {
1238 : template <typename T>
1239 : struct ADType;
1240 :
1241 : template <typename T, std::size_t N, bool value_init>
1242 : struct ADType<MooseUtils::SemidynamicVector<T, N, value_init>>
1243 : {
1244 : typedef MooseUtils::SemidynamicVector<typename ADType<T>::type, N, value_init> type;
1245 : };
1246 : }
1247 :
1248 : /**
1249 : * find, erase, length algorithm for removing a substring from a string
1250 : */
1251 : void removeSubstring(std::string & main, const std::string & sub);
1252 :
1253 : /**
1254 : * find, erase, length algorithm for removing a substring from a copy of a string
1255 : */
1256 : std::string removeSubstring(const std::string & main, const std::string & sub);
|