From 77974b87d285ac536d59803d9691b83829e00c32 Mon Sep 17 00:00:00 2001 From: Seth R Johnson Date: Wed, 4 Feb 2026 13:54:59 -0500 Subject: [PATCH 1/7] Update executor presets --- scripts/cmake-presets/executor.json | 58 +++++++++++++++++------------ 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/scripts/cmake-presets/executor.json b/scripts/cmake-presets/executor.json index 0ef7e7c8b1..979172da86 100644 --- a/scripts/cmake-presets/executor.json +++ b/scripts/cmake-presets/executor.json @@ -14,27 +14,21 @@ "name": ".base", "hidden": true, "generator": "Ninja", - "inherits": [".spack-env"], "binaryDir": "${sourceDir}/build-${presetName}", "cacheVariables": { "BUILD_SHARED_LIBS": {"type": "BOOL", "value": "ON"}, "BUILD_TESTING": {"type": "BOOL", "value": "ON"}, - "CELERITAS_USE_covfie": {"type": "BOOL", "value": "ON"}, "CELERITAS_USE_CUDA": {"type": "BOOL", "value": "OFF"}, "CELERITAS_USE_DD4hep": {"type": "BOOL", "value": "OFF"}, "CELERITAS_USE_LArSoft": {"type": "BOOL", "value": "OFF"}, - "CELERITAS_USE_HepMC3": {"type": "BOOL", "value": "ON"}, "CELERITAS_USE_HIP": {"type": "BOOL", "value": "OFF"}, - "CELERITAS_USE_Geant4": {"type": "BOOL", "value": "ON"}, "CELERITAS_USE_MPI": {"type": "BOOL", "value": "OFF"}, "CELERITAS_USE_OpenMP": {"type": "BOOL", "value": "OFF"}, + "CELERITAS_USE_PNG": {"type": "BOOL", "value": "OFF"}, + "CELERITAS_USE_Python": {"type": "BOOL", "value": "ON"}, "CELERITAS_USE_ROOT": {"type": "BOOL", "value": "OFF"}, "CELERITAS_USE_VecGeom": {"type": "BOOL", "value": "OFF"}, "CELERITAS_BUILD_TESTS": {"type": "BOOL", "value": "ON"}, - "CELERITAS_BUILTIN_CLI11": {"type": "BOOL", "value": "OFF"}, - "CELERITAS_BUILTIN_G4VG": {"type": "BOOL", "value": "OFF"}, - "CELERITAS_BUILTIN_nlohmann_json": {"type": "BOOL", "value": "OFF"}, - "CELERITAS_BUILTIN_GTest": {"type": "BOOL", "value": "OFF"}, "CMAKE_C_COMPILER_LAUNCHER": null, "CMAKE_CXX_COMPILER_LAUNCHER": null, "CMAKE_EXPORT_COMPILE_COMMANDS": {"type": "BOOL", "value": "ON"}, @@ -47,10 +41,29 @@ "CMAKE_CXX_FLAGS": "-Wall -Wextra -Werror -Wno-error=deprecated -pedantic -fdiagnostics-color=always" } }, + { + "name": ".spackbase", + "hidden": true, + "generator": "Ninja", + "inherits": [".base", ".spack-env"], + "binaryDir": "${sourceDir}/build-${presetName}", + "cacheVariables": { + "CELERITAS_USE_covfie": {"type": "BOOL", "value": "ON"}, + "CELERITAS_USE_HepMC3": {"type": "BOOL", "value": "ON"}, + "CELERITAS_USE_Geant4": {"type": "BOOL", "value": "ON"}, + "CELERITAS_USE_PNG": {"type": "BOOL", "value": "ON"}, + "CELERITAS_USE_Python": {"type": "BOOL", "value": "ON"}, + "CELERITAS_BUILD_TESTS": {"type": "BOOL", "value": "ON"}, + "CELERITAS_BUILTIN_CLI11": {"type": "BOOL", "value": "OFF"}, + "CELERITAS_BUILTIN_G4VG": {"type": "BOOL", "value": "OFF"}, + "CELERITAS_BUILTIN_nlohmann_json": {"type": "BOOL", "value": "OFF"}, + "CELERITAS_BUILTIN_GTest": {"type": "BOOL", "value": "OFF"} + } + }, { "name": "iwyu", "displayName": "Include-what-you-use (see scripts/dev/run-iwyu.sh)", - "inherits": [".base"], + "inherits": [".spackbase"], "cacheVariables": { "CELERITAS_BUILD_TESTS": {"type": "BOOL", "value": "OFF"}, "CELERITAS_DEBUG": {"type": "BOOL", "value": "OFF"} @@ -58,7 +71,7 @@ }, { "name": "min", - "displayName": "Goldfinger minimal build", + "displayName": "Executor minimal build", "inherits": [".base", ".debug", "default"], "environment": { "CMAKE_PREFIX_PATH": "$env{SPACK_ROOT}/var/spack/environments/celeritas-min/.spack-env/view" @@ -72,7 +85,7 @@ { "name": "base", "displayName": "Goldfinger default options", - "inherits": [".ccache", ".base", ".debug", "default"], + "inherits": [".ccache", ".spackbase", ".debug", "default"], "binaryDir": "${sourceDir}/build", "cacheVariables": { "CELERITAS_BUILD_DOCS": {"type": "BOOL", "value": "ON"}, @@ -91,15 +104,10 @@ "CELERITAS_REAL_TYPE": "float" } }, - { - "name": "mini", - "displayName": "Minimal deps", - "inherits": ["minimal", ".base", ".debug"] - }, { "name": "si", "displayName": "With SI units", - "inherits": [".base", ".debug", "default"], + "inherits": [".spackbase", ".debug", "default"], "cacheVariables": { "CELERITAS_UNITS": "SI", "CELERITAS_CORE_GEO": "VecGeom", @@ -109,7 +117,7 @@ { "name": "vecgeom", "displayName": "With vecgeom", - "inherits": [".ccache", ".base", ".debug", "default"], + "inherits": [".ccache", ".spackbase", ".debug", "default"], "cacheVariables": { "CELERITAS_USE_VecGeom": {"type": "BOOL", "value": "ON"} } @@ -119,16 +127,18 @@ "displayName": "With local vecgeom", "inherits": [".ccache", ".base", ".debug", "default"], "environment": { - "CMAKE_PREFIX_PATH": "/opt/spack/opt/spack/tahoe/geant4/11.3.2/7oydgus:/opt/spack/opt/spack/tahoe/googletest/1.17.0/c332ssk:/opt/spack/opt/spack/tahoe/cli11/2.5.0/pmtt6hb:/opt/spack/opt/spack/tahoe/nlohmann-json/3.12.0/dkrlqn3:/opt/spack/opt/spack/tahoe/covfie/0.15.3/tlzukkw:/opt/spack/opt/spack/tahoe/zlib-ng/2.2.4/qbbzbte" + "CMAKE_PREFIX_PATH": "/opt/spack/opt/spack/tahoe/geant4/11.3.2/gykadvh:/opt/spack/opt/spack/tahoe/covfie/0.15.4/r5g2psb:/opt/spack/opt/spack/tahoe/nlohmann-json/3.12.0/7mlcviu:/opt/spack/opt/spack/tahoe/cli11/2.6.1/znuwv76:/opt/spack/opt/spack/tahoe/googletest/1.17.0/rmxgcak" }, "cacheVariables": { + "CELERITAS_BUILTIN_CLI11": {"type": "BOOL", "value": "OFF"}, "CELERITAS_BUILTIN_G4VG": {"type": "BOOL", "value": "ON"}, + "CELERITAS_BUILTIN_GTest": {"type": "BOOL", "value": "OFF"}, + "CELERITAS_BUILTIN_nlohmann_json": {"type": "BOOL", "value": "OFF"}, "CELERITAS_USE_HepMC3": {"type": "BOOL", "value": "OFF"}, "CELERITAS_USE_ROOT": {"type": "BOOL", "value": "OFF"}, "CELERITAS_USE_VecGeom": {"type": "BOOL", "value": "ON"}, "CELERITAS_VecGeom_SURFACE": {"type": "BOOL", "value": "ON"}, "VecGeom_DIR": {"type": "PATH", "value": "/Users/seth/Code/vecgeom/install/lib/cmake/VecGeom"} - } }, { @@ -150,7 +160,7 @@ { "name": "geant4", "displayName": "Using Geant4 navigation", - "inherits": [".base", ".debug", "default"], + "inherits": [".spackbase", ".debug", "default"], "cacheVariables": { "CELERITAS_CORE_GEO": "Geant4", "CELERITAS_UNITS": "CLHEP", @@ -162,7 +172,7 @@ { "name": "reldeb", "displayName": "With ORANGE in optimized+errcheck mode", - "inherits": [".reldeb", ".base"], + "inherits": [".reldeb", ".spackbase"], "cacheVariables": { "CELERITAS_USE_ROOT": {"type": "BOOL", "value": "OFF"} } @@ -178,7 +188,7 @@ { "name": "ndebug", "displayName": "With ORANGE in optimized mode", - "inherits": [".ccache", ".ndebug", ".base"], + "inherits": [".ccache", ".ndebug", ".spackbase"], "cacheVariables": { "CELERITAS_USE_ROOT": {"type": "BOOL", "value": "OFF"} } @@ -186,7 +196,7 @@ { "name": "ndebug-vecgeom", "displayName": "With vecgeom, clhep, dev g4 in optimized mode", - "inherits": [".ccache", ".base", ".ndebug", "vecgeom"], + "inherits": [".ccache", ".spackbase", ".ndebug", "vecgeom"], "cacheVariables": { "CELERITAS_USE_HepMC3": {"type": "BOOL", "value": "OFF"}, "CELERITAS_USE_ROOT": {"type": "BOOL", "value": "OFF"}, From 17f8309eafa010b9324b50a4be0ca4d2baafc7cd Mon Sep 17 00:00:00 2001 From: Seth R Johnson Date: Wed, 4 Feb 2026 13:55:09 -0500 Subject: [PATCH 2/7] Document argument order for FMA --- src/corecel/math/Algorithms.hh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/corecel/math/Algorithms.hh b/src/corecel/math/Algorithms.hh index cafba3accf..1922c4bbc3 100644 --- a/src/corecel/math/Algorithms.hh +++ b/src/corecel/math/Algorithms.hh @@ -537,6 +537,8 @@ inline CELER_FUNCTION T fastpow(T a, T b) /*! * Use fused multiply-add for generic calculations. * + * \f[ x \gets a \times b + y \f] + * * This provides a floating point specialization so that \c fma can be used in * code that is accelerated for floating point calculations but still works * correctly with integer arithmetic. From a80869c47137549b3f685a84f88c2854d770354f Mon Sep 17 00:00:00 2001 From: Seth R Johnson Date: Wed, 4 Feb 2026 13:55:42 -0500 Subject: [PATCH 3/7] Only disable tests if core geometry is vecgeom with cuda/2.x --- test/celeritas/CMakeLists.txt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/test/celeritas/CMakeLists.txt b/test/celeritas/CMakeLists.txt index 2288bfcb65..4080e83a9c 100644 --- a/test/celeritas/CMakeLists.txt +++ b/test/celeritas/CMakeLists.txt @@ -248,7 +248,8 @@ endif() #-----------------------------------------------------------------------------# # Field -if(CELERITAS_USE_VecGeom) +if(CELERITAS_CORE_GEO STREQUAL "VecGeom" + AND (CELERITAS_USE_CUDA OR VecGeom_VERSION VERSION_GREATER_EQUAL "2.0")) # VecGeom surface cannot rebuild the geometry set(_fieldprop_filter FILTER @@ -257,10 +258,6 @@ if(CELERITAS_USE_VecGeom) "SimpleCms*" "Cmse*" ) -else() - # Other geometries should run everything (and some tests e.g. CMSE are - # disabled) - set(_fieldprop_filter) endif() celeritas_add_device_test(field/Fields From f3599e099f269eefbf663783f20edda2daf6dcd1 Mon Sep 17 00:00:00 2001 From: Seth R Johnson Date: Wed, 4 Feb 2026 13:57:20 -0500 Subject: [PATCH 4/7] Improve test soft comparator output --- test/testdetail/TestMacrosImpl.hh | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/test/testdetail/TestMacrosImpl.hh b/test/testdetail/TestMacrosImpl.hh index 7390ffd8a4..f04f9aaf27 100644 --- a/test/testdetail/TestMacrosImpl.hh +++ b/test/testdetail/TestMacrosImpl.hh @@ -100,10 +100,12 @@ struct SoftPrecisionType /*! * Get a soft comparison function from a \c SOFT_NEAR argument. * - * If a floating point value, it defaults to + * If a floating point value, it defaults to EqualOrSoftEqual using explicit + * casts to the given value type to avoid errors with mixed-precision + * arithmetic. */ template -constexpr auto soft_comparator(CT&& cmp_or_tol) +constexpr auto make_soft_comparator(CT&& cmp_or_tol) { if constexpr (std::is_floating_point_v>) { @@ -133,8 +135,6 @@ IsSoftEquivImpl(typename BinaryOp::value_type expected, char const* actual_expr, BinaryOp comp) { - using value_type = typename BinaryOp::value_type; - if (comp(expected, actual)) { return ::testing::AssertionSuccess(); @@ -144,19 +144,19 @@ IsSoftEquivImpl(typename BinaryOp::value_type expected, ::testing::AssertionResult result = ::testing::AssertionFailure(); result << "Value of: " << actual_expr << "\n Actual: " << actual - << "\nExpected: " << expected_expr << "\nWhich is: " << expected - << '\n'; + << "\nExpected: " << expected_expr << "\nWhich is: " << expected; - if (SoftZero{comp.abs()}(expected)) + if (std::fabs(actual - expected) > comp.abs()) { - // Avoid divide by zero errors - result << "(Absolute error " << actual - expected - << " exceeds tolerance " << comp.abs() << ")"; + result << '\n' + << "- absolute error " << actual - expected + << " exceeds tolerance " << comp.abs(); } - else + if (expected != 0 + && std::fabs(actual - expected) > std::fabs(expected) * comp.rel()) { - result << "(Relative error " << (actual - expected) / expected - << " exceeds tolerance " << comp.rel() << ")"; + result << "\n- relative error " << actual / expected - 1 + << " exceeds tolerance " << comp.rel(); } return result; } @@ -214,7 +214,7 @@ template expected_expr, static_cast(actual), actual_expr, - soft_comparator(std::forward(cmp_or_tol))); + make_soft_comparator(std::forward(cmp_or_tol))); } //---------------------------------------------------------------------------// @@ -729,7 +729,7 @@ template expected_expr, actual, actual_expr, - soft_comparator(std::forward(cmp_or_tol))); + make_soft_comparator(std::forward(cmp_or_tol))); } //---------------------------------------------------------------------------// From e846b0924ad187a0a0c1bf51ae3ad5de938db146 Mon Sep 17 00:00:00 2001 From: Seth R Johnson Date: Wed, 4 Feb 2026 14:08:10 -0500 Subject: [PATCH 5/7] Add additional testing and allow failures --- test/geocel/CheckedGeoTrackView.cc | 25 ++++++++++++++++++------ test/geocel/GenericGeoResults.cc | 27 ++++++++++++++++++++++++++ test/geocel/GenericGeoResults.hh | 9 +++++++++ test/geocel/GenericGeoTestInterface.cc | 3 ++- 4 files changed, 57 insertions(+), 7 deletions(-) diff --git a/test/geocel/CheckedGeoTrackView.cc b/test/geocel/CheckedGeoTrackView.cc index 7624830c4a..130df9b75c 100644 --- a/test/geocel/CheckedGeoTrackView.cc +++ b/test/geocel/CheckedGeoTrackView.cc @@ -134,6 +134,10 @@ CheckedGeoTrackView::operator=(GeoTrackInitializer const& init) *t_ = init; CGTV_VALIDATE_NOT_FAILED(*this, "initialization"); CGTV_VALIDATE(*this, !t_->is_outside(), << "initialized outside"); + if (t_->is_on_boundary()) + { + CELER_LOG_LOCAL(warning) << "Started on a boundary: " << *this; + } next_boundary_.reset(); return *this; } @@ -267,12 +271,21 @@ Propagation CheckedGeoTrackView::find_next_step(real_type distance) && !started_on_boundary) { real_type safety = t_->find_safety(distance); - CGTV_VALIDATE(*this, - safety <= result.distance, - << "safety " << repr(safety) - << " exceeds actual distance " << repr(result.distance) - << " to boundary at " << t_->pos() << " in " - << t_->impl_volume_id().get()); + if (!(safety <= result.distance)) + { + auto const& units = this->unit_length(); + + CELER_LOG_LOCAL(warning) + << "Calculated safety " << repr(safety) + << " exceeds actual distance " << repr(result.distance) + << " to boundary at " << t_->pos() << " by " + << repr((safety - result.distance) / units.value) << " [" + << units.label << "]: " << *this; + CGTV_VALIDATE(*this, + safety <= 1.1 * result.distance, + << "calculated safety " << repr(safety) + << " is much too large"); + } } if (result.distance == 0) { diff --git a/test/geocel/GenericGeoResults.cc b/test/geocel/GenericGeoResults.cc index af6ee20a93..c53cd3d69a 100644 --- a/test/geocel/GenericGeoResults.cc +++ b/test/geocel/GenericGeoResults.cc @@ -28,6 +28,18 @@ namespace celeritas { namespace test { +namespace +{ + +template +void erase_after(std::vector& vec, std::size_t idx) +{ + auto iter = vec.begin() + std::min(idx, vec.size()); + vec.erase(iter, vec.end()); +} + +} // namespace + //---------------------------------------------------------------------------// ::testing::AssertionResult IsNormalEquiv(char const* expected_expr, char const* actual_expr, @@ -78,6 +90,16 @@ void GenericGeoTrackingResult::clear_boring_normals() } } +void GenericGeoTrackingResult::fail_at(std::size_t index) +{ + erase_after(volumes, index); + erase_after(volume_instances, index); + erase_after(distances, index); + erase_after(dot_normal, index); + erase_after(halfway_safeties, index); + volumes.push_back("[FAILURE]"); +} + void GenericGeoTrackingResult::print_expected() const { using std::cout; @@ -198,6 +220,11 @@ void GenericGeoVolumeStackResult::print_expected() const "/*** END CODE ***/\n"; } +void GenericGeoVolumeStackResult::fail() +{ + this->volume_instances.push_back("[FAILURE]"); +} + ::testing::AssertionResult IsRefEq(char const* expr1, char const* expr2, GenericGeoVolumeStackResult const& val1, diff --git a/test/geocel/GenericGeoResults.hh b/test/geocel/GenericGeoResults.hh index 2b3432b033..a9b41adf59 100644 --- a/test/geocel/GenericGeoResults.hh +++ b/test/geocel/GenericGeoResults.hh @@ -69,6 +69,12 @@ struct GenericGeoTrackingResult // Whether surface normals are disabled bool disabled_surface_normal() const; + //! Add a failure sentinel at the end + void fail() { this->fail_at(volumes.size()); } + + // Add a failure sentinel at a certain index + void fail_at(std::size_t index); + // Print expected expression to cout void print_expected() const; }; @@ -113,6 +119,9 @@ struct GenericGeoVolumeStackResult static GenericGeoVolumeStackResult from_span(LabelMap const&, Span); void print_expected() const; + + //! Add a failure sentinel at the end + void fail(); }; ::testing::AssertionResult IsRefEq(char const* expected_expr, diff --git a/test/geocel/GenericGeoTestInterface.cc b/test/geocel/GenericGeoTestInterface.cc index 438f8ac37e..f8a76b7363 100644 --- a/test/geocel/GenericGeoTestInterface.cc +++ b/test/geocel/GenericGeoTestInterface.cc @@ -68,7 +68,8 @@ auto GenericGeoTestInterface::track(Real3 const& pos, } \ msg << "at " << d.file << ':' << d.line << " during '" << #ACTION \ << "'"; \ - ADD_FAILURE() << d.what; \ + CELER_LOG(error) << "Failed: " << d.what; \ + result.fail(); \ return result; \ } \ catch (std::exception const& e) \ From b8092675e1a047ceccb9610dbb9325decdf2fc9c Mon Sep 17 00:00:00 2001 From: Seth R Johnson Date: Wed, 4 Feb 2026 16:21:38 -0500 Subject: [PATCH 6/7] Improve field propagation testing - Don't propagate if zero distance for electron_tangent_cross_smallradius - Add try/catch where appropriate - Check for geo failures --- test/celeritas/field/FieldPropagator.test.cc | 251 ++++++++----------- 1 file changed, 98 insertions(+), 153 deletions(-) diff --git a/test/celeritas/field/FieldPropagator.test.cc b/test/celeritas/field/FieldPropagator.test.cc index 56a46a54a8..8a3fb26534 100644 --- a/test/celeritas/field/FieldPropagator.test.cc +++ b/test/celeritas/field/FieldPropagator.test.cc @@ -1031,51 +1031,71 @@ TEST_F(TwoBoxesTest, for (int i : range(2)) { SCOPED_TRACE(i); - auto result = propagate(radius * dtheta); - if (result.boundary) + Propagation result; + result.distance = 0; + result.boundary = false; + + try { - geo.cross_boundary(); + if (!geo.failed()) + { + result = propagate(radius * dtheta); + } + } + catch (CheckedGeoError const& e) + { + CELER_LOG(error) << e.what(); } - boundary.push_back(result.boundary); + if (result.distance > 0) + { + boundary.push_back(result.boundary); + volumes.push_back(this->volume_name(geo)); + } + else + { + // Error sentinel + boundary.push_back(-1); + volumes.push_back("[FAILURE]"); + } distances.push_back(result.distance); substeps.push_back(integrate.count()); - volumes.push_back(this->volume_name(geo)); integrate.reset_count(); } } - std::vector expected_boundary = {1, 1, 1, 1, 1, 0, 1, 0, 1, 0}; + std::vector expected_boundary = {1, 0, 1, 0, 1, 0, 1, 0, 1, 0}; std::vector expected_distances = { 0.0078534718906499, - 0.0028235332722979, + 0.0078539816339745, 0.0044879852658442, - 0.0028259738005751, - 1e-05, + 0.0044879895051283, 1e-05, + 1e-06, 9.9999658622419e-09, 1e-08, 9.9981633254417e-12, 1e-11, }; - std::vector expected_substeps = {1, 25, 1, 12, 1, 1, 1, 1, 1, 1}; + std::vector expected_substeps = {1, 1, 1, 1, 1, 4, 1, 1, 1, 1}; std::vector expected_volumes = { - "world", "inner", - "world", "inner", - "world", - "world", - "world", - "world", - "world", - "world", + "inner", + "inner", + "inner", + "inner", + "inner", + "inner", + "inner", + "inner", }; - EXPECT_VEC_EQ(expected_boundary, boundary); - EXPECT_VEC_NEAR(expected_distances, distances, real_type{.1} * coarse_eps); - EXPECT_VEC_EQ(expected_substeps, substeps); - EXPECT_VEC_EQ(expected_volumes, volumes); + EXPECT_VEC_EQ(expected_boundary, boundary) << repr(boundary); + EXPECT_VEC_NEAR(expected_distances, distances, real_type{.1} * coarse_eps) + << repr(distances); + EXPECT_VEC_EQ(expected_substeps, substeps) << repr(substeps); + EXPECT_VEC_EQ(expected_volumes, volumes) << repr(volumes); } // Heuristic test: plotting points with finer propagation distance show a track @@ -1248,6 +1268,7 @@ TEST_F(SimpleCmsTest, TEST_IF_CELERITAS_DOUBLE(electron_stuck)) // Surface geometry does not intersect the cylinder boundary, so // the track keeps going until the "looping" counter is hit EXPECT_SOFT_EQ(1.0314309658010318e-13, result.distance); + EXPECT_LT(result.distance, 2e-13); EXPECT_FALSE(result.looping); } else @@ -1267,18 +1288,35 @@ TEST_F(SimpleCmsTest, TEST_IF_CELERITAS_DOUBLE(electron_stuck)) { auto integrate = make_mag_field_integrator( field, particle.charge()); + auto propagate = make_field_propagator(integrate, driver_options, particle, geo); - auto result = propagate(30); + Propagation result; + try + { + result = propagate(30); + } + catch (RuntimeError const& e) + { + if (using_surface_vg) + { + EXPECT_TRUE(geo.failed()); + GTEST_SKIP() + << "FIXME: VecGeom surface model fails: " << e.what(); + } + FAIL() << e.what(); + } + + if (using_solids_vg && CELERITAS_VECGEOM_VERSION >= 0x020000) + { + GTEST_SKIP() << "FIXME: VecGeom solid method didn't start in the " + "right place"; + } + EXPECT_EQ(result.boundary, geo.is_on_boundary()); EXPECT_SOFT_NEAR( double{30}, static_cast(integrate.count()), 0.2); - if (using_surface_vg) - { - GTEST_SKIP() << "FIXME: VecGeom surface model fails a boundary " - "requirement."; - } ASSERT_TRUE(geo.is_on_boundary()); if (geo.check_normal()) @@ -1315,7 +1353,6 @@ TEST_F(SimpleCmsTest, TEST_IF_CELERITAS_DOUBLE(vecgeom_failure)) auto calc_radius = [&geo]() { return std::hypot(geo.pos()[0], geo.pos()[1]); }; - bool successful_reentry = false; { auto particle = this->make_particle_view( pdg::electron(), MevEnergy{3.27089632881079409e-02}); @@ -1323,7 +1360,8 @@ TEST_F(SimpleCmsTest, TEST_IF_CELERITAS_DOUBLE(vecgeom_failure)) field, particle.charge()); auto propagate = make_field_propagator(integrate, driver_options, particle, geo); - auto result = propagate(1.39170198361108938e-05); + Propagation result; + EXPECT_NO_THROW(result = propagate(1.39170198361108938e-05)); EXPECT_EQ(result.boundary, geo.is_on_boundary()); EXPECT_EQ("em_calorimeter", this->volume_name(geo)); EXPECT_SOFT_EQ(125.00000000000001, calc_radius()); @@ -1337,132 +1375,26 @@ TEST_F(SimpleCmsTest, TEST_IF_CELERITAS_DOUBLE(vecgeom_failure)) geo.set_dir({-1.31178657592616127e-01, -8.29310561920304168e-01, -5.43172303859124073e-01}); - geo.cross_boundary(); // TODO: hack cross_boundary to handle this case - successful_reentry = (this->volume_name(geo) == "em_calorimeter"); - if (CELERITAS_CORE_GEO == CELERITAS_CORE_GEO_ORANGE) + try { - // ORANGE should successfully reenter. However, under certain - // system configurations, VecGeom will end up in the world volume, - // so we don't test in all cases. - EXPECT_EQ("em_calorimeter", this->volume_name(geo)); - } - else - { - EXPECT_TRUE(scoped_log_.empty()) << scoped_log_; - } - - if (!successful_reentry) - { - // Note that this is expected behavior in Geant4 and VecGeom, as - // it is assumed that the track will actually change volumes at the - // boundary (tangent tracks are not the norm and maybe not properly - // handled). - CELER_LOG(warning) << "Reentry failed for " << cmake::core_geo - << " geometry: post-propagation volume is " - << this->volume_name(geo); - - if (using_solids_vg && CELERITAS_VECGEOM_VERSION < 0x020000) - { - // FIXME: VecGeom 1.x navigation failure (solids model) - EXPECT_EQ("world", this->volume_name(geo)); - } - else - { - EXPECT_EQ("si_tracker", this->volume_name(geo)); - } - - // Interestingly, VecGeom surf and solid models see that surface - // slightly differently. Only surface model thinks the surface - // was actually crossed, therefore the next step will find distinct - // results - auto result = geo.find_next_step(1); - EXPECT_LT(result.distance, 2e-8); - - if (result.distance < 1e-6) - { - geo.move_to_boundary(); - geo.cross_boundary(); // back into em_calorimeter - } - - // then they are back to agreement - result = geo.find_next_step(1); - EXPECT_EQ(result.distance, 1); - EXPECT_FALSE(result.boundary); - EXPECT_TRUE(geo.is_on_boundary()); - EXPECT_EQ("em_calorimeter", this->volume_name(geo)); - EXPECT_SOFT_EQ(125.00000000000001, calc_radius()); - } - else - { - CELER_LOG(debug) << "Reentry succeeded: " << scoped_log_; + geo.cross_boundary(); } - } - { - ScopedLogStorer scoped_log_{&celeritas::self_logger()}; - auto particle = this->make_particle_view( - pdg::electron(), MevEnergy{3.25917780979408864e-02}); - auto integrate = make_mag_field_integrator( - field, particle.charge()); - auto propagate - = make_field_propagator(integrate, driver_options, particle, geo); - - Propagation result; - // This absurdly long step is because in the "failed" case the - // track thinks it's in the world volume (nearly vacuum) - result = propagate(2.12621374950874703e+21); - - if (CELERITAS_CORE_GEO == CELERITAS_CORE_GEO_GEANT4 - && result.boundary != geo.is_on_boundary()) + catch (CheckedGeoError const& e) { - // FIXME: see #882 - GTEST_SKIP() << "The current fix fails with the Geant4 navigator"; + FAIL() << e.what(); } - EXPECT_EQ(result.boundary, geo.is_on_boundary()); - EXPECT_SOFT_NEAR(125, calc_radius(), 1e-2); - if (successful_reentry) + if (CELERITAS_CORE_GEO == CELERITAS_CORE_GEO_ORANGE) { - // ORANGE and *sometimes* vecgeom/geant4: extremely long - // propagation stopped by substep countdown - EXPECT_FALSE(result.boundary); - EXPECT_TRUE(result.looping); - EXPECT_TRUE(scoped_log_.empty()) << scoped_log_; - - EXPECT_SOFT_EQ(12.02714054426572, result.distance); + // ORANGE should successfully reenter. However, under certain + // system configurations, VecGeom will end up in the world volume, + // so we don't test in all cases. EXPECT_EQ("em_calorimeter", this->volume_name(geo)); - EXPECT_EQ(573, integrate.count()); - EXPECT_TRUE(result.looping); } else { - // Repeated substep bisection failed; particle is bumped - EXPECT_SOFT_NEAR(result.distance, 12.02714054426572, coarse_eps); - // Minor floating point differences could make this 98 or so - EXPECT_SOFT_NEAR( - real_type(573), real_type(integrate.count()), 0.05); - EXPECT_FALSE(result.boundary); // FIXME: should have reentered - EXPECT_TRUE(result.looping); // FIXME: looping?? - - if (scoped_log_.empty()) {} - else if (CELERITAS_CORE_GEO == CELERITAS_CORE_GEO_GEANT4) - { - static char const* const expected_log_levels[] = {"error"}; - EXPECT_VEC_EQ(expected_log_levels, scoped_log_.levels()) - << scoped_log_; - } - else if (using_solids_vg) - { - static char const* const expected_log_messages[] = { - R"(Moved internally from boundary but safety didn't increase: volume 6 from {123.3, -20.82, -40.83} to {123.3, -20.82, -40.83} (distance: 1.000e-6))", - }; - EXPECT_VEC_EQ(expected_log_messages, scoped_log_.messages()); - static char const* const expected_log_levels[] = {"warning"}; - EXPECT_VEC_EQ(expected_log_levels, scoped_log_.levels()); - } - else - { - ADD_FAILURE() << "Logged warning/error:" << scoped_log_; - } + // FIXME: see GeoTests: TwoBoxesGeoTest::test_tangent + GTEST_SKIP(); } } } @@ -1486,7 +1418,6 @@ TEST_F(CmseTest, coarse) std::vector num_integration; ScopedLogStorer scoped_log_{&celeritas::self_logger()}; - bool failed{false}; for (real_type radius : {5, 10, 20, 50}) { @@ -1502,23 +1433,36 @@ TEST_F(CmseTest, coarse) int step_count = 0; int boundary_count = 0; int const max_steps = 10000; - while (!geo.is_outside() && step_count++ < max_steps) + while (!geo.is_outside() && !geo.failed() && step_count++ < max_steps) { Propagation result; try { result = propagate(radius); } - catch (RuntimeError const& e) + catch (CheckedGeoError const& e) { - // Failure during Geant4 propagation CELER_LOG(error) << e.what(); - failed = true; break; } if (result.boundary) { - geo.cross_boundary(); + try + { + geo.cross_boundary(); + } + catch (CheckedGeoError const& e) + { + if (CELERITAS_CORE_GEO == CELERITAS_CORE_GEO_VECGEOM) + { + CELER_LOG(error) << e.details().what; + break; + } + else + { + FAIL() << e.what(); + } + } ++boundary_count; } } @@ -1533,8 +1477,9 @@ TEST_F(CmseTest, coarse) std::vector expected_num_step = {10001, 6450, 3236, 1303}; std::vector expected_num_intercept = {30419, 19521, 16170, 9956}; std::vector expected_num_integration = {80659, 58204, 41914, 26114}; + std::vector expected_log_messages; - if (CELERITAS_CORE_GEO == CELERITAS_CORE_GEO_GEANT4 && failed) + if (CELERITAS_CORE_GEO == CELERITAS_CORE_GEO_GEANT4) { // FIXME: this happens because of incorrect momentum update expected_num_boundary = {134, 37, 60, 40}; From f05c448f595e1e3c9130ccd8b171758f6874b886 Mon Sep 17 00:00:00 2001 From: Seth R Johnson Date: Wed, 4 Feb 2026 18:09:25 -0500 Subject: [PATCH 7/7] Revert accidental changes --- test/celeritas/field/FieldPropagator.test.cc | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/test/celeritas/field/FieldPropagator.test.cc b/test/celeritas/field/FieldPropagator.test.cc index 8a3fb26534..1919bf3b41 100644 --- a/test/celeritas/field/FieldPropagator.test.cc +++ b/test/celeritas/field/FieldPropagator.test.cc @@ -1298,25 +1298,18 @@ TEST_F(SimpleCmsTest, TEST_IF_CELERITAS_DOUBLE(electron_stuck)) } catch (RuntimeError const& e) { - if (using_surface_vg) - { - EXPECT_TRUE(geo.failed()); - GTEST_SKIP() - << "FIXME: VecGeom surface model fails: " << e.what(); - } FAIL() << e.what(); } - if (using_solids_vg && CELERITAS_VECGEOM_VERSION >= 0x020000) - { - GTEST_SKIP() << "FIXME: VecGeom solid method didn't start in the " - "right place"; - } - EXPECT_EQ(result.boundary, geo.is_on_boundary()); EXPECT_SOFT_NEAR( double{30}, static_cast(integrate.count()), 0.2); + if (using_surface_vg) + { + EXPECT_FALSE(geo.is_on_boundary()); + GTEST_SKIP() << "FIXME: VecGeom surface model fails"; + } ASSERT_TRUE(geo.is_on_boundary()); if (geo.check_normal())