From 4fdcd38571ce0d29256836f733e6643ad10efec5 Mon Sep 17 00:00:00 2001 From: Amanda Lund Date: Thu, 18 Dec 2025 16:52:15 +0000 Subject: [PATCH 1/4] Store aux registry in optical core params --- src/accel/LocalOpticalOffload.cc | 4 +- src/celeritas/optical/CoreParams.cc | 5 ++ src/celeritas/optical/CoreParams.hh | 4 ++ src/celeritas/optical/OpticalCollector.cc | 4 +- .../optical/gen/DirectGeneratorAction.cc | 6 +- .../optical/gen/DirectGeneratorAction.hh | 2 +- src/celeritas/optical/gen/GeneratorAction.cc | 6 +- src/celeritas/optical/gen/GeneratorAction.hh | 4 +- .../optical/gen/PrimaryGeneratorAction.cc | 8 +-- .../optical/gen/PrimaryGeneratorAction.hh | 2 +- src/celeritas/setup/Problem.cc | 71 ++++++++++--------- test/celeritas/GlobalTestBase.cc | 1 + test/celeritas/optical/Generator.test.cc | 10 +-- .../optical/SurfacePhysicsIntegration.test.cc | 4 +- 14 files changed, 68 insertions(+), 63 deletions(-) diff --git a/src/accel/LocalOpticalOffload.cc b/src/accel/LocalOpticalOffload.cc index 8ef2870aae..f3fac7fc56 100644 --- a/src/accel/LocalOpticalOffload.cc +++ b/src/accel/LocalOpticalOffload.cc @@ -88,10 +88,10 @@ LocalOpticalOffload::LocalOpticalOffload(SetupOptions const& options, } // Allocate auxiliary data - if (params.Params()->aux_reg()) + if (optical_params.aux_reg()) { state_->aux() = std::make_shared( - *params.Params()->aux_reg(), memspace, stream_id, capacity.tracks); + *optical_params.aux_reg(), memspace, stream_id, capacity.tracks); } CELER_ENSURE(*this); diff --git a/src/celeritas/optical/CoreParams.cc b/src/celeritas/optical/CoreParams.cc index 3ea1f9bb36..1511e1a3ec 100644 --- a/src/celeritas/optical/CoreParams.cc +++ b/src/celeritas/optical/CoreParams.cc @@ -6,6 +6,7 @@ //---------------------------------------------------------------------------// #include "CoreParams.hh" +#include "corecel/data/AuxParamsRegistry.hh" #include "corecel/io/Logger.hh" #include "corecel/random/params/RngParams.hh" #include "corecel/sys/ActionRegistry.hh" @@ -132,6 +133,10 @@ CoreParams::CoreParams(Input&& input) : input_(std::move(input)) { detectors_ = std::make_shared(); } + if (!input_.aux_reg) + { + input_.aux_reg = std::make_shared(); + } ScopedMem record_mem("optical::CoreParams.construct"); diff --git a/src/celeritas/optical/CoreParams.hh b/src/celeritas/optical/CoreParams.hh index 483b60655b..210a1b517c 100644 --- a/src/celeritas/optical/CoreParams.hh +++ b/src/celeritas/optical/CoreParams.hh @@ -22,6 +22,7 @@ namespace celeritas { //---------------------------------------------------------------------------// class ActionRegistry; +class AuxParamsRegistry; class CherenkovParams; class GeneratorRegistry; class ScintillationParams; @@ -51,6 +52,7 @@ class CoreParams final : public ParamsDataInterface using SPConstSurface = std::shared_ptr; using SPConstSurfacePhysics = std::shared_ptr; using SPActionRegistry = std::shared_ptr; + using SPAuxRegistry = std::shared_ptr; using SPGeneratorRegistry = std::shared_ptr; using SPConstDetectors = std::shared_ptr; using SPConstCherenkov = std::shared_ptr; @@ -77,6 +79,7 @@ class CoreParams final : public ParamsDataInterface SPActionRegistry action_reg; SPGeneratorRegistry gen_reg; + SPAuxRegistry aux_reg; //!< Optional, empty default //! Maximum number of simultaneous threads/tasks per process StreamId::size_type max_streams{1}; @@ -115,6 +118,7 @@ class CoreParams final : public ParamsDataInterface return input_.surface_physics; } SPActionRegistry const& action_reg() const { return input_.action_reg; } + SPAuxRegistry const& aux_reg() const { return input_.aux_reg; } SPGeneratorRegistry const& gen_reg() const { return input_.gen_reg; } SPConstDetectors const& detectors() const { return detectors_; } SPConstCherenkov const& cherenkov() const { return input_.cherenkov; } diff --git a/src/celeritas/optical/OpticalCollector.cc b/src/celeritas/optical/OpticalCollector.cc index fbf6cb43b1..73ba44ca07 100644 --- a/src/celeritas/optical/OpticalCollector.cc +++ b/src/celeritas/optical/OpticalCollector.cc @@ -59,8 +59,8 @@ OpticalCollector::OpticalCollector(CoreParams const& core, Input&& inp) = OffloadGatherAction::make_and_insert(core); // Create optical action to generate Cherenkov or scintillation photons - generate_ = optical::GeneratorAction::make_and_insert( - core, *inp.optical_params, inp.buffer_capacity); + generate_ = optical::GeneratorAction::make_and_insert(*inp.optical_params, + inp.buffer_capacity); if (inp.optical_params->cherenkov()) { diff --git a/src/celeritas/optical/gen/DirectGeneratorAction.cc b/src/celeritas/optical/gen/DirectGeneratorAction.cc index b6a174e0df..1137d0e70f 100644 --- a/src/celeritas/optical/gen/DirectGeneratorAction.cc +++ b/src/celeritas/optical/gen/DirectGeneratorAction.cc @@ -48,11 +48,11 @@ auto make_state(StreamId stream, size_type size) /*! * Construct and add to core params. */ -std::shared_ptr DirectGeneratorAction::make_and_insert( - ::celeritas::CoreParams const& core_params, CoreParams const& params) +std::shared_ptr +DirectGeneratorAction::make_and_insert(CoreParams const& params) { ActionRegistry& actions = *params.action_reg(); - AuxParamsRegistry& aux = *core_params.aux_reg(); + AuxParamsRegistry& aux = *params.aux_reg(); GeneratorRegistry& gen = *params.gen_reg(); auto result = std::make_shared( actions.next_id(), aux.next_id(), gen.next_id()); diff --git a/src/celeritas/optical/gen/DirectGeneratorAction.hh b/src/celeritas/optical/gen/DirectGeneratorAction.hh index b7c7ba1157..683ce84e1e 100644 --- a/src/celeritas/optical/gen/DirectGeneratorAction.hh +++ b/src/celeritas/optical/gen/DirectGeneratorAction.hh @@ -43,7 +43,7 @@ class DirectGeneratorAction final : public GeneratorBase public: // Construct and add to core params static std::shared_ptr - make_and_insert(::celeritas::CoreParams const&, CoreParams const&); + make_and_insert(CoreParams const&); // Construct with action ID and data IDs DirectGeneratorAction(ActionId, AuxId, GeneratorId); diff --git a/src/celeritas/optical/gen/GeneratorAction.cc b/src/celeritas/optical/gen/GeneratorAction.cc index 6e11eb67ee..1454fdc4d7 100644 --- a/src/celeritas/optical/gen/GeneratorAction.cc +++ b/src/celeritas/optical/gen/GeneratorAction.cc @@ -59,12 +59,10 @@ auto make_state(StreamId stream, size_type size) * Construct and add to core params. */ std::shared_ptr -GeneratorAction::make_and_insert(::celeritas::CoreParams const& core_params, - CoreParams const& params, - size_type capacity) +GeneratorAction::make_and_insert(CoreParams const& params, size_type capacity) { ActionRegistry& actions = *params.action_reg(); - AuxParamsRegistry& aux = *core_params.aux_reg(); + AuxParamsRegistry& aux = *params.aux_reg(); GeneratorRegistry& gen = *params.gen_reg(); auto result = std::make_shared( actions.next_id(), aux.next_id(), gen.next_id(), capacity); diff --git a/src/celeritas/optical/gen/GeneratorAction.hh b/src/celeritas/optical/gen/GeneratorAction.hh index 4c1ef6ffbe..5ab30b8c35 100644 --- a/src/celeritas/optical/gen/GeneratorAction.hh +++ b/src/celeritas/optical/gen/GeneratorAction.hh @@ -48,9 +48,7 @@ class GeneratorAction final : public GeneratorBase public: // Construct and add to core params static std::shared_ptr - make_and_insert(::celeritas::CoreParams const&, - CoreParams const&, - size_type capacity); + make_and_insert(CoreParams const&, size_type capacity); // Construct with action ID, data IDs, and optical properties GeneratorAction(ActionId, AuxId, GeneratorId, size_type capacity); diff --git a/src/celeritas/optical/gen/PrimaryGeneratorAction.cc b/src/celeritas/optical/gen/PrimaryGeneratorAction.cc index 8fceb1eb81..dafea685eb 100644 --- a/src/celeritas/optical/gen/PrimaryGeneratorAction.cc +++ b/src/celeritas/optical/gen/PrimaryGeneratorAction.cc @@ -33,14 +33,12 @@ namespace optical /*! * Construct and add to core params. */ -std::shared_ptr PrimaryGeneratorAction::make_and_insert( - ::celeritas::CoreParams const& core_params, - CoreParams const& params, - Input&& input) +std::shared_ptr +PrimaryGeneratorAction::make_and_insert(CoreParams const& params, Input&& input) { CELER_EXPECT(input); ActionRegistry& actions = *params.action_reg(); - AuxParamsRegistry& aux = *core_params.aux_reg(); + AuxParamsRegistry& aux = *params.aux_reg(); GeneratorRegistry& gen = *params.gen_reg(); auto result = std::make_shared( actions.next_id(), aux.next_id(), gen.next_id(), std::move(input)); diff --git a/src/celeritas/optical/gen/PrimaryGeneratorAction.hh b/src/celeritas/optical/gen/PrimaryGeneratorAction.hh index aa9cc2a76a..bee7dde715 100644 --- a/src/celeritas/optical/gen/PrimaryGeneratorAction.hh +++ b/src/celeritas/optical/gen/PrimaryGeneratorAction.hh @@ -47,7 +47,7 @@ class PrimaryGeneratorAction final : public GeneratorBase public: // Construct and add to core params static std::shared_ptr - make_and_insert(::celeritas::CoreParams const&, CoreParams const&, Input&&); + make_and_insert(CoreParams const&, Input&&); // Construct with IDs and distributions PrimaryGeneratorAction(ActionId, AuxId, GeneratorId, Input); diff --git a/src/celeritas/setup/Problem.cc b/src/celeritas/setup/Problem.cc index 1de82d1ac2..83d478f15b 100644 --- a/src/celeritas/setup/Problem.cc +++ b/src/celeritas/setup/Problem.cc @@ -313,6 +313,7 @@ auto build_optical_params(inp::Problem const& p, params.surface = core.surface(); params.action_reg = std::make_shared(); params.gen_reg = std::make_shared(); + params.aux_reg = core.aux_reg(); params.max_streams = core.max_streams(); { // Construct optical physics models @@ -689,41 +690,41 @@ ProblemLoaded problem(inp::Problem const& p, ImportData const& imported) "optical-sizes", std::move(sizes))); - std::visit( - Overload{ - [&](inp::OpticalEmGenerator) { - // Generate Cherenkov or scintillation optical - // photons from Celeritas tracks - result.optical_collector = build_optical_offload( - p, *core_params, optical_params); - }, - [&](inp::OpticalOffloadGenerator) { - // Generate Cherenkov or scintillation photons - optical::GeneratorAction::make_and_insert( - *core_params, *optical_params, capacity.generators); - - // Build the optical transporter \em after all optical - // actions have been added to the registry - optical::Transporter::Input inp; - inp.params = optical_params; - if (action_times) - { - // Create aux data to accumulate optical action times - inp.action_times = ActionTimes::make_and_insert( - optical_params->action_reg(), - core_params->aux_reg(), - "optical-action-times"); - } - result.optical_transporter - = std::make_shared( - std::move(inp)); - }, - [](inp::OpticalPrimaryGenerator) { - //! \todo Enable optical primary generator - CELER_NOT_IMPLEMENTED("optical primary generator"); - }, - }, - p.physics.optical_generator); + std::visit(Overload{ + [&](inp::OpticalEmGenerator) { + // Generate Cherenkov or scintillation optical + // photons from Celeritas tracks + result.optical_collector = build_optical_offload( + p, *core_params, optical_params); + }, + [&](inp::OpticalOffloadGenerator) { + // Generate Cherenkov or scintillation photons + optical::GeneratorAction::make_and_insert( + *optical_params, capacity.generators); + + // Build the optical transporter \em after all + // optical actions have been added to the registry + optical::Transporter::Input inp; + inp.params = optical_params; + if (action_times) + { + // Create aux data to accumulate optical action + // times + inp.action_times = ActionTimes::make_and_insert( + optical_params->action_reg(), + core_params->aux_reg(), + "optical-action-times"); + } + result.optical_transporter + = std::make_shared( + std::move(inp)); + }, + [](inp::OpticalPrimaryGenerator) { + //! \todo Enable optical primary generator + CELER_NOT_IMPLEMENTED("optical primary generator"); + }, + }, + p.physics.optical_generator); } else { diff --git a/test/celeritas/GlobalTestBase.cc b/test/celeritas/GlobalTestBase.cc index 6bb1609305..b021339c8a 100644 --- a/test/celeritas/GlobalTestBase.cc +++ b/test/celeritas/GlobalTestBase.cc @@ -186,6 +186,7 @@ optical::CoreParams::Input GlobalTestBase::optical_params_input() inp.surface = this->core()->surface(); inp.action_reg = this->optical_action_reg(); inp.gen_reg = std::make_shared(); + inp.aux_reg = this->core()->aux_reg(); inp.physics = this->optical_physics(); inp.sim = this->optical_sim(); inp.surface_physics = this->optical_surface_physics(); diff --git a/test/celeritas/optical/Generator.test.cc b/test/celeritas/optical/Generator.test.cc index 05578a4876..ae74aad2fb 100644 --- a/test/celeritas/optical/Generator.test.cc +++ b/test/celeritas/optical/Generator.test.cc @@ -143,7 +143,7 @@ TEST_F(LArSphereGeneratorTest, primary_generator) inp.angle = inp::IsotropicDistribution{}; inp.shape = inp::PointDistribution{{0, 0, 0}}; auto generate = optical::PrimaryGeneratorAction::make_and_insert( - *this->core(), *this->optical_params(), std::move(inp)); + *this->optical_params(), std::move(inp)); this->build_transporter(); this->build_state(4096); @@ -180,7 +180,7 @@ TEST_F(LArSphereGeneratorTest, TEST_IF_CELER_DEVICE(device_primary_generator)) inp.angle = inp::MonodirectionalDistribution{{1, 0, 0}}; inp.shape = inp::UniformBoxDistribution{{-10, -10, -10}, {10, 10, 10}}; auto generate = optical::PrimaryGeneratorAction::make_and_insert( - *this->core(), *this->optical_params(), std::move(inp)); + *this->optical_params(), std::move(inp)); this->build_transporter(); this->build_state(16384); @@ -220,7 +220,7 @@ TEST_F(LArSphereGeneratorTest, direct_generator) 0, ImplVolumeId{0}}); auto generate = optical::DirectGeneratorAction::make_and_insert( - *this->core(), *this->optical_params()); + *this->optical_params()); this->build_transporter(); this->build_state(32); @@ -253,7 +253,7 @@ TEST_F(LArSphereGeneratorTest, generator) // Create optical action to generate Cherenkov and scintillation photons size_type capacity = 512; auto generate = optical::GeneratorAction::make_and_insert( - *this->core(), *this->optical_params(), capacity); + *this->optical_params(), capacity); this->build_transporter(); this->build_state(4096); @@ -312,7 +312,7 @@ TEST_F(LArSphereGeneratorTest, TEST_IF_CELER_DEVICE(device_generator)) // Create optical action to generate Cherenkov and scintillation photons size_type capacity = 4096; auto generate = optical::GeneratorAction::make_and_insert( - *this->core(), *this->optical_params(), capacity); + *this->optical_params(), capacity); this->build_transporter(); this->build_state(16384); diff --git a/test/celeritas/optical/SurfacePhysicsIntegration.test.cc b/test/celeritas/optical/SurfacePhysicsIntegration.test.cc index 86d2c43240..b6825b611e 100644 --- a/test/celeritas/optical/SurfacePhysicsIntegration.test.cc +++ b/test/celeritas/optical/SurfacePhysicsIntegration.test.cc @@ -256,8 +256,8 @@ class SurfacePhysicsIntegrationTest : public GeantTestBase //! Run over a set of angles and collect the results SurfaceTestResults run(std::vector const& angles) { - auto generate = DirectGeneratorAction::make_and_insert( - *this->core(), *this->optical_params()); + auto generate + = DirectGeneratorAction::make_and_insert(*this->optical_params()); this->make_collector(); this->build_transporter(); From c22e26d72c2efdaae550bd489db97acb7bcdf9c4 Mon Sep 17 00:00:00 2001 From: Amanda Lund Date: Mon, 5 Jan 2026 18:33:05 +0000 Subject: [PATCH 2/4] Refactor input and setup to support optical-only celeritas simulation --- src/accel/LocalOpticalOffload.cc | 8 +- src/accel/LocalTransporter.cc | 5 +- src/accel/LocalTransporter.hh | 4 +- src/accel/SetupOptions.cc | 55 +++- src/accel/SetupOptions.hh | 6 +- src/accel/SharedParams.cc | 126 +++------ src/accel/SharedParams.hh | 62 ++--- src/celeritas/global/CoreParams.cc | 24 +- src/celeritas/inp/Events.hh | 11 +- src/celeritas/inp/FrameworkInput.hh | 4 + src/celeritas/inp/Physics.hh | 4 +- src/celeritas/inp/Problem.hh | 26 ++ .../detail => celeritas/io}/OffloadWriter.hh | 7 +- src/celeritas/optical/CoreParams.cc | 24 ++ src/celeritas/optical/CoreParams.hh | 13 +- src/celeritas/optical/PhysicsParams.cc | 26 ++ src/celeritas/optical/PhysicsParams.hh | 11 + src/celeritas/setup/FrameworkInput.cc | 89 +++--- src/celeritas/setup/FrameworkInput.hh | 5 +- src/celeritas/setup/Problem.cc | 262 ++++++++++++------ src/celeritas/setup/Problem.hh | 22 +- src/corecel/io/OutputRegistry.cc | 29 ++ src/corecel/io/OutputRegistry.hh | 3 + test/accel/TrackingManagerIntegration.test.cc | 6 +- test/celeritas/GlobalTestBase.cc | 5 + 25 files changed, 533 insertions(+), 304 deletions(-) rename src/{accel/detail => celeritas/io}/OffloadWriter.hh (93%) diff --git a/src/accel/LocalOpticalOffload.cc b/src/accel/LocalOpticalOffload.cc index f3fac7fc56..ea8896e475 100644 --- a/src/accel/LocalOpticalOffload.cc +++ b/src/accel/LocalOpticalOffload.cc @@ -44,16 +44,16 @@ LocalOpticalOffload::LocalOpticalOffload(SetupOptions const& options, << "invalid optical photon generation mechanism for local " "optical offload"); - // Check the thread ID and MT model - validate_geant_threading(params.Params()->max_streams()); - // Save a pointer to the optical transporter - transport_ = params.optical_transporter(); + transport_ = params.optical_problem_loaded().transporter; CELER_ASSERT(transport_); CELER_ASSERT(transport_->params()); auto const& optical_params = *transport_->params(); + // Check the thread ID and MT model + validate_geant_threading(optical_params.max_streams()); + // Save a pointer to the generator action auto const& gen_reg = *optical_params.gen_reg(); for (auto gen_id : range(GeneratorId(gen_reg.size()))) diff --git a/src/accel/LocalTransporter.cc b/src/accel/LocalTransporter.cc index 7e6a6a40ae..8248fc4f49 100644 --- a/src/accel/LocalTransporter.cc +++ b/src/accel/LocalTransporter.cc @@ -43,6 +43,7 @@ #include "celeritas/inp/Control.hh" #include "celeritas/io/EventWriter.hh" #include "celeritas/io/JsonEventWriter.hh" +#include "celeritas/io/OffloadWriter.hh" #include "celeritas/io/RootEventWriter.hh" #include "celeritas/optical/CoreState.hh" #include "celeritas/optical/OpticalCollector.hh" @@ -52,8 +53,6 @@ #include "SetupOptions.hh" #include "SharedParams.hh" -#include "detail/OffloadWriter.hh" - namespace celeritas { namespace @@ -209,7 +208,7 @@ LocalTransporter::LocalTransporter(SetupOptions const& options, params.set_state(stream_id.get(), step_->sp_state()); // Save optical pointers if available, for diagnostics - optical_ = params.optical_collector(); + optical_ = params.problem_loaded().optical_collector; CELER_ENSURE(*this); } diff --git a/src/accel/LocalTransporter.hh b/src/accel/LocalTransporter.hh index b9510d564b..597bd2c53a 100644 --- a/src/accel/LocalTransporter.hh +++ b/src/accel/LocalTransporter.hh @@ -26,11 +26,11 @@ namespace celeritas namespace detail { class HitProcessor; -class OffloadWriter; } // namespace detail struct SetupOptions; class CoreStateInterface; +class OffloadWriter; class OpticalCollector; class ParticleParams; class SharedParams; @@ -102,7 +102,7 @@ class LocalTransporter final : public LocalOffloadInterface private: //// TYPES //// - using SPOffloadWriter = std::shared_ptr; + using SPOffloadWriter = std::shared_ptr; using BBox = BoundingBox; struct BufferAccum diff --git a/src/accel/SetupOptions.cc b/src/accel/SetupOptions.cc index 61c11f6748..14fa504e23 100644 --- a/src/accel/SetupOptions.cc +++ b/src/accel/SetupOptions.cc @@ -126,9 +126,7 @@ void ProblemSetup::operator()(inp::Problem& p) const if (so.optical) { p.control.optical_capacity = so.optical->capacity; - p.physics.optical_generator = so.optical->generator; - p.tracking.optical_limits.steps = so.optical->max_steps; - p.tracking.optical_limits.step_iters = so.optical->max_step_iters; + p.tracking.optical_limits = so.optical->limits; } if (so.track_order != TrackOrder::size_) @@ -218,6 +216,44 @@ void ProblemSetup::operator()(inp::Problem& p) const p.diagnostics.add_user_actions = so.add_user_actions; } +//---------------------------------------------------------------------------// +/*! + * FrameworkInput adapter function for optical-only offload. + */ +struct OpticalProblemSetup +{ + SetupOptions const& so; + + void operator()(inp::OpticalProblem&) const; +}; + +//---------------------------------------------------------------------------// +/*! + * Set a Celeritas optical problem input definition. + */ +void OpticalProblemSetup::operator()(inp::OpticalProblem& p) const +{ + if (!so.geometry_file.empty()) + { + p.model.geometry = so.geometry_file; + } + + p.num_streams = [&so = this->so] { + if (so.get_num_streams) + { + return so.get_num_streams(); + } + return celeritas::get_geant_num_threads(); + }(); + + p.generator = so.optical->generator; + p.capacity = so.optical->capacity; + p.limits = so.optical->limits; + p.seed = CLHEP::HepRandom::getTheSeed(); + p.timers.action = so.action_times; + p.output_file = so.output_file; +} + //---------------------------------------------------------------------------// } // namespace @@ -296,7 +332,18 @@ inp::FrameworkInput to_inp(SetupOptions const& so) result.physics_import.data_selection.particles = selection; result.physics_import.data_selection.processes = selection; - result.adjust = ProblemSetup{so}; + if (!so.optical + || std::holds_alternative( + so.optical->generator)) + { + result.adjust = ProblemSetup{so}; + } + else + { + // Optical-only offload + result.adjust_optical = OpticalProblemSetup{so}; + } + return result; } diff --git a/src/accel/SetupOptions.hh b/src/accel/SetupOptions.hh index 37713b4025..c787a2b1cd 100644 --- a/src/accel/SetupOptions.hh +++ b/src/accel/SetupOptions.hh @@ -125,10 +125,8 @@ struct OpticalSetupOptions inp::OpticalStateCapacity capacity; //! Optical photon generation mechanism inp::OpticalGenerator generator; - //! Limit on number of steps per track before killing - size_type max_steps{inp::TrackingLimits::unlimited}; - //! Limit on number of optical step iterations before aborting - size_type max_step_iters{inp::TrackingLimits::unlimited}; + //! Limits for the optical stepping loop + inp::OpticalTrackingLimits limits; }; //---------------------------------------------------------------------------// diff --git a/src/accel/SharedParams.cc b/src/accel/SharedParams.cc index 33f23ec135..c147e79f01 100644 --- a/src/accel/SharedParams.cc +++ b/src/accel/SharedParams.cc @@ -29,21 +29,16 @@ #include "corecel/Assert.hh" #include "corecel/cont/ArrayIO.hh" +#include "corecel/cont/VariantUtils.hh" #include "corecel/io/BuildOutput.hh" #include "corecel/io/Join.hh" #include "corecel/io/Logger.hh" #include "corecel/io/OutputInterfaceAdapter.hh" #include "corecel/io/OutputRegistry.hh" #include "corecel/io/ScopedTimeLog.hh" -#include "corecel/io/StringUtils.hh" #include "corecel/random/params/RngParams.hh" #include "corecel/sys/ActionRegistry.hh" #include "corecel/sys/Device.hh" -#include "corecel/sys/Environment.hh" -#include "corecel/sys/EnvironmentIO.json.hh" -#include "corecel/sys/KernelRegistry.hh" -#include "corecel/sys/MemRegistry.hh" -#include "corecel/sys/MemRegistryIO.json.hh" #include "corecel/sys/ScopedMem.hh" #include "corecel/sys/ScopedProfiling.hh" #include "corecel/sys/ThreadId.hh" @@ -60,12 +55,10 @@ #include "celeritas/global/CoreParams.hh" #include "celeritas/inp/FrameworkInput.hh" #include "celeritas/inp/Scoring.hh" -#include "celeritas/io/EventWriter.hh" #include "celeritas/io/ImportData.hh" -#include "celeritas/io/JsonEventWriter.hh" -#include "celeritas/io/RootEventWriter.hh" #include "celeritas/mat/MaterialParams.hh" #include "celeritas/optical/CoreParams.hh" +#include "celeritas/optical/Transporter.hh" #include "celeritas/phys/CutoffParams.hh" #include "celeritas/phys/ParticleParams.hh" #include "celeritas/phys/PhysicsParams.hh" @@ -82,7 +75,6 @@ #include "TimeOutput.hh" #include "detail/IntegrationSingleton.hh" -#include "detail/OffloadWriter.hh" namespace celeritas { @@ -282,35 +274,24 @@ SharedParams::SharedParams(SetupOptions const& options) ? *options.offload_particles : default_offload_particles(); } - if (mode_ != Mode::enabled) { // Stop initializing but create output registry for diagnostics output_reg_ = std::make_shared(); - output_filename_ = options.output_file; + loaded_.output_file = options.output_file; // Create the timing output timer_ = std::make_shared(celeritas::get_geant_num_threads()); - if (!output_filename_.empty()) + if (!loaded_.output_file.empty()) { CELER_LOG(debug) << R"(Constructing output registry for no-offload run)"; // Celeritas core params didn't add system metadata: do it // ourselves to save system diagnostic information - output_reg_->insert( - OutputInterfaceAdapter::from_const_ref( - OutputInterface::Category::system, - "memory", - celeritas::mem_registry())); - output_reg_->insert( - OutputInterfaceAdapter::from_const_ref( - OutputInterface::Category::system, - "environ", - celeritas::environment())); - output_reg_->insert(std::make_shared()); + output_reg_->insert_system_diagnostics(); output_reg_->insert(timer_); } @@ -319,60 +300,40 @@ SharedParams::SharedParams(SetupOptions const& options) // Construct input and then build the problem setup auto framework_inp = to_inp(options); - auto loaded = setup::framework_input(framework_inp); - params_ = std::move(loaded.problem.core_params); - CELER_ASSERT(params_); - output_filename_ = loaded.problem.output_file; - - if (auto const& opt = options.optical) - { - if (std::holds_alternative(opt->generator)) - { - optical_transporter_ - = std::move(loaded.problem.optical_transporter); - CELER_ASSERT(optical_transporter_); - } - else if (std::holds_alternative(opt->generator)) - { - optical_collector_ = std::move(loaded.problem.optical_collector); - CELER_ASSERT(optical_collector_); - } - else - { - CELER_VALIDATE(false, - << "invalid optical photon generation mechanism"); - } - } - - // Save action sequence - actions_ = std::move(loaded.problem.actions); - CELER_ASSERT(actions_); + loaded_ = setup::framework_input(framework_inp); // Load geant4 geometry adapter and save as "global" - CELER_ASSERT(loaded.geo); - geant_geo_ = std::move(loaded.geo); - celeritas::global_geant_geo(geant_geo_); - - // Save built attributes - output_reg_ = params_->output_reg(); - geant_sd_ = std::move(loaded.problem.geant_sd); - step_collector_ = std::move(loaded.problem.step_collector); - - // Translate supported particles - verify_offload( - offload_particles_, *params_->particle(), *params_->physics()); + CELER_ASSERT(loaded_.geo); + celeritas::global_geant_geo(loaded_.geo); // Create bounding box from navigator geometry - bbox_ = geant_geo_->get_clhep_bbox(); - - // Create streams - this->set_num_streams(params_->max_streams()); + bbox_ = loaded_.geo->get_clhep_bbox(); + + std::visit( + Overload{ + [&](setup::ProblemLoaded const& p) { + // Translate supported particles + verify_offload(offload_particles_, + *p.core_params->particle(), + *p.core_params->physics()); + + // Set streams and output registry from core params + output_reg_ = p.core_params->output_reg(); + this->set_num_streams(p.core_params->max_streams()); + }, + [&](setup::OpticalProblemLoaded const& p) { + // Set streams and output registry from optical params + output_reg_ = p.transporter->params()->output_reg(); + this->set_num_streams(p.transporter->params()->max_streams()); + }, + }, + loaded_.problem); // Add timing output - timer_ = std::make_shared(params_->max_streams()); + timer_ = std::make_shared(this->num_streams()); output_reg_->insert(timer_); - if (output_filename_ != "-") + if (loaded_.output_file != "-") { // Write output after params are constructed before anything can go // wrong @@ -384,29 +345,6 @@ SharedParams::SharedParams(SetupOptions const& options) << R"(Skipping 'startup' JSON output since writing to stdout)"; } - if (auto const& offload_file = loaded.problem.offload_file; - !offload_file.empty()) - { - std::unique_ptr writer; - if (ends_with(offload_file, ".jsonl")) - { - writer.reset( - new JsonEventWriter(offload_file, params_->particle())); - } - else if (ends_with(offload_file, ".root")) - { - writer.reset(new RootEventWriter( - std::make_shared(offload_file.c_str()), - params_->particle())); - } - else - { - writer.reset(new EventWriter(offload_file, params_->particle())); - } - offload_writer_ - = std::make_shared(std::move(writer)); - } - CELER_ENSURE(*this); } @@ -526,7 +464,7 @@ void SharedParams::set_num_streams(unsigned int num_streams) */ void SharedParams::try_output() const { - std::string filename = output_filename_; + std::string filename = loaded_.output_file; if (filename.empty()) { CELER_LOG(debug) << "Skipping output: SetupOptions::output_file is " diff --git a/src/accel/SharedParams.hh b/src/accel/SharedParams.hh index 357fd43583..7118fcdd9e 100644 --- a/src/accel/SharedParams.hh +++ b/src/accel/SharedParams.hh @@ -12,6 +12,7 @@ #include "corecel/Assert.hh" #include "geocel/BoundingBox.hh" +#include "celeritas/setup/FrameworkInput.hh" #include "Types.hh" @@ -21,11 +22,6 @@ class G4VPhysicalVolume; namespace celeritas { //---------------------------------------------------------------------------// -namespace detail -{ -class OffloadWriter; -} // namespace detail - namespace optical { class Transporter; @@ -36,6 +32,7 @@ class CoreParams; class CoreStateInterface; class GeantGeoParams; class GeantSd; +class OffloadWriter; class OpticalCollector; class OutputRegistry; class StepCollector; @@ -136,23 +133,20 @@ class SharedParams using SPActionSequence = std::shared_ptr; using SPGeantSd = std::shared_ptr; - using SPOffloadWriter = std::shared_ptr; + using SPOffloadWriter = std::shared_ptr; using SPOutputRegistry = std::shared_ptr; using SPTimeOutput = std::shared_ptr; using SPState = std::shared_ptr; - using SPOpticalCollector = std::shared_ptr; - using SPOpticalTransporter = std::shared_ptr; - using SPConstGeantGeoParams = std::shared_ptr; using BBox = BoundingBox; //! Initialization status and integration mode Mode mode() const { return mode_; } - // Optical properties (only if using optical physics) - inline SPOpticalCollector const& optical_collector() const; + // Access core Celeritas loaded problem data + inline setup::ProblemLoaded const& problem_loaded() const; - // Optical params (only if using optical physics) - inline SPOpticalTransporter const& optical_transporter() const; + // Access optical Celeritas loaded problem data + inline setup::OpticalProblemLoaded const& optical_problem_loaded() const; // Action sequence inline SPActionSequence const& actions() const; @@ -184,16 +178,8 @@ class SharedParams // Created during initialization Mode mode_{Mode::uninitialized}; - SPConstGeantGeoParams geant_geo_; - std::shared_ptr params_; - SPOpticalCollector optical_collector_; - SPOpticalTransporter optical_transporter_; - SPActionSequence actions_; - std::shared_ptr geant_sd_; - std::shared_ptr step_collector_; + setup::FrameworkLoaded loaded_; VecG4PD offload_particles_; - std::string output_filename_; - SPOffloadWriter offload_writer_; std::vector> states_; SPOutputRegistry output_reg_; SPTimeOutput timer_; @@ -223,8 +209,10 @@ void SharedParams::Initialize(SetupOptions const& options) auto SharedParams::Params() -> SPParams const& { CELER_EXPECT(mode_ == Mode::enabled); - CELER_ENSURE(params_); - return params_; + CELER_EXPECT(std::holds_alternative(loaded_.problem)); + auto const& p = std::get(loaded_.problem); + CELER_ENSURE(p.core_params); + return p.core_params; } //---------------------------------------------------------------------------// @@ -236,8 +224,8 @@ auto SharedParams::Params() -> SPParams const& auto SharedParams::Params() const -> SPConstParams { CELER_EXPECT(mode_ == Mode::enabled); - CELER_ENSURE(params_); - return params_; + CELER_ENSURE(this->problem_loaded().core_params); + return this->problem_loaded().core_params; } //---------------------------------------------------------------------------// @@ -252,22 +240,26 @@ auto SharedParams::OffloadParticles() const -> VecG4PD const& //---------------------------------------------------------------------------// /*! - * Optical transporter: null if Celeritas optical physics is disabled. + * Access Celeritas loaded core problem data. */ -auto SharedParams::optical_transporter() const -> SPOpticalTransporter const& +auto SharedParams::problem_loaded() const -> setup::ProblemLoaded const& { CELER_EXPECT(*this); - return optical_transporter_; + CELER_EXPECT(std::holds_alternative(loaded_.problem)); + return std::get(loaded_.problem); } //---------------------------------------------------------------------------// /*! - * Optical data: null if Celeritas optical physics is disabled. + * Access Celeritas loaded optical problem data. */ -auto SharedParams::optical_collector() const -> SPOpticalCollector const& +auto SharedParams::optical_problem_loaded() const + -> setup::OpticalProblemLoaded const& { CELER_EXPECT(*this); - return optical_collector_; + CELER_EXPECT( + std::holds_alternative(loaded_.problem)); + return std::get(loaded_.problem); } //---------------------------------------------------------------------------// @@ -279,7 +271,7 @@ auto SharedParams::optical_collector() const -> SPOpticalCollector const& auto SharedParams::hit_manager() const -> SPGeantSd const& { CELER_EXPECT(*this); - return geant_sd_; + return this->problem_loaded().geant_sd; } //---------------------------------------------------------------------------// @@ -289,7 +281,7 @@ auto SharedParams::hit_manager() const -> SPGeantSd const& auto SharedParams::actions() const -> SPActionSequence const& { CELER_EXPECT(*this); - return actions_; + return this->problem_loaded().actions; } //---------------------------------------------------------------------------// @@ -299,7 +291,7 @@ auto SharedParams::actions() const -> SPActionSequence const& auto SharedParams::offload_writer() const -> SPOffloadWriter const& { CELER_EXPECT(*this); - return offload_writer_; + return this->problem_loaded().offload_writer; } //---------------------------------------------------------------------------// diff --git a/src/celeritas/global/CoreParams.cc b/src/celeritas/global/CoreParams.cc index 19a91ee519..30e88e5664 100644 --- a/src/celeritas/global/CoreParams.cc +++ b/src/celeritas/global/CoreParams.cc @@ -15,21 +15,12 @@ #include "corecel/Assert.hh" #include "corecel/data/AuxParamsRegistry.hh" // IWYU pragma: keep #include "corecel/data/Ref.hh" -#include "corecel/io/BuildOutput.hh" #include "corecel/io/Logger.hh" #include "corecel/io/OutputInterfaceAdapter.hh" #include "corecel/io/OutputRegistry.hh" // IWYU pragma: keep #include "corecel/random/params/RngParams.hh" // IWYU pragma: keep #include "corecel/sys/ActionRegistry.hh" // IWYU pragma: keep #include "corecel/sys/ActionRegistryOutput.hh" -#include "corecel/sys/Device.hh" -#include "corecel/sys/DeviceIO.json.hh" -#include "corecel/sys/Environment.hh" -#include "corecel/sys/EnvironmentIO.json.hh" -#include "corecel/sys/KernelRegistry.hh" -#include "corecel/sys/KernelRegistryIO.json.hh" -#include "corecel/sys/MemRegistry.hh" -#include "corecel/sys/MemRegistryIO.json.hh" #include "corecel/sys/MpiCommunicator.hh" #include "corecel/sys/ScopedMem.hh" #include "geocel/GeoParamsOutput.hh" @@ -332,18 +323,9 @@ CoreParams::CoreParams(Input input) : input_(std::move(input)) } // Save system diagnostic information - input_.output_reg->insert(OutputInterfaceAdapter::from_const_ref( - OutputInterface::Category::system, "device", celeritas::device())); - input_.output_reg->insert( - OutputInterfaceAdapter::from_const_ref( - OutputInterface::Category::system, - "kernels", - celeritas::kernel_registry())); - input_.output_reg->insert(OutputInterfaceAdapter::from_const_ref( - OutputInterface::Category::system, "memory", celeritas::mem_registry())); - input_.output_reg->insert(OutputInterfaceAdapter::from_const_ref( - OutputInterface::Category::system, "environ", celeritas::environment())); - input_.output_reg->insert(std::make_shared()); + input_.output_reg->insert_system_diagnostics(); + + // Save core sizes input_.output_reg->insert( OutputInterfaceAdapter::from_rvalue_ref( OutputInterface::Category::internal, diff --git a/src/celeritas/inp/Events.hh b/src/celeritas/inp/Events.hh index 117774e32c..d22cf1dc18 100644 --- a/src/celeritas/inp/Events.hh +++ b/src/celeritas/inp/Events.hh @@ -119,11 +119,20 @@ struct OpticalOffloadGenerator { }; +//---------------------------------------------------------------------------// +/*! + * Generate optical photons directly from optical track initializers. + */ +struct OpticalDirectGenerator +{ +}; + //---------------------------------------------------------------------------// //! Mechanism for generating optical photons using OpticalGenerator = std::variant; + OpticalPrimaryGenerator, + OpticalDirectGenerator>; //---------------------------------------------------------------------------// /*! diff --git a/src/celeritas/inp/FrameworkInput.hh b/src/celeritas/inp/FrameworkInput.hh index 08cd75e69c..3cb42ee1ae 100644 --- a/src/celeritas/inp/FrameworkInput.hh +++ b/src/celeritas/inp/FrameworkInput.hh @@ -17,6 +17,7 @@ namespace celeritas namespace inp { struct Problem; +struct OpticalProblem; //---------------------------------------------------------------------------// /*! @@ -40,6 +41,9 @@ struct FrameworkInput //! User application/framework-defined adjustments std::function adjust; + + //! User application/framework-defined optical adjustments + std::function adjust_optical; }; //---------------------------------------------------------------------------// diff --git a/src/celeritas/inp/Physics.hh b/src/celeritas/inp/Physics.hh index d5efebe191..918efbb316 100644 --- a/src/celeritas/inp/Physics.hh +++ b/src/celeritas/inp/Physics.hh @@ -57,6 +57,8 @@ struct EmPhysics /*! * Optical physics processes, options, and surface definitions. * + * If scintillation or Cherenkov is enabled, optical photons will be generated. + * * \todo Move cherenkov/scintillation to a OpticalGenPhysics class. */ struct OpticalPhysics @@ -110,8 +112,6 @@ struct Physics //! Physics for optical photons OpticalPhysics optical; - //! Optical photon generation mechanism - OpticalGenerator optical_generator; }; //---------------------------------------------------------------------------// diff --git a/src/celeritas/inp/Problem.hh b/src/celeritas/inp/Problem.hh index 7243ce398d..387443d4b1 100644 --- a/src/celeritas/inp/Problem.hh +++ b/src/celeritas/inp/Problem.hh @@ -76,6 +76,32 @@ struct Problem Diagnostics diagnostics; }; +//---------------------------------------------------------------------------// +/*! + * Celeritas optical-only problem input definition. + */ +struct OpticalProblem +{ + //! Geometry, material, and region definitions + Model model; + //! Physics models and options + OpticalPhysics physics; + //! Optical photon generation mechanism + OpticalGenerator generator; + //! Hard cutoffs for counters + OpticalTrackingLimits limits; + //! Per-process state sizes for optical tracking loop + OpticalStateCapacity capacity; + //! Number of streams + size_type num_streams{}; + //! Random number generator seed + unsigned int seed{}; + //! Set up step or action timers + Timers timers; + //! Write Celeritas diagnostics to this file ("-" is stdout) + std::string output_file{"-"}; +}; + //---------------------------------------------------------------------------// } // namespace inp } // namespace celeritas diff --git a/src/accel/detail/OffloadWriter.hh b/src/celeritas/io/OffloadWriter.hh similarity index 93% rename from src/accel/detail/OffloadWriter.hh rename to src/celeritas/io/OffloadWriter.hh index 64d2c56a8a..2f60fe543f 100644 --- a/src/accel/detail/OffloadWriter.hh +++ b/src/celeritas/io/OffloadWriter.hh @@ -2,19 +2,17 @@ // Copyright Celeritas contributors: see top-level COPYRIGHT file for details // SPDX-License-Identifier: (Apache-2.0 OR MIT) //---------------------------------------------------------------------------// -//! \file accel/detail/OffloadWriter.hh +//! \file celeritas/io/OffloadWriter.hh //---------------------------------------------------------------------------// #pragma once #include #include -#include "celeritas/io/EventIOInterface.hh" +#include "EventIOInterface.hh" namespace celeritas { -namespace detail -{ //---------------------------------------------------------------------------// /*! * Dump primaries to a shared file, one event per flush. @@ -64,5 +62,4 @@ void OffloadWriter::operator()(argument_type primaries) } //---------------------------------------------------------------------------// -} // namespace detail } // namespace celeritas diff --git a/src/celeritas/optical/CoreParams.cc b/src/celeritas/optical/CoreParams.cc index 1511e1a3ec..a851a18965 100644 --- a/src/celeritas/optical/CoreParams.cc +++ b/src/celeritas/optical/CoreParams.cc @@ -8,12 +8,16 @@ #include "corecel/data/AuxParamsRegistry.hh" #include "corecel/io/Logger.hh" +#include "corecel/io/OutputInterfaceAdapter.hh" +#include "corecel/io/OutputRegistry.hh" #include "corecel/random/params/RngParams.hh" #include "corecel/sys/ActionRegistry.hh" +#include "corecel/sys/ActionRegistryOutput.hh" #include "corecel/sys/ScopedMem.hh" #include "geocel/SurfaceParams.hh" #include "celeritas/geo/CoreGeoParams.hh" #include "celeritas/mat/MaterialParams.hh" +#include "celeritas/optical/OpticalSizes.json.hh" #include "celeritas/phys/GeneratorRegistry.hh" #include "celeritas/track/SimParams.hh" @@ -137,6 +141,26 @@ CoreParams::CoreParams(Input&& input) : input_(std::move(input)) { input_.aux_reg = std::make_shared(); } + if (!input_.output_reg) + { + input_.output_reg = std::make_shared(); + input_.output_reg->insert_system_diagnostics(); + } + + // Save optical action diagnostic information + input_.output_reg->insert(std::make_shared( + input_.action_reg, "optical-actions")); + + // Add optical sizes + OpticalSizes sizes; + sizes.streams = this->max_streams(); + sizes.generators = input_.capacity.generators; + sizes.tracks = input_.capacity.tracks; + input_.output_reg->insert( + OutputInterfaceAdapter::from_rvalue_ref( + OutputInterface::Category::internal, + "optical-sizes", + std::move(sizes))); ScopedMem record_mem("optical::CoreParams.construct"); diff --git a/src/celeritas/optical/CoreParams.hh b/src/celeritas/optical/CoreParams.hh index 210a1b517c..6c6e4a6090 100644 --- a/src/celeritas/optical/CoreParams.hh +++ b/src/celeritas/optical/CoreParams.hh @@ -13,7 +13,9 @@ #include "corecel/data/ObserverPtr.hh" #include "corecel/data/ParamsDataInterface.hh" #include "corecel/random/params/RngParamsFwd.hh" +#include "corecel/sys/Device.hh" #include "celeritas/geo/GeoFwd.hh" +#include "celeritas/inp/Control.hh" #include "celeritas/user/SDParams.hh" #include "CoreTrackData.hh" @@ -25,6 +27,7 @@ class ActionRegistry; class AuxParamsRegistry; class CherenkovParams; class GeneratorRegistry; +class OutputRegistry; class ScintillationParams; class SurfaceParams; @@ -52,6 +55,7 @@ class CoreParams final : public ParamsDataInterface using SPConstSurface = std::shared_ptr; using SPConstSurfacePhysics = std::shared_ptr; using SPActionRegistry = std::shared_ptr; + using SPOutputRegistry = std::shared_ptr; using SPAuxRegistry = std::shared_ptr; using SPGeneratorRegistry = std::shared_ptr; using SPConstDetectors = std::shared_ptr; @@ -78,17 +82,23 @@ class CoreParams final : public ParamsDataInterface SPConstScintillation scintillation; //!< Optional SPActionRegistry action_reg; + SPOutputRegistry output_reg; SPGeneratorRegistry gen_reg; SPAuxRegistry aux_reg; //!< Optional, empty default //! Maximum number of simultaneous threads/tasks per process StreamId::size_type max_streams{1}; + //! Per-process state and buffer capacities + inp::OpticalStateCapacity capacity; + //! True if all params are assigned and valid explicit operator bool() const { return geometry && material && rng && sim && surface - && surface_physics && action_reg && gen_reg && max_streams; + && surface_physics && action_reg && gen_reg && max_streams + && capacity.generators > 0 && capacity.tracks > 0 + && capacity.primaries > 0; } }; @@ -118,6 +128,7 @@ class CoreParams final : public ParamsDataInterface return input_.surface_physics; } SPActionRegistry const& action_reg() const { return input_.action_reg; } + SPOutputRegistry const& output_reg() const { return input_.output_reg; } SPAuxRegistry const& aux_reg() const { return input_.aux_reg; } SPGeneratorRegistry const& gen_reg() const { return input_.gen_reg; } SPConstDetectors const& detectors() const { return detectors_; } diff --git a/src/celeritas/optical/PhysicsParams.cc b/src/celeritas/optical/PhysicsParams.cc index c879cc5bd9..aa890833ab 100644 --- a/src/celeritas/optical/PhysicsParams.cc +++ b/src/celeritas/optical/PhysicsParams.cc @@ -7,6 +7,8 @@ #include "PhysicsParams.hh" #include "corecel/sys/ActionRegistry.hh" +#include "celeritas/io/ImportData.hh" +#include "celeritas/optical/ModelImporter.hh" #include "MaterialParams.hh" #include "MfpBuilder.hh" @@ -17,6 +19,30 @@ namespace celeritas { namespace optical { +//---------------------------------------------------------------------------// +/*! + * Construct with imported data. + */ +std::shared_ptr +PhysicsParams::from_import(ImportData const& data, + SPConstCoreMaterials core_materials, + SPConstMaterials materials, + SPActionRegistry action_reg) +{ + Input input; + input.materials = materials; + input.action_registry = action_reg.get(); + ModelImporter importer{data, materials, core_materials}; + for (auto const& model : data.optical_models) + { + if (auto builder = importer(model.model_class)) + { + input.model_builders.push_back(*builder); + } + } + return std::make_shared(std::move(input)); +} + //---------------------------------------------------------------------------// /*! * Construct from imported and shared data. diff --git a/src/celeritas/optical/PhysicsParams.hh b/src/celeritas/optical/PhysicsParams.hh index bcc1292098..d7ef4af167 100644 --- a/src/celeritas/optical/PhysicsParams.hh +++ b/src/celeritas/optical/PhysicsParams.hh @@ -16,6 +16,8 @@ namespace celeritas { class ActionRegistry; +class ImportData; +class MaterialParams; namespace optical { @@ -27,7 +29,10 @@ class PhysicsParams final : public ParamsDataInterface public: //!@{ //! \name Type aliases + using SPActionRegistry = std::shared_ptr; using SPConstModel = std::shared_ptr; + using SPConstCoreMaterials + = std::shared_ptr; using SPConstMaterials = std::shared_ptr; using VecModels = std::vector; @@ -44,6 +49,12 @@ class PhysicsParams final : public ParamsDataInterface }; public: + // Construct with imported data, material params, and action registry + static std::shared_ptr from_import(ImportData const&, + SPConstCoreMaterials, + SPConstMaterials, + SPActionRegistry); + // Construct from models explicit PhysicsParams(Input input); diff --git a/src/celeritas/setup/FrameworkInput.cc b/src/celeritas/setup/FrameworkInput.cc index 8085a75eb7..858e3a6082 100644 --- a/src/celeritas/setup/FrameworkInput.cc +++ b/src/celeritas/setup/FrameworkInput.cc @@ -33,13 +33,22 @@ FrameworkLoaded framework_input(inp::FrameworkInput& fi) CELER_LOG(info) << "Activating Celeritas version " << version_string << " on " << (Device::num_devices() > 0 ? "GPU" : "CPU"); + CELER_VALIDATE(!(fi.adjust && fi.adjust_optical), + << "cannot setup both a problem and an optical-only " + "problem"); + + // TODO: How to determine which problem to setup without requiring adjust? + CELER_EXPECT(fi.adjust || fi.adjust_optical); + + FrameworkLoaded result; + // Set up system setup::system(fi.system); // Load Geant4 geometry wrapper, which saves it as global CELER_ASSERT(celeritas::global_geant_geo().expired()); - auto geo = GeantGeoParams::from_tracking_manager(); - CELER_ASSERT(geo); + result.geo = GeantGeoParams::from_tracking_manager(); + CELER_ASSERT(result.geo); // Load Geant4 data from user setup ImportData imported; @@ -48,45 +57,63 @@ FrameworkLoaded framework_input(inp::FrameworkInput& fi) // Load physics from external Geant4 data files setup::physics_from(inp::PhysicsFromGeantFiles{}, imported); - // Set up problem - inp::Problem problem; + if (fi.adjust_optical) + { + // Set up optical-only problem + inp::OpticalProblem problem; - // Copy optical physics from import data - // (TODO: will be replaced) - problem.physics.optical = imported.optical_physics; + problem.physics = imported.optical_physics; + problem.model = result.geo->make_model_input(); - // Load geometry, surfaces, regions from Geant4 world pointer - problem.model = geo->make_model_input(); + // Adjust problem + fi.adjust_optical(problem); - // Load physics - for (std::string const& process_name : fi.physics_import.ignore_processes) + // Save filename for diagnostic output + result.output_file = problem.output_file; + + // Set up core params + result.problem = setup::problem(problem, imported); + } + else { - ImportProcessClass ipc; - try - { - ipc = geant_name_to_import_process_class(process_name); - } - catch (RuntimeError const&) + // Set up problem + inp::Problem problem; + + // Copy optical physics from import data + // (TODO: will be replaced) + problem.physics.optical = imported.optical_physics; + + // Load geometry, surfaces, regions from Geant4 world pointer + problem.model = result.geo->make_model_input(); + + // Load physics + for (std::string const& process_name : + fi.physics_import.ignore_processes) { - CELER_LOG(error) << "User-ignored process '" << process_name - << "' is unknown to Celeritas"; - continue; + ImportProcessClass ipc; + try + { + ipc = geant_name_to_import_process_class(process_name); + } + catch (RuntimeError const&) + { + CELER_LOG(error) << "User-ignored process '" << process_name + << "' is unknown to Celeritas"; + continue; + } + problem.physics.em.user_processes.emplace( + ipc, WarnAndIgnoreProcess{ipc}); } - problem.physics.em.user_processes.emplace(ipc, - WarnAndIgnoreProcess{ipc}); - } - // Adjust problem - if (fi.adjust) - { + // Adjust problem fi.adjust(problem); - } - FrameworkLoaded result; - result.geo = std::move(geo); + // Save filename for diagnostic output + result.output_file = problem.diagnostics.output_file; - // Set up core params - result.problem = setup::problem(problem, imported); + // Set up core params + result.problem = setup::problem(problem, imported); + } return result; } diff --git a/src/celeritas/setup/FrameworkInput.hh b/src/celeritas/setup/FrameworkInput.hh index 9486b5e57e..65c9b58087 100644 --- a/src/celeritas/setup/FrameworkInput.hh +++ b/src/celeritas/setup/FrameworkInput.hh @@ -7,6 +7,7 @@ #pragma once #include +#include #include "Problem.hh" @@ -26,9 +27,11 @@ namespace setup struct FrameworkLoaded { //! Loaded problem - ProblemLoaded problem; + std::variant problem; //! Geant4 geometry wrapper std::shared_ptr geo; + //! Write diagnostic output + std::string output_file; }; //---------------------------------------------------------------------------// diff --git a/src/celeritas/setup/Problem.cc b/src/celeritas/setup/Problem.cc index 83d478f15b..199f6037a1 100644 --- a/src/celeritas/setup/Problem.cc +++ b/src/celeritas/setup/Problem.cc @@ -18,6 +18,7 @@ #include "corecel/io/Logger.hh" #include "corecel/io/OutputInterfaceAdapter.hh" #include "corecel/io/OutputRegistry.hh" +#include "corecel/io/StringUtils.hh" #include "corecel/math/Algorithms.hh" #include "corecel/random/params/RngParams.hh" #include "corecel/sys/ActionRegistry.hh" @@ -57,20 +58,24 @@ #include "celeritas/inp/Problem.hh" #include "celeritas/inp/Scoring.hh" #include "celeritas/inp/Tracking.hh" +#include "celeritas/io/EventWriter.hh" #include "celeritas/io/ImportData.hh" #include "celeritas/io/ImportProcess.hh" +#include "celeritas/io/JsonEventWriter.hh" +#include "celeritas/io/OffloadWriter.hh" #include "celeritas/io/RootCoreParamsOutput.hh" +#include "celeritas/io/RootEventWriter.hh" #include "celeritas/mat/MaterialParams.hh" #include "celeritas/optical/CoreParams.hh" #include "celeritas/optical/MaterialParams.hh" -#include "celeritas/optical/ModelImporter.hh" #include "celeritas/optical/OpticalCollector.hh" -#include "celeritas/optical/OpticalSizes.json.hh" #include "celeritas/optical/PhysicsParams.hh" #include "celeritas/optical/SimParams.hh" #include "celeritas/optical/Transporter.hh" #include "celeritas/optical/gen/CherenkovParams.hh" +#include "celeritas/optical/gen/DirectGeneratorAction.hh" #include "celeritas/optical/gen/GeneratorAction.hh" +#include "celeritas/optical/gen/PrimaryGeneratorAction.hh" #include "celeritas/optical/gen/ScintillationParams.hh" #include "celeritas/optical/surface/SurfacePhysicsParams.hh" #include "celeritas/phys/CutoffParams.hh" @@ -303,6 +308,8 @@ auto build_optical_params(inp::Problem const& p, "materials are present"); optical::CoreParams::Input params; + CELER_ASSERT(p.control.optical_capacity); + params.capacity = *p.control.optical_capacity; params.geometry = core.geometry(); params.material = optical::MaterialParams::from_import( imported, *core.geomaterial(), *core.material()); @@ -313,25 +320,13 @@ auto build_optical_params(inp::Problem const& p, params.surface = core.surface(); params.action_reg = std::make_shared(); params.gen_reg = std::make_shared(); + params.output_reg = core.output_reg(); params.aux_reg = core.aux_reg(); params.max_streams = core.max_streams(); - { - // Construct optical physics models - optical::PhysicsParams::Input pp_inp; - pp_inp.materials = params.material; - pp_inp.action_registry = params.action_reg.get(); - optical::ModelImporter importer{ - imported, params.material, core.material()}; - for (auto const& model : imported.optical_models) - { - if (auto builder = importer(model.model_class)) - { - pp_inp.model_builders.push_back(*builder); - } - } - params.physics - = std::make_shared(std::move(pp_inp)); - } + + // Construct optical physics models + params.physics = optical::PhysicsParams::from_import( + imported, core.material(), params.material, params.action_reg); // Construct optical surface physics models CELER_ASSERT(p.physics.optical); @@ -365,9 +360,6 @@ auto build_optical_offload( CoreParams const& params, std::shared_ptr const& optical_params) { - CELER_EXPECT(std::holds_alternative( - p.physics.optical_generator)); - CELER_VALIDATE(optical_params->cherenkov() || optical_params->scintillation(), << "failed to construct optical offload procesess"); @@ -527,8 +519,6 @@ ProblemLoaded problem(inp::Problem const& p, ImportData const& imported) //// DIAGNOSTICS //// - result.output_file = p.diagnostics.output_file; - // TODO: timers, counters, perfetto_file if (p.diagnostics.action) @@ -605,7 +595,28 @@ ProblemLoaded problem(inp::Problem const& p, ImportData const& imported) // TODO: this is only implemented in accel::SharedParams, not in // celeritas core: should hook this into the to-be-updated // "primary" mechanism - result.offload_file = ef.offload; + if (!ef.offload.empty()) + { + std::unique_ptr writer; + if (ends_with(ef.offload, ".jsonl")) + { + writer.reset( + new JsonEventWriter(ef.offload, core_params->particle())); + } + else if (ends_with(ef.offload, ".root")) + { + writer.reset(new RootEventWriter( + std::make_shared(ef.offload.c_str()), + core_params->particle())); + } + else + { + writer.reset( + new EventWriter(ef.offload, core_params->particle())); + } + result.offload_writer + = std::make_shared(std::move(writer)); + } } //// STEP COLLECTORS //// @@ -655,11 +666,6 @@ ProblemLoaded problem(inp::Problem const& p, ImportData const& imported) *core_params, std::move(step_interfaces)); } - // Whether to accumulate timing results for actions - bool action_times - = (!celeritas::device() - || (p.control.device_debug && p.control.device_debug->sync_stream)); - if (p.control.optical_capacity) { if (core_params->surface()->empty()) @@ -668,63 +674,12 @@ ProblemLoaded problem(inp::Problem const& p, ImportData const& imported) "any geometry surface definitions: default " "physics will be used for all surfaces"; } - + // Construct the optical params from the core params auto optical_params = build_optical_params(p, *core_params, imported); - // Save optical diagnostic information - core_params->output_reg()->insert( - std::make_shared( - optical_params->action_reg(), "optical-actions")); - - auto const& capacity = *p.control.optical_capacity; - - // Add optical sizes - OpticalSizes sizes; - sizes.streams = core_params->max_streams(); - sizes.generators = capacity.generators; - sizes.tracks = capacity.tracks; - - core_params->output_reg()->insert( - OutputInterfaceAdapter::from_rvalue_ref( - OutputInterface::Category::internal, - "optical-sizes", - std::move(sizes))); - - std::visit(Overload{ - [&](inp::OpticalEmGenerator) { - // Generate Cherenkov or scintillation optical - // photons from Celeritas tracks - result.optical_collector = build_optical_offload( - p, *core_params, optical_params); - }, - [&](inp::OpticalOffloadGenerator) { - // Generate Cherenkov or scintillation photons - optical::GeneratorAction::make_and_insert( - *optical_params, capacity.generators); - - // Build the optical transporter \em after all - // optical actions have been added to the registry - optical::Transporter::Input inp; - inp.params = optical_params; - if (action_times) - { - // Create aux data to accumulate optical action - // times - inp.action_times = ActionTimes::make_and_insert( - optical_params->action_reg(), - core_params->aux_reg(), - "optical-action-times"); - } - result.optical_transporter - = std::make_shared( - std::move(inp)); - }, - [](inp::OpticalPrimaryGenerator) { - //! \todo Enable optical primary generator - CELER_NOT_IMPLEMENTED("optical primary generator"); - }, - }, - p.physics.optical_generator); + // Construct the optical offload and generation actions + result.optical_collector + = build_optical_offload(p, *core_params, optical_params); } else { @@ -739,7 +694,8 @@ ProblemLoaded problem(inp::Problem const& p, ImportData const& imported) result.actions = [&] { ActionSequence::Options opt; auto const& action_reg = core_params->action_reg(); - if (action_times) + if (!celeritas::device() + || (p.control.device_debug && p.control.device_debug->sync_stream)) { // Create aux data to accumulate action times opt.action_times = ActionTimes::make_and_insert( @@ -756,6 +712,140 @@ ProblemLoaded problem(inp::Problem const& p, ImportData const& imported) return result; } +//---------------------------------------------------------------------------// +/*! + * Create optical "core params" from a problem definition and import data. + * + * This constructs the optical params \em without additionally constructing the + * core params. + */ +OpticalProblemLoaded +problem(inp::OpticalProblem const& p, ImportData const& imported) +{ + CELER_LOG(status) << "Initializing problem"; + + ScopedMem record_mem("setup::problem"); + ScopedProfiling profile_this{"setup::problem"}; + + CELER_VALIDATE(!imported.optical_materials.empty(), + << "an optical tracking loop was requested but no optical " + "materials are present"); + + optical::CoreParams::Input pi; + + // Per-process state sizes + pi.capacity = p.capacity; + + // Create action manager and generator registry + pi.action_reg = std::make_shared(); + pi.gen_reg = std::make_shared(); + + // Load geometry and model + if (auto* filename = std::get_if(&p.model.geometry)) + { + CELER_VALIDATE(!filename->empty(), + << "empty filename in problem.model.geometry"); + } + auto loaded_model = setup::model(p.model); + pi.geometry = std::move(loaded_model.geometry); + pi.surface = std::move(loaded_model.surface); + CELER_VALIDATE(pi.surface && !pi.surface->disabled(), + << "surfaces are required for optical physics"); + if (pi.surface->empty()) + { + CELER_LOG(warning) << "Problem contains optical physics without any " + "geometry surface definitions: default physics " + "will be used for all surfaces"; + } + + // Create materials and geometry/material coupling + auto material = MaterialParams::from_import(imported); + auto geomaterial = GeoMaterialParams::from_import( + imported, pi.geometry, loaded_model.volume, material); + + // Create optical materials + pi.material = optical::MaterialParams::from_import( + imported, *geomaterial, *material); + + // Construct RNG params + pi.rng = std::make_shared(p.seed); + + // Construct simulation params + pi.sim = std::make_shared(p.limits); + + // Set up streams + CELER_VALIDATE(p.num_streams > 0, + << "p.num_streams must be manually set before setup"); + pi.max_streams = p.num_streams; + if (auto& device = celeritas::device()) + { + device.create_streams(pi.max_streams); + } + + // Construct optical bulk physics models + pi.physics = optical::PhysicsParams::from_import( + imported, material, pi.material, pi.action_reg); + + // Construct optical surface physics models + pi.surface_physics = std::make_shared( + pi.action_reg.get(), p.physics.surfaces); + + // Add photon generating processes + if (p.physics.cherenkov) + { + pi.cherenkov = std::make_shared(*pi.material); + } + if (p.physics.scintillation) + { + auto particle = ParticleParams::from_import(imported); + pi.scintillation = ScintillationParams::from_import(imported, particle); + CELER_ASSERT(pi.scintillation); + } + + //! \todo Get sensitive detectors + + CELER_ASSERT(pi); + auto params = std::make_shared(std::move(pi)); + + // Construct the optical generator + std::visit(Overload{ + [&](inp::OpticalEmGenerator) { + CELER_VALIDATE(false, + << "OpticatEmGenerator cannot be used " + "with only optical physics enabled"); + }, + [&](inp::OpticalOffloadGenerator) { + optical::GeneratorAction::make_and_insert( + *params, p.capacity.generators); + }, + [&](inp::OpticalPrimaryGenerator opg) { + optical::PrimaryGeneratorAction::make_and_insert( + *params, std::move(opg)); + }, + [&](inp::OpticalDirectGenerator) { + optical::DirectGeneratorAction::make_and_insert(*params); + }, + }, + p.generator); + + OpticalProblemLoaded result; + + // Build the optical transporter \em after all optical actions have been + // added to the registry + optical::Transporter::Input ti; + if (p.timers.action) + { + // Create aux data to accumulate optical action times + ti.action_times = ActionTimes::make_and_insert( + params->action_reg(), params->aux_reg(), "optical-action-times"); + } + ti.params = std::move(params); + result.transporter = std::make_shared(std::move(ti)); + + CELER_ENSURE(result.transporter); + return result; +} + //---------------------------------------------------------------------------// } // namespace setup } // namespace celeritas diff --git a/src/celeritas/setup/Problem.hh b/src/celeritas/setup/Problem.hh index c41daddbb5..967b67e651 100644 --- a/src/celeritas/setup/Problem.hh +++ b/src/celeritas/setup/Problem.hh @@ -14,6 +14,7 @@ namespace celeritas //---------------------------------------------------------------------------// namespace inp { +struct OpticalProblem; struct Problem; } // namespace inp @@ -25,6 +26,7 @@ class Transporter; class ActionSequence; class CoreParams; class GeantSd; +class OffloadWriter; class OpticalCollector; class RootFileManager; class StepCollector; @@ -44,8 +46,6 @@ struct ProblemLoaded //! Step collector std::shared_ptr step_collector; - //! Optical-only offload management - std::shared_ptr optical_transporter; //! Combined EM and optical offload management std::shared_ptr optical_collector; //! Geant4 SD interface @@ -54,24 +54,30 @@ struct ProblemLoaded std::shared_ptr root_manager; //! Action sequence std::shared_ptr actions; - //!@} //!@{ //! \name Temporary: to be used downstream - //! \todo These should be refactored: should be built in Problem //! Write offloaded primaries - std::string offload_file; - //! Write diagnostic output - std::string output_file; - + std::shared_ptr offload_writer; //!@} }; +//---------------------------------------------------------------------------// +//! Result from loaded optical standalone input to be used in front-end apps +struct OpticalProblemLoaded +{ + //! Optical-only problem setup + std::shared_ptr transporter; +}; + //---------------------------------------------------------------------------// // Set up the problem ProblemLoaded problem(inp::Problem const& p, ImportData const& imported); +// Set up the optical-only problem +OpticalProblemLoaded +problem(inp::OpticalProblem const& p, ImportData const& imported); //---------------------------------------------------------------------------// } // namespace setup diff --git a/src/corecel/io/OutputRegistry.cc b/src/corecel/io/OutputRegistry.cc index 5ad7b9da9e..33f9eb7723 100644 --- a/src/corecel/io/OutputRegistry.cc +++ b/src/corecel/io/OutputRegistry.cc @@ -16,11 +16,21 @@ #include "corecel/Assert.hh" #include "corecel/cont/Range.hh" +#include "corecel/io/BuildOutput.hh" #include "corecel/io/EnumStringMapper.hh" +#include "corecel/sys/Device.hh" +#include "corecel/sys/DeviceIO.json.hh" +#include "corecel/sys/Environment.hh" +#include "corecel/sys/EnvironmentIO.json.hh" +#include "corecel/sys/KernelRegistry.hh" +#include "corecel/sys/KernelRegistryIO.json.hh" +#include "corecel/sys/MemRegistry.hh" +#include "corecel/sys/MemRegistryIO.json.hh" #include "JsonPimpl.hh" #include "Logger.hh" // IWYU pragma: keep #include "OutputInterface.hh" +#include "OutputInterfaceAdapter.hh" namespace celeritas { @@ -44,6 +54,25 @@ void OutputRegistry::insert(SPConstInterface interface) << "' for category '" << cat << "'"); } +//---------------------------------------------------------------------------// +/*! + * Add interfaces for writing system diagnostics. + */ +void OutputRegistry::insert_system_diagnostics() +{ + this->insert(OutputInterfaceAdapter::from_const_ref( + OutputInterface::Category::system, "device", celeritas::device())); + this->insert(OutputInterfaceAdapter::from_const_ref( + OutputInterface::Category::system, + "kernels", + celeritas::kernel_registry())); + this->insert(OutputInterfaceAdapter::from_const_ref( + OutputInterface::Category::system, "memory", celeritas::mem_registry())); + this->insert(OutputInterfaceAdapter::from_const_ref( + OutputInterface::Category::system, "environ", celeritas::environment())); + this->insert(std::make_shared()); +} + //---------------------------------------------------------------------------// /*! * Output all classes to a JSON object. diff --git a/src/corecel/io/OutputRegistry.hh b/src/corecel/io/OutputRegistry.hh index ec4fe3efce..586d5ba492 100644 --- a/src/corecel/io/OutputRegistry.hh +++ b/src/corecel/io/OutputRegistry.hh @@ -40,6 +40,9 @@ class OutputRegistry // Add an interface for writing void insert(SPConstInterface); + // Add an interfaces for writing system diagnostics + void insert_system_diagnostics(); + // Write output to the given JSON object void output(JsonPimpl*) const; diff --git a/test/accel/TrackingManagerIntegration.test.cc b/test/accel/TrackingManagerIntegration.test.cc index 7b90f12238..4723e3d6bc 100644 --- a/test/accel/TrackingManagerIntegration.test.cc +++ b/test/accel/TrackingManagerIntegration.test.cc @@ -349,7 +349,8 @@ void LarSphereOptical::EndOfRunAction(G4Run const* run) EXPECT_EQ(is_running_events(), static_cast(local_transporter)); EXPECT_TRUE(shared_params) << "Celeritas was not enabled"; - auto const& optical_collector = shared_params.optical_collector(); + auto const& optical_collector + = shared_params.problem_loaded().optical_collector; EXPECT_TRUE(optical_collector) << "optical offloading was not enabled"; if (local_transporter && optical_collector) { @@ -459,7 +460,8 @@ void OpNoviceOptical::EndOfRunAction(G4Run const* run) EXPECT_EQ(is_running_events(), static_cast(local_transporter)); EXPECT_TRUE(shared_params) << "Celeritas was not enabled"; - auto const& optical_collector = shared_params.optical_collector(); + auto const& optical_collector + = shared_params.problem_loaded().optical_collector; EXPECT_TRUE(optical_collector) << "optical offloading was not enabled"; if (local_transporter && optical_collector) { diff --git a/test/celeritas/GlobalTestBase.cc b/test/celeritas/GlobalTestBase.cc index b021339c8a..0fe9c84082 100644 --- a/test/celeritas/GlobalTestBase.cc +++ b/test/celeritas/GlobalTestBase.cc @@ -20,6 +20,7 @@ #include "corecel/io/OutputRegistry.hh" #include "corecel/random/params/RngParams.hh" #include "corecel/sys/ActionRegistry.hh" +#include "corecel/sys/Device.hh" #include "geocel/GeantGeoParams.hh" #include "geocel/SurfaceParams.hh" #include "geocel/VolumeParams.hh" @@ -27,6 +28,7 @@ #include "celeritas/ext/ScopedRootErrorHandler.hh" #include "celeritas/geo/CoreGeoParams.hh" #include "celeritas/global/CoreParams.hh" +#include "celeritas/inp/Control.hh" #include "celeritas/phys/GeneratorRegistry.hh" #include "celeritas/track/ExtendFromPrimariesAction.hh" #include "celeritas/track/StatusChecker.hh" @@ -185,6 +187,7 @@ optical::CoreParams::Input GlobalTestBase::optical_params_input() inp.rng = this->rng(); inp.surface = this->core()->surface(); inp.action_reg = this->optical_action_reg(); + inp.output_reg = this->core()->output_reg(); inp.gen_reg = std::make_shared(); inp.aux_reg = this->core()->aux_reg(); inp.physics = this->optical_physics(); @@ -192,6 +195,8 @@ optical::CoreParams::Input GlobalTestBase::optical_params_input() inp.surface_physics = this->optical_surface_physics(); inp.cherenkov = this->cherenkov(); inp.scintillation = this->scintillation(); + inp.capacity = inp::OpticalStateCapacity::from_default( + celeritas::Device::num_devices()); CELER_ENSURE(inp); return inp; From f344b6fc419323e848cc01ac17083fec70072127 Mon Sep 17 00:00:00 2001 From: Amanda Lund Date: Sat, 10 Jan 2026 23:54:16 +0000 Subject: [PATCH 3/4] Fix mismatched tags --- src/celeritas/optical/PhysicsParams.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/celeritas/optical/PhysicsParams.hh b/src/celeritas/optical/PhysicsParams.hh index d7ef4af167..1d857c8913 100644 --- a/src/celeritas/optical/PhysicsParams.hh +++ b/src/celeritas/optical/PhysicsParams.hh @@ -16,7 +16,7 @@ namespace celeritas { class ActionRegistry; -class ImportData; +struct ImportData; class MaterialParams; namespace optical From b7da7e5801d2c2a80e42271abe420305d108d592 Mon Sep 17 00:00:00 2001 From: Amanda Lund Date: Sun, 11 Jan 2026 00:32:41 +0000 Subject: [PATCH 4/4] clang-tidy --- src/accel/SetupOptions.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/accel/SetupOptions.cc b/src/accel/SetupOptions.cc index 14fa504e23..dcadf3e1ce 100644 --- a/src/accel/SetupOptions.cc +++ b/src/accel/SetupOptions.cc @@ -246,6 +246,7 @@ void OpticalProblemSetup::operator()(inp::OpticalProblem& p) const return celeritas::get_geant_num_threads(); }(); + CELER_ASSERT(so.optical); p.generator = so.optical->generator; p.capacity = so.optical->capacity; p.limits = so.optical->limits;