ATLAS Offline Software
Loading...
Searching...
No Matches
ColumnarMETMaker.cxx
Go to the documentation of this file.
1
2
3/*
4 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
5*/
6
7// ColumnarMETMaker.cxx
8// Implementation file for class ColumnarMETMaker
9// Author: T.J.Khoo<khoo@cern.ch>
11
12// METUtilities includes
15
16// MET EDM
22
23// Jet EDM
25
26// Tracking EDM
29
30// Shallow copy
32
33// Muon EDM
35
36// Electron EDM
39
40// framework includes
46
47#include <memory>
48
49namespace met {
50
51 using xAOD::MissingET;
57 using xAOD::IParticle;
62
64 static const SG::AuxElement::ConstAccessor< iplink_t > acc_originalObject("originalObjectLink");
65 static const SG::AuxElement::ConstAccessor< iplink_t > acc_nominalObject("nominalObjectLink");
66 static const SG::AuxElement::ConstAccessor< std::vector<iplink_t > > acc_ghostMuons("GhostMuon");
67
68 static const SG::AuxElement::ConstAccessor<float> acc_Eloss("EnergyLoss");
69
70 static const SG::AuxElement::Accessor< std::vector<iplink_t> > dec_constitObjLinks("ConstitObjectLinks");
71 static const SG::AuxElement::Accessor< std::vector<float> > dec_constitObjWeights("ConstitObjectWeights");
72
74 // Public methods:
76
77 // Constructors
81 m_PVkey("PrimaryVertices"),
82 m_trkseltool(""),
83 m_JvtTool("", this)
84 {
85 //
86 // Property declaration
87 //
88 declareProperty("JetJvtMomentName", m_jetJvtMomentName = "Jvt" );
89 declareProperty("JetRejectionDec", m_jetRejectionDec = "" );
90 declareProperty("JetMinEFrac", m_jetMinEfrac = 0.0 );
91 declareProperty("JetMinWeightedPt", m_jetMinWeightedPt = 20.0e3 );
92 declareProperty("JetConstitScaleMom", m_jetConstitScaleMom = "JetConstitScaleMomentum");
93 declareProperty("CorrectJetPhi", m_jetCorrectPhi = false );
94 declareProperty("DoPFlow", m_doPFlow = false );
95 declareProperty("DoSoftTruth", m_doSoftTruth = false );
96 declareProperty("DoJetTruth", m_doConstJet = false );
97
98 declareProperty("JetSelection", m_jetSelection = "Tight" );
99 declareProperty("JetEtaMax", m_JetEtaMax = 4.5 );
100 declareProperty("JetEtaForw", m_JetEtaForw = 2.5 );
101 declareProperty("CustomCentralJetPt", m_customCenJetPtCut = 20e3 );
102 declareProperty("CustomForwardJetPt", m_customFwdJetPtCut = 20e3 );
103 declareProperty("CustomJetJvtPtMax", m_customJvtPtMax = 60e3 );
104 declareProperty("CustomJetJvtWP", m_customJvtWP = "FixedEffPt" );
105
106 declareProperty("DoMuonEloss", m_muEloss = false );
107 declareProperty("ORCaloTaggedMuons", m_orCaloTaggedMuon = true );
108 declareProperty("GreedyPhotons", m_greedyPhotons = false );
109 declareProperty("VeryGreedyPhotons", m_veryGreedyPhotons = false );
110
111 declareProperty("UseGhostMuons", m_useGhostMuons = false );
112 declareProperty("DoRemoveMuonJets", m_doRemoveMuonJets = true );
113 declareProperty("DoSetMuonJetEMScale", m_doSetMuonJetEMScale = true );
114
115 declareProperty("DoRemoveElecTrks", m_doRemoveElecTrks = true );
116 declareProperty("DoRemoveElecTrksEM", m_doRemoveElecTrksEM = false );
117
118 declareProperty("skipSystematicJetSelection", m_skipSystematicJetSelection = false,
119 "EXPERIMENTAL: whether to use simplified OR based on nominal jets "
120 "and for jet-related systematics only. "
121 "WARNING: this property is strictly for doing physics studies of the feasibility "
122 "of this OR scheme, it should not be used in a regular analysis");
123
124 // muon overlap variables (expert use only)
125 declareProperty("JetTrkNMuOlap", m_jetTrkNMuOlap = 5 );
126 declareProperty("JetWidthMuOlap", m_jetWidthMuOlap = 0.1 );
127 declareProperty("JetPsEMuOlap", m_jetPsEMuOlap = 2.5e3 );
128 declareProperty("JetEmfMuOlap", m_jetEmfMuOlap = 0.9 );
129 declareProperty("JetTrkPtMuPt", m_jetTrkPtMuPt = 0.8 );
130 declareProperty("muIDPTJetPtRatioMuOlap", m_muIDPTJetPtRatioMuOlap = 2.0 );
131
132 declareProperty("MissingObjWarnThreshold", m_missObjWarningPtThreshold = 7.0e3 );
133
134 declareProperty("TrackSelectorTool", m_trkseltool );
135 declareProperty("JvtSelTool", m_JvtTool );
136 }
137
138 // Destructor
141 = default;
142
143 // Athena algtool's Hooks
146 {
147 ATH_MSG_INFO ("Initializing " << name() << "...");
148
149 ATH_MSG_INFO("Use jet selection criterion: " << m_jetSelection << " PFlow: " << m_doPFlow);
150 if (m_jetSelection == "Loose") { m_CenJetPtCut = 20e3; m_FwdJetPtCut = 20e3; m_JvtWP = "FixedEffPt"; m_JvtPtMax = 60e3; }
151 else if (m_jetSelection == "Tight") { m_CenJetPtCut = 20e3; m_FwdJetPtCut = 30e3; m_JvtWP = "FixedEffPt"; m_JvtPtMax = 60e3; }
152 else if (m_jetSelection == "Tighter"){ m_CenJetPtCut = 20e3; m_FwdJetPtCut = 35e3; m_JvtWP = "FixedEffPt"; m_JvtPtMax = 60e3; }
153 else if (m_jetSelection == "Tenacious"){ m_CenJetPtCut = 20e3; m_FwdJetPtCut = 40e3; m_JvtWP = "FixedEffPt"; m_JvtPtMax = 60e3; }
154 else if (m_jetSelection == "Tier0") { m_CenJetPtCut = 0; m_FwdJetPtCut = 0; m_JvtWP = "None"; }
155 else if (m_jetSelection == "Expert") {
156 ATH_MSG_INFO("Custom jet selection configured. *** FOR EXPERT USE ONLY ***");
161 }
162 else if (m_jetSelection == "HRecoil") {
163 ATH_MSG_INFO("Jet selection for hadronic recoil calculation is configured.");
164 m_CenJetPtCut = 9999e3;
165 m_FwdJetPtCut = 9999e3;
166 m_JetEtaMax = 5;
167 m_JvtWP = "None";
168 }
169 else {
170 if (m_jetSelection == "Default") ATH_MSG_WARNING( "WARNING: Default is now deprecated" );
171 ATH_MSG_ERROR( "Error: No available jet selection found! Please update JetSelection in ColumnarMETMaker. Choose one: Loose, Tight (recommended), Tighter, Tenacious" );
172 return StatusCode::FAILURE;
173 }
174
175 if (!m_trkseltool.empty()) ATH_CHECK( m_trkseltool.retrieve() );
176
177 if (m_JvtWP != "None"){
178 if (m_JvtTool.empty()) {
179 asg::AsgToolConfig config_jvt ("CP::NNJvtSelectionTool/JvtSelTool");
180 ATH_CHECK(config_jvt.setProperty("JetContainer", m_jetContainer.key()));
181 ATH_CHECK(config_jvt.setProperty("WorkingPoint", m_JvtWP));
182 ATH_CHECK(config_jvt.setProperty("JvtMomentName", "NNJvt"));
183 ATH_CHECK(config_jvt.setProperty("MaxPtForJvt", m_JvtPtMax));
185 }
186 ATH_CHECK(m_JvtTool.retrieve());
187 }
188
189 // ReadHandleKey(s)
190 ATH_CHECK( m_PVkey.initialize() );
191
192 // configurable accessors
193 if (!m_jetRejectionDec.empty()) {
195 ATH_MSG_INFO("Applying additional jet rejection criterium in MET calculation: " << m_jetRejectionDec);
196 }
197
198 ATH_MSG_INFO("Suppressing warnings of objects missing in METAssociationMap for objects with pT < " << m_missObjWarningPtThreshold/1e3 << " GeV.");
199
200 // overlap removal simplification?
202 ATH_MSG_INFO("Requesting simplified overlap removal procedure in MET calculation");
203 }
204
205 if (!m_inputPreselectionName.empty()) {
207 }
208 if (!m_jetConstitScaleMom.empty()) {
210 m_jetConstitScaleMomFixedAcc.emplace(*this, "JetConstitScaleMomentum");
211 }
212 return StatusCode::SUCCESS;
213 }
214
215
216 // **** Rebuild generic MET term ****
217
218 StatusCode ColumnarMETMaker::rebuildMET(const std::string& metKey,
221 const xAOD::IParticleContainer* collection,
224 {
225 if (!metCont)
226 {
227 ATH_MSG_ERROR("No MET container provided");
228 return StatusCode::FAILURE;
229 }
230 if (!collection)
231 {
232 ATH_MSG_ERROR("No input collection provided for MET term \"" << metKey << "\"");
233 return StatusCode::FAILURE;
234 }
235
236 return rebuildMET(metKey,metType,columnar::MutableMetRange (*metCont),columnar::ParticleRange (*collection),m_assocAcc(helper),objScale);
237 }
238
239 StatusCode ColumnarMETMaker::rebuildMET(const std::string& metKey,
242 columnar::ParticleRange collection,
245 {
247 switch(metType) {
250 break;
252 metSource = MissingETBase::Source::photon();
253 break;
254 case xAOD::Type::Tau:
255 metSource = MissingETBase::Source::tau();
256 break;
257 case xAOD::Type::Muon:
258 metSource = MissingETBase::Source::muon();
259 break;
260 case xAOD::Type::Jet:
261 ATH_MSG_WARNING("Incorrect use of rebuildMET -- use rebuildJetMET for RefJet term");
262 return StatusCode::FAILURE;
263 default:
264 ATH_MSG_WARNING("Invalid object type provided: " << metType);
265 return StatusCode::FAILURE;
266 }
267
268 columnar::MutableMetId met = m_outputMetMapAcc.fillMET (metCont, metKey, metSource);
269
270 // If muon eloss corrections are required, create a new term to hold these if it doesn't already exist
272 if (m_outputMetMapAcc.tryCreateIfMissing (metCont, "MuonEloss", MissingETBase::Source::Type::Muon | MissingETBase::Source::Category::Calo) != StatusCode::SUCCESS) {
273 ATH_MSG_ERROR("failed to create Muon Eloss MET term");
274 return StatusCode::FAILURE;
275 }
276 }
277
278 return rebuildMET(met,collection,helper,objScale);
279 }
280
282 const xAOD::IParticleContainer* collection,
285 {
286 if (!met)
287 {
288 ATH_MSG_ERROR("No MET object provided");
289 return StatusCode::FAILURE;
290 }
291 if (!collection)
292 {
293 ATH_MSG_ERROR("No input collection provided for MET term \"" << met->name() << "\"");
294 return StatusCode::FAILURE;
295 }
296
297 return rebuildMET(columnar::MutableMetId(*met), columnar::ParticleRange(*collection), m_assocAcc(helper), objScale);
298 }
299
301 columnar::ParticleRange collection,
304 {
306 bool removeOverlap = true;
307 if (m_inputObjTypeAcc(collection) == xAOD::Type::Muon) {
309 removeOverlap = false;
310 }
313 return rebuildMET(met,collection,helper,p,removeOverlap,objScale);
314 }
315
317 const xAOD::IParticleContainer* collection,
320 bool removeOverlap,
322 if(!met || !collection) {
323 ATH_MSG_ERROR("Invalid pointer supplied for "
324 << "MET (" << met << ") or "
325 << "collection (" << collection << ").");
326 return StatusCode::FAILURE;
327 }
328
329 return rebuildMET(columnar::MutableMetId(*met),columnar::ParticleRange (*collection),m_assocAcc(helper),p,removeOverlap,objScale);
330 }
331
333 columnar::ParticleRange collection,
336 bool removeOverlap,
338 if(helper.map().empty()) {
339 ATH_MSG_WARNING("Incomplete association map received. Cannot rebuild MET.");
340 ATH_MSG_WARNING("Note: ColumnarMETMaker should only be run on events containing at least one PV");
341 return StatusCode::SUCCESS;
342 }
343 ATH_MSG_VERBOSE("Building MET term " << met(m_outputMetNameAcc));
345
346 if(collection.empty()) return StatusCode::SUCCESS;
347 columnar::MetHelpers::OriginalObjectHandle collectionOriginals(*this,collection);
348
349 if(collectionOriginals.isShallowCopy() && collectionOriginals.originalInputs()) {
350 ATH_MSG_WARNING("Shallow copy provided without \"originalObjectLinks\" decoration! "
351 << "Overlap removal cannot be done. "
352 << "Will not compute this term.");
353 ATH_MSG_WARNING("Please apply xAOD::setOriginalObjectLink() from xAODBase/IParticleHelpers.h");
354 return StatusCode::SUCCESS;
355 }
356 ATH_MSG_VERBOSE("Original inputs? " << collectionOriginals.originalInputs());
357 for(const auto obj : collection) {
358 if (m_inputPreselectionAcc&&!(*m_inputPreselectionAcc)(obj)) continue;
359 bool selected = false;
360 auto orig = collectionOriginals.getOriginal(obj);
361 auto assocs = helper.getAssociations(orig);
362 if(assocs.empty()) {
363 std::string message = "Object is not in association map. Did you make a deep copy but fail to set the \"originalObjectLinks\" decoration? "
364 "If not, Please apply xAOD::setOriginalObjectLink() from xAODBase/IParticleHelpers.h";
365 // Avoid warnings for leptons with pT below threshold for association map
367 ATH_MSG_WARNING(message);
368 } else {
369 ATH_MSG_DEBUG(message);
370 }
371 // if this is an uncalibrated electron below the threshold, then we put it into the soft term
373 metWeights.emplace_back( obj, 0 );
374 message = "Missing an electron from the MET map. Included as a track in the soft term. pT: " + std::to_string(m_inputMomAcc.pt(obj)/1e3) + " GeV";
376 ATH_MSG_WARNING(message);
377 } else {
378 ATH_MSG_DEBUG(message);
379 }
380 continue;
381 } else {
382 ATH_MSG_ERROR("Missing an object: " << m_inputObjTypeAcc(orig) << " pT: " << m_inputMomAcc.pt(obj)/1e3 << " GeV, may be duplicated in the soft term.");
383 }
384 }
385
386 // If the object has already been selected and processed, ignore it.
387 if(helper.objSelected(orig)) continue;
388 selected = helper.selectIfNoOverlaps(orig,p) || !removeOverlap;
389 ATH_MSG_VERBOSE(m_inputObjTypeAcc(obj) << " (" << orig <<") with pt " << m_inputMomAcc.pt(obj)
390 << " is " << ( selected ? "non-" : "") << "overlapping");
391
392 // Greedy photon options: set selection flags
394 for(auto assoc : assocs){
395 auto indices = m_assocAcc.overlapIndices(assoc,orig);
396 auto allObjects = m_assocAcc.objects(assoc);
397 for (size_t index : indices){
398 const xAOD::IParticle* thisObj = allObjects[index].getXAODObject();
399 if(!thisObj) continue;
400 if ((thisObj->type() == xAOD::Type::Jet && m_veryGreedyPhotons) ||
401 thisObj->type() == xAOD::Type::Electron)
402 helper.setObjSelectionFlag(assoc, thisObj, true);
403 }
404 }
405 }
406
407 //Do special overlap removal for calo tagged muons
408 if(m_orCaloTaggedMuon && !removeOverlap && m_inputObjTypeAcc(orig)==xAOD::Type::Muon &&
409 m_inputMuonTypeAcc.getOptional(orig)==xAOD::Muon::MuonType::CaloTagged) {
410 for (decltype(auto) assoc : assocs) {
411 auto ind = m_assocAcc.overlapIndices(assoc,orig);
412 auto allObjects = m_assocAcc.objects(assoc);
413 for (size_t indi = 0; indi < ind.size(); indi++) if (allObjects[ind[indi]]) {
414 if (allObjects[ind[indi]].isContainer<columnar::ElectronDef>()
415 && helper.objSelected(assoc, ind[indi])) {
416 selected = false;
417 break;
418 }
419 }
420 }
421 }
422 // Don't overlap remove muons, but flag the non-overlapping muons to take out their tracks from jets
423 // Removed eloss from here -- clusters already flagged.
424 // To be handled in rebuildJetMET
425 if(selected) {
427 ATH_MSG_VERBOSE("Add object with pt " << m_inputMomAcc.pt(obj));
428 m_outputMetMomAcc.addParticle (met, m_inputMomAcc, obj);
429 } else {
430 MissingETBase::Types::constvec_t constvec = helper.getConstVec(obj,objScale);
431 ATH_MSG_VERBOSE("Add truth object with pt " << constvec.cpt());
432 m_outputMetMomAcc.addParticle (met, constvec.cpx(),constvec.cpy(),constvec.cpt());
433 }
434 metWeights.emplace_back( obj, 1. );
435 }
436 }
437 ATH_MSG_DEBUG("Built met term " << met(m_outputMetNameAcc) << ", with magnitude " << m_outputMetMomAcc.met(met));
438 return StatusCode::SUCCESS;
439 }
440
441 StatusCode ColumnarMETMaker::rebuildJetMET(const std::string& metJetKey,
442 const std::string& softKey,
444 const xAOD::JetContainer* jets,
445 const xAOD::MissingETContainer* metCoreCont,
447 bool doJetJVT) const
448 {
449 if (!metCont || !metCoreCont)
450 {
451 ATH_MSG_ERROR("No MET container provided");
452 return StatusCode::FAILURE;
453 }
454 if (!jets)
455 {
456 ATH_MSG_ERROR("No input collection provided for MET term \"" << metJetKey << "\" and soft term: " << softKey);
457 return StatusCode::FAILURE;
458 }
459
460 return rebuildJetMET(metJetKey,softKey,columnar::MutableMetRange(*metCont),columnar::JetRange(*jets),columnar::Met1Range(*metCoreCont),m_assocAcc(helper),doJetJVT);
461 }
462
463 StatusCode ColumnarMETMaker::rebuildJetMET(const std::string& metJetKey,
464 const std::string& softKey,
467 columnar::Met1Range metCoreCont,
469 bool doJetJVT) const
470 {
471 ATH_MSG_VERBOSE("Rebuild jet term: " << metJetKey << " and soft term: " << softKey);
472
473 columnar::MutableMetId metJet {m_outputMetMapAcc.fillMET (metCont, metJetKey, MissingETBase::Source::jet())};
474
475 columnar::OptMet1Id coreSoftClus, coreSoftTrk;
476 columnar::OptMutableMetId metSoftClus, metSoftTrk;
477
478 columnar::OptMet1Id coreSoft = m_inputMetMapAcc(metCoreCont,softKey+"Core");
479 if(!coreSoft) {
480 ATH_MSG_WARNING("Invalid soft term key supplied: " << softKey);
481 return StatusCode::FAILURE;
482 }
484 coreSoftTrk = coreSoft;
485
486 metSoftTrk = m_outputMetMapAcc.fillMET (metCont, softKey, coreSoftTrk.value()(m_inputMetSourceAcc));
487 } else {
488 coreSoftClus = coreSoft;
489
490 metSoftClus = m_outputMetMapAcc.fillMET (metCont, softKey, coreSoftClus.value()(m_inputMetSourceAcc));
491 }
492
493 return rebuildJetMET(metJet, metCont, jets, helper,
494 metSoftClus, coreSoftClus,
495 metSoftTrk, coreSoftTrk,
496 doJetJVT);
497 }
498
499 StatusCode ColumnarMETMaker::rebuildTrackMET(const std::string& metJetKey,
500 const std::string& softKey,
502 const xAOD::JetContainer* jets,
503 const xAOD::MissingETContainer* metCoreCont,
505 bool doJetJVT) const
506 {
507 if (!metCont)
508 {
509 ATH_MSG_ERROR("No MET container provided");
510 return StatusCode::FAILURE;
511 }
512 if (!jets)
513 {
514 ATH_MSG_ERROR("No input collection provided for MET term \"" << metJetKey << "\" and soft term: " << softKey);
515 return StatusCode::FAILURE;
516 }
517
518 return rebuildTrackMET(metJetKey,softKey,columnar::MutableMetRange(*metCont),columnar::JetRange(*jets),columnar::Met1Range(*metCoreCont),m_assocAcc(helper),doJetJVT);
519 }
520
521 StatusCode ColumnarMETMaker::rebuildTrackMET(const std::string& metJetKey,
522 const std::string& softKey,
525 columnar::Met1Range metCoreCont,
527 bool doJetJVT) const
528 {
529 ATH_MSG_VERBOSE("Rebuild jet term: " << metJetKey << " and soft term: " << softKey);
530
532
533 columnar::OptMet1Id coreSoft = m_inputMetMapAcc(metCoreCont,softKey+"Core");
534 if(!coreSoft) {
535 ATH_MSG_WARNING("Invalid soft term key supplied: " << softKey);
536 return StatusCode::FAILURE;
537 }
538 auto coreSoftTrk = coreSoft.value();
539
540 columnar::MutableMetId metSoftTrk {m_outputMetMapAcc.fillMET (metCont, softKey, coreSoftTrk(m_inputMetSourceAcc))};
541
542 return rebuildTrackMET(metJet, metCont, jets, helper,
543 metSoftTrk, coreSoftTrk,
544 doJetJVT);
545 }
546
547 StatusCode ColumnarMETMaker::rebuildJetMET(const std::string& metJetKey,
548 const std::string& softClusKey,
549 const std::string& softTrkKey,
551 const xAOD::JetContainer* jets,
552 const xAOD::MissingETContainer* metCoreCont,
554 bool doJetJVT) const
555 {
556 if (!metCont)
557 {
558 ATH_MSG_ERROR("No MET container provided");
559 return StatusCode::FAILURE;
560 }
561 if (!jets)
562 {
563 ATH_MSG_ERROR("No input collection provided for MET term \"" << metJetKey << "\" and soft term: " << softClusKey << " and " << softTrkKey);
564 return StatusCode::FAILURE;
565 }
566
567 return rebuildJetMET(metJetKey,softClusKey,softTrkKey,columnar::MutableMetRange(*metCont),columnar::JetRange(*jets),columnar::Met1Range(*metCoreCont),m_assocAcc(helper),doJetJVT);
568 }
569
570 StatusCode ColumnarMETMaker::rebuildJetMET(const std::string& metJetKey,
571 const std::string& softClusKey,
572 const std::string& softTrkKey,
575 columnar::Met1Range metCoreCont,
577 bool doJetJVT) const
578 {
579
580 ATH_MSG_VERBOSE("Create Jet MET " << metJetKey);
581 columnar::MutableMetId metJet {m_outputMetMapAcc.fillMET (metCont, metJetKey, MissingETBase::Source::jet())};
582 ATH_MSG_VERBOSE("Create SoftClus MET " << softClusKey);
583 auto coreSoftClus = m_inputMetMapAcc(metCoreCont,softClusKey+"Core");
584 ATH_MSG_VERBOSE("Create SoftTrk MET " << softTrkKey);
585 auto coreSoftTrk = m_inputMetMapAcc(metCoreCont,softTrkKey+"Core");
586 if(!coreSoftClus) {
587 ATH_MSG_WARNING("Invalid cluster soft term key supplied: " << softClusKey);
588 return StatusCode::FAILURE;
589 }
590 if(!coreSoftTrk) {
591 ATH_MSG_WARNING("Invalid track soft term key supplied: " << softTrkKey);
592 return StatusCode::FAILURE;
593 }
594 columnar::MutableMetId metSoftClus {m_outputMetMapAcc.fillMET (metCont, softClusKey, m_inputMetSourceAcc(coreSoftClus.value()))};
595
596 columnar::MutableMetId metSoftTrk {m_outputMetMapAcc.fillMET (metCont, softTrkKey, m_inputMetSourceAcc(coreSoftTrk.value()))};
597
598 return rebuildJetMET(metJet, metCont, jets, helper,
599 metSoftClus, coreSoftClus,
600 metSoftTrk, coreSoftTrk,
601 doJetJVT);
602 }
603
605 const xAOD::JetContainer* jets,
607 xAOD::MissingET* metSoftClus,
608 const xAOD::MissingET* coreSoftClus,
609 xAOD::MissingET* metSoftTrk,
610 const xAOD::MissingET* coreSoftTrk,
611 bool doJetJVT,
612 bool tracksForHardJets,
613 std::vector<const xAOD::IParticle*>* softConst) const {
614 if(!metJet || !jets) {
615 ATH_MSG_ERROR("Invalid pointer supplied for "
616 << "MET (" << metJet << ") or "
617 << "jet collection (" << jets << ").");
618 return StatusCode::FAILURE;
619 }
620 columnar::MutableMetRange metCont (*static_cast<MissingETContainer*>(metJet->container()));
621 return rebuildJetMET(columnar::MutableMetId(*metJet), metCont, columnar::JetRange(*jets), m_assocAcc(helper),
622 columnar::OptMutableMetId(metSoftClus), columnar::OptMet1Id (coreSoftClus),
623 columnar::OptMutableMetId(metSoftTrk), columnar::OptMet1Id (coreSoftTrk),
624 doJetJVT, tracksForHardJets, softConst);
625 }
626
631 columnar::OptMutableMetId metSoftClus,
632 columnar::OptMet1Id coreSoftClus,
633 columnar::OptMutableMetId metSoftTrk,
634 columnar::OptMet1Id coreSoftTrk,
635 bool doJetJVT,
636 bool tracksForHardJets,
637 std::vector<const xAOD::IParticle*>* softConst) const {
638 if(softConst && m_trkseltool.empty() && !m_doPFlow && !m_doSoftTruth) {
639 ATH_MSG_WARNING( "Requested soft track element links, but no track selection tool supplied.");
640 }
641 const xAOD::Vertex *pv = softConst?getPV():nullptr;
642
643 if(helper.map().empty()) {
644 ATH_MSG_WARNING("Incomplete association map received. Cannot rebuild MET.");
645 ATH_MSG_WARNING("Note: ColumnarMETMaker should only be run on events containing at least one PV");
646 return StatusCode::SUCCESS;
647 }
648
649 if(doJetJVT && m_JvtWP == "None"){
650 ATH_MSG_WARNING("rebuildJetMET requested JVT, which is inconsistent with jet selection " << m_jetSelection << ". Ignoring JVT.");
651 doJetJVT = false;
652 }
653
654 ATH_MSG_VERBOSE("Building MET jet term " << metJet(m_outputMetNameAcc));
655 if(!metSoftClus && !metSoftTrk) {
656 ATH_MSG_WARNING("Neither soft cluster nor soft track term has been supplied!");
657 return StatusCode::SUCCESS;
658 }
659 static const SG::AuxElement::ConstAccessor<std::vector<ElementLink<IParticleContainer> > > acc_softConst("softConstituents");
660 std::optional<columnar::MetHelpers::ObjectWeightHandle<columnar::MutableMetDef,columnar::JetDef>> metSoftClusLinks;
661 if(metSoftClus) {
662 metSoftClusLinks.emplace(*this,m_jetOutputMetWeightDecSoft,metSoftClus.value(),jets);
663 if(!coreSoftClus) {
664 ATH_MSG_ERROR("Soft cluster term provided without a core term!");
665 return StatusCode::FAILURE;
666 }
667 ATH_MSG_VERBOSE("Building MET soft cluster term " << metSoftClus.value()(m_outputMetNameAcc));
668 ATH_MSG_VERBOSE("Core soft cluster mpx " << m_inputMetMomAcc.mpx(coreSoftClus.value())
669 << ", mpy " << m_inputMetMomAcc.mpy(coreSoftClus.value())
670 << " sumet " << m_inputMetMomAcc.sumet(coreSoftClus.value()));
671 m_outputMetMomAcc.addMet (metSoftClus.value(), m_inputMetMomAcc, coreSoftClus.value());
672 // Fill a vector with the soft constituents, if one was provided.
673 // For now, only setting up to work with those corresponding to the jet constituents.
674 // Can expand if needed.
675 if(softConst && acc_softConst.isAvailable(*coreSoftClus.getXAODObject())) {
676 for(const auto& constit : acc_softConst(*coreSoftClus.getXAODObject())) {
677 softConst->push_back(*constit);
678 }
679 ATH_MSG_DEBUG(softConst->size() << " soft constituents from core term");
680 }
681 }
682 std::optional<columnar::MetHelpers::ObjectWeightHandle<columnar::MutableMetDef,columnar::JetDef>> metSoftTrkLinks;
683 if(metSoftTrk) {
684 metSoftTrkLinks.emplace(*this,m_jetOutputMetWeightDecSoft,metSoftTrk.value(),jets);
685 if(!coreSoftTrk) {
686 ATH_MSG_ERROR("Soft track term provided without a core term!");
687 return StatusCode::FAILURE;
688 }
689 ATH_MSG_VERBOSE("Building MET soft track term " << metSoftTrk.value()(m_outputMetNameAcc));
690 ATH_MSG_VERBOSE("Core soft track mpx " << m_inputMetMomAcc.mpx(coreSoftTrk.value())
691 << ", mpy " << m_inputMetMomAcc.mpy(coreSoftTrk.value())
692 << " sumet " << m_inputMetMomAcc.sumet(coreSoftTrk.value()));
693 m_outputMetMomAcc.addMet (metSoftTrk.value(), m_inputMetMomAcc, coreSoftTrk.value());
694 if(softConst && acc_softConst.isAvailable(*coreSoftTrk.getXAODObject()) && !m_doPFlow && !m_doSoftTruth) {
695 for(const auto& constit : acc_softConst(*coreSoftTrk.getXAODObject())) {
696 softConst->push_back(*constit);
697 }
698 ATH_MSG_DEBUG(softConst->size() << " soft constituents from trk core term");
699 }
700 }
701
703
704 // Get the hashed key of this jet, if we can. Though his only works if
705 // 1. the container is an owning container, and not just a view;
706 // 2. the container is in the event store already.
707 // Since we will be creating ElementLink-s to these jets later on in the
708 // code, and it should work in AnalysisBase, only the first one of these
709 // is checked. Since the code can not work otherwise.
710
712 for(auto jet : jets) {
713 auto originalJet = jetsOriginals.getOriginal(jet);
714 auto assoc = helper.getJetAssociation(originalJet);
715 if(!assoc || m_assocAcc.isMisc(*assoc)) {
716 ATH_MSG_WARNING( "Jet without association found!" );
717 continue;
718 }
719
721 // retrieve nominal calibrated jet
722 if (acc_nominalObject.isAvailable(jet.getXAODObject())){
723 ATH_MSG_VERBOSE( "Jet pt before nominal replacement = " << m_jetMomAcc.pt(jet));
724 jet = *static_cast<const xAOD::Jet*>(*acc_nominalObject(jet.getXAODObject()));
725 }
726 else
727 ATH_MSG_ERROR("No nominal calibrated jet available for jet " << jet << ". Cannot simplify overlap removal!");
728 }
729 ATH_MSG_VERBOSE( "Jet pt = " << m_jetMomAcc.pt(jet));
730
731 bool selected = (std::abs(m_jetMomAcc.eta(jet))<m_JetEtaForw && m_jetMomAcc.pt(jet)>m_CenJetPtCut) || (std::abs(m_jetMomAcc.eta(jet))>=m_JetEtaForw && m_jetMomAcc.pt(jet)>m_FwdJetPtCut );
732 bool JVT_reject(false);
733 bool isMuFSRJet(false);
734
735 // Apply a cut on the maximum jet eta. This restricts jets to those with calibration. Excluding more forward jets was found to have a minimal impact on the MET in Zee events
736 if (m_JetEtaMax > 0.0 && std::abs(m_jetMomAcc.eta(jet)) > m_JetEtaMax)
737 JVT_reject = true;
738
739 if(doJetJVT) {
740 // intrinsically checks that is within range to apply Jvt requirement
741 JVT_reject = !bool(m_JvtTool->accept(&jet.getXAODObject()));
742 ATH_MSG_VERBOSE("Jet " << (JVT_reject ? "fails" : "passes") <<" JVT selection");
743 }
744
745 // if defined apply additional jet criterium
746 if (m_acc_jetRejectionDec && (*m_acc_jetRejectionDec)(jet)==0) JVT_reject = true;
747 bool hardJet(false);
748 MissingETBase::Types::constvec_t calvec = helper.overlapCalVec(*assoc);
749 bool caloverlap = false;
750 caloverlap = calvec.ce()>0;
751 ATH_MSG_DEBUG("Jet " << jet << " is " << ( caloverlap ? "" : "non-") << "overlapping");
752
753 if(m_veryGreedyPhotons && caloverlap) {
754 for(const auto object : m_assocAcc.objects(*assoc)) {
755 // Correctly handle this jet if we're using very greedy photons
756 if (object && object.getXAODObject()->type() == xAOD::Type::Photon) hardJet = true;
757 }
758 }
759
760 xAOD::JetFourMom_t constjet;
761 double constSF(1);
762 if(m_jetConstitScaleMom.empty() && m_assocAcc.hasAlternateConstVec(*assoc)){
763 constjet = m_assocAcc.getAlternateConstVec(*assoc);
764 } else {
765 constjet = m_jetConstitScaleMomAcc.value().jetP4(jet);//grab a constituent scale added by the JetMomentTool/JetConstitFourMomTool.cxx
766 double denom = (m_assocAcc.hasAlternateConstVec(*assoc) ? m_assocAcc.getAlternateConstVec(*assoc) : m_jetConstitScaleMomFixedAcc.value().jetP4(jet)).E();
767 constSF = denom>1e-9 ? constjet.E()/denom : 0.;
768 ATH_MSG_VERBOSE("Scale const jet by factor " << constSF);
769 calvec *= constSF;
770 }
771 double jpx = constjet.Px();
772 double jpy = constjet.Py();
773 double jpt = constjet.Pt();
774 double opx = jpx - calvec.cpx();
775 double opy = jpy - calvec.cpy();
776
777 columnar::OptMutableMetId met_muonEloss;
779 // Get a term to hold the Eloss corrections
780 met_muonEloss = m_outputMetMapAcc.getRequired(metCont, "MuonEloss");
781 if(!met_muonEloss) {
782 ATH_MSG_WARNING("Attempted to apply muon Eloss correction, but corresponding MET term does not exist!");
783 return StatusCode::FAILURE;
784 }
785 }
786
787 float total_eloss(0);
788 MissingETBase::Types::bitmask_t muons_selflags(0);
789 std::vector<columnar::MuonId> muons_in_jet;
790 std::vector<columnar::ElectronId> electrons_in_jet;
791 bool passJetForEl=false;
792 if(m_useGhostMuons) { // for backwards-compatibility
793 if(!acc_ghostMuons.isAvailable(jet.getXAODObject())){
794 ATH_MSG_ERROR("Ghost muons requested but not found!");
795 return StatusCode::FAILURE;
796 }
797 for(const auto& el : acc_ghostMuons(jet.getXAODObject())) {
798 if(!el.isValid()){
799 ATH_MSG_ERROR("Invalid element link to ghost muon! Quitting.");
800 return StatusCode::FAILURE;
801 }
802 muons_in_jet.push_back(*static_cast<const xAOD::Muon*>(*el));
803 }
804 }
805 for(const auto obj : m_assocAcc.objects(*assoc)) {
806 if(!obj) continue;
807 if(!m_useGhostMuons && obj.isContainer<columnar::MuonDef>()) {
808 auto mu_test = obj.tryGetVariant<columnar::MuonDef>().value();
809 ATH_MSG_VERBOSE("Muon " << mu_test << " found in jet " << jet);
812 if(acc_originalObject.isAvailable(mu_test.getXAODObject())) mu_test = *static_cast<const xAOD::Muon*>(*acc_originalObject(mu_test.getXAODObject()));
813 }
814 if(helper.objSelected(mu_test)) { //
815 muons_in_jet.push_back(mu_test);
816 ATH_MSG_VERBOSE("Muon is selected by MET.");
817 }
818 }
819 } else if(m_doRemoveElecTrks && obj.isContainer<columnar::ElectronDef>()) {
820 auto el_test = obj.tryGetVariant<columnar::ElectronDef>().value();
821 ATH_MSG_VERBOSE("Electron " << el_test << " found in jet " << jet);
823 if(acc_originalObject.isAvailable(el_test.getXAODObject())) el_test = *static_cast<const xAOD::Electron*>(*acc_originalObject(el_test.getXAODObject()));
824 }
825 if(helper.objSelected(*assoc,el_test)){
826 if(el_test(m_electronPtAcc)>90.0e3) { // only worry about high-pt electrons?
827 electrons_in_jet.push_back(el_test);
828 ATH_MSG_VERBOSE("High-pt electron is selected by MET.");
829 }
830 }
831 }
832 }
834 MissingETBase::Types::constvec_t initialTrkMom = m_assocAcc.jetTrkVec(*assoc);
835 float jet_ORtrk_sumpt = helper.overlapTrkVec(*assoc).sumpt();
836 float jet_all_trk_pt = initialTrkMom.sumpt();
837 float jet_unique_trk_pt = jet_all_trk_pt - jet_ORtrk_sumpt;
840 for(const auto& elec : electrons_in_jet) {
841 el_calvec += m_assocAcc.calVec(*assoc,elec);
842 el_trkvec += m_assocAcc.trkVec(*assoc,&elec.getXAODObject());
843 }
844 float el_cal_pt = el_calvec.cpt();
845 float el_trk_pt = el_trkvec.cpt();
846 ATH_MSG_VERBOSE("Elec trk: " << el_trk_pt
847 << " jetalltrk: " << jet_all_trk_pt
848 << " jetORtrk: " << jet_ORtrk_sumpt
849 << " electrk-jetORtrk: " << (el_trk_pt-jet_ORtrk_sumpt)
850 << " elec cal: " << el_cal_pt
851 << " jetalltrk-electrk: " << (jet_all_trk_pt-el_trk_pt)
852 << " jetalltrk-jetORtrk: " << (jet_all_trk_pt-jet_ORtrk_sumpt) );
853 // Want to use the jet calo measurement if we had at least one electron
854 // and the jet has a lot of residual track pt
855 // Is the cut appropriate?
856 if(el_trk_pt>1e-9 && jet_unique_trk_pt>10.0e3) passJetForEl=true;
857 } // end ele-track removal
858
859 for(auto mu_in_jet : muons_in_jet) {
860 float mu_Eloss = acc_Eloss(mu_in_jet.getXAODObject());
861
862 if(!JVT_reject) {
863 if (m_doRemoveMuonJets) {
864 // need to investigate how this is affected by the recording of muon clusters in the map
865 float mu_id_pt = mu_in_jet.getXAODObject().trackParticle(xAOD::Muon::InnerDetectorTrackParticle) ? mu_in_jet.getXAODObject().trackParticle(xAOD::Muon::InnerDetectorTrackParticle)->pt() : 0.;
866 float jet_trk_sumpt = m_acc_trksumpt.isAvailable(jet) && this->getPV() ? m_acc_trksumpt(jet)[this->getPV()->index()] : 0.;
867
868 // missed the muon, so we should add it back
869 if(0.9999*mu_id_pt>jet_trk_sumpt)
870 jet_trk_sumpt+=mu_id_pt;
871 float jet_trk_N = m_acc_trkN.isAvailable(jet) && this->getPV() ? m_acc_trkN(jet)[this->getPV()->index()] : 0.;
872 ATH_MSG_VERBOSE("Muon has ID pt " << mu_id_pt);
873 ATH_MSG_VERBOSE("Jet has pt " << m_jetMomAcc.pt(jet) << ", trk sumpt " << jet_trk_sumpt << ", trk N " << jet_trk_N);
874 bool jet_from_muon = mu_id_pt>1e-9 && jet_trk_sumpt>1e-9 && (m_jetMomAcc.pt(jet)/mu_id_pt < m_muIDPTJetPtRatioMuOlap && mu_id_pt/jet_trk_sumpt>m_jetTrkPtMuPt) && jet_trk_N<m_jetTrkNMuOlap;
875 if(jet_from_muon) {
876 ATH_MSG_VERBOSE("Jet is from muon -- remove.");
877 JVT_reject = true;
878 }
879 }
880
882 // need to investigate how this is affected by the recording of muon clusters in the map
883 float mu_id_pt = mu_in_jet.getXAODObject().trackParticle(xAOD::Muon::InnerDetectorTrackParticle) ? mu_in_jet.getXAODObject().trackParticle(xAOD::Muon::InnerDetectorTrackParticle)->pt() : 0.;
884 float jet_trk_sumpt = m_acc_trksumpt.isAvailable(jet) && this->getPV() ? m_acc_trksumpt(jet)[this->getPV()->index()] : 0.;
885 // missed the muon, so we should add it back
886 if(0.9999*mu_id_pt>jet_trk_sumpt)
887 jet_trk_sumpt+=mu_id_pt;
888 float jet_trk_N = m_acc_trkN.isAvailable(jet) && this->getPV() ? m_acc_trkN(jet)[this->getPV()->index()] : 0.;
889
890 float jet_psE = 0.;
891 if (m_acc_psf.isAvailable(jet)){
892 jet_psE = m_acc_psf(jet);
893 } else if (m_acc_sampleE.isAvailable(jet)){
894 jet_psE = m_acc_sampleE(jet)[0] + m_acc_sampleE(jet)[4];
895 } else {
896 ATH_MSG_ERROR("Jet PS fraction or sampling energy must be available to calculate MET with doSetMuonJetEMScale");
897 return StatusCode::FAILURE;
898 }
899
900 bool jet_from_muon = jet_trk_sumpt>1e-9 && jet_trk_N<3 && mu_id_pt / jet_trk_sumpt > m_jetTrkPtMuPt && m_acc_emf(jet)>m_jetEmfMuOlap && m_acc_width(jet)<m_jetWidthMuOlap && jet_psE>m_jetPsEMuOlap;
901 ATH_MSG_VERBOSE("Muon has ID pt " << mu_id_pt);
902 ATH_MSG_VERBOSE("Jet has trk sumpt " << jet_trk_sumpt << ", trk N " << jet_trk_N << ", PS E " << jet_psE << ", width " << m_acc_width(jet) << ", emfrac " << m_acc_emf(jet));
903
904 if(jet_from_muon) {
905 ATH_MSG_VERBOSE("Jet is from muon -- set to EM scale and subtract Eloss.");
906 // Using constjet now because we focus on AntiKt4EMTopo.
907 // Probably not a massive difference to LC, but PF needs some consideration
908 ATH_MSG_VERBOSE("Jet e: " << constjet.E() << ", mu Eloss: " << mu_Eloss);
909 float elosscorr = mu_Eloss >= constjet.e() ? 0. : 1.-mu_Eloss/constjet.e();
910 // Effectively, take the unique fraction of the jet times the eloss-corrected fraction
911 // This might in some cases oversubtract, but should err on the side of undercounting the jet contribution
912 opx *= elosscorr;
913 opy *= elosscorr;
914 ATH_MSG_VERBOSE(" Jet eloss factor " << elosscorr << ", final pt: " << sqrt(opx*opx+opy*opy));
915 // Don't treat this jet normally. Instead, just add to the Eloss term
916 isMuFSRJet = true;
917 }
918 }
919 } // end muon-jet overlap-removal
920
921 switch(mu_in_jet.getXAODObject().energyLossType()) {
922 using enum xAOD::Muon::EnergyLossType;
923 case Parametrized:
924 case MOP:
925 case Tail:
926 case FSRcandidate:
927 case NotIsolated:
928 // For now don't differentiate the behaviour
929 // Remove the Eloss assuming the parameterised value
930 // The correction is limited to the selected clusters
931 total_eloss += mu_Eloss;
932 muons_selflags |= (1<<m_assocAcc.findIndex(*assoc,mu_in_jet));
933 }
934 }
935 ATH_MSG_VERBOSE("Muon selection flags: " << muons_selflags);
936 ATH_MSG_VERBOSE("Muon total eloss: " << total_eloss);
937
939 // borrowed from overlapCalVec
940 for(size_t iKey = 0; iKey < m_assocAcc.sizeCal(*assoc); iKey++) {
941 bool selector = (muons_selflags & m_assocAcc.calkey(*assoc)[iKey]);
942 if(selector) mu_calovec += m_assocAcc.calVec(*assoc,iKey);
943 ATH_MSG_VERBOSE("This key: " << m_assocAcc.calkey(*assoc)[iKey] << ", selector: " << selector);
944 }
945 ATH_MSG_VERBOSE("Mu calovec pt, no Eloss: " << mu_calovec.cpt());
946 if(m_muEloss) mu_calovec *= std::max<float>(0.,1-(total_eloss/mu_calovec.ce()));
947 ATH_MSG_VERBOSE("Mu calovec pt, with Eloss: " << mu_calovec.cpt());
948
949 // re-add calo components of muons beyond Eloss correction
950 ATH_MSG_VERBOSE("Jet " << jet << " const pT before OR " << jpt);
951 ATH_MSG_VERBOSE("Jet " << jet << " const pT after OR " << sqrt(opx*opx+opy*opy));
952 opx += mu_calovec.cpx();
953 opy += mu_calovec.cpy();
954 double opt = sqrt( opx*opx+opy*opy );
955 ATH_MSG_VERBOSE("Jet " << jet << " const pT diff after OR readding muon clusters " << opt-jpt);
956 double uniquefrac = 1. - (calvec.ce() - mu_calovec.ce()) / constjet.E();
957 ATH_MSG_VERBOSE( "Jet constscale px, py, pt, E = " << jpx << ", " << jpy << ", " << jpt << ", " << constjet.E() );
958 ATH_MSG_VERBOSE( "Jet overlap E = " << calvec.ce() - mu_calovec.ce() );
959 ATH_MSG_VERBOSE( "Jet OR px, py, pt, E = " << opx << ", " << opy << ", " << opt << ", " << constjet.E() - calvec.ce() );
960
961 if(isMuFSRJet) {
962 if(!met_muonEloss){
963 ATH_MSG_ERROR("Attempted to apply muon Eloss correction, but corresponding MET term does not exist!");
964 return StatusCode::FAILURE;
965 }
966 m_outputMetMomAcc.addParticle(met_muonEloss.value(),opx,opy,opt);
967 continue;
968 }
969
970 if(selected && !JVT_reject) {
971 if(!caloverlap) {
972 // add jet full four-vector
973 hardJet = true;
974 if (!tracksForHardJets) {
975 if(m_doConstJet)
976 m_outputMetMomAcc.addParticle(metJet,jpx,jpy,jpt);
977 else
978 m_outputMetMomAcc.addParticle(metJet,m_jetMomAcc,jet);
979 }
980 }
981 else if((uniquefrac>m_jetMinEfrac || passJetForEl) && opt>m_jetMinWeightedPt){
982 // add jet corrected for overlaps if sufficient unique fraction
983 hardJet = true;
984 if(!tracksForHardJets) {
985 if(m_jetCorrectPhi) {
986 if (m_doConstJet)
987 m_outputMetMomAcc.addParticle(metJet,opx,opy,opt);
988 else {
989 double jesF = m_jetMomAcc.pt(jet) / jpt;
990 m_outputMetMomAcc.addParticle(metJet,opx*jesF,opy*jesF,opt*jesF);
991 }
992 } else {
993 if (m_doConstJet)
994 m_outputMetMomAcc.addParticle(metJet,uniquefrac*jpx,uniquefrac*jpy,uniquefrac*jpt);
995 else{
996 if(passJetForEl && m_doRemoveElecTrksEM)
997 m_outputMetMomAcc.addParticle(metJet,opx,opy,opt);
998 else
999 m_outputMetMomAcc.addParticle(metJet,uniquefrac*m_jetMomAcc.px(jet),uniquefrac*m_jetMomAcc.py(jet),uniquefrac*m_jetMomAcc.pt(jet));
1000 }
1001 }
1002 }
1003 }
1004 } // hard jet selection
1005
1006 if(hardJet){
1007 ATH_MSG_VERBOSE("Jet added at full scale");
1008 metJetWeights.emplace_back(jet, uniquefrac);
1009 } else {
1010 if(metSoftClus && !JVT_reject) {
1011 // add fractional contribution
1012 ATH_MSG_VERBOSE("Jet added at const scale");
1013 if (std::abs(m_jetMomAcc.eta(jet))<2.5 || !(coreSoftClus.value()(m_inputMetSourceAcc)&MissingETBase::Source::Region::Central)) {
1014 metSoftClusLinks->emplace_back (jet, uniquefrac);
1015 m_outputMetMomAcc.addParticle(metSoftClus.value(),opx,opy,opt);
1016 }
1017
1018 // Fill a vector with the soft constituents, if one was provided.
1019 // For now, only setting up to work with those corresponding to the jet constituents.
1020 // Can expand if needed.
1021 // This ignores overlap removal.
1022 //
1023 if(softConst) {
1024 for(size_t iConst=0; iConst<jet.getXAODObject().numConstituents(); ++iConst) {
1025 const IParticle* constit = jet.getXAODObject().rawConstituent(iConst);
1026 softConst->push_back(constit);
1027 }
1028 }
1029 }
1030 } // hard jet or CST
1031
1032 if(!metSoftTrk || (hardJet && !tracksForHardJets)) continue;
1033
1034 // use jet tracks
1035 // remove any tracks already used by other objects
1036 MissingETBase::Types::constvec_t trkvec = helper.overlapTrkVec(*assoc);
1037 MissingETBase::Types::constvec_t jettrkvec = m_assocAcc.jetTrkVec(*assoc);
1038 if(jettrkvec.ce()>1e-9) {
1039 jpx = jettrkvec.cpx();
1040 jpy = jettrkvec.cpy();
1041 jpt = jettrkvec.sumpt();
1042 jettrkvec -= trkvec;
1043 opx = jettrkvec.cpx();
1044 opy = jettrkvec.cpy();
1045 opt = jettrkvec.sumpt();
1046 ATH_MSG_VERBOSE( "Jet track px, py, sumpt = " << jpx << ", " << jpy << ", " << jpt );
1047 ATH_MSG_VERBOSE( "Jet OR px, py, sumpt = " << opx << ", " << opy << ", " << opt );
1048 } else {
1049 opx = opy = opt = 0;
1050 ATH_MSG_VERBOSE( "This jet has no associated tracks" );
1051 }
1052 if (hardJet) m_outputMetMomAcc.addParticle(metJet,opx,opy,opt);
1053 else if (std::abs(m_jetMomAcc.eta(jet))<2.5 || !(coreSoftTrk.value()(m_inputMetSourceAcc)&MissingETBase::Source::Region::Central)) {
1054 m_outputMetMomAcc.addParticle(metSoftTrk.value(),opx,opy,opt);
1055 // Don't need to add if already done for softclus.
1056 if(!metSoftClus) {
1057 if (metSoftTrkLinks) metSoftTrkLinks->emplace_back (jet, uniquefrac);
1058 }
1059
1060 // Fill a vector with the soft constituents, if one was provided.
1061 // For now, only setting up to work with those corresponding to the jet constituents.
1062 // Can expand if needed.
1063 // This ignores overlap removal.
1064 //
1065 if(softConst && !m_doPFlow && !m_doSoftTruth) {
1066 std::vector<const IParticle*> jettracks;
1067 jet.getXAODObject().getAssociatedObjects<IParticle>(xAOD::JetAttribute::GhostTrack,jettracks);
1068 for(size_t iConst=0; iConst<jettracks.size(); ++iConst) {
1069 const TrackParticle* pTrk = static_cast<const TrackParticle*>(jettracks[iConst]);
1070 if (acceptTrack(pTrk,pv)) softConst->push_back(pTrk);
1071 }
1072 }
1073 }
1074 } // jet loop
1075
1076 ATH_MSG_DEBUG("Number of selected jets: " << metJetWeights.size());
1077
1078 if(metSoftTrk) {
1079 ATH_MSG_DEBUG("Number of softtrk jets: " << metSoftTrkLinks->size());
1080 }
1081
1082 if(metSoftClus) {
1083 ATH_MSG_DEBUG("Number of softclus jets: " << metSoftClusLinks->size());
1084 }
1085
1086 if(softConst) ATH_MSG_DEBUG(softConst->size() << " soft constituents from core term + jets");
1087
1088 auto assoc = helper.getMiscAssociation();
1089 if(!assoc) return StatusCode::SUCCESS;
1090
1091 if(metSoftTrk) {
1092 // supplement track term with any tracks associated to isolated muons
1093 // these are recorded in the misc association
1094 MissingETBase::Types::constvec_t trkvec = helper.overlapTrkVec(*assoc);
1095 double opx = trkvec.cpx();
1096 double opy = trkvec.cpy();
1097 double osumpt = trkvec.sumpt();
1098 ATH_MSG_VERBOSE( "Misc track px, py, sumpt = " << opx << ", " << opy << ", " << osumpt );
1099 m_outputMetMomAcc.addParticle(metSoftTrk.value(),opx,opy,osumpt);
1100 ATH_MSG_VERBOSE("Final soft track mpx " << m_outputMetMomAcc.mpx(metSoftTrk.value())
1101 << ", mpy " << m_outputMetMomAcc.mpy(metSoftTrk.value())
1102 << " sumet " << m_outputMetMomAcc.sumet(metSoftTrk.value()));
1103 }
1104
1105 if(metSoftClus) {
1106 // supplement cluster term with any clusters associated to isolated e/gamma
1107 // these are recorded in the misc association
1108 float total_eloss(0.);
1109 MissingETBase::Types::bitmask_t muons_selflags(0);
1110 MissingETBase::Types::constvec_t calvec = helper.overlapCalVec(*assoc);
1111 double opx = calvec.cpx();
1112 double opy = calvec.cpy();
1113 double osumpt = calvec.sumpt();
1114 for(const auto objId : m_assocAcc.objects(*assoc)) {
1115 auto *obj = objId.getXAODObject();
1116 if (!obj || obj->type() != xAOD::Type::Muon) continue;
1117 const xAOD::Muon* mu_test(static_cast<const xAOD::Muon*>(obj));
1118 if(acc_originalObject.isAvailable(*mu_test)) mu_test = static_cast<const xAOD::Muon*>(*acc_originalObject(*mu_test));
1119 if(helper.objSelected(mu_test)) { //
1120 float mu_Eloss = acc_Eloss(*mu_test);
1121 switch(mu_test->energyLossType()) {
1122 using enum xAOD::Muon::EnergyLossType;
1123 case Parametrized:
1124 case MOP:
1125 case Tail:
1126 case FSRcandidate:
1127 case NotIsolated:
1128 // For now don't differentiate the behaviour
1129 // Remove the Eloss assuming the parameterised value
1130 // The correction is limited to the selected clusters
1131 total_eloss += mu_Eloss;
1132 muons_selflags |= (1<<m_assocAcc.findIndex(*assoc,mu_test));
1133 }
1134 ATH_MSG_VERBOSE("Mu index " << mu_test->index());
1135 }
1136 }
1137 ATH_MSG_VERBOSE("Mu selection flags " << muons_selflags);
1138 ATH_MSG_VERBOSE("Mu total eloss " << total_eloss);
1139
1141 // borrowed from overlapCalVec
1142 for(size_t iKey = 0; iKey < m_assocAcc.sizeCal(*assoc); iKey++) {
1143 bool selector = (muons_selflags & m_assocAcc.calkey(*assoc)[iKey]);
1144 ATH_MSG_VERBOSE("This key: " << m_assocAcc.calkey(*assoc)[iKey] << ", selector: " << selector
1145 << " this calvec E: " << m_assocAcc.calVec(*assoc,iKey).ce());
1146 if(selector) mu_calovec += m_assocAcc.calVec(*assoc,iKey);
1147 }
1148 if(m_muEloss){
1149 mu_calovec *= std::max<float>(0.,1-(total_eloss/mu_calovec.ce()));
1150 opx += mu_calovec.cpx();
1151 opy += mu_calovec.cpy();
1152 osumpt += mu_calovec.sumpt();
1153 }
1154 ATH_MSG_VERBOSE("Mu cluster sumpt " << mu_calovec.sumpt());
1155
1156 ATH_MSG_VERBOSE( "Misc cluster px, py, sumpt = " << opx << ", " << opy << ", " << osumpt );
1157 m_outputMetMomAcc.addParticle(metSoftClus.value(),opx,opy,osumpt);
1158 ATH_MSG_VERBOSE("Final soft cluster mpx " << m_outputMetMomAcc.mpx(metSoftClus.value())
1159 << ", mpy " << m_outputMetMomAcc.mpy(metSoftClus.value())
1160 << " sumet " << m_outputMetMomAcc.sumet(metSoftClus.value()));
1161 }
1162
1163 return StatusCode::SUCCESS;
1164 }
1165
1167 const xAOD::JetContainer* jets,
1169 xAOD::MissingET* metSoftTrk,
1170 const xAOD::MissingET* coreSoftTrk,
1171 bool doJetJVT) const {
1172 if (!metJet) {
1173 ATH_MSG_ERROR("No MET object provided for track MET rebuilding");
1174 return StatusCode::FAILURE;
1175 }
1176 if (!metSoftTrk) {
1177 ATH_MSG_ERROR("No MET object provided for soft track MET rebuilding");
1178 return StatusCode::FAILURE;
1179 }
1180 if (!jets) {
1181 ATH_MSG_ERROR("No jet container provided for track MET rebuilding");
1182 return StatusCode::FAILURE;
1183 }
1184
1185 columnar::MutableMetRange metCont (*static_cast<MissingETContainer*>(metJet->container()));
1186 return rebuildJetMET(columnar::MutableMetId(*metJet),metCont,columnar::JetRange(*jets),m_assocAcc(helper),std::nullopt,nullptr,columnar::MutableMetId(*metSoftTrk),coreSoftTrk,doJetJVT,true);
1187 }
1188
1191 columnar::JetRange jets,
1193 columnar::MutableMetId metSoftTrk,
1194 columnar::Met1Id coreSoftTrk,
1195 bool doJetJVT) const {
1196 return rebuildJetMET(metJet,metCont,jets,helper,std::nullopt,nullptr,metSoftTrk,coreSoftTrk,doJetJVT,true);
1197 }
1198
1199 // **** Remove objects and any overlaps from MET calculation ****
1202 xAOD::MissingETContainer* metCont) const
1203 {
1204 if (!collection) {
1205 ATH_MSG_ERROR("No object container provided for marking invisible");
1206 return StatusCode::FAILURE;
1207 }
1208 if (!metCont) {
1209 ATH_MSG_ERROR("No MET container provided for marking invisible");
1210 return StatusCode::FAILURE;
1211 }
1212
1213 return markInvisible(columnar::ParticleRange(*collection),m_assocAcc(helper),columnar::MutableMetRange(*metCont));
1214 }
1215
1218 columnar::MutableMetRange metCont) const
1219 {
1220 columnar::MutableMetId met = m_outputMetMapAcc.fillMET (metCont, "Invisibles", invisSource);
1222 }
1223
1225 {
1226 return static_cast<bool>(m_trkseltool->accept( *trk, vx ));
1227 }
1228
1230
1232
1233 if(!h_PV.isValid()) {
1234 ATH_MSG_WARNING("Unable to retrieve primary vertex container PrimaryVertices");
1235 return nullptr;
1236 }
1237 ATH_MSG_DEBUG("Successfully retrieved primary vertex container");
1238 if(h_PV->empty()) ATH_MSG_WARNING("Event has no primary vertices!");
1239 for(const xAOD::Vertex* vx : *h_PV) {
1240 if(vx->vertexType()==xAOD::VxType::PriVtx) return vx;
1241 }
1242 return nullptr;
1243 }
1244
1245
1246
1248 {
1249 for (columnar::EventContextId event : events)
1250 {
1251 auto met = m_outputMetHandle (event);
1252 auto metcore = m_inputMetHandle (event);
1253 auto metassoc = m_metAssocHandle (event);
1254 if (m_columnarOperation.value() == 0u)
1255 {
1256 auto particles = m_particlesHandle (event);
1258 throw std::runtime_error ("Failed to rebuild MET");
1259 } else if (m_columnarOperation.value() == 1u)
1260 {
1261 auto jets = m_jetsHandle (event);
1262 if (rebuildJetMET (m_columnarJetKey.value(), m_columnarSoftClusKey.value(), met, jets, metcore, m_assocAcc(metassoc), m_columnarDoJetJVT.value()).isFailure())
1263 throw std::runtime_error ("Failed to rebuild jet MET");
1264 } else if (m_columnarOperation.value() == 2u)
1265 {
1266 auto jets = m_jetsHandle (event);
1267 if (rebuildTrackMET (m_columnarJetKey.value(), m_columnarSoftClusKey.value(), met, jets, metcore, m_assocAcc(metassoc), m_columnarDoJetJVT.value()).isFailure())
1268 throw std::runtime_error ("Failed to rebuild track MET");
1269 } else
1270 {
1271 throw std::runtime_error ("Unknown columnar operation");
1272 }
1273 }
1274 }
1275
1276} //> end namespace met
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
Handle class for reading from StoreGate.
Base class for elements of a container that can have aux data.
Defines enum to access jet attribute and associated particles/objects.
Gaudi::Details::PropertyBase & declareProperty(Gaudi::Property< T, V, H > &t)
virtual bool isValid() override final
Can the handle be successfully dereferenced?
StatusCode setProperty(const std::string &name, const T &value)
set the given property
an object that can create a AsgTool
::StatusCode makePrivateTool(ToolHandle< T > &toolHandle) const
make a private tool with the given configuration
AsgTool(const std::string &name)
Constructor specifying the tool instance's name.
Definition AsgTool.cxx:58
a columnar version of xAOD::MissingETAssociationHelper
a "handle" for recording object weights via ObjectWeightDecorator
Definition MetOutput.h:70
columnar::ColumnAccessor< columnar::MetAssociationDef, columnar::ObjectColumn > m_metAssocHandle
columnar::MetHelpers::MetMomentumAccessors< columnar::MutableMetDef > m_outputMetMomAcc
Gaudi::Property< std::string > m_inputPreselectionName
void callEvents(columnar::EventContextRange events) const override
Gaudi::Property< std::string > m_columnarJetKey
bool acceptTrack(const xAOD::TrackParticle *trk, const xAOD::Vertex *vx) const
columnar::JetAccessor< float > m_acc_width
columnar::JetAccessor< std::vector< int > > m_acc_trkN
std::optional< columnar::ParticleAccessor< char > > m_inputPreselectionAcc
columnar::MetAssocationAccessors m_assocAcc
virtual ~ColumnarMETMaker()
Destructor:
virtual StatusCode markInvisible(const xAOD::IParticleContainer *collection, xAOD::MissingETAssociationHelper &helper, xAOD::MissingETContainer *metCont) const override final
virtual StatusCode initialize() override final
Dummy implementation of the initialisation function.
columnar::Met1Accessor< columnar::ObjectColumn > m_inputMetHandle
columnar::ParticleAccessor< columnar::RetypeColumn< xAOD::Muon::MuonType, std::uint16_t > > m_inputMuonTypeAcc
columnar::MutableMetAccessor< std::string > m_outputMetNameAcc
columnar::MetHelpers::MapLookupAccessor< columnar::MutableMetDef > m_outputMetMapAcc
columnar::JetAccessor< float > m_acc_emf
ToolHandle< InDet::IInDetTrackSelectionTool > m_trkseltool
columnar::JetAccessor< columnar::ObjectColumn > m_jetsHandle
columnar::MutableMetAccessor< columnar::ObjectColumn > m_outputMetHandle
virtual StatusCode rebuildTrackMET(const std::string &metJetKey, const std::string &softTrkKey, xAOD::MissingETContainer *metCont, const xAOD::JetContainer *jets, const xAOD::MissingETContainer *metCoreCont, xAOD::MissingETAssociationHelper &helper, bool doJetJVT) const override final
Gaudi::Property< bool > m_columnarDoJetJVT
columnar::JetAccessor< std::vector< float > > m_acc_trksumpt
columnar::Met1Accessor< MissingETBase::Types::bitmask_t > m_inputMetSourceAcc
Gaudi::Property< std::string > m_columnarTermName
SG::ReadHandleKey< xAOD::VertexContainer > m_PVkey
columnar::JetAccessor< std::vector< float > > m_acc_sampleE
virtual StatusCode rebuildJetMET(const std::string &metJetKey, const std::string &softClusKey, const std::string &softTrkKey, xAOD::MissingETContainer *metCont, const xAOD::JetContainer *jets, const xAOD::MissingETContainer *metCoreCont, xAOD::MissingETAssociationHelper &helper, bool doJetJVT) const override final
columnar::MetHelpers::InputMomentumAccessors m_inputMomAcc
columnar::MetHelpers::ObjectWeightDecorator m_outputMetWeightDecRegular
columnar::JetAccessor< float > m_acc_psf
std::optional< columnar::MetHelpers::InputMomentumAccessors< columnar::JetDef > > m_jetConstitScaleMomAcc
columnar::MetHelpers::ObjectWeightDecorator< columnar::MutableMetDef, columnar::JetDef > m_jetOutputMetWeightDecSoft
const xAOD::Vertex * getPV() const
columnar::MetHelpers::MapLookupAccessor< columnar::Met1Def > m_inputMetMapAcc
SG::ReadHandleKey< xAOD::JetContainer > m_jetContainer
columnar::MetHelpers::ObjectWeightDecorator< columnar::MutableMetDef, columnar::JetDef > m_jetOutputMetWeightDecRegular
columnar::MetHelpers::MetMomentumAccessors< columnar::Met1Def > m_inputMetMomAcc
columnar::ParticleAccessor< columnar::ObjectColumn > m_particlesHandle
ColumnarMETMaker()
Default constructor:
std::optional< columnar::MetHelpers::InputMomentumAccessors< columnar::JetDef > > m_jetConstitScaleMomFixedAcc
Gaudi::Property< unsigned > m_columnarOperation
ToolHandle< IAsgSelectionTool > m_JvtTool
columnar::ElectronAccessor< columnar::RetypeColumn< double, float > > m_electronPtAcc
columnar::MetHelpers::ObjectTypeAccessor< columnar::ParticleDef > m_inputObjTypeAcc
Gaudi::Property< std::string > m_columnarSoftClusKey
std::optional< columnar::JetAccessor< char > > m_acc_jetRejectionDec
columnar::MetHelpers::InputMomentumAccessors< columnar::JetDef > m_jetMomAcc
Gaudi::Property< unsigned > m_columnarParticleType
virtual StatusCode rebuildMET(const std::string &metKey, xAOD::Type::ObjectType metType, xAOD::MissingETContainer *metCont, const xAOD::IParticleContainer *collection, xAOD::MissingETAssociationHelper &helper, MissingETBase::UsageHandler::Policy objScale) const override final
Class providing the definition of the 4-vector interface.
virtual Type::ObjectType type() const =0
The type of the object as a simple enumeration.
A vector of jet constituents at the scale used during jet finding.
float sumpt() const
Returns sum of component pt.
EnergyLossType energyLossType(void) const
Energy determined from parametrization or not (measured).
xAOD::MissingETAssociation_v1::ConstVec constvec_t
Type for constituent vector.
uint64_t bitmask_t
Type for status word bit mask.
@ OnlyCluster
CaloCluster based only.
@ ParticleFlow
Particle Flow Object based.
ObjectRange< Met1Def > Met1Range
Definition MetDef.h:58
ObjectRange< JetDef > JetRange
Definition JetDef.h:22
OptObjectId< Met1Def > OptMet1Id
Definition MetDef.h:60
ObjectRange< EventContextDef > EventContextRange
ObjectId< EventContextDef > EventContextId
OptObjectId< MutableMetDef > OptMutableMetId
Definition MetDef.h:66
ObjectId< Met1Def > Met1Id
Definition MetDef.h:59
ObjectId< MutableMetDef > MutableMetId
Definition MetDef.h:65
ObjectRange< MutableMetDef > MutableMetRange
Definition MetDef.h:64
ObjectRange< ParticleDef > ParticleRange
Definition ParticleDef.h:32
Definition index.py:1
ElementLink< xAOD::IParticleContainer > iplink_t
static const SG::AuxElement::Accessor< std::vector< float > > dec_constitObjWeights("ConstitObjectWeights")
static const SG::AuxElement::ConstAccessor< float > acc_Eloss("EnergyLoss")
static const SG::AuxElement::ConstAccessor< iplink_t > acc_nominalObject("nominalObjectLink")
static const SG::AuxElement::Accessor< std::vector< iplink_t > > dec_constitObjLinks("ConstitObjectLinks")
static const MissingETBase::Types::bitmask_t invisSource
Definition METHelpers.h:38
static const SG::AuxElement::ConstAccessor< iplink_t > acc_originalObject("originalObjectLink")
static const SG::AuxElement::ConstAccessor< std::vector< iplink_t > > acc_ghostMuons("GhostMuon")
ObjectType
Type of objects that have a representation in the xAOD EDM.
Definition ObjectType.h:32
@ Jet
The object is a jet.
Definition ObjectType.h:40
@ Photon
The object is a photon.
Definition ObjectType.h:47
@ Muon
The object is a muon.
Definition ObjectType.h:48
@ Electron
The object is an electron.
Definition ObjectType.h:46
@ Tau
The object is a tau (jet).
Definition ObjectType.h:49
@ PriVtx
Primary vertex.
Jet_v1 Jet
Definition of the current "jet version".
MissingETAssociation_v1 MissingETAssociation
Version control by type definition.
MissingET_v1 MissingET
Version control by type defintion.
TrackParticle_v1 TrackParticle
Reference the current persistent version:
Vertex_v1 Vertex
Define the latest version of the vertex class.
Muon_v1 Muon
Reference the current persistent version:
setBGCode setTAP setLVL2ErrorBits bool
MissingETAuxContainer_v1 MissingETAuxContainer
JetContainer_v1 JetContainer
Definition of the current "jet container version".
MissingETAssociationMap_v1 MissingETAssociationMap
Version control by type defintion.
Electron_v1 Electron
Definition of the current "egamma version".
DataVector< IParticle > IParticleContainer
Simple convenience declaration of IParticleContainer.
ROOT::Math::LorentzVector< ROOT::Math::PtEtaPhiM4D< double > > JetFourMom_t
Base 4 Momentum type for Jet.
Definition JetTypes.h:17
static Types::bitmask_t muon(Region reg=Region::FullAcceptance)
Standard MET term from reconstructed muons.
static Types::bitmask_t jet(Region reg=Region::FullAcceptance)
Standard MET term from reconstructed jets.
@ Calo
Indicator for MET terms reconstructed from calorimeter signals alone.
static Types::bitmask_t track(Region reg=Region::FullAcceptance)
Bit mask for MET term from Track signal objects.
static bool isTrackTerm(Types::bitmask_t bits, Region reg=Region::FullAcceptance)
static Types::bitmask_t tau(Region reg=Region::FullAcceptance)
Standard MET term from reconstructed tau leptons.
static Types::bitmask_t electron(Region reg=Region::FullAcceptance)
Standard MET term from reconstructed electrons.
static Types::bitmask_t photon(Region reg=Region::FullAcceptance)
Standard MET term from reconstructed photons.
@ Central
Indicator for MET contribution from the central region.
static constexpr bool isXAOD
Whether this is the xAOD mode.
Collection of functions managing the MET composition map and association map.