CMS 3D CMS Logo

HGCalCLUEAlgo.cc
Go to the documentation of this file.
2 
3 // Geometry
8 
10 //
12 #include "tbb/task_arena.h"
13 #include "tbb/tbb.h"
14 
15 using namespace hgcal_clustering;
16 
18  // loop over all hits and create the Hexel structure, skip energies below ecut
19 
20  if (dependSensor_) {
21  // for each layer and wafer calculate the thresholds (sigmaNoise and energy)
22  // once
23  computeThreshold();
24  }
25 
26  std::vector<bool> firstHit(2 * (maxlayer + 1), true);
27  for (unsigned int i = 0; i < hits.size(); ++i) {
28  const HGCRecHit &hgrh = hits[i];
29  DetId detid = hgrh.detid();
30  unsigned int layer = rhtools_.getLayerWithOffset(detid);
31  float thickness = 0.f;
32  // set sigmaNoise default value 1 to use kappa value directly in case of
33  // sensor-independent thresholds
34  float sigmaNoise = 1.f;
35  if (dependSensor_) {
36  thickness = rhtools_.getSiThickness(detid);
37  int thickness_index = rhtools_.getSiThickIndex(detid);
38  if (thickness_index == -1) thickness_index = 3;
39  double storedThreshold = thresholds_[layer - 1][thickness_index];
40  sigmaNoise = v_sigmaNoise_[layer - 1][thickness_index];
41 
42  if (hgrh.energy() < storedThreshold)
43  continue; // this sets the ZS threshold at ecut times the sigma noise
44  // for the sensor
45  }
46  if (!dependSensor_ && hgrh.energy() < ecut_) continue;
47 
48  // map layers from positive endcap (z) to layer + maxlayer+1 to prevent
49  // mixing up hits from different sides
50  layer += int(rhtools_.zside(detid) > 0) * (maxlayer + 1);
51 
52  // determine whether this is a half-hexagon
53  bool isHalf = rhtools_.isHalfCell(detid);
54  const GlobalPoint position(rhtools_.getPosition(detid));
55 
56  // here's were the KDNode is passed its dims arguments - note that these are
57  // *copied* from the Hexel
58  points_[layer].emplace_back(Hexel(hgrh, detid, isHalf, sigmaNoise, thickness, &rhtools_),
59  position.x(), position.y());
60 
61  // for each layer, store the minimum and maximum x and y coordinates for the
62  // KDTreeBox boundaries
63  if (firstHit[layer]) {
64  minpos_[layer][0] = position.x();
65  minpos_[layer][1] = position.y();
66  maxpos_[layer][0] = position.x();
67  maxpos_[layer][1] = position.y();
68  firstHit[layer] = false;
69  } else {
70  minpos_[layer][0] = std::min((float)position.x(), minpos_[layer][0]);
71  minpos_[layer][1] = std::min((float)position.y(), minpos_[layer][1]);
72  maxpos_[layer][0] = std::max((float)position.x(), maxpos_[layer][0]);
73  maxpos_[layer][1] = std::max((float)position.y(), maxpos_[layer][1]);
74  }
75  } // end loop hits
76 }
77 
78 // Create a vector of Hexels associated to one cluster from a collection of
79 // HGCalRecHits - this can be used directly to make the final cluster list -
80 // this method can be invoked multiple times for the same event with different
81 // input (reset should be called between events)
83  layerClustersPerLayer_.resize(2 * maxlayer + 2);
84  // assign all hits in each layer to a cluster core
85  tbb::this_task_arena::isolate([&] {
86  tbb::parallel_for(size_t(0), size_t(2 * maxlayer + 2), [&](size_t i) {
87  KDTreeBox bounds(minpos_[i][0], maxpos_[i][0], minpos_[i][1], maxpos_[i][1]);
88  KDTree hit_kdtree;
89  hit_kdtree.build(points_[i], bounds);
90 
91  unsigned int actualLayer = i > maxlayer
92  ? (i - (maxlayer + 1))
93  : i; // maps back from index used for KD trees to actual layer
94 
95  double maxdensity = calculateLocalDensity(points_[i], hit_kdtree,
96  actualLayer); // also stores rho (energy
97  // density) for each point (node)
98  // calculate distance to nearest point with higher density storing
99  // distance (delta) and point's index
100  calculateDistanceToHigher(points_[i]);
101  findAndAssignClusters(points_[i], hit_kdtree, maxdensity, bounds, actualLayer,
102  layerClustersPerLayer_[i]);
103  });
104  });
105  //Now that we have the density per point we can store it
106  for(auto const& p: points_) { setDensity(p); }
107 }
108 
109 std::vector<reco::BasicCluster> HGCalCLUEAlgo::getClusters(bool) {
111  std::vector<std::pair<DetId, float>> thisCluster;
112  for (const auto &clsOnLayer : layerClustersPerLayer_) {
113  int index = 0;
114  for (const auto &cl : clsOnLayer) {
115  double energy = 0;
116  Point position;
117  // Will save the maximum density hit of the cluster
118  size_t rsmax = max_index(cl);
119  position = calculatePosition(cl); // energy-weighted position
120  for (const auto &it : cl) {
121  energy += it.data.weight;
122  thisCluster.emplace_back(it.data.detid, 1.f);
123  }
124  if (verbosity_ < pINFO) {
125  LogDebug("HGCalCLUEAlgo")
126  << "******** NEW CLUSTER (HGCIA) ********"
127  << "Index " << index
128  << "No. of cells = " << cl.size()
129  << " Energy = " << energy
130  << " Phi = " << position.phi()
131  << " Eta = " << position.eta()
132  << "*****************************" << std::endl;
133  }
134  clusters_v_.emplace_back(energy, position, caloID, thisCluster, algoId_);
135  if (!clusters_v_.empty()) {
136  clusters_v_.back().setSeed(cl[rsmax].data.detid);
137  }
138  thisCluster.clear();
139  index++;
140  }
141  }
142  return clusters_v_;
143 }
144 
145 math::XYZPoint HGCalCLUEAlgo::calculatePosition(const std::vector<KDNode> &v) const {
146  float total_weight = 0.f;
147  float x = 0.f;
148  float y = 0.f;
149 
150  unsigned int v_size = v.size();
151  unsigned int maxEnergyIndex = 0;
152  float maxEnergyValue = 0;
153 
154  // loop over hits in cluster candidate
155  // determining the maximum energy hit
156  for (unsigned int i = 0; i < v_size; i++) {
157  if (v[i].data.weight > maxEnergyValue) {
158  maxEnergyValue = v[i].data.weight;
159  maxEnergyIndex = i;
160  }
161  }
162 
163  // Si cell or Scintillator. Used to set approach and parameters
164  int thick = rhtools_.getSiThickIndex(v[maxEnergyIndex].data.detid);
165 
166  // for hits within positionDeltaRho_c_ from maximum energy hit
167  // build up weight for energy-weighted position
168  // and save corresponding hits indices
169  std::vector<unsigned int> innerIndices;
170  for (unsigned int i = 0; i < v_size; i++) {
171  if (thick == -1 || distance2(v[i].data, v[maxEnergyIndex].data) < positionDeltaRho_c_[thick]) {
172  innerIndices.push_back(i);
173 
174  float rhEnergy = v[i].data.weight;
175  total_weight += rhEnergy;
176  // just fill x, y for scintillator
177  // for Si it is overwritten later anyway
178  if (thick == -1) {
179  x += v[i].data.x * rhEnergy;
180  y += v[i].data.y * rhEnergy;
181  }
182  }
183  }
184  // just loop on reduced vector of interesting indices
185  // to compute log weighting
186  if (thick != -1 && total_weight != 0.) { // Silicon case
187  float total_weight_log = 0.f;
188  float x_log = 0.f;
189  float y_log = 0.f;
190  for (auto idx : innerIndices) {
191  float rhEnergy = v[idx].data.weight;
192  if (rhEnergy == 0.) continue;
193  float Wi = std::max(thresholdW0_[thick] + std::log(rhEnergy / total_weight), 0.);
194  x_log += v[idx].data.x * Wi;
195  y_log += v[idx].data.y * Wi;
196  total_weight_log += Wi;
197  }
198  total_weight = total_weight_log;
199  x = x_log;
200  y = y_log;
201  }
202 
203  if (total_weight != 0.) {
204  auto inv_tot_weight = 1. / total_weight;
205  return math::XYZPoint(x * inv_tot_weight, y * inv_tot_weight, v[maxEnergyIndex].data.z);
206  }
207  return math::XYZPoint(0, 0, 0);
208 }
209 
210 double HGCalCLUEAlgo::calculateLocalDensity(std::vector<KDNode> &nd, KDTree &lp,
211  const unsigned int layer) const {
212  double maxdensity = 0.;
213  float delta_c; // maximum search distance (critical distance) for local
214  // density calculation
215  if (layer <= lastLayerEE)
216  delta_c = vecDeltas_[0];
217  else if (layer <= lastLayerFH)
218  delta_c = vecDeltas_[1];
219  else
220  delta_c = vecDeltas_[2];
221 
222  // for each node calculate local density rho and store it
223  for (unsigned int i = 0; i < nd.size(); ++i) {
224  // speec up search by looking within +/- delta_c window only
225  KDTreeBox search_box(nd[i].dims[0] - delta_c, nd[i].dims[0] + delta_c, nd[i].dims[1] - delta_c,
226  nd[i].dims[1] + delta_c);
227  std::vector<KDNode> found;
228  lp.search(search_box, found);
229  const unsigned int found_size = found.size();
230  for (unsigned int j = 0; j < found_size; j++) {
231  if (distance(nd[i].data, found[j].data) < delta_c) {
232  nd[i].data.rho += (nd[i].data.detid == found[j].data.detid ? 1. : 0.5) * found[j].data.weight;
233  maxdensity = std::max(maxdensity, nd[i].data.rho);
234  }
235  } // end loop found
236  } // end loop nodes
237  return maxdensity;
238 }
239 
240 double HGCalCLUEAlgo::calculateDistanceToHigher(std::vector<KDNode> &nd) const {
241  // sort vector of Hexels by decreasing local density
242  std::vector<size_t> &&rs = sorted_indices(nd);
243 
244  double maxdensity = 0.0;
245  int nearestHigher = -1;
246 
247  if (!rs.empty())
248  maxdensity = nd[rs[0]].data.rho;
249  else
250  return maxdensity; // there are no hits
251  double dist2 = 0.;
252  // start by setting delta for the highest density hit to
253  // the most distant hit - this is a convention
254 
255  for (const auto &j : nd) {
256  double tmp = distance2(nd[rs[0]].data, j.data);
257  if (tmp > dist2) dist2 = tmp;
258  }
259  nd[rs[0]].data.delta = std::sqrt(dist2);
260  nd[rs[0]].data.nearestHigher = nearestHigher;
261 
262  // now we save the largest distance as a starting point
263  const double max_dist2 = dist2;
264  const unsigned int nd_size = nd.size();
265 
266  for (unsigned int oi = 1; oi < nd_size; ++oi) { // start from second-highest density
267  dist2 = max_dist2;
268  unsigned int i = rs[oi];
269  // we only need to check up to oi since hits
270  // are ordered by decreasing density
271  // and all points coming BEFORE oi are guaranteed to have higher rho
272  // and the ones AFTER to have lower rho
273  for (unsigned int oj = 0; oj < oi; ++oj) {
274  unsigned int j = rs[oj];
275  double tmp = distance2(nd[i].data, nd[j].data);
276  if (tmp <= dist2) { // this "<=" instead of "<" addresses the (rare) case
277  // when there are only two hits
278  dist2 = tmp;
279  nearestHigher = j;
280  }
281  }
282  nd[i].data.delta = std::sqrt(dist2);
283  nd[i].data.nearestHigher = nearestHigher; // this uses the original unsorted hitlist
284  }
285  return maxdensity;
286 }
287 int HGCalCLUEAlgo::findAndAssignClusters(std::vector<KDNode> &nd, KDTree &lp, double maxdensity,
288  KDTreeBox &bounds, const unsigned int layer,
289  std::vector<std::vector<KDNode>> &clustersOnLayer) const {
290  // this is called once per layer and endcap...
291  // so when filling the cluster temporary vector of Hexels we resize each time
292  // by the number of clusters found. This is always equal to the number of
293  // cluster centers...
294 
295  unsigned int nClustersOnLayer = 0;
296  float delta_c; // critical distance
297  if (layer <= lastLayerEE)
298  delta_c = vecDeltas_[0];
299  else if (layer <= lastLayerFH)
300  delta_c = vecDeltas_[1];
301  else
302  delta_c = vecDeltas_[2];
303 
304  std::vector<size_t> rs = sorted_indices(nd); // indices sorted by decreasing rho
305  std::vector<size_t> ds = sort_by_delta(nd); // sort in decreasing distance to higher
306 
307  const unsigned int nd_size = nd.size();
308  for (unsigned int i = 0; i < nd_size; ++i) {
309  if (nd[ds[i]].data.delta < delta_c) break; // no more cluster centers to be looked at
310  if (dependSensor_) {
311  float rho_c = kappa_ * nd[ds[i]].data.sigmaNoise;
312  if (nd[ds[i]].data.rho < rho_c) continue; // set equal to kappa times noise threshold
313 
314  } else if (nd[ds[i]].data.rho * kappa_ < maxdensity)
315  continue;
316 
317  nd[ds[i]].data.clusterIndex = nClustersOnLayer;
318  if (verbosity_ < pINFO) {
319  LogDebug("HGCalCLUEAlgo")
320  << "Adding new cluster with index " << nClustersOnLayer
321  << "Cluster center is hit " << ds[i] << std::endl;
322  }
323  nClustersOnLayer++;
324  }
325 
326  // at this point nClustersOnLayer is equal to the number of cluster centers -
327  // if it is zero we are done
328  if (nClustersOnLayer == 0) return nClustersOnLayer;
329 
330  // assign remaining points to clusters, using the nearestHigher set from
331  // previous step (always set except
332  // for top density hit that is skipped...)
333  for (unsigned int oi = 1; oi < nd_size; ++oi) {
334  unsigned int i = rs[oi];
335  int ci = nd[i].data.clusterIndex;
336  if (ci == -1 && nd[i].data.delta < 2. * delta_c) {
337  nd[i].data.clusterIndex = nd[nd[i].data.nearestHigher].data.clusterIndex;
338  }
339  }
340 
341  // make room in the temporary cluster vector for the additional clusterIndex
342  // clusters
343  // from this layer
344  if (verbosity_ < pINFO) {
345  LogDebug("HGCalCLUEAlgo")
346  << "resizing cluster vector by " << nClustersOnLayer << std::endl;
347  }
348  clustersOnLayer.resize(nClustersOnLayer);
349 
350  // Fill the cluster vector
351  for (unsigned int i = 0; i < nd_size; ++i) {
352  int ci = nd[i].data.clusterIndex;
353  if (ci != -1) {
354  clustersOnLayer[ci].push_back(nd[i]);
355  if (verbosity_ < pINFO) {
356  LogDebug("HGCalCLUEAlgo")
357  << "Pushing hit " << i << " into cluster with index " << ci << std::endl;
358  }
359  }
360  }
361 
362  // prepare the offset for the next layer if there is one
363  if (verbosity_ < pINFO) {
364  LogDebug("HGCalCLUEAlgo") << "moving cluster offset by " << nClustersOnLayer << std::endl;
365  }
366  return nClustersOnLayer;
367 }
368 
370  // To support the TDR geometry and also the post-TDR one (v9 onwards), we
371  // need to change the logic of the vectors containing signal to noise and
372  // thresholds. The first 3 indices will keep on addressing the different
373  // thicknesses of the Silicon detectors, while the last one, number 3 (the
374  // fourth) will address the Scintillators. This change will support both
375  // geometries at the same time.
376 
377  if (initialized_) return; // only need to calculate thresholds once
378 
379  initialized_ = true;
380 
381  std::vector<double> dummy;
382  const unsigned maxNumberOfThickIndices = 3;
383  dummy.resize(maxNumberOfThickIndices + 1, 0); // +1 to accomodate for the Scintillators
384  thresholds_.resize(maxlayer, dummy);
385  v_sigmaNoise_.resize(maxlayer, dummy);
386 
387  for (unsigned ilayer = 1; ilayer <= maxlayer; ++ilayer) {
388  for (unsigned ithick = 0; ithick < maxNumberOfThickIndices; ++ithick) {
389  float sigmaNoise = 0.001f * fcPerEle_ * nonAgedNoises_[ithick] * dEdXweights_[ilayer] /
390  (fcPerMip_[ithick] * thicknessCorrection_[ithick]);
391  thresholds_[ilayer - 1][ithick] = sigmaNoise * ecut_;
392  v_sigmaNoise_[ilayer - 1][ithick] = sigmaNoise;
393  }
394  float scintillators_sigmaNoise = 0.001f * noiseMip_ * dEdXweights_[ilayer];
395  thresholds_[ilayer - 1][maxNumberOfThickIndices] = ecut_ * scintillators_sigmaNoise;
396  v_sigmaNoise_[ilayer - 1][maxNumberOfThickIndices] = scintillators_sigmaNoise;
397  }
398 }
399 
400 void HGCalCLUEAlgo::setDensity(const std::vector<KDNode> &nd){
401 
402  // for each node store the computer local density
403  for (auto &i : nd){
404  density_[ i.data.detid ] = i.data.rho ;
405  }
406 }
407 
409  return density_;
410 }
#define LogDebug(id)
constexpr float energy() const
Definition: CaloRecHit.h:31
int findAndAssignClusters(std::vector< KDNode > &, KDTree &, double, KDTreeBox &, const unsigned int, std::vector< std::vector< KDNode > > &) const
constexpr const DetId & detid() const
Definition: CaloRecHit.h:35
void makeClusters() override
double calculateDistanceToHigher(std::vector< KDNode > &) const
std::vector< reco::BasicCluster > getClusters(bool) override
Density getDensity() override
hgcal_clustering::Density Density
Definition: HGCalCLUEAlgo.h:29
std::pair< double, double > calculatePosition(TVirtualPad *myPad, int boundary)
void computeThreshold()
T sqrt(T t)
Definition: SSEVec.h:18
std::vector< size_t > sorted_indices(const std::vector< T > &v)
void search(const KDTreeBox &searchBox, std::vector< DATA > &resRecHitList)
T min(T a, T b)
Definition: MathUtil.h:58
void populate(const HGCRecHitCollection &hits) override
double calculateLocalDensity(std::vector< KDNode > &, KDTree &, const unsigned int) const
void build(std::vector< KDTreeNodeInfo< DATA > > &eltList, const KDTreeBox &region)
Definition: DetId.h:18
XYZPointD XYZPoint
point in space with cartesian internal representation
Definition: Point3D.h:12
std::vector< std::vector< double > > tmp
Definition: MVATrainer.cc:100
char data[epos_bytes_allocation]
Definition: EPOS_Wrapper.h:82
size_type size() const
Structure Point Contains parameters of Gaussian fits to DMRs.
Definition: DMRtrends.cc:55
static int position[264][3]
Definition: ReadPGInfo.cc:509
size_t max_index(const std::vector< T > &v)
math::XYZPoint calculatePosition(const std::vector< KDNode > &) const
void setDensity(const std::vector< KDNode > &nd)