Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-11-03 09:49:40

0001 //------------------------------- -*- C++ -*- -------------------------------//
0002 // Copyright Celeritas contributors: see top-level COPYRIGHT file for details
0003 // SPDX-License-Identifier: (Apache-2.0 OR MIT)
0004 //---------------------------------------------------------------------------//
0005 //! \file accel/LocalTransporter.hh
0006 //! \sa TrackingManagerIntegration.test.cc
0007 //---------------------------------------------------------------------------//
0008 #pragma once
0009 
0010 #include <memory>
0011 
0012 #include "corecel/Types.hh"
0013 #include "corecel/io/Logger.hh"
0014 #include "geocel/BoundingBox.hh"
0015 #include "celeritas/Types.hh"
0016 #include "celeritas/ext/GeantTrackReconstruction.hh"
0017 
0018 #include "TrackOffloadInterface.hh"
0019 
0020 class G4EventManager;
0021 class G4Track;
0022 
0023 namespace celeritas
0024 {
0025 //---------------------------------------------------------------------------//
0026 namespace detail
0027 {
0028 class HitProcessor;
0029 }  // namespace detail
0030 
0031 struct SetupOptions;
0032 class CoreStateInterface;
0033 class OffloadWriter;
0034 class OpticalCollector;
0035 class ParticleParams;
0036 class SharedParams;
0037 class StepperInterface;
0038 struct StepperResult;
0039 
0040 //---------------------------------------------------------------------------//
0041 /*!
0042  * Manage offloading of tracks to Celeritas.
0043  *
0044  * This class \em must be constructed locally on each worker
0045  * thread/task/stream, usually as a shared pointer that's accessible to:
0046  * - a run action (for initialization),
0047  * - an event action (to set the event ID and flush offloaded tracks at the end
0048  *   of the event)
0049  * - a tracking action (to try offloading every track)
0050  *
0051  * \par Primary buffering
0052  *
0053  * Stepper owns two fixed-capacity host primary buffers and the device event
0054  * that protects the source of an asynchronous H2D copy. LocalTransporter
0055  * validates and converts Geant4 tracks, pushes accepted primaries into the
0056  * Stepper producer buffer, and keeps the corresponding Geant4 energy and loss
0057  * accounting. Primaries outside the Celeritas geometry are counted as lost but
0058  * are not added to either Stepper buffer.
0059  *
0060  * \par Device execution
0061  *
0062  * When the producer buffer first reaches capacity, \c Push stages it and
0063  * immediately calls \c StepperInterface::async. Same-stream ordering ensures
0064  * that the primary-copy completes before the stepping kernels use it. The
0065  * Stepper retains the staged host storage until the copy completes, while
0066  * Geant4 can continue filling the second buffer.
0067  *
0068  * At most one step result, one staged batch, and one producer batch can be
0069  * pending. Before reusing a full producer buffer, \c Push submits any staged
0070  * batch. It then applies backpressure by consuming and advancing the current
0071  * result only until start-of-step initialization can consume enough queued
0072  * tracks and primaries to preserve room for the secondary stack. Active tail
0073  * tracks may remain when a new batch is staged and launched. Calls to \c
0074  * stage_primaries and \c async can still block on synchronization internal to
0075  * the current Stepper implementation.
0076  * Before accepting each subsequent track, \c Push polls a pending step. If it
0077  * is ready, its result is consumed and another step is launched whenever
0078  * existing transport remains or another batch is staged. This allows device
0079  * transport to progress while Geant4 continues producing primaries. A full
0080  * producer batch is staged only when it fits alongside queued initializers and
0081  * leaves the maximum usable initializer capacity for secondaries after vacant
0082  * track slots have been filled.
0083  *
0084  * \par Event completion
0085  *
0086  * At event end, \c Flush first submits any staged batch. If a partially filled
0087  * producer buffer remains, it advances the current transport until that batch
0088  * satisfies the same initializer and secondary-capacity admission rule, then
0089  * stages and launches the producer batch. It finally steps all transport
0090  * synchronously to completion. Selection and compaction of device-generated
0091  * Geant SD data are enqueued on the device during the action launch, so a step
0092  * is not ready until they complete. The compacted data is copied to the host
0093  * and reconstructed only when the completed step result is consumed, keeping
0094  * the host copy and hit processing out of the asynchronous action launch. The
0095  * current copy still synchronizes before the next step starts. Hit processing
0096  * and Geant4 track reconstruction
0097  * are kept alive across the asynchronous work and cleared only after the drain
0098  * completes. A flush with only rejected primaries still reports and clears
0099  * their loss accounting.
0100  *
0101  * Host mode has the same buffering interface but no asynchronous overlap: a
0102  * full producer buffer calls \c Flush and is transported to completion before
0103  * \c Push returns.
0104  *
0105  * \internal
0106  *
0107  * LocalTransporter accounting follows the Stepper primary lifecycle:
0108  *
0109  * | State | Stepper state | Local accounting |
0110  * | ----- | ------------- | ---------------- |
0111  * | Producer | Primaries accepted by \c push_primary | \c buffered_accum_ |
0112  * | Staged | H2D copy queued; actions not submitted | \c staged_accum_ |
0113  * | Submitted | Batch submitted by \c async | \c in_flight_accum_ |
0114  * | Accounted | First result for batch consumed | Added to \c run_accum_ |
0115  *
0116  * \c in_flight_accum_ is cleared when the first result for its submitted batch
0117  * is consumed, even if the resulting active tracks require additional steps.
0118  * \c GetBufferSize returns accepted primaries that have not yet reached this
0119  * accounting point. It does not count rejected primaries, active Celeritas
0120  * tracks, or generated secondaries. The \c transport_active_ flag records
0121  * whether the last consumed result requires another step, while \c valid on
0122  * the Stepper records a currently pending result. The step iteration count
0123  * spans all overlapping primary batches and resets only after transport
0124  * becomes empty. Calling \c Finalize requires both states to be idle, normally
0125  * by first calling \c Flush.
0126  *
0127  * \warning Due to Geant4 thread-local allocators, this class \em must be
0128  * finalized or destroyed on the same CPU thread in which is created and used!
0129  */
0130 class LocalTransporter final : public TrackOffloadInterface
0131 {
0132   public:
0133     // Construct in an invalid state
0134     LocalTransporter() = default;
0135 
0136     // Initialized with shared (across threads) params
0137     LocalTransporter(SetupOptions const& options, SharedParams& params);
0138 
0139     //!@{
0140     //! \name LocalOffload interface
0141 
0142     // Alternative to construction + move assignment
0143     inline void Initialize(SetupOptions const& options,
0144                            SharedParams& params) final;
0145 
0146     // Set the event ID and reseed the Celeritas RNG at the start of an event
0147     void InitializeEvent(int) final;
0148 
0149     // Transport all queued and in-flight tracks to completion
0150     void Flush() final;
0151 
0152     // Clear local data and return to an invalid state
0153     void Finalize() final;
0154 
0155     // Whether the class instance is initialized
0156     bool Initialized() const final { return static_cast<bool>(step_); }
0157 
0158     // Number of accepted primaries not yet accounted as transported
0159     size_type GetBufferSize() const final;
0160 
0161     // Get accumulated action times
0162     MapStrDbl GetActionTime() const final;
0163     //!@}
0164 
0165     // Offload this track
0166     void Push(G4Track&) final;
0167 
0168     // Access core state data for user diagnostics
0169     CoreStateInterface const& GetState() const;
0170 
0171     // Access core state data for user diagnostics
0172     CoreStateInterface& GetState();
0173 
0174     //! Whether the class instance is initialized
0175     explicit operator bool() const { return this->Initialized(); }
0176 
0177   private:
0178     //// TYPES ////
0179 
0180     using SPOffloadWriter = std::shared_ptr<OffloadWriter>;
0181     using BBox = BoundingBox<double>;
0182 
0183     struct BufferAccum
0184     {
0185         size_type primaries{0};
0186         double energy{0};  // MeV
0187         double lost_energy{0};  // MeV
0188         std::size_t lost_primaries{0};
0189 
0190         bool empty() const { return primaries == 0 && lost_primaries == 0; }
0191     };
0192 
0193     struct RunAccum
0194     {
0195         std::size_t events{0};
0196         std::size_t flushes{0};
0197         std::size_t primaries{0};
0198         std::size_t steps{0};
0199         std::size_t lost_primaries{0};
0200         std::size_t hits{0};
0201     };
0202 
0203     //// HELPER FUNCTIONS ////
0204 
0205     void flush_impl();
0206     void push_impl(G4Track&);
0207     void stage_buffered_primaries(StepperResult const&);
0208     size_type available_primary_capacity(StepperResult const&) const;
0209     void launch_step();
0210     StepperResult complete_step();
0211     void advance_if_ready();
0212     StepperResult advance_transport();
0213     StepperResult wait_for_initializer_capacity();
0214     void drain_transport();
0215     void reset_local_state();
0216     void launch_pending_primaries();
0217 
0218     //// DATA ////
0219 
0220     // Shared problem data and local configuration
0221     std::shared_ptr<ParticleParams const> particles_;
0222     BBox bbox_;
0223     SPOffloadWriter dump_primaries_;
0224     size_type max_step_iters_{};
0225 
0226     // Thread-local stepper data
0227     std::shared_ptr<StepperInterface> step_;
0228     BufferAccum buffered_accum_;
0229     BufferAccum staged_accum_;
0230     BufferAccum in_flight_accum_;
0231     // Completed iterations in the current uninterrupted transport epoch
0232     size_type step_iters_{0};
0233     // Whether the last consumed result requires another step
0234     bool transport_active_{false};
0235 
0236     // Thread-local Geant4 integration data
0237     std::shared_ptr<detail::HitProcessor> hit_processor_;
0238     std::shared_ptr<GeantTrackReconstruction> track_reconstruction_;
0239     std::shared_ptr<OpticalCollector const> optical_;
0240 
0241     // Last seen event ID and manager for obtaining it
0242     int event_id_{-1};
0243     G4EventManager* event_manager_{nullptr};
0244 
0245     // Run summary diagnostics
0246     RunAccum run_accum_;
0247 };
0248 
0249 //---------------------------------------------------------------------------//
0250 /*!
0251  * Helper for making initialization more obvious from user code.
0252  *
0253  * This gives it some symmetry with Finalize, which is provided as an
0254  * exception-friendly destructor.
0255  */
0256 void LocalTransporter::Initialize(SetupOptions const& options,
0257                                   SharedParams& params)
0258 {
0259     *this = LocalTransporter(options, params);
0260 }
0261 
0262 //---------------------------------------------------------------------------//
0263 }  // namespace celeritas