LDMX Software
CLUE.cxx
1
2#include "Ecal/CLUE.h"
3
4#include <cmath>
5
6namespace ecal {
7
14template <typename T>
15T CLUE::dist(T x1, T y1, T x2, T y2) {
16 auto delta_x = x1 - x2;
17 auto delta_y = y1 - y2;
18 auto r_square = delta_x * delta_x + delta_y * delta_y;
19 return std::sqrt(r_square);
20}
21
22// 3D version, overloaded
23template <typename T>
24T CLUE::dist(T x1, T y1, T z1, T x2, T y2, T z2) {
25 auto delta_x = x1 - x2;
26 auto delta_y = y1 - y2;
27 auto delta_z = z1 - z2;
28 auto r_square = delta_x * delta_x + delta_y * delta_y + delta_z * delta_z;
29 return std::sqrt(r_square);
30}
31
32/* Old code, idea was to do electron reclustering based on first layer
33 centroids' distances to each other I.e. if electrons are close together =>
34 likely merged => recluster Did not quite work and I don't remember the idea
35 anymore but leaving the code here for inspo */
36
37void CLUE::electronSeparation(std::vector<ldmx::EcalHit> hits) {
38 std::vector<double> layer_thickness = {2., 3.5, 5.3, 5.3, 5.3, 5.3,
39 5.3, 5.3, 5.3, 5.3, 5.3, 10.5,
40 10.5, 10.5, 10.5, 10.5};
41 double air = 10.;
42 // sort hits in z
43 std::sort(hits.begin(), hits.end(),
44 [](const ldmx::EcalHit& a, const ldmx::EcalHit& b) {
45 return a.getZPos() < b.getZPos();
46 });
47
48 std::vector<ldmx::EcalHit> first_layers;
49 std::vector<IntermediateCluster> first_layer_clusters;
50 int layer_tag = 0;
51 double layer_z = hits[0].getZPos();
52 for (const auto& hit : hits) {
53 if (hit.getZPos() > layer_z + layer_thickness[layer_tag] + air) {
54 layer_tag++;
55 // if (layerTag > limit) break;
56 break;
57 }
58 first_layers.push_back(hit);
59 IntermediateCluster cluster(hit);
60 cluster.setLayer(layer_tag);
61 first_layer_clusters.push_back(cluster);
62 }
63 bool merge = false;
64 do {
65 merge = false;
66 for (int i = 0; i < first_layer_clusters.size(); i++) {
67 if (first_layer_clusters[i].empty()) continue;
68 // if (firstLayerClusters[i].energy() >= seedThreshold_) {
69 for (int j = i + 1; j < first_layer_clusters.size(); j++) {
70 if (first_layer_clusters[j].empty()) continue;
71 if (dist(first_layer_clusters[i].centroidX(),
72 first_layer_clusters[i].centroidY(),
73 first_layer_clusters[j].centroidX(),
74 first_layer_clusters[j].centroidY()) < 8.) {
75 first_layer_clusters[i].add(first_layer_clusters[j]);
76 first_layer_clusters[j].clear();
77 merge = true;
78 }
79 }
80 // } else break;
81 }
82 } while (merge);
83 ldmx_log(trace) << "--- ELECTRON SEPARATION ---";
84 for (int i = 0; i < first_layer_clusters.size(); i++) {
85 if (first_layer_clusters[i].empty()) continue;
86 ldmx_log(trace) << " Cluster " << i
87 << " x: " << first_layer_clusters[i].centroidX()
88 << " y: " << first_layer_clusters[i].centroidY();
89 for (int j = i + 1; j < first_layer_clusters.size(); j++) {
90 if (first_layer_clusters[j].empty()) continue;
91 auto d = dist(first_layer_clusters[i].centroidX(),
92 first_layer_clusters[i].centroidY(),
93 first_layer_clusters[j].centroidX(),
94 first_layer_clusters[j].centroidY());
95 ldmx_log(trace) << "Dist to cluster " << j << ": " << d;
96 }
97 }
98}
99
100// Function to create layers from hits based on their layer number
101std::vector<std::vector<const ldmx::EcalHit*>> CLUE::createLayers(
102 const std::vector<const ldmx::EcalHit*>& hits) {
103 ldmx_log(trace) << "--- LAYER CREATION ---";
104 ldmx_log(trace) << "Number of layers: " << nbr_of_layers_;
105
106 // vector of layers, each layer having a vector of hits
107 // initialize with nbr_of_layers_ empty vectors
108 std::vector<std::vector<const ldmx::EcalHit*>> hits_per_layer(nbr_of_layers_);
109
110 // Track highest energy per layer for proper rho_c calculation
111 std::vector<double> layer_max_energies(nbr_of_layers_, 0.0);
112
113 // Clear any existing layer_rho_c_ values
114 layer_rho_c_.clear();
115
116 // This is rather ad-hoc, but this way it's still tunable via rhoc_ parameter
117 double rhoc_factor = rhoc_ / 250.;
118
119 for (const auto& hit : hits) {
120 ldmx::EcalID ecal_id(hit->getID());
121 // layer number from EcalID, starting at 0
122 int layer = ecal_id.layer();
123
124 if (layer >= nbr_of_layers_) {
125 ldmx_log(trace) << "Skipping hit in layer " << layer
126 << " (beyond nbr_of_layers_ = " << nbr_of_layers_ << ")";
127 continue;
128 }
129
130 // Track the highest energy in this specific layer
131 if (hit->getEnergy() > layer_max_energies[layer]) {
132 layer_max_energies[layer] = hit->getEnergy();
133 }
134
135 ldmx_log(trace) << " Adding hit with energy " << hit->getEnergy()
136 << " to layer " << layer;
137 hits_per_layer[layer].push_back(hit);
138 } // end of loop over hits
139
140 // Calculate rhoc for each layer based on that layer's maximum energy
141 for (int i = 0; i < nbr_of_layers_; i++) {
142 double rho_c = layer_max_energies[i] / rhoc_factor;
143 layer_rho_c_.push_back(rho_c);
144 ldmx_log(trace) << "Layer " << i << " has " << hits_per_layer[i].size()
145 << " hits, max energy " << layer_max_energies[i]
146 << ", rho_c " << rho_c;
147 }
148
149 ldmx_log(trace) << "Created " << hits_per_layer.size() << " layers";
150 return hits_per_layer;
151} // end of createLayers
152
153float CLUE::roundToDecimal(float x_, int num_decimal_precision_digits) {
154 float power_of_10 = std::pow(10, num_decimal_precision_digits);
155 return std::round(x_ * power_of_10) / power_of_10;
156}
157
158std::vector<std::shared_ptr<CLUE::Density>> CLUE::setup(
159 const std::vector<const ldmx::EcalHit*>& hits) {
160 std::vector<std::shared_ptr<Density>> densities;
161 std::map<std::pair<float, float>, std::shared_ptr<Density>> density_map;
162 event_centroid_ = IntermediateCluster();
163 ldmx_log(trace) << "--- SETUP ---";
164 ldmx_log(trace) << "Building densities";
165 for (const auto& hit : hits) {
166 // collapse z dimension
167 float x = roundToDecimal(hit->getXPos(), 4);
168 float y = roundToDecimal(hit->getYPos(), 4);
169 float z = roundToDecimal(hit->getZPos(), 4);
170 ldmx_log(trace) << " New hit { x: " << x << " y: " << y << "}"
171 << " (and z: " << z << ")";
172 std::pair<float, float> coords;
173 if (dc_ != 0 && nbr_of_layers_ > 1) {
174 // if more than one layer, divide hit into densities with side dc
175 double i = std::ceil(std::abs(x) / dc_);
176 double j = std::ceil(std::abs(y) / dc_);
177 if (x < 0) {
178 i = -i;
179 x = (i + 0.5) * dc_;
180 } else {
181 x = (i - 0.5) * dc_;
182 }
183 if (y < 0) {
184 j = -j;
185 y = (j + 0.5) * dc_;
186 } else {
187 y = (j - 0.5) * dc_;
188 }
189 coords = {i, j};
190 ldmx_log(trace) << " Index " << i << ", " << j << "; x: " << x
191 << " y: " << y;
192 } else {
193 // if just one layer, have all densities with the same x,y be in same
194 // density
195 coords = {x, y};
196 }
197
198 if (density_map.find(coords) == density_map.end()) {
199 density_map.emplace(coords, std::make_shared<CLUE::Density>(x, y));
200 ldmx_log(trace) << " * New density created";
201 } else {
202 ldmx_log(trace) << " --> Found density with x: "
203 << density_map[coords]->x_
204 << " y: " << density_map[coords]->y_;
205 }
206 density_map[coords]->hits_.push_back(hit);
207 density_map[coords]->total_energy_ += hit->getEnergy();
208 density_map[coords]->z_ += hit->getZPos();
209
210 event_centroid_.add(hit);
211 } // end of loop over hits
212
213 densities.reserve(density_map.size());
214 for (const auto& entry : density_map) {
215 densities.push_back(std::move(entry.second));
216 }
217 // sort according to energy
218 std::sort(densities.begin(), densities.end(),
219 [](const std::shared_ptr<CLUE::Density>& a,
220 const std::shared_ptr<CLUE::Density>& b) {
221 return a->total_energy_ > b->total_energy_;
222 });
223
224 ldmx_log(trace) << "Decide parents";
225
226 // decide delta_ and follower_of_
227 for (int i = 0; i < densities.size(); i++) {
228 densities[i]->index_ = i;
229 // avg z position
230 densities[i]->z_ = densities[i]->z_ / densities[i]->hits_.size();
231 ldmx_log(trace) << " Index: " << i << "; x: " << densities[i]->x_
232 << "; y: " << densities[i]->y_
233 << "; Energy: " << densities[i]->total_energy_;
234 // loop through all higher energy densities
235 for (int j = 0; j < i; j++) {
236 float distance_2d = dist(densities[i]->x_, densities[i]->y_,
237 densities[j]->x_, densities[j]->y_);
238 // condition energyJ > energyI but this should be baked in as we sorted
239 // according to energy
240 if ((distance_2d < dm_) && (distance_2d < densities[i]->delta_)) {
241 densities[i]->delta_ = distance_2d;
242 densities[i]->follower_of_ = j;
243 ldmx_log(trace) << " New parent, index " << j
244 << "; delta_2d: " << std::setprecision(4)
245 << distance_2d;
246 }
247 }
248 }
249 return densities;
250} // end of setup
251
252// connecting_layers marks if we're currently doing 3D clustering (i.e.
253// connecting seeds between layers) otherwise, layer_index tells us which layer
254// number we're working on
255std::vector<std::vector<const ldmx::EcalHit*>> CLUE::clustering(
256 std::vector<std::shared_ptr<CLUE::Density>>& densities,
257 bool connecting_layers, int layer_index) {
258 ldmx_log(trace) << "--- CLUSTERING ---";
259 ldmx_log(trace) << "Number of densities: " << densities.size()
260 << "; connecting_layers: " << connecting_layers
261 << "; layer_index: " << layer_index;
262 if (!connecting_layers && nbr_of_layers_ > 1) {
263 // if layerwise clustering override rhoc_ and deltac_ with per-layer values
264 rhoc_ = layer_rho_c_[layer_index];
265 ldmx_log(trace) << "Setting rho_c on layer " << layer_index << " to "
266 << rhoc_;
267 if (layer_index * 2 - 1 < radius_.size()) {
268 deltac_ = radius_[layer_index * 2 - 1];
269 ldmx_log(trace) << "Setting delta_c on layer " << layer_index << " to "
270 << deltac_;
271 }
272 } else if (connecting_layers) {
273 // if doing 3D clustering, override deltac_ and rhoc_ with other values
274 // NOT IMPLEMENTED YET
275 // if currently doing 3D clustering
276 // IF 3D clustering
277 // deltao_ = 200.;
278 // deltac_ = 100.;
279 // rhoc_ = 1000.;
280 }
281
282 bool energy_overload = false;
283 double max_energy = 10000.;
284 clustering_loops_ = 0;
285 double delta_c_mod = deltac_;
286 double centroid_radius = 10.;
287
288 // stores seeds of this layer
289 std::vector<std::shared_ptr<Density>>& layer_seeds = seeds_[layer_index];
290
291 // stores hits in cluster
292 std::vector<std::vector<const ldmx::EcalHit*>> clusters;
293 // keeps track of which densities have merged; only used if reclustering
294 std::vector<bool> merged_densities; // index_= cluster id
295 merged_densities.resize(densities.size());
296 // keeps track of cluster energies
297 std::vector<double> cluster_energies;
298 do {
299 // while no cluster has merged
300 if (energy_overload) {
301 // makes delta_c smaller if clusters have merged
302 delta_c_mod = delta_c_mod / 1.1;
303 ldmx_log(trace) << "Energy overload, new delta_cmod: " << delta_c_mod;
304 energy_overload = false;
305 }
306
307 clustering_loops_++;
308 ldmx_log(trace) << "Clustering loop " << clustering_loops_;
309
310 // cluster index
311 int k = 0;
312
313 layer_seeds.clear();
314 layer_seeds.reserve(densities.size());
315 clusters.clear();
316 clusters.reserve(densities.size());
317 cluster_energies.clear();
318 cluster_energies.reserve(densities.size());
319
320 std::stack<int> cluster_stack;
321 // stores followers of densities at corr index_
322 std::vector<std::vector<int>> followers;
323 followers.resize(densities.size());
324
325 // Mark as seed, follower, or outlier
326 for (auto& density : densities) {
327 // funky line to generalize this function for both 2D and 3D case
328 ldmx_log(trace) << " Index: " << density->index_
329 << "; x: " << density->x_ << "; y: " << density->y_
330 << "; Energy: " << density->total_energy_
331 << " Parent ID: " << density->follower_of_
332 << "; Delta: " << density->delta_;
333
334 bool is_seed;
335 if (delta_c_mod != deltac_ && density->cluster_id_ >= 0 &&
336 merged_densities[density->cluster_id_] &&
337 dist(density->x_, density->y_, event_centroid_.centroidX(),
338 event_centroid_.centroidY()) < centroid_radius) {
339 // if energy has been overloaded and this density belongs to cluster
340 // that was overloaded and this density is close enough to event
341 // centroid use modded delta c
342 is_seed =
343 density->total_energy_ > rhoc_ && density->delta_ > delta_c_mod;
344 } else {
345 is_seed = density->total_energy_ > rhoc_ && density->delta_ > deltac_;
346 if (is_seed) {
347 ldmx_log(trace) << " Distance to event centroid: "
348 << dist(density->x_, density->y_,
349 event_centroid_.centroidX(),
350 event_centroid_.centroidY());
351 }
352 }
353 bool is_outlier =
354 (density->total_energy_ < rhoc_) && (density->delta_ > deltao_);
355 density->cluster_id_ = -1;
356 if (is_seed) {
357 ldmx_log(trace) << " This is a Seed";
358 ldmx_log(trace) << " Distance to centroid: "
359 << dist(density->x_, density->y_,
360 event_centroid_.centroidX(),
361 event_centroid_.centroidY())
362 << "; with delta " << density->delta_;
363 ldmx_log(trace) << " Setting cluster ID to " << k;
364 density->cluster_id_ = k;
365 k++;
366 // get the index of the seed density
367 cluster_stack.push(density->index_);
368 clusters.push_back(density->hits_);
369 cluster_energies.push_back(density->total_energy_);
370 layer_seeds.push_back(density);
371 } else if (!is_outlier) {
372 ldmx_log(trace) << " This is a Follower";
373 int& parent_index = density->follower_of_;
374 if (parent_index != -1)
375 followers[parent_index].push_back(density->index_);
376 else
377 ldmx_log(error)
378 << " Somehow found a follower with parent index -1: id = "
379 << density->index_;
380 } else {
381 ldmx_log(trace) << " This is an Outlier";
382 }
383 }
384
385 merged_densities.clear();
386 merged_densities.resize(densities.size());
387
388 // Go through all seeds and add followers, then follower's followers, etc.
389 while (cluster_stack.size() > 0) {
390 auto& d = densities[cluster_stack.top()];
391 cluster_stack.pop();
392 auto& cid = d->cluster_id_;
393 // for indices of followers of dp
394 for (const auto follower_index : followers[d->index_]) {
395 auto& follower = densities[follower_index];
396 // Set cluster index of the follower to the cluster index of `d`
397 follower->cluster_id_ = cid;
398 cluster_energies[cid] += follower->total_energy_;
399
400 if (reclustering_ && cluster_energies[cid] > max_energy &&
401 delta_c_mod > 0.5 && clustering_loops_ < 100) {
402 // If reclustering is on and cluster energy is too high,
403 // delta_c_mod is not too low, and we haven't tried for too long
404 merged_densities[cid] = true;
405 if (!energy_overload && clustering_loops_ == 99) {
406 ldmx_log(warn) << "Merging clusters, max cluster loops hit";
407 }
408 energy_overload = true;
409 if (clustering_loops_ != 1) {
410 goto endwhile; // Don't break on the first loop to save the initial
411 // cluster number
412 }
413 }
414
415 clusters[cid].insert(std::end(clusters[cid]),
416 std::begin(follower->hits_),
417 std::end(follower->hits_));
418 // Add follower to the stack so its followers can also get the correct
419 // cluster index
420 cluster_stack.push(follower_index);
421 }
422 }
423 // for first clusteringloop, we want to save number of clusters before
424 // reclustering
425 if (clustering_loops_ == 1 && energy_overload)
426 initial_cluster_nbr_ = clusters.size();
427 endwhile:;
428 } while (energy_overload);
429 // if we have more than one layer and we are not currently doing CLUE3D
430 if (!connecting_layers && nbr_of_layers_ > 1) {
431 // Overwrite seed densities' properties with cluster properties
432 // Might be cleaner to just create new densities for cluster seeds
433 for (auto& seed : layer_seeds) {
434 seed->delta_ = std::numeric_limits<float>::max();
435 seed->hits_ = clusters[seed->cluster_id_];
436 seed->total_energy_ = cluster_energies[seed->cluster_id_];
437 seed->index_ = seed_index_;
438 seed_index_++;
439 }
440 // Sort seeds in layer based on energy
441 std::sort(layer_seeds.begin(), layer_seeds.end(),
442 [](const std::shared_ptr<Density>& a,
443 const std::shared_ptr<Density>& b) {
444 return a->total_energy_ > b->total_energy_;
445 });
446 }
447 return clusters;
448} // end of clustering
449
450// Function to connect seeds between layers to form 3D clusters
451// ONLY used in CLUE3D
452std::vector<std::shared_ptr<CLUE::Density>> CLUE::setupForClue3D() {
453 ldmx_log(trace) << "--- LAYER SETUP ---";
454 std::vector<std::shared_ptr<CLUE::Density>> densities;
455 layer_rho_c_.clear();
456 for (int layer = 0; layer < nbr_of_layers_; layer++) {
457 ldmx_log(trace) << " LAYER " << layer << " with " << seeds_[layer].size()
458 << " seeds";
459 auto& seeds_in_current_layer = seeds_[layer];
460 double highest_energy = 0.;
461 for (const auto& current_seed : seeds_in_current_layer) {
462 // for each seed in layer
463 current_seed->layer_ = layer;
464 if (current_seed->total_energy_ > highest_energy)
465 highest_energy = current_seed->total_energy_;
466 ldmx_log(trace) << " Density with index " << current_seed->index_
467 << ", energy: " << current_seed->total_energy_
468 << " position (x,y)= {" << current_seed->x_ << ","
469 << current_seed->y_ << ")";
470 int depth = 1;
471 // decide delta_ and followerof from seeds in previous and next layer_
472 // do {
473 // depth++;
474 if ((layer - depth >= 0) && (layer - depth < seeds_.size())) {
475 ldmx_log(trace) << " Looking at pre-layer: " << layer - depth;
476 // look at previous layer
477 ldmx_log(trace) << " In previous layer... ";
478 auto& previous_layer = seeds_[layer - depth];
479 ldmx_log(trace) << " Got " << previous_layer.size() << " seeds";
480 for (const auto& prev_seed : previous_layer) {
481 // for each seed in previous layer
482 auto distance_2d_prev = dist(current_seed->x_, current_seed->y_,
483 prev_seed->x_, prev_seed->y_);
484 auto dz_prev = std::abs(current_seed->z_ - prev_seed->z_);
485 ldmx_log(trace) << " DeltaXY to index " << prev_seed->index_
486 << ": " << std::setprecision(4) << distance_2d_prev;
487 ldmx_log(trace) << " DeltaZ to index " << prev_seed->index_
488 << ": " << std::setprecision(4) << dz_prev;
489 if (prev_seed->total_energy_ > current_seed->total_energy_ &&
490 distance_2d_prev < current_seed->delta_ &&
491 dz_prev < current_seed->z_delta_) {
492 ldmx_log(trace) << " New parent: index " << prev_seed->index_
493 << " on layer " << layer - depth << "; energy "
494 << prev_seed->total_energy_;
495 ldmx_log(trace) << " New 2D distance to prev-layer: "
496 << std::setprecision(4) << distance_2d_prev;
497 ldmx_log(trace)
498 << " New delta Z: " << std::setprecision(4) << dz_prev;
499 current_seed->delta_ = distance_2d_prev;
500 current_seed->z_delta_ = dz_prev;
501 current_seed->follower_of_ = prev_seed->index_;
502 } else if (prev_seed->total_energy_ < current_seed->total_energy_) {
503 ldmx_log(trace) << " Breaking on index " << prev_seed->index_
504 << " with energy " << prev_seed->total_energy_;
505 break;
506 }
507 }
508 }
509
510 if (layer + depth < nbr_of_layers_ && layer + depth < seeds_.size()) {
511 ldmx_log(trace) << " Looking at post-layer: " << layer + depth;
512 auto& next_layer = seeds_[layer + depth];
513 ldmx_log(trace) << " Got " << next_layer.size() << " seeds";
514 for (const auto& next_seed : next_layer) {
515 auto distance_2d_next = dist(current_seed->x_, current_seed->y_,
516 next_seed->x_, next_seed->y_);
517 auto dz_next = std::abs(current_seed->z_ - next_seed->z_);
518 ldmx_log(trace) << " DeltaXY to index " << next_seed->index_
519 << ": " << std::setprecision(4) << distance_2d_next;
520 ldmx_log(trace) << " DeltaZ to index_" << next_seed->index_
521 << ": " << std::setprecision(4) << dz_next;
522 if (next_seed->total_energy_ > current_seed->total_energy_ &&
523 distance_2d_next < current_seed->delta_ &&
524 dz_next < current_seed->z_delta_) {
525 ldmx_log(trace) << " New parent: index_" << next_seed->index_
526 << " on layer " << layer + depth << "; energy "
527 << next_seed->total_energy_;
528 ldmx_log(trace) << " New 2D distance to next-layer: "
529 << std::setprecision(4) << distance_2d_next;
530 ldmx_log(trace)
531 << " New delta_Z: " << std::setprecision(4) << dz_next;
532 current_seed->delta_ = distance_2d_next;
533 current_seed->z_delta_ = dz_next;
534 current_seed->follower_of_ = next_seed->index_;
535 } else if (next_seed->total_energy_ < current_seed->total_energy_) {
536 ldmx_log(trace) << " Breaking on index_" << next_seed->index_
537 << " with energy " << next_seed->total_energy_;
538 break;
539 }
540 } // end loop on seeds in next layer
541 } // end of looking at next layer
542 // } while (currentLayer[i]->layerFollowerOf == -1 && (layer - depth >=
543 // 0 || layer + depth < nbr_of_layers_));
544 ldmx_log(trace) << " Done setting parents.";
545 densities.push_back(current_seed);
546 }
547 // TODO: This 2 needs to be configurable
548 layer_rho_c_.push_back(highest_energy / 2);
549 } // end loop over layers
550 return densities;
551} // end of setupForClue3D
552
553void CLUE::convertToIntermediateClusters(
554 std::vector<std::vector<const ldmx::EcalHit*>>& clusters) {
555 // Convert to workingecalclusters to ensure compatibility with
556 // EcalClusterProducer
557 for (const auto& cluster : clusters) {
558 IntermediateCluster intermediate_cluster{},
559 intermediate_cluster_first_layer{};
560
561 for (const auto& hit : cluster) {
562 intermediate_cluster.add(hit);
563 // if hit is in first layer, add to first layer cluster
564 ldmx::EcalID ecal_id(hit->getID());
565 auto layer = ecal_id.layer();
566 intermediate_cluster.setLayer(layer);
567 if (layer == 0) {
568 intermediate_cluster_first_layer.add(hit);
569 intermediate_cluster_first_layer.setLayer(layer);
570 }
571 }
572 final_clusters_.push_back(intermediate_cluster);
573 first_layer_centroids_.push_back(intermediate_cluster_first_layer);
574 auto cent_x = intermediate_cluster.centroidX();
575 auto cent_y = intermediate_cluster.centroidY();
576 auto event_cent_x = event_centroid_.centroidX();
577 auto event_cent_y = event_centroid_.centroidY();
578 const auto& distance = dist(cent_x, cent_y, event_cent_x, event_cent_y);
579 centroid_distances_.push_back(distance);
580 }
581}
582
583void CLUE::cluster(const std::vector<ldmx::EcalHit>& unsorted_hits, double dc,
584 double rc, double delta_c, double delta_o, int nbr_of_layers,
585 bool reclustering) {
586 ldmx_log(info) << "Starting CLUE clustering with parameters:" << "dc " << dc
587 << ", rc " << rc << ", delta_c " << delta_c << ", delta_o "
588 << delta_o << ", nbr_of_layers " << nbr_of_layers
589 << ", reclustering " << reclustering;
590 // cutoff distance for local density
591 dc_ = dc;
592 // min density to promote as seed/max density to demote as outlier
593 rhoc_ = rc;
594 // min separation distance for seeds
595 deltac_ = delta_c;
596 // min separation distance for outliers
597 deltao_ = delta_o;
598 // max distance to parent for both seeds and followers
599 dm_ = std::max(delta_c, delta_o);
600
601 // Recluster merged clusters or not
602 reclustering_ = reclustering;
603 nbr_of_layers_ = nbr_of_layers;
604
605 if (nbr_of_layers_ < 1) {
606 // anything below 1 => include all layers
607 nbr_of_layers_ = max_layers_;
608 } else if (nbr_of_layers_ > max_layers_) {
609 ldmx_log(warn) << "nbr_of_layers_ " << nbr_of_layers_
610 << " exceeds max layers " << max_layers_
611 << ", setting to max layers";
612 nbr_of_layers_ = max_layers_;
613 }
614
615 // first copy *addresses* so we are only ever passing around pointers
616 std::vector<const ldmx::EcalHit*> hits;
617 hits.reserve(unsorted_hits.size());
618 ldmx_log(debug) << "Clustering " << unsorted_hits.size() << " hits";
619 for (const auto& unsorted_hit : unsorted_hits) {
620 hits.push_back(&unsorted_hit);
621 }
622 // sort hits by Z position
623 ldmx_log(debug) << "Sorting hits by Z position";
624 std::sort(hits.begin(), hits.end(),
625 [](const ldmx::EcalHit* a, const ldmx::EcalHit* b) {
626 return a->getZPos() < b->getZPos();
627 });
628
629 seeds_.resize(nbr_of_layers_);
630
631 if (nbr_of_layers_ > 1) {
632 ldmx_log(debug) << "Creating layers";
633 // returns a vector of layers, with each layer having a vector of hits
634 const auto layers = createLayers(hits);
635 ldmx_log(debug) << "Doing layer-wise clustering on " << layers.size()
636 << " layers";
637 for (int i = 0; i < layers.size(); i++) {
638 ldmx_log(trace) << "--- LAYER " << i + 1 << " ---";
639 auto densities = setup(layers[i]);
640 auto clusters = clustering(densities, false, i);
641 convertToIntermediateClusters(clusters);
642 // clustering without 3D
643 }
644 // Below for CLUE3D, comment for just layer clustering
645 // This does not work properly yet
646 // auto densities = setupForClue3D();
647 // auto clusters = clustering(densities, true);
648 // convertToIntermediateClusters(clusters);
649 } else {
650 ldmx_log(debug) << "Only one layer, doing 2D clustering";
651 auto densities = setup(hits);
652 auto clusters = clustering(densities, false);
653 convertToIntermediateClusters(clusters);
654 }
655}
656
657} // namespace ecal
A version of CLUE (CMS) for clustering in ECal.
recon::WorkingCluster< ldmx::EcalHit > IntermediateCluster
Type alias for WorkingCluster specialized for EcalHit.
T dist(T x1, T y1, T x2, T y2)
Euclidean distance between two points.
Definition CLUE.cxx:15
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
double centroidY() const
Get the centroid Y position (energy-weighted).
double centroidX() const
Get the centroid X position (energy-weighted).
void add(const HitType *hit)
Add a hit to the cluster using its stored position.