Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ jobs:
-DSINGULARITY_PLUGINS=$(pwd)/../example/plugin \
-DCMAKE_LINKER=ld.gold \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_CXX_STANDARD=20 \

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no longer needed as we're now just on C++20

..
#-DSINGULARITY_TEST_PYTHON=ON \
#-DSINGULARITY_TEST_STELLAR_COLLAPSE=ON \
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- [[PR630]](https://github.com/lanl/singularity-eos/pull/630) Thread execution spaces through vector API.

### Fixed (Repair bugs, etc)
- [[PR637]](https://github.com/lanl/singularity-eos/pull/637) Fix is_normal

### Changed (changing behavior/API/variables/...)

Expand Down
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -225,12 +225,12 @@ if(SINGULARITY_BUILD_PYTHON)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif()

# require at least C++17, but allow newer versions to become a client requirement if
# require at least C++20, but allow newer versions to become a client requirement if
# explicitly set at build time (needed for building with newer Kokkos)
if(CMAKE_CXX_STANDARD)
target_compile_features(singularity-eos_Interface INTERFACE cxx_std_${CMAKE_CXX_STANDARD})
else()
target_compile_features(singularity-eos_Interface INTERFACE cxx_std_17)
target_compile_features(singularity-eos_Interface INTERFACE cxx_std_20)
endif()

# checks if this is our build, or we've been imported via `add_subdirectory` NB:
Expand Down
24 changes: 16 additions & 8 deletions singularity-eos/base/error_utils.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//------------------------------------------------------------------------------
// © 2021-2025. Triad National Security, LLC. All rights reserved. This
// © 2021-2026. Triad National Security, LLC. All rights reserved. This
// program was produced under U.S. Government contract 89233218CNA000001
// for Los Alamos National Laboratory (LANL), which is operated by Triad
// National Security, LLC for the U.S. Department of Energy/National
Expand All @@ -12,27 +12,33 @@
// publicly and display publicly, and to permit others to do so.
//------------------------------------------------------------------------------

// This file created in part with the assistance of generative AI

#ifndef SINGULARITY_EOS_BASE_ERROR_UTILS_HPP_
#define SINGULARITY_EOS_BASE_ERROR_UTILS_HPP_

#include <cmath>
#include <limits>
#include <type_traits>

#include <ports-of-call/portability.hpp>
#include <ports-of-call/robust_utils.hpp>

namespace singularity {
namespace error_utils {

using PortsOfCall::printf;

constexpr double _NORMAL_FACTOR = 1.0e10;
// Only accept values that are safely away from overflow by this factor. In
// other words, a nonzero value must be normal and have magnitude no larger
// than max()/NORMAL_FACTOR.
constexpr double NORMAL_FACTOR = 1.0e10;

struct is_normal_or_zero {
template <typename valT>
constexpr bool PORTABLE_FORCEINLINE_FUNCTION operator()(valT value) const {
static_assert(std::is_floating_point<valT>::value);
return (value == valT{0}) ||
(std::isnormal(_NORMAL_FACTOR * value) && std::isnormal(value));
return PortsOfCall::Robust::is_normal_or_zero(value,
static_cast<valT>(NORMAL_FACTOR));
}
};

Expand All @@ -52,16 +58,18 @@ struct is_non_negative {
}
};

// Checks whether a value obeys some sort of provided condition. If not, returns true and
// prints the provided error message, variable name, and value (but does not abort!)
// Checks whether a value obeys some sort of provided condition. If not, returns true
// and prints the provided error message, variable name, and value (but does not abort!)
template <typename valT, typename condT, typename nameT>
PORTABLE_FORCEINLINE_FUNCTION bool violates_condition(valT &&value, condT &&condition,
nameT &&var_name) {
const bool good = condition(std::forward<valT>(value));
#ifndef NDEBUG

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now added for performance and convenience

if (!good) {
printf("### ERROR: Bad singularity-eos value\n Var: %s\n Value: %.15e\n",
var_name, value);
}
#endif // NDEBUG
return !good;
}

Expand All @@ -78,7 +86,7 @@ PORTABLE_FORCEINLINE_FUNCTION bool non_positive_value(valT &&value, nameT &&var_
}
template <typename valT, typename nameT>
PORTABLE_FORCEINLINE_FUNCTION bool negative_value(valT &&value, nameT &&var_name) {
return violates_condition(std::forward<valT>(value), is_strictly_positive{},
return violates_condition(std::forward<valT>(value), is_non_negative{},
Comment on lines -81 to +89

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is just a typo. this condition was identical to the one above.

std::forward<nameT>(var_name));
}

Expand Down
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ add_executable(
test_eos_modifiers_minimal.cpp
test_eos_vector.cpp
test_math_utils.cpp
test_error_utils.cpp
test_robust_utils.cpp
test_modifier_floored_energy.cpp
test_variadic_utils.cpp
Expand Down
104 changes: 104 additions & 0 deletions test/test_error_utils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
//------------------------------------------------------------------------------
// © 2021-2026. Triad National Security, LLC. All rights reserved. This
// program was produced under U.S. Government contract 89233218CNA000001
// for Los Alamos National Laboratory (LANL), which is operated by Triad
// National Security, LLC for the U.S. Department of Energy/National
// Nuclear Security Administration. All rights in the program are
// reserved by Triad National Security, LLC, and the U.S. Department of
// Energy/National Nuclear Security Administration. The Government is
// granted for itself and others acting on its behalf a nonexclusive,
// paid-up, irrevocable worldwide license in this material to reproduce,
// prepare derivative works, distribute copies to the public, perform
// publicly and display publicly, and to permit others to do so.
//------------------------------------------------------------------------------

// This file created with the assistance of generative AI

#include <cmath>
#include <limits>

#include <ports-of-call/portability.hpp>
#include <singularity-eos/base/error_utils.hpp>

#ifndef CATCH_CONFIG_FAST_COMPILE
#define CATCH_CONFIG_FAST_COMPILE
#include <catch2/catch_test_macros.hpp>
#endif

namespace error_utils = singularity::error_utils;

template <typename T>
void CheckNormalOrZeroClassification(const char *type_name) {
using limits = std::numeric_limits<T>;
const T factor = static_cast<T>(error_utils::NORMAL_FACTOR);
const auto condition = error_utils::is_normal_or_zero{};

CAPTURE(type_name);

REQUIRE(condition(T{0}));
REQUIRE(condition(-T{0}));
REQUIRE(condition(static_cast<T>(1.382884838243760e+06)));
REQUIRE(condition(limits::min()));
REQUIRE(condition(-limits::min()));

if constexpr (limits::has_denorm != std::denorm_absent) {
REQUIRE_FALSE(condition(limits::denorm_min()));
REQUIRE_FALSE(condition(-limits::denorm_min()));
}

REQUIRE_FALSE(condition(limits::quiet_NaN()));
REQUIRE_FALSE(condition(limits::infinity()));
REQUIRE_FALSE(condition(-limits::infinity()));

const T safely_bounded = (limits::max() / factor) * T{0.5};
const T too_large = (limits::max() / factor) * T{2.0};
REQUIRE(condition(safely_bounded));
REQUIRE_FALSE(condition(too_large));
}

template <typename T>
void CheckNormalOrZeroClassificationOnDevice(const char *type_name) {
using limits = std::numeric_limits<T>;
const T factor = static_cast<T>(error_utils::NORMAL_FACTOR);
const auto condition = error_utils::is_normal_or_zero{};

CAPTURE(type_name);

int nwrong = 0;
portableReduce(
"Check error_utils::is_normal_or_zero on device", 0, 1,
PORTABLE_LAMBDA(const int /*i*/, int &nw) {
nw += !condition(T{0});
nw += !condition(-T{0});
nw += !condition(static_cast<T>(1.382884838243760e+06));
nw += !condition(limits::min());
nw += !condition(-limits::min());

if constexpr (limits::has_denorm != std::denorm_absent) {
nw += condition(limits::denorm_min());
nw += condition(-limits::denorm_min());
}

nw += condition(limits::quiet_NaN());
nw += condition(limits::infinity());
nw += condition(-limits::infinity());
nw += !condition((limits::max() / factor) * T{0.5});
nw += condition((limits::max() / factor) * T{2.0});
},
nwrong);

REQUIRE(nwrong == 0);
}

SCENARIO(
"Error utilities classify normal values, denormals, and overflow-adjacent values",
"[ErrorUtils]") {
CheckNormalOrZeroClassification<float>("float");
CheckNormalOrZeroClassification<double>("double");
}

SCENARIO("Error utilities classify values consistently in device reductions",
"[ErrorUtils][Device]") {
CheckNormalOrZeroClassificationOnDevice<float>("float");
CheckNormalOrZeroClassificationOnDevice<double>("double");
}
2 changes: 1 addition & 1 deletion utils/kokkos
Submodule kokkos updated 1580 files
2 changes: 1 addition & 1 deletion utils/kokkos-kernels
Submodule kokkos-kernels updated 1651 files
Loading