Unbinned PID resampling using kernel density estimation


PIDGen is a set of scripts within PIDCalib package to correct the PID response for MC signal samples based on PID calibration data (the same data as used by "traditional" PIDCalib approach). It implements two alternative approaches:

  • PID resampling, where the PID response is completely replaced by the one randomly generated from calibration PDFs.

  • Transformation of PID variables, where the PID variables from the simulation are transformed in such a way that they are distributed as in data.

Like in PIDCalib, the PID correction is a function of track kinematics (momentum and pseudorapidity) and event multiplicity (number of tracks). Unlike PIDCalib, the correction is done with an unbinned approach, where the calibration PDFs in four dimensions (PID, Pt, eta, Ntrack) are described by a kernel density estimation procedure using the Meerkat library.

The corrected PID response from both the PID resampling and PID variable transformation can be used as an input to a multivariate classifier.

The PID resampling approach has an important limitation that the PID variables for the same track are generated independently, and thus no correlations between them are reproduced. Therefore, only one PID variable per track can be used in the selection (correlations between variables for different tracks are, naturally, preserved via correlations with kinematics of tracks).

The approach with PID variable transformation aims to remove this limitation. In that case, the corrected PID variables preserve correlations with the output of simulation, and through the correlations in simulation the correlations between PID variables for the same track are reproduced "in the first order". This is not perfect by probably acceptable in most cases. The drawback of this approach is that it also relies on the parametrisation of PID PDFs in simulation (which are extracted from samples that are typically much smaller than calibration data).

More details about the approach can be found in this presentation and in the LHCb-INT-2017-007 internal note draft.

Contents of the package

All the user-level scripts are located in $PIDPERFSCRIPTSROOT/scripts/python/PIDGenUser/ directory under PIDCalib package in Urania project. Here is the GitLab repository of the code.

  • PIDGen.py - Python script for PID resampling.

  • PIDCorr.py - Python script for PID variable transformation.

  • Examples/Lb2Lcpi/Lb2Lcpi_pidgen.py - Example PID resampling script for Lb->Lcpi MC sample

  • Examples/Lb2Lcpi/Lb2Lcpi_pidcorr.py - Example script for PID variable transformation for Lb->Lcpi MC sample

Note that the scripts use data stored in CERN EOS, and assume that it's accessible at root://eoslhcb.cern.ch/ server.

Using PIDGen with MC samples

Generation of PID response is performed by one of the two scripts, PIDGen.py or PIDCorr.py. The scripts require many parameters (MC data location, variable names, location of the calibrated PID response etc.). The sample python scripts are provided, Examples/Lb2Lcpi/Lb2Lcpi_{pidgen, pidcorr}, which the user can modify to actually run the PID generation for their own MC sample.

Running PID resampling (PIDGen)

To run PIDGen resampling, you need to setup Urania v7r0, copy the example script to a directory with write access (the output ntuple will be created there) and run it. To benefit from the latest updates in the package, it is recommended to run from Urania HEAD nightlies:

$ lb-run -c x86_64-slc6-gcc8-opt --nightly lhcb-head Urania/HEAD bash
$ cp $PIDPERFSCRIPTSROOT/scripts/python/PIDGenUser/Examples/Lb2Lcpi/Lb2Lcpi_pidgen.py {your_place}
$ cd {your_place}
$ python Lb2Lcpi_pidgen.py

The complete list of PID configurations available in Run 1 and Run 2 can be obtained by running PIDGen.py without arguments:

$ python $PIDPERFSCRIPTSROOT/scripts/python/PIDGenUser/PIDGen.py
Note that Run 1 and Run 2 PID configurations have different names!

Running PID variable transformation (PIDCorr)

To run PIDCorr variable transformation, you need to setup Urania, copy the example script to a directory with write access (the output ntuple will be created there) and run it:

$ lb-run -c x86_64-slc6-gcc8-opt --nightly lhcb-head Urania/HEAD bash
$ cp $PIDPERFSCRIPTSROOT/scripts/python/PIDGenUser/Examples/Lb2Lcpi/Lb2Lcpi_pidcorr.py {your_place}
$ cd {your_place}
$ python Lb2Lcpi_pidcorr.py

The result of PIDCorr is dependent on the version of MC used to generate your signal (sim08 or sim09)! The complete list of PID configurations available can be obtained by running PIDCorr.py without arguments:

$ python $PIDPERFSCRIPTSROOT/scripts/python/PIDGenUser/PIDGen.py

PID templates available in Urania v7r0 and nightlies

