Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate 4D vertexing and downstream reco to full simulation/reconstruction track timestamps and add TOF PID (10_5_X) #25628

Merged
merged 27 commits into from Jan 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
a0c1929
migrate 4d vertexing to fullsim and first mostly working version of t…
bendavid Jan 9, 2019
b446e6d
add protection for infinite loop in 4d vertex reco
bendavid Jan 10, 2019
f616a36
migrate PFCandidate times to fullsim/reconstruction
bendavid Jan 11, 2019
48db64e
produce also fastsim-based 4d vertices for now
bendavid Jan 11, 2019
c834441
tweak pid algorithm and add some configurable parameters
bendavid Jan 11, 2019
f487a29
rearrange RecoVertex configuration for timing era so that 4d vertices…
bendavid Jan 11, 2019
cbda9a7
remove beamspot from TOFPIDProducer (not used for the moment)
bendavid Jan 11, 2019
f7cade4
fix input collections for validation sequence
bendavid Jan 12, 2019
3ca4322
fix computation of vertex time uncertainty
bendavid Jan 16, 2019
d354469
add outlier rejection for use of timing information in 4d vertex reco…
bendavid Jan 16, 2019
51e702d
inflate uncertainty on track times to account for ambiguity in mass h…
bendavid Jan 16, 2019
b770cf6
update logic and settings in TOFPIDProducer to produce an additional …
bendavid Jan 16, 2019
f777755
code cleanup and review
bendavid Jan 16, 2019
e2d6955
add primary vertex configuration for mtd to separate file
bendavid Jan 16, 2019
5fa91cf
add fillDescriptions to TOFPIDProducer
bendavid Jan 16, 2019
75d5b75
add helper functions to fill value maps
bendavid Jan 16, 2019
11c3795
make outlier rejection remove tracks entirely rather than just drop t…
bendavid Jan 17, 2019
7810d59
tune parameters for particle id
bendavid Jan 17, 2019
9cec85f
further improvements to vertex time uncertainty calculation
bendavid Jan 17, 2019
e1a553e
address further code review
bendavid Jan 17, 2019
0c7e810
make sure phase2_timing era (fastsim only for MTD) is still functional
bendavid Jan 17, 2019
a67e6a4
fix broken import
bendavid Jan 18, 2019
8ee451a
fix typo in default input collections
bendavid Jan 18, 2019
a4b2368
cleanup imports and modifications
bendavid Jan 18, 2019
64bc78d
revert to standard 3D vertices for default collections
bendavid Jan 18, 2019
a1ae1b8
add additional slimmed primary vertex collection to miniaod with timing
bendavid Jan 18, 2019
bbff806
split 4D vertices into separate cfi file
bendavid Jan 18, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
353 changes: 353 additions & 0 deletions CommonTools/RecoAlgos/plugins/TOFPIDProducer.cc
@@ -0,0 +1,353 @@
#include "FWCore/Framework/interface/Frameworkfwd.h"
#include "FWCore/Framework/interface/stream/EDProducer.h"
#include "FWCore/Framework/interface/Event.h"
#include "FWCore/Framework/interface/EventSetup.h"
#include "FWCore/Framework/interface/ESHandle.h"
#include "FWCore/ParameterSet/interface/ParameterSet.h"
#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h"

#include "DataFormats/VertexReco/interface/VertexFwd.h"
#include "DataFormats/TrackReco/interface/TrackFwd.h"
#include "DataFormats/Common/interface/ValueMap.h"
#include "DataFormats/BeamSpot/interface/BeamSpot.h"
#include "DataFormats/TrackReco/interface/Track.h"
#include "DataFormats/VertexReco/interface/Vertex.h"
#include "CLHEP/Units/GlobalPhysicalConstants.h"
#include "CLHEP/Units/GlobalSystemOfUnits.h"

using namespace std;
using namespace edm;

