Back to home page

EIC code displayed by LXR

 
 

    


Warning, file /npsim/src/plugins/include/npdet/VolumeDispatchFilter.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_VOLUMEDISPATCHFILTER_H
0012 #define NPDET_VOLUMEDISPATCHFILTER_H
0013 
0014 /// Framework include files
0015 #include <DD4hep/Plugins.h>
0016 #include <DDG4/Geant4Action.h>
0017 #include <DDG4/Geant4SensDetAction.h>
0018 #include <DDG4/Geant4FastSimSpot.h>
0019 
0020 #include <G4Step.hh>
0021 #include <G4VPhysicalVolume.hh>
0022 #include <G4LogicalVolume.hh>
0023 
0024 #include <nlohmann/json.hpp>
0025 
0026 #include <memory>
0027 #include <optional>
0028 #include <regex>
0029 #include <string>
0030 #include <vector>
0031 
0032 /// Namespace for the AIDA detector description toolkit
0033 namespace dd4hep {
0034 
0035   /// Namespace for the Geant4 based simulation part of the AIDA detector description toolkit
0036   namespace sim {
0037 
0038     /**
0039      * \addtogroup Geant4SDActionPlugin
0040      *
0041      * @{
0042      * \package VolumeDispatchFilter
0043      *
0044      * \brief Particle filter companion to VolumeDispatchAction.
0045      *
0046      *  Maps logical-volume regexes to existing DDG4/DDSim filter plugins via
0047      *  the \c Properties JSON property.  Each entry specifies the filter
0048      *  type/name using DDG4's \c TypeName convention and any additional
0049      *  filter-specific properties.  Volumes absent from the object are not
0050      *  filtered (accept all).
0051      *
0052      * \param string Properties
0053      *   JSON object \c {"volume_regex": ["TypeName/Instance", {"key": "val", ...}], ...}
0054      *
0055      *   Each value is a JSON array whose first element is the DDG4 TypeName
0056      *   (type/instance split on the first \c /) and whose optional second
0057      *   element is a parameter dict.
0058      *
0059      *  Example:
0060      *  \code
0061      *    Properties = json.dumps({
0062      *      "mcp_vol": ("ParticleSelectFilter/OpticalPhotonSelector",
0063      *                  {"particle": "opticalphoton"})
0064      *    })
0065      *  \endcode
0066      *
0067      * @}
0068      */
0069     class VolumeDispatchFilter : public Geant4Filter {
0070 
0071       struct VolumeEntry {
0072         std::string               pattern;
0073         Geant4Filter*             filter     { nullptr };
0074         bool                      configured { false };   ///< true when a plugin spec was present
0075         std::optional<std::regex> compiled_regex;
0076         mutable std::size_t       steps_accepted { 0 };
0077         mutable std::size_t       steps_rejected { 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       VolumeDispatchFilter(Geant4Context* c, const std::string& n)
0086           : Geant4Filter(c, n) {
0087         declareProperty("Properties", m_properties_json);
0088       }
0089 
0090       virtual ~VolumeDispatchFilter() {
0091         // Summary line so users can confirm the filter was operational
0092         printout(INFO, name().c_str(),
0093                  "+++ VolumeDispatchFilter summary: %zu volumes configured",
0094                  m_entries.size());
0095         for (const auto& entry : m_entries) {
0096           const char* status = entry.filter ? "OK" : (entry.configured ? "FAILED" : "no filter");
0097           printout(INFO, name().c_str(),
0098                    "    volume regex '%-30s': %s, accepted %zu / rejected %zu steps",
0099                    entry.pattern.c_str(), status,
0100                    entry.steps_accepted, entry.steps_rejected);
0101         }
0102         for (auto& entry : m_entries)
0103           if (entry.filter) entry.filter->release();
0104       }
0105 
0106       void ensureEntries() const {
0107         if (m_entries_parsed) return;
0108         m_entries.clear();
0109         if (!m_properties_json.empty()) {
0110           try {
0111             auto j = nlohmann::json::parse(m_properties_json);
0112             for (const auto& [vol, val] : j.items()) {
0113               VolumeEntry entry;
0114               entry.pattern = vol;
0115               if (!entry.pattern.empty()) {
0116                 try {
0117                   entry.compiled_regex.emplace(
0118                       entry.pattern,
0119                       std::regex_constants::ECMAScript | std::regex_constants::optimize);
0120                 } catch (const std::regex_error& e) {
0121                   printout(ERROR, name().c_str(),
0122                            "Invalid regex '%s': %s", entry.pattern.c_str(), e.what());
0123                 }
0124               }
0125               // Value is a JSON array: ["TypeName/Instance", {"key": "val", ...}]
0126               // The second element (params dict) is optional.
0127               if (val.is_array() && !val.empty()) {
0128                 entry.configured = true;   // plugin spec was present
0129                 const auto tn = TypeName::split(val[0].get<std::string>());
0130                 auto* act = PluginService::Create<Geant4Action*>(
0131                     tn.first, const_cast<Geant4Context*>(context()), tn.second);
0132                 if (!act) {
0133                   printout(ERROR, name().c_str(),
0134                            "Failed to create filter '%s' for volume '%s'",
0135                            tn.first.c_str(), vol.c_str());
0136                 } else {
0137                   entry.filter = dynamic_cast<Geant4Filter*>(act);
0138                   if (!entry.filter) {
0139                     printout(ERROR, name().c_str(),
0140                              "Plugin '%s' is not a Geant4Filter", tn.first.c_str());
0141                     act->release();
0142                   } else {
0143                     // Apply optional params dict (second array element)
0144                     if (val.size() > 1 && val[1].is_object()) {
0145                       for (const auto& [key, pval] : val[1].items()) {
0146                         if (entry.filter->hasProperty(key)) {
0147                           entry.filter->property(key).str(pval.is_string()
0148                               ? pval.get<std::string>()
0149                               : pval.dump());
0150                         } else {
0151                           printout(WARNING, name().c_str(),
0152                                    "Filter '%s' has no property '%s'",
0153                                    tn.first.c_str(), key.c_str());
0154                         }
0155                       }
0156                     }
0157                   }
0158                 }
0159               }
0160               m_entries.push_back(std::move(entry));
0161             }
0162           } catch (const nlohmann::json::exception& e) {
0163             printout(ERROR, name().c_str(),
0164                      "Failed to parse Properties JSON: %s", e.what());
0165           }
0166         }
0167         m_entries_parsed = true;
0168       }
0169 
0170       virtual bool operator()(const G4Step* step) const override {
0171         ensureEntries();
0172         const G4VPhysicalVolume* pv = step->GetPreStepPoint()->GetPhysicalVolume();
0173         if (!pv) return true;
0174         const std::string lv_name = pv->GetLogicalVolume()->GetName();
0175 
0176         for (const auto& entry : m_entries) {
0177           if (!entry.matches(lv_name)) continue;
0178           bool result;
0179           if (!entry.filter) {
0180             if (entry.configured)
0181               printout(ERROR, name().c_str(),
0182                        "Filter for volume '%s' was not created; rejecting step",
0183                        entry.pattern.c_str());
0184             result = !entry.configured; // unconfigured → accept all; misconfigured → reject
0185           } else {
0186             result = (*entry.filter)(step);
0187           }
0188           result ? ++entry.steps_accepted : ++entry.steps_rejected;
0189           return result;
0190         }
0191         return true; // unmatched volume
0192       }
0193 
0194       virtual bool operator()(const Geant4FastSimSpot* spot) const override {
0195         ensureEntries();
0196         const G4VPhysicalVolume* pv = spot ? spot->volume() : nullptr;
0197         if (!pv) return true;
0198         const std::string lv_name = pv->GetLogicalVolume()->GetName();
0199 
0200         for (const auto& entry : m_entries) {
0201           if (!entry.matches(lv_name)) continue;
0202           bool result;
0203           if (!entry.filter) {
0204             if (entry.configured)
0205               printout(ERROR, name().c_str(),
0206                        "Filter for volume '%s' was not created; rejecting spot",
0207                        entry.pattern.c_str());
0208             result = !entry.configured;
0209           } else {
0210             result = (*entry.filter)(spot);
0211           }
0212           result ? ++entry.steps_accepted : ++entry.steps_rejected;
0213           return result;
0214         }
0215         return true;
0216       }
0217 
0218     private:
0219       std::string                      m_properties_json;
0220       mutable std::vector<VolumeEntry> m_entries;
0221       mutable bool                     m_entries_parsed { false };
0222     };
0223 
0224   } // namespace sim
0225 } // namespace dd4hep
0226 
0227 #endif // NPDET_VOLUMEDISPATCHFILTER_H