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 318645 : pathjoin(const std::filesystem::path & p, Args... args)
66 : {
67 318645 : 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 99791 : join(Iterator begin, Iterator end, const std::string & delimiter)
143 : {
144 99791 : std::ostringstream oss;
145 99791 : std::copy(begin, end, infix_ostream_iterator<std::string>(oss, delimiter.c_str()));
146 199582 : return oss.str();
147 99791 : }
148 :
149 : /**
150 : * Python-like join function for strings over a container.
151 : */
152 : template <typename T>
153 : std::string
154 99653 : join(const T & strings, const std::string & delimiter)
155 : {
156 99653 : 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 63690 : splitFileName(const T & full_file)
262 : {
263 63690 : const auto p = std::filesystem::path(std::string(full_file));
264 : // Error if path ends with /
265 63690 : if (!p.has_filename())
266 1 : mooseError("Invalid full file name: ", p);
267 :
268 63689 : const auto d = p.parent_path();
269 127378 : return {d.empty() ? "." : d, p.filename()};
270 63690 : }
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 147214 : prettyCppType(const T * obj = nullptr)
344 : {
345 147214 : if (obj)
346 12000 : return prettyCppType(libMesh::demangle(typeid(*obj).name()));
347 : else
348 135214 : 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 108390 : doesMapContainValue(const std::map<T1, T2> & the_map, const T2 & value)
357 : {
358 368709 : for (typename std::map<T1, T2>::const_iterator iter = the_map.begin(); iter != the_map.end();
359 260319 : ++iter)
360 352653 : if (iter->second == value)
361 92334 : return true;
362 16056 : 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 40623479 : absoluteFuzzyEqual(const T & var1,
381 : const T2 & var2,
382 69541869 : const T3 & tol = libMesh::TOLERANCE * libMesh::TOLERANCE)
383 : {
384 40623479 : return (std::abs(MetaPhysicL::raw_value(var1) - MetaPhysicL::raw_value(var2)) <=
385 40623479 : 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 257022 : absoluteFuzzyGreaterEqual(const T & var1,
405 : const T2 & var2,
406 345653 : const T3 & tol = libMesh::TOLERANCE * libMesh::TOLERANCE)
407 : {
408 257022 : return (MetaPhysicL::raw_value(var1) >=
409 257022 : (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 4747202 : absoluteFuzzyGreaterThan(const T & var1,
429 : const T2 & var2,
430 7149437 : const T3 & tol = libMesh::TOLERANCE * libMesh::TOLERANCE)
431 : {
432 4747202 : return (MetaPhysicL::raw_value(var1) >
433 4747202 : (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 270 : absoluteFuzzyLessEqual(const T & var1,
453 : const T2 & var2,
454 237 : const T3 & tol = libMesh::TOLERANCE * libMesh::TOLERANCE)
455 : {
456 270 : return (MetaPhysicL::raw_value(var1) <=
457 270 : (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 622313 : absoluteFuzzyLessThan(const T & var1,
476 : const T2 & var2,
477 827368 : const T3 & tol = libMesh::TOLERANCE * libMesh::TOLERANCE)
478 : {
479 622313 : return (MetaPhysicL::raw_value(var1) <
480 622313 : (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 5384148 : relativeFuzzyEqual(const T & var1,
493 : const T2 & var2,
494 637 : 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 10768296 : return absoluteFuzzyEqual(
504 : var1,
505 : var2,
506 5384148 : 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 4 : relativeFuzzyGreaterEqual(const T & var1,
554 : const T2 & var2,
555 8 : const T3 & tol = libMesh::TOLERANCE * libMesh::TOLERANCE)
556 : {
557 8 : return (absoluteFuzzyGreaterEqual(
558 : var1,
559 : var2,
560 4 : 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 2 : relativeFuzzyGreaterThan(const T & var1,
579 : const T2 & var2,
580 4 : const T3 & tol = libMesh::TOLERANCE * libMesh::TOLERANCE)
581 : {
582 4 : return (absoluteFuzzyGreaterThan(
583 : var1,
584 : var2,
585 2 : 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 4 : relativeFuzzyLessEqual(const T & var1,
605 : const T2 & var2,
606 8 : const T3 & tol = libMesh::TOLERANCE * libMesh::TOLERANCE)
607 : {
608 8 : return (absoluteFuzzyLessEqual(
609 : var1,
610 : var2,
611 4 : 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 4714 : relativeFuzzyLessThan(const T & var1,
630 : const T2 & var2,
631 7072 : const T3 & tol = libMesh::TOLERANCE * libMesh::TOLERANCE)
632 : {
633 9428 : return (absoluteFuzzyLessThan(
634 : var1,
635 : var2,
636 4714 : 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 39419 : getUnion(const std::vector<T> & vector1, const std::vector<T> & vector2, std::vector<T> & common)
646 : {
647 39419 : std::unordered_set<T> unique_elements;
648 39419 : unique_elements.reserve(vector1.size() + vector2.size());
649 :
650 74812 : for (const T & entry : vector1)
651 35393 : unique_elements.insert(entry);
652 74812 : for (const T & entry : vector2)
653 35393 : unique_elements.insert(entry);
654 :
655 : // Now populate the common vector with the union
656 39419 : common.clear();
657 39419 : common.assign(unique_elements.begin(), unique_elements.end());
658 39419 : }
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 20 : isZero(const T & value, const Real tolerance = TOLERANCE * TOLERANCE * TOLERANCE)
692 : {
693 : if constexpr (Has_size<T>::value)
694 : {
695 6 : for (const auto & element : value)
696 4 : if (!isZero(element, tolerance))
697 2 : return false;
698 :
699 2 : return true;
700 : }
701 : else if constexpr (libMesh::TensorTools::TensorTraits<T>::rank == 0)
702 8 : return MooseUtils::absoluteFuzzyEqual(MetaPhysicL::raw_value(value), 0, tolerance);
703 : else if constexpr (libMesh::TensorTools::TensorTraits<T>::rank == 1)
704 : {
705 10 : for (const auto i : make_range(Moose::dim))
706 8 : if (!MooseUtils::absoluteFuzzyEqual(MetaPhysicL::raw_value(value(i)), 0, tolerance))
707 2 : return false;
708 :
709 2 : return true;
710 : }
711 : else if constexpr (libMesh::TensorTools::TensorTraits<T>::rank == 2)
712 : {
713 10 : for (const auto i : make_range(Moose::dim))
714 26 : for (const auto j : make_range(Moose::dim))
715 20 : if (!MooseUtils::absoluteFuzzyEqual(MetaPhysicL::raw_value(value(i, j)), 0, tolerance))
716 2 : return false;
717 :
718 2 : 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 24 : expandAllMatches(const std::vector<T> & candidates, std::vector<T> & patterns)
816 : {
817 24 : std::set<T> expanded;
818 72 : for (const auto & p : patterns)
819 : {
820 48 : unsigned int found = 0;
821 240 : for (const auto & c : candidates)
822 192 : if (globCompare(c, p))
823 : {
824 48 : expanded.insert(c);
825 48 : found++;
826 : }
827 48 : if (!found)
828 0 : throw std::invalid_argument(p);
829 : }
830 24 : patterns.assign(expanded.begin(), expanded.end());
831 24 : }
832 :
833 : /**
834 : * convert takes a string representation of a number type and converts it to the number.
835 : * This method is here to get around deficiencies in the STL stoi and stod methods where they
836 : * might successfully convert part of a string to a number when we'd like to throw an error.
837 : */
838 : template <typename T>
839 : T
840 6713 : convert(const std::string & str, bool throw_on_failure = false)
841 : {
842 6713 : std::stringstream ss(str);
843 1720 : T val;
844 6713 : if ((ss >> val).fail() || !ss.eof())
845 : {
846 13 : std::string msg = std::string("Unable to convert '") + str + "' to type " +
847 : libMesh::demangle(typeid(T).name());
848 :
849 13 : if (throw_on_failure)
850 13 : throw std::invalid_argument(msg);
851 : else
852 0 : mooseError(msg);
853 13 : }
854 :
855 8420 : return val;
856 6713 : }
857 :
858 : template <>
859 : short int convert<short int>(const std::string & str, bool throw_on_failure);
860 :
861 : template <>
862 : unsigned short int convert<unsigned short int>(const std::string & str, bool throw_on_failure);
863 :
864 : template <>
865 : int convert<int>(const std::string & str, bool throw_on_failure);
866 :
867 : template <>
868 : unsigned int convert<unsigned int>(const std::string & str, bool throw_on_failure);
869 :
870 : template <>
871 : long int convert<long int>(const std::string & str, bool throw_on_failure);
872 :
873 : template <>
874 : unsigned long int convert<unsigned long int>(const std::string & str, bool throw_on_failure);
875 :
876 : template <>
877 : long long int convert<long long int>(const std::string & str, bool throw_on_failure);
878 :
879 : template <>
880 : unsigned long long int convert<unsigned long long int>(const std::string & str,
881 : bool throw_on_failure);
882 :
883 : /**
884 : * Create a symbolic link, if the link already exists it is replaced.
885 : */
886 : void createSymlink(const std::string & target, const std::string & link);
887 :
888 : /**
889 : * Remove a symbolic link, if the given filename is a link.
890 : */
891 : void clearSymlink(const std::string & link);
892 :
893 : /**
894 : * Returns a container that contains the content of second passed in container
895 : * inserted into the first passed in container (set or map union).
896 : */
897 : template <typename T>
898 : T
899 : concatenate(T c1, const T & c2)
900 : {
901 : c1.insert(c2.begin(), c2.end());
902 : return c1;
903 : }
904 :
905 : /**
906 : * Returns a vector that contains is the concatenation of the two passed in vectors.
907 : */
908 : template <typename T>
909 : std::vector<T>
910 : concatenate(std::vector<T> c1, const std::vector<T> & c2)
911 : {
912 : c1.insert(c1.end(), c2.begin(), c2.end());
913 : return c1;
914 : }
915 :
916 : /**
917 : * Returns the passed in vector with the item appended to it.
918 : */
919 : template <typename T>
920 : std::vector<T>
921 33 : concatenate(std::vector<T> c1, const T & item)
922 : {
923 33 : c1.push_back(item);
924 33 : return c1;
925 : }
926 :
927 : /**
928 : * Concatenates \p value into a single string separated by \p separator
929 : */
930 : std::string stringJoin(const std::vector<std::string> & values,
931 : const std::string & separator = " ");
932 :
933 : /**
934 : * @return Whether or not \p value begins with \p begin_value
935 : */
936 : bool beginsWith(const std::string & value, const std::string & begin_value);
937 :
938 : /**
939 : * Return the number of digits for a number.
940 : *
941 : * This can foster quite a large discussion:
942 : * https://stackoverflow.com/questions/1489830/efficient-way-to-determine-number-of-digits-in-an-integer
943 : *
944 : * For our purposes I like the following algorithm.
945 : */
946 : template <typename T>
947 : int
948 1747 : numDigits(const T & num)
949 : {
950 1747 : return num > 9 ? static_cast<int>(std::log10(static_cast<double>(num))) + 1 : 1;
951 : }
952 :
953 : /**
954 : * Return the default ExecFlagEnum for MOOSE.
955 : */
956 : ExecFlagEnum getDefaultExecFlagEnum();
957 :
958 : /**
959 : * Robust string to integer conversion that fails for cases such at "1foo".
960 : * @param input The string to convert.
961 : * @param throw_on_failure Throw an invalid_argument exception instead of mooseError.
962 : */
963 : int stringToInteger(const std::string & input, bool throw_on_failure = false);
964 :
965 : /**
966 : * Linearly partition a number of items
967 : *
968 : * @param num_items The number of items to partition
969 : * @param num_chunks The number of chunks to partition into
970 : * @param chunk_id The ID of the chunk you are trying to get information about (typically the
971 : * current MPI rank)
972 : * @param num_local_items Output: The number of items for this chunk_id
973 : * @param local_items_begin Output: The first item for this chunk_id
974 : * @param local_items_end Output: One past the final item for this chunk_id
975 : */
976 : void linearPartitionItems(dof_id_type num_items,
977 : dof_id_type num_chunks,
978 : dof_id_type chunk_id,
979 : dof_id_type & num_local_items,
980 : dof_id_type & local_items_begin,
981 : dof_id_type & local_items_end);
982 :
983 : /**
984 : * Return the chunk_id that is assigned to handle item_id
985 : *
986 : * @param num_items Global number of items to partition
987 : * @param num_chunks Total number of chunks to split into
988 : * @param item_id The item to find the chunk_id for
989 : * @return The chunk_id of the chunk that contains item_id
990 : */
991 : processor_id_type
992 : linearPartitionChunk(dof_id_type num_items, dof_id_type num_chunks, dof_id_type item_id);
993 :
994 : /**
995 : * Wrapper around PetscGetRealPath, which is a cross-platform replacement for realpath
996 : */
997 : std::string realpath(const std::string & path);
998 :
999 : /**
1000 : * Custom type trait that has a ::value of true for types that cam be use interchangeably
1001 : * with Real. Most notably it is false for complex numbers, which do not have a
1002 : * strict ordering (and therefore no <,>,<=,>= operators).
1003 : */
1004 : template <typename T>
1005 : struct IsLikeReal
1006 : {
1007 : static constexpr bool value = false;
1008 : };
1009 : template <>
1010 : struct IsLikeReal<Real>
1011 : {
1012 : static constexpr bool value = true;
1013 : };
1014 : template <>
1015 : struct IsLikeReal<ADReal>
1016 : {
1017 : static constexpr bool value = true;
1018 : };
1019 :
1020 : /**
1021 : * Custom type trait that has a ::value of true for types that can be broadcasted
1022 : */
1023 : template <typename T>
1024 : struct canBroadcast
1025 : {
1026 : static constexpr bool value = std::is_base_of<TIMPI::DataType, TIMPI::StandardType<T>>::value ||
1027 : TIMPI::Has_buffer_type<TIMPI::Packing<T>>::value;
1028 : };
1029 :
1030 : ///@{ Comparison helpers that support the MooseUtils::Any wildcard which will match any value
1031 : const static struct AnyType
1032 : {
1033 : } Any;
1034 :
1035 : template <typename T1, typename T2>
1036 : bool
1037 43769 : wildcardEqual(const T1 & a, const T2 & b)
1038 : {
1039 43769 : return a == b;
1040 : }
1041 :
1042 : template <typename T>
1043 : bool
1044 : wildcardEqual(const T &, AnyType)
1045 : {
1046 : return true;
1047 : }
1048 : template <typename T>
1049 : bool
1050 5072 : wildcardEqual(AnyType, const T &)
1051 : {
1052 5072 : return true;
1053 : }
1054 : ///@}
1055 :
1056 : /**
1057 : * Find a specific pair in a container matching on first, second or both pair components
1058 : */
1059 : template <typename C, typename It, typename M1, typename M2>
1060 : auto
1061 53411 : findPair(C & container, It start_iterator, const M1 & first, const M2 & second)
1062 : {
1063 53411 : return std::find_if(start_iterator,
1064 : container.end(),
1065 48729 : [&](auto & item) {
1066 48841 : return wildcardEqual(first, item.first) &&
1067 48841 : wildcardEqual(second, item.second);
1068 53411 : });
1069 : }
1070 :
1071 : /**
1072 : * Construct a valid bounding box from 2 arbitrary points
1073 : *
1074 : * If you have 2 points in space and you wish to construct a bounding box, you should use
1075 : * this method to avoid unexpected behavior of the underlying BoundingBox class in libMesh.
1076 : * BoundingBox class expect 2 points whose coordinates are "sorted" (i.e., x-, y- and -z
1077 : * coordinates of the first point are smaller then the corresponding coordinates of the second
1078 : * point). If this "sorting" is not present, the BoundingBox class will build an empty box and
1079 : * any further testing of points inside the box will fail. This method will allow you to obtain
1080 : * the correct bounding box for any valid combination of 2 corner points of a box.
1081 : *
1082 : * @param p1 First corner of the constructed bounding box
1083 : * @param p2 Second corner of the constructed bounding box
1084 : * @return Valid bounding box
1085 : */
1086 : libMesh::BoundingBox buildBoundingBox(const Point & p1, const Point & p2);
1087 :
1088 : /**
1089 : * Utility class template for a semidynamic vector with a maximum size N
1090 : * and a chosen dynamic size. This container avoids heap allocation and
1091 : * is meant as a replacement for small local std::vector variables.
1092 : * By default this class uses `value initialization`. This can be disabled
1093 : * using the third template parameter if uninitialized storage is acceptable,
1094 : */
1095 : template <typename T, std::size_t N, bool value_init = true>
1096 : class SemidynamicVector : public MetaPhysicL::DynamicStdArrayWrapper<T, MetaPhysicL::NWrapper<N>>
1097 : {
1098 : typedef MetaPhysicL::DynamicStdArrayWrapper<T, MetaPhysicL::NWrapper<N>> Parent;
1099 :
1100 : public:
1101 19232003 : SemidynamicVector(std::size_t size) : Parent()
1102 : {
1103 4808003 : Parent::resize(size);
1104 : if constexpr (value_init)
1105 19201934 : for (const auto i : make_range(size))
1106 14393932 : _data[i] = T{};
1107 4808003 : }
1108 :
1109 1 : void resize(std::size_t new_size)
1110 : {
1111 1 : [[maybe_unused]] const auto old_dynamic_n = Parent::size();
1112 :
1113 1 : Parent::resize(new_size);
1114 :
1115 : if constexpr (value_init)
1116 : for (const auto i : make_range(old_dynamic_n, _dynamic_n))
1117 : _data[i] = T{};
1118 1 : }
1119 :
1120 1 : void push_back(const T & v)
1121 : {
1122 1 : const auto old_dynamic_n = Parent::size();
1123 1 : Parent::resize(old_dynamic_n + 1);
1124 1 : _data[old_dynamic_n] = v;
1125 1 : }
1126 :
1127 : template <typename... Args>
1128 1 : void emplace_back(Args &&... args)
1129 : {
1130 1 : const auto old_dynamic_n = Parent::size();
1131 1 : Parent::resize(old_dynamic_n + 1);
1132 1 : (::new (&_data[old_dynamic_n]) T(std::forward<Args>(args)...));
1133 1 : }
1134 :
1135 1 : std::size_t max_size() const { return N; }
1136 :
1137 : using Parent::_data;
1138 : using Parent::_dynamic_n;
1139 : };
1140 :
1141 : /**
1142 : * The MooseUtils::get() specializations are used to support making
1143 : * forwards-compatible code changes from dumb pointers to smart pointers.
1144 : * The same line of code, e.g.
1145 : *
1146 : * libMesh::Parameters::Value * value = MooseUtils::get(map_iter->second);
1147 : *
1148 : * will then work regardless of whether map_iter->second is a dumb pointer
1149 : * or a smart pointer. Note that the smart pointer get() functions are const
1150 : * so they can be (ab)used to get a non-const pointer to the underlying
1151 : * resource. We are simply following this convention here.
1152 : */
1153 : template <typename T>
1154 : T *
1155 684436052 : get(const std::unique_ptr<T> & u)
1156 : {
1157 684436052 : return u.get();
1158 : }
1159 :
1160 : template <typename T>
1161 : T *
1162 : get(T * p)
1163 : {
1164 : return p;
1165 : }
1166 :
1167 : template <typename T>
1168 : T *
1169 : get(const std::shared_ptr<T> & s)
1170 : {
1171 : return s.get();
1172 : }
1173 :
1174 : /**
1175 : * This method detects whether two sets intersect without building a result set.
1176 : * It exits as soon as any intersection is detected.
1177 : */
1178 : template <class InputIterator>
1179 : bool
1180 35 : setsIntersect(InputIterator first1, InputIterator last1, InputIterator first2, InputIterator last2)
1181 : {
1182 48 : while (first1 != last1 && first2 != last2)
1183 : {
1184 17 : if (*first1 == *first2)
1185 4 : return true;
1186 :
1187 13 : if (*first1 < *first2)
1188 0 : ++first1;
1189 13 : else if (*first1 > *first2)
1190 13 : ++first2;
1191 : }
1192 31 : return false;
1193 : }
1194 :
1195 : template <class T>
1196 : bool
1197 35 : setsIntersect(const T & s1, const T & s2)
1198 : {
1199 35 : return setsIntersect(s1.begin(), s1.end(), s2.begin(), s2.end());
1200 : }
1201 :
1202 : /**
1203 : * Courtesy https://stackoverflow.com/a/8889045 and
1204 : * https://en.cppreference.com/w/cpp/string/byte/isdigit
1205 : * @return Whether every character in the string is a digit
1206 : */
1207 : inline bool
1208 1786580 : isDigits(const std::string & str)
1209 : {
1210 4082627 : return std::all_of(str.begin(), str.end(), [](unsigned char c) { return std::isdigit(c); });
1211 : }
1212 :
1213 : /**
1214 : * Courtesy https://stackoverflow.com/a/57163016 and
1215 : * https://stackoverflow.com/questions/447206/c-isfloat-function
1216 : * @return Whether the string is convertible to a float
1217 : */
1218 : inline bool
1219 16 : isFloat(const std::string & str)
1220 : {
1221 16 : if (str.empty())
1222 0 : return false;
1223 : char * ptr;
1224 16 : strtof(str.c_str(), &ptr);
1225 16 : return (*ptr) == '\0';
1226 : }
1227 :
1228 : /**
1229 : * Gets the canonical path of the given path
1230 : */
1231 : std::string canonicalPath(const std::string & path);
1232 :
1233 : /**
1234 : * @returns Whether the \p string1 starts with \p string2
1235 : */
1236 : bool startsWith(const std::string & string1, const std::string & string2);
1237 :
1238 : /**
1239 : * Replace the starting string \p string2 of \p string1 with \p string3. A user should have checked
1240 : * that \p string1 \p startsWith \p string2
1241 : */
1242 : void replaceStart(std::string & string1, const std::string & string2, const std::string & string3);
1243 :
1244 : /**
1245 : * @returns whether every alphabetic character in a string is lower-case
1246 : */
1247 : bool isAllLowercase(const std::string & str);
1248 : } // MooseUtils namespace
1249 :
1250 : namespace Moose
1251 : {
1252 : template <typename T>
1253 : struct ADType;
1254 :
1255 : template <typename T, std::size_t N, bool value_init>
1256 : struct ADType<MooseUtils::SemidynamicVector<T, N, value_init>>
1257 : {
1258 : typedef MooseUtils::SemidynamicVector<typename ADType<T>::type, N, value_init> type;
1259 : };
1260 : }
1261 :
1262 : /**
1263 : * find, erase, length algorithm for removing a substring from a string
1264 : */
1265 : void removeSubstring(std::string & main, const std::string & sub);
1266 :
1267 : /**
1268 : * find, erase, length algorithm for removing a substring from a copy of a string
1269 : */
1270 : std::string removeSubstring(const std::string & main, const std::string & sub);
|