From 36c11391939f4b6001bff387843f68b43ad113c1 Mon Sep 17 00:00:00 2001 From: Jonah Maxwell Miller Date: Fri, 17 Jul 2026 22:41:17 -0400 Subject: [PATCH 01/14] merge/rebase main man this really should never trigger so maybe it shouldn't be hidden behind an if? --- .../singularity-utils/error_utils.hpp | 14 ++- test/CMakeLists.txt | 1 + test/test_error_utils.cpp | 104 ++++++++++++++++++ 3 files changed, 116 insertions(+), 3 deletions(-) create mode 100644 test/test_error_utils.cpp diff --git a/singularity-utils/singularity-utils/error_utils.hpp b/singularity-utils/singularity-utils/error_utils.hpp index fcf306ec3c2..5bdb6f88b6f 100644 --- a/singularity-utils/singularity-utils/error_utils.hpp +++ b/singularity-utils/singularity-utils/error_utils.hpp @@ -12,10 +12,12 @@ // publicly and display publicly, and to permit others to do so. //------------------------------------------------------------------------------ +// This file created in part with the assistance of generative AI #ifndef SINGULARITY_UTILS_ERROR_UTILS_HPP_ #define SINGULARITY_UTILS_ERROR_UTILS_HPP_ #include +#include #include #include @@ -25,14 +27,20 @@ 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 constexpr bool PORTABLE_FORCEINLINE_FUNCTION operator()(valT value) const { static_assert(std::is_floating_point::value); + using limits = std::numeric_limits; + const valT abs_value = (value < valT{0}) ? -value : value; return (value == valT{0}) || - (std::isnormal(_NORMAL_FACTOR * value) && std::isnormal(value)); + ((abs_value >= limits::min()) && + (abs_value * static_cast(NORMAL_FACTOR) <= limits::max())); } }; @@ -78,7 +86,7 @@ PORTABLE_FORCEINLINE_FUNCTION bool non_positive_value(valT &&value, nameT &&var_ } template PORTABLE_FORCEINLINE_FUNCTION bool negative_value(valT &&value, nameT &&var_name) { - return violates_condition(std::forward(value), is_strictly_positive{}, + return violates_condition(std::forward(value), is_non_negative{}, std::forward(var_name)); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9b40ff1972f..97b676ff91c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -38,6 +38,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 diff --git a/test/test_error_utils.cpp b/test/test_error_utils.cpp new file mode 100644 index 00000000000..c2a3d67ec4f --- /dev/null +++ b/test/test_error_utils.cpp @@ -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 +#include + +#include +#include + +#ifndef CATCH_CONFIG_FAST_COMPILE +#define CATCH_CONFIG_FAST_COMPILE +#include +#endif + +namespace error_utils = singularity::error_utils; + +template +void CheckNormalOrZeroClassification(const char *type_name) { + using limits = std::numeric_limits; + const T factor = static_cast(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(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 +void CheckNormalOrZeroClassificationOnDevice(const char *type_name) { + using limits = std::numeric_limits; + const T factor = static_cast(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(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"); + CheckNormalOrZeroClassification("double"); +} + +SCENARIO("Error utilities classify values consistently in device reductions", + "[ErrorUtils][Device]") { + CheckNormalOrZeroClassificationOnDevice("float"); + CheckNormalOrZeroClassificationOnDevice("double"); +} From 728cb91497683e2485d87d77101b087bd2983f21 Mon Sep 17 00:00:00 2001 From: Jonah Maxwell Miller Date: Fri, 17 Jul 2026 22:42:26 -0400 Subject: [PATCH 02/14] merge changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 064874acc79..fb7a8516976 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,8 @@ ### Fixed (Repair bugs, etc) - [[PR645]](https://github.com/lanl/singularity-eos/pull/645) Fix internal energy from density and pressure for JWL - [[PR63]](https://github.com/lanl/singularity-eos/pull/639) Fixed UnitSystem temperature bug. Temperature is now treated in the same as the time, mass, and length unit factors. - +- [[PR637]](https://github.com/lanl/singularity-eos/pull/637) Fix is_normal + ### Changed (changing behavior/API/variables/...) - [[PR641]](https://github.com/lanl/singularity-eos/pull/641) The location of some header files located in `` have moved to ``. From 35379d539ba477308b8325a15b822d46b8228511 Mon Sep 17 00:00:00 2001 From: Jonah Maxwell Miller Date: Fri, 17 Jul 2026 22:48:54 -0400 Subject: [PATCH 03/14] merge cmakelists --- CMakeLists.txt | 25 +++++++++++++++++++ .../singularity-utils/error_utils.hpp | 15 ++++++----- utils/ports-of-call | 2 +- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index aaecf898839..2556ab46913 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -247,6 +247,31 @@ if(SINGULARITY_BUILD_PYTHON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) endif() +# 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_20) +endif() + +# checks if this is our build, or we've been imported via `add_subdirectory` NB: +# this should make the `option(SINGULARITY_SUBMODULE_MODE ...)` unnecessary +if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) + set(CMAKE_CXX_EXTENSIONS OFF) +else() + message( + STATUS + "Detected that `singularity-eos` is a subproject, will configure build in submodule mode" + ) + set(SINGULARITY_SUBMODULE_MODE ON) +endif() + +if(SINGULARITY_FORCE_SUBMODULE_MODE) + message(STATUS "Building as though project was a submodule.") + set(SINGULARITY_SUBMODULE_MODE ON) +endif() + # Use to determine if Eigen is used or not set(SINGULARITY_USE_EIGEN OFF diff --git a/singularity-utils/singularity-utils/error_utils.hpp b/singularity-utils/singularity-utils/error_utils.hpp index 5bdb6f88b6f..c4687d5fc18 100644 --- a/singularity-utils/singularity-utils/error_utils.hpp +++ b/singularity-utils/singularity-utils/error_utils.hpp @@ -21,6 +21,7 @@ #include #include +#include namespace singularity { namespace error_utils { @@ -35,12 +36,8 @@ constexpr double NORMAL_FACTOR = 1.0e10; struct is_normal_or_zero { template constexpr bool PORTABLE_FORCEINLINE_FUNCTION operator()(valT value) const { - static_assert(std::is_floating_point::value); - using limits = std::numeric_limits; - const valT abs_value = (value < valT{0}) ? -value : value; - return (value == valT{0}) || - ((abs_value >= limits::min()) && - (abs_value * static_cast(NORMAL_FACTOR) <= limits::max())); + return PortsOfCall::Robust::is_normal_or_zero(value, + static_cast(NORMAL_FACTOR)); } }; @@ -60,16 +57,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 PORTABLE_FORCEINLINE_FUNCTION bool violates_condition(valT &&value, condT &&condition, nameT &&var_name) { const bool good = condition(std::forward(value)); +#ifndef NDEBUG if (!good) { printf("### ERROR: Bad singularity-eos value\n Var: %s\n Value: %.15e\n", var_name, value); } +#endif // NDEBUG return !good; } diff --git a/utils/ports-of-call b/utils/ports-of-call index 0445b367a2a..2c687c13c6a 160000 --- a/utils/ports-of-call +++ b/utils/ports-of-call @@ -1 +1 @@ -Subproject commit 0445b367a2a55f6837f8b694797503e16176b6d6 +Subproject commit 2c687c13c6adb34b01ba56c34d15a4bed505c17f From b8c8627fd376d51d4d034030797902cefdcdc3a4 Mon Sep 17 00:00:00 2001 From: Jonah Miller Date: Fri, 8 May 2026 18:00:56 -0400 Subject: [PATCH 04/14] no need to explicitly test C++20 now --- .github/workflows/tests.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9b164e416af..28ea411ffe3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -44,7 +44,6 @@ jobs: -DSINGULARITY_PLUGINS=$(pwd)/../example/plugin \ -DCMAKE_LINKER=ld.gold \ -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_CXX_STANDARD=20 \ .. #-DSINGULARITY_TEST_PYTHON=ON \ #-DSINGULARITY_TEST_STELLAR_COLLAPSE=ON \ From df19cc7a98ec155c058869c9bdaa673ef1612681 Mon Sep 17 00:00:00 2001 From: Jonah Miller Date: Fri, 8 May 2026 18:03:41 -0400 Subject: [PATCH 05/14] # This is a combination of 2 commits. # This is the 1st commit message: update kokkos, kokkos-kernels to 5.1.0 # This is the commit message #2: host-mirror-t --- test/profile_eos.cpp | 2 +- utils/kokkos | 2 +- utils/kokkos-kernels | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/profile_eos.cpp b/test/profile_eos.cpp index 96980e872ce..73167a5b442 100644 --- a/test/profile_eos.cpp +++ b/test/profile_eos.cpp @@ -46,7 +46,7 @@ using RegularGrid1D = Spiner::RegularGrid1D; #ifdef PORTABILITY_STRATEGY_KOKKOS using RView = Kokkos::View; -using RMirror = typename RView::HostMirror; +using RMirror = typename RView::host_mirror_type; #endif constexpr Real RHO_MIN = 1e-2; // g/cm^3 diff --git a/utils/kokkos b/utils/kokkos index 15dc143e5f3..3ec81abe181 160000 --- a/utils/kokkos +++ b/utils/kokkos @@ -1 +1 @@ -Subproject commit 15dc143e5f39949eece972a798e175c4b463d4b8 +Subproject commit 3ec81abe1816109f6f62ac48cef41921f91a4d00 diff --git a/utils/kokkos-kernels b/utils/kokkos-kernels index 957ac84922a..2c7c3c301e8 160000 --- a/utils/kokkos-kernels +++ b/utils/kokkos-kernels @@ -1 +1 @@ -Subproject commit 957ac84922a25ac76d0c67815f4cdfc243897f6a +Subproject commit 2c7c3c301e810c7ebde0f154db900850a108ddb0 From 99ca23e8df7f9fe026732a3cc88211d166ce9af5 Mon Sep 17 00:00:00 2001 From: Jonah Maxwell Miller Date: Fri, 17 Jul 2026 22:50:06 -0400 Subject: [PATCH 06/14] # This is a combination of 4 commits. # This is the 1st commit message: update kokkos, kokkos-kernels to 5.1.0 # This is the commit message #2: host-mirror-t # This is the commit message #3: remove C++20 from cmake # This is the commit message #4: revert test on github to main --- .github/workflows/tests.yml | 1 + CMakeLists.txt | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 28ea411ffe3..9b164e416af 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -44,6 +44,7 @@ jobs: -DSINGULARITY_PLUGINS=$(pwd)/../example/plugin \ -DCMAKE_LINKER=ld.gold \ -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_CXX_STANDARD=20 \ .. #-DSINGULARITY_TEST_PYTHON=ON \ #-DSINGULARITY_TEST_STELLAR_COLLAPSE=ON \ diff --git a/CMakeLists.txt b/CMakeLists.txt index 2556ab46913..1f9ac19ab3b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -247,12 +247,12 @@ if(SINGULARITY_BUILD_PYTHON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) endif() -# require at least C++20, but allow newer versions to become a client requirement if +# require at least C++17, 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_20) + target_compile_features(singularity-eos_Interface INTERFACE cxx_std_17) endif() # checks if this is our build, or we've been imported via `add_subdirectory` NB: @@ -650,7 +650,8 @@ target_compile_options( # `-Wclass-memaccess now default with -Wall but we explicitly # manage this ourselves in our serialization routines. $<$,$>:-Wno-class-memaccess> - # Disable finite-math-only in Debug builds to allow NaN/Inf checks (Intel compilers) + # Intel compilers enable -ffast-math even in Debug builds. This disables finite-math-only in + # Debug builds to allow NaN/Inf checks without pages of warnings $<$,$,$>>:-fno-finite-math-only> ) if (SINGULARITY_STRICT_WARNINGS) From 05d2daa95a8d57117055676cb1617acd758b40d3 Mon Sep 17 00:00:00 2001 From: Jonah Maxwell Miller Date: Fri, 17 Jul 2026 22:50:56 -0400 Subject: [PATCH 07/14] update kokkos, kokkos-kernels to 5.1.0 change to 2026-07-07 deployment --- .gitlab/kessel.sh | 5 +++-- CMakeLists.txt | 7 ++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.gitlab/kessel.sh b/.gitlab/kessel.sh index 7ead80d40fd..c20507ff00a 100644 --- a/.gitlab/kessel.sh +++ b/.gitlab/kessel.sh @@ -1,10 +1,11 @@ -export DEPLOYMENT_VERSION_CURRENT_DEFAULT="2026-03-03" +export DEPLOYMENT_VERSION_CURRENT_DEFAULT="2026-07-07" export DEPLOYMENT_VERSION_DEFAULT="${DEPLOYMENT_VERSION_DEFAULT:-$DEPLOYMENT_VERSION_CURRENT_DEFAULT}" SCRIPT_PATH=${BASH_SOURCE[0]:-${(%):-%x}} PARENT_DIR="$( cd "$( dirname "${SCRIPT_PATH}" )" &>/dev/null && pwd )" if [ -n "$DEPLOYMENT_MR" ]; then - export DEPLOYMENT_VERSION=${DEPLOYMENT_VERSION:-mr/$DEPLOYMENT_MR/$DEPLOYMENT_VERSION_DEFAULT} + DEPLOYMENT_MR_ID=$(printf "%08d\n" "${DEPLOYMENT_MR}") + export DEPLOYMENT_VERSION=${DEPLOYMENT_VERSION:-mr/$DEPLOYMENT_MR_ID/$DEPLOYMENT_VERSION_DEFAULT} else export DEPLOYMENT_VERSION=${DEPLOYMENT_VERSION:-$DEPLOYMENT_VERSION_DEFAULT} fi diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f9ac19ab3b..98d39881118 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -247,12 +247,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: @@ -650,9 +650,6 @@ target_compile_options( # `-Wclass-memaccess now default with -Wall but we explicitly # manage this ourselves in our serialization routines. $<$,$>:-Wno-class-memaccess> - # Intel compilers enable -ffast-math even in Debug builds. This disables finite-math-only in - # Debug builds to allow NaN/Inf checks without pages of warnings - $<$,$,$>>:-fno-finite-math-only> ) if (SINGULARITY_STRICT_WARNINGS) target_compile_options(singularity-eos_Interface INTERFACE From f8ca2137de57c622ca7c431263abb7b4cfa4598f Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Tue, 14 Jul 2026 17:51:22 -0600 Subject: [PATCH 08/14] ci: switch to 2026-07-07 deployment --- .gitlab-ci.yml | 19 +++++++++++++++---- .gitlab/common.yml | 26 +++++++++----------------- .gitlab/kessel.sh | 2 +- test/compare_on_data.cpp | 2 +- test/compare_on_dataset.cpp | 2 +- test/compare_to_eospac.cpp | 7 +++---- test/profile_eospac.cpp | 2 +- 7 files changed, 31 insertions(+), 29 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e31b4cef754..a92d68642b6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -204,6 +204,17 @@ rocinante_craympich_gcc: SUBMIT_ON_ERROR: false # Compute nodes don't have network access, don't try to send from compute node SUBMIT_AFTER: "${ENABLE_CDASH}" # after_script runs on a network connected system +venadito_craympich_cuda_gracehopper_gcc: + extends: [.ascgit_job, .venadito_job, .venadito_regular_job, .build_and_test] + needs: + - prereq_offline_deps + variables: + SPACK_ENV_NAME: craympich-cuda-gracehopper-gcc + SUBMIT_TO_CDASH: false + BUILD_WITH_CTEST: "${ENABLE_CDASH}" + SUBMIT_ON_ERROR: false # Compute nodes don't have network access, don't try to send from compute node + SUBMIT_AFTER: "${ENABLE_CDASH}" # after_script runs on a network connected system + rocinante_craympich_fortran_gcc: extends: [.ascgit_job, .rocinante_job, .rocinante_regular_job, .build_and_test] needs: @@ -215,23 +226,23 @@ rocinante_craympich_fortran_gcc: SUBMIT_ON_ERROR: false # Compute nodes don't have network access, don't try to send from compute node SUBMIT_AFTER: "${ENABLE_CDASH}" # after_script runs on a network connected system -rzvernal_craympich_rocm_mi250_cce: +rzvernal_craympich_rocm_mi250_rocmcc: extends: [.ascgit_job, .rzvernal_job, .rzvernal_regular_job, .build_and_test] needs: - prereq_offline_deps variables: - SPACK_ENV_NAME: craympich-rocm-gfx90a-cce + SPACK_ENV_NAME: craympich-rocm-gfx90a-rocmcc SUBMIT_TO_CDASH: false BUILD_WITH_CTEST: "${ENABLE_CDASH}" SUBMIT_ON_ERROR: false # Compute nodes don't have network access, don't try to send from compute node SUBMIT_AFTER: "${ENABLE_CDASH}" # after_script runs on a network connected system -rzadams_craympich_rocm_mi300_cce: +rzadams_craympich_rocm_mi300_rocmcc: extends: [.ascgit_job, .rzadams_job, .rzadams_regular_job, .build_and_test] needs: - prereq_offline_deps variables: - SPACK_ENV_NAME: craympich-rocm-gfx942-cce + SPACK_ENV_NAME: craympich-rocm-gfx942-rocmcc SUBMIT_TO_CDASH: false BUILD_WITH_CTEST: "${ENABLE_CDASH}" SUBMIT_ON_ERROR: false # Compute nodes don't have network access, don't try to send from compute node diff --git a/.gitlab/common.yml b/.gitlab/common.yml index 0410bb66130..2c56c5d2526 100644 --- a/.gitlab/common.yml +++ b/.gitlab/common.yml @@ -74,30 +74,22 @@ default: - if: $ENABLED_CLUSTERS =~ /rocinante/ && $CI_PIPELINE_SOURCE == "web" - if: $ENABLED_CLUSTERS =~ /rocinante/ && $CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH -.venado_job: +.venadito_job: variables: - CLUSTER: venado - SCHEDULER_PARAMETERS: "-N 1 -A asc-ci_g -p ci_g --qos ci_g --reservation ci_g --time=02:00:00" + CLUSTER: venadito + SCHEDULER_PARAMETERS: "-N 1 -A vt-ci_g --reservation ci_g --time=02:00:00" tags: - - venado + - venadito - batch -.venado_shell_gpu_job: - variables: - CLUSTER: venado - tags: - - venado - - shell - - gpu - -.venado_regular_job: +.venadito_regular_job: variables: CTEST_MODE: Continuous rules: - - if: $ENABLED_CLUSTERS =~ /venado/ && $GITLAB_USER_LOGIN =~ $VENADO_USERS && $CI_PIPELINE_SOURCE == "merge_request_event" - - if: $ENABLED_CLUSTERS =~ /venado/ && $GITLAB_USER_LOGIN =~ $VENADO_USERS && $CI_PIPELINE_SOURCE == "schedule" - - if: $ENABLED_CLUSTERS =~ /venado/ && $GITLAB_USER_LOGIN =~ $VENADO_USERS && $CI_PIPELINE_SOURCE == "web" - - if: $ENABLED_CLUSTERS =~ /venado/ && $GITLAB_USER_LOGIN =~ $VENADO_USERS && $CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + - if: $ENABLED_CLUSTERS =~ /venadito/ && $CI_PIPELINE_SOURCE == "merge_request_event" + - if: $ENABLED_CLUSTERS =~ /venadito/ && $CI_PIPELINE_SOURCE == "schedule" + - if: $ENABLED_CLUSTERS =~ /venadito/ && $CI_PIPELINE_SOURCE == "web" + - if: $ENABLED_CLUSTERS =~ /venadito/ && $CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH .rzansel_job: tags: diff --git a/.gitlab/kessel.sh b/.gitlab/kessel.sh index c20507ff00a..dcf2f4e944a 100644 --- a/.gitlab/kessel.sh +++ b/.gitlab/kessel.sh @@ -27,7 +27,7 @@ fi _KESSEL_WORKFLOW_DEPLOYMENT="$KESSEL_WORKFLOW_DEPLOYMENT" export KESSEL_WORKFLOW_DEPLOYMENT="${KESSEL_WORKFLOW_DEPLOYMENT:-${TMPDIR:-/tmp}/$USER-ci-envs}" -if [ "$SYSTEM_NAME" == "darwin" ] || [ "$SYSTEM_NAME" == "rocinante" ]; then +if [ "$SYSTEM_NAME" == "darwin" ] || [ "$SYSTEM_NAME" == "rocinante" ] || [ "$SYSTEM_NAME" == "venadito" ]; then export KESSEL_DEPLOYMENT=${KESSEL_DEPLOYMENT:-/usr/projects/xcap/oss/deployments/$DEPLOYMENT_VERSION/$SYSTEM_NAME} elif [ "$SYSTEM_NAME" == "rzadams" ] || [ "$SYSTEM_NAME" == "rzvernal" ] || [ "$SYSTEM_NAME" == "elcapitan" ] || [ "$SYSTEM_NAME" == "tuolumne" ]; then export KESSEL_DEPLOYMENT=${KESSEL_DEPLOYMENT:-/usr/workspace/xcap/oss/deployments/$DEPLOYMENT_VERSION/$SYSTEM_NAME} diff --git a/test/compare_on_data.cpp b/test/compare_on_data.cpp index 12f4f1f8208..790acb9bd8b 100644 --- a/test/compare_on_data.cpp +++ b/test/compare_on_data.cpp @@ -55,7 +55,7 @@ using Spiner::RegularGrid1D; #ifdef PORTABILITY_STRATEGY_KOKKOS using RView = Kokkos::View; -using RMirror = typename RView::HostMirror; +using RMirror = typename RView::host_mirror_type; #endif struct Data { diff --git a/test/compare_on_dataset.cpp b/test/compare_on_dataset.cpp index f100f5cc052..a2f0ceb1bbf 100644 --- a/test/compare_on_dataset.cpp +++ b/test/compare_on_dataset.cpp @@ -57,7 +57,7 @@ using BEOS = SpinerEOSDependsRhoT; #ifdef PORTABILITY_STRATEGY_KOKKOS using RView = Kokkos::View; -using RMirror = typename RView::HostMirror; +using RMirror = typename RView::host_mirror_type; using KokkosAtomic = Kokkos::MemoryTraits; #endif diff --git a/test/compare_to_eospac.cpp b/test/compare_to_eospac.cpp index bfe3cbc3063..f2bffbe008b 100644 --- a/test/compare_to_eospac.cpp +++ b/test/compare_to_eospac.cpp @@ -160,8 +160,7 @@ int main(int argc, char *argv[]) { #ifdef PORTABILITY_STRATEGY_KOKKOS using RView = Kokkos::View; RView pressSpinerDView("pressSpinerDevice", nFineRho * nFineT); - typename RView::HostMirror pressSpinerHView = - Kokkos::create_mirror_view(pressSpinerDView); + auto pressSpinerHView = Kokkos::create_mirror_view(pressSpinerDView); DataBox pressSpiner_d(pressSpinerDView.data(), nFineRho, nFineT); DataBox pressSpiner_hm(pressSpinerHView.data(), nFineRho, nFineT); #else @@ -181,8 +180,8 @@ int main(int argc, char *argv[]) { #ifdef PORTABILITY_STRATEGY_KOKKOS RView tempSpinerDView("tempSpinerDevice", nFineRho * nFineT); RView tempSpinerDViewE("tempSpinerSieDevice", nFineRho * nFineT); - RView::HostMirror tempSpinerHView = Kokkos::create_mirror_view(tempSpinerDView); - RView::HostMirror tempSpinerHViewE = Kokkos::create_mirror_view(tempSpinerDViewE); + auto tempSpinerHView = Kokkos::create_mirror_view(tempSpinerDView); + auto tempSpinerHViewE = Kokkos::create_mirror_view(tempSpinerDViewE); DataBox tempSpiner_d(tempSpinerDView.data(), nFineRho, nFineT); DataBox tempSpiner_hm(tempSpinerHView.data(), nFineRho, nFineT); DataBox tempSpinerE_d(tempSpinerDViewE.data(), nFineRho, nFineT); diff --git a/test/profile_eospac.cpp b/test/profile_eospac.cpp index 4a051d87fbe..481b5bc9b08 100644 --- a/test/profile_eospac.cpp +++ b/test/profile_eospac.cpp @@ -49,7 +49,7 @@ using RegularGrid1D = Spiner::RegularGrid1D; #ifdef PORTABILITY_STRATEGY_KOKKOS using RView = Kokkos::View; -using RMirror = typename RView::HostMirror; +using RMirror = typename RView::host_mirror_type; #endif constexpr Real RHO_MIN = 1e-2; // g/cm^3 From 47862e680fa4e0ec2cc09896729310c2445a229c Mon Sep 17 00:00:00 2001 From: Jonah Maxwell Miller Date: Thu, 16 Jul 2026 13:03:21 -0400 Subject: [PATCH 09/14] address cuda warnings --- test/test_eos_modifiers.cpp | 2 +- test/test_eos_tabulated.cpp | 3 --- test/test_indexable_types.cpp | 5 +++++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/test/test_eos_modifiers.cpp b/test/test_eos_modifiers.cpp index c4e99d06ccb..6eae711e4b3 100644 --- a/test/test_eos_modifiers.cpp +++ b/test/test_eos_modifiers.cpp @@ -95,7 +95,7 @@ using variadic_utils::transform_variadic_list; static constexpr const auto full_eos_list = tl{}; // modifiers that get applied to all eos's -static constexpr const auto apply_to_all = al{}; +// static constexpr const auto apply_to_all = al{}; // variadic list of eos's with shifted or scaled modifiers static constexpr const auto shifted = transform_variadic_list(full_eos_list, al{}); diff --git a/test/test_eos_tabulated.cpp b/test/test_eos_tabulated.cpp index 352aaff16d9..b3d457f6e05 100644 --- a/test/test_eos_tabulated.cpp +++ b/test/test_eos_tabulated.cpp @@ -50,9 +50,6 @@ namespace thermalqs = singularity::thermalqs; using singularity::variadic_utils::np; const std::string eosName = "../materials.sp5"; -const std::string airName = "air"; -const std::string steelName = "stainless steel 347"; -const std::string tinName = "tin"; #ifdef SPINER_USE_HDF #ifdef SINGULARITY_TEST_SESAME diff --git a/test/test_indexable_types.cpp b/test/test_indexable_types.cpp index 390a9e22726..343728adfd6 100644 --- a/test/test_indexable_types.cpp +++ b/test/test_indexable_types.cpp @@ -34,10 +34,15 @@ class ManualLambda_t { public: constexpr static size_t length = 3; ManualLambda_t() = default; + PORTABLE_FORCEINLINE_FUNCTION const Real &operator[](const std::size_t idx) const { return data_[idx]; } + PORTABLE_FORCEINLINE_FUNCTION Real &operator[](const std::size_t idx) { return data_[idx]; } + PORTABLE_FORCEINLINE_FUNCTION Real &operator[](const MeanIonizationState &zbar) { return data_[0]; } + PORTABLE_FORCEINLINE_FUNCTION Real &operator[](const ElectronFraction &Ye) { return data_[1]; } + PORTABLE_FORCEINLINE_FUNCTION Real &operator[](const LogDensity &lRho) { return data_[2]; } private: From 29ce21f5a740fb1d1b7b58ac68764ccda1db954c Mon Sep 17 00:00:00 2001 From: Jonah Maxwell Miller Date: Thu, 16 Jul 2026 17:36:42 -0400 Subject: [PATCH 10/14] add check that adds kokkos initialize for python bindings --- doc/sphinx/src/python.rst | 76 +++++++++++++++++++++++++++----- example/get_sound_speed_press.py | 11 ++++- python/module.cpp | 50 ++++++++++++++++++++- python/module.hpp | 57 ++++++++++++++++++++++++ test/python_bindings.py | 15 ++++++- test/test_error_utils.cpp | 13 ++++++ 6 files changed, 207 insertions(+), 15 deletions(-) diff --git a/doc/sphinx/src/python.rst b/doc/sphinx/src/python.rst index aeb69f54485..94fecf45c69 100644 --- a/doc/sphinx/src/python.rst +++ b/doc/sphinx/src/python.rst @@ -20,20 +20,62 @@ Example :: - from singularity_eos import IdealGas + import singularity_eos as eos - # Parameters for ideal gas - gm1 = 0.6 - Cv = 2 - eos = IdealGas(gm1, Cv) - rho = ... - sie = ... - P = eos.PressureFromDensityInternalEnergy(rho, sie) - eos.Finalize() + with eos.context(): + # Parameters for ideal gas + gm1 = 0.6 + Cv = 2 + ideal_gas = eos.IdealGas(gm1, Cv) + rho = ... + sie = ... + P = ideal_gas.PressureFromDensityInternalEnergy(rho, sie) + ideal_gas.Finalize() + + # Leaving the context does not finalize the shared runtime. + eos.finalize() which should work for both scalar and vector (via numpy array) calls. A more elaborate example can be found in -``examples/get_sound_speed_press.py``. +``example/get_sound_speed_press.py``. + +Runtime Lifecycle +----------------- + +The module owns a single runtime context, returned by :func:`context`. Entering +the context calls :func:`initialize`, but leaving the context does **not** call +:func:`finalize`. This lets EOS objects and work created in separate ``with`` +blocks share the same runtime safely. + +When the bindings are built with Kokkos support, :func:`initialize` first +checks whether Kokkos is already initialized. If another library initialized +Kokkos, Singularity EOS uses that runtime but does not take ownership of it; +in that case, :func:`finalize` does not shut Kokkos down. If Singularity EOS +starts Kokkos, it records ownership and :func:`finalize` shuts it down. + +Calls to :func:`initialize` are idempotent while the runtime is active. Kokkos +cannot be initialized again after it has been finalized, so calling +:func:`initialize` after finalization raises an exception. When the bindings +are built without Kokkos, both lifecycle functions are no-ops. + +The runtime may also be managed explicitly without a context manager: + +:: + + import singularity_eos as eos + + eos.initialize() + try: + ideal_gas = eos.IdealGas(0.6, 2.0) + # Use ideal_gas here. + ideal_gas.Finalize() + finally: + eos.finalize() + +Call :func:`finalize` only after all Kokkos-dependent work and cleanup are +complete. An EOS object's ``Finalize()`` method releases resources owned by +that EOS object; it is separate from the module-level :func:`finalize`, which +manages the shared runtime. .. currentmodule:: singularity_eos @@ -41,6 +83,7 @@ Classes ------- List may not be complete. + * :class:`SingularityContext` * :class:`IdealGas` * :class:`Gruneisen` * :class:`JWL` @@ -79,10 +122,18 @@ along with any modifier arguments. ``Scaled(Shifted(IdealGas(gm1, Cv), shift), scale)`` will return a Python object that wraps the ``ScaledEOS>`` C++ type. -Class Reference ---------------- +API Reference +------------- List may not be complete. +.. autofunction:: initialize + +.. autofunction:: finalize + +.. autofunction:: context + +.. autoclass:: SingularityContext + .. autoclass:: EOSState :members: @@ -115,3 +166,4 @@ List may not be complete. .. autofunction:: UnitSystem +This file was made in part with generative AI. diff --git a/example/get_sound_speed_press.py b/example/get_sound_speed_press.py index 9d1591dfd4c..5ba7c3bd8b2 100644 --- a/example/get_sound_speed_press.py +++ b/example/get_sound_speed_press.py @@ -12,12 +12,18 @@ # publicly and display publicly, and to permit others to do so. #------------------------------------------------------------------------------ -from singularity_eos import IdealGas, thermalqs +# This file was made in part with generative AI. + +from singularity_eos import IdealGas, finalize, initialize, thermalqs import numpy as np import math SMALL = 1e-20 # to avoid dividing by zero +# Initialize the shared runtime before making EOS calls. This is idempotent and +# does not take ownership of Kokkos if another component initialized it first. +initialize() + def PressureSoundSpeedFromDensityEnergyDensity(rho, uu, eos, # inputs P, cs, Ncells # outputs ): @@ -123,3 +129,6 @@ def print_results(): # This usually only does anything if you're on device, but for GPU-data it # will call the appropriate destructor. eos1.Finalize() + +# Shut down the shared runtime if this module initialized it. +finalize() diff --git a/python/module.cpp b/python/module.cpp index 0472b2e28b2..4ec5b8c2da6 100644 --- a/python/module.cpp +++ b/python/module.cpp @@ -11,10 +11,58 @@ // prepare derivative works, distribute copies to the public, perform // publicly and display publicly, and to permit others to do so. //------------------------------------------------------------------------------ + +// This file was made in part with generative AI. + // clang-format off #include "module.hpp" PYBIND11_MODULE(singularity_eos, m) { + py::class_( + m, "SingularityContext", + "Module-owned context manager for the Singularity EOS runtime.\n\n" + "Use the singleton returned by ``singularity_eos.context()`` rather than " + "constructing this type directly. Entering the context initializes the " + "runtime, but leaving it does not finalize the runtime.") + .def("__enter__", + [](SingularityContext &self) -> SingularityContext & { + InitializeSingularity(); + return self; + }, + py::return_value_policy::reference_internal, + "Initialize the shared runtime and return this context." + ) + .def("__exit__", + [](SingularityContext &self, + py::object, + py::object, + py::object) { + (void)self; + return false; // do not suppress exceptions + }, + "Leave the context without finalizing the shared runtime." + ); + + m.def("context", + []() -> SingularityContext & { + return SingularityContext::Instance(); + }, + py::return_value_policy::reference, + "Return the module-owned Singularity EOS runtime context.\n\n" + "The same context is returned on every call. Entering it initializes " + "the runtime; leaving it does not finalize the runtime."); + m.def("initialize", &InitializeSingularity, + "Initialize the Singularity EOS runtime if necessary.\n\n" + "When built with Kokkos, this starts Kokkos only if it is not already " + "initialized. Calls are idempotent while Kokkos remains active. If " + "another component initialized Kokkos first, Singularity EOS does not " + "take ownership of its lifetime."); + m.def("finalize", &FinalizeSingularity, + "Finalize the Singularity EOS runtime when owned by this module.\n\n" + "When built with Kokkos, this finalizes Kokkos only if " + "singularity_eos.initialize() or singularity_eos.context() originally " + "started it. Kokkos cannot be initialized again after finalization."); + py::class_(m, "EOSState") .def(py::init()) .def_readwrite("density", &EOSState::density) @@ -157,5 +205,5 @@ PYBIND11_MODULE(singularity_eos, m) { eos_units.attr("ThermalUnits") = eos_units_init::thermal_units_init_tag; eos_units.attr("LengthTimeUnits") = eos_units_init::length_time_units_init_tag; - m.doc() = "Singularity EOS Python Bindings"; + m.doc() = "Python bindings for Singularity EOS."; } diff --git a/python/module.hpp b/python/module.hpp index 834292f13e9..06e93174ded 100644 --- a/python/module.hpp +++ b/python/module.hpp @@ -11,11 +11,15 @@ // prepare derivative works, distribute copies to the public, perform // publicly and display publicly, and to permit others to do so. //------------------------------------------------------------------------------ + +// This file was made in part with generative AI. + // clang-format off #include #include #include #include +#include #include #include @@ -100,6 +104,59 @@ class PyArrayHelper { const std::size_t stride_; }; +class SingularityContext { +public: + SingularityContext(const SingularityContext&) = delete; + SingularityContext& operator=(const SingularityContext&) = delete; + + SingularityContext(SingularityContext&&) = delete; + SingularityContext& operator=(SingularityContext&&) = delete; + + static SingularityContext &Instance() { + static SingularityContext context; + return context; + } + + void Initialize() { +#if defined(PORTABILITY_STRATEGY_KOKKOS) + if (Kokkos::is_finalized()) { + throw std::runtime_error("Kokkos has already been finalized " + "and cannot be initialized again."); + } + + if (!Kokkos::is_initialized()) { + Kokkos::initialize(); + owns_kokkos_ = true; + } +#endif // defined(PORTABILITY_STRATEGY_KOKKOS) + } + + void Finalize() { +#if defined(PORTABILITY_STRATEGY_KOKKOS) + if (owns_kokkos_ && Kokkos::is_initialized() && !Kokkos::is_finalized()) { + Kokkos::finalize(); + } +#endif // defined(PORTABILITY_STRATEGY_KOKKOS) + owns_kokkos_ = false; + } + + ~SingularityContext() { + Finalize(); + } + +private: + SingularityContext() = default; + + bool owns_kokkos_ = false; +}; + +inline void InitializeSingularity() { + SingularityContext::Instance().Initialize(); +} + +inline void FinalizeSingularity() { + SingularityContext::Instance().Finalize(); +} // so far didn't find a good way of working with template member function pointers // to generalize this without the preprocessor. diff --git a/test/python_bindings.py b/test/python_bindings.py index c900f888a8e..93c02d4c34f 100644 --- a/test/python_bindings.py +++ b/test/python_bindings.py @@ -11,12 +11,18 @@ # prepare derivative works, distribute copies to the public, perform # publicly and display publicly, and to permit others to do so. #------------------------------------------------------------------------------ + +# This file was made in part with generative AI. + import math import unittest import singularity_eos import numpy as np from numpy.testing import assert_allclose +def setUpModule(): + singularity_eos.initialize() + class EOSTestBase(object): def assertIsClose(self, a, b, eps=5e-2): rel = abs(b - a) / (abs(a + b) + 1e-20) @@ -53,6 +59,12 @@ def assertEqualEOS(self, test_e, ref_e): self.assertIsClose(test_e.MinimumTemperature(), ref_e.MinimumTemperature(), 1.e-15) class EOS(unittest.TestCase): + def testContextSingleton(self): + context = singularity_eos.context() + self.assertIs(context, singularity_eos.context()) + with context as entered_context: + self.assertIs(entered_context, context) + def testConstants(self): from singularity_eos import thermalqs self.assertEqual(thermalqs.all_values, thermalqs.none | @@ -693,4 +705,5 @@ def test_particle_vel_and_hugoniot(self): self.assertIsClose(pres, true_pres, 1e-12) if __name__ == "__main__": - unittest.main() + with singularity_eos.context(): + unittest.main() diff --git a/test/test_error_utils.cpp b/test/test_error_utils.cpp index c2a3d67ec4f..84bc2232d67 100644 --- a/test/test_error_utils.cpp +++ b/test/test_error_utils.cpp @@ -69,21 +69,34 @@ void CheckNormalOrZeroClassificationOnDevice(const char *type_name) { "Check error_utils::is_normal_or_zero on device", 0, 1, PORTABLE_LAMBDA(const int /*i*/, int &nw) { nw += !condition(T{0}); + printf("0: %d\n", !condition(T{0})); nw += !condition(-T{0}); + printf("-0: %d\n", !condition(-T{0})); nw += !condition(static_cast(1.382884838243760e+06)); + printf("afloat: %d\n", !condition(static_cast(1.382884838243760e+06))); nw += !condition(limits::min()); + printf("min: %d\n", !condition(limits::min())); nw += !condition(-limits::min()); + printf("-min: %d\n", !condition(-limits::min())); + if constexpr (limits::has_denorm != std::denorm_absent) { nw += condition(limits::denorm_min()); + printf("denorm_min: %d\n", condition(limits::denorm_min())); nw += condition(-limits::denorm_min()); + printf("-denorm_min: %d\n", condition(-limits::denorm_min())); } nw += condition(limits::quiet_NaN()); + printf("quiet_nan: %d\n", condition(limits::quiet_NaN())); nw += condition(limits::infinity()); + printf("infinity: %d\n", condition(limits::infinity())); nw += condition(-limits::infinity()); + printf("-infinity: %d\n", condition(-limits::infinity())); nw += !condition((limits::max() / factor) * T{0.5}); + printf("maxfac: %d\n", !condition((limits::max() / factor) * T{0.5})); nw += condition((limits::max() / factor) * T{2.0}); + printf("maxfac: %d\n", condition((limits::max() / factor) * T{2.0})); }, nwrong); From c91b4822951ae8432d123f4aff44a9535c089472 Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Thu, 16 Jul 2026 22:53:08 -0600 Subject: [PATCH 11/14] ci: use rocmcc envs on darwin --- .gitlab-ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a92d68642b6..fcd6f895d90 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -175,21 +175,21 @@ openmpi_fortran_kokkos_static_openmp_clang: SPACK_ENV_NAME: openmpi-fortran-kokkos-static-openmp-clang SUBMIT_TO_CDASH: "${ENABLE_CDASH}" -openmpi_rocm_gcc: +openmpi_rocm_rocmcc: extends: [.ascgit_job, .darwin_job, .darwin_regular_job, .build_and_test] needs: - prereq_offline_deps variables: - SPACK_ENV_NAME: openmpi-rocm-gcc + SPACK_ENV_NAME: openmpi-rocm-rocmcc SCHEDULER_PARAMETERS: "-n 1 --qos=debug -p shared-gpu-amd-mi250" SUBMIT_TO_CDASH: "${ENABLE_CDASH}" -openmpi_fortran_rocm_gcc: +openmpi_fortran_rocm_rocmcc: extends: [.ascgit_job, .darwin_job, .darwin_regular_job, .build_and_test] needs: - prereq_offline_deps variables: - SPACK_ENV_NAME: openmpi-fortran-rocm-gcc + SPACK_ENV_NAME: openmpi-fortran-rocm-rocmcc SCHEDULER_PARAMETERS: "-n 1 --qos=debug -p shared-gpu-amd-mi250" SUBMIT_TO_CDASH: "${ENABLE_CDASH}" From 88ce8907b59ce77ce33899556173ba0d40b3a417 Mon Sep 17 00:00:00 2001 From: Richard Berger Date: Fri, 17 Jul 2026 01:16:46 -0600 Subject: [PATCH 12/14] ci: add missing build env in test executions --- .kessel/workflows/cmake.py | 1 + test/CMakeLists.txt | 6 ++++++ test/test_error_utils.cpp | 44 +++++++++++++++++++------------------- 3 files changed, 29 insertions(+), 22 deletions(-) diff --git a/.kessel/workflows/cmake.py b/.kessel/workflows/cmake.py index 094ab9503e3..445cd0b22af 100644 --- a/.kessel/workflows/cmake.py +++ b/.kessel/workflows/cmake.py @@ -63,6 +63,7 @@ def test(self, args): """Testing""" self.exec(f""" pushd {self.build_dir} + source "$KESSEL_BUILD_ENV" if [[ -f sesame2spiner/sesame2spiner ]]; then echo "/usr/projects/data/eos/eos-developmental/Sn2162/v01/sn2162-v01.bin" > sesameFilesDir.txt sesame2spiner/sesame2spiner -s materials.sp5 ../sesame2spiner/examples/unit_tests/mats.dat diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 97b676ff91c..b3ccc7c498d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -47,6 +47,12 @@ add_executable( test_generic_indxer.cpp ) +# We want to test things like quiet NaN so finite math/fast-math needs to be disabled. +target_compile_options(eos_infrastructure_tests PRIVATE + $<$:-fno-fast-math;-fno-finite-math-only> + $<$:--ftz=false;--prec-div=true;--prec-sqrt=true> +) + add_executable( eos_tabulated_unit_tests catch2_define.cpp diff --git a/test/test_error_utils.cpp b/test/test_error_utils.cpp index 84bc2232d67..3f910028414 100644 --- a/test/test_error_utils.cpp +++ b/test/test_error_utils.cpp @@ -25,6 +25,12 @@ #include #endif +#if defined(__FINITE_MATH_ONLY__) && (__FINITE_MATH_ONLY__ > 0) +#define SG_FINITE_MATH_ONLY 1 +#else +#define SG_FINITE_MATH_ONLY 0 +#endif // finite math only + namespace error_utils = singularity::error_utils; template @@ -32,6 +38,8 @@ void CheckNormalOrZeroClassification(const char *type_name) { using limits = std::numeric_limits; const T factor = static_cast(error_utils::NORMAL_FACTOR); const auto condition = error_utils::is_normal_or_zero{}; + constexpr bool NANS_DISABLED = + SG_FINITE_MATH_ONLY || (limits::quiet_NaN() == limits::quiet_NaN()); CAPTURE(type_name); @@ -46,14 +54,20 @@ void CheckNormalOrZeroClassification(const char *type_name) { 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)); + + if constexpr (NANS_DISABLED) { + INFO("Compiler does not honor IEEE NaN comparisons on host"); + } else { + REQUIRE_FALSE(condition(limits::quiet_NaN())); + if constexpr (limits::has_infinity) { + REQUIRE_FALSE(condition(limits::infinity())); + REQUIRE_FALSE(condition(-limits::infinity())); + } + } } template @@ -64,42 +78,28 @@ void CheckNormalOrZeroClassificationOnDevice(const char *type_name) { CAPTURE(type_name); + // TODO(JMM): For whatever reason, quiet NaNs do not behave on + // nvidia GPUs. It seems like nvidia does some NaN optimization I + // can't turn off. int nwrong = 0; portableReduce( - "Check error_utils::is_normal_or_zero on device", 0, 1, + "Check error_utils::is_normal_or_zero on device, no NaNs", 0, 1, PORTABLE_LAMBDA(const int /*i*/, int &nw) { nw += !condition(T{0}); - printf("0: %d\n", !condition(T{0})); nw += !condition(-T{0}); - printf("-0: %d\n", !condition(-T{0})); nw += !condition(static_cast(1.382884838243760e+06)); - printf("afloat: %d\n", !condition(static_cast(1.382884838243760e+06))); nw += !condition(limits::min()); - printf("min: %d\n", !condition(limits::min())); nw += !condition(-limits::min()); - printf("-min: %d\n", !condition(-limits::min())); - if constexpr (limits::has_denorm != std::denorm_absent) { nw += condition(limits::denorm_min()); - printf("denorm_min: %d\n", condition(limits::denorm_min())); nw += condition(-limits::denorm_min()); - printf("-denorm_min: %d\n", condition(-limits::denorm_min())); } - nw += condition(limits::quiet_NaN()); - printf("quiet_nan: %d\n", condition(limits::quiet_NaN())); - nw += condition(limits::infinity()); - printf("infinity: %d\n", condition(limits::infinity())); - nw += condition(-limits::infinity()); - printf("-infinity: %d\n", condition(-limits::infinity())); nw += !condition((limits::max() / factor) * T{0.5}); - printf("maxfac: %d\n", !condition((limits::max() / factor) * T{0.5})); nw += condition((limits::max() / factor) * T{2.0}); - printf("maxfac: %d\n", condition((limits::max() / factor) * T{2.0})); }, nwrong); - REQUIRE(nwrong == 0); } From a92c4111e7b861671056104c72b4b29524df6f76 Mon Sep 17 00:00:00 2001 From: Jonah Maxwell Miller Date: Fri, 17 Jul 2026 23:11:55 -0400 Subject: [PATCH 13/14] oops path changed --- test/test_error_utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_error_utils.cpp b/test/test_error_utils.cpp index 3f910028414..93dc3924931 100644 --- a/test/test_error_utils.cpp +++ b/test/test_error_utils.cpp @@ -18,7 +18,7 @@ #include #include -#include +#include #ifndef CATCH_CONFIG_FAST_COMPILE #define CATCH_CONFIG_FAST_COMPILE From 2bf4e334411eb8ad319593a992f3d59c22f75de4 Mon Sep 17 00:00:00 2001 From: Jonah Maxwell Miller Date: Fri, 17 Jul 2026 23:36:44 -0400 Subject: [PATCH 14/14] changelog note for breaking change --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb7a8516976..db383d27344 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - [[PR637]](https://github.com/lanl/singularity-eos/pull/637) Fix is_normal ### Changed (changing behavior/API/variables/...) +- [[PR637]](https://github.com/lanl/singularity-eos/pull/637) For Kokkos 5/C++20, Python bindings now require context manager if run standalone - [[PR641]](https://github.com/lanl/singularity-eos/pull/641) The location of some header files located in `` have moved to ``. ### Infrastructure (changes irrelevant to downstream codes)