Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
#include "CommonTools/RecoAlgos/interface/PrimaryVertexAssignment.h"
#include "DataFormats/ParticleFlowCandidate/interface/PFCandidate.h"
#include "DataFormats/VertexReco/interface/Vertex.h"
#include "TrackingTools/TransientTrack/interface/TransientTrackBuilder.h"
#include "DataFormats/Math/interface/deltaR.h"
#include "TrackingTools/IPTools/interface/IPTools.h"
#include "FWCore/Utilities/interface/isFinite.h"

std::pair<int, PrimaryVertexAssignment::Quality> PrimaryVertexAssignment::chargedHadronVertex(
    const reco::VertexCollection& vertices,
    const reco::TrackRef& trackRef,
    const reco::Track* track,
    float time,
    float timeReso,  // <0 if timing not available for this object
    const edm::View<reco::Candidate>& jets,
    const TransientTrackBuilder& builder) const {
  typedef reco::VertexCollection::const_iterator IV;
  typedef reco::Vertex::trackRef_iterator IT;

  int iVertex = -1;
  size_t index = 0;
  float bestweight = 0;
  for (auto const& vtx : vertices) {
    float w = vtx.trackWeight(trackRef);
    if (w > bestweight) {
      bestweight = w;
      iVertex = index;
    }
    index++;
  }
  return chargedHadronVertex(vertices, iVertex, track, time, timeReso, jets, builder);
}

