Project
Loading...
Searching...
No Matches
Clusterer.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
14#include <memory>
15
16#include "CPVReconstruction/Clusterer.h" // for LOG
17#include "CPVBase/Geometry.h"
21#include "CCDB/CcdbApi.h"
24#include <bitset>
25#include <fairlogger/Logger.h> // for LOG
26
27using namespace o2::cpv;
28
30
31//____________________________________________________________________________
37
38//____________________________________________________________________________
39void Clusterer::process(gsl::span<const Digit> digits, gsl::span<const TriggerRecord> dtr,
41 std::vector<Cluster>* clusters, std::vector<TriggerRecord>* trigRec,
43 std::vector<Digit>* calibDigits)
44{
45 clusters->clear(); // final out list of clusters
46 trigRec->clear();
47 calibDigits->clear();
48 if (mRunMC) {
49 cluMC->clear();
50 }
51
52 for (const auto& tr : dtr) {
53
54 mFirstDigitInEvent = tr.getFirstEntry();
55 mLastDigitInEvent = mFirstDigitInEvent + tr.getNumberOfObjects();
56 int indexStart = clusters->size();
57 mClusters.clear(); // internal list of FullClusters
58
59 LOG(debug) << "Starting clusteriztion digits from " << mFirstDigitInEvent << " to " << mLastDigitInEvent;
60
61 // Collect digits to clusters
63
64 // Unfold overlapped clusters
65 // Split clusters with several local maxima if necessary
66 if (o2::cpv::CPVSimParams::Instance().mUnfoldClusters) {
67 // calibdigits will be prepared in this routine
69 } else {
70 // otherwise prepare calibdigits only
71 makeCalibDigits(calibDigits);
72 }
73
74 // Calculate properties of collected clusters (Local position, energy, disp etc.)
75 evalCluProperties(digits, clusters, dmc, cluMC);
76
77 LOG(debug) << "Found clusters from " << indexStart << " to " << clusters->size();
78
79 trigRec->emplace_back(tr.getBCData(), indexStart, clusters->size() - indexStart);
80 }
81}
82//____________________________________________________________________________
83void Clusterer::makeClusters(gsl::span<const Digit> digits)
84{
85 // A cluster is defined as a list of neighbour digits
86
87 // Mark all digits as unused yet
88 const int maxNDigits = 23040; // There is no digits more than in CPV modules ;)
89 std::bitset<maxNDigits> digitsUsed;
90 digitsUsed.reset();
91
92 int iFirst = mFirstDigitInEvent; // first index of digit which potentially can be a part of cluster
93
94 for (int i = iFirst; i < mLastDigitInEvent; i++) {
95 if (digitsUsed.test(i - mFirstDigitInEvent)) {
96 continue;
97 }
98
99 const Digit& digitSeed = digits[i];
100 float digitSeedEnergy = digitSeed.getAmplitude(); // already calibrated digits
101 if (digitSeedEnergy < o2::cpv::CPVSimParams::Instance().mDigitMinEnergy) {
102 continue;
103 }
104
105 // is this digit so energetic that start cluster?
106 if (digitSeedEnergy < o2::cpv::CPVSimParams::Instance().mClusteringThreshold) {
107 continue;
108 }
109 // start new cluster
110 mClusters.emplace_back(digitSeed.getAbsId(), digitSeedEnergy, digitSeed.getLabel());
111 FullCluster& clu = mClusters.back();
112 digitsUsed.set(i - mFirstDigitInEvent, true);
113 int iDigitInCluster = 1;
114
115 // Now scan remaining digits in list to find neigbours of our seed
116 int index = 0;
117 while (index < iDigitInCluster) { // scan over digits already in cluster
118 short digitSeedAbsId = clu.getDigitAbsId(index);
119 index++;
120 bool runLoop = true;
121 for (Int_t j = iFirst; runLoop && (j < mLastDigitInEvent); j++) {
122 if (digitsUsed.test(j - mFirstDigitInEvent)) {
123 continue; // look through remaining digits
124 }
125 const Digit& digitN = digits[j];
126 float digitNEnergy = digitN.getAmplitude(); // Already calibrated digits!
128 continue;
129 }
130
131 // call (digit,digitN) in THAT oder !!!!!
132 Int_t ineb = Geometry::areNeighbours(digitSeedAbsId, digitN.getAbsId());
133 switch (ineb) {
134 case -1: // too early (e.g. previous module), do not look before j at subsequent passes
135 iFirst = j;
136 break;
137 case 0: // not a neighbour
138 break;
139 case 1: // are neighbours
140 clu.addDigit(digitN.getAbsId(), digitNEnergy, digitN.getLabel());
141 iDigitInCluster++;
142 digitsUsed.set(j - mFirstDigitInEvent, true);
143 break;
144 case 2: // too far from each other
145 default:
146 runLoop = false;
147 break;
148 } // switch
149 }
150 } // loop over cluster
151 } // energy theshold
152}
153//__________________________________________________________________________
154void Clusterer::makeCalibDigits(std::vector<Digit>* calibDigits)
155{
156 std::array<int, NLMMax> maxAt; // NLMMax:Maximal number of local maxima
157 for (auto& clu : mClusters) {
158 if (clu.getNumberOfLocalMax(maxAt) == 1) { // cluster with only one local maximum
159 // and appropriate size
160 if ((o2::cpv::CPVCalibParams::Instance().gainMinClusterMultForCalib <= clu.getMultiplicity()) &&
162 const FullCluster::CluElement& maxElement = clu.getElementList()->at(maxAt[0]);
163 calibDigits->emplace_back(maxElement.absId, maxElement.energy, maxElement.label);
164 }
165 }
166 }
167}
168//__________________________________________________________________________
169void Clusterer::makeUnfoldingsAndCalibDigits(gsl::span<const Digit> digits, std::vector<Digit>* calibDigits)
170{
171 // Split cluster if several local maxima are found
172
173 std::array<int, NLMMax> maxAt; // NLMMax:Maximal number of local maxima
174
175 int numberOfNotUnfolded = mClusters.size();
176
177 for (int i = 0; i < numberOfNotUnfolded; i++) { // can not use iterator here as list can expand
179 if (clu.getNExMax() > -1) { // already unfolded
180 continue;
181 }
182 // char nMultipl = clu.getMultiplicity();
183 char nMax = clu.getNumberOfLocalMax(maxAt);
184 if (nMax > 1) {
185 unfoldOneCluster(clu, nMax, maxAt, digits);
186 clu.setEnergy(0); // will be skipped later
187 } else {
188 clu.setNExMax(nMax); // Only one local maximum
189 // make calib digits from cluster with only one local maximum and appropriate size
190 if ((o2::cpv::CPVCalibParams::Instance().gainMinClusterMultForCalib <= clu.getMultiplicity()) &&
192 const FullCluster::CluElement& maxElement = clu.getElementList()->at(maxAt[0]);
193 calibDigits->emplace_back(maxElement.absId, maxElement.energy, maxElement.label);
194 }
195 }
196 }
197}
198//____________________________________________________________________________
199void Clusterer::unfoldOneCluster(FullCluster& iniClu, char nMax, gsl::span<int> digitId, gsl::span<const Digit> digits)
200{
201 // Performs the unfolding of a cluster with nMax overlapping showers
202 // Parameters: iniClu cluster to be unfolded
203 // nMax number of local maxima found (this is the number of new clusters)
204 // digitId: index of digits, corresponding to local maxima
205 // maxAtEnergy: energies of digits, corresponding to local maxima
206
207 // Take initial cluster and calculate local coordinates of digits
208 // To avoid multiple re-calculation of same parameters
209 char mult = iniClu.getMultiplicity();
210 if (meInClusters.capacity() < static_cast<unsigned int>(mult)) {
211 meInClusters.reserve(mult);
212 mfij.reserve(mult);
213 }
214
215 const std::vector<FullCluster::CluElement>* cluElist = iniClu.getElementList();
216
217 // Coordinates of centers of clusters
218 std::array<float, NLMMax> xMax;
219 std::array<float, NLMMax> zMax;
220 std::array<float, NLMMax> eMax;
221
222 // transient variables
223 std::array<float, NLMMax> a;
224 std::array<float, NLMMax> b;
225 std::array<float, NLMMax> c;
226
227 for (int iclu = 0; iclu < nMax; iclu++) {
228 xMax[iclu] = (*cluElist)[digitId[iclu]].localX;
229 zMax[iclu] = (*cluElist)[digitId[iclu]].localZ;
230 eMax[iclu] = 2. * (*cluElist)[digitId[iclu]].energy;
231 }
232
233 std::array<float, NLMMax> prop; // proportion of clusters in the current digit
234
235 // Try to decompose cluster to contributions
236 int nIterations = 0;
237 bool insuficientAccuracy = true;
238 while (insuficientAccuracy && nIterations < o2::cpv::CPVSimParams::Instance().mNMaxIterations) {
239 insuficientAccuracy = false; // will be true if at least one parameter changed too much
240 std::memset(&a, 0, sizeof a);
241 std::memset(&b, 0, sizeof b);
242 std::memset(&c, 0, sizeof c);
243 // First calculate shower shapes
244 for (int idig = 0; idig < mult; idig++) {
245 auto it = (*cluElist)[idig];
246 for (int iclu = 0; iclu < nMax; iclu++) {
247 mfij[idig][iclu] = responseShape(it.localX - xMax[iclu], it.localZ - zMax[iclu]);
248 }
249 }
250
251 // Fit energies
252 for (int idig = 0; idig < mult; idig++) {
253 auto it = (*cluElist)[idig];
254 for (int iclu = 0; iclu < nMax; iclu++) {
255 a[iclu] += mfij[idig][iclu] * mfij[idig][iclu];
256 b[iclu] += it.energy * mfij[idig][iclu];
257 for (int kclu = 0; kclu < nMax; kclu++) {
258 if (iclu == kclu) {
259 continue;
260 }
261 c[iclu] += eMax[kclu] * mfij[idig][iclu] * mfij[idig][kclu];
262 }
263 }
264 }
265 // Evaluate new maximal energies
266 for (int iclu = 0; iclu < nMax; iclu++) {
267 if (a[iclu] != 0.) {
268 float eNew = (b[iclu] - c[iclu]) / a[iclu];
269 insuficientAccuracy += (std::abs(eMax[iclu] - eNew) > eNew * o2::cpv::CPVSimParams::Instance().mUnfogingEAccuracy);
270 eMax[iclu] = eNew;
271 }
272 } // otherwise keep old value
273
274 // Loop over all digits of parent cluster and split their energies between daughter clusters
275 // according to shower shape
276 // then re-evaluate local position of clusters
277 for (int idig = 0; idig < mult; idig++) {
278 float eEstimated = 0;
279 for (int iclu = 0; iclu < nMax; iclu++) {
280 prop[iclu] = eMax[iclu] * mfij[idig][iclu];
281 eEstimated += prop[iclu];
282 }
283 if (eEstimated == 0.) { // numerical accuracy
284 continue;
285 }
286 // Split energy of digit according to contributions
287 for (int iclu = 0; iclu < nMax; iclu++) {
288 meInClusters[idig][iclu] = (*cluElist)[idig].energy * prop[iclu] / eEstimated;
289 }
290 }
291
292 // Recalculate parameters of clusters and check relative variation of energy and absolute of position
293 for (int iclu = 0; iclu < nMax; iclu++) {
294 float oldX = xMax[iclu];
295 float oldZ = zMax[iclu];
296 // full energy, need for weight
297 float eTotNew = 0;
298 for (int idig = 0; idig < mult; idig++) {
299 eTotNew += meInClusters[idig][iclu];
300 }
301 xMax[iclu] = 0;
302 zMax[iclu] = 0.;
303 float wtot = 0.;
304 for (int idig = 0; idig < mult; idig++) {
305 if (meInClusters[idig][iclu] > 0) {
306 // In unfolding it is better to use linear weight to reduce contribution of unfolded tails
307 float w = meInClusters[idig][iclu] / eTotNew;
308 // float w = std::max(std::log(eInClusters[idig][iclu] / eTotNew) + o2::cpv::CPVSimParams::Instance().mLogWeight, float(0.));
309 xMax[iclu] += (*cluElist)[idig].localX * w;
310 zMax[iclu] += (*cluElist)[idig].localZ * w;
311 wtot += w;
312 }
313 }
314 if (wtot > 0.) {
315 wtot = 1. / wtot;
316 xMax[iclu] *= wtot;
317 zMax[iclu] *= wtot;
318 }
319 // Compare variation of parameters
320 insuficientAccuracy += (std::abs(xMax[iclu] - oldX) > o2::cpv::CPVSimParams::Instance().mUnfogingXZAccuracy);
321 insuficientAccuracy += (std::abs(zMax[iclu] - oldZ) > o2::cpv::CPVSimParams::Instance().mUnfogingXZAccuracy);
322 }
323 nIterations++;
324 }
325 // Iterations finished, add new clusters
326 for (int iclu = 0; iclu < nMax; iclu++) {
327 mClusters.emplace_back();
328 FullCluster& clu = mClusters.back();
329 clu.setNExMax(nMax);
330 for (int idig = 0; idig < mult; idig++) {
331 float eDigit = meInClusters[idig][iclu];
332 idig++;
334 continue;
335 }
336 clu.addDigit((*cluElist)[idig].absId, eDigit, (*cluElist)[idig].label);
337 }
338 }
339}
340
341//____________________________________________________________________________
342void Clusterer::evalCluProperties(gsl::span<const Digit> digits, std::vector<Cluster>* clusters,
345{
346
347 if (clusters->capacity() - clusters->size() < mClusters.size()) { // avoid expanding vector per element
348 clusters->reserve(clusters->size() + mClusters.size());
349 }
350
351 int labelIndex = 0;
352 if (mRunMC) {
353 labelIndex = cluMC->getIndexedSize();
354 }
355
356 auto clu = mClusters.begin();
357
358 while (clu != mClusters.end()) {
359
360 if (clu->getEnergy() < 1.e-4) { // Marked earlier for removal
361 ++clu;
362 continue;
363 }
364
365 // may be soft digits remain after unfolding
366 clu->purify();
367
368 // LOG(debug) << "Purify done";
369 clu->evalAll();
370
371 if (clu->getEnergy() > 1.e-4) { // Non-empty cluster
372 clusters->emplace_back(*clu);
373
374 if (mRunMC) { // Handle labels
375 // Calculate list of primaries
376 // loop over entries in digit MCTruthContainer
377 const std::vector<FullCluster::CluElement>* vl = clu->getElementList();
378 auto ll = vl->begin();
379 while (ll != vl->end()) {
380 int i = (*ll).label; // index
381 if (i < 0) {
382 ++ll;
383 continue;
384 }
385 gsl::span<const o2::MCCompLabel> spDigList = dmc->getLabels(i);
386 gsl::span<o2::MCCompLabel> spCluList = cluMC->getLabels(labelIndex); // get updated list
387 auto digL = spDigList.begin();
388 while (digL != spDigList.end()) {
389 bool exist = false;
390 auto cluL = spCluList.begin();
391 while (cluL != spCluList.end()) {
392 if (*digL == *cluL) { // exist
393 exist = true;
394 break;
395 }
396 ++cluL;
397 }
398 if (!exist) { // just add label
399 cluMC->addElement(labelIndex, (*digL));
400 }
401 ++digL;
402 }
403 ++ll;
404 }
405 labelIndex++;
406 } // Work with MC
407 }
408
409 ++clu;
410 }
411}
412//____________________________________________________________________________
413float Clusterer::responseShape(float x, float z)
414{
415 // Shape of the shower (see CPV TDR)
416 // we neglect dependence on the incident angle.
417
418 const float width = 1. / (2. * 2.32 * 2.32 * 2.32 * 2.32 * 2.32 * 2.32);
419 float r2 = x * x + z * z;
420 return TMath::Exp(-r2 * r2 * r2 * width);
421/* float r2 = x * x + z * z;
422 float r4 = r2 * r2;
423 float r295 = TMath::Power(r2, 2.95 / 2.);
424 float shape = TMath::Exp(-r4 * (1. / (2.32 + 0.26 * r4) + 0.0316 / (1 + 0.0652 * r295)));
425 return shape;
426*/}
Definition of the CPV cluster finder.
ClassImp(Clusterer)
int32_t i
float float float & zMax
uint32_t j
Definition RawData.h:0
uint32_t c
Definition RawData.h:2
std::ostringstream debug
char getNExMax() const
Definition Cluster.h:74
void setEnergy(float e)
Definition Cluster.h:60
unsigned char getMultiplicity() const
Definition Cluster.h:68
float getEnergy() const
Definition Cluster.h:61
void setNExMax(char nmax=1)
Definition Cluster.h:73
void process(gsl::span< const Digit > digits, gsl::span< const TriggerRecord > dtr, const o2::dataformats::MCTruthContainer< o2::MCCompLabel > *dmc, std::vector< Cluster > *clusters, std::vector< TriggerRecord > *trigRec, o2::dataformats::MCTruthContainer< o2::MCCompLabel > *cluMC, std::vector< Digit > *calibDigits)
Definition Clusterer.cxx:39
void unfoldOneCluster(FullCluster &iniClu, char nMax, gsl::span< int > digitId, gsl::span< const Digit > digits)
void makeCalibDigits(std::vector< Digit > *calibDigits)
std::vector< std::vector< float > > mfij
Definition Clusterer.h:62
void makeClusters(gsl::span< const Digit > digits)
Definition Clusterer.cxx:83
void evalCluProperties(gsl::span< const Digit > digits, std::vector< Cluster > *clusters, const o2::dataformats::MCTruthContainer< o2::MCCompLabel > *dmc, o2::dataformats::MCTruthContainer< o2::MCCompLabel > *cluMC)
std::vector< FullCluster > mClusters
internal vector of clusters
Definition Clusterer.h:58
void makeUnfoldingsAndCalibDigits(gsl::span< const Digit > digits, std::vector< Digit > *calibDigits)
std::vector< std::vector< float > > meInClusters
Definition Clusterer.h:61
float responseShape(float dx, float dz)
int mLastDigitInEvent
Range of digits from one event.
Definition Clusterer.h:57
int mFirstDigitInEvent
Range of digits from one event.
Definition Clusterer.h:56
bool mRunMC
Process MC info.
Definition Clusterer.h:55
float getAmplitude() const
Energy deposited in a cell.
Definition Digit.h:91
unsigned short getAbsId() const
Absolute sell id.
Definition Digit.h:87
int getLabel() const
index of entry in MCLabels array
Definition Digit.h:96
CPV cluster implementation.
Definition FullCluster.h:28
const std::vector< CluElement > * getElementList() const
Definition FullCluster.h:56
static short areNeighbours(unsigned short absId1, unsigned short absId2)
Definition Geometry.cxx:49
A container to hold and manage MC truth information/labels.
gsl::span< TruthElement > getLabels(uint32_t dataindex)
void addElement(uint32_t dataindex, TruthElement const &element, bool noElement=false)
GLint GLenum GLint x
Definition glcorearb.h:403
GLuint index
Definition glcorearb.h:781
GLint GLsizei width
Definition glcorearb.h:270
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
GLubyte GLubyte GLubyte GLubyte w
Definition glcorearb.h:852
GLdouble GLdouble GLdouble z
Definition glcorearb.h:843
unsigned char gainMaxClusterMultForCalib
Max cluster multiplicity for calibration digit production.
float mUnfogingXZAccuracy
Accuracy of position calculation in unfolding procedure (cm)
float mUnfogingEAccuracy
Accuracy of energy calculation in unfoding prosedure (GeV)
float mDigitMinEnergy
Minimal amplitude of a digit to be used in cluster.
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
Cluster clu
std::vector< Cluster > clusters
std::vector< Digit > digits