Belle II Software development
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
28using namespace Belle2;
29using namespace ECL;
30
31//-----------------------------------------------------------------
32// Register the Module
33//-----------------------------------------------------------------
34REG_MODULE(ECLFinalizer);
35REG_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 m_eclShowers.requireRelationTo(m_eclCalDigits);
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
200int 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()) {
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)
virtual const char * eclShowerArrayName() const
We need names for the data objects to differentiate between PureCsI and default.
virtual const char * eclClusterArrayName() const
Default name ECLCluster.
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 void endRun() override
End run.
virtual void terminate() override
Terminate.
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.
virtual const char * eclCalDigitArrayName() const
Default name ECLCalDigits.
int makeCluster(int, double)
Make a cluster from a given shower array index.
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
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
#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.