From cc4d20d81d8e9601c11e517d2847ca3b8fc0a0b2 Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Fri, 21 Jun 2024 15:49:50 -0700 Subject: [PATCH 01/22] Add fuzzy_equal --- include/utils/fuzzy_equal.h | 173 ++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 include/utils/fuzzy_equal.h diff --git a/include/utils/fuzzy_equal.h b/include/utils/fuzzy_equal.h new file mode 100644 index 00000000000..ff39381bd9b --- /dev/null +++ b/include/utils/fuzzy_equal.h @@ -0,0 +1,173 @@ +// The libMesh Finite Element Library. +// Copyright (C) 2002-2024 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner + +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#ifndef LIBMESH_FUZZY_EQUAL_H +#define LIBMESH_FUZZY_EQUAL_H + +#include "libmesh/libmesh_common.h" +#include "libmesh/tensor_tools.h" +#include "libmesh/compare_types.h" + +namespace libMesh +{ +#ifdef LIBMESH_HAVE_METAPHYSICL + +#include "metaphysicl/raw_type.h" + +/** + * Function to check whether two variables are equal within an absolute tolerance + * @param var1 The first variable to be checked + * @param var2 The second variable to be checked + * @param tol The tolerance to be used + * @return true if var1 and var2 are equal within tol + */ +template ::value && ScalarTraits::value && + ScalarTraits::value, + int>::type = 0> +bool +absolute_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE * TOLERANCE) +{ + return (std::abs(MetaPhysicL::raw_value(var1) - MetaPhysicL::raw_value(var2)) <= + MetaPhysicL::raw_value(tol)); +} + +/** + * Function to check whether two variables are equal within a relative tolerance + * @param var1 The first variable to be checked + * @param var2 The second variable to be checked + * @param tol The relative tolerance to be used + * @return true if var1 and var2 are equal within relative tol + */ +template +bool +relative_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE * TOLERANCE) +{ + if constexpr (ScalarTraits::value || TensorTools::MathWrapperTraits::value) + { + static_assert(TensorTools::TensorTraits::rank == TensorTools::TensorTraits::rank, + "Mathematical types must be same for arguments to relativelyFuzzEqual"); + if constexpr (TensorTools::TensorTraits::rank == 0) + return absolute_fuzzy_equal( + var1, + var2, + tol * (std::abs(MetaPhysicL::raw_value(var1)) + std::abs(MetaPhysicL::raw_value(var2)))); + else if constexpr (TensorTools::TensorTraits::rank == 1) + { + for (const auto i : make_range(max_dim)) + if (!relativeFuzzyEqual(var1(i), var2(i), tol)) + return false; + + return true; + } + else if constexpr (TensorTools::TensorTraits::rank == 2) + { + for (const auto i : make_range(max_dim)) + for (const auto j : make_range(max_dim)) + if (!relativeFuzzyEqual(var1(i, j), var2(i, j), tol)) + return false; + + return true; + } + } + else + { + // We dare to dream + mooseAssert(var1.size() == var2.size(), "These must be the same size"); + for (const auto i : index_range(var1)) + if (!relativeFuzzyEqual(var1(i), var2(i), tol)) + return false; + + return true; + } +} + +#else + +/** + * Function to check whether two variables are equal within an absolute tolerance + * @param var1 The first variable to be checked + * @param var2 The second variable to be checked + * @param tol The tolerance to be used + * @return true if var1 and var2 are equal within tol + */ +template ::value && ScalarTraits::value && + ScalarTraits::value, + int>::type = 0> +bool +absolute_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE * TOLERANCE) +{ + return (std::abs(var1 - var2) <= tol); +} + +/** + * Function to check whether two variables are equal within a relative tolerance + * @param var1 The first variable to be checked + * @param var2 The second variable to be checked + * @param tol The relative tolerance to be used + * @return true if var1 and var2 are equal within relative tol + */ +template +bool +relative_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE * TOLERANCE) +{ + if constexpr (ScalarTraits::value || TensorTools::MathWrapperTraits::value) + { + static_assert(TensorTools::TensorTraits::rank == TensorTools::TensorTraits::rank, + "Mathematical types must be same for arguments to relativelyFuzzEqual"); + if constexpr (TensorTools::TensorTraits::rank == 0) + return absolute_fuzzy_equal(var1, var2, tol * (std::abs(var1) + std::abs(var2))); + else if constexpr (TensorTools::TensorTraits::rank == 1) + { + for (const auto i : make_range(max_dim)) + if (!relativeFuzzyEqual(var1(i), var2(i), tol)) + return false; + + return true; + } + else if constexpr (TensorTools::TensorTraits::rank == 2) + { + for (const auto i : make_range(max_dim)) + for (const auto j : make_range(max_dim)) + if (!relativeFuzzyEqual(var1(i, j), var2(i, j), tol)) + return false; + + return true; + } + } + else + { + // We dare to dream + mooseAssert(var1.size() == var2.size(), "These must be the same size"); + for (const auto i : index_range(var1)) + if (!relativeFuzzyEqual(var1(i), var2(i), tol)) + return false; + + return true; + } +} + +#endif // !LIBMESH_HAVE_METAPHYSICL + +} // namespace libMesh + +#endif // LIBMESH_FUZZY_EQUAL_H From b70cb76674f15045d629f60ad9f271d862915fe8 Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Sat, 22 Jun 2024 11:01:42 -0700 Subject: [PATCH 02/22] Write fuzzy_equal for NumericVector/SparseMatrix --- include/numerics/numeric_vector.h | 5 ++ include/numerics/petsc_matrix.h | 2 + include/numerics/sparse_matrix.h | 7 ++- src/numerics/numeric_vector.C | 24 ++++++++ src/numerics/petsc_matrix.C | 97 +++++++++++++++++++++++++++++++ src/numerics/sparse_matrix.C | 30 ++++++++++ 6 files changed, 164 insertions(+), 1 deletion(-) diff --git a/include/numerics/numeric_vector.h b/include/numerics/numeric_vector.h index 0ac3de4cec7..e7cf975bacb 100644 --- a/include/numerics/numeric_vector.h +++ b/include/numerics/numeric_vector.h @@ -786,6 +786,11 @@ class NumericVector : public ReferenceCountedObject>, */ bool compatible(const NumericVector & v) const; + /** + * checks whether the vector \p v is fuzzy equal to this vector + */ + bool fuzzy_equal(const NumericVector & v) const; + protected: /** diff --git a/include/numerics/petsc_matrix.h b/include/numerics/petsc_matrix.h index 733ef30373f..78f5fd90284 100644 --- a/include/numerics/petsc_matrix.h +++ b/include/numerics/petsc_matrix.h @@ -348,6 +348,8 @@ class PetscMatrix final : public SparseMatrix virtual void create_submatrix_nosort(SparseMatrix & submatrix, const std::vector & rows, const std::vector & cols) const override; + + virtual bool fuzzy_equal(const SparseMatrix & other) const override; protected: /** diff --git a/include/numerics/sparse_matrix.h b/include/numerics/sparse_matrix.h index b4d1bed2b47..9b61bd9a4f8 100644 --- a/include/numerics/sparse_matrix.h +++ b/include/numerics/sparse_matrix.h @@ -573,8 +573,13 @@ class SparseMatrix : public ReferenceCountedObject>, std::vector & indices, std::vector & values) const = 0; -protected: + /** + * checks whether the matrix \p other is fuzzy equal to this matrix + */ + virtual bool fuzzy_equal(const SparseMatrix & other) const; + +protected: /** * Protected implementation of the create_submatrix and reinit_submatrix * routines. diff --git a/src/numerics/numeric_vector.C b/src/numerics/numeric_vector.C index af06583546f..a45c338c2a7 100644 --- a/src/numerics/numeric_vector.C +++ b/src/numerics/numeric_vector.C @@ -27,6 +27,7 @@ #include "libmesh/tensor_tools.h" #include "libmesh/enum_solver_package.h" #include "libmesh/int_range.h" +#include "libmesh/fuzzy_equal.h" // C++ includes @@ -430,6 +431,29 @@ bool NumericVector::compatible (const NumericVector & v) const } +template +bool NumericVector::fuzzy_equal(const NumericVector & v) const +{ + bool equiv = true; + if (this->local_size() != v.local_size()) + equiv = false; + + if (equiv) + for (const auto i : make_range(this->first_local_index(), this->last_local_index())) + { + if (relative_fuzzy_equal((*this)(i), v(i)) || absolute_fuzzy_equal((*this)(i), v(i))) + continue; + else + { + equiv = false; + break; + } + } + + this->comm().min(equiv); + + return equiv; +} //------------------------------------------------------------------ // Explicit instantiations diff --git a/src/numerics/petsc_matrix.C b/src/numerics/petsc_matrix.C index 0e31bc90b2c..06a06408b56 100644 --- a/src/numerics/petsc_matrix.C +++ b/src/numerics/petsc_matrix.C @@ -32,6 +32,9 @@ #include "libmesh/parallel.h" #include "libmesh/utility.h" #include "libmesh/wrapped_petsc.h" +#include "libmesh/fuzzy_equal.h" + +#include // C++ includes #ifdef LIBMESH_HAVE_UNISTD_H @@ -1575,6 +1578,100 @@ SparseMatrix & PetscMatrix::operator= (const SparseMatrix & v) return *this; } +template +bool +PetscMatrix::fuzzy_equal(const SparseMatrix & other) const +{ + const auto * const petsc_other = dynamic_cast *>(&other); + if (!petsc_other) + // This result should be the same on all procs + return false; + + Mat petsc_other_mat = petsc_other->_mat; + const PetscScalar *petsc_row, *petsc_row_other; + const PetscInt *petsc_cols, *petsc_cols_other; + + PetscErrorCode ierr = static_cast(0); + PetscInt ncols = 0, ncols_other = 0; + + bool equiv = true; + if ((this->local_m() != other.local_m()) || (this->local_n() != other.local_n())) + { + equiv = false; + goto endLoop; + } + + for (const auto i : make_range(this->row_start(), this->row_stop())) + { + const auto i_val = static_cast(i); + + // the matrix needs to be closed for this to work + // this->close(); + // but closing it is a semiparallel operation; we want operator() + // to run on one processor. + libmesh_assert(this->closed()); + libmesh_assert(petsc_other->closed()); + + ierr = MatGetRow(_mat, i_val, &ncols, &petsc_cols, &petsc_row); + LIBMESH_CHKERR(ierr); + ierr = MatGetRow(petsc_other_mat, i_val, &ncols_other, &petsc_cols_other, &petsc_row_other); + LIBMESH_CHKERR(ierr); + + auto restore_rows = [this, + i_val, + petsc_other_mat, + &ierr, + &ncols, + &petsc_cols, + &petsc_row, + &ncols_other, + &petsc_cols_other, + &petsc_row_other]() + { + ierr = MatRestoreRow(_mat, i_val, &ncols, &petsc_cols, &petsc_row); + LIBMESH_CHKERR(ierr); + ierr = + MatRestoreRow(petsc_other_mat, i_val, &ncols_other, &petsc_cols_other, &petsc_row_other); + LIBMESH_CHKERR(ierr); + }; + + auto compared_false = [&equiv, restore_rows]() + { + restore_rows(); + equiv = false; + }; + + if (ncols != ncols_other) + { + compared_false(); + goto endLoop; + } + + // No need for fuzzy comparison here + PetscBool petsc_equiv; + ierr = PetscArraycmp(petsc_cols, petsc_cols_other, ncols, &petsc_equiv); + LIBMESH_CHKERR(ierr); + if (petsc_equiv == PETSC_FALSE) + compared_false(); + + for (const auto j_val : make_range(ncols)) + if (relative_fuzzy_equal(petsc_row[j_val], petsc_row_other[j_val]) || + absolute_fuzzy_equal(petsc_row[j_val], petsc_row_other[j_val])) + continue; + else + { + compared_false(); + goto endLoop; + } + + restore_rows(); + } + +endLoop: + this->comm().min(equiv); + + return equiv; +} //------------------------------------------------------------------ // Explicit instantiations diff --git a/src/numerics/sparse_matrix.C b/src/numerics/sparse_matrix.C index 31d85cdd78e..6c6969089fb 100644 --- a/src/numerics/sparse_matrix.C +++ b/src/numerics/sparse_matrix.C @@ -32,6 +32,8 @@ #include "libmesh/trilinos_epetra_matrix.h" #include "libmesh/numeric_vector.h" #include "libmesh/enum_solver_package.h" +#include "libmesh/fuzzy_equal.h" + // gzstream for reading compressed files as a stream #ifdef LIBMESH_HAVE_GZSTREAM @@ -855,6 +857,34 @@ void SparseMatrix::read_petsc_hdf5(const std::string &) +template +bool SparseMatrix::fuzzy_equal(const SparseMatrix & other) const +{ + bool equiv = true; + if ((this->local_m() != other.local_m()) || (this->local_n() != other.local_n())) + equiv = false; + + if (equiv) + for (const auto i : make_range(this->row_start(), this->row_stop())) + for (const auto j : make_range(this->col_start(), this->col_stop())) + { + if (relative_fuzzy_equal((*this)(i, j), other(i, j)) || + absolute_fuzzy_equal((*this)(i, j), other(i, j))) + continue; + else + { + equiv = false; + goto endLoops; + } + } + +endLoops: + this->comm().min(equiv); + + return equiv; +} + + //------------------------------------------------------------------ // Explicit instantiations template class LIBMESH_EXPORT SparseMatrix; From 440f10b9052f9cfe8e35933de4a72f93c2736a2c Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Fri, 21 Jun 2024 10:35:13 -0700 Subject: [PATCH 03/22] Update Makefile.am files. --- include/base/libmesh_common.h | 2 ++ include/include_HEADERS | 1 + include/libmesh/Makefile.am | 4 ++++ include/utils/utility.h | 3 +-- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/include/base/libmesh_common.h b/include/base/libmesh_common.h index 8687f07e3eb..63d6028e0a4 100644 --- a/include/base/libmesh_common.h +++ b/include/base/libmesh_common.h @@ -634,6 +634,8 @@ inline Tnew libmesh_cast_int (Told oldvar) template constexpr std::false_type always_false{}; +static constexpr std::size_t max_dim = LIBMESH_DIM; + // build a integer representation of version #define LIBMESH_VERSION_ID(major,minor,patch) (((major) << 16) | ((minor) << 8) | ((patch) & 0xFF)) diff --git a/include/include_HEADERS b/include/include_HEADERS index 0abe4ee0070..148aa79fb22 100644 --- a/include/include_HEADERS +++ b/include/include_HEADERS @@ -458,6 +458,7 @@ include_HEADERS = \ utils/compare_types.h \ utils/enum_to_string.h \ utils/error_vector.h \ + utils/fuzzy_equal.h \ utils/hashing.h \ utils/hashword.h \ utils/ignore_warnings.h \ diff --git a/include/libmesh/Makefile.am b/include/libmesh/Makefile.am index e266a7ecd73..64a2836031b 100644 --- a/include/libmesh/Makefile.am +++ b/include/libmesh/Makefile.am @@ -454,6 +454,7 @@ BUILT_SOURCES = \ compare_types.h \ enum_to_string.h \ error_vector.h \ + fuzzy_equal.h \ hashing.h \ hashword.h \ ignore_warnings.h \ @@ -1937,6 +1938,9 @@ enum_to_string.h: $(top_srcdir)/include/utils/enum_to_string.h error_vector.h: $(top_srcdir)/include/utils/error_vector.h $(AM_V_GEN)rm -f $@ && $(LN_S) -f $< $@ +fuzzy_equal.h: $(top_srcdir)/include/utils/fuzzy_equal.h + $(AM_V_GEN)rm -f $@ && $(LN_S) -f $< $@ + hashing.h: $(top_srcdir)/include/utils/hashing.h $(AM_V_GEN)rm -f $@ && $(LN_S) -f $< $@ diff --git a/include/utils/utility.h b/include/utils/utility.h index e7ea6c1c385..49ef7116d59 100644 --- a/include/utils/utility.h +++ b/include/utils/utility.h @@ -538,8 +538,7 @@ struct CompareUnderlying } }; - -} +} // namespace Utility } // namespace libMesh From e86c9bb6460e44afd584ea89d8e5c27c9529a3ac Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Mon, 24 Jun 2024 08:56:28 -0700 Subject: [PATCH 04/22] max_dim -> max_mesh_dim --- include/base/libmesh_common.h | 2 +- include/utils/fuzzy_equal.h | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/include/base/libmesh_common.h b/include/base/libmesh_common.h index 63d6028e0a4..81b825ed28e 100644 --- a/include/base/libmesh_common.h +++ b/include/base/libmesh_common.h @@ -634,7 +634,7 @@ inline Tnew libmesh_cast_int (Told oldvar) template constexpr std::false_type always_false{}; -static constexpr std::size_t max_dim = LIBMESH_DIM; +static constexpr std::size_t max_mesh_dim = LIBMESH_DIM; // build a integer representation of version #define LIBMESH_VERSION_ID(major,minor,patch) (((major) << 16) | ((minor) << 8) | ((patch) & 0xFF)) diff --git a/include/utils/fuzzy_equal.h b/include/utils/fuzzy_equal.h index ff39381bd9b..cb568a48aa5 100644 --- a/include/utils/fuzzy_equal.h +++ b/include/utils/fuzzy_equal.h @@ -21,6 +21,7 @@ #include "libmesh/libmesh_common.h" #include "libmesh/tensor_tools.h" #include "libmesh/compare_types.h" +#include "libmesh/int_range.h" namespace libMesh { @@ -70,7 +71,7 @@ relative_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE tol * (std::abs(MetaPhysicL::raw_value(var1)) + std::abs(MetaPhysicL::raw_value(var2)))); else if constexpr (TensorTools::TensorTraits::rank == 1) { - for (const auto i : make_range(max_dim)) + for (const auto i : make_range(max_mesh_dim)) if (!relativeFuzzyEqual(var1(i), var2(i), tol)) return false; @@ -78,8 +79,8 @@ relative_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE } else if constexpr (TensorTools::TensorTraits::rank == 2) { - for (const auto i : make_range(max_dim)) - for (const auto j : make_range(max_dim)) + for (const auto i : make_range(max_mesh_dim)) + for (const auto j : make_range(max_mesh_dim)) if (!relativeFuzzyEqual(var1(i, j), var2(i, j), tol)) return false; @@ -138,7 +139,7 @@ relative_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE return absolute_fuzzy_equal(var1, var2, tol * (std::abs(var1) + std::abs(var2))); else if constexpr (TensorTools::TensorTraits::rank == 1) { - for (const auto i : make_range(max_dim)) + for (const auto i : make_range(max_mesh_dim)) if (!relativeFuzzyEqual(var1(i), var2(i), tol)) return false; @@ -146,8 +147,8 @@ relative_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE } else if constexpr (TensorTools::TensorTraits::rank == 2) { - for (const auto i : make_range(max_dim)) - for (const auto j : make_range(max_dim)) + for (const auto i : make_range(max_mesh_dim)) + for (const auto j : make_range(max_mesh_dim)) if (!relativeFuzzyEqual(var1(i, j), var2(i, j), tol)) return false; From 8be04bef89effff6e81d2921720fbe77155adbfe Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Tue, 25 Jun 2024 10:20:07 -0700 Subject: [PATCH 05/22] Fix fuzzy equal names --- include/utils/fuzzy_equal.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/utils/fuzzy_equal.h b/include/utils/fuzzy_equal.h index cb568a48aa5..4ac3b8cb004 100644 --- a/include/utils/fuzzy_equal.h +++ b/include/utils/fuzzy_equal.h @@ -72,7 +72,7 @@ relative_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE else if constexpr (TensorTools::TensorTraits::rank == 1) { for (const auto i : make_range(max_mesh_dim)) - if (!relativeFuzzyEqual(var1(i), var2(i), tol)) + if (!relative_fuzzy_equal(var1(i), var2(i), tol)) return false; return true; @@ -81,7 +81,7 @@ relative_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE { for (const auto i : make_range(max_mesh_dim)) for (const auto j : make_range(max_mesh_dim)) - if (!relativeFuzzyEqual(var1(i, j), var2(i, j), tol)) + if (!relative_fuzzy_equal(var1(i, j), var2(i, j), tol)) return false; return true; @@ -92,7 +92,7 @@ relative_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE // We dare to dream mooseAssert(var1.size() == var2.size(), "These must be the same size"); for (const auto i : index_range(var1)) - if (!relativeFuzzyEqual(var1(i), var2(i), tol)) + if (!relative_fuzzy_equal(var1(i), var2(i), tol)) return false; return true; @@ -140,7 +140,7 @@ relative_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE else if constexpr (TensorTools::TensorTraits::rank == 1) { for (const auto i : make_range(max_mesh_dim)) - if (!relativeFuzzyEqual(var1(i), var2(i), tol)) + if (!relative_fuzzy_equal(var1(i), var2(i), tol)) return false; return true; @@ -149,7 +149,7 @@ relative_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE { for (const auto i : make_range(max_mesh_dim)) for (const auto j : make_range(max_mesh_dim)) - if (!relativeFuzzyEqual(var1(i, j), var2(i, j), tol)) + if (!relative_fuzzy_equal(var1(i, j), var2(i, j), tol)) return false; return true; @@ -160,7 +160,7 @@ relative_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE // We dare to dream mooseAssert(var1.size() == var2.size(), "These must be the same size"); for (const auto i : index_range(var1)) - if (!relativeFuzzyEqual(var1(i), var2(i), tol)) + if (!relative_fuzzy_equal(var1(i), var2(i), tol)) return false; return true; From b74890703f82fdcbc41f306dd235f37a37330985 Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Tue, 25 Jun 2024 11:47:24 -0700 Subject: [PATCH 06/22] Add fuzzy_equal complex overloads --- include/utils/compare_types.h | 6 ++++ include/utils/fuzzy_equal.h | 57 ++++++++++++++++++++++++++--------- 2 files changed, 49 insertions(+), 14 deletions(-) diff --git a/include/utils/compare_types.h b/include/utils/compare_types.h index f45cb731ff9..28843f40abb 100644 --- a/include/utils/compare_types.h +++ b/include/utils/compare_types.h @@ -279,6 +279,12 @@ struct ScalarTraits> { static const bool value = ScalarTraits::value; }; +template +struct RealTraits> +{ + static const bool value = RealTraits::value; +}; + } // namespace libMesh #endif // LIBMESH_HAVE_METAPHYSICL diff --git a/include/utils/fuzzy_equal.h b/include/utils/fuzzy_equal.h index 4ac3b8cb004..f13b4c21f64 100644 --- a/include/utils/fuzzy_equal.h +++ b/include/utils/fuzzy_equal.h @@ -25,6 +25,42 @@ namespace libMesh { +/** + * Function to check whether two variables are equal within an absolute tolerance + * @param var1 The first variable to be checked + * @param var2 The second variable to be checked + * @param tol The tolerance to be used + * @return true if var1 and var2 are equal within tol + */ +template ::value && ScalarTraits::value && + ScalarTraits::value, + int>::type = 0> +bool absolute_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE * TOLERANCE); + +/** + * Function to check whether both the real and imaginary parts of two complex variables are equal + * within an absolute tolerance + * @param var1 The first complex variable to be checked + * @param var2 The second complex variable to be checked + * @param tol The real tolerance to be used for comparing both real and imgainary parts + * @return true if both real and imginary parts are equal within tol + */ +template ::value, int>::type = 0> +bool +absolute_fuzzy_equal(const std::complex & var1, + const std::complex & var2, + const T3 & tol = TOLERANCE * TOLERANCE) +{ + return absolute_fuzzy_equal(var1.real(), var2.real(), tol) && + absolute_fuzzy_equal(var1.imag(), var2.imag(), tol); +} + #ifdef LIBMESH_HAVE_METAPHYSICL #include "metaphysicl/raw_type.h" @@ -38,12 +74,12 @@ namespace libMesh */ template ::value && ScalarTraits::value && ScalarTraits::value, - int>::type = 0> + int>::type> bool -absolute_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE * TOLERANCE) +absolute_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol) { return (std::abs(MetaPhysicL::raw_value(var1) - MetaPhysicL::raw_value(var2)) <= MetaPhysicL::raw_value(tol)); @@ -90,7 +126,7 @@ relative_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE else { // We dare to dream - mooseAssert(var1.size() == var2.size(), "These must be the same size"); + libmesh_assert(var1.size() == var2.size()); for (const auto i : index_range(var1)) if (!relative_fuzzy_equal(var1(i), var2(i), tol)) return false; @@ -101,21 +137,14 @@ relative_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE #else -/** - * Function to check whether two variables are equal within an absolute tolerance - * @param var1 The first variable to be checked - * @param var2 The second variable to be checked - * @param tol The tolerance to be used - * @return true if var1 and var2 are equal within tol - */ template ::value && ScalarTraits::value && ScalarTraits::value, - int>::type = 0> + int>::type> bool -absolute_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE * TOLERANCE) +absolute_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol) { return (std::abs(var1 - var2) <= tol); } From 35cddda61ee9dbd2cafcc32adc49f140f55bb7f1 Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Tue, 25 Jun 2024 13:14:07 -0700 Subject: [PATCH 07/22] Add tol parameter to global structure fuzzy_equal's --- include/numerics/numeric_vector.h | 2 +- include/numerics/petsc_matrix.h | 4 +++- include/numerics/sparse_matrix.h | 3 ++- src/numerics/numeric_vector.C | 4 ++-- src/numerics/petsc_matrix.C | 6 +++--- src/numerics/sparse_matrix.C | 6 +++--- 6 files changed, 14 insertions(+), 11 deletions(-) diff --git a/include/numerics/numeric_vector.h b/include/numerics/numeric_vector.h index e7cf975bacb..6e6adb35486 100644 --- a/include/numerics/numeric_vector.h +++ b/include/numerics/numeric_vector.h @@ -789,7 +789,7 @@ class NumericVector : public ReferenceCountedObject>, /** * checks whether the vector \p v is fuzzy equal to this vector */ - bool fuzzy_equal(const NumericVector & v) const; + bool fuzzy_equal(const NumericVector & v, const Real tol = TOLERANCE) const; protected: diff --git a/include/numerics/petsc_matrix.h b/include/numerics/petsc_matrix.h index 78f5fd90284..afab0ac8e5a 100644 --- a/include/numerics/petsc_matrix.h +++ b/include/numerics/petsc_matrix.h @@ -349,7 +349,9 @@ class PetscMatrix final : public SparseMatrix const std::vector & rows, const std::vector & cols) const override; - virtual bool fuzzy_equal(const SparseMatrix & other) const override; + virtual bool fuzzy_equal(const SparseMatrix & other, + const Real tol = TOLERANCE) const override; + protected: /** diff --git a/include/numerics/sparse_matrix.h b/include/numerics/sparse_matrix.h index 9b61bd9a4f8..e0b4240b1d3 100644 --- a/include/numerics/sparse_matrix.h +++ b/include/numerics/sparse_matrix.h @@ -577,7 +577,8 @@ class SparseMatrix : public ReferenceCountedObject>, /** * checks whether the matrix \p other is fuzzy equal to this matrix */ - virtual bool fuzzy_equal(const SparseMatrix & other) const; + virtual bool fuzzy_equal(const SparseMatrix & other, + const Real tol = TOLERANCE) const; protected: /** diff --git a/src/numerics/numeric_vector.C b/src/numerics/numeric_vector.C index a45c338c2a7..5cab731c547 100644 --- a/src/numerics/numeric_vector.C +++ b/src/numerics/numeric_vector.C @@ -432,7 +432,7 @@ bool NumericVector::compatible (const NumericVector & v) const template -bool NumericVector::fuzzy_equal(const NumericVector & v) const +bool NumericVector::fuzzy_equal(const NumericVector & v, const Real tol) const { bool equiv = true; if (this->local_size() != v.local_size()) @@ -441,7 +441,7 @@ bool NumericVector::fuzzy_equal(const NumericVector & v) const if (equiv) for (const auto i : make_range(this->first_local_index(), this->last_local_index())) { - if (relative_fuzzy_equal((*this)(i), v(i)) || absolute_fuzzy_equal((*this)(i), v(i))) + if (relative_fuzzy_equal((*this)(i), v(i), tol) || absolute_fuzzy_equal((*this)(i), v(i), tol)) continue; else { diff --git a/src/numerics/petsc_matrix.C b/src/numerics/petsc_matrix.C index 06a06408b56..2d2a186d776 100644 --- a/src/numerics/petsc_matrix.C +++ b/src/numerics/petsc_matrix.C @@ -1580,7 +1580,7 @@ SparseMatrix & PetscMatrix::operator= (const SparseMatrix & v) template bool -PetscMatrix::fuzzy_equal(const SparseMatrix & other) const +PetscMatrix::fuzzy_equal(const SparseMatrix & other, const Real tol) const { const auto * const petsc_other = dynamic_cast *>(&other); if (!petsc_other) @@ -1655,8 +1655,8 @@ PetscMatrix::fuzzy_equal(const SparseMatrix & other) const compared_false(); for (const auto j_val : make_range(ncols)) - if (relative_fuzzy_equal(petsc_row[j_val], petsc_row_other[j_val]) || - absolute_fuzzy_equal(petsc_row[j_val], petsc_row_other[j_val])) + if (relative_fuzzy_equal(petsc_row[j_val], petsc_row_other[j_val], tol) || + absolute_fuzzy_equal(petsc_row[j_val], petsc_row_other[j_val], tol)) continue; else { diff --git a/src/numerics/sparse_matrix.C b/src/numerics/sparse_matrix.C index 6c6969089fb..c2fd1a8325b 100644 --- a/src/numerics/sparse_matrix.C +++ b/src/numerics/sparse_matrix.C @@ -858,7 +858,7 @@ void SparseMatrix::read_petsc_hdf5(const std::string &) template -bool SparseMatrix::fuzzy_equal(const SparseMatrix & other) const +bool SparseMatrix::fuzzy_equal(const SparseMatrix & other, const Real tol) const { bool equiv = true; if ((this->local_m() != other.local_m()) || (this->local_n() != other.local_n())) @@ -868,8 +868,8 @@ bool SparseMatrix::fuzzy_equal(const SparseMatrix & other) const for (const auto i : make_range(this->row_start(), this->row_stop())) for (const auto j : make_range(this->col_start(), this->col_stop())) { - if (relative_fuzzy_equal((*this)(i, j), other(i, j)) || - absolute_fuzzy_equal((*this)(i, j), other(i, j))) + if (relative_fuzzy_equal((*this)(i, j), other(i, j), tol) || + absolute_fuzzy_equal((*this)(i, j), other(i, j), tol)) continue; else { From 733cfa21555e577d8ddf18b281cf10a6f05396b3 Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Mon, 1 Jul 2024 13:00:59 -0700 Subject: [PATCH 08/22] Run bootstrap --- include/Makefile.in | 1 + include/libmesh/Makefile.in | 24 ++++++++++++++---------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/include/Makefile.in b/include/Makefile.in index 3ab4b8a75f6..e8c512255a8 100644 --- a/include/Makefile.in +++ b/include/Makefile.in @@ -1053,6 +1053,7 @@ include_HEADERS = \ utils/compare_types.h \ utils/enum_to_string.h \ utils/error_vector.h \ + utils/fuzzy_equal.h \ utils/hashing.h \ utils/hashword.h \ utils/ignore_warnings.h \ diff --git a/include/libmesh/Makefile.in b/include/libmesh/Makefile.in index 0350f542e8e..cbb8d939f41 100644 --- a/include/libmesh/Makefile.in +++ b/include/libmesh/Makefile.in @@ -670,16 +670,17 @@ BUILT_SOURCES = auto_ptr.h dirichlet_boundaries.h dof_map.h \ post_wait_free_buffer.h post_wait_unpack_buffer.h \ post_wait_work.h request.h standard_type.h status.h \ chunked_mapvector.h compare_types.h enum_to_string.h \ - error_vector.h hashing.h hashword.h ignore_warnings.h \ - int_range.h jacobi_polynomials.h libmesh_nullptr.h \ - location_maps.h mapvector.h null_output_iterator.h \ - number_lookups.h ostream_proxy.h parameters.h perf_log.h \ - perfmon.h plt_loader.h point_locator_base.h \ - point_locator_nanoflann.h point_locator_tree.h \ - pointer_to_pointer_iter.h pool_allocator.h restore_warnings.h \ - simple_range.h statistics.h string_to_enum.h timestamp.h \ - topology_map.h tree.h tree_base.h tree_node.h utility.h \ - vectormap.h win_gettimeofday.h xdr_cxx.h \ + error_vector.h fuzzy_equal.h hashing.h hashword.h \ + ignore_warnings.h int_range.h jacobi_polynomials.h \ + libmesh_nullptr.h location_maps.h mapvector.h \ + null_output_iterator.h number_lookups.h ostream_proxy.h \ + parameters.h perf_log.h perfmon.h plt_loader.h \ + point_locator_base.h point_locator_nanoflann.h \ + point_locator_tree.h pointer_to_pointer_iter.h \ + pool_allocator.h restore_warnings.h simple_range.h \ + statistics.h string_to_enum.h timestamp.h topology_map.h \ + tree.h tree_base.h tree_node.h utility.h vectormap.h \ + win_gettimeofday.h xdr_cxx.h \ parallel_communicator_specializations $(am__append_1) \ $(am__append_3) $(am__append_5) $(am__append_7) \ $(am__append_9) $(am__append_11) $(am__append_13) \ @@ -2273,6 +2274,9 @@ enum_to_string.h: $(top_srcdir)/include/utils/enum_to_string.h error_vector.h: $(top_srcdir)/include/utils/error_vector.h $(AM_V_GEN)rm -f $@ && $(LN_S) -f $< $@ +fuzzy_equal.h: $(top_srcdir)/include/utils/fuzzy_equal.h + $(AM_V_GEN)rm -f $@ && $(LN_S) -f $< $@ + hashing.h: $(top_srcdir)/include/utils/hashing.h $(AM_V_GEN)rm -f $@ && $(LN_S) -f $< $@ From 949dc40cffce6b85367695c4f2a6681a396a382f Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Mon, 1 Jul 2024 13:08:01 -0700 Subject: [PATCH 09/22] max_mesh_dim -> libmesh_dim --- include/base/libmesh_common.h | 2 +- include/utils/fuzzy_equal.h | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/base/libmesh_common.h b/include/base/libmesh_common.h index 81b825ed28e..7197fd74605 100644 --- a/include/base/libmesh_common.h +++ b/include/base/libmesh_common.h @@ -634,7 +634,7 @@ inline Tnew libmesh_cast_int (Told oldvar) template constexpr std::false_type always_false{}; -static constexpr std::size_t max_mesh_dim = LIBMESH_DIM; +static constexpr std::size_t libmesh_dim = LIBMESH_DIM; // build a integer representation of version #define LIBMESH_VERSION_ID(major,minor,patch) (((major) << 16) | ((minor) << 8) | ((patch) & 0xFF)) diff --git a/include/utils/fuzzy_equal.h b/include/utils/fuzzy_equal.h index f13b4c21f64..4f667d14784 100644 --- a/include/utils/fuzzy_equal.h +++ b/include/utils/fuzzy_equal.h @@ -107,7 +107,7 @@ relative_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE tol * (std::abs(MetaPhysicL::raw_value(var1)) + std::abs(MetaPhysicL::raw_value(var2)))); else if constexpr (TensorTools::TensorTraits::rank == 1) { - for (const auto i : make_range(max_mesh_dim)) + for (const auto i : make_range(libmesh_dim)) if (!relative_fuzzy_equal(var1(i), var2(i), tol)) return false; @@ -115,8 +115,8 @@ relative_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE } else if constexpr (TensorTools::TensorTraits::rank == 2) { - for (const auto i : make_range(max_mesh_dim)) - for (const auto j : make_range(max_mesh_dim)) + for (const auto i : make_range(libmesh_dim)) + for (const auto j : make_range(libmesh_dim)) if (!relative_fuzzy_equal(var1(i, j), var2(i, j), tol)) return false; @@ -168,7 +168,7 @@ relative_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE return absolute_fuzzy_equal(var1, var2, tol * (std::abs(var1) + std::abs(var2))); else if constexpr (TensorTools::TensorTraits::rank == 1) { - for (const auto i : make_range(max_mesh_dim)) + for (const auto i : make_range(libmesh_dim)) if (!relative_fuzzy_equal(var1(i), var2(i), tol)) return false; @@ -176,8 +176,8 @@ relative_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE } else if constexpr (TensorTools::TensorTraits::rank == 2) { - for (const auto i : make_range(max_mesh_dim)) - for (const auto j : make_range(max_mesh_dim)) + for (const auto i : make_range(libmesh_dim)) + for (const auto j : make_range(libmesh_dim)) if (!relative_fuzzy_equal(var1(i, j), var2(i, j), tol)) return false; From 3e5bba4707f61fd25e718109f474e1b31d229914 Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Mon, 1 Jul 2024 13:18:29 -0700 Subject: [PATCH 10/22] Clearly delineate between rel and abs tol in vec/mat comparisons --- include/numerics/numeric_vector.h | 8 ++++++-- include/numerics/petsc_matrix.h | 3 ++- include/numerics/sparse_matrix.h | 8 +++++--- src/numerics/numeric_vector.C | 9 ++++++--- src/numerics/petsc_matrix.C | 16 +++++++++------- src/numerics/sparse_matrix.C | 16 ++++++++-------- 6 files changed, 36 insertions(+), 24 deletions(-) diff --git a/include/numerics/numeric_vector.h b/include/numerics/numeric_vector.h index 6e6adb35486..e4b895ddc1b 100644 --- a/include/numerics/numeric_vector.h +++ b/include/numerics/numeric_vector.h @@ -787,9 +787,13 @@ class NumericVector : public ReferenceCountedObject>, bool compatible(const NumericVector & v) const; /** - * checks whether the vector \p v is fuzzy equal to this vector + * checks whether the vector \p v is fuzzy equal to this vector by doing element-wise comparisons. + * A given element will be deemed fuzzy equal if either a relative or absolute tolerance fuzzy + * equal comparison returns true */ - bool fuzzy_equal(const NumericVector & v, const Real tol = TOLERANCE) const; + bool fuzzy_equal(const NumericVector & v, + const Real rel_tol = TOLERANCE, + const Real abs_tol = TOLERANCE) const; protected: diff --git a/include/numerics/petsc_matrix.h b/include/numerics/petsc_matrix.h index afab0ac8e5a..494dc383501 100644 --- a/include/numerics/petsc_matrix.h +++ b/include/numerics/petsc_matrix.h @@ -350,7 +350,8 @@ class PetscMatrix final : public SparseMatrix const std::vector & cols) const override; virtual bool fuzzy_equal(const SparseMatrix & other, - const Real tol = TOLERANCE) const override; + const Real rel_tol = TOLERANCE, + const Real abs_tol = TOLERANCE) const override; protected: diff --git a/include/numerics/sparse_matrix.h b/include/numerics/sparse_matrix.h index e0b4240b1d3..4ea885ba039 100644 --- a/include/numerics/sparse_matrix.h +++ b/include/numerics/sparse_matrix.h @@ -573,12 +573,14 @@ class SparseMatrix : public ReferenceCountedObject>, std::vector & indices, std::vector & values) const = 0; - /** - * checks whether the matrix \p other is fuzzy equal to this matrix + * checks whether the matrix \p other is fuzzy equal to this matrix by doing element-wise + * comparisons. A given element will be deemed fuzzy equal if either a relative or absolute + * tolerance fuzzy equal comparison returns true */ virtual bool fuzzy_equal(const SparseMatrix & other, - const Real tol = TOLERANCE) const; + const Real rel_tol = TOLERANCE, + const Real abs_tol = TOLERANCE) const; protected: /** diff --git a/src/numerics/numeric_vector.C b/src/numerics/numeric_vector.C index 5cab731c547..7046f4ee93c 100644 --- a/src/numerics/numeric_vector.C +++ b/src/numerics/numeric_vector.C @@ -430,9 +430,11 @@ bool NumericVector::compatible (const NumericVector & v) const this->last_local_index() == v.last_local_index(); } - template -bool NumericVector::fuzzy_equal(const NumericVector & v, const Real tol) const +bool +NumericVector::fuzzy_equal(const NumericVector & v, + const Real rel_tol, + const Real abs_tol) const { bool equiv = true; if (this->local_size() != v.local_size()) @@ -441,7 +443,8 @@ bool NumericVector::fuzzy_equal(const NumericVector & v, const Real tol) c if (equiv) for (const auto i : make_range(this->first_local_index(), this->last_local_index())) { - if (relative_fuzzy_equal((*this)(i), v(i), tol) || absolute_fuzzy_equal((*this)(i), v(i), tol)) + if (relative_fuzzy_equal((*this)(i), v(i), rel_tol) || + absolute_fuzzy_equal((*this)(i), v(i), abs_tol)) continue; else { diff --git a/src/numerics/petsc_matrix.C b/src/numerics/petsc_matrix.C index 2d2a186d776..d1c4a2ee135 100644 --- a/src/numerics/petsc_matrix.C +++ b/src/numerics/petsc_matrix.C @@ -1580,7 +1580,9 @@ SparseMatrix & PetscMatrix::operator= (const SparseMatrix & v) template bool -PetscMatrix::fuzzy_equal(const SparseMatrix & other, const Real tol) const +PetscMatrix::fuzzy_equal(const SparseMatrix & other, + const Real rel_tol, + const Real abs_tol) const { const auto * const petsc_other = dynamic_cast *>(&other); if (!petsc_other) @@ -1598,7 +1600,7 @@ PetscMatrix::fuzzy_equal(const SparseMatrix & other, const Real tol) const if ((this->local_m() != other.local_m()) || (this->local_n() != other.local_n())) { equiv = false; - goto endLoop; + goto globalComm; } for (const auto i : make_range(this->row_start(), this->row_stop())) @@ -1644,7 +1646,7 @@ PetscMatrix::fuzzy_equal(const SparseMatrix & other, const Real tol) const if (ncols != ncols_other) { compared_false(); - goto endLoop; + goto globalComm; } // No need for fuzzy comparison here @@ -1655,19 +1657,19 @@ PetscMatrix::fuzzy_equal(const SparseMatrix & other, const Real tol) const compared_false(); for (const auto j_val : make_range(ncols)) - if (relative_fuzzy_equal(petsc_row[j_val], petsc_row_other[j_val], tol) || - absolute_fuzzy_equal(petsc_row[j_val], petsc_row_other[j_val], tol)) + if (relative_fuzzy_equal(petsc_row[j_val], petsc_row_other[j_val], rel_tol) || + absolute_fuzzy_equal(petsc_row[j_val], petsc_row_other[j_val], abs_tol)) continue; else { compared_false(); - goto endLoop; + goto globalComm; } restore_rows(); } -endLoop: +globalComm: this->comm().min(equiv); return equiv; diff --git a/src/numerics/sparse_matrix.C b/src/numerics/sparse_matrix.C index c2fd1a8325b..7858e5b1179 100644 --- a/src/numerics/sparse_matrix.C +++ b/src/numerics/sparse_matrix.C @@ -855,10 +855,11 @@ void SparseMatrix::read_petsc_hdf5(const std::string &) ("libMesh cannot read PETSc HDF5-format files into non-PETSc matrices"); } - - template -bool SparseMatrix::fuzzy_equal(const SparseMatrix & other, const Real tol) const +bool +SparseMatrix::fuzzy_equal(const SparseMatrix & other, + const Real rel_tol, + const Real abs_tol) const { bool equiv = true; if ((this->local_m() != other.local_m()) || (this->local_n() != other.local_n())) @@ -868,23 +869,22 @@ bool SparseMatrix::fuzzy_equal(const SparseMatrix & other, const Real tol) for (const auto i : make_range(this->row_start(), this->row_stop())) for (const auto j : make_range(this->col_start(), this->col_stop())) { - if (relative_fuzzy_equal((*this)(i, j), other(i, j), tol) || - absolute_fuzzy_equal((*this)(i, j), other(i, j), tol)) + if (relative_fuzzy_equal((*this)(i, j), other(i, j), rel_tol) || + absolute_fuzzy_equal((*this)(i, j), other(i, j), abs_tol)) continue; else { equiv = false; - goto endLoops; + goto globalComm; } } -endLoops: +globalComm: this->comm().min(equiv); return equiv; } - //------------------------------------------------------------------ // Explicit instantiations template class LIBMESH_EXPORT SparseMatrix; From 50057503430712e9be0d1060610beacb4b90275f Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Wed, 26 Jun 2024 06:30:10 -0700 Subject: [PATCH 11/22] Don't use petscarraycmp because of really old PETSc --- src/numerics/petsc_matrix.C | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/numerics/petsc_matrix.C b/src/numerics/petsc_matrix.C index d1c4a2ee135..7252f843062 100644 --- a/src/numerics/petsc_matrix.C +++ b/src/numerics/petsc_matrix.C @@ -34,8 +34,6 @@ #include "libmesh/wrapped_petsc.h" #include "libmesh/fuzzy_equal.h" -#include - // C++ includes #ifdef LIBMESH_HAVE_UNISTD_H #include // mkstemp @@ -1650,11 +1648,12 @@ PetscMatrix::fuzzy_equal(const SparseMatrix & other, } // No need for fuzzy comparison here - PetscBool petsc_equiv; - ierr = PetscArraycmp(petsc_cols, petsc_cols_other, ncols, &petsc_equiv); - LIBMESH_CHKERR(ierr); - if (petsc_equiv == PETSC_FALSE) - compared_false(); + for (const auto j_val : make_range(ncols)) + if (petsc_cols[j_val] != petsc_cols_other[j_val]) + { + compared_false(); + goto globalComm; + } for (const auto j_val : make_range(ncols)) if (relative_fuzzy_equal(petsc_row[j_val], petsc_row_other[j_val], rel_tol) || From 89362cc044e78bf5e7eecc7e2679bb0bebcd92e8 Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Tue, 2 Jul 2024 08:49:38 -0700 Subject: [PATCH 12/22] fuzzy_equal -> fuzzy_equals --- include/Makefile.in | 2 +- include/include_HEADERS | 2 +- include/libmesh/Makefile.am | 4 +-- include/libmesh/Makefile.in | 4 +-- include/numerics/numeric_vector.h | 6 ++-- include/numerics/petsc_matrix.h | 6 ++-- include/numerics/sparse_matrix.h | 6 ++-- .../utils/{fuzzy_equal.h => fuzzy_equals.h} | 32 +++++++++---------- src/numerics/numeric_vector.C | 12 +++---- src/numerics/petsc_matrix.C | 12 +++---- src/numerics/sparse_matrix.C | 12 +++---- 11 files changed, 49 insertions(+), 49 deletions(-) rename include/utils/{fuzzy_equal.h => fuzzy_equals.h} (84%) diff --git a/include/Makefile.in b/include/Makefile.in index e8c512255a8..56dc6c6766c 100644 --- a/include/Makefile.in +++ b/include/Makefile.in @@ -1053,7 +1053,7 @@ include_HEADERS = \ utils/compare_types.h \ utils/enum_to_string.h \ utils/error_vector.h \ - utils/fuzzy_equal.h \ + utils/fuzzy_equals.h \ utils/hashing.h \ utils/hashword.h \ utils/ignore_warnings.h \ diff --git a/include/include_HEADERS b/include/include_HEADERS index 148aa79fb22..7b4d9bc991c 100644 --- a/include/include_HEADERS +++ b/include/include_HEADERS @@ -458,7 +458,7 @@ include_HEADERS = \ utils/compare_types.h \ utils/enum_to_string.h \ utils/error_vector.h \ - utils/fuzzy_equal.h \ + utils/fuzzy_equals.h \ utils/hashing.h \ utils/hashword.h \ utils/ignore_warnings.h \ diff --git a/include/libmesh/Makefile.am b/include/libmesh/Makefile.am index 64a2836031b..7b3ce941807 100644 --- a/include/libmesh/Makefile.am +++ b/include/libmesh/Makefile.am @@ -454,7 +454,7 @@ BUILT_SOURCES = \ compare_types.h \ enum_to_string.h \ error_vector.h \ - fuzzy_equal.h \ + fuzzy_equals.h \ hashing.h \ hashword.h \ ignore_warnings.h \ @@ -1938,7 +1938,7 @@ enum_to_string.h: $(top_srcdir)/include/utils/enum_to_string.h error_vector.h: $(top_srcdir)/include/utils/error_vector.h $(AM_V_GEN)rm -f $@ && $(LN_S) -f $< $@ -fuzzy_equal.h: $(top_srcdir)/include/utils/fuzzy_equal.h +fuzzy_equals.h: $(top_srcdir)/include/utils/fuzzy_equals.h $(AM_V_GEN)rm -f $@ && $(LN_S) -f $< $@ hashing.h: $(top_srcdir)/include/utils/hashing.h diff --git a/include/libmesh/Makefile.in b/include/libmesh/Makefile.in index cbb8d939f41..fea8481b416 100644 --- a/include/libmesh/Makefile.in +++ b/include/libmesh/Makefile.in @@ -670,7 +670,7 @@ BUILT_SOURCES = auto_ptr.h dirichlet_boundaries.h dof_map.h \ post_wait_free_buffer.h post_wait_unpack_buffer.h \ post_wait_work.h request.h standard_type.h status.h \ chunked_mapvector.h compare_types.h enum_to_string.h \ - error_vector.h fuzzy_equal.h hashing.h hashword.h \ + error_vector.h fuzzy_equals.h hashing.h hashword.h \ ignore_warnings.h int_range.h jacobi_polynomials.h \ libmesh_nullptr.h location_maps.h mapvector.h \ null_output_iterator.h number_lookups.h ostream_proxy.h \ @@ -2274,7 +2274,7 @@ enum_to_string.h: $(top_srcdir)/include/utils/enum_to_string.h error_vector.h: $(top_srcdir)/include/utils/error_vector.h $(AM_V_GEN)rm -f $@ && $(LN_S) -f $< $@ -fuzzy_equal.h: $(top_srcdir)/include/utils/fuzzy_equal.h +fuzzy_equals.h: $(top_srcdir)/include/utils/fuzzy_equals.h $(AM_V_GEN)rm -f $@ && $(LN_S) -f $< $@ hashing.h: $(top_srcdir)/include/utils/hashing.h diff --git a/include/numerics/numeric_vector.h b/include/numerics/numeric_vector.h index e4b895ddc1b..2c62bce8ed2 100644 --- a/include/numerics/numeric_vector.h +++ b/include/numerics/numeric_vector.h @@ -791,9 +791,9 @@ class NumericVector : public ReferenceCountedObject>, * A given element will be deemed fuzzy equal if either a relative or absolute tolerance fuzzy * equal comparison returns true */ - bool fuzzy_equal(const NumericVector & v, - const Real rel_tol = TOLERANCE, - const Real abs_tol = TOLERANCE) const; + bool fuzzy_equals(const NumericVector & v, + const Real rel_tol = TOLERANCE, + const Real abs_tol = TOLERANCE) const; protected: diff --git a/include/numerics/petsc_matrix.h b/include/numerics/petsc_matrix.h index 494dc383501..fd3d0bc9b68 100644 --- a/include/numerics/petsc_matrix.h +++ b/include/numerics/petsc_matrix.h @@ -349,9 +349,9 @@ class PetscMatrix final : public SparseMatrix const std::vector & rows, const std::vector & cols) const override; - virtual bool fuzzy_equal(const SparseMatrix & other, - const Real rel_tol = TOLERANCE, - const Real abs_tol = TOLERANCE) const override; + virtual bool fuzzy_equals(const SparseMatrix & other, + const Real rel_tol = TOLERANCE, + const Real abs_tol = TOLERANCE) const override; protected: diff --git a/include/numerics/sparse_matrix.h b/include/numerics/sparse_matrix.h index 4ea885ba039..f76cf8235f5 100644 --- a/include/numerics/sparse_matrix.h +++ b/include/numerics/sparse_matrix.h @@ -578,9 +578,9 @@ class SparseMatrix : public ReferenceCountedObject>, * comparisons. A given element will be deemed fuzzy equal if either a relative or absolute * tolerance fuzzy equal comparison returns true */ - virtual bool fuzzy_equal(const SparseMatrix & other, - const Real rel_tol = TOLERANCE, - const Real abs_tol = TOLERANCE) const; + virtual bool fuzzy_equals(const SparseMatrix & other, + const Real rel_tol = TOLERANCE, + const Real abs_tol = TOLERANCE) const; protected: /** diff --git a/include/utils/fuzzy_equal.h b/include/utils/fuzzy_equals.h similarity index 84% rename from include/utils/fuzzy_equal.h rename to include/utils/fuzzy_equals.h index 4f667d14784..98107274956 100644 --- a/include/utils/fuzzy_equal.h +++ b/include/utils/fuzzy_equals.h @@ -38,7 +38,7 @@ template ::value && ScalarTraits::value && ScalarTraits::value, int>::type = 0> -bool absolute_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE * TOLERANCE); +bool absolute_fuzzy_equals(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE * TOLERANCE); /** * Function to check whether both the real and imaginary parts of two complex variables are equal @@ -53,12 +53,12 @@ template ::value, int>::type = 0> bool -absolute_fuzzy_equal(const std::complex & var1, +absolute_fuzzy_equals(const std::complex & var1, const std::complex & var2, const T3 & tol = TOLERANCE * TOLERANCE) { - return absolute_fuzzy_equal(var1.real(), var2.real(), tol) && - absolute_fuzzy_equal(var1.imag(), var2.imag(), tol); + return absolute_fuzzy_equals(var1.real(), var2.real(), tol) && + absolute_fuzzy_equals(var1.imag(), var2.imag(), tol); } #ifdef LIBMESH_HAVE_METAPHYSICL @@ -79,7 +79,7 @@ template ::value, int>::type> bool -absolute_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol) +absolute_fuzzy_equals(const T & var1, const T2 & var2, const T3 & tol) { return (std::abs(MetaPhysicL::raw_value(var1) - MetaPhysicL::raw_value(var2)) <= MetaPhysicL::raw_value(tol)); @@ -94,21 +94,21 @@ absolute_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol) */ template bool -relative_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE * TOLERANCE) +relative_fuzzy_equals(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE * TOLERANCE) { if constexpr (ScalarTraits::value || TensorTools::MathWrapperTraits::value) { static_assert(TensorTools::TensorTraits::rank == TensorTools::TensorTraits::rank, "Mathematical types must be same for arguments to relativelyFuzzEqual"); if constexpr (TensorTools::TensorTraits::rank == 0) - return absolute_fuzzy_equal( + return absolute_fuzzy_equals( var1, var2, tol * (std::abs(MetaPhysicL::raw_value(var1)) + std::abs(MetaPhysicL::raw_value(var2)))); else if constexpr (TensorTools::TensorTraits::rank == 1) { for (const auto i : make_range(libmesh_dim)) - if (!relative_fuzzy_equal(var1(i), var2(i), tol)) + if (!relative_fuzzy_equals(var1(i), var2(i), tol)) return false; return true; @@ -117,7 +117,7 @@ relative_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE { for (const auto i : make_range(libmesh_dim)) for (const auto j : make_range(libmesh_dim)) - if (!relative_fuzzy_equal(var1(i, j), var2(i, j), tol)) + if (!relative_fuzzy_equals(var1(i, j), var2(i, j), tol)) return false; return true; @@ -128,7 +128,7 @@ relative_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE // We dare to dream libmesh_assert(var1.size() == var2.size()); for (const auto i : index_range(var1)) - if (!relative_fuzzy_equal(var1(i), var2(i), tol)) + if (!relative_fuzzy_equals(var1(i), var2(i), tol)) return false; return true; @@ -144,7 +144,7 @@ template ::value, int>::type> bool -absolute_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol) +absolute_fuzzy_equals(const T & var1, const T2 & var2, const T3 & tol) { return (std::abs(var1 - var2) <= tol); } @@ -158,18 +158,18 @@ absolute_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol) */ template bool -relative_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE * TOLERANCE) +relative_fuzzy_equals(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE * TOLERANCE) { if constexpr (ScalarTraits::value || TensorTools::MathWrapperTraits::value) { static_assert(TensorTools::TensorTraits::rank == TensorTools::TensorTraits::rank, "Mathematical types must be same for arguments to relativelyFuzzEqual"); if constexpr (TensorTools::TensorTraits::rank == 0) - return absolute_fuzzy_equal(var1, var2, tol * (std::abs(var1) + std::abs(var2))); + return absolute_fuzzy_equals(var1, var2, tol * (std::abs(var1) + std::abs(var2))); else if constexpr (TensorTools::TensorTraits::rank == 1) { for (const auto i : make_range(libmesh_dim)) - if (!relative_fuzzy_equal(var1(i), var2(i), tol)) + if (!relative_fuzzy_equals(var1(i), var2(i), tol)) return false; return true; @@ -178,7 +178,7 @@ relative_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE { for (const auto i : make_range(libmesh_dim)) for (const auto j : make_range(libmesh_dim)) - if (!relative_fuzzy_equal(var1(i, j), var2(i, j), tol)) + if (!relative_fuzzy_equals(var1(i, j), var2(i, j), tol)) return false; return true; @@ -189,7 +189,7 @@ relative_fuzzy_equal(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE // We dare to dream mooseAssert(var1.size() == var2.size(), "These must be the same size"); for (const auto i : index_range(var1)) - if (!relative_fuzzy_equal(var1(i), var2(i), tol)) + if (!relative_fuzzy_equals(var1(i), var2(i), tol)) return false; return true; diff --git a/src/numerics/numeric_vector.C b/src/numerics/numeric_vector.C index 7046f4ee93c..018892663a0 100644 --- a/src/numerics/numeric_vector.C +++ b/src/numerics/numeric_vector.C @@ -27,7 +27,7 @@ #include "libmesh/tensor_tools.h" #include "libmesh/enum_solver_package.h" #include "libmesh/int_range.h" -#include "libmesh/fuzzy_equal.h" +#include "libmesh/fuzzy_equals.h" // C++ includes @@ -432,9 +432,9 @@ bool NumericVector::compatible (const NumericVector & v) const template bool -NumericVector::fuzzy_equal(const NumericVector & v, - const Real rel_tol, - const Real abs_tol) const +NumericVector::fuzzy_equals(const NumericVector & v, + const Real rel_tol, + const Real abs_tol) const { bool equiv = true; if (this->local_size() != v.local_size()) @@ -443,8 +443,8 @@ NumericVector::fuzzy_equal(const NumericVector & v, if (equiv) for (const auto i : make_range(this->first_local_index(), this->last_local_index())) { - if (relative_fuzzy_equal((*this)(i), v(i), rel_tol) || - absolute_fuzzy_equal((*this)(i), v(i), abs_tol)) + if (relative_fuzzy_equals((*this)(i), v(i), rel_tol) || + absolute_fuzzy_equals((*this)(i), v(i), abs_tol)) continue; else { diff --git a/src/numerics/petsc_matrix.C b/src/numerics/petsc_matrix.C index 7252f843062..1a2a22553c5 100644 --- a/src/numerics/petsc_matrix.C +++ b/src/numerics/petsc_matrix.C @@ -32,7 +32,7 @@ #include "libmesh/parallel.h" #include "libmesh/utility.h" #include "libmesh/wrapped_petsc.h" -#include "libmesh/fuzzy_equal.h" +#include "libmesh/fuzzy_equals.h" // C++ includes #ifdef LIBMESH_HAVE_UNISTD_H @@ -1578,9 +1578,9 @@ SparseMatrix & PetscMatrix::operator= (const SparseMatrix & v) template bool -PetscMatrix::fuzzy_equal(const SparseMatrix & other, - const Real rel_tol, - const Real abs_tol) const +PetscMatrix::fuzzy_equals(const SparseMatrix & other, + const Real rel_tol, + const Real abs_tol) const { const auto * const petsc_other = dynamic_cast *>(&other); if (!petsc_other) @@ -1656,8 +1656,8 @@ PetscMatrix::fuzzy_equal(const SparseMatrix & other, } for (const auto j_val : make_range(ncols)) - if (relative_fuzzy_equal(petsc_row[j_val], petsc_row_other[j_val], rel_tol) || - absolute_fuzzy_equal(petsc_row[j_val], petsc_row_other[j_val], abs_tol)) + if (relative_fuzzy_equals(petsc_row[j_val], petsc_row_other[j_val], rel_tol) || + absolute_fuzzy_equals(petsc_row[j_val], petsc_row_other[j_val], abs_tol)) continue; else { diff --git a/src/numerics/sparse_matrix.C b/src/numerics/sparse_matrix.C index 7858e5b1179..19b4d498c4d 100644 --- a/src/numerics/sparse_matrix.C +++ b/src/numerics/sparse_matrix.C @@ -32,7 +32,7 @@ #include "libmesh/trilinos_epetra_matrix.h" #include "libmesh/numeric_vector.h" #include "libmesh/enum_solver_package.h" -#include "libmesh/fuzzy_equal.h" +#include "libmesh/fuzzy_equals.h" // gzstream for reading compressed files as a stream @@ -857,9 +857,9 @@ void SparseMatrix::read_petsc_hdf5(const std::string &) template bool -SparseMatrix::fuzzy_equal(const SparseMatrix & other, - const Real rel_tol, - const Real abs_tol) const +SparseMatrix::fuzzy_equals(const SparseMatrix & other, + const Real rel_tol, + const Real abs_tol) const { bool equiv = true; if ((this->local_m() != other.local_m()) || (this->local_n() != other.local_n())) @@ -869,8 +869,8 @@ SparseMatrix::fuzzy_equal(const SparseMatrix & other, for (const auto i : make_range(this->row_start(), this->row_stop())) for (const auto j : make_range(this->col_start(), this->col_stop())) { - if (relative_fuzzy_equal((*this)(i, j), other(i, j), rel_tol) || - absolute_fuzzy_equal((*this)(i, j), other(i, j), abs_tol)) + if (relative_fuzzy_equals((*this)(i, j), other(i, j), rel_tol) || + absolute_fuzzy_equals((*this)(i, j), other(i, j), abs_tol)) continue; else { From 52a5ebea7ce8571068cc6b95cd40897b079b3ac0 Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Tue, 2 Jul 2024 09:33:43 -0700 Subject: [PATCH 13/22] Unit test Also introduce a `scale` API for `SparseMatrix` --- include/numerics/petsc_matrix.h | 2 ++ include/numerics/sparse_matrix.h | 5 +++++ src/numerics/petsc_matrix.C | 9 +++++++++ src/numerics/sparse_matrix.C | 14 ++++++++++++++ tests/numerics/numeric_vector_test.h | 4 ++++ tests/numerics/sparse_matrix_test.h | 5 +++++ 6 files changed, 39 insertions(+) diff --git a/include/numerics/petsc_matrix.h b/include/numerics/petsc_matrix.h index fd3d0bc9b68..06f73d506b1 100644 --- a/include/numerics/petsc_matrix.h +++ b/include/numerics/petsc_matrix.h @@ -353,6 +353,8 @@ class PetscMatrix final : public SparseMatrix const Real rel_tol = TOLERANCE, const Real abs_tol = TOLERANCE) const override; + virtual void scale(const T scale) override; + protected: /** diff --git a/include/numerics/sparse_matrix.h b/include/numerics/sparse_matrix.h index f76cf8235f5..f8d6649c507 100644 --- a/include/numerics/sparse_matrix.h +++ b/include/numerics/sparse_matrix.h @@ -582,6 +582,11 @@ class SparseMatrix : public ReferenceCountedObject>, const Real rel_tol = TOLERANCE, const Real abs_tol = TOLERANCE) const; + /** + * Scales all elements of this matrix by \p scale + */ + virtual void scale(const T scale); + protected: /** * Protected implementation of the create_submatrix and reinit_submatrix diff --git a/src/numerics/petsc_matrix.C b/src/numerics/petsc_matrix.C index 1a2a22553c5..d893ba1090e 100644 --- a/src/numerics/petsc_matrix.C +++ b/src/numerics/petsc_matrix.C @@ -1674,6 +1674,15 @@ globalComm: return equiv; } +template +void PetscMatrix::scale(const T scale) +{ + libmesh_assert(this->closed()); + + const auto ierr = MatScale(this->_mat, scale); + LIBMESH_CHKERR(ierr); +} + //------------------------------------------------------------------ // Explicit instantiations template class LIBMESH_EXPORT PetscMatrix; diff --git a/src/numerics/sparse_matrix.C b/src/numerics/sparse_matrix.C index 19b4d498c4d..c4d68d1480d 100644 --- a/src/numerics/sparse_matrix.C +++ b/src/numerics/sparse_matrix.C @@ -855,6 +855,8 @@ void SparseMatrix::read_petsc_hdf5(const std::string &) ("libMesh cannot read PETSc HDF5-format files into non-PETSc matrices"); } + + template bool SparseMatrix::fuzzy_equals(const SparseMatrix & other, @@ -885,6 +887,18 @@ globalComm: return equiv; } + + +template +void SparseMatrix::scale(const T scale) +{ + libmesh_assert(this->closed()); + + for (const auto i : make_range(this->row_start(), this->row_stop())) + for (const auto j : make_range(this->col_start(), this->col_stop())) + this->set(i, j, (*this)(i, j) * scale); +} + //------------------------------------------------------------------ // Explicit instantiations template class LIBMESH_EXPORT SparseMatrix; diff --git a/tests/numerics/numeric_vector_test.h b/tests/numerics/numeric_vector_test.h index 372e849f4d5..8037ef83b12 100644 --- a/tests/numerics/numeric_vector_test.h +++ b/tests/numerics/numeric_vector_test.h @@ -74,8 +74,12 @@ class NumericVectorTest : public CppUnit::TestCase { auto v_clone = v.clone(); auto & vorig = *v_clone; + CPPUNIT_ASSERT(v.fuzzy_equals(vorig)); + v += v; + CPPUNIT_ASSERT(!v.fuzzy_equals(vorig)); + for (libMesh::dof_id_type n=first; n != last; n++) LIBMESH_ASSERT_FP_EQUAL(libMesh::libmesh_real(v(n)), libMesh::Real(2*n+2), diff --git a/tests/numerics/sparse_matrix_test.h b/tests/numerics/sparse_matrix_test.h index 52228af014c..a9c4c9b98b9 100644 --- a/tests/numerics/sparse_matrix_test.h +++ b/tests/numerics/sparse_matrix_test.h @@ -223,6 +223,8 @@ class SparseMatrixTest : public CppUnit::TestCase { LOG_UNIT_TEST; + setValues(); + // Matrix must be closed before it can be cloned. matrix->close(); @@ -239,6 +241,9 @@ class SparseMatrixTest : public CppUnit::TestCase // Check that copy has same values as original LIBMESH_ASSERT_FP_EQUAL(copy->l1_norm(), matrix->l1_norm(), _tolerance); + CPPUNIT_ASSERT(matrix->fuzzy_equals(*copy)); + copy->scale(2); + CPPUNIT_ASSERT(!matrix->fuzzy_equals(*copy)); } { From 73667cedb663b60c046786fb3ba1e84bc37a2716 Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Mon, 15 Jul 2024 11:02:42 -0700 Subject: [PATCH 14/22] Just use Real for tolerancing --- include/utils/fuzzy_equals.h | 40 +++++++++++++----------------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/include/utils/fuzzy_equals.h b/include/utils/fuzzy_equals.h index 98107274956..c71e23e208b 100644 --- a/include/utils/fuzzy_equals.h +++ b/include/utils/fuzzy_equals.h @@ -34,28 +34,22 @@ namespace libMesh */ template ::value && ScalarTraits::value && - ScalarTraits::value, - int>::type = 0> -bool absolute_fuzzy_equals(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE * TOLERANCE); + typename std::enable_if::value && ScalarTraits::value, int>::type = 0> +bool absolute_fuzzy_equals(const T & var1, const T2 & var2, const Real tol = TOLERANCE * TOLERANCE); /** * Function to check whether both the real and imaginary parts of two complex variables are equal * within an absolute tolerance * @param var1 The first complex variable to be checked * @param var2 The second complex variable to be checked - * @param tol The real tolerance to be used for comparing both real and imgainary parts + * @param tol The real tolerance to be used for comparing both real and imaginary parts * @return true if both real and imginary parts are equal within tol */ -template ::value, int>::type = 0> +template bool absolute_fuzzy_equals(const std::complex & var1, - const std::complex & var2, - const T3 & tol = TOLERANCE * TOLERANCE) + const std::complex & var2, + const Real tol = TOLERANCE * TOLERANCE) { return absolute_fuzzy_equals(var1.real(), var2.real(), tol) && absolute_fuzzy_equals(var1.imag(), var2.imag(), tol); @@ -74,12 +68,9 @@ absolute_fuzzy_equals(const std::complex & var1, */ template ::value && ScalarTraits::value && - ScalarTraits::value, - int>::type> + typename std::enable_if::value && ScalarTraits::value, int>::type> bool -absolute_fuzzy_equals(const T & var1, const T2 & var2, const T3 & tol) +absolute_fuzzy_equals(const T & var1, const T2 & var2, const Real tol) { return (std::abs(MetaPhysicL::raw_value(var1) - MetaPhysicL::raw_value(var2)) <= MetaPhysicL::raw_value(tol)); @@ -92,9 +83,9 @@ absolute_fuzzy_equals(const T & var1, const T2 & var2, const T3 & tol) * @param tol The relative tolerance to be used * @return true if var1 and var2 are equal within relative tol */ -template +template bool -relative_fuzzy_equals(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE * TOLERANCE) +relative_fuzzy_equals(const T & var1, const T2 & var2, const Real tol = TOLERANCE * TOLERANCE) { if constexpr (ScalarTraits::value || TensorTools::MathWrapperTraits::value) { @@ -139,12 +130,9 @@ relative_fuzzy_equals(const T & var1, const T2 & var2, const T3 & tol = TOLERANC template ::value && ScalarTraits::value && - ScalarTraits::value, - int>::type> + typename std::enable_if::value && ScalarTraits::value, int>::type> bool -absolute_fuzzy_equals(const T & var1, const T2 & var2, const T3 & tol) +absolute_fuzzy_equals(const T & var1, const T2 & var2, const Real tol) { return (std::abs(var1 - var2) <= tol); } @@ -156,9 +144,9 @@ absolute_fuzzy_equals(const T & var1, const T2 & var2, const T3 & tol) * @param tol The relative tolerance to be used * @return true if var1 and var2 are equal within relative tol */ -template +template bool -relative_fuzzy_equals(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE * TOLERANCE) +relative_fuzzy_equals(const T & var1, const T2 & var2, const Real tol = TOLERANCE * TOLERANCE) { if constexpr (ScalarTraits::value || TensorTools::MathWrapperTraits::value) { From 65d7c4b256c9ffd069f5fe1e3fa2b38c1e3b5539 Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Mon, 15 Jul 2024 13:01:23 -0700 Subject: [PATCH 15/22] Refactor TypeVector::*fuzzy_equals --- include/numerics/type_vector.h | 86 ++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 34 deletions(-) diff --git a/include/numerics/type_vector.h b/include/numerics/type_vector.h index b6de64921ce..c3934f5cb2a 100644 --- a/include/numerics/type_vector.h +++ b/include/numerics/type_vector.h @@ -24,6 +24,7 @@ #include "libmesh/libmesh_common.h" #include "libmesh/compare_types.h" #include "libmesh/tensor_tools.h" +#include "libmesh/int_range.h" // C++ includes #include // *must* precede for proper std:abs() on PGI, Sun Studio CC @@ -73,6 +74,8 @@ namespace std { template auto norm(const libMesh::TypeVector & vector) -> decltype(std::norm(T())); +template +auto abs(const libMesh::TypeVector & vector) -> decltype(std::abs(T())); } namespace libMesh @@ -333,6 +336,19 @@ class TypeVector */ auto norm_sq() const -> decltype(std::norm(T())); + /** + * \returns The L1 norm of the vector + */ + auto l1_norm() const -> decltype(std::abs(T())); + + /** + * \returns The \f$ \ell_1 \f$-norm of \f$ \vec{u} - \vec{v} \f$, where + * \f$ \vec{u} \f$ is \p this. + */ + template + auto l1_norm_diff(const TypeVector & other_vec) const + -> decltype(std::abs(typename CompareTypes::supertype{})); + /** * \returns True if all values in the vector are zero */ @@ -437,6 +453,9 @@ class TypeVector * The coordinates of the \p TypeVector. */ T _coords[LIBMESH_DIM]; + + template + friend class TypeVector; }; @@ -975,51 +994,43 @@ bool TypeVector::is_zero() const } template -inline -bool TypeVector::absolute_fuzzy_equals(const TypeVector & rhs, Real tol) const +auto +TypeVector::l1_norm() const -> decltype(std::abs(T())) { -#if LIBMESH_DIM == 1 - return (std::abs(_coords[0] - rhs._coords[0]) - <= tol); -#endif + decltype(std::abs(T())) ret{}; + for (const auto i : make_range(libmesh_dim)) + ret += std::abs(_coords[i]); -#if LIBMESH_DIM == 2 - return (std::abs(_coords[0] - rhs._coords[0]) + - std::abs(_coords[1] - rhs._coords[1]) - <= tol); -#endif - -#if LIBMESH_DIM == 3 - return (std::abs(_coords[0] - rhs._coords[0]) + - std::abs(_coords[1] - rhs._coords[1]) + - std::abs(_coords[2] - rhs._coords[2]) - <= tol); -#endif + return ret; } +template +template +auto +TypeVector::l1_norm_diff(const TypeVector & other_vec) const + -> decltype(std::abs(typename CompareTypes::supertype{})) +{ + decltype(std::abs(typename CompareTypes::supertype{})) ret{}; + for (const auto i : make_range(libmesh_dim)) + ret += std::abs(_coords[i] - other_vec._coords[i]); + return ret; +} template inline -bool TypeVector::relative_fuzzy_equals(const TypeVector & rhs, Real tol) const +bool TypeVector::absolute_fuzzy_equals(const TypeVector & rhs, Real tol) const { -#if LIBMESH_DIM == 1 - return this->absolute_fuzzy_equals(rhs, tol * - (std::abs(_coords[0]) + std::abs(rhs._coords[0]))); -#endif + return this->l1_norm_diff(rhs) <= tol; +} -#if LIBMESH_DIM == 2 - return this->absolute_fuzzy_equals(rhs, tol * - (std::abs(_coords[0]) + std::abs(rhs._coords[0]) + - std::abs(_coords[1]) + std::abs(rhs._coords[1]))); -#endif -#if LIBMESH_DIM == 3 - return this->absolute_fuzzy_equals(rhs, tol * - (std::abs(_coords[0]) + std::abs(rhs._coords[0]) + - std::abs(_coords[1]) + std::abs(rhs._coords[1]) + - std::abs(_coords[2]) + std::abs(rhs._coords[2]))); -#endif + +template +inline +bool TypeVector::relative_fuzzy_equals(const TypeVector & rhs, Real tol) const +{ + return this->absolute_fuzzy_equals(rhs, tol * (this->l1_norm() + rhs.l1_norm())); } @@ -1181,6 +1192,13 @@ namespace std { template auto norm(const libMesh::TypeVector & vector) -> decltype(std::norm(T())) +{ + // Yea I agree it's dumb that the standard returns the square of the Euclidean norm + return vector.norm_sq(); +} + +template +auto abs(const libMesh::TypeVector & vector) -> decltype(std::abs(T())) { return vector.norm(); } From 97f633c2b50ede45c523b749f9baf1548b53727c Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Mon, 15 Jul 2024 13:01:56 -0700 Subject: [PATCH 16/22] Simplify logic in fuzzy_equals.h Developers should overload `libMesh::l1_norm` and `libMesh::l1_norm_diff` for their numeric types and then they automatically get correct application of `libMesh::*fuzzy_equals` --- include/numerics/type_vector.h | 15 +++ include/utils/fuzzy_equals.h | 160 ++++++++++-------------------- tests/numerics/type_vector_test.h | 3 + 3 files changed, 68 insertions(+), 110 deletions(-) diff --git a/include/numerics/type_vector.h b/include/numerics/type_vector.h index c3934f5cb2a..b4a0e102771 100644 --- a/include/numerics/type_vector.h +++ b/include/numerics/type_vector.h @@ -1186,6 +1186,21 @@ outer_product(const TypeVector & a, const T2 & b) return ret; } + +template +auto +l1_norm(const TypeVector & var) -> decltype(var.l1_norm()) +{ + return var.l1_norm(); +} + +template +auto +l1_norm_diff(const TypeVector & var) -> decltype(var.l1_norm_diff()) +{ + return var.l1_norm_diff(); +} + } // namespace libMesh namespace std diff --git a/include/utils/fuzzy_equals.h b/include/utils/fuzzy_equals.h index c71e23e208b..2c7a5482521 100644 --- a/include/utils/fuzzy_equals.h +++ b/include/utils/fuzzy_equals.h @@ -23,42 +23,49 @@ #include "libmesh/compare_types.h" #include "libmesh/int_range.h" +#ifdef LIBMESH_HAVE_METAPHYSICL +#include "metaphysicl/raw_type.h" +#endif + namespace libMesh { /** - * Function to check whether two variables are equal within an absolute tolerance - * @param var1 The first variable to be checked - * @param var2 The second variable to be checked - * @param tol The tolerance to be used - * @return true if var1 and var2 are equal within tol + * Computes the L1 norm */ -template ::value && ScalarTraits::value, int>::type = 0> -bool absolute_fuzzy_equals(const T & var1, const T2 & var2, const Real tol = TOLERANCE * TOLERANCE); +template +auto +l1_norm(const T & var) -> decltype(std::abs(T{})) +{ + return std::abs(var); +} + +template +auto +l1_norm(const std::complex & var) -> decltype(std::abs(T{})) +{ + return std::abs(var.real()) + std::abs(var.imag()); +} /** - * Function to check whether both the real and imaginary parts of two complex variables are equal - * within an absolute tolerance - * @param var1 The first complex variable to be checked - * @param var2 The second complex variable to be checked - * @param tol The real tolerance to be used for comparing both real and imaginary parts - * @return true if both real and imginary parts are equal within tol + * Computes the L1 norm of the diff between \p var1 and \p var2 */ template -bool -absolute_fuzzy_equals(const std::complex & var1, - const std::complex & var2, - const Real tol = TOLERANCE * TOLERANCE) +auto +l1_norm_diff(const T & var1, + const T2 & var2) -> decltype(std::abs(typename CompareTypes::supertype{})) { - return absolute_fuzzy_equals(var1.real(), var2.real(), tol) && - absolute_fuzzy_equals(var1.imag(), var2.imag(), tol); + return std::abs(var1 - var2); } -#ifdef LIBMESH_HAVE_METAPHYSICL - -#include "metaphysicl/raw_type.h" +template +auto +l1_norm_diff(const std::complex & var1, const std::complex & var2) + -> decltype(std::abs(typename CompareTypes::supertype{})) +{ + return std::abs(var1.real() - var2.real()) + std::abs(var1.imag() - var2.imag()); +} +#ifdef LIBMESH_HAVE_METAPHYSICL /** * Function to check whether two variables are equal within an absolute tolerance * @param var1 The first variable to be checked @@ -66,14 +73,11 @@ absolute_fuzzy_equals(const std::complex & var1, * @param tol The tolerance to be used * @return true if var1 and var2 are equal within tol */ -template ::value && ScalarTraits::value, int>::type> +template bool -absolute_fuzzy_equals(const T & var1, const T2 & var2, const Real tol) +absolute_fuzzy_equals(const T & var1, const T2 & var2, const Real tol = TOLERANCE * TOLERANCE) { - return (std::abs(MetaPhysicL::raw_value(var1) - MetaPhysicL::raw_value(var2)) <= - MetaPhysicL::raw_value(tol)); + return MetaPhysicL::raw_value(l1_norm_diff(var1, var2)) <= tol; } /** @@ -87,54 +91,24 @@ template bool relative_fuzzy_equals(const T & var1, const T2 & var2, const Real tol = TOLERANCE * TOLERANCE) { - if constexpr (ScalarTraits::value || TensorTools::MathWrapperTraits::value) - { - static_assert(TensorTools::TensorTraits::rank == TensorTools::TensorTraits::rank, - "Mathematical types must be same for arguments to relativelyFuzzEqual"); - if constexpr (TensorTools::TensorTraits::rank == 0) - return absolute_fuzzy_equals( - var1, - var2, - tol * (std::abs(MetaPhysicL::raw_value(var1)) + std::abs(MetaPhysicL::raw_value(var2)))); - else if constexpr (TensorTools::TensorTraits::rank == 1) - { - for (const auto i : make_range(libmesh_dim)) - if (!relative_fuzzy_equals(var1(i), var2(i), tol)) - return false; - - return true; - } - else if constexpr (TensorTools::TensorTraits::rank == 2) - { - for (const auto i : make_range(libmesh_dim)) - for (const auto j : make_range(libmesh_dim)) - if (!relative_fuzzy_equals(var1(i, j), var2(i, j), tol)) - return false; - - return true; - } - } - else - { - // We dare to dream - libmesh_assert(var1.size() == var2.size()); - for (const auto i : index_range(var1)) - if (!relative_fuzzy_equals(var1(i), var2(i), tol)) - return false; - - return true; - } + return absolute_fuzzy_equals( + var1, + var2, + tol * (MetaPhysicL::raw_value(l1_norm(var1)) + MetaPhysicL::raw_value(l1_norm(var2)))); } - #else - -template ::value && ScalarTraits::value, int>::type> +/** + * Function to check whether two variables are equal within an absolute tolerance + * @param var1 The first variable to be checked + * @param var2 The second variable to be checked + * @param tol The tolerance to be used + * @return true if var1 and var2 are equal within tol + */ +template bool -absolute_fuzzy_equals(const T & var1, const T2 & var2, const Real tol) +absolute_fuzzy_equals(const T & var1, const T2 & var2, const Real tol = TOLERANCE * TOLERANCE) { - return (std::abs(var1 - var2) <= tol); + return l1_norm_diff(var1, var2) <= tol; } /** @@ -148,43 +122,9 @@ template bool relative_fuzzy_equals(const T & var1, const T2 & var2, const Real tol = TOLERANCE * TOLERANCE) { - if constexpr (ScalarTraits::value || TensorTools::MathWrapperTraits::value) - { - static_assert(TensorTools::TensorTraits::rank == TensorTools::TensorTraits::rank, - "Mathematical types must be same for arguments to relativelyFuzzEqual"); - if constexpr (TensorTools::TensorTraits::rank == 0) - return absolute_fuzzy_equals(var1, var2, tol * (std::abs(var1) + std::abs(var2))); - else if constexpr (TensorTools::TensorTraits::rank == 1) - { - for (const auto i : make_range(libmesh_dim)) - if (!relative_fuzzy_equals(var1(i), var2(i), tol)) - return false; - - return true; - } - else if constexpr (TensorTools::TensorTraits::rank == 2) - { - for (const auto i : make_range(libmesh_dim)) - for (const auto j : make_range(libmesh_dim)) - if (!relative_fuzzy_equals(var1(i, j), var2(i, j), tol)) - return false; - - return true; - } - } - else - { - // We dare to dream - mooseAssert(var1.size() == var2.size(), "These must be the same size"); - for (const auto i : index_range(var1)) - if (!relative_fuzzy_equals(var1(i), var2(i), tol)) - return false; - - return true; - } + return absolute_fuzzy_equals(var1, var2, tol * (l1_norm(var1) + l1_norm(var2))); } - -#endif // !LIBMESH_HAVE_METAPHYSICL +#endif } // namespace libMesh diff --git a/tests/numerics/type_vector_test.h b/tests/numerics/type_vector_test.h index e1a97940ecf..381d9100460 100644 --- a/tests/numerics/type_vector_test.h +++ b/tests/numerics/type_vector_test.h @@ -2,6 +2,7 @@ #define TYPE_VECTOR_TEST_H #include +#include #include "libmesh_cppunit.h" @@ -364,6 +365,7 @@ class TypeVectorTestBase : public CppUnit::TestCase { CPPUNIT_ASSERT( (*basem_1_1_1) == (*basem_1_1_1) ); CPPUNIT_ASSERT( !((*basem_1_1_1) == (*basem_n1_1_n1)) ); + CPPUNIT_ASSERT( relative_fuzzy_equals(*basem_1_1_1, *basem_1_1_1) ); } void testInEqualityBase() @@ -372,6 +374,7 @@ class TypeVectorTestBase : public CppUnit::TestCase { CPPUNIT_ASSERT( !((*basem_1_1_1) != (*basem_1_1_1)) ); CPPUNIT_ASSERT( (*basem_1_1_1) != (*basem_n1_1_n1) ); + CPPUNIT_ASSERT( !relative_fuzzy_equals(*basem_1_1_1, *basem_n1_1_n1) ); } void testAssignmentBase() From 5cf023e72c94acc6c8d3e22c6d51e5b48293e111 Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Mon, 15 Jul 2024 13:45:09 -0700 Subject: [PATCH 17/22] Change for global data structures --- include/numerics/numeric_vector.h | 33 +++++++--- include/numerics/petsc_matrix.h | 4 -- include/numerics/sparse_matrix.h | 28 +++++--- include/numerics/type_vector.h | 37 ++--------- include/utils/fuzzy_equals.h | 23 +++---- src/numerics/numeric_vector.C | 44 +++++-------- src/numerics/petsc_matrix.C | 99 ---------------------------- src/numerics/sparse_matrix.C | 41 +++--------- tests/numerics/numeric_vector_test.h | 5 +- tests/numerics/sparse_matrix_test.h | 5 +- 10 files changed, 88 insertions(+), 231 deletions(-) diff --git a/include/numerics/numeric_vector.h b/include/numerics/numeric_vector.h index 2c62bce8ed2..1e83952ccae 100644 --- a/include/numerics/numeric_vector.h +++ b/include/numerics/numeric_vector.h @@ -314,6 +314,12 @@ class NumericVector : public ReferenceCountedObject>, */ Real l2_norm_diff (const NumericVector & other_vec) const; + /** + * \returns The \f$ \ell_1 \f$-norm of \f$ \vec{u} - \vec{v} \f$, where + * \f$ \vec{u} \f$ is \p this. + */ + Real l1_norm_diff (const NumericVector & other_vec) const; + /** * \returns The size of the vector. */ @@ -786,15 +792,6 @@ class NumericVector : public ReferenceCountedObject>, */ bool compatible(const NumericVector & v) const; - /** - * checks whether the vector \p v is fuzzy equal to this vector by doing element-wise comparisons. - * A given element will be deemed fuzzy equal if either a relative or absolute tolerance fuzzy - * equal comparison returns true - */ - bool fuzzy_equals(const NumericVector & v, - const Real rel_tol = TOLERANCE, - const Real abs_tol = TOLERANCE) const; - protected: /** @@ -1077,8 +1074,24 @@ void NumericVector::swap (NumericVector & v) } -} // namespace libMesh +template +auto +l1_norm(const NumericVector & vec) -> decltype(vec.l1_norm()) +{ + return vec.l1_norm(); +} + + + +template +auto +l1_norm_diff(const NumericVector & vec1, + const NumericVector & vec2) -> decltype(vec1.l1_norm_diff(vec2)) +{ + return vec1.l1_norm_diff(vec2); +} +} // namespace libMesh // Workaround for weird boost/NumericVector interaction bug #ifdef LIBMESH_DEFAULT_QUADRUPLE_PRECISION diff --git a/include/numerics/petsc_matrix.h b/include/numerics/petsc_matrix.h index 06f73d506b1..cee7d3409c3 100644 --- a/include/numerics/petsc_matrix.h +++ b/include/numerics/petsc_matrix.h @@ -349,10 +349,6 @@ class PetscMatrix final : public SparseMatrix const std::vector & rows, const std::vector & cols) const override; - virtual bool fuzzy_equals(const SparseMatrix & other, - const Real rel_tol = TOLERANCE, - const Real abs_tol = TOLERANCE) const override; - virtual void scale(const T scale) override; protected: diff --git a/include/numerics/sparse_matrix.h b/include/numerics/sparse_matrix.h index f8d6649c507..2623ed8442d 100644 --- a/include/numerics/sparse_matrix.h +++ b/include/numerics/sparse_matrix.h @@ -385,6 +385,11 @@ class SparseMatrix : public ReferenceCountedObject>, */ virtual Real linfty_norm () const = 0; + /** + * \returns The l1_norm() of the difference of \p this and \p other_mat + */ + Real l1_norm_diff (const SparseMatrix & other_mat) const; + /** * \returns \p true if the matrix has been assembled. */ @@ -573,15 +578,6 @@ class SparseMatrix : public ReferenceCountedObject>, std::vector & indices, std::vector & values) const = 0; - /** - * checks whether the matrix \p other is fuzzy equal to this matrix by doing element-wise - * comparisons. A given element will be deemed fuzzy equal if either a relative or absolute - * tolerance fuzzy equal comparison returns true - */ - virtual bool fuzzy_equals(const SparseMatrix & other, - const Real rel_tol = TOLERANCE, - const Real abs_tol = TOLERANCE) const; - /** * Scales all elements of this matrix by \p scale */ @@ -639,6 +635,20 @@ std::ostream & operator << (std::ostream & os, const SparseMatrix & m) return os; } +template +auto +l1_norm(const SparseMatrix & mat) -> decltype(mat.l1_norm()) +{ + return mat.l1_norm(); +} + +template +auto +l1_norm_diff(const SparseMatrix & mat1, + const SparseMatrix & mat2) -> decltype(mat1.l1_norm_diff(mat2)) +{ + return mat1.l1_norm_diff(mat2); +} } // namespace libMesh diff --git a/include/numerics/type_vector.h b/include/numerics/type_vector.h index b4a0e102771..80d9de09204 100644 --- a/include/numerics/type_vector.h +++ b/include/numerics/type_vector.h @@ -25,6 +25,7 @@ #include "libmesh/compare_types.h" #include "libmesh/tensor_tools.h" #include "libmesh/int_range.h" +#include "libmesh/fuzzy_equals.h" // C++ includes #include // *must* precede for proper std:abs() on PGI, Sun Studio CC @@ -341,14 +342,6 @@ class TypeVector */ auto l1_norm() const -> decltype(std::abs(T())); - /** - * \returns The \f$ \ell_1 \f$-norm of \f$ \vec{u} - \vec{v} \f$, where - * \f$ \vec{u} \f$ is \p this. - */ - template - auto l1_norm_diff(const TypeVector & other_vec) const - -> decltype(std::abs(typename CompareTypes::supertype{})); - /** * \returns True if all values in the vector are zero */ @@ -453,9 +446,6 @@ class TypeVector * The coordinates of the \p TypeVector. */ T _coords[LIBMESH_DIM]; - - template - friend class TypeVector; }; @@ -1004,24 +994,11 @@ TypeVector::l1_norm() const -> decltype(std::abs(T())) return ret; } -template -template -auto -TypeVector::l1_norm_diff(const TypeVector & other_vec) const - -> decltype(std::abs(typename CompareTypes::supertype{})) -{ - decltype(std::abs(typename CompareTypes::supertype{})) ret{}; - for (const auto i : make_range(libmesh_dim)) - ret += std::abs(_coords[i] - other_vec._coords[i]); - - return ret; -} - template inline bool TypeVector::absolute_fuzzy_equals(const TypeVector & rhs, Real tol) const { - return this->l1_norm_diff(rhs) <= tol; + return libMesh::absolute_fuzzy_equals(*this, rhs, tol); } @@ -1030,7 +1007,7 @@ template inline bool TypeVector::relative_fuzzy_equals(const TypeVector & rhs, Real tol) const { - return this->absolute_fuzzy_equals(rhs, tol * (this->l1_norm() + rhs.l1_norm())); + return libMesh::relative_fuzzy_equals(*this, rhs, tol); } @@ -1189,16 +1166,16 @@ outer_product(const TypeVector & a, const T2 & b) template auto -l1_norm(const TypeVector & var) -> decltype(var.l1_norm()) +l1_norm(const TypeVector & var) { return var.l1_norm(); } -template +template auto -l1_norm_diff(const TypeVector & var) -> decltype(var.l1_norm_diff()) +l1_norm_diff(const TypeVector & vec1, const TypeVector & vec2) { - return var.l1_norm_diff(); + return l1_norm(vec1 - vec2); } } // namespace libMesh diff --git a/include/utils/fuzzy_equals.h b/include/utils/fuzzy_equals.h index 2c7a5482521..223beccb2c1 100644 --- a/include/utils/fuzzy_equals.h +++ b/include/utils/fuzzy_equals.h @@ -32,16 +32,16 @@ namespace libMesh /** * Computes the L1 norm */ -template +template ::value, int>::type = 0> auto -l1_norm(const T & var) -> decltype(std::abs(T{})) +l1_norm(const T & var) { return std::abs(var); } template auto -l1_norm(const std::complex & var) -> decltype(std::abs(T{})) +l1_norm(const std::complex & var) { return std::abs(var.real()) + std::abs(var.imag()); } @@ -49,20 +49,13 @@ l1_norm(const std::complex & var) -> decltype(std::abs(T{})) /** * Computes the L1 norm of the diff between \p var1 and \p var2 */ -template -auto -l1_norm_diff(const T & var1, - const T2 & var2) -> decltype(std::abs(typename CompareTypes::supertype{})) -{ - return std::abs(var1 - var2); -} - -template +template ::value && ScalarTraits::value, int>::type = 0> auto -l1_norm_diff(const std::complex & var1, const std::complex & var2) - -> decltype(std::abs(typename CompareTypes::supertype{})) +l1_norm_diff(const T & var1, const T2 & var2) { - return std::abs(var1.real() - var2.real()) + std::abs(var1.imag() - var2.imag()); + return l1_norm(var1 - var2); } #ifdef LIBMESH_HAVE_METAPHYSICL diff --git a/src/numerics/numeric_vector.C b/src/numerics/numeric_vector.C index 018892663a0..58eae5e563d 100644 --- a/src/numerics/numeric_vector.C +++ b/src/numerics/numeric_vector.C @@ -376,6 +376,22 @@ Real NumericVector::l2_norm_diff (const NumericVector & v) const +template +Real NumericVector::l1_norm_diff (const NumericVector & v) const +{ + libmesh_assert(this->compatible(v)); + + Real norm = 0; + for (const auto i : make_range(this->first_local_index(), this->last_local_index())) + norm += libMesh::l1_norm_diff((*this)(i), v(i)); + + this->comm().sum(norm); + + return norm; +} + + + template void NumericVector::add_vector (const T * v, const std::vector & dof_indices) @@ -430,34 +446,6 @@ bool NumericVector::compatible (const NumericVector & v) const this->last_local_index() == v.last_local_index(); } -template -bool -NumericVector::fuzzy_equals(const NumericVector & v, - const Real rel_tol, - const Real abs_tol) const -{ - bool equiv = true; - if (this->local_size() != v.local_size()) - equiv = false; - - if (equiv) - for (const auto i : make_range(this->first_local_index(), this->last_local_index())) - { - if (relative_fuzzy_equals((*this)(i), v(i), rel_tol) || - absolute_fuzzy_equals((*this)(i), v(i), abs_tol)) - continue; - else - { - equiv = false; - break; - } - } - - this->comm().min(equiv); - - return equiv; -} - //------------------------------------------------------------------ // Explicit instantiations template class LIBMESH_EXPORT NumericVector; diff --git a/src/numerics/petsc_matrix.C b/src/numerics/petsc_matrix.C index d893ba1090e..cdae001b2b5 100644 --- a/src/numerics/petsc_matrix.C +++ b/src/numerics/petsc_matrix.C @@ -32,7 +32,6 @@ #include "libmesh/parallel.h" #include "libmesh/utility.h" #include "libmesh/wrapped_petsc.h" -#include "libmesh/fuzzy_equals.h" // C++ includes #ifdef LIBMESH_HAVE_UNISTD_H @@ -1576,104 +1575,6 @@ SparseMatrix & PetscMatrix::operator= (const SparseMatrix & v) return *this; } -template -bool -PetscMatrix::fuzzy_equals(const SparseMatrix & other, - const Real rel_tol, - const Real abs_tol) const -{ - const auto * const petsc_other = dynamic_cast *>(&other); - if (!petsc_other) - // This result should be the same on all procs - return false; - - Mat petsc_other_mat = petsc_other->_mat; - const PetscScalar *petsc_row, *petsc_row_other; - const PetscInt *petsc_cols, *petsc_cols_other; - - PetscErrorCode ierr = static_cast(0); - PetscInt ncols = 0, ncols_other = 0; - - bool equiv = true; - if ((this->local_m() != other.local_m()) || (this->local_n() != other.local_n())) - { - equiv = false; - goto globalComm; - } - - for (const auto i : make_range(this->row_start(), this->row_stop())) - { - const auto i_val = static_cast(i); - - // the matrix needs to be closed for this to work - // this->close(); - // but closing it is a semiparallel operation; we want operator() - // to run on one processor. - libmesh_assert(this->closed()); - libmesh_assert(petsc_other->closed()); - - ierr = MatGetRow(_mat, i_val, &ncols, &petsc_cols, &petsc_row); - LIBMESH_CHKERR(ierr); - ierr = MatGetRow(petsc_other_mat, i_val, &ncols_other, &petsc_cols_other, &petsc_row_other); - LIBMESH_CHKERR(ierr); - - auto restore_rows = [this, - i_val, - petsc_other_mat, - &ierr, - &ncols, - &petsc_cols, - &petsc_row, - &ncols_other, - &petsc_cols_other, - &petsc_row_other]() - { - ierr = MatRestoreRow(_mat, i_val, &ncols, &petsc_cols, &petsc_row); - LIBMESH_CHKERR(ierr); - ierr = - MatRestoreRow(petsc_other_mat, i_val, &ncols_other, &petsc_cols_other, &petsc_row_other); - LIBMESH_CHKERR(ierr); - }; - - auto compared_false = [&equiv, restore_rows]() - { - restore_rows(); - equiv = false; - }; - - if (ncols != ncols_other) - { - compared_false(); - goto globalComm; - } - - // No need for fuzzy comparison here - for (const auto j_val : make_range(ncols)) - if (petsc_cols[j_val] != petsc_cols_other[j_val]) - { - compared_false(); - goto globalComm; - } - - for (const auto j_val : make_range(ncols)) - if (relative_fuzzy_equals(petsc_row[j_val], petsc_row_other[j_val], rel_tol) || - absolute_fuzzy_equals(petsc_row[j_val], petsc_row_other[j_val], abs_tol)) - continue; - else - { - compared_false(); - goto globalComm; - } - - restore_rows(); - } - -globalComm: - this->comm().min(equiv); - - return equiv; -} - template void PetscMatrix::scale(const T scale) { diff --git a/src/numerics/sparse_matrix.C b/src/numerics/sparse_matrix.C index c4d68d1480d..18ef4927e8f 100644 --- a/src/numerics/sparse_matrix.C +++ b/src/numerics/sparse_matrix.C @@ -32,7 +32,6 @@ #include "libmesh/trilinos_epetra_matrix.h" #include "libmesh/numeric_vector.h" #include "libmesh/enum_solver_package.h" -#include "libmesh/fuzzy_equals.h" // gzstream for reading compressed files as a stream @@ -858,45 +857,23 @@ void SparseMatrix::read_petsc_hdf5(const std::string &) template -bool -SparseMatrix::fuzzy_equals(const SparseMatrix & other, - const Real rel_tol, - const Real abs_tol) const +void SparseMatrix::scale(const T scale) { - bool equiv = true; - if ((this->local_m() != other.local_m()) || (this->local_n() != other.local_n())) - equiv = false; - - if (equiv) - for (const auto i : make_range(this->row_start(), this->row_stop())) - for (const auto j : make_range(this->col_start(), this->col_stop())) - { - if (relative_fuzzy_equals((*this)(i, j), other(i, j), rel_tol) || - absolute_fuzzy_equals((*this)(i, j), other(i, j), abs_tol)) - continue; - else - { - equiv = false; - goto globalComm; - } - } - -globalComm: - this->comm().min(equiv); + libmesh_assert(this->closed()); - return equiv; + for (const auto i : make_range(this->row_start(), this->row_stop())) + for (const auto j : make_range(this->col_start(), this->col_stop())) + this->set(i, j, (*this)(i, j) * scale); } template -void SparseMatrix::scale(const T scale) +Real SparseMatrix::l1_norm_diff(const SparseMatrix & other_mat) const { - libmesh_assert(this->closed()); - - for (const auto i : make_range(this->row_start(), this->row_stop())) - for (const auto j : make_range(this->col_start(), this->col_stop())) - this->set(i, j, (*this)(i, j) * scale); + auto diff_mat = this->clone(); + diff_mat->add(-1.0, other_mat); + return diff_mat->l1_norm(); } //------------------------------------------------------------------ diff --git a/tests/numerics/numeric_vector_test.h b/tests/numerics/numeric_vector_test.h index 8037ef83b12..cb390114edd 100644 --- a/tests/numerics/numeric_vector_test.h +++ b/tests/numerics/numeric_vector_test.h @@ -6,6 +6,7 @@ // libMesh includes #include +#include #include "libmesh_cppunit.h" @@ -74,11 +75,11 @@ class NumericVectorTest : public CppUnit::TestCase { auto v_clone = v.clone(); auto & vorig = *v_clone; - CPPUNIT_ASSERT(v.fuzzy_equals(vorig)); + CPPUNIT_ASSERT(relative_fuzzy_equals(v, vorig)); v += v; - CPPUNIT_ASSERT(!v.fuzzy_equals(vorig)); + CPPUNIT_ASSERT(!relative_fuzzy_equals(v, vorig)); for (libMesh::dof_id_type n=first; n != last; n++) LIBMESH_ASSERT_FP_EQUAL(libMesh::libmesh_real(v(n)), diff --git a/tests/numerics/sparse_matrix_test.h b/tests/numerics/sparse_matrix_test.h index a9c4c9b98b9..765f5097837 100644 --- a/tests/numerics/sparse_matrix_test.h +++ b/tests/numerics/sparse_matrix_test.h @@ -7,6 +7,7 @@ // libMesh includes #include #include +#include #include "libmesh_cppunit.h" @@ -241,9 +242,9 @@ class SparseMatrixTest : public CppUnit::TestCase // Check that copy has same values as original LIBMESH_ASSERT_FP_EQUAL(copy->l1_norm(), matrix->l1_norm(), _tolerance); - CPPUNIT_ASSERT(matrix->fuzzy_equals(*copy)); + CPPUNIT_ASSERT(relative_fuzzy_equals(*matrix, *copy)); copy->scale(2); - CPPUNIT_ASSERT(!matrix->fuzzy_equals(*copy)); + CPPUNIT_ASSERT(!relative_fuzzy_equals(*matrix, *copy)); } { From 02b0e97fe5beb91652f964774deed86f98cf5aab Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Mon, 15 Jul 2024 19:37:34 -0700 Subject: [PATCH 18/22] Remove uninformative trailing return types --- include/numerics/numeric_vector.h | 10 +++------- include/numerics/sparse_matrix.h | 5 ++--- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/include/numerics/numeric_vector.h b/include/numerics/numeric_vector.h index 1e83952ccae..d4884743cf6 100644 --- a/include/numerics/numeric_vector.h +++ b/include/numerics/numeric_vector.h @@ -1073,24 +1073,20 @@ void NumericVector::swap (NumericVector & v) std::swap(_type, v._type); } - - template auto -l1_norm(const NumericVector & vec) -> decltype(vec.l1_norm()) +l1_norm(const NumericVector & vec) { return vec.l1_norm(); } - - template auto -l1_norm_diff(const NumericVector & vec1, - const NumericVector & vec2) -> decltype(vec1.l1_norm_diff(vec2)) +l1_norm_diff(const NumericVector & vec1, const NumericVector & vec2) { return vec1.l1_norm_diff(vec2); } + } // namespace libMesh // Workaround for weird boost/NumericVector interaction bug diff --git a/include/numerics/sparse_matrix.h b/include/numerics/sparse_matrix.h index 2623ed8442d..b369eae9c12 100644 --- a/include/numerics/sparse_matrix.h +++ b/include/numerics/sparse_matrix.h @@ -637,15 +637,14 @@ std::ostream & operator << (std::ostream & os, const SparseMatrix & m) template auto -l1_norm(const SparseMatrix & mat) -> decltype(mat.l1_norm()) +l1_norm(const SparseMatrix & mat) { return mat.l1_norm(); } template auto -l1_norm_diff(const SparseMatrix & mat1, - const SparseMatrix & mat2) -> decltype(mat1.l1_norm_diff(mat2)) +l1_norm_diff(const SparseMatrix & mat1, const SparseMatrix & mat2) { return mat1.l1_norm_diff(mat2); } From 8835d1205be2703b3fe70be6310f90591b579615 Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Mon, 15 Jul 2024 19:37:51 -0700 Subject: [PATCH 19/22] Remove whitespace diffs --- include/numerics/numeric_vector.h | 1 + include/utils/utility.h | 3 ++- src/numerics/numeric_vector.C | 2 ++ src/numerics/sparse_matrix.C | 1 - 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/include/numerics/numeric_vector.h b/include/numerics/numeric_vector.h index d4884743cf6..7254d5c4743 100644 --- a/include/numerics/numeric_vector.h +++ b/include/numerics/numeric_vector.h @@ -1089,6 +1089,7 @@ l1_norm_diff(const NumericVector & vec1, const NumericVector & vec2) } // namespace libMesh + // Workaround for weird boost/NumericVector interaction bug #ifdef LIBMESH_DEFAULT_QUADRUPLE_PRECISION namespace boost { namespace multiprecision { namespace detail { diff --git a/include/utils/utility.h b/include/utils/utility.h index 49ef7116d59..e7ea6c1c385 100644 --- a/include/utils/utility.h +++ b/include/utils/utility.h @@ -538,7 +538,8 @@ struct CompareUnderlying } }; -} // namespace Utility + +} } // namespace libMesh diff --git a/src/numerics/numeric_vector.C b/src/numerics/numeric_vector.C index 58eae5e563d..e820babe628 100644 --- a/src/numerics/numeric_vector.C +++ b/src/numerics/numeric_vector.C @@ -446,6 +446,8 @@ bool NumericVector::compatible (const NumericVector & v) const this->last_local_index() == v.last_local_index(); } + + //------------------------------------------------------------------ // Explicit instantiations template class LIBMESH_EXPORT NumericVector; diff --git a/src/numerics/sparse_matrix.C b/src/numerics/sparse_matrix.C index 18ef4927e8f..cd2af923169 100644 --- a/src/numerics/sparse_matrix.C +++ b/src/numerics/sparse_matrix.C @@ -33,7 +33,6 @@ #include "libmesh/numeric_vector.h" #include "libmesh/enum_solver_package.h" - // gzstream for reading compressed files as a stream #ifdef LIBMESH_HAVE_GZSTREAM # include "libmesh/ignore_warnings.h" // shadowing in gzstream.h From 7267198cf92e58ef674230a1bb520d55bf6a2e31 Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Mon, 15 Jul 2024 20:25:05 -0700 Subject: [PATCH 20/22] Specialize for TypeVector::l1_norm --- include/numerics/type_vector.h | 8 ++++++-- src/numerics/type_vector.C | 13 +++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/include/numerics/type_vector.h b/include/numerics/type_vector.h index 80d9de09204..f613b158568 100644 --- a/include/numerics/type_vector.h +++ b/include/numerics/type_vector.h @@ -340,7 +340,7 @@ class TypeVector /** * \returns The L1 norm of the vector */ - auto l1_norm() const -> decltype(std::abs(T())); + auto l1_norm() const; /** * \returns True if all values in the vector are zero @@ -983,9 +983,13 @@ bool TypeVector::is_zero() const return true; } +template <> +auto +TypeVector::l1_norm() const; + template auto -TypeVector::l1_norm() const -> decltype(std::abs(T())) +TypeVector::l1_norm() const { decltype(std::abs(T())) ret{}; for (const auto i : make_range(libmesh_dim)) diff --git a/src/numerics/type_vector.C b/src/numerics/type_vector.C index 5f747003ead..3191b66fc26 100644 --- a/src/numerics/type_vector.C +++ b/src/numerics/type_vector.C @@ -222,6 +222,19 @@ bool TypeVector::operator >= (const TypeVector & rhs) const +template <> +auto +TypeVector::l1_norm() const +{ + bool ret{}; + for (const auto i : make_range(libmesh_dim)) + ret += _coords[i]; + + return ret; +} + + + // ------------------------------------------------------------ // Explicit instantiations template class LIBMESH_EXPORT TypeVector; From c825455e163598f122e643c75dc99fa0e803fd10 Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Mon, 15 Jul 2024 20:26:17 -0700 Subject: [PATCH 21/22] Remove no longer necessary std::abs TypeVector overloads --- include/numerics/type_vector.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/include/numerics/type_vector.h b/include/numerics/type_vector.h index f613b158568..8fedfcdede5 100644 --- a/include/numerics/type_vector.h +++ b/include/numerics/type_vector.h @@ -75,8 +75,6 @@ namespace std { template auto norm(const libMesh::TypeVector & vector) -> decltype(std::norm(T())); -template -auto abs(const libMesh::TypeVector & vector) -> decltype(std::abs(T())); } namespace libMesh @@ -1192,12 +1190,6 @@ auto norm(const libMesh::TypeVector & vector) -> decltype(std::norm(T())) // Yea I agree it's dumb that the standard returns the square of the Euclidean norm return vector.norm_sq(); } - -template -auto abs(const libMesh::TypeVector & vector) -> decltype(std::abs(T())) -{ - return vector.norm(); -} } // namespace std #ifdef LIBMESH_HAVE_METAPHYSICL From 6af10646b2d3a566d675946ea6118e3a975c6c0e Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Wed, 17 Jul 2024 07:47:28 -0700 Subject: [PATCH 22/22] Remove unnecessary l1_norm overload for std::complex --- include/utils/fuzzy_equals.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/include/utils/fuzzy_equals.h b/include/utils/fuzzy_equals.h index 223beccb2c1..069588f496e 100644 --- a/include/utils/fuzzy_equals.h +++ b/include/utils/fuzzy_equals.h @@ -39,13 +39,6 @@ l1_norm(const T & var) return std::abs(var); } -template -auto -l1_norm(const std::complex & var) -{ - return std::abs(var.real()) + std::abs(var.imag()); -} - /** * Computes the L1 norm of the diff between \p var1 and \p var2 */