StFms  0.0.0
FMS software in the STAR framework
StFmsClusterFinder.cxx
Go to the documentation of this file.
1 // $Id$
2 //
3 // $Log$
15 
16 #include <algorithm>
17 #include <cmath>
18 #include <functional>
19 #include <memory> // For unique_ptr
20 
21 #include "TObjArray.h"
22 
23 #include "St_base/StMessMgr.h"
24 #include "StEvent/StFmsCluster.h"
25 #include "StEvent/StFmsHit.h"
26 
29 
30 namespace {
31 /*
32  Cluster-finding constants used within this file.
33  Some are only used once, but it is easier to keep track of their values, and
34  more self-documenting, if they are all named, and set in the same location.
35  */
36 const Float_t maxDistanceFromPeak = 0.3;
37 const Int_t minTowerCatag02 = 5;
38 const Float_t cutEcSigma[2][2] = {{2.1, 7.0}, {2.1, 2.0}};
39 const Float_t minEcSigma2Ph = 35.;
40 const Float_t maxEcSigma1Ph = 10.;
41 const Float_t minTowerEnergy = 0.01;
42 const Float_t minRatioPeakTower = 1.6;
43 // Extreme distance between towers (no distance can be this large!)
44 const Float_t ExtremelyFaraway = 99999;
45 
47 typedef TowerList::iterator TowerIter;
48 typedef TowerList::const_reverse_iterator TowerConstRIter;
49 typedef FMSCluster::ClusterList::iterator ClusterIter;
50 typedef FMSCluster::ClusterList::value_type ClusterPtr;
51 
53 
54 /*
55  Test for a tower that can be a cluster peak.
56 
57  Returns true if a tower can be a peak tower, given a global population of
58  known non-peak towers and a minimum ratio between the energy of peak towers and
59  non-peak towers. Returns false if the tower can not possibly be a peak. Note
60  that returning true does not mean the tower *is* a peak, merely that it *can*
61  be (i.e. it is consistent with that hypothesis given this input).
62  */
63 Bool_t couldBePeakTower(const StFmsTower* tower, TowerList* nonPeakTowers) {
64  Bool_t couldBePeak(true);
65  for (TowerIter i = nonPeakTowers->begin(); i != nonPeakTowers->end(); ++i) {
66  // Compare this tower's energy with that of its immediate neighbours
67  if (tower->isNeighbor(**i)) {
68  if (tower->hit()->energy() < minRatioPeakTower * (*i)->hit()->energy()) {
69  couldBePeak = false;
70  break;
71  } // if
72  } // if
73  } // end of for loop over non-peak towers
74  return couldBePeak;
75 }
76 
78 bool ascendingTowerEnergySorter(const StFmsTower* a, const StFmsTower* b) {
79  return a->hit()->energy() < b->hit()->energy();
80 }
81 
83 bool descendingTowerEnergySorter(const StFmsTower* a, const StFmsTower* b) {
84  return a->hit()->energy() > b->hit()->energy();
85 }
86 
87 /* Predicate testing for tower energy above the global cutoff */
88 bool towerEnergyIsAboveThreshold(const StFmsTower* tower) {
89  return !(tower->hit()->energy() < minTowerEnergy);
90 }
91 
100 bool towerIsNeighbor(const StFmsTower* test, const StFmsTower* reference) {
101  if (towerEnergyIsAboveThreshold(test)) {
102  return test->isNeighbor(*reference);
103  } // if
104  return false;
105 }
106 
107 /*
108  Filter out towers below the minimum energy threshold from a list.
109 
110  Returns a pointer list of below-threshold towers and erases those towers from
111  the input list. The order of towers after filtering is not guaranteed.
112  */
113 TowerList filterTowersBelowEnergyThreshold(TowerList* towers) {
114  // Move towers above threshold to the front and those below to the end
115  // newEnd marks the end of the above-threshold towers and the beginning of the
116  // below-threshold towers
117  TowerIter newEnd = std::partition(towers->begin(), towers->end(),
118  std::ptr_fun(&towerEnergyIsAboveThreshold));
119  // Store the below-threshold towers in a new list
120  TowerList belowThreshold(newEnd, towers->end());
121  // Remove the below-threshold towers from the input list
122  towers->erase(newEnd, towers->end());
123  return belowThreshold;
124 }
125 
126 /* There are different ways of calculating a tower-to-cluster distance */
128  kPeakTower, // Distance from tower to peak tower in cluster
129  kClusterCenter // Distance from tower to calculated center of cluster
130 };
131 
138 void sortTowersEnergyDescending(FMSCluster::ClusterList* clusters,
139  int nClusters) {
140  for (ClusterIter i = clusters->begin(); i != clusters->end(); ++i) {
141  (*i)->towers().sort(std::ptr_fun(&descendingTowerEnergySorter));
142  } // for
143 }
144 } // unnamed namespace
145 
146 namespace FMSCluster {
158 class TowerClusterAssociation : public TObject {
159  public:
165  explicit TowerClusterAssociation(StFmsTower* tower) : mTower(tower) { }
167  StFmsTower* tower() { return mTower; }
169  const StFmsTower* tower() const { return mTower; }
171  std::list<StFmsTowerCluster*>* clusters() { return &mClusters; }
179  double separation(const StFmsTower* tower) {
180  return sqrt(pow(tower->column() - mTower->column(), 2.) +
181  pow(tower->row() - mTower->row(), 2.));
182  }
193  double separation(const StFmsTowerCluster* cluster,
194  const ETowerClusterDistance distance) {
195  if (kPeakTower == distance) {
196  const StFmsTower* peak = cluster->towers().front();
197  return separation(peak);
198  } else {
199  // Use calculated cluster center (x0, y0).
200  // Subtract 0.5 from tower (column, row) to give tower center.
201  return sqrt(pow(cluster->cluster()->x() - (mTower->column() - 0.5), 2.) +
202  pow(cluster->cluster()->y() - (mTower->row() - 0.5), 2.));
203  } // if
204  }
219  bool canAssociate(const StFmsTowerCluster* cluster) {
220  const StFmsTowerCluster::Towers& towers = cluster->towers();
221  // The peak tower in a cluster is always the first
222  const StFmsTower* peak = towers.front();
223  // Make sure that this tower has lower energy than the peak, but be careful;
224  // because of digitization, it is possible that the "neighbor" tower
225  // has the exact same energy as the peak tower, not just less
226  if (peak->hit()->energy() < mTower->hit()->energy()) {
227  return false;
228  } // if
229  // Loop over all towers in this cluster to see if this tower is
230  // physically adjacent to any of them.
231  typedef StFmsTowerCluster::Towers::const_iterator TowerIter;
232  for (TowerIter tower = towers.begin(); tower != towers.end(); ++tower) {
233  // Place an energy selection when determining adjacent towers, as a
234  // neighbor cannot exceed an adjacent tower by a factor more than
235  // minRatioPeakTower, otherwise it will be considered a peak itself.
236  if (mTower->isNeighbor(**tower) &&
237  mTower->hit()->energy() < minRatioPeakTower *
238  (*tower)->hit()->energy()) {
239  return true; // Stop looping once we find any match
240  } // if
241  } // for loop over all towers in a cluster
242  return false;
243  }
263  bool add(StFmsTowerCluster* cluster, const ETowerClusterDistance distance) {
264  bool inserted(false);
265  if (canAssociate(cluster)) {
266  if (mClusters.empty()) {
267  // There is nothing in the list yet, add the cluster, simples!
268  mClusters.push_back(cluster);
269  mTower->setCluster(cluster->index());
270  inserted = true;
271  } else {
272  // Cluster(s) are already present, so only add the new one if it is
273  // not further away. If it is closer, remove the existing cluster.
274  double distNew = separation(cluster, distance);
275  double distOld = separation(mClusters.front(), distance);
276  // If the new cluster is closer, remove the old ones
277  if (distNew < distOld) {
278  mClusters.clear();
279  } // if
282  // Add the new cluster if it is not further away than existing ones
283  if (distNew <= distOld) {
284  mClusters.push_back(cluster);
285  mTower->setCluster(cluster->index());
286  inserted = true;
287  } // if
288  } // if
289  } // if
290  return inserted;
291  }
302  StFmsTowerCluster* nearest(NULL);
303  double minDist = ExtremelyFaraway;
304  std::list<StFmsTowerCluster*>::iterator i;
305  for (i = mClusters.begin(); i != mClusters.end(); ++i) {
306  float distance = separation(*i, kClusterCenter);
307  // Check if the distance to the "center" of this cluster is smaller
308  if (distance < minDist) {
309  minDist = distance;
310  nearest = *i;
311  } // if
312  } // for
313  return nearest;
314  }
315 
316  private:
318  std::list<StFmsTowerCluster*> mClusters;
319 };
320 
323 }
324 
326 }
327 
328 // Calculate moments of a cluster (position, sigma...)
330  StFmsTowerCluster* cluster) const {
331  if (cluster) {
333  cluster->cluster()->setNTowers(cluster->towers().size());
334  } // if
335 }
336 
337 // Categorise a cluster
339  // If the number of towers in a cluster is less than "minTowerCatag02"
340  // always consider the cluster a one-photon cluster
341  if (cluster->cluster()->nTowers() < minTowerCatag02) {
342  cluster->cluster()->setCategory(k1PhotonCluster);
343  } else {
344  // Categorise cluster based on its properties
345  Float_t sMaxEc = cluster->cluster()->sigmaMax() *
346  cluster->cluster()->energy();
347  if (cluster->cluster()->energy() < cutEcSigma[0][0] *
348  (sMaxEc - cutEcSigma[0][1])) {
349  if (sMaxEc > minEcSigma2Ph) {
350  cluster->cluster()->setCategory(k2PhotonCluster);
351  } else {
353  } // if
354  } else if (cluster->cluster()->energy() >
355  cutEcSigma[1][0] * (sMaxEc - cutEcSigma[1][1])) {
356  if (sMaxEc < maxEcSigma1Ph) {
357  cluster->cluster()->setCategory(k1PhotonCluster);
358  } else {
360  } // if
361  } else {
363  } // if (cluster->hit->energy()...)
364  } // if (cluster->numbTower...)
365  return cluster->cluster()->category();
366 }
367 
368 int StFmsClusterFinder::findClusters(TowerList* towers, ClusterList* clusters) {
369  // Remove towers below energy threshold, but save them for later use
370  TowerList belowThreshold = filterTowersBelowEnergyThreshold(towers);
371  // List of non-peak towers in clusters
372  TowerList neighbors;
373  // Locate cluster seeds
374  locateClusterSeeds(towers, &neighbors, clusters);
375  // We have now found all seeds. Now decide the affiliation of neighbor towers
376  // i.e. which peak each neighbor is associated with in a cluster.
377  // First, we need to sort the neighbors towers, because we want to
378  // consider them from higher towers to lower towers
379  neighbors.sort(std::ptr_fun(&ascendingTowerEnergySorter));
380  // Associated neighbor towers grow outward from the seed tower.
381  // Keep trying to make tower-cluster associations until we make an entire loop
382  // through all neighbors without successfully associating anything. Then stop,
383  // otherwise we end up in an infinite loop when we can't associate all the
384  // neighbors with a cluster (which we usually can't).
385  TObjArray valleys(16); // Stores towers equidistant between seeds
386  valleys.SetOwner(true);
387  unsigned nAssociations(0);
388  do {
389  nAssociations = associateTowersWithClusters(&neighbors, clusters, &valleys);
390  } while (nAssociations > 0);
391  // Calculate the moments of clusters. We need to do this before calling
392  // TowerClusterAssociation::nearestCluster, which uses the cluster moment
393  // to determine tower-cluster separations for the valley towers.
394  for (auto i = clusters->begin(); i != clusters->end(); ++i) {
395  calculateClusterMoments(i->get());
396  } // for
397  // Ambiguous "valley" towers that were equally spaced between clusters can
398  // now be associated
399  associateValleyTowersWithClusters(&neighbors, clusters, &valleys);
400  // If there are still towers left in "neighbor", distribute them to clusters
401  do {
402  nAssociations = associateResidualTowersWithClusters(&neighbors, clusters);
403  } while (nAssociations > 0);
404  sortTowersEnergyDescending(clusters, mNClusts);
405  // Recalculate various moment of clusters
406  for (ClusterIter i = clusters->begin(); i != clusters->end(); ++i) {
407  calculateClusterMoments(i->get());
408  } // for
409  // Finally add "zero" energy towers to the clusters
410  associateSubThresholdTowersWithClusters(&belowThreshold, clusters);
411  return mNClusts;
412 }
413 
414 unsigned StFmsClusterFinder::locateClusterSeeds(TowerList* towers,
415  TowerList* neighbors,
416  ClusterList* clusters) const {
417  // The algorithm requires we sort towers in descending order or energy
418  towers->sort(std::ptr_fun(&descendingTowerEnergySorter));
419  while (!towers->empty() && clusters->size() < kMaxNClusters) {
420  // By design, this tower is the highest tower remaining in towers, but it
421  // could be lower than a tower in neighbors
422  StFmsTower* high = towers->front();
423  towers->pop_front();
424  // Compare this highest tower with all towers in neighbors, and if it is
425  // lower than any of those, make it a neighbor. Otherwise, it is a
426  // peak (seed) tower so add it to a new cluster.
427  if (couldBePeakTower(high, neighbors)) {
428  // Add "high" to cluster and move towers neighboring "high" to "neighbor"
429  high->setCluster(clusters->size());
430  clusters->push_back(ClusterPtr(new StFmsTowerCluster(new StFmsCluster)));
431  clusters->back()->setIndex(high->cluster());
432  clusters->back()->towers().push_back(high);
433  // Add neighbors of the new peak tower to the neighbor list.
434  // Partition the remaining towers so that neighbours of the high tower are
435  // placed at the beginning, and non-neighbours placed at the end. Use
436  // stable_partition so we don't alter the energy ordering.
437  TowerIter neighborEnd =
438  std::stable_partition(towers->begin(), towers->end(),
439  std::bind2nd(std::ptr_fun(&towerIsNeighbor),
440  high));
441  // Copy neighbors to the neighbor list, erase them from the tower list
442  neighbors->insert(neighbors->end(), towers->begin(), neighborEnd);
443  towers->erase(towers->begin(), neighborEnd);
444  } else { // Not a peak, add it to the neighbor collection
445  neighbors->push_back(high);
446  } // when "high" is a "peak"
447  // A tower separated from neighbors only by towers of the same energy will
448  // become a peak by the above logic. To close this loophole, loop again
449  // over towers and move any with energy <= any of its neighbors to the
450  // neighbor list.
451  TowerIter towerIter = towers->begin();
452  while (towerIter != towers->end()) {
453  // Need to remove list items whilst iterating, so be careful to increment
454  // the iterator before erasing items to avoid iterator invalidation
455  if (!couldBePeakTower(*towerIter, neighbors)) {
456  neighbors->push_back(*towerIter);
457  towers->erase(towerIter++); // Increment will evaluate before erase()
458  } else {
459  ++towerIter;
460  } // if
461  } // while
462  } // End of for loop over "arrTow"
463  return clusters->size();
464 }
465 
467  TowerList* neighbors,
468  ClusterList* clusters,
469  TObjArray* valleys) const {
470  TowerList associated; // Store neighbors we associate
471  // Towers are sorted in ascending energy, so use reverse iterator to go from
472  // highest to lowest energy
473  TowerConstRIter tower;
474  for (tower = neighbors->rbegin(); tower != neighbors->rend(); ++tower) {
475  // Populate association information of this tower with each cluster
476  std::unique_ptr<TowerClusterAssociation> association(
477  new TowerClusterAssociation(*tower));
478  for (ClusterIter i = clusters->begin(); i != clusters->end(); ++i) {
479  association->add(i->get(), kPeakTower);
480  } // for
481  // Attempt to move the tower to the appropriate cluster
482  if (association->clusters()->size() == 1) {
483  // Only one peak is closest to the tower; the tower belongs to this peak
484  association->clusters()->front()->towers().push_back(*tower);
485  associated.push_back(*tower);
486  } else if (association->clusters()->size() > 1) {
487  // Multiple potential clusters, need to do something more sophisticated
488  // Add this association to the "valley" array so we can use it later
489  valleys->Add(association.release());
490  associated.push_back(*tower);
491  } // if
492  } // loop over TObjArray "neighbor"
493  // Remove associated neighbors from the neighbor list
494  for (TowerIter i = associated.begin(); i != associated.end(); ++i) {
495  neighbors->remove(*i);
496  } // for
497  return associated.size();
498 }
499 
501  TowerList* neighbors,
502  ClusterList* clusters,
503  TObjArray* valleys) const {
504  unsigned size = neighbors->size();
505  for (Int_t i(0); i < valleys->GetEntriesFast(); ++i) {
506  TowerClusterAssociation* association =
507  static_cast<TowerClusterAssociation*>(valleys->At(i));
508  StFmsTowerCluster* cluster = association->nearestCluster();
509  if (cluster) {
510  // Move the tower to the appropriate cluster
511  association->tower()->setCluster(cluster->index());
512  neighbors->remove(association->tower());
513  cluster->towers().push_back(association->tower());
514  } else {
515  LOG_INFO << "Something is wrong! The following \"Valley\" tower does "
516  << "not belong to any cluster! Error!" << endm;
517  association->tower()->Print();
518  } // if (cluster)
519  } // end of for loop over valley towers
520  return size - neighbors->size();
521 }
522 
524  TowerList* neighbors,
525  ClusterList* clusters) const {
526  TowerList associated;
527  TowerConstRIter tower;
528  for (tower = neighbors->rbegin(); tower != neighbors->rend(); ++tower) {
529  // Populate tower-cluster association information
530  TowerClusterAssociation association(*tower);
531  for (ClusterIter i = clusters->begin(); i != clusters->end(); ++i) {
532  // There are already some towers in the cluster so we can use a computed
533  // cluster center to give a better estimate of tower-cluster separation
534  calculateClusterMoments(i->get());
535  association.add(i->get(), kClusterCenter);
536  } // loop over all clusters
537  if (!association.clusters()->empty()) {
538  StFmsTowerCluster* cluster = association.clusters()->front();
539  (*tower)->setCluster(cluster->index());
540  cluster->towers().push_back(*tower);
541  associated.push_back(*tower);
542  } // if
543  } // loop over TObjArray "neighbor"
544  for (TowerIter i = associated.begin(); i != associated.end(); ++i) {
545  neighbors->remove(*i);
546  } // for
547  return associated.size();
548 }
549 
551  TowerList* towers,
552  ClusterList* clusters) const {
553  TowerIter tower;
554  for (tower = towers->begin(); tower != towers->end(); ++tower) {
555  TowerClusterAssociation association(*tower);
556  // loop over all clusters
557  for (ClusterIter i = clusters->begin(); i != clusters->end(); ++i) {
558  association.add(i->get(), kPeakTower);
559  } // for
560  StFmsTowerCluster* cluster = association.nearestCluster();
561  if (cluster &&
562  association.separation(cluster, kClusterCenter) < maxDistanceFromPeak) {
563  (*tower)->setCluster(cluster->index());
564  cluster->towers().push_back(*tower);
565  } // if
566  } // for
567 }
568 } // namespace FMSCluster
ETowerClusterDistance
A cluster created by 2 photons.
Definition: StFmsCluster.h:25
double separation(const StFmsTower *tower)
Float_t mEnergyCutoff
Tower energy cutoff for cluster moments.
Float_t sigmaMax() const
Definition: StFmsCluster.h:63
unsigned associateResidualTowersWithClusters(TowerList *neighbors, ClusterList *clusters) const
Float_t x() const
Definition: StFmsCluster.h:59
bool canAssociate(const StFmsTowerCluster *cluster)
std::list< StFmsTowerCluster * > mClusters
Associable clusters.
StFmsTower * mTower
Reference FMS tower.
A cluster created by 1 photon.
Definition: StFmsCluster.h:24
unsigned locateClusterSeeds(TowerList *towers, TowerList *neighbors, ClusterList *clusters) const
Declaration of StFmsCluster, a group of adjacent FMS hits.
int categorise(StFmsTowerCluster *cluster)
void associateSubThresholdTowersWithClusters(TowerList *towers, ClusterList *clusters) const
Declaration of StFmsTower, a simple FMS tower wrapper.
double separation(const StFmsTowerCluster *cluster, const ETowerClusterDistance distance)
Int_t nTowers() const
Definition: StFmsCluster.h:53
Int_t cluster() const
Definition: StFmsTower.h:86
Int_t column() const
Definition: StFmsTower.h:82
std::list< FMSCluster::StFmsTower * > TowerList
void setMomentEnergyCutoff(float cutoff=0.5)
Declaration of StFmsClusterFinder, an FMS tower clustering algorithm.
unsigned associateTowersWithClusters(TowerList *neighbors, ClusterList *clusters, TObjArray *valleys) const
Int_t mNClusts
Counter for number of found clusters.
std::list< StFmsTower * > Towers
Shorthand for tower collection.
void setCluster(Int_t cluster)
Definition: StFmsTower.h:88
Declaration of StFmsTowerCluster, a cluster of FMS towers.
std::list< StFmsTowerCluster * > * clusters()
Bool_t isNeighbor(const StFmsTower &tower) const
Definition: StFmsTower.cxx:37
std::list< std::unique_ptr< StFmsTowerCluster > > ClusterList
const StFmsHit * hit() const
Definition: StFmsTower.h:80
Float_t y() const
Definition: StFmsCluster.h:61
void setNTowers(Int_t numbTower)
Definition: StFmsCluster.h:79
StMuArrays test
Definition: StMuArrays.cxx:143
unsigned associateValleyTowersWithClusters(TowerList *neighbors, ClusterList *clusters, TObjArray *valleys) const
void calculateClusterMoments(StFmsTowerCluster *cluster) const
int findClusters(TowerList *towers, ClusterList *clusters)
bool add(StFmsTowerCluster *cluster, const ETowerClusterDistance distance)
void calculateClusterMoments(Float_t energyCutoff)
Int_t row() const
Definition: StFmsTower.h:84
Int_t category() const
Definition: StFmsCluster.h:51
void setCategory(Int_t catag)
Definition: StFmsCluster.h:77
Float_t energy() const
Definition: StFmsCluster.h:57
Could be 1- or 2-photon, needs to be fitted.
Definition: StFmsCluster.h:23
static const unsigned kMaxNClusters
We stop looking after this many.