]> git.uio.no Git - u/mrichter/AliRoot.git/blob - STEER/AliMagF.cxx
ESD can init the Bfield via AliESDRun::InitMagneticField()
[u/mrichter/AliRoot.git] / STEER / AliMagF.cxx
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
16
17 #include <TClass.h>
18 #include <TFile.h>
19 #include <TSystem.h>
20 #include <TPRegexp.h>
21
22 #include "AliMagF.h"
23 #include "AliMagWrapCheb.h"
24 #include "AliLog.h"
25
26 ClassImp(AliMagF)
27
28 const Double_t AliMagF::fgkSol2DipZ    =  -700.;  
29 const UShort_t AliMagF::fgkPolarityConvention = kConvLHC;
30
31 /*
32  Explanation for polarity conventions: these are the mapping between the
33  current signs and main field components in L3 (Bz) and Dipole (Bx) (in Alice frame)
34  1) kConvMap2005: used for the field mapping in 2005
35  positive L3  current -> negative Bz
36  positive Dip current -> positive Bx 
37  2) kConvMapDCS2008: defined by the microswitches/cabling of power converters as of 2008 - 1st half 2009
38  positive L3  current -> positive Bz
39  positive Dip current -> positive Bx
40  3) kConvLHC : defined by LHC
41  positive L3  current -> negative Bz
42  positive Dip current -> negative Bx
43  
44  Note: only "negative Bz(L3) with postive Bx(Dipole)" and its inverse was mapped in 2005. Hence 
45  the GRP Manager will reject the runs with the current combinations (in the convention defined by the
46  static Int_t AliMagF::GetPolarityConvention()) which do not lead to such field polarities.
47 */
48 //_______________________________________________________________________
49 AliMagF::AliMagF():
50   TVirtualMagField(),
51   fMeasuredMap(0),
52   fMapType(k5kG),
53   fSolenoid(0),
54   fBeamType(kNoBeamField),
55   fBeamEnergy(0),
56   //
57   fInteg(0),
58   fPrecInteg(0),
59   fFactorSol(1.),
60   fFactorDip(1.),
61   fMax(15),
62   fDipoleOFF(kFALSE),
63   //
64   fQuadGradient(0),
65   fDipoleField(0),
66   fCCorrField(0), 
67   fACorr1Field(0),
68   fACorr2Field(0),
69   fParNames("","")
70 {
71   // Default constructor
72   //
73 }
74
75 //_______________________________________________________________________
76 AliMagF::AliMagF(const char *name, const char* title, Int_t integ, 
77                  Double_t factorSol, Double_t factorDip, 
78                  Double_t fmax, BMap_t maptype, const char* path,
79                  BeamType_t bt, Double_t be):
80   TVirtualMagField(name),
81   fMeasuredMap(0),
82   fMapType(maptype),
83   fSolenoid(0),
84   fBeamType(bt),
85   fBeamEnergy(be),
86   //
87   fInteg(integ),
88   fPrecInteg(1),
89   fFactorSol(1.),
90   fFactorDip(1.),
91   fMax(fmax),
92   fDipoleOFF(factorDip==0.),
93   //
94   fQuadGradient(0),
95   fDipoleField(0),
96   fCCorrField(0), 
97   fACorr1Field(0),
98   fACorr2Field(0),
99   fParNames("","")
100 {
101   // Initialize the field with Geant integration option "integ" and max field "fmax,
102   // Impose scaling of parameterized L3 field by factorSol and of dipole by factorDip.
103   // The "be" is the energy of the beam in GeV/nucleon
104   //
105   SetTitle(title);
106   if(integ<0 || integ > 2) {
107     AliWarning(Form("Invalid magnetic field flag: %5d; Helix tracking chosen instead",integ));
108     fInteg = 2;
109   }
110   if (fInteg == 0) fPrecInteg = 0;
111   //
112   const char* parname = 0;
113   //  
114   if      (fMapType == k2kG) parname = fDipoleOFF ? "Sol12_Dip0_Hole":"Sol12_Dip6_Hole";
115   else if (fMapType == k5kG) parname = fDipoleOFF ? "Sol30_Dip0_Hole":"Sol30_Dip6_Hole";
116   else if (fMapType == k5kGUniform) parname = "Sol30_Dip6_Uniform";
117   else AliFatal(Form("Unknown field identifier %d is requested\n",fMapType));
118   //
119   SetDataFileName(path);
120   SetParamName(parname);
121   //
122   LoadParameterization();
123   InitMachineField(fBeamType,fBeamEnergy);
124   double xyz[3]={0.,0.,0.};
125   fSolenoid = GetBz(xyz);
126   SetFactorSol(factorSol);
127   SetFactorDip(factorDip);
128   AliInfo(Form("Alice   B fields: Solenoid (%+.2f*)%.0f kG, Dipole %s (%+.2f) %s",
129                factorSol,(fMapType==k5kG||fMapType==k5kGUniform)?5.:2.,
130                fDipoleOFF ? "OFF":"ON",factorDip,fMapType==k5kGUniform?" |Constant Field!":""));
131   AliInfo(Form("Machine B fields for %s beam (%.0f GeV): QGrad: %.4f Dipole: %.4f",
132                bt==kBeamTypeAA ? "A-A":(bt==kBeamTypepp ? "p-p":"OFF"),be,fQuadGradient,fDipoleField));
133 }
134
135 //_______________________________________________________________________
136 AliMagF::AliMagF(const AliMagF &src):
137   TVirtualMagField(src),
138   fMeasuredMap(0),
139   fMapType(src.fMapType),
140   fSolenoid(src.fSolenoid),
141   fBeamType(src.fBeamType),
142   fBeamEnergy(src.fBeamEnergy),
143   fInteg(src.fInteg),
144   fPrecInteg(src.fPrecInteg),
145   fFactorSol(src.fFactorSol),
146   fFactorDip(src.fFactorDip),
147   fMax(src.fMax),
148   fDipoleOFF(src.fDipoleOFF),
149   fQuadGradient(src.fQuadGradient),
150   fDipoleField(src.fDipoleField),
151   fCCorrField(src.fCCorrField), 
152   fACorr1Field(src.fACorr1Field),
153   fACorr2Field(src.fACorr2Field),
154   fParNames(src.fParNames)
155 {
156   if (src.fMeasuredMap) fMeasuredMap = new AliMagWrapCheb(*src.fMeasuredMap);
157 }
158
159 //_______________________________________________________________________
160 AliMagF::~AliMagF()
161 {
162   delete fMeasuredMap;
163 }
164
165 //_______________________________________________________________________
166 Bool_t AliMagF::LoadParameterization()
167 {
168   if (fMeasuredMap) {
169     AliError(Form("Field data %s are already loaded from %s\n",GetParamName(),GetDataFileName()));
170     return kTRUE;
171   }
172   //
173   char* fname = gSystem->ExpandPathName(GetDataFileName());
174   TFile* file = TFile::Open(fname);
175   if (!file) {
176     AliError(Form("Failed to open magnetic field data file %s\n",fname)); 
177     return kFALSE;
178   }
179   //
180   fMeasuredMap = dynamic_cast<AliMagWrapCheb*>(file->Get(GetParamName()));
181   if (!fMeasuredMap) {
182     AliError(Form("Did not find field %s in %s\n",GetParamName(),fname)); 
183     return kFALSE;
184   }
185   file->Close();
186   delete file;
187   return kTRUE;
188 }
189
190
191 //_______________________________________________________________________
192 void AliMagF::Field(const Double_t *xyz, Double_t *b)
193 {
194   // Method to calculate the field at point  xyz
195   //
196   //  b[0]=b[1]=b[2]=0.0;
197   if (fMeasuredMap && xyz[2]>fMeasuredMap->GetMinZ() && xyz[2]<fMeasuredMap->GetMaxZ()) {
198     fMeasuredMap->Field(xyz,b);
199     if (xyz[2]>fgkSol2DipZ || fDipoleOFF) for (int i=3;i--;) b[i] *= fFactorSol;
200     else                                  for (int i=3;i--;) b[i] *= fFactorDip;    
201   }
202   else MachineField(xyz, b);
203   //
204 }
205
206 //_______________________________________________________________________
207 Double_t AliMagF::GetBz(const Double_t *xyz) const
208 {
209   // Method to calculate the field at point  xyz
210   //
211   if (fMeasuredMap && xyz[2]>fMeasuredMap->GetMinZ() && xyz[2]<fMeasuredMap->GetMaxZ()) {
212     double bz = fMeasuredMap->GetBz(xyz);
213     return (xyz[2]>fgkSol2DipZ || fDipoleOFF) ? bz*fFactorSol : bz*fFactorDip;    
214   }
215   else return 0.;
216 }
217
218 //_______________________________________________________________________
219 AliMagF& AliMagF::operator=(const AliMagF& src)
220 {
221   if (this != &src && src.fMeasuredMap) { 
222     if (fMeasuredMap) delete fMeasuredMap;
223     fMeasuredMap = new AliMagWrapCheb(*src.fMeasuredMap);
224     SetName(src.GetName());
225     fSolenoid    = src.fSolenoid;
226     fBeamType    = src.fBeamType;
227     fBeamEnergy  = src.fBeamEnergy;
228     fInteg       = src.fInteg;
229     fPrecInteg   = src.fPrecInteg;
230     fFactorSol   = src.fFactorSol;
231     fFactorDip   = src.fFactorDip;
232     fMax         = src.fMax;
233     fDipoleOFF   = src.fDipoleOFF;
234     fParNames    = src.fParNames;
235   }
236   return *this;
237 }
238
239 //_______________________________________________________________________
240 void AliMagF::InitMachineField(BeamType_t btype, Double_t benergy)
241 {
242   if (btype==kNoBeamField || benergy<1.) {
243     fQuadGradient = fDipoleField = fCCorrField = fACorr1Field = fACorr2Field = 0.;
244     return;
245   }
246   //
247   double rigScale = benergy/7000.;   // scale according to ratio of E/Enominal
248   // for ions assume PbPb (with energy provided per nucleon) and account for A/Z
249   if (btype == kBeamTypeAA) rigScale *= 208./82.;
250   //
251   fQuadGradient = 22.0002*rigScale;
252   fDipoleField  = 37.8781*rigScale;
253   //
254   // SIDE C
255   fCCorrField   = -9.6980;
256   // SIDE A
257   fACorr1Field  = -13.2247;
258   fACorr2Field  =  11.7905;
259   //
260 }
261
262 //_______________________________________________________________________
263 void AliMagF::MachineField(const Double_t *x, Double_t *b) const
264 {
265   // ---- This is the ZDC part
266   // Compansators for Alice Muon Arm Dipole
267   const Double_t kBComp1CZ = 1075., kBComp1hDZ = 260./2., kBComp1SqR = 4.0*4.0; 
268   const Double_t kBComp2CZ = 2049., kBComp2hDZ = 153./2., kBComp2SqR = 4.5*4.5; 
269   //  
270   const Double_t kTripQ1CZ = 2615., kTripQ1hDZ = 637./2., kTripQ1SqR = 3.5*3.5;
271   const Double_t kTripQ2CZ = 3480., kTripQ2hDZ = 550./2., kTripQ2SqR = 3.5*3.5;
272   const Double_t kTripQ3CZ = 4130., kTripQ3hDZ = 550./2., kTripQ3SqR = 3.5*3.5;
273   const Double_t kTripQ4CZ = 5015., kTripQ4hDZ = 637./2., kTripQ4SqR = 3.5*3.5;
274   //
275   const Double_t kDip1CZ = 6310.8,  kDip1hDZ = 945./2., kDip1SqRC = 4.5*4.5, kDip1SqRA = 3.375*3.375;
276   const Double_t kDip2CZ = 12640.3, kDip2hDZ = 945./2., kDip2SqRC = 4.5*4.5, kDip2SqRA = 3.75*3.75;
277   const Double_t kDip2DXC = 9.7, kDip2DXA = 9.4;
278   //
279   double rad2 = x[0] * x[0] + x[1] * x[1];
280   //
281   b[0] = b[1] = b[2] = 0;
282   //
283   // SIDE C **************************************************
284   if(x[2]<0.){  
285     if(TMath::Abs(x[2]+kBComp2CZ)<kBComp2hDZ && rad2 < kBComp2SqR){
286       b[0] = fCCorrField*fFactorDip;
287     } 
288     else if(TMath::Abs(x[2]+kTripQ1CZ)<kTripQ1hDZ && rad2 < kTripQ1SqR){
289       b[0] = fQuadGradient*x[1];
290       b[1] = fQuadGradient*x[0];
291     }
292     else if(TMath::Abs(x[2]+kTripQ2CZ)<kTripQ2hDZ && rad2 < kTripQ2SqR){
293       b[0] = -fQuadGradient*x[1];
294       b[1] = -fQuadGradient*x[0];
295     }
296     else if(TMath::Abs(x[2]+kTripQ3CZ)<kTripQ3hDZ && rad2 < kTripQ3SqR){
297       b[0] = -fQuadGradient*x[1];
298       b[1] = -fQuadGradient*x[0];
299     }
300     else if(TMath::Abs(x[2]+kTripQ4CZ)<kTripQ4hDZ && rad2 < kTripQ4SqR){
301       b[0] = fQuadGradient*x[1];
302       b[1] = fQuadGradient*x[0];
303     }
304     else if(TMath::Abs(x[2]+kDip1CZ)<kDip1hDZ && rad2 < kDip1SqRC){
305       b[1] = fDipoleField;
306     }
307     else if(TMath::Abs(x[2]+kDip2CZ)<kDip2hDZ && rad2 < kDip2SqRC) {
308       double dxabs = TMath::Abs(x[0])-kDip2DXC;
309       if ( (dxabs*dxabs + x[1]*x[1])<kDip2SqRC) {
310         b[1] = -fDipoleField;
311       }
312     }
313   }
314   //
315   // SIDE A **************************************************
316   else{        
317     if(TMath::Abs(x[2]-kBComp1CZ)<kBComp1hDZ && rad2 < kBComp1SqR) {
318       // Compensator magnet at z = 1075 m 
319       b[0] = fACorr1Field*fFactorDip;
320     }
321     //
322     if(TMath::Abs(x[2]-kBComp2CZ)<kBComp2hDZ && rad2 < kBComp2SqR){
323       b[0] = fACorr2Field*fFactorDip;
324     }
325     else if(TMath::Abs(x[2]-kTripQ1CZ)<kTripQ1hDZ && rad2 < kTripQ1SqR){
326       b[0] = -fQuadGradient*x[1];
327       b[1] = -fQuadGradient*x[0];
328     }
329     else if(TMath::Abs(x[2]-kTripQ2CZ)<kTripQ2hDZ && rad2 < kTripQ2SqR){
330       b[0] =  fQuadGradient*x[1];
331       b[1] =  fQuadGradient*x[0];
332     }
333     else if(TMath::Abs(x[2]-kTripQ3CZ)<kTripQ3hDZ && rad2 < kTripQ3SqR){
334       b[0] =  fQuadGradient*x[1];
335       b[1] =  fQuadGradient*x[0];
336     }
337     else if(TMath::Abs(x[2]-kTripQ4CZ)<kTripQ4hDZ && rad2 < kTripQ4SqR){
338       b[0] = -fQuadGradient*x[1];
339       b[1] = -fQuadGradient*x[0];
340     }
341     else if(TMath::Abs(x[2]-kDip1CZ)<kDip1hDZ && rad2 < kDip1SqRA){
342       b[1] = -fDipoleField;
343     }
344     else if(TMath::Abs(x[2]-kDip2CZ)<kDip2hDZ && rad2 < kDip2SqRA) {
345       double dxabs = TMath::Abs(x[0])-kDip2DXA;
346       if ( (dxabs*dxabs + x[1]*x[1])<kDip2SqRA) {
347         b[1] = fDipoleField;
348       }
349     }
350   }
351   //
352 }
353
354 //_______________________________________________________________________
355 void AliMagF::GetTPCInt(const Double_t *xyz, Double_t *b) const
356 {
357   // Method to calculate the integral of magnetic integral from xyz to nearest cathode plane
358   b[0]=b[1]=b[2]=0.0;
359   if (fMeasuredMap) {
360     fMeasuredMap->GetTPCInt(xyz,b);
361     for (int i=3;i--;) b[i] *= fFactorSol;
362   }
363 }
364
365 //_______________________________________________________________________
366 void AliMagF::GetTPCIntCyl(const Double_t *rphiz, Double_t *b) const
367 {
368   // Method to calculate the integral of magnetic integral from point to nearest cathode plane
369   // in cylindrical coordiates ( -pi<phi<pi convention )
370   b[0]=b[1]=b[2]=0.0;
371   if (fMeasuredMap) {
372     fMeasuredMap->GetTPCIntCyl(rphiz,b);
373     for (int i=3;i--;) b[i] *= fFactorSol;
374   }
375 }
376
377 //_______________________________________________________________________
378 void AliMagF::SetFactorSol(Float_t fc)
379 {
380   // set the sign/scale of the current in the L3 according to fgkPolarityConvention
381   switch (fgkPolarityConvention) {
382   case kConvDCS2008: fFactorSol = -fc; break;
383   case kConvLHC    : fFactorSol = -fc; break;
384   default          : fFactorSol =  fc; break;  // case kConvMap2005: fFactorSol =  fc; break;
385   }
386 }
387
388 //_______________________________________________________________________
389 void AliMagF::SetFactorDip(Float_t fc)
390 {
391   // set the sign*scale of the current in the Dipole according to fgkPolarityConvention
392   switch (fgkPolarityConvention) {
393   case kConvDCS2008: fFactorDip =  fc; break;
394   case kConvLHC    : fFactorDip = -fc; break;
395   default          : fFactorDip =  fc; break;  // case kConvMap2005: fFactorDip =  fc; break;
396   }
397 }
398
399 //_______________________________________________________________________
400 Double_t AliMagF::GetFactorSol() const
401 {
402   // return the sign*scale of the current in the Dipole according to fgkPolarityConventionthe 
403   switch (fgkPolarityConvention) {
404   case kConvDCS2008: return -fFactorSol;
405   case kConvLHC    : return -fFactorSol;
406   default          : return  fFactorSol;       //  case kConvMap2005: return  fFactorSol;
407   }
408 }
409
410 //_______________________________________________________________________
411 Double_t AliMagF::GetFactorDip() const
412 {
413   // return the sign*scale of the current in the Dipole according to fgkPolarityConventionthe 
414   switch (fgkPolarityConvention) {
415   case kConvDCS2008: return  fFactorDip;
416   case kConvLHC    : return -fFactorDip;
417   default          : return  fFactorDip;       //  case kConvMap2005: return  fFactorDip;
418   }
419 }
420
421 //_____________________________________________________________________________
422 AliMagF* AliMagF::CreateFieldMap(Float_t l3Cur, Float_t diCur, Int_t convention, Bool_t uniform,
423                                  Float_t beamenergy, const Char_t *beamtype, const Char_t *path) 
424 {
425   //------------------------------------------------
426   // The magnetic field map, defined externally...
427   // L3 current 30000 A  -> 0.5 T
428   // L3 current 12000 A  -> 0.2 T
429   // dipole current 6000 A
430   // The polarities must match the convention (LHC or DCS2008) 
431   // unless the special uniform map was used for MC
432   //------------------------------------------------
433   const Float_t l3NominalCurrent1=30000.; // (A)
434   const Float_t l3NominalCurrent2=12000.; // (A)
435   const Float_t diNominalCurrent =6000. ; // (A)
436
437   const Float_t tolerance=0.03; // relative current tolerance
438   const Float_t zero=77.;       // "zero" current (A)
439   //
440   BMap_t map;
441   double sclL3,sclDip;
442   //
443   Float_t l3Pol = l3Cur > 0 ? 1:-1;
444   Float_t diPol = diCur > 0 ? 1:-1;
445  
446   l3Cur = TMath::Abs(l3Cur);
447   diCur = TMath::Abs(diCur);
448   //
449   if (TMath::Abs((sclDip=diCur/diNominalCurrent)-1.) > tolerance && !uniform) {
450     if (diCur <= zero) sclDip = 0.; // some small current.. -> Dipole OFF
451     else {
452       AliErrorGeneral("AliMagF",Form("Wrong dipole current (%f A)!",diCur));
453       return 0;
454     }
455   }
456   //
457   if (uniform) { 
458     // special treatment of special MC with uniform mag field (normalized to 0.5 T)
459     // no check for scaling/polarities are done
460     map   = k5kGUniform;
461     sclL3 = l3Cur/l3NominalCurrent1; 
462   }
463   else {
464     if      (TMath::Abs((sclL3=l3Cur/l3NominalCurrent1)-1.) < tolerance) map  = k5kG;
465     else if (TMath::Abs((sclL3=l3Cur/l3NominalCurrent2)-1.) < tolerance) map  = k2kG;
466     else if (l3Cur <= zero)                                { sclL3 = 0;  map  = k5kGUniform;}
467     else {
468       AliErrorGeneral("AliMagF",Form("Wrong L3 current (%f A)!",l3Cur));
469       return 0;
470     }
471   }
472   //
473   if (sclDip!=0 && (map==k5kG || map==k2kG) &&
474       ((convention==kConvLHC     && l3Pol!=diPol) ||
475        (convention==kConvDCS2008 && l3Pol==diPol)) ) { 
476     AliErrorGeneral("AliMagF",Form("Wrong combination for L3/Dipole polarities (%c/%c) for convention %d",
477                                    l3Pol>0?'+':'-',diPol>0?'+':'-',GetPolarityConvention()));
478     return 0;
479   }
480   //
481   if (l3Pol<0) sclL3  = -sclL3;
482   if (diPol<0) sclDip = -sclDip;
483   //
484   BeamType_t btype = kNoBeamField;
485   TString btypestr = beamtype;
486   btypestr.ToLower();
487   TPRegexp protonBeam("(proton|p)\\s*-?\\s*\\1");
488   TPRegexp ionBeam("(lead|pb|ion|a)\\s*-?\\s*\\1");
489   if (btypestr.Contains(ionBeam)) btype = kBeamTypeAA;
490   else if (btypestr.Contains(protonBeam)) btype = kBeamTypepp;
491   else AliInfoGeneral("AliMagF",Form("Assume no LHC magnet field for the beam type %s, ",beamtype));
492   char ttl[80];
493   sprintf(ttl,"L3: %+5d Dip: %+4d kA; %s | Polarities in %s convention",(int)TMath::Sign(l3Cur,float(sclL3)),
494           (int)TMath::Sign(diCur,float(sclDip)),uniform ? " Constant":"",
495           convention==kConvLHC ? "LHC":"DCS2008");
496   // LHC and DCS08 conventions have opposite dipole polarities
497   if ( GetPolarityConvention() != convention) sclDip = -sclDip;
498   //
499   return new AliMagF("MagneticFieldMap", ttl, 2, sclL3, sclDip, 10., map, path,btype,beamenergy);
500   //
501 }
502
503 //_____________________________________________________________________________
504 const char*  AliMagF::GetBeamTypeText() const
505 {
506   const char *beamNA  = "No Beam";
507   const char *beamPP  = "p-p";
508   const char *beamPbPb= "Pb-Pb";
509   switch ( fBeamType ) {
510   case kBeamTypepp : return beamPP;
511   case kBeamTypeAA : return beamPbPb;
512   case kNoBeamField: 
513   default:           return beamNA;
514   }
515 }
516