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