All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups
Compute_dEdxProcessor.cc
Go to the documentation of this file.
1 #include <string>
2 #include <iostream>
3 #include <sstream>
4 #include <fstream>
5 #include <utility>
6 
7 #define _USE_MATH_DEFINES
8 #include <cmath>
9 #include <math.h>
10 
11 #include <marlin/Global.h>
12 #include <marlin/Processor.h>
13 #include <marlin/AIDAProcessor.h>
14 
15 #include <lcio.h>
16 #include <EVENT/LCCollection.h>
17 #include <EVENT/Track.h>
18 #include <EVENT/TrackerHit.h>
19 #include <IMPL/TrackImpl.h>
20 
21 #include <DD4hep/Detector.h>
22 #include <DDRec/DetectorData.h>
23 #include <DD4hep/DD4hepUnits.h>
24 
25 #include <UTIL/BitField64.h>
26 #include "UTIL/LCTrackerConf.h"
27 
28 // MarlinUtil
29 #include "LCGeometryTypes.h"
30 #include "SimpleHelix.h"
31 #include "HelixClass.h"
32 
33 #include "TCanvas.h"
34 #include "TImage.h"
35 #include "TStyle.h"
36 
37 #include "Compute_dEdxProcessor.hh"
38 
40 
42  : Processor("Compute_dEdxProcessor") {
43 
44  // Processor description
45  _description = "dE/dx calculation using truncation method" ;
46 
47  registerInputCollection(LCIO::TRACK,
48  "LDCTrackCollection",
49  "LDC track collection name",
51  std::string("MarlinTrkTracks"));
52 
53  registerProcessorParameter( "Write_dEdx",
54  "If set, the calculated dEdx value will be attached to its corresponding track (default: true).",
55  _writedEdx,
56  bool(true));
57 
58  registerProcessorParameter( "EnergyLossErrorTPC",
59  "Fractional error of dEdx in the TPC",
61  float(0.054));
62 
63  registerProcessorParameter( "LowerTruncationFraction",
64  "Lower truncation fraction, default: 0.08 (8%)",
66  float(0.08));
67 
68  registerProcessorParameter( "UpperTruncationFraction",
69  "Upper truncation fraction, default: 0.3 (30%)",
71  float(0.3));
72 
73  registerProcessorParameter( "isSmearing",
74  "Flag for dEdx Smearing",
76  bool(0));
77 
78  registerProcessorParameter( "smearingFactor",
79  "Smearing factor for dEdx smearing",
81  float(0.035));
82 
83  registerProcessorParameter( "NumberofHitsCorrectionParameters",
84  "parameters for number of hits correction",
85  _ncorrpar,
86  float(1.468));
87 
88  std::vector<float> apar = {0.635762, -0.0573237};
89  registerProcessorParameter( "AngularCorrectionParameters",
90  "parameters for angular correction",
91  _acorrpar,
92  apar);
93 
94  std::vector<float> errexp = {-0.34, -0.45};
95  registerProcessorParameter( "dEdxErrorScalingExponents",
96  "scaling exponents of the dE/dx error for path length and number of hits, respectively",
97  _errexp,
98  errexp);
99 
100  registerProcessorParameter( "dxStrategy",
101  "ID of the track length calculation: 1 - hit-to-hit distance; 2 - hit-to-hit path length of projected hits; 3 - path over hit row",
102  _dxStrategy,
103  int(1));
104 
105  registerProcessorParameter( "StrategyCompHist",
106  "If set, an AIDA root file will be generated with Bethe-Bloch histograms (dE/dx over momentum) for each dx strategy (default: false).",
108  bool(false));
109 
110  registerProcessorParameter( "StrategyCompHistWeight",
111  "If set, the Bethe-Bloch histograms will have a sqrt(nHits) weighting (default: false).",
113  bool(false));
114 
115  registerProcessorParameter( "StrategyCompHistFiles",
116  "File names of the generated dx strategy comparison histograms (if chosen). The respective strategy number and '.png' is added.",
118  std::string("dEdx_Histo_Strategy"));
119 
120 
121 }
122 
124  streamlog_out(DEBUG) << " init called " << std::endl ;
125 
126  // it's usually a good idea to
127  printParameters();
128 
129  //get TPC radii, pad height and B field
130  dd4hep::Detector& lcdd = dd4hep::Detector::getInstance();
131  dd4hep::DetElement tpcDE = lcdd.detector("TPC") ;
132  const dd4hep::rec::FixedPadSizeTPCData* tpc = tpcDE.extension<dd4hep::rec::FixedPadSizeTPCData>() ;
133  _TPC_inner = tpc->rMinReadout/dd4hep::mm;
134  _TPC_outer = tpc->rMaxReadout/dd4hep::mm;
135  _TPC_padHeight = tpc->padHeight/dd4hep::mm;
136  double bfieldV[3] ;
137  lcdd.field().magneticField( { 0., 0., 0. } , bfieldV ) ;
138  _bField = bfieldV[2]/dd4hep::tesla ;
139 
140  engine = new std::default_random_engine();
141 
142  streamlog_out(MESSAGE) << "TPC inner radius = " << _TPC_inner << " mm TPC outer radius = " << _TPC_outer << " mm"<< std::endl;
143  streamlog_out(MESSAGE) << "TPC pad height = " << _TPC_padHeight << " mm B field = " << _bField << " T" <<std::endl;
144  streamlog_out(DEBUG) << "dd4hep::mm = " << dd4hep::mm << " dd4hep::cm = " << dd4hep::cm << std::endl;
145 
146  if (_StratCompHist)
147  {
148  // prepare the histogram bins - this could also be implemented as processor parameters
149  const int nBinsX=1000, nBinsY=300;
150  double minBinX=-1, maxBinX=2; // log10(min/max momentum / GeV)
151  double minBinY= 0, maxBinY=1e-6;
152  double histbinsX[nBinsX+1], histbinsY[nBinsY+1];
153  for (int i=0; i<nBinsX+1; i++) histbinsX[i] = pow( 10, minBinX + (maxBinX-minBinX)*i/(float)(nBinsX) );
154  for (int i=0; i<nBinsY+1; i++) histbinsY[i] = minBinY + (maxBinY-minBinY)*i/(float)(nBinsY);
155 
156  // create the histograms
157  AIDAProcessor::tree(this);
158  _BBHist_Strategy1 = new TH2D("BBHist_Strategy1","Bethe-Bloch curve for dx strategy 1: hit-to-hit distance",nBinsX,histbinsX,nBinsY,histbinsY);
159  _BBHist_Strategy2 = new TH2D("BBHist_Strategy2","Bethe-Bloch curve for dx strategy 2: hit-to-hit path length of projected hits",nBinsX,histbinsX,nBinsY,histbinsY);
160  _BBHist_Strategy3 = new TH2D("BBHist_Strategy3","Bethe-Bloch curve for dx strategy 3: path over hit row",nBinsX,histbinsX,nBinsY,histbinsY);
161  }
162 }
163 
165 }
166 
167 void Compute_dEdxProcessor::processEvent( LCEvent * evt ) {
168  _LDCCol = evt->getCollection( _LDCTrackCollection ) ;
169  int nTrkCand = _LDCCol->getNumberOfElements();
170 
171  for (int iTRK=0;iTRK<nTrkCand;++iTRK) {
172 
173  TrackImpl * trkCand = (TrackImpl*) _LDCCol->getElementAt( iTRK );
174  TrackerHitVec trkHits = trkCand->getTrackerHits();
175 
176  //calculate dEdx here when the subtrack is the TPC track!
177  //silicon dE/dx can also be calculated. is it necessary??
178  float dedx=0.0;
179  float unum=0.0;
180 
181  //get tpc hits only
182  TrackerHitVec tpcHitVec;
183  for(unsigned int sdet=0;sdet<trkHits.size(); sdet++){
184  if(trkHits[sdet]->getType() == 0){ //it is very suspicious condition...
185  //check whether this hit is in TPC
186  float x=trkHits[sdet]->getPosition()[0];
187  float y=trkHits[sdet]->getPosition()[1];
188  if(sqrt(x*x+y*y)>=_TPC_inner && sqrt(x*x+y*y)<=_TPC_outer) tpcHitVec.push_back(trkHits[sdet]); // add hit
189  else streamlog_out(DEBUG) << "hit not in TPC: " << x << " " << y << " " << trkHits[sdet]->getPosition()[2] << " " << sqrt(x*x+y*y) << std::endl;
190  }
191  }
192 
193  std::pair<double,double> dummy = CalculateEnergyLoss(tpcHitVec, trkCand);
194  dedx = dummy.first;
195  unum = dummy.second*_energyLossErrorTPC;
196 
197  //fill values
198  if (_writedEdx)
199  {
200  trkCand->setdEdx(dedx);
201  trkCand->setdEdxError(unum);
202  }
203  }
204 }
205 
206 void Compute_dEdxProcessor::check( LCEvent * ) {
207 }
208 
210 
211  if (_StratCompHist)
212  {
213  TCanvas *can = new TCanvas;
214  TImage *img = TImage::Create();
215  gStyle->SetPalette(kBird);
216  can->SetGrid();
217  can->SetLogx();
218  _BBHist_Strategy1->SetXTitle("momentum / GeV");
219  _BBHist_Strategy1->SetYTitle("dE/dx / (GeV/mm)");
220  _BBHist_Strategy2->SetXTitle("momentum / GeV");
221  _BBHist_Strategy2->SetYTitle("dE/dx / (GeV/mm)");
222  _BBHist_Strategy3->SetXTitle("momentum / GeV");
223  _BBHist_Strategy3->SetYTitle("dE/dx / (GeV/mm)");
224 
225  std::string StratCompHistFiles2 = _StratCompHistFiles;
226  _BBHist_Strategy1->Draw("colz");
227  img->FromPad(can);
228  _StratCompHistFiles.append("1.png");
229  img->WriteImage(_StratCompHistFiles.c_str());
230  _BBHist_Strategy2->Draw("colz");
231  img->FromPad(can);
232  _StratCompHistFiles.replace(_StratCompHistFiles.end()-5,_StratCompHistFiles.end()-4,"2");
233  img->WriteImage(_StratCompHistFiles.c_str());
234  _BBHist_Strategy3->Draw("colz");
235  img->FromPad(can);
236  _StratCompHistFiles.replace(_StratCompHistFiles.end()-5,_StratCompHistFiles.end()-4,"3");
237  img->WriteImage(_StratCompHistFiles.c_str());
238 
239  delete can;
240  delete img;
241  }
242 
243  delete engine;
244 }
245 
246 
247 std::pair<double,double> Compute_dEdxProcessor::CalculateEnergyLoss(TrackerHitVec& hitVec, Track* trk){
248  int tH=hitVec.size();
249 
250  // fill the track into a SimpleHelix and also into a HelixClass to make use of their specific geometric utilities
251  const float* refPoint = trk->getReferencePoint();
252  LCVector3D refPointV3D = LCVector3D(refPoint[0],refPoint[1],refPoint[2]); // MarlinUtil::LCVector3D is CLHEP::Hep3Vector
253  SimpleHelix trkHelix = SimpleHelix(trk->getD0(),trk->getPhi(),trk->getOmega(),trk->getZ0(),trk->getTanLambda(),refPointV3D);
254  HelixClass trkHelixC;
255  trkHelixC.Initialize_Canonical(trk->getPhi(),trk->getD0(),trk->getZ0(),trk->getOmega(),trk->getTanLambda(),_bField);
256 
257  // set up some variables
258  std::vector<double> dEdxVec1, dEdxVec2, dEdxVec3; // one vector per dx strategy
259  double dx=0, dy=0, dz=0, length=0, totlength=0;
260  double dxy=0, r=0, theta=0, l=0;
261  double path=0, totpath=0, cpath=0;
262  double over=0, totover=0, cover=0;
263  double hitX =0, hitY =0, hitZ =0;
264  double hit0X=0, hit0Y=0, hit0Z=0;
265 
266  for(int iH=0; iH<tH; iH++){
267  TrackerHit * hit = hitVec[iH];
268  hitX = hit ->getPosition()[0];
269  hitY = hit ->getPosition()[1];
270  hitZ = hit ->getPosition()[2];
271 
272  if (iH>0)
273  {
274  if (_dxStrategy==1 || _dxStrategy==2 || _StratCompHist)
275  {
276  TrackerHit * hit0 = hitVec[iH-1];
277  hit0X = hit0->getPosition()[0];
278  hit0Y = hit0->getPosition()[1];
279  hit0Z = hit0->getPosition()[2];
280 
281  if (_dxStrategy==1 || _StratCompHist)
282  { // strategy 1: dx = distance between 2 hits
283  dx=hitX-hit0X;
284  dy=hitY-hit0Y;
285  dz=hitZ-hit0Z;
286 
287  //cal. length of curvature
288  dxy=sqrt(dx*dx+dy*dy);
289  r=1.0/trk->getOmega();
290  theta=2.0*asin(0.5*dxy/r);
291  l=r*theta;
292  //total helix length
293  length=sqrt(l*l+dz*dz);
294 
295  //sum of flight length
296  totlength += length;
297  dEdxVec1.push_back(hit->getEDep()/length);
298  }
299 
300  if (_dxStrategy==2 || _StratCompHist)
301  { // strategy 2: dx = path difference on the track via SimpleHelix
302  double path1 = trkHelix.getPathAt(LCVector3D(hitX ,hitY ,hitZ ));
303  double path2 = trkHelix.getPathAt(LCVector3D(hit0X,hit0Y,hit0Z));
304  path = fabs(path2-path1);
305  totpath += path;
306  dEdxVec2.push_back(hit->getEDep()/path);
307  cpath++;
308  }
309  }
310  }
311 
312  if (_dxStrategy==3 || _StratCompHist)
313  { // strategy 3: dx = path of track over the hit's row via SimpleHelix and HelixClass
314  // since this strategy does not require a distance between two hits, it can be used for every single hit
315  double hitRadius = sqrt(hitX*hitX + hitY*hitY);
316 
317  // create cylinders of the row's inner and outer edge, check if the track crosses both, then get the path difference of these crossing points
318  double cyl1_radius = hitRadius - _TPC_padHeight*.5; // assuming the radial hit position is always in the center of the row
319  double cyl2_radius = hitRadius + _TPC_padHeight*.5;
320  float trkRefP[3];
321  trkRefP[0] = (trkHelixC.getReferencePoint())[0];
322  trkRefP[1] = (trkHelixC.getReferencePoint())[1];
323  trkRefP[2] = (trkHelixC.getReferencePoint())[2];
324  float cross1[6], cross2[6];
325  float time1 = trkHelixC.getPointOnCircle(cyl1_radius, trkRefP, cross1);
326  float time2 = trkHelixC.getPointOnCircle(cyl2_radius, trkRefP, cross2);
327 
328  bool rejected = false;
329  if (fabs(time1)<1e+10 && fabs(time2)<1e+10) { // getPointOnCircle returns -1e+20 when the helix doesn't cross the cylinder
330  if (sqrt(pow(cross1[0]-cross1[3],2) + pow(cross1[1]-cross1[4],2) + pow(cross1[2]-cross1[5],2)) < 2*_TPC_padHeight) rejected = true; // curler protection
331  if (sqrt(pow(cross2[0]-cross2[3],2) + pow(cross2[1]-cross2[4],2) + pow(cross2[2]-cross2[5],2)) < 2*_TPC_padHeight) rejected = true;
332  }
333  else rejected = true;
334 
335  if (!rejected)
336  {
337  float* cross_1 = cross1; // check which crossing point is the correct one by comparing to the hit position
338  double dist1 = sqrt(pow(cross1[0]-hitX,2) + pow(cross1[1]-hitY,2) + pow(cross1[2]-hitZ,2));
339  double dist2 = sqrt(pow(cross1[3]-hitX,2) + pow(cross1[4]-hitY,2) + pow(cross1[5]-hitZ,2));
340  if (dist2 < dist1) cross_1 = &cross1[3];
341  float* cross_2 = cross2;
342  dist1 = sqrt(pow(cross2[0]-hitX,2) + pow(cross2[1]-hitY,2) + pow(cross2[2]-hitZ,2));
343  dist2 = sqrt(pow(cross2[3]-hitX,2) + pow(cross2[4]-hitY,2) + pow(cross2[5]-hitZ,2));
344  if (dist2 < dist1) cross_2 = &cross2[3];
345 
346  // again correct for curvature
347  dxy = sqrt(pow(cross_1[0]-cross_2[0],2) + pow(cross_1[1]-cross_2[1],2));
348  r = 1./trk->getOmega();
349  l = r*2.*asin(0.5*dxy/r);
350  over= sqrt(l*l + pow(cross_1[2]-cross_2[2],2));
351 
352  totover += over;
353  cover++;
354  dEdxVec3.push_back(hit->getEDep()/over);
355  }
356  }
357 
358  streamlog_out(DEBUG3) << "1: " << length << " 2: " << path << " 3: " << over << " EDep: " << hit->getEDep() << std::endl;
359  if (length/over>1.5 || over/length>1.5) streamlog_out(DEBUG2) << " " << hitX << " " << hitY << " " << hitZ << " " << sqrt(hitX*hitX + hitY*hitY) << std::endl;
360  }
361  streamlog_out(DEBUG4) << "1: " << totlength << " 2: " << totpath << " of " << cpath << " 3: " << totover << " of " << cover << std::endl;
362 
363 
364  int nTruncate=0; // number of remaining track hits after truncation
365  double normdedx=0, totway=0;
366  double trkcos = sqrt(trk->getTanLambda()*trk->getTanLambda()/(1.0+trk->getTanLambda()*trk->getTanLambda()));
367 
368  // if comparison plots should be made, the dE/dx for all three strategies is calculated, filled in histograms,
369  // and the one of the chosen strategy is also transferred to be returned
370  if (_StratCompHist)
371  {
372  double dedx1=0, dedx2=0, dedx3=0;
373  int nTruncate1=0, nTruncate2=0, nTruncate3=0;
374 
375  std::sort(dEdxVec1.begin(),dEdxVec1.end());
376  std::sort(dEdxVec2.begin(),dEdxVec2.end());
377  std::sort(dEdxVec3.begin(),dEdxVec3.end());
378 
379  for (int idE=dEdxVec1.size()*_lowerTrunFrac; idE<dEdxVec1.size()*(1-_upperTrunFrac); idE++) {dedx1 += dEdxVec1[idE]; nTruncate1++;}
380  for (int idE=dEdxVec2.size()*_lowerTrunFrac; idE<dEdxVec2.size()*(1-_upperTrunFrac); idE++) {dedx2 += dEdxVec2[idE]; nTruncate2++;}
381  for (int idE=dEdxVec3.size()*_lowerTrunFrac; idE<dEdxVec3.size()*(1-_upperTrunFrac); idE++) {dedx3 += dEdxVec3[idE]; nTruncate3++;}
382 
383  //cal. truncated mean
384  dedx1 = dedx1/(float)(nTruncate1);
385  dedx2 = dedx2/(float)(nTruncate2);
386  dedx3 = dedx3/(float)(nTruncate3);
387  streamlog_out(DEBUG4) << " check dedx: " << dedx1 << " " << dedx2 << " " << dedx3 << std::endl;
388 
389  double normdedx1 = getNormalization(dedx1, (float)nTruncate1, trkcos);
390  double normdedx2 = getNormalization(dedx2, (float)nTruncate2, trkcos);
391  double normdedx3 = getNormalization(dedx3, (float)nTruncate3, trkcos);
392  if(_isSmearing)
393  {
394  normdedx1 += getSmearing(normdedx1);
395  normdedx2 += getSmearing(normdedx2);
396  normdedx3 += getSmearing(normdedx3);
397  }
398 
399  double lmom = sqrt(pow(trkHelixC.getMomentum()[0],2) + pow(trkHelixC.getMomentum()[1],2) + pow(trkHelixC.getMomentum()[2],2));
400 
401  if (_StratCompHistWeight) {
402  _BBHist_Strategy1->Fill(lmom,normdedx1,sqrt(nTruncate1));
403  _BBHist_Strategy2->Fill(lmom,normdedx2,sqrt(nTruncate2));
404  _BBHist_Strategy3->Fill(lmom,normdedx3,sqrt(nTruncate3));
405  }
406  else {
407  _BBHist_Strategy1->Fill(lmom,normdedx1);
408  _BBHist_Strategy2->Fill(lmom,normdedx2);
409  _BBHist_Strategy3->Fill(lmom,normdedx3);
410  }
411 
412  switch (_dxStrategy)
413  {
414  case 1: normdedx = normdedx1; nTruncate = nTruncate1; totway = totlength; break;
415  case 2: normdedx = normdedx2; nTruncate = nTruncate2; totway = totpath; break;
416  case 3: normdedx = normdedx3; nTruncate = nTruncate3; totway = totover; break;
417  }
418  }
419  else // to if (_StratCompHist)
420  {
421  double dedx=0;
422  std::vector<double> dEdxVec;
423  switch (_dxStrategy)
424  {
425  case 1: dEdxVec = dEdxVec1; totway = totlength; break;
426  case 2: dEdxVec = dEdxVec2; totway = totpath; break;
427  case 3: dEdxVec = dEdxVec3; totway = totover; break;
428  }
429 
430  std::sort(dEdxVec.begin(),dEdxVec.end());
431  for (int idE=dEdxVec.size()*_lowerTrunFrac; idE<dEdxVec.size()*(1-_upperTrunFrac); idE++) {dedx += dEdxVec[idE]; nTruncate++;}
432  dedx = dedx/(float)(nTruncate);
433  normdedx = getNormalization(dedx, (float)nTruncate, trkcos);
434  if(_isSmearing) normdedx += getSmearing(normdedx);
435  }
436 
437 
438  std::pair<double,double> ret;
439  if(std::isnan(normdedx))
440  {
441  ret.first =0;
442  ret.second=0;
443  streamlog_out(DEBUG4) << "NAN!" << std::endl;
444  }
445  else
446  {
447  ret.first =normdedx;
448  ret.second=normdedx*std::pow(0.001*totway, _errexp[0])*std::pow(nTruncate, _errexp[1]); //todo: what is gas pressure in TPC?
449  }
450 
451  return ret;
452 }
453 
454 //correct polar angle dependence and number of hits dependence
455 double Compute_dEdxProcessor::getNormalization(double dedx, float nHit, double trkcos){
456  //cal. hit dep.
457  double f1 = 1 + std::exp(-nHit/_ncorrpar);
458  //cal. polar angle dep.
459  // double c=1.0/sqrt(1.0-trkcos*trkcos);
460  // double f2=1.0/(1.0-0.08887*std::log(c));
461 
462  //cal. polar angle dep. 20160702
463  //double c = std::acos(trkcos);
464  //if(c>3.141592/2.0) c= 3.141592-c;
465  //double f2 = 1.0/std::pow(c, 0.0703);
466 
467  //change polar angle dep. 20170901
468  double f2 = _acorrpar[0] / (_acorrpar[0] + _acorrpar[1] * trkcos * trkcos);
469 
470  return dedx/(f1*f2);
471 }
472 
473 //create smearing factor
475  double z=sqrt( -2.0*std::log(dist(*engine)) ) * std::sin( 2.0*M_PI*dist(*engine) ); // 3.141592
476 
477  //std::cout << dist(*engine) << " " << z << std::endl;
478 
479  return 0.0 + dEdx * _smearingFactor * z;
480 }
static const float mm
virtual void processRunHeader(LCRunHeader *run)
virtual void check(LCEvent *evt)
double getNormalization(double dedx, float hit, double trkcos)
std::pair< double, double > CalculateEnergyLoss(TrackerHitVec &hitVec, Track *trk)
std::vector< float > _acorrpar
std::uniform_real_distribution dist
std::vector< float > _errexp
std::default_random_engine * engine
static const float e
static const float cm
Compute_dEdxProcessor aCompute_dEdxProcessor
Compute dE/dx Processor This processor calculates the dE/dx for every track.
double getSmearing(double dEdx)
virtual void processEvent(LCEvent *evt)