AliRoot Core  3dc7879 (3dc7879)
AliTPCBoundaryVoltError.cxx
Go to the documentation of this file.
1 /**************************************************************************
2  * Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
3  * *
4  * Author: The ALICE Off-line Project. *
5  * Contributors are mentioned in the code where appropriate. *
6  * *
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  **************************************************************************/
15 
45 
46 
47 #include "AliMagF.h"
48 #include "TGeoGlobalMagField.h"
49 #include "AliTPCcalibDB.h"
50 #include "AliTPCParam.h"
51 #include "AliLog.h"
52 #include "TMatrixD.h"
53 
54 #include "TMath.h"
55 #include "AliTPCROC.h"
57 
61 
63  : AliTPCCorrection("BoundaryVoltError","Boundary Voltage Error"),
64  fC0(0.),fC1(0.),
65  fROCdisplacement(kTRUE),
66  fInitLookUp(kFALSE)
67 {
68  //
69  // default constructor
70  //
71  for (Int_t i=0; i<8; i++){
72  fBoundariesA[i]= 0;
73  if (i<6) fBoundariesC[i]= 0;
74  }
75 }
76 
79 
80 }
81 
82 
83 
84 
90 
91  if (corr==NULL) {
92  AliError("Zerro pointer - correction");
93  return kFALSE;
94  }
95  AliTPCBoundaryVoltError* corrC = dynamic_cast<AliTPCBoundaryVoltError *>(corr);
96  if (corrC == NULL) {
97  AliError(TString::Format("Inconsistent class types: %s\%s",IsA()->GetName(),corr->IsA()->GetName()).Data());
98  return kFALSE;
99  }
100  if (fROCdisplacement!=corrC->fROCdisplacement){
101  AliError(TString::Format("Inconsistent fROCdisplacement : %s\%s",IsA()->GetName(),corr->IsA()->GetName()).Data());
102  return kFALSE;
103  }
104  for (Int_t i=0;i <8; i++){
105  fBoundariesA[i]+= corrC->fBoundariesA[i]*weight;
106  fBoundariesC[i]+= corrC->fBoundariesC[i]*weight;
107  }
108  //
109  return kTRUE;
110 }
111 
112 
113 
114 
117 
118  AliMagF* magF= (AliMagF*)TGeoGlobalMagField::Instance()->GetField();
119  if (!magF) AliError("Magneticd field - not initialized");
120  Double_t bzField = magF->SolenoidField()/10.; //field in T
122  if (!param) AliError("Parameters - not initialized");
123  Double_t vdrift = param->GetDriftV()/1000000.; // [cm/us] // From dataBase: to be updated: per second (ideally)
124  Double_t ezField = 400; // [V/cm] // to be updated: never (hopefully)
125  Double_t wt = -10.0 * (bzField*10) * vdrift / ezField ;
126  // Correction Terms for effective omegaTau; obtained by a laser calibration run
127  SetOmegaTauT1T2(wt,fT1,fT2);
128 
130 }
131 
132 void AliTPCBoundaryVoltError::Update(const TTimeStamp &/*timeStamp*/) {
134 
135  AliMagF* magF= (AliMagF*)TGeoGlobalMagField::Instance()->GetField();
136  if (!magF) AliError("Magneticd field - not initialized");
137  Double_t bzField = magF->SolenoidField()/10.; //field in T
139  if (!param) AliError("Parameters - not initialized");
140  Double_t vdrift = param->GetDriftV()/1000000.; // [cm/us] // From dataBase: to be updated: per second (ideally)
141  Double_t ezField = 400; // [V/cm] // to be updated: never (hopefully)
142  Double_t wt = -10.0 * (bzField*10) * vdrift / ezField ;
143  // Correction Terms for effective omegaTau; obtained by a laser calibration run
144  SetOmegaTauT1T2(wt,fT1,fT2);
145 
146 }
147 
148 
149 
150 void AliTPCBoundaryVoltError::GetCorrection(const Float_t x[],const Short_t roc,Float_t dx[]) {
152 
153  if (!fInitLookUp) {
154  AliInfo("Lookup table was not initialized! Perform the inizialisation now ...");
156  }
157 
158  Int_t order = 1 ; // FIXME: hardcoded? Linear interpolation = 1, Quadratic = 2
159  // note that the poisson solution was linearly mirroed on this grid!
160  Double_t intEr, intEphi, intdEz ;
161  Double_t r, phi, z ;
162  Int_t sign;
163 
164  r = TMath::Sqrt( x[0]*x[0] + x[1]*x[1] ) ;
165  phi = TMath::ATan2(x[1],x[0]) ;
166  if ( phi < 0 ) phi += TMath::TwoPi() ; // Table uses phi from 0 to 2*Pi
167  z = x[2] ; // Create temporary copy of x[2]
168 
169  if ( (roc%36) < 18 ) {
170  sign = 1; // (TPC A side)
171  } else {
172  sign = -1; // (TPC C side)
173  }
174 
175  if ( sign==1 && z < fgkZOffSet ) z = fgkZOffSet; // Protect against discontinuity at CE
176  if ( sign==-1 && z > -fgkZOffSet ) z = -fgkZOffSet; // Protect against discontinuity at CE
177 
178 
179  intEphi = 0.0; // Efield is symmetric in phi - 2D calculation
180 
181  if ( (sign==1 && z<0) || (sign==-1 && z>0) ) // just a consistency check
182  AliError("ROC number does not correspond to z coordinate! Calculation of distortions is most likely wrong!");
183 
184  // Get the E field integral
185  Interpolate2DEdistortion( order, r, z, fLookUpErOverEz, intEr );
186  // Get DeltaEz field integral
187  Interpolate2DEdistortion( order, r, z, fLookUpDeltaEz, intdEz );
188 
189  // Calculate distorted position
190  if ( r > 0.0 ) {
191  phi = phi + ( fC0*intEphi - fC1*intEr ) / r;
192  r = r + ( fC0*intEr + fC1*intEphi );
193  }
194 
195  // Calculate correction in cartesian coordinates
196  dx[0] = r * TMath::Cos(phi) - x[0];
197  dx[1] = r * TMath::Sin(phi) - x[1];
198  dx[2] = intdEz; // z distortion - (internally scaled with driftvelocity dependency
199  // on the Ez field plus the actual ROC misalignment (if set TRUE)
200 
201 
202 }
203 
207 
208  const Float_t gridSizeR = (fgkOFCRadius-fgkIFCRadius) / (kRows-1) ;
209  const Float_t gridSizeZ = fgkTPCZ0 / (kColumns-1) ;
210 
211  TMatrixD voltArrayA(kRows,kColumns), voltArrayC(kRows,kColumns); // boundary vectors
212  TMatrixD chargeDensity(kRows,kColumns); // dummy charge
213  TMatrixD arrayErOverEzA(kRows,kColumns), arrayErOverEzC(kRows,kColumns); // solution
214  TMatrixD arrayDeltaEzA(kRows,kColumns), arrayDeltaEzC(kRows,kColumns); // solution
215 
216  Double_t rList[kRows], zedList[kColumns] ;
217 
218  // Fill arrays with initial conditions. V on the boundary and ChargeDensity in the volume.
219  for ( Int_t j = 0 ; j < kColumns ; j++ ) {
220  Double_t zed = j*gridSizeZ ;
221  zedList[j] = zed ;
222  for ( Int_t i = 0 ; i < kRows ; i++ ) {
223  Double_t radius = fgkIFCRadius + i*gridSizeR ;
224  rList[i] = radius ;
225  voltArrayA(i,j) = 0; // Initialize voltArrayA to zero
226  voltArrayC(i,j) = 0; // Initialize voltArrayC to zero
227  chargeDensity(i,j) = 0; // Initialize ChargeDensity to zero - not used in this class
228  }
229  }
230 
231 
232  // check if boundary values are the same for the C side (for later, saving some calculation time)
233 
234  Int_t symmetry = -1; // assume that A and C side are identical (but anti-symmetric!) // e.g conical deformation
235  Int_t sVec[8];
236 
237  // check if boundaries are different (regardless the sign)
238  for (Int_t i=0; i<8; i++) {
239  if (TMath::Abs(TMath::Abs(fBoundariesA[i]) - TMath::Abs(fBoundariesC[i])) > 1e-5)
240  symmetry = 0;
241  sVec[i] = (Int_t)( TMath::Sign((Float_t)1.,fBoundariesA[i]) * TMath::Sign((Float_t)1.,fBoundariesC[i]));
242  }
243  if (symmetry==-1) { // still the same values?
244  // check the kind of symmetry , if even ...
245  if (sVec[0]==1 && sVec[1]==1 && sVec[2]==1 && sVec[3]==1 && sVec[4]==1 && sVec[5]==1 && sVec[6]==1 && sVec[7]==1 )
246  symmetry = 1;
247  else if (sVec[0]==-1 && sVec[1]==-1 && sVec[2]==-1 && sVec[3]==-1 && sVec[4]==-1 && sVec[5]==-1 && sVec[6]==-1 && sVec[7]==-1 )
248  symmetry = -1;
249  else
250  symmetry = 0; // some of the values differ in the sign -> neither symmetric nor antisymmetric
251  }
252 
253 
254 
255  // Solve the electrosatic problem in 2D
256 
257  // Fill the complete Boundary vectors
258  // Start at IFC at CE and work anti-clockwise through IFC, ROC, OFC, and CE (clockwise for C side)
259  for ( Int_t j = 0 ; j < kColumns ; j++ ) {
260  Double_t zed = j*gridSizeZ ;
261  for ( Int_t i = 0 ; i < kRows ; i++ ) {
262  Double_t radius = fgkIFCRadius + i*gridSizeR ;
263 
264  // A side boundary vectors
265  if ( i == 0 ) voltArrayA(i,j) += zed *((fBoundariesA[1]-fBoundariesA[0])/((kColumns-1)*gridSizeZ))
266  + fBoundariesA[0] ; // IFC
267  if ( j == kColumns-1 ) voltArrayA(i,j) += (radius-fgkIFCRadius)*((fBoundariesA[3]-fBoundariesA[2])/((kRows-1)*gridSizeR))
268  + fBoundariesA[2] ; // ROC
269  if ( i == kRows-1 ) voltArrayA(i,j) += zed *((fBoundariesA[4]-fBoundariesA[5])/((kColumns-1)*gridSizeZ))
270  + fBoundariesA[5] ; // OFC
271  if ( j == 0 ) voltArrayA(i,j) += (radius-fgkIFCRadius)*((fBoundariesA[6]-fBoundariesA[7])/((kRows-1)*gridSizeR))
272  + fBoundariesA[7] ; // CE
273 
274  if (symmetry==0) {
275  // C side boundary vectors
276  if ( i == 0 ) voltArrayC(i,j) += zed *((fBoundariesC[1]-fBoundariesC[0])/((kColumns-1)*gridSizeZ))
277  + fBoundariesC[0] ; // IFC
278  if ( j == kColumns-1 ) voltArrayC(i,j) += (radius-fgkIFCRadius)*((fBoundariesC[3]-fBoundariesC[2])/((kRows-1)*gridSizeR))
279  + fBoundariesC[2] ; // ROC
280  if ( i == kRows-1 ) voltArrayC(i,j) += zed *((fBoundariesC[4]-fBoundariesC[5])/((kColumns-1)*gridSizeZ))
281  + fBoundariesC[5] ; // OFC
282  if ( j == 0 ) voltArrayC(i,j) += (radius-fgkIFCRadius)*((fBoundariesC[6]-fBoundariesC[7])/((kRows-1)*gridSizeR))
283  + fBoundariesC[7] ; // CE
284  }
285 
286  }
287  }
288 
289  voltArrayA(0,0) *= 0.5 ; // Use average boundary condition at corner
290  voltArrayA(kRows-1,0) *= 0.5 ; // Use average boundary condition at corner
291  voltArrayA(0,kColumns-1) *= 0.5 ; // Use average boundary condition at corner
292  voltArrayA(kRows-1,kColumns-1)*= 0.5 ; // Use average boundary condition at corner
293 
294  if (symmetry==0) {
295  voltArrayC(0,0) *= 0.5 ; // Use average boundary condition at corner
296  voltArrayC(kRows-1,0) *= 0.5 ; // Use average boundary condition at corner
297  voltArrayC(0,kColumns-1) *= 0.5 ; // Use average boundary condition at corner
298  voltArrayC(kRows-1,kColumns-1)*= 0.5 ; // Use average boundary condition at corner
299  }
300 
301 
302  // always solve the problem on the A side
303  PoissonRelaxation2D( voltArrayA, chargeDensity, arrayErOverEzA, arrayDeltaEzA,
304  kRows, kColumns, kIterations, fROCdisplacement ) ;
305 
306  if (symmetry!=0) { // A and C side are the same ("anti-symmetric" or "symmetric")
307  for ( Int_t j = 0 ; j < kColumns ; j++ ) {
308  for ( Int_t i = 0 ; i < kRows ; i++ ) {
309  arrayErOverEzC(i,j) = symmetry*arrayErOverEzA(i,j);
310  arrayDeltaEzC(i,j) = -symmetry*arrayDeltaEzA(i,j);
311  }
312  }
313  } else if (symmetry==0) { // A and C side are different - Solve the problem on the C side too
314  PoissonRelaxation2D( voltArrayC, chargeDensity, arrayErOverEzC, arrayDeltaEzC,
315  kRows, kColumns, kIterations, fROCdisplacement ) ;
316  for ( Int_t j = 0 ; j < kColumns ; j++ ) {
317  for ( Int_t i = 0 ; i < kRows ; i++ ) {
318  arrayDeltaEzC(i,j) = -arrayDeltaEzC(i,j); // negative z coordinate!
319  }
320  }
321  }
322 
323  // Interpolate results onto standard grid for Electric Fields
324  Int_t ilow=0, jlow=0 ;
325  Double_t z,r;
326  Float_t saveEr[2] ;
327  Float_t saveEz[2] ;
328  for ( Int_t i = 0 ; i < kNZ ; ++i ) {
329  z = TMath::Abs( fgkZList[i] ) ;
330  for ( Int_t j = 0 ; j < kNR ; ++j ) {
331  // Linear interpolation !!
332  r = fgkRList[j] ;
333  Search( kRows, rList, r, ilow ) ; // Note switch - R in rows and Z in columns
334  Search( kColumns, zedList, z, jlow ) ;
335  if ( ilow < 0 ) ilow = 0 ; // check if out of range
336  if ( jlow < 0 ) jlow = 0 ;
337  if ( ilow + 1 >= kRows - 1 ) ilow = kRows - 2 ;
338  if ( jlow + 1 >= kColumns - 1 ) jlow = kColumns - 2 ;
339 
340  if (fgkZList[i]>0) { // A side solution
341  saveEr[0] = arrayErOverEzA(ilow,jlow) +
342  (arrayErOverEzA(ilow,jlow+1)-arrayErOverEzA(ilow,jlow))*(z-zedList[jlow])/gridSizeZ ;
343  saveEr[1] = arrayErOverEzA(ilow+1,jlow) +
344  (arrayErOverEzA(ilow+1,jlow+1)-arrayErOverEzA(ilow+1,jlow))*(z-zedList[jlow])/gridSizeZ ;
345  saveEz[0] = arrayDeltaEzA(ilow,jlow) +
346  (arrayDeltaEzA(ilow,jlow+1)-arrayDeltaEzA(ilow,jlow))*(z-zedList[jlow])/gridSizeZ ;
347  saveEz[1] = arrayDeltaEzA(ilow+1,jlow) +
348  (arrayDeltaEzA(ilow+1,jlow+1)-arrayDeltaEzA(ilow+1,jlow))*(z-zedList[jlow])/gridSizeZ ;
349 
350  } else if (fgkZList[i]<0) { // C side solution
351  saveEr[0] = arrayErOverEzC(ilow,jlow) +
352  (arrayErOverEzC(ilow,jlow+1)-arrayErOverEzC(ilow,jlow))*(z-zedList[jlow])/gridSizeZ ;
353  saveEr[1] = arrayErOverEzC(ilow+1,jlow) +
354  (arrayErOverEzC(ilow+1,jlow+1)-arrayErOverEzC(ilow+1,jlow))*(z-zedList[jlow])/gridSizeZ ;
355  saveEz[0] = arrayDeltaEzC(ilow,jlow) +
356  (arrayDeltaEzC(ilow,jlow+1)-arrayDeltaEzC(ilow,jlow))*(z-zedList[jlow])/gridSizeZ ;
357  saveEz[1] = arrayDeltaEzC(ilow+1,jlow) +
358  (arrayDeltaEzC(ilow+1,jlow+1)-arrayDeltaEzC(ilow+1,jlow))*(z-zedList[jlow])/gridSizeZ ;
359 
360  } else {
361  AliWarning("Field calculation at z=0 (CE) is not allowed!");
362  saveEr[0]=0; saveEr[1]=0;
363  saveEz[0]=0; saveEz[1]=0;
364  }
365  fLookUpErOverEz[i][j] = saveEr[0] + (saveEr[1]-saveEr[0])*(r-rList[ilow])/gridSizeR ;
366  fLookUpDeltaEz[i][j] = saveEz[0] + (saveEz[1]-saveEz[0])*(r-rList[ilow])/gridSizeR ;
367  }
368  }
369 
370  voltArrayA.Clear();
371  voltArrayC.Clear();
372  chargeDensity.Clear();
373  arrayErOverEzA.Clear();
374  arrayErOverEzC.Clear();
375  arrayDeltaEzA.Clear();
376  arrayDeltaEzC.Clear();
377 
378  fInitLookUp = kTRUE;
379 
380 }
381 
382 void AliTPCBoundaryVoltError::Print(const Option_t* option) const {
385 
386  TString opt = option; opt.ToLower();
387  printf("%s\n",GetTitle());
388  printf(" - Voltage settings (on the TPC boundaries) - linearly interpolated\n");
389  printf(" : A-side (anti-clockwise)\n");
390  printf(" (0,1):\t IFC (CE) : %3.1f V \t IFC (ROC): %3.1f V \n",fBoundariesA[0],fBoundariesA[1]);
391  printf(" (2,3):\t ROC (IFC): %3.1f V \t ROC (OFC): %3.1f V \n",fBoundariesA[2],fBoundariesA[3]);
392  printf(" (4,5):\t OFC (ROC): %3.1f V \t OFC (CE) : %3.1f V \n",fBoundariesA[4],fBoundariesA[5]);
393  printf(" (6,7):\t CE (OFC): %3.1f V \t CE (IFC): %3.1f V \n",fBoundariesA[6],fBoundariesA[7]);
394  printf(" : C-side (clockwise)\n");
395  printf(" (0,1):\t IFC (CE) : %3.1f V \t IFC (ROC): %3.1f V \n",fBoundariesC[0],fBoundariesC[1]);
396  printf(" (2,3):\t ROC (IFC): %3.1f V \t ROC (OFC): %3.1f V \n",fBoundariesC[2],fBoundariesC[3]);
397  printf(" (4,5):\t OFC (ROC): %3.1f V \t OFC (CE) : %3.1f V \n",fBoundariesC[4],fBoundariesC[5]);
398  printf(" (6,7):\t CE (OFC): %3.1f V \t CE (IFC): %3.1f V \n",fBoundariesC[6],fBoundariesC[7]);
399 
400  // Check wether the settings of the Central Electrode agree (on the A and C side)
401  // Note: they have to be antisymmetric!
402  if (( TMath::Abs(fBoundariesA[6]+fBoundariesC[6])>1e-5) || ( TMath::Abs(fBoundariesA[7]+fBoundariesC[7])>1e-5 ) ){
403  AliWarning("Boundary parameters for the Central Electrode (CE) are not anti-symmetric! HOW DID YOU MANAGE THAT?");
404  AliWarning("Congratulations, you just splitted the Central Electrode of the TPC!");
405  AliWarning("Non-physical settings of the boundary parameter at the Central Electrode");
406  }
407 
408  if (opt.Contains("a")) { // Print all details
409  printf(" - T1: %1.4f, T2: %1.4f \n",fT1,fT2);
410  printf(" - C1: %1.4f, C0: %1.4f \n",fC1,fC0);
411  }
412 
413  if (!fInitLookUp)
414  AliError("Lookup table was not initialized! You should do InitBoundaryVoltErrorDistortion() ...");
415 
416 }
417 
418 
419 void AliTPCBoundaryVoltError::SetBoundariesA(Float_t boundariesA[8]){
430 
431  for (Int_t i=0; i<8; i++) {
432  fBoundariesA[i]= boundariesA[i];
433  if (i>5) fBoundariesC[i]= -boundariesA[i]; // setting for the CE is passed to C side
434  }
435  fInitLookUp=kFALSE;
436 }
437 void AliTPCBoundaryVoltError::SetBoundariesC(Float_t boundariesC[6]){
448 
449  for (Int_t i=0; i<6; i++) {
450  fBoundariesC[i]= boundariesC[i];
451  }
452  fInitLookUp=kFALSE;
453 }
static AliTPCcalibDB * Instance()
printf("Chi2/npoints = %f\n", TMath::Sqrt(chi2/npoints))
Double_t fgkZList[kNZ]
points in the z direction (for the lookup table)
Bool_t fInitLookUp
flag to check it the Look Up table was created
Float_t fC1
coefficient C1 (compare Jim Thomas&#39;s notes for definitions)
Manager and of geomety classes for set: TPC.
Definition: AliTPCParam.h:18
Float_t GetDriftV() const
Definition: AliTPCParam.h:342
static const Double_t fgkOFCRadius
Mean Radius of the Outer Field Cage (252.55 min, 256.45 max) (cm)
AliTPCParam * GetParameters() const
Double_t SolenoidField() const
Definition: AliMagF.h:67
#define AliWarning(message)
Definition: AliLog.h:541
Double_t fT1
tensor term of wt - T1
static const Double_t fgkZOffSet
Offset from CE: calculate all distortions closer to CE as if at this point.
void PoissonRelaxation2D(TMatrixD &arrayV, TMatrixD &chargeDensity, TMatrixD &arrayErOverEz, TMatrixD &arrayDeltaEz, Int_t rows, Int_t columns, Int_t iterations, Bool_t rocDisplacement=kTRUE)
AliTPCCorrection class.
Float_t fC0
coefficient C0 (compare Jim Thomas&#39;s notes for definitions)
void SetBoundariesA(Float_t boundariesA[8])
Bool_t fROCdisplacement
flag for ROC displacement (important for z distortions)
static const Double_t fgkTPCZ0
nominal gating grid position
#define AliInfo(message)
Definition: AliLog.h:484
Float_t fBoundariesC[8]
Boundary values on the C side (see Setter function)
virtual void SetOmegaTauT1T2(Float_t omegaTau, Float_t t1, Float_t t2)
virtual Bool_t AddCorrectionCompact(AliTPCCorrection *corr, Double_t weight)
virtual void GetCorrection(const Float_t x[], const Short_t roc, Float_t dx[])
void Search(Int_t n, const Double_t xArray[], Double_t x, Int_t &low)
virtual void Update(const TTimeStamp &timeStamp)
void SetBoundariesC(Float_t boundariesC[6])
virtual void Print(const Option_t *option="") const
Double_t fLookUpErOverEz[kNZ][kNR]
Array to store electric field integral (int Er/Ez)
void Interpolate2DEdistortion(Int_t order, Double_t r, Double_t z, const Double_t er[kNZ][kNR], Double_t &erValue)
Double_t fLookUpDeltaEz[kNZ][kNR]
Array to store electric field integral (int Delta Ez)
Double_t fT2
tensor term of wt - T2
Double_t vdrift
Definition: DriftKalman.C:98
Double_t fgkRList[kNR]
points in the radial direction (for the lookup table)
#define AliError(message)
Definition: AliLog.h:591
static const Double_t fgkIFCRadius
Mean Radius of the Inner Field Cage ( 82.43 min, 83.70 max) (cm)/hera/alice/wiechula/calib/guiTrees.
Float_t fBoundariesA[8]
Boundary values on the A side (see Setter function)