Skip to content

Commit 5dec98a

Browse files
committed
TOF: Add dead chip area to TOF sim
1 parent cde8c99 commit 5dec98a

File tree

1 file changed

+177
-3
lines changed

1 file changed

+177
-3
lines changed

ALICE3/TableProducer/OTF/onTheFlyTofPid.cxx

Lines changed: 177 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)