Belle II Software  release-08-01-10
eclee5x5Algorithm.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 
9 /* Own header. */
10 #include <ecl/calibration/eclee5x5Algorithm.h>
11 
12 /* ECL headers. */
13 #include <ecl/dataobjects/ECLElementNumbers.h>
14 #include <ecl/dbobjects/ECLCrystalCalib.h>
15 
16 /* ROOT headers. */
17 #include <TDecompLU.h>
18 #include <TH2F.h>
19 #include <TF1.h>
20 #include <TFile.h>
21 #include <TMatrixD.h>
22 #include <TMatrixDSym.h>
23 #include <TROOT.h>
24 
25 using namespace std;
26 using namespace Belle2;
27 using namespace ECL;
28 
30 eclee5x5Algorithm::eclee5x5Algorithm(): CalibrationAlgorithm("eclee5x5Collector")
31 {
33  "Perform energy calibration of ecl crystals by analyzing energy in 25-crystal sums from Bhabha events"
34  );
35 }
36 
38 {
41  TH1F* dummy;
42  dummy = (TH1F*)gROOT->FindObject("AverageExpECrys");
43  if (dummy) {delete dummy;}
44  dummy = (TH1F*)gROOT->FindObject("AverageElecCalib");
45  if (dummy) {delete dummy;}
46  dummy = (TH1F*)gROOT->FindObject("AverageInitCalib");
47  if (dummy) {delete dummy;}
48  dummy = (TH1F*)gROOT->FindObject("meanEnvsCrysID");
49  if (dummy) {delete dummy;}
50 
52  gROOT->SetBatch();
53 
56  B2INFO("eclee5x5Algorithm parameters:");
57  B2INFO("outputName = " << m_outputName);
58  B2INFO("minEntries = " << m_minEntries);
59  B2INFO("payloadName = " << m_payloadName);
60  B2INFO("storeConst = " << m_storeConst);
61  if (m_payloadName == "ECLeedPhiData" or m_payloadName == "ECLeedPhiMC" or m_payloadName == "None") {
62  B2INFO("fracLo = " << m_fracLo);
63  B2INFO("fracHiSym = " << m_fracHiSym);
64  B2INFO("fracHiASym = " << m_fracHiASym);
65  B2INFO("nsigLo = " << m_nsigLo);
66  B2INFO("nsigHiSym = " << m_nsigHiSym);
67  B2INFO("nsigHiASym = " << m_nsigHiASym);
68  }
69 
72  auto EnVsCrysID = getObjectPtr<TH2F>("EnVsCrysID");
73  auto RvsCrysID = getObjectPtr<TH1F>("RvsCrysID");
74  auto NRvsCrysID = getObjectPtr<TH1F>("NRvsCrysID");
75  auto Qmatrix = getObjectPtr<TH2F>("Qmatrix");
76  auto ElecCalibvsCrys = getObjectPtr<TH1F>("ElecCalibvsCrys");
77  auto ExpEvsCrys = getObjectPtr<TH1F>("ExpEvsCrys");
78  auto InitialCalibvsCrys = getObjectPtr<TH1F>("InitialCalibvsCrys");
79  auto CalibEntriesvsCrys = getObjectPtr<TH1F>("CalibEntriesvsCrys");
80  auto EntriesvsCrys = getObjectPtr<TH1F>("EntriesvsCrys");
81  auto dPhivsThetaID = getObjectPtr<TH2F>("dPhivsThetaID");
82 
86  TH1F* AverageExpECrys = new TH1F("AverageExpECrys", "Average expected E per crys from collector;Crystal ID;Energy (GeV)",
89  TH1F* AverageElecCalib = new TH1F("AverageElecCalib", "Average electronics calib const vs crystal;Crystal ID;Calibration constant",
91  TH1F* AverageInitCalib = new TH1F("AverageInitCalib", "Average initial calib const vs crystal;Crystal ID;Calibration constant",
93  TH1F* meanEnvsCrysID = new TH1F("meanEnvsCrysID", "Mean normalized energy vs crystal;CrystalID;E/Eexp",
95 
96  for (int cellID = 1; cellID <= ECLElementNumbers::c_NCrystals; cellID++) {
97  double TotEntries = CalibEntriesvsCrys->GetBinContent(cellID);
98  if (TotEntries > 0.) {
99  AverageElecCalib->SetBinContent(cellID, ElecCalibvsCrys->GetBinContent(cellID) / TotEntries);
100  AverageExpECrys->SetBinContent(cellID, ExpEvsCrys->GetBinContent(cellID) / TotEntries);
101  AverageInitCalib->SetBinContent(cellID, InitialCalibvsCrys->GetBinContent(cellID) / TotEntries);
102  }
103 
104  TH1D* En = EnVsCrysID->ProjectionY("En", cellID, cellID);
105  meanEnvsCrysID->SetBinContent(cellID, En->GetMean());
106  meanEnvsCrysID->SetBinError(cellID, En->GetStdDev());
107  }
108 
111  TString fName = m_outputName;
112  TFile* histfile = new TFile(fName, "recreate");
113  EnVsCrysID->Write();
114  RvsCrysID->Write();
115  NRvsCrysID->Write();
116  Qmatrix->Write();
117  AverageElecCalib->Write();
118  AverageExpECrys->Write();
119  AverageInitCalib->Write();
120  EntriesvsCrys->Write();
121  dPhivsThetaID->Write();
122  meanEnvsCrysID->Write();
123 
126  if (m_payloadName == "None") {
127  B2INFO("eclee5x5Algorithm has not been asked to find constants; copying input histograms and quitting");
128  histfile->Close();
129  return c_NotEnoughData;
130  }
131 
134  for (int cellID = 1; cellID <= ECLElementNumbers::c_NCrystals; cellID++) {
135 
137  if (AverageInitCalib->GetBinContent(cellID) > 0.) {
138  if (EntriesvsCrys->GetBinContent(cellID) < m_minEntries) {
139  histfile->Close();
140  B2INFO("eclee5x5Algorithm: insufficient data for cellID = " << cellID << " " << EntriesvsCrys->GetBinContent(cellID) << " entries");
141  return c_NotEnoughData;
142  }
143  }
144  }
145 
146 
150 
151 
154  bool foundConst = false;
155  TH1F* gVsCrysID = new TH1F("gVsCrysID", "Ratio of new to old calibration vs crystal ID;crystal ID;vector g",
157  TH1F* CalibVsCrysID = new TH1F("CalibVsCrysID", "Calibration constant vs crystal ID;crystal ID;counts per GeV",
159  TH1F* ExpEnergyperCrys = new TH1F("ExpEnergyperCrys", "Expected energy per crystal;Crystal ID;Energy in 25 crystal sum (GeV)",
162  TString title = "dPhi cut per crystal " + m_payloadName + ";Crystal ID;dPhi requirement (deg)";
163  TH1F* dPhiperCrys = new TH1F("dPhiperCrys", title, ECLElementNumbers::c_NCrystals, 0, ECLElementNumbers::c_NCrystals);
164 
167  if (m_payloadName == "ECLExpee5x5E") {
168  for (int cellID = 1; cellID <= ECLElementNumbers::c_NCrystals; cellID++) {
169  float mean = meanEnvsCrysID->GetBinContent(cellID);
170  float stdDev = meanEnvsCrysID->GetBinError(cellID);
171  float inputE = AverageExpECrys->GetBinContent(cellID);
172  if (mean > 0. and stdDev > 0.) {
173  ExpEnergyperCrys->SetBinContent(cellID, mean * abs(inputE));
174  ExpEnergyperCrys->SetBinError(cellID, stdDev * abs(inputE));
175  } else {
176  ExpEnergyperCrys->SetBinContent(cellID, inputE);
177  ExpEnergyperCrys->SetBinError(cellID, 0.05 * inputE);
178  }
179  }
180 
181  //..Generate the payload, if requested
182  foundConst = true;
183  if (m_storeConst) {
184  std::vector<float> tempCalib;
185  std::vector<float> tempCalibStdDev;
186 
187  for (int cellID = 1; cellID <= ECLElementNumbers::c_NCrystals; cellID++) {
188  tempCalib.push_back(ExpEnergyperCrys->GetBinContent(cellID));
189  tempCalibStdDev.push_back(ExpEnergyperCrys->GetBinError(cellID));
190  }
191  ECLCrystalCalib* ExpectedE = new ECLCrystalCalib();
192  ExpectedE->setCalibVector(tempCalib, tempCalibStdDev);
193  saveCalibration(ExpectedE, "ECLExpee5x5E");
194  B2INFO("eclee5x5Algorithm: successfully stored expected energies ECLExpee5x5E");
195  }
196 
199  } else if (m_payloadName == "ECLCrystalEnergy5x5") {
200 
201  //..Create the Q matrix and the R vector
202  TMatrixDSym matrixQ(ECLElementNumbers::c_NCrystals);
203  TVectorD vectorR(ECLElementNumbers::c_NCrystals);
204  for (int ix = 1; ix <= ECLElementNumbers::c_NCrystals; ix++) {
205  vectorR[ix - 1] = RvsCrysID->GetBinContent(ix);
206  for (int iy = 1; iy <= ECLElementNumbers::c_NCrystals; iy++) {
207  matrixQ[ix - 1][iy - 1] = Qmatrix->GetBinContent(ix, iy);
208  }
209  }
210 
211  //..Crystals that are not being calibrated have no entries in NR. Adjust R and Q to get g=-1
212  int nNotCalibrated = 0;
213  for (int cellID = 1; cellID <= ECLElementNumbers::c_NCrystals; cellID++) {
214  if (NRvsCrysID->GetBinContent(cellID) < 0.5) {
215  for (int othercell = 1; othercell <= ECLElementNumbers::c_NCrystals; othercell++) {
216  matrixQ[cellID - 1][othercell - 1] = 0.;
217  matrixQ[othercell - 1][cellID - 1] = 0.;
218  }
219  matrixQ[cellID - 1][cellID - 1] = 1.;
220  vectorR[cellID - 1] = -1.;
221  nNotCalibrated++;
222  }
223  }
224  B2INFO("eclee5x5Algorithm: " << nNotCalibrated << " crystals will not be calibrated. ");
225 
226  //..Invert to solve Q g = R
227  TDecompLU lu(matrixQ);
228  bool solved;
229  TVectorD vectorg = lu.Solve(vectorR, solved);
230 
231  //..Fill histograms and check that there are no unexpected negative output values
232  if (solved) {
233  foundConst = true;
234  for (int cellID = 1; cellID <= ECLElementNumbers::c_NCrystals; cellID++) {
235  gVsCrysID->SetBinContent(cellID, vectorg[cellID - 1]);
236  gVsCrysID->SetBinError(cellID, 0.);
237  float newCalib = vectorg[cellID - 1] * abs(AverageInitCalib->GetBinContent(cellID));
238  CalibVsCrysID->SetBinContent(cellID, newCalib);
239  CalibVsCrysID->SetBinError(cellID, 0.);
240 
241  if (vectorg[cellID - 1] < 0. and NRvsCrysID->GetBinContent(cellID) > 0.) {foundConst = false;}
242  }
243  }
244 
245  //..Generate the payload if requested, and if the matrix inversion worked
246  if (m_storeConst and foundConst) {
247  std::vector<float> tempCalib;
248  std::vector<float> tempCalibStdDev;
249 
250  for (int cellID = 1; cellID <= ECLElementNumbers::c_NCrystals; cellID++) {
251  tempCalib.push_back(CalibVsCrysID->GetBinContent(cellID));
252  tempCalibStdDev.push_back(CalibVsCrysID->GetBinError(cellID));
253  }
254  ECLCrystalCalib* e5x5ECalib = new ECLCrystalCalib();
255  e5x5ECalib->setCalibVector(tempCalib, tempCalibStdDev);
256  saveCalibration(e5x5ECalib, "ECLCrystalEnergyee5x5");
257  B2INFO("eclee5x5Algorithm: successfully stored calibration ECLCrystalEnergyee5x5");
258  }
259 
262  } else if (m_payloadName == "ECLeedPhiData" or m_payloadName == "ECLeedPhiMC") {
263 
264  //..Find mean and sigma of Gaussian fit to ThetaID projections with sufficient statistics
265  float dPhiCenter[69] = {};
266  float dPhiHalfWidth[69] = {};
267  int firstID = 0;
268  int lastID = 0;
269  const int nbins = dPhivsThetaID->GetNbinsX();
270  for (int ib = 1; ib <= nbins; ib++) {
271  TH1D* proj = dPhivsThetaID->ProjectionY("proj", ib, ib);
272  if (proj->Integral() < 100) {continue;}
273  int thetaID = (int)dPhivsThetaID->GetXaxis()->GetBinCenter(ib);
274  if (firstID == 0) { firstID = thetaID; }
275  lastID = thetaID;
276 
277  //..Find the fit limits
278  double fracHi = m_fracHiSym;
279  double nsigHi = m_nsigHiSym;
280  if (thetaID <= m_lastLoThetaID) {
281  fracHi = m_fracHiASym;
282  nsigHi = m_nsigHiASym;
283  }
284 
285  //..Fit range includes all bins with entries>m_fracLo*peak on the low side of the
286  // peak, and entries>m_fracHi*peak on the high side.
287  // i.e. low edge of bin on low side, high edge on high side = low edge of bin+1
288  double peak = proj->GetMaximum();
289  int nPhiBins = proj->GetNbinsX();
290  int binLo = 1;
291  do {
292  binLo++;
293  } while (proj->GetBinContent(binLo) < m_fracLo * peak and binLo < nPhiBins);
294  double xfitLo = proj->GetBinLowEdge(binLo);
295 
296  //..Start search for the upper edge of the fit range at 175 deg to avoid gamma gamma
297  // and e gamma peaks
298  int binHi = proj->GetXaxis()->FindBin(175.01);
299  do {
300  binHi--;
301  } while (proj->GetBinContent(binHi)<fracHi* peak and binHi>1);
302  double xfitHi = proj->GetBinLowEdge(binHi + 1);
303 
304  //..Check that the fit region is sensible
305  int peakBin = proj->GetMaximumBin();
306  if (binLo >= peakBin or binHi <= peakBin) {
307  B2ERROR("Flawed dPhi fit range for thetaID = " << thetaID << " peakBin = " << peakBin << " binLo = " << binLo << "binHi = " <<
308  binHi);
309  }
310 
311  //..Now fit a Gaussian to the selected region
312  proj->Fit("gaus", "", "", xfitLo, xfitHi);
313 
314  //..Find mean, sigma, and selection range. Record center and half-width of range.
315  TF1* fitGaus = proj->GetFunction("gaus");
316  float mean = fitGaus->GetParameter(1);
317  float sigma = fitGaus->GetParameter(2);
318  float dPhiLo = mean - m_nsigLo * sigma;
319  float dPhiHi = mean + nsigHi * sigma;
320  dPhiCenter[thetaID] = 0.5 * (dPhiLo + dPhiHi);
321  dPhiHalfWidth[thetaID] = 0.5 * (dPhiHi - dPhiLo);
322  }
323 
324  //..Pad the thetaID's without fits with the first or last thetaID values
325  for (int thetaID = 0; thetaID < firstID; thetaID++) {
326  dPhiCenter[thetaID] = dPhiCenter[firstID];
327  dPhiHalfWidth[thetaID] = dPhiHalfWidth[firstID];
328  }
329  for (int thetaID = lastID + 1; thetaID < 69; thetaID++) {
330  dPhiCenter[thetaID] = dPhiCenter[lastID];
331  dPhiHalfWidth[thetaID] = dPhiHalfWidth[lastID];
332  }
333 
334  //..Now copy these to each crystal to generate the payload and fill the output histogram.
335  // We will use ECLNeighours to get the number of crystals in each theta ring
337  std::vector<float> tempCalib;
338  std::vector<float> tempCalibWidth;
339  tempCalib.resize(ECLElementNumbers::c_NCrystals);
340  tempCalibWidth.resize(ECLElementNumbers::c_NCrystals);
341  int crysID = 0;
342  for (int thetaID = 0; thetaID < 69; thetaID++) {
343  for (int ic = 0; ic < m_eclNeighbours5x5->getCrystalsPerRing(thetaID); ic++) {
344  tempCalib.at(crysID) = dPhiCenter[thetaID];
345  tempCalibWidth.at(crysID) = dPhiHalfWidth[thetaID];
346  dPhiperCrys->SetBinContent(crysID + 1, dPhiCenter[thetaID]);
347  dPhiperCrys->SetBinError(crysID + 1, dPhiHalfWidth[thetaID]);
348  crysID++;
349  }
350  }
351 
352  //..Store the payload, if requested
353  foundConst = true;
354  if (m_storeConst) {
355  ECLCrystalCalib* eedPhi = new ECLCrystalCalib();
356  eedPhi->setCalibVector(tempCalib, tempCalibWidth);
358  B2INFO("eclee5x5Algorithm: successfully stored calibration " << m_payloadName);
359  }
360 
363  } else {
364  B2ERROR("eclee5x5Algorithm: invalid payload name: m_payloadName = " << m_payloadName);
365  }
366 
369  histfile->cd();
370  if (m_payloadName == "ECLExpee5x5E") {
371  ExpEnergyperCrys->Write();
372  } else if (m_payloadName == "ECLCrystalEnergy5x5") {
373  gVsCrysID->Write();
374  CalibVsCrysID->Write();
375  } else if (m_payloadName == "ECLeedPhiData" or m_payloadName == "ECLeedPhiMC") {
376  dPhiperCrys->Write();
377  }
378  histfile->Close();
379 
380  dummy = (TH1F*)gROOT->FindObject("gVsCrysID"); delete dummy;
381  dummy = (TH1F*)gROOT->FindObject("CalibVsCrysID"); delete dummy;
382  dummy = (TH1F*)gROOT->FindObject("ExpEnergyperCrys"); delete dummy;
383  dummy = (TH1F*)gROOT->FindObject("dPhiperCrys"); delete dummy;
384 
387  if (!m_storeConst) {
388  if (foundConst) {
389  B2INFO("eclee5x5Algorithm successfully found constants but was not asked to store them");
390  } else {
391  B2INFO("eclee5x5Algorithm was not asked to store constants, and did not succeed in finding them");
392  }
393  return c_Failure;
394  } else if (!foundConst) {
395  if (m_payloadName == "ECLExpee5x5E") {
396  B2INFO("eclee5x5Algorithm: failed to store expected values");
397  } else if (m_payloadName == "ECLCrystalEnergy5x5") {
398  B2INFO("eclee5x5Algorithm: failed to store calibration constants");
399  } else if (m_payloadName == "ECLeedPhiData" or m_payloadName == "ECLeedPhiMC") {
400  B2INFO("eclee5x5Algorithm: failed to find dPhi* selection criteria");
401  }
402  return c_Failure;
403  }
404  return c_OK;
405 }
Base class for calibration algorithms.
void saveCalibration(TClonesArray *data, const std::string &name)
Store DBArray payload with given name with default IOV.
void setDescription(const std::string &description)
Set algorithm description (in constructor)
EResult
The result of calibration.
@ c_OK
Finished successfuly =0 in Python.
@ c_NotEnoughData
Needs more data =2 in Python.
@ c_Failure
Failed =3 in Python.
General DB object to store one calibration number per ECL crystal.
void setCalibVector(const std::vector< float > &CalibConst, const std::vector< float > &CalibConstUnc)
Set vector of constants with uncertainties.
Class to get the neighbours for a given cell id.
Definition: ECLNeighbours.h:25
short int getCrystalsPerRing(const short int thetaid) const
return number of crystals in a given theta ring
Definition: ECLNeighbours.h:39
int m_minEntries
all crystals to be calibrated must have this many entries
double m_fracHiASym
or fracHiASym*peak, at low values of thetaID
double m_fracLo
start dPhi fit where data is > fraclo*peak
double m_nsigHiASym
or mean+nsigHiASym*sigma at low thetaID
double m_nsigLo
dPhi region is mean - nsigLo*sigma
std::string m_payloadName
Name of the payload to be stored.
ECL::ECLNeighbours * m_eclNeighbours5x5
Neighbours, used to get nCrys per ring.
std::string m_outputName
..Parameters to control job to find energy calibration using Bhabhas
bool m_storeConst
write payload to localdb if true
int m_lastLoThetaID
use asymmetric dPhi range for thetaID<= this value
virtual EResult calibrate() override
..Run algorithm on events
double m_fracHiSym
end dPhi fit where data is > fracHiSym*peak
double m_nsigHiSym
to mean + nsigHiSym*sigma
const int c_NCrystals
Number of crystals.
Abstract base class for different kinds of events.