Belle II Software  release-06-00-14
ECLFinalizerModule.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 // THIS MODULE
10 #include <ecl/modules/eclFinalize/ECLFinalizerModule.h>
11 
12 // FRAMEWORK
13 #include <framework/gearbox/Unit.h>
14 #include <framework/logging/Logger.h>
15 #include <framework/dataobjects/EventT0.h>
16 
17 //ECL
18 #include <ecl/dataobjects/ECLShower.h>
19 #include <ecl/dataobjects/ECLCalDigit.h>
20 #include <ecl/utility/utilityFunctions.h>
21 
22 //MDST
23 #include <mdst/dataobjects/ECLCluster.h>
24 #include <mdst/dataobjects/EventLevelClusteringInfo.h>
25 
26 // NAMESPACES
27 using namespace Belle2;
28 using namespace ECL;
29 
30 //-----------------------------------------------------------------
31 // Register the Module
32 //-----------------------------------------------------------------
33 REG_MODULE(ECLFinalizer)
34 REG_MODULE(ECLFinalizerPureCsI)
35 
36 //-----------------------------------------------------------------
37 // Implementation
38 //-----------------------------------------------------------------
40 {
41  // Set description
42  setDescription("ECLFinalizerModule: Converts ecl shower dataobjects to mdst eclcluster dataobjects");
43 
44  addParam("clusterEnergyCutMin", m_clusterEnergyCutMin, "Min value for the cluster energy cut.",
45  20.0 * Belle2::Unit::MeV);
46  addParam("clusterTimeCutMaxEnergy", m_clusterTimeCutMaxEnergy, "All clusters above this energy are kept.",
47  50.0 * Belle2::Unit::MeV);
48  addParam("clusterLossyFraction", m_clusterLossyFraction,
49  "Keep neutral hadron hypothesis clusters only if the number of crystals is different from that of the n photons hypothesis cluster by this fraction: abs(n(neutral hadron)-n(photon))/n(photon)",
50  1e-4);
51 
52  setPropertyFlags(c_ParallelProcessingCertified);
53 }
54 
56 {
57 }
58 
60 {
61  // Register in datastore.
62  m_eclShowers.isRequired(eclShowerArrayName());
63  m_eclClusters.registerInDataStore(eclClusterArrayName());
64  m_eclCalDigits.isRequired(eclCalDigitArrayName());
65  m_eventLevelClusteringInfo.registerInDataStore();
66  m_eventT0.isRequired();
67 
68  // Register relations.
69  m_eclClusters.registerRelationTo(m_eclShowers);
70  m_eclClusters.registerRelationTo(m_eclCalDigits);
71 
72 }
73 
75 {
76  // Do not use this blindly for database updates, they will probably not follow the concept of a "run"
77  ;
78 }
79 
81 {
82  //EventLevelClusteringInfo counters
83  uint rejectedShowersFwd = 0;
84  uint rejectedShowersBrl = 0;
85  uint rejectedShowersBwd = 0;
86 
87  // event T0
88  double eventT0 = 0.;
89  if (m_eventT0->hasEventT0()) {
90  eventT0 = m_eventT0->getEventT0();
91  }
92 
93  // map connected regions to different cluster hypothesis: key (CRId, HypothesisID) -> list arrayindex of shower
94  std::map<std::pair<int, int>, std::vector<int>> hypoShowerMap;
95  for (const auto& eclShower : m_eclShowers) {
96  // if they pass cuts, put them in this map
97  // get shower time, energy and highest energy for cuts
98  const double showerTime = eclShower.getTime() - eventT0;
99  const double showerdt99 = eclShower.getDeltaTime99();
100  const double showerEnergy = eclShower.getEnergy();
101 
102  if (showerEnergy > m_clusterEnergyCutMin and ((fabs(showerTime) < showerdt99) or (showerEnergy > m_clusterTimeCutMaxEnergy))) {
103  hypoShowerMap[std::make_pair(eclShower.getConnectedRegionId(), eclShower.getHypothesisId())].push_back(eclShower.getArrayIndex());
104  } else {
105  if (eclShower.getHypothesisId() == Belle2::ECLShower::c_nPhotons) {
106 
107  // Get detector region
108  const auto detectorRegion = eclShower.getDetectorRegion();
109 
110  B2DEBUG(39, "ECLFinalizerModule::event: Rejected shower with energy " << showerEnergy << ", time = " << showerTime << ", theta = "
111  << eclShower.getTheta()
112  << ", region " << detectorRegion);
113  // Increment counters
114  if (detectorRegion == static_cast<int>(ECL::DetectorRegion::FWD)) {
115  ++rejectedShowersFwd;
116  } else if (detectorRegion == ECL::DetectorRegion::BRL) {
117  ++rejectedShowersBrl;
118  } else if (detectorRegion == ECL::DetectorRegion::BWD) {
119  ++rejectedShowersBwd;
120  }
121  }
122  }
123  }
124 
125  // map connected regions to different cluster hypothesis: key (CRId, HypothesisID) -> list arrayindex of cluster
126  std::map<std::pair<int, int>, std::vector<int>> hypoClusterMap;
127 
128  // now loop over all photon showers from the map and make clusters for those and put them in the map
129  for (const auto & [keypair, indexlist] : hypoShowerMap) {
130  if (keypair.second == Belle2::ECLShower::c_nPhotons) {
131  for (const auto& index : indexlist) {
132  hypoClusterMap[keypair].push_back(makeCluster(index, eventT0));
133  }
134  }
135  }
136 
137  // now loop over all other showers
138  for (const auto & [keypair, indexlist] : hypoShowerMap) {
139  if (keypair.second != Belle2::ECLShower::c_nPhotons) {
140  for (const auto& index : indexlist) {
141  // no photon entry exists (maybe we did not run the splitter or selection cuts failed)
142  if (hypoShowerMap.count(std::make_pair(keypair.first, Belle2::ECLShower::c_nPhotons)) < 1) {
143  hypoClusterMap[keypair].push_back(makeCluster(index, eventT0));
144  }
145  // there is more than one photon cluster, i.e. CR is split into multiple clusters
146  else if (hypoShowerMap[std::make_pair(keypair.first, Belle2::ECLShower::c_nPhotons)].size() > 1) {
147  hypoClusterMap[keypair].push_back(makeCluster(index, eventT0));
148  } else {
149  // get the already existing nPhotons cluster index
150  const int indexOfCorrespondingNPhotonsCluster = hypoClusterMap[std::make_pair(keypair.first, Belle2::ECLShower::c_nPhotons)][0];
151  double lossfraction = fabs(m_eclClusters[indexOfCorrespondingNPhotonsCluster]->getNumberOfCrystals() -
152  m_eclShowers[index]->getNumberOfCrystals()) / m_eclClusters[indexOfCorrespondingNPhotonsCluster]->getNumberOfCrystals();
153 
154  B2DEBUG(35, "ECLFinalizerModule::event n(photon shower) = " <<
155  m_eclClusters[indexOfCorrespondingNPhotonsCluster]->getNumberOfCrystals() << ", n(hadron shower): " <<
156  m_eclShowers[index]->getNumberOfCrystals());
157  B2DEBUG(35, "ECLFinalizerModule::event lossfraction = " << lossfraction);
158 
159  if (lossfraction > m_clusterLossyFraction) {
160  hypoClusterMap[keypair].push_back(makeCluster(index, eventT0));
161  } else {
162  if (keypair.second == ECLShower::c_neutralHadron) m_eclClusters[indexOfCorrespondingNPhotonsCluster]->addHypothesis(
164  else {
165  B2ERROR("This ECLShower hypothesis is not supported yet: " << keypair.second);
166  }
167  }
168  }
169  }
170  }
171  }
172 
173  // Save EventLevelClusteringInfo
174  if (!m_eventLevelClusteringInfo) {
175  m_eventLevelClusteringInfo.create();
176  }
177  m_eventLevelClusteringInfo->setNECLShowersRejectedFWD(rejectedShowersFwd);
178  m_eventLevelClusteringInfo->setNECLShowersRejectedBarrel(rejectedShowersBrl);
179  m_eventLevelClusteringInfo->setNECLShowersRejectedBWD(rejectedShowersBwd);
180 
181  B2DEBUG(35, "ECLFinalizerModule::event found " << rejectedShowersFwd << ", " << rejectedShowersBrl << ", " << rejectedShowersBwd
182  << " rejected showers in FWD, BRL, BWD");
183 }
184 
186 
188 
189 
190 int ECLFinalizerModule::makeCluster(int index, double evtt0)
191 {
192 
193  const auto eclShower = m_eclShowers[index];
194 
195  // create an mdst cluster for each ecl shower
196  const auto eclCluster = m_eclClusters.appendNew();
197 
198  // status between showers and clusters may be different:
199  if (eclShower->hasPulseShapeDiscrimination()) {
201  }
202 
203  eclCluster->setConnectedRegionId(eclShower->getConnectedRegionId());
204 
205  // ECLShowers have *one* hypothesisID...
206  const int hyp = eclShower->getHypothesisId();
207 
208  // ECLClusters can have *multiple*, but not at the creation of an ECLCluster: use "set" and not "add"
209  if (hyp == ECLShower::c_nPhotons) {
210  eclCluster->setHypothesis(ECLCluster::EHypothesisBit::c_nPhotons);
211  } else if (hyp == ECLShower::c_neutralHadron) {
212  eclCluster->setHypothesis(ECLCluster::EHypothesisBit::c_neutralHadron);
213  } else {
214  B2ERROR("This ECLShower hypothesis is not supported yet: " << hyp);
215  }
216 
217  eclCluster->setClusterId(eclShower->getShowerId());
218 
219  eclCluster->setEnergy(eclShower->getEnergy());
220  eclCluster->setEnergyRaw(eclShower->getEnergyRaw());
221  eclCluster->setEnergyHighestCrystal(eclShower->getEnergyHighestCrystal());
222  eclCluster->setMaxECellId(static_cast<unsigned short>(eclShower->getCentralCellId()));
223 
224  double covmat[6] = {
225  eclShower->getUncertaintyEnergy()* eclShower->getUncertaintyEnergy(),
226  0.0,
227  eclShower->getUncertaintyPhi()* eclShower->getUncertaintyPhi(),
228  0.0,
229  0.0,
230  eclShower->getUncertaintyTheta()* eclShower->getUncertaintyTheta()
231  };
232  eclCluster->setCovarianceMatrix(covmat);
233 
234  eclCluster->setAbsZernike40(eclShower->getAbsZernike40());
235  eclCluster->setAbsZernike51(eclShower->getAbsZernike51());
236  eclCluster->setZernikeMVA(eclShower->getZernikeMVA());
237  eclCluster->setE1oE9(eclShower->getE1oE9());
238  eclCluster->setE9oE21(eclShower->getE9oE21());
239  eclCluster->setSecondMoment(eclShower->getSecondMoment());
240  eclCluster->setLAT(eclShower->getLateralEnergy());
241  eclCluster->setNumberOfCrystals(eclShower->getNumberOfCrystals());
242  eclCluster->setTime(eclShower->getTime() - evtt0); // If bad timing fit, this value is changed later in the code
243  eclCluster->setDeltaTime99(eclShower->getDeltaTime99());
244  eclCluster->setTheta(eclShower->getTheta());
245  eclCluster->setPhi(eclShower->getPhi());
246  eclCluster->setR(eclShower->getR());
247  eclCluster->setPulseShapeDiscriminationMVA(eclShower->getPulseShapeDiscriminationMVA());
248  eclCluster->setNumberOfHadronDigits(eclShower->getNumberOfHadronDigits());
249 
250  // set relation to ECLShower
251  eclCluster->addRelationTo(eclShower);
252 
253  // set relation to ECLCalDigits and set failed timing flags
254  auto cellIDOfMaxEnergyCrystal = eclShower->getCentralCellId() ;
255  auto showerDigitRelations = eclShower->getRelationsTo<ECLCalDigit>(eclCalDigitArrayName());
256  for (unsigned int iRel = 0; iRel < showerDigitRelations.size(); ++iRel) {
257  const auto calDigit = showerDigitRelations.object(iRel);
258  const auto weight = showerDigitRelations.weight(iRel);
259 
260  eclCluster->addRelationTo(calDigit, weight);
261 
262 
263  // Set the failed timing flag for the crystal that defines the cluster time
264  if (calDigit->getCellId() == cellIDOfMaxEnergyCrystal) {
265  if (calDigit->isFailedFit()) {
266  eclCluster->addStatus(ECLCluster::EStatusBit::c_fitTimeFailed);
267  eclCluster->setTime(eclShower->getTime());
268  }
269  if (calDigit->isTimeResolutionFailed()) {
270  eclCluster->addStatus(ECLCluster::EStatusBit::c_timeResolutionFailed);
271  }
272  }
273  }
274 
275  return eclCluster->getArrayIndex();
276 
277 }
Class to store calibrated ECLDigits: ECLCalDigits.
Definition: ECLCalDigit.h:23
@ c_PulseShapeDiscrimination
bit 2: ECLCluster has pulse shape discrimination variables.
@ c_timeResolutionFailed
bit 4: ECLCluster has time resolution calculation that failed.
@ c_fitTimeFailed
bit 3: ECLCluster has fit time that failed.
@ c_nPhotons
CR is split into n photons (N1)
@ c_neutralHadron
CR is reconstructed as a neutral hadron (N2)
Class to perform the shower correction.
virtual void initialize() override
Initialize.
virtual void event() override
Event.
virtual void endRun() override
End run.
virtual void terminate() override
Terminate.
virtual void beginRun() override
Begin run.
int makeCluster(int, double)
Make a cluster from a given shower array index.
@ c_neutralHadron
CR is reconstructed as a neutral hadron (N2)
Definition: ECLShower.h:39
@ c_nPhotons
CR is split into n photons (N1)
Definition: ECLShower.h:37
Base class for Modules.
Definition: Module.h:72
static const double MeV
[megaelectronvolt]
Definition: Unit.h:114
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:650
Abstract base class for different kinds of events.