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 **************************************************************************/
23 #include "AliMagWrapCheb.h"
28 const Double_t AliMagF::fgkSol2DipZ = -700.;
29 const UShort_t AliMagF::fgkPolarityConvention = kConvLHC;
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
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.
48 //_______________________________________________________________________
54 fBeamType(kNoBeamField),
71 // Default constructor
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),
92 fDipoleOFF(factorDip==0.),
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
106 if(integ<0 || integ > 2) {
107 AliWarning(Form("Invalid magnetic field flag: %5d; Helix tracking chosen instead",integ));
110 if (fInteg == 0) fPrecInteg = 0;
112 const char* parname = 0;
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));
119 SetDataFileName(path);
120 SetParamName(parname);
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));
135 //_______________________________________________________________________
136 AliMagF::AliMagF(const AliMagF &src):
137 TVirtualMagField(src),
139 fMapType(src.fMapType),
140 fSolenoid(src.fSolenoid),
141 fBeamType(src.fBeamType),
142 fBeamEnergy(src.fBeamEnergy),
144 fPrecInteg(src.fPrecInteg),
145 fFactorSol(src.fFactorSol),
146 fFactorDip(src.fFactorDip),
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)
156 if (src.fMeasuredMap) fMeasuredMap = new AliMagWrapCheb(*src.fMeasuredMap);
159 //_______________________________________________________________________
165 //_______________________________________________________________________
166 Bool_t AliMagF::LoadParameterization()
169 AliError(Form("Field data %s are already loaded from %s\n",GetParamName(),GetDataFileName()));
173 char* fname = gSystem->ExpandPathName(GetDataFileName());
174 TFile* file = TFile::Open(fname);
176 AliError(Form("Failed to open magnetic field data file %s\n",fname));
180 fMeasuredMap = dynamic_cast<AliMagWrapCheb*>(file->Get(GetParamName()));
182 AliError(Form("Did not find field %s in %s\n",GetParamName(),fname));
191 //_______________________________________________________________________
192 void AliMagF::Field(const Double_t *xyz, Double_t *b)
194 // Method to calculate the field at point xyz
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;
202 else MachineField(xyz, b);
206 //_______________________________________________________________________
207 Double_t AliMagF::GetBz(const Double_t *xyz) const
209 // Method to calculate the field at point xyz
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;
218 //_______________________________________________________________________
219 AliMagF& AliMagF::operator=(const AliMagF& src)
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;
229 fPrecInteg = src.fPrecInteg;
230 fFactorSol = src.fFactorSol;
231 fFactorDip = src.fFactorDip;
233 fDipoleOFF = src.fDipoleOFF;
234 fParNames = src.fParNames;
239 //_______________________________________________________________________
240 void AliMagF::InitMachineField(BeamType_t btype, Double_t benergy)
242 if (btype==kNoBeamField || benergy<1.) {
243 fQuadGradient = fDipoleField = fCCorrField = fACorr1Field = fACorr2Field = 0.;
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.;
251 fQuadGradient = 22.0002*rigScale;
252 fDipoleField = 37.8781*rigScale;
255 fCCorrField = -9.6980;
257 fACorr1Field = -13.2247;
258 fACorr2Field = 11.7905;
262 //_______________________________________________________________________
263 void AliMagF::MachineField(const Double_t *x, Double_t *b) const
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;
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;
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;
279 double rad2 = x[0] * x[0] + x[1] * x[1];
281 b[0] = b[1] = b[2] = 0;
283 // SIDE C **************************************************
285 if(TMath::Abs(x[2]+kBComp2CZ)<kBComp2hDZ && rad2 < kBComp2SqR){
286 b[0] = fCCorrField*fFactorDip;
288 else if(TMath::Abs(x[2]+kTripQ1CZ)<kTripQ1hDZ && rad2 < kTripQ1SqR){
289 b[0] = fQuadGradient*x[1];
290 b[1] = fQuadGradient*x[0];
292 else if(TMath::Abs(x[2]+kTripQ2CZ)<kTripQ2hDZ && rad2 < kTripQ2SqR){
293 b[0] = -fQuadGradient*x[1];
294 b[1] = -fQuadGradient*x[0];
296 else if(TMath::Abs(x[2]+kTripQ3CZ)<kTripQ3hDZ && rad2 < kTripQ3SqR){
297 b[0] = -fQuadGradient*x[1];
298 b[1] = -fQuadGradient*x[0];
300 else if(TMath::Abs(x[2]+kTripQ4CZ)<kTripQ4hDZ && rad2 < kTripQ4SqR){
301 b[0] = fQuadGradient*x[1];
302 b[1] = fQuadGradient*x[0];
304 else if(TMath::Abs(x[2]+kDip1CZ)<kDip1hDZ && rad2 < kDip1SqRC){
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;
315 // SIDE A **************************************************
317 if(TMath::Abs(x[2]-kBComp1CZ)<kBComp1hDZ && rad2 < kBComp1SqR) {
318 // Compensator magnet at z = 1075 m
319 b[0] = fACorr1Field*fFactorDip;
322 if(TMath::Abs(x[2]-kBComp2CZ)<kBComp2hDZ && rad2 < kBComp2SqR){
323 b[0] = fACorr2Field*fFactorDip;
325 else if(TMath::Abs(x[2]-kTripQ1CZ)<kTripQ1hDZ && rad2 < kTripQ1SqR){
326 b[0] = -fQuadGradient*x[1];
327 b[1] = -fQuadGradient*x[0];
329 else if(TMath::Abs(x[2]-kTripQ2CZ)<kTripQ2hDZ && rad2 < kTripQ2SqR){
330 b[0] = fQuadGradient*x[1];
331 b[1] = fQuadGradient*x[0];
333 else if(TMath::Abs(x[2]-kTripQ3CZ)<kTripQ3hDZ && rad2 < kTripQ3SqR){
334 b[0] = fQuadGradient*x[1];
335 b[1] = fQuadGradient*x[0];
337 else if(TMath::Abs(x[2]-kTripQ4CZ)<kTripQ4hDZ && rad2 < kTripQ4SqR){
338 b[0] = -fQuadGradient*x[1];
339 b[1] = -fQuadGradient*x[0];
341 else if(TMath::Abs(x[2]-kDip1CZ)<kDip1hDZ && rad2 < kDip1SqRA){
342 b[1] = -fDipoleField;
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) {
354 //_______________________________________________________________________
355 void AliMagF::GetTPCInt(const Double_t *xyz, Double_t *b) const
357 // Method to calculate the integral of magnetic integral from xyz to nearest cathode plane
360 fMeasuredMap->GetTPCInt(xyz,b);
361 for (int i=3;i--;) b[i] *= fFactorSol;
365 //_______________________________________________________________________
366 void AliMagF::GetTPCIntCyl(const Double_t *rphiz, Double_t *b) const
368 // Method to calculate the integral of magnetic integral from point to nearest cathode plane
369 // in cylindrical coordiates ( -pi<phi<pi convention )
372 fMeasuredMap->GetTPCIntCyl(rphiz,b);
373 for (int i=3;i--;) b[i] *= fFactorSol;
377 //_______________________________________________________________________
378 void AliMagF::SetFactorSol(Float_t fc)
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;
388 //_______________________________________________________________________
389 void AliMagF::SetFactorDip(Float_t fc)
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;
399 //_______________________________________________________________________
400 Double_t AliMagF::GetFactorSol() const
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;
410 //_______________________________________________________________________
411 Double_t AliMagF::GetFactorDip() const
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;
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)
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)
437 const Float_t tolerance=0.03; // relative current tolerance
438 const Float_t zero=77.; // "zero" current (A)
443 Float_t l3Pol = l3Cur > 0 ? 1:-1;
444 Float_t diPol = diCur > 0 ? 1:-1;
446 l3Cur = TMath::Abs(l3Cur);
447 diCur = TMath::Abs(diCur);
449 if (TMath::Abs((sclDip=diCur/diNominalCurrent)-1.) > tolerance && !uniform) {
450 if (diCur <= zero) sclDip = 0.; // some small current.. -> Dipole OFF
452 AliErrorGeneral("AliMagF",Form("Wrong dipole current (%f A)!",diCur));
458 // special treatment of special MC with uniform mag field (normalized to 0.5 T)
459 // no check for scaling/polarities are done
461 sclL3 = l3Cur/l3NominalCurrent1;
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;}
468 AliErrorGeneral("AliMagF",Form("Wrong L3 current (%f A)!",l3Cur));
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()));
481 if (l3Pol<0) sclL3 = -sclL3;
482 if (diPol<0) sclDip = -sclDip;
484 BeamType_t btype = kNoBeamField;
485 TString btypestr = beamtype;
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));
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;
499 return new AliMagF("MagneticFieldMap", ttl, 2, sclL3, sclDip, 10., map, path,btype,beamenergy);
503 //_____________________________________________________________________________
504 const char* AliMagF::GetBeamTypeText() const
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;
513 default: return beamNA;