bendavid marked this conversation as resolved.
Show resolved Hide resolved
class TOFPIDProducer : public edm::stream::EDProducer<> {
public:

TOFPIDProducer(const ParameterSet& pset);

static void fillDescriptions(edm::ConfigurationDescriptions & descriptions);

template <class H, class T>
void fillValueMap(edm::Event& iEvent, const edm::Handle<H>& handle, const std::vector<T>& vec, const std::string& name) const;

void produce(edm::Event& ev, const edm::EventSetup& es) final;

bendavid marked this conversation as resolved.
Show resolved Hide resolved
private:
static constexpr char t0Name[] = "t0";
static constexpr char sigmat0Name[] = "sigmat0";
static constexpr char t0safeName[] = "t0safe";
static constexpr char sigmat0safeName[] = "sigmat0safe";
static constexpr char probPiName[] = "probPi";
static constexpr char probKName[] = "probK";
static constexpr char probPName[] = "probP";

edm::EDGetTokenT<reco::TrackCollection> tracksToken_;
edm::EDGetTokenT<edm::ValueMap<float> > t0Token_;
edm::EDGetTokenT<edm::ValueMap<float> > tmtdToken_;
edm::EDGetTokenT<edm::ValueMap<float> > sigmat0Token_;
edm::EDGetTokenT<edm::ValueMap<float> > sigmatmtdToken_;
edm::EDGetTokenT<edm::ValueMap<float> > pathLengthToken_;
edm::EDGetTokenT<edm::ValueMap<float> > pToken_;
edm::EDGetTokenT<reco::VertexCollection> vtxsToken_;
double vtxMaxSigmaT_;
double maxDz_;
double maxDtSignificance_;
double minProbHeavy_;
};



