AliPhysics  8b695ca (8b695ca)
AliAODForwardMult.cxx
Go to the documentation of this file.
1 //
2 // Class that contains the forward multiplicity data per event
3 //
4 // This class contains a histogram of
5 // @f[
6 // \frac{d^2N_{ch}}{d\eta d\phi}\quad,
7 // @f]
8 // as well as a trigger mask for each analysed event.
9 //
10 // The eta acceptance of the event is stored in the underflow bins of
11 // the histogram. So to build the final histogram, one needs to
12 // correct for this acceptance (properly weighted by the events), and
13 // the vertex efficiency. This simply boils down to defining a 2D
14 // histogram and summing the event histograms in that histogram. One
15 // should of course also do proper book-keeping of the accepted event.
16 //
17 #include "AliAODForwardMult.h"
18 #include <TBrowser.h>
19 #include <iostream>
20 #include <TMath.h>
21 #include <TObjString.h>
22 #include <TObjArray.h>
23 #include "AliLog.h"
24 ClassImp(AliAODForwardMult)
25 #if 0 // def DOXY_INPUT
26 ; // For Emacs
27 #endif
28 
29 //____________________________________________________________________
31 
32 //____________________________________________________________________
34  : fIsMC(false),
35  fHist(),
36  fTriggers(0),
37  fIpZ(fgkInvalidIpZ),
38  fCentrality(-1),
39  fNClusters(0)
40 {
41  //
42  // Constructor
43  //
44 }
45 
46 //____________________________________________________________________
48  : fIsMC(isMC),
49  fHist("forwardMult", "d^{2}N_{ch}/d#etad#varphi in the forward regions",
50  200, -4, 6, 20, 0, 2*TMath::Pi()),
51  fTriggers(0),
53  fCentrality(-1),
54  fNClusters(0)
55 {
56  //
57  // Constructor
58  //
59  // Parameters:
60  // isMC If set to true this is for MC data (effects branch name)
61  //
62  fHist.SetXTitle("#eta");
63  fHist.SetYTitle("#varphi [radians]");
64  fHist.SetZTitle("#frac{d^{2}N_{ch}}{d#etad#varphi}");
65  fHist.SetDirectory(0);
66  fHist.Sumw2();
67 }
68 
69 //____________________________________________________________________
70 void
72 {
73  // Initialize the histogram with an eta axis
74  //
75  // Parameters:
76  // etaAxis Eta axis to use
77  //
78  fHist.SetBins(etaAxis.GetNbins(), etaAxis.GetXmin(), etaAxis.GetXmax(),
79  20, 0, 2*TMath::Pi());
80 }
81 
82 //____________________________________________________________________
83 void
85 {
86  // Clear (or reset) internal values
87  //
88  // Parameters:
89  // option Passed to TH1::Reset
90  //
91  fHist.Reset(option);
92  fTriggers = 0;
94  fNClusters = 0;
95 }
96 
97 //____________________________________________________________________
98 TH1*
100 {
101  Int_t bin = GetEtaCoverageBin();
102  TH1* ret = fHist.ProjectionX(Form("%s_etacov", fHist.GetName()),
103  bin, bin, "e");
104  ret->SetDirectory(0);
105  ret->SetYTitle("#it{#eta} coverage");
106  return ret;
107 }
108 //____________________________________________________________________
109 TH1*
111 {
112  Int_t bin = GetPhiAcceptanceBin();
113  TH1* ret = fHist.ProjectionX(Form("%s_phiacc", fHist.GetName()),
114  bin, bin, "e");
115  ret->SetDirectory(0);
116  ret->SetYTitle("#it{#varphi} acceptance");
117  return ret;
118 }
119 //____________________________________________________________________
120 void
122 {
123  TH1* hh = GetEtaCoverage();
124  h.Add(hh);
125 }
126 //____________________________________________________________________
127 void
129 {
130  TH1* hh = GetPhiAcceptance();
131  h.Add(hh);
132 }
133 //____________________________________________________________________
134 void
136 {
137  // set the center of mass energy per nucleon pair (GeV).
138  // This is stored in bin (0,0) of the histogram
139  //
140  // Parameters:
141  // sNN Center of mass energy per nuclean
142  fHist.SetBinContent(0,0,snn);
143 }
144 //____________________________________________________________________
145 void
147 {
148  // set the center of mass energy per nucleon pair (GeV).
149  // This is stored in bin (N+1,0) of the histogram
150  //
151  // Parameters:
152  // sys Collision system number
153  fHist.SetBinContent(fHist.GetNbinsX()+1,0,sys);
154 }
155 
156 //____________________________________________________________________
157 Bool_t
159 {
160  // Check if we have valid z coordinate of the interaction point
161  //
162  // Return:
163  // true if the z coordinate of the interaction point is valid
164  //
165  return TMath::Abs(fIpZ - fgkInvalidIpZ) > 1;
166 }
167 
168 //____________________________________________________________________
169 UShort_t
171 {
172  // set the center of mass energy per nucleon pair (GeV).
173  // This is stored in bin (0,0) of the histogram
174  //
175  // Parameters:
176  // sNN Center of mass energy per nuclean
177  return UShort_t(fHist.GetBinContent(0,0));
178 }
179 
180 //____________________________________________________________________
181 UShort_t
183 {
184  // set the center of mass energy per nucleon pair (GeV).
185  // This is stored in bin (N+1,0) of the histogram
186  //
187  // Parameters:
188  // sNN Center of mass energy per nuclean
189  return UShort_t(fHist.GetBinContent(fHist.GetNbinsX()+1,0));
190 }
191 
192 //____________________________________________________________________
193 void
195 {
196  // Browse this object
197  //
198  // Parameters:
199  // b Browser to use
200  static TObjString ipz;
201  static TObjString trg;
202  static TObjString cnt;
203  static TObjString ncl;
204  ipz = Form("ip_z=%fcm", fIpZ);
206  cnt = Form("%+6.1f%%", fCentrality);
207  ncl = Form("%d clusters", fNClusters);
208  b->Add(&fHist);
209  b->Add(&ipz);
210  b->Add(&trg);
211  b->Add(&cnt);
212  b->Add(&ncl);
213 }
214 
215 namespace {
216  void AppendAnd(TString& trg, const char* sep, const TString& what)
217  {
218  if (!trg.IsNull()) trg.Append(Form(" %s ", sep));
219  trg.Append(what);
220  }
221 }
222 //____________________________________________________________________
223 const Char_t*
225 {
226  // Get a string that describes the triggers
227  //
228  // Parameters:
229  // mask Bit pattern of triggers
230  // Return:
231  // Character string representation of mask
232  static TString trg;
233  trg = "";
234  if (mask == 0) return "none";
235 
236  bool isOr = mask & kInclusive;
237  UInt_t tmp = 0x7FFFFFFF & mask;
238  TString s(sep);
239  if (s.IsNull()) s = isOr ? "|" : "&";
240 
241  if ((tmp & kInel) != 0x0) AppendAnd(trg, s, "MBOR");
242  if ((tmp & kInelGt0) != 0x0) AppendAnd(trg, s, "INEL>0");
243  if ((tmp & kNSD) != 0x0) AppendAnd(trg, s, "MBAND");
244  if ((tmp & kV0AND) != 0x0) AppendAnd(trg, s, "V0AND");
245  if ((tmp & kA) != 0x0) AppendAnd(trg, s, "A");
246  if ((tmp & kB) != 0x0) AppendAnd(trg, s, "B");
247  if ((tmp & kC) != 0x0) AppendAnd(trg, s, "C");
248  if ((tmp & kE) != 0x0) AppendAnd(trg, s, "E");
249  if ((tmp & kMCNSD) != 0x0) AppendAnd(trg, s, "MCNSD");
250  if ((tmp & kNClusterGt0) != 0x0) AppendAnd(trg, s, "NCluster>0");
251  if ((tmp & kSatellite) != 0x0) AppendAnd(trg, s, "Satellite");
252  if ((tmp & kOffline) != 0x0) AppendAnd(trg, s, "Offline");
253  if ((tmp & kSPDOutlier) != 0x0) AppendAnd(trg, s, "Outlier");
254  if ((tmp & kPileUp) != 0x0) AppendAnd(trg, s, "Pileup");
255  if ((tmp & kPileupSPD) != 0x0) AppendAnd(trg, s, "Pileup-SPD");
256  if ((tmp & kPileupTrack) != 0x0) AppendAnd(trg, s, "Pileup-TRK");
257  if ((tmp & kPileupBC) != 0x0) AppendAnd(trg, s, "Pileup-BC");
258  if ((tmp & kPileupBins) != 0x0) AppendAnd(trg, s, "Pileup-BIN");
259  if ((tmp & kADOR) != 0x0) AppendAnd(trg, s, "ADOR");
260  if ((tmp & kADAND) != 0x0) AppendAnd(trg, s, "ADAND");
261  return trg.Data();
262 }
263 
264 //____________________________________________________________________
265 TH1I*
266 AliAODForwardMult::MakeTriggerHistogram(const char* name, UInt_t triggerMask)
267 {
268  //
269  // Make a histogram to record triggers in.
270  //
271  // The bins defined by the trigger enumeration in this class. One
272  // can use this enumeration to retrieve the number of triggers for
273  // each class.
274  //
275  // Parameters:
276  // name Name of the histogram
277  //
278  // Return:
279  // Newly allocated histogram
280  //
281  TString sel = GetTriggerString(triggerMask, "");
282  TH1I* ret = new TH1I(name,
283  Form("Triggers (%s)", sel.Data()),
284  kAccepted, .5, kAccepted+.5);
285  ret->SetYTitle("Events");
286  ret->SetFillColor(kRed-2);
287  ret->SetFillStyle(3002);
288  ret->GetXaxis()->SetBinLabel(kBinAll, "All events");
289  ret->GetXaxis()->SetBinLabel(kBinInel, "Coll. & MBOR");
290  ret->GetXaxis()->SetBinLabel(kBinInelGt0, "Coll. & MBOR&&nTracklet>0");
291  ret->GetXaxis()->SetBinLabel(kBinNSD, "Coll. & V0AND||FASTOR>5");
292  ret->GetXaxis()->SetBinLabel(kBinV0AND, "Coll. & V0AND");
293  ret->GetXaxis()->SetBinLabel(kBinADOR, "Coll. & ADOR");
294  ret->GetXaxis()->SetBinLabel(kBinADAND, "Coll. & ADAND");
295  ret->GetXaxis()->SetBinLabel(kBinB, "B (Coll.) & Sel.");
296  ret->GetXaxis()->SetBinLabel(kBinA, "A & Sel.");
297  ret->GetXaxis()->SetBinLabel(kBinC, "C & Sel.");
298  ret->GetXaxis()->SetBinLabel(kBinE, "E & Sel.");
299  ret->GetXaxis()->SetBinLabel(kBinSatellite, "Satellite");
300  ret->GetXaxis()->SetBinLabel(kBinMCNSD, "NSD (MC truth)");
301  ret->GetXaxis()->SetBinLabel(kBinPileUp, "w/Pileup");
302  ret->GetXaxis()->SetBinLabel(kBinOffline, "w/Offline");
303  ret->GetXaxis()->SetBinLabel(kBinNClusterGt0, "w/N_{cluster}>1");
304  ret->GetXaxis()->SetBinLabel(kWithVertex, "w/Vertex");
305  ret->GetXaxis()->SetBinLabel(kWithTrigger, "w/Selected trigger");
306  ret->GetXaxis()->SetBinLabel(kAccepted, "Accepted by cut");
307  ret->GetXaxis()->SetNdivisions(kAccepted, false);
308  ret->SetStats(0);
309 
310  return ret;
311 }
312 
313 //____________________________________________________________________
314 TH1I*
316 {
317  //
318  // Make a histogram to record status in.
319  //
320  // The bins defined by the status enumeration in this class.
321  //
322  // Parameters:
323  // name Name of the histogram
324  //
325  // Return:
326  // Newly allocated histogram
327  //
328  Int_t nBins = kOutlierEvent;
329  TH1I* ret = new TH1I(name, "Event selection status", nBins+1, -.5, nBins+.5);
330  ret->SetYTitle("Events");
331  ret->SetFillColor(kBlue+1);
332  ret->SetFillStyle(3002);
333  ret->GetXaxis()->SetBinLabel(kGoodEvent+1, "Good");
334  ret->GetXaxis()->SetBinLabel(kWrongCentrality+1, "Out-of-range centrality");
335  ret->GetXaxis()->SetBinLabel(kWrongTrigger+1, "Wrong trigger");
336  ret->GetXaxis()->SetBinLabel(kIsPileup+1, "Pile-up");
337  ret->GetXaxis()->SetBinLabel(kIsFilterOut+1, "Filtered out");
338  ret->GetXaxis()->SetBinLabel(kNoVertex+1, "No IP_{z}");
339  ret->GetXaxis()->SetBinLabel(kWrongVertex+1, "Out-or-range IP_{z}");
340  ret->GetXaxis()->SetBinLabel(kOutlierEvent+1, "SPD Outlier");
341  ret->GetXaxis()->SetNdivisions(nBins, false);
342  ret->SetStats(0);
343  return ret;
344 }
345 
346 //____________________________________________________________________
347 UInt_t
348 AliAODForwardMult::MakeTriggerMask(const char* what, const char* sep)
349 {
350  UInt_t trgMask = 0;
351  TString trgs(what);
352  trgs.ToUpper();
353  if (trgs.EqualTo("NONE") || trgs.EqualTo("ALL")) return trgMask;
354  TObjArray* parts = trgs.Tokenize(sep);
355  TObjString* trg;
356  TIter next(parts);
357  if (sep && sep[0] == '|') trgMask |= kInclusive;
358  else if (sep && sep[0] == '&') trgMask &= ~kInclusive;
359  while ((trg = static_cast<TObjString*>(next()))) {
360  TString s(trg->GetString());
361  s.Strip(TString::kBoth, ' ');
362  s.ToUpper();
363  // Printf("Full: %s, part: %s", what, s.Data());
364  if (s.IsNull()) continue;
365  if (s.CompareTo("INEL") == 0) trgMask |= kInel;
366  else if (s.CompareTo("MBOR") == 0) trgMask |= kInel;
367  else if (s.CompareTo("INEL>0") == 0) trgMask |= kInelGt0;
368  else if (s.CompareTo("INELGT0") == 0) trgMask |= kInelGt0;
369  else if (s.CompareTo("MBAND") == 0) trgMask |= kNSD;
370  else if (s.CompareTo("NSD") == 0) trgMask |= kV0AND;
371  else if (s.CompareTo("V0AND") == 0) trgMask |= kV0AND;
372  else if (s.CompareTo("MCNSD") == 0) trgMask |= kMCNSD;
373  else if (s.CompareTo("B") == 0) trgMask |= kB;
374  else if (s.CompareTo("A") == 0) trgMask |= kA;
375  else if (s.CompareTo("C") == 0) trgMask |= kC;
376  else if (s.CompareTo("SAT") == 0) trgMask |= kSatellite;
377  else if (s.CompareTo("E") == 0) trgMask |= kE;
378  else if (s.CompareTo("NCLUSTER>0") == 0) trgMask |= kNClusterGt0;
379  else if (s.CompareTo("CENT") == 0) trgMask |= kInel;
380  else if (s.CompareTo("MULT") == 0) trgMask |= kInel;
381  else if (s.CompareTo("OFFLINE") == 0) trgMask |= kOffline;
382  else if (s.CompareTo("OUTLIER") == 0) trgMask |= kSPDOutlier;
383  else if (s.CompareTo("PILEUP-SPD") == 0) trgMask |= kPileupSPD;
384  else if (s.CompareTo("PILEUP-TRK") == 0) trgMask |= kPileupTrack;
385  else if (s.CompareTo("PILEUP-BIN") == 0) trgMask |= kPileupBins;
386  else if (s.CompareTo("PILEUP-BC") == 0) trgMask |= kPileupBC;
387  else if (s.CompareTo("PILEUP") == 0) trgMask |= kPileUp;
388  else if (s.CompareTo("ADOR") == 0) trgMask |= kADOR;
389  else if (s.CompareTo("ADAND") == 0) trgMask |= kADAND;
390  else if (s.CompareTo("CENTCALIB") == 0) trgMask |= kCentNoCalib;
391  else if (s.BeginsWith("INCL")) trgMask |= kInclusive;
392  // trgMask &= ~(kInel|kInelGt0|kNSD|kV0AND|kMCNSD);
393  else
394  AliWarningGeneral("MakeTriggerMask",
395  Form("Unknown trigger %s", s.Data()));
396  }
397  delete parts;
398  return trgMask;
399 }
400 
401 
402 //____________________________________________________________________
403 void
405  UInt_t trg,
406  TH1* hist)
407 {
408  if (!hist) return;
409 
410  UInt_t tmp = triggerMask & ~kB;
411  hist->Fill(kBinAll);
412  if (IsTriggerBits(kB|kInel, trg)) hist->Fill(kBinInel);
413  if (IsTriggerBits(kB|kInelGt0, trg)) hist->Fill(kBinInelGt0);
414  if (IsTriggerBits(kB|kNSD, trg)) hist->Fill(kBinNSD);
415  if (IsTriggerBits(kB|kV0AND, trg)) hist->Fill(kBinV0AND);
416  if (IsTriggerBits(kB|kADOR, trg)) hist->Fill(kBinADOR);
417  if (IsTriggerBits(kB|kADAND, trg)) hist->Fill(kBinADAND);
418  if (IsTriggerBits(kB, trg) &&
419  IsTriggerBits(tmp, trg)) hist->Fill(kBinB);
420  if (IsTriggerBits(kA, trg) &&
421  IsTriggerBits(tmp, trg)) hist->Fill(kBinA);
422  if (IsTriggerBits(kC, trg) &&
423  IsTriggerBits(tmp, trg)) hist->Fill(kBinC);
424  if (IsTriggerBits(kE, trg) &&
425  IsTriggerBits(tmp, trg)) hist->Fill(kBinE);
426  if (IsTriggerBits(kSatellite, trg)) hist->Fill(kBinSatellite);
427  if (IsTriggerBits(kMCNSD, trg)) hist->Fill(kBinMCNSD);
428  if (IsTriggerBits(kPileUp, trg)) hist->Fill(kBinPileUp);
429  if (IsTriggerBits(kOffline, trg)) hist->Fill(kBinOffline);
430  if (IsTriggerBits(kNClusterGt0,trg)) hist->Fill(kBinNClusterGt0);
431  if (IsTriggerBits(triggerMask,trg) &&
432  !IsTriggerBits(kB|tmp,trg))
433  ::Warning("FillTriggerHistogram",
434  "event: 0x%x, mask: 0x%x, tmp: 0x%x, tmp|b: 0x%x",
435  trg, triggerMask, tmp, tmp|kB);
436 }
437 
438 //____________________________________________________________________
439 Bool_t
441  TH1* hist, TH1* status,
442  UInt_t filterMask) const
443 {
444  // Check if event is SPD outlier
445  if (filterMask & kSPDOutlier && IsTriggerBits(kSPDOutlier)) {
446  if (status) status->Fill(kOutlierEvent);
447  return false;
448  }
449  // Check for pileup
450  UInt_t checkPileup = (filterMask & (kPileUp |
451  kPileupSPD |
452  kPileupTrack |
453  kPileupBC |
454  kPileupBins));
455  if (checkPileup && IsTriggerBits(kInclusive | checkPileup)) {
456  if (status) status->Fill(kIsPileup);
457  return false;
458  }
459  UInt_t other = filterMask & ~(kPileUp |
460  kPileupSPD |
461  kPileupTrack |
462  kPileupBC |
463  kPileupBins |
464  kSPDOutlier);
465  // Unspecified stuff
466  if (other && IsTriggerBits(kInclusive | other)) {
467  if (status) status->Fill(kIsFilterOut);
468  return false;
469  }
470  // Got a non-vetoed trigger
471  if (hist) hist->Fill(kWithTrigger);
472 
473  // Check that we have a valid vertex
474  if (vzMin < vzMax && !HasIpZ()) {
475  if (status) status->Fill(kNoVertex);
476  return false;
477  }
478  // Got valid vertex
479  if (hist) hist->Fill(kWithVertex);
480 
481  // Check that vertex is within cuts
482  if (vzMin < vzMax && !InRange(vzMin, vzMax)) {
483  if (status) status->Fill(kWrongVertex);
484  return false;
485  }
486  // Trigger and vertex
487  if (hist) hist->Fill(kAccepted);
488  if (status) status->Fill(kGoodEvent);
489 
490  return true;
491 }
492 
493 //____________________________________________________________________
494 Bool_t
496  Double_t vzMin, Double_t vzMax,
497  Double_t cMin, Double_t cMax,
498  TH1* hist, TH1* status,
499  UInt_t filterMask) const
500 {
501  //
502  // Check if event meets the passses requirements.
503  //
504  // It returns true if @e all of the following is true
505  //
506  // - The trigger is within the bit mask passed.
507  // - The vertex is within the specified limits.
508  // - The centrality is within the specified limits, or if lower
509  // limit is equal to or larger than the upper limit.
510  //
511  // If a histogram is passed in the last parameter, then that
512  // histogram is filled with the trigger bits.
513  //
514  // Parameters:
515  // triggerMask Trigger mask
516  // vzMin Minimum @f$ v_z@f$ (in centimeters)
517  // vzMax Maximum @f$ v_z@f$ (in centimeters)
518  // cMin Minimum centrality (in percent)
519  // cMax Maximum centrality (in percent)
520  // hist Histogram to fill
521  //
522  // Return:
523  // @c true if the event meets the requirements
524  //
525  if (cMin < cMax && (cMin > fCentrality || cMax <= fCentrality)) {
526  if (status) status->Fill(kWrongCentrality);
527  return false;
528  }
529  FillTriggerHistogram(triggerMask, fTriggers, hist);
530 
531  // Check if we have an event of interest.
532  if (!IsTriggerBits(triggerMask)) {
533  if (status) status->Fill(kWrongTrigger);
534  return false;
535  }
536  return FilterEvent(vzMin,vzMax,hist,status,filterMask);
537 }
538 
539 //____________________________________________________________________
540 void
542 {
543  // Print this object
544  //
545  // Parameters:
546  // option Passed to TH1::Print
547  fHist.Print(option);
548  UShort_t sys = GetSystem();
549  TString str = "unknown";
550  switch (sys) {
551  case 1: str = "pp"; break;
552  case 2: str = "PbPb"; break;
553  case 3: str = "pPb" ; break;
554  }
555  std::cout << "Ipz: " << fIpZ << "cm " << (HasIpZ() ? "" : "in")
556  << "valid\n"
557  << "Triggers: " << GetTriggerString(fTriggers) << "\n"
558  << "sNN: " << GetSNN() << "GeV\n"
559  << "System: " << str << "\n"
560  << "Centrality: " << fCentrality << "%"
561  << std::endl;
562 }
563 
564 //____________________________________________________________________
565 //
566 // EOF
567 //
UShort_t GetSystem() const
static UInt_t MakeTriggerMask(const char *what, const char *sep="&")
double Double_t
Definition: External.C:58
static void FillTriggerHistogram(UInt_t triggerMask, UInt_t trg, TH1 *hist)
TH1 * GetPhiAcceptance() const
void SetSNN(UShort_t sNN)
void Clear(Option_t *option="")
char Char_t
Definition: External.C:18
static TH1I * MakeTriggerHistogram(const char *name="triggers", UInt_t mask=0)
UShort_t GetSNN() const
void FillPhiAcceptance(TH1 &h) const
Per-event per bin.
int Int_t
Definition: External.C:63
void Init(const TAxis &etaAxis)
Definition: External.C:204
unsigned int UInt_t
Definition: External.C:33
static Bool_t IsTriggerBits(UInt_t bits, UInt_t trg)
float Float_t
Definition: External.C:68
TH1 * GetEtaCoverage() const
void SetSystem(UShort_t sys)
Bool_t HasIpZ() const
Bool_t FilterEvent(Double_t vzMin, Double_t vzMax, TH1 *hist, TH1 *status, UInt_t filterMask) const
void Print(Option_t *option="") const
static const Char_t * GetTriggerString(UInt_t mask, const char *sep="&")
void Browse(TBrowser *b)
static TH1I * MakeStatusHistogram(const char *name="status")
Bool_t isMC
Bool_t InRange(Float_t low, Float_t high) const
Int_t GetEtaCoverageBin() const
static const Float_t fgkInvalidIpZ
unsigned short UShort_t
Definition: External.C:28
const char Option_t
Definition: External.C:48
bool Bool_t
Definition: External.C:53
void FillEtaCoverage(TH1 &h) const
Int_t GetPhiAcceptanceBin() const
Bool_t CheckEvent(UInt_t triggerMask=kInel, Double_t vzMin=-10, Double_t vzMax=10, Double_t cMin=0, Double_t cMax=100, TH1 *hist=0, TH1 *status=0, UInt_t filterMask=kDefaultFilter) const
Definition: External.C:196