Offline Primary Vertex Production

Complete: 4
Detailed Review status

Contents

Algorithm description

The offline primary vertex reconstruction proceeds as follows:

  • reconstructed tracks are selected based on their compatibilty with the beam spot, number of hits and fit quality
  • the tracks are clustered into several primary vertex candidates, according to the z-coordinate of the point of closest approach of the tracks to the z-axis
  • a (3d) vertex fit is performed with the tracks of each primary vertex candidate
  • primary vertex candidates compatible with the beam line are retained.

Starting with CMSSW 4.2.0.pre7 the clustering algorithm is the deterministic annealing (DA) clustering, which is briefly described below. A description of the gap and DA clustering can be found in CMS IN-2011/014.

From CMSSW 1.8.0 onwards, if no reconstructed vertex is found in an event, a vertex based on the beam-spot is put into the event. In this case, the vertex contains no tracks (as non have been used) , the chi^2 and the ndof are 0, and the flag isFake() is set to true.

The reconstruction of Primary vertices using the full tracks is part of the default reconstruction sequence, using the tracks from the generaltracks collection and the offlineBeamSpot BeamSpot. The vertices are stored in the event as reco::Vertex collections. Two passes are done:

  • OfflinePrimaryVertices: Primary vertex reconstructed using the tracks taken from the generalTracks collection, using the BeamSpot only for the track selection and the final cleanup. (config in cvs)
  • OfflinePrimaryVerticesWithBS: Primary vertex reconstructed using the tracks taken from the generalTracks collection, and imposing the offline beam spot as a constraint in the fit of the vertex position. (config in cvs)

Between CMSSW 4.2.X and CMSSW 4.4.x the producer was reorganized such that the clustering is only done once for both, the unconstrained and the constrained collection. This requires some changes in the configuration files that are described below.

Production of primary vertices

The example below shows how to reconstruct primary vertices from a file containing tracks, and store these vertices in a file called pv_reco.root. The module sequence is defined in the following CMSSW config file demoProducePrimaryVertex_cfg.py. The tracks used on input are also copied, for analysis and debugging.

In the src area of your release, do:

addpkg Validation/RecoVertex
cd Validation/RecoVertex
scramv1 b
cd test
cmsRun demoProducePrimaryVertex.cfg
This produces the file pv_reco.root, which contains the data collections retained, tracks and vertices. Opening a this file in root, the data of the primary vertices (such as the coordinates, chi-squared, degrees of freedom) can be viewed directly.

A simple analysis of the primary vertices is described in the Workbook.

Redoing primary vertex reconstruction on the fly

The vertex reconstruction may have to be redone when the reconstruction parameters used at the time of the original processing were not optimal for the events of interest. This can be done when the generalTracks are present in the input file. An example can be found in redoPV_cfg.py

CMSSW_3_6_0

The config file format has changed slightly for CMSSW_3_6_0. A configurable cut on the minimal number of degrees of freedom has been introduced to permit the rejection of vertices effectively consisting of only one track (i.e. two or more mutually incompatible tracks). The track selection now uses layers with hits instead of hits. An alternative clustering algorithm is now available. For a description see below this . The format of the config files now permits selecting the different clustering algorithms. A config file running the deterministic annealing clustering has been added under the name OfflinePrimaryVerticesDA_cfi.py and an example for running it can be found in produceAndAnalyzePrimaryVertex_cfg.py.

DA clustering

The DA clustering is based on the same principles as the AdaptiveVertexFitter or the MultiVertexFitter. A global chi^2 in which each track is assigned to vertex prototypes is minimized. The assignment is soft, i.e. the weights can be any number between 0 and 1 based on the compatibilty of vertex and track. The softness of the assignment is controlled by a "temperature" paramter. At infinite temperature all assignement weights are equal, while at T=0 the weights are either 0 or 1. The gradual decrease of the temperature is called annealing. The clustering starts with a single protoype at high T. For each iteration the protoype is split into two identical prototypes separated by tiny distance in z. If all tracks are compatible with a single vertex, the two protoypes collaps to the same position. As the temperature decreases and the assignment becomes less soft, the two protoypes start to separate and track assignment weights start to differ. In subsequent iterations, separated prototypes can again split as required. This is annealing phase is continued down to a configurable temperature Tmin. At this point the splitting stops. Protoypes not containing at least two tracks that are incompatible with any other prototype are removed one by one in order to avoid fake vertices caused by track outliers. In the following phase, between T=Tmin and T=1, vertex prototypes are not split anymore, only vertex positions and assignment weights evolve. Finally, each protopype is becomes a vertex candidate with all tracks that have an assignment weight greater than 0.5. This track list is passed to the AdaptiveVertexFitter for a full three dimensional fit of the vertex (the clustering only uses the z-coordinate). A more detailed description can be found in this presentation.