Available combinations of track and PID variable in Run 1:

Variable pi K p e mu
CombDLLK G/C8/C9 G/C8/C9 G/C8/C9   G
CombDLLK_IsMuon         G
CombDLLe G     G/C8 G
CombDLLmu G G     G/C9
CombDLLmu_IsMuon         G
CombDLLp G/C8/C9 G/C8/C9 G/C8/C9   G
S20CombDLLe       G  
S20V3ProbNNe       G  
V2ProbNNK G/C8/C9 G/C8/C9 G/C8/C9    
V2ProbNNe G        
V2ProbNNp G/C8/C9 G/C8/C9 G/C8/C9    
V2ProbNNpi G/C8/C9 G/C8/C9 G/C8/C9    
V3ProbNNK G/C8/C9 G/C8/C9 G/C8/C9 G/C8 G/C9
V3ProbNNe       G/C8  
V3ProbNNmu G G     G/C9
V3ProbNNp G/C8/C9 G/C8/C9 G/C8/C9    
V3ProbNNpi G/C8/C9 G/C8/C9 G/C8/C9 G/C8 G/C9

Available combinations of track and PID variable in Run 2:

Variable pi K p e mu
CombDLLK_Brunel G/C9 G/C9 G G G
CombDLLK_IsMuon_Brunel         G
CombDLLK_Stripping       G  
CombDLLe_Brunel G     G G
CombDLLe_Stripping       G  
CombDLLmu_Brunel G/C9 G/C9   G G
CombDLLmu_IsMuon_Brunel         G
CombDLLmu_Stripping       G  
CombDLLmu_isMuon_Brunel         G
CombDLLp_Brunel G/C9 G/C9 G G G
CombDLLp_Stripping       G  
LbLcMu_MC15TuneV1_ProbNNp_Brunel     G    
LbLcPi_MC12TuneV2gt0.05_MC15TuneV     G    
LbLcPi_MC15TuneV1_ProbNNK_Brunel     G/C9    
LbLcPi_MC15TuneV1_ProbNNp_Brunel     G/C9    
LbLcPi_MC15TuneV1_ProbNNpi_Brunel     G/C9    
MC15TuneV1_ProbNNKNotpi_Brunel G G      
MC15TuneV1_ProbNNK_Brunel G/C9 G/C9 G G G
MC15TuneV1_ProbNNK_Brunel_Mod2   G/C9      
MC15TuneV1_ProbNNK_Stripping       G  
MC15TuneV1_ProbNNe_Brunel G     G  
MC15TuneV1_ProbNNe_Stripping       G  
MC15TuneV1_ProbNNmu_Brunel G/C9 G/C9   G G
MC15TuneV1_ProbNNmu_Stripping       G  
MC15TuneV1_ProbNNp_Brunel G/C9 G/C9 G G  
MC15TuneV1_ProbNNp_Stripping       G  
MC15TuneV1_ProbNNpiNotK_Brunel G G      
MC15TuneV1_ProbNNpi_Brunel G/C9 G/C9 G G G
MC15TuneV1_ProbNNpi_Brunel_Mod2 G/C9        
MC15TuneV1_ProbNNpi_Stripping       G  

G: PIDGen, C8: PIDCorr with sim08 MC, C9: PIDCorr with sim09 MC,

Electron templates ending with _Stripping are produced from the Stripping28 samples (updated sWeights calculation using HasBremAdded category). It is recommended to use those instead of the _Brunel versions (direct output from Turcal).

K_MC15TuneV1_ProbNNK_Brunel_Mod2 and pi_MC15TuneV1_ProbNNpi_Brunel_Mod2 templates feature improved description of the distribution near ProbNN=0 and are recommended to be used instead of x_MC15TuneV1_ProbNNx_Brunel versions (where a step-like structure can be observed around zero).

PIDGen templates MC15TuneV1_ProbNNpiNotK_Brunel and MC15TuneV1_ProbNNKNotpi_Brunel provide resampling for ProbNNpi*(1-ProbNNK)* and ProbNNK*(1-ProbNNpi) combinations of variables, respectively (for instance, for tracker-only MC where PIDCorr cannot be used).


In some cases, separate templates are created for different categories of datasets. There are currently two classes of such categories:

  • Templates from datasets split by track charge. These are available for most pion, kaon and proton responses and can be accessed by appending "_Plus" or "_Minus" to the dataset name, e.g. "MagDown_2012_Plus".

  • Templates from datasets split by "BremAdded" category for electrons. Can be accessed by appending "_Brem" or "_NoBrem" to the dataset name, e.g. "MagDown_2012_Brem".