std::pair<int, PrimaryVertexAssignment::Quality> PrimaryVertexAssignment::chargedHadronVertex(
    const reco::VertexCollection& vertices,
    int iVertex,
    const reco::Track* track,
    float time,
    float timeReso,  // <0 if timing not available for this object
    const edm::View<reco::Candidate>& jets,
    const TransientTrackBuilder& builder) const {
  typedef reco::VertexCollection::const_iterator IV;
  typedef reco::Vertex::trackRef_iterator IT;

  bool useTime = useTiming_;
  if (edm::isNotFinite(time) || timeReso < 1e-6) {
    useTime = false;
    time = 0.;
    timeReso = -1.;
  }

  if (preferHighRanked_) {
    for (IV iv = vertices.begin(); iv != vertices.end(); ++iv) {
      int ivtx = iv - vertices.begin();
      if (useVertexFit_ && (iVertex == ivtx))
        return {ivtx, PrimaryVertexAssignment::UsedInFit};

      double dz = std::abs(track->dz(iv->position()));
      double dt = std::abs(time - iv->t());

      bool useTimeVtx = useTime && iv->tError() > 0.;

      if ((dz < maxDzForPrimaryAssignment_ or dz / track->dzError() < maxDzSigForPrimaryAssignment_) and
          (!useTimeVtx or dt / timeReso < maxDtSigForPrimaryAssignment_)) {
        return {ivtx, PrimaryVertexAssignment::PrimaryDz};
      }
    }
  }

  double firstVertexDz = std::numeric_limits<double>::max();
  if (!vertices.empty())
    firstVertexDz = std::abs(track->dz(vertices[0].position()));
  // recover cases where the primary vertex is split
  if (useVertexFit_ && (iVertex > 0) && (iVertex <= int(fNumOfPUVtxsForCharged_)) &&
      firstVertexDz < fDzCutForChargedFromPUVtxs_)
    return {0, PrimaryVertexAssignment::PrimaryDz};

  if (useVertexFit_ && (iVertex >= 0))
    return {iVertex, PrimaryVertexAssignment::UsedInFit};

  double distmin = std::numeric_limits<double>::max();
  double dzmin = std::numeric_limits<double>::max();
  double dtmin = std::numeric_limits<double>::max();
  int vtxIdMinSignif = -1;
  for (IV iv = vertices.begin(); iv != vertices.end(); ++iv) {
    double dz = std::abs(track->dz(iv->position()));
    double dt = std::abs(time - iv->t());

    double dzsig = dz / track->dzError();
    double dist = dzsig * dzsig;

    bool useTimeVtx = useTime && iv->tError() > 0.;
    if (useTime && !useTimeVtx) {
      dt = timeReso;
    }

    if (useTime) {
      double dtsig = dt / timeReso;

      dist += dtsig * dtsig;
    }
    if (dist < distmin) {
      distmin = dist;
      dzmin = dz;
      dtmin = dt;
      vtxIdMinSignif = iv - vertices.begin();
    }
  }

  // protect high pT particles from association to pileup vertices and assign them to the first vertex
  if ((fPtMaxCharged_ > 0) && (vtxIdMinSignif >= 0) && (track->pt() > fPtMaxCharged_)) {
    iVertex = 0;
  } else {
    // first use "closest in Z" with tight cuts (targetting primary particles)
    const float add_cov = vtxIdMinSignif >= 0 ? vertices[vtxIdMinSignif].covariance(2, 2) : 0.f;
    const float dzE = sqrt(track->dzError() * track->dzError() + add_cov);
    if (!fOnlyUseFirstDz_) {
      if (vtxIdMinSignif >= 0 and
          (dzmin < maxDzForPrimaryAssignment_ and dzmin / dzE < maxDzSigForPrimaryAssignment_ and
           track->dzError() < maxDzErrorForPrimaryAssignment_) and
          (!useTime or dtmin / timeReso < maxDtSigForPrimaryAssignment_))
        iVertex = vtxIdMinSignif;
    } else {
      // consider only distances to first vertex for association (originally used in PUPPI)
      if ((vtxIdMinSignif >= 0) && (std::abs(track->eta()) > fEtaMinUseDz_) &&
          ((firstVertexDz < maxDzForPrimaryAssignment_ && firstVertexDz / dzE < maxDzSigForPrimaryAssignment_ &&
            track->dzError() < maxDzErrorForPrimaryAssignment_) &&
           (!useTime || std::abs(time - vertices[0].t()) / timeReso < maxDtSigForPrimaryAssignment_)))
        iVertex = 0;
    }
  }

  if (iVertex >= 0)
    return {iVertex, PrimaryVertexAssignment::PrimaryDz};

  // if track not assigned yet, it could be a b-decay secondary , use jet axis dist criterion
  // first find the closest jet within maxJetDeltaR_
  int jetIdx = -1;
  double minDeltaR2 = maxJetDeltaR_ * maxJetDeltaR_;
  for (edm::View<reco::Candidate>::const_iterator ij = jets.begin(); ij != jets.end(); ++ij) {
    if (ij->pt() < minJetPt_)
      continue;  // skip jets below the jet Pt threshold

    double deltaR2 = reco::deltaR2(*ij, *track);
    if (deltaR2 < minDeltaR2 and track->dzError() < maxDzErrorForPrimaryAssignment_) {
      minDeltaR2 = deltaR2;
      jetIdx = std::distance(jets.begin(), ij);
    }
  }
  // if jet found
  if (jetIdx != -1) {
    reco::TransientTrack transientTrack = builder.build(*track);
    GlobalVector direction(jets.at(jetIdx).px(), jets.at(jetIdx).py(), jets.at(jetIdx).pz());
    // find the vertex with the smallest distanceToJetAxis that is still within maxDistaneToJetAxis_
    int vtxIdx = -1;
    double minDistanceToJetAxis = maxDistanceToJetAxis_;
    for (IV iv = vertices.begin(); iv != vertices.end(); ++iv) {
      // only check for vertices that are close enough in Z and for tracks that have not too high dXY
      if (std::abs(track->dz(iv->position())) > maxDzForJetAxisAssigment_ ||
          std::abs(track->dxy(iv->position())) > maxDxyForJetAxisAssigment_)
        continue;

      double distanceToJetAxis = IPTools::jetTrackDistance(transientTrack, direction, *iv).second.value();
      if (distanceToJetAxis < minDistanceToJetAxis) {
        minDistanceToJetAxis = distanceToJetAxis;
        vtxIdx = std::distance(vertices.begin(), iv);
      }
    }
    if (vtxIdx >= 0) {
      iVertex = vtxIdx;
    }
  }
  if (iVertex >= 0)
    return {iVertex, PrimaryVertexAssignment::BTrack};

  // if the track is not compatible with other PVs but is compatible with the BeamSpot, we may simply have not reco'ed the PV!
  //  we still point it to the closest in Z, but flag it as possible orphan-primary
  if (!vertices.empty() && std::abs(track->dxy(vertices[0].position())) < maxDxyForNotReconstructedPrimary_ &&
      std::abs(track->dxy(vertices[0].position()) / track->dxyError()) < maxDxySigForNotReconstructedPrimary_)
    return {vtxIdMinSignif, PrimaryVertexAssignment::NotReconstructedPrimary};

  //FIXME: here we could better handle V0s and NucInt

  // all other tracks could be non-B secondaries and we just attach them with closest Z
  if (vtxIdMinSignif >= 0)
    return {vtxIdMinSignif, PrimaryVertexAssignment::OtherDz};
  //If for some reason even the dz failed (when?) we consider the track not assigned
  return {-1, PrimaryVertexAssignment::Unassigned};
}