1 /**************************************************************************
2 * Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
4 * Author: The ALICE Off-line Project. *
5 * Contributors are mentioned in the code where appropriate. *
7 * Permission to use, copy, modify and distribute this software and its *
8 * documentation strictly for non-commercial purposes is hereby granted *
9 * without fee, provided that the above copyright notice appears in all *
10 * copies and that both the copyright notice and this permission notice *
11 * appear in the supporting documentation. The authors make no claims *
12 * about the suitability of this software for any purpose. It is *
13 * provided "as is" without express or implied warranty. *
14 **************************************************************************/
18 /* History of cvs commits:
21 * Revision 1.110 2007/03/10 08:58:52 kharlov
22 * Protection for noCPV geometry
24 * Revision 1.109 2007/03/01 11:37:37 kharlov
25 * Strip units changed from 8x1 to 8x2 (T.Pocheptsov)
27 * Revision 1.108 2007/02/02 09:40:50 alibrary
28 * Includes required by ROOT head
30 * Revision 1.107 2007/02/01 10:34:47 hristov
31 * Removing warnings on Solaris x86
33 * Revision 1.106 2006/11/14 17:11:15 hristov
34 * Removing inheritances from TAttLine, TAttMarker and AliRndm in AliModule. The copy constructor and assignment operators are moved to the private part of the class and not implemented. The corresponding changes are propagated to the detectors
36 * Revision 1.105 2006/09/13 07:31:01 kharlov
37 * Effective C++ corrections (T.Pocheptsov)
39 * Revision 1.104 2005/05/28 14:19:05 schutz
40 * Compilation warnings fixed by T.P.
44 //_________________________________________________________________________
45 // Implementation version v1 of PHOS Manager class
48 // Layout EMC + CPV has name IHEP:
49 // Produces hits for CPV, cumulated hits
52 //*-- Author: Yves Schutz (SUBATECH)
55 // --- ROOT system ---
56 #include <TClonesArray.h>
57 #include <TParticle.h>
58 #include <TVirtualMC.h>
60 // --- Standard library ---
63 // --- AliRoot header files ---
64 #include "AliPHOSCPVDigit.h"
65 #include "AliPHOSGeometry.h"
66 #include "AliPHOSHit.h"
67 #include "AliPHOSv1.h"
74 //____________________________________________________________________________
75 AliPHOSv1::AliPHOSv1():
77 fIntrinsicPINEfficiency(0.),
78 fLightYieldAttenuation(0.),
79 fRecalibrationFactor(0.),
88 //____________________________________________________________________________
89 AliPHOSv1::AliPHOSv1(const char *name, const char *title):
90 AliPHOSv0(name,title),
92 fIntrinsicPINEfficiency(0.),
93 fLightYieldAttenuation(0.),
94 fRecalibrationFactor(0.),
102 // - fHits (the "normal" one), which retains the hits associated with
103 // the current primary particle being tracked
104 // (this array is reset after each primary has been tracked).
109 // We do not want to save in TreeH the raw hits
110 // But save the cumulated hits instead (need to create the branch myself)
111 // It is put in the Digit Tree because the TreeH is filled after each primary
112 // and the TreeD at the end of the event (branch is set in FinishEvent() ).
114 fHits= new TClonesArray("AliPHOSHit",1000) ;
115 gAlice->GetMCApp()->AddHitList(fHits) ;
119 fIshunt = 2 ; // All hits are associated with primary particles
121 //Photoelectron statistics:
122 // The light yield is a poissonian distribution of the number of
123 // photons created in the PbWo4 crystal, calculated using following formula
124 // NumberOfPhotons = EnergyLost * LightYieldMean* APDEfficiency *
125 // exp (-LightYieldAttenuation * DistanceToPINdiodeFromTheHit);
126 // LightYieldMean is parameter calculated to be over 47000 photons per GeV
127 // APDEfficiency is 0.02655
128 // k_0 is 0.0045 from Valery Antonenko
129 // The number of electrons created in the APD is
130 // NumberOfElectrons = APDGain * LightYield
131 // The APD Gain is 300
132 fLightYieldMean = 47000;
133 fIntrinsicPINEfficiency = 0.02655 ; //APD= 0.1875/0.1271 * 0.018 (PIN)
134 fLightYieldAttenuation = 0.0045 ;
135 fRecalibrationFactor = 13.418/ fLightYieldMean ;
136 fElectronsPerGeV = 2.77e+8 ;
138 fLightFactor = fLightYieldMean * fIntrinsicPINEfficiency ;
139 fAPDFactor = (fRecalibrationFactor/100.) * fAPDGain ;
142 //____________________________________________________________________________
143 AliPHOSv1::~AliPHOSv1()
153 //____________________________________________________________________________
154 void AliPHOSv1::AddHit(Int_t shunt, Int_t primary, Int_t Id, Float_t * hits)
156 // Add a hit to the hit list.
157 // A PHOS hit is the sum of all hits in a single crystal from one primary and within some time gate
162 Bool_t deja = kFALSE ;
163 AliPHOSGeometry * geom = GetGeometry() ;
165 newHit = new AliPHOSHit(shunt, primary, Id, hits) ;
167 for ( hitCounter = fNhits-1 ; hitCounter >= 0 && !deja ; hitCounter-- ) {
168 curHit = dynamic_cast<AliPHOSHit*>((*fHits)[hitCounter]) ;
169 if(curHit->GetPrimary() != primary) break ;
170 // We add hits with the same primary, while GEANT treats primaries succesively
171 if( *curHit == *newHit ) {
178 new((*fHits)[fNhits]) AliPHOSHit(*newHit) ;
179 // get the block Id number
181 geom->AbsToRelNumbering(Id, relid) ;
189 //____________________________________________________________________________
190 void AliPHOSv1::FinishPrimary()
192 // called at the end of each track (primary) by AliRun
193 // hits are reset for each new track
194 // accumulate the total hit-multiplicity
198 //____________________________________________________________________________
199 void AliPHOSv1::FinishEvent()
201 // called at the end of each event by AliRun
202 // accumulate the hit-multiplicity and total energy per block
203 // if the values have been updated check it
205 AliDetector::FinishEvent();
207 //____________________________________________________________________________
208 void AliPHOSv1::StepManager(void)
210 // Accumulates hits as long as the track stays in a single crystal or CPV gas Cell
212 Int_t relid[4] ; // (box, layer, row, column) indices
213 Int_t absid ; // absolute cell ID number
214 Float_t xyzte[5]={-1000.,-1000.,-1000.,0.,0.} ; // position wrt MRS, time and energy deposited
215 TLorentzVector pos ; // Lorentz vector of the track current position
220 static Int_t idPCPQ = -1;
221 if (strstr(fTitle.Data(),"noCPV") == 0)
222 idPCPQ = gMC->VolId("PCPQ");
224 if( gMC->CurrentVolID(copy) == idPCPQ &&
225 (gMC->IsTrackEntering() ) &&
226 gMC->TrackCharge() != 0) {
228 gMC -> TrackPosition(pos);
230 Float_t xyzm[3], xyzd[3] ;
232 for (i=0; i<3; i++) xyzm[i] = pos[i];
233 gMC -> Gmtod (xyzm, xyzd, 1); // transform coordinate from master to daughter system
235 Float_t xyd[3]={0,0,0} ; //local position of the entering
240 // Current momentum of the hit's track in the local ref. system
241 TLorentzVector pmom ; //momentum of the particle initiated hit
242 gMC -> TrackMomentum(pmom);
243 Float_t pm[3], pd[3];
247 gMC -> Gmtod (pm, pd, 2); // transform 3-momentum from master to daughter system
252 // Digitize the current CPV hit:
254 // 1. find pad response and
255 gMC->CurrentVolOffID(3,moduleNumber);
258 TClonesArray *cpvDigits = new TClonesArray("AliPHOSCPVDigit",0); // array of digits for current hit
259 CPVDigitize(pmom,xyd,cpvDigits);
264 Int_t idigit,ndigits;
266 // 2. go through the current digit list and sum digits in pads
268 ndigits = cpvDigits->GetEntriesFast();
269 for (idigit=0; idigit<ndigits-1; idigit++) {
270 AliPHOSCPVDigit *cpvDigit1 = dynamic_cast<AliPHOSCPVDigit*>(cpvDigits->UncheckedAt(idigit));
271 Float_t x1 = cpvDigit1->GetXpad() ;
272 Float_t z1 = cpvDigit1->GetYpad() ;
273 for (Int_t jdigit=idigit+1; jdigit<ndigits; jdigit++) {
274 AliPHOSCPVDigit *cpvDigit2 = dynamic_cast<AliPHOSCPVDigit*>(cpvDigits->UncheckedAt(jdigit));
275 Float_t x2 = cpvDigit2->GetXpad() ;
276 Float_t z2 = cpvDigit2->GetYpad() ;
277 if (x1==x2 && z1==z2) {
278 Float_t qsumpad = cpvDigit1->GetQpad() + cpvDigit2->GetQpad() ;
279 cpvDigit2->SetQpad(qsumpad) ;
280 cpvDigits->RemoveAt(idigit) ;
284 cpvDigits->Compress() ;
286 // 3. add digits to temporary hit list fTmpHits
288 ndigits = cpvDigits->GetEntriesFast();
289 for (idigit=0; idigit<ndigits; idigit++) {
290 AliPHOSCPVDigit *cpvDigit = dynamic_cast<AliPHOSCPVDigit*>(cpvDigits->UncheckedAt(idigit));
291 relid[0] = moduleNumber + 1 ; // CPV (or PHOS) module number
292 relid[1] =-1 ; // means CPV
293 relid[2] = cpvDigit->GetXpad() ; // column number of a pad
294 relid[3] = cpvDigit->GetYpad() ; // row number of a pad
296 // get the absolute Id number
297 GetGeometry()->RelToAbsNumbering(relid, absid) ;
299 // add current digit to the temporary hit list
301 xyzte[3] = gMC->TrackTime() ;
302 xyzte[4] = cpvDigit->GetQpad() ; // amplitude in a pad
304 Int_t primary = gAlice->GetMCApp()->GetPrimary( gAlice->GetMCApp()->GetCurrentTrackNumber() );
305 AddHit(fIshunt, primary, absid, xyzte);
307 if (cpvDigit->GetQpad() > 0.02) {
308 xmean += cpvDigit->GetQpad() * (cpvDigit->GetXpad() + 0.5);
309 zmean += cpvDigit->GetQpad() * (cpvDigit->GetYpad() + 0.5);
310 qsum += cpvDigit->GetQpad();
321 static Int_t idPXTL = gMC->VolId("PXTL");
322 if(gMC->CurrentVolID(copy) == idPXTL ) { // We are inside a PBWO crystal
324 gMC->TrackPosition(pos) ;
329 Float_t global[3], local[3] ;
333 Float_t lostenergy = gMC->Edep();
335 //Put in the TreeK particle entering PHOS and all its parents
336 if ( gMC->IsTrackEntering() ){
338 gMC -> Gmtod (xyzte, xyzd, 1); // transform coordinate from master to daughter system
339 if (xyzd[1] < -GetGeometry()->GetCrystalSize(1)/2.+0.1){ //Entered close to forward surface
340 Int_t parent = gAlice->GetMCApp()->GetCurrentTrackNumber() ;
341 TParticle * part = gAlice->GetMCApp()->Particle(parent) ;
342 Float_t vert[3],vertd[3] ;
346 gMC -> Gmtod (vert, vertd, 1); // transform coordinate from master to daughter system
347 if(vertd[1]<-GetGeometry()->GetCrystalSize(1)/2.-0.1){ //Particle is created in foront of PHOS
348 //0.1 to get rid of numerical errors
349 part->SetBit(kKeepBit);
350 while ( parent != -1 ) {
351 part = gAlice->GetMCApp()->Particle(parent) ;
352 part->SetBit(kKeepBit);
353 parent = part->GetFirstMother() ;
358 if ( lostenergy != 0 ) { // Track is inside the crystal and deposits some energy
359 xyzte[3] = gMC->TrackTime() ;
361 gMC->CurrentVolOffID(10, moduleNumber) ; // get the PHOS module number ;
364 gMC->CurrentVolOffID(3, strip);
366 gMC->CurrentVolOffID(2, cell);
368 //Old formula for row is wrong. For example, I have strip 56 (28 for 2 x 8), row must be 1.
369 //But row == 1 + 56 - 56 % 56 == 57 (row == 1 + 28 - 28 % 28 == 29)
370 //Int_t row = 1 + GetGeometry()->GetEMCAGeometry()->GetNStripZ() - strip % (GetGeometry()->GetEMCAGeometry()->GetNStripZ()) ;
371 Int_t row = GetGeometry()->GetEMCAGeometry()->GetNStripZ() - (strip - 1) % (GetGeometry()->GetEMCAGeometry()->GetNStripZ()) ;
372 Int_t col = (Int_t) TMath::Ceil((Double_t) strip/(GetGeometry()->GetEMCAGeometry()->GetNStripZ())) -1 ;
374 // Absid for 8x2-strips. Looks nice :)
375 absid = (moduleNumber-1)*GetGeometry()->GetNCristalsInModule() +
376 row * 2 + (col*GetGeometry()->GetEMCAGeometry()->GetNCellsXInStrip() + (cell - 1) / 2)*GetGeometry()->GetNZ() - (cell & 1 ? 1 : 0);
378 gMC->Gmtod(global, local, 1) ;
380 //Calculates the light yield, the number of photons produced in the
382 Float_t lightYield = gRandom->Poisson(fLightFactor * lostenergy *
383 exp(-fLightYieldAttenuation *
384 (local[1]+GetGeometry()->GetCrystalSize(1)/2.0 ))
387 //Calculates de energy deposited in the crystal
388 xyzte[4] = fAPDFactor * lightYield ;
392 primary = gAlice->GetMCApp()->GetCurrentTrackNumber() ;
393 TParticle * part = gAlice->GetMCApp()->Particle(primary) ;
394 while ( !part->TestBit(kKeepBit) ) {
395 primary = part->GetFirstMother() ;
397 primary = gAlice->GetMCApp()->GetPrimary( gAlice->GetMCApp()->GetCurrentTrackNumber() );
398 break ; //there is a possibility that particle passed e.g. thermal isulator and hits a side
399 //surface of the crystal. In this case it may have no primary at all.
400 //We can not easily separate this case from the case when this is part of the shower,
401 //developed in the neighboring crystal.
403 part = gAlice->GetMCApp()->Particle(primary) ;
407 primary = gAlice->GetMCApp()->GetPrimary( gAlice->GetMCApp()->GetCurrentTrackNumber() );
411 // add current hit to the hit list
412 // Info("StepManager","%d %d", primary, tracknumber) ;
413 AddHit(fIshunt, primary, absid, xyzte);
415 } // there is deposited energy
416 } // we are inside a PHOS Xtal
420 //____________________________________________________________________________
421 void AliPHOSv1::CPVDigitize (TLorentzVector p, Float_t *zxhit, TClonesArray *cpvDigits)
423 // ------------------------------------------------------------------------
424 // Digitize one CPV hit:
425 // On input take exact 4-momentum p and position zxhit of the hit,
426 // find the pad response around this hit and
427 // put the amplitudes in the pads into array digits
429 // Author: Yuri Kharlov (after Serguei Sadovsky)
431 // ------------------------------------------------------------------------
433 const Float_t kCelWr = GetGeometry()->GetPadSizePhi()/2; // Distance between wires (2 wires above 1 pad)
434 const Float_t kDetR = 0.1; // Relative energy fluctuation in track for 100 e-
435 const Float_t kdEdx = 4.0; // Average energy loss in CPV;
436 const Int_t kNgamz = 5; // Ionization size in Z
437 const Int_t kNgamx = 9; // Ionization size in Phi
438 const Float_t kNoise = 0.03; // charge noise in one pad
442 // Just a reminder on axes notation in the CPV module:
443 // axis Z goes along the beam
444 // axis X goes across the beam in the module plane
445 // axis Y is a normal to the module plane showing from the IP
447 Float_t hitX = zxhit[0];
448 Float_t hitZ =-zxhit[1];
451 Float_t pNorm = p.Py();
452 Float_t eloss = kdEdx;
454 //Info("CPVDigitize", "YVK : %f %f | %f %f %d", hitX, hitZ, pX, pZ, pNorm) ;
456 Float_t dZY = pZ/pNorm * GetGeometry()->GetCPVGasThickness();
457 Float_t dXY = pX/pNorm * GetGeometry()->GetCPVGasThickness();
458 gRandom->Rannor(rnor1,rnor2);
459 eloss *= (1 + kDetR*rnor1) *
460 TMath::Sqrt((1 + ( pow(dZY,2) + pow(dXY,2) ) / pow(GetGeometry()->GetCPVGasThickness(),2)));
461 Float_t zhit1 = hitZ + GetGeometry()->GetCPVActiveSize(1)/2 - dZY/2;
462 Float_t xhit1 = hitX + GetGeometry()->GetCPVActiveSize(0)/2 - dXY/2;
463 Float_t zhit2 = zhit1 + dZY;
464 Float_t xhit2 = xhit1 + dXY;
466 Int_t iwht1 = (Int_t) (xhit1 / kCelWr); // wire (x) coordinate "in"
467 Int_t iwht2 = (Int_t) (xhit2 / kCelWr); // wire (x) coordinate "out"
471 if (iwht1==iwht2) { // incline 1-wire hit
473 zxe[0][0] = (zhit1 + zhit2 - dZY*0.57735) / 2;
474 zxe[1][0] = (iwht1 + 0.5) * kCelWr;
476 zxe[0][1] = (zhit1 + zhit2 + dZY*0.57735) / 2;
477 zxe[1][1] = (iwht1 + 0.5) * kCelWr;
480 else if (TMath::Abs(iwht1-iwht2) != 1) { // incline 3-wire hit
482 Int_t iwht3 = (iwht1 + iwht2) / 2;
483 Float_t xwht1 = (iwht1 + 0.5) * kCelWr; // wire 1
484 Float_t xwht2 = (iwht2 + 0.5) * kCelWr; // wire 2
485 Float_t xwht3 = (iwht3 + 0.5) * kCelWr; // wire 3
486 Float_t xwr13 = (xwht1 + xwht3) / 2; // center 13
487 Float_t xwr23 = (xwht2 + xwht3) / 2; // center 23
488 Float_t dxw1 = xhit1 - xwr13;
489 Float_t dxw2 = xhit2 - xwr23;
490 Float_t egm1 = TMath::Abs(dxw1) / ( TMath::Abs(dxw1) + TMath::Abs(dxw2) + kCelWr );
491 Float_t egm2 = TMath::Abs(dxw2) / ( TMath::Abs(dxw1) + TMath::Abs(dxw2) + kCelWr );
492 Float_t egm3 = kCelWr / ( TMath::Abs(dxw1) + TMath::Abs(dxw2) + kCelWr );
493 zxe[0][0] = (dXY*(xwr13-xwht1)/dXY + zhit1 + zhit1) / 2;
495 zxe[2][0] = eloss * egm1;
496 zxe[0][1] = (dXY*(xwr23-xwht1)/dXY + zhit1 + zhit2) / 2;
498 zxe[2][1] = eloss * egm2;
499 zxe[0][2] = dXY*(xwht3-xwht1)/dXY + zhit1;
501 zxe[2][2] = eloss * egm3;
503 else { // incline 2-wire hit
505 Float_t xwht1 = (iwht1 + 0.5) * kCelWr;
506 Float_t xwht2 = (iwht2 + 0.5) * kCelWr;
507 Float_t xwr12 = (xwht1 + xwht2) / 2;
508 Float_t dxw1 = xhit1 - xwr12;
509 Float_t dxw2 = xhit2 - xwr12;
510 Float_t egm1 = TMath::Abs(dxw1) / ( TMath::Abs(dxw1) + TMath::Abs(dxw2) );
511 Float_t egm2 = TMath::Abs(dxw2) / ( TMath::Abs(dxw1) + TMath::Abs(dxw2) );
512 zxe[0][0] = (zhit1 + zhit2 - dZY*egm1) / 2;
514 zxe[2][0] = eloss * egm1;
515 zxe[0][1] = (zhit1 + zhit2 + dZY*egm2) / 2;
517 zxe[2][1] = eloss * egm2;
520 // Finite size of ionization region
522 Int_t nCellZ = GetGeometry()->GetNumberOfCPVPadsZ();
523 Int_t nCellX = GetGeometry()->GetNumberOfCPVPadsPhi();
524 Int_t nz3 = (kNgamz+1)/2;
525 Int_t nx3 = (kNgamx+1)/2;
526 cpvDigits->Expand(nIter*kNgamx*kNgamz);
527 TClonesArray &ldigits = *(static_cast<TClonesArray *>(cpvDigits));
529 for (Int_t iter=0; iter<nIter; iter++) {
531 Float_t zhit = zxe[0][iter];
532 Float_t xhit = zxe[1][iter];
533 Float_t qhit = zxe[2][iter];
534 Float_t zcell = zhit / GetGeometry()->GetPadSizeZ();
535 Float_t xcell = xhit / GetGeometry()->GetPadSizePhi();
536 if ( zcell<=0 || xcell<=0 ||
537 zcell>=nCellZ || xcell>=nCellX) return;
538 Int_t izcell = (Int_t) zcell;
539 Int_t ixcell = (Int_t) xcell;
540 Float_t zc = zcell - izcell - 0.5;
541 Float_t xc = xcell - ixcell - 0.5;
542 for (Int_t iz=1; iz<=kNgamz; iz++) {
543 Int_t kzg = izcell + iz - nz3;
544 if (kzg<=0 || kzg>nCellZ) continue;
545 Float_t zg = (Float_t)(iz-nz3) - zc;
546 for (Int_t ix=1; ix<=kNgamx; ix++) {
547 Int_t kxg = ixcell + ix - nx3;
548 if (kxg<=0 || kxg>nCellX) continue;
549 Float_t xg = (Float_t)(ix-nx3) - xc;
551 // Now calculate pad response
552 Float_t qpad = CPVPadResponseFunction(qhit,zg,xg);
553 qpad += kNoise*rnor2;
554 if (qpad<0) continue;
556 // Fill the array with pad response ID and amplitude
557 new(ldigits[cpvDigits->GetEntriesFast()]) AliPHOSCPVDigit(kxg,kzg,qpad);
563 //____________________________________________________________________________
564 Float_t AliPHOSv1::CPVPadResponseFunction(Float_t qhit, Float_t zhit, Float_t xhit) {
565 // ------------------------------------------------------------------------
566 // Calculate the amplitude in one CPV pad using the
567 // cumulative pad response function
568 // Author: Yuri Kharlov (after Serguei Sadovski)
570 // ------------------------------------------------------------------------
572 Double_t dz = GetGeometry()->GetPadSizeZ() / 2;
573 Double_t dx = GetGeometry()->GetPadSizePhi() / 2;
574 Double_t z = zhit * GetGeometry()->GetPadSizeZ();
575 Double_t x = xhit * GetGeometry()->GetPadSizePhi();
576 Double_t amplitude = qhit *
577 (CPVCumulPadResponse(z+dz,x+dx) - CPVCumulPadResponse(z+dz,x-dx) -
578 CPVCumulPadResponse(z-dz,x+dx) + CPVCumulPadResponse(z-dz,x-dx));
579 return (Float_t)amplitude;
582 //____________________________________________________________________________
583 Double_t AliPHOSv1::CPVCumulPadResponse(Double_t x, Double_t y) {
584 // ------------------------------------------------------------------------
585 // Cumulative pad response function
586 // It includes several terms from the CF decomposition in electrostatics
587 // Note: this cumulative function is wrong since omits some terms
588 // but the cell amplitude obtained with it is correct because
589 // these omitting terms cancel
590 // Author: Yuri Kharlov (after Serguei Sadovski)
592 // ------------------------------------------------------------------------
594 const Double_t kA=1.0;
595 const Double_t kB=0.7;
597 Double_t r2 = x*x + y*y;
599 Double_t cumulPRF = 0;
600 for (Int_t i=0; i<=4; i++) {
601 Double_t b1 = (2*i + 1) * kB;
602 cumulPRF += TMath::Power(-1,i) * TMath::ATan( xy / (b1*TMath::Sqrt(b1*b1 + r2)) );
604 cumulPRF *= kA/(2*TMath::Pi());