CbmRoot
Loading...
Searching...
No Matches
CbmFieldMapDistorted.cxx
Go to the documentation of this file.
1/* Copyright (C) 2008-2021 Justus-Liebig-Universitaet Giessen, Giessen
2 SPDX-License-Identifier: GPL-3.0-only
3 Authors: Elena Litvinenko, Elena Lebedeva [committer], Florian Uhlig */
4
5// -------------------------------------------------------------------------
6// ----- CbmFieldMapDistorted source file -----
7// ----- Created 18/01/08 by E.Litvinenko -----
8// -------------------------------------------------------------------------
10
11#include "CbmFieldConst.h" // for CbmFieldConst
12#include "CbmFieldMapSym1.h" // for CbmFieldMapSym1
13#include "CbmFieldMapSym2.h" // for CbmFieldMapSym2
14#include "CbmFieldMapSym3.h" // for CbmFieldMapSym3
15#include "CbmFieldPar.h" // for CbmFieldPar, kTypeDistorted
16
17#include <FairField.h> // for FairField
18
19#include <TFile.h> // for TFile, gFile
20#include <TFormula.h> // for TFormula
21#include <TH1.h> // for TH1D
22#include <TSystem.h> // for TSystem, gSystem, kFileExists
23
24#include <cstring> // for strlen
25#include <iostream> // for operator<<, endl, ostream, basic_ostream
26
27using std::cerr;
28using std::cout;
29using std::endl;
30
31// ------------- Default constructor ----------------------------------
46// ------------------------------------------------------------------------
47
48
49// ------------- Standard constructor (with FieldMap Parent Field ) ---------------------------------
50CbmFieldMapDistorted::CbmFieldMapDistorted(const char* mapName, const char* pfDistortionFilename,
51 const char* parentName, const char* fileTypeParent, Int_t pfTypeOfParent)
52 : CbmFieldMap()
53 , fParentField(nullptr)
54 , fTypeOfParent(pfTypeOfParent)
55 , fDistortionFilename(pfDistortionFilename)
62{
63 fName = mapName;
64 fType = kTypeDistorted;
65
66 switch (pfTypeOfParent) {
67 case 3: fParentField = new CbmFieldMapSym3(parentName, fileTypeParent); break;
68 case 2: fParentField = new CbmFieldMapSym2(parentName, fileTypeParent); break;
69 case 5: fParentField = new CbmFieldMapSym1(parentName, fileTypeParent); break;
70 default: fParentField = new CbmFieldMap(parentName, fileTypeParent); break;
71 }
72}
73// ------------------------------------------------------------------------
74
75// ------------- Constructor (with Constant Parent Field ) -------------------------
76CbmFieldMapDistorted::CbmFieldMapDistorted(Double_t xMin, Double_t xMax, Double_t yMin, Double_t yMax, Double_t zMin,
77 Double_t zMax, Double_t bX, Double_t bY, Double_t bZ, const char* mapName,
78 const char* pfDistortionFilename, const char* parentname)
79 : CbmFieldMap()
80 , fParentField(new CbmFieldConst(parentname, xMin, xMax, yMin, yMax, zMin, zMax, bX, bY, bZ))
81 , fTypeOfParent(0)
82 , fDistortionFilename(pfDistortionFilename)
89{
90 fName = mapName;
91 fType = kTypeDistorted;
92}
93
94
95// ------------- Constructor from CbmFieldPar -------------------------
97 : CbmFieldMap()
98 , fParentField(nullptr)
99 , fTypeOfParent(0)
101 , fBxDistortionFormulaMult(nullptr)
102 , fBxDistortionFormulaAdd(nullptr)
103 , fByDistortionFormulaMult(nullptr)
104 , fByDistortionFormulaAdd(nullptr)
105 , fBzDistortionFormulaMult(nullptr)
106 , fBzDistortionFormulaAdd(nullptr)
107{
108 if (fieldPar) {
109 fieldPar->MapName(fName);
110 fType = fieldPar->GetType();
111
112 if (fType == kTypeDistorted) { // normal case of distorted field map parameters as input
113
114 fTypeOfParent = fieldPar->GetTypeOfParent();
115
116 TString parentName;
117 fieldPar->GetParentName(parentName);
118
119 switch (fTypeOfParent) {
120 case 3: fParentField = new CbmFieldMapSym3(parentName.Data(), "R"); break;
121 case 2: fParentField = new CbmFieldMapSym2(parentName.Data(), "R"); break;
122 case 5: fParentField = new CbmFieldMapSym1(parentName.Data(), "R"); break;
123 case 0:
125 parentName.Data(), fieldPar->GetXmin(), fieldPar->GetXmax(), fieldPar->GetYmin(), fieldPar->GetYmax(),
126 fieldPar->GetZmin(), fieldPar->GetZmax(), fieldPar->GetBx(), fieldPar->GetBy(), fieldPar->GetBz());
127 break;
128 default: fParentField = new CbmFieldMap(parentName.Data(), "R"); break;
129 }
130
131 if ((fParentField) && (fTypeOfParent)) { // for field map parent
133 ->SetPosition(fieldPar->GetPositionX(), fieldPar->GetPositionY(), fieldPar->GetPositionZ());
134 ((CbmFieldMap*) fParentField)->SetScale(fieldPar->GetScale());
135 }
136
138 }
139 else { // try to create a distorted field map from the parameters of constant field or normal field map as input
140 switch (fType) {
141 case 3: fParentField = new CbmFieldMapSym3(fieldPar); break;
142 case 2: fParentField = new CbmFieldMapSym2(fieldPar); break;
143 case 5: fParentField = new CbmFieldMapSym1(fieldPar); break;
144 case 0: fParentField = new CbmFieldConst(fieldPar); break;
145 default: fParentField = new CbmFieldMap(fieldPar); break;
146 }
147 fType = kTypeDistorted;
149 }
150 }
151}
152// ------------------------------------------------------------------------
153
154
155// ------------ Destructor --------------------------------------------
157// ------------------------------------------------------------------------
158
159
160// ----------- Intialisation ------------------------------------------
162{
163 fParentField->Init();
164 fParentField->Print("");
167
168 // Fill values needed in the Print() function. This is needed to allow
169 // a constant Print() function.
170 Double_t po[3], b[3];
171 po[0] = 0;
172 po[1] = 0;
173 po[2] = 0;
174 GetFieldValue(po, b);
175 fBxOrigin = b[0];
176 fByOrigin = b[1];
177 fBzOrigin = b[2];
178}
179// ------------------------------------------------------------------------
180
181
182// ------------- Set from parent CbmField -------------------------
184{
185 fTypeOfParent = 0;
186 fParentField = field;
187
188 if (field) {
189 fTypeOfParent = field->GetType();
190
191 CbmFieldConst* field0 = 0;
192 CbmFieldMap* field1 = 0;
193
194 switch (fTypeOfParent) {
195
196 case 0:
197 field0 = (CbmFieldConst*) field;
198 fXmin = field0->GetXmin();
199 fXmax = field0->GetXmax();
200 fYmin = field0->GetYmin();
201 fZmax = field0->GetYmax();
202 fZmin = field0->GetZmin();
203 fZmax = field0->GetZmax();
204 break;
205
206 default:
207 field1 = (CbmFieldMap*) field;
208 fXmin = field1->GetXmin();
209 fXmax = field1->GetXmax();
210 fYmin = field1->GetYmin();
211 fZmax = field1->GetYmax();
212 fZmin = field1->GetZmin();
213 fZmax = field1->GetZmax();
214
215 fNx = field1->GetNx();
216 fNy = field1->GetNy();
217 fNz = field1->GetNz();
218
219 fXstep = field1->GetXstep();
220 fYstep = field1->GetYstep();
221 fZstep = field1->GetZstep();
222
223 fScale = field1->GetScale();
224 fPosX = field1->GetPositionX();
225 fPosY = field1->GetPositionY();
226 fPosZ = field1->GetPositionZ();
227 break;
228 }
229 }
230}
231// ------------------------------------------------------------------------
232
233// ----------- Read Distortion Formulas from Distortion File ------------------------------------------
235{
237 TFile* oldFile = gFile;
238 TDirectory* oldDir = gDirectory;
239
240 if (filename) {
241 if (strlen(filename)) {
242 fDistortionFilename = filename;
243 }
244 }
245 if (fDistortionFilename.Data()) {
246 if (strlen(fDistortionFilename.Data())) {
247 if (gSystem->AccessPathName(fDistortionFilename + ".root", kFileExists)) {
248 cerr << "CbmFieldMapDistorted::ReadDistortionInformation Warning: file " << (fDistortionFilename.Data())
249 << " not exists yet !!!" << endl;
250 }
251 else {
252 TFile* f = new TFile(fDistortionFilename + ".root");
253 if (f) {
254 fBxDistortionFormulaMult = f->Get<TFormula>("BxDistortionFormulaMult");
255 LOG_IF(fatal, !fBxDistortionFormulaMult) << "fBxDistortionFormulaMult not found in file " << f->GetName();
256 fBxDistortionFormulaAdd = f->Get<TFormula>("BxDistortionFormulaAdd");
257 LOG_IF(fatal, !fBxDistortionFormulaAdd) << "fBxDistortionFormulaAdd not found in file " << f->GetName();
258 fByDistortionFormulaMult = f->Get<TFormula>("ByDistortionFormulaMult");
259 LOG_IF(fatal, !fByDistortionFormulaMult) << "fByDistortionFormulaMult not found in file " << f->GetName();
260 fByDistortionFormulaAdd = f->Get<TFormula>("ByDistortionFormulaAdd");
261 LOG_IF(fatal, !fByDistortionFormulaAdd) << "fByDistortionFormulaAdd not found in file " << f->GetName();
262 fBzDistortionFormulaMult = f->Get<TFormula>("BzDistortionFormulaMult");
263 LOG_IF(fatal, !fBzDistortionFormulaMult) << "fBzDistortionFormulaMult not found in file " << f->GetName();
264 fBzDistortionFormulaAdd = f->Get<TFormula>("BzDistortionFormulaAdd");
265 LOG_IF(fatal, !fBzDistortionFormulaAdd) << "fBzDistortionFormulaAdd not found in file " << f->GetName();
266 f->Close();
267 }
268 else {
269 cerr << "CbmFieldMapDistorted::ReadDistortionInformation ERROR: file " << (fDistortionFilename.Data())
270 << " can not be read !!!" << endl;
271 }
272 }
273 }
274 }
276 gFile = oldFile;
277 gDirectory = oldDir;
278}
279
280// ----------- Write Distortion Formulas to Distortion File ------------------------------------------
282{
284 TFile* oldFile = gFile;
285 TDirectory* oldDir = gDirectory;
286
287 if (filename) {
288 if (strlen(filename)) {
289 fDistortionFilename = filename;
290 }
291 }
292
293 if (fDistortionFilename.Data()) {
294 if (strlen(fDistortionFilename.Data())) {
295 TFile* f = new TFile(fDistortionFilename + ".root", "RECREATE");
296 if (f) {
297
298 if (fBxDistortionFormulaMult) fBxDistortionFormulaMult->Write("BxDistortionFormulaMult");
299 if (fBxDistortionFormulaAdd) fBxDistortionFormulaAdd->Write("BxDistortionFormulaAdd");
300
301 if (fByDistortionFormulaMult) fByDistortionFormulaMult->Write("ByDistortionFormulaMult");
302 if (fByDistortionFormulaAdd) fByDistortionFormulaAdd->Write("ByDistortionFormulaAdd");
303
304 if (fBzDistortionFormulaMult) fBzDistortionFormulaMult->Write("BzDistortionFormulaMult");
305 if (fBzDistortionFormulaAdd) fBzDistortionFormulaAdd->Write("BzDistortionFormulaAdd");
306
307 f->Write();
308 f->Close();
309 }
310 }
311 }
313 gFile = oldFile;
314 gDirectory = oldDir;
315}
316
317// ---------------Getter and Setter for Distortion Formulas------------------------------
318TFormula* CbmFieldMapDistorted::GetDistortionFormula(const char* component_option, const char* action_option)
319{
320 // component_option: "x","y","z"; action_option: "m","a"
321
322 TString co = component_option;
323 TString ao = action_option;
324
325 if (co.Contains("y") && ao.Contains("m")) return fByDistortionFormulaMult;
326 if (co.Contains("y") && ao.Contains("a")) return fByDistortionFormulaAdd;
327
328 if (co.Contains("x") && ao.Contains("m")) return fBxDistortionFormulaMult;
329 if (co.Contains("x") && ao.Contains("a")) return fBxDistortionFormulaAdd;
330
331 if (co.Contains("z") && ao.Contains("m")) return fBzDistortionFormulaMult;
332 if (co.Contains("z") && ao.Contains("a")) return fBzDistortionFormulaAdd;
333
334 return 0;
335}
336
337Bool_t CbmFieldMapDistorted::SetDistortionFormula(TFormula* parDistortionFormula, const char* component_option,
338 const char* action_option)
339{
340 TString co = component_option;
341 TString ao = action_option;
342 Int_t counter = 0;
343
344 if (co.Contains("y") && ao.Contains("m")) {
345 fByDistortionFormulaMult = parDistortionFormula;
346 counter++;
347 }
348 if (co.Contains("y") && ao.Contains("a")) {
349 fByDistortionFormulaAdd = parDistortionFormula;
350 counter++;
351 }
352
353 if (co.Contains("x") && ao.Contains("m")) {
354 fBxDistortionFormulaMult = parDistortionFormula;
355 counter++;
356 }
357 if (co.Contains("x") && ao.Contains("a")) {
358 fBxDistortionFormulaAdd = parDistortionFormula;
359 counter++;
360 }
361
362 if (co.Contains("z") && ao.Contains("m")) {
363 fBzDistortionFormulaMult = parDistortionFormula;
364 counter++;
365 }
366 if (co.Contains("z") && ao.Contains("a")) {
367 fBzDistortionFormulaAdd = parDistortionFormula;
368 counter++;
369 }
370 return (counter > 0);
371}
372
373Bool_t CbmFieldMapDistorted::SetDistortionFormula(const char* parDistortionFormulaText, const char* component_option,
374 const char* action_option)
375{
376 TString co = component_option;
377 TString ao = action_option;
378 Int_t counter = 0;
379
380 if (co.Contains("y") && ao.Contains("m")) {
381 fByDistortionFormulaMult = new TFormula("ByDistortionFormulaMult", parDistortionFormulaText);
382 counter++;
383 }
384 if (co.Contains("y") && ao.Contains("a")) {
385 fByDistortionFormulaAdd = new TFormula("ByDistortionFormulaAdd", parDistortionFormulaText);
386 counter++;
387 }
388
389 if (co.Contains("x") && ao.Contains("m")) {
390 fBxDistortionFormulaMult = new TFormula("BxDistortionFormulaMult", parDistortionFormulaText);
391 counter++;
392 }
393 if (co.Contains("x") && ao.Contains("a")) {
394 fBxDistortionFormulaAdd = new TFormula("BxDistortionFormulaAdd", parDistortionFormulaText);
395 counter++;
396 }
397
398 if (co.Contains("z") && ao.Contains("m")) {
399 fBzDistortionFormulaMult = new TFormula("BzDistortionFormulaMult", parDistortionFormulaText);
400 counter++;
401 }
402 if (co.Contains("z") && ao.Contains("a")) {
403 fBzDistortionFormulaAdd = new TFormula("BzDistortionFormulaAdd", parDistortionFormulaText);
404 counter++;
405 }
406 return (counter > 0);
407}
408// ------------------------------------------------------------------------
409
410// ----------- Get x component of the field ---------------------------
411Double_t CbmFieldMapDistorted::GetBx(Double_t x, Double_t y, Double_t z)
412{
413 Double_t bx = fParentField->GetBx(x, y, z);
414
416 bx *= fBxDistortionFormulaMult->Eval(x, y, z);
417 }
418
420 bx += fBxDistortionFormulaAdd->Eval(x, y, z);
421 }
422
423 return bx;
424}
425
426// ----------- Get y component of the field ---------------------------
427Double_t CbmFieldMapDistorted::GetBy(Double_t x, Double_t y, Double_t z)
428{
429 Double_t by = fParentField->GetBy(x, y, z);
430
432 by *= fByDistortionFormulaMult->Eval(x, y, z);
433 }
435 by += fByDistortionFormulaAdd->Eval(x, y, z);
436 }
437
438 return by;
439}
440
441// ----------- Get z component of the field ---------------------------
442Double_t CbmFieldMapDistorted::GetBz(Double_t x, Double_t y, Double_t z)
443{
444 Double_t bz = fParentField->GetBz(x, y, z);
445
447 bz *= fBzDistortionFormulaMult->Eval(x, y, z);
448 }
450 bz += fBzDistortionFormulaAdd->Eval(x, y, z);
451 }
452
453 return bz;
454}
455
456
457// ------------------ Get the field value at a point ----------------------
458void CbmFieldMapDistorted::GetFieldValue(const Double_t point[3], Double_t* bField)
459{
464 fParentField->GetFieldValue(point, bField);
465 Double_t x = point[0];
466 Double_t y = point[1];
467 Double_t z = point[2];
468 if (fBxDistortionFormulaMult) bField[0] *= fBxDistortionFormulaMult->Eval(x, y, z);
469 if (fByDistortionFormulaMult) bField[1] *= fByDistortionFormulaMult->Eval(x, y, z);
470 if (fBzDistortionFormulaMult) bField[2] *= fBzDistortionFormulaMult->Eval(x, y, z);
471
472 if (fBxDistortionFormulaAdd) bField[0] += fBxDistortionFormulaAdd->Eval(x, y, z);
473 if (fByDistortionFormulaAdd) bField[1] += fByDistortionFormulaAdd->Eval(x, y, z);
474 if (fBzDistortionFormulaAdd) bField[2] += fBzDistortionFormulaAdd->Eval(x, y, z);
475}
476// ------------------------------------------------------------------------
477
478
479void CbmFieldMapDistorted::Print(Option_t*) const
480{
481 cout << "=============================================================" << endl;
482 cout << "---- " << fTitle << " : " << fName << endl;
483
484 cout << "== Parent Field: ==" << endl;
485 fParentField->Print("");
486 cout << "==============================" << endl;
487
488 cout << "== Distortion Information File : ==" << endl;
489 cout << fDistortionFilename << endl;
490 cout << "==============================" << endl;
491
492 cout << "== Bx Distortion Formula Mult : ==" << endl;
493 if (fBxDistortionFormulaMult) cout << fBxDistortionFormulaMult->GetExpFormula("p") << endl;
494 cout << "== Bx Distortion Formula Add : ==" << endl;
495 if (fBxDistortionFormulaAdd) cout << fBxDistortionFormulaAdd->GetExpFormula("p") << endl;
496 cout << "==============================" << endl;
497
498 cout << "== By Distortion Formula Mult : ==" << endl;
499 if (fByDistortionFormulaMult) cout << fByDistortionFormulaMult->GetExpFormula("p") << endl;
500 cout << "== By Distortion Formula Add : ==" << endl;
501 if (fByDistortionFormulaAdd) cout << fByDistortionFormulaAdd->GetExpFormula("p") << endl;
502 cout << "==============================" << endl;
503
504 cout << "== Bz Distortion Formula Mult : ==" << endl;
505 if (fBzDistortionFormulaMult) cout << fBzDistortionFormulaMult->GetExpFormula("p") << endl;
506 cout << "== Bz Distortion Formula Add : ==" << endl;
507 if (fBzDistortionFormulaAdd) cout << fBzDistortionFormulaAdd->GetExpFormula("p") << endl;
508 cout << "==============================" << endl;
509
510
511 cout << "---- Distorted field at origin is ( " << fBxOrigin << ", " << fByOrigin << ", " << fBzOrigin << ") kG"
512 << endl;
513
514 cout << "=============================================================" << endl;
515}
516// ------------------------------------------------------------------------
517
518
519// ----- Set the position of the field centre in global coordinates -----
520void CbmFieldMapDistorted::SetPosition(Double_t x, Double_t y, Double_t z)
521{
522 fPosX = x;
523 fPosY = y;
524 fPosZ = z;
525 if (fTypeOfParent) ((CbmFieldMap*) fParentField)->SetPosition(x, y, z);
526}
527// ------------------------------------------------------------------------
528
529// ---------------------Set a global field scaling factor------------------
531{
532 fScale = factor;
533 if (fTypeOfParent) ((CbmFieldMap*) fParentField)->SetScale(factor);
534}
535// ------------------------------------------------------------------------
536
537// ---------------------Plot distorted and parend field (By component)-----
538void CbmFieldMapDistorted::PlotBy(Int_t n, Double_t zmin, Double_t zmax)
539{
540 TH1D *h, *hp;
541 Int_t n0 = n;
542 if (!n) n0 = 1;
543 Double_t dz = (zmax - zmin) / n0;
544
545 h = new TH1D("hField", fName, n, zmin, zmax);
546 for (Int_t i = 0; i < n0; i++)
547 h->SetBinContent(i + 1, GetBy(0., 0., zmin + i * dz));
548 h->SetLineWidth(2);
549 h->Draw();
550
551 if (fParentField) {
552 hp = new TH1D("hFieldParent", fParentField->GetName(), n, zmin, zmax);
553 for (Int_t i = 0; i < n0; i++)
554 hp->SetBinContent(i + 1, fParentField->GetBy(0., 0., zmin + i * dz));
555 hp->SetLineColor(2);
556 hp->Draw("sames");
557 }
558}
559// ------------------------------------------------------------------------
const int kTypeDistorted
Definition CbmFieldPar.h:33
int Int_t
bool Bool_t
Double_t GetXmax() const
Double_t GetZmax() const
Double_t GetZmin() const
Double_t GetYmin() const
Double_t GetXmin() const
Double_t GetYmax() const
TFormula * fByDistortionFormulaMult
getter/setter options: ("x","m"), ("x","a")
void SetFromParent(FairField *field)
virtual void Print(Option_t *="") const override
TFormula * GetDistortionFormula(const char *component_option="y", const char *action_option="m")
virtual void SetScale(Double_t factor) override
virtual void GetFieldValue(const Double_t point[3], Double_t *bField) override
Get the field value at a point.
void ReadDistortionInformation(const char *filename=0)
virtual Double_t GetBy(Double_t x, Double_t y, Double_t z) override
virtual void SetPosition(Double_t x, Double_t y, Double_t z) override
void PlotBy(Int_t n=250, Double_t zmin=-50, Double_t zmax=450)
virtual void Init() override
void WriteDistortionInformation(const char *filename=0)
Bool_t SetDistortionFormula(TFormula *parDistortionFormula, const char *component_option="y", const char *action_option="m")
TFormula * fBzDistortionFormulaMult
getter/setter options: ("y","m"), ("y","a")
Double_t fPosZ
Double_t fBxOrigin
Int_t GetNy() const
Double_t GetZmax() const
Double_t fPosY
TArrayF * GetBz() const
Double_t GetXstep() const
Double_t fPosX
Double_t fZmin
Double_t fZmax
Double_t fYmin
Double_t GetYmin() const
TArrayF * GetBx() const
Double_t GetPositionY() const
Double_t fXmin
Double_t GetYmax() const
Double_t GetYstep() const
Double_t GetPositionZ() const
Double_t fYstep
Double_t fZstep
Double_t GetXmin() const
Int_t GetNz() const
Double_t GetPositionX() const
Int_t GetNx() const
Double_t fXstep
Double_t fBzOrigin
y-component of the field at the origin
Double_t GetXmax() const
Double_t GetZmin() const
Double_t GetZstep() const
Double_t fScale
Double_t fByOrigin
x-component of the field at the origin
Double_t GetScale() const
TArrayF * GetBy() const
Double_t fXmax
Double_t GetXmin() const
Definition CbmFieldPar.h:62
Double_t GetBy() const
Definition CbmFieldPar.h:69
Double_t GetXmax() const
Definition CbmFieldPar.h:63
Double_t GetPositionZ() const
Definition CbmFieldPar.h:74
Int_t GetType() const
Definition CbmFieldPar.h:61
Int_t GetTypeOfParent() const
Definition CbmFieldPar.h:79
Double_t GetBx() const
Definition CbmFieldPar.h:68
Double_t GetYmin() const
Definition CbmFieldPar.h:64
void GetParentName(TString &parentname)
Definition CbmFieldPar.h:78
Double_t GetZmax() const
Definition CbmFieldPar.h:67
void GetDistortionFilename(TString &filename)
Definition CbmFieldPar.h:77
void MapName(TString &name)
Definition CbmFieldPar.h:71
Double_t GetPositionY() const
Definition CbmFieldPar.h:73
Double_t GetZmin() const
Definition CbmFieldPar.h:66
Double_t GetBz() const
Definition CbmFieldPar.h:70
Double_t GetScale() const
Definition CbmFieldPar.h:75
Double_t GetYmax() const
Definition CbmFieldPar.h:65
Double_t GetPositionX() const
Definition CbmFieldPar.h:72
Data class with information on a STS local track.