The default configuration of the clustering looks as follows:

    TkClusParameters = cms.PSet(
        algorithm   = cms.string("DA"),
        TkDAClusParameters = cms.PSet(
            coolingFactor = cms.double(0.6),  #  moderate annealing speed
            Tmin = cms.double(4.),            #  end of annealing
            vertexSize = cms.double(0.01),    #  ~ resolution / sqrt(Tmin)
            d0CutOff = cms.double(3.),        # downweight high IP tracks                    (only in 42X and higher)
            dzCutOff = cms.double(4.)         # outlier rejection after freeze-out (T<Tmin)  (only in 42X and higher)
        )
    )

The coolingFactor determines how much the Temperature is decreased between iterations. The start temperature is chosen automatically slightly above the point where the initial cluster starts to separate. At Tmin, the vertex splitting is stopped. In order to reduce splitting of vertices by secondary tracks, vertices are allowed to have a "size". The vertexSize parameter is added quadratically to the z-error of every track. The impact of secondary tracks is further reduces by downweighting tracks with significant impact parameter (~1./(1.+exp(d0^2-d0CutOff^2))). The dzCutOff parameter is only used during the last phase to allow outlier downweighting as in the AdaptiveVertexFitter.

More detailed information is available in in the CMS IN-2011/014. A study of some properties is described in this report.

Primary vertex sorting

The primary vertex collection is sorted according to the sum of the Pt squared of the tracks associated to each vertex, such that the vertex with largest sum, likely to be the "signal" vertex, appears first. Some justification of this can be found in the Higgs note CMS-AN-11-129.

Change of the configuration file between 42X and 44X

In CMSSW4.2.X and earlier, the three steps of selection, clustering and fitting are done for each vertexcollection separately. Since for the two standard collection the first two steps were always the same and the clustering time became a significant part of the CPU time with high pile-up, this was changed in CMSSW_4.4.X and higher.

This change is reflected in the configuration. Files that contain or modify configurations for the PrimaryVertexProducer may have to be modified. The parameters controlling vertex fitting and selection ("algorithm","useBeamConstraint", "minNdof" and "maxDistanceToBeam") are new part of VPset called "vertexCollections". For the normal reco sequence this looks as follows:

    vertexCollections = cms.VPSet(
     [cms.PSet(label=cms.string(""),
               algorithm=cms.string("AdaptiveVertexFitter"),
               minNdof=cms.double(0.0),
               useBeamConstraint = cms.bool(False),
               maxDistanceToBeam = cms.double(1.0)
               ),
      cms.PSet(label=cms.string("WithBS"),
               algorithm = cms.string('AdaptiveVertexFitter'),
               minNdof=cms.double(2.0),
               useBeamConstraint = cms.bool(True),
               maxDistanceToBeam = cms.double(1.0)
               )
      ]
    )

A configurable label is used to distinguish the collections. The (intermediate) result of the standard reco sequence offlinePrimaryVertices are the two collections "offlinePrimaryVertices__" and "offlinePrimaryVertices_withBS_". In order to make this change transparent the latter collection is renamed copied to "offlinePrimaryVerticeswithBS__" and "offlinePrimaryVertices_withBS_" is dropped.

A config file that attempts to override e.g. the fit algorithm will have to be modified from offlinePrimaryVertices.algorithm = cms.string("KalmanVertexFitter") to offlinePrimaryVertices.vertexCollection[0].algorithm = cms.string("KalmanVertexFitter") , and for the minNdof parameter offlinePrimaryVertices.PVSelParameters.minNdof = cms.double(4.0) to offlinePrimaryVertices.vertexCollection[0].minNdof = cms.double(4.0) .

