11/*
22TICL plugin for electron superclustering in HGCAL using a DNN.
3- DNN designed by Alessandro Tarabini.
3+ DNN designed by Alessandro Tarabini, Florian Beaudette, Gamze Sokmen, Shamik Ghosh, Theo Cuisset .
44
55Inputs are CLUE3D EM tracksters. Outputs are superclusters (as vectors of IDs of trackster)
66"Seed trackster" : seed of supercluster, always highest pT trackster of supercluster, normally should be an electron
@@ -18,6 +18,9 @@ The loop is first on candidate, then on seeds as it is more efficient for step 4
1818
1919Authors : Theo Cuisset <[email protected] >, Shamik Ghosh <[email protected] > 2020Date : 11/2023
21+
22+ Updates : Logic works as it should and switching to v3 (Shamik)
23+ Date: 07/2025
2124*/
2225
2326#include < string>
@@ -146,7 +149,8 @@ void TracksterLinkingbySuperClusteringDNN::linkTracksters(
146149 Trackster const & ts_cand = inputTracksters[trackstersIndicesPt[ts_cand_idx_pt]];
147150
148151 if (ts_cand.raw_energy () < candidateEnergyThreshold_ ||
149- !checkExplainedVarianceRatioCut (ts_cand)) // || !trackstersPassesPIDCut(ts_cand)
152+ // !checkExplainedVarianceRatioCut(ts_cand)) // || !trackstersPassesPIDCut(ts_cand)
153+ !checkExplainedVarianceRatioCut (ts_cand)) // || !trackstersPassesPIDCut(ts_cand))
150154 continue ;
151155
152156 auto & tracksterTiles = tracksterTilesBothEndcaps_pt[ts_cand.barycenter ().eta () > 0 ];
@@ -245,37 +249,37 @@ void TracksterLinkingbySuperClusteringDNN::linkTracksters(
245249 Also mask seeds (only needed to add tracksters not in a supercluster to the output). */
246250 std::vector<bool > tracksterMask (tracksterCount, false );
247251
248- /* Index of the seed trackster of the previous iteration
249- Initialized with an id that cannot be obtained in input */
252+ // ///////////////////////////////////////////////////////////////////////TRKBUILDINGMOD
253+
250254 unsigned int previousCandTrackster_idx = std::numeric_limits<unsigned int >::max ();
251255 unsigned int bestSeedForCurrentCandidate_idx = std::numeric_limits<unsigned int >::max ();
252256 float bestSeedForCurrentCandidate_dnnScore = nnWorkingPoint_;
253257
254- // Lambda to be called when there is a transition from one candidate to the next (as well as after the last iteration)
255- // Does the actual supercluster creation
258+ // Track which tracksters were ever used as candidates
259+ std::vector<bool > usedAsCandidate (tracksterCount, false );
260+
256261 auto onCandidateTransition = [&](unsigned ts_cand_idx) {
257- if (bestSeedForCurrentCandidate_idx <
258- std::numeric_limits< unsigned int >:: max ()) { // At least one seed can be superclustered with the candidate
259- tracksterMask [ts_cand_idx] = true ; // Mask the candidate so it is not considered as seed in later iterations
260-
261- // Look for a supercluster of the seed
262- std::vector<std::vector< unsigned int >>::iterator seed_supercluster_it =
263- std::find_if (outputSuperclusters.begin (),
264- outputSuperclusters.end (),
265- [bestSeedForCurrentCandidate_idx](std::vector<unsigned int > const & sc) {
266- return sc[0 ] == bestSeedForCurrentCandidate_idx;
267- });
268-
269- if (seed_supercluster_it == outputSuperclusters. end ()) { // No supercluster exists yet for the seed. Create one.
262+ if (bestSeedForCurrentCandidate_idx < std::numeric_limits< unsigned int >:: max ()) {
263+ tracksterMask[ts_cand_idx] = true ; // Mask the candidate so it’s not reused as a seed
264+ usedAsCandidate [ts_cand_idx] = true ;
265+
266+ // Find the supercluster the seed belongs to (even if it's already used in another supercluster)
267+ // Find existing supercluster for the seed
268+ auto seed_supercluster_it = std::find_if (outputSuperclusters.begin (),
269+ outputSuperclusters.end (),
270+ [bestSeedForCurrentCandidate_idx](const std::vector<unsigned int >& sc) {
271+ return sc[0 ] == bestSeedForCurrentCandidate_idx;
272+ });
273+ if (seed_supercluster_it == outputSuperclusters. end ()) {
274+ // No supercluster exists for this seed, create one
270275 outputSuperclusters.emplace_back (std::initializer_list<unsigned int >{bestSeedForCurrentCandidate_idx});
271276 resultTracksters.emplace_back (inputTracksters[bestSeedForCurrentCandidate_idx]);
272277 linkedTracksterIdToInputTracksterId.emplace_back (
273278 std::initializer_list<unsigned int >{bestSeedForCurrentCandidate_idx});
274279 seed_supercluster_it = outputSuperclusters.end () - 1 ;
275- tracksterMask[bestSeedForCurrentCandidate_idx] =
276- true ; // mask the seed as well (needed to find tracksters not in any supercluster)
280+ tracksterMask[bestSeedForCurrentCandidate_idx] = true ;
277281 }
278- // Index of the supercluster into resultTracksters, outputSuperclusters and linkedTracksterIdToInputTracksterId collections (the indices are the same)
282+
279283 unsigned int indexIntoOutputTracksters = seed_supercluster_it - outputSuperclusters.begin ();
280284 seed_supercluster_it->push_back (ts_cand_idx);
281285 resultTracksters[indexIntoOutputTracksters].mergeTracksters (inputTracksters[ts_cand_idx]);
@@ -290,10 +294,10 @@ void TracksterLinkingbySuperClusteringDNN::linkTracksters(
290294 }
291295 };
292296
293- // Iterate over minibatches
297+ // Iterate over minibatches
294298 for (unsigned int batchIndex = 0 ; batchIndex < batchOutputs.size (); batchIndex++) {
295- std::vector<float > const & currentBatchOutputs = batchOutputs[batchIndex]; // DNN score outputs
296- // Iterate over seed-candidate pairs inside current minibatch
299+ std::vector<float > const & currentBatchOutputs = batchOutputs[batchIndex];
300+
297301 for (unsigned int indexInBatch = 0 ; indexInBatch < tracksterIndicesUsedInDNN[batchIndex].size (); indexInBatch++) {
298302 assert (indexInBatch < static_cast <unsigned int >(batchOutputs[batchIndex].size ()));
299303
@@ -303,21 +307,21 @@ void TracksterLinkingbySuperClusteringDNN::linkTracksters(
303307
304308 if (previousCandTrackster_idx != std::numeric_limits<unsigned int >::max () &&
305309 ts_cand_idx != previousCandTrackster_idx) {
306- // There is a transition from one seed to the next (don't make a transition for the first iteration)
307310 onCandidateTransition (previousCandTrackster_idx);
308311 }
309312
310- if (currentDnnScore > bestSeedForCurrentCandidate_dnnScore && !tracksterMask[ts_seed_idx]) {
311- // Check that the DNN suggests superclustering, that this seed-candidate assoc is better than previous ones, and that the seed is not already in a supercluster as candidate
313+ // Ignore seed if it was previously used as a candidate
314+ if (currentDnnScore > bestSeedForCurrentCandidate_dnnScore && !usedAsCandidate[ts_seed_idx]) {
312315 bestSeedForCurrentCandidate_idx = ts_seed_idx;
313316 bestSeedForCurrentCandidate_dnnScore = currentDnnScore;
314317 }
318+
315319 previousCandTrackster_idx = ts_cand_idx;
316320 }
317321 }
318322 onCandidateTransition (previousCandTrackster_idx);
319323
320- // Adding one-trackster superclusters for all tracksters not in a supercluster already that pass the seed threshold
324+ // Create singleton superclusters for unused tracksters with enough pt
321325 for (unsigned int ts_id = 0 ; ts_id < tracksterCount; ts_id++) {
322326 if (!tracksterMask[ts_id] && inputTracksters[ts_id].raw_pt () >= seedPtThreshold_) {
323327 outputSuperclusters.emplace_back (std::initializer_list<unsigned int >{ts_id});
@@ -326,6 +330,8 @@ void TracksterLinkingbySuperClusteringDNN::linkTracksters(
326330 }
327331 }
328332
333+ // ///////////////////////////////////////////////////////////////////////TRKBUILDINGMOD
334+
329335#ifdef EDM_ML_DEBUG
330336 for (std::vector<unsigned int > const & sc : outputSuperclusters) {
331337 std::ostringstream s;
@@ -340,8 +346,8 @@ void TracksterLinkingbySuperClusteringDNN::linkTracksters(
340346void TracksterLinkingbySuperClusteringDNN::fillPSetDescription (edm::ParameterSetDescription& desc) {
341347 TracksterLinkingAlgoBase::fillPSetDescription (desc); // adds algo_verbosity
342348 desc.add <edm::FileInPath>(" onnxModelPath" )->setComment (" Path to DNN (as ONNX model)" );
343- desc.ifValue (edm::ParameterDescription<std::string>(" dnnInputsVersion" , " v2 " , true ),
344- edm::allowedValues<std::string>(" v1" , " v2" ))
349+ desc.ifValue (edm::ParameterDescription<std::string>(" dnnInputsVersion" , " v3 " , true ),
350+ edm::allowedValues<std::string>(" v1" , " v2" , " v3 " ))
345351 ->setComment (
346352 " DNN inputs version tag. Defines which set of features is fed to the DNN. Must match with the actual DNN." );
347353 desc.add <unsigned int >(" inferenceBatchSize" , 1e5 )
@@ -379,4 +385,4 @@ void TracksterLinkingbySuperClusteringDNN::fillPSetDescription(edm::ParameterSet
379385 {static_cast <int >(Trackster::ParticleType::photon), static_cast <int >(Trackster::ParticleType::electron)})
380386 ->setComment (" List of PID particle types (ticl::Trackster::ParticleType enum) to consider for PID filtering" );
381387 desc.add <double >(" PIDThreshold" , 0.8 )->setComment (" PID score threshold" );
382- }
388+ }
0 commit comments