TOFPIDProducer::TOFPIDProducer(const ParameterSet& iConfig) :
tracksToken_(consumes<reco::TrackCollection>(iConfig.getParameter<edm::InputTag>("tracksSrc"))),
t0Token_(consumes<edm::ValueMap<float> >(iConfig.getParameter<edm::InputTag>("t0Src"))),
tmtdToken_(consumes<edm::ValueMap<float> >(iConfig.getParameter<edm::InputTag>("tmtdSrc"))),
sigmat0Token_(consumes<edm::ValueMap<float> >(iConfig.getParameter<edm::InputTag>("sigmat0Src"))),
sigmatmtdToken_(consumes<edm::ValueMap<float> >(iConfig.getParameter<edm::InputTag>("sigmatmtdSrc"))),
pathLengthToken_(consumes<edm::ValueMap<float> >(iConfig.getParameter<edm::InputTag>("pathLengthSrc"))),
pToken_(consumes<edm::ValueMap<float> >(iConfig.getParameter<edm::InputTag>("pSrc"))),
vtxsToken_(consumes<reco::VertexCollection>(iConfig.getParameter<edm::InputTag>("vtxsSrc"))),
vtxMaxSigmaT_(iConfig.getParameter<double>("vtxMaxSigmaT")),
maxDz_(iConfig.getParameter<double>("maxDz")),
maxDtSignificance_(iConfig.getParameter<double>("maxDtSignificance")),
minProbHeavy_(iConfig.getParameter<double>("minProbHeavy"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit surprised that there is no selection on impact parameter of the tracks.
Does it make much sense to associate tracks at d0 larger than maxDz?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Conversion tracks for example point more or less to the PV in z, but not in the transverse plane, so possibly yes.

{
produces<edm::ValueMap<float> >(t0Name);
produces<edm::ValueMap<float> >(sigmat0Name);
produces<edm::ValueMap<float> >(t0safeName);
produces<edm::ValueMap<float> >(sigmat0safeName);
produces<edm::ValueMap<float> >(probPiName);
produces<edm::ValueMap<float> >(probKName);
produces<edm::ValueMap<float> >(probPName);
}

// Configuration descriptions
void TOFPIDProducer::fillDescriptions(edm::ConfigurationDescriptions& descriptions) {
edm::ParameterSetDescription desc;
desc.add<edm::InputTag>("tracksSrc", edm::InputTag("generalTracks"))->
setComment("Input tracks collection");
desc.add<edm::InputTag>("t0Src", edm::InputTag("trackExtenderWithMTD:generalTrackt0"))->
setComment("Input ValueMap for track time at beamline");
desc.add<edm::InputTag>("tmtdSrc", edm::InputTag("trackExtenderWithMTD:generalTracktmtd"))->
setComment("Input ValueMap for track time at MTD");
desc.add<edm::InputTag>("sigmat0Src", edm::InputTag("trackExtenderWithMTD:generalTracksigmat0"))->
setComment("Input ValueMap for track time uncertainty at beamline");
desc.add<edm::InputTag>("sigmatmtdSrc", edm::InputTag("trackExtenderWithMTD:generalTracksigmatmtd"))->
setComment("Input ValueMap for track time uncertainty at MTD");
desc.add<edm::InputTag>("pathLengthSrc", edm::InputTag("trackExtenderWithMTD:generalTrackPathLength"))->
setComment("Input ValueMap for track path lengh from beamline to MTD");
desc.add<edm::InputTag>("pSrc", edm::InputTag("trackExtenderWithMTD:generalTrackp"))->
setComment("Input ValueMap for track momentum magnitude (normally from refit with MTD hits)");
desc.add<edm::InputTag>("vtxsSrc", edm::InputTag("unsortedOfflinePrimaryVertices4DnoPID"))->
setComment("Input primary vertex collection");
desc.add<double>("vtxMaxSigmaT", 0.025)->
setComment("Maximum primary vertex time uncertainty for use in particle id [ns]");
desc.add<double>("maxDz", 0.1)->
setComment("Maximum distance in z for track-primary vertex association for particle id [cm]");
desc.add<double>("maxDtSignificance", 5.0)->
setComment("Maximum distance in time (normalized by uncertainty) for track-primary vertex association for particle id");
desc.add<double>("minProbHeavy", 0.75)->
setComment("Minimum probability for a particle to be a kaon or proton before reassigning the timestamp");

descriptions.add("tofPIDProducer", desc);
}

template <class H, class T>
void TOFPIDProducer::fillValueMap(edm::Event& iEvent, const edm::Handle<H>& handle, const std::vector<T>& vec, const std::string& name) const {
auto out = std::make_unique<edm::ValueMap<T>>();
typename edm::ValueMap<T>::Filler filler(*out);
filler.insert(handle, vec.begin(), vec.end());
filler.fill();
iEvent.put(std::move(out),name);
}

void TOFPIDProducer::produce( edm::Event& ev, const edm::EventSetup& es ) {
constexpr double m_k = 0.493677; //[GeV]
constexpr double m_p = 0.9382720813; //[GeV]
constexpr double c_cm_ns = CLHEP::c_light*CLHEP::ns/CLHEP::cm; //[cm/ns]
constexpr double c_inv = 1.0/c_cm_ns;

edm::Handle<reco::TrackCollection> tracksH;
ev.getByToken(tracksToken_,tracksH);
const auto& tracks = *tracksH;

edm::Handle<edm::ValueMap<float> > t0H;
ev.getByToken(t0Token_, t0H);
const auto &t0In = *t0H;

edm::Handle<edm::ValueMap<float> > tmtdH;
ev.getByToken(tmtdToken_, tmtdH);
const auto &tmtdIn = *tmtdH;

edm::Handle<edm::ValueMap<float> > sigmat0H;
ev.getByToken(sigmat0Token_, sigmat0H);
const auto &sigmat0In = *sigmat0H;

edm::Handle<edm::ValueMap<float> > sigmatmtdH;
ev.getByToken(sigmatmtdToken_, sigmatmtdH);
const auto &sigmatmtdIn = *sigmatmtdH;

edm::Handle<edm::ValueMap<float> > pathLengthH;
ev.getByToken(pathLengthToken_, pathLengthH);
const auto &pathLengthIn = *pathLengthH;

edm::Handle<edm::ValueMap<float> > pH;
ev.getByToken(pToken_, pH);
const auto &pIn = *pH;

edm::Handle<reco::VertexCollection> vtxsH;
ev.getByToken(vtxsToken_,vtxsH);
const auto& vtxs = *vtxsH;

//output value maps (PID probabilities and recalculated time at beamline)
std::vector<float> t0OutRaw;
std::vector<float> sigmat0OutRaw;
std::vector<float> t0safeOutRaw;
std::vector<float> sigmat0safeOutRaw;
std::vector<float> probPiOutRaw;
std::vector<float> probKOutRaw;
std::vector<float> probPOutRaw;

//Do work here
for (unsigned int itrack = 0; itrack<tracks.size(); ++itrack) {
const reco::Track &track = tracks[itrack];
const reco::TrackRef trackref(tracksH,itrack);
float t0 = t0In[trackref];
float t0safe = t0;
float sigmat0safe = sigmat0In[trackref];
float sigmatmtd = sigmatmtdIn[trackref];
float sigmat0 = sigmatmtd;

float prob_pi = -1.;
float prob_k = -1.;
float prob_p = -1.;

if (sigmat0>0.) {

double rsigmazsq = 1./track.dzError()/track.dzError();
double rsigmat = 1./sigmatmtd;

//find associated vertex
int vtxidx = -1;
int vtxidxmindz = -1;
int vtxidxminchisq = -1;
double mindz = maxDz_;
double minchisq = std::numeric_limits<double>::max();
//first try based on association weights, but keep track of closest in z and z-t as well
for (unsigned int ivtx = 0; ivtx<vtxs.size(); ++ivtx) {
const reco::Vertex &vtx = vtxs[ivtx];
float w = vtx.trackWeight(trackref);
if (w>0.5) {
vtxidx = ivtx;
break;
}
double dz = std::abs(track.dz(vtx.position()));
if (dz<mindz) {
mindz = dz;
vtxidxmindz = ivtx;
}
if (vtx.tError()>0. && vtx.tError()<vtxMaxSigmaT_) {
double dt = std::abs(t0-vtx.t());
double dtsig = dt*rsigmat;
double chisq = dz*dz*rsigmazsq + dtsig*dtsig;
if (dz<maxDz_ && dtsig<maxDtSignificance_ && chisq<minchisq) {
minchisq = chisq;
vtxidxminchisq = ivtx;
}
}
}

//if no vertex found based on association weights, fall back to closest in z or z-t
if (vtxidx<0) {
//if closest vertex in z does not have valid time information, just use it,
//otherwise use the closest vertex in z-t plane with timing info, with a fallback to the closest in z
if (vtxidxmindz>=0 && !(vtxs[vtxidxmindz].tError()>0. && vtxs[vtxidxmindz].tError()<vtxMaxSigmaT_)) {
vtxidx = vtxidxmindz;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't this full if/else equivalent to
vtxidx = vtxidxminchisq>=0 ? vtxidxminchisq : vtxidxmindz; ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, because vtxidxmindz may be different from vtxidxminchisq, and in some cases we prefer the former

else if (vtxidxminchisq>=0) {
vtxidx = vtxidxminchisq;
}
else if (vtxidxmindz>=0) {
vtxidx = vtxidxmindz;
}
}

//testing mass hypotheses only possible if there is an associated vertex with time information
if (vtxidx>=0 && vtxs[vtxidx].tError()>0. && vtxs[vtxidx].tError()<vtxMaxSigmaT_) {
//compute chisq in z-t plane for nominal vertex and mass hypothesis (pion)
const reco::Vertex &vtxnom = vtxs[vtxidx];
double dznom = std::abs(track.dz(vtxnom.position()));
double dtnom = std::abs(t0 - vtxnom.t());
double dtsignom = dtnom*rsigmat;
double chisqnom = dznom*dznom*rsigmazsq + dtsignom*dtsignom;

//recompute t0 for alternate mass hypotheses
double t0_best = t0;

//reliable match, revert to raw mtd time uncertainty
if (dtsignom < maxDtSignificance_) {
sigmat0safe = sigmatmtd;
}

double tmtd = tmtdIn[trackref];
double pathlength = pathLengthIn[trackref];
double magp = pIn[trackref];

double gammasq_k = 1. + magp*magp/m_k/m_k;
double beta_k = std::sqrt(1.-1./gammasq_k);
double t0_k = tmtd - pathlength/beta_k*c_inv;

double gammasq_p = 1. + magp*magp/m_p/m_p;
double beta_p = std::sqrt(1.-1./gammasq_p);
double t0_p = tmtd - pathlength/beta_p*c_inv;

double chisqmin = chisqnom;

double chisqmin_pi = chisqnom;
double chisqmin_k = std::numeric_limits<double>::max();
double chisqmin_p = std::numeric_limits<double>::max();
//loop through vertices and check for better matches
for (const reco::Vertex &vtx : vtxs) {
if (!(vtx.tError()>0. && vtx.tError()<vtxMaxSigmaT_)) {
continue;
}

double dz = std::abs(track.dz(vtx.position()));
if (dz>=maxDz_) {
continue;
}

double chisqdz = dz*dz*rsigmazsq;

double dt_k = std::abs(t0_k - vtx.t());
double dtsig_k = dt_k*rsigmat;
double chisq_k = chisqdz + dtsig_k*dtsig_k;

if (dtsig_k < maxDtSignificance_ && chisq_k<chisqmin_k) {
chisqmin_k = chisq_k;
}

double dt_p = std::abs(t0_p - vtx.t());
double dtsig_p = dt_p*rsigmat;
double chisq_p = chisqdz + dtsig_p*dtsig_p;

if (dtsig_p < maxDtSignificance_ && chisq_p<chisqmin_p) {
chisqmin_p = chisq_p;
}

if (dtsig_k < maxDtSignificance_ && chisq_k<chisqmin) {
chisqmin = chisq_k;
t0_best = t0_k;
t0safe = t0_k;
sigmat0safe = sigmatmtd;
}
if (dtsig_p < maxDtSignificance_ && chisq_p<chisqmin) {
chisqmin = chisq_p;
t0_best = t0_p;
t0safe = t0_p;
sigmat0safe = sigmatmtd;
}

}

//compute PID probabilities
//*TODO* deal with heavier nucleons and/or BSM case here?
double rawprob_pi = exp(-0.5*chisqmin_pi);
double rawprob_k = exp(-0.5*chisqmin_k);
double rawprob_p = exp(-0.5*chisqmin_p);

double normprob = 1./(rawprob_pi + rawprob_k + rawprob_p);

prob_pi = rawprob_pi*normprob;
prob_k = rawprob_k*normprob;
prob_p = rawprob_p*normprob;

double prob_heavy = 1.-prob_pi;

if (prob_heavy>minProbHeavy_) {
t0 = t0_best;
}

}

}

t0OutRaw.push_back(t0);
sigmat0OutRaw.push_back(sigmat0);
t0safeOutRaw.push_back(t0safe);
sigmat0safeOutRaw.push_back(sigmat0safe);
probPiOutRaw.push_back(prob_pi);
probKOutRaw.push_back(prob_k);
probPOutRaw.push_back(prob_p);
}

fillValueMap(ev, tracksH, t0OutRaw, t0Name);
fillValueMap(ev, tracksH, sigmat0OutRaw, sigmat0Name);
fillValueMap(ev, tracksH, t0safeOutRaw, t0safeName);
fillValueMap(ev, tracksH, sigmat0safeOutRaw, sigmat0safeName);
fillValueMap(ev, tracksH, probPiOutRaw, probPiName);
fillValueMap(ev, tracksH, probKOutRaw, probKName);
fillValueMap(ev, tracksH, probPOutRaw, probPName);

}

//define this as a plug-in
#include <FWCore/Framework/interface/MakerMacros.h>
DEFINE_FWK_MODULE(TOFPIDProducer);
4 changes: 4 additions & 0 deletions CommonTools/RecoAlgos/python/tofPID_cfi.py
@@ -0,0 +1,4 @@
import FWCore.ParameterSet.Config as cms

from CommonTools.RecoAlgos.tofPIDProducer_cfi import tofPIDProducer
tofPID = tofPIDProducer.clone()
Expand Up @@ -137,3 +137,7 @@
_phase2_hgc_extraCommands = ["keep *_slimmedElectronsFromMultiCl_*_*", "keep *_slimmedPhotonsFromMultiCl_*_*"]
from Configuration.Eras.Modifier_phase2_hgcal_cff import phase2_hgcal
phase2_hgcal.toModify(MicroEventContentMC, outputCommands = MicroEventContentMC.outputCommands + _phase2_hgc_extraCommands)

_phase2_timing_extraCommands = ["keep *_offlineSlimmedPrimaryVertices4D_*_*"]
from Configuration.Eras.Modifier_phase2_timing_cff import phase2_timing
phase2_timing.toModify(MicroEventContentMC, outputCommands = MicroEventContentMC.outputCommands + _phase2_timing_extraCommands)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bendavid @slava77 given the other discussions in the review about phase2_timing vs phase2_timing_layer , is this really the desired behaviour?

@@ -0,0 +1,5 @@
import FWCore.ParameterSet.Config as cms

offlineSlimmedPrimaryVertices4D = cms.EDProducer("PATVertexSlimmer",
src = cms.InputTag("offlinePrimaryVertices4D"),
)
6 changes: 6 additions & 0 deletions PhysicsTools/PatAlgos/python/slimming/slimming_cff.py
Expand Up @@ -4,6 +4,7 @@
from PhysicsTools.PatAlgos.slimming.isolatedTracks_cfi import *
from PhysicsTools.PatAlgos.slimming.lostTracks_cfi import *
from PhysicsTools.PatAlgos.slimming.offlineSlimmedPrimaryVertices_cfi import *
from PhysicsTools.PatAlgos.slimming.offlineSlimmedPrimaryVertices4D_cfi import *
from PhysicsTools.PatAlgos.slimming.primaryVertexAssociation_cfi import *
from PhysicsTools.PatAlgos.slimming.genParticles_cff import *
from PhysicsTools.PatAlgos.slimming.selectedPatTrigger_cfi import *
Expand Down Expand Up @@ -56,3 +57,8 @@

from Configuration.Eras.Modifier_pp_on_AA_2018_cff import pp_on_AA_2018
pp_on_AA_2018.toReplaceWith(slimmingTask, slimmingTask.copyAndExclude([slimmedOOTPhotons]))

from Configuration.Eras.Modifier_phase2_timing_cff import phase2_timing
_phase2_timing_slimmingTask = cms.Task(slimmingTask.copy(),
offlineSlimmedPrimaryVertices4D)
phase2_timing.toReplaceWith(slimmingTask,_phase2_timing_slimmingTask)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bendavid @slava77 given the other discussions in the review about phase2_timing vs phase2_timing_layer , is this really the desired behaviour?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this adds an additional slimmed primary vertex collection to the miniaod. In the case of phase2_timing alone, this corresponds to the fastsim 4d vertices, in the case of phase2_timing_layer, this corresponds to fullsim 4d vertices.