Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-10-20 08:45:50

0001 #include "simphony.h"
0002 
0003 #include <atomic>
0004 #include <mutex>
0005 #include <optional>
0006 #include <stdexcept>
0007 #include <string>
0008 
0009 #include "CSGFoundry.h"
0010 #include "CSGOptiX.h"
0011 #include "SEventConfig.hh"
0012 #include "SLOG.hh"
0013 #include "SSim.hh"
0014 #include "U4GDML.h"
0015 #include "U4Tree.h"
0016 
0017 namespace
0018 {
0019 struct Runtime
0020 {
0021     std::mutex                mutex;
0022     std::atomic<bool>         initialized{false};
0023     std::atomic<bool>         gpu_available{false};
0024     const G4VPhysicalVolume*  world = nullptr;
0025     simphony::GeometryOptions options;
0026     U4Tree*                   tree = nullptr;
0027     CSGFoundry*               foundry = nullptr;
0028     CSGOptiX*                 backend = nullptr;
0029     std::optional<int>        pending_event;
0030     bool                      initialization_failed = false;
0031 };
0032 
0033 Runtime& runtime()
0034 {
0035     static Runtime instance;
0036     return instance;
0037 }
0038 
0039 bool same_options(
0040     const simphony::GeometryOptions& lhs,
0041     const simphony::GeometryOptions& rhs) noexcept
0042 {
0043     return lhs.sensor_identifier == rhs.sensor_identifier && lhs.gpu == rhs.gpu;
0044 }
0045 
0046 void require_initialized(const Runtime& state)
0047 {
0048     if (!state.initialized.load(std::memory_order_acquire))
0049         throw std::logic_error("simphony is not initialized");
0050 }
0051 
0052 void require_gpu(const Runtime& state)
0053 {
0054     require_initialized(state);
0055     if (state.backend == nullptr)
0056         throw std::logic_error("simphony has no GPU backend");
0057 }
0058 } // namespace
0059 
0060 namespace simphony
0061 {
0062 void initialize(const G4VPhysicalVolume* world, const GeometryOptions& options)
0063 {
0064     if (world == nullptr)
0065         throw std::invalid_argument("simphony::initialize requires a non-null Geant4 world");
0066 
0067     Runtime&        state = runtime();
0068     std::lock_guard lock(state.mutex);
0069 
0070     if (state.initialization_failed)
0071         throw std::logic_error("simphony initialization previously failed; restart the process before retrying");
0072 
0073     if (state.initialized.load(std::memory_order_acquire))
0074     {
0075         if (state.world == world && same_options(state.options, options))
0076             return;
0077         throw std::logic_error("simphony is already initialized with different geometry or options");
0078     }
0079 
0080     bool create_gpu = false;
0081     if (options.gpu != GpuRequirement::Disabled)
0082     {
0083         if (SEventConfig::CONTEXT == nullptr)
0084             SEventConfig::Initialize();
0085         create_gpu = SEventConfig::HasDevice();
0086         if (options.gpu == GpuRequirement::Required && !create_gpu)
0087             throw std::runtime_error("simphony requires a CUDA-capable GPU, but none is available");
0088     }
0089 
0090     try
0091     {
0092         SSim* simulation = SSim::CreateOrReuse();
0093         if (simulation == nullptr)
0094             throw std::runtime_error("simphony failed to create its simulation state");
0095 
0096         U4Tree* tree = U4Tree::Create(
0097             simulation->get_tree(), world, options.sensor_identifier);
0098         if (tree == nullptr)
0099             throw std::runtime_error("simphony failed to translate the Geant4 geometry");
0100 
0101         simulation->initSceneFromTree();
0102         CSGFoundry* foundry = CSGFoundry::CreateFromSim();
0103         if (foundry == nullptr)
0104             throw std::runtime_error("simphony failed to create its CSG geometry");
0105 
0106         CSGOptiX* backend = create_gpu ? CSGOptiX::Create(foundry) : nullptr;
0107         if (create_gpu && backend == nullptr)
0108             throw std::runtime_error("simphony failed to create its GPU backend");
0109 
0110         state.world = world;
0111         state.options = options;
0112         state.tree = tree;
0113         state.foundry = foundry;
0114         state.backend = backend;
0115         state.gpu_available.store(backend != nullptr, std::memory_order_release);
0116         state.initialized.store(true, std::memory_order_release);
0117     }
0118     catch (...)
0119     {
0120         state.initialization_failed = true;
0121         throw;
0122     }
0123 }
0124 
0125 bool is_initialized() noexcept
0126 {
0127     return runtime().initialized.load(std::memory_order_acquire);
0128 }
0129 
0130 bool has_gpu() noexcept
0131 {
0132     return runtime().gpu_available.load(std::memory_order_acquire);
0133 }
0134 
0135 void simulate(int event_id, bool reset_after)
0136 {
0137     if (event_id < 0)
0138         throw std::invalid_argument("simphony::simulate requires a non-negative event id");
0139 
0140     Runtime&        state = runtime();
0141     std::lock_guard lock(state.mutex);
0142     require_gpu(state);
0143 
0144     if (state.pending_event.has_value())
0145         throw std::logic_error("simphony has an event awaiting reset");
0146 
0147     state.backend->simulate(event_id, reset_after);
0148     if (!reset_after)
0149         state.pending_event = event_id;
0150 }
0151 
0152 void reset(int event_id)
0153 {
0154     if (event_id < 0)
0155         throw std::invalid_argument("simphony::reset requires a non-negative event id");
0156 
0157     Runtime&        state = runtime();
0158     std::lock_guard lock(state.mutex);
0159     require_gpu(state);
0160 
0161     if (!state.pending_event.has_value())
0162         throw std::logic_error("simphony has no event awaiting reset");
0163     if (*state.pending_event != event_id)
0164         throw std::logic_error("simphony reset event id does not match the pending event");
0165 
0166     state.backend->reset(event_id);
0167     state.pending_event.reset();
0168 }
0169 
0170 void save_geometry(const std::filesystem::path& directory)
0171 {
0172     if (directory.empty())
0173         throw std::invalid_argument("simphony::save_geometry requires a destination directory");
0174 
0175     Runtime&        state = runtime();
0176     std::lock_guard lock(state.mutex);
0177     require_initialized(state);
0178 
0179     const std::string base = directory.string();
0180     U4GDML::Write(state.world, base.c_str(), "origin.gdml");
0181     state.foundry->save(base.c_str());
0182 }
0183 } // namespace simphony