ATLAS Offline Software
Loading...
Searching...
No Matches
gFEXJetAlgo.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4//***************************************************************************
5// gFEXJetAlgo - JetFinder algorithm for gFEX
6// -------------------
7// begin : 01 04 2021
8// email : cecilia.tosciri@cern.ch
9//***************************************************************************
10
11
12
13#include "gFEXJetAlgo.h"
16#include "L1CaloFEXSim/gTower.h"
17
18#include <vector>
19namespace LVL1 {
20
21 // default constructor for persistency
22gFEXJetAlgo::gFEXJetAlgo(const std::string& type, const std::string& name, const IInterface* parent):
23 AthAlgTool(type, name, parent)
24 {
25 declareInterface<IgFEXJetAlgo>(this);
26 }
27
28
30
32
33 return StatusCode::SUCCESS;
34
35}
36
37
38
39std::vector<std::unique_ptr<gFEXJetTOB>> gFEXJetAlgo::largeRfinder(
40 const gTowersType& Atwr,
41 const gTowersType& Btwr,
42 const gTowersType& Ctwr,
43 const gTowersType& Asat,
44 const gTowersType& Bsat,
45 const gTowersType& Csat,
46 int pucA, int pucB, int pucC, int gLJ_seedThrA, int gLJ_seedThrB, int gLJ_seedThrC,
47 int gJ_ptMinToTopoCounts1, int gJ_ptMinToTopoCounts2,
48 int jetThreshold, int gLJ_ptMinToTopoCounts1, int gLJ_ptMinToTopoCounts2,
49 std::array<uint32_t, 7> & ATOB1_dat, std::array<uint32_t, 7> & ATOB2_dat,
50 std::array<uint32_t, 7> & BTOB1_dat, std::array<uint32_t, 7> & BTOB2_dat,
51 std::array<uint32_t, 7> & CTOB1_dat, std::array<uint32_t, 7> & CTOB2_dat) const {
52
53 // Arrays for gJets
54 // s = status (1 if above threshold)
55 // v = value 200 MeV LSB
56 // eta and phi are bin numbers
57 std::array<int, FEXAlgoSpaceDefs::gJetTOBfib> gTOBsat = {{0,0,0,0,0,0}};
58 std::array<int, FEXAlgoSpaceDefs::gJetTOBfib> gJetTOBs = {{0,0,0,0,0,0}};
59 std::array<int, FEXAlgoSpaceDefs::gJetTOBfib> gJetTOBv = {{0,0,0,0,0,0}};
60 std::array<int, FEXAlgoSpaceDefs::gJetTOBfib> gJetTOBeta = {{0,0,0,0,0,0}};
61 std::array<int, FEXAlgoSpaceDefs::gJetTOBfib> gJetTOBphi = {{0,0,0,0,0,0}};
62
63 // Arrays for gBlocks (so far only 2 gBlocks per fiber)
64 // first index is for the fiber number (A 0 and 1, B 0 and 1, C P and C N )
65 // second index is for leading, sub-leading, and sub-sub-leading (not used)
66 std::array<std::array<int, 3>, FEXAlgoSpaceDefs::BTOBFIB> gBlockTOBv = {{ {{0,0,0}}, {{0,0,0}}, {{0,0,0}}, {{0,0,0}}, {{0,0,0}}, {{0,0,0}} }};
67 std::array<std::array<int, 3>, FEXAlgoSpaceDefs::BTOBFIB> gBlockTOBeta = {{ {{0,0,0}}, {{0,0,0}}, {{0,0,0}}, {{0,0,0}}, {{0,0,0}}, {{0,0,0}} }};
68 std::array<std::array<int, 3>, FEXAlgoSpaceDefs::BTOBFIB> gBlockTOBphi = {{ {{0,0,0}}, {{0,0,0}}, {{0,0,0}}, {{0,0,0}}, {{0,0,0}}, {{0,0,0}} }};
69
70
71 //Alternate format for FPGC C (split in two)
72
73 gTowersJetEngine CNtwr;
74 gTowersJetEngine CPtwr;
75
76 for(int irow=0; irow<FEXAlgoSpaceDefs::ABCrows; irow++){
77 for(int jcolumn=0;jcolumn<FEXAlgoSpaceDefs::ABCcolumnsEng;jcolumn++){
78 CNtwr[irow][jcolumn] = Ctwr[irow][jcolumn] ;
79 CPtwr[irow][jcolumn] = Ctwr[irow][jcolumn+FEXAlgoSpaceDefs::ABCcolumnsEng] ;
80 }
81
82 for(unsigned int jcolumn=0;jcolumn<FEXAlgoSpaceDefs::ABcolumns;jcolumn++){
83
84 if( jcolumn < 6 ) {
85 // TOB1
86 if( Asat[irow][jcolumn] == 1 ) gTOBsat[0] = 1;
87 if( Bsat[irow][jcolumn] == 1 ) gTOBsat[2] = 1;
88 if( Csat[irow][jcolumn] == 1 ) gTOBsat[4] = 1;
89 } else {
90 //TOB2
91 if( Asat[irow][jcolumn] == 1 ) gTOBsat[1] = 1;
92 if( Bsat[irow][jcolumn] == 1 ) gTOBsat[3] = 1;
93 if( Csat[irow][jcolumn] == 1 ) gTOBsat[5] = 1;
94 }
95 }
96 }
97
98
99
100 gTowersType AjetsAlt;
101 singleHalf(Atwr,AjetsAlt);
102
103 gTowersPartialSums AlpsAltOut;
104 gTowersPartialSums ArpsAltOut;
105
106 InternalPartialAB(Atwr, AlpsAltOut, ArpsAltOut);
107
108 gTowersType BjetsAlt;
109 singleHalf(Btwr,BjetsAlt);
110
111 gTowersPartialSums BlpsAltOut;
112 gTowersPartialSums BrpsAltOut;
113
114 InternalPartialAB(Btwr, BlpsAltOut, BrpsAltOut);
115
116 gTowersType CjetsAlt;
117 singleHalf(Ctwr,CjetsAlt);
118
119 // Add the left local partial sum (zero if right column)
120 addInternalLin(AjetsAlt, AlpsAltOut);
121 addInternalLin(BjetsAlt, BlpsAltOut);
122
123 pileUpCorrectionAB(AjetsAlt,pucA);
124 pileUpCorrectionAB(BjetsAlt,pucB);
125 pileUpCorrectionAB(CjetsAlt,pucC);
126
127 // If the local sum is less than zero set it to zero
128 ZeroNegative(AjetsAlt);
129 ZeroNegative(BjetsAlt);
130 ZeroNegative(CjetsAlt);
131
132 // Add the right local partial sum (zero if right column)
133 addInternalRin(AjetsAlt, ArpsAltOut);
134 addInternalRin(BjetsAlt, BrpsAltOut);
135
136 for(unsigned int irow = 0; irow < FEXAlgoSpaceDefs::ABCrows; irow++ ){
137 for(unsigned int icolumn =0; icolumn<FEXAlgoSpaceDefs::ABcolumns; icolumn++){
138 // set 18 bits on
139 if (Asat[irow][icolumn] != 0){
140 }
141 }
142 }
143
144 SaturateJets( AjetsAlt, Asat, 0 );
145 SaturateJets( BjetsAlt, Bsat, 1 );
146 SaturateJets( CjetsAlt, Csat, 2 );
147
148 gTowersType AjetsRestricted;
149 gTowersType BjetsRestricted;
150 gTowersType CjetsRestricted;
151
152
153 for(int irow=0; irow<FEXAlgoSpaceDefs::ABCrows; irow++){
154 for(int icolumn=0; icolumn<FEXAlgoSpaceDefs::ABcolumns; icolumn++){
155 AjetsRestricted[irow][icolumn] = AjetsAlt[irow][icolumn];
156 BjetsRestricted[irow][icolumn] = BjetsAlt[irow][icolumn];
157 CjetsRestricted[irow][icolumn] = CjetsAlt[irow][icolumn];
158 }
159 }
160
161 // find gBlocks
162 gTowersType gBLKA;
163 gTowersType hasSeedA;
164 gBlockAB(Atwr, gBLKA, hasSeedA, gLJ_seedThrA);
165
166 SaturateBlocks(gBLKA, Asat, 0);
167
168 // TODO: Temporary fix to match FW saturation eta-alignment bug.
169 // In firmware, seed_ovf forces et9 to max positive → have_seed=1 unconditionally.
170 // The same −1 eta shift applies: overflow at col K forces have_seed at col K-1.
171 // @see jet_eng.vhd lines 849-870: seed_ovf → et9=max → have_seed=1
172 // Revert this block when the FW saturation alignment is fixed.
173 for(unsigned int irow = 0; irow < FEXAlgoSpaceDefs::ABCrows; irow++){
174 for(unsigned int icol = 0; icol < FEXAlgoSpaceDefs::ABcolumns; icol++){
175 if(Asat[irow][icol] && (icol % 6 != 0)) hasSeedA[irow][icol - 1] = 1;
176 }
177 }
178
179 gTowersType gBLKB;
180 gTowersType hasSeedB;
181 gBlockAB(Btwr, gBLKB, hasSeedB, gLJ_seedThrB);
182 SaturateBlocks(gBLKB, Bsat, 1);
183
184 // TODO: Temporary fix to match FW saturation eta-alignment bug.
185 // Revert this block when the FW saturation alignment is fixed.
186 for(unsigned int irow = 0; irow < FEXAlgoSpaceDefs::ABCrows; irow++){
187 for(unsigned int icol = 0; icol < FEXAlgoSpaceDefs::ABcolumns; icol++){
188 if(Bsat[irow][icol] && (icol % 6 != 0) && icol != 11) hasSeedB[irow][icol - 1] = 1;
189 }
190 }
191
192 gTowersType gBLKC;
193 gTowersType hasSeedC;
194 gBlockAB(Ctwr, gBLKC, hasSeedC, gLJ_seedThrC);
195 SaturateBlocks(gBLKC, Csat, 2);
196
197 // TODO: Temporary fix to match FW saturation eta-alignment bug.
198 // Revert this block when the FW saturation alignment is fixed.
199 for(unsigned int irow = 0; irow < FEXAlgoSpaceDefs::ABCrows; irow++){
200 for(unsigned int icol = 0; icol < FEXAlgoSpaceDefs::ABcolumns; icol++){
201 if(Csat[irow][icol] && (icol % 6 != 0) && (icol % 6 != 5)) hasSeedC[irow][icol - 1] = 1;
202 }
203 }
204
205 // sorting by jet engine -- not done in FPGA
206 std::array<int, 32> AgBlockOutL{};
207 std::array<int, 32> AgBlockEtaIndL{};
208 std::array<int, 32> AgBlockOutR{};
209 std::array<int, 32> AgBlockEtaIndR{};
210 blkOutAB(gBLKA, AgBlockOutL, AgBlockEtaIndL, AgBlockOutR, AgBlockEtaIndR);
211
212 std::array<int, 32> BgBlockOutL{};
213 std::array<int, 32> BgBlockEtaIndL{};
214 std::array<int, 32> BgBlockOutR{};
215 std::array<int, 32> BgBlockEtaIndR{};
216 blkOutAB(gBLKB, BgBlockOutL, BgBlockEtaIndL, BgBlockOutR, BgBlockEtaIndR);
217
218 std::array<int, 32> CgBlockOutL{};
219 std::array<int, 32> CgBlockEtaIndL{};
220 std::array<int, 32> CgBlockOutR{};
221 std::array<int, 32> CgBlockEtaIndR{};
222 blkOutAB(gBLKC, CgBlockOutL, CgBlockEtaIndL, CgBlockOutR, CgBlockEtaIndR) ;
223
224 // sorting by 192 blocks 0 = left, 1 = right
225 // second index is column number, the eta value is 2 + 6*column number + local eta
226
227 // find the leading and subleading gBlock in the left column of FPGA A
228 gBlockMax2(gBLKA, 1, 0, gBlockTOBv[0], gBlockTOBeta[0], gBlockTOBphi[0]);
229 // find the leading and subleading gBlock in the right column of FPGA A
230 gBlockMax2(gBLKA, 2, 1, gBlockTOBv[1], gBlockTOBeta[1], gBlockTOBphi[1]);
231
232 // find the leading and subleading gBlock in the left column of FPGA B
233 gBlockMax2(gBLKB, 3, 0, gBlockTOBv[2], gBlockTOBeta[2], gBlockTOBphi[2]);
234 // find the leading and subleading gBlock in the right column of FPGA B
235 gBlockMax2(gBLKB, 4, 1, gBlockTOBv[3], gBlockTOBeta[3], gBlockTOBphi[3]);
236
237 // find the leading and subleading gBlock in the CN column of FPGA C
238 gBlockMax2( gBLKC, 0, 0, gBlockTOBv[4], gBlockTOBeta[4], gBlockTOBphi[4]);
239 // find the leading and subleading gBlock in the CP column of FPGA C
240 gBlockMax2( gBLKC, 5, 1, gBlockTOBv[5], gBlockTOBeta[5], gBlockTOBphi[5]);
241
242 // in hardware vetos happen before remote sums come in.
243 gBlockVetoAB(AjetsRestricted, hasSeedA);
244 gBlockVetoAB(BjetsRestricted, hasSeedB);
245 gBlockVetoAB(CjetsRestricted, hasSeedC);
246
247 // calculate A & B remote partial sums first
248 gTowersPartialSums RAlps_out, RArps_out;
249 RemotePartialAB(Atwr, RAlps_out, RArps_out);
250
251 gTowersPartialSums RBlps_out, RBrps_out;
252 RemotePartialAB(Btwr, RBlps_out, RBrps_out);
253
254 gTowersPartialSums RCNrps_out, RCPlps_out;
255 RemotePartialCN(CNtwr, RCNrps_out);
256 RemotePartialCP(CPtwr, RCPlps_out);
257
258
260
261 // input partial sums from FPGA B to A (lps_out -> rps_in)
263
264 // input partial sums from FPGA B to A (lps_out -> rps_in)
266
267 }
268
270
272
274
275 }
276
277// this are added into the jets you have to truncate to the number of bits on the interFPGA communication
278
280
282
284
285 }
286
287 // Emulate firmware sum69o_del_ovf: after remote partial sums are added,
288 // re-saturate jets whose input towers were saturated.
289 // In firmware (jet_eng.vhd), sum69o_del_ovf = delayK(dat_ovf_in, SUM69O_DEL_DELAY)
290 // overrides sum69o_del to max positive *instead of* adding remote_ps.
291 // Without this, the simulation adds remote_ps on top of an already-max jet value,
292 // biasing the max-finder (jetOutAB) toward columns that receive remote partial sums
293 // and shifting the reported eta.
294 // The same -1 eta shift as SaturateJets applies here.
295 SaturateJets( AjetsRestricted, Asat, 0 );
296 SaturateJets( BjetsRestricted, Bsat, 1 );
297 SaturateJets( CjetsRestricted, Csat, 2 );
298
299 //Emulate switch to unsigned values by zeroing everything below the jet threshold
300 //https://gitlab.cern.ch/atlas-l1calo/gfex/firmware/-/blob/devel/common/jet_finder/HDL/jet_eng.vhd#L550
301
302 gJetVetoAB(AjetsRestricted, jetThreshold);
303 gJetVetoAB(BjetsRestricted, jetThreshold);
304 gJetVetoAB(CjetsRestricted, jetThreshold);
305
306 std::array<int, 32> AjetOutL;
307 std::array<int, 32> AetaIndL;
308 std::array<int, 32> AjetOutR;
309 std::array<int, 32> AetaIndR;
310
311
312 jetOutAB(AjetsRestricted, AjetOutL, AetaIndL, AjetOutR, AetaIndR);
313
314 std::array<int, 32> BjetOutL;
315 std::array<int, 32> BetaIndL;
316 std::array<int, 32> BjetOutR;
317 std::array<int, 32> BetaIndR;
318
319 jetOutAB(BjetsRestricted, BjetOutL, BetaIndL, BjetOutR, BetaIndR);
320
321 std::array<int, 32> CNjetOut;
322 std::array<int, 32> CNetaInd;
323 std::array<int, 32> CPjetOut;
324 std::array<int, 32> CPetaInd;
325
326 jetOutAB(CjetsRestricted, CNjetOut, CNetaInd, CPjetOut, CPetaInd);
327
328 gJetTOBgen(AjetOutL, AetaIndL, 0, jetThreshold, gJetTOBs, gJetTOBv, gJetTOBeta, gJetTOBphi);
329 gJetTOBgen(AjetOutR, AetaIndR, 1, jetThreshold, gJetTOBs, gJetTOBv, gJetTOBeta, gJetTOBphi);
330
331 gJetTOBgen(BjetOutL, BetaIndL, 2, jetThreshold, gJetTOBs, gJetTOBv, gJetTOBeta, gJetTOBphi);
332 gJetTOBgen(BjetOutR, BetaIndR, 3, jetThreshold, gJetTOBs, gJetTOBv, gJetTOBeta, gJetTOBphi);
333
334 gJetTOBgen(CNjetOut, CNetaInd, 4, jetThreshold, gJetTOBs, gJetTOBv, gJetTOBeta, gJetTOBphi);
335 gJetTOBgen(CPjetOut, CPetaInd, 5, jetThreshold, gJetTOBs, gJetTOBv, gJetTOBeta, gJetTOBphi);
336
338 std::vector<std::unique_ptr<gFEXJetTOB>> tobs_v;
339 tobs_v.resize(14);
340
341 // fill in TOBs
342 ATOB1_dat[0] = 0;
343 BTOB1_dat[0] = 0;
344 CTOB1_dat[0] = 0;
345
346 ATOB2_dat[0] = (( pucA & 0x0000FFFF ) << 8);
347 BTOB2_dat[0] = (( pucB & 0x0000FFFF ) << 8);
348 CTOB2_dat[0] = (( pucC & 0x0000FFFF ) << 8);
349
350 // //First available TOBs are the gRho for each central FPGA
351 tobs_v[0] = std::make_unique<gFEXJetTOB>();
352 tobs_v[0]->setWord(ATOB2_dat[0]);
353 tobs_v[0]->setET(pucA);
354 tobs_v[0]->setEta(0);
355 tobs_v[0]->setPhi(0);
356 tobs_v[0]->setTobID(0);
357 tobs_v[0]->setStatus(1);//is 1 only if rho calculation is valid, but rho is not calculated at the moment
358
359 tobs_v[1] = std::make_unique<gFEXJetTOB>();
360 tobs_v[1]->setWord(BTOB2_dat[0]);
361 tobs_v[1]->setET(pucB);
362 tobs_v[1]->setEta(0);
363 tobs_v[1]->setPhi(0);
364 tobs_v[1]->setTobID(0);
365 tobs_v[1]->setStatus(1);//is 1 only if rho calculation is valid, but rho is not calculated at the moment
366
367 // leading gBlocks (available first and go in first TOB value)
368 // TOBs 2-5 are leading gBlocks
369 ATOB1_dat[1] = 0x00000001; //set the TOB ID in the corresponding slot (LSB)
370 if( gBlockTOBv[0][0] > gJ_ptMinToTopoCounts2 ) ATOB1_dat[1] = ATOB1_dat[1] | 0x00000080;//status
371 ATOB1_dat[1] = ATOB1_dat[1] | ( ( gBlockTOBv[0][0] & 0x00000FFF ) << 8);
372 ATOB1_dat[1] = ATOB1_dat[1] | ( ( gBlockTOBeta[0][0] & 0x0000003F ) <<20);
373 ATOB1_dat[1] = ATOB1_dat[1] | ( ( gBlockTOBphi[0][0] & 0x0000001F ) <<26);
374
375 tobs_v[2] = std::make_unique<gFEXJetTOB>();
376 tobs_v[2]->setWord(ATOB1_dat[1]);
377 tobs_v[2]->setET(gBlockTOBv[0][0]);
378 tobs_v[2]->setEta(gBlockTOBeta[0][0]);
379 tobs_v[2]->setPhi(gBlockTOBphi[0][0]);
380 tobs_v[2]->setTobID(1);
381 if( gBlockTOBv[0][0] > gJ_ptMinToTopoCounts2 ) tobs_v[2]->setStatus(1);
382 else tobs_v[2]->setStatus(0);
383
384 ATOB2_dat[1] = 0x00000002;
385 if( gBlockTOBv[1][0] > gJ_ptMinToTopoCounts1 ) ATOB2_dat[1] = ATOB2_dat[1] | 0x00000080;
386 ATOB2_dat[1] = ATOB2_dat[1] | ( ( gBlockTOBv[1][0] & 0x00000FFF ) << 8);
387 ATOB2_dat[1] = ATOB2_dat[1] | ( ( gBlockTOBeta[1][0] & 0x0000003F ) <<20);
388 ATOB2_dat[1] = ATOB2_dat[1] | ( ( gBlockTOBphi[1][0] & 0x0000001F ) <<26);
389
390 tobs_v[3] = std::make_unique<gFEXJetTOB>();
391 tobs_v[3]->setWord(ATOB2_dat[1]);
392 tobs_v[3]->setET(gBlockTOBv[1][0]);
393 tobs_v[3]->setEta(gBlockTOBeta[1][0]);
394 tobs_v[3]->setPhi(gBlockTOBphi[1][0]);
395 tobs_v[3]->setTobID(2);
396 if( gBlockTOBv[1][0] > gJ_ptMinToTopoCounts1 ) tobs_v[3]->setStatus(1);
397 else tobs_v[3]->setStatus(0);
398
399
400 BTOB1_dat[1] = 0x00000001;
401 if( gBlockTOBv[2][0] > gJ_ptMinToTopoCounts1 ) BTOB1_dat[1] = BTOB1_dat[1] | 0x00000080;
402 BTOB1_dat[1] = BTOB1_dat[1] | ( ( gBlockTOBv[2][0] & 0x00000FFF ) << 8);
403 BTOB1_dat[1] = BTOB1_dat[1] | ( ( gBlockTOBeta[2][0] & 0x0000003F ) <<20);
404 BTOB1_dat[1] = BTOB1_dat[1] | ( ( gBlockTOBphi[2][0] & 0x0000001F) <<26);
405
406 tobs_v[4] = std::make_unique<gFEXJetTOB>();
407 tobs_v[4]->setWord(BTOB1_dat[1]);
408 tobs_v[4]->setET(gBlockTOBv[2][0]);
409 tobs_v[4]->setEta(gBlockTOBeta[2][0]);
410 tobs_v[4]->setPhi(gBlockTOBphi[2][0]);
411 tobs_v[4]->setTobID(1);
412 if( gBlockTOBv[2][0] > gJ_ptMinToTopoCounts1 ) tobs_v[4]->setStatus(1);
413 else tobs_v[4]->setStatus(0);
414
415
416 BTOB2_dat[1] = 0x00000002;
417 if( gBlockTOBv[3][0] > gJ_ptMinToTopoCounts2 ) BTOB2_dat[1] = BTOB2_dat[1] | 0x00000080;
418 BTOB2_dat[1] = BTOB2_dat[1] | ( ( gBlockTOBv[3][0] & 0x00000FFF ) << 8);
419 BTOB2_dat[1] = BTOB2_dat[1] | ( ( gBlockTOBeta[3][0] & 0x0000003F ) <<20);
420 BTOB2_dat[1] = BTOB2_dat[1] | ( ( gBlockTOBphi[3][0] & 0x0000001F ) <<26);
421
422 tobs_v[5] = std::make_unique<gFEXJetTOB>();
423 tobs_v[5]->setWord(BTOB2_dat[1]);
424 tobs_v[5]->setET(gBlockTOBv[3][0]);
425 tobs_v[5]->setEta(gBlockTOBeta[3][0]);
426 tobs_v[5]->setPhi(gBlockTOBphi[3][0]);
427 tobs_v[5]->setTobID(2);
428 if( gBlockTOBv[3][0] > gJ_ptMinToTopoCounts2 ) tobs_v[5]->setStatus(1);
429 else tobs_v[5]->setStatus(0);
430
431
432 CTOB1_dat[1] = 0x00000001;
433 if( gBlockTOBv[4][0] > gJ_ptMinToTopoCounts1 ) CTOB1_dat[1] = CTOB1_dat[1] | 0x00000080;
434 CTOB1_dat[1] = CTOB1_dat[1] | ( ( gBlockTOBv[4][0] & 0x00000FFF ) << 8);
435 CTOB1_dat[1] = CTOB1_dat[1] | ( ( gBlockTOBeta[4][0] & 0x0000003F ) <<20);
436 CTOB1_dat[1] = CTOB1_dat[1] | ( ( gBlockTOBphi[4][0] & 0x0000001F) <<26);
437
438
439 CTOB2_dat[1] = 0x00000002;
440 if( gBlockTOBv[5][0] > gJ_ptMinToTopoCounts2) CTOB2_dat[1] = CTOB2_dat[1] | 0x00000080;
441 CTOB2_dat[1] = CTOB2_dat[1] | ( ( gBlockTOBv[5][0] & 0x00000FFF ) << 8);
442 CTOB2_dat[1] = CTOB2_dat[1] | ( ( gBlockTOBeta[5][0] & 0x0000003F ) <<20);
443 CTOB2_dat[1] = CTOB2_dat[1] | ( ( gBlockTOBphi[5][0] & 0x0000001F ) <<26);
444
445
446 // subleading gBlocks
447 // TOBs 6-9 are subleading gBlocks
448 ATOB1_dat[2] = 0x00000003;
449 if( gBlockTOBv[0][1] > gJ_ptMinToTopoCounts2 ) ATOB1_dat[2] = ATOB1_dat[2] | 0x00000080;
450 ATOB1_dat[2] = ATOB1_dat[2] | ( ( gBlockTOBv[0][1] & 0x00000FFF ) << 8);
451 ATOB1_dat[2] = ATOB1_dat[2] | ( ( gBlockTOBeta[0][1] & 0x0000003F ) <<20);
452 ATOB1_dat[2] = ATOB1_dat[2] | ( ( gBlockTOBphi[0][1] & 0x0000001F ) <<26);
453
454 tobs_v[6] = std::make_unique<gFEXJetTOB>();
455 tobs_v[6]->setWord(ATOB1_dat[2]);
456 tobs_v[6]->setET(gBlockTOBv[0][1]);
457 tobs_v[6]->setEta(gBlockTOBeta[0][1]);
458 tobs_v[6]->setPhi(gBlockTOBphi[0][1]);
459 tobs_v[6]->setTobID(3);
460 if( gBlockTOBv[0][1] > gJ_ptMinToTopoCounts2 ) tobs_v[6]->setStatus(1);
461 else tobs_v[6]->setStatus(0);
462
463
464 ATOB2_dat[2] = 0x00000004;
465 if( gBlockTOBv[1][1] > gJ_ptMinToTopoCounts1 ) ATOB2_dat[2] = ATOB2_dat[2] | 0x00000080;
466 ATOB2_dat[2] = ATOB2_dat[2] | ( ( gBlockTOBv[1][1] & 0x00000FFF ) << 8);
467 ATOB2_dat[2] = ATOB2_dat[2] | ( ( gBlockTOBeta[1][1] & 0x0000003F ) <<20);
468 ATOB2_dat[2] = ATOB2_dat[2] | ( ( gBlockTOBphi[1][1] & 0x0000001F ) <<26);
469
470 tobs_v[7] = std::make_unique<gFEXJetTOB>();
471 tobs_v[7]->setWord(ATOB2_dat[2]);
472 tobs_v[7]->setET(gBlockTOBv[1][1]);
473 tobs_v[7]->setEta(gBlockTOBeta[1][1]);
474 tobs_v[7]->setPhi(gBlockTOBphi[1][1]);
475 tobs_v[7]->setTobID(4);
476 if( gBlockTOBv[1][1] > gJ_ptMinToTopoCounts1 ) tobs_v[7]->setStatus(1);
477 else tobs_v[7]->setStatus(0);
478
479
480 BTOB1_dat[2] = 0x00000003;
481 if( gBlockTOBv[2][1] > gJ_ptMinToTopoCounts1 ) BTOB1_dat[2] = BTOB1_dat[2] | 0x00000080;
482 BTOB1_dat[2] = BTOB1_dat[2] | ( ( gBlockTOBv[2][1] & 0x00000FFF ) << 8);
483 BTOB1_dat[2] = BTOB1_dat[2] | ( ( gBlockTOBeta[2][1] & 0x0000003F ) <<20);
484 BTOB1_dat[2] = BTOB1_dat[2] | ( ( gBlockTOBphi[2][1] & 0x0000001F ) <<26);
485
486 tobs_v[8] = std::make_unique<gFEXJetTOB>();
487 tobs_v[8]->setWord(BTOB1_dat[2]);
488 tobs_v[8]->setET(gBlockTOBv[2][1]);
489 tobs_v[8]->setEta(gBlockTOBeta[2][1]);
490 tobs_v[8]->setPhi(gBlockTOBphi[2][1]);
491 tobs_v[8]->setTobID(3);
492 if( gBlockTOBv[2][1] > gJ_ptMinToTopoCounts1 ) tobs_v[8]->setStatus(1);
493 else tobs_v[8]->setStatus(0);
494
495
496 BTOB2_dat[2] = 0x00000004;
497 if( gBlockTOBv[3][1] > gJ_ptMinToTopoCounts2 ) BTOB2_dat[2] = BTOB2_dat[2] | 0x00000080;
498 BTOB2_dat[2] = BTOB2_dat[2] | ( ( gBlockTOBv[3][1] & 0x00000FFF ) << 8);
499 BTOB2_dat[2] = BTOB2_dat[2] | ( ( gBlockTOBeta[3][1] & 0x0000003F ) <<20);
500 BTOB2_dat[2] = BTOB2_dat[2] | ( ( gBlockTOBphi[3][1] & 0x0000001F ) <<26);
501
502 tobs_v[9] = std::make_unique<gFEXJetTOB>();
503 tobs_v[9]->setWord(BTOB2_dat[2]);
504 tobs_v[9]->setET(gBlockTOBv[3][1]);
505 tobs_v[9]->setEta(gBlockTOBeta[3][1]);
506 tobs_v[9]->setPhi(gBlockTOBphi[3][1]);
507 tobs_v[9]->setTobID(4);
508 if( gBlockTOBv[3][1] > gJ_ptMinToTopoCounts2 ) tobs_v[9]->setStatus(1);
509 else tobs_v[9]->setStatus(0);
510
511
512 CTOB1_dat[2] = 0x00000003;
513 if( gBlockTOBv[4][1] > gJ_ptMinToTopoCounts1 ) CTOB1_dat[2] = CTOB1_dat[2] | 0x00000080;
514 CTOB1_dat[2] = CTOB1_dat[2] | ( ( gBlockTOBv[4][1] & 0x00000FFF ) << 8);
515 CTOB1_dat[2] = CTOB1_dat[2] | ( ( gBlockTOBeta[4][1] & 0x0000003F ) <<20);
516 CTOB1_dat[2] = CTOB1_dat[2] | ( ( gBlockTOBphi[4][1] & 0x0000001F ) <<26);
517
518 CTOB2_dat[2] = 0x00000004;
519 if( gBlockTOBv[5][1] > gJ_ptMinToTopoCounts2 ) CTOB2_dat[2] = CTOB2_dat[2] | 0x00000080;
520 CTOB2_dat[2] = CTOB2_dat[2] | ( ( gBlockTOBv[5][1] & 0x00000FFF ) << 8);
521 CTOB2_dat[2] = CTOB2_dat[2] | ( ( gBlockTOBeta[5][1] & 0x0000003F ) <<20);
522 CTOB2_dat[2] = CTOB2_dat[2] | ( ( gBlockTOBphi[5][1] & 0x0000001F ) <<26);
523
524
525 // finally the main event -- lead gJET
526 // according the specification https://docs.google.com/spreadsheets/d/15YVVtGofhXMtV7jXRFzWO0FVUtUAjS-X-aQjh3FKE_w/edit#gid=523371660
527 // we should have an lsb of 3.2 GeV -- so ignore lower 4 bits.
528
529 // TOBs 10-13 are leading gJets
530 // shift is done before sorting as in firmware!
531 int tobvShift = 8;
532 int tobvMask = 0x00000FFF;
533
534
535 ATOB1_dat[3] = 0x00000005;
536 if( gJetTOBv[0] > gLJ_ptMinToTopoCounts2 ) ATOB1_dat[3] = ATOB1_dat[3] | 0x00000080;
537 ATOB1_dat[3] = ATOB1_dat[3] | ( ( gJetTOBv[0] & tobvMask) << tobvShift);
538 ATOB1_dat[3] = ATOB1_dat[3] | ( ( gJetTOBeta[0] & 0x0000003F ) <<20);
539 ATOB1_dat[3] = ATOB1_dat[3] | ( ( gJetTOBphi[0] & 0x0000001F ) <<26);
540
541 tobs_v[10] = std::make_unique<gFEXJetTOB>();
542 tobs_v[10]->setWord(ATOB1_dat[3]);
543 tobs_v[10]->setET(gJetTOBv[0]);
544 tobs_v[10]->setEta(gJetTOBeta[0]);
545 tobs_v[10]->setPhi(gJetTOBphi[0]);
546 tobs_v[10]->setTobID(5);
547 if(gJetTOBv[0] > gLJ_ptMinToTopoCounts2 ) tobs_v[10]->setStatus(1);
548 else tobs_v[10]->setStatus(0);
549
550
551 ATOB2_dat[3] = 0x00000006;
552 if( gJetTOBv[1] > gLJ_ptMinToTopoCounts1 ) ATOB2_dat[3] = ATOB2_dat[3] | 0x00000080;
553 ATOB2_dat[3] = ATOB2_dat[3] | ( ( gJetTOBv[1] & tobvMask ) << tobvShift);
554 ATOB2_dat[3] = ATOB2_dat[3] | ( ( gJetTOBeta[1] & 0x0000003F ) <<20);
555 ATOB2_dat[3] = ATOB2_dat[3] | ( ( gJetTOBphi[1] & 0x0000001F ) <<26);
556
557 tobs_v[11] = std::make_unique<gFEXJetTOB>();
558 tobs_v[11]->setWord(ATOB2_dat[3]);
559 tobs_v[11]->setET(gJetTOBv[1]);
560 tobs_v[11]->setEta(gJetTOBeta[1]);
561 tobs_v[11]->setPhi(gJetTOBphi[1]);
562 tobs_v[11]->setTobID(6);
563 if( gJetTOBv[1] > gLJ_ptMinToTopoCounts1 ) tobs_v[11]->setStatus(1);
564 else tobs_v[11]->setStatus(0);
565
566
567 BTOB1_dat[3] = 0x00000005;
568 if( gJetTOBv[2] > gLJ_ptMinToTopoCounts1 ) BTOB1_dat[3] = BTOB1_dat[3] | 0x00000080;
569 BTOB1_dat[3] = BTOB1_dat[3] | ( ( gJetTOBv[2] & tobvMask) << tobvShift);
570 BTOB1_dat[3] = BTOB1_dat[3] | ( ( gJetTOBeta[2] & 0x0000003F ) <<20);
571 BTOB1_dat[3] = BTOB1_dat[3] | ( ( gJetTOBphi[2] & 0x0000001F ) <<26);
572
573 tobs_v[12] = std::make_unique<gFEXJetTOB>();
574 tobs_v[12]->setWord(BTOB1_dat[3]);
575 tobs_v[12]->setET(gJetTOBv[2]);
576 tobs_v[12]->setEta(gJetTOBeta[2]);
577 tobs_v[12]->setPhi(gJetTOBphi[2]);
578 tobs_v[12]->setTobID(5);
579 if( gJetTOBv[2] > gLJ_ptMinToTopoCounts1 ) tobs_v[12]->setStatus(1);
580 else tobs_v[12]->setStatus(0);
581
582
583 BTOB2_dat[3] = 0x00000006;
584 if( gJetTOBv[3] > gLJ_ptMinToTopoCounts2 ) BTOB2_dat[3] = BTOB2_dat[3] | 0x00000080;
585 BTOB2_dat[3] = BTOB2_dat[3] | ( ( gJetTOBv[3] & tobvMask ) << tobvShift);
586 BTOB2_dat[3] = BTOB2_dat[3] | ( ( gJetTOBeta[3] & 0x0000003F ) <<20);
587 BTOB2_dat[3] = BTOB2_dat[3] | ( ( gJetTOBphi[3] & 0x0000001F ) <<26);
588
589 tobs_v[13] = std::make_unique<gFEXJetTOB>();
590 tobs_v[13]->setWord(BTOB2_dat[3]);
591 tobs_v[13]->setET(gJetTOBv[3]);
592 tobs_v[13]->setEta(gJetTOBeta[3]);
593 tobs_v[13]->setPhi(gJetTOBphi[3]);
594 tobs_v[13]->setTobID(6);
595 if( gJetTOBv[3] > gLJ_ptMinToTopoCounts2 ) tobs_v[13]->setStatus(1);
596 else tobs_v[13]->setStatus(0);
597
598 // // CECILIA - TO BE CHECKED
599 CTOB1_dat[3] = 0x00000005;
600 if( gJetTOBv[4] > gLJ_ptMinToTopoCounts1 ) CTOB1_dat[3] = CTOB1_dat[3] | 0x00000080;
601 CTOB1_dat[3] = CTOB1_dat[3] | ( ( gJetTOBv[4] & tobvMask) << tobvShift);
602 CTOB1_dat[3] = CTOB1_dat[3] | ( ( gJetTOBeta[4] & 0x0000003F ) <<20);
603 CTOB1_dat[3] = CTOB1_dat[3] | ( ( gJetTOBphi[4] & 0x0000001F ) <<26);
604
605 CTOB2_dat[3] = 0x00000006;
606 if( gJetTOBv[5] > gLJ_ptMinToTopoCounts2 ) CTOB2_dat[3] = CTOB2_dat[3] | 0x00000080;
607 CTOB2_dat[3] = CTOB2_dat[3] | ( ( gJetTOBv[5] & tobvMask ) << tobvShift);
608 CTOB2_dat[3] = CTOB2_dat[3] | ( ( gJetTOBeta[5] & 0x0000003F ) <<20);
609 CTOB2_dat[3] = CTOB2_dat[3] | ( ( gJetTOBphi[5] & 0x0000001F ) <<26);
610
611
612// saturation currently not set in firmware
613//
614//
615 bool setSat = 0;
616 if ( setSat) {
617 if( gTOBsat[0] ) {
618 ATOB1_dat[1] = ( ATOB1_dat[1] | 0x80000000 );
619 ATOB1_dat[3] = ( ATOB1_dat[3] | 0x80000000 );
620 }
621
622 if( gTOBsat[1] ) {
623 ATOB2_dat[1] = ( ATOB2_dat[1] | 0x80000000 );
624 ATOB2_dat[3] = ( ATOB2_dat[3] | 0x80000000 );
625 }
626
627 if( gTOBsat[2] ) {
628 BTOB1_dat[1] = ( BTOB1_dat[1] | 0x80000000 );
629 BTOB1_dat[3] = ( BTOB1_dat[3] | 0x80000000 );
630 }
631
632 if( gTOBsat[3] ) {
633 BTOB2_dat[1] = ( BTOB2_dat[1] | 0x80000000 );
634 BTOB2_dat[3] = ( BTOB2_dat[3] | 0x80000000 );
635 }
636
637 if( gTOBsat[4] ) {
638 CTOB1_dat[1] = ( CTOB1_dat[1] | 0x80000000 );
639 CTOB1_dat[3] = ( CTOB1_dat[3] | 0x80000000 );
640 }
641
642 if( gTOBsat[5] ) {
643 CTOB2_dat[1] = ( CTOB2_dat[1] | 0x80000000 );
644 CTOB2_dat[3] = ( CTOB2_dat[3] | 0x80000000 );
645 }
646 }
647
648 // zero tob 4 word
649
650 ATOB1_dat[4] = 0 ;
651 ATOB2_dat[4] = 0 ;
652 BTOB1_dat[4] = 0 ;
653 BTOB2_dat[4] = 0 ;
654 CTOB1_dat[4] = 0 ;
655 CTOB2_dat[4] = 0 ;
656
657
658 // add seven bits as in the hardware of BCID in 6th TOB word ( index 5)
659 int BCID = 0;
660
661 ATOB1_dat[5] = ( (BCID&0x0000007F)<<8 ) ;
662 ATOB2_dat[5] = ( (BCID&0x0000007F)<<8 ) ;
663 BTOB1_dat[5] = ( (BCID&0x0000007F)<<8 ) ;
664 BTOB2_dat[5] = ( (BCID&0x0000007F)<<8 ) ;
665 CTOB1_dat[5] = ( (BCID&0x0000007F)<<8 ) ;
666 CTOB2_dat[5] = ( (BCID&0x0000007F)<<8 ) ;
667
668 // 3 is gFEX FEX number, set CRC to zero for now
669 int CRC = 0;
670
671
672 ATOB1_dat[6] = 0x000000BC | ( (BCID&0x0000000F)<<8 ) | (3<<12) | (CRC<<23);
673 ATOB2_dat[6] = 0x000000BC | ( (BCID&0x0000000F)<<8 ) | (3<<12) | (CRC<<23);
674 BTOB1_dat[6] = 0x000000BC | ( (BCID&0x0000000F)<<8 ) | (3<<12) | (CRC<<23);
675 BTOB2_dat[6] = 0x000000BC | ( (BCID&0x0000000F)<<8 ) | (3<<12) | (CRC<<23);
676 CTOB1_dat[6] = 0x000000BC | ( (BCID&0x0000000F)<<8 ) | (3<<12) | (CRC<<23);
677 CTOB2_dat[6] = 0x000000BC | ( (BCID&0x0000000F)<<8 ) | (3<<12) | (CRC<<23);
678
679
680
681 return tobs_v;
682
683}
684
685
687
688 // Computes partial sums for FPGA A or B
689 // twrs are the 32 x 12 = 284 gTowers in FPGA A or B
690
691 // number of rows above/below for right partial sum
692 // when sending to the right send values for largest partial sum first, i.e. for column 0
693 typedef std::array<std::array<int, 4>, 4> gTowersNeighbours;
694 gTowersNeighbours NUpDwnR = {{ {{2,3,4,4}}, {{0,2,3,4}}, {{0,0,2,3}}, {{0,0,0,2}} }};
695
696 // number of rows above or below for left partial sum
697 // when sending to the left send values for smallest partial sum first (i.e. for column 8 )
698 gTowersNeighbours NUpDwnL = {{ {{2,0,0,0}}, {{3,2,0,0}}, {{4,3,2,0}}, {{4,4,3,2}} }};
699
700 // Do partial sum for output to right FPGA first
701 for( int irow = 0; irow < FEXAlgoSpaceDefs::ABCrows; irow++ ){
702 for(int rcolumn = 0; rcolumn<FEXAlgoSpaceDefs::n_partial; rcolumn++){
703 // start calculating right partial sums for remote column rcolumn
704 rps[irow][rcolumn] = 0;
705 for(int lcolumn = 0; lcolumn<FEXAlgoSpaceDefs::n_partial; lcolumn++){
706 // add in any energy from towers in this row
707 // no need of modular arithmetic
708 if ( NUpDwnR[rcolumn][lcolumn] > 0 ) {
709 rps[irow][rcolumn] = rps[irow][rcolumn] + twrs[irow][lcolumn+8];
710 }
711 // now add rup1, rup2, rup3, rup4, ldn1, ldn2, ln3, ln4 -- use a loop instead of enumeratin in firmware
712 for( int rowOff = 1 ; rowOff < NUpDwnR[rcolumn][lcolumn]+1; rowOff++){
713 int rowModUp = (irow + rowOff)%32;
714 int rowModDn = (irow - rowOff + 32 )%32;
715 // printf("row %d remote column %d local column %d rowOff %d rowModUp %d rowModDn %d \n",
716 rps[irow][rcolumn] = rps[irow][rcolumn] + twrs[rowModUp][lcolumn+8] + twrs[rowModDn][lcolumn+8];
717 }
718 }
719 }
720 }
721
722 // do partial sum for output to left FPGA second
723 for( int irow = 0; irow < FEXAlgoSpaceDefs::ABCrows; irow++ ){
724 for(int rcolumn = 0; rcolumn<FEXAlgoSpaceDefs::n_partial; rcolumn++){
725 // start calculating right partial sums for remote column rcolumn
726 lps[irow][rcolumn] = 0;
727 for(int lcolumn = 0; lcolumn<FEXAlgoSpaceDefs::n_partial; lcolumn++){
728 // add in any energy from towers in this row
729 // no need of modular arithmetic
730 if ( NUpDwnL[rcolumn][lcolumn] > 0 ) {
731 lps[irow][rcolumn] = lps[irow][rcolumn] + twrs[irow][lcolumn];
732 }
733 // now add rup1, rup2, rup3, rup4, ldn1, ldn2, ln3, ln4 -- use a loop instead of enumeratin in firmware
734 for( int rowOff = 1 ; rowOff < NUpDwnL[rcolumn][lcolumn]+1; rowOff++){
735 int rowModUp = (irow + rowOff)%32;
736 int rowModDn = (irow - rowOff + 32 )%32;
737 lps[irow][rcolumn] = lps[irow][rcolumn] + twrs[rowModUp][lcolumn] + twrs[rowModDn][lcolumn];
738 }
739 }
740 }
741 }
742
743}
744
745
747
748 typedef std::array<std::array<int, 4>, 4> gTowersNeighbours;
749 gTowersNeighbours NUpDwnR = {{ {{2,3,4,4}}, {{0,2,3,4}}, {{0,0,2,3}}, {{0,0,0,2}} }};
750
751 // same as AB for right partial sum
752 // starts from the righ most partial sum -- largest partial sum
753 for( int irow = 0; irow < FEXAlgoSpaceDefs::ABCrows; irow++ ){
754 for(int rcolumn = 0; rcolumn<FEXAlgoSpaceDefs::n_partial; rcolumn++){
755 rps[irow][rcolumn] = 0;
756 for(int lcolumn = 0; lcolumn<FEXAlgoSpaceDefs::n_partial; lcolumn++){
757 if ( NUpDwnR[rcolumn][lcolumn] > 0 ) {
758 rps[irow][rcolumn] = rps[irow][rcolumn] + twrs[irow][lcolumn+2];
759 }
760 for( int rowOff = 1 ; rowOff < NUpDwnR[rcolumn][lcolumn]+1; rowOff++){
761 int rowModUp = (irow + rowOff)%32;
762 int rowModDn = (irow - rowOff + 32 )%32;
763 rps[irow][rcolumn] = rps[irow][rcolumn] + twrs[rowModUp][lcolumn+2] + twrs[rowModDn][lcolumn+2];
764 }
765 }
766 }
767 }
768}
769
770
772
773 typedef std::array<std::array<int, 4>, 4> gTowersNeighbours;
774 gTowersNeighbours NUpDwnL = {{ {{2,0,0,0}}, {{3,2,0,0}}, {{4,3,2,0}}, {{4,4,3,2}} }};
775 // do partial sum for output to left FPGA second
776 for( int irow = 0; irow < FEXAlgoSpaceDefs::ABCrows; irow++ ){
777 for(int rcolumn = 0; rcolumn<FEXAlgoSpaceDefs::n_partial; rcolumn++){
778 // start calculating right partial sums for remote column rcolumn
779 lps[irow][rcolumn] = 0;
780 for(int lcolumn = 0; lcolumn<FEXAlgoSpaceDefs::n_partial; lcolumn++){
781 // add in any energy from towers in this row
782 // no need of modular arithmetic
783 if ( NUpDwnL[rcolumn][lcolumn] > 0 ) {
784 lps[irow][rcolumn] = lps[irow][rcolumn] + twrs[irow][lcolumn];
785 }
786 for( int rowOff = 1 ; rowOff < NUpDwnL[rcolumn][lcolumn]+1; rowOff++){
787 int rowModUp = (irow + rowOff)%32;
788 int rowModDn = (irow - rowOff + 32 )%32;
789 lps[irow][rcolumn] = lps[irow][rcolumn] + twrs[rowModUp][lcolumn] + twrs[rowModDn][lcolumn];
790 }
791 }
792 }
793 }
794}
795
796
797void gFEXJetAlgo::singleHalf(const gTowersType& twrs, gTowersType & FPGAsum) const {
798 // Finds jets in a single FPGA
799 // This version only gives sum in the right or left half of the FPGA
800
801 // Number of up and down FPGAs to add
802 std::array<int, 9> NUpDwn = {{2,3,4,4,4,4,4,3,2}};
803
804 // Evaluate jet centered on each possible eta and phi gTower
805 for( int irow = 0; irow < FEXAlgoSpaceDefs::ABCrows; irow++ ){
806 for(int jcolumn = 0; jcolumn<FEXAlgoSpaceDefs::ABcolumns; jcolumn++){
807 // zero jet sum here
808 FPGAsum[irow][jcolumn] = 0;
809
810 // local column goes from 0 to 8
811 for(int localColumn = 0; localColumn<9; localColumn++){
812 int sumColumn = jcolumn + localColumn - 4;
813
814 // calculate min and max columns for FPGA half
815 int lmin = 0;
816 int lmax = 5;
817
818 if( jcolumn > 5 ) {
819 lmin = 6;
820 lmax = 11;
821 }
822
823 // check if tower to be summed is actually in FPGA
824 if( (sumColumn >= lmin) && (sumColumn <= lmax) ) {
825
826 // sum tower in the same row as jet center
827 FPGAsum[irow][jcolumn] = FPGAsum[irow][jcolumn] + twrs[irow][sumColumn];
828
829 // add rows above and below according NUpDwn
830 // localRow goes from 1 to 2, 1 to 3 or 1 to 4
831 for(int localRow = 1; localRow<=NUpDwn[localColumn]; localRow++){
832 int krowUp = (irow + localRow);
833 if( krowUp > 31 ) krowUp = krowUp - 32;
834 int krowDn = (irow - localRow);
835 if( krowDn < 0 ) krowDn = krowDn + 32;
836 FPGAsum[irow][jcolumn] = FPGAsum[irow][jcolumn] +
837 twrs[krowUp][sumColumn] +
838 twrs[krowDn][sumColumn];
839 }
840 }
841 }
842 }
843 }
844}
845
846
848
849 // Computes partial sums for FPGA A or B
850 // twrs are the 32 x 12 = 284 gTowers in FPGA A or B
851 //
852 // lps: partial sum for left hand side of FPGA
853 // rps: partial sum for right half of the FPGA
854
855 // NOTE rps is available first
856
857 // number of rows above/below for right partial sum
858 // when sending to the right send values for largest partial sum first, i.e. for column 6
859 unsigned int NUpDwnR[4][4] = { {2,3,4,4}, {0,2,3,4}, {0,0,2,3}, {0,0,0,2} };
860
861 // number of rows above or below for left partial sum
862 // when sending to the left send values for smallest partial sumn first (i.e. for column 2 )
863 unsigned int NUpDwnL[4][4] = { {2,0,0,0}, {3,2,0,0}, {4,3,2,0}, {4,4,3,2} };
864
865
866 // Do partial sum for output to right side FPGA first
867 // for the right partial sum this starts from the right-most value first
868 for(unsigned int irow = 0; irow < FEXAlgoSpaceDefs::ABCrows; irow++ ){
869 for(unsigned int rcolumn = 0; rcolumn<FEXAlgoSpaceDefs::n_partial; rcolumn++){
870 // start calculating right partial sums for remote column rcolumn
871 rps[irow][rcolumn] = 0;
872 for(unsigned int lcolumn = 0; lcolumn<FEXAlgoSpaceDefs::n_partial; lcolumn++){
873 // add in any energy from towers in this row
874 // no need of modular arithmetic
875 if ( NUpDwnR[rcolumn][lcolumn] > 0 ) {
876 // this is partial sum for the right half of the FPGA -- columns 2,3,4,5
877 rps[irow][rcolumn] = rps[irow][rcolumn] + twrs[irow][lcolumn+2];
878 }
879 // now add rup1, rup2, rup3, rup4, ldn1, ldn2, ln3, ln4 -- use a loop instead of enumeratin in firmware
880 for(unsigned int rowOff = 1 ; rowOff < NUpDwnR[rcolumn][lcolumn]+1; rowOff++){
881 const int row = static_cast<int>(irow);
882 const int off = static_cast<int>(rowOff);
883 int rowModUp = (row + off)%32;
884 int rowModDn = (row - off + 32 )%32;
885 // this is partial sum for the right half of the FPGA -- columns 2,3,4,5
886 rps[irow][rcolumn] = rps[irow][rcolumn] + twrs[rowModUp][lcolumn+2] + twrs[rowModDn][lcolumn+2];
887 }
888 }
889 }
890 }
891 // do partial sum for output to left half of the FPGA second
892 // for the left partial sum this starts also with the right most column -- which is the smallest sum
893 for(unsigned int irow = 0; irow < FEXAlgoSpaceDefs::ABCrows; irow++ ){
894 for(unsigned int rcolumn = 0; rcolumn<FEXAlgoSpaceDefs::n_partial; rcolumn++){
895 // start calculating right partial sums for remote column rcolumn
896 lps[irow][rcolumn] = 0;
897 for(unsigned int lcolumn = 0; lcolumn<FEXAlgoSpaceDefs::n_partial; lcolumn++){
898 // add in any energy from towers in this row
899 // no need of modular arithmetic
900 if ( NUpDwnL[rcolumn][lcolumn] > 0 ) {
901 lps[irow][rcolumn] = lps[irow][rcolumn] + twrs[irow][lcolumn+6];
902 }
903 // now add rup1, rup2, rup3, rup4, ldn1, ldn2, ln3, ln4 -- use a loop instead of enumeratin in firmware
904 for(unsigned int rowOff = 1 ; rowOff < NUpDwnL[rcolumn][lcolumn]+1; rowOff++){
905 int rowModUp = (irow + rowOff)%32;
906 int rowModDn = (irow - rowOff + 32 )%32;
907 // this is partial sum for the left half of the FPGA -- columns 6,7,8,9
908 lps[irow][rcolumn] = lps[irow][rcolumn] + twrs[rowModUp][lcolumn+6] + twrs[rowModDn][lcolumn+6];
909 }
910 }
911 }
912 }
913}
914
915
917 // add parial sums for left side of FPGA
918 for(unsigned int irow=0;irow<FEXAlgoSpaceDefs::ABCrows;irow++){
919 for(unsigned int ipartial= 0; ipartial <FEXAlgoSpaceDefs::n_partial; ipartial++){
920 jets[irow][2+ipartial] = jets[irow][2+ipartial] + partial[irow][ipartial];
921 }
922 }
923}
924
925
927 // add parial sums for right side of FPGA
928 for(unsigned int irow=0;irow<FEXAlgoSpaceDefs::ABCrows;irow++){
929 for(unsigned int ipartial= 0; ipartial <FEXAlgoSpaceDefs::n_partial; ipartial++){
930 jets[irow][6+ipartial] = jets[irow][6+ipartial] + partial[irow][ipartial];
931 }
932 }
933}
934
935
937 int rows = jets.size();
938 int cols = jets[0].size();
939 for(int irow=0;irow<rows;irow++){
940 for(int icolumn=0;icolumn<cols;icolumn++){
941 jets[irow][icolumn] = jets[irow][icolumn] - puc;
942 }
943 }
944}
945
946
948 for(unsigned int irow = 0; irow < FEXAlgoSpaceDefs::ABCrows; irow++ ){
949 for(unsigned int icolumn =0; icolumn<FEXAlgoSpaceDefs::ABcolumns; icolumn++){
950 if(jets[irow][icolumn] < 0) jets[irow][icolumn] = 0;
951 }
952 }
953}
954
955
956// https://gitlab.cern.ch/atlas-l1calo/gfex/firmware/-/blob/devel/common/jet_finder/HDL/jet_eng.vhd#L538
957void gFEXJetAlgo::SaturateJets( gTowersType & jets, const gTowersType & sat, int fpga ) const {
958 // TODO: Temporary fix to match FW saturation eta-alignment bug.
959 // In firmware, sum69_ovf = delayK(dat_ovf_in, SUM69_OVF_DELAY=16).
960 // dat_ovf_in for col K arrives at clock K; sum69_ovf fires at clock K+16.
961 // sum69o for col J is computed at clock 17+J (LPS_A_DLY+J).
962 // So overflow at col K fires during sum69o for col K+16-17 = K-1.
963 // Engine-boundary columns:
964 // - First column (local col 0 = global 0,6): K-1 falls in the dead/gap
965 // phase of the serialiser → no valid target, skip (icol%6 != 0).
966 // - Last column (local col 5 = global 5,11): the override is gated by
967 // have_seed_d5 in jet_eng.vhd. For extended columns the seed is
968 // computed from regular etower+htower only (the extended xetower
969 // contribution is added after the seed stage via lps_a), so the seed
970 // typically fails and the override is blocked. This is FPGA-specific:
971 // FPGA A (fpga=0): cols 5,11 are regular → override goes through.
972 // FPGA B (fpga=1): col 11 is extended (xetower+xhtower) → skip.
973 // col 5 is regular → override goes through.
974 // FPGA C (fpga=2): all columns extended → skip all last cols.
975 // @see jet_eng.vhd: sum69_ovf forces sum69o to max positive
976 // @see jet_eng.vhd: have_seed_d5 gates delm_din, blocking override
977 // @see delayK.vhd: DLY FFs with combinatorial in/out
978 // Revert this to use icol (not icol-1) when the FW saturation alignment is fixed.
979 for(unsigned int irow = 0; irow < FEXAlgoSpaceDefs::ABCrows; irow++ ){
980 for(unsigned int icol = 0; icol < FEXAlgoSpaceDefs::ABcolumns; icol++){
981 if(sat[irow][icol] && (icol % 6 != 0)){
982 if (fpga == 1 && icol == 11) continue; // FPGA B col 11 extended
983 if (fpga == 2 && icol % 6 == 5) continue; // FPGA C all last cols extended
984 jets[irow][icol - 1] = 0x0003ffff;
985 }
986 }
987 }
988}
989
990void gFEXJetAlgo::SaturateBlocks( gTowersType & gBlkSum, const gTowersType & sat, int fpga ) const {
991 // TODO: Temporary fix to match FW saturation eta-alignment bug.
992 // In firmware, seed_ovf = delayK(dat_ovf_in, SEED_OVF_DELAY=10).
993 // dat_ovf_in for col K arrives at clock K; seed_ovf fires at clock K+10.
994 // et9 for col J is computed at clock 11+J (SEED_DLY+J).
995 // So overflow at col K fires during et9 for col K+10-11 = K-1.
996 // First column (icol%6==0) is always skipped (pulse falls in dead/gap).
997 // Last column skipping is FPGA-specific (see SaturateJets comments):
998 // FPGA A: no last-col skip (cols 5,11 regular).
999 // FPGA B: skip col 11 only (extended).
1000 // FPGA C: skip all last cols (all extended).
1001 // @see jet_eng.vhd lines 849-863: seed_ovf forces et9/seed_out to max
1002 // @see delayK.vhd: DLY FFs with combinatorial in/out
1003 // Revert this to use icol (not icol-1) when the FW saturation alignment is fixed.
1004 for(unsigned int irow = 0; irow < FEXAlgoSpaceDefs::ABCrows; irow++ ){
1005 for(unsigned int icol = 0; icol < FEXAlgoSpaceDefs::ABcolumns; icol++){
1006 if(sat[irow][icol] && (icol % 6 != 0)){
1007 if (fpga == 1 && icol == 11) continue; // FPGA B col 11 extended
1008 if (fpga == 2 && icol % 6 == 5) continue; // FPGA C all last cols extended
1009 gBlkSum[irow][icol - 1] = 0x00000fff;
1010 }
1011 }
1012 }
1013}
1014
1015
1016
1017void gFEXJetAlgo::gBlockAB(const gTowersType & twrs, gTowersType & gBlkSum, gTowersType & hasSeed, int seedThreshold) const {
1018
1019 const int rows = twrs.size();
1020 const int cols = twrs[0].size();
1021 for( int irow = 0; irow < rows; irow++ ){
1022 for(int jcolumn = 0; jcolumn<cols; jcolumn++){
1023 // zero jet sum here
1024 gBlkSum[irow][jcolumn] = 0;
1025 int krowUp = (irow + 1)%32;
1026 int krowDn = (irow - 1 +32)%32;
1027 if( (jcolumn == 0) || (jcolumn == 6) ) {
1028 //left edge case
1029 gBlkSum[irow][jcolumn] =
1030 twrs[irow][jcolumn] + twrs[krowUp][jcolumn] + twrs[krowDn][jcolumn] +
1031 twrs[irow][jcolumn+1] + twrs[krowUp][jcolumn+1] + twrs[krowDn][jcolumn+1];
1032 } else if( (jcolumn == 5) || (jcolumn == 11)) {
1033 // right edge case
1034 gBlkSum[irow][jcolumn] =
1035 twrs[irow][jcolumn] + twrs[krowUp][jcolumn] + twrs[krowDn][jcolumn] +
1036 twrs[irow][jcolumn-1] + twrs[krowUp][jcolumn-1] + twrs[krowDn][jcolumn-1];
1037 } else{
1038 // normal case; jcolumn is not 11 so does not overrun
1039 //coverity[OVERRUN:FALSE]
1040 gBlkSum[irow][jcolumn] =
1041 twrs[irow][jcolumn] + twrs[krowUp][jcolumn] + twrs[krowDn][jcolumn] +
1042 twrs[irow][jcolumn-1] + twrs[krowUp][jcolumn-1] + twrs[krowDn][jcolumn-1] +
1043 twrs[irow][jcolumn+1] + twrs[krowUp][jcolumn+1] + twrs[krowDn][jcolumn+1];
1044 }
1045
1046 if( gBlkSum[irow][jcolumn] > seedThreshold) {
1047 hasSeed[irow][jcolumn] = 1;
1048 } else {
1049 hasSeed[irow][jcolumn] = 0;
1050 }
1051
1052 if ( gBlkSum[irow][jcolumn] < 0 )
1053 gBlkSum[irow][jcolumn] = 0;
1054
1055 // was bits 11+3 downto 3, now is 11 downto 0
1056 if ( gBlkSum[irow][jcolumn] > FEXAlgoSpaceDefs::gBlockMax ) {
1057 gBlkSum[irow][jcolumn] = FEXAlgoSpaceDefs::gBlockMax;
1058 }
1059 }
1060 }
1061}
1062
1063
1064
1065void gFEXJetAlgo::blkOutAB(gTowersType & blocks, std::array<int, 32> & jetOutL, std::array<int, 32> & etaIndL, std::array<int, 32> & jetOutR, std::array<int, 32> & etaIndR) const{
1066
1067 // find maximum in each jet engine for gBlocks (not done in hardware)
1068
1069 //loop over left engines
1070 for(unsigned int ieng=0; ieng<FEXAlgoSpaceDefs::ABCrows; ieng++){
1071 for(unsigned int localEta = 0; localEta<6; localEta++){
1072 if( blocks[ieng][localEta] > jetOutL[ieng] ){
1073 jetOutL[ieng] = blocks[ieng][localEta];
1074 etaIndL[ieng] = localEta;
1075 }
1076 }
1077 }
1078 // loop over right engines
1079 for(unsigned int ieng=0; ieng<FEXAlgoSpaceDefs::ABCrows; ieng++){
1080 for(unsigned int localEta = 0; localEta<6; localEta++){
1081 if( blocks[ieng][localEta+6] > jetOutR[ieng] ) {
1082 jetOutR[ieng] = blocks[ieng][localEta+6];
1083 etaIndR[ieng] = localEta;
1084 }
1085 }
1086 }
1087}
1088
1089
1090void gFEXJetAlgo::gBlockMax2(const gTowersType & gBlkSum, int BjetColumn, int localColumn, std::array<int, 3> & gBlockV, std::array<int, 3> & gBlockEta, std::array<int, 3> & gBlockPhi) const {
1091 // gBlkSum are the 9 or 6 gTower sums
1092 // BjetColumn is the Block Column -- 0 for CN, 1, 2 for A 3, 4 for B and 5 for CP
1093 // gBlockV is the array of values currently 2
1094 // gBlockEta is the eta in global
1095
1096 gTowersJetEngine gBlkSumC;
1097
1098 // copy the correct column
1099 for( int irow = 0; irow<FEXAlgoSpaceDefs::ABCrows; irow++){
1100 for( int icolumn =0; icolumn<FEXAlgoSpaceDefs::ABCcolumnsEng; icolumn++){
1101 gBlkSumC[irow][icolumn] = gBlkSum[irow][icolumn + localColumn*FEXAlgoSpaceDefs::ABCcolumnsEng];
1102 }
1103 }
1104
1105 gBlockMax192(gBlkSumC, gBlockV, gBlockEta, gBlockPhi, 0);
1106
1107 for(int i = -1; i<2; i++){
1108 int iGlobal = i + gBlockPhi[0];
1109 // map row number in to 0-31
1110 if ( iGlobal < 0 ) iGlobal = iGlobal + 32;
1111 if ( iGlobal > 31 ) iGlobal = iGlobal - 32;
1112 // don't do anything outside of the six columsn
1113 for( int j = -1; j<2; j++){
1114 int jGlobal = j + gBlockEta[0];
1115 if( (jGlobal > -1) && (jGlobal < 6) ) {
1116 gBlkSumC[iGlobal][jGlobal] = 0;
1117 }
1118 }
1119 }
1120
1121 gBlockMax192(gBlkSumC, gBlockV, gBlockEta, gBlockPhi, 1);
1122
1123 // need to wait until the end for this!
1124 gBlockEta[0] = gBlockEta[0] + 2 + 6*BjetColumn;
1125 gBlockEta[1] = gBlockEta[1] + 2 + 6*BjetColumn;
1126}
1127
1128
1130 std::array<int, 3> & gBlockVp,
1131 std::array<int, 3> & gBlockEtap,
1132 std::array<int, 3> & gBlockPhip,
1133 int index) const {
1134 // gBLKSum are the sums
1135 // gBlockVp is the returned value for the max block
1136 // gBlockEtap is the eta in the local corrdinate system
1137 // gBlockPhip is the phi (global and local are the same)
1138
1139 int inpv[192]{};
1140 int maxv1[96]{};
1141 int maxv2[48]{};
1142 int maxv3[24]{};
1143 int maxv4[12]{};
1144 int maxv5[6]{};
1145 int maxv6[3]{};
1146
1147 int inpi[192]{};
1148 int maxi1[96]{};
1149 int maxi2[48]{};
1150 int maxi3[24]{};
1151 int maxi4[12]{};
1152 int maxi5[6]{};
1153 int maxi6[3]{};
1154
1155 int maxvall;
1156 int maxiall;
1157
1158
1159 // ABCcolumnsEng is 6 in hardware
1160 int maxv = 0;
1161
1162 for(unsigned int icolumn = 0; icolumn<FEXAlgoSpaceDefs::ABCcolumnsEng; icolumn++){
1163 for(unsigned int irow = 0; irow<FEXAlgoSpaceDefs::ABCrows; irow++){
1164 inpv[irow + icolumn*FEXAlgoSpaceDefs::ABCrows] = gBlkSum[irow][icolumn];
1165 inpi[irow + icolumn*FEXAlgoSpaceDefs::ABCrows] = irow + icolumn*FEXAlgoSpaceDefs::ABCrows;
1166
1167 if( gBlkSum[irow][icolumn] > maxv){
1168 maxv = gBlkSum[irow][icolumn];
1169 }
1170 }
1171 }
1172
1173 for(int i = 0; i<96; i++){
1174 if( inpv[2*i+1] > inpv[2*i] ) {
1175 maxv1[i] = inpv[2*i+1];
1176 maxi1[i] = inpi[2*i+1];
1177 } else {
1178 maxv1[i] = inpv[2*i];
1179 maxi1[i] = inpi[2*i];
1180 }
1181 }
1182
1183 for(int i = 0; i<48; i++){
1184 if( maxv1[2*i+1] > maxv1[2*i] ) {
1185 maxv2[i] = maxv1[2*i+1];
1186 maxi2[i] = maxi1[2*i+1];
1187 } else {
1188 maxv2[i] = maxv1[2*i];
1189 maxi2[i] = maxi1[2*i];
1190 }
1191 }
1192
1193 for(int i = 0; i<24; i++){
1194 if( maxv2[2*i+1] > maxv2[2*i] ) {
1195 maxv3[i] = maxv2[2*i+1];
1196 maxi3[i] = maxi2[2*i+1];
1197 } else {
1198 maxv3[i] = maxv2[2*i];
1199 maxi3[i] = maxi2[2*i];
1200 }
1201 }
1202
1203 for(int i = 0; i<12; i++){
1204 if( maxv3[2*i+1] > maxv3[2*i] ) {
1205 maxv4[i] = maxv3[2*i+1];
1206 maxi4[i] = maxi3[2*i+1];
1207 } else {
1208 maxv4[i] = maxv3[2*i];
1209 maxi4[i] = maxi3[2*i];
1210 }
1211 }
1212
1213 for(int i = 0; i<6; i++){
1214 if( maxv4[2*i+1] > maxv4[2*i] ) {
1215 maxv5[i] = maxv4[2*i+1];
1216 maxi5[i] = maxi4[2*i+1];
1217 } else {
1218 maxv5[i] = maxv4[2*i];
1219 maxi5[i] = maxi4[2*i];
1220 }
1221 }
1222
1223 for(int i = 0; i<3; i++){
1224 if( maxv5[2*i+1] > maxv5[2*i] ) {
1225 maxv6[i] = maxv5[2*i+1];
1226 maxi6[i] = maxi5[2*i+1];
1227 } else {
1228 maxv6[i] = maxv5[2*i];
1229 maxi6[i] = maxi5[2*i];
1230 }
1231 }
1232
1233 if( ( maxv6[1] > maxv6[0] ) && ( maxv6[1] > maxv6[2] ) ) {
1234 maxvall = maxv6[1];
1235 maxiall = maxi6[1];
1236 } else {
1237 if( maxv6[0] > maxv6[2] ){
1238 maxvall = maxv6[0];
1239 maxiall = maxi6[0];
1240 } else {
1241 maxvall = maxv6[2];
1242 maxiall = maxi6[2];
1243 }
1244 }
1245
1246
1247 gBlockVp[index] = maxvall ;
1248 gBlockEtap[index] = maxiall/32;
1249 gBlockPhip[index] = maxiall%32;
1250
1251}
1252
1253
1255 int rows = jets.size();
1256 int cols = jets[0].size();
1257 for( int irow = 0; irow < rows; irow++ ){
1258 for(int jcolumn = 0; jcolumn < cols; jcolumn++){
1259 if( hasSeed[irow][jcolumn] == 0 ) {
1260 // set to negative value as in hardware
1261 // https://gitlab.cern.ch/atlas-l1calo/gfex/firmware/-/blob/devel/common/jet_finder/HDL/jet_eng.vhd#L561
1262 jets[irow][jcolumn] = 0XFFFFF000;
1263 }
1264 }
1265 }
1266}
1267
1268
1270 int ps_upper, int ps_lower, int ps_shift) const {
1271
1272 // See https://gitlab.cern.ch/atlas-l1calo/gfex/firmware/-/blob/kw-dev/jwj_verif/common/jet_finder/HDL/ab_encode.vhd
1273
1274 int rows = partial.size();
1275 int cols = partial[0].size();
1276 // add partial sums
1277 for(int irow=0;irow<rows;irow++){
1278 for(int ipartial= 0; ipartial <cols; ipartial++){
1279 // current behavior
1280 int truncPart = -1;
1281 if( partial[irow][ipartial] > ps_upper ) {
1282 truncPart = ps_upper;
1283 } else if ( partial[irow][ipartial] < ps_lower ) {
1284 truncPart = ps_lower;
1285 } else {
1286 truncPart = partial[irow][ipartial];
1287 }
1288 // change LSB from 200 MeV to 1600 MeV and then back to 200 MeV.
1289 truncPart = (truncPart >> ps_shift );
1290 truncPart = (truncPart << ps_shift );
1291
1292 jets[irow][8+ipartial] = jets[irow][8+ipartial] + truncPart;
1293 }
1294 }
1295}
1296
1298 int ps_upper, int ps_lower, int ps_shift) const {
1299
1300 int rows = partial.size();
1301 int cols = partial[0].size();
1302 // add partial sums
1303 for(int irow=0;irow<rows;irow++){
1304 for(int ipartial= 0; ipartial <cols; ipartial++){
1305 // current behavior
1306 int truncPart = -1;
1307 if( partial[irow][ipartial] > ps_upper ) {
1308 truncPart = ps_upper;
1309 } else if ( partial[irow][ipartial] < ps_lower ) {
1310 truncPart = ps_lower;
1311 } else {
1312 truncPart = partial[irow][ipartial];
1313 }
1314 // change LSB from 200 MeV to 1600 MeV and then back to 200 MeV.
1315 truncPart = (truncPart >> ps_shift );
1316 truncPart = (truncPart << ps_shift );
1317
1318 jets[irow][ipartial] = jets[irow][ipartial] + truncPart;
1319 }
1320 }
1321}
1322
1323
1325 int ps_upper, int ps_lower, int ps_shift ) const {
1326
1327 int rows = partial.size();
1328 int cols = partial[0].size();
1329 // add partial sums
1330 for(int irow=0; irow < rows; irow++){
1331 for(int ipartial= 0; ipartial <cols; ipartial++){
1332 // current behavior
1333 int truncPart = -1;
1334 if( partial[irow][ipartial] > ps_upper ) {
1335 truncPart = ps_upper;
1336 } else if ( partial[irow][ipartial] < ps_lower ) {
1337 truncPart = ps_lower;
1338 } else {
1339 truncPart = partial[irow][ipartial];
1340 }
1341
1342 truncPart = (truncPart >> ps_shift );
1343 truncPart = (truncPart << ps_shift );
1344
1345 jets[irow][ipartial+2] = jets[irow][ipartial+2] + truncPart;
1346
1347 }
1348 }
1349}
1350
1352 int ps_upper, int ps_lower, int ps_shift ) const {
1353
1354 int rows = partial.size();
1355 int cols = partial[0].size();
1356 // add partial sums
1357 for(int irow=0;irow<rows;irow++){
1358 for(int ipartial= 0; ipartial <cols; ipartial++){
1359 int truncPart = -1;
1360 if( partial[irow][ipartial] > ps_upper ) {
1361 truncPart = ps_upper;
1362 } else if ( partial[irow][ipartial] < ps_lower ) {
1363 truncPart = ps_lower;
1364 } else {
1365 truncPart = partial[irow][ipartial];
1366 }
1367
1368 truncPart = (truncPart >> ps_shift );
1369 truncPart = (truncPart << ps_shift );
1370
1371 jets[irow][ipartial+6] = jets[irow][ipartial+6] + truncPart;
1372
1373 }
1374 }
1375}
1376
1377
1378void gFEXJetAlgo::gJetVetoAB( gTowersType &twrs ,int jet_threshold ) const {
1379 int rows = twrs.size();
1380 int cols = twrs[0].size();
1381 for( int irow = 0; irow < rows; irow++ ){
1382 for(int jcolumn = 0; jcolumn<cols; jcolumn++){
1383 if( twrs[irow][jcolumn] < jet_threshold+1 ) {
1384 twrs[irow][jcolumn] = 0;
1385 }
1386 }
1387 }
1388}
1389
1390
1392 std::array<int, 32> & jetOutL, std::array<int, 32> & etaIndL,
1393 std::array<int, 32> & jetOutR, std::array<int, 32> & etaIndR ) const {
1394 // find maximum in each jet engine or either gJets and requires corresponding gBlock be above threhsold
1395 //loop over left engines
1396 for(int ieng=0; ieng<FEXAlgoSpaceDefs::ABCrows; ieng++){
1397 jetOutL[ieng] = 0;
1398 etaIndL[ieng] = 0;
1399 for(int localEta = 0; localEta<6; localEta++){
1400 if( jets[ieng][localEta] > jetOutL[ieng] ){
1401 jetOutL[ieng] = jets[ieng][localEta];
1402 etaIndL[ieng] = localEta;
1403 }
1404 }
1405 // truncation and mapping from 12 to 15 bits
1406 // https://gitlab.cern.ch/atlas-l1calo/gfex/firmware/-/blob/kw-dev/jet_finder_abc/common/jet_finder/HDL/jet_eng.vhd#L561
1407 // https://gitlab.cern.ch/atlas-l1calo/gfex/firmware/-/blob/kw-dev/jet_finder_abc/common/jet_finder/HDL/jet_eng.vhd#L561
1408 // Turncate to 15 bits as in VHDL (Corresponds to 13 TeV)
1409
1410 if( jetOutL[ieng] > FEXAlgoSpaceDefs::gJetMax ) jetOutL[ieng] = FEXAlgoSpaceDefs::gJetMax;
1411 if( jetOutL[ieng] < 0 ) jetOutL[ieng] = 0;
1412 }
1413 // loop over right engines
1414 for(int ieng=0; ieng<FEXAlgoSpaceDefs::ABCrows; ieng++){
1415 jetOutR[ieng] = 0;
1416 etaIndR[ieng] = 0;
1417 for(int localEta = 0; localEta < 6; localEta++){
1418 if( jets[ieng][localEta+6] > jetOutR[ieng] ){
1419 jetOutR[ieng] = jets[ieng][localEta+6];
1420 etaIndR[ieng] = localEta;
1421 }
1422 }
1423 // Turncate to 15 bits as in VHDL (orresponds to 13 TeV)
1424 if( jetOutR[ieng] > FEXAlgoSpaceDefs::gJetMax ) jetOutR[ieng] = FEXAlgoSpaceDefs::gJetMax;
1425 if( jetOutR[ieng] < 0 ) jetOutR[ieng] = 0;
1426 // https://gitlab.cern.ch/atlas-l1calo/gfex/firmware/-/blob/kw-dev/jet_finder_abc/common/jet_finder/HDL/jet_finder_abc.vhd#L1103
1427 // https://gitlab.cern.ch/atlas-l1calo/gfex/firmware/-/blob/kw-dev/jet_finder_abc/common/jet_finder/HDL/jet_finder_abc.vhd#L1119
1428
1429 }
1430
1431}
1432
1433
1434void gFEXJetAlgo::pileUpCalculation(gTowersType &twrs, int rhoThreshold_Max, int inputScale, int &PUCp, int &PUC_JWJ /*, int &PUChres*/) const {
1435 // input are 50 MeV "fine" scale towers (i.e. inputScale = 1)
1436 // to use 200 MeV towers use inputScale = 4
1437 // PUCp output is the pileup correction for 69 towers at 200 MeV energy scale
1438
1439 int rows = twrs.size();
1440 int cols = twrs[0].size();
1441 int pucSum = 0;
1442 int pucSumJWJ = 0;
1443 int nSum = 0;
1444 for(int irow=0; irow<rows; irow++){
1445 for( int icolumn=0; icolumn<cols; icolumn++){
1446 int fineGT = twrs[irow][icolumn]*inputScale;
1447 // set floor and ceiling here (currently 255 and -256 )
1450
1451 if( fineGT < rhoThreshold_Max ) {
1452 pucSum = pucSum + fineGT;
1453 nSum = nSum + 1;
1454 }
1455 }
1456 }
1457 // in firmware this is done with a lookup table see https://gitlab.cern.ch/atlas-l1calo/gfex/firmware/-/blob/devel/common/jet_finder/HDL/inv_lut19.vhd
1458 // See also https://gitlab.cern.ch/atlas-l1calo/gfex/firmware/-/blob/devel/common/jet_finder/HDL/gt_build_all_AB.vhd#L1471
1459
1460 // oneOverN is stored as a 32 bit number in inv_lut19
1461 unsigned int oneOverNTab = 69<<25;
1462 unsigned int JWJoneOverNTab = 69<<25;
1463 if( nSum > 0 && nSum < 385 ) {
1464 oneOverNTab = FEXAlgoSpaceDefs::inv19[nSum];
1465 JWJoneOverNTab = FEXAlgoSpaceDefs::jwjinv19[nSum];
1466 } else {
1467 oneOverNTab = 0;
1468 JWJoneOverNTab = 0;
1469 }
1470
1471 int oneOverN = oneOverNTab;
1472 int JWJoneOverN = JWJoneOverNTab;
1473
1474 // largest value should be 255*2^14 ~ 2^22 -- should easily fit in int -- try expliciting putting in int here.
1475 pucSumJWJ = pucSum * JWJoneOverN;
1476 pucSum = pucSum * oneOverN;
1477
1478 // PUChres = pucSum;
1479
1480 // current system 19 bits in table (69*4096 is max value) sign exten
1481 pucSum = ( pucSum >> 14);
1482 pucSumJWJ = ( pucSumJWJ >> 16);
1483 PUCp = pucSum;
1484 PUC_JWJ = pucSumJWJ;
1485
1486}
1487
1488void gFEXJetAlgo::gJetTOBgen(const std::array<int, FEXAlgoSpaceDefs::ABCrows>& jetOut,
1489 const std::array<int, FEXAlgoSpaceDefs::ABCrows>& etaInd,
1490 int TOBnum, int jetThreshold, std::array<int, FEXAlgoSpaceDefs::gJetTOBfib> & gJetTOBs,
1491 std::array<int, FEXAlgoSpaceDefs::gJetTOBfib> & gJetTOBv,
1492 std::array<int, FEXAlgoSpaceDefs::gJetTOBfib> & gJetTOBeta,
1493 std::array<int, FEXAlgoSpaceDefs::gJetTOBfib> & gJetTOBphi ) const {
1494
1495 int jetOutZS[FEXAlgoSpaceDefs::ABCrows]{};
1496 // apply the tobthreshold to the values
1497 //note that jetThreshold is not a configurable parameter in firmware, it is used to check that jet values are positive
1498 for( int irow =0; irow<FEXAlgoSpaceDefs::ABCrows; irow++){
1499 if( jetOut[irow] > jetThreshold ) {
1500 jetOutZS[irow] = jetOut[irow];
1501 } else {
1502 jetOutZS[irow] = 0 ;
1503 }
1504 }
1505
1506
1507 // offset of TOBs according to official format
1508 int etaOff[FEXAlgoSpaceDefs::gJetTOBfib] = {8,14,20,26,2,32};
1509
1510 // see tob_gen.vhd
1511 int l1mv[16]{};
1512 int l1met[16]{};
1513 int l1mphi[16]{};
1514
1515 int l2mv[8]{};
1516 int l2met[8]{};
1517 int l2mphi[8]{};
1518
1519 int l3mv[4]{};
1520 int l3met[4]{};
1521 int l3mphi[4]{};
1522
1523 int l4mv[2]{};
1524 int l4met[2]{};
1525 int l4mphi[2]{};
1526
1527 for(int i=0; i<16; i++){
1528 if( jetOut[2*i + 1] > jetOutZS[2*i] ){
1529 l1mv[i] = jetOutZS[2*i + 1];
1530 l1met[i] = etaInd[2*i + 1];
1531 l1mphi[i] = 2*i + 1;
1532 } else {
1533 l1mv[i] = jetOutZS[2*i ];
1534 l1met[i] = etaInd[2*i ];
1535 l1mphi[i] = 2*i ;
1536 }
1537 }
1538
1539 for(int i=0; i<8; i++){
1540 if( l1mv[2*i + 1] > l1mv[2*i] ){
1541 l2mv[i] = l1mv[ 2*i + 1 ];
1542 l2met[i] = l1met[ 2*i + 1];
1543 l2mphi[i] = l1mphi[2*i + 1];
1544 } else {
1545 l2mv[i] = l1mv[ 2*i ];
1546 l2met[i] = l1met[ 2*i ];
1547 l2mphi[i] = l1mphi[2*i ];
1548 }
1549 }
1550
1551 for(int i=0; i<4; i++){
1552 if( l2mv[2*i + 1] > l2mv[2*i] ){
1553 l3mv[i] = l2mv[ 2*i + 1];
1554 l3met[i] = l2met[ 2*i + 1];
1555 l3mphi[i] = l2mphi[2*i + 1];
1556 } else {
1557 l3mv[i] = l2mv[2*i ];
1558 l3met[i] = l2met[2*i ];
1559 l3mphi[i] = l2mphi[2*i ];
1560 }
1561 }
1562
1563 for(int i=0; i<2; i++){
1564 if( l3mv[2*i + 1] > l3mv[2*i] ){
1565 l4mv[i] = l3mv[ 2*i + 1];
1566 l4met[i] = l3met[ 2*i + 1];
1567 l4mphi[i] = l3mphi[2*i + 1];
1568 } else {
1569 l4mv[i] = l3mv[2*i ];
1570 l4met[i] = l3met[2*i ];
1571 l4mphi[i] = l3mphi[2*i ];
1572 }
1573 }
1574
1575 if( l4mv[1] > l4mv[0] ){
1576 if(l4mv[1] > jetThreshold){
1577 gJetTOBv[TOBnum] = l4mv[1];
1578 gJetTOBeta[TOBnum] = l4met[1] + etaOff[TOBnum];
1579 gJetTOBphi[TOBnum] = l4mphi[1];
1580 gJetTOBs[TOBnum] = 1;
1581 } else {
1582 gJetTOBv[TOBnum] = l4mv[1];
1583 gJetTOBeta[TOBnum] = l4met[1] + etaOff[TOBnum];
1584 gJetTOBphi[TOBnum] = l4mphi[1];
1585 gJetTOBs[TOBnum] = 0;
1586 }
1587 } else {
1588 if(l4mv[0] > jetThreshold){
1589 gJetTOBv[TOBnum] = l4mv[0];
1590 gJetTOBeta[TOBnum] = l4met[0] + etaOff[TOBnum];
1591 gJetTOBphi[TOBnum] = l4mphi[0];
1592 gJetTOBs[TOBnum] = 1;
1593 } else {
1594 gJetTOBv[TOBnum] = l4mv[0];
1595 gJetTOBeta[TOBnum] = l4met[0] + etaOff[TOBnum];
1596 gJetTOBphi[TOBnum] = l4mphi[0];
1597 gJetTOBs[TOBnum] = 0;
1598 }
1599 }
1600
1601}
1602
1603
1604} // namespace LVL1
#define ATH_CHECK
Evaluate an expression and check for errors.
AthAlgTool(const std::string &type, const std::string &name, const IInterface *parent)
Constructor with parameters:
static constexpr int PS_UPPER_AB
static constexpr std::array< unsigned int, 513 > jwjinv19
static constexpr int gJetMax
static constexpr int ABCrows
static constexpr std::array< unsigned int, 385 > inv19
static constexpr int fineCeiling
static constexpr int BTOBFIB
static constexpr int fineFloor
static constexpr bool ENABLE_INTER_AB
static constexpr int gJetTOBfib
static constexpr bool ENABLE_INTER_ABC
static constexpr int n_partial
static constexpr bool ENABLE_INTER_C
static constexpr int PS_SHIFT_C
static constexpr int gBlockMax
static constexpr int PS_LOWER_C
static constexpr int ABcolumns
static constexpr int ABCcolumnsEng
static constexpr int PS_SHIFT_AB
static constexpr int PS_LOWER_AB
static constexpr int PS_UPPER_C
virtual void gBlockMax2(const gTowersType &gBlkSum, int BjetColumn, int localColumn, std::array< int, 3 > &gBlockV, std::array< int, 3 > &gBlockEta, std::array< int, 3 > &gBlockPhi) const
virtual void gBlockVetoAB(gTowersType &jets, gTowersType &hasSeed) const
virtual void pileUpCorrectionAB(gTowersType &jets, int puc) const
virtual void gBlockMax192(const gTowersJetEngine &gBlkSum, std::array< int, 3 > &gBlockVp, std::array< int, 3 > &gBlockEtap, std::array< int, 3 > &gBlockPhip, int index) const
virtual StatusCode initialize() override
standard Athena-Algorithm method
virtual void addRemoteCPin(gTowersType &jets, const gTowersPartialSums &partial, int ps_upper, int ps_lower, int ps_shift) const
SG::ReadHandleKey< LVL1::gTowerContainer > m_gFEXJetAlgo_gTowerContainerKey
virtual void ZeroNegative(gTowersType &jets) const
virtual void addRemoteLin(gTowersType &jets, const gTowersPartialSums &partial, int ps_upper, int ps_lower, int ps_shift) const
virtual void gJetVetoAB(gTowersType &twrs, int jet_threshold) const
virtual void blkOutAB(gTowersType &blocks, std::array< int, 32 > &jetOutL, std::array< int, 32 > &etaIndL, std::array< int, 32 > &jetOutR, std::array< int, 32 > &etaIndR) const
virtual void InternalPartialAB(const gTowersType &twrs, gTowersPartialSums &lps, gTowersPartialSums &rps) const
virtual void RemotePartialCN(const gTowersJetEngine &twrs, gTowersPartialSums &rps) const
virtual void addRemoteRin(gTowersType &jets, const gTowersPartialSums &partial, int ps_upper, int ps_lower, int ps_shift) const
virtual void SaturateJets(gTowersType &jets, const gTowersType &sat, int fpga) const
virtual void singleHalf(const gTowersType &twrs, gTowersType &FPGAsum) const
virtual void RemotePartialCP(const gTowersJetEngine &twrs, gTowersPartialSums &lps) const
gFEXJetAlgo(const std::string &type, const std::string &name, const IInterface *parent)
Constructors.
virtual void gBlockAB(const gTowersType &twrs, gTowersType &gBlkSum, gTowersType &hasSeed, int seedThreshold) const
virtual void gJetTOBgen(const std::array< int, FEXAlgoSpaceDefs::ABCrows > &jetOut, const std::array< int, FEXAlgoSpaceDefs::ABCrows > &etaInd, int TOBnum, int jetThreshold, std::array< int, FEXAlgoSpaceDefs::gJetTOBfib > &gJetTOBs, std::array< int, FEXAlgoSpaceDefs::gJetTOBfib > &gJetTOBv, std::array< int, FEXAlgoSpaceDefs::gJetTOBfib > &gJetTOBeta, std::array< int, FEXAlgoSpaceDefs::gJetTOBfib > &gJetTOBphi) const
virtual void addRemoteCNin(gTowersType &jets, const gTowersPartialSums &partial, int ps_upper, int ps_lower, int ps_shift) const
virtual void SaturateBlocks(gTowersType &gBlkSum, const gTowersType &sat, int fpga) const
virtual std::vector< std::unique_ptr< gFEXJetTOB > > largeRfinder(const gTowersType &Atwr, const gTowersType &Btwr, const gTowersType &CNtwr, const gTowersType &Asat, const gTowersType &Bsat, const gTowersType &CNsat, int pucA, int pucB, int pucC, int gLJ_seedThrA, int gLJ_seedThrB, int gLJ_seedThrC, int gJ_ptMinToTopoCounts1, int gJ_ptMinToTopoCounts2, int jetThreshold, int gLJ_ptMinToTopoCounts1, int gLJ_ptMinToTopoCounts2, std::array< uint32_t, 7 > &ATOB1_dat, std::array< uint32_t, 7 > &ATOB2_dat, std::array< uint32_t, 7 > &BTOB1_dat, std::array< uint32_t, 7 > &BTOB2_dat, std::array< uint32_t, 7 > &CTOB1_dat, std::array< uint32_t, 7 > &CTOB2_dat) const override
virtual void addInternalRin(gTowersType &jets, gTowersPartialSums &partial) const
virtual void jetOutAB(const gTowersType &jets, std::array< int, 32 > &jetOutL, std::array< int, 32 > &etaIndL, std::array< int, 32 > &jetOutR, std::array< int, 32 > &etaIndR) const
virtual void pileUpCalculation(gTowersType &twrs, int rhoThreshold_Max, int inputScale, int &PUCp, int &PUC_JWJ) const override
virtual void addInternalLin(gTowersType &jets, gTowersPartialSums &partial) const
virtual void RemotePartialAB(const gTowersType &twrs, gTowersPartialSums &lps, gTowersPartialSums &rps) const
eFexTowerBuilder creates xAOD::eFexTowerContainer from supercells (LATOME) and triggerTowers (TREX) i...
std::array< std::array< int, 6 >, 32 > gTowersJetEngine
Definition gFEXJetAlgo.h:28
std::array< std::array< int, 12 >, 32 > gTowersType
Definition IgFEXFPGA.h:25
std::array< std::array< int, 4 >, 32 > gTowersPartialSums
Definition gFEXJetAlgo.h:29
Definition index.py:1