]> git.uio.no Git - u/mrichter/AliRoot.git/blame - TPC/AliTPCdataQA.cxx
Coding Conventions
[u/mrichter/AliRoot.git] / TPC / AliTPCdataQA.cxx
CommitLineData
0ffacf98 1/**************************************************************************
2 * Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
3 * *
4 * Author: The ALICE Off-line Project. *
5 * Contributors are mentioned in the code where appropriate. *
6 * *
7 * Permission to use, copy, modify and distribute this software and its *
8 * documentation strictly for non-commercial purposes is hereby granted *
9 * without fee, provided that the above copyright notice appears in all *
10 * copies and that both the copyright notice and this permission notice *
11 * appear in the supporting documentation. The authors make no claims *
12 * about the suitability of this software for any purpose. It is *
13 * provided "as is" without express or implied warranty. *
14 **************************************************************************/
15
16
17/* $Id$ */
18
266f8637 19/*
20 February 2008
21
22 The code has been heavily modified so that now the RAW data is
23 "expanded" for each sector and stored in a big signal array. Then a
24 simple version of the code in AliTPCclustererMI is used to identify
25 the local maxima and these are then used for QA. This gives a better
26 estimate of the charge (both max and total) and also limits the
27 effect of noise.
28
29 Implementation:
30
31 In Update the RAW signals >= 3 ADC channels are stored in the arrays.
32
33 There are 3 arrays:
34 Float_t** fAllBins 2d array [row][bin(pad, time)] ADC signal
35 Int_t** fAllSigBins 2d array [row][signal#] bin(with signal)
36 Int_t* fAllNSigBins; 1d array [row] Nsignals
37
38 This is done sector by sector.
39
40 When data from a new sector is encountered, the method
41 FindLocalMaxima is called on the data from the previous sector, and
42 the calibration/data objects are updated with the "cluster"
43 info. Finally the arrays are cleared.
44
45 The requirements for a local maxima is:
46 Charge in bin is >= 5 ADC channels.
47 Charge in bin is larger than all the 8 neighboring bins.
48 At least one of the two pad neighbors has a signal.
49 At least one of the two time neighbors has a signal.
50
51 Before accessing the data it is expected that the Analyse method is
52 called. This normalizes some of the data objects to per event or per
53 cluster.
54 If more data is passed to the class after Analyse has been called
55 the normalization is reversed and Analyse has to be called again.
56*/
57
0ffacf98 58
f11b3071 59// stl includes
60#include <iostream>
61
62using namespace std;
63
0ffacf98 64//Root includes
65#include <TH1F.h>
66#include <TH2F.h>
67#include <TString.h>
68#include <TMath.h>
0ffacf98 69#include <TDirectory.h>
70#include <TFile.h>
266f8637 71#include <TError.h>
ac940b58 72#include <TMap.h>
6a50ff96 73#include <TProfile.h>
0ffacf98 74//AliRoot includes
75#include "AliRawReader.h"
76#include "AliRawReaderRoot.h"
77#include "AliRawReaderDate.h"
78#include "AliTPCRawStream.h"
0c25417d 79#include "AliTPCRawStreamV3.h"
0ffacf98 80#include "AliTPCCalROC.h"
81#include "AliTPCROC.h"
82#include "AliMathBase.h"
83#include "TTreeStream.h"
84#include "AliTPCRawStreamFast.h"
85
86//date
87#include "event.h"
88#include "AliTPCCalPad.h"
258cd111 89#include "AliTPCPreprocessorOnline.h"
0ffacf98 90
91//header file
92#include "AliTPCdataQA.h"
ce0175fa 93#include "AliLog.h"
0ffacf98 94
0ffacf98 95ClassImp(AliTPCdataQA)
96
336156cc 97AliTPCdataQA::AliTPCdataQA() : /*FOLD00*/
0ffacf98 98 fFirstTimeBin(60),
99 fLastTimeBin(1000),
100 fAdcMin(1),
101 fAdcMax(100),
0ffacf98 102 fMapping(NULL),
f11b3071 103 fPedestal(0),
104 fNoise(0),
266f8637 105 fNLocalMaxima(0),
0ffacf98 106 fMaxCharge(0),
f11b3071 107 fMeanCharge(0),
108 fNoThreshold(0),
266f8637 109 fNTimeBins(0),
110 fNPads(0),
111 fTimePosition(0),
c94a79e1 112 fOverThreshold10(0),
113 fOverThreshold20(0),
114 fOverThreshold30(0),
23c9ab21 115 fHistQVsTimeSideA(0),
116 fHistQVsTimeSideC(0),
117 fHistQMaxVsTimeSideA(0),
118 fHistQMaxVsTimeSideC(0),
ce0175fa 119 fHistOccupancyVsEvent(0),
120 fHistNclustersVsEvent(0),
266f8637 121 fEventCounter(0),
122 fIsAnalysed(kFALSE),
ce0175fa 123 fMaxEvents(500000), // Max events for event histograms
124 fEventsPerBin(1000), // Events per bin for event histograms
125 fSignalCounter(0), // Signal counter
126 fClusterCounter(0), // Cluster counter
266f8637 127 fAllBins(0),
128 fAllSigBins(0),
129 fAllNSigBins(0),
130 fRowsMax(0),
131 fPadsMax(0),
132 fTimeBinsMax(0)
0ffacf98 133{
134 //
135 // default constructor
136 //
137}
138
57acd2d2 139//_____________________________________________________________________
140AliTPCdataQA::AliTPCdataQA(AliRecoParam::EventSpecie_t es) :
141fFirstTimeBin(60),
142fLastTimeBin(1000),
143fAdcMin(1),
144fAdcMax(100),
145fMapping(NULL),
146fPedestal(0),
147fNoise(0),
148fNLocalMaxima(0),
149fMaxCharge(0),
150fMeanCharge(0),
151fNoThreshold(0),
152fNTimeBins(0),
153fNPads(0),
154fTimePosition(0),
155fOverThreshold10(0),
156fOverThreshold20(0),
157fOverThreshold30(0),
23c9ab21 158fHistQVsTimeSideA(0),
159fHistQVsTimeSideC(0),
160fHistQMaxVsTimeSideA(0),
161fHistQMaxVsTimeSideC(0),
ce0175fa 162fHistOccupancyVsEvent(0),
163fHistNclustersVsEvent(0),
57acd2d2 164fEventCounter(0),
165fIsAnalysed(kFALSE),
ce0175fa 166fMaxEvents(500000),
167fEventsPerBin(1000),
168fSignalCounter(0),
169fClusterCounter(0),
57acd2d2 170fAllBins(0),
171fAllSigBins(0),
172fAllNSigBins(0),
173fRowsMax(0),
174fPadsMax(0),
175fTimeBinsMax(0)
176{
177// ctor creating the histogram
178 char * name = Form("TPCRAW_%s", AliRecoParam::GetEventSpecieName(es)) ;
179 TH1F(name, name,100,0,100) ;
180}
0ffacf98 181
182//_____________________________________________________________________
183AliTPCdataQA::AliTPCdataQA(const AliTPCdataQA &ped) : /*FOLD00*/
336156cc 184 TH1F(ped),
0ffacf98 185 fFirstTimeBin(ped.GetFirstTimeBin()),
186 fLastTimeBin(ped.GetLastTimeBin()),
187 fAdcMin(ped.GetAdcMin()),
188 fAdcMax(ped.GetAdcMax()),
266f8637 189 fMapping(NULL),
190 fPedestal(0),
191 fNoise(0),
192 fNLocalMaxima(0),
193 fMaxCharge(0),
194 fMeanCharge(0),
195 fNoThreshold(0),
266f8637 196 fNTimeBins(0),
197 fNPads(0),
198 fTimePosition(0),
c94a79e1 199 fOverThreshold10(0),
200 fOverThreshold20(0),
201 fOverThreshold30(0),
23c9ab21 202 fHistQVsTimeSideA(0),
203 fHistQVsTimeSideC(0),
204 fHistQMaxVsTimeSideA(0),
205 fHistQMaxVsTimeSideC(0),
ce0175fa 206 fHistOccupancyVsEvent(0),
207 fHistNclustersVsEvent(0),
266f8637 208 fEventCounter(ped.GetEventCounter()),
209 fIsAnalysed(ped.GetIsAnalysed()),
ce0175fa 210 fMaxEvents(ped.GetMaxEvents()),
211 fEventsPerBin(ped.GetEventsPerBin()),
212 fSignalCounter(ped.GetSignalCounter()),
213 fClusterCounter(ped.GetClusterCounter()),
266f8637 214 fAllBins(0),
215 fAllSigBins(0),
216 fAllNSigBins(0),
217 fRowsMax(0),
218 fPadsMax(0),
219 fTimeBinsMax(0)
0ffacf98 220{
221 //
222 // copy constructor
223 //
266f8637 224 if(ped.GetNLocalMaxima())
225 fNLocalMaxima = new AliTPCCalPad(*ped.GetNLocalMaxima());
226 if(ped.GetMaxCharge())
227 fMaxCharge = new AliTPCCalPad(*ped.GetMaxCharge());
228 if(ped.GetMeanCharge())
229 fMeanCharge = new AliTPCCalPad(*ped.GetMeanCharge());
230 if(ped.GetNoThreshold())
231 fNoThreshold = new AliTPCCalPad(*ped.GetNoThreshold());
232 if(ped.GetNTimeBins())
233 fNTimeBins = new AliTPCCalPad(*ped.GetNTimeBins());
234 if(ped.GetNPads())
235 fNPads = new AliTPCCalPad(*ped.GetNPads());
236 if(ped.GetTimePosition())
237 fTimePosition = new AliTPCCalPad(*ped.GetTimePosition());
238 if(ped.GetOverThreshold10())
239 fOverThreshold10 = new AliTPCCalPad(*ped.GetOverThreshold10());
240 if(ped.GetOverThreshold20())
241 fOverThreshold20 = new AliTPCCalPad(*ped.GetOverThreshold20());
242 if(ped.GetOverThreshold30())
243 fOverThreshold30 = new AliTPCCalPad(*ped.GetOverThreshold30());
1cb9ffdb 244 if(ped.GetHistQVsTimeSideA()) {
23c9ab21 245 fHistQVsTimeSideA = new TProfile(*ped.GetHistQVsTimeSideA());
1cb9ffdb 246 fHistQVsTimeSideA->SetDirectory(0);
247 }
248 if(ped.GetHistQVsTimeSideC()) {
23c9ab21 249 fHistQVsTimeSideC = new TProfile(*ped.GetHistQVsTimeSideC());
1cb9ffdb 250 fHistQVsTimeSideC->SetDirectory(0);
251 }
252 if(ped.GetHistQMaxVsTimeSideA()) {
23c9ab21 253 fHistQMaxVsTimeSideA = new TProfile(*ped.GetHistQMaxVsTimeSideA());
1cb9ffdb 254 fHistQMaxVsTimeSideA->SetDirectory(0);
255 }
256 if(ped.GetHistQMaxVsTimeSideC()) {
23c9ab21 257 fHistQMaxVsTimeSideC = new TProfile(*ped.GetHistQMaxVsTimeSideC());
1cb9ffdb 258 fHistQMaxVsTimeSideC->SetDirectory(0);
259 }
ce0175fa 260 if(ped.GetHistOccupancyVsEventConst()) {
261 fHistOccupancyVsEvent = new TH1F(*ped.GetHistOccupancyVsEventConst());
262 fHistOccupancyVsEvent->SetDirectory(0);
263 }
264 if(ped.GetHistNclustersVsEventConst()) {
265 fHistNclustersVsEvent = new TH1F(*ped.GetHistNclustersVsEventConst());
266 fHistNclustersVsEvent->SetDirectory(0);
267 }
0ffacf98 268}
269
ac940b58 270//_____________________________________________________________________
8ba97cc8 271AliTPCdataQA::AliTPCdataQA(const TMap *config) : /*FOLD00*/
ac940b58 272 TH1F("TPCRAW","TPCRAW",100,0,100),
273 fFirstTimeBin(60),
274 fLastTimeBin(1000),
275 fAdcMin(1),
276 fAdcMax(100),
277 fMapping(NULL),
278 fPedestal(0),
279 fNoise(0),
280 fNLocalMaxima(0),
281 fMaxCharge(0),
282 fMeanCharge(0),
283 fNoThreshold(0),
284 fNTimeBins(0),
285 fNPads(0),
286 fTimePosition(0),
287 fOverThreshold10(0),
288 fOverThreshold20(0),
289 fOverThreshold30(0),
23c9ab21 290 fHistQVsTimeSideA(0),
291 fHistQVsTimeSideC(0),
292 fHistQMaxVsTimeSideA(0),
293 fHistQMaxVsTimeSideC(0),
ce0175fa 294 fHistOccupancyVsEvent(0),
295 fHistNclustersVsEvent(0),
ac940b58 296 fEventCounter(0),
297 fIsAnalysed(kFALSE),
ce0175fa 298 fMaxEvents(500000),
299 fEventsPerBin(1000),
300 fSignalCounter(0),
301 fClusterCounter(0),
ac940b58 302 fAllBins(0),
303 fAllSigBins(0),
304 fAllNSigBins(0),
305 fRowsMax(0),
306 fPadsMax(0),
307 fTimeBinsMax(0)
308{
309 //
310 // default constructor
311 //
312 if (config->GetValue("FirstTimeBin")) fFirstTimeBin = ((TObjString*)config->GetValue("FirstTimeBin"))->GetString().Atoi();
313 if (config->GetValue("LastTimeBin")) fLastTimeBin = ((TObjString*)config->GetValue("LastTimeBin"))->GetString().Atoi();
314 if (config->GetValue("AdcMin")) fAdcMin = ((TObjString*)config->GetValue("AdcMin"))->GetString().Atoi();
315 if (config->GetValue("AdcMax")) fAdcMax = ((TObjString*)config->GetValue("AdcMax"))->GetString().Atoi();
ce0175fa 316 if (config->GetValue("MaxEvents")) fMaxEvents = ((TObjString*)config->GetValue("MaxEvents"))->GetString().Atoi();
317 if (config->GetValue("EventsPerBin")) fAdcMax = ((TObjString*)config->GetValue("EventsPerBin"))->GetString().Atoi();
ac940b58 318}
0ffacf98 319
320//_____________________________________________________________________
321AliTPCdataQA& AliTPCdataQA::operator = (const AliTPCdataQA &source)
322{
323 //
324 // assignment operator
325 //
326 if (&source == this) return *this;
327 new (this) AliTPCdataQA(source);
328
329 return *this;
330}
331
332
333//_____________________________________________________________________
334AliTPCdataQA::~AliTPCdataQA() /*FOLD00*/
335{
336 //
337 // destructor
338 //
339
340 // do not delete fMapping, because we do not own it.
266f8637 341 // do not delete fMapping, because we do not own it.
342 // do not delete fNoise and fPedestal, because we do not own them.
343
344 delete fNLocalMaxima;
345 delete fMaxCharge;
346 delete fMeanCharge;
347 delete fNoThreshold;
348 delete fNTimeBins;
349 delete fNPads;
350 delete fTimePosition;
351 delete fOverThreshold10;
352 delete fOverThreshold20;
353 delete fOverThreshold30;
23c9ab21 354 delete fHistQVsTimeSideA;
355 delete fHistQVsTimeSideC;
356 delete fHistQMaxVsTimeSideA;
357 delete fHistQMaxVsTimeSideC;
ce0175fa 358 delete fHistOccupancyVsEvent;
359 delete fHistNclustersVsEvent;
266f8637 360
361 for (Int_t iRow = 0; iRow < fRowsMax; iRow++) {
362 delete [] fAllBins[iRow];
363 delete [] fAllSigBins[iRow];
364 }
365 delete [] fAllBins;
366 delete [] fAllSigBins;
367 delete [] fAllNSigBins;
0ffacf98 368}
ce0175fa 369
370//_____________________________________________________________________
371TH1F* AliTPCdataQA::GetHistOccupancyVsEvent()
372{
373 //
374 // Create Occupancy vs event histogram
375 // (we create this histogram differently then the other histograms
376 // because this we want to be able to access and copy
377 // from AliTPCQAMakerRec before it normally would be created)
378 //
379 if(!fHistOccupancyVsEvent) {
380
381 Int_t nBins = fMaxEvents/fEventsPerBin;
382 fHistOccupancyVsEvent = new TH1F("hOccupancyVsEvent", "Occupancy vs event number (~time); Event number; Occupancy", nBins, 0, nBins*fEventsPerBin);
383 fHistOccupancyVsEvent->SetDirectory(0);
384 fHistOccupancyVsEvent->GetXaxis()->SetRange(0, 10);
385 }
386
387 return fHistOccupancyVsEvent;
388}
389
390//_____________________________________________________________________
391TH1F* AliTPCdataQA::GetHistNclustersVsEvent()
392{
393 //
394 // Create Nclusters vs event histogram
395 // (we create this histogram differently then the other histograms
396 // because this we want to be able to access and copy
397 // from AliTPCQAMakerRec before it normally would be created)
398 //
399 if(!fHistNclustersVsEvent) {
400
401 Int_t nBins = fMaxEvents/fEventsPerBin;
402 fHistNclustersVsEvent = new TH1F("hNclustersVsEvent", "Nclusters vs event number (~time); Event number; Nclusters per event", nBins, 0, nBins*fEventsPerBin);
403 fHistNclustersVsEvent->SetDirectory(0);
404 fHistNclustersVsEvent->GetXaxis()->SetRange(0, 10);
405 }
406
407 return fHistNclustersVsEvent;
408}
409
410//_____________________________________________________________________
411void AliTPCdataQA::UpdateEventHistograms()
412{
413 // Update histograms that display occupancy and
414 // number of clusters as a function of number of
415 // events
416 if (!fHistOccupancyVsEvent)
417 GetHistOccupancyVsEvent();
418 if (!fHistNclustersVsEvent)
419 GetHistNclustersVsEvent();
420
421 Float_t averageOccupancy =
422 Float_t(fSignalCounter)/fEventsPerBin/(fLastTimeBin - fFirstTimeBin +1.0)
423 / 570132; // 570,132 is number of pads
424 if(fEventCounter<=fMaxEvents)
425 UpdateEventHisto(fHistOccupancyVsEvent, averageOccupancy);
426 fSignalCounter = 0;
427
428 Float_t averageNclusters =
429 Float_t(fClusterCounter)/fEventsPerBin;
430 if(fEventCounter<=fMaxEvents)
431 UpdateEventHisto(fHistNclustersVsEvent, averageNclusters);
432 fClusterCounter = 0;
433}
434
435//_____________________________________________________________________
436void AliTPCdataQA::UpdateEventHisto(TH1F* hist, Float_t average)
437{
438 // Do the actually updating of each histogram and
439 // change the visible range if needed
440
441 // in case someone would have overwritten the value here
442 // (not so pretty but OK for this I think)
443 fEventsPerBin = hist->GetXaxis()->GetBinWidth(1);
444 Int_t bin = TMath::Nint(Float_t(fEventCounter)/fEventsPerBin);
445
446 if (hist->GetBinContent(bin)>0) {
447 AliError("Bin already filled. This should not happen.");
448 } else {
449 hist->SetBinContent(bin, average);
450 }
451
452 // expand the visible range of the histogram if needed
453 if(hist->GetXaxis()->GetLast()<= bin) {
454 hist->GetXaxis()->SetRange(0, Int_t(1.3*bin));
455 }
456}
457
0c25417d 458//_____________________________________________________________________
6a50ff96 459Bool_t AliTPCdataQA::ProcessEvent(AliTPCRawStreamV3 *const rawStreamV3)
0c25417d 460{
461 //
462 // Event Processing loop - AliTPCRawStreamV3
463 //
464 Bool_t withInput = kFALSE;
465 Int_t nSignals = 0;
466 Int_t lastSector = -1;
467
468 while ( rawStreamV3->NextDDL() ){
469 while ( rawStreamV3->NextChannel() ){
470 Int_t iSector = rawStreamV3->GetSector(); // current sector
471 Int_t iRow = rawStreamV3->GetRow(); // current row
472 Int_t iPad = rawStreamV3->GetPad(); // current pad
473 if (iRow<0 || iPad<0) continue;
474 // Call local maxima finder if the data is in a new sector
475 if(iSector != lastSector) {
476
477 if(nSignals>0)
478 FindLocalMaxima(lastSector);
479
480 CleanArrays();
481 lastSector = iSector;
482 nSignals = 0;
483 }
484
485 while ( rawStreamV3->NextBunch() ){
486 Int_t startTbin = (Int_t)rawStreamV3->GetStartTimeBin();
487 Int_t bunchlength = (Int_t)rawStreamV3->GetBunchLength();
488 const UShort_t *sig = rawStreamV3->GetSignals();
489
490 for (Int_t iTimeBin = 0; iTimeBin<bunchlength; iTimeBin++){
491 Float_t signal=(Float_t)sig[iTimeBin];
492 nSignals += Update(iSector,iRow,iPad,startTbin--,signal);
493 withInput = kTRUE;
494 }
495 }
496 }
497 }
498
499 if (lastSector>=0&&nSignals>0)
500 FindLocalMaxima(lastSector);
501
502 return withInput;
503}
504
505//_____________________________________________________________________
6a50ff96 506Bool_t AliTPCdataQA::ProcessEvent(AliRawReader *const rawReader)
0c25417d 507{
508 //
509 // Event processing loop - AliRawReader
510 //
c75ba816 511 AliTPCRawStreamV3 rawStreamV3(rawReader, (AliAltroMapping**)fMapping);
512 Bool_t res=ProcessEvent(&rawStreamV3);
ce0175fa 513 if(res) {
0c25417d 514 fEventCounter++; // only increment event counter if there is TPC data
ce0175fa 515
516 if(fEventCounter%fEventsPerBin==0)
517 UpdateEventHistograms();
518 }
0c25417d 519 return res;
520}
0ffacf98 521
0ffacf98 522//_____________________________________________________________________
6a50ff96 523Bool_t AliTPCdataQA::ProcessEventFast(AliTPCRawStreamFast *const rawStreamFast)
0ffacf98 524{
525 //
526 // Event Processing loop - AliTPCRawStream
527 //
528 Bool_t withInput = kFALSE;
266f8637 529 Int_t nSignals = 0;
530 Int_t lastSector = -1;
0ffacf98 531
532 while ( rawStreamFast->NextDDL() ){
0c25417d 533 while ( rawStreamFast->NextChannel() ){
534
535 Int_t iSector = rawStreamFast->GetSector(); // current sector
536 Int_t iRow = rawStreamFast->GetRow(); // current row
537 Int_t iPad = rawStreamFast->GetPad(); // current pad
538 // Call local maxima finder if the data is in a new sector
539 if(iSector != lastSector) {
540
541 if(nSignals>0)
542 FindLocalMaxima(lastSector);
543
544 CleanArrays();
545 lastSector = iSector;
546 nSignals = 0;
0ffacf98 547 }
0c25417d 548
549 while ( rawStreamFast->NextBunch() ){
550 Int_t startTbin = (Int_t)rawStreamFast->GetStartTimeBin();
551 Int_t endTbin = (Int_t)rawStreamFast->GetEndTimeBin();
552
553 for (Int_t iTimeBin = startTbin; iTimeBin < endTbin; iTimeBin++){
554 Float_t signal = rawStreamFast->GetSignals()[iTimeBin-startTbin];
555 nSignals += Update(iSector,iRow,iPad,iTimeBin+1,signal);
556 withInput = kTRUE;
557 }
558 }
559 }
0ffacf98 560 }
266f8637 561
0ffacf98 562 return withInput;
563}
564//_____________________________________________________________________
6a50ff96 565Bool_t AliTPCdataQA::ProcessEventFast(AliRawReader *const rawReader)
0ffacf98 566{
567 //
568 // Event processing loop - AliRawReader
569 //
c75ba816 570 AliTPCRawStreamFast rawStreamFast(rawReader, (AliAltroMapping**)fMapping);
571 Bool_t res=ProcessEventFast(&rawStreamFast);
c75bf2f1 572 if(res)
573 fEventCounter++; // only increment event counter if there is TPC data
574 // otherwise Analyse (called in QA) fails
575
0ffacf98 576 return res;
577}
578
579//_____________________________________________________________________
6a50ff96 580Bool_t AliTPCdataQA::ProcessEvent(AliTPCRawStream *const rawStream)
0ffacf98 581{
582 //
583 // Event Processing loop - AliTPCRawStream
584 //
585
0ffacf98 586 Bool_t withInput = kFALSE;
266f8637 587 Int_t nSignals = 0;
588 Int_t lastSector = -1;
0ffacf98 589
590 while (rawStream->Next()) {
591
592 Int_t iSector = rawStream->GetSector(); // current ROC
593 Int_t iRow = rawStream->GetRow(); // current row
594 Int_t iPad = rawStream->GetPad(); // current pad
595 Int_t iTimeBin = rawStream->GetTime(); // current time bin
596 Float_t signal = rawStream->GetSignal(); // current ADC signal
0c25417d 597
266f8637 598 // Call local maxima finder if the data is in a new sector
599 if(iSector != lastSector) {
600
601 if(nSignals>0)
0c25417d 602 FindLocalMaxima(lastSector);
266f8637 603
604 CleanArrays();
605 lastSector = iSector;
606 nSignals = 0;
607 }
0ffacf98 608
266f8637 609 // Sometimes iRow==-1 if there is problems to read the data
610 if(iRow>=0) {
611 nSignals += Update(iSector,iRow,iPad,iTimeBin,signal);
612 withInput = kTRUE;
613 }
0ffacf98 614 }
615
0c25417d 616 if (lastSector>=0&&nSignals>0)
617 FindLocalMaxima(lastSector);
618
0ffacf98 619 return withInput;
620}
621
622
623//_____________________________________________________________________
6a50ff96 624Bool_t AliTPCdataQA::ProcessEventOld(AliRawReader *const rawReader)
0ffacf98 625{
626 //
627 // Event processing loop - AliRawReader
628 //
629
630 // if fMapping is NULL the rawstream will crate its own mapping
631 AliTPCRawStream rawStream(rawReader, (AliAltroMapping**)fMapping);
632 rawReader->Select("TPC");
c75bf2f1 633 Bool_t res = ProcessEvent(&rawStream);
634
635 if(res)
636 fEventCounter++; // only increment event counter if there is TPC data
637 // otherwise Analyse (called in QA) fails
638 return res;
0ffacf98 639}
640
641
642//_____________________________________________________________________
6a50ff96 643Bool_t AliTPCdataQA::ProcessEvent(eventHeaderStruct *const event)
0ffacf98 644{
645 //
646 // process date event
647 //
648
c75ba816 649 AliRawReaderDate rawReader((void*)event);
650 Bool_t result=ProcessEvent(&rawReader);
0ffacf98 651 return result;
652}
653
654
655
656//_____________________________________________________________________
657void AliTPCdataQA::DumpToFile(const Char_t *filename, const Char_t *dir, Bool_t append) /*FOLD00*/
658{
659 //
660 // Write class to file
661 //
662
663 TString sDir(dir);
664 TString option;
665
666 if ( append )
667 option = "update";
668 else
669 option = "recreate";
670
671 TDirectory *backup = gDirectory;
672 TFile f(filename,option.Data());
673 f.cd();
674 if ( !sDir.IsNull() ){
675 f.mkdir(sDir.Data());
676 f.cd(sDir);
677 }
678 this->Write();
679 f.Close();
680
681 if ( backup ) backup->cd();
682}
683
684
685//_____________________________________________________________________
266f8637 686Int_t AliTPCdataQA::Update(const Int_t iSector, /*FOLD00*/
687 const Int_t iRow,
688 const Int_t iPad,
689 const Int_t iTimeBin,
690 Float_t signal)
0ffacf98 691{
692 //
693 // Signal filling method
694 //
266f8637 695
696 //
697 // Define the calibration objects the first time Update is called
698 // NB! This has to be done first even if the data is rejected by the time
699 // cut to make sure that the objects are available in Analyse
700 //
701 if (!fNLocalMaxima) fNLocalMaxima = new AliTPCCalPad("NLocalMaxima","NLocalMaxima");
0ffacf98 702 if (!fMaxCharge) fMaxCharge = new AliTPCCalPad("MaxCharge","MaxCharge");
f11b3071 703 if (!fMeanCharge) fMeanCharge = new AliTPCCalPad("MeanCharge","MeanCharge");
704 if (!fNoThreshold) fNoThreshold = new AliTPCCalPad("NoThreshold","NoThreshold");
266f8637 705 if (!fNTimeBins) fNTimeBins = new AliTPCCalPad("NTimeBins","NTimeBins");
706 if (!fNPads) fNPads = new AliTPCCalPad("NPads","NPads");
707 if (!fTimePosition) fTimePosition = new AliTPCCalPad("TimePosition","TimePosition");
0ffacf98 708 if (!fOverThreshold10) fOverThreshold10 = new AliTPCCalPad("OverThreshold10","OverThreshold10");
709 if (!fOverThreshold20) fOverThreshold20 = new AliTPCCalPad("OverThreshold20","OverThreshold20");
710 if (!fOverThreshold30) fOverThreshold30 = new AliTPCCalPad("OverThreshold30","OverThreshold30");
1cb9ffdb 711 if (!fHistQVsTimeSideA) {
23c9ab21 712 fHistQVsTimeSideA = new TProfile("hQVsTimeSideA", "Q vs time (side A); Time [Timebin]; Q [ADC ch]", 100, 0, 1000);
1cb9ffdb 713 fHistQVsTimeSideA->SetDirectory(0);
714 }
715 if (!fHistQVsTimeSideC) {
23c9ab21 716 fHistQVsTimeSideC = new TProfile("hQVsTimeSideC", "Q vs time (side C); Time [Timebin]; Q [ADC ch]", 100, 0, 1000);
1cb9ffdb 717 fHistQVsTimeSideC->SetDirectory(0);
718 }
719 if (!fHistQMaxVsTimeSideA) {
23c9ab21 720 fHistQMaxVsTimeSideA = new TProfile("hQMaxVsTimeSideA", "Q_{MAX} vs time (side A); Time [Timebin]; Q_{MAX} [ADC ch]", 100, 0, 1000);
1cb9ffdb 721 fHistQMaxVsTimeSideA->SetDirectory(0);
722 }
723 if (!fHistQMaxVsTimeSideC) {
23c9ab21 724 fHistQMaxVsTimeSideC = new TProfile("hQMaxVsTimeSideC", "Q_{MAX} vs time (side C); Time [Timebin]; Q_{MAX} [ADC ch]", 100, 0, 1000);
1cb9ffdb 725 fHistQMaxVsTimeSideC->SetDirectory(0);
726 }
727
266f8637 728 // Make the arrays for expanding the data
f11b3071 729
266f8637 730 if (!fAllBins)
731 MakeArrays();
732
733 //
734 // If Analyse has been previously called we need now to denormalize the data
735 // as more data is coming
736 //
737 if(fIsAnalysed == kTRUE) {
738
739 const Int_t nTimeBins = fLastTimeBin - fFirstTimeBin +1;
740 const Float_t denormalization = Float_t(fEventCounter * nTimeBins);
741 fNoThreshold->Multiply(denormalization);
742
743 fMeanCharge->Multiply(fNLocalMaxima);
744 fMaxCharge->Multiply(fNLocalMaxima);
745 fNTimeBins->Multiply(fNLocalMaxima);
746 fNPads->Multiply(fNLocalMaxima);
747 fTimePosition->Multiply(fNLocalMaxima);
748 fIsAnalysed = kFALSE;
749 }
f11b3071 750
266f8637 751 //
752 // TimeBin cut
753 //
754 if (iTimeBin<fFirstTimeBin) return 0;
755 if (iTimeBin>fLastTimeBin) return 0;
756
f11b3071 757 // if pedestal calibrations are loaded subtract pedestals
758 if(fPedestal) {
759
266f8637 760 Float_t ped = fPedestal->GetCalROC(iSector)->GetValue(iRow, iPad);
761 // Don't use data from pads where pedestals are abnormally small or large
762 if(ped<10 || ped>90)
f11b3071 763 return 0;
266f8637 764 signal -= ped;
f11b3071 765 }
266f8637 766
767 // In fNoThreshold we fill all data to estimate the ZS volume
768 Float_t count = fNoThreshold->GetCalROC(iSector)->GetValue(iRow, iPad);
769 fNoThreshold->GetCalROC(iSector)->SetValue(iRow, iPad,count+1);
770
f11b3071 771 // Require at least 3 ADC channels
266f8637 772 if (signal < 3.0)
f11b3071 773 return 0;
774
775 // if noise calibrations are loaded require at least 3*sigmaNoise
776 if(fNoise) {
266f8637 777
778 Float_t noise = fNoise->GetCalROC(iSector)->GetValue(iRow, iPad);
779
780 if(signal < noise*3.0)
f11b3071 781 return 0;
782 }
266f8637 783
f11b3071 784 //
266f8637 785 // This signal is ok and we store it in the cluster map
0ffacf98 786 //
f11b3071 787
266f8637 788 SetExpandDigit(iRow, iPad, iTimeBin, signal);
ce0175fa 789
790 fSignalCounter++;
266f8637 791
792 return 1; // signal was accepted
f11b3071 793}
266f8637 794
f11b3071 795//_____________________________________________________________________
266f8637 796void AliTPCdataQA::FindLocalMaxima(const Int_t iSector)
f11b3071 797{
798 //
266f8637 799 // This method is called after the data from each sector has been
800 // exapanded into an array
801 // Loop over the signals and identify local maxima and fill the
802 // calibration objects with the information
f11b3071 803 //
266f8637 804
805 Int_t nLocalMaxima = 0;
806 const Int_t maxTimeBin = fTimeBinsMax+4; // Used to step between neighboring pads
807 // Because we have tha pad-time data in a
808 // 1d array
809
810 for (Int_t iRow = 0; iRow < fRowsMax; iRow++) {
811
812 Float_t* allBins = fAllBins[iRow];
813 Int_t* sigBins = fAllSigBins[iRow];
814 const Int_t nSigBins = fAllNSigBins[iRow];
815
816 for (Int_t iSig = 0; iSig < nSigBins; iSig++) {
817
818 Int_t bin = sigBins[iSig];
819 Float_t *b = &allBins[bin];
820
821 //
822 // Now we check if this is a local maximum
823 //
824
825 Float_t qMax = b[0];
826
827 // First check that the charge is bigger than the threshold
828 if (qMax<5)
829 continue;
830
831 // Require at least one neighboring pad with signal
832 if (b[-maxTimeBin]+b[maxTimeBin]<=0) continue;
833
834 // Require at least one neighboring time bin with signal
835 if (b[-1]+b[1]<=0) continue;
836
837 //
838 // Check that this is a local maximum
839 // Note that the checking is done so that if 2 charges has the same
840 // qMax then only 1 cluster is generated
841 // (that is why there is BOTH > and >=)
842 //
843 if (b[-maxTimeBin] >= qMax) continue;
844 if (b[-1 ] >= qMax) continue;
845 if (b[+maxTimeBin] > qMax) continue;
846 if (b[+1 ] > qMax) continue;
847 if (b[-maxTimeBin-1] >= qMax) continue;
848 if (b[+maxTimeBin-1] >= qMax) continue;
849 if (b[+maxTimeBin+1] > qMax) continue;
850 if (b[-maxTimeBin+1] >= qMax) continue;
851
852 //
853 // Now we accept the local maximum and fill the calibration/data objects
854 //
855 nLocalMaxima++;
856
857 Int_t iPad, iTimeBin;
858 GetPadAndTimeBin(bin, iPad, iTimeBin);
859
860 Float_t count = fNLocalMaxima->GetCalROC(iSector)->GetValue(iRow, iPad);
861 fNLocalMaxima->GetCalROC(iSector)->SetValue(iRow, iPad, count+1);
862
863 count = fTimePosition->GetCalROC(iSector)->GetValue(iRow, iPad);
864 fTimePosition->GetCalROC(iSector)->SetValue(iRow, iPad, count+iTimeBin);
865
866 Float_t charge = fMaxCharge->GetCalROC(iSector)->GetValue(iRow, iPad);
867 fMaxCharge->GetCalROC(iSector)->SetValue(iRow, iPad, charge + qMax);
868
869 if(qMax>=10) {
870 count = fOverThreshold10->GetCalROC(iSector)->GetValue(iRow, iPad);
871 fOverThreshold10->GetCalROC(iSector)->SetValue(iRow, iPad, count+1);
872 }
873 if(qMax>=20) {
874 count = fOverThreshold20->GetCalROC(iSector)->GetValue(iRow, iPad);
875 fOverThreshold20->GetCalROC(iSector)->SetValue(iRow, iPad, count+1);
876 }
877 if(qMax>=30) {
878 count = fOverThreshold30->GetCalROC(iSector)->GetValue(iRow, iPad);
879 fOverThreshold30->GetCalROC(iSector)->SetValue(iRow, iPad, count+1);
880 }
881
882 //
883 // Calculate the total charge as the sum over the region:
884 //
885 // o o o o o
886 // o i i i o
887 // o i C i o
888 // o i i i o
889 // o o o o o
890 //
891 // with qmax at the center C.
892 //
893 // The inner charge (i) we always add, but we only add the outer
894 // charge (o) if the neighboring inner bin (i) has a signal.
895 //
896 Int_t minP = 0, maxP = 0, minT = 0, maxT = 0;
897 Float_t qTot = qMax;
898 for(Int_t i = -1; i<=1; i++) {
899 for(Int_t j = -1; j<=1; j++) {
900
901 if(i==0 && j==0)
902 continue;
903
77f88633 904 Float_t charge1 = GetQ(b, i, j, maxTimeBin, minT, maxT, minP, maxP);
905 qTot += charge1;
906 if(charge1>0) {
266f8637 907 // see if the next neighbor is also above threshold
908 if(i*j==0) {
909 qTot += GetQ(b, 2*i, 2*j, maxTimeBin, minT, maxT, minP, maxP);
910 } else {
911 // we are in a diagonal corner
912 qTot += GetQ(b, i, 2*j, maxTimeBin, minT, maxT, minP, maxP);
913 qTot += GetQ(b, 2*i, j, maxTimeBin, minT, maxT, minP, maxP);
914 qTot += GetQ(b, 2*i, 2*j, maxTimeBin, minT, maxT, minP, maxP);
915 }
916 }
917 }
918 }
919
920 charge = fMeanCharge->GetCalROC(iSector)->GetValue(iRow, iPad);
921 fMeanCharge->GetCalROC(iSector)->SetValue(iRow, iPad, charge + qTot);
922
923 count = fNTimeBins->GetCalROC(iSector)->GetValue(iRow, iPad);
924 fNTimeBins->GetCalROC(iSector)->SetValue(iRow, iPad, count + maxT-minT+1);
925
926 count = fNPads->GetCalROC(iSector)->GetValue(iRow, iPad);
927 fNPads->GetCalROC(iSector)->SetValue(iRow, iPad, count + maxP-minP+1);
928
23c9ab21 929 if((iSector%36)<18) { // A side
930 fHistQVsTimeSideA->Fill(iTimeBin, qTot);
931 fHistQMaxVsTimeSideA->Fill(iTimeBin, qMax);
932 } else {
933 fHistQVsTimeSideC->Fill(iTimeBin, qTot);
934 fHistQMaxVsTimeSideC->Fill(iTimeBin, qMax);
935 }
266f8637 936 } // end loop over signals
937 } // end loop over rows
f11b3071 938
266f8637 939 // cout << "Number of local maximas found: " << nLocalMaxima << endl;
ce0175fa 940 fClusterCounter += nLocalMaxima;
0ffacf98 941}
11ccf1c1 942
f11b3071 943//_____________________________________________________________________
944void AliTPCdataQA::Analyse()
945{
11ccf1c1 946 //
f11b3071 947 // Calculate calibration constants
11ccf1c1 948 //
f11b3071 949
950 cout << "Analyse called" << endl;
951
266f8637 952 if(fIsAnalysed == kTRUE) {
953
954 cout << "No new data since Analyse was called last time" << endl;
955 return;
956 }
f11b3071 957
266f8637 958 if(fEventCounter==0) {
959
f11b3071 960 cout << "EventCounter == 0, Cannot analyse" << endl;
961 return;
962 }
266f8637 963
f11b3071 964 Int_t nTimeBins = fLastTimeBin - fFirstTimeBin +1;
c75bf2f1 965 cout << "EventCounter: " << fEventCounter << endl
f11b3071 966 << "TimeBins: " << nTimeBins << endl;
967
f11b3071 968 Float_t normalization = 1.0 / Float_t(fEventCounter * nTimeBins);
266f8637 969 fNoThreshold->Multiply(normalization);
970
971 fMeanCharge->Divide(fNLocalMaxima);
972 fMaxCharge->Divide(fNLocalMaxima);
973 fNTimeBins->Divide(fNLocalMaxima);
974 fNPads->Divide(fNLocalMaxima);
975 fTimePosition->Divide(fNLocalMaxima);
976
977 fIsAnalysed = kTRUE;
11ccf1c1 978}
258cd111 979
980
266f8637 981//_____________________________________________________________________
6a50ff96 982void AliTPCdataQA::MakeTree(const char *fname) const {
258cd111 983 //
984 // Export result to the tree -located in the file
985 // This file can be analyzed using AliTPCCalibViewer
986 //
258cd111 987 AliTPCPreprocessorOnline preprocesor;
266f8637 988
989 if (fNLocalMaxima) preprocesor.AddComponent(fNLocalMaxima);
990 if (fMaxCharge) preprocesor.AddComponent(fMaxCharge);
991 if (fMeanCharge) preprocesor.AddComponent(fMeanCharge);
992 if (fNoThreshold) preprocesor.AddComponent(fNoThreshold);
993 if (fNTimeBins) preprocesor.AddComponent(fNTimeBins);
994 if (fNPads) preprocesor.AddComponent(fNPads);
995 if (fTimePosition) preprocesor.AddComponent(fTimePosition);
996 if (fOverThreshold10) preprocesor.AddComponent(fOverThreshold10);
997 if (fOverThreshold20) preprocesor.AddComponent(fOverThreshold20);
998 if (fOverThreshold30) preprocesor.AddComponent(fOverThreshold30);
999
258cd111 1000 preprocesor.DumpToFile(fname);
1001}
c322f08a 1002
1003
266f8637 1004//_____________________________________________________________________
c322f08a 1005void AliTPCdataQA::MakeArrays(){
1006 //
266f8637 1007 // The arrays for expanding the raw data are defined and
1008 // som parameters are intialised
c322f08a 1009 //
1010 AliTPCROC * roc = AliTPCROC::Instance();
1011 //
266f8637 1012 // To make the array big enough for all sectors we take
1013 // the dimensions from the outer row of an OROC (the last sector)
1014 //
1015 fRowsMax = roc->GetNRows(roc->GetNSector()-1);
1016 fPadsMax = roc->GetNPads(roc->GetNSector()-1,fRowsMax-1);
1017 fTimeBinsMax = fLastTimeBin - fFirstTimeBin +1;
1018
1019 //
1020 // Since we have added 2 pads (TimeBins) before and after the real pads (TimeBins)
1021 // to make sure that we can always query the exanded table even when the
1022 // max is on the edge
1023 //
1024
c322f08a 1025
266f8637 1026 fAllBins = new Float_t*[fRowsMax];
1027 fAllSigBins = new Int_t*[fRowsMax];
1028 fAllNSigBins = new Int_t[fRowsMax];
1029
1030 for (Int_t iRow = 0; iRow < fRowsMax; iRow++) {
c322f08a 1031 //
266f8637 1032 Int_t maxBin = (fTimeBinsMax+4)*(fPadsMax+4);
c322f08a 1033 fAllBins[iRow] = new Float_t[maxBin];
266f8637 1034 memset(fAllBins[iRow],0,sizeof(Float_t)*maxBin); // set all values to 0
c322f08a 1035 fAllSigBins[iRow] = new Int_t[maxBin];
266f8637 1036 fAllNSigBins[iRow] = 0;
c322f08a 1037 }
1038}
1039
1040
266f8637 1041//_____________________________________________________________________
c322f08a 1042void AliTPCdataQA::CleanArrays(){
1043 //
1044 //
1045 //
266f8637 1046
1047 for (Int_t iRow = 0; iRow < fRowsMax; iRow++) {
42919b08 1048
1049 // To speed up the performance by a factor 2 on cosmic data (and
1050 // presumably pp data as well) where the ocupancy is low, the
1051 // memset is only called if there is more than 1000 signals for a
1052 // row (of the order 1% occupancy)
1053 if(fAllNSigBins[iRow]<1000) {
1054
1055 Float_t* allBins = fAllBins[iRow];
1056 Int_t* sigBins = fAllSigBins[iRow];
1057 const Int_t nSignals = fAllNSigBins[iRow];
1058 for(Int_t i = 0; i < nSignals; i++)
23c9ab21 1059 allBins[sigBins[i]]=0;
42919b08 1060 } else {
23c9ab21 1061
42919b08 1062 Int_t maxBin = (fTimeBinsMax+4)*(fPadsMax+4);
1063 memset(fAllBins[iRow],0,sizeof(Float_t)*maxBin);
1064 }
1065
c322f08a 1066 fAllNSigBins[iRow]=0;
1067 }
1068}
1069
266f8637 1070//_____________________________________________________________________
1071void AliTPCdataQA::GetPadAndTimeBin(Int_t bin, Int_t& iPad, Int_t& iTimeBin){
1072 //
1073 // Return pad and timebin for a given bin
c322f08a 1074 //
266f8637 1075
1076 // Int_t bin = iPad*(fTimeBinsMax+4)+iTimeBin;
1077 iTimeBin = bin%(fTimeBinsMax+4);
1078 iPad = (bin-iTimeBin)/(fTimeBinsMax+4);
1079
1080 iPad -= 2;
1081 iTimeBin -= 2;
1082 iTimeBin += fFirstTimeBin;
1083
1084 R__ASSERT(iPad>=0 && iPad<=fPadsMax);
1085 R__ASSERT(iTimeBin>=fFirstTimeBin && iTimeBin<=fLastTimeBin);
1086}
1087
1088//_____________________________________________________________________
1089void AliTPCdataQA::SetExpandDigit(const Int_t iRow, Int_t iPad,
1090 Int_t iTimeBin, const Float_t signal)
1091{
c322f08a 1092 //
266f8637 1093 //
c322f08a 1094 //
266f8637 1095 R__ASSERT(iRow>=0 && iRow<fRowsMax);
1096 R__ASSERT(iPad>=0 && iPad<=fPadsMax);
1097 R__ASSERT(iTimeBin>=fFirstTimeBin && iTimeBin<=fLastTimeBin);
1098
1099 iTimeBin -= fFirstTimeBin;
1100 iPad += 2;
1101 iTimeBin += 2;
c322f08a 1102
266f8637 1103 Int_t bin = iPad*(fTimeBinsMax+4)+iTimeBin;
1104 fAllBins[iRow][bin] = signal;
1105 fAllSigBins[iRow][fAllNSigBins[iRow]] = bin;
1106 fAllNSigBins[iRow]++;
1107}
1108
1109Float_t AliTPCdataQA::GetQ(const Float_t* adcArray, const Int_t time,
1110 const Int_t pad, const Int_t maxTimeBins,
1111 Int_t& timeMin, Int_t& timeMax,
6a50ff96 1112 Int_t& padMin, Int_t& padMax) const
266f8637 1113{
1114 //
1115 // This methods return the charge in the bin time+pad*maxTimeBins
1116 // If the charge is above 0 it also updates the padMin, padMax, timeMin
1117 // and timeMax if necessary
1118 //
1119 Float_t charge = adcArray[time + pad*maxTimeBins];
1120 if(charge > 0) {
1121 timeMin = TMath::Min(time, timeMin); timeMax = TMath::Max(time, timeMax);
1122 padMin = TMath::Min(pad, padMin); padMax = TMath::Max(pad, padMax);
1123 }
1124 return charge;
c322f08a 1125}
ce4b4255 1126
1127//______________________________________________________________________________
6a50ff96 1128void AliTPCdataQA::Streamer(TBuffer &xRuub)
ce4b4255 1129{
1130 // Automatic schema evolution was first used from revision 4
1131 // Code based on:
1132 // http://root.cern.ch/root/roottalk/roottalk02/3207.html
1133
6a50ff96 1134 UInt_t xRuus, xRuuc;
1135 if (xRuub.IsReading()) {
1136 Version_t xRuuv = xRuub.ReadVersion(&xRuus, &xRuuc);
ce4b4255 1137 //we use the automatic algorithm for class version > 3
6a50ff96 1138 if (xRuuv > 3) {
1139 AliTPCdataQA::Class()->ReadBuffer(xRuub, this, xRuuv, xRuus,
1140 xRuuc);
ce4b4255 1141 return;
1142 }
6a50ff96 1143 TH1F::Streamer(xRuub);
1144 xRuub >> fFirstTimeBin;
1145 xRuub >> fLastTimeBin;
1146 xRuub >> fAdcMin;
1147 xRuub >> fAdcMax;
1148 xRuub >> fNLocalMaxima;
1149 xRuub >> fMaxCharge;
1150 xRuub >> fMeanCharge;
1151 xRuub >> fNoThreshold;
1152 xRuub >> fNTimeBins;
1153 xRuub >> fNPads;
1154 xRuub >> fTimePosition;
1155 xRuub >> fEventCounter;
1156 xRuub >> fIsAnalysed;
1157 xRuub.CheckByteCount(xRuus, xRuuc, AliTPCdataQA::IsA());
ce4b4255 1158 } else {
6a50ff96 1159 AliTPCdataQA::Class()->WriteBuffer(xRuub,this);
ce4b4255 1160 }
1161}