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.111 2007/07/24 09:41:19 morsch
22 * AliStack included for kKeepBit.
24 * Revision 1.110 2007/03/10 08:58:52 kharlov
25 * Protection for noCPV geometry
27 * Revision 1.109 2007/03/01 11:37:37 kharlov
28 * Strip units changed from 8x1 to 8x2 (T.Pocheptsov)
30 * Revision 1.108 2007/02/02 09:40:50 alibrary
31 * Includes required by ROOT head
33 * Revision 1.107 2007/02/01 10:34:47 hristov
34 * Removing warnings on Solaris x86
36 * Revision 1.106 2006/11/14 17:11:15 hristov
37 * 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
39 * Revision 1.105 2006/09/13 07:31:01 kharlov
40 * Effective C++ corrections (T.Pocheptsov)
42 * Revision 1.104 2005/05/28 14:19:05 schutz
43 * Compilation warnings fixed by T.P.
47 //_________________________________________________________________________
48 // Implementation version v1 of PHOS Manager class
51 // Layout EMC + CPV has name IHEP:
52 // Produces hits for CPV, cumulated hits
55 //*-- Author: Yves Schutz (SUBATECH)
58 // --- ROOT system ---
59 #include <TClonesArray.h>
60 #include <TParticle.h>
61 #include <TVirtualMC.h>
63 // --- Standard library ---
66 // --- AliRoot header files ---
67 #include "AliPHOSCPVDigit.h"
68 #include "AliPHOSGeometry.h"
69 #include "AliPHOSHit.h"
70 #include "AliPHOSv1.h"
74 #include "AliPHOSSimParam.h"
78 //____________________________________________________________________________
79 AliPHOSv1::AliPHOSv1() : fCPVDigits("AliPHOSCPVDigit",20)
84 //____________________________________________________________________________
85 AliPHOSv1::AliPHOSv1(const char *name, const char *title):
86 AliPHOSv0(name,title), fCPVDigits("AliPHOSCPVDigit",20)
90 // - fHits (the "normal" one), which retains the hits associated with
91 // the current primary particle being tracked
92 // (this array is reset after each primary has been tracked).
97 // We do not want to save in TreeH the raw hits
98 // But save the cumulated hits instead (need to create the branch myself)
99 // It is put in the Digit Tree because the TreeH is filled after each primary
100 // and the TreeD at the end of the event (branch is set in FinishEvent() ).
102 fHits= new TClonesArray("AliPHOSHit",1000) ;
103 // fCPVDigits("AliPHOSCPVDigit",20);
104 gAlice->GetMCApp()->AddHitList(fHits) ;
108 fIshunt = 2 ; // All hits are associated with primary particles
111 //____________________________________________________________________________
112 AliPHOSv1::~AliPHOSv1()
122 //____________________________________________________________________________
123 void AliPHOSv1::AddHit(Int_t shunt, Int_t primary, Int_t Id, Float_t * hits)
125 // Add a hit to the hit list.
126 // A PHOS hit is the sum of all hits in a single crystal from one primary and within some time gate
131 Bool_t deja = kFALSE ;
132 AliPHOSGeometry * geom = GetGeometry() ;
134 newHit = new AliPHOSHit(shunt, primary, Id, hits) ;
136 for ( hitCounter = fNhits-1 ; hitCounter >= 0 && !deja ; hitCounter-- ) {
137 curHit = static_cast<AliPHOSHit*>((*fHits)[hitCounter]) ;
138 if(curHit->GetPrimary() != primary) break ;
139 // We add hits with the same primary, while GEANT treats primaries succesively
140 if( *curHit == *newHit ) {
147 new((*fHits)[fNhits]) AliPHOSHit(*newHit) ;
148 // get the block Id number
150 geom->AbsToRelNumbering(Id, relid) ;
158 //____________________________________________________________________________
159 void AliPHOSv1::FinishPrimary()
161 // called at the end of each track (primary) by AliRun
162 // hits are reset for each new track
163 // accumulate the total hit-multiplicity
167 //____________________________________________________________________________
168 void AliPHOSv1::FinishEvent()
170 // called at the end of each event by AliRun
171 // accumulate the hit-multiplicity and total energy per block
172 // if the values have been updated check it
174 AliDetector::FinishEvent();
176 //____________________________________________________________________________
177 void AliPHOSv1::StepManager(void)
179 // Accumulates hits as long as the track stays in a single crystal or CPV gas Cell
181 Int_t relid[4] ; // (box, layer, row, column) indices
182 Int_t absid ; // absolute cell ID number
183 Float_t xyzte[5]={-1000.,-1000.,-1000.,0.,0.} ; // position wrt MRS, time and energy deposited
184 TLorentzVector pos ; // Lorentz vector of the track current position
189 static Int_t idPCPQ = -1;
190 if (strstr(fTitle.Data(),"noCPV") == 0)
191 idPCPQ = TVirtualMC::GetMC()->VolId("PCPQ");
193 if( TVirtualMC::GetMC()->CurrentVolID(copy) == idPCPQ &&
194 (TVirtualMC::GetMC()->IsTrackEntering() ) &&
195 TVirtualMC::GetMC()->TrackCharge() != 0) {
197 TVirtualMC::GetMC() -> TrackPosition(pos);
199 Float_t xyzm[3], xyzd[3] ;
201 for (i=0; i<3; i++) xyzm[i] = pos[i];
202 TVirtualMC::GetMC() -> Gmtod (xyzm, xyzd, 1); // transform coordinate from master to daughter system
205 Float_t xyd[3]={0,0,0} ; //local position of the entering
210 // Current momentum of the hit's track in the local ref. system
211 TLorentzVector pmom ; //momentum of the particle initiated hit
212 TVirtualMC::GetMC() -> TrackMomentum(pmom);
213 Float_t pm[3], pd[3];
217 TVirtualMC::GetMC() -> Gmtod (pm, pd, 2); // transform 3-momentum from master to daughter system
222 // Digitize the current CPV hit:
224 // 1. find pad response and
225 TVirtualMC::GetMC()->CurrentVolOffID(3,moduleNumber);
228 // TClonesArray *cpvDigits = new TClonesArray("AliPHOSCPVDigit",0); // array of digits for current hit
229 CPVDigitize(pmom,xyd,&fCPVDigits);
234 Int_t idigit,ndigits;
236 // 2. go through the current digit list and sum digits in pads
238 ndigits = fCPVDigits.GetEntriesFast();
239 for (idigit=0; idigit<ndigits-1; idigit++) {
240 AliPHOSCPVDigit *cpvDigit1 = static_cast<AliPHOSCPVDigit*>(fCPVDigits.UncheckedAt(idigit));
241 Float_t x1 = cpvDigit1->GetXpad() ;
242 Float_t z1 = cpvDigit1->GetYpad() ;
243 for (Int_t jdigit=idigit+1; jdigit<ndigits; jdigit++) {
244 AliPHOSCPVDigit *cpvDigit2 = static_cast<AliPHOSCPVDigit*>(fCPVDigits.UncheckedAt(jdigit));
245 Float_t x2 = cpvDigit2->GetXpad() ;
246 Float_t z2 = cpvDigit2->GetYpad() ;
247 if (x1==x2 && z1==z2) {
248 Float_t qsumpad = cpvDigit1->GetQpad() + cpvDigit2->GetQpad() ;
249 cpvDigit2->SetQpad(qsumpad) ;
250 fCPVDigits.RemoveAt(idigit) ;
254 fCPVDigits.Compress() ;
256 // 3. add digits to temporary hit list fTmpHits
258 ndigits = fCPVDigits.GetEntriesFast();
259 for (idigit=0; idigit<ndigits; idigit++) {
260 AliPHOSCPVDigit *cpvDigit = static_cast<AliPHOSCPVDigit*>(fCPVDigits.UncheckedAt(idigit));
261 relid[0] = moduleNumber + 1 ; // CPV (or PHOS) module number
262 relid[1] =-1 ; // means CPV
263 relid[2] = cpvDigit->GetXpad() ; // column number of a pad
264 relid[3] = cpvDigit->GetYpad() ; // row number of a pad
266 // get the absolute Id number
267 GetGeometry()->RelToAbsNumbering(relid, absid) ;
269 // add current digit to the temporary hit list
271 xyzte[3] = TVirtualMC::GetMC()->TrackTime() ;
272 xyzte[4] = cpvDigit->GetQpad() ; // amplitude in a pad
274 Int_t primary = gAlice->GetMCApp()->GetPrimary( gAlice->GetMCApp()->GetCurrentTrackNumber() );
275 AddHit(fIshunt, primary, absid, xyzte);
277 if (cpvDigit->GetQpad() > 0.02) {
278 xmean += cpvDigit->GetQpad() * (cpvDigit->GetXpad() + 0.5);
279 zmean += cpvDigit->GetQpad() * (cpvDigit->GetYpad() + 0.5);
280 qsum += cpvDigit->GetQpad();
287 static Int_t idPXTL = TVirtualMC::GetMC()->VolId("PXTL");
288 if(TVirtualMC::GetMC()->CurrentVolID(copy) == idPXTL ) { // We are inside a PBWO crystal
290 TVirtualMC::GetMC()->TrackPosition(pos) ;
295 Float_t lostenergy = TVirtualMC::GetMC()->Edep();
297 //Put in the TreeK particle entering PHOS and all its parents
298 if ( TVirtualMC::GetMC()->IsTrackEntering() ){
300 TVirtualMC::GetMC() -> Gmtod (xyzte, xyzd, 1); // transform coordinate from master to daughter system
301 if (xyzd[1] < -GetGeometry()->GetCrystalSize(1)/2.+0.1){ //Entered close to forward surface
302 Int_t parent = gAlice->GetMCApp()->GetCurrentTrackNumber() ;
303 TParticle * part = gAlice->GetMCApp()->Particle(parent) ;
304 Float_t vert[3],vertd[3] ;
308 TVirtualMC::GetMC() -> Gmtod (vert, vertd, 1); // transform coordinate from master to daughter system
309 if(vertd[1]<-GetGeometry()->GetCrystalSize(1)/2.-0.1){ //Particle is created in foront of PHOS
310 //0.1 to get rid of numerical errors
311 part->SetBit(kKeepBit);
312 while ( parent != -1 ) {
313 part = gAlice->GetMCApp()->Particle(parent) ;
314 part->SetBit(kKeepBit);
315 parent = part->GetFirstMother() ;
320 if ( lostenergy != 0 ) { // Track is inside the crystal and deposits some energy
321 xyzte[3] = TVirtualMC::GetMC()->TrackTime() ;
323 TVirtualMC::GetMC()->CurrentVolOffID(10, moduleNumber) ; // get the PHOS module number ;
326 TVirtualMC::GetMC()->CurrentVolOffID(3, strip);
328 TVirtualMC::GetMC()->CurrentVolOffID(2, cell);
330 //Old formula for row is wrong. For example, I have strip 56 (28 for 2 x 8), row must be 1.
331 //But row == 1 + 56 - 56 % 56 == 57 (row == 1 + 28 - 28 % 28 == 29)
332 //Int_t row = 1 + GetGeometry()->GetEMCAGeometry()->GetNStripZ() - strip % (GetGeometry()->GetEMCAGeometry()->GetNStripZ()) ;
333 Int_t row = GetGeometry()->GetEMCAGeometry()->GetNStripZ() - (strip - 1) % (GetGeometry()->GetEMCAGeometry()->GetNStripZ()) ;
334 Int_t col = (Int_t) TMath::Ceil((Double_t) strip/(GetGeometry()->GetEMCAGeometry()->GetNStripZ())) -1 ;
336 // Absid for 8x2-strips. Looks nice :)
337 absid = (moduleNumber-1)*GetGeometry()->GetNCristalsInModule() +
338 row * 2 + (col*GetGeometry()->GetEMCAGeometry()->GetNCellsXInStrip() + (cell - 1) / 2)*GetGeometry()->GetNZ() - (cell & 1 ? 1 : 0);
340 //Calculates the light yield, the number of photons produced in the
342 //There is no dependence of reponce on distance from energy deposition to APD
343 Float_t lightYield = gRandom->Poisson(AliPHOSSimParam::GetInstance()->GetLightFactor() * lostenergy) ;
345 //Calculates de energy deposited in the crystal
346 xyzte[4] = AliPHOSSimParam::GetInstance()->GetAPDFactor() * lightYield ;
350 primary = gAlice->GetMCApp()->GetCurrentTrackNumber() ;
351 TParticle * part = gAlice->GetMCApp()->Particle(primary) ;
352 while ( !part->TestBit(kKeepBit) ) {
353 primary = part->GetFirstMother() ;
355 primary = gAlice->GetMCApp()->GetPrimary( gAlice->GetMCApp()->GetCurrentTrackNumber() );
356 break ; //there is a possibility that particle passed e.g. thermal isulator and hits a side
357 //surface of the crystal. In this case it may have no primary at all.
358 //We can not easily separate this case from the case when this is part of the shower,
359 //developed in the neighboring crystal.
361 part = gAlice->GetMCApp()->Particle(primary) ;
365 primary = gAlice->GetMCApp()->GetPrimary( gAlice->GetMCApp()->GetCurrentTrackNumber() );
368 // add current hit to the hit list
369 // Info("StepManager","%d %d", primary, tracknumber) ;
370 AddHit(fIshunt, primary, absid, xyzte);
372 } // there is deposited energy
373 } // we are inside a PHOS Xtal
377 //____________________________________________________________________________
378 void AliPHOSv1::CPVDigitize (TLorentzVector p, Float_t *zxhit, TClonesArray *cpvDigits)
380 // ------------------------------------------------------------------------
381 // Digitize one CPV hit:
382 // On input take exact 4-momentum p and position zxhit of the hit,
383 // find the pad response around this hit and
384 // put the amplitudes in the pads into array digits
386 // Author: Yuri Kharlov (after Serguei Sadovsky)
388 // ------------------------------------------------------------------------
390 const Float_t kCelWr = GetGeometry()->GetPadSizePhi()/2; // Distance between wires (2 wires above 1 pad)
391 const Float_t kDetR = 0.1; // Relative energy fluctuation in track for 100 e-
392 const Float_t kdEdx = 4.0; // Average energy loss in CPV;
393 const Int_t kNgamz = 5; // Ionization size in Z
394 const Int_t kNgamx = 9; // Ionization size in Phi
395 const Float_t kNoise = 0.03; // charge noise in one pad
399 // Just a reminder on axes notation in the CPV module:
400 // axis Z goes along the beam
401 // axis X goes across the beam in the module plane
402 // axis Y is a normal to the module plane showing from the IP
404 Float_t hitX = zxhit[0];
405 Float_t hitZ =-zxhit[1];
408 Float_t pNorm = p.Py();
409 Float_t eloss = kdEdx;
411 Float_t dZY = pZ/pNorm * GetGeometry()->GetCPVGasThickness();
412 Float_t dXY = pX/pNorm * GetGeometry()->GetCPVGasThickness();
413 gRandom->Rannor(rnor1,rnor2);
414 eloss *= (1 + kDetR*rnor1) *
415 TMath::Sqrt((1 + ( pow(dZY,2) + pow(dXY,2) ) / pow(GetGeometry()->GetCPVGasThickness(),2)));
416 Float_t zhit1 = hitZ + GetGeometry()->GetCPVActiveSize(1)/2 - dZY/2;
417 Float_t xhit1 = hitX + GetGeometry()->GetCPVActiveSize(0)/2 - dXY/2;
418 Float_t zhit2 = zhit1 + dZY;
419 Float_t xhit2 = xhit1 + dXY;
421 Int_t iwht1 = (Int_t) (xhit1 / kCelWr); // wire (x) coordinate "in"
422 Int_t iwht2 = (Int_t) (xhit2 / kCelWr); // wire (x) coordinate "out"
426 if (iwht1==iwht2) { // incline 1-wire hit
428 zxe[0][0] = (zhit1 + zhit2 - dZY*0.57735) / 2;
429 zxe[1][0] = (iwht1 + 0.5) * kCelWr;
431 zxe[0][1] = (zhit1 + zhit2 + dZY*0.57735) / 2;
432 zxe[1][1] = (iwht1 + 0.5) * kCelWr;
435 else if (TMath::Abs(iwht1-iwht2) != 1) { // incline 3-wire hit
437 Int_t iwht3 = (iwht1 + iwht2) / 2;
438 Float_t xwht1 = (iwht1 + 0.5) * kCelWr; // wire 1
439 Float_t xwht2 = (iwht2 + 0.5) * kCelWr; // wire 2
440 Float_t xwht3 = (iwht3 + 0.5) * kCelWr; // wire 3
441 Float_t xwr13 = (xwht1 + xwht3) / 2; // center 13
442 Float_t xwr23 = (xwht2 + xwht3) / 2; // center 23
443 Float_t dxw1 = xhit1 - xwr13;
444 Float_t dxw2 = xhit2 - xwr23;
445 Float_t egm1 = TMath::Abs(dxw1) / ( TMath::Abs(dxw1) + TMath::Abs(dxw2) + kCelWr );
446 Float_t egm2 = TMath::Abs(dxw2) / ( TMath::Abs(dxw1) + TMath::Abs(dxw2) + kCelWr );
447 Float_t egm3 = kCelWr / ( TMath::Abs(dxw1) + TMath::Abs(dxw2) + kCelWr );
448 zxe[0][0] = (dXY*(xwr13-xwht1)/dXY + zhit1 + zhit1) / 2;
450 zxe[2][0] = eloss * egm1;
451 zxe[0][1] = (dXY*(xwr23-xwht1)/dXY + zhit1 + zhit2) / 2;
453 zxe[2][1] = eloss * egm2;
454 zxe[0][2] = dXY*(xwht3-xwht1)/dXY + zhit1;
456 zxe[2][2] = eloss * egm3;
458 else { // incline 2-wire hit
460 Float_t xwht1 = (iwht1 + 0.5) * kCelWr;
461 Float_t xwht2 = (iwht2 + 0.5) * kCelWr;
462 Float_t xwr12 = (xwht1 + xwht2) / 2;
463 Float_t dxw1 = xhit1 - xwr12;
464 Float_t dxw2 = xhit2 - xwr12;
465 Float_t egm1 = TMath::Abs(dxw1) / ( TMath::Abs(dxw1) + TMath::Abs(dxw2) );
466 Float_t egm2 = TMath::Abs(dxw2) / ( TMath::Abs(dxw1) + TMath::Abs(dxw2) );
467 zxe[0][0] = (zhit1 + zhit2 - dZY*egm1) / 2;
469 zxe[2][0] = eloss * egm1;
470 zxe[0][1] = (zhit1 + zhit2 + dZY*egm2) / 2;
472 zxe[2][1] = eloss * egm2;
475 // Finite size of ionization region
477 Int_t nCellZ = GetGeometry()->GetNumberOfCPVPadsZ();
478 Int_t nCellX = GetGeometry()->GetNumberOfCPVPadsPhi();
479 Int_t nz3 = (kNgamz+1)/2;
480 Int_t nx3 = (kNgamx+1)/2;
481 cpvDigits->Expand(nIter*kNgamx*kNgamz);
482 TClonesArray &ldigits = *(static_cast<TClonesArray *>(cpvDigits));
484 for (Int_t iter=0; iter<nIter; iter++) {
486 Float_t zhit = zxe[0][iter];
487 Float_t xhit = zxe[1][iter];
488 Float_t qhit = zxe[2][iter];
489 Float_t zcell = zhit / GetGeometry()->GetPadSizeZ();
490 Float_t xcell = xhit / GetGeometry()->GetPadSizePhi();
491 if ( zcell<=0 || xcell<=0 ||
492 zcell>=nCellZ || xcell>=nCellX) return;
493 Int_t izcell = (Int_t) zcell;
494 Int_t ixcell = (Int_t) xcell;
495 Float_t zc = zcell - izcell - 0.5;
496 Float_t xc = xcell - ixcell - 0.5;
497 for (Int_t iz=1; iz<=kNgamz; iz++) {
498 Int_t kzg = izcell + iz - nz3;
499 if (kzg<=0 || kzg>nCellZ) continue;
500 Float_t zg = (Float_t)(iz-nz3) - zc;
501 for (Int_t ix=1; ix<=kNgamx; ix++) {
502 Int_t kxg = ixcell + ix - nx3;
503 if (kxg<=0 || kxg>nCellX) continue;
504 Float_t xg = (Float_t)(ix-nx3) - xc;
506 // Now calculate pad response
507 Float_t qpad = CPVPadResponseFunction(qhit,zg,xg);
508 qpad += kNoise*rnor2;
509 if (qpad<0) continue;
511 // Fill the array with pad response ID and amplitude
512 new(ldigits[cpvDigits->GetEntriesFast()]) AliPHOSCPVDigit(kxg,kzg,qpad);
518 //____________________________________________________________________________
519 Float_t AliPHOSv1::CPVPadResponseFunction(Float_t qhit, Float_t zhit, Float_t xhit) {
520 // ------------------------------------------------------------------------
521 // Calculate the amplitude in one CPV pad using the
522 // cumulative pad response function
523 // Author: Yuri Kharlov (after Serguei Sadovski)
525 // ------------------------------------------------------------------------
527 Double_t dz = GetGeometry()->GetPadSizeZ() / 2;
528 Double_t dx = GetGeometry()->GetPadSizePhi() / 2;
529 Double_t z = zhit * GetGeometry()->GetPadSizeZ();
530 Double_t x = xhit * GetGeometry()->GetPadSizePhi();
531 Double_t amplitude = qhit *
532 (CPVCumulPadResponse(z+dz,x+dx) - CPVCumulPadResponse(z+dz,x-dx) -
533 CPVCumulPadResponse(z-dz,x+dx) + CPVCumulPadResponse(z-dz,x-dx));
534 return (Float_t)amplitude;
537 //____________________________________________________________________________
538 Double_t AliPHOSv1::CPVCumulPadResponse(Double_t x, Double_t y) {
539 // ------------------------------------------------------------------------
540 // Cumulative pad response function
541 // It includes several terms from the CF decomposition in electrostatics
542 // Note: this cumulative function is wrong since omits some terms
543 // but the cell amplitude obtained with it is correct because
544 // these omitting terms cancel
545 // Author: Yuri Kharlov (after Serguei Sadovski)
547 // ------------------------------------------------------------------------
549 const Double_t kA=1.0;
550 const Double_t kB=0.7;
552 Double_t r2 = x*x + y*y;
554 Double_t cumulPRF = 0;
555 for (Int_t i=0; i<=4; i++) {
556 Double_t b1 = (2*i + 1) * kB;
557 cumulPRF += TMath::Power(-1,i) * TMath::ATan( xy / (b1*TMath::Sqrt(b1*b1 + r2)) );
559 cumulPRF *= kA/(2*TMath::Pi());