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