Refitting vertices with selected tracks / excluding tracks from the vertex fit

It is sometimes useful to redo the vertex fit with a modified tracklist, notably excluding certain tracks that are candidates for a secondary vertex. The required steps are illustrated in the following code snippet.

   vector<TransientTrack> mytracks;
 
  for(auto tv=v->tracks_begin(); tv!=v->tracks_end(); tv++){
    const reco::TrackRef trackRef = tv->castTo<reco::TrackRef>();

     if ( this track should be used for fitting ) {
       TransientTrack  transientTrack = theTransientTrackBuilder_->build(trackRef); 
       transientTrack.setBeamSpot(vertexBeamSpot);
       mytracks.push_back(transientTrack);
  }
  }

followed by

     AdaptiveVertexFitter  theFitter;
    TransientVertex myVertex = theFitter.vertex(mytracks, vertexBeamSpot);  // if you want the beam constraint
 

A complete example could look like this:

...::analyze(const Event& iEvent, const EventSetup& iSetup){

   /*** get beamspot and TransientTrackBuilder from the event/eventSetup ***/
   edm::Handle<reco::BeamSpot> recoBeamSpotHandle;
   iEvent.getByLabel("offlineBeamSpot", recoBeamSpotHandle);
   reco::BeamSpot vertexBeamSpot= *recoBeamSpotHandle;
   edm::ESHandle<TransientTrackBuilder> theTransientTrackBuilder;
   iSetup.get<TransientTrackRecord>().get("TransientTrackBuilder",theTransientTrackBuilder);



   /*** select your vertex ***/
    Handle<reco::VertexCollection> recVtxs;
    iEvent.getByLabel("offlinePrimaryVertices", recVtxs);
   // the following just takes the first one. With pile-up this is very likely not the right choice
   const reco::Vertex* v=&(*recVtxs->begin()); 


   /*** get the tracks from the vertex and build a new tracklist ***/
   vector<TransientTrack> mytracks;
 
  for(auto tv=v->tracks_begin(); tv!=v->tracks_end(); tv++){
    const reco::TrackRef trackRef = tv->castTo<reco::TrackRef>();

    /*** filter out some tracks ***/
    if (trackRef->px()>0) continue;
    // if(trackRef.key()==someothertrack.key()) continue;

    /*** use all others for fitting, needs transient tracks ***/
    TransientTrack  transientTrack = theTransientTrackBuilder_->build(trackRef); 
    transientTrack.setBeamSpot(vertexBeamSpot);
    mytracks.push_back(transientTrack);
  }


  /*** fit the vertex with the selected tracks ***/
   if( mytracks.size()>1 ){
     AdaptiveVertexFitter  theFitter;
    //TransientVertex myVertex = theFitter.vertex(mytracks, vertexBeamSpot);  // if you want the beam constraint
     TransientVertex myVertex = theFitter.vertex(mytracks);  // if you don't want the beam constraint
    // now you have a new vertex, can e.g. be compared with the original
     std::cout << "TEST   " << v->position().z() << " " << myVertex.position().z() << std::endl;
  }else{
    std::cout << "not enough tracks left" <<std::endl;
  }
 

}

Review status

Reviewer/Editor and Date (copy from screen) Comments
Main.werdmann - 04 Dec 2006 update
ThomasSpeer - 11 Feb 2008 update
ThomasSpeer - 13 Oct 2008 update
IanTomalin - 21 Jun 2011 update

Responsible: ThomasSpeer
Last reviewed by: ThomasSpeer - 11 Feb 2008

Edit | Attach | Watch | Print version | History: r18 < r17 < r16 < r15 < r14 | Backlinks | Raw View | WYSIWYG | More topic actions
Topic revision: r18 - 2015-11-23 - WolframErdmann
 
    • Cern Search Icon Cern Search
    • TWiki Search Icon TWiki Search
    • Google Search Icon Google Search

    CMSPublic All webs login

This site is powered by the TWiki collaboration platform Powered by PerlCopyright & 2008-2019 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback