ATLAS Offline Software
Loading...
Searching...
No Matches
CaloClusterMomentsMaker.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//-----------------------------------------------------------------------
6// File and Version Information:
7//
8// Description: see CaloClusterMomentsMaker.h
9//
10// Environment:
11// Software developed for the ATLAS Detector at CERN LHC
12//
13// Author List:
14// Sven Menke
15// Peter Loch
16//
17//-----------------------------------------------------------------------
18
20#include "CaloEvent/CaloCell.h"
21#include "CaloEvent/CaloClusterContainer.h"
22#include "CaloEvent/CaloCluster.h"
29
32
33#include "CLHEP/Units/SystemOfUnits.h"
34#include "CxxUtils/prefetch.h"
36#include <Eigen/Dense>
37#include <cmath>
38#include <cstdint>
39#include <iterator>
40#include <limits>
41#include <sstream>
42
43#include <map>
44#include <vector>
45#include <tuple>
46#include <string>
47#include <cstdio>
48#include <cmath>
49
50using CLHEP::deg;
51using CLHEP::cm;
53
54// Known moments
55namespace {
56 // name -> enum translator
57 const std::map<std::string,xAOD::CaloCluster::MomentType> momentNameToEnumMap = {
58 { "AVG_LAR_Q", AVG_LAR_Q },
59 { "AVG_TILE_Q", AVG_TILE_Q },
60 { "BADLARQ_FRAC", BADLARQ_FRAC },
61 { "BAD_CELLS_CORR_E", BAD_CELLS_CORR_E },
62 { "CELL_SIGNIFICANCE", CELL_SIGNIFICANCE },
63 { "CELL_SIG_SAMPLING", CELL_SIG_SAMPLING },
64 { "CENTER_LAMBDA", CENTER_LAMBDA },
65 { "CENTER_MAG", CENTER_MAG },
66 { "CENTER_X", CENTER_X },
67 { "CENTER_Y", CENTER_Y },
68 { "CENTER_Z", CENTER_Z },
69 { "DELTA_ALPHA", DELTA_ALPHA },
70 { "DELTA_PHI", DELTA_PHI },
71 { "DELTA_THETA", DELTA_THETA },
72 { "ENG_BAD_CELLS", ENG_BAD_CELLS },
73 { "ENG_BAD_HV_CELLS", ENG_BAD_HV_CELLS },
74 { "ENG_FRAC_CORE", ENG_FRAC_CORE },
75 { "ENG_FRAC_EM", ENG_FRAC_EM },
76 { "ENG_FRAC_MAX", ENG_FRAC_MAX },
77 { "ENG_POS", ENG_POS },
78 { "FIRST_ENG_DENS", FIRST_ENG_DENS },
79 { "FIRST_ETA", FIRST_ETA },
80 { "FIRST_PHI", FIRST_PHI },
81 { "ISOLATION", ISOLATION },
82 { "LATERAL", LATERAL },
83 { "LONGITUDINAL", LONGITUDINAL },
84 { "MASS", MASS },
85 { "N_BAD_CELLS", N_BAD_CELLS },
86 { "N_BAD_HV_CELLS", N_BAD_HV_CELLS },
87 { "N_BAD_CELLS_CORR", N_BAD_CELLS_CORR },
88 { "PTD", PTD },
89 { "SECOND_ENG_DENS", SECOND_ENG_DENS },
90 { "SECOND_LAMBDA", SECOND_LAMBDA },
91 { "SECOND_R", SECOND_R },
92 { "SECOND_TIME", SECOND_TIME },
93 { "SIGNIFICANCE", SIGNIFICANCE },
94 { "EM_PROBABILITY", EM_PROBABILITY },
95 { "NCELL_SAMPLING", NCELL_SAMPLING }
96 };
97 // enum -> name translator
98 const std::map<xAOD::CaloCluster::MomentType,std::string> momentEnumToNameMap = {
99 { AVG_LAR_Q, "AVG_LAR_Q" },
100 { AVG_TILE_Q, "AVG_TILE_Q" },
101 { BADLARQ_FRAC, "BADLARQ_FRAC" },
102 { BAD_CELLS_CORR_E, "BAD_CELLS_CORR_E" },
103 { CELL_SIGNIFICANCE, "CELL_SIGNIFICANCE"},
104 { CELL_SIG_SAMPLING, "CELL_SIG_SAMPLING"},
105 { CENTER_LAMBDA, "CENTER_LAMBDA" },
106 { CENTER_MAG, "CENTER_MAG" },
107 { CENTER_X, "CENTER_X" },
108 { CENTER_Y, "CENTER_Y" },
109 { CENTER_Z, "CENTER_Z" },
110 { DELTA_ALPHA, "DELTA_ALPHA" },
111 { DELTA_PHI, "DELTA_PHI" },
112 { DELTA_THETA, "DELTA_THETA" },
113 { ENG_BAD_CELLS, "ENG_BAD_CELLS" },
114 { ENG_BAD_HV_CELLS, "ENG_BAD_HV_CELLS" },
115 { ENG_FRAC_CORE, "ENG_FRAC_CORE" },
116 { ENG_FRAC_EM, "ENG_FRAC_EM" },
117 { ENG_FRAC_MAX, "ENG_FRAC_MAX" },
118 { ENG_POS, "ENG_POS" },
119 { FIRST_ENG_DENS, "FIRST_ENG_DENS" },
120 { FIRST_ETA, "FIRST_ETA" },
121 { FIRST_PHI, "FIRST_PHI" },
122 { ISOLATION, "ISOLATION" },
123 { LATERAL, "LATERAL" },
124 { LONGITUDINAL, "LONGITUDINAL" },
125 { MASS, "MASS" },
126 { N_BAD_CELLS, "N_BAD_CELLS" },
127 { N_BAD_HV_CELLS, "N_BAD_HV_CELLS" },
128 { N_BAD_CELLS_CORR, "N_BAD_CELLS_CORR" },
129 { PTD, "PTD" },
130 { SECOND_ENG_DENS, "SECOND_ENG_DENS" },
131 { SECOND_LAMBDA, "SECOND_LAMBDA" },
132 { SECOND_R, "SECOND_R" },
133 { SECOND_TIME, "SECOND_TIME" },
134 { SIGNIFICANCE, "SIGNIFICANCE" },
135 { EM_PROBABILITY, "EM_PROBABILITY" },
136 { NCELL_SAMPLING, "NCELL_SAMPLING" }
137 };
138}
139
140//###############################################################################
141
143 const std::string& name,
144 const IInterface* parent)
145 : AthAlgTool(type, name, parent),
146 m_calo_id(nullptr),
147 m_maxAxisAngle(20*deg),
148 m_minRLateral(4*cm),
150 m_minBadLArQuality(4000),
154 m_twoGaussianNoise(false),
155 m_caloDepthTool("CaloDepthTool",this),
156 m_larHVFraction("LArHVFraction",this),
157 m_absOpt(false)
158{
159 declareInterface<CaloClusterCollectionProcessor> (this);
160 // Name(s) of Moments to calculate
161 declareProperty("MomentsNames",m_momentsNames);
162
163 // Maximum allowed angle between shower axis and the vector pointing
164 // to the shower center from the IP in degrees. This property is needed
165 // to protect against cases where all significant cells are in one sampling
166 // and the shower axis can thus not be defined.
167 declareProperty("MaxAxisAngle",m_maxAxisAngle);
168 declareProperty("MinRLateral",m_minRLateral);
169 declareProperty("MinLLongitudinal",m_minLLongitudinal);
170 declareProperty("MinBadLArQuality",m_minBadLArQuality);
171 // Use 2-gaussian noise for Tile
172 declareProperty("TwoGaussianNoise",m_twoGaussianNoise);
173 declareProperty("LArHVFraction",m_larHVFraction,"Tool Handle for LArHVFraction");
174 // Not used anymore (with xAOD), but required when configured from COOL.
175 declareProperty("AODMomentsNames",m_momentsNamesAOD);
176 // Use weighting of neg. clusters option?
177 declareProperty("WeightingOfNegClusters", m_absOpt);
178 // Set eta boundary for transition from outer to inner wheel in EME2
179 declareProperty("EMECAbsEtaWheelTransition",m_etaInnerWheel);
180}
181
182//###############################################################################
183
185{
186 xAOD::CaloCluster dummyCluster;
187
188 // loop list of requested moments
189 std::string::size_type nstr(0); int nmom(0);
190 for (const auto& mom : m_momentsNames) {
191 ATH_MSG_DEBUG("Moment " << mom << " requested");
192 // check if moment is known (enumerator available)
193 auto fmap(momentNameToEnumMap.find(mom));
194 if (fmap != momentNameToEnumMap.end()) {
195 // valid moment found
196 nstr = std::max(nstr, mom.length());
197 ++nmom;
198 if (fmap->second == SECOND_TIME) {
199 // special flag for second moment of cell times - this moment is not
200 // calculated in this tool! Do not add to internal (!) valid moments
201 // list. Its value is available from xAOD::CaloCluster::secondTime()!
202 m_secondTime = true;
203
204 // Make sure the variable used for the moment is declared
205 // to the auxiliary variable registry. Otherwise, if we don't
206 // set the moment for the first event (perhaps because there
207 // are no clusters), then we can get warnings from AuxSelection.
208 (void)dummyCluster.getMomentValue (fmap->second);
209 } else if (fmap->second == NCELL_SAMPLING) {
210 // flag indicates if number of cells in a sampling should be counted.
211 // This is a vector of integers counts that is filled in this tool but
212 // does not need any post-processing (e.g. normalization). It is not
213 // added to the valid moments list for this reason.
214 ATH_MSG_DEBUG("moment " << fmap->first << " found");
215 m_nCellsPerSampling = true;
217
218 // Make sure the variable used for the moment is declared
219 // to the auxiliary variable registry.
220 (void)dummyCluster.retrieveMoment (fmap->second, cellsdum);
221 } else if (fmap->second == EM_PROBABILITY) {
223 << " not calculated in this tool - misconfiguration?");
224 } else {
225 // Make sure the variable used for the moment is declared
226 // to the auxiliary variable registry.
227 (void)dummyCluster.getMomentValue (fmap->second);
228
229 // all other valid moments
230 m_validMoments.push_back(fmap->second);
231 // flag some special requests
232 switch (fmap->second) {
233 case SIGNIFICANCE:
234 case CELL_SIGNIFICANCE:
236 break;
237 case ISOLATION:
239 break;
240 case ENG_BAD_HV_CELLS:
242 break;
243 default:
244 break;
245 } // set special processing flags
246 } // moment calculated with this tool
247 } else {
248 ATH_MSG_ERROR("Moment name " << mom << " not known; known moments are:");
249 char buffer[128];
250 std::string::size_type lstr(nstr);
251 // determine field size
252 for (const auto& fmom : momentNameToEnumMap) {
253 lstr = std::max(lstr, fmom.first.length());
254 }
255 // print available moments
256 for (const auto& fmom : momentNameToEnumMap) {
257 sprintf(buffer, "moment name: %-*.*s - enumerator: %i", (int)lstr,
258 (int)lstr, fmom.first.c_str(), (int)fmom.second);
259 ATH_MSG_ERROR(buffer);
260 }
261 auto fmom(momentNameToEnumMap.find("SECOND_TIME"));
262 sprintf(buffer, "moment name: %-*.*s - enumerator: %i", (int)nstr,
263 (int)nstr, fmom->first.c_str(), (int)fmom->second);
264 ATH_MSG_ERROR(buffer);
265 fmom = momentNameToEnumMap.find("NCELL_SAMPLING");
266 sprintf(buffer, "moment name: %-*.*s - enumerator: %i", (int)nstr,
267 (int)nstr, fmom->first.c_str(), (int)fmom->second);
268 ATH_MSG_ERROR(buffer);
269 return StatusCode::FAILURE;
270 } // found unknown moment name
271 } // loop configured moment names
272
273 // sort and remove duplicates
275 m_validMoments.erase(
277 m_validMoments.end());
278
279 // print configured moments
280 ATH_MSG_INFO("Construct and save " << nmom << " cluster moments: ");
281 char buffer[128];
282 for (auto menum : m_validMoments) {
283 sprintf(buffer, "moment name: %-*.*s - enumerator: %i", (int)nstr,
284 (int)nstr, momentEnumToNameMap.at(menum).c_str(), (int)menum);
285 ATH_MSG_INFO(buffer);
286 }
287 if (m_secondTime) {
288 auto fmom(momentNameToEnumMap.find("SECOND_TIME"));
289 sprintf(buffer, "moment name: %-*.*s - enumerator: %i (save only)",
290 (int)nstr, (int)nstr, fmom->first.c_str(), (int)fmom->second);
291 ATH_MSG_INFO(buffer);
292 }
294 auto fmom(momentNameToEnumMap.find("NCELL_SAMPLING"));
295 sprintf(buffer, "moment name: %-*.*s - enumerator: %i", (int)nstr,
296 (int)nstr, fmom->first.c_str(), (int)fmom->second);
297 ATH_MSG_INFO(buffer);
298 }
299
300 // retrieve CaloCell ID server
301 CHECK(detStore()->retrieve(m_calo_id,"CaloCell_ID"));
302
303 // retrieve the calo depth tool
304 CHECK(m_caloDepthTool.retrieve());
305 ATH_CHECK(m_caloMgrKey.initialize());
306
307 // retrieve specific servers and tools for selected processes
309 if (m_calculateLArHVFraction) { ATH_CHECK(m_larHVFraction.retrieve()); } else { m_larHVFraction.disable(); }
310
311 return StatusCode::SUCCESS;
312}
313
315{
316 return StatusCode::SUCCESS;
317}
318
319//#############################################################################
320
322
323
324struct cellinfo {
325 double x{};
326 double y{};
327 double z{};
328 double energy{};
329 double eta{};
330 double phi{};
331 double r{};
332 double lambda{};
333 double volume{};
335 unsigned int identifier{};
336};
337
338} // namespace CaloClusterMomentsMaker_detail
339
340StatusCode
341CaloClusterMomentsMaker::execute(const EventContext& ctx,
342 xAOD::CaloClusterContainer *theClusColl)
343 const
344{
345 ATH_MSG_DEBUG("Executing " << name());
346
347 // Maps cell IdentifierHash to cluster index in cluster collection.
348 // Only used when cluster isolation moment is calculated.
349 using clusterIdx_t = std::uint16_t;
350 typedef std::pair<clusterIdx_t, clusterIdx_t> clusterPair_t;
351 std::vector<clusterPair_t> clusterIdx;
352 const clusterIdx_t noCluster = std::numeric_limits<clusterIdx_t>::max();
353
354 const CaloNoise* noise=nullptr;
357 noise=*noiseHdl;
358 }
359
361 const CaloDetDescrManager* caloDDMgr = *caloMgrHandle;
362 // Counters for number of empty and non-empty neighbor cells per sampling
363 // layer Only used when cluster isolation moment is calculated.
364 int nbEmpty[CaloCell_ID::Unknown];
365 int nbNonEmpty[CaloCell_ID::Unknown];
366
367 // prepare stuff from entire collection in case isolation moment
368 // should be calculated
369
370 if ( m_calculateIsolation ) {
371
372 if (theClusColl->size() >= noCluster) {
373 msg(MSG::ERROR) << "Too many clusters" << endmsg;
374 return StatusCode::FAILURE;
375 }
376
377 // initialize with "empty" values
378 clusterIdx.resize(m_calo_id->calo_cell_hash_max(),
379 clusterPair_t(noCluster, noCluster));
380
381 int iClus = 0;
382 for (xAOD::CaloCluster* theCluster : *theClusColl) {
383 // loop over all cell members and fill cell vector for used cells
384 xAOD::CaloCluster::cell_iterator cellIter = theCluster->cell_begin();
385 xAOD::CaloCluster::cell_iterator cellIterEnd = theCluster->cell_end();
386 for(; cellIter != cellIterEnd; cellIter++ ){
387 CxxUtils::prefetchNext(cellIter, cellIterEnd);
388 const CaloCell* pCell = *cellIter;
389
390 Identifier myId = pCell->ID();
391 IdentifierHash myHashId = m_calo_id->calo_cell_hash(myId);
392 if ( clusterIdx[(unsigned int)myHashId].first != noCluster) {
393 // check weight and assign to current cluster if weight is > 0.5
394 double weight = cellIter.weight();
395 if ( weight > 0.5 )
396 clusterIdx[(unsigned int)myHashId].first = iClus;
397 }
398 else {
399 clusterIdx[(unsigned int)myHashId].first = iClus;
400 }
401 }
402 ++iClus;
403 }
404 }
405
406 // Move allocation of temporary arrays outside the cluster loop.
407 // That way, we don't need to delete and reallocate them
408 // each time through the loop.
409
410 std::vector<CaloClusterMomentsMaker_detail::cellinfo> cellinfo;
411 std::vector<double> maxSampE (CaloCell_ID::Unknown);
412 std::vector<double> myMoments(m_validMoments.size(),0);
413 std::vector<double> myNorms(m_validMoments.size(),0);
414 std::vector<std::tuple<int,int> > nCellsSamp; nCellsSamp.reserve(CaloCell_ID::Unknown);
415 std::vector<IdentifierHash> theNeighbors;
416 // loop over individual clusters
417 xAOD::CaloClusterContainer::iterator clusIter = theClusColl->begin();
418 xAOD::CaloClusterContainer::iterator clusIterEnd = theClusColl->end();
419 int iClus = 0;
420 for( ;clusIter!=clusIterEnd;++clusIter,++iClus) {
421 xAOD::CaloCluster * theCluster = *clusIter;
422
423 double w(0),xc(0),yc(0),zc(0),mx(0),my(0),mz(0),mass(0);
424 double eBad(0),ebad_dac(0),ePos(0),eBadLArQ(0),sumSig2(0),maxAbsSig(0);
425 double eLAr2(0),eLAr2Q(0);
426 double eTile2(0),eTile2Q(0);
427 double eBadLArHV(0);
428 int nbad(0),nbad_dac(0),nBadLArHV(0);
429 unsigned int i,nSigSampl(0);
430 unsigned int theNumOfCells = theCluster->size();
431
432 // these two are needed for the LATERAL moment
433 int iCellMax(-1);
434 int iCellScndMax(-1);
435
436 cellinfo.clear();
437 if (cellinfo.capacity() == 0)
438 cellinfo.reserve (theNumOfCells*2);
439
440 for(i=0;i<(unsigned int)CaloCell_ID::Unknown;i++)
441 maxSampE[i] = 0;
442
443 if ( !m_momentsNames.empty() ) {
444 std::fill (myMoments.begin(), myMoments.end(), 0);
445 std::fill (myNorms.begin(), myNorms.end(), 0);
446 if ( m_calculateIsolation ) {
447 std::fill_n(nbNonEmpty, CaloCell_ID::Unknown, 0);
448 std::fill_n(nbEmpty, CaloCell_ID::Unknown, 0);
449 }
450
451 // loop over all cell members and calculate the center of mass
452 xAOD::CaloCluster::cell_iterator cellIter = theCluster->cell_begin();
453 xAOD::CaloCluster::cell_iterator cellIterEnd = theCluster->cell_end();
454 for(; cellIter != cellIterEnd; cellIter++ ){
455 CaloPrefetch::nextDDE(cellIter, cellIterEnd);
456
457 const CaloCell* pCell = (*cellIter);
458 Identifier myId = pCell->ID();
459 const CaloDetDescrElement* myCDDE = pCell->caloDDE();
460 double ene = pCell->e();
461 if(m_absOpt) ene = std::abs(ene);
462 double weight = cellIter.weight();//theCluster->getCellWeight(cellIter);
463 if ( pCell->badcell() ) {
464 eBad += ene*weight;
465 nbad++;
466 if(ene!=0){
467 ebad_dac+=ene*weight;
468 nbad_dac++;
469 }
470 }
471 else {
472 if ( myCDDE && ! (myCDDE->is_tile())
473 && ((pCell->provenance() & 0x2000) == 0x2000)
474 && !((pCell->provenance() & 0x0800) == 0x0800)) {
475 if ( pCell->quality() > m_minBadLArQuality ) {
476 eBadLArQ += ene*weight;
477 }
478 eLAr2 += ene*weight*ene*weight;
479 eLAr2Q += ene*weight*ene*weight*pCell->quality();
480 }
481 if ( myCDDE && myCDDE->is_tile() ) {
482 uint16_t tq = pCell->quality();
483 uint8_t tq1 = (0xFF00&tq)>>8; // quality in channel 1
484 uint8_t tq2 = (0xFF&tq); // quality in channel 2
485 // reject cells with either 0xFF00 or 0xFF
486 if ( ((tq1&0xFF) != 0xFF) && ((tq2&0xFF) != 0xFF) ) {
487 eTile2 += ene*weight*ene*weight;
488 // take the worse of both qualities (one might be 0 in
489 // 1-channel cases)
490 eTile2Q += ene*weight*ene*weight*(tq1>tq2?tq1:tq2);
491 }
492 }
493 }
494 if ( ene > 0 ) {
495 ePos += ene*weight;
496 }
497
499 // Avoid a spurious FPE from clang.
501
502 const float sigma = m_twoGaussianNoise ?\
503 noise->getEffectiveSigma(pCell->ID(),pCell->gain(),pCell->energy()) : \
504 noise->getNoise(pCell->ID(),pCell->gain());
505
506 sumSig2 += sigma*sigma;
507 // use geometry weighted energy of cell for leading cell significance
508 double Sig = (sigma>0?ene*weight/sigma:0);
509 if (m_useGPUCriteria) {
510 unsigned int thisSampl = myCDDE->getSampling();
511 if ( ( std::abs(Sig) > std::abs(maxAbsSig) ) ||
512 ( std::abs(Sig) == std::abs(maxAbsSig) && thisSampl > nSigSampl ) ||
513 ( std::abs(Sig) == std::abs(maxAbsSig) && thisSampl == nSigSampl && Sig > maxAbsSig ) ) {
514 maxAbsSig = Sig;
515 nSigSampl = thisSampl;
516 }
517
518 }
519 else {
520 if ( std::abs(Sig) > std::abs(maxAbsSig) ) {
521 maxAbsSig = Sig;
522 nSigSampl = myCDDE->getSampling();
523 }
524 }
525 }
526 if ( m_calculateIsolation ) {
527 // get all 2D Neighbours if the cell is not inside another cluster with
528 // larger weight
529
530 IdentifierHash myHashId = m_calo_id->calo_cell_hash(myId);
531 if ( clusterIdx[myHashId].first == iClus ) {
532 theNeighbors.clear();
533 m_calo_id->get_neighbours(myHashId, LArNeighbours::all2D, theNeighbors);
534 for (const auto& nhash: theNeighbors) {
535 clusterPair_t& idx = clusterIdx[nhash];
536
537 // only need to look at each cell once per cluster
538 if ( idx.second == iClus ) continue;
539 idx.second = iClus;
540
541 if ( idx.first == noCluster ) {
542 ++ nbEmpty[m_calo_id->calo_sample(nhash)];
543 } else if ( idx.first != iClus ) {
544 ++ nbNonEmpty[m_calo_id->calo_sample(nhash)];
545 }
546
547 }
548 }
549 }
550
551 if ( myCDDE != nullptr ) {
552 if ( m_nCellsPerSampling ) {
553 CaloCell_ID::CaloSample sam = myCDDE->getSampling();
554 size_t idx((size_t)sam);
555 if ( idx >= nCellsSamp.size() ) { nCellsSamp.resize(idx+1, { 0, 0 } ); }
556 std::get<0>(nCellsSamp[idx])++;
557 // special count for inner wheel cells in EME2
558 if ( sam == CaloCell_ID::EME2 && std::abs(myCDDE->eta()) > m_etaInnerWheel ) { std::get<1>(nCellsSamp[idx])++; }
559 }
560 if ( ene > 0. && weight > 0) {
561 // get all geometric information needed ...
562 cellinfo.push_back (CaloClusterMomentsMaker_detail::cellinfo {
563 .x = myCDDE->x(),
564 .y = myCDDE->y(),
565 .z = myCDDE->z(),
566 .energy = ene*weight,
567 .eta = myCDDE->eta(),
568 .phi = myCDDE->phi(),
569 .r = 0, // These two are filled in later.
570 .lambda = 0,
571 .volume = myCDDE->volume(),
572 .sample = myCDDE->getSampling(),
573 .identifier = cellIter.index()
574 //Using the index instead of the hash ID as disambiguation criterion
575 //is relevant for the GPU now that we no longer assume the two are the same,
576 //and this gives us better performance.
577 });
578
579 CaloClusterMomentsMaker_detail::cellinfo& ci = cellinfo.back();
580
581 if ( ci.energy > maxSampE[(unsigned int)ci.sample] )
582 maxSampE[(unsigned int)ci.sample] = ci.energy;
583
584 if (m_useGPUCriteria) {
585 if (iCellMax < 0 ||
586 ci.energy > cellinfo[iCellMax].energy ||
587 (ci.energy == cellinfo[iCellMax].energy && ci.identifier > cellinfo[iCellMax].identifier) ) {
588 iCellScndMax = iCellMax;
589 iCellMax = cellinfo.size()-1;
590 }
591 else if (iCellScndMax < 0 ||
592 ci.energy > cellinfo[iCellScndMax].energy ||
593 (ci.energy == cellinfo[iCellScndMax].energy && ci.identifier > cellinfo[iCellScndMax].identifier) )
594 {
595 iCellScndMax = cellinfo.size()-1;
596 }
597 }
598 else {
599 if (iCellMax < 0 || ci.energy > cellinfo[iCellMax].energy ) {
600 iCellScndMax = iCellMax;
601 iCellMax = cellinfo.size()-1;
602 }
603 else if (iCellScndMax < 0 ||
604 ci.energy > cellinfo[iCellScndMax].energy )
605 {
606 iCellScndMax = cellinfo.size()-1;
607 }
608 }
609
610 xc += ci.energy*ci.x;
611 yc += ci.energy*ci.y;
612 zc += ci.energy*ci.z;
613
614 double dir = ci.x*ci.x+ci.y*ci.y+ci.z*ci.z;
615
616 if ( dir > 0) {
617 dir = sqrt(dir);
618 dir = 1./dir;
619 }
620 mx += ci.energy*ci.x*dir;
621 my += ci.energy*ci.y*dir;
622 mz += ci.energy*ci.z*dir;
623
624 w += ci.energy;
625 } // cell has E>0 and weight != 0
626 } // cell has valid DDE
627 } //end of loop over all cells
629 const auto hvFrac=m_larHVFraction->getLArHVFrac(theCluster->getCellLinks(),ctx);
630 eBadLArHV= hvFrac.first;
631 nBadLArHV=hvFrac.second;
632 }
633
634 if ( w > 0 || (m_useGPUCriteria && w >= 0) ) {
635 mass = w*w - mx*mx - my*my - mz*mz;
636 if ( mass > 0) {
637 mass = sqrt(mass);
638 }
639 else {
640 // make mass negative if m^2 was negative
641 mass = -sqrt(-mass);
642 }
643
644 if (w == 0) {
645 w = 1.0;
646 }
647
648 xc/=w;
649 yc/=w;
650 zc/=w;
651 Amg::Vector3D showerCenter(xc,yc,zc);
652 w=0;
653
654
655 //log << MSG::WARNING << "Found bad cells " << xbad_dac << " " << ybad_dac << " " << zbad_dac << " " << ebad_dac << endmsg;
656 //log << MSG::WARNING << "Found Cluster " << xbad_dac << " " << ybad_dac << " " << zbad_dac << " " << endmsg;
657 // shower axis is just the vector pointing from the IP to the shower center
658 // in case there are less than 3 cells in the cluster
659
660 Amg::Vector3D showerAxis(xc,yc,zc);
661 Amg::setMag(showerAxis,1.0);
662
663 // otherwise the principal direction with the largest absolute
664 // eigenvalue will be used unless it's angle w.r.t. the vector pointing
665 // from the IP to the shower center is larger than allowed by the
666 // property m_maxAxisAngle
667
668 double angle(0),deltaPhi(0),deltaTheta(0);
669 if ( cellinfo.size() > 2 ) {
670 Eigen::Matrix3d C=Eigen::Matrix3d::Zero();
671 for(const CaloClusterMomentsMaker_detail::cellinfo& ci : cellinfo) {
672 const double e2 = ci.energy * ci.energy;
673
674 C(0,0) += e2*(ci.x-xc)*(ci.x-xc);
675 C(1,0) += e2*(ci.x-xc)*(ci.y-yc);
676 C(2,0) += e2*(ci.x-xc)*(ci.z-zc);
677
678 C(1,1) += e2*(ci.y-yc)*(ci.y-yc);
679 C(2,1) += e2*(ci.y-yc)*(ci.z-zc);
680
681 C(2,2) += e2*(ci.z-zc)*(ci.z-zc);
682 w += e2;
683 }
684 C/=(w != 0 ? w : 1.0);
685
686 Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> eigensolver(C);
687 if (eigensolver.info() != Eigen::Success) {
688 msg(MSG::WARNING) << "Failed to compute Eigenvalues -> Can't determine shower axis" << endmsg;
689 }
690 else {
691 // don't use the principal axes if at least one of the 3
692 // diagonal elements is 0
693
694 const Eigen::Vector3d& S=eigensolver.eigenvalues();
695 const Eigen::Matrix3d& U=eigensolver.eigenvectors();
696
697 const double epsilon = 1.E-6;
698
699 if ( std::abs(S[0]) >= epsilon && std::abs(S[1]) >= epsilon && std::abs(S[2]) >= epsilon ) {
700
701 Amg::Vector3D prAxis(showerAxis);
702 int iEigen = -1;
703
704 for (i=0;i<3;i++) {
705 Amg::Vector3D tmpAxis=U.col(i);
706
707 // calculate the angle
708 double tmpAngle=Amg::angle(tmpAxis,showerAxis);
709
710 if ( tmpAngle > 90*deg ) {
711 tmpAngle = 180*deg - tmpAngle;
712 tmpAxis = -tmpAxis;
713 }
714
715 if ( iEigen == -1 || tmpAngle < angle ) {
716 iEigen = i;
717 angle = tmpAngle;
718 prAxis = tmpAxis;
719 }
720 }//end for loop
721
722 // calculate theta and phi angle differences
723
724 deltaPhi = CaloPhiRange::diff(showerAxis.phi(),prAxis.phi());
725
726 deltaTheta = showerAxis.theta() - prAxis.theta();
727
728 // check the angle
729
730 if ( angle < m_maxAxisAngle ) {
731 showerAxis = prAxis;
732 }
733 else
734 ATH_MSG_DEBUG("principal Direction (" << prAxis[Amg::x] << ", "
735 << prAxis[Amg::y] << ", " << prAxis[Amg::z] << ") deviates more than "
736 << m_maxAxisAngle*(1./deg)
737 << " deg from IP-to-ClusterCenter-axis (" << showerAxis[Amg::x] << ", "
738 << showerAxis[Amg::y] << ", " << showerAxis[Amg::z] << ")");
739 }//end if std::abs(S)<epsilon
740 else {
741 ATH_MSG_DEBUG("Eigenvalues close to 0, do not use principal axis");
742 }
743 }//end got eigenvalues
744 } //end if cellinfo.size()>2
745
746 ATH_MSG_DEBUG("Shower Axis = (" << showerAxis[Amg::x] << ", "
747 << showerAxis[Amg::y] << ", " << showerAxis[Amg::z] << ")");
748
749
750 // calculate radial distance from and the longitudinal distance
751 // along the shower axis for each cell. The cluster center is
752 // at r=0 and lambda=0
753
754 for (auto& ci : cellinfo) {
755 const Amg::Vector3D currentCell(ci.x,ci.y,ci.z);
756 // calculate distance from shower axis r
757 ci.r = ((currentCell-showerCenter).cross(showerAxis)).mag();
758 // calculate distance from shower center along shower axis
759 ci.lambda = (currentCell-showerCenter).dot(showerAxis);
760 }
761
762 // loop over all positive energy cells and calculate all desired moments
763
764 // define common norm for all simple moments
765 double commonNorm = 0;
766 double phi0 = cellinfo.size() > 0 ? cellinfo[0].phi : 0;
767
768 for(unsigned i=0;i<cellinfo.size();i++) {
769 const CaloClusterMomentsMaker_detail::cellinfo& ci = cellinfo[i];
770 // loop over all valid moments
771 commonNorm += ci.energy;
772 for(size_t iMoment = 0, size = m_validMoments.size();
773 iMoment != size;
774 ++ iMoment)
775 {
776 // now calculate the actual moments
777 switch (m_validMoments[iMoment]) {
778 case FIRST_ETA:
779 myMoments[iMoment] += ci.energy*ci.eta;
780 break;
781 case FIRST_PHI:
782 // first cell decides the sign in order to avoid
783 // overlap problem at phi = -pi == +pi
784 // need to be normalized to the range [-pi,+pi] in the end
785 myMoments[iMoment] += ci.energy * proxim (ci.phi, phi0);
786 break;
787 case SECOND_R:
788 myMoments[iMoment] += ci.energy*ci.r*ci.r;
789 break;
790 case SECOND_LAMBDA:
791 myMoments[iMoment] += ci.energy*ci.lambda*ci.lambda;
792 break;
793 case LATERAL:
794 if ( (int)i != iCellMax && (int)i != iCellScndMax ) {
795 myMoments[iMoment] += ci.energy*ci.r*ci.r;
796 myNorms[iMoment] += ci.energy*ci.r*ci.r;
797 }
798 else {
799 double rm = ci.r;
800 if ( rm < m_minRLateral )
801 rm = m_minRLateral;
802 myNorms[iMoment] += rm*rm*ci.energy;
803 }
804 break;
805 case LONGITUDINAL:
806 if ( (int)i != iCellMax && (int)i != iCellScndMax ) {
807 myMoments[iMoment] += ci.energy*ci.lambda*ci.lambda;
808 myNorms[iMoment] += ci.energy*ci.lambda*ci.lambda;
809 }
810 else {
811 double lm = ci.lambda;
812 if ( lm < m_minLLongitudinal )
814 myNorms[iMoment] += lm*lm*ci.energy;
815 }
816 break;
817 case FIRST_ENG_DENS:
818 if ( ci.volume > 0 ) {
819 myMoments[iMoment] += ci.energy*ci.energy/ci.volume;
820 myNorms[iMoment] += ci.energy;
821 }
822 break;
823 case SECOND_ENG_DENS:
824 if ( ci.volume > 0 ) {
825 myMoments[iMoment] += ci.energy*std::pow(ci.energy/ci.volume,2);
826 myNorms[iMoment] += ci.energy;
827 }
828 break;
829 case ENG_FRAC_EM:
830 if ( ci.sample == CaloCell_ID::EMB1
831 || ci.sample == CaloCell_ID::EMB2
832 || ci.sample == CaloCell_ID::EMB3
833 || ci.sample == CaloCell_ID::EME1
834 || ci.sample == CaloCell_ID::EME2
835 || ci.sample == CaloCell_ID::EME3
836 || ci.sample == CaloCell_ID::FCAL0 )
837 myMoments[iMoment] += ci.energy;
838 break;
839 case ENG_FRAC_MAX:
840 if ( (int)i == iCellMax )
841 myMoments[iMoment] = ci.energy;
842 break;
843 case PTD:
844 // do not convert to pT since clusters are small and
845 // there is virtually no difference and cosh just costs
846 // time ...
847 myMoments[iMoment] += ci.energy*ci.energy;
848 myNorms[iMoment] += ci.energy;
849 break;
850 default:
851 // nothing to be done for other moments
852 break;
853 }
854 }
855 } //end of loop over cell
856
857
858 // assign moments which don't need the loop over the cells
859 for (size_t iMoment = 0, size = m_validMoments.size();
860 iMoment != size;
861 ++ iMoment)
862 {
863 // now calculate the actual moments
864 switch (m_validMoments[iMoment]) {
865 case FIRST_ETA:
866 case FIRST_PHI:
867 case SECOND_R:
868 case SECOND_LAMBDA:
869 case ENG_FRAC_EM:
870 case ENG_FRAC_MAX:
871 myNorms[iMoment] = commonNorm;
872 break;
873 case DELTA_PHI:
874 myMoments[iMoment] = deltaPhi;
875 break;
876 case DELTA_THETA:
877 myMoments[iMoment] = deltaTheta;
878 break;
879 case DELTA_ALPHA:
880 myMoments[iMoment] = angle;
881 break;
882 case CENTER_X:
883 myMoments[iMoment] = showerCenter.x();
884 break;
885 case CENTER_Y:
886 myMoments[iMoment] = showerCenter.y();
887 break;
888 case CENTER_Z:
889 myMoments[iMoment] = showerCenter.z();
890 break;
891 case CENTER_MAG:
892 myMoments[iMoment] = showerCenter.mag();
893 break;
894 case CENTER_LAMBDA:
895 // calculate the longitudinal distance along the shower axis
896 // of the shower center from the calorimeter start
897
898 // first need calo boundary at given eta phi try LAREM barrel
899 // first, then LAREM endcap OW, then LAREM endcap IW, then
900 // FCal
901 {
902 double r_calo(0),z_calo(0),lambda_c(0);
903 r_calo = m_caloDepthTool->get_entrance_radius(CaloCell_ID::EMB1,
904 showerCenter.eta(),
905 showerCenter.phi(),
906 caloDDMgr);
907 if ( r_calo == 0 ) {
908 z_calo = m_caloDepthTool->get_entrance_z(CaloCell_ID::EME1,
909 showerCenter.eta(),
910 showerCenter.phi(),
911 caloDDMgr);
912 if ( z_calo == 0 )
913 z_calo = m_caloDepthTool->get_entrance_z(CaloCell_ID::EME2,
914 showerCenter.eta(),
915 showerCenter.phi(),
916 caloDDMgr);
917 if ( z_calo == 0 )
918 z_calo = m_caloDepthTool->get_entrance_z(CaloCell_ID::FCAL0,
919 showerCenter.eta(),
920 showerCenter.phi(),
921 caloDDMgr);
922 if ( z_calo == 0 ) // for H6 TB without EMEC outer wheel
923 z_calo = m_caloDepthTool->get_entrance_z(CaloCell_ID::HEC0,
924 showerCenter.eta(),
925 showerCenter.phi(),
926 caloDDMgr);
927 if ( z_calo != 0 && showerAxis.z() != 0 ) {
928 lambda_c = std::abs((z_calo-showerCenter.z())/showerAxis.z());
929 }
930 }
931 else {
932 double r_s2 = showerAxis.x()*showerAxis.x()
933 +showerAxis.y()*showerAxis.y();
934 double r_cs = showerAxis.x()*showerCenter.x()
935 +showerAxis.y()*showerCenter.y();
936 double r_cr = showerCenter.x()*showerCenter.x()
937 +showerCenter.y()*showerCenter.y()-r_calo*r_calo;
938 if ( r_s2 > 0 ) {
939 double det = r_cs*r_cs/(r_s2*r_s2) - r_cr/r_s2;
940 if ( det > 0 ) {
941 det = sqrt(det);
942 double l1(-r_cs/r_s2);
943 double l2(l1);
944 l1 += det;
945 l2 -= det;
946 if ( std::abs(l1) < std::abs(l2) )
947 lambda_c = std::abs(l1);
948 else
949 lambda_c = std::abs(l2);
950 }
951 }
952 }
953 myMoments[iMoment] = lambda_c;
954 }
955 break;
956 case ENG_FRAC_CORE:
957 for(i=0;i<(int)CaloCell_ID::Unknown;i++)
958 myMoments[iMoment] += maxSampE[i];
959 myNorms[iMoment] = commonNorm;
960 break;
961 case ISOLATION:
962 {
963 // loop over empty and filled perimeter cells and
964 // get a weighted ratio by means of energy fraction per layer
965 for(unsigned int i=0; i != CaloSampling::Unknown; ++ i) {
967 if (theCluster->hasSampling(s)) {
968 const double eSample = theCluster->eSample(s);
969 if (eSample > 0) {
970 int nAll = nbEmpty[i]+nbNonEmpty[i];
971 if (nAll > 0) {
972 myMoments[iMoment] += (eSample*nbEmpty[i])/nAll;
973 myNorms[iMoment] += eSample;
974 }
975 }//end of eSample>0
976 }//end has sampling
977 }//end loop over samplings
978 }
979 break;
980 case ENG_BAD_CELLS:
981 myMoments[iMoment] = eBad;
982 break;
983 case N_BAD_CELLS:
984 myMoments[iMoment] = nbad;
985 break;
986 case N_BAD_CELLS_CORR:
987 myMoments[iMoment] = nbad_dac;
988 break;
989 case BAD_CELLS_CORR_E:
990 myMoments[iMoment] = ebad_dac;
991 break;
992 case BADLARQ_FRAC:
993 myMoments[iMoment] = eBadLArQ/(theCluster->e()!=0.?theCluster->e():1.);
994 break;
995 case ENG_POS:
996 myMoments[iMoment] = ePos;
997 break;
998 case SIGNIFICANCE:
999 myMoments[iMoment] = (sumSig2>0?theCluster->e()/sqrt(sumSig2):0.);
1000 break;
1001 case CELL_SIGNIFICANCE:
1002 myMoments[iMoment] = maxAbsSig;
1003 break;
1004 case CELL_SIG_SAMPLING:
1005 myMoments[iMoment] = nSigSampl;
1006 break;
1007 case AVG_LAR_Q:
1008 myMoments[iMoment] = eLAr2Q/(eLAr2>0?eLAr2:1);
1009 break;
1010 case AVG_TILE_Q:
1011 myMoments[iMoment] = eTile2Q/(eTile2>0?eTile2:1);
1012 break;
1013 case ENG_BAD_HV_CELLS:
1014 myMoments[iMoment] = eBadLArHV;
1015 break;
1016 case N_BAD_HV_CELLS:
1017 myMoments[iMoment] = nBadLArHV;
1018 break;
1019 case PTD:
1020 myMoments[iMoment] = sqrt(myMoments[iMoment]);
1021 break;
1022 case MASS:
1023 myMoments[iMoment] = mass;
1024 break;
1025 default:
1026 // nothing to be done for other moments
1027 break;
1028 }
1029 }
1030 }
1031
1032 // normalize moments and copy to Cluster Moment Store
1033 size_t size= m_validMoments.size();
1034 for (size_t iMoment = 0; iMoment != size; ++iMoment) {
1036 if ( myNorms[iMoment] != 0 )
1037 myMoments[iMoment] /= myNorms[iMoment];
1038 if ( moment == FIRST_PHI )
1039 myMoments[iMoment] = CaloPhiRange::fix(myMoments[iMoment]);
1040 theCluster->insertMoment(moment,myMoments[iMoment]);
1041 } // loop on moments for cluster
1042 } // check on requested moments
1043 // check on second moment of time if requested
1044 if ( m_secondTime ) { theCluster->insertMoment(SECOND_TIME,theCluster->secondTime()); }
1045 // check on number of cells per sampling moment if requested
1046 if ( m_nCellsPerSampling ) {
1047 for ( size_t isam(0); isam < nCellsSamp.size(); ++isam ) {
1048 theCluster->setNumberCellsInSampling((CaloCell_ID::CaloSample)isam,std::get<0>(nCellsSamp.at(isam)),false);
1049 if ( isam == (size_t)CaloCell_ID::EME2 && std::get<1>(nCellsSamp.at(isam)) > 0 ) {
1050 theCluster->setNumberCellsInSampling((CaloCell_ID::CaloSample)isam,std::get<1>(nCellsSamp.at(isam)),true);
1051 }
1052 } // loop on samplings
1053 nCellsSamp.clear();
1054 }
1055 } // loop on clusters
1056
1057 return StatusCode::SUCCESS;
1058}
Scalar deltaPhi(const MatrixBase< Derived > &vec) const
#define endmsg
STL-style allocator wrapper for ArenaPoolAllocator.
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
CaloPhiRange class declaration.
#define CHECK(...)
Evaluate an expression and check for errors.
#define deg
size_t size() const
Number of registered mappings.
double angle(const GeoTrf::Vector2D &a, const GeoTrf::Vector2D &b)
AthAlgTool(const std::string &type, const std::string &name, const IInterface *parent)
Constructor with parameters:
Gaudi::Details::PropertyBase & declareProperty(Gaudi::Property< T, V, H > &t)
const ServiceHandle< StoreGateSvc > & detStore() const
MsgStream & msg() const
CaloSampling::CaloSample CaloSample
Definition CaloCell_ID.h:53
Data object for each calorimeter readout cell.
Definition CaloCell.h:57
virtual double e() const override final
get energy (data member) (synonym to method energy()
Definition CaloCell.h:333
double energy() const
get energy (data member)
Definition CaloCell.h:327
const CaloDetDescrElement * caloDDE() const
get pointer to CaloDetDescrElement (data member)
Definition CaloCell.h:321
uint16_t provenance() const
get provenance (data member)
Definition CaloCell.h:354
uint16_t quality() const
get quality (data member)
Definition CaloCell.h:348
virtual bool badcell() const
check is cell is dead
Definition CaloCell.cxx:144
CaloGain::CaloGain gain() const
get gain (data member )
Definition CaloCell.h:361
Identifier ID() const
get ID (from cached data member) non-virtual and inline for fast access
Definition CaloCell.h:295
SG::ReadCondHandleKey< CaloDetDescrManager > m_caloMgrKey
double m_maxAxisAngle
the maximal allowed deviation from the IP-to-ClusterCenter-axis.
bool m_calculateIsolation
Set to true if cluster isolation is to be calculated.
double m_minRLateral
the minimal in the definition of the Lateral moment
bool m_calculateSignificance
Set to true if significance moments are need.
bool m_calculateLArHVFraction
Set to true to calculate E and N of cells affected by LAr HV corrections.
bool m_secondTime
Retrieve second moment of cell times and store as moment.
ToolHandle< CaloDepthTool > m_caloDepthTool
std::vector< xAOD::CaloCluster::MomentType > m_validMoments
set of moments which will be calculated.
virtual StatusCode execute(const EventContext &ctx, xAOD::CaloClusterContainer *theClusColl) const override final
Execute on an entire collection of clusters.
CaloClusterMomentsMaker(const std::string &type, const std::string &name, const IInterface *parent)
std::string m_momentsNamesAOD
Not used anymore (with xAOD), but required when configured from COOL.
Gaudi::Property< bool > m_useGPUCriteria
bool m_absOpt
if set to true use abs E value of cells to calculate cluster moments
bool m_nCellsPerSampling
store number of cells per sampling layer as moment
double m_minLLongitudinal
the minimal in the definition of the Longitudinal moment
std::vector< std::string > m_momentsNames
vector holding the input list of names of moments to calculate.
double m_etaInnerWheel
Transition from outer to inner wheel in EME2.
double m_minBadLArQuality
the minimal cell quality in the LAr for declaring a cell bad
bool m_twoGaussianNoise
if set to true use 2-gaussian noise description for TileCal
virtual StatusCode finalize() override
ToolHandle< ILArHVFraction > m_larHVFraction
SG::ReadCondHandleKey< CaloNoise > m_noiseCDOKey
Key of the CaloNoise Conditions data object.
virtual StatusCode initialize() override
This class groups all DetDescr information related to a CaloCell.
CaloCell_ID::CaloSample getSampling() const
cell sampling
This class provides the client interface for accessing the detector description information common to...
static double fix(double phi)
static double diff(double phi1, double phi2)
simple phi1 - phi2 calculation, but result is fixed to respect range.
DataModel_detail::iterator< DataVector > iterator
Definition DataVector.h:842
const_iterator end() const noexcept
Return a const_iterator pointing past the end of the collection.
const_iterator begin() const noexcept
Return a const_iterator pointing at the beginning of the collection.
size_type size() const noexcept
Returns the number of elements in the collection.
This is a "hash" representation of an Identifier.
bool retrieveMoment(MomentType type, double &value) const
Retrieve individual moment.
double getMomentValue(MomentType type) const
Retrieve individual moment - no check for existance! Returns -999 on error.
const CaloClusterCellLink * getCellLinks() const
Get a pointer to the CaloClusterCellLink object (const version).
size_t size() const
size method (forwarded from CaloClusterCellLink obj)
CaloClusterCellLink::iterator cell_iterator
Iterator of the underlying CaloClusterCellLink (non-const version).
virtual double e() const
The total energy of the particle.
void insertMoment(MomentType type, double value)
const_cell_iterator cell_end() const
std::vector< uint16_t > ncells_store_t
Store type for number-of-cells-in-sampling counter.
float eSample(const CaloSample sampling) const
flt_t secondTime() const
Access second moment of cell timing distribution.
MomentType
Enums to identify different moments.
const_cell_iterator cell_begin() const
Iterator of the underlying CaloClusterCellLink (const version).
void setNumberCellsInSampling(CaloSampling::CaloSample samp, int ncells, bool isInnerWheel=false)
Set the number of cells in a sampling layer.
CaloSampling::CaloSample CaloSample
bool hasSampling(const CaloSample s) const
Checks if certain smapling contributes to cluster.
struct color C
void setMag(Amg::Vector3D &v, double mag)
scales the vector length without changing the angles
double angle(const Amg::Vector3D &v1, const Amg::Vector3D &v2)
calculates the opening angle between two vectors
Eigen::Matrix< double, 3, 1 > Vector3D
void nextDDE(Iter iter, Iter endIter)
Prefetch next CaloDDE.
void prefetchNext(Iter iter, Iter endIter)
Prefetch next object in sequence.
Definition prefetch.h:130
bool FIRST_ENG_DENS(const xAOD::TauJet &, const xAOD::CaloVertexedTopoCluster &cluster, float &out)
bool CENTER_MAG(const xAOD::TauJet &, const xAOD::CaloVertexedTopoCluster &cluster, float &out)
bool CENTER_LAMBDA(const xAOD::TauJet &, const xAOD::CaloVertexedTopoCluster &cluster, float &out)
bool EM_PROBABILITY(const xAOD::TauJet &, const xAOD::CaloVertexedTopoCluster &cluster, float &out)
bool SECOND_R(const xAOD::TauJet &, const xAOD::CaloVertexedTopoCluster &cluster, float &out)
bool SECOND_LAMBDA(const xAOD::TauJet &, const xAOD::CaloVertexedTopoCluster &cluster, float &out)
Definition dot.py:1
DataModel_detail::iterator< DVL > unique(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of unique for DataVector/List.
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.
CaloCluster_v1 CaloCluster
Define the latest version of the calorimeter cluster class.
CaloClusterContainer_v1 CaloClusterContainer
Define the latest version of the calorimeter cluster container.
Functions to prefetch blocks of memory.
double proxim(double b, double a)
Definition proxim.h:17
Tell the compiler to optimize assuming that FP may trap.
#define CXXUTILS_TRAPPING_FP
Definition trapping_fp.h:24