Back to home page

EIC code displayed by LXR

 
 

    


Warning, file /npsim/src/plugins/include/npdet/VolumeDispatchAction.h was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 //==========================================================================
0002 //  AIDA Detector description implementation
0003 //--------------------------------------------------------------------------
0004 // Copyright (C) Organisation europeenne pour la Recherche nucleaire (CERN)
0005 // All rights reserved.
0006 //
0007 // For the licensing terms see $DD4hepINSTALL/LICENSE.
0008 // For the list of contributors see $DD4hepINSTALL/doc/CREDITS.
0009 //
0010 //==========================================================================
0011 #ifndef NPDET_VOLUMEDISPATCHACTION_H
0012 #define NPDET_VOLUMEDISPATCHACTION_H
0013 
0014 /// Framework include files
0015 #include <DD4hep/Plugins.h>
0016 #include <DDG4/Geant4Action.h>
0017 #include <DDG4/Geant4Data.h>
0018 #include <DDG4/Geant4SensDetAction.h>
0019 #include <DDG4/Geant4FastSimSpot.h>
0020 
0021 #include <G4Step.hh>
0022 #include <G4VPhysicalVolume.hh>
0023 #include <G4LogicalVolume.hh>
0024 
0025 #include <nlohmann/json.hpp>
0026 
0027 #include <memory>
0028 #include <optional>
0029 #include <regex>
0030 #include <string>
0031 #include <vector>
0032 
0033 /// Namespace for the AIDA detector description toolkit
0034 namespace dd4hep {
0035 
0036   /// Namespace for the Geant4 based simulation part of the AIDA detector description toolkit
0037   namespace sim {
0038 
0039     /**
0040      * \addtogroup Geant4SDActionPlugin
0041      *
0042      * @{
0043      * \package VolumeDispatchAction
0044      *
0045      * \brief Sensitive detector action that routes steps to per-volume DDG4 SD
0046      *        action plugins, instantiated at run-time via PluginService::Create.
0047      *
0048      *  Any registered \c Geant4Sensitive plugin may be used as a per-volume
0049      *  action.  Hit collections are shared through the common
0050      *  \c Geant4SensDetActionSequence, so hits from all volumes land in the
0051      *  same readout.
0052      *
0053      * \param string Properties
0054      *   JSON object \c {"volume_regex": ["TypeName/Instance", {"key": "val", ...}], ...}
0055      *
0056      *   Each value is a JSON array whose first element is the DDG4 TypeName
0057      *   (type/instance split on the first \c /) and whose optional second
0058      *   element is a parameter dict.
0059      *
0060      *  Example:
0061      *  \code
0062      *    Properties = json.dumps({
0063      *      "mcp_vol": ("Geant4OpticalTrackerAction", {}),
0064      *      "bar_vol": ("Geant4TrackerWeightedAction",
0065      *                  {"CollectSingleDeposits": "false"}),
0066      *    })
0067      *  \endcode
0068      *
0069      * @}
0070      */
0071     class VolumeDispatchAction : public Geant4Sensitive {
0072 
0073       struct VolumeEntry {
0074         std::string               pattern;
0075         Geant4Sensitive*          action { nullptr };
0076         std::optional<std::regex> compiled_regex;
0077         mutable std::size_t       steps_dispatched { 0 };
0078 
0079         bool matches(const std::string& lv_name) const {
0080           return compiled_regex && std::regex_search(lv_name, *compiled_regex);
0081         }
0082       };
0083 
0084     public:
0085       VolumeDispatchAction(Geant4Context* ctxt, const std::string& n,
0086                                    DetElement det, Detector& dsc)
0087           : Geant4Sensitive(ctxt, n, det, dsc) {
0088         declareProperty("Properties", m_properties_json);
0089       }
0090 
0091       virtual ~VolumeDispatchAction() {
0092         // Summary line so users can confirm the action was operational
0093         printout(INFO, name().c_str(),
0094                  "+++ VolumeDispatchAction summary: %zu volumes configured",
0095                  m_entries.size());
0096         for (const auto& entry : m_entries) {
0097           const char* status = entry.action ? "OK" : "FAILED (no action created)";
0098           printout(INFO, name().c_str(),
0099                    "    volume regex '%-30s': %s, %zu steps dispatched",
0100                    entry.pattern.c_str(), status, entry.steps_dispatched);
0101         }
0102         if (m_steps_unmatched > 0)
0103           printout(INFO, name().c_str(),
0104                    "    %zu steps matched no volume entry and were dropped",
0105                    m_steps_unmatched);
0106         for (auto& entry : m_entries)
0107           if (entry.action) entry.action->release();
0108       }
0109 
0110       /// Called during setup after setDetector(); create and initialise sub-actions here.
0111       virtual void defineCollections() override {
0112         if (m_entries_initialised) return;
0113 
0114         // The shared hit collection is registered by the first successfully-created
0115         // sub-action via its own defineCollections() call.  This ensures that the
0116         // collection's factory function and owner pointer in m_collections are set
0117         // by the actual Geant4SensitiveAction<T> subclass (which knows the concrete
0118         // hit type), rather than by this dispatcher class.
0119         //
0120         // Sub-actions are called defineCollections() at most ONCE (the first one that
0121         // succeeds).  Subsequent sub-actions are NOT allowed to call it — doing so
0122         // would append additional entries to m_collections, giving each sub-action a
0123         // different m_collectionID and causing hits to land in separate (potentially
0124         // unwritten) secondary collections.  Because m_collectionID defaults to 0 and
0125         // TrackerWeightedAction hardcodes collection(0), all sub-actions write to the
0126         // single shared collection at index 0.
0127         bool collection_defined = false;
0128 
0129         if (!m_properties_json.empty()) {
0130           try {
0131             auto j = nlohmann::json::parse(m_properties_json);
0132             for (const auto& [vol, val] : j.items()) {
0133               VolumeEntry entry;
0134               entry.pattern = vol;
0135               if (!entry.pattern.empty()) {
0136                 try {
0137                   entry.compiled_regex.emplace(
0138                       entry.pattern,
0139                       std::regex_constants::ECMAScript | std::regex_constants::optimize);
0140                 } catch (const std::regex_error& e) {
0141                   printout(ERROR, name().c_str(),
0142                            "Invalid regex '%s': %s", entry.pattern.c_str(), e.what());
0143                 }
0144               }
0145               // Value is a JSON array: ["TypeName/Instance", {"key": "val", ...}]
0146               // The second element (params dict) is optional.
0147               if (val.is_array() && !val.empty()) {
0148                 const auto tn = TypeName::split(val[0].get<std::string>());
0149                 Geant4Sensitive* act = PluginService::Create<Geant4Sensitive*>(
0150                     tn.first, context(), tn.second, &m_detector, &m_detDesc);
0151                 if (!act) {
0152                   printout(ERROR, name().c_str(),
0153                            "Failed to create SD action '%s' for volume '%s'",
0154                            tn.first.c_str(), vol.c_str());
0155                 } else {
0156                   // Apply optional params dict (second array element)
0157                   if (val.size() > 1 && val[1].is_object()) {
0158                     for (const auto& [key, pval] : val[1].items()) {
0159                       if (act->hasProperty(key)) {
0160                         act->property(key).str(pval.is_string()
0161                             ? pval.get<std::string>()
0162                             : pval.dump());
0163                       } else {
0164                         printout(WARNING, name().c_str(),
0165                                  "Action '%s' has no property '%s'",
0166                                  tn.first.c_str(), key.c_str());
0167                       }
0168                     }
0169                   }
0170                   act->setDetector(&detector());
0171                   // The first sub-action registers the shared collection (index 0) and
0172                   // has its m_collectionID set to 0 explicitly via defineCollections().
0173                   // Subsequent sub-actions use m_collectionID=0 by default.
0174                   if (!collection_defined) {
0175                     act->defineCollections();
0176                     collection_defined = true;
0177                     printout(INFO, name().c_str(),
0178                              "+++ Registered shared hit collection via sub-action '%s'",
0179                              tn.first.c_str());
0180                   }
0181                   entry.action = act;
0182                 }
0183               } else {
0184                 printout(ERROR, name().c_str(),
0185                          "Malformed Properties entry for volume '%s': "
0186                          "expected [\"Type/Instance\", {params}], got '%s'. "
0187                          "Steps in this volume will be dropped.",
0188                          vol.c_str(), val.dump().c_str());
0189               }
0190               m_entries.push_back(std::move(entry));
0191             }
0192           } catch (const nlohmann::json::exception& e) {
0193             printout(ERROR, name().c_str(),
0194                      "Failed to parse Properties JSON: %s", e.what());
0195           }
0196         }
0197         if (!collection_defined) {
0198           // Fallback: register the collection directly if no sub-action was created
0199           printout(WARNING, name().c_str(),
0200                    "+++ No sub-action created; registering collection directly");
0201           defineCollection<Geant4Tracker::Hit>(m_readout.name());
0202         }
0203         m_entries_initialised = true;
0204       }
0205 
0206       virtual void begin(G4HCofThisEvent* hce) override {
0207         for (auto& entry : m_entries)
0208           if (entry.action) entry.action->begin(hce);
0209       }
0210 
0211       virtual void end(G4HCofThisEvent* hce) override {
0212         for (auto& entry : m_entries)
0213           if (entry.action) entry.action->end(hce);
0214       }
0215 
0216       virtual void clear(G4HCofThisEvent* hce) override {
0217         for (auto& entry : m_entries)
0218           if (entry.action) entry.action->clear(hce);
0219       }
0220 
0221       virtual bool process(const G4Step* step, G4TouchableHistory* history) override {
0222         const G4VPhysicalVolume* pv = step->GetPreStepPoint()->GetPhysicalVolume();
0223         if (!pv) return false;
0224         const std::string lv_name = pv->GetLogicalVolume()->GetName();
0225 
0226         for (auto& entry : m_entries) {
0227           if (!entry.matches(lv_name)) continue;
0228           ++entry.steps_dispatched;
0229           if (!entry.action) return false;
0230           return entry.action->process(step, history);
0231         }
0232         ++m_steps_unmatched;
0233         return false; // unmatched volume
0234       }
0235 
0236       virtual bool processFastSim(const Geant4FastSimSpot* spot,
0237                                    G4TouchableHistory* history) override {
0238         const G4VPhysicalVolume* pv = spot ? spot->volume() : nullptr;
0239         if (!pv) return false;
0240         const std::string lv_name = pv->GetLogicalVolume()->GetName();
0241 
0242         for (auto& entry : m_entries) {
0243           if (!entry.matches(lv_name)) continue;
0244           ++entry.steps_dispatched;
0245           if (!entry.action) return false;
0246           return entry.action->processFastSim(spot, history);
0247         }
0248         ++m_steps_unmatched;
0249         return false;
0250       }
0251 
0252     private:
0253       std::string              m_properties_json;
0254       std::vector<VolumeEntry> m_entries;
0255       bool                     m_entries_initialised { false };
0256       std::size_t              m_steps_unmatched     { 0 };
0257     };
0258 
0259   } // namespace sim
0260 } // namespace dd4hep
0261 
0262 #endif // NPDET_VOLUMEDISPATCHACTION_H