StRoot  1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
CSMStatusUtils.cxx
1 /*
2 Indexing is an issue in this code, so I'm going to be explicit.
3 When I read from the data, the data are indexed from 1 to N.
4 When I write the data to the histograms, it's from 1 to N.
5 When I write text data to the status files, it's from 1 to N.
6 EVERYTHING is from 1 to N.
7 
8 except the db ROOT files.
9 db ROOT files are from 0 to N-1.
10 I explicitly write them this way.
11 */
12 
13 
14 #include "CSMStatusUtils.h"
15 
16 #include "TMath.h"
17 #include "TH2.h"
18 #include "TAxis.h"
19 #include "TROOT.h"
20 #include "TKey.h"
21 #include "TIterator.h"
22 #include "TFile.h"
23 #include "TSystem.h"
24 #include "TF1.h"
25 #include "TCanvas.h"
26 #include "TStyle.h"
27 #include "tables/St_emcStatus_Table.h"
28 #include "tables/St_emcPed_Table.h"
29 #include "StEmcUtil/database/StEmcDecoder.h"
30 
31 #include <iostream>
32 #include <fstream>
33 #include <iomanip>
34 #include <map>
35 #include <set>
36 #include <string>
37 #include <stdio.h>
38 
39 using namespace std;
40 
41 typedef map<Int_t,vector<Short_t>*>::const_iterator IntToPtrVecShortConstIter ;
42 
43 //this sets the detector type
44 void
45 CSMStatusUtils::setDetectorFlavor(TString flavor) {
46  mDetectorFlavor=flavor;
47  if(mDetectorFlavor=="bemc") {
48  mDetectorSize=4800;
49  mDetectorActualSize=4800;
50  mRunStatusMapPtr=&mBEMCRunStatusMap;
51  } else if(mDetectorFlavor=="eemc") {
52  mDetectorSize=720;
53  mDetectorActualSize=720;
54  mRunStatusMapPtr=&mEEMCRunStatusMap;
55  TString tms;
56  ifstream ifs("StRoot/StEmcPool/CSMStatusUtils/eemccratemap");
57  int tower, crate, channel;
58  while(!ifs.eof()) {
59  tms.ReadLine(ifs);
60  sscanf(tms.Data(),"%d %d %d",&crate,&channel,&tower);
61  eemcCrateMap[crate-1][channel] = tower;
62  }
63  ifs.close();
64  }
65 }
66 
67 //this takes the files containing the 2D histograms for each run
68 //and puts their names into mHistFileMap
69 
70 Int_t
71 CSMStatusUtils::initializeHistFileFromDir(TString directory,TString filter) {
72 
73  void *dir = NULL;
74  if ((dir = gSystem->OpenDirectory(directory.Data())) != NULL) {
75  const Char_t *dirEntry;
76  while ((dirEntry = gSystem->GetDirEntry(dir)) != NULL) {
77  Char_t buffer[2048];
78  strcpy(buffer,directory.Data());
79  if (buffer[strlen(buffer)-1] != '/') strcat(buffer,"/");
80  strcat(buffer,dirEntry);
81  if (!strstr(buffer,filter.Data())) continue;
82  Char_t* needle = strstr(buffer,"run");
83  Int_t runNumber = 0;
84  if (needle) {
85  needle+=3;
86  Char_t runString[10];
87  strncpy(runString,needle,7);
88  runNumber = atoi(runString);
89  }
90  if (runNumber != 0) mHistFileMap[runNumber] = buffer;
91  }
92  }
93  return mHistFileMap.size();
94 }
95 
96 //this fills mRunStatusMap with information from status files
97 //it also fills the time- and date-stamp maps with run specific info
98 
99 Int_t
100 CSMStatusUtils::readTablesFromASCII(TString directory,TString filter) {
101  void* dir = NULL;
102  TString buffert = directory + "/status/";
103  TString tmpstr;
104  if ((dir = gSystem->OpenDirectory(buffert.Data())) != NULL) {
105  const Char_t *dirEntry;
106  while ((dirEntry = gSystem->GetDirEntry(dir)) != NULL) {
107  Char_t buffer[2048];
108  strcpy(buffer,buffert.Data());
109  strcat(buffer,dirEntry);
110  if (!strstr(buffer,filter.Data())) continue;
111  tmpstr = dirEntry;
112  if(!tmpstr.Contains(mDetectorFlavor.Data())) continue;
113  Char_t* needle = strstr(buffer,"run");
114  Int_t runNumber = 0, thetime = 0, thedate = 0;
115  if (needle) {
116  needle+=3;
117  Char_t runString[10];
118  Char_t timeString[10];
119  Char_t dateString[10];
120  strncpy(runString,needle,7);
121  runNumber = atoi(runString);
122  needle+=13;
123  strncpy(dateString,needle,8);
124  thedate = atoi(dateString);
125  needle+=9;
126  strncpy(timeString,needle,6);
127  thetime = atoi(timeString);
128  }
129  if (runNumber != 0) {
130  ifstream in(buffer);
131  if(in.good()) {
132  Int_t status,itemp;
133  vector<Short_t>* vec = new vector<Short_t>(mDetectorSize+1);
134  for(int id=1; id<mDetectorSize+1; id++) {
135 // cout << buffer << "mrz" << endl;
136  in >> itemp >> status;
137 // cout << status << "mrz" << itemp << endl;
138  (*vec)[id] = status;
139  }
140 cout << buffer << " is status file that was read" << endl;
141  (*mRunStatusMapPtr)[runNumber] = vec;
142  mRunTimestampMap[runNumber] = thetime;
143  mRunDatestampMap[runNumber] = thedate;
144 // cout << mRunDatestampMap[runNumber] << "\t" << mRunTimestampMap[runNumber] << "\t" << runNumber << endl;
145 // cout << thedate << "aaa" << thetime << "\t" << runNumber << endl;
146  }
147  in.close();
148  }
149  }
150  }
151  return mRunStatusMapPtr->size();
152 }
153 
154 void
155 CSMStatusUtils::plotAllStatuses(TString rootfiledir,int year,int runstart) {
156  void* dir = NULL;
157  TString tmpstr;
158  vector<Int_t> runlist;
159  int runnumber;
160  if ((dir = gSystem->OpenDirectory(rootfiledir.Data())) != NULL) {
161  const Char_t *dirEntry;
162  while ((dirEntry = gSystem->GetDirEntry(dir)) != NULL) {
163  Char_t buffer[2048];
164  strcpy(buffer,rootfiledir.Data());
165  strcat(buffer,dirEntry);
166  if (!strstr(buffer,".root")) continue;
167  if (strstr(buffer,"run.root")) continue;
168  Char_t* needle = strstr(buffer,"run");
169  Int_t runNumber = 0;
170  if (needle) {
171  needle+=3;
172  Char_t runString[10];
173  strncpy(runString,needle,7);
174  runNumber = atoi(runString);
175  runlist.push_back(runNumber);
176  }
177  }
178  }
179  tm ptm;
180  ptm.tm_year = year-1900;
181  int firstdate=99999999, firsttime,lastdate=0, lasttime, firstrun=99999999, lastrun=0;
182  IntToPtrVecShortConstIter first = mRunStatusMapPtr->begin();
183  IntToPtrVecShortConstIter last = mRunStatusMapPtr->end();
184  IntToPtrVecShortConstIter iter = first;
185  for(iter=first;iter!=last;iter++) {
186  runnumber = iter->first;
187  if(runnumber < runstart) continue;
188  if(firstdate > mRunDatestampMap[runnumber]) {
189  firstdate = mRunDatestampMap[runnumber];
190  firsttime = mRunTimestampMap[runnumber];
191  } else if (firstdate == mRunDatestampMap[runnumber] && firsttime > mRunTimestampMap[runnumber]) {
192  firsttime = mRunTimestampMap[runnumber];
193  }
194  if(lastdate < mRunDatestampMap[runnumber]) {
195  lastdate = mRunDatestampMap[runnumber];
196  lasttime = mRunTimestampMap[runnumber];
197  } else if (lastdate == mRunDatestampMap[runnumber] && lasttime < mRunTimestampMap[runnumber]) {
198  lasttime = mRunTimestampMap[runnumber];
199  }
200  if(firstrun > runnumber && runnumber > runstart) firstrun = runnumber;
201  if(lastrun < runnumber) lastrun = runnumber;
202  }
203 //cout << firstdate << "\t" << firsttime << "\t" << lastdate << "\t" << lasttime << endl;
204  int bigyearnumber = year*10000;
205 //cout << bigyearnumber << endl;
206  ptm.tm_mon = ((firstdate-bigyearnumber)/100)+1;
207  ptm.tm_mday = (firstdate-bigyearnumber)%100;
208  ptm.tm_hour = firsttime/10000;
209  ptm.tm_min = (firsttime%10000)/100;
210  ptm.tm_sec = firsttime%100;
211  time_t firstdatet = mktime(&ptm);
212  ptm.tm_mon = ((lastdate-bigyearnumber)/100)+1;
213  ptm.tm_mday = (lastdate-bigyearnumber)%100;
214  ptm.tm_hour = lasttime/10000;
215  ptm.tm_min = (lasttime%10000)/100;
216  ptm.tm_sec = lasttime%100;
217  time_t lastdatet = mktime(&ptm);
218 // int difference = lastrun - firstrun;
219  int diffseconds = (int)difftime(lastdatet,firstdatet);
220  TCanvas* tc = new TCanvas("peter","Status vs Run Number",800,800);
221  tc->cd();
222  int timegap = 3600;
223 //cout << diffseconds << "\t" << timegap << "\t" << diffseconds/timegap << endl;
224  TH2F* histogram = new TH2F("stevegeech","Time (Hours) vs BEMC Channels (bad)",4801,-0.5,4800.5,diffseconds/timegap+1,-0.5,diffseconds/timegap+0.5);
225 //cout << diffseconds/timegap << "is the difference" << endl;
226  // TH2F* histogram = new TH2F("stevegeech","Status vs Run Number",4801,-0.5,4800.5,runlist.size()+1,-0.5,runlist.size()+0.5);
227 /* for(int i=0; i<runlist.size(); i++) {
228  runnumber = runlist[i];
229  cout << i<< runnumber << endl;
230  if(runnumber<6100000 || runnumber>6130000) continue;
231  vector<Short_t>* statusVector = (*mRunStatusMapPtr)[runnumber];
232  if(statusVector) {
233  for(int j=1; j<2401; j++) {
234 // if((*statusVector)[j]==1) histogram->Fill(j,runnumber);
235 // cout << i << endl;
236  }
237  }
238  }*/
239  vector<int> binbounds;
240  int currentbin;
241  for(iter=first;iter!=last;iter++) {
242  runnumber = iter->first;
243  if(runnumber < runstart) continue;
244  vector<Short_t>* statusVector = (*mRunStatusMapPtr)[runnumber];
245  if(statusVector) {
246  ptm.tm_mon = ((mRunDatestampMap[runnumber]-bigyearnumber)/100)+1;
247  ptm.tm_mday = (mRunDatestampMap[runnumber]-bigyearnumber)%100;
248  ptm.tm_hour = mRunTimestampMap[runnumber]/10000;
249  ptm.tm_min = (mRunTimestampMap[runnumber]%10000)/100;
250  ptm.tm_sec = mRunTimestampMap[runnumber]%100;
251  time_t timet = mktime(&ptm);
252  double timedifference = difftime(timet,firstdatet);
253  int ratiodifftogap = (int)timedifference/timegap;
254  currentbin = histogram->GetBin(0,ratiodifftogap)/(mDetectorSize+3);
255 //cout << runnumber << "\trrrr" << currentbin << endl;
256  binbounds.push_back(currentbin);
257  }
258  }
259  binbounds.push_back(currentbin);
260  int bb=0;
261  for(iter=first;iter!=last;iter++) {
262  runnumber = iter->first;
263  if(runnumber < runstart) continue;
264  vector<Short_t>* statusVector = (*mRunStatusMapPtr)[runnumber];
265  if(statusVector) {
266 // ptm.tm_mon = ((mRunDatestampMap[runnumber]-bigyearnumber)/100)+1;
267 // ptm.tm_mday = (mRunDatestampMap[runnumber]-bigyearnumber)%100;
268 // ptm.tm_hour = mRunTimestampMap[runnumber]/10000;
269 // ptm.tm_min = (mRunTimestampMap[runnumber]%10000)/100;
270 // ptm.tm_sec = mRunTimestampMap[runnumber]%100;
271 // time_t timet = mktime(&ptm);
272 // double timedifference = difftime(timet,firstdatet);
273 //cout << timedifference/timegap << endl;
274 //cout << binbounds[bb] << "\t" << binbounds[bb+1] << endl;
275  for(int i=1; i<4801; i++) {
276  if((*statusVector)[i]!=1) {
277  for(int bound=binbounds[bb]; bound<binbounds[bb+1]; bound++) {
278  histogram->Fill(i,bound);
279 //cout << "I fill here" << i << endl;
280  }
281  }
282  }
283 //cout << "ended the filling" << endl;
284  }
285  bb++;
286  }
287  histogram->Draw("colz");
288 }
289 
290 //this finds out which towers' statuses changed between runs
291 //and writes only the changes to the abbreviated status files
292 
293 //however, currently it ALWAYS writes out a root files, regardless
294 //of the number of changes. this may change.
295 
296 Int_t
297 CSMStatusUtils::saveAbbreviatedStatusTablesToASCII(TString directory) {
298 
299  TString tmpstr;
300  int runnumber;
301  IntToPtrVecShortConstIter first = mRunStatusMapPtr->begin();
302  IntToPtrVecShortConstIter last = mRunStatusMapPtr->end();
303  IntToPtrVecShortConstIter iter = first;
304  IntToPtrVecShortConstIter preiter = first;
305 
306 //first, we want to catch channels which flip back and forth between
307 //"good" and "bad" states, ie channels whose pedestals are close to
308 //the boundaries, or channels who had intermittently stuck bits.
309 //For all of these channels, if they flip more than 10 percent of
310 //the analyzed runs, we flag them as bad permanently.
311 //also, due to the barrel scheme, I have to introduce a fake zero
312 //for the bookkeepping. it's fairly obvious how this works.
313 
314  vector<Float_t> statuscounter(mDetectorSize+1);
315  vector<Short_t> totalStatusVector(mDetectorSize+1);
316  for(int i=0; i<mDetectorSize+1; i++) {
317  totalStatusVector[i] = 1;
318  statuscounter[i] = 0;
319  }
320  Short_t fakeZero = 1024;
321  Bool_t firstone = kTRUE;
322  Short_t oldstatus, status;
323  Int_t badChannelsInRun;
324  for(iter=first;iter!=last;iter++) {
325  runnumber = iter->first;
326  vector<Short_t>* statusVector = iter->second;
327  vector<Short_t>* oldStatusVector = preiter->second;
328  badChannelsInRun = 0;
329  for (Int_t i = 1; i < mDetectorSize + 1; i++) {
330  oldstatus = (*oldStatusVector)[i];
331  status = (*statusVector)[i];
332  if(oldstatus == 0) oldstatus = fakeZero;
333  if(status == 0) status = fakeZero;
334 
335 // if(i == 1) cout << oldstatus << "\t" << status << endl;
336 
337 //this part sets the "absolute" status vector, namely the vector
338 //that gives the worst possible state of the channel over all runs
339 
340  if(firstone) { //initialization
341  totalStatusVector[i] = status;
342  } else if(oldstatus == 1 && status == 1) {
343  continue;
344  } else if(status != 1) {
345  if(totalStatusVector[i] == 1) {
346  totalStatusVector[i] = status;
347  } else {
348  totalStatusVector[i] |= status;
349  }
350  } else if(oldstatus != 1) {
351  if(totalStatusVector[i] == 1) {
352  totalStatusVector[i] = oldstatus;
353  } else {
354  totalStatusVector[i] |= oldstatus;
355  }
356  } else { //both != 1
357  if(totalStatusVector[i] == 1) {
358  totalStatusVector[i] = (oldstatus | status);
359  } else {
360  totalStatusVector[i] |= (oldstatus | status);
361  }
362  }
363 //how many channels changed status for this run?
364 //the logic is that if you've gotten here, at least one of
365 //(status,oldstatus) is bad
366  if(i < mDetectorActualSize) badChannelsInRun++;
367 
368 //the next line records the number of times the channel changed status
369  if (oldstatus != status) statuscounter[i] += 1;
370  } //channels
371 //for really crappy runs, don't count channel changes toward the maximum number
372  for (Int_t i = 1; i < mDetectorSize + 1; i++) {
373  oldstatus = (*oldStatusVector)[i];
374  status = (*statusVector)[i];
375  if(badChannelsInRun > 0.5 * mDetectorActualSize && oldstatus != status)
376  statuscounter[i] -= 1;
377  }
378  preiter = iter;
379  firstone = kFALSE;
380  } //runs
381 
382 //now we write all status bits to either the root files or text files
383  iter = first;
384  preiter = first;
385  TString datetimestring, runnumberstring;
386  firstone = kTRUE;
387  for(iter=first; iter!=last; iter++) {
388  int numberofchangedchannels=0;
389  runnumber = iter->first;
390  runnumberstring = "";
391  runnumberstring += runnumber;
392  tmpstr = directory + "/status/short_status_" + mDetectorFlavor + "_run"
393  + runnumberstring + ".status";
394  ofstream ofs(tmpstr.Data());
395 
396  St_emcStatus *bemc_status=new St_emcStatus("bemcStatus",1);
397  emcStatus_st *emcstatus=bemc_status->GetTable();
398  for (Int_t i_tow=0; i_tow<4800; i_tow++) emcstatus->Status[i_tow]=1;
399 //for eemc, I will eventually do unixtime (stop and start)
400  vector<Short_t>* statusVector = iter->second;
401  vector<Short_t>* oldStatusVector = preiter->second;
402  for (UInt_t i = 1; i < statusVector->size(); i++) {
403  oldstatus = (*oldStatusVector)[i];
404  status = (*statusVector)[i];
405 //if this is the first run and the channel changes status a lot,
406 //write down the sum of all bad statuses it has for all runs
407  if ( firstone && (statuscounter[i]/mRunStatusMapPtr->size() > 0.1 ) && mRunStatusMapPtr->size() > 3) {
408 //account for the fake zero
409 
410  cout << "statcou is " << statuscounter[i] << " for channel " << i << " and size is " << mRunStatusMapPtr->size() << endl;
411  if(totalStatusVector[i] & fakeZero) ofs << i << "\t" << "0" << endl;
412  else ofs << i << "\t" << totalStatusVector[i] << endl;
413 //otherwise, only write down the status if this is the first run,
414 //or if the status has changed
415  } else if (firstone ||
416  (oldstatus != status && (statuscounter[i]/mRunStatusMapPtr->size() <= 0.1 || mRunStatusMapPtr->size() <= 10))) {
417  ofs << i << "\t" << status << endl;
418  numberofchangedchannels++;
419  }
420  //Optional if statement to print these flickering status towers as permanently bad in .root files --- D.Staszak 11/06
421  /*if ((statuscounter[i]/mRunStatusMapPtr->size() > 0.1 ) && mRunStatusMapPtr->size() > 3) {
422  emcstatus->Status[i-1]=totalStatusVector[i];
423  } else {
424  emcstatus->Status[i-1]=status;
425  }*/
426  //Original version - line below:
427  emcstatus->Status[i-1]=status; //indexed from 0
428  }
429  ofs << numberofchangedchannels << " channels changed" << endl;
430  ofs.close();
431  datetimestring = getDateTimeString(runnumber);
432  TString statusrootfilename =
433  directory + "/status/" + mDetectorFlavor + "Status" + datetimestring + "root";
434  TFile* fout_status = new TFile(statusrootfilename.Data(),"RECREATE");
435  fout_status->cd();
436  bemc_status->AddAt(emcstatus,0);
437  bemc_status->Write();
438  fout_status->Close();
439  delete bemc_status;
440  delete fout_status;
441 
442  preiter = iter;
443  firstone = kFALSE;
444  }
445  return 0;
446 }
447 
448 //this takes HistFileMap, opens each file in it, creates the
449 //hot tower histogram, calls analyseStatusHistogram, and if there
450 //are enough statistics in the run, it will draw the
451 //histograms, and output to an html file the abbreviated results,
452 //such as the number of good towers in a run.
453 
454 //the logic is outlined here:
455 //"RI" refers to a run's (or set of runs') Run Information:
456  //2d histogram, earliest run number, and earliest time and datestamps
457 //there are three separate RIs:
458 //1. the run RI (of the run being analyzed),
459 //2. the current RI (maybe of multiple runs),
460 //3. the prior RI (ditto)
461 
462 //for(all runs)
463  //[implied "if(run is last in fill) set Last in Fill Flag (LFF)"]
464 
465  //add this run's RI to the current RI
466  //analyze the current RI
467 
468  //if(statistics acceptable)
469  //set prior RI = current RI
470  //clear current RI (but prior RI is kept!)
471  //else if(LFF)
472  //add prior RI to the current RI
473  //analyze current RI
474  //else [implied "go to next run if !LFF and statistics low"]
475  //continue
476 
477  //save QA info
478 
479  //if(LFF)
480  //clear current RI and prior RI
481 
482 //It then looks to see if the status of a particular channel has
483 //changed from the last RI to this RI, and if it has,
484 //it saves that channel's histograms.
485 //It will also run to Taco Bell if you are hungry.
486 //It is *that* versatile.
487 
488 Int_t
490 
491 //set up graphics
492  gStyle->SetOptStat(0);
493  gStyle->SetOptTitle(0);
494  TString tmpstr;
495  tmpstr = mDetectorFlavor + "StatusPlots";
496  TCanvas *c1 = new TCanvas(tmpstr.Data(),tmpstr.Data());
497  c1->SetLogy();
498  c1->Draw();
499  TCanvas *c2 = new TCanvas("towerAdcPlots","towerAdcPlots",400,400);
500  c2->SetLogy();
501  c2->Draw();
502 
503 //set up output html file
504  tmpstr = plotDir + mDetectorFlavor + "Status.html";
505  cout << "htmlSummary: " << tmpstr << endl;
506  ofstream htmlSummary(tmpstr.Data()); //
507  if (!htmlSummary) cout << "htmlSummary isn't defined!" << endl;
508  writeHtmlHeaderSummary(htmlSummary); //
509 
510  Char_t buffer[2048];
511 
512  findFillEnds();
513 
514 
515  TH2F* priorHist = NULL;
516  TH2F* currentHist = NULL;
517  TH2F* tmpHist = NULL;
518  TString runnumberstring;
519  int runnumber, priorRunNumber, currentRunNumber=99999999, savedCurrentRunNumber;
520  int priorTimeStamp, currentTimeStamp=99999999;
521  int priorDateStamp, currentDateStamp=99999999;
522  Bool_t firstGoodRun = kTRUE;
523  Float_t averageNumberHitsPerChan;
524  Int_t goodTowers;
525 
526  std::vector<Short_t>* statusVector;
527  std::vector<Float_t>* pedestalmean;
528  std::vector<Float_t>* pedestalwidth;
529  std::vector<Float_t>* pedestalchi;
530  TH1F* hHotTower;
531  Int_t hottowerPlotNameIter = -1;
532  ofstream outputlog("MOSTRECENTLOG.txt");
533 
534  for (map<Int_t,string>::const_iterator iter = mHistFileMap.begin();
535  iter != mHistFileMap.end(); ++iter) {
536 // iter != mHistFileMap.end(); iter = mHistFileMap.end()) {
537  TFile* file = new TFile(iter->second.c_str(),"READ");
538 outputlog << "doing file " << iter->second.c_str() << endl;
539 cout << "doing file " << iter->second.c_str() << endl;
540  if (file && file->IsOpen()) {
541 outputlog << " it opened" << endl;
542 cout << " it opened" << endl;
543  runnumber = iter->first;
544  // cout << "note - iter->first: " << iter->first;
545  runnumberstring = "";
546  runnumberstring += runnumber;
547  hottowerPlotNameIter++;
548  tmpstr = mDetectorFlavor + "StatusAdc_" + runnumberstring;
549  TH2F* runHist = dynamic_cast<TH2F*>(file->Get(tmpstr.Data()));
550  TTree* myTree = dynamic_cast<TTree*>(file->Get("calinfo"));
551  assert(runHist);
552  assert(myTree);
553 
554 //if first good run, initialize currentHist and priorHist
555 //otherwise, add run histogram to currentHist
556  if(firstGoodRun) {
557 outputlog << "it's the first good run" << endl;
558 cout << "it's the first good run" << endl;
559  currentHist = dynamic_cast<TH2F*>(runHist->Clone("ch1"));
560  currentHist->SetDirectory(0);
561  priorHist = dynamic_cast<TH2F*>(runHist->Clone("ph1"));
562  priorHist->SetDirectory(0);
563  priorHist->Reset();
564  firstGoodRun = kFALSE;
565  } else {
566  currentHist->Add(runHist);
567  }
568 
569  setDateTimeInfo(runnumber,myTree);
570 
571  //Keep track of the fill number D.Staszak 8/06
572  Float_t runFillNum;
573  myTree->SetBranchAddress("fillnum",&runFillNum);
574  myTree->GetEvent(0);
575  //if(runFillNum==0) {
576  //cout << iter->second.c_str() << endl;
577  cout<<"runfillnumb is " << runFillNum << endl;
578  //}
579  //assert(runFillNum);
580 
581 
582  if(runnumber < currentRunNumber) currentRunNumber = runnumber;
583  runnumberstring = "";
584  runnumberstring += currentRunNumber;
585  savedCurrentRunNumber = currentRunNumber;
586  if(mRunDatestampMap[runnumber] < currentDateStamp) {
587  currentDateStamp = mRunDatestampMap[runnumber];
588  currentTimeStamp = mRunTimestampMap[runnumber];
589  } else if(mRunDatestampMap[runnumber] == currentDateStamp &&
590  mRunTimestampMap[runnumber] < currentTimeStamp) {
591  currentTimeStamp = mRunTimestampMap[runnumber];
592  }
593 
594 //analyze this RI
595 //warning - this code will have a LOT of memory leakage until I clean it
596  statusVector = new vector<Short_t>(mDetectorSize+1);
597  pedestalmean = new vector<Float_t>(mDetectorSize+1);
598  pedestalwidth = new vector<Float_t>(mDetectorSize+1);
599  pedestalchi = new vector<Float_t>(mDetectorSize+1);
600 
601  tmpstr = "hotTower";
602  tmpstr += hottowerPlotNameIter;
603 
604  hHotTower = new TH1F(tmpstr.Data(),"# of tower hits",mDetectorSize+1,-0.5,mDetectorSize+1-0.5);
605  hHotTower->GetXaxis()->SetTitle("Tower Id");
606  hHotTower->GetYaxis()->SetTitle("Number of Hits Above Pedestal");
607 
608 
609  // analyze
610  goodTowers = analyseStatusHistogram(currentHist,plotDir,averageNumberHitsPerChan,
611  currentDateStamp,currentTimeStamp,
612  *statusVector,*pedestalmean,*pedestalwidth,*pedestalchi,hHotTower);
613 
614  if(averageNumberHitsPerChan > 100) {
615 //set prior RI to current RI; clear current RI
616 outputlog << " good statistics!" << endl;
617  cout << " good statistics!" << endl << endl;
618  //------For Single Run, to use html comment out below-------//
619  priorHist->Reset();
620  tmpHist = priorHist;
621  priorHist = currentHist;
622  currentHist = tmpHist;
623  priorTimeStamp = currentTimeStamp;
624  currentTimeStamp = 99999999;
625  priorDateStamp = currentDateStamp;
626  currentDateStamp = 99999999;
627  priorRunNumber = currentRunNumber;
628  currentRunNumber = 99999999;
629  //----------------------------------------------//
630  } else if(mFillEndMap[runnumber]) {
631 outputlog << "end of fill and poor statistics! average: " << averageNumberHitsPerChan << endl;
632 cout << "end of fill and poor statistics! average: " << averageNumberHitsPerChan << endl << endl;
633  currentHist->Add(priorHist);
634  if(priorRunNumber < currentRunNumber) currentRunNumber = priorRunNumber;
635  runnumberstring = "";
636  runnumberstring += currentRunNumber;
637  savedCurrentRunNumber = currentRunNumber;
638  if(priorDateStamp < currentDateStamp) {
639  currentDateStamp = priorDateStamp;
640  currentTimeStamp = priorTimeStamp;
641  } else if(priorDateStamp == currentDateStamp &&
642  priorTimeStamp < currentTimeStamp) {
643  currentTimeStamp = priorTimeStamp;
644  }
645  delete statusVector;
646  delete pedestalmean;
647  delete pedestalwidth;
648  delete pedestalchi;
649  statusVector = new vector<Short_t>(mDetectorSize+1);
650  pedestalmean = new vector<Float_t>(mDetectorSize+1);
651  pedestalwidth = new vector<Float_t>(mDetectorSize+1);
652  pedestalchi = new vector<Float_t>(mDetectorSize+1);
653  hHotTower->Reset();
654  hHotTower->GetXaxis()->SetTitle("Tower Id");
655  hHotTower->GetYaxis()->SetTitle("Number of Hits Above Pedestal");
656  goodTowers = analyseStatusHistogram(currentHist,plotDir,averageNumberHitsPerChan,
657  currentDateStamp,currentTimeStamp,
658  *statusVector,*pedestalmean,*pedestalwidth,*pedestalchi,hHotTower);
659  if(averageNumberHitsPerChan < 50) {
660 outputlog << " fill has ended and stats suck! Number is " << averageNumberHitsPerChan << endl;
661 cout << " fill has ended and stats suck! Number is " << averageNumberHitsPerChan << endl << endl;
662  priorHist->Reset();
663  currentHist->Reset();
664  priorTimeStamp = 99999999;
665  currentTimeStamp = 99999999;
666  priorDateStamp = 99999999;
667  currentDateStamp = 99999999;
668  priorRunNumber = 99999999;
669  currentRunNumber = 99999999;
670  delete statusVector;
671  delete pedestalmean;
672  delete pedestalwidth;
673  delete pedestalchi;
674  delete hHotTower;
675  file->Close();
676  delete file;
677  continue;
678  }
679  } else {
680 outputlog << " poor statistics! average: " << averageNumberHitsPerChan << endl;
681 cout << " poor statistics! average: " << averageNumberHitsPerChan << endl;
682  delete statusVector;
683  delete pedestalmean;
684  delete pedestalwidth;
685  delete pedestalchi;
686  delete hHotTower;
687  file->Close();
688  delete file;
689  continue;
690  }
691 
692  if(mFillEndMap[runnumber]) {
693 outputlog << " fill has ended!" << endl;
694  cout << " fill has ended!" << endl << endl;
695  //-----For Single Run, to use html comment out below---------//
696  //priorHist->Reset(); // Comment this out for multi-run Fill running
697  currentHist->Reset();
698  priorTimeStamp = 99999999;
699  currentTimeStamp = 99999999;
700  priorDateStamp = 99999999;
701  currentDateStamp = 99999999;
702  priorRunNumber = 99999999;
703  currentRunNumber = 99999999;
704  //-----------------------------------------------//
705  }
706 
707  cout << "Got here? 1 " << endl;
708 
709 //if the RI has less than 10% of the towers functioning
710 //ignore the html file
711 /* if (goodTowers < 0.05 * mDetectorSize) {
712 outputlog<<"special case - everything sucks!" << endl;
713  cout<<"special case - everything sucks!" << endl;
714  htmlSummary << "<tr> <td>" << runnumber << "</td>" //
715  << "<td> BAD </td> <td> - </td> <td> - </td>" //
716  << "<td> - </td> <td> - </td> <td> - </td> </tr><br>" //
717  << endl; //
718  delete statusVector;
719  delete pedestalmean;
720  delete pedestalwidth;
721  delete pedestalchi;
722  delete hHotTower;
723  file->Close();
724  delete file;
725  continue;
726  }*/
727 
728  (*mRunStatusMapPtr)[savedCurrentRunNumber] = statusVector;
729 //write out pedestals for this run
730  writePedestals(savedCurrentRunNumber,plotDir,*statusVector,*pedestalmean,*pedestalwidth,*pedestalchi);
731 
732 // save tower status bits to a text file
733  tmpstr = plotDir + "/status/";
734  saveStatusTablesToASCII(tmpstr.Data(), savedCurrentRunNumber);
735 
736  gStyle->SetOptStat(1111);
737  c1->cd();
738  hHotTower->Draw();
739  c1->Update();
740  tmpstr = plotDir + "/run" + runnumberstring + "_" + mDetectorFlavor + "_hotTowers.gif";
741  if(gROOT->IsBatch()) {
742  tmpstr = plotDir + "/run" + runnumberstring + "_" + mDetectorFlavor + "_hotTowers.eps";
743  }
744  c1->SaveAs(tmpstr.Data());
745  delete hHotTower;
746 
747  htmlSummary << "<tr>" << endl //
748  << "<td>" << "Fill " << runFillNum << "</td>" << ", " << endl //
749  << "<td> " << "Run " << savedCurrentRunNumber << " </td> " << endl //
750  << "<td> " << goodTowers << " good towers" << " </td>" << endl //
751  << "<td> " << getNumberOfChangedTowers(savedCurrentRunNumber) << " towers changed from previous run"//run # //
752  << " </td>" << endl; //
753 
754  tmpstr = "./run" + runnumberstring + "_" + mDetectorFlavor + "_badTowers.html";
755  htmlSummary << "<td> <a href=\"" << tmpstr.Data() << "\"> list </a></td><br>" //
756  << endl; //
757 
758 // save tower status bits to the htmlfile
759 
760  tmpstr = plotDir + "/run" + runnumberstring + "_" + mDetectorFlavor + "_badTowers.html";
761  ofstream htmlout(tmpstr.Data()); //
762  writeHtmlHeaderBadTowerList(htmlout,savedCurrentRunNumber); //
763 
764 
765 // check if first run - if yes plot every bad tower
766 // if a previous run exists just plot towers which changed status to bad,
767 // unless there are more than 25 bad towers, in which case, don't,
768 // since disk space is apparently "important"
769  for (Int_t i=1; i<=mDetectorSize; i++) {
770  if ((*statusVector)[i] != 1 && (*statusVector)[i] != 18 && (*statusVector)[i] != 0) {
771 
772  htmlout << "<tr> <td> " << i << " </td> <td> "
773  << (*statusVector)[i] << " </td> <td> " << endl;
774 
775  IntToPtrVecShortConstIter statusIter;
776  statusIter = mRunStatusMapPtr->find(savedCurrentRunNumber);
777 
778  if (statusIter != mRunStatusMapPtr->begin()) { //checking to make sure not the first run
779  IntToPtrVecShortConstIter preIter = statusIter;
780  preIter--;
781 
782  if ((*(statusIter->second))[i] == (*(preIter->second))[i] ||
783  (*(statusIter->second))[i] == 1 || //good channel
784  (*(statusIter->second))[i] == 0) { //no need to plot dead channels
785  htmlout << "- </td> </tr><br>" << endl;
786  continue;
787  }
788  if(getNumberOfChangedTowers(runnumber) > 25) {
789  //cout << ">25 changed towers --- " << endl;
790  htmlout << "- </td> </tr><br>" << endl;
791  continue;
792  }
793  }
794 
795  // For single running, use currentHist to ouput plots
796  //Int_t bin = currentHist->GetXaxis()->FindFixBin(i);
797  //TH1D *hTemp = currentHist->ProjectionY("projTemp",bin,bin);
798 
799  // For multi-run fills, use priorHist to output plots, also, comment out reset above
800  Int_t bin = priorHist->GetXaxis()->FindFixBin(i);
801  TH1D *hTemp = priorHist->ProjectionY("projTemp",bin,bin);
802  c2->cd();
803  c2->Clear();
804  hTemp->GetXaxis()->SetTitle("adc");
805  hTemp->GetXaxis()->SetRange(0,150);
806  hTemp->Draw();
807  c2->Update();
808  sprintf(buffer,"%s/run%dtower%d_adc.gif",plotDir.Data(),iter->first,i);
809  c2->SaveAs(buffer);
810  sprintf(buffer,"./run%dtower%d_adc.gif",savedCurrentRunNumber,i);
811 
812  htmlout << "<a href=\"" << buffer << "\" > plot </a>"
813  << "</td> </tr>" << endl;
814  delete hTemp;
815  }
816  }
817 
818 
819 // Now output a default of 5 good towers for comparison (for the first run in group/fill only) D.Staszak 6.06
820  IntToPtrVecShortConstIter statusIter;
821  statusIter = mRunStatusMapPtr->find(savedCurrentRunNumber);
822  if (statusIter == mRunStatusMapPtr->begin()) { //checking that it's the first run
823 
824  htmlout << "</tbody>" << endl;
825  htmlout << "</table>" << endl;
826  htmlout << "<br><br> <div>Partial Good Tower List for Comparison</div><br>" << endl;
827  htmlout << "<table border=\"1\">" << endl;
828  htmlout << "<tbody>" << endl;
829  htmlout << "<tr> <th width=\"50\"> Tower ID </th> <th width=\"50\"> Status Code </th> <th width=\"100\"> ADC plot </th> </tr>" << endl;
830 
831  Int_t good_toplot = 1;
832  for (Int_t i=100; i<mDetectorSize && good_toplot<10; i = i+100) { // Set good_toplot to iterate below to restrict number of good plots printed
833  if ((*statusVector)[i] == 1) {
834  htmlout << "<tr> <td> " << i << " </td> <td> "
835  << (*statusVector)[i] << " </td> <td> " << endl;
836 
837  //Int_t bin = currentHist->GetXaxis()->FindFixBin(i);
838  //TH1D *hTemp = currentHist->ProjectionY("projTemp",bin,bin);
839  Int_t bin = priorHist->GetXaxis()->FindFixBin(i);
840  TH1D *hTemp = priorHist->ProjectionY("projTemp",bin,bin);
841  //hTemp->GetXaxis()->SetRange(0,150);
842  c2->cd();
843  c2->Clear();
844  hTemp->GetXaxis()->SetTitle("adc");
845  hTemp->Draw();
846  c2->Update();
847 
848  sprintf(buffer,"%s/run%dtower%d_adc.gif",plotDir.Data(),iter->first,i);
849  c2->SaveAs(buffer);
850  sprintf(buffer,"./run%dtower%d_adc.gif",savedCurrentRunNumber,i);
851  htmlout << "<a href=\"" << buffer << "\" > plot </a>"
852  << "</td> </tr>" << endl;
853  delete hTemp;
854  }
855  good_toplot++;
856 
857  }
858  }
859  writeHtmlFooterSummary(htmlout);
860  htmlSummary << "</tr>" << endl;
861  htmlout.close();
862  // cout << "I got here and there is no problem yet" << endl;
863  // exit(1);
864 
865  }
866  file->Close();
867  delete file;
868  }
869  htmlSummary.close();
870  outputlog.close();
871  writeHtmlFooterSummary(htmlSummary);
872  TH2F* statusHist = makeStatusVersusTimePlot();
873  if (statusHist) {
874  c1->cd();
875  c1->SetLogy(kFALSE);
876  c1->Clear();
877  //statusHist->GetXaxis()->SetRangeUser(0.5,2400.5);
878  statusHist->Draw("colz");
879  sprintf(buffer,"%s/bemcStatusPlot.gif",plotDir.Data());
880  c1->SaveAs(buffer);
881 // sprintf(buffer,"%s/bemcStatusPlot.svg",plotDir);
882 // c1->SaveAs(buffer);
883  delete statusHist;
884  }
885  delete c1;
886  delete c2;
887  return 0;
888 }
889 
890 //this takes the 2d histograms and performs status checks on each
891 //channel. The output of the status checks is saved in statusVector.
892 //The tower frequency plot is saved in hHotTower.
893 //mDetectorFlavor defaults to "bemc", but can be "eemc"
894 
895 Int_t
896 CSMStatusUtils::analyseStatusHistogram(TH2F* hist,
897  TString directory,
898  Float_t& averageNumberOfHitsPerChannel,
899  Int_t dateStamp,
900  Int_t timeStamp,
901  std::vector<Short_t>& statusVector,
902  std::vector<Float_t>& pedestalmean,
903  std::vector<Float_t>& pedestalwidth,
904  std::vector<Float_t>& pedestalchi,
905  TH1F* hHotTower,
906  TH1F* hPedMean,
907  TH1F* hPedWidth) {
908 
909  TString runnumber = hist->GetName();
910  runnumber = runnumber(runnumber.Length()-7,7);
911 
912  void* dir;
913 //create the directory, if it doesn't exist
914  if ((dir = gSystem->OpenDirectory(directory)) == NULL)
915  gSystem->MakeDirectory(directory);
916 
917  TF1* gaus = new TF1("gaus","gaus");
918 
919 // initialize status vector
920  for (vector<Short_t>::iterator iter = statusVector.begin(); iter != statusVector.end(); ++iter)
921  *iter = 0;
922 
923 //loop through all the channels
924  for (Int_t chanId = 1; chanId < mDetectorSize+1; chanId++) {
925 
926 //the next line has the "+1"s because the first bin is underflow
927  TH1D* proj = hist->ProjectionY("projTemp",chanId+1,chanId+1);
928 
929  if (proj) {
930 
931 // find maximum, which should be the pedestal peak
932  Int_t maxBin = 0;
933  Float_t maxValue = -1;
934  // for (Int_t j = 1; j < proj->GetXaxis()->GetNbins(); j++) {
935  for (Int_t j = 2; j < proj->GetXaxis()->GetNbins(); j++) { // D.Staszak
936  if (proj->GetBinContent(j) > maxValue) {
937  maxBin = j;
938  maxValue = proj->GetBinContent(j);
939  }
940  }
941  Float_t pedMean = proj->GetXaxis()->GetBinCenter(maxBin);
942 
943 //pedestal mean test --- Modified this test to allow some below pedMean of 4, see below --- D.Staszak
944 // if (mDetectorFlavor=="bemc" && (pedMean < 4 || pedMean > 145) ||
945 // mDetectorFlavor=="eemc" && (pedMean < 3 || pedMean > 145)) {
946 // statusVector[chanId] |= 4;
947 // cout << "Before fit - Mean too low --- Id: " << chanId << " mean: " << pedMean << endl;
948 // }
949 
950 // D.Staszak - Need to be careful about the influence of 0 bin on mean fit measurement.
951 // In 2006pp, many runs have multiple towers filled with a lot of 0 ADC counts. Other
952 // than this, the data is fine...so we just need to worry how it is affecting pedMean
953  // --- Include the below line when running over files with '0's ---
954  //Float_t zero_chk = pedMean - 11;
955 
956  // fit a gaussian to the pedestal peak
957  gaus->SetParameter(0,maxValue);
958  gaus->SetParameter(1,pedMean);
959  gaus->SetParameter(2,3.5);
960  // --- Include this when running over files with '0's ---
961  //if (zero_chk < 1)
962  // gaus->SetRange(pedMean-(10.0+zero_chk),pedMean+(10.0+zero_chk));
963  //else
964  // --- End comment ---
965  gaus->SetRange(pedMean-10,pedMean+10);
966  proj->Fit(gaus,"0RQ");
967 
968  if(hPedMean) hPedMean->Fill(gaus->GetParameter(1));
969  if(hPedWidth) hPedWidth->Fill(gaus->GetParameter(2));
970 
971  pedestalmean[chanId] = gaus->GetParameter(1);
972  pedestalwidth[chanId] = gaus->GetParameter(2);
973  pedestalchi[chanId] = gaus->GetChisquare();
974 
975 //pedestal width test
976 //SHOULD THIS BE DIFFERENT FOR THE EEMC???
977 
978  if (pedestalwidth[chanId] <= 0.5 || pedestalwidth[chanId] > 2.8) {
979  //cout << "In wide ped, Id: " << chanId << " width: " << pedestalwidth[chanId] << " mean: " << pedestalmean[chanId] << endl;
980  statusVector[chanId] |= 4+32;
981  }
982 
983 
984 //pedestal mean test
985  if (mDetectorFlavor=="bemc" && (pedestalmean[chanId] < 4 || pedestalmean[chanId] > 145)) {
986  if (pedestalmean[chanId] > 2.6 && pedestalwidth[chanId] < 1.5) { //allow lower mean with thin width
987  //cout << "Saved: " << chanId << " mean - " << pedestalmean[chanId] << " width - " << pedestalwidth[chanId] << endl;
988  } else {
989  statusVector[chanId] |= 4;
990  //cout << "After fit - Still didn't make it " << chanId << endl;
991  }
992  }
993 
994 
995 
996 //preparation for hot tower/cold tower test
997 //using a threshold of 10 sigma above pedestal
998 //FIRST PART OF COLD TOWER TEST DONE HERE NOW
999  Int_t minBin = proj->GetXaxis()->FindFixBin(pedestalmean[chanId] + 10*pedestalwidth[chanId]);
1000  maxBin = proj->GetXaxis()->GetNbins() - 1;
1001  Int_t hottowerthreshold = minBin;
1002  if(hHotTower) {
1003 
1004  Float_t nHitsAbovePedestal = proj->Integral(hottowerthreshold,maxBin);
1005 
1006  if(nHitsAbovePedestal==0) {
1007  nHitsAbovePedestal=1; //(just for log plot sakes)
1008 
1009 
1010  // Quick Fix to cold channels being labelled dead, D.Staszak
1011  Float_t deadorno = proj->Integral(2,maxBin);
1012  if (deadorno) {
1013  nHitsAbovePedestal=2; // Set at 2 because of a >2 cut above...again, quick fix here
1014  }
1015  else {
1016  statusVector[chanId] |= mZerobit;
1017  //cout << chanId << " has zero counts!" << endl;
1018  }
1019  //if (proj->Integral(1,maxBin) == 0) cout << chanId << " truly has no counts!" << endl;
1020 
1021  }
1022  hHotTower->AddAt(nHitsAbovePedestal,chanId);
1023  }
1024 
1025 
1026 
1027 //stuck bit test (off & on!)
1028 //brief rant here - for checking whether or not bits are stuck on,
1029 //I wanted to just include all bits from 1 to 64
1030 //problem is, sometimes a channel has a high pedestal, and you
1031 //only get hits above 64. So that eliminates 64. Also, sometimes
1032 //a channel has a pedestal above 32, but the hit spectrum doesn't
1033 //extend to 64 (this happens with surprising regularity). That
1034 //eliminates 32.
1035 //I'm adding 16 to the mix because of an obviously stuck bit in the
1036 //barrel. To do so, I'm doing a separate loop with a separate test
1037 //which searches for the stuck on/off state of the 16 bit, and then
1038 //looks to see whether it can find 3 or more "clumps" of data.
1039 //(in other words, N/16 = j, j must take on at least 3 values)
1040 
1041  int numberofnonzerohits = 0;
1042  Short_t bitoff = 0;
1043  Short_t bitcompare = (1+2+4+8);
1044  Short_t biton = bitcompare;
1045  Short_t sixteenbiton = 16;
1046  for(Short_t bin=1; bin<maxBin; bin++) {
1047  if(proj->GetBinContent(bin) > 0) {
1048  bitoff |= (bin-1);
1049  biton &= (bin-1);
1050  sixteenbiton &= (bin-1);
1051  numberofnonzerohits++;
1052  }
1053  }
1054  Bool_t typea=kFALSE,typeb=kFALSE,typec=kFALSE;
1055  if((bitoff & 16) != 16 || (sixteenbiton & 16) != 0) { //these will not happen together
1056  for(Short_t bin=1; bin<maxBin; bin++) {
1057  if(proj->GetBinContent(bin) > 0) {
1058  if(!typea) {
1059  typea=kTRUE;
1060  bin = bin/16*16+15;
1061  } else if(!typeb) {
1062  typeb=kTRUE;
1063  bin = bin/16*16+15;
1064  } else if(!typec) {
1065  typec=kTRUE;
1066  break;
1067  }
1068  }
1069  }
1070  }
1071 
1072  if((bitoff & bitcompare) != bitcompare && numberofnonzerohits > 10) {
1073  cout << "136: " << chanId << " bitoff: " << bitoff << endl;
1074  statusVector[chanId] = statusVector[chanId] | (8+128);
1075  }
1076  if(biton != 0 && numberofnonzerohits > 10) {
1077  cout << "72: " << chanId << " biton: " << biton << endl;
1078  statusVector[chanId] = statusVector[chanId] | (8+64);
1079  }
1080 
1081  if((bitoff & 16) != 16 && typec && numberofnonzerohits > 10) {
1082  cout << "136: " << chanId << " 16" << endl;
1083  statusVector[chanId] = statusVector[chanId] | (8+128);
1084  }
1085  if((sixteenbiton & 16) != 0 && typec && numberofnonzerohits > 10) {
1086  cout << "72: " << chanId << " 16" << endl;
1087  statusVector[chanId] = statusVector[chanId] | (8+64);
1088  }
1089 
1090  for(Short_t bin=1; bin<maxBin; bin++) {
1091  if(proj->GetBinContent(bin) > 0) {
1092  bitoff |= (bin-1);
1093  biton &= (bin-1);
1094  numberofnonzerohits++;
1095  }
1096  }
1097 
1098 //total number of hits test
1099  Float_t entries = proj->Integral(1,proj->GetXaxis()->GetNbins());
1100  if (entries == 0) {
1101  statusVector[chanId] |= mZerobit; //channel has no pedestal?
1102  if (chanId==50 || chanId==139) cout << "entries=0 " << chanId << " Status: " << statusVector[chanId] << endl;
1103  }
1104 
1105 
1106 // D.Staszak --- Use this test to identify larger problems, as a first check -
1107 // a lot of entries in the zero bin is an indication of some problems
1108 // seen with crates, also this finds towers with a lot counts below the
1109 // pedestal peak
1110  // --- Comment out for running with 0's present: ---
1111  /*
1112  if ( (proj->GetBinContent(1) > 10 && (proj->GetBinContent(2)<10 || proj->GetBinContent(3)<10 ) && (proj->Integral(2,proj->GetXaxis()->GetNbins()) > 0)) ||
1113  (proj->GetBinContent(1) > 50 && (proj->GetBinContent(2)<50 || proj->GetBinContent(3)<50 ) && (proj->Integral(2,proj->GetXaxis()->GetNbins()) > 0))
1114  ) {
1115  if (pedestalmean[chanId] < 15.0) {
1116  //cout << "Ignoring - Caught with entries in 0: " << chanId << " Ped-mean: " << pedestalmean[chanId] << endl;
1117  } else {
1118  //cout << "Caught with entries in 0: " << chanId << " Ped-mean: " << pedestalmean[chanId] << endl;
1119  statusVector[chanId] = statusVector[chanId] | (8+64);
1120  }
1121  }
1122  */
1123  //--- End comment ----
1124 
1125 
1126  delete proj;
1127  } else {
1128 //lack of histogram test
1129  statusVector[chanId] |= mZerobit;
1130  }
1131 
1132  }
1133 
1134  unsigned int date = dateStamp;
1135  unsigned int time = timeStamp;
1136  StEmcDecoder barry(date,time);
1137  Int_t towerId,nextTowerId;
1138  Bool_t histogramsAreSame;
1139 
1140 //identical channel test
1141 
1142 //towerId indexed from 1
1143 //histogram projection is from 2 to N+1 (stupid... but true)
1144 
1145  if(mDetectorFlavor == "bemc") {
1146 //30 crates, indexed from 1
1147 //160 channels per crate, indexed from 0
1148 
1149  for(int crate=1; crate<31; crate++) {
1150  for(int channel=0; channel<160-1; channel++) { //comparing adjacent channels
1151  histogramsAreSame = kTRUE;
1152  barry.GetTowerIdFromCrate(crate, channel, towerId);
1153  barry.GetTowerIdFromCrate(crate, channel+1, nextTowerId);
1154 
1155  TH1D* projnow = hist->ProjectionY("projTemp2",towerId+1,towerId+1);
1156  TH1D* projnext = hist->ProjectionY("projTemp3",nextTowerId+1,nextTowerId+1);
1157 
1158  for (Int_t i=1; i<projnow->GetXaxis()->GetNbins() && histogramsAreSame; i++) {
1159  if( projnow->GetBinContent(i) != projnext->GetBinContent(i))
1160  histogramsAreSame = kFALSE;
1161  }
1162  if(histogramsAreSame) {
1163  statusVector[towerId] |= 256;
1164  statusVector[nextTowerId] |= 256;
1165  }
1166  }
1167  }
1168  } else { //flavor is eemc
1169 //6 crates, indexed from 0
1170 //120 channels per crate, indexed from 0
1171 
1172  for(int crate=0; crate<6; crate++) {
1173  for(int channel=0; channel<120-1; channel++) {
1174  histogramsAreSame = kTRUE;
1175  towerId = eemcCrateMap[crate][channel];
1176  nextTowerId = eemcCrateMap[crate][channel+1];
1177 //cout << towerId << nextTowerId << "is they!" << endl;
1178  TH1D* projnow = hist->ProjectionY("projTemp2",towerId+1,towerId+1);
1179  TH1D* projnext = hist->ProjectionY("projTemp3",nextTowerId+1,nextTowerId+1);
1180 
1181  for (Int_t i=1; i<projnow->GetXaxis()->GetNbins() && histogramsAreSame; i++) {
1182  if( projnow->GetBinContent(i) != projnext->GetBinContent(i))
1183  histogramsAreSame = kFALSE;
1184  }
1185  if(histogramsAreSame) {
1186  statusVector[towerId] |= 256;
1187  statusVector[nextTowerId] |= 256;
1188  }
1189  }
1190  }
1191  }
1192 
1193 
1194 //hot tower/cold tower tests
1195  Float_t sumofhits=0, nbinhits=0, ncratehottowers=0;
1196  Int_t goodTowers = 0;
1197  for(int i=1; i<mDetectorSize+1; i++) {
1198  if(hHotTower->GetBinContent(i) > 2) {
1199  sumofhits += hHotTower->GetBinContent(i);
1200  nbinhits++;
1201  }
1202  }
1203  if(nbinhits!=0) {
1204 //redo to nullify effects of hot and cold towers on the average
1205 //individual channel tests
1206  averageNumberOfHitsPerChannel = sumofhits/nbinhits;
1207  for(int i=1; i<mDetectorSize+1; i++) {
1208  if ( i==509 || i==533 || i==1306 || i==1397 || i==1503 || i==1892 || i==1893 || i==2074 || i==2075 ) cout << i << " Avg numberHits/chan= " << averageNumberOfHitsPerChannel << " for this tower: " << hHotTower->GetBinContent(i) << endl;
1209 
1210  //if(hHotTower->GetBinContent(i) > 10*averageNumberOfHitsPerChannel) {
1211  //if(hHotTower->GetBinContent(i) > 5*averageNumberOfHitsPerChannel) {
1212  if( (hHotTower->GetBinContent(i) > 8*averageNumberOfHitsPerChannel)
1213  || (i==1503 && (hHotTower->GetBinContent(i) > 5*averageNumberOfHitsPerChannel)) // a couple channels in 06 that were ugly and missed often
1214  || (i==1612 && (hHotTower->GetBinContent(i) > 5*averageNumberOfHitsPerChannel)) ) {
1215  statusVector[i] |= 2;
1216  cout << "ID: " << i << " average: " << averageNumberOfHitsPerChannel << " this tower: " << hHotTower->GetBinContent(i) << endl;
1217  }
1218  if(hHotTower->GetBinContent(i) < averageNumberOfHitsPerChannel/40) statusVector[i] |= 2+16;
1219  }
1220 
1221 //crate tests
1222 //(tighter cold tower test for whole crates to catch timing problems)
1223 //
1224 //This is not trivial.
1225 //Pretend that some crate is malfunctioning.
1226 //I realized that the malfunction could take *any* form,
1227 //meaning it could include both hot and cold towers.
1228 //Consequently, my crate test now says the following:
1229 //If more than 25% of the towers are hot, it's a bad crate.
1230 //Otherwise, ignore ALL hot towers when averaging the crate channels
1231 //together to see if the crate is malfunctioning.
1232 //I've also set the cut to be 20% of the average, as opposed to 10%
1233 //like individual channels.
1234 //
1235  if(mDetectorFlavor == "bemc") {
1236  for(int crate=1; crate<31; crate++) {
1237  sumofhits = 0;
1238  nbinhits = 0;
1239  ncratehottowers=0;
1240  for(int channel=0; channel<160; channel++) {
1241  barry.GetTowerIdFromCrate(crate, channel, towerId);
1242  if(hHotTower->GetBinContent(towerId) > 10*averageNumberOfHitsPerChannel) {
1243  ncratehottowers++;
1244  } else if(hHotTower->GetBinContent(towerId) > 2) {
1245  sumofhits += hHotTower->GetBinContent(towerId);
1246  nbinhits++;
1247  }
1248  }
1249  if(nbinhits == 0 || (nbinhits != 0 && sumofhits/nbinhits < averageNumberOfHitsPerChannel/5) || ncratehottowers >= 40) {
1250  for(int channel=0; channel<160; channel++) {
1251  barry.GetTowerIdFromCrate(crate, channel, towerId);
1252  statusVector[towerId] |= 2+16;
1253  }
1254  }
1255  }
1256  } else { //EEMC
1257  for(int crate=0; crate<6; crate++) {
1258  sumofhits = 0;
1259  nbinhits = 0;
1260  ncratehottowers=0;
1261  for(int channel=0; channel<120; channel++) {
1262  towerId = eemcCrateMap[crate][channel];
1263  if(hHotTower->GetBinContent(towerId) > 10*averageNumberOfHitsPerChannel) {
1264  ncratehottowers++;
1265  } else if(hHotTower->GetBinContent(towerId) > 2) {
1266  sumofhits += hHotTower->GetBinContent(towerId);
1267  nbinhits++;
1268  }
1269  }
1270  if(nbinhits == 0 || (nbinhits != 0 && sumofhits/nbinhits < averageNumberOfHitsPerChannel/5) || ncratehottowers >= 30) {
1271  for(int channel=0; channel<120; channel++) {
1272  towerId = eemcCrateMap[crate][channel];
1273  statusVector[towerId] |= 2+16;
1274  }
1275  }
1276  }
1277  }
1278 //rezeroing
1279  for(int i=1; i<mDetectorSize+1; i++) {
1280  if(statusVector[i] == 0) {
1281  statusVector[i]=1;
1282  if (i==50 || i==139) cout << "status=1 " << i << " Status: " << statusVector[i] << endl;
1283  goodTowers++;
1284  } else if(statusVector[i] & mZerobit) {
1285  statusVector[i]=0;
1286  if (i==50 || i==139) cout << "statusVec & mZerobit " << i << " Status: " << statusVector[i] << endl;
1287  }
1288  }
1289  } else {
1290  for(int i=1; i<mDetectorSize+1; i++) {
1291  statusVector[i]=0;
1292  if (i==50 || i==139) cout << "nbinhits=0 " << i << " Status: " << statusVector[i] << endl;
1293  }
1294  }
1295  delete gaus;
1296 
1297  return goodTowers;
1298 }
1299 
1300 void
1301 CSMStatusUtils::setDateTimeInfo(int runnumber,TTree* ttree) {
1302 
1303  Int_t thedate, thetime;
1304  TString thedatestring = "", thetimestring = "", tmpstr="";
1305  if(ttree) {
1306  ttree->SetBranchAddress("thedate",&thedate);
1307  ttree->SetBranchAddress("thetime",&thetime);
1308  ttree->GetEvent(0);
1309  mRunTimestampMap[runnumber] = thetime;
1310  mRunDatestampMap[runnumber] = thedate;
1311  } else {
1312  assert(ttree);
1313  }
1314 }
1315 
1316 TString
1317 CSMStatusUtils::getDateTimeString(int runnumber,TTree* ttree) {
1318 
1319  if(ttree) setDateTimeInfo(runnumber,ttree);
1320  Int_t thedate, thetime;
1321  TString thedatestring = "", thetimestring = "", tmpstr="";
1322  if(mRunTimestampMap.count(runnumber)>0) {
1323 // map<Int_t,Int_t>::const_iterator timeiter = mRunTimestampMap.find(runnumber);
1324 // map<Int_t,Int_t>::const_iterator dateiter = mRunDatestampMap.find(runnumber);
1325 // thedate = timeiter->second;
1326 // thetime = dateiter->second;
1327  thedate = mRunDatestampMap[runnumber];
1328  thetime = mRunTimestampMap[runnumber];
1329  } else {
1330  thedate = 0;
1331  thetime = 0;
1332  }
1333  thedatestring += thedate;
1334  thetimestring += thetime;
1335 //the next line makes me want to gag. Seriously.
1336  for(int i=0; i<5-TMath::Floor(TMath::Log10(thetime)); i++) tmpstr += "0";
1337 //that, my friends, is how to pad a TString without using sprintf
1338 
1339  TString datetimestring = "." + thedatestring + "." + tmpstr + thetimestring + ".";
1340  cout << datetimestring << endl;
1341  return datetimestring;
1342 }
1343 
1344 //this saves mRunStatusMap to an ASCII file
1345 
1346 Int_t
1347 CSMStatusUtils::saveStatusTablesToASCII(TString directory,int runnumber) {
1348 
1349  void* dir;
1350  if ((dir = gSystem->OpenDirectory(directory)) == NULL)
1351  gSystem->MakeDirectory(directory);
1352 
1353  IntToPtrVecShortConstIter first = mRunStatusMapPtr->begin();
1354  IntToPtrVecShortConstIter last = mRunStatusMapPtr->end();
1355  if(runnumber != 0) {
1356  first = mRunStatusMapPtr->find(runnumber);
1357  last = first;
1358  last++;
1359  }
1360  TString tmpstr, runnumberstring, datetimestring;
1361  for (IntToPtrVecShortConstIter iter = first; iter != last; ++iter) {
1362  int runnumber = iter->first;
1363  runnumberstring = "";
1364  runnumberstring += runnumber;
1365  datetimestring = getDateTimeString(runnumber);
1366  tmpstr = directory + "/run" + runnumberstring + "_" + mDetectorFlavor
1367  + datetimestring + "badTowers.txt";
1368  ofstream txtout(tmpstr.Data());
1369  for (Int_t i = 1; i <= mDetectorSize; i++) {
1370  txtout << i << "\t" << (*(iter->second))[i] << endl;
1371  }
1372  txtout.close();
1373  }
1374  return 0;
1375 }
1376 
1377 //this gets the number of towers whose status bits changed
1378 //between runs
1379 
1380 Int_t
1381 CSMStatusUtils::getNumberOfChangedTowers(Int_t runnumber) {
1382 
1383  IntToPtrVecShortConstIter iter = mRunStatusMapPtr->find(runnumber);
1384 // first run - nothing to compare with
1385  if (iter == mRunStatusMapPtr->begin())
1386  return -1;
1387 // run not found
1388  if (iter == mRunStatusMapPtr->end())
1389  return -1;
1390 
1391  // get previous run
1392  IntToPtrVecShortConstIter preIter = iter;
1393  preIter--;
1394  Int_t changedTowers = 0;
1395  vector<Short_t>* statusVector = iter->second;
1396  vector<Short_t>* oldStatusVector = preIter->second;
1397  for (UInt_t i = 1; i < statusVector->size(); i++)
1398  if ((((*statusVector)[i] != 1) && ((*oldStatusVector)[i] == 1)) ||
1399  (((*statusVector)[i] == 1) && ((*oldStatusVector)[i] != 1)))
1400  changedTowers++;
1401 
1402  return changedTowers;
1403 }
1404 
1405 //write pedestals to ROOT db files and to text files
1406 
1407 void
1408 CSMStatusUtils::writePedestals(Int_t runNumber, TString directory,
1409  std::vector<Short_t>& statusVector,
1410  std::vector<Float_t>& pedestalmean,
1411  std::vector<Float_t>& pedestalwidth,
1412  std::vector<Float_t>& pedestalchi) {
1413 
1414 //write out pedestals to the text and root pedestalfiles
1415  TString pedtxtfilename = directory + "/pedestals/";
1416 
1417 //create the pedestal directory, if it doesn't exist
1418  void* dir = NULL;
1419  if ((dir = gSystem->OpenDirectory(pedtxtfilename.Data())) == NULL)
1420  gSystem->MakeDirectory(pedtxtfilename.Data());
1421 
1422  TString runnumber = "";
1423  runnumber += runNumber;
1424  pedtxtfilename = directory + "/pedestals/" + mDetectorFlavor
1425  + "pedestals_for_run_" + runnumber + ".ped";
1426  ofstream pedestalfile(pedtxtfilename.Data());
1427  pedestalfile.setf(ios::left);
1428  pedestalfile << setw(8) << "ID" <<
1429  setw(8) << "PED" <<
1430  setw(8) << "RMS" <<
1431  setw(8) << "STATUS" << endl;
1432 
1433  St_emcPed *bemc_ped=new St_emcPed("bemcPed",1);
1434  emcPed_st t_ped;
1435  cout << t_ped.Status[367] << endl;
1436  TString datetimestring = getDateTimeString(runNumber);
1437  TString pedrootfilename = directory + "/pedestals/" + mDetectorFlavor
1438  + "Ped" + datetimestring + "root";
1439  TFile* fout_status = new TFile(pedrootfilename.Data(),"RECREATE");
1440 
1441  Short_t shortpedmean,shortpedwidth;
1442  for (UInt_t i = 1; i < statusVector.size(); i++) {
1443  shortpedmean = TMath::Nint(100*pedestalmean[i]);
1444  shortpedwidth = TMath::Nint(100*pedestalwidth[i]);
1445  t_ped.Status[i-1] = statusVector[i]; //indexed from 0
1446  t_ped.AdcPedestal[i-1] = shortpedmean; //indexed from 0
1447  t_ped.AdcPedestalRMS[i-1] = shortpedwidth; //indexed from 0
1448  t_ped.ChiSquare[i-1]=pedestalchi[i]; //indexed from 0
1449  pedestalfile << setw(8) << i <<
1450  setw(8) << setprecision(4) << shortpedmean <<
1451  setw(8) << setprecision(3) << shortpedwidth <<
1452  setw(8) << setprecision(3) << statusVector[i] << endl;
1453  }
1454  pedestalfile.close();
1455 //gzip the text pedestal files
1456  TString tmpstr = "rm -f " + pedtxtfilename + ".gz";
1457  gSystem->Exec(tmpstr.Data());
1458  tmpstr = "gzip " + pedtxtfilename;
1459  gSystem->Exec(tmpstr.Data());
1460 
1461  fout_status->cd();
1462  bemc_ped->AddAt(&t_ped,0);
1463  bemc_ped->Write();
1464  fout_status->Close();
1465  delete bemc_ped;
1466  delete fout_status;
1467 }
1468 
1469 //this finds the runs which are at the ends of fills, and writes
1470 //that information into the mFillEndMap
1471 
1472 void
1473 CSMStatusUtils::findFillEnds() {
1474 
1475  Float_t runFillNumber, priorRunFillNumber=-1;
1476  int runnumber, priorRunNumber=0;
1477  for (map<Int_t,string>::const_iterator iter = mHistFileMap.begin();
1478  iter != mHistFileMap.end(); ++iter) {
1479  TFile* file = new TFile(iter->second.c_str(),"READ");
1480  if (file && file->IsOpen()) {
1481  runnumber = iter->first;
1482  assert(runnumber);
1483  TTree* runTree = dynamic_cast<TTree*>(file->Get("calinfo"));
1484  assert(runTree);
1485  runTree->SetBranchAddress("fillnum",&runFillNumber);
1486  runTree->GetEvent(0);
1487 //if(runFillNumber==0) {
1488 // cout << iter->second.c_str() << endl;
1489 // cout<<"runfillnumb is " << runFillNumber << endl;
1490 //}
1491 // assert(runFillNumber);
1492  if(priorRunNumber != 0) {
1493  if(runFillNumber != priorRunFillNumber)
1494  mFillEndMap[priorRunNumber] = kTRUE;
1495  else
1496  mFillEndMap[priorRunNumber] = kFALSE;
1497  }
1498  ++iter;
1499  if(iter == mHistFileMap.end())
1500  mFillEndMap[runnumber] = kTRUE;
1501  --iter;
1502 
1503  priorRunNumber = runnumber;
1504  priorRunFillNumber = runFillNumber;
1505  }
1506  file->Close();
1507  delete file;
1508  }
1509 }
1510 
1511 
1512 //this takes RunStatusMap, creates a brand new 2d histogram
1513 //of channel vs run, and fills each point with the status of
1514 //the channel
1515 
1516 TH2F*
1517 CSMStatusUtils::makeStatusVersusTimePlot() {
1518  gStyle->SetPalette(1,0);
1519  Int_t runs = mRunStatusMapPtr->size();
1520  TH2F* hist;
1521  if(mDetectorFlavor=="bemc")
1522  hist = new TH2F("bemcStatus_run","bemcStatus vs run",
1523  4801,-0.5,4800.5,runs+1,-0.5,runs+0.5);
1524  else
1525  hist = new TH2F("eemcStatus_run","eemcStatus vs run",
1526  721,-0.5,720.5,runs+1,-0.5,runs+0.5);
1527 
1528  hist->GetXaxis()->SetTitle("tower id");
1529  hist->GetYaxis()->SetTitle("relative run number");
1530  Int_t runNumber = 0;
1531  for (IntToPtrVecShortConstIter iter = mRunStatusMapPtr->begin();
1532  iter != mRunStatusMapPtr->end(); ++iter) {
1533  for (Int_t i=1; i<=mDetectorSize; i++) {
1534  if ((*(iter->second))[i] != 0) {
1535  hist->Fill(i,runNumber,(*(iter->second))[i]);
1536  }
1537  }
1538  runNumber++;
1539  }
1540  return hist;
1541 }
1542 
1543 void CSMStatusUtils::writeHtmlHeaderBadTowerList(ofstream& out,Int_t runnumber) {
1544  out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl;
1545  out << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 STRICT//EN\"" << endl;
1546  out << " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">" << endl;
1547  out << "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\" >" << endl;
1548  out << "<head>" << endl;
1549  out << "<meta content=\"text/html; charset=UTF-8\" />" << endl;
1550  out << "<title> Bad BEMC Tower List Run " << runnumber << " </title>" << endl;
1551  out << "<link rel=\"StyleSheet\" href=\"../../myStyle.css\" type=\"text/css\" />" << endl;
1552  out << "</head>" << endl;
1553  out << "<body xml:lang=\"en\" lang=\"en\" >" << endl;
1554  out << "<h1> Bad BEMC Tower List Run " << runnumber << " </h1>" << endl;
1555  out << "<div class=\"header\">Status Codes</div>" << endl;
1556  out << "(codes are backward compatible with prior status tables)" << endl;
1557  out << "<ul>" << endl;
1558  out << "<li> 0 == channel does not exist </li>" << endl;
1559  out << "<li> 1 == channel is good </li>" << endl;
1560  out << "<li> 2 == channel is either hot or cold (see bit 16) </li>" << endl;
1561  out << "<li> 4 == channel has a weird pedestal (see bit 32)</li>" << endl;
1562  out << "<li> 8 == channel has a stuck bit (see bits 64 and 128) </li>" << endl;
1563  out << "<li> 16 == if off, hot tower (10x as many hits as others); if on, " <<
1564  "cold tower (40x fewer hits than others) </li>" << endl;
1565  out << "<li> 32 == if off, pedestal mean is out of bounds; if on, " <<
1566  "pedestal width is too large/small</li>" << endl;
1567  out << "<li> 64 == bit stuck on</li>" << endl;
1568  out << "<li> 128 == bit stuck off</li>" << endl;
1569  out << "</ul>" << endl;
1570  out << "<div class=\"header\">Bad Tower List</div>" << endl;
1571  out << "<p> Tower ADC plots are only available if the tower status has changed"
1572  << " compared to the previous run</p>" << endl;
1573  out << "<table border=\"1\">" << endl;
1574  out << "<tbody>" << endl;
1575  out << "<tr> <th width=\"50\"> Tower ID </th> <th width=\"50\"> Status Code </th> <th width=\"100\"> ADC plot </th> </tr>" << endl;
1576 }
1577 
1578 void CSMStatusUtils::writeHtmlFooterBadTowerList(ofstream& out) {
1579  out << "</tbody>" << endl;
1580  out << "</table>" << endl;
1581  out << "<address> Thorsten Kollegger - last updated";
1582  out << " 7/30/2004";
1583  out << "</address>" << endl;
1584  out << "</body>" << endl;
1585  out << "</html>" << endl;
1586 }
1587 
1588 void CSMStatusUtils::writeHtmlHeaderSummary(ofstream& out) {
1589  out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl;
1590  out << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 STRICT//EN\"" << endl;
1591  out << " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">" << endl;
1592  out << "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\" >" << endl;
1593  out << "<head>" << endl;
1594  out << "<meta content=\"text/html; charset=UTF-8\" />" << endl;
1595  out << "<title> BEMC Tower Status Analysis </title>" << endl;
1596  out << "<link rel=\"StyleSheet\" href=\"../../myStyle.css\" type=\"text/css\" />" << endl;
1597  out << "</head>" << endl;
1598  out << "<body xml:lang=\"en\" lang=\"en\" >" << endl;
1599  ifstream in("StRoot/StElectronInvMassAna/CSMStatusUtilsSummary.html");
1600  Char_t buffer[2048];
1601  while (in.is_open()) {
1602  in.getline(buffer,2048);
1603  if (in.eof()) {
1604  break;
1605  }
1606  out << buffer << endl;
1607  }
1608 }
1609 
1610 void CSMStatusUtils::writeHtmlFooterSummary(ofstream& out) {
1611  out << "</tbody>" << endl;
1612  out << "</table>" << endl;
1613  out << "<address> David Staszak (taken from David Relyea, Thorsten Kollegger) - last updated";
1614  out << " 8/20/2006";
1615  out << "</address>" << endl;
1616  out << "</body>" << endl;
1617  out << "</html>" << endl;
1618 }
1619 
1620 ClassImp(CSMStatusUtils)
Int_t makeStatusPlots(TString plotDir)