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