Frequently asked questions

I don't have the Eta (pseudorapidity) variable in my simulation ntuple.
You can use PIDGen (PIDCorr) with either Pt and Eta variables (-p <ptvar> -e <etavar>), or Pt and P variables (-p <ptvar> -q <pvar>) taken from the ntuple. In the latter case Eta will be calculated from P and Pt in the script.

The PID response I need is missing in PIDGen
Probably nobody asked for it before, please let us know. But check the Urania nightlies first, it might happen that the response you need was just added recently.

The PID response I need is present in PIDGen but not in PIDCorr
Probably nobody asked for it before, please let us know. But PIDCorr relies on MC samples as well, so you might need to wait until the sample is generated before the PID response you need becomes available.

The result of PIDGen does not match data
  1. Check that there are no "hidden" PID cuts in the simulated or data samples (e.g. in the stripping).
  2. Check that kinematic distributions of the simulated sample match those in data. It might be necessary to e.g. reweigh MC events with the Dalitz plot distributions observed in data.
  3. Usually, one needs to either reweigh MC such that the number of tracks distribution matches that in data (or at least rescale the Ntracks variable by multiplying it by 1.10-1.15 such that it roughly resembles data distribution). If the nTracks is the only variable you need to correct, you can use the command line option in PIDGen/PIDCorr.py scripts such as "--ntrscale 1.15" to do this internally.
  4. If data distribution is obtained with sWeights, make sure there are no biases in the distribution (e.g. correlations between reweighting and PID variables, or backgrounds that peak in the reweighting variable).

The result of PIDCorr does not match data
In addition to the issues mentioned above, there can be a few issues specific to PIDCorr:
  1. The version of simulation (sim08, sim09) used to generate your signal sample should match the version used to create PID response templates. PIDCorr allows one to choose either sim08 or sim09 (but note that not all PID responses are available for both versions).
  2. Typically, MC samples used to create simulation response templates are much smaller than the data calibration samples. Therefore, uncertainty due to the size of calibration samples can be larger with PIDCorr than with PIDGen.

Can I set up PIDGen locally in my university?
It can be done, but will require plenty of space:
  1. Make sure Urania is accessible in your local machine (e.g. via cvmfs)
  2. Copy the contents of /eos/lhcb/wg/PID/PIDGen to your local machine and change the value of eosdir and eosrootdir variables in PIDGenExpert/Run*/Config*.py files to point to this location.
  3. Change the environment variable $PIDPERFSCRIPTSROOT to point to the location of modified PIDCalib scripts before running PIDGen or PIDCorr

What stripping version is used for Run1 electron samples?
e_CombDLLe and e_VxProbNNe variables correspond to Stripping 21, e_S20CombDLLe and e_S20VxProbNNe to Stripping 20.

I use weights to match simulation kinematics to data. Should I pass the weights to PIDGen/PIDCorr?
You don't need to pass your weights to PIDGen/PIDCorr. PIDGen/PIDCorr ensures that for each particular Pt, Eta, Ntracks the PID distribution will match data. Thus if your Pt, Eta, Ntracks distribution matches data, PID response will be correct. How exactly you make Pt, Eta, Ntracks match data is up to you. If you use event-by-event weighs, you will just correct each single event with PIDGen/PIDCorr and then use that event weight later to e.g. compare with your data calibration sample of BDT tuning.

Distribution of ProbNNp for protons in Run2 doesn't look good
Run2 uses Lambda0 calibration sample which is biased for tracks coming from the PV. See this presentation. The solution is to either use the variable p_LbLcPi_MC15TuneV1_ProbNNp_Brunel (coming from the low-stats Lb->LcPi sample) or wait for inclusive Lc sample to be produced.

Nightlies are broken
Sometimes the nightlies in "today's" slot are not compiled properly. In this case, one can try to specify an older slot: $ lb-run -c x86_64-slc6-gcc8-opt --nightly lhcb-head Mon Urania/master bash (note the "Mon" part, which means take the Monday slot).

-- AntonPoluektov - 2017-05-09

Edit | Attach | Watch | Print version | History: r38 < r37 < r36 < r35 < r34 | Backlinks | Raw View | WYSIWYG | More topic actions
Topic revision: r38 - 2019-06-12 - AntonPoluektov
    • Cern Search Icon Cern Search
    • TWiki Search Icon TWiki Search
    • Google Search Icon Google Search

    LHCb 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