diff --git a/src/celeritas/CMakeLists.txt b/src/celeritas/CMakeLists.txt index 920c46f770..9cdcc0fe51 100644 --- a/src/celeritas/CMakeLists.txt +++ b/src/celeritas/CMakeLists.txt @@ -120,8 +120,8 @@ if(CELERITAS_USE_Geant4) ext/GeantImporter.cc ext/GeantSetup.cc ext/GeantVolumeMapper.cc + ext/ScopedGeantExceptionHandler.cc ext/detail/GeantBremsstrahlungProcess.cc - ext/detail/GeantExceptionHandler.cc ext/detail/GeantLoggerAdapter.cc ext/detail/GeantMicroXsCalculator.cc ext/detail/GeantModelImporter.cc diff --git a/src/celeritas/ext/GeantSetup.cc b/src/celeritas/ext/GeantSetup.cc index 039d8e9dbf..6b5ee7904f 100644 --- a/src/celeritas/ext/GeantSetup.cc +++ b/src/celeritas/ext/GeantSetup.cc @@ -31,7 +31,7 @@ #include "corecel/sys/ScopedMem.hh" #include "GeantGeoUtils.hh" -#include "detail/GeantExceptionHandler.hh" +#include "ScopedGeantExceptionHandler.hh" #include "detail/GeantLoggerAdapter.hh" #include "detail/GeantPhysicsList.hh" @@ -94,7 +94,7 @@ GeantSetup::GeantSetup(std::string const& gdml_filename, Options options) { // Run manager writes output that cannot be redirected... ScopedTimeAndRedirect scoped_time("G4RunManager"); - detail::GeantExceptionHandler scoped_exception_handler; + ScopedGeantExceptionHandler scoped_exceptions; // Access the particle table before creating the run manager, so that // missing environment variables like G4ENSDFSTATEDATA get caught // cleanly rather than segfaulting @@ -123,7 +123,7 @@ GeantSetup::GeantSetup(std::string const& gdml_filename, Options options) } detail::GeantLoggerAdapter scoped_logger; - detail::GeantExceptionHandler scoped_exception_handler; + ScopedGeantExceptionHandler scoped_exceptions; { CELER_LOG(status) << "Initializing Geant4 geometry and physics"; diff --git a/src/celeritas/ext/ScopedGeantExceptionHandler.cc b/src/celeritas/ext/ScopedGeantExceptionHandler.cc new file mode 100644 index 0000000000..db503fd714 --- /dev/null +++ b/src/celeritas/ext/ScopedGeantExceptionHandler.cc @@ -0,0 +1,112 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2023 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/ext/ScopedGeantExceptionHandler.cc +//---------------------------------------------------------------------------// +#include "ScopedGeantExceptionHandler.hh" + +#include +#include +#include +#include + +#include "corecel/Assert.hh" +#include "corecel/io/Logger.hh" + +namespace celeritas +{ +namespace +{ +//---------------------------------------------------------------------------// +/*! + * Process Geant4 exceptions with Celeritas. + * + * The Geant exception handler base class changes global state in its + * constructor (assigning "this") so this class instance must stay in scope + * once created. There is no way to save or restore the previous handler. + * Furthermore, creating a G4RunManagerKernel also resets the exception + * handler, so errors thrown during setup *CANNOT* be caught by celeritas, and + * this class can only be used after creating the G4RunManager. + */ +class GeantExceptionHandler final : public G4VExceptionHandler +{ + public: + // Accept error codes from geant4 + G4bool Notify(char const* originOfException, + char const* exceptionCode, + G4ExceptionSeverity severity, + char const* description) final; +}; + +//---------------------------------------------------------------------------// +/*! + * Propagate exceptions to Celeritas. + */ +G4bool GeantExceptionHandler::Notify(char const* origin_of_exception, + char const* exception_code, + G4ExceptionSeverity severity, + char const* description) +{ + CELER_EXPECT(origin_of_exception); + CELER_EXPECT(exception_code); + + // Construct message + auto err = RuntimeError::from_geant_exception( + origin_of_exception, exception_code, description); + + switch (severity) + { + case FatalException: + case FatalErrorInArgument: + case RunMustBeAborted: + case EventMustBeAborted: + // Severe or initialization error + throw err; + case JustWarning: + // Display a message + CELER_LOG_LOCAL(error) << err.what(); + break; + default: + CELER_ASSERT_UNREACHABLE(); + } + + // Return "true" to cause Geant4 to crash the program, or "false" to let it + // know that we've handled the exception. + return false; +} + +//---------------------------------------------------------------------------// +} // namespace + +//---------------------------------------------------------------------------// +/*! + * Install the Celeritas Geant4 exception handler. + * + * The base class of the exception handler calls SetExceptionHandler... + */ +ScopedGeantExceptionHandler::ScopedGeantExceptionHandler() +{ + auto* state_mgr = G4StateManager::GetStateManager(); + CELER_ASSERT(state_mgr); + previous_ = state_mgr->GetExceptionHandler(); + current_ = std::make_unique(); + CELER_ENSURE(state_mgr->GetExceptionHandler() == current_.get()); +} + +//---------------------------------------------------------------------------// +/*! + * Revert to the previous exception handler. + */ +ScopedGeantExceptionHandler::~ScopedGeantExceptionHandler() +{ + auto* state_mgr = G4StateManager::GetStateManager(); + if (state_mgr->GetExceptionHandler() == current_.get()) + { + state_mgr->SetExceptionHandler(previous_); + } +} + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/celeritas/ext/ScopedGeantExceptionHandler.hh b/src/celeritas/ext/ScopedGeantExceptionHandler.hh new file mode 100644 index 0000000000..16cfa715e8 --- /dev/null +++ b/src/celeritas/ext/ScopedGeantExceptionHandler.hh @@ -0,0 +1,51 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2023 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/ext/ScopedGeantExceptionHandler.hh +//---------------------------------------------------------------------------// +#pragma once + +#include + +#include "celeritas_config.h" + +class G4VExceptionHandler; + +namespace celeritas +{ +//---------------------------------------------------------------------------// +/*! + * Install and clear a Geant exception handler during this class lifetime. + * + * Note that creating a \c G4RunManagerKernel resets the exception + * handler, so errors thrown during setup *CANNOT* be caught by Celeritas, and + * this class can only be used after creating the \c G4RunManager. + */ +class ScopedGeantExceptionHandler +{ + public: + // Construct exception handler + ScopedGeantExceptionHandler(); + + // Clear on destruction + ~ScopedGeantExceptionHandler(); + + private: +#if CELERITAS_USE_GEANT4 + G4VExceptionHandler* previous_{nullptr}; + std::unique_ptr current_; +#endif +}; + +#if !CELERITAS_USE_GEANT4 +//!@{ +//! Do nothing if Geant4 is disabled (source file will not be compiled) +inline ScopedGeantExceptionHandler::ScopedGeantExceptionHandler() {} +inline ScopedGeantExceptionHandler::~ScopedGeantExceptionHandler() {} +//!@} +#endif + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/celeritas/ext/ScopedRootErrorHandler.cc b/src/celeritas/ext/ScopedRootErrorHandler.cc index 8400d07ef0..4cdeb589b3 100644 --- a/src/celeritas/ext/ScopedRootErrorHandler.cc +++ b/src/celeritas/ext/ScopedRootErrorHandler.cc @@ -71,17 +71,22 @@ void RootErrorHandler(Int_t rootlevel, //---------------------------------------------------------------------------// /*! - * Install the Celeritas ROOT error handler + * Clear ROOT's signal handlers that get installed on startup/activation. + */ +void ScopedRootErrorHandler::disable_signal_handler() +{ + gSystem->ResetSignals(); +} + +//---------------------------------------------------------------------------// +/*! + * Install the Celeritas ROOT error handler. */ ScopedRootErrorHandler::ScopedRootErrorHandler() : previous_(SetErrorHandler(RootErrorHandler)) , prev_errored_{g_has_root_errored_} { - // Disable ROOT interception of system signals the first time we run - [[maybe_unused]] static bool const disabled_root_backtrace = [] { - gSystem->ResetSignals(); - return true; - }(); + ScopedRootErrorHandler::disable_signal_handler(); } //---------------------------------------------------------------------------// @@ -101,7 +106,7 @@ void ScopedRootErrorHandler::throw_if_errors() const //---------------------------------------------------------------------------// /*! - * Revert to the previous ROOT error handler + * Revert to the previous ROOT error handler. */ ScopedRootErrorHandler::~ScopedRootErrorHandler() { diff --git a/src/celeritas/ext/ScopedRootErrorHandler.hh b/src/celeritas/ext/ScopedRootErrorHandler.hh index 8d6db81335..8c528195c8 100644 --- a/src/celeritas/ext/ScopedRootErrorHandler.hh +++ b/src/celeritas/ext/ScopedRootErrorHandler.hh @@ -19,6 +19,9 @@ namespace celeritas class ScopedRootErrorHandler { public: + // Clear ROOT's signal handlers that get installed on startup/activation + static void disable_signal_handler(); + // Install the error handler ScopedRootErrorHandler(); @@ -38,6 +41,7 @@ class ScopedRootErrorHandler #if !CELERITAS_USE_ROOT //!@{ //! Do nothing if ROOT is disabled (source file will not be compiled) +inline void ScopedRootErrorHandler::disable_signal_handler() {} inline ScopedRootErrorHandler::ScopedRootErrorHandler() { (void)sizeof(previous_); diff --git a/src/celeritas/ext/detail/GeantExceptionHandler.cc b/src/celeritas/ext/detail/GeantExceptionHandler.cc deleted file mode 100644 index b76e7f0d40..0000000000 --- a/src/celeritas/ext/detail/GeantExceptionHandler.cc +++ /dev/null @@ -1,58 +0,0 @@ -//----------------------------------*-C++-*----------------------------------// -// Copyright 2020-2023 UT-Battelle, LLC, and other Celeritas developers. -// See the top-level COPYRIGHT file for details. -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -//---------------------------------------------------------------------------// -//! \file celeritas/ext/detail/GeantExceptionHandler.cc -//---------------------------------------------------------------------------// -#include "GeantExceptionHandler.hh" - -#include - -#include "corecel/Assert.hh" -#include "corecel/io/Logger.hh" - -namespace celeritas -{ -namespace detail -{ -//---------------------------------------------------------------------------// -/*! - * Propagate exceptions to Celeritas. - */ -G4bool GeantExceptionHandler::Notify(char const* origin_of_exception, - char const* exception_code, - G4ExceptionSeverity severity, - char const* description) -{ - CELER_EXPECT(origin_of_exception); - CELER_EXPECT(exception_code); - - // Construct message - auto err = RuntimeError::from_geant_exception( - origin_of_exception, exception_code, description); - - switch (severity) - { - case FatalException: - case FatalErrorInArgument: - case RunMustBeAborted: - case EventMustBeAborted: - // Severe or initialization error - throw err; - case JustWarning: - // Display a message - CELER_LOG(error) << err.what(); - break; - default: - CELER_ASSERT_UNREACHABLE(); - } - - // Return "true" to cause Geant4 to crash the program, or "false" to let it - // know that we've handled the exception. - return false; -} - -//---------------------------------------------------------------------------// -} // namespace detail -} // namespace celeritas diff --git a/src/celeritas/ext/detail/GeantExceptionHandler.hh b/src/celeritas/ext/detail/GeantExceptionHandler.hh deleted file mode 100644 index 8607a3e8ae..0000000000 --- a/src/celeritas/ext/detail/GeantExceptionHandler.hh +++ /dev/null @@ -1,41 +0,0 @@ -//----------------------------------*-C++-*----------------------------------// -// Copyright 2020-2023 UT-Battelle, LLC, and other Celeritas developers. -// See the top-level COPYRIGHT file for details. -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -//---------------------------------------------------------------------------// -//! \file celeritas/ext/detail/GeantExceptionHandler.hh -//---------------------------------------------------------------------------// -#pragma once - -#include -#include -#include - -namespace celeritas -{ -namespace detail -{ -//---------------------------------------------------------------------------// -/*! - * Process Geant4 exceptions with Celeritas. - * - * The Geant exception handler base class changes global state in its - * constructor (assigning "this") so this class instance must stay in scope - * once created. There is no way to save or restore the previous handler. - * Furthermore, creating a G4RunManagerKernel also resets the exception - * handler, so errors thrown during setup *CANNOT* be caught by celeritas, and - * this class can only be used after creating the G4RunManager. - */ -class GeantExceptionHandler final : public G4VExceptionHandler -{ - public: - // Accept error codes from geant4 - G4bool Notify(char const* originOfException, - char const* exceptionCode, - G4ExceptionSeverity severity, - char const* description) final; -}; - -//---------------------------------------------------------------------------// -} // namespace detail -} // namespace celeritas diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0f70656488..bd801ec215 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -251,6 +251,7 @@ celeritas_add_library(testcel_celeritas celeritas/user/ExampleMctruth.cc celeritas/user/CaloTestBase.cc celeritas/user/MctruthTestBase.cc + celeritas/user/StepCollectorTestBase.cc ) celeritas_target_link_libraries(testcel_celeritas diff --git a/test/celeritas/GeantTestBase.cc b/test/celeritas/GeantTestBase.cc index c667f939a5..f30e99f78b 100644 --- a/test/celeritas/GeantTestBase.cc +++ b/test/celeritas/GeantTestBase.cc @@ -15,11 +15,16 @@ #include "celeritas/ext/GeantImporter.hh" #include "celeritas/ext/GeantPhysicsOptions.hh" #include "celeritas/ext/GeantSetup.hh" +#include "celeritas/ext/ScopedGeantExceptionHandler.hh" #include "celeritas/global/ActionRegistry.hh" #include "celeritas/global/alongstep/AlongStepGeneralLinearAction.hh" #include "celeritas/io/ImportData.hh" #include "celeritas/track/TrackInitParams.hh" +#if CELERITAS_CORE_GEO != CELERITAS_CORE_GEO_ORANGE +# include "celeritas/geo/GeoParams.hh" +#endif + namespace celeritas { namespace test @@ -45,6 +50,7 @@ struct GeantTestBase::ImportHelper std::string geometry_basename{}; GeantPhysicsOptions options{}; GeantImportDataSelection selection{}; + std::unique_ptr scoped_exceptions; ImportData imported; }; @@ -104,6 +110,15 @@ G4VPhysicalVolume const* GeantTestBase::get_world_volume() //---------------------------------------------------------------------------// // PROTECTED MEMBER FUNCTIONS +//---------------------------------------------------------------------------// +auto GeantTestBase::build_geant_options() const -> GeantPhysicsOptions +{ + GeantPhysicsOptions options; + options.em_bins_per_decade = 14; + options.rayleigh_scattering = false; + return options; +} + //---------------------------------------------------------------------------// auto GeantTestBase::build_init() -> SPConstTrackInit { @@ -136,12 +151,20 @@ auto GeantTestBase::build_along_step() -> SPConstAction } //---------------------------------------------------------------------------// -auto GeantTestBase::build_geant_options() const -> GeantPhysicsOptions +auto GeantTestBase::build_fresh_geometry(std::string_view filename) + -> SPConstGeoI { - GeantPhysicsOptions options; - options.em_bins_per_decade = 14; - options.rayleigh_scattering = false; - return options; +#if CELERITAS_CORE_GEO == CELERITAS_CORE_GEO_ORANGE + // Load fake version of geometry + return Base::build_fresh_geometry(filename); +#else + // Import geometry from Geant4 + CELER_LOG(info) << "Importing Geant4 geometry instead of loading from " + << filename; + auto* world = this->get_world_volume(); + CELER_EXPECT(world); + return std::make_shared(world); +#endif } //---------------------------------------------------------------------------// @@ -160,6 +183,7 @@ auto GeantTestBase::imported_data() const -> ImportData const& "celeritas", (i.geometry_basename + ".gdml").c_str()); i.import = std::make_unique( GeantSetup{gdml_inp.c_str(), i.options}); + i.scoped_exceptions = std::make_unique(); i.imported = (*i.import)(sel); i.options.verbose = false; } diff --git a/test/celeritas/GeantTestBase.hh b/test/celeritas/GeantTestBase.hh index 1571f24e68..3a3d7dc8f5 100644 --- a/test/celeritas/GeantTestBase.hh +++ b/test/celeritas/GeantTestBase.hh @@ -29,6 +29,8 @@ namespace test */ class GeantTestBase : public ImportedDataTestBase { + using Base = ImportedDataTestBase; + public: //!@{ //! Whether the Geant4 configuration match a certain machine @@ -48,6 +50,7 @@ class GeantTestBase : public ImportedDataTestBase SPConstTrackInit build_init() override; SPConstAction build_along_step() override; + SPConstGeoI build_fresh_geometry(std::string_view) override; // Access lazily loaded static geant4 data ImportData const& imported_data() const final; diff --git a/test/celeritas/GlobalGeoTestBase.hh b/test/celeritas/GlobalGeoTestBase.hh index e528bb8e75..5d79a46115 100644 --- a/test/celeritas/GlobalGeoTestBase.hh +++ b/test/celeritas/GlobalGeoTestBase.hh @@ -27,7 +27,8 @@ namespace test * The "geometry basename" should be the filename without extension of a * geometry file inside `test/celeritas/data`. */ -class GlobalGeoTestBase : virtual public GlobalTestBase, private LazyGeoManager +class GlobalGeoTestBase : virtual public GlobalTestBase, + protected LazyGeoManager { public: // Overload with the base filename of the geometry @@ -39,7 +40,7 @@ class GlobalGeoTestBase : virtual public GlobalTestBase, private LazyGeoManager // Clear the lazy geometry static void reset_geometry(); - private: + protected: //// LAZY GEOMETRY CONSTRUCTION AND CLEANUP //// SPConstGeoI build_fresh_geometry(std::string_view) override; diff --git a/test/celeritas/GlobalTestBase.cc b/test/celeritas/GlobalTestBase.cc index 7f8956e1af..3bc76f2f90 100644 --- a/test/celeritas/GlobalTestBase.cc +++ b/test/celeritas/GlobalTestBase.cc @@ -16,6 +16,7 @@ #include "corecel/io/JsonPimpl.hh" #include "corecel/io/Logger.hh" #include "corecel/io/OutputRegistry.hh" +#include "celeritas/ext/ScopedRootErrorHandler.hh" #include "celeritas/global/ActionRegistry.hh" #include "celeritas/global/CoreParams.hh" #include "celeritas/random/RngParams.hh" @@ -30,27 +31,41 @@ namespace test //---------------------------------------------------------------------------// GlobalTestBase::GlobalTestBase() { +#ifndef __APPLE__ + // ROOT injects handlers simply by being linked on Linux systems + ScopedRootErrorHandler::disable_signal_handler(); +#endif + + // Create output registry output_reg_ = std::make_shared(); } //---------------------------------------------------------------------------// GlobalTestBase::~GlobalTestBase() { - if (this->HasFailure() && !this->output_reg()->empty()) + if (this->HasFailure() && output_reg_ && !output_reg_->empty()) { - std::string destination = "screen"; - std::ostream* os = &std::cout; - std::ofstream ofile; - if (celeritas::use_color()) + try { - destination = this->make_unique_filename(".json"); - ofile.open(destination, std::ios_base::out | std::ios_base::trunc); - os = &ofile; - } + std::string destination = "screen"; + std::ostream* os = &std::cout; + std::ofstream ofile; + if (celeritas::use_color()) + { + destination = this->make_unique_filename(".json"); + ofile.open(destination, + std::ios_base::out | std::ios_base::trunc); + os = &ofile; + } - std::cerr << "Writing diagnostic output to " << destination - << " because test failed\n"; - this->write_output(*os); + std::cerr << "Writing diagnostic output to " << destination + << " because test failed\n"; + this->write_output(*os); + } + catch (std::exception const& e) + { + std::cerr << "Failed to write diagnostics: " << e.what(); + } } } diff --git a/test/celeritas/LazyGeoManager.cc b/test/celeritas/LazyGeoManager.cc index 2be086d469..f5a6f68a4b 100644 --- a/test/celeritas/LazyGeoManager.cc +++ b/test/celeritas/LazyGeoManager.cc @@ -11,10 +11,6 @@ #include "celeritas_config.h" #include "corecel/io/Logger.hh" -#if CELERITAS_USE_VECGEOM -# include "celeritas/ext/VecgeomParams.hh" -#endif -#include "orange/OrangeParams.hh" namespace celeritas { diff --git a/test/celeritas/user/CaloTestBase.cc b/test/celeritas/user/CaloTestBase.cc index 315f7c505f..9cdd1b89cb 100644 --- a/test/celeritas/user/CaloTestBase.cc +++ b/test/celeritas/user/CaloTestBase.cc @@ -10,8 +10,6 @@ #include #include "corecel/cont/Span.hh" -#include "corecel/io/OutputRegistry.hh" -#include "celeritas/global/Stepper.hh" #include "celeritas/user/SimpleCalo.hh" #include "celeritas/user/StepCollector.hh" @@ -67,21 +65,7 @@ void CaloTestBase::RunResult::print_expected() const template auto CaloTestBase::run(size_type num_tracks, size_type num_steps) -> RunResult { - StepperInput step_inp; - step_inp.params = this->core(); - step_inp.stream_id = StreamId{0}; - step_inp.num_track_slots = num_tracks; - - Stepper step(step_inp); - - // Initial step - auto primaries = this->make_primaries(num_tracks); - auto count = step(make_span(primaries)); - - while (count && --num_steps > 0) - { - count = step(); - } + this->run_impl(num_tracks, num_steps); RunResult result; result.edep = calo_->calc_total_energy_deposition(); diff --git a/test/celeritas/user/DiagnosticTestBase.cc b/test/celeritas/user/DiagnosticTestBase.cc index b0fc1de2a5..cc96b0e7bb 100644 --- a/test/celeritas/user/DiagnosticTestBase.cc +++ b/test/celeritas/user/DiagnosticTestBase.cc @@ -95,21 +95,7 @@ template auto DiagnosticTestBase::run(size_type num_tracks, size_type num_steps) -> RunResult { - StepperInput step_inp; - step_inp.params = this->core(); - step_inp.stream_id = StreamId{0}; - step_inp.num_track_slots = num_tracks; - - Stepper step(step_inp); - - // Initial step - auto primaries = this->make_primaries(num_tracks); - auto count = step(make_span(primaries)); - - while (count && --num_steps > 0) - { - count = step(); - } + this->run_impl(num_tracks, num_steps); RunResult result; diff --git a/test/celeritas/user/MctruthTestBase.cc b/test/celeritas/user/MctruthTestBase.cc index 8b38938df5..1cc5a35c0d 100644 --- a/test/celeritas/user/MctruthTestBase.cc +++ b/test/celeritas/user/MctruthTestBase.cc @@ -10,6 +10,7 @@ #include #include "corecel/cont/Span.hh" +#include "corecel/io/LogContextException.hh" #include "celeritas/global/Stepper.hh" #include "celeritas/user/StepCollector.hh" @@ -79,21 +80,7 @@ void MctruthTestBase::RunResult::print_expected() const auto MctruthTestBase::run(size_type num_tracks, size_type num_steps) -> RunResult { - StepperInput step_inp; - step_inp.params = this->core(); - step_inp.stream_id = StreamId{0}; - step_inp.num_track_slots = num_tracks; - - Stepper step(step_inp); - - // Initial step - auto primaries = this->make_primaries(num_tracks); - auto count = step(make_span(primaries)); - - while (count && --num_steps > 0) - { - count = step(); - } + this->run_impl(num_tracks, num_steps); example_mctruth_->sort(); diff --git a/test/celeritas/user/StepCollector.test.cc b/test/celeritas/user/StepCollector.test.cc index 52981a17ad..e80dae6c69 100644 --- a/test/celeritas/user/StepCollector.test.cc +++ b/test/celeritas/user/StepCollector.test.cc @@ -8,6 +8,7 @@ #include "celeritas/user/StepCollector.hh" #include "corecel/cont/Span.hh" +#include "corecel/io/LogContextException.hh" #include "celeritas/em/UrbanMscParams.hh" #include "celeritas/global/ActionRegistry.hh" #include "celeritas/global/Stepper.hh" @@ -167,7 +168,8 @@ TEST_F(KnStepCollectorTestBase, multiple_interfaces) Stepper step(step_inp); auto primaries = this->make_primaries(2); - step(make_span(primaries)); + CELER_TRY_HANDLE(step(make_span(primaries)), + LogContextException{this->output_reg().get()}); } EXPECT_EQ(4, mctruth->steps().size()); diff --git a/test/celeritas/user/StepCollectorTestBase.cc b/test/celeritas/user/StepCollectorTestBase.cc new file mode 100644 index 0000000000..d0cdf23110 --- /dev/null +++ b/test/celeritas/user/StepCollectorTestBase.cc @@ -0,0 +1,50 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2023 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/user/StepCollectorTestBase.cc +//---------------------------------------------------------------------------// +#include "StepCollectorTestBase.hh" + +#include "corecel/io/LogContextException.hh" +#include "celeritas/global/Stepper.hh" + +namespace celeritas +{ +namespace test +{ +//---------------------------------------------------------------------------// +/*! + * Run a stepping loop with the core data. + */ +template +void StepCollectorTestBase::run_impl(size_type num_tracks, size_type num_steps) +{ + StepperInput step_inp; + step_inp.params = this->core(); + step_inp.stream_id = StreamId{0}; + step_inp.num_track_slots = num_tracks; + + Stepper step(step_inp); + LogContextException log_context{this->output_reg().get()}; + + // Initial step + auto primaries = this->make_primaries(num_tracks); + StepperResult count; + CELER_TRY_HANDLE(count = step(make_span(primaries)), log_context); + + while (count && --num_steps > 0) + { + CELER_TRY_HANDLE(count = step(), log_context); + } +} + +template void + StepCollectorTestBase::run_impl(size_type, size_type); +template void + StepCollectorTestBase::run_impl(size_type, size_type); + +//---------------------------------------------------------------------------// +} // namespace test +} // namespace celeritas diff --git a/test/celeritas/user/StepCollectorTestBase.hh b/test/celeritas/user/StepCollectorTestBase.hh index 403f32ac07..b74984053d 100644 --- a/test/celeritas/user/StepCollectorTestBase.hh +++ b/test/celeritas/user/StepCollectorTestBase.hh @@ -7,6 +7,11 @@ //---------------------------------------------------------------------------// #pragma once +#include + +#include "corecel/Types.hh" +#include "celeritas/phys/Primary.hh" + #include "../GlobalTestBase.hh" namespace celeritas @@ -25,6 +30,10 @@ class StepCollectorTestBase : virtual public GlobalTestBase public: virtual VecPrimary make_primaries(size_type count) = 0; + + protected: + template + void run_impl(size_type num_tracks, size_type num_steps); }; //---------------------------------------------------------------------------//