Belle II Software development
eclNOptimalCollectorModule.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/modules/eclNOptimalCollector/eclNOptimalCollectorModule.h>
11
12/* ECL headers. */
13#include <ecl/dataobjects/ECLCalDigit.h>
14#include <ecl/dataobjects/ECLShower.h>
15#include <ecl/geometry/ECLNeighbours.h>
16
17/* Basf2 headers. */
18#include <mdst/dataobjects/MCParticle.h>
19#include <mdst/dataobjects/ECLCluster.h>
20#include <mdst/dataobjects/EventLevelClusteringInfo.h>
21#include <mdst/dataobjects/EventLevelTriggerTimeInfo.h>
22
23/* ROOT headers. */
24#include <TH2F.h>
25#include <Math/Vector3D.h>
26#include <Math/VectorUtil.h>
27
28/* C++ headers. */
29#include <iostream>
30
31using namespace std;
32using namespace Belle2;
33using namespace ECL;
34
35//-----------------------------------------------------------------
36// Register the Module
37//-----------------------------------------------------------------
38REG_MODULE(eclNOptimalCollector);
39
40//-----------------------------------------------------------------
41// Implementation
42//-----------------------------------------------------------------
43
47{
48 // Set module properties
49 setDescription("Calibration collector module to find optimal number of crystal for cluster energies");
51 addParam("numberEnergies", m_numberEnergies, "number of generated energies", 8);
52 addParam("energiesForward", m_energiesForward, "generated photon energies, forward", std::vector<double> {0.030, 0.050, 0.100, 0.200, 0.483, 1.166, 2.816, 6.800});
53 addParam("energiesBarrel", m_energiesBarrel, "generated photon energies, barrel", std::vector<double> {0.030, 0.050, 0.100, 0.200, 0.458, 1.049, 2.402, 5.500});
54 addParam("energiesBackward", m_energiesBackward, "generated photon energies, backward", std::vector<double> {0.030, 0.050, 0.100, 0.200, 0.428, 0.917, 1.962, 4.200});
55 addParam("digitArrayName", m_digitArrayName, "name of ECLCalDigit data object", std::string("ECLTrimmedDigits"));
56 addParam("showerArrayName", m_showerArrayName, "name of ECLShower data object", std::string("ECLTrimmedShowers"));
57 addParam("nGroupPerThetaID", m_nGroupPerThetaID, "groups per standard thetaID", 8);
58}
59
63{
64
65 //--------------------------------------------------------
66 //..Check that input parameters are consistent
67 const int nForward = m_energiesForward.size();
68 const int nBarrel = m_energiesBarrel.size();
69 const int nBackward = m_energiesBackward.size();
70 if (nForward != m_numberEnergies or nBarrel != m_numberEnergies or nBackward != m_numberEnergies) {
71 B2FATAL("eclNOptimalCollector: length of energy vectors inconsistent with parameter numberEnergies: " << nForward << " " <<
72 nBarrel << " " << nBackward << " " << m_numberEnergies);
73 }
74
75 //..Store generated energies as integers in MeV
76 iEnergies.resize(nLeakReg, std::vector<int>(m_numberEnergies, 0));
77 for (int ie = 0; ie < m_numberEnergies; ie++) {
78 iEnergies[0][ie] = (int)(1000.*m_energiesForward[ie] + 0.5);
79 iEnergies[1][ie] = (int)(1000.*m_energiesBarrel[ie] + 0.5);
80 iEnergies[2][ie] = (int)(1000.*m_energiesBackward[ie] + 0.5);
81 }
82
83 //--------------------------------------------------------
84 //..Required data objects
86 m_eclClusterArray.isRequired("ECLClusters");
88 m_mcParticleArray.isRequired();
89 m_eventLevelClusteringInfo.isRequired();
90 m_eventLevelTriggerTimeInfo.isRequired();
91
92 //--------------------------------------------------------
93 //..Sort the crystals into groups of similar performance
94
95 //..Record the thetaID of each cellID
96 neighbours = new ECLNeighbours("N", 1);
97 std::vector<int> nCrysPerRing;
98 for (int thID = 0; thID < 69; thID++) {
99 const int nCrys = neighbours->getCrystalsPerRing(thID);
100 nCrysPerRing.push_back(nCrys);
101 for (int phID = 0; phID < nCrys; phID++) {
102 thetaIDofCrysID.push_back(thID);
103 }
104 }
105
106 //..First crystalID of each thetaID
107 std::vector<int> firstCrysIdPerRing;
108 firstCrysIdPerRing.push_back(0);
109 for (int thID = 1; thID < 69; thID++) {
110 const int iCrysID = firstCrysIdPerRing[thID - 1] + nCrysPerRing[thID - 1];
111 firstCrysIdPerRing.push_back(iCrysID);
112 }
113
114 //..Range of thetaID from first and last cellID
115 const int firstThetaId = thetaIDofCrysID[iFirstCellId - 1];
116 const int lastThetaId = thetaIDofCrysID[iLastCellId - 1];
117
118 //..Nominal groups per thetaID, but some thetaID have double that
119 const int specialThetaID[4] = {5, 11, 60, 65};
120 int iSp = 0;
121 int firstGroupOfThetaID = -m_nGroupPerThetaID;
122 for (int thID = firstThetaId; thID <= lastThetaId; thID++) {
123 firstGroupOfThetaID += m_nGroupPerThetaID;
124 const int nCrysPerGroup = nCrysPerRing[thID] / m_nGroupPerThetaID;
125 for (int phID = 0; phID < nCrysPerRing[thID]; phID++) {
126 const int iCrysID = firstCrysIdPerRing[thID] + phID;
127 const int iLocalGroup = phID / nCrysPerGroup;
128 iGroupOfCrystal[iCrysID] = firstGroupOfThetaID + iLocalGroup;
129 }
130
131 //..Special thetaID with double the number of groups
132 if (thID == specialThetaID[iSp]) {
133 iSp++;
134 firstGroupOfThetaID += m_nGroupPerThetaID;
135 for (int phID = 1; phID < nCrysPerRing[thID]; phID += 3) {
136 const int iCrysID = firstCrysIdPerRing[thID] + phID;
137 const int iLocalGroup = phID / nCrysPerGroup;
138 iGroupOfCrystal[iCrysID] = firstGroupOfThetaID + iLocalGroup;
139 }
140
141 }
142 }
143
144 //..Assign crystals outside of the useful range to the first or last group
145 for (int ic = 1; ic < iFirstCellId; ic++) {
146 const int iCrysID = ic - 1;
148 }
149
150 for (int ic = iLastCellId + 1; ic <= ECLElementNumbers::c_NCrystals; ic++) {
151 const int iCrysID = ic - 1;
153 }
154
155 //..Number of groups
157
158 //-----------------------------------------------------------------
159 //..Define histogram to store parameters
160 const int nBinX = 3 + nLeakReg * m_numberEnergies;
161 auto inputParameters = new TH1F("inputParameters", "eclNOptimalCollector job parameters", nBinX, 0, nBinX);
162 registerObject<TH1F>("inputParameters", inputParameters);
163
164 //..Store group number for each crystal
165 auto groupNumberOfEachCellID = new TH1F("groupNumberOfEachCellID", "group number of each cellID;cellID",
167 registerObject<TH1F>("groupNumberOfEachCellID", groupNumberOfEachCellID);
168
169
170 //-----------------------------------------------------------------
171 //..Histograms to check MC sample properties
172 auto entriesPerThetaIdEnergy = new TH2F("entriesPerThetaIdEnergy", "Entries per thetaID/energy point;thetaID;energy point", 69, 0,
174 registerObject<TH2F>("entriesPerThetaIdEnergy", entriesPerThetaIdEnergy);
175
176 auto mcEnergyDiff = new TH2F("mcEnergyDiff", "mc E minus nominal (MeV) per thetaID/energy point;thetaID;energy point", 69, 0, 69,
178 mcEnergyDiff->Sumw2();
179 registerObject<TH2F>("mcEnergyDiff", mcEnergyDiff);
180
181 //..Criteria to clean the sample
182 auto eMCOverEGenerated = new TH1D("eMCOverEGenerated", "MC energy of cluster divided by generated;ratio", 101, 0, 1.01);
183 registerObject<TH1D>("eMCOverEGenerated", eMCOverEGenerated);
184
185 auto clusterTime = new TH1D("clusterTime", "clusterTiming of matched clusters;clusterTiming (ns)", 2048, -1000, 1000);
186 registerObject<TH1D>("clusterTime", clusterTime);
187
188 auto angularDiff = new TH1D("angularDiff",
189 "angular difference between generated photon and cluster;angular difference (rad);photons per 0.01", 300, 0, 0.3);
190 registerObject<TH1D>("angularDiff", angularDiff);
191
192 auto nOutOfTimeCrystals = new TH1D("nOutOfTimeCrystals", "Out-of-time crystals in single particle MC;Out of time crystals", 2000, 0,
193 2000);
194 registerObject<TH1D>("nOutOfTimeCrystals", nOutOfTimeCrystals);
195
196 auto timeSinceInjection = new TH1D("timeSinceInjection",
197 "Time since injection in single particle MC;time (micro seconds);events per 10 us", 8000, 0, 80000);
198 registerObject<TH1D>("timeSinceInjection", timeSinceInjection);
199
200
201 //-----------------------------------------------------------------
202 //..Histograms to store the sum of n crystals. One per group / energy.
203 // Include an extra nCrystal bin at the end to store the current raw energy
204 const int nHist = nCrystalGroups * m_numberEnergies;
205 vector<TH2F*> eSum(nHist);
206 vector<TH2F*> biasSum(nHist);
207 int iHist = -1;
208 for (int ig = 0; ig < nCrystalGroups; ig++) {
209 for (int ie = 0; ie < m_numberEnergies; ie++) {
210 iHist++;
211 std::string name = "eSum_" + std::to_string(ig) + "_" + std::to_string(ie);
212 TString hname = name;
213 TString title = "energy summed over nCrys divided by eMC, group " + std::to_string(ig) + ", E point " + std::to_string(
214 ie) + ";nCrys;energy sum / Etrue";
215 eSum[iHist] = new TH2F(hname, title, nCrysMax + 1, 1., nCrysMax + 2., 2000, 0., 2.);
216 registerObject<TH2F>(name, eSum[iHist]);
217
218 name = "biasSum_" + std::to_string(ig) + "_" + std::to_string(ie);
219 hname = name;
220 title = "energy minus mc true summing over nCrys, group " + std::to_string(ig) + ", E point " + std::to_string(
221 ie) + ";nCrys;bias = energy minus mc truth (GeV)";
222 biasSum[iHist] = new TH2F(hname, title, nCrysMax + 1, 1., nCrysMax + 2., 1000, -0.1, 0.1);
223 registerObject<TH2F>(name, biasSum[iHist]);
224 }
225 }
226
227 B2INFO("Finished setup for eclNOptimalCollectorModule");
228
229}
230
234{
235
236 //-----------------------------------------------------------------
237 //..First time, store the job parameters
238 if (storeParameters) {
239 getObjectPtr<TH1F>("inputParameters")->Fill(0.01, nCrystalGroups);
240 getObjectPtr<TH1F>("inputParameters")->Fill(1.01, m_numberEnergies);
241 double firstBin = 2.01;
242 for (int ie = 0; ie < m_numberEnergies; ie++) {
243 getObjectPtr<TH1F>("inputParameters")->Fill(firstBin + ie, m_energiesForward[ie]);
244 }
245 firstBin += m_numberEnergies;
246 for (int ie = 0; ie < m_numberEnergies; ie++) {
247 getObjectPtr<TH1F>("inputParameters")->Fill(firstBin + ie, m_energiesBarrel[ie]);
248 }
249 firstBin += m_numberEnergies;
250 for (int ie = 0; ie < m_numberEnergies; ie++) {
251 getObjectPtr<TH1F>("inputParameters")->Fill(firstBin + ie, m_energiesBackward[ie]);
252 }
253
254 //..Keep track of how many times inputParameters was filled
255 int lastBin = getObjectPtr<TH1F>("inputParameters")->GetNbinsX();
256 getObjectPtr<TH1F>("inputParameters")->SetBinContent(lastBin, 1.);
257
258 //..Store the group number for every cellID
259 for (int ic = 1; ic < 8737; ic++) {
260 getObjectPtr<TH1F>("groupNumberOfEachCellID")->Fill(ic + 0.01, iGroupOfCrystal[ic - 1]);
261 }
262
263 //..Call each eSum hist once to ensure they exist (with non-real nCrys)
264 for (int ig = 0; ig < nCrystalGroups; ig++) {
265 for (int ie = 0; ie < m_numberEnergies; ie++) {
266 std::string histName = "eSum_" + std::to_string(ig) + "_" + std::to_string(ie);
267 getObjectPtr<TH2F>(histName)->Fill(0.01, 0.96);
268
269 std::string histNameBias = "biasSum_" + std::to_string(ig) + "_" + std::to_string(ie);
270 getObjectPtr<TH2F>(histNameBias)->Fill(0.01, 0.96);
271 }
272 }
273
274 storeParameters = false;
275 }
276
277 //--------------------------------------------------------
278 //..Find the ECLShower (should only be one when using trimmed data object)
279 int iMax = -1;
280 double showerMaxE = 0.; // shower energy before leakage correction
281 const int nShower = m_eclShowerArray.getEntries();
282 for (int i = 0; i < nShower; i++) {
283 if (m_eclShowerArray[i]->getHypothesisId() == ECLShower::c_nPhotons) {
284 const double nominalE = m_eclShowerArray[i]->getEnergyRaw();
285 if (nominalE > showerMaxE) {
286 showerMaxE = nominalE;
287 iMax = i;
288 }
289 }
290 }
291
292 //..Quit now if no shower
293 if (iMax == -1) {return;}
294
295 //--------------------------------------------------------
296 //..Get cellID from related cluster. Also store clusterTiming and angles,
297 // used later to clean the sample.
298 int iCellId = -1;
299 double timing = -1000.;
300 double thetaLab = 0.;
301 double phiLab = 0.;
302 double rLab = 999.;
303 const auto showerClusterRelations = m_eclShowerArray[iMax]->getRelationsWith<ECLCluster>();
304 const unsigned int nRelatedClusters = showerClusterRelations.size();
305 if (nRelatedClusters > 0) {
306 const auto cluster = showerClusterRelations.object(0);
307 iCellId = cluster->getMaxECellId();
308 timing = cluster->getTime();
309 thetaLab = cluster->getTheta();
310 phiLab = cluster->getPhi();
311 rLab = cluster->getR();
312 }
313
314 //..Quit if cellID is not in the range being calibrated
315 if (iCellId<iFirstCellId or iCellId>iLastCellId) {return;}
316
317 //..ECL region forward/barrel/backward
318 int iRegion = 1;
319 if (ECLElementNumbers::isForward(iCellId)) {
320 iRegion = 0;
321 } else if (ECLElementNumbers::isBackward(iCellId)) {
322 iRegion = 2;
323 }
324
325 //--------------------------------------------------------
326 //..Get the generated energy from the MCParticles block
327
328 //..Should only be one entry. Quit if this is not the case.
329 const int nMC = m_mcParticleArray.getEntries();
330 if (nMC != 1) {return;}
331
332 //..Energy. Convert to MeV to get corresponding bin
333 const double eTrue = m_mcParticleArray[0]->getEnergy();
334 const float genEnergyMeV = 1000.*eTrue;
335 const float tolerance = std::max(0.002 * genEnergyMeV, 1.0);
336
337 //..Look for generated energy to be close enough to the expected one
338 int iEnergy = -1;
339 double minDiff = 9999.;
340 for (int ie = 0; ie < m_numberEnergies; ie++) {
341 double diff = std::abs(genEnergyMeV - iEnergies[iRegion][ie]);
342 if (diff < std::abs(minDiff)) {
343 minDiff = genEnergyMeV - iEnergies[iRegion][ie];
344 }
345 if (diff < tolerance) {
346 iEnergy = ie;
347 break;
348 }
349 }
350
351 //..Quit if the true energy is not equal to a generated one.
352 // This happens if the cluster is reconstructed in the wrong region.
353 if (iEnergy == -1) {return;}
354
355 //..Some statistics for validation
356 getObjectPtr<TH2F>("entriesPerThetaIdEnergy")->Fill(thetaIDofCrysID[iCellId - 1] + 0.001, iEnergy + 0.001);
357 getObjectPtr<TH2F>("mcEnergyDiff")->Fill(thetaIDofCrysID[iCellId - 1] + 0.001, iEnergy + 0.001, minDiff);
358
359
360 //--------------------------------------------------------
361 //..Get the ECLCalDigits and weights associated with the cluster,
362 // plus MC true energy
363 std::vector<double> digitEnergies;
364 std::vector < std::pair<double, double> > energies;
365
366 const auto showerDigitRelations = m_eclShowerArray[iMax]->getRelationsWith<ECLCalDigit>(m_digitArrayName);
367 unsigned int nRelatedDigits = showerDigitRelations.size();
368 double eMCTotal = 0.;
369 for (unsigned int ir = 0; ir < nRelatedDigits; ir++) {
370 const auto calDigit = showerDigitRelations.object(ir);
371 const auto weight = showerDigitRelations.weight(ir);
372 digitEnergies.push_back(calDigit->getEnergy() * weight);
373 const double eCalDigit = weight * calDigit->getEnergy();
374
375 //..MC energy via relation to ECLCalDigit
376 auto digitMCRelations = calDigit->getRelationsTo<MCParticle>();
377 double eMC = 0.;
378 for (unsigned int i = 0; i < digitMCRelations.size(); i++) {
379 eMC += digitMCRelations.weight(i);
380 }
381 std::pair<double, double> pTemp = std::make_pair(eCalDigit, eMC);
382 energies.push_back(pTemp);
383 eMCTotal += eMC;
384 }
385
386 //..Sort digit and mc energies from lowest to highest
387 std::sort(energies.begin(), energies.end());
388
389
390 //--------------------------------------------------------
391 //..Clean up the sample
392
393 //..True energy content of cluster must be >15% of generated
394 getObjectPtr<TH1D>("eMCOverEGenerated")->Fill(eMCTotal / eTrue);
395 const double minTrueFrac = 0.15;
396 if (eMCTotal < minTrueFrac * eTrue) {return;}
397
398 //..Standard timing cut
399 getObjectPtr<TH1D>("clusterTime")->Fill(timing);
400 const double maxClusterTiming = 200.;
401 if (abs(timing) > maxClusterTiming) {return;}
402
403 //..Direction (3 momentum) of the generated photon
404 ROOT::Math::XYZVector momentumGen = m_mcParticleArray[0]->getMomentum();
405
406 //..Make the cluster vector from the vertex and the postition in the ECL
407 ROOT::Math::XYZVector vertex = m_mcParticleArray[0]->getProductionVertex();
408 ROOT::Math::Polar3DVector position(rLab, thetaLab, phiLab);
409 ROOT::Math::XYZVector direction = ROOT::Math::XYZVector(position) - vertex;
410
411 double angle = ROOT::Math::VectorUtil::Angle(direction, momentumGen);
412 getObjectPtr<TH1D>("angularDiff")->Fill(angle);
413 const double maxAngularDiff = 0.09;
414 if (angle > maxAngularDiff) {return;}
415
416 //..Out of time crystals.
417 double OutOfTime = m_eventLevelClusteringInfo->getNECLCalDigitsOutOfTime();
418 getObjectPtr<TH1D>("nOutOfTimeCrystals")->Fill(OutOfTime);
419 const double maxOutOfTime = 900.;
420 if (OutOfTime > maxOutOfTime) {return;}
421
422 //..Time since injection to monitor background reuse
423 double timeSinceInjectionMicroS = m_eventLevelTriggerTimeInfo->getTimeSinceLastInjectionInMicroSeconds();
424 getObjectPtr<TH1D>("timeSinceInjection")->Fill(timeSinceInjectionMicroS);
425
426
427 //--------------------------------------------------------
428 //..Store the energy sum of 1, 2, ..., n crystals (max 21), and corresponding bias
429 std::string histName = "eSum_" + std::to_string(iGroupOfCrystal[iCellId - 1]) + "_" + std::to_string(iEnergy);
430 std::string histNameBias = "biasSum_" + std::to_string(iGroupOfCrystal[iCellId - 1]) + "_" + std::to_string(iEnergy);
431
432 double eSumOfN = 0.;
433 double biasSumOfN = 0.;
434 for (int isum = 0; isum < nCrysMax; isum++) {
435 int i = (int)nRelatedDigits - 1 - isum;
436 if (i >= 0) {
437 eSumOfN += energies[i].first;
438 biasSumOfN += (energies[i].first - energies[i].second);
439 }
440 getObjectPtr<TH2F>(histName)->Fill(isum + 1.01, eSumOfN / eTrue);
441 getObjectPtr<TH2F>(histNameBias)->Fill(isum + 1.01, biasSumOfN);
442 }
443
444 //..Also store the current raw energy for monitoring purposes
445 getObjectPtr<TH2F>(histName)->Fill(nCrysMax + 1.01, showerMaxE / eTrue);
446
447}
void registerObject(const std::string &name, T *obj)
Register object with a name, takes ownership, do not access the pointer beyond prepare()
T * getObjectPtr(const std::string &name)
Calls the CalibObjManager to get the requested stored collector data.
CalibrationCollectorModule()
Constructor. Sets the default prefix for calibration dataobjects.
Class to store calibrated ECLDigits: ECLCalDigits.
Definition ECLCalDigit.h:23
ECL cluster data.
Definition ECLCluster.h:27
@ c_nPhotons
CR is split into n photons (N1)
Definition ECLShower.h:42
Class to get the neighbours for a given cell id.
A Class to store the Monte Carlo particle information.
Definition MCParticle.h:32
void setDescription(const std::string &description)
Sets the description of the module.
Definition Module.cc:214
void setPropertyFlags(unsigned int propertyFlags)
Sets the flags for the module properties.
Definition Module.cc:208
@ c_ParallelProcessingCertified
This module can be run in parallel processing mode safely (All I/O must be done through the data stor...
Definition Module.h:80
StoreArray< MCParticle > m_mcParticleArray
Array of MCParticles.
StoreArray< ECLShower > m_eclShowerArray
Required arrays.
const int iFirstCellId
first useful cellID (first of thetaID 3)
int m_nGroupPerThetaID
number of groups per standard thetaID
std::vector< std::vector< int > > iEnergies
Some other useful quantities.
std::vector< double > m_energiesBarrel
generated photon energies, barrel
const int iLastCellId
first useful cellID (last of thetaID 66)
const int nCrysMax
max number of crystals used to calculate energy
int nCrystalGroups
sort the crystals into this many groups
const int nLeakReg
0 = forward, 1 = barrel, 2 = backward
StoreArray< ECLCluster > m_eclClusterArray
Array of ECLClusters.
int iGroupOfCrystal[ECLElementNumbers::c_NCrystals]
group number of each crystal
ECL::ECLNeighbours * neighbours
neighbours to crystal
std::vector< int > thetaIDofCrysID
thetaID of each crystal
void collect() override
Select events and crystals and accumulate histograms.
eclNOptimalCollectorModule()
Constructor: Sets the description, the properties and the parameters of the module.
std::string m_showerArrayName
Name of ECLShower StoreArray.
std::string m_digitArrayName
Name of ECLCalDigit StoreArray.
StoreArray< ECLCalDigit > m_eclCalDigitArray
Array of ECLCalDigits.
StoreObjPtr< EventLevelTriggerTimeInfo > m_eventLevelTriggerTimeInfo
EventLevelTriggerTimeInfo.
void prepare() override
Define histograms.
std::vector< double > m_energiesForward
generated photon energies, forward
bool storeParameters
store parameters first event
std::vector< double > m_energiesBackward
generated photon energies, backward
StoreObjPtr< EventLevelClusteringInfo > m_eventLevelClusteringInfo
EventLevelClusteringInfo.
void addParam(const std::string &name, T &paramVariable, const std::string &description, const T &defaultValue)
Adds a new parameter to the module.
Definition Module.h:559
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition Module.h:649
bool isForward(int cellId)
Check whether the crystal is in forward ECL.
const int c_NCrystals
Number of crystals.
bool isBackward(int cellId)
Check whether the crystal is in backward ECL.
Abstract base class for different kinds of events.
STL namespace.