StRoot  1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
StFmsEventClusterer.cxx
Go to the documentation of this file.
1 // $Id: StFmsEventClusterer.cxx,v 1.16 2019/06/26 16:49:53 akio Exp $
2 //
3 // $Log: StFmsEventClusterer.cxx,v $
4 // Revision 1.16 2019/06/26 16:49:53 akio
5 // shower shape scaling for all shapes
6 //
7 // Revision 1.15 2018/03/23 18:43:01 smirnovd
8 // Turn off excessive output from StFmsEventClusterer
9 //
10 // Revision 1.14 2018/03/02 20:27:29 akio
11 // Big update from Zhanwen Zhu with new shower shape and six z slices
12 //
13 // Revision 1.13 2018/01/04 17:35:45 smirnovd
14 // [Cosmetic] Remove StRoot/ from include path
15 //
16 // $STAR/StRoot is already in the default path search
17 //
18 // Revision 1.12 2016/06/07 15:51:44 akio
19 // Making code better based on Coverity reports
20 //
21 // Revision 1.11 2016/01/26 14:42:48 akio
22 // better chi2 handling
23 //
24 // Revision 1.10 2015/12/11 18:05:08 akio
25 // move some LOG_INFO to LOG_DEBUG
26 //
27 // Revision 1.9 2015/11/05 17:54:57 akio
28 // Adding option to scale up shower shape function for large cells
29 //
30 // Revision 1.8 2015/11/02 22:44:49 akio
31 // Fix photonEnergyInTower()
32 //
33 // Revision 1.7 2015/10/30 21:33:56 akio
34 // fix parameter initialization
35 // adding new cluster categorization method
36 //
37 // Revision 1.6 2015/10/29 21:14:55 akio
38 // increase max number of clusters
39 // a bug fixes in valley tower association
40 // removing some debug comments
41 //
42 // Revision 1.5 2015/10/21 15:58:04 akio
43 // Code speed up (~x2) by optimizing minimization fuctions and showershape function
44 // Add option to merge small cells to large, so that it finds cluster at border
45 // Add option to perform 1photon fit when 2photon fit faield
46 // Add option to turn on/off global refit
47 // Moment analysis done without ECUTOFF when no tower in cluster exceed ECUTOFF=0.5GeV
48 //
49 // Revision 1.4 2015/10/01 18:19:59 akio
50 // fixed typo
51 //
52 // Revision 1.3 2015/10/01 18:08:58 akio
53 // adding warning if removing cluster with too many towers
54 //
55 // Revision 1.2 2015/09/02 15:01:32 akio
56 // Removing StFmsGeometry class, and now it uses StFmsDbMaker to get appropriate parameters.
57 //
58 // Revision 1.1 2015/03/10 14:38:54 jeromel
59 // First version of FmsUtil from Yuxi Pan - reviewd 2015/02
60 //
70 #include "StFmsEventClusterer.h"
71 #include "StFmsDbMaker/StFmsDbMaker.h"
72 
73 #include <algorithm>
74 #include <array>
75 #include <functional>
76 #include <iterator>
77 #include <list>
78 #include <numeric>
79 
80 #include "TF2.h" // To use shower-shape function
81 #include "TMath.h"
82 #include "TRandom.h" // For ROOT global random generator, gRandom
83 
84 #include "St_base/StMessMgr.h"
85 #include "StEvent/StFmsCluster.h"
86 #include "StEvent/StFmsHit.h"
87 
88 #include "StFmsClusterFitter.h"
89 #include "StFmsFittedPhoton.h"
90 //#include "StFmsGeometry.h"
91 #include "StFmsTower.h"
92 #include "StFmsTowerCluster.h"
93 #include "StFmsConstant.h"
94 
95 namespace {
96 namespace fms = FMSCluster;
97 // We use the tower list defined in StFmsTowerCluster throughout this file.
98 // Define some typedefs for convenience.
99 typedef fms::StFmsTowerCluster::Towers Towers;
100 typedef fms::ClusterList::iterator ClusterIter;
101 
102 /* Helper function to add numbers of photons using std::accumulate */
103 int accumulatePhotons(int nPhotons,
104  const fms::ClusterList::value_type& cluster) {
105  return nPhotons + cluster->photons().size();
106 }
107 
108 /* Sum the total number of photons in a list of clusters */
109 template<class Iterator>
110 int sumPhotonsOverClusters(Iterator start, Iterator end) {
111  return std::accumulate(start, end, 0, accumulatePhotons);
112 }
113 
114 /* Sum the total number of photons in a list of clusters */
115 template<class Container>
116 int sumPhotonsOverClusters(const Container& clusters) {
117  return sumPhotonsOverClusters(clusters.begin(), clusters.end());
118 }
119 
120 /* Unary predicate for selecting bad clusters. */
121 struct IsBadCluster {
122  // Set minimum allowed cluster energy and maximum number of towers
123  IsBadCluster(double minEnergy, unsigned maxTowers)
124  : energy(minEnergy), towers(maxTowers) { }
125  bool operator()(const fms::ClusterList::value_type& cluster) const {
126  if(cluster->cluster()->energy() <= energy){
127  LOG_DEBUG << Form("Removing cluster because E=%f <= %f (NTower=%d)",cluster->cluster()->energy(),energy,cluster->towers().size()) << endm;
128  }
129  if(cluster->towers().size() > towers){
130  LOG_INFO << Form("Removing cluster because NTower=%d > %d (E=%f)",cluster->towers().size(),towers,cluster->cluster()->energy()) << endm;
131  }
132  return cluster->cluster()->energy() <= energy ||
133  cluster->towers().size() > towers;
134  }
135  double energy;
136  unsigned towers;
137 };
138 
139 /*
140  Returns a pointer to the lowest energy photon in a cluster
141 
142  Assumes the cluster is either 1- or 2-photon
143  Returns nullptr if there is no photon in the cluster
144  */
145 const fms::StFmsFittedPhoton* findLowestEnergyPhoton(
146  const fms::StFmsTowerCluster* cluster) {
147  const fms::StFmsFittedPhoton* photon = nullptr;
148  const auto& photons = cluster->photons();
149  switch (photons.size()) {
150  case 1:
151  photon = &(photons.front());
152  break;
153  case 2:
154  if (photons.front().energy < photons.back().energy) {
155  photon = &(photons.front());
156  } else {
157  photon = &(photons.back());
158  } // if
159  default:
160  break; // photon remains nullptr
161  } // switch
162  return photon;
163 }
164 
165 /* Functor returning true if a tower matches (row, column) */
166 struct HasRowColumn {
167  int det, row, column;
168  HasRowColumn(int d, int r, int c) : det(d), row(r), column(c) { }
169  bool operator()(const fms::StFmsTower* tower) const {
170  return tower->hit()->detectorId() == det && tower->row() == row && tower->column() == column ;
171  }
172 };
173 
174 /*
175  Search towers in a cluster for one matching a row and column number
176 
177  Return a pointer to the matching tower if one is found, nullptr otherwise.
178  */
179 const fms::StFmsTower* searchClusterTowers(int det,int row, int column, const fms::StFmsTowerCluster& cluster) {
180  auto found = std::find_if(cluster.towers().begin(), cluster.towers().end(),
181  HasRowColumn(det, row, column));
182  if (found != cluster.towers().end()) {
183  return *found;
184  } // if
185  return nullptr;
186 }
187 
188 /*
189  Gives fit parameter start points and limits for 1-photon fit.
190 
191  See SFmsClusterFitter::fitNPhoton() for parameter meanings
192  */
193 struct OnePhotonFitParameters {
194  std::vector<double> start, steps, lower, upper;
195  OnePhotonFitParameters(const std::vector<double>& xyWidth,
196  const StFmsCluster* cluster, const double w) {
197  const double x = xyWidth.at(0); //width of cell coordinate, while w is width of top energy cell
198  const double y = xyWidth.at(1);
199  start = {
200  PH1_START_NPH,
201  x * cluster->x(),
202  y * cluster->y(),
203  cluster->energy()
204  };
205  steps = {0.0, 0.1*(x+y)/2.0/w, 0.1*(x+y)/2.0/w, 0.2};
206  const std::vector<double> delta = {
207  PH1_DELTA_N,
208  w * PH1_DELTA_X,
209  w * PH1_DELTA_Y,
210  cluster->energy() * PH1_DELTA_E
211  };
212  for (unsigned i(0); i < start.size(); ++i) {
213  lower.push_back(start.at(i) - delta.at(i));
214  upper.push_back(start.at(i) + delta.at(i));
215  } // for
216  }
217 };
218 
219 /*
220  Gives fit parameter start points and limits for 2-photon fit.
221 
222  See SFmsClusterFitter::fit2Photon() for parameter meanings
223  */
224 struct TwoPhotonFitParameters {
225  std::array<double, 7> start, steps, lower, upper;
226  TwoPhotonFitParameters(const std::vector<double>& xyWidth,
227  const fms::StFmsTowerCluster* towerCluster, const int largesmall , double vertexZ) {
228  const double x = xyWidth.at(0); //width of cell coordinate, while largesmall indicate top cells l/s
229  const double y = xyWidth.at(1);
230  const auto cluster = towerCluster->cluster();
231 
232  //When small merge to large , x y above from xyWidth are large cell width for coodinate calc
233  //Overwrite w1 and w2 for step/limit calc
234  double w1=0, w2=0;
235  if (largesmall==0) {w1=x; w2=y;}// could be no merging or large cell cluster when merging, anyway ,nothing need be done
236  else {w1=3.822; w2=3.875;}
237  LOG_DEBUG<<"x in FitP "<< x <<" w1="<<w1<<endm;
238 
239  start = std::array<double, 7>{ {
240  PH2_START_NPH,
241  x * cluster->x(),// both terms are scaled, no need to use w1/w2
242  y * cluster->y(),
243  //PH2_START_FSIGMAMAX * x * cluster->sigmaMax(), // original
244  PH2_START_FSIGMAMAX * x * cluster->sigmaMax() / 2.0, // above is too big, scale down by 1/2
245  towerCluster->thetaAxis(),
246  gRandom->Uniform(PH2_RAN_LOW, PH2_RAN_HIGH),
247  cluster->energy(),
248  } };
249  steps = std::array<double, 7>{ {PH2_STEP_0, PH2_STEP_1, PH2_STEP_2, PH2_STEP_3, PH2_STEP_4, PH2_STEP_5, PH2_STEP_6} };
250  const double sigmaMaxE = cluster->sigmaMax() * cluster->energy();
251  double maxTheta = cluster->sigmaMin() / cluster->sigmaMax() / PH2_MAXTHETA_F;
252  maxTheta = std::min(maxTheta, TMath::PiOver2());
253  lower = std::array<double, 7>{ {
254  PH2_LOWER_NPH,
255  start.at(1) - PH2_LOWER_XF * w1 ,
256  start.at(2) - PH2_LOWER_YF * w2,
257  //use vertex for lower limit!!!
258  //if starting point is close to the thoretical limit, still allowed to go down -20% by limit below
259  std::max(0.1345*2/cluster->energy()*(735.45-vertexZ) , 0.5*w1 ),
260  //theoretical lower limit assuming vertex=0,
261  //std::max(0.1345*2/cluster->energy()*735.45 , 0.5*w1 ),
262  //This is original limit * 0.5
263  // std::max(PH2_LOWER_XMAX_F / pow(sigmaMaxE, PH2_LOWER_XMAX_POW), PH2_LOWER_XMAX_LIMIT) * x / 2,
264  //This is original limit which is too high
265  // std::max(PH2_LOWER_XMAX_F / pow(sigmaMaxE, PH2_LOWER_XMAX_POW), PH2_LOWER_XMAX_LIMIT) * x,
266  start.at(4) - maxTheta,
267  PH2_LOWER_5_F,
268  start.at(6) * PH2_LOWER_6_F
269  } };
270  upper = std::array<double, 7>{ {
271  PH2_UPPER_NPH,
272  start.at(1) + PH2_UPPER_XF * w1,
273  start.at(2) + PH2_UPPER_YF * w2,
274  // original * 5, still it will be overwroitten by limit at few lines below if it goes negative
275  std::min(PH2_UPPER_XMIN_F * (PH2_UPPER_XMIN_P0 - sigmaMaxE), PH2_UPPER_XMIN_LIMIT) * w1 * 5,
276  //original, wrong at high energy and goes negative
277  //std::min(PH2_UPPER_XMIN_F * (PH2_UPPER_XMIN_P0 - sigmaMaxE), PH2_UPPER_XMIN_LIMIT) * w1 * 5,
278  start.at(4) + maxTheta,
279  PH2_UPPER_5_F,
280  start.at(6) * PH2_UPPER_6_F
281  } };
282  // With the above approach the limits on parameter 3 can sometimes go beyond
283  // sensible values, so limit them.
284  lower.at(3) = std::min(lower.at(3), start.at(3) * 0.8);
285  upper.at(3) = std::max(upper.at(3), start.at(3) * 3.0);
286  //original is too tight
287  // lower.at(3) = std::min(lower.at(3), start.at(3) * PH2_3_LIMIT_LOWER);
288  // upper.at(3) = std::max(upper.at(3), start.at(3) * PH2_3_LIMIT_UPPER);
289  }
290 };
291 
292 /* Gives fit parameters for global photon fit */
293 struct GlobalPhotonFitParameters {
294  std::vector<double> start, lower, upper;
295  GlobalPhotonFitParameters(unsigned nPhotons,
296  ClusterIter first, ClusterIter end)
297  // Initialise N-photons parameters as the first element
298  : start(1, nPhotons), lower(1, GL_LOWER_1),
299  upper(1, fms::StFmsClusterFitter::maxNFittedPhotons() + GL_UPPER_DELTA_MAXN) {
300  // Append (x, y, E) fit parameters for each photon
301  for (auto cluster = first; cluster != end; ++cluster) {
302  const auto& photons = (*cluster)->photons();
303  for (auto p = photons.begin(); p != photons.end(); ++p) {
304  start.push_back(p->x);
305  lower.push_back(start.back() - GL_0_DLOWER);
306  upper.push_back(start.back() + GL_0_DUPPER);
307  start.push_back(p->y);
308  lower.push_back(start.back() - GL_1_DLOWER);
309  upper.push_back(start.back() + GL_1_DUPPER);
310  start.push_back(p->energy);
311  lower.push_back(start.back() * (1 - GL_2_DLOWER)); // Limit to +/- 30% energy
312  upper.push_back(start.back() * (1 + GL_2_DUPPER));
313  } // for
314  } // for
315  }
316 };
317 } // unnamed namespace
318 
319 namespace FMSCluster {
320  StFmsEventClusterer::StFmsEventClusterer( //const StFmsGeometry* geometry,
321  StFmsDbMaker* db, Int_t detectorId,
322  Int_t globalrefit, Int_t mergeSmallToLarge,
323  Int_t try1PhotonFit, Int_t categorizationAlgo,
324  Float_t scaleShowerShapeLarge , Float_t scaleShowerShapeSmall,
325  Int_t showerShapeWithAngle ,double vertexZ)
326  : mClusterFinder(0.5), /*mGeometry(geometry),*/ mDetectorId(detectorId), mTowers(0),
327  mFmsDbMaker(db), mGlobalRefit(globalrefit), mMergeSmallToLarge(mergeSmallToLarge),
328  mTry1PhotonFitWhen2PhotonFitFailed(try1PhotonFit), mCategorizationAlgo(categorizationAlgo),
329  mScaleShowerShapeLarge(scaleShowerShapeLarge), mScaleShowerShapeSmall(scaleShowerShapeSmall),
330  mShowerShapeWithAngle(showerShapeWithAngle), vertexz(vertexZ) { }
331 
333 
334 Bool_t StFmsEventClusterer::cluster(std::vector<StFmsTower>* towerList) {
335  mTowers = towerList;
336  //mTowerWidthXY = mGeometry->towerWidths(mDetectorId);
337  if(!mFmsDbMaker){
338  LOG_ERROR << "StFmsEventCusterer cannot find StFmsDbMaker !!!!!!"<<endm;
339  return false;
340  }
341  Float_t xw = mFmsDbMaker->getXWidth(mDetectorId);
342  Float_t yw = mFmsDbMaker->getYWidth(mDetectorId);
343  mTowerWidthXY.clear();
344  mTowerWidthXY.push_back(xw);
345  mTowerWidthXY.push_back(yw);
347  if (mTowers->size() > TOTAL_TOWERS) {
348  LOG_ERROR << "Too many towers for Fit" << endm;
349  return false;
350  } // if
351  mFitter.reset(new StFmsClusterFitter(/*mGeometry,*/ mDetectorId,xw,yw,
352  mScaleShowerShapeLarge,mScaleShowerShapeSmall,
353  mShowerShapeWithAngle,mMergeSmallToLarge,vertexz) );
354  return fitEvent(); // Return true for success
355 }
356 
357 Int_t StFmsEventClusterer::fitEvent() {
358  if(findClusters()) {
359  if(fitClusters()){
360  if(mGlobalRefit==0) return true;
361  if(refitClusters()) return true;
362  LOG_INFO << "StFmsEventClusterer::fitEvent() refitClusters failed" <<endm;
363  return false;
364  }
365  LOG_INFO << "StFmsEventClusterer::fitEvent() fitClusters failed" <<endm;
366  return false;
367  }
368  //LOG_INFO << "StFmsEventClusterer::fitEvent() findClusters failed" <<endm;
369  return false;
370 }
371 
372 Int_t StFmsEventClusterer::findClusters() {
373  StFmsClusterFinder::TowerList towerList;
374  for (auto i = mTowers->begin(); i != mTowers->end(); ++i) {
375  towerList.push_back(&(*i));
376  } // for
377  mClusterFinder.findClusters(&towerList, &mClusters, mDetectorId);
378  switch(mFmsDbMaker->largeSmall(mDetectorId)){
379  case 0:
380  mClusters.remove_if(IsBadCluster(BAD_MIN_E_LRG, BAD_MAX_TOW_LRG));
381  break;
382  case 1:
383  mClusters.remove_if(IsBadCluster(BAD_MIN_E_SML, BAD_MAX_TOW_SML));
384  break;
385  default:
386  break;
387  } // switch
388  // Must do moment analysis before catagorization
389  for (auto i = mClusters.begin(); i != mClusters.end(); ++i) {
390  (*i)->findClusterAxis(mClusterFinder.momentEnergyCutoff(),mTowerWidthXY.at(0),mTowerWidthXY.at(1));
391  } // for
392  return mClusters.size();
393 }
394 
395 Bool_t StFmsEventClusterer::fitClusters() {
396  // Loop over clusters, catagorize, guess the photon locations for cat 0 or 2
397  // clusters then fit, compare, and choose the best fit
398  bool badFit = false;
399  int Clucount=0;
400  for (auto iter = mClusters.begin(); iter != mClusters.end(); ++iter) {
401  int category = -1;
402  ++Clucount;
403 
404  if(mCategorizationAlgo==0) {category = mClusterFinder.categorise(iter->get());}
405  else {category = mClusterFinder.categorise2(iter->get());}
406  mFitter->setTowers(&(*iter)->towers());
407  switch (category) {
408  case k1PhotonCluster:
409  fit1PhotonCluster(iter->get());
410  break;
411  case k2PhotonCluster:
412  fit2PhotonCluster(iter);
413  break;
414  case kAmbiguousCluster:
415  category = fitAmbiguousCluster(iter);
416  break;
417  default:
418  LOG_ERROR << "The logic of cluster catagory is wrong and something "
419  << "impossible has happened! This a catagory-" << category <<
420  " cluster! Do not know how to fit it!" << endm;
421  break;
422  } // switch
423  if (category == k2PhotonCluster && (*iter)->chiSquare() > BAD_2PH_CHI2) {
424  float chi2=(*iter)->chiSquare();
425  LOG_DEBUG << Form("chi2=%f > BAD_2PH_CHI2=%f",chi2,BAD_2PH_CHI2) << endm;
426  if(mTry1PhotonFitWhen2PhotonFitFailed){
427  const std::vector<StFmsFittedPhoton> keep = (*iter)->photons(); //copy in case 2-photon fit is better
428  fit1PhotonCluster(iter->get()); //try 1-photon fit
429  float chi1=(*iter)->chiSquare();
430  LOG_DEBUG << Form("Tried 1-photon fit resulted with chi2=%f", chi1)<< endm;
431  if(chi2<chi1){
432  LOG_DEBUG << "2-photon fit was better" << endm;
433  (*iter)->photons().assign(keep.begin(), keep.end());
434  (*iter)->setChiSquare(chi2);
435  //badFit = true;
436  }else{
437  LOG_DEBUG << "Taking 1-photon fit" << endm;
438  }
439  }
440  } // if
441  } // Loop over all real clusters
442  return !badFit;
443 }
444 
445 Bool_t StFmsEventClusterer::refitClusters() {
446  // Only do a global fit for 2 or more clusters (2-photon fit for one cluster
447  // already performs a global fit as part of its normal procedure)
448  if (mClusters.size() < 2) {
449  return true;
450  } // if
451  Towers towers;
452  for (auto i = mClusters.begin(); i != mClusters.end(); ++i) {
453  towers.insert(towers.end(), (*i)->towers().begin(), (*i)->towers().end());
454  } // for
455  mFitter->setTowers(&towers);
456  const int nPhotons = sumPhotonsOverClusters(mClusters);
457  fitGlobalClusters(nPhotons, mClusters.size(), mClusters.begin());
458  return nPhotons == sumPhotonsOverClusters(mClusters); // Shouldn't change
459 }
460 
461 Double_t StFmsEventClusterer::photonEnergyInCluster(
462  const StFmsTowerCluster* cluster,
463  const StFmsFittedPhoton* photon) const {
464  double energy = 0.;
465  const Towers& towers = cluster->towers();
466  for (auto tower = towers.begin(); tower != towers.end(); ++tower) {
467  energy += photonEnergyInTower(*tower, photon);
468  } // for
469  return energy;
470 }
471 
472 Double_t StFmsEventClusterer::photonEnergyInTower(
473  const StFmsTower* tower,
474  const StFmsFittedPhoton* photon) const {
475  //double x = (tower->column() - 0.5) * mTowerWidthXY.at(0) - photon->x;
476  //double y = (tower->row() - 0.5) * mTowerWidthXY.at(1) - photon->y;
477  //return photon->energy * mFitter->showerShapeFunction()->Eval(x, y);
478 
479  if (mShowerShapeWithAngle>0) return photon->energy * mFitter->showerShapeFunction()->Eval(tower->x(),photon->x,tower->y(),photon->y);
480  if (mShowerShapeWithAngle==0) return photon->energy * mFitter->showerShapeFunction()->Eval(tower->x()-photon->x,tower->y()-photon->y);
481 }
482 
483 /* 1-photon fitting function */
484 Double_t StFmsEventClusterer::fit1PhotonCluster(StFmsTowerCluster* towerCluster) {
485  double w=towerCluster->towers().front()->w(); //get tower width from 1st/top tower
486  OnePhotonFitParameters parameters(mTowerWidthXY, towerCluster->cluster(), w);
487  PhotonList photons;
488  double chiSquare = mFitter->fitNPhoton(parameters.start, parameters.steps,
489  parameters.lower,parameters.upper, &photons);
490  if (photons.empty()) { // check return status in case of a bad fit
491  LOG_ERROR << "1-photon Minuit fit found no photons" << endm;
492  } else {
493  towerCluster->photons().assign(photons.begin(), photons.end());
494  } // if
495  const int nDegreesOfFreedom =
496  std::max(int(towerCluster->towers().size()) - 3, 1);
497  towerCluster->setChiSquare(chiSquare / nDegreesOfFreedom);
498  towerCluster->setChiSquare1(chiSquare / nDegreesOfFreedom);
499  return towerCluster->chiSquare();
500 }
501 
502 /* 2-photon fitting function */
503 Double_t StFmsEventClusterer::fit2PhotonCluster(ClusterIter towerCluster) {
504  double x=towerCluster->get()->towers().front()->x();
505  double y=towerCluster->get()->towers().front()->y(); //get top energy tower position
506  double w=0;//indicator
507  if(mMergeSmallToLarge>0 && x<8.0 && y>9.0 && y<25.0){
508  w=1;
509  }
510  TwoPhotonFitParameters parameters(mTowerWidthXY, towerCluster->get(), w,vertexz);
511  PhotonList photons;
512  double chiSquare =
513  mFitter->fit2Photon(parameters.start, parameters.steps,
514  parameters.lower, parameters.upper, &photons);
515  if (photons.size() == 2) {
516  (*towerCluster)->photons().assign(photons.begin(), photons.end());
517  } else {
518  LOG_WARN << "2-photon Minuit fit found " << photons.size() << " photons"
519  << endm;
520  } // if
521  chiSquare = fitGlobalClusters(2, 1, towerCluster);
522  const int nDegreesOfFreedom = std::max(1,
523  int((*towerCluster)->towers().size() - 6));
524  (*towerCluster)->setChiSquare(chiSquare / nDegreesOfFreedom);
525  (*towerCluster)->setChiSquare2(chiSquare / nDegreesOfFreedom);
526  return (*towerCluster)->chiSquare();
527 }
528 
529 /* Distinguish an ambiguous cluster as either 1- or 2-photon */
530 Int_t StFmsEventClusterer::fitAmbiguousCluster(ClusterIter towerCluster) {
531  const double chiSquare1Photon = fit1PhotonCluster(towerCluster->get());
532  const StFmsFittedPhoton photon = (*towerCluster)->photons().front(); // Cache
533  // Decide if this 1-photon fit is good enough, if not try 2-photon fit
534  int category = k1PhotonCluster;
535  LOG_DEBUG << "fitAmbiguousCluster chi2 for 1photon fit="<<chiSquare1Photon<<endm;
536  if (chiSquare1Photon >= 5.) {
537  const double chiSquare2Photon=fit2PhotonCluster(towerCluster);
538  LOG_DEBUG << "fitAmbiguousCluster chi2 for 2photon fit="<<chiSquare2Photon<<endm;
539  if(chiSquare2Photon <= chiSquare1Photon ){
540  LOG_DEBUG << "fitAmbiguousCluster 2 photon fit is better, validate2ndPhoton"<<endm;
541  if(validate2ndPhoton(towerCluster)) {
542  category = k2PhotonCluster;
543  }
544  } // if
545  } // if
546  // The 2-photon fit updated the cluster, so if the 1-photon fit turns out to
547  // have been better, restore its properties
548  if (category == k1PhotonCluster) {
549  (*towerCluster)->setChiSquare(chiSquare1Photon);
550  (*towerCluster)->photons().assign(1, photon);
551  } // if
552  return category;
553 }
554 
555 /* Global fitting function, fitting photons across all clusters */
556 Double_t StFmsEventClusterer::fitGlobalClusters(unsigned int nPhotons,
557  const unsigned int nClusters,
558  ClusterIter first) {
559  ClusterIter end = first;
560  std::advance(end, nClusters); // Marks end point for cluster iteration
561  const unsigned int totalPhotons = sumPhotonsOverClusters(first, end);
562  if (totalPhotons != nPhotons) {
563  LOG_WARN << "Global fit called for " << nPhotons << " but found " <<
564  totalPhotons << "... will proceed with " << totalPhotons << endm;
565  nPhotons = totalPhotons;
566  } // if
567  if (int(nPhotons) > StFmsClusterFitter::maxNFittedPhotons() || nPhotons < 2) {
568  LOG_ERROR << "Global fit cannot fit " << nPhotons << " photons" << endm;
569  return -9999;
570  } // if
571  GlobalPhotonFitParameters parameters(nPhotons, first, end);
572  PhotonList photons;
573  Double_t chiSquare = mFitter->fitNPhoton(parameters.start, parameters.lower,
574  parameters.upper, &photons);
575  if (photons.size() == nPhotons) {
576  // Put the fit result back in the clusters
577  auto photon = photons.begin();
578  for (ClusterIter cluster = first; cluster != end; ++cluster) {
579  auto& ph = (*cluster)->photons();
580  for (auto p = ph.begin(); p != ph.end(); ++p, ++photon) {
581  *p = *photon;
582  } // for
583  } // for loop over clusters
584  } else {
585  LOG_WARN << "Global Minuit fit found " << photons.size() <<
586  " photons but expected " << nPhotons << endm;
587  } // if
588  return chiSquare;
589 }
590 
591 /*
592  Further information:
593  If one photon peak lies on top of a low (compared to photon energy) or even
594  zero tower, this photon is definitely bogus. This could happen if a nearby
595  cluster took away towers that might make the fit have a large Chi-square.
596  So first check that the fitted photon of the lower-energy photon (we assume the
597  higher energy photon should be fine) is over one of the non-zero towers of the
598  cluster. First of all, this ensures that we don't have an "outside of cluster"
599  bogus photon, i.e. a bogus photon that could be the result of minimizing the
600  chi-square over towers that do not include the supposed peak tower.
601 */
602 bool StFmsEventClusterer::validate2ndPhoton(ClusterConstIter cluster) const {
603  // Find the tower hit by the lowest energy photon in a cluster
604  const StFmsFittedPhoton* photon = findLowestEnergyPhoton(cluster->get());
605  double x=photon->x / mTowerWidthXY.at(0);
606  double y=photon->y / mTowerWidthXY.at(1);
607  int det=mDetectorId;
608  int column = 1 + int(x);
609  int row = 1 + int(y);
610  if(mMergeSmallToLarge>0 && x<8.0 && y>9.0 && y<25.0){
611  column = 1 + int(x*1.5); //ZZW
612  row = 1 + int((y-9.0)*1.5);
613  det+=2;
614  }
615  const StFmsTower* tower = searchClusterTowers(det, row, column, **cluster);
616  // If tower is nullptr, the photon doesn't hit in a tower in this cluster.
617  if (!tower) {
618  LOG_DEBUG << "StFmsEventClusterer::validate2ndPhoton No hit on photon" << endm;
619  return false;
620  } // if
621  // Check if the fitted energy is too large compared to the energy of the tower
622  if (tower->hit()->energy() < VALID_FT * photon->energy) {
623  LOG_DEBUG << "StFmsEventClusterer::validate2ndPhoton hit on photon too low" << endm;
624  return false;
625  } // ifvalidate2ndPhoton
626  // Check if the 2nd photon's "high-hower" energy is too large compared to its
627  // fitted energy. If so, it is probably splitting one photon into two
628  if (tower->hit()->energy() > VALID_2ND_FT * photonEnergyInTower(tower, photon)) {
629  LOG_DEBUG << "StFmsEventClusterer::validate2ndPhoton photon energy too low compared to other photon" << endm;
630  return false;
631  } // if
632  // Check that the 2nd photon is not near the edge of another cluster
633  const double energyInOwnCluster =
634  photonEnergyInCluster(cluster->get(), photon);
635  for (ClusterConstIter i = mClusters.begin(); i != mClusters.end(); ++i) {
636  if (i != cluster) { // Skip the photon's own cluster
637  if (photonEnergyInCluster(i->get(), photon) > VALID_E_OWN * energyInOwnCluster) {
638  LOG_DEBUG << "StFmsEventClusterer::validate2ndPhoton photon is at edge of another cluster" << endm;
639  return false; // Stop as soon as we fail for one cluster
640  } // if
641  } // if
642  } // for
643  LOG_DEBUG << "StFmsEventClusterer::validate2ndPhoton OK" << endm;
644  return true; // The photon passed all tests; it's real
645 }
646 } // namespace FMSCluster
Could be 1- or 2-photon, needs to be fitted.
A cluster created by 2 photons.
Declaration of StFmsClusterFitter, shower-shape fitting routine.
Float_t getXWidth(Int_t detectorId)
get the offset of the detector
Bool_t cluster(std::vector< FMSCluster::StFmsTower > *towers)
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.
Int_t largeSmall(Int_t detectorId)
north or south side
Declaration of StFmsTowerCluster, a cluster of FMS towers.
Declaration of StFmsEventClusterer, manager class for clustering.
int findClusters(TowerList *towers, ClusterList *clusters, int detetorId)
Float_t getYWidth(Int_t detectorId)
get the X width of the cell
Int_t row() const
Definition: StFmsTower.h:94
A cluster created by 1 photon.
Declaration of StFmsFittedPhoton, a photon fitted to an FMS cluster.
StFmsEventClusterer(StFmsDbMaker *db, Int_t detectorId, Int_t globalrefit, Int_t mergeSmallToLarge, Int_t try1Photon, Int_t categorizationAlgo, Float_t scaleShowerShapeLarge, Float_t scaleShowerShapeSmall, Int_t showerShapeWithAngle, double vertexz)