CMS 3D CMS Logo

HGCalImagingAlgo.cc
Go to the documentation of this file.
2 
3 // Geometry
7 
9 //
11 
12 
13 // Create a vector of Hexels associated to one cluster from a collection of HGCalRecHits - this can be used
14 // directly to make the final cluster list - this method can be invoked multiple times for the same event
15 // with different input (reset should be called between events)
17 {
18  //used for speedy search
19  std::vector<std::vector<KDNode> >points(2*(maxlayer+1));
20  std::vector<KDTree> hit_kdtree(2*(maxlayer+1));
21 
22  std::vector<std::array<float,2> > minpos(2*(maxlayer+1),{ {0.0f,0.0f} }),maxpos(2*(maxlayer+1),{ {0.0f,0.0f} });
23 
24  // std::vector<std::vector<Hexel> > points(2*(maxlayer+1)); //a vector of vectors of hexels, one for each layer
25  //@@EM todo: the number of layers should be obtained programmatically - the range is 1-n instead of 0-n-1...
26 
27 
28  if (verbosity < pINFO)
29  {
30  std::cout << "-------------------------------------------------------------" << std::endl;
31  std::cout << "HGC Imaging algorithm invoked for " << std::endl;
32  std::cout << "delta_c " << delta_c << " kappa " << kappa;
33  // if( doSharing ) std::cout << " showerSigma " << std::sqrt(sigma2);
34  std::cout << std::endl;
35  }
36 
37  //loop over all hits and create the Hexel structure, skip energies below ecut
38  for (unsigned int i=0;i<hits.size();++i) {
39  const HGCRecHit& hgrh = hits[i];
40  if(hgrh.energy() < ecut) continue;
41  DetId detid = hgrh.detid();
42 
43  int layer = rhtools_.getLayerWithOffset(detid)+int(HGCalDetId(detid).zside()>0)*(maxlayer+1);
44 
45  // determine whether this is a half-hexagon
46  bool isHalf = rhtools_.isHalfCell(detid);
47  const GlobalPoint position( std::move( rhtools_.getPosition( detid ) ) );
48  //here's were the KDNode is passed its dims arguments - note that these are *copied* from the Hexel
49  points[layer].emplace_back(Hexel(hgrh,detid,isHalf,&rhtools_),position.x(),position.y());
50  if(points[layer].size()==0){
51  minpos[layer][0] = position.x(); minpos[layer][1] = position.y();
52  maxpos[layer][0] = position.x(); maxpos[layer][1] = position.y();
53  }else{
54  minpos[layer][0] = std::min((float)position.x(),minpos[layer][0]);
55  minpos[layer][1] = std::min((float)position.y(),minpos[layer][1]);
56  maxpos[layer][0] = std::max((float)position.x(),maxpos[layer][0]);
57  maxpos[layer][1] = std::max((float)position.y(),maxpos[layer][1]);
58 
59  }
60  }
61 
62  //assign all hits in each layer to a cluster core or halo
63  for (unsigned int i = 0; i <= 2*maxlayer+1; ++i) {
64  KDTreeBox bounds(minpos[i][0],maxpos[i][0],
65  minpos[i][1],maxpos[i][1]);
66 
67  hit_kdtree[i].build(points[i],bounds);
68 
69  double maxdensity = calculateLocalDensity(points[i],hit_kdtree[i]);
70  // std::cout << "layer " << i << " max density " << maxdensity
71  // << " total hits " << points[i].size() << std::endl;
72  calculateDistanceToHigher(points[i],hit_kdtree[i]);
73  findAndAssignClusters(points[i],hit_kdtree[i],maxdensity,bounds);
74  // std::cout << "found " << nclusters << " clusters" << std::endl;
75  }
76  //make the cluster vector
77 }
78 
79 std::vector<reco::BasicCluster> HGCalImagingAlgo::getClusters(bool doSharing){
80 
82  std::vector< std::pair<DetId, float> > thisCluster;
83  for (unsigned int i = 0; i < current_v.size(); ++i){
84  double energy = 0;
86 
87  if( doSharing ) {
88 
89  std::vector<unsigned> seeds = findLocalMaximaInCluster(current_v[i]);
90  //std::cout << " sharing found " << seeds.size() << " sub-cluster seeds in cluster " << i << std::endl;
91 
92  std::vector<std::vector<double> > fractions;
93  // first pass can have noise it in
94  shareEnergy(current_v[i],seeds,fractions);
95 
96  // reset and run second pass after vetoing seeds
97  // that result in trivial clusters (less than 2 effective cells)
98 
99 
100  for( unsigned isub = 0; isub < fractions.size(); ++isub ) {
101  double effective_hits = 0.0;
102  double energy = calculateEnergyWithFraction(current_v[i],fractions[isub]);
103  Point position = calculatePositionWithFraction(current_v[i],fractions[isub]);
104 
105  //std::cout << "Fractions*Energies: ";
106  for( unsigned ihit = 0; ihit < fractions[isub].size(); ++ihit ) {
107  const double fraction = fractions[isub][ihit];
108  if( fraction > 1e-7 ) {
109  //std::cout << fraction << "*" << current_v[i][ihit].weight << " ";
110  effective_hits += fraction;
111  thisCluster.emplace_back(current_v[i][ihit].data.detid,fraction);
112  }
113  }
114  //std::cout << std::endl;
115 
116  if (verbosity < pINFO)
117  {
118  std::cout << "\t******** NEW CLUSTER (SHARING) ********" << std::endl;
119  std::cout << "\tEff. No. of cells = " << effective_hits << std::endl;
120  std::cout << "\t Energy = " << energy << std::endl;
121  std::cout << "\t Phi = " << position.phi() << std::endl;
122  std::cout << "\t Eta = " << position.eta() << std::endl;
123  std::cout << "\t*****************************" << std::endl;
124  }
125  clusters_v.push_back(reco::BasicCluster(energy, position, caloID, thisCluster,
126  algoId));
127  thisCluster.clear();
128  }
129  }else{
130  position = calculatePosition(current_v[i]);
131  std::vector< KDNode >::iterator it;
132  for (it = current_v[i].begin(); it != current_v[i].end(); it++)
133  {
134  energy += (*it).data.isHalo ? 0. : (*it).data.weight;
135  thisCluster.emplace_back(std::pair<DetId, float>((*it).data.detid,((*it).data.isHalo?0.:1.)));
136  };
137  if (verbosity < pINFO)
138  {
139  std::cout << "******** NEW CLUSTER (HGCIA) ********" << std::endl;
140  std::cout << "Index " << i << std::endl;
141  std::cout << "No. of cells = " << current_v[i].size() << std::endl;
142  std::cout << " Energy = " << energy << std::endl;
143  std::cout << " Phi = " << position.phi() << std::endl;
144  std::cout << " Eta = " << position.eta() << std::endl;
145  std::cout << "*****************************" << std::endl;
146  }
147  clusters_v.push_back(reco::BasicCluster(energy, position, caloID, thisCluster,
148  algoId));
149  thisCluster.clear();
150  }
151  }
152  return clusters_v;
153 }
154 
156  float total_weight = 0.;
157  float x = 0.;
158  float y = 0.;
159  float z = 0.;
160  for (unsigned int i = 0; i < v.size(); i++){
161  if(!v[i].data.isHalo){
162  total_weight += v[i].data.weight;
163  x += v[i].data.x*v[i].data.weight;
164  y += v[i].data.y*v[i].data.weight;
165  z += v[i].data.z*v[i].data.weight;
166  }
167  }
168 
169  return math::XYZPoint( x/total_weight,
170  y/total_weight,
171  z/total_weight );
172 }
173 
174 double HGCalImagingAlgo::calculateLocalDensity(std::vector<KDNode> &nd, KDTree &lp){
175  double maxdensity = 0.;
176  for(unsigned int i = 0; i < nd.size(); ++i){
177  KDTreeBox search_box(nd[i].dims[0]-delta_c,nd[i].dims[0]+delta_c,
178  nd[i].dims[1]-delta_c,nd[i].dims[1]+delta_c);
179  std::vector<KDNode> found;
180  lp.search(search_box,found);
181  for(unsigned int j = 0; j < found.size(); j++){
182  if(distance(nd[i].data,found[j].data) < delta_c){
183  nd[i].data.rho += found[j].data.weight;
184  if(nd[i].data.rho > maxdensity) maxdensity = nd[i].data.rho;
185  }
186  }
187  }
188  return maxdensity;
189 }
190 
191 double HGCalImagingAlgo::calculateDistanceToHigher(std::vector<KDNode> &nd, KDTree &lp){
192 
193 
194  //sort vector of Hexels by decreasing local density
195  std::vector<size_t> rs = sorted_indices(nd);
196 
197  double maxdensity = 0.0;
198  int nearestHigher = -1;
199 
200 
201  if(rs.size()>0)
202  maxdensity = nd[rs[0]].data.rho;
203  else
204  return maxdensity; // there are no hits
205  double dist2 = 2500.0;
206  //start by setting delta for the highest density hit to
207  //the most distant hit - this is a convention
208 
209  for(unsigned int j = 0; j < nd.size(); j++){
210  double tmp = distance2(nd[rs[0]].data, nd[j].data);
211  dist2 = tmp > dist2 ? tmp : dist2;
212  }
213  nd[rs[0]].data.delta = std::sqrt(dist2);
214  nd[rs[0]].data.nearestHigher = nearestHigher;
215 
216  //now we save the largest distance as a starting point
217 
218  const double max_dist2 = dist2;
219 
220  for(unsigned int oi = 1; oi < nd.size(); ++oi){ // start from second-highest density
221  dist2 = max_dist2;
222  unsigned int i = rs[oi];
223  // we only need to check up to oi since hits
224  // are ordered by decreasing density
225  // and all points coming BEFORE oi are guaranteed to have higher rho
226  // and the ones AFTER to have lower rho
227  for(unsigned int oj = 0; oj < oi; oj++){
228  unsigned int j = rs[oj];
229  double tmp = distance2(nd[i].data, nd[j].data);
230  if(tmp <= dist2){ //this "<=" instead of "<" addresses the (rare) case when there are only two hits
231  dist2 = tmp;
232  nearestHigher = j;
233  }
234  }
235  nd[i].data.delta = std::sqrt(dist2);
236  nd[i].data.nearestHigher = nearestHigher; //this uses the original unsorted hitlist
237  }
238  return maxdensity;
239 }
240 
241 int HGCalImagingAlgo::findAndAssignClusters(std::vector<KDNode> &nd,KDTree &lp, double maxdensity, KDTreeBox &bounds){
242 
243  //this is called once per layer...
244  //so when filling the cluster temporary vector of Hexels we resize each time by the number
245  //of clusters found. This is always equal to the number of cluster centers...
246 
247  unsigned int clusterIndex = 0;
248 
249  std::vector<size_t> rs = sorted_indices(nd); // indices sorted by decreasing rho
250  std::vector<size_t> ds = sort_by_delta(nd); // sort in decreasing distance to higher
251 
252 
253  for(unsigned int i =0; i < nd.size(); ++i){
254 
255  // std::cout << " delta " << lp[ds[i]].delta << " rho " << lp[ds[i]].rho << std::endl;
256  if(nd[ds[i]].data.delta < delta_c) break; // no more cluster centers to be looked at
257  if(nd[ds[i]].data.rho < maxdensity/kappa /* || lp[ds[i]].rho<0.001*/) continue;
258  //skip this as a potential cluster center because it fails the density cut
259 
260  nd[ds[i]].data.clusterIndex = clusterIndex;
261  if (verbosity < pINFO)
262  {
263  std::cout << "Adding new cluster with index " << clusterIndex+cluster_offset << std::endl;
264  std::cout << "Cluster center is hit " << ds[i] << std::endl;
265  }
266  clusterIndex++;
267  }
268 
269  //at this point clusterIndex is equal to the number of cluster centers - if it is zero we are
270  //done
271  if(clusterIndex==0) return clusterIndex;
272 
273  //assign to clusters, using the nearestHigher set from previous step (always set except
274  // for top density hit that is skipped...
275  for(unsigned int oi =1; oi < nd.size(); ++oi){
276  unsigned int i = rs[oi];
277  int ci = nd[i].data.clusterIndex;
278  if(ci == -1){
279  nd[i].data.clusterIndex = nd[nd[i].data.nearestHigher].data.clusterIndex;
280  }
281  }
282 
283  //make room in the temporary cluster vector for the additional clusterIndex clusters
284  // from this layer
285  if (verbosity < pINFO)
286  {
287  std::cout << "resizing cluster vector by "<< clusterIndex << std::endl;
288  }
289  current_v.resize(cluster_offset+clusterIndex);
290 
291  //assign points closer than dc to other clusters to border region
292  //and find critical border density
293  std::vector<double> rho_b(clusterIndex,0.);
294  lp.clear();
295  lp.build(nd,bounds);
296  //now loop on all hits again :( and check: if there are hits from another cluster within d_c -> flag as border hit
297  for(unsigned int i = 0; i < nd.size(); ++i){
298  int ci = nd[i].data.clusterIndex;
299  bool flag_isolated = true;
300  if(ci != -1){
301  KDTreeBox search_box(nd[i].dims[0]-delta_c,nd[i].dims[0]+delta_c,
302  nd[i].dims[1]-delta_c,nd[i].dims[1]+delta_c);
303  std::vector<KDNode> found;
304  lp.search(search_box,found);
305 
306  for(unsigned int j = 1; j < found.size(); j++){
307  //check if the hit is not within d_c of another cluster
308  if(found[j].data.clusterIndex!=-1){
309  float dist = distance(found[j].data,nd[i].data);
310  if(dist < delta_c && found[j].data.clusterIndex!=ci){
311  //in which case we assign it to the border
312  nd[i].data.isBorder = true;
313  break;
314  }
315  //because we are using two different containers, we have to make sure that we don't unflag the
316  // hit when it finds *itself* closer than delta_c
317  if(dist < delta_c && dist != 0. && found[j].data.clusterIndex==ci){
318  //this is not an isolated hit
319  flag_isolated = false;
320  }
321  }
322  }
323  if(flag_isolated) nd[i].data.isBorder = true; //the hit is more than delta_c from any of its brethren
324  }
325  //check if this border hit has density larger than the current rho_b and update
326  if(nd[i].data.isBorder && rho_b[ci] < nd[i].data.rho)
327  rho_b[ci] = nd[i].data.rho;
328  }
329 
330  //flag points in cluster with density < rho_b as halo points, then fill the cluster vector
331  for(unsigned int i = 0; i < nd.size(); ++i){
332  int ci = nd[i].data.clusterIndex;
333  if(ci!=-1 && nd[i].data.rho < rho_b[ci])
334  nd[i].data.isHalo = true;
335  if(nd[i].data.clusterIndex!=-1){
336  current_v[ci+cluster_offset].push_back(nd[i]);
337  if (verbosity < pINFO)
338  {
339  std::cout << "Pushing hit " << i << " into cluster with index " << ci+cluster_offset << std::endl;
340  std::cout << "Size now " << current_v[ci+cluster_offset].size() << std::endl;
341  }
342  }
343  }
344 
345  //prepare the offset for the next layer if there is one
346  if (verbosity < pINFO)
347  {
348  std::cout << "moving cluster offset by " << clusterIndex << std::endl;
349  }
350  cluster_offset += clusterIndex;
351  return clusterIndex;
352 }
353 
354 // find local maxima within delta_c, marking the indices in the cluster
355 std::vector<unsigned> HGCalImagingAlgo::findLocalMaximaInCluster(const std::vector<KDNode>& cluster) {
356  std::vector<unsigned> result;
357  std::vector<bool> seed(cluster.size(),true);
358 
359  for( unsigned i = 0; i < cluster.size(); ++i ) {
360  for( unsigned j = 0; j < cluster.size(); ++j ) {
361  if( distance(cluster[i].data,cluster[j].data) < delta_c && i != j) {
362  //std::cout << "hit-to-hit distance = " << distance(cluster[i],cluster[j]) << std::endl;
363  if( cluster[i].data.weight < cluster[j].data.weight ) {
364  seed[i] = false;
365  break;
366  }
367  }
368  }
369  }
370 
371  for( unsigned i = 0 ; i < cluster.size(); ++i ) {
372  if( seed[i] && cluster[i].data.weight > 5e-4) {
373  //std::cout << "seed at " << i << " with energy " << cluster[i].weight << std::endl;
374  result.push_back(i);
375  }
376  }
377 
378  //std::cout << "Found " << result.size() << " sub-clusters in input cluster of length: " << cluster.size() << std::endl;
379 
380  return result;
381 }
382 
384  const std::vector<double>& fractions) {
385  double norm(0.0), x(0.0), y(0.0), z(0.0);
386  for( unsigned i = 0; i < hits.size(); ++i ) {
387  const double weight = fractions[i]*hits[i].data.weight;
388  norm += weight;
389  x += weight*hits[i].data.x;
390  y += weight*hits[i].data.y;
391  z += weight*hits[i].data.z;
392  }
394  double norm_inv = 1.0/norm;
395  result *= norm_inv;
396  return result;
397 }
398 
399 double HGCalImagingAlgo::calculateEnergyWithFraction(const std::vector<KDNode>& hits,
400  const std::vector<double>& fractions) {
401  double result = 0.0;
402  for( unsigned i = 0 ; i < hits.size(); ++i ) {
403  result += fractions[i]*hits[i].data.weight;
404  }
405  return result;
406 }
407 
408 void HGCalImagingAlgo::shareEnergy(const std::vector<KDNode>& incluster,
409  const std::vector<unsigned>& seeds,
410  std::vector<std::vector<double> >& outclusters) {
411  std::vector<bool> isaseed(incluster.size(),false);
412  outclusters.clear();
413  outclusters.resize(seeds.size());
414  std::vector<Point> centroids(seeds.size());
415  std::vector<double> energies(seeds.size());
416 
417  if( seeds.size() == 1 ) { // short circuit the case of a lone cluster
418  outclusters[0].clear();
419  outclusters[0].resize(incluster.size(),1.0);
420  return;
421  }
422 
423  //std::cout << "saving seeds" << std::endl;
424 
425  // create quick seed lookup
426  for( unsigned i = 0; i < seeds.size(); ++i ) {
427  isaseed[seeds[i]] = true;
428  }
429 
430  //std::cout << "seeds saved" << std::endl;
431 
432  // initialize clusters to be shared
433  // centroids start off at seed positions
434  // seeds always have fraction 1.0, to stabilize fit
435  //std::cout << "initializing fit" << std::endl;
436  for( unsigned i = 0; i < seeds.size(); ++i ) {
437  outclusters[i].resize(incluster.size(),0.0);
438  for( unsigned j = 0; j < incluster.size(); ++j ) {
439  if( j == seeds[i] ) {
440  outclusters[i][j] = 1.0;
441  centroids[i] = math::XYZPoint(incluster[j].data.x,incluster[j].data.y,incluster[j].data.z);
442  energies[i] = incluster[j].data.weight;
443  }
444  }
445  }
446 
447  //return;
448 
449  //std::cout << "fit initialized" << std::endl;
450 
451  // run the fit while we are less than max iterations, and clusters are still moving
452  const double minFracTot = 1e-20;
453  unsigned iter = 0;
454  const unsigned iterMax = 50;
456  const double stoppingTolerance = 1e-8;
457  const double toleranceScaling = std::pow(std::max(1.0,seeds.size()-1.0),2.0);
458  std::vector<Point> prevCentroids;
459  std::vector<double> frac(seeds.size()), dist2(seeds.size());
460  while( iter++ < iterMax && diff > stoppingTolerance*toleranceScaling ) {
461  for( unsigned i = 0; i < incluster.size(); ++i ) {
462  const Hexel& ihit = incluster[i].data;
463  double fraction(0.0), fracTot(0.0), d2(0.0);
464  for( unsigned j = 0; j < seeds.size(); ++j ) {
465  fraction = 0.0;
466  d2 = ( std::pow(ihit.x - centroids[j].x(),2.0) +
467  std::pow(ihit.y - centroids[j].y(),2.0) +
468  std::pow(ihit.z - centroids[j].z(),2.0) )/sigma2;
469  dist2[j] = d2;
470  // now we set the fractions up based on hit type
471  if( i == seeds[j] ) { // this cluster's seed
472  fraction = 1.0;
473  } else if( isaseed[i] ) {
474  fraction = 0.0;
475  } else {
476  fraction = energies[j]*std::exp( -0.5*d2 );
477  }
478  fracTot += fraction;
479  frac[j] = fraction;
480  }
481  // now that we have calculated all fractions for all hits
482  // assign the new fractions
483  for( unsigned j = 0; j < seeds.size(); ++j ) {
484  if( fracTot > minFracTot ||
485  ( i == seeds[j] && fracTot > 0.0 ) ) {
486  outclusters[j][i] = frac[j]/fracTot;
487  } else {
488  outclusters[j][i] = 0.0;
489  }
490  }
491  }
492 
493  // save previous centroids
494  prevCentroids = std::move(centroids);
495  // finally update the position of the centroids from the last iteration
496  centroids.resize(seeds.size());
497  double diff2 = 0.0;
498  for( unsigned i = 0; i < seeds.size(); ++i ) {
499  centroids[i] = calculatePositionWithFraction(incluster,outclusters[i]);
500  energies[i] = calculateEnergyWithFraction(incluster,outclusters[i]);
501  // calculate convergence parameters
502  const double delta2 = (prevCentroids[i]-centroids[i]).perp2();
503  if( delta2 > diff2 ) diff2 = delta2;
504  }
505  //update convergance parameter outside loop
506  diff = std::sqrt(diff2);
507  //std::cout << " iteration = " << iter << " diff = " << diff << std::endl;
508  }
509 }
size
Write out results.
double calculateDistanceToHigher(std::vector< KDNode > &, KDTree &)
bool isHalfCell(const DetId &) const
Definition: RecHitTools.cc:158
double calculateEnergyWithFraction(const std::vector< KDNode > &, const std::vector< double > &)
hgcal::RecHitTools rhtools_
std::vector< std::vector< KDNode > > current_v
math::XYZPoint calculatePositionWithFraction(const std::vector< KDNode > &, const std::vector< double > &)
double distance(const Hexel &pt1, const Hexel &pt2)
reco::CaloCluster::AlgoId algoId
static const unsigned int maxlayer
const DetId & detid() const
Definition: CaloRecHit.h:21
double calculateLocalDensity(std::vector< KDNode > &, KDTree &)
void build(std::vector< KDTreeNodeInfoT< DATA, DIM > > &eltList, const KDTreeBoxT< DIM > &region)
T y() const
Definition: PV3DBase.h:63
math::XYZPoint Point
point in the space
Definition: weight.py:1
void search(const KDTreeBoxT< DIM > &searchBox, std::vector< KDTreeNodeInfoT< DATA, DIM > > &resRecHitList)
void makeClusters(const HGCRecHitCollection &hits)
std::vector< size_t > sort_by_delta(const std::vector< KDNode > &v)
void shareEnergy(const std::vector< KDNode > &, const std::vector< unsigned > &, std::vector< std::vector< double > > &)
float energy() const
Definition: CaloRecHit.h:17
T sqrt(T t)
Definition: SSEVec.h:18
unsigned int cluster_offset
std::vector< std::vector< Hexel > > points
VerbosityLevel verbosity
T min(T a, T b)
Definition: MathUtil.h:58
std::vector< size_t > sorted_indices(const std::vector< T > &v)
std::vector< reco::BasicCluster > getClusters(bool)
std::vector< unsigned > findLocalMaximaInCluster(const std::vector< KDNode > &)
Definition: DetId.h:18
unsigned int getLayerWithOffset(const DetId &) const
Definition: RecHitTools.cc:126
XYZPointD XYZPoint
point in space with cartesian internal representation
Definition: Point3D.h:12
T perp2() const
Squared magnitude of transverse component.
GlobalPoint getPosition(const DetId &id) const
Definition: RecHitTools.cc:73
math::XYZPoint calculatePosition(std::vector< KDNode > &)
std::vector< std::vector< double > > tmp
Definition: MVATrainer.cc:100
#define begin
Definition: vmac.h:30
char data[epos_bytes_allocation]
Definition: EPOS_Wrapper.h:82
size_type size() const
double distance2(const Hexel &pt1, const Hexel &pt2)
static int position[264][3]
Definition: ReadPGInfo.cc:509
int findAndAssignClusters(std::vector< KDNode > &, KDTree &, double, KDTreeBox &)
T x() const
Definition: PV3DBase.h:62
Power< A, B >::type pow(const A &a, const B &b)
Definition: Power.h:40
def move(src, dest)
Definition: eostools.py:510
std::vector< reco::BasicCluster > clusters_v