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
67
69
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 && m_inputMuonTypeAcc.getOptional(orig)==xAOD::Muon::CaloTagged) {
409 for (decltype(auto) assoc : assocs) {
410 auto ind = m_assocAcc.overlapIndices(assoc,orig);
411 auto allObjects = m_assocAcc.objects(assoc);
412 for (size_t indi = 0; indi < ind.size(); indi++) if (allObjects[ind[indi]]) {
413 if (allObjects[ind[indi]].isContainer<columnar::ContainerId::electron>()
414 && helper.objSelected(assoc, ind[indi])) {
415 selected = false;
416 break;
417 }
418 }
419 }
420 }
421 // Don't overlap remove muons, but flag the non-overlapping muons to take out their tracks from jets
422 // Removed eloss from here -- clusters already flagged.
423 // To be handled in rebuildJetMET
424 if(selected) {
426 ATH_MSG_VERBOSE("Add object with pt " << m_inputMomAcc.pt(obj));
427 m_outputMetMomAcc.addParticle (met, m_inputMomAcc, obj);
428 } else {
429 MissingETBase::Types::constvec_t constvec = helper.getConstVec(obj,objScale);
430 ATH_MSG_VERBOSE("Add truth object with pt " << constvec.cpt());
431 m_outputMetMomAcc.addParticle (met, constvec.cpx(),constvec.cpy(),constvec.cpt());
432 }
433 metWeights.emplace_back( obj, 1. );
434 }
435 }
436 ATH_MSG_DEBUG("Built met term " << met(m_outputMetNameAcc) << ", with magnitude " << m_outputMetMomAcc.met(met));
437 return StatusCode::SUCCESS;
438 }
439
440 StatusCode ColumnarMETMaker::rebuildJetMET(const std::string& metJetKey,
441 const std::string& softKey,
443 const xAOD::JetContainer* jets,
444 const xAOD::MissingETContainer* metCoreCont,
446 bool doJetJVT) const
447 {
448 if (!metCont || !metCoreCont)
449 {
450 ATH_MSG_ERROR("No MET container provided");
451 return StatusCode::FAILURE;
452 }
453 if (!jets)
454 {
455 ATH_MSG_ERROR("No input collection provided for MET term \"" << metJetKey << "\" and soft term: " << softKey);
456 return StatusCode::FAILURE;
457 }
458
459 return rebuildJetMET(metJetKey,softKey,columnar::MutableMetRange(*metCont),columnar::JetRange(*jets),columnar::Met1Range(*metCoreCont),m_assocAcc(helper),doJetJVT);
460 }
461
462 StatusCode ColumnarMETMaker::rebuildJetMET(const std::string& metJetKey,
463 const std::string& softKey,
466 columnar::Met1Range metCoreCont,
468 bool doJetJVT) const
469 {
470 ATH_MSG_VERBOSE("Rebuild jet term: " << metJetKey << " and soft term: " << softKey);
471
472 columnar::MutableMetId metJet {m_outputMetMapAcc.fillMET (metCont, metJetKey, MissingETBase::Source::jet())};
473
474 columnar::OptMet1Id coreSoftClus, coreSoftTrk;
475 columnar::OptMutableMetId metSoftClus, metSoftTrk;
476
477 columnar::OptMet1Id coreSoft = m_inputMetMapAcc(metCoreCont,softKey+"Core");
478 if(!coreSoft) {
479 ATH_MSG_WARNING("Invalid soft term key supplied: " << softKey);
480 return StatusCode::FAILURE;
481 }
483 coreSoftTrk = coreSoft;
484
485 metSoftTrk = m_outputMetMapAcc.fillMET (metCont, softKey, coreSoftTrk.value()(m_inputMetSourceAcc));
486 } else {
487 coreSoftClus = coreSoft;
488
489 metSoftClus = m_outputMetMapAcc.fillMET (metCont, softKey, coreSoftClus.value()(m_inputMetSourceAcc));
490 }
491
492 return rebuildJetMET(metJet, metCont, jets, helper,
493 metSoftClus, coreSoftClus,
494 metSoftTrk, coreSoftTrk,
495 doJetJVT);
496 }
497
498 StatusCode ColumnarMETMaker::rebuildTrackMET(const std::string& metJetKey,
499 const std::string& softKey,
501 const xAOD::JetContainer* jets,
502 const xAOD::MissingETContainer* metCoreCont,
504 bool doJetJVT) const
505 {
506 if (!metCont)
507 {
508 ATH_MSG_ERROR("No MET container provided");
509 return StatusCode::FAILURE;
510 }
511 if (!jets)
512 {
513 ATH_MSG_ERROR("No input collection provided for MET term \"" << metJetKey << "\" and soft term: " << softKey);
514 return StatusCode::FAILURE;
515 }
516
517 return rebuildTrackMET(metJetKey,softKey,columnar::MutableMetRange(*metCont),columnar::JetRange(*jets),columnar::Met1Range(*metCoreCont),m_assocAcc(helper),doJetJVT);
518 }
519
520 StatusCode ColumnarMETMaker::rebuildTrackMET(const std::string& metJetKey,
521 const std::string& softKey,
524 columnar::Met1Range metCoreCont,
526 bool doJetJVT) const
527 {
528 ATH_MSG_VERBOSE("Rebuild jet term: " << metJetKey << " and soft term: " << softKey);
529
531
532 columnar::OptMet1Id coreSoft = m_inputMetMapAcc(metCoreCont,softKey+"Core");
533 if(!coreSoft) {
534 ATH_MSG_WARNING("Invalid soft term key supplied: " << softKey);
535 return StatusCode::FAILURE;
536 }
537 auto coreSoftTrk = coreSoft.value();
538
539 columnar::MutableMetId metSoftTrk {m_outputMetMapAcc.fillMET (metCont, softKey, coreSoftTrk(m_inputMetSourceAcc))};
540
541 return rebuildTrackMET(metJet, metCont, jets, helper,
542 metSoftTrk, coreSoftTrk,
543 doJetJVT);
544 }
545
546 StatusCode ColumnarMETMaker::rebuildJetMET(const std::string& metJetKey,
547 const std::string& softClusKey,
548 const std::string& softTrkKey,
550 const xAOD::JetContainer* jets,
551 const xAOD::MissingETContainer* metCoreCont,
553 bool doJetJVT) const
554 {
555 if (!metCont)
556 {
557 ATH_MSG_ERROR("No MET container provided");
558 return StatusCode::FAILURE;
559 }
560 if (!jets)
561 {
562 ATH_MSG_ERROR("No input collection provided for MET term \"" << metJetKey << "\" and soft term: " << softClusKey << " and " << softTrkKey);
563 return StatusCode::FAILURE;
564 }
565
566 return rebuildJetMET(metJetKey,softClusKey,softTrkKey,columnar::MutableMetRange(*metCont),columnar::JetRange(*jets),columnar::Met1Range(*metCoreCont),m_assocAcc(helper),doJetJVT);
567 }
568
569 StatusCode ColumnarMETMaker::rebuildJetMET(const std::string& metJetKey,
570 const std::string& softClusKey,
571 const std::string& softTrkKey,
574 columnar::Met1Range metCoreCont,
576 bool doJetJVT) const
577 {
578
579 ATH_MSG_VERBOSE("Create Jet MET " << metJetKey);
580 columnar::MutableMetId metJet {m_outputMetMapAcc.fillMET (metCont, metJetKey, MissingETBase::Source::jet())};
581 ATH_MSG_VERBOSE("Create SoftClus MET " << softClusKey);
582 auto coreSoftClus = m_inputMetMapAcc(metCoreCont,softClusKey+"Core");
583 ATH_MSG_VERBOSE("Create SoftTrk MET " << softTrkKey);
584 auto coreSoftTrk = m_inputMetMapAcc(metCoreCont,softTrkKey+"Core");
585 if(!coreSoftClus) {
586 ATH_MSG_WARNING("Invalid cluster soft term key supplied: " << softClusKey);
587 return StatusCode::FAILURE;
588 }
589 if(!coreSoftTrk) {
590 ATH_MSG_WARNING("Invalid track soft term key supplied: " << softTrkKey);
591 return StatusCode::FAILURE;
592 }
593 columnar::MutableMetId metSoftClus {m_outputMetMapAcc.fillMET (metCont, softClusKey, m_inputMetSourceAcc(coreSoftClus.value()))};
594
595 columnar::MutableMetId metSoftTrk {m_outputMetMapAcc.fillMET (metCont, softTrkKey, m_inputMetSourceAcc(coreSoftTrk.value()))};
596
597 return rebuildJetMET(metJet, metCont, jets, helper,
598 metSoftClus, coreSoftClus,
599 metSoftTrk, coreSoftTrk,
600 doJetJVT);
601 }
602
604 const xAOD::JetContainer* jets,
606 xAOD::MissingET* metSoftClus,
607 const xAOD::MissingET* coreSoftClus,
608 xAOD::MissingET* metSoftTrk,
609 const xAOD::MissingET* coreSoftTrk,
610 bool doJetJVT,
611 bool tracksForHardJets,
612 std::vector<const xAOD::IParticle*>* softConst) const {
613 if(!metJet || !jets) {
614 ATH_MSG_ERROR("Invalid pointer supplied for "
615 << "MET (" << metJet << ") or "
616 << "jet collection (" << jets << ").");
617 return StatusCode::FAILURE;
618 }
619 columnar::MutableMetRange metCont (*static_cast<MissingETContainer*>(metJet->container()));
620 return rebuildJetMET(columnar::MutableMetId(*metJet), metCont, columnar::JetRange(*jets), m_assocAcc(helper),
621 columnar::OptMutableMetId(metSoftClus), columnar::OptMet1Id (coreSoftClus),
622 columnar::OptMutableMetId(metSoftTrk), columnar::OptMet1Id (coreSoftTrk),
623 doJetJVT, tracksForHardJets, softConst);
624 }
625
630 columnar::OptMutableMetId metSoftClus,
631 columnar::OptMet1Id coreSoftClus,
632 columnar::OptMutableMetId metSoftTrk,
633 columnar::OptMet1Id coreSoftTrk,
634 bool doJetJVT,
635 bool tracksForHardJets,
636 std::vector<const xAOD::IParticle*>* softConst) const {
637 if(softConst && m_trkseltool.empty() && !m_doPFlow && !m_doSoftTruth) {
638 ATH_MSG_WARNING( "Requested soft track element links, but no track selection tool supplied.");
639 }
640 const xAOD::Vertex *pv = softConst?getPV():nullptr;
641
642 if(helper.map().empty()) {
643 ATH_MSG_WARNING("Incomplete association map received. Cannot rebuild MET.");
644 ATH_MSG_WARNING("Note: ColumnarMETMaker should only be run on events containing at least one PV");
645 return StatusCode::SUCCESS;
646 }
647
648 if(doJetJVT && m_JvtWP == "None"){
649 ATH_MSG_WARNING("rebuildJetMET requested JVT, which is inconsistent with jet selection " << m_jetSelection << ". Ignoring JVT.");
650 doJetJVT = false;
651 }
652
653 ATH_MSG_VERBOSE("Building MET jet term " << metJet(m_outputMetNameAcc));
654 if(!metSoftClus && !metSoftTrk) {
655 ATH_MSG_WARNING("Neither soft cluster nor soft track term has been supplied!");
656 return StatusCode::SUCCESS;
657 }
658 static const SG::AuxElement::ConstAccessor<std::vector<ElementLink<IParticleContainer> > > acc_softConst("softConstituents");
659 std::optional<columnar::MetHelpers::ObjectWeightHandle<columnar::ContainerId::mutableMet,columnar::ContainerId::jet>> metSoftClusLinks;
660 if(metSoftClus) {
661 metSoftClusLinks.emplace(*this,m_jetOutputMetWeightDecSoft,metSoftClus.value(),jets);
662 if(!coreSoftClus) {
663 ATH_MSG_ERROR("Soft cluster term provided without a core term!");
664 return StatusCode::FAILURE;
665 }
666 ATH_MSG_VERBOSE("Building MET soft cluster term " << metSoftClus.value()(m_outputMetNameAcc));
667 ATH_MSG_VERBOSE("Core soft cluster mpx " << m_inputMetMomAcc.mpx(coreSoftClus.value())
668 << ", mpy " << m_inputMetMomAcc.mpy(coreSoftClus.value())
669 << " sumet " << m_inputMetMomAcc.sumet(coreSoftClus.value()));
670 m_outputMetMomAcc.addMet (metSoftClus.value(), m_inputMetMomAcc, coreSoftClus.value());
671 // Fill a vector with the soft constituents, if one was provided.
672 // For now, only setting up to work with those corresponding to the jet constituents.
673 // Can expand if needed.
674 if(softConst && acc_softConst.isAvailable(*coreSoftClus.getXAODObject())) {
675 for(const auto& constit : acc_softConst(*coreSoftClus.getXAODObject())) {
676 softConst->push_back(*constit);
677 }
678 ATH_MSG_DEBUG(softConst->size() << " soft constituents from core term");
679 }
680 }
681 std::optional<columnar::MetHelpers::ObjectWeightHandle<columnar::ContainerId::mutableMet,columnar::ContainerId::jet>> metSoftTrkLinks;
682 if(metSoftTrk) {
683 metSoftTrkLinks.emplace(*this,m_jetOutputMetWeightDecSoft,metSoftTrk.value(),jets);
684 if(!coreSoftTrk) {
685 ATH_MSG_ERROR("Soft track term provided without a core term!");
686 return StatusCode::FAILURE;
687 }
688 ATH_MSG_VERBOSE("Building MET soft track term " << metSoftTrk.value()(m_outputMetNameAcc));
689 ATH_MSG_VERBOSE("Core soft track mpx " << m_inputMetMomAcc.mpx(coreSoftTrk.value())
690 << ", mpy " << m_inputMetMomAcc.mpy(coreSoftTrk.value())
691 << " sumet " << m_inputMetMomAcc.sumet(coreSoftTrk.value()));
692 m_outputMetMomAcc.addMet (metSoftTrk.value(), m_inputMetMomAcc, coreSoftTrk.value());
693 if(softConst && acc_softConst.isAvailable(*coreSoftTrk.getXAODObject()) && !m_doPFlow && !m_doSoftTruth) {
694 for(const auto& constit : acc_softConst(*coreSoftTrk.getXAODObject())) {
695 softConst->push_back(*constit);
696 }
697 ATH_MSG_DEBUG(softConst->size() << " soft constituents from trk core term");
698 }
699 }
700
702
703 // Get the hashed key of this jet, if we can. Though his only works if
704 // 1. the container is an owning container, and not just a view;
705 // 2. the container is in the event store already.
706 // Since we will be creating ElementLink-s to these jets later on in the
707 // code, and it should work in AnalysisBase, only the first one of these
708 // is checked. Since the code can not work otherwise.
709
711 for(auto jet : jets) {
712 auto originalJet = jetsOriginals.getOriginal(jet);
713 auto assoc = helper.getJetAssociation(originalJet);
714 if(!assoc || m_assocAcc.isMisc(*assoc)) {
715 ATH_MSG_WARNING( "Jet without association found!" );
716 continue;
717 }
718
720 // retrieve nominal calibrated jet
721 if (acc_nominalObject.isAvailable(jet.getXAODObject())){
722 ATH_MSG_VERBOSE( "Jet pt before nominal replacement = " << m_jetMomAcc.pt(jet));
723 jet = *static_cast<const xAOD::Jet*>(*acc_nominalObject(jet.getXAODObject()));
724 }
725 else
726 ATH_MSG_ERROR("No nominal calibrated jet available for jet " << jet << ". Cannot simplify overlap removal!");
727 }
728 ATH_MSG_VERBOSE( "Jet pt = " << m_jetMomAcc.pt(jet));
729
730 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 );
731 bool JVT_reject(false);
732 bool isMuFSRJet(false);
733
734 // 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
735 if (m_JetEtaMax > 0.0 && std::abs(m_jetMomAcc.eta(jet)) > m_JetEtaMax)
736 JVT_reject = true;
737
738 if(doJetJVT) {
739 // intrinsically checks that is within range to apply Jvt requirement
740 JVT_reject = !bool(m_JvtTool->accept(&jet.getXAODObject()));
741 ATH_MSG_VERBOSE("Jet " << (JVT_reject ? "fails" : "passes") <<" JVT selection");
742 }
743
744 // if defined apply additional jet criterium
745 if (m_acc_jetRejectionDec && (*m_acc_jetRejectionDec)(jet)==0) JVT_reject = true;
746 bool hardJet(false);
747 MissingETBase::Types::constvec_t calvec = helper.overlapCalVec(*assoc);
748 bool caloverlap = false;
749 caloverlap = calvec.ce()>0;
750 ATH_MSG_DEBUG("Jet " << jet << " is " << ( caloverlap ? "" : "non-") << "overlapping");
751
752 if(m_veryGreedyPhotons && caloverlap) {
753 for(const auto object : m_assocAcc.objects(*assoc)) {
754 // Correctly handle this jet if we're using very greedy photons
755 if (object && object.getXAODObject()->type() == xAOD::Type::Photon) hardJet = true;
756 }
757 }
758
759 xAOD::JetFourMom_t constjet;
760 double constSF(1);
761 if(m_jetConstitScaleMom.empty() && m_assocAcc.hasAlternateConstVec(*assoc)){
762 constjet = m_assocAcc.getAlternateConstVec(*assoc);
763 } else {
764 constjet = m_jetConstitScaleMomAcc.value().jetP4(jet);//grab a constituent scale added by the JetMomentTool/JetConstitFourMomTool.cxx
765 double denom = (m_assocAcc.hasAlternateConstVec(*assoc) ? m_assocAcc.getAlternateConstVec(*assoc) : m_jetConstitScaleMomFixedAcc.value().jetP4(jet)).E();
766 constSF = denom>1e-9 ? constjet.E()/denom : 0.;
767 ATH_MSG_VERBOSE("Scale const jet by factor " << constSF);
768 calvec *= constSF;
769 }
770 double jpx = constjet.Px();
771 double jpy = constjet.Py();
772 double jpt = constjet.Pt();
773 double opx = jpx - calvec.cpx();
774 double opy = jpy - calvec.cpy();
775
776 columnar::OptMutableMetId met_muonEloss;
778 // Get a term to hold the Eloss corrections
779 met_muonEloss = m_outputMetMapAcc.getRequired(metCont, "MuonEloss");
780 if(!met_muonEloss) {
781 ATH_MSG_WARNING("Attempted to apply muon Eloss correction, but corresponding MET term does not exist!");
782 return StatusCode::FAILURE;
783 }
784 }
785
786 float total_eloss(0);
787 MissingETBase::Types::bitmask_t muons_selflags(0);
788 std::vector<columnar::MuonId> muons_in_jet;
789 std::vector<columnar::ElectronId> electrons_in_jet;
790 bool passJetForEl=false;
791 if(m_useGhostMuons) { // for backwards-compatibility
792 if(!acc_ghostMuons.isAvailable(jet.getXAODObject())){
793 ATH_MSG_ERROR("Ghost muons requested but not found!");
794 return StatusCode::FAILURE;
795 }
796 for(const auto& el : acc_ghostMuons(jet.getXAODObject())) {
797 if(!el.isValid()){
798 ATH_MSG_ERROR("Invalid element link to ghost muon! Quitting.");
799 return StatusCode::FAILURE;
800 }
801 muons_in_jet.push_back(*static_cast<const xAOD::Muon*>(*el));
802 }
803 }
804 for(const auto obj : m_assocAcc.objects(*assoc)) {
805 if(!obj) continue;
806 if(!m_useGhostMuons && obj.isContainer<columnar::ContainerId::muon>()) {
807 auto mu_test = obj.tryGetVariant<columnar::ContainerId::muon>().value();
808 ATH_MSG_VERBOSE("Muon " << mu_test << " found in jet " << jet);
811 if(acc_originalObject.isAvailable(mu_test.getXAODObject())) mu_test = *static_cast<const xAOD::Muon*>(*acc_originalObject(mu_test.getXAODObject()));
812 }
813 if(helper.objSelected(mu_test)) { //
814 muons_in_jet.push_back(mu_test);
815 ATH_MSG_VERBOSE("Muon is selected by MET.");
816 }
817 }
818 } else if(m_doRemoveElecTrks && obj.isContainer<columnar::ContainerId::electron>()) {
819 auto el_test = obj.tryGetVariant<columnar::ContainerId::electron>().value();
820 ATH_MSG_VERBOSE("Electron " << el_test << " found in jet " << jet);
822 if(acc_originalObject.isAvailable(el_test.getXAODObject())) el_test = *static_cast<const xAOD::Electron*>(*acc_originalObject(el_test.getXAODObject()));
823 }
824 if(helper.objSelected(*assoc,el_test)){
825 if(el_test(m_electronPtAcc)>90.0e3) { // only worry about high-pt electrons?
826 electrons_in_jet.push_back(el_test);
827 ATH_MSG_VERBOSE("High-pt electron is selected by MET.");
828 }
829 }
830 }
831 }
833 MissingETBase::Types::constvec_t initialTrkMom = m_assocAcc.jetTrkVec(*assoc);
834 float jet_ORtrk_sumpt = helper.overlapTrkVec(*assoc).sumpt();
835 float jet_all_trk_pt = initialTrkMom.sumpt();
836 float jet_unique_trk_pt = jet_all_trk_pt - jet_ORtrk_sumpt;
839 for(const auto& elec : electrons_in_jet) {
840 el_calvec += m_assocAcc.calVec(*assoc,elec);
841 el_trkvec += m_assocAcc.trkVec(*assoc,&elec.getXAODObject());
842 }
843 float el_cal_pt = el_calvec.cpt();
844 float el_trk_pt = el_trkvec.cpt();
845 ATH_MSG_VERBOSE("Elec trk: " << el_trk_pt
846 << " jetalltrk: " << jet_all_trk_pt
847 << " jetORtrk: " << jet_ORtrk_sumpt
848 << " electrk-jetORtrk: " << (el_trk_pt-jet_ORtrk_sumpt)
849 << " elec cal: " << el_cal_pt
850 << " jetalltrk-electrk: " << (jet_all_trk_pt-el_trk_pt)
851 << " jetalltrk-jetORtrk: " << (jet_all_trk_pt-jet_ORtrk_sumpt) );
852 // Want to use the jet calo measurement if we had at least one electron
853 // and the jet has a lot of residual track pt
854 // Is the cut appropriate?
855 if(el_trk_pt>1e-9 && jet_unique_trk_pt>10.0e3) passJetForEl=true;
856 } // end ele-track removal
857
858 for(auto mu_in_jet : muons_in_jet) {
859 float mu_Eloss = acc_Eloss(mu_in_jet.getXAODObject());
860
861 if(!JVT_reject) {
862 if (m_doRemoveMuonJets) {
863 // need to investigate how this is affected by the recording of muon clusters in the map
864 float mu_id_pt = mu_in_jet.getXAODObject().trackParticle(xAOD::Muon::InnerDetectorTrackParticle) ? mu_in_jet.getXAODObject().trackParticle(xAOD::Muon::InnerDetectorTrackParticle)->pt() : 0.;
865 float jet_trk_sumpt = m_acc_trksumpt.isAvailable(jet) && this->getPV() ? m_acc_trksumpt(jet)[this->getPV()->index()] : 0.;
866
867 // missed the muon, so we should add it back
868 if(0.9999*mu_id_pt>jet_trk_sumpt)
869 jet_trk_sumpt+=mu_id_pt;
870 float jet_trk_N = m_acc_trkN.isAvailable(jet) && this->getPV() ? m_acc_trkN(jet)[this->getPV()->index()] : 0.;
871 ATH_MSG_VERBOSE("Muon has ID pt " << mu_id_pt);
872 ATH_MSG_VERBOSE("Jet has pt " << m_jetMomAcc.pt(jet) << ", trk sumpt " << jet_trk_sumpt << ", trk N " << jet_trk_N);
873 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;
874 if(jet_from_muon) {
875 ATH_MSG_VERBOSE("Jet is from muon -- remove.");
876 JVT_reject = true;
877 }
878 }
879
881 // need to investigate how this is affected by the recording of muon clusters in the map
882 float mu_id_pt = mu_in_jet.getXAODObject().trackParticle(xAOD::Muon::InnerDetectorTrackParticle) ? mu_in_jet.getXAODObject().trackParticle(xAOD::Muon::InnerDetectorTrackParticle)->pt() : 0.;
883 float jet_trk_sumpt = m_acc_trksumpt.isAvailable(jet) && this->getPV() ? m_acc_trksumpt(jet)[this->getPV()->index()] : 0.;
884 // missed the muon, so we should add it back
885 if(0.9999*mu_id_pt>jet_trk_sumpt)
886 jet_trk_sumpt+=mu_id_pt;
887 float jet_trk_N = m_acc_trkN.isAvailable(jet) && this->getPV() ? m_acc_trkN(jet)[this->getPV()->index()] : 0.;
888
889 float jet_psE = 0.;
890 if (m_acc_psf.isAvailable(jet)){
891 jet_psE = m_acc_psf(jet);
892 } else if (m_acc_sampleE.isAvailable(jet)){
893 jet_psE = m_acc_sampleE(jet)[0] + m_acc_sampleE(jet)[4];
894 } else {
895 ATH_MSG_ERROR("Jet PS fraction or sampling energy must be available to calculate MET with doSetMuonJetEMScale");
896 return StatusCode::FAILURE;
897 }
898
899 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;
900 ATH_MSG_VERBOSE("Muon has ID pt " << mu_id_pt);
901 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));
902
903 if(jet_from_muon) {
904 ATH_MSG_VERBOSE("Jet is from muon -- set to EM scale and subtract Eloss.");
905 // Using constjet now because we focus on AntiKt4EMTopo.
906 // Probably not a massive difference to LC, but PF needs some consideration
907 ATH_MSG_VERBOSE("Jet e: " << constjet.E() << ", mu Eloss: " << mu_Eloss);
908 float elosscorr = mu_Eloss >= constjet.e() ? 0. : 1.-mu_Eloss/constjet.e();
909 // Effectively, take the unique fraction of the jet times the eloss-corrected fraction
910 // This might in some cases oversubtract, but should err on the side of undercounting the jet contribution
911 opx *= elosscorr;
912 opy *= elosscorr;
913 ATH_MSG_VERBOSE(" Jet eloss factor " << elosscorr << ", final pt: " << sqrt(opx*opx+opy*opy));
914 // Don't treat this jet normally. Instead, just add to the Eloss term
915 isMuFSRJet = true;
916 }
917 }
918 } // end muon-jet overlap-removal
919
920 switch(mu_in_jet.getXAODObject().energyLossType()) {
921 case xAOD::Muon::Parametrized:
922 case xAOD::Muon::MOP:
923 case xAOD::Muon::Tail:
924 case xAOD::Muon::FSRcandidate:
925 case xAOD::Muon::NotIsolated:
926 // For now don't differentiate the behaviour
927 // Remove the Eloss assuming the parameterised value
928 // The correction is limited to the selected clusters
929 total_eloss += mu_Eloss;
930 muons_selflags |= (1<<m_assocAcc.findIndex(*assoc,mu_in_jet));
931 }
932 }
933 ATH_MSG_VERBOSE("Muon selection flags: " << muons_selflags);
934 ATH_MSG_VERBOSE("Muon total eloss: " << total_eloss);
935
937 // borrowed from overlapCalVec
938 for(size_t iKey = 0; iKey < m_assocAcc.sizeCal(*assoc); iKey++) {
939 bool selector = (muons_selflags & m_assocAcc.calkey(*assoc)[iKey]);
940 if(selector) mu_calovec += m_assocAcc.calVec(*assoc,iKey);
941 ATH_MSG_VERBOSE("This key: " << m_assocAcc.calkey(*assoc)[iKey] << ", selector: " << selector);
942 }
943 ATH_MSG_VERBOSE("Mu calovec pt, no Eloss: " << mu_calovec.cpt());
944 if(m_muEloss) mu_calovec *= std::max<float>(0.,1-(total_eloss/mu_calovec.ce()));
945 ATH_MSG_VERBOSE("Mu calovec pt, with Eloss: " << mu_calovec.cpt());
946
947 // re-add calo components of muons beyond Eloss correction
948 ATH_MSG_VERBOSE("Jet " << jet << " const pT before OR " << jpt);
949 ATH_MSG_VERBOSE("Jet " << jet << " const pT after OR " << sqrt(opx*opx+opy*opy));
950 opx += mu_calovec.cpx();
951 opy += mu_calovec.cpy();
952 double opt = sqrt( opx*opx+opy*opy );
953 ATH_MSG_VERBOSE("Jet " << jet << " const pT diff after OR readding muon clusters " << opt-jpt);
954 double uniquefrac = 1. - (calvec.ce() - mu_calovec.ce()) / constjet.E();
955 ATH_MSG_VERBOSE( "Jet constscale px, py, pt, E = " << jpx << ", " << jpy << ", " << jpt << ", " << constjet.E() );
956 ATH_MSG_VERBOSE( "Jet overlap E = " << calvec.ce() - mu_calovec.ce() );
957 ATH_MSG_VERBOSE( "Jet OR px, py, pt, E = " << opx << ", " << opy << ", " << opt << ", " << constjet.E() - calvec.ce() );
958
959 if(isMuFSRJet) {
960 if(!met_muonEloss){
961 ATH_MSG_ERROR("Attempted to apply muon Eloss correction, but corresponding MET term does not exist!");
962 return StatusCode::FAILURE;
963 }
964 m_outputMetMomAcc.addParticle(met_muonEloss.value(),opx,opy,opt);
965 continue;
966 }
967
968 if(selected && !JVT_reject) {
969 if(!caloverlap) {
970 // add jet full four-vector
971 hardJet = true;
972 if (!tracksForHardJets) {
973 if(m_doConstJet)
974 m_outputMetMomAcc.addParticle(metJet,jpx,jpy,jpt);
975 else
976 m_outputMetMomAcc.addParticle(metJet,m_jetMomAcc,jet);
977 }
978 }
979 else if((uniquefrac>m_jetMinEfrac || passJetForEl) && opt>m_jetMinWeightedPt){
980 // add jet corrected for overlaps if sufficient unique fraction
981 hardJet = true;
982 if(!tracksForHardJets) {
983 if(m_jetCorrectPhi) {
984 if (m_doConstJet)
985 m_outputMetMomAcc.addParticle(metJet,opx,opy,opt);
986 else {
987 double jesF = m_jetMomAcc.pt(jet) / jpt;
988 m_outputMetMomAcc.addParticle(metJet,opx*jesF,opy*jesF,opt*jesF);
989 }
990 } else {
991 if (m_doConstJet)
992 m_outputMetMomAcc.addParticle(metJet,uniquefrac*jpx,uniquefrac*jpy,uniquefrac*jpt);
993 else{
994 if(passJetForEl && m_doRemoveElecTrksEM)
995 m_outputMetMomAcc.addParticle(metJet,opx,opy,opt);
996 else
997 m_outputMetMomAcc.addParticle(metJet,uniquefrac*m_jetMomAcc.px(jet),uniquefrac*m_jetMomAcc.py(jet),uniquefrac*m_jetMomAcc.pt(jet));
998 }
999 }
1000 }
1001 }
1002 } // hard jet selection
1003
1004 if(hardJet){
1005 ATH_MSG_VERBOSE("Jet added at full scale");
1006 metJetWeights.emplace_back(jet, uniquefrac);
1007 } else {
1008 if(metSoftClus && !JVT_reject) {
1009 // add fractional contribution
1010 ATH_MSG_VERBOSE("Jet added at const scale");
1011 if (std::abs(m_jetMomAcc.eta(jet))<2.5 || !(coreSoftClus.value()(m_inputMetSourceAcc)&MissingETBase::Source::Region::Central)) {
1012 metSoftClusLinks->emplace_back (jet, uniquefrac);
1013 m_outputMetMomAcc.addParticle(metSoftClus.value(),opx,opy,opt);
1014 }
1015
1016 // Fill a vector with the soft constituents, if one was provided.
1017 // For now, only setting up to work with those corresponding to the jet constituents.
1018 // Can expand if needed.
1019 // This ignores overlap removal.
1020 //
1021 if(softConst) {
1022 for(size_t iConst=0; iConst<jet.getXAODObject().numConstituents(); ++iConst) {
1023 const IParticle* constit = jet.getXAODObject().rawConstituent(iConst);
1024 softConst->push_back(constit);
1025 }
1026 }
1027 }
1028 } // hard jet or CST
1029
1030 if(!metSoftTrk || (hardJet && !tracksForHardJets)) continue;
1031
1032 // use jet tracks
1033 // remove any tracks already used by other objects
1034 MissingETBase::Types::constvec_t trkvec = helper.overlapTrkVec(*assoc);
1035 MissingETBase::Types::constvec_t jettrkvec = m_assocAcc.jetTrkVec(*assoc);
1036 if(jettrkvec.ce()>1e-9) {
1037 jpx = jettrkvec.cpx();
1038 jpy = jettrkvec.cpy();
1039 jpt = jettrkvec.sumpt();
1040 jettrkvec -= trkvec;
1041 opx = jettrkvec.cpx();
1042 opy = jettrkvec.cpy();
1043 opt = jettrkvec.sumpt();
1044 ATH_MSG_VERBOSE( "Jet track px, py, sumpt = " << jpx << ", " << jpy << ", " << jpt );
1045 ATH_MSG_VERBOSE( "Jet OR px, py, sumpt = " << opx << ", " << opy << ", " << opt );
1046 } else {
1047 opx = opy = opt = 0;
1048 ATH_MSG_VERBOSE( "This jet has no associated tracks" );
1049 }
1050 if (hardJet) m_outputMetMomAcc.addParticle(metJet,opx,opy,opt);
1051 else if (std::abs(m_jetMomAcc.eta(jet))<2.5 || !(coreSoftTrk.value()(m_inputMetSourceAcc)&MissingETBase::Source::Region::Central)) {
1052 m_outputMetMomAcc.addParticle(metSoftTrk.value(),opx,opy,opt);
1053 // Don't need to add if already done for softclus.
1054 if(!metSoftClus) {
1055 if (metSoftTrkLinks) metSoftTrkLinks->emplace_back (jet, uniquefrac);
1056 }
1057
1058 // Fill a vector with the soft constituents, if one was provided.
1059 // For now, only setting up to work with those corresponding to the jet constituents.
1060 // Can expand if needed.
1061 // This ignores overlap removal.
1062 //
1063 if(softConst && !m_doPFlow && !m_doSoftTruth) {
1064 std::vector<const IParticle*> jettracks;
1065 jet.getXAODObject().getAssociatedObjects<IParticle>(xAOD::JetAttribute::GhostTrack,jettracks);
1066 for(size_t iConst=0; iConst<jettracks.size(); ++iConst) {
1067 const TrackParticle* pTrk = static_cast<const TrackParticle*>(jettracks[iConst]);
1068 if (acceptTrack(pTrk,pv)) softConst->push_back(pTrk);
1069 }
1070 }
1071 }
1072 } // jet loop
1073
1074 ATH_MSG_DEBUG("Number of selected jets: " << metJetWeights.size());
1075
1076 if(metSoftTrk) {
1077 ATH_MSG_DEBUG("Number of softtrk jets: " << metSoftTrkLinks->size());
1078 }
1079
1080 if(metSoftClus) {
1081 ATH_MSG_DEBUG("Number of softclus jets: " << metSoftClusLinks->size());
1082 }
1083
1084 if(softConst) ATH_MSG_DEBUG(softConst->size() << " soft constituents from core term + jets");
1085
1086 auto assoc = helper.getMiscAssociation();
1087 if(!assoc) return StatusCode::SUCCESS;
1088
1089 if(metSoftTrk) {
1090 // supplement track term with any tracks associated to isolated muons
1091 // these are recorded in the misc association
1092 MissingETBase::Types::constvec_t trkvec = helper.overlapTrkVec(*assoc);
1093 double opx = trkvec.cpx();
1094 double opy = trkvec.cpy();
1095 double osumpt = trkvec.sumpt();
1096 ATH_MSG_VERBOSE( "Misc track px, py, sumpt = " << opx << ", " << opy << ", " << osumpt );
1097 m_outputMetMomAcc.addParticle(metSoftTrk.value(),opx,opy,osumpt);
1098 ATH_MSG_VERBOSE("Final soft track mpx " << m_outputMetMomAcc.mpx(metSoftTrk.value())
1099 << ", mpy " << m_outputMetMomAcc.mpy(metSoftTrk.value())
1100 << " sumet " << m_outputMetMomAcc.sumet(metSoftTrk.value()));
1101 }
1102
1103 if(metSoftClus) {
1104 // supplement cluster term with any clusters associated to isolated e/gamma
1105 // these are recorded in the misc association
1106 float total_eloss(0.);
1107 MissingETBase::Types::bitmask_t muons_selflags(0);
1108 MissingETBase::Types::constvec_t calvec = helper.overlapCalVec(*assoc);
1109 double opx = calvec.cpx();
1110 double opy = calvec.cpy();
1111 double osumpt = calvec.sumpt();
1112 for(const auto objId : m_assocAcc.objects(*assoc)) {
1113 auto *obj = objId.getXAODObject();
1114 if (!obj || obj->type() != xAOD::Type::Muon) continue;
1115 const xAOD::Muon* mu_test(static_cast<const xAOD::Muon*>(obj));
1116 if(acc_originalObject.isAvailable(*mu_test)) mu_test = static_cast<const xAOD::Muon*>(*acc_originalObject(*mu_test));
1117 if(helper.objSelected(mu_test)) { //
1118 float mu_Eloss = acc_Eloss(*mu_test);
1119 switch(mu_test->energyLossType()) {
1120 case xAOD::Muon::Parametrized:
1121 case xAOD::Muon::MOP:
1122 case xAOD::Muon::Tail:
1123 case xAOD::Muon::FSRcandidate:
1124 case xAOD::Muon::NotIsolated:
1125 // For now don't differentiate the behaviour
1126 // Remove the Eloss assuming the parameterised value
1127 // The correction is limited to the selected clusters
1128 total_eloss += mu_Eloss;
1129 muons_selflags |= (1<<m_assocAcc.findIndex(*assoc,mu_test));
1130 }
1131 ATH_MSG_VERBOSE("Mu index " << mu_test->index());
1132 }
1133 }
1134 ATH_MSG_VERBOSE("Mu selection flags " << muons_selflags);
1135 ATH_MSG_VERBOSE("Mu total eloss " << total_eloss);
1136
1138 // borrowed from overlapCalVec
1139 for(size_t iKey = 0; iKey < m_assocAcc.sizeCal(*assoc); iKey++) {
1140 bool selector = (muons_selflags & m_assocAcc.calkey(*assoc)[iKey]);
1141 ATH_MSG_VERBOSE("This key: " << m_assocAcc.calkey(*assoc)[iKey] << ", selector: " << selector
1142 << " this calvec E: " << m_assocAcc.calVec(*assoc,iKey).ce());
1143 if(selector) mu_calovec += m_assocAcc.calVec(*assoc,iKey);
1144 }
1145 if(m_muEloss){
1146 mu_calovec *= std::max<float>(0.,1-(total_eloss/mu_calovec.ce()));
1147 opx += mu_calovec.cpx();
1148 opy += mu_calovec.cpy();
1149 osumpt += mu_calovec.sumpt();
1150 }
1151 ATH_MSG_VERBOSE("Mu cluster sumpt " << mu_calovec.sumpt());
1152
1153 ATH_MSG_VERBOSE( "Misc cluster px, py, sumpt = " << opx << ", " << opy << ", " << osumpt );
1154 m_outputMetMomAcc.addParticle(metSoftClus.value(),opx,opy,osumpt);
1155 ATH_MSG_VERBOSE("Final soft cluster mpx " << m_outputMetMomAcc.mpx(metSoftClus.value())
1156 << ", mpy " << m_outputMetMomAcc.mpy(metSoftClus.value())
1157 << " sumet " << m_outputMetMomAcc.sumet(metSoftClus.value()));
1158 }
1159
1160 return StatusCode::SUCCESS;
1161 }
1162
1164 const xAOD::JetContainer* jets,
1166 xAOD::MissingET* metSoftTrk,
1167 const xAOD::MissingET* coreSoftTrk,
1168 bool doJetJVT) const {
1169 if (!metJet) {
1170 ATH_MSG_ERROR("No MET object provided for track MET rebuilding");
1171 return StatusCode::FAILURE;
1172 }
1173 if (!metSoftTrk) {
1174 ATH_MSG_ERROR("No MET object provided for soft track MET rebuilding");
1175 return StatusCode::FAILURE;
1176 }
1177 if (!jets) {
1178 ATH_MSG_ERROR("No jet container provided for track MET rebuilding");
1179 return StatusCode::FAILURE;
1180 }
1181
1182 columnar::MutableMetRange metCont (*static_cast<MissingETContainer*>(metJet->container()));
1183 return rebuildJetMET(columnar::MutableMetId(*metJet),metCont,columnar::JetRange(*jets),m_assocAcc(helper),std::nullopt,nullptr,columnar::MutableMetId(*metSoftTrk),coreSoftTrk,doJetJVT,true);
1184 }
1185
1188 columnar::JetRange jets,
1190 columnar::MutableMetId metSoftTrk,
1191 columnar::Met1Id coreSoftTrk,
1192 bool doJetJVT) const {
1193 return rebuildJetMET(metJet,metCont,jets,helper,std::nullopt,nullptr,metSoftTrk,coreSoftTrk,doJetJVT,true);
1194 }
1195
1196 // **** Remove objects and any overlaps from MET calculation ****
1199 xAOD::MissingETContainer* metCont) const
1200 {
1201 if (!collection) {
1202 ATH_MSG_ERROR("No object container provided for marking invisible");
1203 return StatusCode::FAILURE;
1204 }
1205 if (!metCont) {
1206 ATH_MSG_ERROR("No MET container provided for marking invisible");
1207 return StatusCode::FAILURE;
1208 }
1209
1210 return markInvisible(columnar::ParticleRange(*collection),m_assocAcc(helper),columnar::MutableMetRange(*metCont));
1211 }
1212
1215 columnar::MutableMetRange metCont) const
1216 {
1217 columnar::MutableMetId met = m_outputMetMapAcc.fillMET (metCont, "Invisibles", invisSource);
1219 }
1220
1222 {
1223 return static_cast<bool>(m_trkseltool->accept( *trk, vx ));
1224 }
1225
1227
1229
1230 if(!h_PV.isValid()) {
1231 ATH_MSG_WARNING("Unable to retrieve primary vertex container PrimaryVertices");
1232 return nullptr;
1233 }
1234 ATH_MSG_DEBUG("Successfully retrieved primary vertex container");
1235 if(h_PV->empty()) ATH_MSG_WARNING("Event has no primary vertices!");
1236 for(const xAOD::Vertex* vx : *h_PV) {
1237 if(vx->vertexType()==xAOD::VxType::PriVtx) return vx;
1238 }
1239 return nullptr;
1240 }
1241
1242
1243
1245 {
1246 for (columnar::EventContextId event : events)
1247 {
1248 auto met = m_outputMetHandle (event);
1249 auto metcore = m_inputMetHandle (event);
1250 auto metassoc = m_metAssocHandle (event);
1251 if (m_columnarOperation.value() == 0u)
1252 {
1253 auto particles = m_particlesHandle (event);
1255 throw std::runtime_error ("Failed to rebuild MET");
1256 } else if (m_columnarOperation.value() == 1u)
1257 {
1258 auto jets = m_jetsHandle (event);
1259 if (rebuildJetMET (m_columnarJetKey.value(), m_columnarSoftClusKey.value(), met, jets, metcore, m_assocAcc(metassoc), m_columnarDoJetJVT.value()).isFailure())
1260 throw std::runtime_error ("Failed to rebuild jet MET");
1261 } else if (m_columnarOperation.value() == 2u)
1262 {
1263 auto jets = m_jetsHandle (event);
1264 if (rebuildTrackMET (m_columnarJetKey.value(), m_columnarSoftClusKey.value(), met, jets, metcore, m_assocAcc(metassoc), m_columnarDoJetJVT.value()).isFailure())
1265 throw std::runtime_error ("Failed to rebuild track MET");
1266 } else
1267 {
1268 throw std::runtime_error ("Unknown columnar operation");
1269 }
1270 }
1271 }
1272
1273} //> 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)
SG::ConstAccessor< T, ALLOC > ConstAccessor
Definition AuxElement.h:569
SG::Accessor< T, ALLOC > Accessor
Definition AuxElement.h:572
const SG::AuxVectorData * container() const
Return the container holding this element.
size_t index() const
Return the index of this element within its container.
bool isAvailable(const ELT &e) const
Test to see if this variable exists in the store.
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
Gaudi::Property< std::string > m_inputPreselectionName
columnar::MetHelpers::MapLookupAccessor< columnar::ContainerId::mutableMet > m_outputMetMapAcc
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::MetHelpers::ObjectWeightDecorator< columnar::ContainerId::mutableMet, columnar::ContainerId::jet > m_jetOutputMetWeightDecSoft
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::MetHelpers::MetMomentumAccessors< columnar::ContainerId::mutableMet > m_outputMetMomAcc
columnar::Met1Accessor< columnar::ObjectColumn > m_inputMetHandle
columnar::ColumnAccessor< columnar::ContainerId::metAssociation, columnar::ObjectColumn > m_metAssocHandle
columnar::ParticleAccessor< columnar::RetypeColumn< xAOD::Muon::MuonType, std::uint16_t > > m_inputMuonTypeAcc
columnar::MutableMetAccessor< std::string > m_outputMetNameAcc
columnar::JetAccessor< float > m_acc_emf
std::optional< columnar::MetHelpers::InputMomentumAccessors< columnar::ContainerId::jet > > m_jetConstitScaleMomFixedAcc
columnar::MetHelpers::MapLookupAccessor< columnar::ContainerId::met1 > m_inputMetMapAcc
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::MetHelpers::ObjectTypeAccessor< columnar::ContainerId::particle > m_inputObjTypeAcc
columnar::Met1Accessor< MissingETBase::Types::bitmask_t > m_inputMetSourceAcc
Gaudi::Property< std::string > m_columnarTermName
columnar::MetHelpers::MetMomentumAccessors< columnar::ContainerId::met1 > m_inputMetMomAcc
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
const xAOD::Vertex * getPV() const
SG::ReadHandleKey< xAOD::JetContainer > m_jetContainer
columnar::ParticleAccessor< columnar::ObjectColumn > m_particlesHandle
ColumnarMETMaker()
Default constructor:
Gaudi::Property< unsigned > m_columnarOperation
ToolHandle< IAsgSelectionTool > m_JvtTool
columnar::ElectronAccessor< columnar::RetypeColumn< double, float > > m_electronPtAcc
std::optional< columnar::MetHelpers::InputMomentumAccessors< columnar::ContainerId::jet > > m_jetConstitScaleMomAcc
Gaudi::Property< std::string > m_columnarSoftClusKey
std::optional< columnar::JetAccessor< char > > m_acc_jetRejectionDec
columnar::MetHelpers::InputMomentumAccessors< columnar::ContainerId::jet > m_jetMomAcc
Gaudi::Property< unsigned > m_columnarParticleType
columnar::MetHelpers::ObjectWeightDecorator< columnar::ContainerId::mutableMet, columnar::ContainerId::jet > m_jetOutputMetWeightDecRegular
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< ContainerId::particle > ParticleRange
Definition ParticleDef.h:35
ObjectId< ContainerId::met1 > Met1Id
Definition MetDef.h:62
OptObjectId< ContainerId::met1 > OptMet1Id
Definition MetDef.h:63
ObjectId< ContainerId::mutableMet > MutableMetId
Definition MetDef.h:68
OptObjectId< ContainerId::mutableMet > OptMutableMetId
Definition MetDef.h:69
ObjectRange< ContainerId::eventContext > EventContextRange
ObjectId< ContainerId::eventContext > EventContextId
ObjectRange< ContainerId::jet > JetRange
Definition JetDef.h:25
ObjectRange< ContainerId::met1 > Met1Range
Definition MetDef.h:61
ObjectRange< ContainerId::mutableMet > MutableMetRange
Definition MetDef.h:67
Definition index.py:1
static const SG::AuxElement::ConstAccessor< iplink_t > acc_nominalObject("nominalObjectLink")
ElementLink< xAOD::IParticleContainer > iplink_t
static const SG::AuxElement::ConstAccessor< iplink_t > acc_originalObject("originalObjectLink")
static const SG::AuxElement::ConstAccessor< float > acc_Eloss("EnergyLoss")
static const SG::AuxElement::Accessor< std::vector< iplink_t > > dec_constitObjLinks("ConstitObjectLinks")
static const SG::AuxElement::Accessor< std::vector< float > > dec_constitObjWeights("ConstitObjectWeights")
static const SG::AuxElement::ConstAccessor< std::vector< iplink_t > > acc_ghostMuons("GhostMuon")
static const MissingETBase::Types::bitmask_t invisSource
Definition METHelpers.h:27
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.
Definition ColumnarDef.h:20
Collection of functions managing the MET composition map and association map.