1515
1616#include < Framework/RuntimeError.h>
1717
18+ #include < TRandom.h>
19+
1820namespace o2 ::delphes
1921{
2022int TrackSmearer::getIndexPDG (int pdg) const
@@ -69,6 +71,15 @@ const char* TrackSmearer::getParticleName(int pdg) const
6971 }
7072}
7173
74+ void TrackSmearer::setWhatEfficiency (int val)
75+ {
76+ // FIXME: this really should be an enum
77+ if (val > 2 ) {
78+ throw framework::runtime_error_f (" getLUTEntry: unknown efficiency type %d" , mWhatEfficiency );
79+ }
80+ mWhatEfficiency = val;
81+ }
82+
7283bool TrackSmearer::loadTable (int pdg, const char * filename, bool forceReload)
7384{
7485 if (!filename || filename[0 ] == ' \0 ' ) {
@@ -102,10 +113,9 @@ bool TrackSmearer::loadTable(int pdg, const char* filename, bool forceReload)
102113 LOGF (error, " %s" , framework::error_from_ref (ref).what );
103114 return false ;
104115 }
105- mHeaders [ipdg] = &mLUTData [ipdg].getHeaderRef ();
106116
107117 LOGF (info, " Successfully read LUT for PDG %d: %s" , pdg, localFilename.c_str ());
108- mHeaders [ipdg]-> print ();
118+ mLUTData [ipdg]. getHeaderRef (). print ();
109119 return true ;
110120}
111121
@@ -126,10 +136,9 @@ bool TrackSmearer::adoptTable(int pdg, const uint8_t* buffer, size_t size, bool
126136 } catch (framework::RuntimeErrorRef ref) {
127137 LOGF (error, " %s" , framework::error_from_ref (ref).what );
128138 }
129- mHeaders [ipdg] = &mLUTData [ipdg].getHeaderRef ();
130139
131140 LOGF (info, " Successfully adopted LUT for PDG %d" , pdg);
132- mHeaders [ipdg]-> print ();
141+ mLUTData [ipdg]. getHeaderRef (). print ();
133142 return true ;
134143}
135144
@@ -150,10 +159,9 @@ bool TrackSmearer::viewTable(int pdg, const uint8_t* buffer, size_t size, bool f
150159 } catch (framework::RuntimeErrorRef ref) {
151160 LOGF (error, " %s" , framework::error_from_ref (ref).what );
152161 }
153- mHeaders [ipdg] = &mLUTData [ipdg].getHeaderRef ();
154162
155163 LOGF (info, " Successfully adopted LUT for PDG %d" , pdg);
156- mHeaders [ipdg]-> print ();
164+ mLUTData [ipdg]. getHeaderRef (). print ();
157165 return true ;
158166}
159167
@@ -181,5 +189,221 @@ bool TrackSmearer::checkSpecialCase(int pdg, lutHeader_t const& header)
181189 return specialPdgCase;
182190}
183191
192+ const lutHeader_t* TrackSmearer::getLUTHeader (int pdg) const
193+ {
194+ const int ipdg = getIndexPDG (pdg);
195+ if (!mLUTData [ipdg].isLoaded ()) {
196+ return nullptr ;
197+ }
198+ return &mLUTData [ipdg].getHeaderRef ();
199+ }
200+
201+ const lutEntry_t* TrackSmearer::getLUTEntry (const int pdg, const float nch, const float radius, const float eta, const float pt, float & interpolatedEff) const
202+ {
203+ const int ipdg = getIndexPDG (pdg);
204+ if (!mLUTData [ipdg].isLoaded ()) {
205+ return nullptr ;
206+ }
207+
208+ const auto & header = mLUTData [ipdg].getHeaderRef ();
209+
210+ auto inch = header.nchmap .find (nch);
211+ auto irad = header.radmap .find (radius);
212+ auto ieta = header.etamap .find (eta);
213+ auto ipt = header.ptmap .find (pt);
214+
215+ // Interpolate efficiency if requested
216+ auto fraction = header.nchmap .fracPositionWithinBin (nch);
217+ if (mInterpolateEfficiency ) {
218+ static constexpr float kFractionThreshold = 0 .5f ;
219+ if (fraction > kFractionThreshold ) {
220+ switch (mWhatEfficiency ) {
221+ case 1 : {
222+ const auto * entry_curr = mLUTData [ipdg].getEntryRef (inch, irad, ieta, ipt);
223+ if (inch < header.nchmap .nbins - 1 ) {
224+ const auto * entry_next = mLUTData [ipdg].getEntryRef (inch + 1 , irad, ieta, ipt);
225+ interpolatedEff = (1 .5f - fraction) * entry_curr->eff + (-0 .5f + fraction) * entry_next->eff ;
226+ } else {
227+ interpolatedEff = entry_curr->eff ;
228+ }
229+ break ;
230+ }
231+ case 2 : {
232+ const auto * entry_curr = mLUTData [ipdg].getEntryRef (inch, irad, ieta, ipt);
233+ if (inch < header.nchmap .nbins - 1 ) {
234+ const auto * entry_next = mLUTData [ipdg].getEntryRef (inch + 1 , irad, ieta, ipt);
235+ interpolatedEff = (1 .5f - fraction) * entry_curr->eff2 + (-0 .5f + fraction) * entry_next->eff2 ;
236+ } else {
237+ interpolatedEff = entry_curr->eff2 ;
238+ }
239+ break ;
240+ }
241+ }
242+ } else {
243+ float comparisonValue = header.nchmap .log ? std::log10 (nch) : nch;
244+ switch (mWhatEfficiency ) {
245+ case 1 : {
246+ const auto * entry_curr = mLUTData [ipdg].getEntryRef (inch, irad, ieta, ipt);
247+ if (inch > 0 && comparisonValue < header.nchmap .max ) {
248+ const auto * entry_prev = mLUTData [ipdg].getEntryRef (inch - 1 , irad, ieta, ipt);
249+ interpolatedEff = (0 .5f + fraction) * entry_curr->eff + (0 .5f - fraction) * entry_prev->eff ;
250+ } else {
251+ interpolatedEff = entry_curr->eff ;
252+ }
253+ break ;
254+ }
255+ case 2 : {
256+ const auto * entry_curr = mLUTData [ipdg].getEntryRef (inch, irad, ieta, ipt);
257+ if (inch > 0 && comparisonValue < header.nchmap .max ) {
258+ const auto * entry_prev = mLUTData [ipdg].getEntryRef (inch - 1 , irad, ieta, ipt);
259+ interpolatedEff = (0 .5f + fraction) * entry_curr->eff2 + (0 .5f - fraction) * entry_prev->eff2 ;
260+ } else {
261+ interpolatedEff = entry_curr->eff2 ;
262+ }
263+ break ;
264+ }
265+ }
266+ }
267+ } else {
268+ const auto * entry = mLUTData [ipdg].getEntryRef (inch, irad, ieta, ipt);
269+ if (entry) {
270+ switch (mWhatEfficiency ) {
271+ case 1 :
272+ interpolatedEff = entry->eff ;
273+ break ;
274+ case 2 :
275+ interpolatedEff = entry->eff2 ;
276+ break ;
277+ }
278+ }
279+ }
280+
281+ return mLUTData [ipdg].getEntryRef (inch, irad, ieta, ipt);
282+ }
283+
284+ bool TrackSmearer::smearTrack (O2Track& o2track, const lutEntry_t* lutEntry, float interpolatedEff)
285+ {
286+ bool isReconstructed = true ;
287+
288+ // Generate efficiency
289+ if (mUseEfficiency ) {
290+ auto eff = 0 .f ;
291+ switch (mWhatEfficiency ) {
292+ case 1 :
293+ eff = lutEntry->eff ;
294+ break ;
295+ case 2 :
296+ eff = lutEntry->eff2 ;
297+ break ;
298+ }
299+ if (mInterpolateEfficiency ) {
300+ eff = interpolatedEff;
301+ }
302+ if (gRandom ->Uniform () > eff) {// FIXME: use a fixed RNG instead of whatever ROOT has as a default
303+ isReconstructed = false ;
304+ }
305+ }
306+
307+ // Return false already now in case not reco'ed
308+ if (!isReconstructed && mSkipUnreconstructed ) {
309+ return false ;
310+ }
311+
312+ // Transform params vector and smear
313+ static constexpr int kParSize = 5 ;
314+ double params[kParSize ];
315+ for (int i = 0 ; i < kParSize ; ++i) {
316+ double val = 0 .;
317+ for (int j = 0 ; j < kParSize ; ++j) {
318+ val += lutEntry->eigvec [j][i] * o2track.getParam (j);
319+ }
320+ params[i] = gRandom ->Gaus (val, std::sqrt (lutEntry->eigval [i]));
321+ }
322+
323+ // Transform back params vector
324+ for (int i = 0 ; i < kParSize ; ++i) {
325+ double val = 0 .;
326+ for (int j = 0 ; j < kParSize ; ++j) {
327+ val += lutEntry->eiginv [j][i] * params[j];
328+ }
329+ o2track.setParam (val, i);
330+ }
331+
332+ // Sanity check that par[2] sin(phi) is in [-1, 1]
333+ if (std::fabs (o2track.getParam (2 )) > 1 .) {
334+ LOGF (warn, " smearTrack failed sin(phi) sanity check: %f" , o2track.getParam (2 ));
335+ }
336+
337+ // Set covariance matrix
338+ static constexpr int kCovMatSize = 15 ;
339+ for (int i = 0 ; i < kCovMatSize ; ++i) {
340+ o2track.setCov (lutEntry->covm [i], i);
341+ }
342+
343+ return isReconstructed;
344+ }
345+
346+ bool TrackSmearer::smearTrack (O2Track& o2track, int pdg, float nch)
347+ {
348+ auto pt = o2track.getPt ();
349+ switch (pdg) {
350+ case o2::constants::physics::kHelium3 :
351+ case -o2::constants::physics::kHelium3 :
352+ pt *= 2 .f ;
353+ break ;
354+ }
355+
356+ auto eta = o2track.getEta ();
357+ float interpolatedEff = 0 .0f ;
358+ const lutEntry_t* lutEntry = getLUTEntry (pdg, nch, 0 .f , eta, pt, interpolatedEff);
359+
360+ if (!lutEntry || !lutEntry->valid ) {
361+ return false ;
362+ }
363+
364+ return smearTrack (o2track, lutEntry, interpolatedEff);
365+ }
366+
367+ double TrackSmearer::getPtRes (const int pdg, const float nch, const float eta, const float pt) const
368+ {
369+ float dummy = 0 .0f ;
370+ const lutEntry_t* lutEntry = getLUTEntry (pdg, nch, 0 .f , eta, pt, dummy);
371+ auto val = std::sqrt (lutEntry->covm [14 ]) * lutEntry->pt ;
372+ return val;
373+ }
374+
375+ double TrackSmearer::getEtaRes (const int pdg, const float nch, const float eta, const float pt) const
376+ {
377+ float dummy = 0 .0f ;
378+ const lutEntry_t* lutEntry = getLUTEntry (pdg, nch, 0 .f , eta, pt, dummy);
379+ auto sigmatgl = std::sqrt (lutEntry->covm [9 ]); // sigmatgl2
380+ auto etaRes = std::fabs (std::sin (2.0 * std::atan (std::exp (-eta)))) * sigmatgl; // propagate tgl to eta uncertainty
381+ etaRes /= lutEntry->eta ; // relative uncertainty
382+ return etaRes;
383+ }
384+
385+ double TrackSmearer::getAbsPtRes (const int pdg, const float nch, const float eta, const float pt) const
386+ {
387+ float dummy = 0 .0f ;
388+ const lutEntry_t* lutEntry = getLUTEntry (pdg, nch, 0 .f , eta, pt, dummy);
389+ auto val = std::sqrt (lutEntry->covm [14 ]) * lutEntry->pt * lutEntry->pt ;
390+ return val;
391+ }
392+
393+ double TrackSmearer::getAbsEtaRes (const int pdg, const float nch, const float eta, const float pt) const
394+ {
395+ float dummy = 0 .0f ;
396+ const lutEntry_t* lutEntry = getLUTEntry (pdg, nch, 0 .f , eta, pt, dummy);
397+ auto sigmatgl = std::sqrt (lutEntry->covm [9 ]); // sigmatgl2
398+ auto etaRes = std::fabs (std::sin (2.0 * std::atan (std::exp (-eta)))) * sigmatgl; // propagate tgl to eta uncertainty
399+ return etaRes;
400+ }
401+
402+ double TrackSmearer::getEfficiency (const int pdg, const float nch, const float eta, const float pt) const
403+ {
404+ float efficiency = 0 .0f ;
405+ (void )getLUTEntry (pdg, nch, 0 .f , eta, pt, efficiency);
406+ return efficiency;
407+ }
184408
185409} // namespace o2::delphes
0 commit comments