@@ -234,7 +234,7 @@ void MatLayerCylSet::finalizeStructures()
234234 o2::gpu::FlatObject::resizeArray (get ()->mR2Intervals , 0 , nR2Int);
235235 o2::gpu::FlatObject::resizeArray (get ()->mInterval2LrID , 0 , nR2Int);
236236 get ()->mR2Intervals [0 ] = get ()->mRMin2 ;
237- get ()->mR2Intervals [1 ] = get ()-> mRMax2 ;
237+ get ()->mR2Intervals [1 ] = getLayer ( 0 ). getRMax2 () ;
238238 get ()->mInterval2LrID [0 ] = 0 ;
239239 auto & nRIntervals = get ()->mNRIntervals ;
240240 nRIntervals = 1 ;
@@ -319,14 +319,17 @@ void MatLayerCylSet::initLayerVoxelLU()
319319 if (LayerRMax < get ()->mRMax ) {
320320 LOG (fatal) << " Cannot initialized layer voxel lookup due to dimension problem (fix constants in MatLayerCylSet.h)" ;
321321 }
322+ // the top bit of an entry carries the ambiguity flag, so the interval index has one bit less
323+ if (get ()->mNRIntervals > VoxelSegmentMask) {
324+ LOG (fatal) << " Too many R intervals (" << get ()->mNRIntervals << " ) to pack into a layer voxel lookup entry" ;
325+ }
322326 for (int voxel = 0 ; voxel < NumVoxels; ++voxel) {
323327 // check the 2 extremes of this voxel "covering"
324328 const auto lowerR = voxel * VoxelRDelta;
325329 const auto upperR = lowerR + VoxelRDelta;
326330 const auto lowerSegment = searchSegment (lowerR * lowerR);
327331 const auto upperSegment = searchSegment (upperR * upperR);
328- mLayerVoxelLU [2 * voxel] = lowerSegment;
329- mLayerVoxelLU [2 * voxel + 1 ] = upperSegment;
332+ mLayerVoxelLU [voxel] = uint16_t (lowerSegment) | (lowerSegment != upperSegment ? VoxelAmbiguousBit : uint16_t {0 });
330333 }
331334 mInitializedLayerVoxelLU = true ;
332335}
@@ -477,7 +480,10 @@ GPUd() MatBudget MatLayerCylSet::getMatBudget(float x0, float y0, float z0, floa
477480 tEndPhi = cross2;
478481 checkMorePhi = false ;
479482 } else { // last phi slice still not reached
480- tEndPhi = ray.crossRadial (lr, (stepPhiID > 0 ? phiID + 1 : phiID) % nphiSlices);
483+ const int boundaryPhiID = stepPhiID > 0 ? phiID + 1 : phiID;
484+ // phiID may be offset by one revolution to handle wrapping, but never by more.
485+ const int wrappedBoundaryPhiID = boundaryPhiID < nphiSlices ? boundaryPhiID : boundaryPhiID - nphiSlices;
486+ tEndPhi = ray.crossRadial (lr, wrappedBoundaryPhiID);
481487 if (tEndPhi == Ray::InvalidT) {
482488 break ; // ray parallel to radial line, abandon check for phi bin change
483489 }
@@ -490,6 +496,8 @@ GPUd() MatBudget MatLayerCylSet::getMatBudget(float x0, float y0, float z0, floa
490496 }
491497 auto zID = lr.getZBinID (ray.getZ (tStartPhi));
492498 auto zIDLast = lr.getZBinID (ray.getZ (tEndPhi));
499+ const int wrappedPhiID = phiID < nphiSlices ? phiID : phiID - nphiSlices;
500+ const auto * cellRow = lr.getCellRow (wrappedPhiID);
493501 // check if Zbins are crossed
494502
495503#ifdef _DBG_LOC_
@@ -512,7 +520,7 @@ GPUd() MatBudget MatLayerCylSet::getMatBudget(float x0, float y0, float z0, floa
512520 }
513521 // account materials of this step
514522 float step = tEndZ > tStartZ ? tEndZ - tStartZ : tStartZ - tEndZ; // the real step is ray.getDist(tEnd-tStart), will rescale all later
515- const auto & cell = lr. getCell (phiID % nphiSlices, zID) ;
523+ const auto & cell = cellRow[ zID] ;
516524 rval.meanRho += cell.meanRho * step;
517525 rval.meanX2X0 += cell.meanX2X0 * step;
518526 rval.length += step;
@@ -523,7 +531,7 @@ GPUd() MatBudget MatLayerCylSet::getMatBudget(float x0, float y0, float z0, floa
523531 printf (
524532 " Lr#%3d / cross#%d : account %f<t<%f at phiSlice %d | Zbin: %3d (%3d) |[%+e %+e +%e]:[%+e %+e %+e] "
525533 " Step: %.3e StrpCor: %.3e\n " ,
526- lrID, ic, tEndZ, tStartZ, phiID % nphiSlices , zID, zIDLast,
534+ lrID, ic, tEndZ, tStartZ, wrappedPhiID , zID, zIDLast,
527535 pos0[0 ], pos0[1 ], pos0[2 ], pos1[0 ], pos1[1 ], pos1[2 ], step, ray.getDist (step));
528536#endif
529537
@@ -532,7 +540,7 @@ GPUd() MatBudget MatLayerCylSet::getMatBudget(float x0, float y0, float z0, floa
532540 } while (checkMoreZ);
533541 } else {
534542 float step = tEndPhi > tStartPhi ? tEndPhi - tStartPhi : tStartPhi - tEndPhi; // the real step is |ray.getDist(tEnd-tStart)|, will rescale all later
535- const auto & cell = lr. getCell (phiID % nphiSlices, zID) ;
543+ const auto & cell = cellRow[ zID] ;
536544 rval.meanRho += cell.meanRho * step;
537545 rval.meanX2X0 += cell.meanX2X0 * step;
538546 rval.length += step;
@@ -543,7 +551,7 @@ GPUd() MatBudget MatLayerCylSet::getMatBudget(float x0, float y0, float z0, floa
543551 printf (
544552 " Lr#%3d / cross#%d : account %f<t<%f at phiSlice %d | Zbin: %3d ----- |[%+e %+e +%e]:[%+e %+e %+e]"
545553 " Step: %.3e StrpCor: %.3e\n " ,
546- lrID, ic, tEndPhi, tStartPhi, phiID % nphiSlices , zID,
554+ lrID, ic, tEndPhi, tStartPhi, wrappedPhiID , zID,
547555 pos0[0 ], pos0[1 ], pos0[2 ], pos1[0 ], pos1[1 ], pos1[2 ], step, ray.getDist (step));
548556#endif
549557 }
@@ -581,12 +589,25 @@ GPUd() bool MatLayerCylSet::getLayersRange(const Ray& ray, short& lmin, short& l
581589 return false ;
582590 }
583591 int lmxInt, lmnInt;
584- if (!mInitializedLayerVoxelLU ) {
592+ #ifdef GPUCA_GPUCODE
593+ // On the device the voxel table is always present
594+ constexpr bool haveVoxelLU = true ;
595+ #else
596+ const bool haveVoxelLU = mInitializedLayerVoxelLU ;
597+ #endif
598+ if (!haveVoxelLU) {
585599 lmxInt = rmax2 < getRMax2 () ? searchSegment (rmax2, 0 ) : get ()->mNRIntervals - 2 ;
586600 lmnInt = rmin2 >= getRMin2 () ? searchSegment (rmin2, 0 , lmxInt + 1 ) : 0 ;
587601 } else {
588- lmxInt = rmax2 < getRMax2 () ? searchLayerFast (rmax2, 0 ) : get ()->mNRIntervals - 2 ;
589- lmnInt = rmin2 >= getRMin2 () ? searchLayerFast (rmin2, 0 , lmxInt + 1 ) : 0 ;
602+ // The two lookups are independent so overlapping the pair is worth the clumsier shape.
603+ const bool useMax = rmax2 < getRMax2 ();
604+ const bool useMin = rmin2 >= getRMin2 ();
605+ const int ixMax = useMax ? voxelIndex (rmax2) : 0 ;
606+ const int ixMin = useMin ? voxelIndex (rmin2) : 0 ;
607+ const uint16_t eMax = mLayerVoxelLU [ixMax];
608+ const uint16_t eMin = mLayerVoxelLU [ixMin];
609+ lmxInt = useMax ? resolveLayerRange (rmax2, ixMax, eMax) : get ()->mNRIntervals - 2 ;
610+ lmnInt = useMin ? resolveLayerRange (rmin2, ixMin, eMin) : 0 ;
590611 }
591612
592613 const auto * interval2LrID = get ()->mInterval2LrID ;
@@ -605,11 +626,19 @@ GPUd() bool MatLayerCylSet::getLayersRange(const Ray& ray, short& lmin, short& l
605626GPUd () int MatLayerCylSet::searchLayerFast(float r2, int low, int high) const
606627{
607628 // we can avoid the sqrt .. at the cost of more memory in the lookup
608- const auto index = 2 * int (o2::gpu::CAMath::Sqrt (r2) * InvVoxelRDelta);
609- const auto layersfirst = mLayerVoxelLU [index];
610- const auto layerslast = mLayerVoxelLU [index + 1 ];
611- if (layersfirst != layerslast) {
612- // this means the voxel is undecided and we revert to search
629+ const auto index = voxelIndex (r2);
630+ return resolveLayerRange (r2, index, mLayerVoxelLU [index]);
631+ }
632+
633+ GPUd () int MatLayerCylSet::resolveLayerRange(float r2, int voxel, uint16_t entry) const
634+ {
635+ const int layersfirst = entry & VoxelSegmentMask;
636+ if (entry & VoxelAmbiguousBit) {
637+ // Recreate the upper candidate only for the small fraction of undecided voxels.
638+ // Keep the operations identical to initLayerVoxelLU so boundary behaviour is unchanged.
639+ const auto lowerR = voxel * VoxelRDelta;
640+ const auto upperR = lowerR + VoxelRDelta;
641+ const auto layerslast = searchSegment (upperR * upperR);
613642 return searchSegment (r2, layersfirst, layerslast + 1 );
614643 }
615644 return layersfirst;
0 commit comments