Skip to content

Commit 336fa9c

Browse files
authored
Merge pull request #47603 from gmelachr/master
BPHNanoAODs
2 parents 81460ac + 16405f5 commit 336fa9c

40 files changed

+4549
-29
lines changed

Configuration/PyReleaseValidation/python/relval_nano.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,9 @@ def subnext(self):
179179
steps['NANO_mc14.0'] = merge([{'--era': 'Run3,run3_nanoAOD_pre142X', '--conditions': 'auto:phase1_2024_realistic'},
180180
_NANO_mc])
181181

182+
steps['BPHNANO_mc14.0'] = merge([{'-s': 'NANO:@BPH', '-n': '1000'},
183+
steps['NANO_mc14.0']])
184+
182185
steps['muPOGNANO_mc14.0'] = merge([{'-s': 'NANO:@MUPOG,DQM:@nanoAODDQM', '-n': '1000'},
183186
steps['NANO_mc14.0']])
184187

@@ -236,6 +239,9 @@ def subnext(self):
236239
steps['NANO_data14.0_prompt'] = merge([{'-s': 'NANO:@Prompt,DQM:@nanoAODDQM', '-n': '1000'},
237240
steps['NANO_data14.0']])
238241

242+
steps['BPHNANO_data14.0'] = merge([{'-s': 'NANO:@BPH', '-n': '1000'},
243+
steps['NANO_data14.0']])
244+
239245
steps['muPOGNANO_data14.0'] = merge([{'-s': 'NANO:@MUPOG,DQM:@nanoAODDQM', '-n': '1000'},
240246
steps['NANO_data14.0']])
241247

@@ -367,6 +373,7 @@ def subnext(self):
367373
workflows[_wfn()] = ['lepTrackInfoNANOmc140X', ['TTbarMINIAOD14.0', 'lepTrackInfoNANO_mc14.0']]
368374
workflows[_wfn()] = ['ScoutingNANOmc140X', ['TTbarMINIAOD14.0', 'scoutingNANO_mc14.0']]
369375
workflows[_wfn()] = ['ScoutingNANOwithPromptmc140X', ['TTbarMINIAOD14.0', 'scoutingNANO_withPrompt_mc14.0']]
376+
workflows[_wfn()] = ['BPHNANOmc140X', ['TTbarMINIAOD14.0', 'BPHNANO_mc14.0']]
370377

371378
# POG/PAG custom NANOs, data
372379
_wfn.subnext()
@@ -378,6 +385,7 @@ def subnext(self):
378385
workflows[_wfn()] = ['lepTrackInfoNANOdata140Xrun3', ['MuonEG2024MINIAOD14.0', 'lepTrackInfoNANO_data14.0']]
379386
workflows[_wfn()] = ['ScoutingNANOdata140Xrun3', ['ScoutingPFRun32024RAW14.0', 'scoutingNANO_data14.0']]
380387
workflows[_wfn()] = ['ScoutingNANOwithPromptdata140Xrun3', ['ScoutingPFMonitor2024MINIAOD14.0', 'scoutingNANO_withPrompt_data14.0']]
388+
workflows[_wfn()] = ['BPHNANOdata140Xrun3', ['MuonEG2024MINIAOD14.0', 'BPHNANO_data14.0']]
381389

382390
# DPG custom NANOs, data
383391
_wfn.subnext()

DataFormats/PatCandidates/src/classes_def_objects.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,10 @@
630630
<class name="edm::Association<std::vector<pat::PackedGenParticle> >" />
631631
<class name="edm::Wrapper<edm::Association<std::vector<pat::PackedGenParticle> > >"/>
632632

