@@ -93,6 +93,12 @@ struct OnTheFlyTofPid {
9393 Configurable<bool > considerEventTime{" considerEventTime" , true , " flag to consider event time" };
9494 Configurable<float > innerTOFRadius{" innerTOFRadius" , 20 , " barrel inner TOF radius (cm)" };
9595 Configurable<float > outerTOFRadius{" outerTOFRadius" , 80 , " barrel outer TOF radius (cm)" };
96+ Configurable<float > innerTOFLength{" innerTOFLength" , 124 , " barrel inner TOF length (cm)" };
97+ Configurable<float > outerTOFLength{" outerTOFLength" , 250 , " barrel outer TOF length (cm)" };
98+ Configurable<float > innerTOFPixelArea{" innerTOFPixelArea" , 1.0 , " barrel inner TOF pixel area (mm^2)" };
99+ Configurable<float > innerTOFFractionOfInactiveArea{" innerTOFFractionOfInactiveArea" , 0 .f , " barrel inner TOF fraction of inactive area" };
100+ Configurable<float > outerTOFPixelArea{" outerTOFPixelArea" , 25.0 , " barrel outer TOF pixel area (mm^2)" };
101+ Configurable<float > outerTOFFractionOfInactiveArea{" outerTOFFractionOfInactiveArea" , 0 .f , " barrel outer TOF fraction of inactive area" };
96102 Configurable<float > innerTOFTimeReso{" innerTOFTimeReso" , 20 , " barrel inner TOF time error (ps)" };
97103 Configurable<float > outerTOFTimeReso{" outerTOFTimeReso" , 20 , " barrel outer TOF time error (ps)" };
98104 Configurable<int > nStepsLIntegrator{" nStepsLIntegrator" , 200 , " number of steps in length integrator" };
@@ -183,6 +189,8 @@ struct OnTheFlyTofPid {
183189 histos.add (" h2dEventTimeres" , " h2dEventTimeres" , kTH2F , {axisdNdeta, {300 , 0 , 300 , " resolution" }});
184190 listEfficiency.setObject (new THashList);
185191 listEfficiency->Add (new TEfficiency (" effEventTime" , " effEventTime" , plotsConfig.nBinsMult , 0 .0f , plotsConfig.maxMultRange ));
192+ listEfficiency->Add (new TEfficiency (" MapInnerTOF" , " MapInnerTOF" , 100 , -simConfig.innerTOFLength / 2 , simConfig.innerTOFLength / 2 , 100 , 0 , simConfig.innerTOFRadius * M_PI_2));
193+ listEfficiency->Add (new TEfficiency (" MapOuterTOF" , " MapOuterTOF" , 100 , -simConfig.outerTOFLength / 2 , simConfig.outerTOFLength / 2 , 100 , 0 , simConfig.outerTOFRadius * M_PI_2));
186194
187195 const AxisSpec axisMomentum{static_cast <int >(plotsConfig.nBinsP ), 0 .0f , +10 .0f , " #it{p} (GeV/#it{c})" };
188196 const AxisSpec axisRigidity{static_cast <int >(plotsConfig.nBinsP ), 0 .0f , +10 .0f , " #it{p} / |#it{z}| (GeV/#it{c})" };
@@ -244,11 +252,162 @@ struct OnTheFlyTofPid {
244252 }
245253 }
246254
255+ struct TOFLayerEfficiency {
256+ private:
257+ const float layerRadius;
258+ const float layerLength;
259+ const float pixelArea;
260+ const float fractionInactive;
261+ const float magField;
262+
263+ float pixelSize;
264+ float circumference;
265+ float zLength;
266+ int nPixelsPhi;
267+ int nPixelsZ;
268+ float inactiveBorderPhi;
269+ float inactiveBorderZ;
270+
271+ public:
272+ TOFLayerEfficiency (float r, float l, float pA, float fIA , float m)
273+ : layerRadius(r), layerLength(l), pixelArea(pA), fractionInactive(fIA ), magField(m)
274+ {
275+ // Assuming square pixels for simplicity
276+ pixelSize = std::sqrt (pA);
277+ circumference = 2 .0f * M_PI * layerRadius * 10 .0f ; // convert cm to mm
278+ zLength = layerLength * 10 .0f ; // convert cm to mm
279+ nPixelsPhi = static_cast <int >(circumference / pixelSize);
280+ nPixelsZ = static_cast <int >(zLength / pixelSize);
281+ inactiveBorderPhi = pixelSize * std::sqrt (fractionInactive);
282+ inactiveBorderZ = pixelSize * std::sqrt (fractionInactive);
283+ }
284+
285+ bool isInTOFActiveArea (std::array<float , 3 > hitPosition = {0 .0f , 0 .0f , 0 .0f })
286+ {
287+ if (fractionInactive <= 0 .0f ) {
288+ return true ;
289+ }
290+ if (fractionInactive >= 1 .0f ) {
291+ return false ;
292+ }
293+
294+ // Convert 3D position to cylindrical coordinates for area calculation
295+ const float phi = std::atan2 (hitPosition[1 ], hitPosition[0 ]);
296+ const float z = hitPosition[2 ];
297+ const float r = std::sqrt (hitPosition[0 ] * hitPosition[0 ] + hitPosition[1 ] * hitPosition[1 ]);
298+
299+ // Check if hit is within layer geometric acceptance
300+ if (std::abs (z) > layerLength / 2 .0f || r < layerRadius - 1 .0f || r > layerRadius + 1 .0f ) {
301+ return false ;
302+ }
303+
304+ // Convert hit position to pixel coordinates
305+ const float phiNorm = (phi + M_PI) / (2 .0f * M_PI); // normalize phi to [0,1]
306+ const float zNorm = (z * 10 .0f + zLength / 2 .0f ) / zLength; // normalize z to [0,1]
307+
308+ const int pixelPhi = static_cast <int >(phiNorm * nPixelsPhi) % nPixelsPhi;
309+ const int pixelZ = static_cast <int >(zNorm * nPixelsZ);
310+
311+ // Check if pixel is valid
312+ if (pixelZ < 0 || pixelZ >= nPixelsZ) {
313+ LOG (fatal) << " Pixel Z index out of bounds: " << pixelZ << " (nPixelsZ: " << nPixelsZ << " )" ;
314+ }
315+ if (pixelPhi < 0 || pixelPhi >= nPixelsPhi) {
316+ LOG (fatal) << " Pixel Phi index out of bounds: " << pixelPhi << " (nPixelsPhi: " << nPixelsPhi << " )" ;
317+ }
318+ // Determine if the pixel is active based on the fraction of inactive area
319+ const float phiPosInPixel = std::abs (phiNorm - pixelPhi);
320+ const float zPosInPixel = std::abs (zNorm - pixelZ);
321+ if (phiPosInPixel > inactiveBorderPhi) {
322+ return false ;
323+ }
324+ if (zPosInPixel > inactiveBorderZ) {
325+ return false ;
326+ }
327+ return true ;
328+ }
329+
330+ // / Utility function to estimate hit position on a TOF layer
331+ // / \param track the track parameters
332+ // / \param radius the radius of the TOF layer
333+ // / \return estimated hit position [x, y, z] in cm
334+ std::array<float , 3 > estimateHitPosition (const o2::track::TrackParCov& track)
335+ {
336+ std::array<float , 3 > hitPosition = {0 .0f , 0 .0f , 0 .0f };
337+ // Get the track circle parameters
338+ o2::math_utils::CircleXYf_t trcCircle;
339+ float sna, csa;
340+ track.getCircleParams (magField, trcCircle, sna, csa);
341+
342+ // Calculate intersection points with the cylinder of given radius
343+ const float centerDistance = std::hypot (trcCircle.xC , trcCircle.yC );
344+
345+ if (centerDistance < trcCircle.rC + layerRadius && centerDistance > std::fabs (trcCircle.rC - layerRadius)) {
346+ // Calculate intersection point using the same logic as computeTrackLength
347+ const float ux = trcCircle.xC / centerDistance;
348+ const float uy = trcCircle.yC / centerDistance;
349+ const float vx = -uy;
350+ const float vy = +ux;
351+ const float radical = (centerDistance * centerDistance - trcCircle.rC * trcCircle.rC + layerRadius * layerRadius) / (2 .0f * centerDistance);
352+ const float displace = (0 .5f / centerDistance) * std::sqrt (
353+ (-centerDistance + trcCircle.rC - layerRadius) *
354+ (-centerDistance - trcCircle.rC + layerRadius) *
355+ (-centerDistance + trcCircle.rC + layerRadius) *
356+ (centerDistance + trcCircle.rC + layerRadius));
357+
358+ const float point1[2 ] = {radical * ux + displace * vx, radical * uy + displace * vy};
359+ const float point2[2 ] = {radical * ux - displace * vx, radical * uy - displace * vy};
360+
361+ // Choose the correct intersection point based on momentum direction
362+ std::array<float , 3 > mom;
363+ track.getPxPyPzGlo (mom);
364+ const float scalarProduct1 = point1[0 ] * mom[0 ] + point1[1 ] * mom[1 ];
365+ const float scalarProduct2 = point2[0 ] * mom[0 ] + point2[1 ] * mom[1 ];
366+
367+ if (scalarProduct1 > scalarProduct2) {
368+ hitPosition[0 ] = point1[0 ];
369+ hitPosition[1 ] = point1[1 ];
370+ } else {
371+ hitPosition[0 ] = point2[0 ];
372+ hitPosition[1 ] = point2[1 ];
373+ }
374+
375+ // Estimate Z position using the track slope
376+ float trackLength = computeTrackLength (track, layerRadius, magField);
377+ if (trackLength > 0 ) {
378+ hitPosition[2 ] = track.getZ () + trackLength * track.getTgl ();
379+ }
380+ }
381+ return hitPosition;
382+ }
383+
384+ // / Check if a track hits the active area (convenience function)
385+ // / \param track the track parameters for automatic hit position calculation
386+ // / \return true if the hit is in the active area
387+ bool isTrackInActiveArea (const o2::track::TrackParCov& track)
388+ {
389+ auto hitPosition = estimateHitPosition (track);
390+ return isInTOFActiveArea (hitPosition);
391+ }
392+ };
393+
394+ bool isInInnerTOFActiveArea (const o2::track::TrackParCov& track)
395+ {
396+ static TOFLayerEfficiency innerTOFLayer (simConfig.innerTOFRadius , simConfig.innerTOFLength , simConfig.innerTOFPixelArea , simConfig.innerTOFFractionOfInactiveArea , simConfig.magneticField );
397+ return innerTOFLayer.isTrackInActiveArea (track);
398+ }
399+
400+ bool isInOuterTOFActiveArea (const o2::track::TrackParCov& track)
401+ {
402+ static TOFLayerEfficiency outerTOFLayer (simConfig.outerTOFRadius , simConfig.outerTOFLength , simConfig.outerTOFPixelArea , simConfig.outerTOFFractionOfInactiveArea , simConfig.magneticField );
403+ return outerTOFLayer.isTrackInActiveArea (track);
404+ }
405+
247406 // / function to calculate track length of this track up to a certain radius
248407 // / \param track the input track
249408 // / \param radius the radius of the layer you're calculating the length to
250409 // / \param magneticField the magnetic field to use when propagating
251- float computeTrackLength (o2::track::TrackParCov track, float radius, float magneticField)
410+ static float computeTrackLength (o2::track::TrackParCov track, float radius, float magneticField)
252411 {
253412 // don't make use of the track parametrization
254413 float length = -100 ;
@@ -496,6 +655,21 @@ struct OnTheFlyTofPid {
496655 trackLengthOuterTOF = computeTrackLength (o2track, simConfig.outerTOFRadius , simConfig.magneticField );
497656 }
498657
658+ // Check if the track hit a sensitive area of the TOF
659+ const bool activeInnerTOF = true ; // isInInnerTOFActiveArea(o2track);
660+ float x = 0 .f ;
661+ o2track.getXatLabR (simConfig.innerTOFRadius , x, simConfig.magneticField .value ); // to get phi at inner TOF radius
662+ static_cast <TEfficiency*>(listEfficiency->At (1 ))->Fill (o2track.getZAt (x, simConfig.magneticField .value ), simConfig.innerTOFRadius * o2track.getPhiAt (x, simConfig.magneticField .value ), activeInnerTOF);
663+ if (!activeInnerTOF) {
664+ trackLengthInnerTOF = -999 .f ;
665+ }
666+ const bool activeOuterTOF = isInOuterTOFActiveArea (o2track);
667+ o2track.getXatLabR (simConfig.outerTOFRadius , x, simConfig.magneticField .value ); // to get phi at outer TOF radius
668+ static_cast <TEfficiency*>(listEfficiency->At (2 ))->Fill (o2track.getZAt (x, simConfig.magneticField .value ), simConfig.outerTOFRadius * o2track.getPhiAt (x, simConfig.magneticField .value ), activeOuterTOF);
669+ if (!activeOuterTOF) {
670+ trackLengthOuterTOF = -999 .f ;
671+ }
672+
499673 // get mass to calculate velocity
500674 auto pdgInfo = pdg->GetParticle (mcParticle.pdgCode ());
501675 if (pdgInfo == nullptr ) {
@@ -504,8 +678,8 @@ struct OnTheFlyTofPid {
504678 continue ;
505679 }
506680 const float v = computeParticleVelocity (o2track.getP (), pdgInfo->Mass ());
507- const float expectedTimeInnerTOF = trackLengthInnerTOF / v + eventCollisionTimePS; // arrival time to the Inner TOF in ps
508- const float expectedTimeOuterTOF = trackLengthOuterTOF / v + eventCollisionTimePS; // arrival time to the Outer TOF in ps
681+ const float expectedTimeInnerTOF = trackLengthInnerTOF > 0 ? trackLengthInnerTOF / v + eventCollisionTimePS : - 999 . f ; // arrival time to the Inner TOF in ps
682+ const float expectedTimeOuterTOF = trackLengthOuterTOF > 0 ? trackLengthOuterTOF / v + eventCollisionTimePS : - 999 . f ; // arrival time to the Outer TOF in ps
509683 upgradeTofMC (expectedTimeInnerTOF, trackLengthInnerTOF, expectedTimeOuterTOF, trackLengthOuterTOF);
510684
511685 // Smear with expected resolutions
0 commit comments