]> git.uio.no Git - u/mrichter/AliRoot.git/blame - PWGLF/ThermalFits/AliParticleYield.cxx
Improved searching helpers in AliParticleYield
[u/mrichter/AliRoot.git] / PWGLF / ThermalFits / AliParticleYield.cxx
CommitLineData
2f86df33 1#include <iostream>
5a633e19 2#include <stdio.h>
2f86df33 3#include <fstream>
4#include <iomanip>
5#include "AliParticleYield.h"
6#include "TDatabasePDG.h"
7#include "AliLog.h"
8#include "TClonesArray.h"
9#include "TMath.h"
10#include "AliPDG.h"
11#include "TBranch.h"
12#include "TTree.h"
13#include "TDirectory.h"
14#include "TEventList.h"
8820ee3c 15#include "TCut.h"
2f86df33 16
17using std::endl;
18using std::left;
19using std::setw;
5e2539d9 20using std::ifstream;
21using std::ofstream;
2f86df33 22
23ClassImp(AliParticleYield)
24
25// set statics
26const char * AliParticleYield::kStatusString[] = {"Published", "Preliminary", "Final, but not published", "May change"} ;
27const char * AliParticleYield::kSystemString[] = {"pp", "p-Pb", "Pb-Pb"} ;
28Int_t AliParticleYield::fSignificantDigits = 3;
16163c99 29Float_t AliParticleYield::fEpsilon = 0.000000000000000001;
2f86df33 30
31AliParticleYield::AliParticleYield() :
32TObject(),
33fPdgCode(0),
34fPdgCode2(0),
35fPartName(""),
36fCollisionSystem(0),
37fSqrtS(0),
38fYield(0),
39fStatError(0),
40fSystError(0),
3e8c4dd8 41fNormErrorPos(0),
42fNormErrorNeg(0),
2f86df33 43fYMin(0),
44fYMax(0),
45fStatus(0),
46fMeasurementType(0),
47fCentr(""),
48fIsSum(0),
49fTag("")
50{
51 // constructor
52 AliPDG::AddParticlesToPdgDataBase(); // Make sure that ALICE-defined particles were added to the PDG DB
53}
54
55AliParticleYield::AliParticleYield(Int_t pdg, Int_t system, Float_t sqrts, Float_t value, Float_t stat, Float_t syst, Float_t norm, Float_t ymin, Float_t ymax, Int_t status, Int_t type, TString centr, Int_t isSum, TString tag):
56TObject(),
57fPdgCode(pdg),
58fPdgCode2(0),
59fPartName(""),
60fCollisionSystem(system),
61fSqrtS(sqrts),
62fYield(value),
63fStatError(stat),
64fSystError(syst),
3e8c4dd8 65fNormErrorPos(norm),
66fNormErrorNeg(0),
2f86df33 67fYMin(ymin),
68fYMax(ymax),
69fStatus(status),
70fMeasurementType(type),
71fCentr(centr),
72fIsSum(isSum),
73fTag(tag)
74
75{
76 // Constructor
2f86df33 77 AliPDG::AddParticlesToPdgDataBase(); // Make sure that ALICE-defined particles were added to the PDG DB
8820ee3c 78 TParticlePDG * part = TDatabasePDG::Instance()->GetParticle(fPdgCode);
79 if(!part) AliError(Form("No particle with PDG code %d in the database", fPdgCode));
80 else fPartName = part->GetName();
2f86df33 81}
82
3e8c4dd8 83AliParticleYield::AliParticleYield(Int_t pdg, Int_t system, Float_t sqrts, Float_t value, Float_t stat, Float_t syst, Float_t normPos, Float_t normNeg, Float_t ymin, Float_t ymax, Int_t status, Int_t type, TString centr, Int_t isSum, TString tag):
84TObject(),
85fPdgCode(pdg),
86fPdgCode2(0),
87fPartName(""),
88fCollisionSystem(system),
89fSqrtS(sqrts),
90fYield(value),
91fStatError(stat),
92fSystError(syst),
93fNormErrorPos(normPos),
94fNormErrorNeg(normNeg),
95fYMin(ymin),
96fYMax(ymax),
97fStatus(status),
98fMeasurementType(type),
99fCentr(centr),
100fIsSum(isSum),
101fTag(tag)
102
103{
104 // Constructor
3e8c4dd8 105 AliPDG::AddParticlesToPdgDataBase(); // Make sure that ALICE-defined particles were added to the PDG DB
8820ee3c 106 TParticlePDG * part = TDatabasePDG::Instance()->GetParticle(fPdgCode);
107 if(!part) AliError(Form("No particle with PDG code %d in the database", fPdgCode));
108 else fPartName = part->GetName();
3e8c4dd8 109}
110
111
2f86df33 112AliParticleYield::AliParticleYield(const AliParticleYield& part) :
113TObject(),
114fPdgCode(part.fPdgCode),
115fPdgCode2(part.fPdgCode2),
116fPartName(part.fPartName),
117fCollisionSystem(part.fCollisionSystem),
118fSqrtS(part.fSqrtS),
119fYield(part.fYield),
120fStatError(part.fStatError),
121fSystError(part.fSystError),
3e8c4dd8 122fNormErrorPos(part.fNormErrorPos),
123fNormErrorNeg(part.fNormErrorNeg),
2f86df33 124fYMin(part.fYMin),
125fYMax(part.fYMax),
126fStatus(part.fStatus),
127fMeasurementType(part.fMeasurementType),
128fCentr(part.fCentr),
129fIsSum(part.fIsSum),
130fTag(part.fTag){
131 // Copy constructor
132}
133
134AliParticleYield::~AliParticleYield() {
135
136}
137
589e4fb0 138TTree * AliParticleYield::GetTreeFromArray(TClonesArray * arr) {
139 // Returns a tree from an array of Tparticles
2f86df33 140 AliParticleYield * part = 0;
141 TTree * tree = new TTree ("treePart", "Particle Yields and Ratios");
142 tree->Branch("particles", &part);
143 TIter iterPart (arr);
144 while ((part = (AliParticleYield*) iterPart.Next())){
145 tree->Fill();
146 }
589e4fb0 147 if(part) delete part;
148 return tree;
149
150
151
152}
153
154
155TTree * AliParticleYield::ReadFromASCIIFileAsTree(const char * fileName, const char * separators){
156 // Read the table from an ASCII File and returns a tree of particles. See ReadFromASCIIFile for detailed info on the format
157 TClonesArray * arr = ReadFromASCIIFile(fileName, separators);
158 TTree * tree = GetTreeFromArray(arr);
2f86df33 159 delete arr;
2f86df33 160 return tree;
161}
162
589e4fb0 163TClonesArray * AliParticleYield::GetEntriesMatchingSelection(TTree * tree, TCut selection) {
2f86df33 164 // Returns an array of particles from a tree created with ReadFromASCIIFileAsTree matching the selection. You can use the normal tree sintax for the selection, e.g. "fCentr == \"V0M0010\" && fStatus == 0".
165
166 TClonesArray * arr = new TClonesArray("AliParticleYield");
167 AliParticleYield * part = 0;
168 tree->SetBranchAddress("particles", &part);
169 // In order to get the array, we first create an entry list matching the selection using TTree::Draw, and them we loop over all entries in the tree.
170 tree->Draw(">>particlelist", selection);// Produce selection list
171 TEventList *elist = (TEventList*)gDirectory->Get("particlelist");
172 Int_t npart = elist->GetN();
173 for(Int_t ipart = 0; ipart < npart; ipart++){
2f86df33 174 tree->GetEntry(elist->GetEntry(ipart));
175 new((*arr)[ipart]) AliParticleYield(*part);// We need to clone part, because it is overwritten by the next read
176 }
177 elist->Delete();
178 return arr;
179}
180
181
182TClonesArray * AliParticleYield::ReadFromASCIIFile(const char * fileName, const char * separators){
183 // Read the table from an ASCII File with the format indicated
184 // below. Returns a TClonesArray of AliParticleyields with the
185 // content of the lines. Lines beginning by "#" are skipped.
186 // The order of the columns is compulsory, but the separator can be set (by default whitespaces are assumed).
187
188 // The columns should be:
189 // PDG NAME SYSTEM SQRTS VALUE SYST STAT NORM YMIN YMAX STATUS TYPE CENTR ISSUM TAG
190
191 // PDG should either be an integher or the ratio of two integers (in case of particle ratios), with the following format:
192 // pdgcode/pdgcode2
193 // NO SPACES ARE ALLOWED IN NAMES AND PDG CODE, unless you use a separator which is not a whitespace
194
195 // A Header can be present (lines beginning with the word "PDG" are also skipped
196
197 const Int_t kNCols = 14; // The lines are actually 15, but the last one (TAG) can be empty, so we put 14 here.
198
199 TClonesArray * arr = new TClonesArray ("AliParticleYield");
200 ifstream filestream (fileName);
16163c99 201 if(!filestream.is_open()) {
202 Printf("Cannot open file %s\n", fileName);
203 exit(1);
204 }
2f86df33 205 TString line;
206 Int_t ipart = 0;
ff45f222 207 std::cout << "Reading " << fileName << std::endl;
208
589e4fb0 209 while (line.ReadLine(filestream) ) {
2f86df33 210 // Strip trailing and leading whitespaces
211 line = line.Strip(TString::kLeading, ' ');
212 line = line.Strip(TString::kTrailing, ' ');
213
214 // Skip commented lines and headers
ff45f222 215 if (line.BeginsWith("#")) {
216 //print comments. It if they look like warnings, print them such that they are really visible
217 if(line.Contains("warn", TString::kIgnoreCase)) std::cout << std::endl << "********************************************************" <<std::endl ;
218 std::cout << " " << line.Data() << std::endl;
219 if(line.Contains("warn", TString::kIgnoreCase)) std::cout << "********************************************************" <<std::endl << std::endl;
220
221 continue;
222 }
2f86df33 223 if (line.BeginsWith("PDG")) continue;
224
225 // Tokenize line using custom separator
226 TObjArray * cols = line.Tokenize(separators);
227
228 // Check the number of columns
229 if(cols->GetEntries() < kNCols) {
230 Printf("Wrong number of columns in table %d vs %d expected" , cols->GetEntries(), kNCols);
231 delete arr;
232 return NULL;
233 }
234
235 // Get Values
236 // get type first, as some operations are type-specific
237 UInt_t type = ((TObjString*)cols->At(11)) ->String().Atoi();
238
239 // if it's a ratio, try to get the 2 pdg codes
240 Int_t pdg =0, pdg2 = 0;
241
242 if (type & kTypeParticleRatio) {
243 TString col0 = ((TObjString*)cols->At(0)) ->String();
244 TObjArray * tokens = col0.Tokenize("/");
245 if(tokens->GetEntries() != 2) {
246 Printf("ERROR: Cannot get both PDGs for ratios");
247 } else {
248 pdg = ((TObjString*)tokens->At(0)) ->String().Atoi();
249 pdg2 = ((TObjString*)tokens->At(1)) ->String().Atoi();
250 }
251 }
252 else {
253 pdg = ((TObjString*)cols->At(0)) ->String().Atoi();
254 }
255 TString name = ((TObjString*)cols->At(1)) ->String();
256 Int_t system = ((TObjString*)cols->At(2)) ->String().Atoi();
257 Float_t sqrts = ((TObjString*)cols->At(3)) ->String().Atof();
258 Float_t yield = ((TObjString*)cols->At(4)) ->String().Atof();
259 // The "GetError" function can handle % errors.
260 Float_t stat = GetError(((TObjString*)cols->At(5)) ->String(), yield);
261 Float_t syst = GetError(((TObjString*)cols->At(6)) ->String(), yield);
3e8c4dd8 262 TString normString(((TObjString*)cols->At(7))->String());
263
264 Float_t normPos = 0;
265 Float_t normNeg = 0;
266 if (normString.Contains("+") && normString.Contains("-")) {
267
268 // If the string for the normalization uncertainty contains a + and a -, it means it is asymmetric
269 if(normString.First("+") < normString.First("-") ) {// the + error is quoted first
270 normPos = GetError(normString(1,normString.First("-")-1)+normString(normString.First("e"),normString.Length()), yield); // start from 1 (skip + sign). The second bit is to propagate the scientific notation to the first part of the error
271 normNeg = GetError(normString(normString.First("-")+1,normString.Length()), yield); // +1 -> skip sign
272 }
273 else {
274 // This is the opposite case
275 normNeg = GetError(normString(1,normString.First("+")-1)+normString(normString.First("e"),normString.Length()), yield); // start from 1 (skip + sign). The second bit is to propagate the scientific notation to the first part of the error
276 normPos = GetError(normString(normString.First("+")+1,normString.Length()), yield); // +1 -> skip sign
277 }
278
279 } else {
280 // symmetric error: set only normpos
281 normPos = GetError(((TObjString*)cols->At(7)) ->String(), yield);
282 }
2f86df33 283 Float_t ymin = ((TObjString*)cols->At(8)) ->String().Atof();
284 Float_t ymax = ((TObjString*)cols->At(9)) ->String().Atof();
285 Int_t status = ((TObjString*)cols->At(10)) ->String().Atoi();
286 TString centr = ((TObjString*)cols->At(12)) ->String();
287 Int_t issum = ((TObjString*)cols->At(13)) ->String().Atoi();
288 TString tag = cols->At(14) ? ((TObjString*)cols->At(14)) ->String() : ""; // tag can be empty
289
290 // Cleanup strings
291 name = name.Strip(TString::kLeading, ' ');
292 name = name.Strip(TString::kTrailing, ' ');
293 centr = centr.Strip(TString::kLeading, ' ');
294 centr = centr.Strip(TString::kTrailing, ' ');
295 tag = tag.Strip(TString::kLeading, ' ');
296 tag = tag.Strip(TString::kTrailing, ' ');
297
298 // add to array
3e8c4dd8 299 AliParticleYield * part = new AliParticleYield(pdg,system,sqrts,yield,stat,syst,normPos, normNeg,ymin,ymax,status,type,centr,issum,tag);
16163c99 300 part->SetPartName(name); // Check name and PDG code consistency
301 part->SetPdgCode2(pdg2); // Set second PDG code in case of ratios
302 part->CheckTypeConsistency();
303 if(!part->CheckForDuplicates(arr)) {
304 new ((*arr)[ipart++]) AliParticleYield(*part);
305 }
306 // delete part;
307
2f86df33 308 }
3e8c4dd8 309 std::cout << "<- File read" << std::endl;
2f86df33 310
311
312 return arr;
313}
314
315const char * AliParticleYield::GetLatexName(Int_t pdg) const {
316
317 // Returns a TLatex compatible name for the particle
318 // if pdg == 0 uses fPdgcode;
319 // We need the pdg argument for particle ratios
320
321 if(!pdg && fMeasurementType & kTypeParticleRatio) {
322 // If it's a ratio, we try to build the ratio name. To avoid an infinite loop we have to call GetLatexname with a non-zero argument.
323 static TString name;
90132fab 324 name ="#frac{";
2f86df33 325 name += GetLatexName(fPdgCode);
90132fab 326 name += "}{";
2f86df33 327 name += GetLatexName(fPdgCode2);
90132fab 328 name += "}";
2f86df33 329 return name.Data();
330 }
331
332 if(!pdg) pdg = fPdgCode;
333
334 switch (pdg) {
335 case 211:
336 if (fIsSum) return "(#pi^{+} + #pi^{-})";
337 return "#pi^{+}";
338 case -211:
339 return "#pi^{-}";
340 case 321:
341 if (fIsSum) return "(K^{+} + K^{-})";
342 return "K^{+}";
343 case -321:
344 return "K^{-}";
345 case 2212:
346 if (fIsSum) return "(p + #bar{p})";
347 return "p";
348 case -2212:
349 return "#bar^{p}";
350 case 3122:
351 if (fIsSum) return "(#Omega^{-} + #bar{#Omega}^{+})";
352 return "#Omega^{-}";
353 case -3122:
354 return "#bar{#Omega}^{+}";
355 case 3312:
356 if (fIsSum) return "(#Xi^{-} + #bar{#Xi}^{+})";
357 return "#Xi^{-}";
358 case -3312:
359 return "#bar{#Xi}^{+}";
360 case 3334:
361 if (fIsSum) return "(#Omega^{-} + #bar{#Omega}^{+})";
362 return "#Omega^{-}";
363 case -3334:
364 return "#bar{#Omega}^{+}";
365 case 310:
366 return "K^{0}_{S}";
367 case 333:
368 return "#phi";
369 case 313:
370 return "K^{*}";
371 case 1000010020:
372 if(fIsSum) return "(d + #bar{d})";
373 return "d";// Deuteron
374 case -1000010020:
375 return "#bar{d}";// Deuteron
376 case 1000020030:
377 if(fIsSum) return "(^{3}He + #bar{^{3}He})";
378 return "^{3}He";
379 case -1000020030:
380 return "#bar{^{3}He}";
381 case 1010010030:
e12ba0d8 382 if(fIsSum) return "^{3}_{#Lambda}H + #bar{^{3}_{#Lambda}H}";
383 return "^{3}_{#Lambda}H";
2f86df33 384 case -1010010030:
e12ba0d8 385 return "#bar{^{3}_{#Lambda}H}";
2f86df33 386 }
387 AliWarning("Latex Name not know for this particle");
388 return fPartName.Data();
389
390}
391
392Float_t AliParticleYield::GetTotalError(Bool_t includeNormalization) const {
393 // Returns the total error, including or not the normalization uncertainty
394 // All uncertainties are supposed to be uncorrelated (e.g. summed in quadrature)
395 // If stat and syst are stored separately, the total error is computed summing them in quadrature
396 Float_t error = GetSystError();
397 if (!(fMeasurementType & kTypeOnlyTotError)) error = TMath::Sqrt(error*error + GetStatError()*GetStatError());
398 if(includeNormalization) error = TMath::Sqrt(error*error + GetNormError()*GetNormError());
399
400 return error;
401
402
403}
404
405
406void AliParticleYield::SaveAsASCIIFile(TClonesArray * arr, const char * fileName, const char * separator, Int_t colWidth){
407 // Saves the array as an ASCII File with the format indicated
408 // below.
409
410 // The columns should be:
411 // PDG NAME SYSTEM SQRTS VALUE STAT SYST NORM YMIN YMAX STATUS TYPE CENTR ISSUM TAG
5a633e19 412 if(!arr) {
413 Printf("<AliParticleYield::SaveAsASCIIFile> Error: no array provided");
414 return;
415 }
416 if(!fileName) {
417 Printf("<AliParticleYield::SaveAsASCIIFile> Error: no filename provided");
418 }
2f86df33 419
420
421 ofstream fileOut(fileName);
422 //print header
423 fileOut << FormatCol("PDG", colWidth, separator) << FormatCol("NAME", colWidth, separator) << FormatCol("SYSTEM", colWidth, separator) << FormatCol("SQRTS", colWidth, separator) << FormatCol("VALUE", colWidth, separator) << FormatCol("STAT" , colWidth, separator)<< FormatCol("SYST", colWidth, separator) << FormatCol("NORM", colWidth, separator) << FormatCol("YMIN", colWidth, separator) << FormatCol("YMAX", colWidth, separator) << FormatCol("STATUS", colWidth, separator) << FormatCol("TYPE", colWidth, separator) << FormatCol("CENTR", colWidth, separator) << FormatCol("ISSUM", colWidth, separator) << FormatCol("TAG", colWidth, separator) << endl;
424
5a633e19 425
426 // This is used for float numbers in the table.
427 // The "g" options switches between the normal or scientific notation, whathever is more appropriate.
428 // We want to have up to fSignificantDigits digits after the .
429 char format[20];
430 snprintf(format,20,"%%%dg", fSignificantDigits);
431
3e8c4dd8 432 char formatA[30];// We have to rebuild the format for asymmetric uncertainties...
433 snprintf(formatA,30,"+%%%dg-%%%dg", fSignificantDigits, fSignificantDigits);
434
2f86df33 435 TIter iter(arr);
436 AliParticleYield * part = 0;
3e8c4dd8 437 TString normError ;
2f86df33 438 while ((part = (AliParticleYield*) iter.Next())){
3e8c4dd8 439 if(part->GetNormErrorNeg()) {
440 normError = FormatCol(Form(formatA, // Asymmetric error format
441 RoundToSignificantFigures(part->GetNormErrorPos(),fSignificantDigits),
442 RoundToSignificantFigures(part->GetNormErrorNeg(),fSignificantDigits)),
443 colWidth,
444 separator);
445 }
446 else {
447 normError = FormatCol(Form(format, RoundToSignificantFigures(part->GetNormError(),fSignificantDigits)) , colWidth , separator);
448 }
2f86df33 449 fileOut
5a633e19 450 << FormatCol(Form("%d",part->GetPdgCode()) , colWidth , separator)
451 << FormatCol(part->GetPartName() , colWidth , separator)
452 << FormatCol(Form("%d", part->GetCollisionSystem()) , colWidth , separator)
453 << FormatCol(Form(format, part->GetSqrtS()) , colWidth , separator)
454 << FormatCol(Form(format, RoundToSignificantFigures(part->GetYield(), fSignificantDigits)) , colWidth , separator)
455 << FormatCol(Form(format, RoundToSignificantFigures(part->GetStatError(),fSignificantDigits)) , colWidth , separator)
456 << FormatCol(Form(format, RoundToSignificantFigures(part->GetSystError(),fSignificantDigits)) , colWidth , separator)
3e8c4dd8 457 << normError.Data()
5a633e19 458 << FormatCol(Form(format, part->GetYMin()) , colWidth , separator)
459 << FormatCol(Form(format, part->GetYMax()) , colWidth , separator)
460 << FormatCol(Form("%d",part->GetStatus() ) , colWidth , separator)
461 << FormatCol(Form("%d",part->GetMeasurementType() ) , colWidth , separator)
462 << FormatCol(part->GetCentr() , colWidth , separator)
463 << FormatCol(Form("%d",part->GetIsSum()) , colWidth , separator)
464 << FormatCol(part->GetTag() , colWidth , separator)
2f86df33 465 << endl;
466 }
467
468
469}
470
5a633e19 471void AliParticleYield::WriteThermusFile(TClonesArray * arr, const char * filename, Int_t colwidth) {
472 // Writes a txt file which can we used as input in therums fits
473
474 if(!arr) {
475 Printf("<AliParticleYield::WriteThermusFile> Error: no array provided");
476 return;
477 }
478 if(!filename) {
479 Printf("<AliParticleYield::WriteThermusFile> Error: no filename provided");
3e8c4dd8 480 return;
5a633e19 481 }
482
483 ofstream fileOut(filename);
484
485 TIter iter(arr);
486 AliParticleYield * part = 0;
487 char format[20];
488 // This is used for float numbers in the table.
489 // The "g" options switches between the normal or scientific notation, whathever is more appropriate.
490 // We want to have up to fSignificantDigits digits after the .
491 snprintf(format,20,"%%%dg", fSignificantDigits);
492
493 // snprintf(format, 20, "%d.%d%%f", fSignificantDigits, fSignificantDigits);
494 while ((part = (AliParticleYield*) iter.Next())){
495
496 if(part->IsTypeRatio()) {
497 // If it's a ratio we have to write the 2 pdg codes
cffbaa61 498 fileOut << FormatCol(Form("%d\t%d\t",part->GetPdgCode(), part->GetPdgCode2()) , colwidth)
499 << part->GetTag() << "\t"
500 << Form(format, RoundToSignificantFigures(part->GetYield() , fSignificantDigits)) << "\t"
501 << Form(format, RoundToSignificantFigures(part->GetTotalError() , fSignificantDigits))
5a633e19 502 << endl;
503 }
504 else {
cffbaa61 505 fileOut <<Form("%d",part->GetPdgCode()) << "\t"
506 <<part->GetTag() << "\t"
507 <<Form(format, RoundToSignificantFigures(part->GetYield() , fSignificantDigits)) << "\t"
508 <<Form(format, RoundToSignificantFigures(part->GetTotalError() , fSignificantDigits))
5a633e19 509 << endl;
510 }
511
512 }
513
514}
515
516
2f86df33 517const char * AliParticleYield::FormatCol(const char * text, Int_t width, const char * sep) {
518
519 TString format(Form("%%-%ds %s", width, sep));
520 return Form(format.Data(), text);
521
522}
523
524Double_t AliParticleYield::RoundToSignificantFigures(double num, int n) {
525 // Rounds num to n significant digits.
526 // Recipe from :http://stackoverflow.com/questions/202302/rounding-to-an-arbitrary-number-of-significant-digits
527 // Basically the log is used to determine the number of leading 0s, than convert to an integer by multipliing by the expo,
528 // round the integer and shift back.
529 if(num == 0) {
530 return 0;
531 }
532
533 Double_t d = TMath::Ceil(TMath::Log10(num < 0 ? -num: num));
534 Int_t power = n - (int) d;
535
536 Double_t magnitude = TMath::Power(10, power);
537 Long_t shifted = TMath::Nint(num*magnitude);
538 return shifted/magnitude;
539
540}
541
542
543Float_t AliParticleYield::GetError(TString error, Float_t yield) {
544 // The "GetError" function can handle % errors.
545 if(error.Contains("%")) {
546 return yield * error.Atof()/100;
547 }
548 return error.Atof();
549}
550
551void AliParticleYield::SetPdgCode(TString partName) {
552 // Set pdg code from part name, if there was a previous name, check if it is consistent
553 TParticlePDG * part = TDatabasePDG::Instance()->GetParticle(partName);
554 if(IsTypeRatio() || fIsSum) return; // Name check does not make sense for ratios and sums
555 if(!part) {
556 AliError(Form("No particle %s in TDatabasePDG", partName.Data()));
557 return;
558 }
559 if(fPdgCode != part->PdgCode() && !(fMeasurementType&kTypeParticleRatio)) { // The consistency check on PDG codes is disabled case of ratios
560 AliError(Form("Name and pdg code are not consistent! fPartName: %s partName %s, pdgcode %d fPdgCode %d", fPartName.Data(), partName.Data(), part->PdgCode(), fPdgCode));
561 }
562 fPdgCode = part->PdgCode();
563
564}
565
566void AliParticleYield::SetPartName(Int_t pdgCode) {
567 // Set part name from pdg code, if there was a previous code, check if it is consistent
568 TParticlePDG * part = TDatabasePDG::Instance()->GetParticle(pdgCode);
569 if(IsTypeRatio() || fIsSum) return; // Name check does not make sense for ratios and sums
570 if(!part) {
571 AliError(Form("No particle with code %d in TDatabasePDG", pdgCode));
572 return;
573 }
574 if(fPdgCode != part->PdgCode() && !(fMeasurementType&kTypeParticleRatio)) { // The consistency check on particle names is disabled case of ratios
575 AliError(Form("Name and pdg code are not consistent! fPartName: %s partName %s, pdgcode %d fPdgCode %d", fPartName.Data(), part->GetName(), part->PdgCode(), fPdgCode));
576 }
577 fPartName = part->GetName();
578
579}
580
581Bool_t AliParticleYield::CheckTypeConsistency() const {
582 // Check for any inconsistency with the type mask. Returns true if the object is fully consistent.
583 Bool_t isOK = kTRUE;
584
585 if((fMeasurementType & kTypeOnlyTotError) && GetStatError()) {
586 AliError(Form("Error flagged as total only, but stat error is not 0 (%f)!",GetStatError()));
587 isOK= kFALSE;
588 } else if (!(fMeasurementType & kTypeOnlyTotError) && (!GetStatError() || !GetSystError())) {
589 AliError("Stat or syst errors are null");
590 }
591
592 return isOK;
593}
594
595void AliParticleYield::Print (Option_t *) const {
596 // Print
597 Printf("-------------------------------");
598 CheckTypeConsistency();
599 Printf("%s [%s] (%d) %s %s", fPartName.Data(), GetLatexName(), fPdgCode, fIsSum ? "particle + antiparticle" : "", fTag.Length() ? Form("[%s]", fTag.Data()) : "" );
600 Printf("Status: %s, %s", kStatusString[fStatus], fMeasurementType & kTypeLinearInterpolation ? "Interpolated" : "Measured");
601 Printf("%s , sqrt(s) = %2.2f GeV, %2.2f < y < %2.2f %s", kSystemString[fCollisionSystem], fSqrtS, fYMin, fYMax, fCentr.Data());
602 if(fMeasurementType & kTypeOnlyTotError) {
603 Printf("%f +- %f (total error)", fYield, fSystError);
604 }
605 else {
606 Printf("%f +- %f (stat) +- %f (syst)", fYield, fStatError, fSystError);
607 }
3e8c4dd8 608 if(fNormErrorNeg) {
87d00916 609 Printf("Normalization uncertainty: +%f-%f", fNormErrorPos, fNormErrorNeg);
3e8c4dd8 610 }
611 else {
87d00916 612 Printf("Normalization uncertainty: %f", fNormErrorPos);
3e8c4dd8 613 }
2f86df33 614}
615
16163c99 616Bool_t AliParticleYield::operator==(const AliParticleYield& rhs) {
617 // Check if the two particles are identical
618
619 Bool_t isEqual =
3e8c4dd8 620 (fPdgCode == rhs.fPdgCode ) &&
621 (fPdgCode2 == rhs.fPdgCode2 ) &&
622 (fPartName == rhs.fPartName ) &&
623 (fCollisionSystem == rhs.fCollisionSystem ) &&
624 Compare2Floats(fSqrtS,rhs.fSqrtS ) &&
625 Compare2Floats(fYield,rhs.fYield ) &&
626 Compare2Floats(fStatError,rhs.fStatError ) &&
627 Compare2Floats(fSystError,rhs.fSystError ) &&
628 Compare2Floats(fNormErrorPos,rhs.fNormErrorPos ) &&
629 Compare2Floats(fNormErrorNeg,rhs.fNormErrorNeg ) &&
630 Compare2Floats(fYMin,rhs.fYMin ) &&
631 Compare2Floats(fYMax,rhs.fYMax ) &&
632 (fStatus == rhs.fStatus ) &&
633 (fMeasurementType == rhs.fMeasurementType ) &&
634 (fCentr == rhs.fCentr ) &&
635 (fIsSum == rhs.fIsSum ) &&
636 (fTag == rhs.fTag ) ;
16163c99 637
638 return isEqual;
639
640}
641Bool_t AliParticleYield::IsTheSameMeasurement(AliParticleYield &rhs) {
642
643 // Check the two particles represent the same measurement (independently of the values)
644 Bool_t isEqual =
645 (fPdgCode == rhs.fPdgCode ) &&
646 (fPdgCode2 == rhs.fPdgCode2 ) &&
647 (fCollisionSystem == rhs.fCollisionSystem ) &&
648 Compare2Floats(fSqrtS,rhs.fSqrtS ) &&
649 Compare2Floats(fYMin,rhs.fYMin ) &&
650 Compare2Floats(fYMax,rhs.fYMax ) &&
651 (fStatus == rhs.fStatus ) &&
652 (fCentr == rhs.fCentr ) &&
653 (fIsSum == rhs.fIsSum ) &&
654 (fTag == rhs.fTag ) ;
655
656 return isEqual;
657
658
659}
660
661Bool_t AliParticleYield::CheckForDuplicates(TClonesArray * arr) {
662
663 // loop over all elements on the array and check for duplicates
664 TIter iter(arr);
665 AliParticleYield * part = 0;
666 Bool_t isDuplicate = kFALSE;
667
668 while ((part = (AliParticleYield*) iter.Next())) {
669 if (IsTheSameMeasurement(*part)){
670 AliWarning("Duplicated measurement found");
671 isDuplicate = kTRUE;
672 if (!((*this) == (*part))) {
673 part->Print();
674 Print();
675 AliFatal("The 2 particles are different!");
676 }
677 }
678 }
679 return isDuplicate;
680
681}
682
683Bool_t AliParticleYield::Compare2Floats(Float_t a, Float_t b) {
684 // just a simple helper for the comparison methods
685 if(!a) {
686 if(!b) return kTRUE; // They are both 0;
687 return kFALSE;// return here to avoid division by 0
688 }
689 Bool_t areEqual = (TMath::Abs((a - b)/a) < fEpsilon); // If the relative difference is < epsilon, returns true
690 if(!areEqual) {
691 Printf("Warning: %f and %f are different", a,b);
692 }
693 return areEqual;
694}
3e8c4dd8 695
696
697Float_t AliParticleYield::GetNormError() const {
698 // Returs a symmetrized error in case the normalizatione Error is asymmetric
699 if(fNormErrorNeg) {
700 AliWarning("Error is asymmetric, returining symmetrized uncertainty");
701 return (TMath::Abs(fNormErrorNeg)+TMath::Abs(fNormErrorPos))/2;
702 }
703 else return fNormErrorPos; // If the uncertainty is not asymmetric, fNormErrorPos stores it.
704
705}
90132fab 706
707AliParticleYield * AliParticleYield::FindParticle(TClonesArray * arr, Int_t pdg, Int_t system, Float_t sqrts, TString centrality, Int_t isSum, Int_t status, Int_t pdg2){
708 // Finds a particle in array matching the search criteria. If more than one is found, prints a warning
709 // If status is -1, tries to return the best (lower status value)
710 // If pdg2 is not zero, we try to match it as well (we are looking for a ratio)
711 // The centrality is compared with TString::Contains
712
713 TIter iter(arr);
714 AliParticleYield * part = 0;
715 AliParticleYield * foundPart = 0;
716 while ((part = dynamic_cast<AliParticleYield*>(iter.Next()))){
717 if (part->GetPdgCode() == pdg && // same pdg
718 part->GetCollisionSystem() == system && // same system
719 Compare2Floats(part->GetSqrtS(), sqrts) && // same energy
720 part->GetCentr().Contains(centrality) && // compatible centrality
87d00916 721 (part->GetPdgCode2() == pdg2) && // same PDG2, if requested (we are looking for a ratio). We also need to check explicitly for pdg2=0 not to match ratios
90132fab 722 (status < 0 || part->GetStatus() == status) && // same status, if requested
723 (isSum < 0 || part->GetIsSum() == isSum) // part+antipart or not, if requested
724 ) {
725 if (foundPart) { // we already found a patching particle
726 Printf("WARNING<AliParticleYield::FindParticle>: Found another particle matching the same criteria");
727 foundPart->Print();
728 part->Print();
729 if (part->GetStatus() == foundPart->GetStatus()) { // Does it have the same status?! Don't know what to do!
730 Printf("WARNING<AliParticleYield::FindParticle>: they have the same status, I cannot decide, resetting particle");
731 foundPart = 0;
732 }
733 else if (part->GetStatus()< foundPart->GetStatus()) { // Is it of better quality? select it!
734 Printf("WARNING<AliParticleYield::FindParticle>: the new one has a smaller status: selecting it!");
735 foundPart = part;
736 }
737 } else { // First match
738 foundPart = part;
739 }
740
741 }
742
743 }
744 return foundPart;
745
746}
747
748void AliParticleYield::CombineMetadata(AliParticleYield *part1, AliParticleYield*part2) {
749 // Combines metadata from part1 and part2
750 Int_t pdg1 = part1->GetPdgCode();
87d00916 751 Int_t pdg2 = pdg1 == part2->GetPdgCode() ? part1->GetPdgCode2() : part2->GetPdgCode();
90132fab 752 Int_t system = part1->GetCollisionSystem() == part2->GetCollisionSystem() ? part2->GetCollisionSystem() : -1;
753 Float_t sqrts = Compare2Floats(part1->GetSqrtS(), part2->GetSqrtS()) ? part1->GetSqrtS() : 0;
754 Int_t ymin = part1->GetYMin() == part2->GetYMin() ? part2->GetYMin() : -1000;
755 Int_t ymax = part1->GetYMax() == part2->GetYMax() ? part2->GetYMax() : -1000;
756 Int_t status = part1->GetStatus() == part2->GetStatus() ? part2->GetStatus() : -1;
757 Int_t type = part1->GetMeasurementType() == part2->GetMeasurementType() ? part2->GetMeasurementType() : -1;
758
759 TString centr = part1->GetCentr() == part2->GetCentr() ? part2->GetCentr() : part1->GetCentr()+"/"+part2->GetCentr();
760 TString tag = part1->GetTag() == part2->GetTag() ? part2->GetTag() : part1->GetTag()+"/"+part2->GetTag();
761 Int_t issum = part1->GetIsSum() == part2->GetIsSum() ? part2->GetIsSum() : -1000;
762
763 SetPdgCode(pdg1);
764 SetPdgCode2(pdg2);
765 SetCollisionSystem(AliPYCSystem_t(system));
766 SetSqrtS(sqrts);
767 SetYMin(ymin);
768 SetYMax(ymax);
769 SetStatus(AliPYStatusCode_t(status));
770 SetMeasurementType(type);
771 SetCentr(centr);
772 SetTag(tag);
773 SetIsSum(issum);
774
775}
776
777AliParticleYield * AliParticleYield::Add (AliParticleYield * part1, AliParticleYield * part2, Double_t correlatedError , Option_t * opt){
778
779 // Computes the ratio of 2 particles.
780 // Valid options:
781 // - NQ: Propagates normalization errors quadratically (by default they are propagated linearly)
782 // - SL: propagates STATISTICAL errors linearly
783 // - YQ: propagates SYSTEMATIC errors quadratically
784 // NB by default, statistical errors are propagated quadratically and systematic errors linearly
785 // if "Correlated error" is non null, it is subtracted in quadrature from the result. It should be a fractional error.
786
787 if(!part1 || !part2) {
788 Printf("WARNING<AliParticleYield::Add>: part1 or part2 is null!");
789 return 0;
790 }
791
792 TString sopt(opt);
793 sopt.ToUpper();
794
795 Float_t value = part1->GetYield() + part2->GetYield();
796 Float_t stat = SumErrors(part1, part2, 0, sopt.Contains("SL") ? "L": "" ); // the option decices if it is propagated linearly pr or quadratically
797 Float_t syst = SumErrors(part1, part2, 1, sopt.Contains("YQ") ? "" : "L" );// the option decices if it is propagated linearly pr or quadratically
798 Float_t norm = SumErrors(part1, part2, 2, sopt.Contains("NQ") ? "" :"L");
799
800
801 if(correlatedError) {
802 syst = TMath::Sqrt(syst*syst -correlatedError*correlatedError*value*value); // FIXME: this line was never tested
803 }
804
805 AliParticleYield * part = new AliParticleYield();
806 part->SetYield(value);
807 part->SetStatError(stat);
808 part->SetSystError(syst);
809 part->SetNormError(norm);
810 part->CombineMetadata(part1, part2);
811
812 return part;
813
814
815}
816
817void AliParticleYield::Scale(Float_t scale) {
818 // scales the measurement by an errorless number
819 fYield *= scale;
820 fNormErrorNeg *= scale;
821 fNormErrorPos *= scale;
822 fStatError *= scale;
823 fSystError *= scale;
824
825}
826
827AliParticleYield * AliParticleYield::Divide (AliParticleYield * part1, AliParticleYield * part2, Double_t correlatedError , Option_t * opt) {
828 // Computes the ratio of 2 particles.
829 // Valid options:
830 // - NQ: assumes normalization errors to be uncorrelated and propagates them quadratically (otherwise the normalization error on the ratio is set to 0
831 // - SL: propagates STATISTICAL errors linearly
832 // - YQ: propagates SYSTEMATIC errors quadratically
833 // NB by default, statistical errors are propagated quadratically and systematic errors linearly
834 // if "Correlated error" is non null, it is subtracted in quadrature from the result.It should be a fractional error.
835
836 if(!part1 || !part2) {
837 Printf("WARNING<AliParticleYield::Divide>: part1 or part2 is null!");
838 return 0;
839 }
840
841 TString sopt(opt);
842 sopt.ToUpper();
843 if(part1->IsTypeRatio() || part2->IsTypeRatio()){
844 Printf("WARNING<AliParticleYield::Divide>: If computing a double ratio, some meta info may be not reliable!");
845 }
846
847 Float_t value = part1->GetYield() / part2->GetYield();
87d00916 848 // Since in a ratio we propagate a relative error, we have to multiply it back for value in order to get the absolute uncertainty
849 Float_t stat = SumErrors(part1, part2, 0, sopt.Contains("SL") ? "RL": "R" ) *value; // R means that it's a relative error, the option decices if it is propagated linearly pr or quadratically
850 Float_t syst = SumErrors(part1, part2, 1, sopt.Contains("YQ") ? "R" : "RL" )*value;// R means that it's a relative error, the option decices if it is propagated linearly pr or quadratically
90132fab 851 Float_t norm = 0;
852 if(sopt.Contains("NQ")) {// if opt contains N, propagate the normalization error assuming it is independent
87d00916 853 norm = SumErrors(part1, part2, 2, "R")*value;
90132fab 854 }
855
856 if(correlatedError) {
857 syst = TMath::Sqrt(syst/value*syst/value -correlatedError*correlatedError)*value; // FIXME: this line was never tested
858 }
859
860 AliParticleYield * part = new AliParticleYield();
861 part->SetYield(value);
862 part->SetStatError(stat);
863 part->SetSystError(syst);
864 part->SetNormError(norm);
865 part->CombineMetadata(part1, part2);
866
867 return part;
868
869}
870
871Double_t AliParticleYield::SumErrors(AliParticleYield * part1, AliParticleYield * part2, Int_t error, Option_t * opt) {
872
873 // Combines 2 errors.
874 // error = 0 -> statistical error
875 // error = 1 -> systematic error
876 // error = 2 -> normalization error
877 // Valid options
87d00916 878 // "R" it propagates it as a relative error, WARNING: it also returns a relative error!
90132fab 879 // "L" it propagates it sums the errors linearly (by default it is done in quadrature)
87d00916 880
90132fab 881
882 TString sopt(opt);
883 sopt.ToUpper();
884 Bool_t isRelative = sopt.Contains("R");
885 Bool_t isLinear = sopt.Contains("L");
886
887 Double_t err1 = 0;
888 Double_t err2 = 0;
889 Double_t err = 0;
890 if (error == 0) {
891 err1 = part1->GetStatError();
892 err2 = part2->GetStatError();
893 } else if (error == 1) {
894 err1 = part1->GetSystError();
895 err2 = part2->GetSystError();
896 } else if (error == 2) {
897 err1 = part1->GetNormError();
898 err2 = part2->GetNormError();
899 } else {
900 Printf("ERROR<AliParticleYield::SumErrors>: wrong error #:%d", error);
901 }
902
903 if(isRelative) {
904 err1 /= part1->GetYield();
905 err2 /= part2->GetYield();
906 }
907
908 if(isLinear) {
909 err = err1+err2;
910 } else {
911 err = TMath::Sqrt(err1*err1 + err2*err2);
912 }
913
87d00916 914 if(isRelative) return err;
90132fab 915
916 return err;
917
918}