3434#include < Framework/runDataProcessing.h>
3535
3636#include < TGeoManager.h>
37+ #include < TMath.h>
38+ #include < TPDGCode.h>
3739
3840#include < HMPIDBase/Param.h>
3941
@@ -73,6 +75,12 @@ struct HmpidTableProducer {
7375 Configurable<bool > requireTPC{" requireTPC" , true , " Require TPC track" };
7476 Configurable<bool > requireTOF{" requireTOF" , true , " Require TOF track" };
7577
78+ Configurable<bool > useInAbsorberGeomMethod{" useInAbsorberGeomMethod" , false , " Use geometrical method to check if daughters are born in absorber" };
79+
80+ // (reference) 473 cm - was the legacy value in run2 simulation
81+ Configurable<float > survivalThresholdRich2{" survivalThresholdRich2" , 437 .5f , " survivalThresholdRich2" };
82+ Configurable<float > survivalThresholdRich4{" survivalThresholdRich4" , 439 .0f , " survivalThresholdRich4" };
83+
7684 using CollisionCandidates = o2::soa::Join<aod::Collisions, aod::EvSels, aod::Mults, aod::CentFV0As>;
7785
7886 using TrackCandidates = soa::Join<aod::Tracks, aod::TracksExtra,
@@ -94,30 +102,9 @@ struct HmpidTableProducer {
94102
95103 static constexpr int Rich2 = 2 , Rich4 = 4 ;
96104
97- // -----------------------------------------------------------------------
98- // HMPID absorber geometry (hardcoded from HMPIDSimulation/Detector.cxx,
99- // Detector::ConstructGeometry / Detector::createAbsorber).
100- // The experiment is finalised and this geometry will not change, so the
101- // values are copied here instead of being re-derived from TGeoManager/CCDB
102- // at runtime. If the detector geometry code is ever revisited, these
103- // constants must be updated accordingly.
104- //
105- // Each absorber is a box (TGeoBBox) whose LOCAL->GLOBAL transform is built as:
106- // pMatrix->SetTranslation(T);
107- // pMatrix->RotateZ(theta);
108- // which yields, for a local point p: p_glob = Rz(theta) * p_loc + T.
109- // In particular the box CENTER in global coordinates is exactly T (the
110- // rotation does not affect T, since it is applied to p_loc only, not to
111- // the already-set translation). Only the box AXES are rotated by theta
112- // with respect to the global x,y axes.
113- //
114- // To test whether a global point lies inside the box we invert the
115- // transform: p_loc = Rz(-theta) * (p_glob - T), then compare component-wise
116- // against the box half-widths.
117- // -----------------------------------------------------------------------
105+ // (reference) HMPID Detector class in O2
118106 static constexpr double AbsThetaDeg = 33.5 ;
119- const double mAbsCosT = std::cos(AbsThetaDeg * TMath::DegToRad ());
120- const double mAbsSinT = std::sin(AbsThetaDeg * TMath::DegToRad ());
107+ double mAbsCosT = 0 ., mAbsSinT = 0 .;
121108
122109 // Rich2 absorber: trans2 = {435.5, 0., -155.}, thickness 40mm -> halfX = 2cm
123110 static constexpr double AbsRich2CenterX = 435.5 , AbsRich2CenterZ = -155 .;
@@ -132,6 +119,9 @@ struct HmpidTableProducer {
132119
133120 void init (o2::framework::InitContext&)
134121 {
122+ mAbsCosT = std::cos (AbsThetaDeg * TMath::DegToRad ());
123+ mAbsSinT = std::sin (AbsThetaDeg * TMath::DegToRad ());
124+
135125 ccdb->setURL (ccdbConfig.ccdbUrl );
136126 ccdb->setCaching (true );
137127 ccdb->setLocalObjectValidityChecking ();
@@ -159,6 +149,10 @@ struct HmpidTableProducer {
159149 kTH1F , {{4 , -0.5 , 3.5 , " " }});
160150
161151 histos.add (" hProdVertex" , " ;X (cm);Y (cm);Z (cm)" , HistType::kTH3F , {{500 , -500 ., 500 .}, {500 , -500 ., 500 .}, {500 , -500 ., 500 .}});
152+ histos.add (" hDaughterRCyl_Rich2" , " hDaughterRCyl_Rich2" , kTH1F , {{600 , 0 ., 600 .}});
153+ histos.add (" hDaughterRCyl_Rich4" , " hDaughterRCyl_Rich4" , kTH1F , {{600 , 0 ., 600 .}});
154+ histos.add (" hDaughterRSph_Rich2" , " hDaughterRSph_Rich2" , kTH1F , {{600 , 0 ., 600 .}});
155+ histos.add (" hDaughterRSph_Rich4" , " hDaughterRSph_Rich4" , kTH1F , {{600 , 0 ., 600 .}});
162156 }
163157
164158 // -----------------------------------------------------------------------
@@ -274,8 +268,9 @@ struct HmpidTableProducer {
274268 (x[1 ] - planePoint[1 ]) * planeNormal[1 ] +
275269 (x[2 ] - planePoint[2 ]) * planeNormal[2 ];
276270
277- if (std::abs (dist) >= std::abs (distPrev))
271+ if (std::abs (dist) >= std::abs (distPrev)) {
278272 return false ;
273+ }
279274
280275 distPrev = dist;
281276 s -= dist;
@@ -318,14 +313,16 @@ struct HmpidTableProducer {
318313 // Intersection track - radiator plane
319314 std::array<double , 3 > xRad{}, pAtRad{};
320315
321- if (!intersectHelixPlane (bz, charge, x, p, pRad, nRad, xRad, pAtRad))
316+ if (!intersectHelixPlane (bz, charge, x, p, pRad, nRad, xRad, pAtRad)) {
322317 continue ;
318+ }
323319
324320 // Intersection track - PC plane
325321 std::array<double , 3 > xPc{}, pAtPc{};
326322
327- if (!intersectHelixPlane (bz, charge, xRad, pAtRad, pPc, nPc, xPc, pAtPc))
323+ if (!intersectHelixPlane (bz, charge, xRad, pAtRad, pPc, nPc, xPc, pAtPc)) {
328324 continue ;
325+ }
329326
330327 double theta = 0 ., phi = 0 .;
331328 param->mars2LorsVec (ch, pAtRad.data (), theta, phi);
@@ -334,8 +331,9 @@ struct HmpidTableProducer {
334331 param->mars2Lors (ch, xPc.data (), xL, yL);
335332
336333 // Use isInside to check Chamber intersected
337- if (param->isInside (xL, yL, param->distCut ()))
334+ if (param->isInside (xL, yL, param->distCut ())) {
338335 return ch;
336+ }
339337 }
340338
341339 // No chamber intersected
@@ -361,17 +359,32 @@ struct HmpidTableProducer {
361359 return false ;
362360 }
363361
364- // subtract the box center (translation is not rotated, see geometry block above)
365- const double rx = vx * mAbsCosT + vy * mAbsSinT ;
366- const double ry = -vx * mAbsSinT + vy * mAbsCosT ;
367- const double rz = vz;
362+ // translate to box center
363+ const double lx = vx - centerX ;
364+ const double ly = vy; // centerY = 0
365+ const double lz = vz - centerZ ;
368366
369367 // rotate by -theta into the box local frame
370- const double lx = rx - centerX;
371- const double ly = ry; // centerY = 0
372- const double lz = rz - centerZ;
368+ const double rx = lx * mAbsCosT + ly * mAbsSinT ;
369+ const double ry = -lx * mAbsSinT + ly * mAbsCosT ;
370+ const double rz = lz;
371+
372+ return std::abs (rx) <= halfX && std::abs (ry) <= AbsHalfY && std::abs (rz) <= AbsHalfZ;
373+ }
374+
375+ bool survivedAbsorber (double vx, double vy, int chamber)
376+ {
377+ float thresholdR = 0 .;
378+ if (chamber == Rich2) {
379+ thresholdR = survivalThresholdRich2;
380+ } else if (chamber == Rich4) {
381+ thresholdR = survivalThresholdRich4;
382+ } else {
383+ return false ;
384+ }
373385
374- return std::abs (lx) <= halfX && std::abs (ly) <= AbsHalfY && std::abs (lz) <= AbsHalfZ;
386+ const float r = std::hypot (vx, vy);
387+ return r > thresholdR;
375388 }
376389
377390 void processEvent (CollisionCandidates::iterator const & col,
@@ -397,26 +410,30 @@ struct HmpidTableProducer {
397410
398411 const auto & globalTrack = t.template track_as <TTrackTable>();
399412
400- if (!globalTrack.has_collision ())
413+ if (!globalTrack.has_collision ()) {
401414 continue ;
415+ }
402416
403417 const auto & col = globalTrack.template collision_as <CollisionCandidates>();
404418 initCCDB (col.template bc_as <aod::BCsWithTimestamps>());
405419 uint32_t collId = col.globalIndex ();
406420
407421 if ((requireITS && !globalTrack.hasITS ()) ||
408422 (requireTPC && !globalTrack.hasTPC ()) ||
409- (requireTOF && !globalTrack.hasTOF ()))
423+ (requireTOF && !globalTrack.hasTOF ())) {
410424 continue ;
425+ }
411426
412- if (mCollisionsWithHmpid .insert (collId).second )
427+ if (mCollisionsWithHmpid .insert (collId).second ) {
413428 histos.fill (HIST (" eventsHmpid" ), 0.5 );
429+ }
414430
415431 // clusSize diagnostics
416432 histos.fill (HIST (" hClusSize" ), t.hmpidClusSize ());
417433 bool isCorrupt = (t.hmpidClusSize () <= 0 );
418- if (isCorrupt)
434+ if (isCorrupt) {
419435 histos.fill (HIST (" hClusSizeCorrupt" ), t.hmpidClusSize ());
436+ }
420437
421438 // --- M2: clusSize encoding ---
422439 int chamberM2 = t.hmpidClusSize () / 1000000 ;
@@ -464,20 +481,22 @@ struct HmpidTableProducer {
464481 // bin 2 = clusSize <= 0, M1 recovery (corrupt, M1 ok)
465482 // bin 3 = clusSize <= 0, M1 fails (corrupt, skipped)
466483
467- if (!isCorrupt && chamberM3 >= 0 )
484+ if (!isCorrupt && chamberM3 >= 0 ) {
468485 histos.fill (HIST (" hChamberAssignment" ), 0 .);
469- else if (!isCorrupt && chamberM3 < 0 )
486+ } else if (!isCorrupt && chamberM3 < 0 ) {
470487 histos.fill (HIST (" hChamberAssignment" ), 1 .);
471- else if (isCorrupt && chamberM3 >= 0 )
488+ } else if (isCorrupt && chamberM3 >= 0 ) {
472489 histos.fill (HIST (" hChamberAssignment" ), 2 .);
473- else
490+ } else {
474491 histos.fill (HIST (" hChamberAssignment" ), 3 .);
492+ }
475493
476494 histos.fill (HIST (" hChamberM3" ), chamberM3);
477495 histos.fill (HIST (" hChamberM3vsM2" ), chamberM2, chamberM3);
478496
479- if (chamberM3 < 0 )
497+ if (chamberM3 < 0 ) {
480498 continue ;
499+ }
481500
482501 std::vector<float > hmpidPhotsCharge2 (o2::aod::kDimPhotonsCharge , 0 .f );
483502
@@ -514,23 +533,78 @@ struct HmpidTableProducer {
514533
515534 if ((chamberM3 == Rich2 || chamberM3 == Rich4) && mc.has_daughters ()) {
516535 auto dIds = mc.daughtersIds ();
517-
518- for (int32_t idx = dIds.front (); idx <= dIds.back (); ++idx) {
519- auto daughter = mcParticles.rawIteratorAt (idx);
520-
521- histos.fill (HIST (" hProdVertex" ), daughter.vx (), daughter.vy (), daughter.vz ());
522-
523- if (isInAbsorber (daughter.vx (), daughter.vy (), daughter.vz (), chamberM3)) {
524- interactionInAbsorber = true ;
525- break ;
526- }
527- } // end loop daughters
536+ bool foundRelevantDaughter = false ; // true if at least one non-delta/photon daughter was examined
537+
538+ if (useInAbsorberGeomMethod) {
539+ for (int32_t idx = dIds.front (); idx <= dIds.back (); ++idx) {
540+ auto daughter = mcParticles.rawIteratorAt (idx);
541+
542+ int absPdg = std::abs (daughter.pdgCode ());
543+ if (absPdg == kElectron || absPdg == kGamma ) {
544+ continue ;
545+ }
546+
547+ foundRelevantDaughter = true ;
548+
549+ // diagnostics on daughters distribution
550+ histos.fill (HIST (" hProdVertex" ), daughter.vx (), daughter.vy (), daughter.vz ());
551+
552+ double rCyl = std::hypot (daughter.vx (), daughter.vy ());
553+ double rSph = std::hypot (daughter.vx (), daughter.vy (), daughter.vz ());
554+ if (chamberM3 == Rich2) {
555+ histos.fill (HIST (" hDaughterRCyl_Rich2" ), rCyl);
556+ histos.fill (HIST (" hDaughterRSph_Rich2" ), rSph);
557+ } else {
558+ histos.fill (HIST (" hDaughterRCyl_Rich4" ), rCyl);
559+ histos.fill (HIST (" hDaughterRSph_Rich4" ), rSph);
560+ }
561+
562+ if (isInAbsorber (daughter.vx (), daughter.vy (), daughter.vz (), chamberM3)) {
563+ interactionInAbsorber = true ;
564+ }
565+ } // end loop daughters
566+ } else {
567+ bool survived = false ;
568+ for (int32_t idx = dIds.front (); idx <= dIds.back (); ++idx) {
569+ auto daughter = mcParticles.rawIteratorAt (idx);
570+
571+ // skip delta rays (e-/e+), photons, and HMPID Cherenkov/feedback
572+ int absPdg = std::abs (daughter.pdgCode ());
573+ if (absPdg == kElectron || absPdg == kGamma ) {
574+ continue ;
575+ }
576+
577+ foundRelevantDaughter = true ;
578+
579+ histos.fill (HIST (" hProdVertex" ), daughter.vx (), daughter.vy (), daughter.vz ());
580+
581+ double rCyl = std::hypot (daughter.vx (), daughter.vy ());
582+ double rSph = std::hypot (daughter.vx (), daughter.vy (), daughter.vz ());
583+ if (chamberM3 == Rich2) {
584+ histos.fill (HIST (" hDaughterRCyl_Rich2" ), rCyl);
585+ histos.fill (HIST (" hDaughterRSph_Rich2" ), rSph);
586+ } else {
587+ histos.fill (HIST (" hDaughterRCyl_Rich4" ), rCyl);
588+ histos.fill (HIST (" hDaughterRSph_Rich4" ), rSph);
589+ }
590+
591+ if (survivedAbsorber (daughter.vx (), daughter.vy (), chamberM3)) {
592+ survived = true ;
593+ }
594+ } // end loop daughters
595+
596+ // No relevant daughter found (only delta rays/photons, or no
597+ // daughters at all): no evidence of a genuine interaction -> treat
598+ // as primary/survived, consistent with the "no daughters" case.
599+ interactionInAbsorber = foundRelevantDaughter ? !survived : false ;
600+ }
528601 } // end if has_daughters
529602
530603 hmpidAnalysisMC (mc.pdgCode (), mc.vx (), mc.vy (), mc.vz (),
531604 mc.isPhysicalPrimary (), mc.getProcess (), interactionInAbsorber);
532605 } else {
533- hmpidAnalysisMC (-1 , 0 .f , 0 .f , 0 .f , false , -100 , false );
606+ // No MC truth associated to this track
607+ hmpidAnalysisMC (-999 , -999 .f , -999 .f , -999 .f , false , -100 , false );
534608 }
535609 } // end if constexpr (isMC)
536610
0 commit comments