#ifndef ClassQQQ_h #define ClassQQQ_h #include #include #include #include #include "TGeoManager.h" #include "TGeoVolume.h" #include "TGeoBBox.h" class QQQ{ public: QQQ(){Clear();}; ~QQQ(){} short GetID() const {return id;} short GetChUp() const {return chUp;} short GetChDn() const {return chDn;} short GetChBk() const {return chBk;} TVector3 GetHitPos() const {return hitPos;} TVector3 GetHitPosWithSigma(double sigmaY_mm, double sigmaZ_mm); double GetZFrac() const {return zFrac;} // range from -0.5 to 0.5 void Clear(); void ConstructGeo(); void FindQQQPos(TVector3 pos, TVector3 direction, bool verbose = false); void CalQQQPos(unsigned short ID, unsigned short chUp, unsigned short chDown, unsigned short chBack, float eUp, float eDown); double GetNumDet() const {return numDet;} void Print(){ if( id == -1 ){ printf("Did not hit any QQQ.\n"); }else{ printf("ID: %d, U,D,B: %d %d %d| zFrac : %.2f\n", id, chUp, chDn, chBk, zFrac); printf("Hit Pos: %.2f, %.2f, %.2f\n", hitPos.X(), hitPos.Y(), hitPos.Z()); } } // void CalZFrac(){ // zFrac = (eUp - eDn)/(eUp + eDn); // } private: const int numDet = 4; const float qqqR1 = 50; const float qqqR2 = 100; const float qqqZPos = 23 + 75 + 30; short id; // -1 when no hit short chUp; short chDn; short chBk; double zFrac; // from +1 (downstream) to -1 (upstream) double eUp; double eDn; double eBk; TVector3 hitPos; TGeoManager *geom; TGeoVolume *worldBox; TGeoMedium *Al; // helper function to calculate intersection between line segments, return pair of (fraction along line1, fraction along line2) where the intersection occurs. If no intersection, return (0, -1). std::pair Intersect(TVector3 p1, TVector3 p2, TVector3 q1, TVector3 q2, bool verbose){ //see https://nukephysik101.wordpress.com/2023/12/30/intersect-between-2-line-segments/ //zero all z-component TVector3 a0 = p1; a0.SetZ(0); TVector3 a1 = p2; a1.SetZ(0); TVector3 b0 = q1; b0.SetZ(0); TVector3 b1 = q2; b1.SetZ(0); double h = 0, k = 0; // placeholder values, implementation of intersection logic if( verbose ) printf(" ----h, k : %f, %f\n", h, k); return std::pair(h,k); } }; inline void QQQ::Clear(){ id = -1; chUp = -1; chDn = -1; chBk = -1; zFrac = TMath::QuietNaN(); eUp = TMath::QuietNaN(); eDn = TMath::QuietNaN(); eBk = TMath::QuietNaN(); } inline void QQQ::ConstructGeo(){ TGeoVolume *qqq = geom->MakeTubs("qqq", Al, qqqR1, qqqR2, 0.5, 5, 85); qqq->SetLineColor(7); for( int i = 0; i < 4; i++){ worldBox->AddNode(qqq, i+1, new TGeoCombiTrans( 0, 0, qqqZPos, new TGeoRotation("rot1", 360/4 * (i), 0., 0.))); } } inline void QQQ::FindQQQPos(TVector3 pos, TVector3 direction, bool verbose){ id = -1; chUp = -1; chDn = -1; chBk = -1; //-------------------------------------------- // Intersect trajectory with QQQ plane //-------------------------------------------- if( TMath::Abs(direction.Z()) < 1e-10 ) return; double t = (qqqZPos - pos.Z()) / direction.Z(); if( t <= 0 ) return; hitPos = pos + t * direction; //-------------------------------------------- // Cylindrical coordinates //-------------------------------------------- double x = hitPos.X(); double y = hitPos.Y(); double r = TMath::Sqrt(x*x + y*y); if( r < qqqR1 || r > qqqR2 ) return; double phi = hitPos.Phi() * TMath::RadToDeg(); if( phi < 0 ) phi += 360.0; //-------------------------------------------- // Determine detector ID //-------------------------------------------- id = -1; for(int det = 0; det < 4; det++){ double phiMin = det*90.0 + 5.0; double phiMax = phiMin + 85.0; if( phi >= phiMin && phi <= phiMax ){ id = det; break; } } if( id < 0 ) return; //-------------------------------------------- // Ring number (32 strips) //-------------------------------------------- const double ringWidth = (qqqR2 - qqqR1)/32.0; int ring = (int)((r - qqqR1)/ringWidth); if( ring < 0 ) ring = 0; if( ring > 31 ) ring = 31; //-------------------------------------------- // Sector number (4 strips) //-------------------------------------------- double localPhi = phi - (id*90.0 + 5.0); int sector = (int)(localPhi/(85.0/4.0)); if( sector < 0 ) sector = 0; if( sector > 3 ) sector = 3; chBk = ring; chDn = sector; chUp = sector; zFrac = 0.0; if(verbose){ printf("\nQQQ Hit\n"); printf(" ID = %d\n", id); printf(" Ring = %d\n", ring); printf(" Sector = %d\n", sector); printf(" r = %.2f mm\n", r); printf(" phi = %.2f deg\n", phi); hitPos.Print(); } } /*s inline TVector3 QQQ::GetHitPosWithSigma(double sigmaY_mm, double sigmaZ_mm){ double phi = SNorml[id%numDet].Phi(); TVector3 haha = hitPos; haha.RotateZ(-phi); double y = haha.Y() + gRandom->Gaus(0, sigmaY_mm); if( sigmaY_mm < 0 ){ double deltaW = width/4; y = TMath::Floor((haha.Y()-deltaW)/deltaW)*deltaW + deltaW*1.5; // when ever land on each strip, set the position to be center of the strip. if( y >= 25 ) y = 15; } double z = haha.Z() + gRandom->Gaus(0, sigmaZ_mm); if( sigmaZ_mm < 0 ){ haha.Z(); double delta = length/4; int sign = z > 0 ? 1 : -1; z = TMath::Floor( (abs(z)-gap/2)/delta )*delta + 0.5 * delta + gap/2; if( z >= 107.375 ) z = 88.625; z = sign * z; } haha.SetY(y); haha.SetZ(z); haha.RotateZ(phi); return haha; }*/ inline void QQQ::CalQQQPos(unsigned short ID, unsigned short chUp, unsigned short chDown, unsigned short chBack, float eUp, float eDown){ hitPos.Clear(); if( ID > 3 ) return; if( chBack > 31 ) return; if( chDown > 3 ) return; const double ringWidth = (qqqR2 - qqqR1)/32.0; double r = qqqR1 + (chBack + 0.5)*ringWidth; const double sectorWidth = 85.0/4.0; double phiDeg = ID*90.0 + 5.0 + (chDown + 0.5)*sectorWidth; double phi = phiDeg * TMath::DegToRad(); hitPos.SetXYZ( r*TMath::Cos(phi), r*TMath::Sin(phi), qqqZPos ); id = ID; chBk = chBack; chDn = chDown; chUp = chUp; } #endif