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("useParametersFromDatabase", m_useParametersFromDatabase, "get clusterEnergyCutMin from payload", true);
48 addParam("clusterTimeCutMaxEnergy", m_clusterTimeCutMaxEnergy, "All clusters above this energy are kept.",
49 50.0 * Belle2::Unit::MeV);
50 addParam("clusterLossyFraction", m_clusterLossyFraction,
51 "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)",
52 1e-4);
53
55}
56
60
62{
63 // Register in datastore.
64 m_eclShowers.isRequired(eclShowerArrayName());
65 m_eclClusters.registerInDataStore(eclClusterArrayName());
67 m_eventLevelClusteringInfo.isRequired();
68 m_eventT0.isRequired();
69
70 // Register relations.
71 m_eclClusters.registerRelationTo(m_eclShowers);
72 m_eclClusters.registerRelationTo(m_eclCalDigits);
73 m_eclShowers.requireRelationTo(m_eclCalDigits);
74}
75
77{
78 //..Get m_clusterEnergyCutMin from the payload by default
80 m_clusterEnergyCutMin = m_eclClusteringParameters->getFClusterEnergyCutMin();
81 }
82}
83
85{
86 //EventLevelClusteringInfo counters
87 uint rejectedShowersFwd = 0;
88 uint rejectedShowersBrl = 0;
89 uint rejectedShowersBwd = 0;
90
91 // event T0
92 double eventT0 = 0.;
93 if (m_eventT0->hasEventT0()) {
94 eventT0 = m_eventT0->getEventT0();
95 }
96
97 // map connected regions to different cluster hypothesis: key (CRId, HypothesisID) -> list arrayindex of shower
98 std::map<std::pair<int, int>, std::vector<int>> hypoShowerMap;
99 for (const auto& eclShower : m_eclShowers) {
100 // if they pass cuts, put them in this map
101 // get shower time, energy and highest energy for cuts
102 const double showerTime = eclShower.getTime() - eventT0;
103 const double showerdt99 = eclShower.getDeltaTime99();
104 const double showerEnergy = eclShower.getEnergy();
105
106 if (showerEnergy > m_clusterEnergyCutMin and ((fabs(showerTime) < showerdt99) or (showerEnergy > m_clusterTimeCutMaxEnergy))) {
107 hypoShowerMap[std::make_pair(eclShower.getConnectedRegionId(), eclShower.getHypothesisId())].push_back(eclShower.getArrayIndex());
108 } else {
109 if (eclShower.getHypothesisId() == Belle2::ECLShower::c_nPhotons) {
110
111 // Get detector region
112 const auto detectorRegion = eclShower.getDetectorRegion();
113
114 B2DEBUG(39, "ECLFinalizerModule::event: Rejected shower with energy " << showerEnergy << ", time = " << showerTime << ", theta = "
115 << eclShower.getTheta()
116 << ", region " << detectorRegion);
117 // Increment counters
118 if (detectorRegion == static_cast<int>(ECL::DetectorRegion::FWD)) {
119 ++rejectedShowersFwd;
120 } else if (detectorRegion == ECL::DetectorRegion::BRL) {
121 ++rejectedShowersBrl;
122 } else if (detectorRegion == ECL::DetectorRegion::BWD) {
123 ++rejectedShowersBwd;
124 }
125 }
126 }
127 }
128
129 // map connected regions to different cluster hypothesis: key (CRId, HypothesisID) -> list arrayindex of cluster
130 std::map<std::pair<int, int>, std::vector<int>> hypoClusterMap;
131
132 // now loop over all photon showers from the map and make clusters for those and put them in the map
133 for (const auto & [keypair, indexlist] : hypoShowerMap) {
134 if (keypair.second == Belle2::ECLShower::c_nPhotons) {
135 for (const auto& index : indexlist) {
136 hypoClusterMap[keypair].push_back(makeCluster(index, eventT0));
137 }
138 }
139 }
140
141 // now loop over all other showers
142 for (const auto & [keypair, indexlist] : hypoShowerMap) {
143 if (keypair.second != Belle2::ECLShower::c_nPhotons) {
144 for (const auto& index : indexlist) {
145 // no photon entry exists (maybe we did not run the splitter or selection cuts failed)
146 if (hypoShowerMap.count(std::make_pair(keypair.first, Belle2::ECLShower::c_nPhotons)) < 1) {
147 hypoClusterMap[keypair].push_back(makeCluster(index, eventT0));
148 }
149 // there is more than one photon cluster, i.e. CR is split into multiple clusters
150 else if (hypoShowerMap[std::make_pair(keypair.first, Belle2::ECLShower::c_nPhotons)].size() > 1) {
151 hypoClusterMap[keypair].push_back(makeCluster(index, eventT0));
152 } else {
153 // get the already existing nPhotons cluster index
154 const int indexOfCorrespondingNPhotonsCluster = hypoClusterMap[std::make_pair(keypair.first, Belle2::ECLShower::c_nPhotons)][0];
155 double lossfraction = fabs(m_eclClusters[indexOfCorrespondingNPhotonsCluster]->getNumberOfCrystals() -
156 m_eclShowers[index]->getNumberOfCrystals()) / m_eclClusters[indexOfCorrespondingNPhotonsCluster]->getNumberOfCrystals();
157
158 B2DEBUG(35, "ECLFinalizerModule::event n(photon shower) = " <<
159 m_eclClusters[indexOfCorrespondingNPhotonsCluster]->getNumberOfCrystals() << ", n(hadron shower): " <<
160 m_eclShowers[index]->getNumberOfCrystals());
161 B2DEBUG(35, "ECLFinalizerModule::event lossfraction = " << lossfraction);
162
163 if (lossfraction > m_clusterLossyFraction) {
164 hypoClusterMap[keypair].push_back(makeCluster(index, eventT0));
165 } else {
166 if (keypair.second == ECLShower::c_neutralHadron) m_eclClusters[indexOfCorrespondingNPhotonsCluster]->addHypothesis(
168 else {
169 B2ERROR("This ECLShower hypothesis is not supported yet: " << keypair.second);
170 }
171 }
172 }
173 }
174 }
175 }
176
177 // Save EventLevelClusteringInfo
180 }
181
182 if (rejectedShowersFwd > std::numeric_limits<int>::max())
183 rejectedShowersFwd = std::numeric_limits<int>::max();
184 m_eventLevelClusteringInfo->setNECLShowersRejectedFWD(rejectedShowersFwd);
185
186 if (rejectedShowersBrl > static_cast<int>(std::numeric_limits<uint8_t>::max()))
187 rejectedShowersBrl = static_cast<int>(std::numeric_limits<uint8_t>::max());
188 m_eventLevelClusteringInfo->setNECLShowersRejectedBarrel(rejectedShowersBrl);
189
190 if (rejectedShowersBwd > std::numeric_limits<int>::max())
191 rejectedShowersBwd = std::numeric_limits<int>::max();
192 m_eventLevelClusteringInfo->setNECLShowersRejectedBWD(rejectedShowersBwd);
193
194 B2DEBUG(35, "ECLFinalizerModule::event found " << rejectedShowersFwd << ", " << rejectedShowersBrl << ", " << rejectedShowersBwd
195 << " rejected showers in FWD, BRL, BWD");
196}
197
199
201
202
203int ECLFinalizerModule::makeCluster(int index, double evtt0)
204{
205
206 const auto eclShower = m_eclShowers[index];
207
208 // create an mdst cluster for each ecl shower
209 const auto eclCluster = m_eclClusters.appendNew();
210
211 // status between showers and clusters may be different:
212 if (eclShower->hasPulseShapeDiscrimination()) {
214 }
215
216 eclCluster->setConnectedRegionId(eclShower->getConnectedRegionId());
217
218 // ECLShowers have *one* hypothesisID...
219 const int hyp = eclShower->getHypothesisId();
220
221 // ECLClusters can have *multiple*, but not at the creation of an ECLCluster: use "set" and not "add"
222 if (hyp == ECLShower::c_nPhotons) {
223 eclCluster->setHypothesis(ECLCluster::EHypothesisBit::c_nPhotons);
224 } else if (hyp == ECLShower::c_neutralHadron) {
225 eclCluster->setHypothesis(ECLCluster::EHypothesisBit::c_neutralHadron);
226 } else {
227 B2ERROR("This ECLShower hypothesis is not supported yet: " << hyp);
228 }
229
230 eclCluster->setClusterId(eclShower->getShowerId());
231
232 eclCluster->setEnergy(eclShower->getEnergy());
233 eclCluster->setEnergyRaw(eclShower->getEnergyRaw());
234 eclCluster->setEnergyHighestCrystal(eclShower->getEnergyHighestCrystal());
235 eclCluster->setMaxECellId(static_cast<unsigned short>(eclShower->getCentralCellId()));
236
237 double covmat[6] = {
238 eclShower->getUncertaintyEnergy()* eclShower->getUncertaintyEnergy(),
239 0.0,
240 eclShower->getUncertaintyPhi()* eclShower->getUncertaintyPhi(),
241 0.0,
242 0.0,
243 eclShower->getUncertaintyTheta()* eclShower->getUncertaintyTheta()
244 };
245 eclCluster->setCovarianceMatrix(covmat);
246
247 eclCluster->setAbsZernike40(eclShower->getAbsZernikeMoment(4, 0));
248 eclCluster->setAbsZernike51(eclShower->getAbsZernikeMoment(5, 1));
249 eclCluster->setZernikeMVA(eclShower->getZernikeMVA());
250 eclCluster->setE1oE9(eclShower->getE1oE9());
251 eclCluster->setE9oE21(eclShower->getE9oE21());
252 eclCluster->setSecondMoment(eclShower->getSecondMoment());
253 eclCluster->setLAT(eclShower->getLateralEnergy());
254 eclCluster->setNumberOfCrystals(eclShower->getNumberOfCrystals());
255 eclCluster->setTime(eclShower->getTime() - evtt0); // If bad timing fit, this value is changed later in the code
256 eclCluster->setDeltaTime99(eclShower->getDeltaTime99());
257 eclCluster->setTheta(eclShower->getTheta());
258 eclCluster->setPhi(eclShower->getPhi());
259 eclCluster->setR(eclShower->getR());
260 eclCluster->setPulseShapeDiscriminationMVA(eclShower->getPulseShapeDiscriminationMVA());
261 eclCluster->setNumberOfHadronDigits(eclShower->getNumberOfHadronDigits());
262
263 // set relation to ECLShower
264 eclCluster->addRelationTo(eclShower);
265
266 // set relation to ECLCalDigits and set failed timing flags
267 auto cellIDOfMaxEnergyCrystal = eclShower->getCentralCellId() ;
268 auto showerDigitRelations = eclShower->getRelationsTo<ECLCalDigit>(eclCalDigitArrayName());
269 for (unsigned int iRel = 0; iRel < showerDigitRelations.size(); ++iRel) {
270 const auto calDigit = showerDigitRelations.object(iRel);
271 const auto weight = showerDigitRelations.weight(iRel);
272
273 eclCluster->addRelationTo(calDigit, weight);
274
275
276 // Set the failed timing flag for the crystal that defines the cluster time
277 if (calDigit->getCellId() == cellIDOfMaxEnergyCrystal) {
278 if (calDigit->isFailedFit()) {
279 eclCluster->addStatus(ECLCluster::EStatusBit::c_fitTimeFailed);
280 eclCluster->setTime(eclShower->getTime());
281 }
282 if (calDigit->isTimeResolutionFailed()) {
284 }
285 }
286 }
287
288 return eclCluster->getArrayIndex();
289
290}
Class to store calibrated ECLDigits: ECLCalDigits.
Definition ECLCalDigit.h:23
@ c_PulseShapeDiscrimination
bit 2: ECLCluster has pulse shape discrimination variables.
Definition ECLCluster.h:55
@ c_timeResolutionFailed
bit 4: ECLCluster has time resolution calculation that failed.
Definition ECLCluster.h:59
@ c_fitTimeFailed
bit 3: ECLCluster has fit time that failed.
Definition ECLCluster.h:57
@ c_nPhotons
CR is split into n photons (N1)
Definition ECLCluster.h:41
@ c_neutralHadron
CR is reconstructed as a neutral hadron (N2)
Definition ECLCluster.h:43
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.
DBObjPtr< ECLClusteringParameters > m_eclClusteringParameters
ECLClusteringParameters payload: includes value for clusterEnergyCutMin.
bool m_useParametersFromDatabase
get clusterEnergyCutMin from payload
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
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
Module()
Constructor.
Definition Module.cc:30
@ 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
int getArrayIndex() const
Returns this object's array index (in StoreArray), or -1 if not found.
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:559
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition Module.h:649
Abstract base class for different kinds of events.