StRoot  1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
StFmsClusterFinder.cxx
Go to the documentation of this file.
1 // $Id: StFmsClusterFinder.cxx,v 1.9 2018/03/02 20:27:29 akio Exp $
2 //
3 // $Log: StFmsClusterFinder.cxx,v $
4 // Revision 1.9 2018/03/02 20:27:29 akio
5 // Big update from Zhanwen Zhu with new shower shape and six z slices
6 //
7 // Revision 1.8 2018/01/04 17:35:44 smirnovd
8 // [Cosmetic] Remove StRoot/ from include path
9 //
10 // $STAR/StRoot is already in the default path search
11 //
12 // Revision 1.7 2016/06/08 19:58:33 akio
13 // Applying Coverity report
14 //
15 // Revision 1.6 2016/06/07 15:51:44 akio
16 // Making code better based on Coverity reports
17 //
18 // Revision 1.5 2015/11/02 22:44:49 akio
19 // Fix photonEnergyInTower()
20 //
21 // Revision 1.4 2015/10/30 21:33:55 akio
22 // fix parameter initialization
23 // adding new cluster categorization method
24 //
25 // Revision 1.3 2015/10/29 21:14:55 akio
26 // increase max number of clusters
27 // a bug fixes in valley tower association
28 // removing some debug comments
29 //
30 // Revision 1.2 2015/10/21 15:58:04 akio
31 // Code speed up (~x2) by optimizing minimization fuctions and showershape function
32 // Add option to merge small cells to large, so that it finds cluster at border
33 // Add option to perform 1photon fit when 2photon fit faield
34 // Add option to turn on/off global refit
35 // Moment analysis done without ECUTOFF when no tower in cluster exceed ECUTOFF=0.5GeV
36 //
37 // Revision 1.1 2015/03/10 14:38:54 jeromel
38 // First version of FmsUtil from Yuxi Pan - reviewd 2015/02
39 //
50 #include "StFmsClusterFinder.h"
51 #include "StFmsDbMaker/StFmsDbMaker.h"
52 #include "StFmsDbConfig.h"
53 
54 #include <algorithm>
55 #include <cmath>
56 #include <functional>
57 #include <memory> // For unique_ptr
58 
59 #include "TObjArray.h"
60 
61 #include "St_base/StMessMgr.h"
62 #include "StEvent/StFmsCluster.h"
63 #include "StEvent/StFmsHit.h"
64 
65 #include "StFmsUtil/StFmsTower.h"
66 #include "StFmsUtil/StFmsConstant.h"
68 
69 namespace {
70 typedef FMSCluster::StFmsClusterFinder::TowerList TowerList;
71 typedef TowerList::const_reverse_iterator TowerConstRIter;
72 
74 
75 /*
76  Test if a tower could be a cluster peak compared to another tower.
77 
78  Note returning true does not mean the tower *is* a peak, merely that it *can*
79  be (i.e. it is consistent with that hypothesis given this input).
80  */
81 bool couldBePeakTower(const StFmsTower* tower, const StFmsTower* other) {
82  return (tower->hit()->energy() >= PEAK_TOWER_FACTOR * other->hit()->energy()) ? true : false;
83 }
84 
85 /*
86  Test if a tower could be a peak compared to a group of neighbor towers.
87 
88  Returns true if the towers passes the peak test with all neighbors, false if
89  it fails the test with any.
90  */
91 bool couldBePeakTower(const StFmsTower* tower, TowerList* others) {
92  for (auto i = others->begin(); i != others->end(); ++i) {
93  if (tower->isNeighbor(**i) && !couldBePeakTower(tower, *i)) {
94  return false;
95  } // if
96  } // for
97  return true;
98 }
99 
101 bool ascendingTowerEnergySorter(const StFmsTower* a, const StFmsTower* b) {
102  return a->hit()->energy() < b->hit()->energy();
103 }
104 
106 bool descendingTowerEnergySorter(const StFmsTower* a, const StFmsTower* b) {
107  return a->hit()->energy() > b->hit()->energy();
108 }
109 
110 /* Predicate testing for tower energy above the global cutoff */
111 bool towerEnergyIsAboveThreshold(const StFmsTower* tower) {
112  return !(tower->hit()->energy() < TOWER_E_THRESHOLD);
113 }
114 
123 bool towerIsNeighbor(const StFmsTower* test, const StFmsTower* reference) {
124  if (towerEnergyIsAboveThreshold(test)) {
125  return test->isNeighbor(*reference);
126  } // if
127  return false;
128 }
129 
130 /*
131  Filter out towers below the minimum energy threshold from a list.
132 
133  Returns a pointer list of below-threshold towers and erases those towers from
134  the input list. The order of towers after filtering is not guaranteed.
135  */
136 TowerList filterTowersBelowEnergyThreshold(TowerList* towers) {
137  // Move towers above threshold to the front and those below to the end
138  // newEnd marks the end of the above-threshold towers and the beginning of the
139  // below-threshold towers
140  auto newEnd = std::partition(towers->begin(), towers->end(),
141  std::ptr_fun(&towerEnergyIsAboveThreshold));
142  // Store the below-threshold towers in a new list
143  TowerList belowThreshold(newEnd, towers->end());
144  // Remove the below-threshold towers from the input list
145  towers->erase(newEnd, towers->end());
146  return belowThreshold;
147 }
148 
149 /* There are different ways of calculating a tower-to-cluster distance */
150 enum ETowerClusterDistance {
151  kPeakTower, // Distance from tower to peak tower in cluster
152  kClusterCenter // Distance from tower to calculated center of cluster
153 };
154 
156 void sortTowersEnergyDescending(FMSCluster::ClusterList* clusters) {
157  for (auto i = clusters->begin(); i != clusters->end(); ++i) {
158  (*i)->towers().sort(std::ptr_fun(&descendingTowerEnergySorter));
159  } // for
160 }
161 } // unnamed namespace
162 
163 namespace FMSCluster {
175 class TowerClusterAssociation : public TObject {
176  public:
182  explicit TowerClusterAssociation(StFmsTower* tower, int detectorId) : mTower(tower), mDetectorId(detectorId){ }
184  StFmsTower* tower() { return mTower; }
186  const StFmsTower* tower() const { return mTower; }
188  std::list<StFmsTowerCluster*>* clusters() { return &mClusters; }
196  double separation(const StFmsTower* tower) {
197  int det0=mTower->hit()->detectorId();
198  int det1=tower->hit()->detectorId();
199  //within the same detector
200  if(det0==det1){
201  return sqrt(pow(tower->column() - mTower->column(), 2.) +
202  pow(tower->row() - mTower->row(), 2.));
203  }
204  //different detector (large and small) - make small cell fit to large
205  int rowL,colL,rowS,colS;
206  if(det0<det1){
207  rowL=mTower->row(); rowS=tower->row();
208  colL=mTower->column(); colS=tower->column();
209  }else{
210  rowS=mTower->row(); rowL=tower->row();
211  colS=mTower->column(); colL=tower->column();
212  }
213  float rL=rowL-0.5;
214  float cL=colL-0.5;
215  float rS=(rowS-0.5)/1.5 + 9.0;
216  float cS=(colS-0.5)/1.5;
217  return sqrt(pow(rL-rS,2.0)+pow(cL-cS,2.0));
218  }
229  double separation(const StFmsTowerCluster* cluster,
230  const ETowerClusterDistance distance) {
231  if (kPeakTower == distance) {
232  return separation(cluster->towers().front());
233  } else {
234  // Use calculated cluster center (x0, y0).
235  // Subtract 0.5 from tower (column, row) to give tower center.
236  if(mTower->hit()->detectorId() == mDetectorId){ //within the same detector
237  return sqrt(pow(cluster->cluster()->x() - (mTower->column() - 0.5), 2.) +
238  pow(cluster->cluster()->y() - (mTower->row() - 0.5), 2.));
239  }else{ //different detector (large cell cluster and small cell tower) - make small cell fit to large
240  float col=(mTower->column()-0.5)/1.5;
241  float row=(mTower->row()-0.5)/1.5+9.0;
242  return sqrt(pow(cluster->cluster()->x() - col,2.0) +
243  pow(cluster->cluster()->y() - row,2.0));
244  } // if
245  }
246  }
260  bool canAssociate(const StFmsTowerCluster* cluster) {
261  const StFmsTowerCluster::Towers& towers = cluster->towers();
262  // The peak tower in a cluster is always the first
263  const StFmsTower* peak = towers.front();
264  // Make sure that this tower has lower energy than the peak, but be careful;
265  // because of digitization, it is possible that the "neighbor" tower
266  // has the exact same energy as the peak tower, not just less
267  if (peak->hit()->energy() < mTower->hit()->energy()) {
268  return false;
269  } // if
270  // Loop over all towers in this cluster to see if this tower is
271  // physically adjacent to any of them.
272  for (auto tower = towers.begin(); tower != towers.end(); ++tower) {
273  if (mTower->isNeighbor(**tower) && !couldBePeakTower(mTower, *tower)) {
274  return true; // Stop looping once we find any match
275  } // if
276  } // for loop over all towers in a cluster
277  return false;
278  }
298  void add(StFmsTowerCluster* cluster, const ETowerClusterDistance distance) {
299  if (canAssociate(cluster)) {
300  if (mClusters.empty()) {
301  associate(cluster);
302  } else {
303  // Cluster(s) are already present, so only add the new one if it is
304  // not further away. If it is closer, remove the existing cluster.
305  double distNew = separation(cluster, distance);
306  double distOld = separation(mClusters.front(), distance);
307  if (distNew < distOld) {
308  mClusters.clear();
309  } // if
310  // Add the new cluster if it is not further away than existing ones
311  if (!(distNew > distOld)) {
312  associate(cluster);
313  } // if
314  } // if
315  } // if
316  }
318  void associate(StFmsTowerCluster* cluster) {
319  mClusters.push_back(cluster);
320  mTower->setCluster(cluster->index());
321  }
332  StFmsTowerCluster* nearest = nullptr;
333  double minDist = 99999.;
334  for (auto i = mClusters.begin(); i != mClusters.end(); ++i) {
335  double distance = separation(*i, kClusterCenter);
336  // Check if the distance to the "center" of this cluster is smaller
337  if (distance < minDist) {
338  minDist = distance;
339  nearest = *i;
340  } // if
341  } // for
342  return nearest;
343  }
344 
345  private:
346  StFmsTower* mTower;
347  int mDetectorId;
348  std::list<StFmsTowerCluster*> mClusters;
349 };
350 
352  : mEnergyCutoff(energyCutoff), mNClusts(0), mDetectorId(0) { }
353 
355 
357  StFmsTowerCluster* cluster) const {
358  cluster->calculateClusterMoments(mEnergyCutoff);
359  cluster->cluster()->setNTowers(cluster->towers().size());
360 }
361 
363  StFmsCluster* cluster = towerCluster->cluster();
364  if (cluster->nTowers() < CAT_NTOWERS_PH1) {
365  cluster->setCategory(k1PhotonCluster);
366  } else { // Categorise cluster based on empirical criteria
367  const double sigmaMaxE = cluster->sigmaMax() * cluster->energy();
368  if (cluster->energy() < CAT_EP1_PH2 * (sigmaMaxE - CAT_EP0_PH2)) {
369  if (sigmaMaxE > CAT_SIGMAMAX_MIN_PH2) {
370  cluster->setCategory(k2PhotonCluster);
371  } else {
372  cluster->setCategory(kAmbiguousCluster);
373  } // if
374  } else if (cluster->energy() > CAT_EP1_PH1 * (sigmaMaxE - CAT_EP0_PH1)) {
375  if (sigmaMaxE < CAT_SIGMAMAX_MAX_PH1) {
376  cluster->setCategory(k1PhotonCluster);
377  } else {
378  cluster->setCategory(kAmbiguousCluster);
379  } // if
380  } else {
381  cluster->setCategory(kAmbiguousCluster);
382  } // if
383  } // if
384  return cluster->category();
385 }
386 
387 int StFmsClusterFinder::categorise2(StFmsTowerCluster* towerCluster) {
388  StFmsCluster* cluster = towerCluster->cluster();
389  if (cluster->nTowers() < CAT_NTOWERS_PH1) {
390  cluster->setCategory(k1PhotonCluster);
391  }else{ // Categorise cluster based on empirical criteria
392  //int det=towerCluster->towers().front()->hit()->detectorId();//detectorId for top cell
393  const double sigma=cluster->sigmaMax();
394  const double e =cluster->energy();
395  if(sigma > 1/2.5 + 0.003*e + 7.0/e){
396  cluster->setCategory(k2PhotonCluster);
397  }else if(sigma < 1/2.1 -0.001*e + 2.0/e){
398  cluster->setCategory(k1PhotonCluster);
399  }else{
400  cluster->setCategory(kAmbiguousCluster);
401  }
402  //LOG_INFO << Form("Det=%2d e=%6.2f sigma=%6.3f cat=%1d",det,e,sigma,cluster->category()) <<endm;
403  }
404  return cluster->category();
405 }
406 
407 int StFmsClusterFinder::findClusters(TowerList* towers, ClusterList* clusters, int detectorId) {
408  mDetectorId=detectorId; //current working detectorId
409  // Remove towers below energy threshold, but save them for later use
410  TowerList belowThreshold = filterTowersBelowEnergyThreshold(towers);
411  TowerList neighbors; // List of non-peak towers in clusters
412  locateClusterSeeds(towers, &neighbors, clusters);
413  // We have now found all seeds. Now decide the affiliation of neighbor towers
414  // i.e. which peak each neighbor is associated with in a cluster.
415  neighbors.sort(std::ptr_fun(&ascendingTowerEnergySorter));
416  // Associated neighbor towers grow outward from the seed tower.
417  // Keep trying to make tower-cluster associations until we make an entire loop
418  // through all neighbors without successfully associating anything. Then stop,
419  // otherwise we end up in an infinite loop when we can't associate all the
420  // neighbors with a cluster (which we usually can't).
421  TObjArray valleys(16); // Stores towers equidistant between seeds
422  valleys.SetOwner(true);
423  unsigned nAssociations(0);
424  do {
425  nAssociations = associateTowersWithClusters(&neighbors, clusters, &valleys);
426  } while (nAssociations > 0);
427  // Calculate the moments of clusters. We need to do this before calling
428  // TowerClusterAssociation::nearestCluster, which uses the cluster moment
429  // to determine tower-cluster separations for the valley towers.
430  for (auto i = clusters->begin(); i != clusters->end(); ++i) {
431  calculateClusterMoments(i->get());
432  } // for
433  // Ambiguous "valley" towers that were equally spaced between clusters can
434  // now be associated
435  associateValleyTowersWithClusters(&neighbors, clusters, &valleys);
436  // If there are still towers left in "neighbor", distribute them to clusters
437  do {
438  nAssociations = associateResidualTowersWithClusters(&neighbors, clusters);
439  } while (nAssociations > 0);
441  sortTowersEnergyDescending(clusters);
442  // Recalculate various moment of clusters
443  for (auto i = clusters->begin(); i != clusters->end(); ++i) {
444  calculateClusterMoments(i->get());
445  } // for
446  // Finally add "zero" energy towers to the clusters
447  associateSubThresholdTowersWithClusters(&belowThreshold, clusters);
448  return mNClusts;
449 }
450 
451 unsigned StFmsClusterFinder::locateClusterSeeds(TowerList* towers,
452  TowerList* neighbors,
453  ClusterList* clusters) const {
454  // The algorithm requires we sort towers in descending order or energy
455  towers->sort(std::ptr_fun(&descendingTowerEnergySorter));
456  while (!towers->empty() && clusters->size() < kMaxNClusters) {
457  // By design, this tower is the highest tower remaining in towers, but it
458  // could be lower than a tower in neighbors
459  StFmsTower* high = towers->front();
460  towers->pop_front();
461  // Compare this highest tower with all towers in neighbors, and if it is
462  // lower than any of those, make it a neighbor. Otherwise, it is a
463  // peak (seed) tower so add it to a new cluster.
464  if (couldBePeakTower(high, neighbors)) {
465  // Add "high" to cluster and move towers neighboring "high" to "neighbor"
466  high->setCluster(clusters->size());
467  typedef FMSCluster::ClusterList::value_type ClusterPtr;
468  clusters->push_back(ClusterPtr(new StFmsTowerCluster(new StFmsCluster, mDetectorId)));
469  clusters->back()->setIndex(high->cluster());
470  clusters->back()->towers().push_back(high);
471  // Add neighbors of the new peak tower to the neighbor list.
472  // Partition the remaining towers so that neighbours of the high tower are
473  // placed at the beginning, and non-neighbours placed at the end. Use
474  // stable_partition so we don't alter the energy ordering.
475  auto neighborEnd =
476  std::stable_partition(towers->begin(), towers->end(),
477  std::bind2nd(std::ptr_fun(&towerIsNeighbor),
478  high));
479  // Copy neighbors to the neighbor list, erase them from the tower list
480  neighbors->insert(neighbors->end(), towers->begin(), neighborEnd);
481  towers->erase(towers->begin(), neighborEnd);
482  } else { // Not a peak, add it to the neighbor collection
483  neighbors->push_back(high);
484  } // when "high" is a "peak"
485  // A tower separated from neighbors only by towers of the same energy will
486  // become a peak by the above logic. To close this loophole, loop again
487  // over towers and move any with energy <= any of its neighbors to the
488  // neighbor list.
489  auto towerIter = towers->begin();
490  while (towerIter != towers->end()) {
491  // Need to remove list items whilst iterating, so be careful to increment
492  // the iterator before erasing items to avoid iterator invalidation
493  if (!couldBePeakTower(*towerIter, neighbors)) {
494  neighbors->push_back(*towerIter);
495  towers->erase(towerIter++); // Increment will evaluate before erase()
496  } else {
497  ++towerIter;
498  } // if
499  } // while
500  } // End of for loop over "arrTow"
501  return clusters->size();
502 }
503 
504 unsigned StFmsClusterFinder::associateTowersWithClusters(
505  TowerList* neighbors,
506  ClusterList* clusters,
507  TObjArray* valleys) const {
508  TowerList associated; // Store neighbors we associate
509  // Towers are sorted in ascending energy, so use reverse iterator to go from
510  // highest to lowest energy
511  TowerConstRIter tower;
512  for (tower = neighbors->rbegin(); tower != neighbors->rend(); ++tower) {
513  // Populate association information of this tower with each cluster
514  std::unique_ptr<TowerClusterAssociation> association(new TowerClusterAssociation(*tower,mDetectorId));
515  for (auto i = clusters->begin(); i != clusters->end(); ++i) {
516  association->add(i->get(), kPeakTower);
517  } // for
518  // Attempt to move the tower to the appropriate cluster
519  if (association->clusters()->size() == 1) {
520  // Only one peak is closest to the tower; the tower belongs to this peak
521  association->clusters()->front()->towers().push_back(*tower);
522  associated.push_back(*tower);
523  } else if (association->clusters()->size() > 1) {
524  // Multiple potential clusters, need to do something more sophisticated
525  // Add this association to the "valley" array so we can use it later
526  valleys->Add(association.release());
527  associated.push_back(*tower);
528  } // if
529  } // loop over TObjArray "neighbor"
530  // Remove associated neighbors from the neighbor list.
531  for (auto i = associated.begin(); i != associated.end(); ++i) {
532  neighbors->remove(*i);
533  } // for
534  return associated.size();
535 }
536 
537 unsigned StFmsClusterFinder::associateValleyTowersWithClusters(
538  TowerList* neighbors,
539  ClusterList* clusters,
540  TObjArray* valleys) const {
541  unsigned size = neighbors->size();
542  for (Int_t i(0); i < valleys->GetEntriesFast(); ++i) {
543  TowerClusterAssociation* association = static_cast<TowerClusterAssociation*>(valleys->At(i));
544  StFmsTowerCluster* cluster = association->nearestCluster();
545  if (cluster) {
546  // Move the tower to the appropriate cluster
547  association->tower()->setCluster(cluster->index());
548  cluster->towers().push_back(association->tower());
549  } else {
550  LOG_INFO << "Something is wrong! The following \"Valley\" tower does "
551  << "not belong to any cluster! Error!" << endm;
552  association->tower()->Print();
553  } // if (cluster)
554  } // end of for loop over valley towers
555  return size - neighbors->size();
556 }
557 
558 unsigned StFmsClusterFinder::associateResidualTowersWithClusters(
559  TowerList* neighbors,
560  ClusterList* clusters) const {
561  TowerList associated;
562  TowerConstRIter tower;
563  for (tower = neighbors->rbegin(); tower != neighbors->rend(); ++tower) {
564  // Populate tower-cluster association information
565  TowerClusterAssociation association(*tower,mDetectorId);
566  for (auto i = clusters->begin(); i != clusters->end(); ++i) {
567  // There are already some towers in the cluster so we can use a computed
568  // cluster center to give a better estimate of tower-cluster separation
569  calculateClusterMoments(i->get());
570  association.add(i->get(), kClusterCenter);
571  } // loop over all clusters
572  if (!association.clusters()->empty()) {
573  StFmsTowerCluster* cluster = association.clusters()->front();
574  (*tower)->setCluster(cluster->index());
575  cluster->towers().push_back(*tower);
576  associated.push_back(*tower);
577  } // if
578  } // loop over TObjArray "neighbor"
579  for (auto i = associated.begin(); i != associated.end(); ++i) {
580  neighbors->remove(*i);
581  } // for
582  return associated.size();
583 }
584 
585 void StFmsClusterFinder::associateSubThresholdTowersWithClusters(
586  TowerList* towers,
587  ClusterList* clusters) const {
588  for (auto tower = towers->begin(); tower != towers->end(); ++tower) {
589  TowerClusterAssociation association(*tower,mDetectorId);
590  // loop over all clusters
591  for (auto i = clusters->begin(); i != clusters->end(); ++i) {
592  association.add(i->get(), kPeakTower);
593  } // for
594  StFmsTowerCluster* cluster = association.nearestCluster();
595  if (cluster && association.separation(cluster, kClusterCenter) < 0.3) {
596  (*tower)->setCluster(cluster->index());
597  cluster->towers().push_back(*tower);
598  } // if
599  } // for
600 }
601 } // namespace FMSCluster
StFmsClusterFinder(double energyCutoff=0.5)
Bool_t isNeighbor(const StFmsTower &tower) const
Definition: StFmsTower.cxx:53
Could be 1- or 2-photon, needs to be fitted.
A cluster created by 2 photons.
const StFmsHit * hit() const
Definition: StFmsTower.h:90
int categorise(StFmsTowerCluster *cluster)
Int_t column() const
Definition: StFmsTower.h:92
Declaration of StFmsTower, a simple FMS tower wrapper.
std::list< StFmsTower * > Towers
Shorthand for tower collection.
void calculateClusterMoments(StFmsTowerCluster *cluster) const
double separation(const StFmsTowerCluster *cluster, const ETowerClusterDistance distance)
Declaration of StFmsTowerCluster, a cluster of FMS towers.
int findClusters(TowerList *towers, ClusterList *clusters, int detetorId)
double separation(const StFmsTower *tower)
TowerClusterAssociation(StFmsTower *tower, int detectorId)
void calculateClusterMoments(Double_t energyCutoff)
bool canAssociate(const StFmsTowerCluster *cluster)
Int_t row() const
Definition: StFmsTower.h:94
void setCluster(Int_t cluster)
Definition: StFmsTower.h:98
A cluster created by 1 photon.
std::list< StFmsTowerCluster * > * clusters()
Int_t cluster() const
Definition: StFmsTower.h:96
void add(StFmsTowerCluster *cluster, const ETowerClusterDistance distance)
void associate(StFmsTowerCluster *cluster)
Declaration of StFmsClusterFinder, an FMS tower clustering algorithm.