633+
<class name="edm::RefProd<pat::CompositeCandidateCollection>" />
634+
<class name="edm::Association<pat::CompositeCandidateCollection>" />
635+
<class name="edm::Wrapper<edm::Association<pat::CompositeCandidateCollection> >"/>
636+
633637
<!--- vectors of Ptrs -->
634638
<class name="std::vector<edm::Ptr<pat::Jet> >" />
635639
<class name="std::vector< std::vector<edm::Ptr<pat::Jet> > >" />
Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
/////////////////////////// BPHTrackMerger ////////////////////////////////
2+
/// original authors: G Karathanasis (CERN), G Melachroinos (NKUA)
3+
// Takes Lost tracks and packed candidates filters them removes overlap and
4+
// appl// -ies dz cut wrt to a dilepton vertex. Also applies selection cuts
5+
6+
#include "CommonTools/Utils/interface/StringCutObjectSelector.h"
7+
#include "DataFormats/BeamSpot/interface/BeamSpot.h"
8+
#include "DataFormats/Common/interface/RefToPtr.h"
9+
#include "DataFormats/Common/interface/View.h"
10+
#include "DataFormats/GeometryVector/interface/GlobalPoint.h"
11+
#include "DataFormats/PatCandidates/interface/CompositeCandidate.h"
12+
#include "DataFormats/PatCandidates/interface/Electron.h"
13+
#include "DataFormats/PatCandidates/interface/Muon.h"
14+
#include "DataFormats/PatCandidates/interface/PackedCandidate.h"
15+
#include "FWCore/Framework/interface/Event.h"
16+
#include "FWCore/Framework/interface/MakerMacros.h"
17+
#include "FWCore/Framework/interface/global/EDProducer.h"
18+
#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h"
19+
#include "FWCore/ParameterSet/interface/ParameterSet.h"
20+
#include "FWCore/ParameterSet/interface/ParameterSetDescription.h"
21+
#include "FWCore/Utilities/interface/InputTag.h"
22+
#include "MagneticField/Engine/interface/MagneticField.h"
23+
#include "MagneticField/Records/interface/IdealMagneticFieldRecord.h"
24+
#include "TrackingTools/TransientTrack/interface/TransientTrack.h"
25+
#include "helper.h"
26+
27+
class BPHTrackMerger : public edm::global::EDProducer<> {
28+
public:
29+
// would it be useful to give this a bit more standard structure?
30+
explicit BPHTrackMerger(const edm::ParameterSet &cfg)
31+
: bFieldToken_(esConsumes<MagneticField, IdealMagneticFieldRecord>()),
32+
beamSpotSrc_(consumes<reco::BeamSpot>(cfg.getParameter<edm::InputTag>("beamSpot"))),
33+
tracksToken_(consumes<pat::PackedCandidateCollection>(cfg.getParameter<edm::InputTag>("tracks"))),
34+
lostTracksToken_(consumes<pat::PackedCandidateCollection>(cfg.getParameter<edm::InputTag>("lostTracks"))),
35+
dileptonToken_(consumes<pat::CompositeCandidateCollection>(cfg.getParameter<edm::InputTag>("dileptons"))),
36+
muonToken_(consumes<pat::MuonCollection>(cfg.getParameter<edm::InputTag>("muons"))),
37+
eleToken_(consumes<pat::ElectronCollection>(cfg.getParameter<edm::InputTag>("electrons"))),
38+
pvToken_(consumes<std::vector<reco::Vertex>>(cfg.getParameter<edm::InputTag>("pvSrc"))),
39+
maxDzDilep_(cfg.getParameter<double>("maxDzDilep")),
40+
dcaSig_(cfg.getParameter<double>("dcaSig")),
41+
track_selection_(cfg.getParameter<std::string>("trackSelection")) {
42+
produces<pat::CompositeCandidateCollection>("SelectedTracks");
43+
produces<TransientTrackCollection>("SelectedTransientTracks");
44+
produces<edm::Association<pat::CompositeCandidateCollection>>("SelectedTracks");
45+
}
46+
47+
~BPHTrackMerger() override {}
48+
49+
void produce(edm::StreamID, edm::Event &, const edm::EventSetup &) const override;
50+
51+
private:
52+
const edm::ESGetToken<MagneticField, IdealMagneticFieldRecord> bFieldToken_;
53+
const edm::EDGetTokenT<reco::BeamSpot> beamSpotSrc_;
54+
const edm::EDGetTokenT<pat::PackedCandidateCollection> tracksToken_;
55+
const edm::EDGetTokenT<pat::PackedCandidateCollection> lostTracksToken_;
56+
const edm::EDGetTokenT<pat::CompositeCandidateCollection> dileptonToken_;
57+
const edm::EDGetTokenT<pat::MuonCollection> muonToken_;
58+
const edm::EDGetTokenT<pat::ElectronCollection> eleToken_;
59+
const edm::EDGetTokenT<std::vector<reco::Vertex>> pvToken_;
60+
61+
// selections
62+
const double maxDzDilep_;
63+
const double dcaSig_;
64+
const StringCutObjectSelector<pat::PackedCandidate> track_selection_;
65+
};
66+
67+
void BPHTrackMerger::produce(edm::StreamID, edm::Event &evt, edm::EventSetup const &stp) const {
68+
// input
69+
edm::Handle<reco::BeamSpot> beamSpotHandle;
70+
evt.getByToken(beamSpotSrc_, beamSpotHandle);
71+
if (!beamSpotHandle.isValid()) {
72+
edm::LogError("BToKstllProducer") << "No beam spot available from Event";
73+
}
74+
const reco::BeamSpot &beamSpot = *beamSpotHandle;
75+
76+
const auto &bField = stp.getData(bFieldToken_);
77+
edm::Handle<pat::PackedCandidateCollection> tracks;
78+
evt.getByToken(tracksToken_, tracks);
79+
edm::Handle<pat::PackedCandidateCollection> lostTracks;
80+
evt.getByToken(lostTracksToken_, lostTracks);
81+
edm::Handle<pat::CompositeCandidateCollection> dileptons;
82+
evt.getByToken(dileptonToken_, dileptons);
83+
84+
edm::Handle<pat::MuonCollection> muons;
85+
evt.getByToken(muonToken_, muons);
86+
edm::Handle<pat::ElectronCollection> pfele;
87+
evt.getByToken(eleToken_, pfele);
88+
89+
// for lost tracks / pf discrimination
90+
unsigned int nTracks = tracks->size();
91+
unsigned int totalTracks = nTracks + lostTracks->size();
92+
93+
// ok this was CompositeCandidateCollection
94+
std::unique_ptr<pat::CompositeCandidateCollection> tracks_out(new pat::CompositeCandidateCollection);
95+
std::unique_ptr<TransientTrackCollection> trans_tracks_out(new TransientTrackCollection);
96+
97+
std::vector<std::pair<pat::CompositeCandidate, reco::TransientTrack>> vectrk_ttrk;
98+
// try topreserve same logic avoiding the copy of the full collection
99+
/*
100+
//correct logic but a bit convoluted -> changing to smthn simpler
101+
std::vector<pat::PackedCandidate> totalTracks(*tracks);
102+
totalTracks.insert(totalTracks.end(),lostTracks->begin(),lostTracks->end());
103+
*/
104+
105+
// Retrieve the primary vertex collection
106+
edm::Handle<std::vector<reco::Vertex>> pvs;
107+
evt.getByToken(pvToken_, pvs);
108+
109+
std::vector<int> match_indices(totalTracks, -1);
110+
// for loop is better to be range based - especially for large ensembles
111+
for (unsigned int iTrk = 0; iTrk < totalTracks; ++iTrk) {
112+
const pat::PackedCandidate &trk = (iTrk < nTracks) ? (*tracks)[iTrk] : (*lostTracks)[iTrk - nTracks];
113+
114+
// arranging cuts for speed
115+
if (!trk.hasTrackDetails())
116+
continue;
117+
if (abs(trk.pdgId()) != 211)
118+
continue; // do we want also to keep muons?
119+
if (!track_selection_(trk))
120+
continue;
121+
122+
bool skipTrack = true;
123+
float dzTrg = 0.0;
124+
for (const pat::CompositeCandidate &dilep : *dileptons) {
125+
// if dz is negative it is deactivated
126+
if (fabs(trk.vz() - dilep.vz()) > maxDzDilep_ && maxDzDilep_ > 0)
127+
continue;
128+
skipTrack = false;
129+
dzTrg = trk.vz() - dilep.vz();
130+
break; // at least for one dilepton candidate to pass this cuts
131+
}
132+
133+
// if track is far from all dilepton candidate
134+
if (skipTrack)
135+
continue;
136+
137+
// high purity requirment applied only in packedCands
138+
if (iTrk < nTracks && !trk.trackHighPurity())
139+
continue;
140+
const reco::TransientTrack trackTT((*trk.bestTrack()), &bField);
141+
142+
// distance closest approach in x,y wrt beam spot
143+
std::pair<double, double> DCA = bph::computeDCA(trackTT, beamSpot);
144+
float DCABS = DCA.first;
145+
float DCABSErr = DCA.second;
146+
float DCASig = (DCABSErr != 0 && float(DCABSErr) == DCABSErr) ? fabs(DCABS / DCABSErr) : -1;
147+
148+
if (DCASig > dcaSig_ && dcaSig_ > 0)
149+
continue;
150+
151+
// clean tracks wrt to all muons
152+
int matchedToMuon = 0;
153+
for (const pat::Muon &imutmp : *muons) {
154+
for (unsigned int i = 0; i < imutmp.numberOfSourceCandidatePtrs(); ++i) {
155+
if (!((imutmp.sourceCandidatePtr(i)).isNonnull() && (imutmp.sourceCandidatePtr(i)).isAvailable()))
156+
continue;
157+
158+
const edm::Ptr<reco::Candidate> &source = imutmp.sourceCandidatePtr(i);
159+
if (source.id() == tracks.id() && source.key() == iTrk) {
160+
matchedToMuon = 1;
161+
break;
162+
}
163+
}
164+
}
165+
166+
// clean tracks wrt to all pf electrons
167+
int matchedToEle = 0;
168+
for (const pat::Electron &ietmp : *pfele) {
169+
for (unsigned int i = 0; i < ietmp.numberOfSourceCandidatePtrs(); ++i) {
170+
if (!((ietmp.sourceCandidatePtr(i)).isNonnull() && (ietmp.sourceCandidatePtr(i)).isAvailable()))
171+
continue;
172+
const edm::Ptr<reco::Candidate> &source = ietmp.sourceCandidatePtr(i);
173+
if (source.id() == tracks.id() && source.key() == iTrk) {
174+
matchedToEle = 1;
175+
break;
176+
}
177+
}
178+
}
179+
180+
// IP
181+
const reco::Vertex &pv0 = pvs->front();
182+
183+
// output
184+
pat::CompositeCandidate pcand;
185+
pcand.setP4(trk.p4());
186+
pcand.setCharge(trk.charge());
187+
pcand.setVertex(trk.vertex());
188+
pcand.setPdgId(trk.pdgId());
189+
pcand.addUserInt("isPacked", (iTrk < nTracks));
190+
pcand.addUserInt("isLostTrk", (iTrk < nTracks) ? 0 : 1);
191+
pcand.addUserFloat("dxy", trk.dxy(pv0.position()));
192+
pcand.addUserFloat("dxyS", trk.dxy(pv0.position()) / trk.dxyError());
193+
pcand.addUserFloat("dz", trk.dz(pv0.position()));
194+
pcand.addUserFloat("dzS", trk.dz() / trk.dzError());
195+
pcand.addUserFloat("DCASig", DCASig);
196+
pcand.addUserFloat("dzTrg", dzTrg);
197+
pcand.addUserInt("isMatchedToMuon", matchedToMuon);
198+
pcand.addUserInt("isMatchedToEle", matchedToEle);
199+
pcand.addUserInt("nValidHits", trk.bestTrack()->found());
200+
pcand.addUserInt("keyPacked", iTrk);
201+
202+
// Covariance matrix elements for helix parameters for decay time
203+
// uncertainty
204+
pcand.addUserFloat("covQopQop", trk.bestTrack()->covariance(0, 0));
205+
pcand.addUserFloat("covLamLam", trk.bestTrack()->covariance(1, 1));
206+
pcand.addUserFloat("covPhiPhi", trk.bestTrack()->covariance(2, 2));
207+
pcand.addUserFloat("covQopLam", trk.bestTrack()->covariance(0, 1));
208+
pcand.addUserFloat("covQopPhi", trk.bestTrack()->covariance(0, 2));
209+
pcand.addUserFloat("covLamPhi", trk.bestTrack()->covariance(1, 2));
210+
211+
// Additional track parameters for tagging
212+
pcand.addUserFloat("ptErr", trk.bestTrack()->ptError());
213+
pcand.addUserFloat("normChi2", trk.bestTrack()->normalizedChi2());
214+
pcand.addUserInt("nValidPixelHits", trk.numberOfPixelHits());
215+
216+
// adding the candidate in the composite stuff for fit (need to test)
217+
if (iTrk < nTracks)
218+
pcand.addUserCand("cand", edm::Ptr<pat::PackedCandidate>(tracks, iTrk));
219+
else
220+
pcand.addUserCand("cand", edm::Ptr<pat::PackedCandidate>(lostTracks, iTrk - nTracks));
221+
222+
// in order to avoid revoking the sxpensive ttrack builder many times and
223+
// still have everything sorted, we add them to vector of pairs
224+
match_indices[iTrk] = vectrk_ttrk.size();
225+
vectrk_ttrk.emplace_back(std::make_pair(pcand, trackTT));
226+
}
227+
228+
std::vector<int> sort_indices(vectrk_ttrk.size());
229+
std::iota(sort_indices.begin(), sort_indices.end(), 0);
230+
231+
// sort to be uniform with leptons
232+
// sort by index since we want to update the match too
233+
std::sort(sort_indices.begin(), sort_indices.end(), [&vectrk_ttrk](auto &iTrk1, auto &iTrk2) -> bool {
234+
return (vectrk_ttrk[iTrk1].first).pt() > (vectrk_ttrk[iTrk2].first).pt();
235+
});
236+
// std::sort( vectrk_ttrk.begin(), vectrk_ttrk.end(),
237+
// [] ( auto & trk1, auto & trk2) ->
238+
// bool {return (trk1.first).pt() > (trk2.first).pt();}
239+
// );
240+
241+
// finally save ttrks and trks to the correct _out vectors
242+
// also fill the reverse matching
243+
std::vector<int> reverse_sort_indices(vectrk_ttrk.size());
244+
for (size_t iSort = 0; iSort < sort_indices.size(); iSort++) {
245+
auto iUnsortedTrack = sort_indices[iSort];
246+
auto &&trk = vectrk_ttrk[iUnsortedTrack];
247+
tracks_out->emplace_back(trk.first);
248+
trans_tracks_out->emplace_back(trk.second);
249+
reverse_sort_indices[iUnsortedTrack] = iSort;
250+
}
251+
252+
// Now point the match indices to the sorted output collection
253+
std::transform(
254+
match_indices.begin(), match_indices.end(), match_indices.begin(), [&reverse_sort_indices](int iUnsortedTrack) {
255+
if (iUnsortedTrack < 0)
256+
return -1;
257+
return reverse_sort_indices[iUnsortedTrack];
258+
});
259+
260+
auto tracks_orphan_handle = evt.put(std::move(tracks_out), "SelectedTracks");
261+
evt.put(std::move(trans_tracks_out), "SelectedTransientTracks");
262+
263+
// Associate PackedCandidates to the merged Track collection
264+
auto tracks_out_match = std::make_unique<edm::Association<pat::CompositeCandidateCollection>>(tracks_orphan_handle);
265+
edm::Association<pat::CompositeCandidateCollection>::Filler filler(*tracks_out_match);
266+
267+
filler.insert(tracks, match_indices.begin(), match_indices.begin() + nTracks);
268+
filler.insert(lostTracks, match_indices.begin() + nTracks, match_indices.end());
269+
filler.fill();
270+
271+
evt.put(std::move(tracks_out_match), "SelectedTracks");
272+
}
273+
274+
// define this as a plug-in
275+
DEFINE_FWK_MODULE(BPHTrackMerger);

0 commit comments

Comments
 (0)