-
Notifications
You must be signed in to change notification settings - Fork 4.3k
/
CSCAnodeLCTProcessor.cc
1435 lines (1277 loc) · 59.7 KB
/
CSCAnodeLCTProcessor.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#include "L1Trigger/CSCTriggerPrimitives/interface/CSCAnodeLCTProcessor.h"
#include <set>
// Default values of configuration parameters.
const unsigned int CSCAnodeLCTProcessor::def_fifo_tbins = 16;
const unsigned int CSCAnodeLCTProcessor::def_fifo_pretrig = 10;
const unsigned int CSCAnodeLCTProcessor::def_drift_delay = 2;
const unsigned int CSCAnodeLCTProcessor::def_nplanes_hit_pretrig = 2;
const unsigned int CSCAnodeLCTProcessor::def_nplanes_hit_pattern = 4;
const unsigned int CSCAnodeLCTProcessor::def_nplanes_hit_accel_pretrig = 2;
const unsigned int CSCAnodeLCTProcessor::def_nplanes_hit_accel_pattern = 4;
const unsigned int CSCAnodeLCTProcessor::def_trig_mode = 2; // 3?
const unsigned int CSCAnodeLCTProcessor::def_accel_mode = 0; // 1?
const unsigned int CSCAnodeLCTProcessor::def_l1a_window_width = 7; // 5?
//----------------
// Constructors --
//----------------
CSCAnodeLCTProcessor::CSCAnodeLCTProcessor(unsigned endcap,
unsigned station,
unsigned sector,
unsigned subsector,
unsigned chamber,
CSCBaseboard::Parameters& conf)
: CSCBaseboard(endcap, station, sector, subsector, chamber, conf) {
static std::atomic<bool> config_dumped{false};
// ALCT configuration parameters.
fifo_tbins = conf.alctParams().getParameter<unsigned int>("alctFifoTbins");
fifo_pretrig = conf.alctParams().getParameter<unsigned int>("alctFifoPretrig");
drift_delay = conf.alctParams().getParameter<unsigned int>("alctDriftDelay");
nplanes_hit_pretrig = conf.alctParams().getParameter<unsigned int>("alctNplanesHitPretrig");
nplanes_hit_pattern = conf.alctParams().getParameter<unsigned int>("alctNplanesHitPattern");
nplanes_hit_accel_pretrig = conf.alctParams().getParameter<unsigned int>("alctNplanesHitAccelPretrig");
nplanes_hit_accel_pattern = conf.alctParams().getParameter<unsigned int>("alctNplanesHitAccelPattern");
trig_mode = conf.alctParams().getParameter<unsigned int>("alctTrigMode");
accel_mode = conf.alctParams().getParameter<unsigned int>("alctAccelMode");
l1a_window_width = conf.alctParams().getParameter<unsigned int>("alctL1aWindowWidth");
hit_persist = conf.alctParams().getParameter<unsigned int>("alctHitPersist");
// Verbosity level, set to 0 (no print) by default.
infoV = conf.alctParams().getParameter<int>("verbosity");
// separate handle for early time bins
early_tbins = conf.alctParams().getParameter<int>("alctEarlyTbins");
if (early_tbins < 0)
early_tbins = fifo_pretrig - CSCConstants::ALCT_EMUL_TIME_OFFSET;
// delta BX time depth for ghostCancellationLogic
ghost_cancellation_bx_depth = conf.alctParams().getParameter<int>("alctGhostCancellationBxDepth");
// whether to consider ALCT candidates' qualities while doing ghostCancellationLogic on +-1 wire groups
ghost_cancellation_side_quality = conf.alctParams().getParameter<bool>("alctGhostCancellationSideQuality");
// deadtime clocks after pretrigger (extra in addition to drift_delay)
pretrig_extra_deadtime = conf.alctParams().getParameter<unsigned int>("alctPretrigDeadtime");
// whether to use narrow pattern mask for the rings close to the beam
narrow_mask_r1 = conf.alctParams().getParameter<bool>("alctNarrowMaskForR1");
// Check and print configuration parameters.
checkConfigParameters();
if ((infoV > 0 || (runPhase2_)) && !config_dumped) {
dumpConfigParams();
config_dumped = true;
}
numWireGroups = 0; // Will be set later.
MESelection = (theStation < 3) ? 0 : 1;
// whether to calculate bx as corrected_bx instead of pretrigger one
use_corrected_bx = false;
if (runPhase2_) {
use_corrected_bx = conf.alctParams().getParameter<bool>("alctUseCorrectedBx");
}
// Load appropriate pattern mask.
loadPatternMask();
// quality control of stubs
qualityControl_ = std::make_unique<LCTQualityControl>(endcap, station, sector, subsector, chamber, conf);
const auto& shower = conf.showerParams().getParameterSet("anodeShower");
thresholds_ = shower.getParameter<std::vector<unsigned>>("showerThresholds");
showerNumTBins_ = shower.getParameter<unsigned>("showerNumTBins");
minLayersCentralTBin_ = shower.getParameter<unsigned>("minLayersCentralTBin");
minbx_readout_ = CSCConstants::LCT_CENTRAL_BX - l1a_window_width / 2;
maxbx_readout_ = CSCConstants::LCT_CENTRAL_BX + l1a_window_width / 2;
assert(l1a_window_width / 2 <= CSCConstants::LCT_CENTRAL_BX);
}
void CSCAnodeLCTProcessor::loadPatternMask() {
// Load appropriate pattern mask.
if (narrow_mask_r1 && (theRing == 1 || theRing == 4)) {
alct_pattern_ = CSCPatternBank::alct_pattern_r1_;
} else {
alct_pattern_ = CSCPatternBank::alct_pattern_legacy_;
}
}
void CSCAnodeLCTProcessor::setDefaultConfigParameters() {
// Set default values for configuration parameters.
fifo_tbins = def_fifo_tbins;
fifo_pretrig = def_fifo_pretrig;
drift_delay = def_drift_delay;
nplanes_hit_pretrig = def_nplanes_hit_pretrig;
nplanes_hit_pattern = def_nplanes_hit_pattern;
nplanes_hit_accel_pretrig = def_nplanes_hit_accel_pretrig;
nplanes_hit_accel_pattern = def_nplanes_hit_accel_pattern;
trig_mode = def_trig_mode;
accel_mode = def_accel_mode;
l1a_window_width = def_l1a_window_width;
minbx_readout_ = CSCConstants::LCT_CENTRAL_BX - l1a_window_width / 2;
maxbx_readout_ = CSCConstants::LCT_CENTRAL_BX + l1a_window_width / 2;
}
// Set configuration parameters obtained via EventSetup mechanism.
void CSCAnodeLCTProcessor::setConfigParameters(const CSCDBL1TPParameters* conf) {
static std::atomic<bool> config_dumped{false};
fifo_tbins = conf->alctFifoTbins();
fifo_pretrig = conf->alctFifoPretrig();
drift_delay = conf->alctDriftDelay();
nplanes_hit_pretrig = conf->alctNplanesHitPretrig();
nplanes_hit_pattern = conf->alctNplanesHitPattern();
nplanes_hit_accel_pretrig = conf->alctNplanesHitAccelPretrig();
nplanes_hit_accel_pattern = conf->alctNplanesHitAccelPattern();
trig_mode = conf->alctTrigMode();
accel_mode = conf->alctAccelMode();
l1a_window_width = conf->alctL1aWindowWidth();
// Check and print configuration parameters.
checkConfigParameters();
if (!config_dumped) {
dumpConfigParams();
config_dumped = true;
}
minbx_readout_ = CSCConstants::LCT_CENTRAL_BX - l1a_window_width / 2;
maxbx_readout_ = CSCConstants::LCT_CENTRAL_BX + l1a_window_width / 2;
}
void CSCAnodeLCTProcessor::checkConfigParameters() {
// Make sure that the parameter values are within the allowed range.
// Max expected values.
static const unsigned int max_fifo_tbins = 1 << 5;
static const unsigned int max_fifo_pretrig = 1 << 5;
static const unsigned int max_drift_delay = 1 << 2;
static const unsigned int max_nplanes_hit_pretrig = 1 << 3;
static const unsigned int max_nplanes_hit_pattern = 1 << 3;
static const unsigned int max_nplanes_hit_accel_pretrig = 1 << 3;
static const unsigned int max_nplanes_hit_accel_pattern = 1 << 3;
static const unsigned int max_trig_mode = 1 << 2;
static const unsigned int max_accel_mode = 1 << 2;
static const unsigned int max_l1a_window_width = CSCConstants::MAX_ALCT_TBINS; // 4 bits
// Checks.
CSCBaseboard::checkConfigParameters(fifo_tbins, max_fifo_tbins, def_fifo_tbins, "fifo_tbins");
CSCBaseboard::checkConfigParameters(fifo_pretrig, max_fifo_pretrig, def_fifo_pretrig, "fifo_pretrig");
CSCBaseboard::checkConfigParameters(drift_delay, max_drift_delay, def_drift_delay, "drift_delay");
CSCBaseboard::checkConfigParameters(
nplanes_hit_pretrig, max_nplanes_hit_pretrig, def_nplanes_hit_pretrig, "nplanes_hit_pretrig");
CSCBaseboard::checkConfigParameters(
nplanes_hit_pattern, max_nplanes_hit_pattern, def_nplanes_hit_pattern, "nplanes_hit_pattern");
CSCBaseboard::checkConfigParameters(nplanes_hit_accel_pretrig,
max_nplanes_hit_accel_pretrig,
def_nplanes_hit_accel_pretrig,
"nplanes_hit_accel_pretrig");
CSCBaseboard::checkConfigParameters(nplanes_hit_accel_pattern,
max_nplanes_hit_accel_pattern,
def_nplanes_hit_accel_pattern,
"nplanes_hit_accel_pattern");
CSCBaseboard::checkConfigParameters(trig_mode, max_trig_mode, def_trig_mode, "trig_mode");
CSCBaseboard::checkConfigParameters(accel_mode, max_accel_mode, def_accel_mode, "accel_mode");
CSCBaseboard::checkConfigParameters(l1a_window_width, max_l1a_window_width, def_l1a_window_width, "l1a_window_width");
assert(l1a_window_width / 2 <= CSCConstants::LCT_CENTRAL_BX);
}
void CSCAnodeLCTProcessor::clear() {
for (int bx = 0; bx < CSCConstants::MAX_ALCT_TBINS; bx++) {
bestALCT[bx].clear();
secondALCT[bx].clear();
anode_showers_[bx].clear(); //?
}
lct_list.clear();
}
void CSCAnodeLCTProcessor::clear(const int wire, const int pattern) {
/* Clear the data off of selected pattern */
if (pattern == CSCConstants::ALCT_ACCELERATOR_PATTERN)
quality[wire][CSCConstants::ALCT_ACCELERATOR_PATTERN] = -999;
else {
quality[wire][CSCConstants::ALCT_COLLISIONA_PATTERN] = -999;
quality[wire][CSCConstants::ALCT_COLLISIONB_PATTERN] = -999;
}
}
std::vector<CSCALCTDigi> CSCAnodeLCTProcessor::run(const CSCWireDigiCollection* wiredc, const CSCChamber* cscChamber) {
static std::atomic<bool> config_dumped{false};
if ((infoV > 0 || (runPhase2_)) && !config_dumped) {
dumpConfigParams();
config_dumped = true;
}
// Get the number of wire groups for the given chamber. Do it only once
// per chamber.
if (numWireGroups <= 0 or numWireGroups > CSCConstants::MAX_NUM_WIREGROUPS) {
if (cscChamber) {
numWireGroups = cscChamber->layer(1)->geometry()->numberOfWireGroups();
if (numWireGroups > CSCConstants::MAX_NUM_WIREGROUPS) {
edm::LogError("CSCAnodeLCTProcessor|SetupError")
<< "+++ Number of wire groups, " << numWireGroups << " found in " << theCSCName_ << " (sector " << theSector
<< " subsector " << theSubsector << " trig id. " << theTrigChamber << ")"
<< " exceeds max expected, " << CSCConstants::MAX_NUM_WIREGROUPS << " +++\n"
<< "+++ CSC geometry looks garbled; no emulation possible +++\n";
numWireGroups = -1;
}
} else {
edm::LogError("CSCAnodeLCTProcessor|SetupError")
<< "+++ " << theCSCName_ << " (sector " << theSector << " subsector " << theSubsector << " trig id. "
<< theTrigChamber << ")"
<< " is not defined in current geometry! +++\n"
<< "+++ CSC geometry looks garbled; no emulation possible +++\n";
numWireGroups = -1;
}
}
if (numWireGroups <= 0 or (unsigned) numWireGroups > qualityControl_->get_csc_max_wiregroup(theStation, theRing)) {
edm::LogError("CSCAnodeLCTProcessor|SetupError")
<< "+++ " << theCSCName_ << " (sector " << theSector << " subsector " << theSubsector << " trig id. "
<< theTrigChamber << "):"
<< " numWireGroups = " << numWireGroups << "; ALCT emulation skipped! +++";
std::vector<CSCALCTDigi> emptyV;
return emptyV;
}
// Get wire digis in this chamber from wire digi collection.
bool hasDigis = getDigis(wiredc);
if (hasDigis) {
// First get wiregroup times from the wire digis.
std::vector<int> wireGroupTimes[CSCConstants::NUM_LAYERS][CSCConstants::MAX_NUM_WIREGROUPS];
readWireDigis(wireGroupTimes);
// Pass an array of wire times on to another run() doing the LCT search.
// If the number of layers containing digis is smaller than that
// required to trigger, quit right away.
const unsigned int min_layers =
(nplanes_hit_accel_pattern == 0)
? nplanes_hit_pattern
: ((nplanes_hit_pattern <= nplanes_hit_accel_pattern) ? nplanes_hit_pattern : nplanes_hit_accel_pattern);
unsigned int layersHit = 0;
for (int i_layer = 0; i_layer < CSCConstants::NUM_LAYERS; i_layer++) {
for (int i_wire = 0; i_wire < numWireGroups; i_wire++) {
if (!wireGroupTimes[i_layer][i_wire].empty()) {
layersHit++;
break;
}
}
}
if (layersHit >= min_layers)
run(wireGroupTimes);
// Get the high multiplicity bits in this chamber
encodeHighMultiplicityBits();
}
// Return vector of all found ALCTs.
return getALCTs();
}
void CSCAnodeLCTProcessor::run(const std::vector<int> wire[CSCConstants::NUM_LAYERS][CSCConstants::MAX_NUM_WIREGROUPS]) {
bool trigger = false;
// initialize the pulse array.
pulse_.initialize(numWireGroups);
// Check if there are any in-time hits and do the pulse extension.
bool chamber_empty = pulseExtension(wire);
// define a new pattern map
// for each key wiregroup, and for each pattern, store the 2D collection of fired wiregroup digis
std::map<int, std::map<int, CSCALCTDigi::WireContainer>> hits_in_patterns;
hits_in_patterns.clear();
// Only do the rest of the processing if chamber is not empty.
// Stop drift_delay bx's short of fifo_tbins since at later bx's we will
// not have a full set of hits to start pattern search anyway.
unsigned int stop_bx = fifo_tbins - drift_delay;
if (!chamber_empty) {
for (int i_wire = 0; i_wire < numWireGroups; i_wire++) {
// extra check to make sure only valid wires are processed
const unsigned max_wire = qualityControl_->get_csc_max_wiregroup(theStation, theRing);
if (unsigned(i_wire) >= max_wire)
continue;
unsigned int start_bx = 0;
// Allow for more than one pass over the hits in the time window.
while (start_bx < stop_bx) {
if (preTrigger(i_wire, start_bx)) {
if (infoV > 2)
showPatterns(i_wire);
if (patternDetection(i_wire, hits_in_patterns)) {
trigger = true;
int ghost_cleared[2] = {0, 0};
/*
In older versions of the ALCT emulation, the ghost cancellation was performed after
the ALCTs were found. In December 2018, it became clear that during the study of data
and emulation comparison on 2018 data, a small disagreement between data and emulation
was found. The changes we implemented then allow re-triggering on one wiregroup after
some dead time once an earlier ALCT was constructed built on this wiregroup. Before this
commit the ALCT processor would prohibit the wiregroup from triggering in one event after
an ALCT was found on that wiregroup. In the firwmare, the wiregroup with ALCT is only dead
for a few BX before it can be triggered by next muon. The implementation of ghost cancellation
logic was changed to accommodate the re-triggering change while the idea of ghost cancellation
logic is kept the same.
*/
ghostCancellationLogicOneWire(i_wire, ghost_cleared);
int bx = (use_corrected_bx) ? first_bx_corrected[i_wire] : first_bx[i_wire];
if (bx >= CSCConstants::MAX_ALCT_TBINS)
edm::LogError("CSCAnodeLCTProcessor")
<< " bx of valid trigger : " << bx << " > max allowed value " << CSCConstants::MAX_ALCT_TBINS;
//acceleration mode
if (quality[i_wire][0] > 0 and bx < CSCConstants::MAX_ALCT_TBINS) {
int valid = (ghost_cleared[0] == 0) ? 1 : 0; //cancelled, valid=0, otherwise it is 1
CSCALCTDigi newALCT(valid, quality[i_wire][0], 1, 0, i_wire, bx);
// set the wire digis for this pattern
setWireContainer(newALCT, hits_in_patterns[i_wire][0]);
lct_list.emplace_back(newALCT);
if (infoV > 1)
LogTrace("CSCAnodeLCTProcessor") << "Add one ALCT to list " << lct_list.back();
}
//collision mode
if (quality[i_wire][CSCConstants::ALCT_COLLISIONA_PATTERN] > 0 and bx < CSCConstants::MAX_ALCT_TBINS) {
int valid = (ghost_cleared[CSCConstants::ALCT_COLLISIONA_PATTERN] == 0)
? 1
: 0; //cancelled, valid=0, otherwise it is 1
CSCALCTDigi newALCT(valid,
quality[i_wire][CSCConstants::ALCT_COLLISIONA_PATTERN],
0,
quality[i_wire][CSCConstants::ALCT_COLLISIONB_PATTERN],
i_wire,
bx);
// set the wire digis for this pattern
setWireContainer(newALCT, hits_in_patterns[i_wire][CSCConstants::ALCT_COLLISIONA_PATTERN]);
lct_list.emplace_back(newALCT);
if (infoV > 1)
LogTrace("CSCAnodeLCTProcessor") << "Add one ALCT to list " << lct_list.back();
}
//break;
// Assume that the earliest time when another pre-trigger can
// occur in case pattern detection failed is bx_pretrigger+4:
// this seems to match the data.
start_bx = first_bx[i_wire] + drift_delay + pretrig_extra_deadtime;
} else {
//only pretrigger, no trigger ==> no dead time, continue to find next pretrigger
start_bx = first_bx[i_wire] + 1;
}
} else { //no pretrigger, skip this wiregroup
break;
}
} // end of while
}
}
// Do the rest only if there is at least one trigger candidate.
if (trigger) {
/* In Run-1 and Run-2, the ghost cancellation was done after the trigger.
In the firmware however, the ghost cancellation is done during the trigger
on each wiregroup in parallel. For Run-3 and beyond, the ghost cancellation is
implemented per wiregroup earlier in the code. See function
"ghostCancellationLogicOneWire". There used to be a function ghostCancellationLogic
call here.
*/
lctSearch();
}
}
bool CSCAnodeLCTProcessor::getDigis(const CSCWireDigiCollection* wiredc) {
// Routine for getting digis and filling digiV vector.
bool hasDigis = false;
// Loop over layers and save wire digis on each one into digiV[layer].
for (int i_layer = 0; i_layer < CSCConstants::NUM_LAYERS; i_layer++) {
digiV[i_layer].clear();
CSCDetId detid(theEndcap, theStation, theRing, theChamber, i_layer + 1);
getDigis(wiredc, detid);
// If this is ME1/1, fetch digis in corresponding ME1/A (ring=4) as well.
if (isME11_ && !disableME1a_) {
CSCDetId detid_me1a(theEndcap, theStation, 4, theChamber, i_layer + 1);
getDigis(wiredc, detid_me1a);
}
if (!digiV[i_layer].empty()) {
hasDigis = true;
if (infoV > 1) {
LogTrace("CSCAnodeLCTProcessor") << "found " << digiV[i_layer].size() << " wire digi(s) in layer " << i_layer
<< " of " << theCSCName_ << " (trig. sector " << theSector << " subsector "
<< theSubsector << " id " << theTrigChamber << ")";
for (const auto& wd : digiV[i_layer]) {
LogTrace("CSCAnodeLCTProcessor") << " " << wd;
}
}
}
}
return hasDigis;
}
void CSCAnodeLCTProcessor::getDigis(const CSCWireDigiCollection* wiredc, const CSCDetId& id) {
CSCWireDigiCollection::Range rwired = wiredc->get(id);
for (CSCWireDigiCollection::const_iterator digiIt = rwired.first; digiIt != rwired.second; ++digiIt) {
digiV[id.layer() - 1].push_back(*digiIt);
}
}
void CSCAnodeLCTProcessor::readWireDigis(
std::vector<int> wire[CSCConstants::NUM_LAYERS][CSCConstants::MAX_NUM_WIREGROUPS]) {
// Loop over all 6 layers.
for (int i_layer = 0; i_layer < CSCConstants::NUM_LAYERS; i_layer++) {
// Loop over all digis in the layer and find the wireGroup and bx
// time for each.
for (const auto& wd : digiV[i_layer]) {
int i_wire = wd.getWireGroup() - 1;
std::vector<int> bx_times = wd.getTimeBinsOn();
// Check that the wires and times are appropriate.
if (i_wire < 0 || i_wire >= numWireGroups) {
if (infoV >= 0)
edm::LogWarning("CSCAnodeLCTProcessor|WrongInput")
<< "+++ Found wire digi with wrong wire number = " << i_wire << " (max wires = " << numWireGroups
<< "); skipping it... +++\n";
continue;
}
// Accept digis in expected time window. Total number of time
// bins in DAQ readout is given by fifo_tbins, which thus
// determines the maximum length of time interval. Anode raw
// hits in DAQ readout start (fifo_pretrig - 6) clocks before
// L1Accept. If times earlier than L1Accept were recorded, we
// use them since they can modify the ALCTs found later, via
// ghost-cancellation logic.
int last_time = -999;
if (bx_times.size() == fifo_tbins) {
wire[i_layer][i_wire].push_back(0);
wire[i_layer][i_wire].push_back(6);
} else {
for (unsigned int i = 0; i < bx_times.size(); i++) {
// Find rising edge change
if (i > 0 && bx_times[i] == (bx_times[i - 1] + 1))
continue;
if (bx_times[i] < static_cast<int>(fifo_tbins)) {
if (infoV > 2)
LogTrace("CSCAnodeLCTProcessor")
<< "Digi on layer " << i_layer << " wire " << i_wire << " at time " << bx_times[i];
// Finally save times of hit wires. One shot module will
// not restart if a new pulse comes before the expiration
// of the 6-bx period.
if (last_time < 0 || ((bx_times[i] - last_time) >= 6)) {
wire[i_layer][i_wire].push_back(bx_times[i]);
last_time = bx_times[i];
}
} else {
if (infoV > 1)
LogTrace("CSCAnodeLCTProcessor") << "+++ Skipping wire digi: wire = " << i_wire << " layer = " << i_layer
<< ", bx = " << bx_times[i] << " +++";
}
}
}
}
}
}
bool CSCAnodeLCTProcessor::pulseExtension(
const std::vector<int> wire[CSCConstants::NUM_LAYERS][CSCConstants::MAX_NUM_WIREGROUPS]) {
bool chamber_empty = true;
int i_wire, i_layer, digi_num;
const unsigned bits_in_pulse = pulse_.bitsInPulse();
// Clear pulse array. This array will be used as a bit representation of
// hit times. For example: if strip[1][2] has a value of 3, then 1 shifted
// left 3 will be bit pattern of pulse[1][2]. This would make the pattern
// look like 0000000000001000. Then add on additional bits to signify
// the duration of a signal (hit_persist, formerly bx_width) to simulate
// the TMB's drift delay. So for the same pulse[1][2] with a hit_persist
// of 3 would look like 0000000000111000. This is similating the digital
// one-shot in the TMB.
pulse_.clear();
for (i_wire = 0; i_wire < numWireGroups; i_wire++) {
first_bx[i_wire] = -999;
first_bx_corrected[i_wire] = -999;
for (int j = 0; j < 3; j++)
quality[i_wire][j] = -999;
}
for (i_layer = 0; i_layer < CSCConstants::NUM_LAYERS; i_layer++) {
digi_num = 0;
for (i_wire = 0; i_wire < numWireGroups; i_wire++) {
if (!wire[i_layer][i_wire].empty()) {
std::vector<int> bx_times = wire[i_layer][i_wire];
for (unsigned int i = 0; i < bx_times.size(); i++) {
// Check that min and max times are within the allowed range.
if (bx_times[i] < 0 || bx_times[i] + hit_persist >= bits_in_pulse) {
if (infoV > 0)
edm::LogWarning("CSCAnodeLCTProcessor|OutOfTimeDigi")
<< "+++ BX time of wire digi (wire = " << i_wire << " layer = " << i_layer << ") bx = " << bx_times[i]
<< " is not within the range (0-" << bits_in_pulse
<< "] allowed for pulse extension. Skip this digi! +++\n";
continue;
}
// Found at least one in-time digi; set chamber_empty to false
if (chamber_empty)
chamber_empty = false;
// make the pulse
pulse_.extend(i_layer, i_wire, bx_times[i], hit_persist);
// Debug information.
if (infoV > 1) {
LogTrace("CSCAnodeLCTProcessor") << "Wire digi: layer " << i_layer << " digi #" << ++digi_num
<< " wire group " << i_wire << " time " << bx_times[i];
if (infoV > 2) {
std::ostringstream strstrm;
for (int i = 1; i <= 32; i++) {
strstrm << pulse_.oneShotAtBX(i_layer, i_wire, 32 - i);
}
LogTrace("CSCAnodeLCTProcessor") << " Pulse: " << strstrm.str();
}
}
}
}
}
}
if (infoV > 1 && !chamber_empty) {
dumpDigis(wire);
}
return chamber_empty;
}
bool CSCAnodeLCTProcessor::preTrigger(const int key_wire, const int start_bx) {
int nPreTriggers = 0;
unsigned int layers_hit;
bool hit_layer[CSCConstants::NUM_LAYERS];
int this_wire;
// If nplanes_hit_accel_pretrig is 0, the firmware uses the value
// of nplanes_hit_pretrig instead.
const unsigned int nplanes_hit_pretrig_acc =
(nplanes_hit_accel_pretrig != 0) ? nplanes_hit_accel_pretrig : nplanes_hit_pretrig;
const unsigned int pretrig_thresh[CSCConstants::NUM_ALCT_PATTERNS] = {
nplanes_hit_pretrig_acc, nplanes_hit_pretrig, nplanes_hit_pretrig};
// Loop over bx times, accelerator and collision patterns to
// look for pretrigger.
// Stop drift_delay bx's short of fifo_tbins since at later bx's we will
// not have a full set of hits to start pattern search anyway.
unsigned int stop_bx = fifo_tbins - drift_delay;
for (unsigned int bx_time = start_bx; bx_time < stop_bx; bx_time++) {
for (int i_pattern = 0; i_pattern < CSCConstants::NUM_ALCT_PATTERNS; i_pattern++) {
// initialize the hit layers
for (int i_layer = 0; i_layer < CSCConstants::NUM_LAYERS; i_layer++)
hit_layer[i_layer] = false;
layers_hit = 0;
// now run over all layers and wires
for (int i_layer = 0; i_layer < CSCConstants::NUM_LAYERS; i_layer++) {
for (int i_wire = 0; i_wire < CSCConstants::ALCT_PATTERN_WIDTH; i_wire++) {
// check if the hit is valid
if (alct_pattern_[i_pattern][i_layer][i_wire]) {
this_wire = CSCPatternBank::alct_keywire_offset_[MESelection][i_wire] + key_wire;
if ((this_wire >= 0) && (this_wire < numWireGroups)) {
// Perform bit operation to see if pulse is 1 at a certain bx_time.
if (pulse_.isOneShotHighAtBX(i_layer, this_wire, bx_time)) {
// Store number of layers hit.
if (!hit_layer[i_layer]) {
hit_layer[i_layer] = true;
layers_hit++;
}
// See if number of layers hit is greater than or equal to
// pretrig_thresh.
if (layers_hit >= pretrig_thresh[i_pattern]) {
first_bx[key_wire] = bx_time;
if (infoV > 1) {
LogTrace("CSCAnodeLCTProcessor") << "Pretrigger was satisfied for wire: " << key_wire
<< " pattern: " << i_pattern << " bx_time: " << bx_time;
}
// make a new pre-trigger
nPreTriggers++;
// make a new pre-trigger digi
// useful for calculating DAQ rates
thePreTriggerDigis.emplace_back(
CSCALCTPreTriggerDigi(1, layers_hit - 3, 0, 0, this_wire, bx_time, nPreTriggers));
return true;
}
}
}
}
}
}
}
}
// If the pretrigger was never satisfied, then return false.
return false;
}
bool CSCAnodeLCTProcessor::patternDetection(
const int key_wire, std::map<int, std::map<int, CSCALCTDigi::WireContainer>>& hits_in_patterns) {
bool trigger = false;
bool hit_layer[CSCConstants::NUM_LAYERS];
unsigned int temp_quality;
int this_wire, delta_wire;
// If nplanes_hit_accel_pattern is 0, the firmware uses the value
// of nplanes_hit_pattern instead.
const unsigned int nplanes_hit_pattern_acc =
(nplanes_hit_accel_pattern != 0) ? nplanes_hit_accel_pattern : nplanes_hit_pattern;
const unsigned int pattern_thresh[CSCConstants::NUM_ALCT_PATTERNS] = {
nplanes_hit_pattern_acc, nplanes_hit_pattern, nplanes_hit_pattern};
const std::string ptn_label[] = {"Accelerator", "CollisionA", "CollisionB"};
for (int i_pattern = 0; i_pattern < CSCConstants::NUM_ALCT_PATTERNS; i_pattern++) {
temp_quality = 0;
// initialize the hit layers
for (int i_layer = 0; i_layer < CSCConstants::NUM_LAYERS; i_layer++)
hit_layer[i_layer] = false;
// clear a single pattern!
CSCALCTDigi::WireContainer hits_single_pattern;
hits_single_pattern.clear();
hits_single_pattern.resize(CSCConstants::NUM_LAYERS);
for (auto& p : hits_single_pattern) {
p.resize(CSCConstants::ALCT_PATTERN_WIDTH, CSCConstants::INVALID_WIREGROUP);
}
double num_pattern_hits = 0., times_sum = 0.;
std::multiset<int> mset_for_median;
mset_for_median.clear();
for (int i_layer = 0; i_layer < CSCConstants::NUM_LAYERS; i_layer++) {
for (int i_wire = 0; i_wire < CSCConstants::ALCT_PATTERN_WIDTH; i_wire++) {
// check if the hit is valid
if (alct_pattern_[i_pattern][i_layer][i_wire]) {
delta_wire = CSCPatternBank::alct_keywire_offset_[MESelection][i_wire];
this_wire = delta_wire + key_wire;
if ((this_wire >= 0) && (this_wire < numWireGroups)) {
// Wait a drift_delay time later and look for layers hit in
// the pattern.
if (pulse_.isOneShotHighAtBX(i_layer, this_wire, first_bx[key_wire] + drift_delay)) {
// store hits in the temporary pattern vector
hits_single_pattern[i_layer][i_wire] = this_wire;
// If layer has never had a hit before, then increment number
// of layer hits.
if (!hit_layer[i_layer]) {
temp_quality++;
// keep track of which layers already had hits.
hit_layer[i_layer] = true;
if (infoV > 1)
LogTrace("CSCAnodeLCTProcessor")
<< "bx_time: " << first_bx[key_wire] << " pattern: " << i_pattern << " keywire: " << key_wire
<< " layer: " << i_layer << " quality: " << temp_quality;
}
// for averaged time use only the closest WGs around the key WG
if (abs(delta_wire) < 2) {
// find at what bx did pulse on this wire&layer start
// use hit_pesrist constraint on how far back we can go
int first_bx_layer = first_bx[key_wire] + drift_delay;
for (unsigned int dbx = 0; dbx < hit_persist; dbx++) {
if (pulse_.isOneShotHighAtBX(i_layer, this_wire, first_bx_layer - 1)) {
first_bx_layer--;
} else
break;
}
times_sum += (double)first_bx_layer;
num_pattern_hits += 1.;
mset_for_median.insert(first_bx_layer);
if (infoV > 2)
LogTrace("CSCAnodeLCTProcessor") << " 1st bx in layer: " << first_bx_layer << " sum bx: " << times_sum
<< " #pat. hits: " << num_pattern_hits;
}
}
}
}
}
}
// calculate median
const int sz = mset_for_median.size();
if (sz > 0) {
std::multiset<int>::iterator im = mset_for_median.begin();
if (sz > 1)
std::advance(im, sz / 2 - 1);
if (sz == 1)
first_bx_corrected[key_wire] = *im;
else if ((sz % 2) == 1)
first_bx_corrected[key_wire] = *(++im);
else
first_bx_corrected[key_wire] = ((*im) + (*(++im))) / 2;
#if defined(EDM_ML_DEBUG)
if (infoV > 1) {
auto lt = LogTrace("CSCAnodeLCTProcessor")
<< "bx=" << first_bx[key_wire] << " bx_cor=" << first_bx_corrected[key_wire] << " bxset=";
for (im = mset_for_median.begin(); im != mset_for_median.end(); im++) {
lt << " " << *im;
}
}
#endif
}
// save the pattern information when a trigger was formed!
if (temp_quality >= pattern_thresh[i_pattern]) {
trigger = true;
hits_in_patterns[key_wire][i_pattern] = hits_single_pattern;
// Quality definition changed on 22 June 2007: it no longer depends
// on pattern_thresh.
temp_quality = getTempALCTQuality(temp_quality);
if (i_pattern == CSCConstants::ALCT_ACCELERATOR_PATTERN) {
// Accelerator pattern
quality[key_wire][CSCConstants::ALCT_ACCELERATOR_PATTERN] = temp_quality;
} else {
// Only one collision pattern (of the best quality) is reported
if (static_cast<int>(temp_quality) > quality[key_wire][CSCConstants::ALCT_COLLISIONA_PATTERN]) {
quality[key_wire][CSCConstants::ALCT_COLLISIONA_PATTERN] = temp_quality; //real quality
quality[key_wire][CSCConstants::ALCT_COLLISIONB_PATTERN] = i_pattern - 1; // pattern, left or right
}
}
if (infoV > 1) {
LogTrace("CSCAnodeLCTProcessor") << "Pattern found; keywire: " << key_wire << " type: " << ptn_label[i_pattern]
<< " quality: " << temp_quality << "\n";
}
}
}
if (infoV > 1 && quality[key_wire][CSCConstants::ALCT_COLLISIONA_PATTERN] > 0) {
if (quality[key_wire][CSCConstants::ALCT_COLLISIONB_PATTERN] == 0)
LogTrace("CSCAnodeLCTProcessor") << "Collision Pattern A is chosen"
<< "\n";
else if (quality[key_wire][CSCConstants::ALCT_COLLISIONB_PATTERN] == 1)
LogTrace("CSCAnodeLCTProcessor") << "Collision Pattern B is chosen"
<< "\n";
}
trigMode(key_wire);
return trigger;
}
void CSCAnodeLCTProcessor::ghostCancellationLogicOneWire(const int key_wire, int* ghost_cleared) {
for (int i_pattern = 0; i_pattern < 2; i_pattern++) {
ghost_cleared[i_pattern] = 0;
if (key_wire == 0)
continue; //ignore
// Non-empty wire group.
int qual_this = quality[key_wire][i_pattern];
if (qual_this > 0) {
// Previous wire.
//int qual_prev = (key_wire > 0) ? quality[key_wire-1][i_pattern] : 0;
//previous ALCTs were pushed to lct_list, stop use the array quality[key_wire-1][i_pattern]
for (auto& p : lct_list) {
//ignore whether ALCT is valid or not in ghost cancellation
//if wiregroup 10, 11, 12 all have trigger and same quality, only wiregroup 10 can keep the trigger
//this met with firmware
if (not(p.getKeyWG() == key_wire - 1 and 1 - p.getAccelerator() == i_pattern))
continue;
bool ghost_cleared_prev = false;
int qual_prev = p.getQuality();
int first_bx_prev = p.getBX();
if (infoV > 1)
LogTrace("CSCAnodeLCTProcessor")
<< "ghost concellation logic "
<< ((i_pattern == CSCConstants::ALCT_ACCELERATOR_PATTERN) ? "Accelerator" : "Collision") << " key_wire "
<< key_wire << " quality " << qual_this << " bx " << first_bx[key_wire] << " previous key_wire "
<< key_wire - 1 << " quality " << qual_prev << " bx " << first_bx[key_wire - 1];
//int dt = first_bx[key_wire] - first_bx[key_wire-1];
int dt = first_bx[key_wire] - first_bx_prev;
// Cancel this wire
// 1) If the candidate at the previous wire is at the same bx
// clock and has better quality (or equal quality - this has
// been implemented only in 2004).
// 2) If the candidate at the previous wire is up to 4 clocks
// earlier, regardless of quality.
if (dt == 0) {
if (qual_prev >= qual_this)
ghost_cleared[i_pattern] = 1;
else if (qual_prev < qual_this)
ghost_cleared_prev = true;
} else if (dt > 0 && dt <= ghost_cancellation_bx_depth) {
if ((!ghost_cancellation_side_quality) || (qual_prev > qual_this))
ghost_cleared[i_pattern] = 1;
} else if (dt < 0 && dt * (-1) <= ghost_cancellation_bx_depth) {
if ((!ghost_cancellation_side_quality) || (qual_prev < qual_this))
ghost_cleared_prev = true;
}
if (ghost_cleared[i_pattern] == 1) {
if (infoV > 1)
LogTrace("CSCAnodeLCTProcessor")
<< ((i_pattern == CSCConstants::ALCT_ACCELERATOR_PATTERN) ? "Accelerator" : "Collision")
<< " pattern ghost cancelled on key_wire " << key_wire << " q=" << qual_this << " by wire "
<< key_wire - 1 << " q=" << qual_prev;
//cancellation for key_wire is done when ALCT is created and pushed to lct_list
}
if (ghost_cleared_prev) {
if (infoV > 1)
LogTrace("CSCAnodeLCTProcessor")
<< ((i_pattern == CSCConstants::ALCT_ACCELERATOR_PATTERN) ? "Accelerator" : "Collision")
<< " pattern ghost cancelled on key_wire " << key_wire - 1 << " q=" << qual_prev << " by wire "
<< key_wire << " q=" << qual_this;
p.setValid(0); //clean prev ALCT
}
}
} // if qual_this > 0
} //i_pattern
}
void CSCAnodeLCTProcessor::lctSearch() {
// Best track selector selects two collision and two accelerator ALCTs
// with the best quality per time bin.
const std::vector<CSCALCTDigi>& fourBest = bestTrackSelector(lct_list);
if (infoV > 0) {
int n_alct_all = 0, n_alct = 0;
for (const auto& p : lct_list) {
if (p.isValid() && p.getBX() == CSCConstants::LCT_CENTRAL_BX)
n_alct_all++;
}
for (const auto& p : fourBest) {
if (p.isValid() && p.getBX() == CSCConstants::LCT_CENTRAL_BX)
n_alct++;
}
LogTrace("CSCAnodeLCTProcessor") << "alct_count E:" << theEndcap << "S:" << theStation << "R:" << theRing
<< "C:" << theChamber << " all " << n_alct_all << " found " << n_alct;
}
// Select two best of four per time bin, based on quality and
// accel_mode parameter.
for (const auto& p : fourBest) {
const int bx = p.getBX();
if (bx >= CSCConstants::MAX_ALCT_TBINS) {
if (infoV > 0)
edm::LogWarning("CSCAnodeLCTProcessor|OutOfTimeALCT")
<< "+++ Bx of ALCT candidate, " << bx << ", exceeds max allowed, " << CSCConstants::MAX_ALCT_TBINS - 1
<< "; skipping it... +++\n";
continue;
}
if (isBetterALCT(p, bestALCT[bx])) {
if (isBetterALCT(bestALCT[bx], secondALCT[bx])) {
secondALCT[bx] = bestALCT[bx];
}
bestALCT[bx] = p;
} else if (isBetterALCT(p, secondALCT[bx])) {
secondALCT[bx] = p;
}
}
for (int bx = 0; bx < CSCConstants::MAX_ALCT_TBINS; bx++) {
if (bestALCT[bx].isValid()) {
bestALCT[bx].setTrknmb(1);
// check if the best ALCT is valid
qualityControl_->checkValidReadout(bestALCT[bx]);
if (infoV > 0) {
LogDebug("CSCAnodeLCTProcessor") << bestALCT[bx] << " fullBX = " << bestALCT[bx].getFullBX() << " found in "
<< theCSCName_ << " (sector " << theSector << " subsector " << theSubsector
<< " trig id. " << theTrigChamber << ")"
<< "\n";
}
if (secondALCT[bx].isValid()) {
secondALCT[bx].setTrknmb(2);
// check if the second best ALCT is valid
qualityControl_->checkValidReadout(secondALCT[bx]);
if (infoV > 0) {
LogDebug("CSCAnodeLCTProcessor")
<< secondALCT[bx] << " fullBX = " << secondALCT[bx].getFullBX() << " found in " << theCSCName_
<< " (sector " << theSector << " subsector " << theSubsector << " trig id. " << theTrigChamber << ")"
<< "\n";
}
}
}
}
}
std::vector<CSCALCTDigi> CSCAnodeLCTProcessor::bestTrackSelector(const std::vector<CSCALCTDigi>& all_alcts) {
CSCALCTDigi bestALCTs[CSCConstants::MAX_ALCT_TBINS][CSCConstants::MAX_ALCTS_PER_PROCESSOR];
CSCALCTDigi secondALCTs[CSCConstants::MAX_ALCT_TBINS][CSCConstants::MAX_ALCTS_PER_PROCESSOR];
if (infoV > 1) {
LogTrace("CSCAnodeLCTProcessor") << all_alcts.size() << " ALCTs at the input of best-track selector: ";
for (const auto& p : all_alcts) {
if (!p.isValid())
continue;
LogTrace("CSCAnodeLCTProcessor") << p;
}
}
CSCALCTDigi tA[CSCConstants::MAX_ALCT_TBINS][CSCConstants::MAX_ALCTS_PER_PROCESSOR];
CSCALCTDigi tB[CSCConstants::MAX_ALCT_TBINS][CSCConstants::MAX_ALCTS_PER_PROCESSOR];
for (const auto& p : all_alcts) {
if (!p.isValid())
continue;
// Select two collision and two accelerator ALCTs with the highest
// quality at every bx. The search for best ALCTs is done in parallel
// for collision and accelerator patterns, and simultaneously for
// two ALCTs, tA and tB. If two or more ALCTs have equal qualities,
// the priority is given to the ALCT with larger wiregroup number
// in the search for tA (collision and accelerator), and to the ALCT
// with smaller wiregroup number in the search for tB.
int bx = p.getBX();
int accel = p.getAccelerator();
int qual = p.getQuality();
int wire = p.getKeyWG();
bool vA = tA[bx][accel].isValid();
bool vB = tB[bx][accel].isValid();
int qA = tA[bx][accel].getQuality();
int qB = tB[bx][accel].getQuality();
int wA = tA[bx][accel].getKeyWG();
int wB = tB[bx][accel].getKeyWG();
if (!vA || qual > qA || (qual == qA && wire > wA)) {
tA[bx][accel] = p;
}
if (!vB || qual > qB || (qual == qB && wire < wB)) {
tB[bx][accel] = p;
}
}
for (int bx = 0; bx < CSCConstants::MAX_ALCT_TBINS; bx++) {
for (int accel = 0; accel <= 1; accel++) {
// Best ALCT is always tA.
if (tA[bx][accel].isValid()) {
if (infoV > 2) {
LogTrace("CSCAnodeLCTProcessor") << "tA: " << tA[bx][accel];
LogTrace("CSCAnodeLCTProcessor") << "tB: " << tB[bx][accel];
}
bestALCTs[bx][accel] = tA[bx][accel];
// If tA exists, tB exists too.
if (tA[bx][accel] != tB[bx][accel] && tA[bx][accel].getQuality() == tB[bx][accel].getQuality()) {
secondALCTs[bx][accel] = tB[bx][accel];
} else {
// Funny part: if tA and tB are the same, or the quality of tB
// is inferior to the quality of tA, the second best ALCT is
// not tB. Instead it is the largest-wiregroup ALCT among those
// ALCT whose qualities are lower than the quality of the best one.
for (const auto& p : all_alcts) {
if (p.isValid() && p.getAccelerator() == accel && p.getBX() == bx &&
p.getQuality() < bestALCTs[bx][accel].getQuality() &&
p.getQuality() >= secondALCTs[bx][accel].getQuality() &&
p.getKeyWG() >= secondALCTs[bx][accel].getKeyWG()) {
secondALCTs[bx][accel] = p;
}
}
}
}
}
}
// Fill the vector with up to four best ALCTs per bx and return it.
std::vector<CSCALCTDigi> fourBest;
for (int bx = 0; bx < CSCConstants::MAX_ALCT_TBINS; bx++) {
for (int i = 0; i < CSCConstants::MAX_ALCTS_PER_PROCESSOR; i++) {
if (bestALCTs[bx][i].isValid()) {
fourBest.push_back(bestALCTs[bx][i]);
}
}
for (int i = 0; i < CSCConstants::MAX_ALCTS_PER_PROCESSOR; i++) {
if (secondALCTs[bx][i].isValid()) {