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_hits_passname_ = parameters.get<std::string>("ecal_sp_hits_passname");
31 track_pass_name_ = parameters.get<std::string>("track_pass_name", "");
32 track_collection_ = parameters.get<std::string>("track_collection");
33 recoil_from_tracking_ = parameters.get<bool>("recoil_from_tracking");
34}
35
36void EcalPnetVetoProcessor::produce(framework::Event& event) {
38 // Get the Ecal Geometry
39 const auto& ecal_geometry = getCondition<ldmx::EcalGeometry>(
40 ldmx::EcalGeometry::CONDITIONS_OBJECT_NAME);
41
42 // Get the collection of digitized Ecal hits_ from the event.
43 const auto ecal_rec_hits = event.getCollection<ldmx::EcalHit>(
44 rec_coll_name_, ecal_rec_hits_passname_);
45 auto nhits = std::count_if(
46 ecal_rec_hits.begin(), ecal_rec_hits.end(),
47 [](const ldmx::EcalHit& hit) { return hit.getEnergy() > 0; });
48
49 // check number of hits_
50 ldmx_log(debug) << "nhits = " << nhits << " MAX_NUM_HITS = " << MAX_NUM_HITS;
51 if (nhits < MAX_NUM_HITS) {
52 std::array<double, 3> etraj = {-999., -999., -999.};
53 std::array<double, 3> enorm = {-999., -999., -999.};
54 // Compute electron trajectory}
55 const ldmx::SimTrackerHit* electron_hit = nullptr;
56 // Use Scoring Plane or Tracking
57 if (!recoil_from_tracking_ &&
58 event.exists("EcalScoringPlaneHits", ecal_sp_hits_passname_)) {
59 auto const& ecal_sp_hits = event.getCollection<ldmx::SimTrackerHit>(
60 "EcalScoringPlaneHits", ecal_sp_hits_passname_);
61 double electron_pz_max = -1.0;
62 for (auto const& hit : ecal_sp_hits) {
63 // Look at the electron only
64 if (hit.getPdgID() != 11) continue;
65 double electron_z = hit.getPosition()[2];
66 // Look at the SP in front of the ECAL
67 if (electron_z <= 239.0 || electron_z >= 240.0) continue;
68 double electron_pz = hit.getMomentum()[2];
69 // Find the highest pz electron
70 if (electron_pz > electron_pz_max) {
71 electron_pz_max = electron_pz;
72 electron_hit = &hit;
73 }
74 }
75 // If we found an electron hit at the scoring plane
76 if (electron_hit) {
77 // Get electron hit position/momentum at Ecal surface
78 ldmx_log(debug) << "Electron Found in the Ecal SP!";
79 auto pos = electron_hit->getPosition();
80 auto mom = electron_hit->getMomentum();
81 ldmx_log(debug) << "ECAL SP pos_=(" << pos[0] << "," << pos[1] << ","
82 << pos[2] << ")";
83 ldmx_log(debug) << "ECAL SP mom=(" << mom[0] << "," << mom[1] << ","
84 << mom[2] << ")";
85 etraj = {pos[0], pos[1], pos[2]};
86 double pz = mom[2];
87 if (pz != 0) {
88 // z_-normalized momentum
89 enorm = {mom[0] / pz, mom[1] / pz, 1.0};
90 }
91 }
92 } else if (recoil_from_tracking_) {
93 // Use tracking to get electron hit position/momentum at Ecal surface
94 auto recoil_tracks{event.getCollection<ldmx::Track>(track_collection_,
95 track_pass_name_)};
96 ldmx::TrackStateType ts_at_ecal = ldmx::TrackStateType::AtECAL;
97 auto recoil_track_states_ecal =
98 ecal::trackProp(recoil_tracks, ts_at_ecal, "ecal");
99 if (!recoil_track_states_ecal.empty()) {
100 std::array<double, 3> pos = {recoil_track_states_ecal[0],
101 recoil_track_states_ecal[1],
102 recoil_track_states_ecal[2]};
103 std::array<double, 3> mom = {(recoil_track_states_ecal[3]),
104 (recoil_track_states_ecal[4]),
105 (recoil_track_states_ecal[5])};
106 ldmx_log(debug) << "Electron track pos_=(" << pos[0] << "," << pos[1]
107 << "," << pos[2] << ")";
108 ldmx_log(debug) << "Electron track mom=(" << mom[0] << "," << mom[1]
109 << "," << mom[2] << ")";
110 etraj = pos;
111 double pz = mom[2];
112 if (pz != 0) {
113 enorm = {mom[0] / pz, mom[1] / pz, 1.0};
114 }
115 } else {
116 ldmx_log(info) << " No recoil track at ECAL";
117 }
118 } else {
119 ldmx_log(fatal) << " No electron hit at scoring plane or no tracking";
120 }
121
122 // make inputs
123 makeInputs(ecal_geometry, ecal_rec_hits, etraj, enorm);
124 // run the DNN
125 auto logits = rt_->run(INPUT_NAMES, data_)[0];
126 // make a log softmax of the logits then transform back
127 // to a probability with an exponential
128 auto prob = std::exp((logSoftmax(logits)[1]));
129 result.setDiscValue(prob);
130 } else {
131 result.setDiscValue(-99);
132 }
133
134 ldmx_log(debug) << "ParticleNet disc value = " << result.getDisc();
135
136 result.setVetoResult(result.getDisc() > disc_cut_);
137
138 // If the event passes the veto, keep it. Otherwise, drop the event.
139 if (result.passesVeto()) {
140 setStorageHint(framework::HINT_SHOULD_KEEP);
141 } else {
142 setStorageHint(framework::HINT_SHOULD_DROP);
143 }
144
145 event.add(collection_name_, result);
146}
147
148void EcalPnetVetoProcessor::makeInputs(
149 const ldmx::EcalGeometry& geom,
150 const std::vector<ldmx::EcalHit>& ecal_rec_hits,
151 std::array<double, 3> etraj, std::array<double, 3> enorm) {
152 // clear data
153 for (auto& v : data_) {
154 std::fill(v.begin(), v.end(), 0);
155 }
156
157 // Loop on the rechits
158 unsigned idx = 0;
159 for (const auto& hit : ecal_rec_hits) {
160 if (hit.getEnergy() <= 0) {
161 ldmx_log(warn)
162 << "Hit with zero energy found, should not happen, skipping it.";
163 continue;
164 }
165 ldmx::EcalID id(hit.getID());
166 auto [hit_x, hit_y, hit_z] = geom.getPosition(id);
167
168 double delta_z = hit_z - etraj[2];
169 double etraj_x = etraj[0] + enorm[0] * delta_z;
170 double etraj_y = etraj[1] + enorm[1] * delta_z;
171 data_[0].at(COORDINATE_X_OFFSET + idx) = hit_x - etraj_x;
172 data_[0].at(COORDINATE_Y_OFFSET + idx) = hit_y - etraj_y;
173 data_[0].at(COORDINATE_Z_OFFSET + idx) = hit_z;
174
175 data_[1].at(FEATURE_X_OFFSET + idx) = hit_x - etraj_x;
176 data_[1].at(FEATURE_Y_OFFSET + idx) = hit_y - etraj_y;
177 data_[1].at(FEATURE_Z_OFFSET + idx) = hit_z;
178 data_[1].at(FEATURE_LAYER_ID_OFFSET + idx) = id.layer();
179 data_[1].at(FEATURE_ENERGY_OFFSET + idx) = std::log(hit.getEnergy());
180
181 ++idx;
182 }
183
184 std::stringstream ss;
185 for (unsigned iname = 0; iname < INPUT_NAMES.size(); ++iname) {
186 ss << "=== " << INPUT_NAMES[iname] << " ===";
187 for (unsigned i = 0; i < INPUT_SIZES[iname]; ++i) {
188 ss << data_[iname].at(i) << ", ";
189 if ((i + 1) % MAX_NUM_HITS == 0) {
190 ss << "\n\n";
191 }
192 }
193 }
194 ldmx_log(trace) << ss.str();
195} // end of make inputs
196
197std::vector<float> EcalPnetVetoProcessor::logSoftmax(
198 const std::vector<float>& logits) {
199 // Find max for numerical stability
200 auto max_val = *std::max_element(logits.begin(), logits.end());
201
202 // Compute shifted exponentials and their sum
203 std::vector<float> exp_vals(logits.size());
204 for (size_t i = 0; i < logits.size(); ++i) {
205 exp_vals[i] = std::exp(logits[i] - max_val);
206 }
207
208 float sum_exp = std::accumulate(exp_vals.begin(), exp_vals.end(), 0.0);
209 float log_sum_exp = max_val + std::log(sum_exp);
210
211 // Compute log_softmax
212 std::vector<float> result(logits.size());
213 for (size_t i = 0; i < logits.size(); ++i) {
214 result[i] = logits[i] - log_sum_exp;
215 }
216
217 return result;
218}
219
220} // namespace ecal
221
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:36
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