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
0003
0004
0005
0006
0007
0008
0009
0010
0011 #ifndef NPDET_VOLUMEDISPATCHACTION_H
0012 #define NPDET_VOLUMEDISPATCHACTION_H
0013
0014
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
0034 namespace dd4hep {
0035
0036
0037 namespace sim {
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
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
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
0111 virtual void defineCollections() override {
0112 if (m_entries_initialised) return;
0113
0114
0115
0116
0117
0118
0119
0120
0121
0122
0123
0124
0125
0126
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
0146
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
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
0172
0173
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
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;
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 }
0260 }
0261
0262 #endif