LDMX Software
EcalPnetVetoProcessor.cxx
2
3namespace ecal {
4
5const std::vector<std::string> EcalPnetVetoProcessor::INPUT_NAMES{"points",
6 "features"};
7const std::vector<unsigned int> EcalPnetVetoProcessor::INPUT_SIZES{
8 N_COORDINATE_DIM * MAX_NUM_HITS, N_FEATURE_DIM* MAX_NUM_HITS};
9
10EcalPnetVetoProcessor::EcalPnetVetoProcessor(const std::string& name,
11 framework::Process& process)
12 : Producer(name, process) {
13 for (const auto& s : INPUT_SIZES) {
14 data_.emplace_back(s, 0);
15 }
16}
17
18void EcalPnetVetoProcessor::configure(
20 disc_cut_ = parameters.get<double>("disc_cut");
21 rt_ = std::make_unique<ldmx::ort::ONNXRuntime>(
22 parameters.get<std::string>("model_path"));
23
24 // Set the collection name as defined in the configuration
25 collection_name_ = parameters.get<std::string>("collection_name");
26
27 rec_coll_name_ = parameters.get<std::string>("rec_coll_name");
28 ecal_rec_hits_passname_ =
29 parameters.get<std::string>("ecal_rec_hits_passname");
30 ecal_sp_coll_name_ = parameters.get<std::string>("ecal_sp_coll_name");
31 ecal_sp_hits_passname_ = parameters.get<std::string>("ecal_sp_hits_passname");
32 track_pass_name_ = parameters.get<std::string>("track_pass_name", "");
33 track_collection_ = parameters.get<std::string>("track_collection");
34 recoil_from_tracking_ = parameters.get<bool>("recoil_from_tracking");
35}
36
37void EcalPnetVetoProcessor::produce(framework::Event& event) {
39 // Get the Ecal Geometry
40 const auto& ecal_geometry = getCondition<ldmx::EcalGeometry>(
41 ldmx::EcalGeometry::CONDITIONS_OBJECT_NAME);
42
43 // Get the collection of digitized Ecal hits_ from the event.
44 const auto ecal_rec_hits = event.getCollection<ldmx::EcalHit>(
45 rec_coll_name_, ecal_rec_hits_passname_);
46 auto nhits = std::count_if(
47 ecal_rec_hits.begin(), ecal_rec_hits.end(),
48 [](const ldmx::EcalHit& hit) { return hit.getEnergy() > 0; });
49
50 // check number of hits_
51 ldmx_log(debug) << "nhits = " << nhits << " MAX_NUM_HITS = " << MAX_NUM_HITS;
52 if (nhits < MAX_NUM_HITS) {
53 std::array<double, 3> etraj = {-999., -999., -999.};
54 std::array<double, 3> enorm = {-999., -999., -999.};
55 // Compute electron trajectory}
56 const ldmx::SimTrackerHit* electron_hit = nullptr;
57 // Use Scoring Plane or Tracking
58 if (!recoil_from_tracking_ &&
59 event.exists(ecal_sp_coll_name_, ecal_sp_hits_passname_)) {
60 auto const& ecal_sp_hits = event.getCollection<ldmx::SimTrackerHit>(
61 ecal_sp_coll_name_, ecal_sp_hits_passname_);
62 double electron_pz_max = -1.0;
63 for (auto const& hit : ecal_sp_hits) {
64 // Look at the electron only
65 if (hit.getPdgID() != 11) continue;
66 double electron_z = hit.getPosition()[2];
67 // Look at the SP in front of the ECAL
68 if (electron_z <= 239.0 || electron_z >= 240.0) continue;
69 double electron_pz = hit.getMomentum()[2];
70 // Find the highest pz electron
71 if (electron_pz > electron_pz_max) {
72 electron_pz_max = electron_pz;
73 electron_hit = &hit;
74 }
75 }
76 // If we found an electron hit at the scoring plane
77 if (electron_hit) {
78 // Get electron hit position/momentum at Ecal surface
79 ldmx_log(debug) << "Electron Found in the Ecal SP!";
80 auto pos = electron_hit->getPosition();
81 auto mom = electron_hit->getMomentum();
82 ldmx_log(debug) << "ECAL SP pos_=(" << pos[0] << "," << pos[1] << ","
83 << pos[2] << ")";
84 ldmx_log(debug) << "ECAL SP mom=(" << mom[0] << "," << mom[1] << ","
85 << mom[2] << ")";
86 etraj = {pos[0], pos[1], pos[2]};
87 double pz = mom[2];
88 if (pz != 0) {
89 // z_-normalized momentum
90 enorm = {mom[0] / pz, mom[1] / pz, 1.0};
91 }
92 }
93 } else if (recoil_from_tracking_) {
94 // Use tracking to get electron hit position/momentum at Ecal surface
95 auto recoil_tracks{event.getCollection<ldmx::Track>(track_collection_,
96 track_pass_name_)};
97 ldmx::TrackStateType ts_at_ecal = ldmx::TrackStateType::AtECAL;
98 auto recoil_track_states_ecal =
99 ecal::trackProp(recoil_tracks, ts_at_ecal, "ecal");
100 if (!recoil_track_states_ecal.empty()) {
101 std::array<double, 3> pos = {recoil_track_states_ecal[0],
102 recoil_track_states_ecal[1],
103 recoil_track_states_ecal[2]};
104 std::array<double, 3> mom = {(recoil_track_states_ecal[3]),
105 (recoil_track_states_ecal[4]),
106 (recoil_track_states_ecal[5])};
107 ldmx_log(debug) << "Electron track pos_=(" << pos[0] << "," << pos[1]
108 << "," << pos[2] << ")";
109 ldmx_log(debug) << "Electron track mom=(" << mom[0] << "," << mom[1]
110 << "," << mom[2] << ")";
111 etraj = pos;
112 double pz = mom[2];
113 if (pz != 0) {
114 enorm = {mom[0] / pz, mom[1] / pz, 1.0};
115 }
116 } else {
117 ldmx_log(info) << " No recoil track at ECAL";
118 }
119 } else {
120 ldmx_log(fatal) << " No electron hit at scoring plane or no tracking";
121 }
122
123 // make inputs
124 makeInputs(ecal_geometry, ecal_rec_hits, etraj, enorm);
125 // run the DNN
126 auto logits = rt_->run(INPUT_NAMES, data_)[0];
127 // make a log softmax of the logits then transform back
128 // to a probability with an exponential
129 auto prob = std::exp((logSoftmax(logits)[1]));
130 result.setDiscValue(prob);
131 } else {
132 result.setDiscValue(-99);
133 }
134
135 ldmx_log(debug) << "ParticleNet disc value = " << result.getDisc();
136
137 result.setVetoResult(result.getDisc() > disc_cut_);
138
139 // If the event passes the veto, keep it. Otherwise, drop the event.
140 if (result.passesVeto()) {
141 setStorageHint(framework::HINT_SHOULD_KEEP);
142 } else {
143 setStorageHint(framework::HINT_SHOULD_DROP);
144 }
145
146 event.add(collection_name_, result);
147}
148
149void EcalPnetVetoProcessor::makeInputs(
150 const ldmx::EcalGeometry& geom,
151 const std::vector<ldmx::EcalHit>& ecal_rec_hits,
152 std::array<double, 3> etraj, std::array<double, 3> enorm) {
153 // clear data
154 for (auto& v : data_) {
155 std::fill(v.begin(), v.end(), 0);
156 }
157
158 // Loop on the rechits
159 unsigned idx = 0;
160 for (const auto& hit : ecal_rec_hits) {
161 if (hit.getEnergy() <= 0) {
162 ldmx_log(warn)
163 << "Hit with zero energy found, should not happen, skipping it.";
164 continue;
165 }
166 ldmx::EcalID id(hit.getID());
167 auto [hit_x, hit_y, hit_z] = geom.getPosition(id);
168
169 double delta_z = hit_z - etraj[2];
170 double etraj_x = etraj[0] + enorm[0] * delta_z;
171 double etraj_y = etraj[1] + enorm[1] * delta_z;
172 data_[0].at(COORDINATE_X_OFFSET + idx) = hit_x - etraj_x;
173 data_[0].at(COORDINATE_Y_OFFSET + idx) = hit_y - etraj_y;
174 data_[0].at(COORDINATE_Z_OFFSET + idx) = hit_z;
175
176 data_[1].at(FEATURE_X_OFFSET + idx) = hit_x - etraj_x;
177 data_[1].at(FEATURE_Y_OFFSET + idx) = hit_y - etraj_y;
178 data_[1].at(FEATURE_Z_OFFSET + idx) = hit_z;
179 data_[1].at(FEATURE_LAYER_ID_OFFSET + idx) = id.layer();
180 data_[1].at(FEATURE_ENERGY_OFFSET + idx) = std::log(hit.getEnergy());
181
182 ++idx;
183 }
184
185 std::stringstream ss;
186 for (unsigned iname = 0; iname < INPUT_NAMES.size(); ++iname) {
187 ss << "=== " << INPUT_NAMES[iname] << " ===";
188 for (unsigned i = 0; i < INPUT_SIZES[iname]; ++i) {
189 ss << data_[iname].at(i) << ", ";
190 if ((i + 1) % MAX_NUM_HITS == 0) {
191 ss << "\n\n";
192 }
193 }
194 }
195 ldmx_log(trace) << ss.str();
196} // end of make inputs
197
198std::vector<float> EcalPnetVetoProcessor::logSoftmax(
199 const std::vector<float>& logits) {
200 // Find max for numerical stability
201 auto max_val = *std::max_element(logits.begin(), logits.end());
202
203 // Compute shifted exponentials and their sum
204 std::vector<float> exp_vals(logits.size());
205 for (size_t i = 0; i < logits.size(); ++i) {
206 exp_vals[i] = std::exp(logits[i] - max_val);
207 }
208
209 float sum_exp = std::accumulate(exp_vals.begin(), exp_vals.end(), 0.0);
210 float log_sum_exp = max_val + std::log(sum_exp);
211
212 // Compute log_softmax
213 std::vector<float> result(logits.size());
214 for (size_t i = 0; i < logits.size(); ++i) {
215 result[i] = logits[i] - log_sum_exp;
216 }
217
218 return result;
219}
220
221} // namespace ecal
222
std::vector< float > trackProp(const ldmx::Tracks &tracks, ldmx::TrackStateType ts_type, const std::string &ts_title)
Return a vector of parameters for a propagated recoil track.
Definition EcalHelper.cxx:5
Class that determines if event is vetoable using ECAL hit information w/ a deep neural network.
#define DECLARE_PRODUCER(CLASS)
Macro which allows the framework to construct a producer given its name during configuration.
Determines if event is vetoable using ECAL hit information w/ a deep neural network.
Implements an event buffer system for storing event data.
Definition Event.h:42
bool exists(const std::string &name, const std::string &passName, bool unique=true) const
Check for the existence of an object or collection with the given name and pass name in the event.
Definition Event.cxx:92
Class which represents the process under execution.
Definition Process.h:37
Class encapsulating parameters for configuring a processor.
Definition Parameters.h:29
const T & get(const std::string &name) const
Retrieve the parameter of the given name.
Definition Parameters.h:78
Translation between real-space positions and cell IDs within the ECal.
std::tuple< double, double, double > getPosition(EcalID id) const
Get a cell's position from its ID number.
Stores reconstructed hit information from the ECAL.
Definition EcalHit.h:19
Extension of DetectorID providing access to ECal layers and cell numbers in a hex grid.
Definition EcalID.h:20
bool passesVeto() const
Checks if the event passes the Ecal veto.
Represents a simulated tracker hit in the simulation.
std::vector< float > getPosition() const
Get the XYZ position of the hit [mm].
std::vector< double > getMomentum() const
Get the XYZ momentum of the particle at the position at which the hit took place [MeV].
Implementation of a track object.
Definition Track.h:53
constexpr StorageControl::Hint HINT_SHOULD_DROP
storage control hint alias for backwards compatibility
constexpr StorageControl::Hint HINT_SHOULD_KEEP
storage control hint alias for backwards compatibility