EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
FwdHoughTree.cxx
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file FwdHoughTree.cxx
1 //
2 // AYK (ayk@bnl.gov)
3 //
4 // Hough transform track finder for STAR-specific forward tracker;
5 //
6 // Initial port from OLYMPUS sources: Oct'2015;
7 //
8 
9 #include <stdlib.h>
10 
11 #include <Math/DistFunc.h>
12 #include <TMath.h>
13 
14 #include <FairRunAna.h>
15 
16 #include <htclib.h>
17 
18 #include <FwdTrackFinder.h>
19 #include <FwdHoughTree.h>
20 
21 // ---------------------------------------------------------------------------------------
22 
24 {
25  // May later introduce a boolean variable; for now just fall out if
26  // field is not available;
27  if (!mBzAtIP) {
29  FairField *field = fRun->GetField();
30  assert(field);
31 
32  // Yes, reset b[] as well;
33  double xx[3] = {0.0, 0.0, 0.0}, b[3] = { 0.0, 0.0, 0.0};
34  field->Field(xx, b);
35 
36  // Yes, store in [kGs] as field->Field() returns;
37  mBzAtIP = b[2];
38  assert(mBzAtIP);
39 #if 0
40  //printf("%f\n", mBzAtIP); exit(0);
41  {
42  xx[0] = 20.0;
43  for(unsigned iq=0; iq<300; iq++) {
44  xx[2] = iq*1.0;
45 
46  field->Field(xx, b);
47  printf("%3d -> %7.2f %7.2f %7.2f\n", iq, b[0], b[1], b[2]);
48  } //for iq
49  exit(0);
50  }
51 #endif
52  } //if
53 
54  return mBzAtIP;
55 } // FwdHoughTree::GetBzAtIP()
56 
57 // ---------------------------------------------------------------------------------------
58 
59 void FwdHoughTree::MappingCall(const double par[], t_hough_range id[])
60 {
61  double vz = mTrackFinder->GetVtxNode()->GetZ();
62 
63  // FIXME: do it better later;
64  assert(mTrackFinder->GetThetaId() != -1 && mTrackFinder->GetPhiId() != -1);
65  if (GetDdim() == 3) assert(mTrackFinder->GetMomentumRelatedId() != -1);// || mTrackFinder->GetInvPtId() != -1);
66 
67  double theta = deg2rad(par[mTrackFinder->GetThetaId()]), phi = deg2rad(par[mTrackFinder->GetPhiId()]);
68  double invp = 0.0, invpt = 0.0;
69 
70  if (GetDdim() == 3) {
71  if (mTrackFinder->GetInvMomentumId() != -1) invp = par[mTrackFinder->GetInvMomentumId()];
72  if (mTrackFinder->GetInvPtId() != -1) invpt = par[mTrackFinder->GetInvPtId()];
73  } //if
74 
75  // FIXME: may want to unify these two parts at some point;
76  // Yes, check 'invp != 0' here rather than 'GetDdim() == 2' because magnet-on case
77  // may also require cell edge processing with infinitely high momentum (so de-facto
78  // the same straight tracks);
79  if (!invp && !invpt) {
80  // FIXME: assume (0,0,vz) vertex;
81  t_3d_line track(TVector3(0,0,vz), TVector3(sin(theta)*cos(phi), sin(theta)*sin(phi), cos(theta)));
82 
83  // Ok, now need to calculate hit wire numbers for all planes;
84  for(int gr=0; gr<mGroups.size(); gr++) {
85  TVector3 vcrs;
86  FwdHoughNodeGroup *ngroup = GetNodeGroup(gr);
87 
88  assert(!cross_p_l(ngroup->GetLocation()->GetPlane(), &track, vcrs));
89 
90  // Put inside the allowed limits if needed; the idea behind
91  // this trick is the following: Hough code does not need to
92  // loop up to the highest resolution level in order to decide
93  // on a given edge cell range (CPU issues!); it stops few levels
94  // earlier and marks acceptance for a given (big) cell a bit
95  // larger than it actually is; still within the allowed limits;
96  id[gr] = ngroup->Pack(vcrs);
97  } //for gr
98  } else {
99  // NB: field was stored and is returned back in [kGs] here;
100  double pt = invpt ? 1.0/invpt : sin(theta)/invp;
101  // Well, 30MeV/c particle in 1kGs field has R=1m; just rescale and convert to [cm];
102  double b = GetBzAtIP(), R = 100.0 * (pt/0.03) * (1.0/b);
103 
104  // Ok, now need to calculate hit wire numbers for all planes;
105  for(int gr=0; gr<mGroups.size(); gr++) {
106  FwdHoughNodeGroup *ngroup = GetNodeGroup(gr);
107  TrKalmanNodeLocation *location = ngroup->GetLocation();
108 
109  // Length of the radial projection should just be (z0-zv)*tg(theta) I guess;
110  // assume, that vertex XY are (0,0);
111  double dr = (location->GetZ() - vz)*tan(theta), alfa = dr/R;
112  double yy = R*(1.0 - cos(alfa)), xx = R*sin(alfa);
113  //printf(" @PT@ pt=%f, theta=%f -> yy=%f\n", pt, theta, yy);
114  // Apply 1D phi-rotation by hand; NB: this way the signs are correct;
115  // FIXME: in fact need crs[2] only (see old code);
116  TVector3 crs = TVector3(xx*cos(phi)+yy*sin(phi), xx*sin(phi)-yy*cos(phi), location->GetZ());
117 
118  id[gr] = ngroup->Pack(crs);
119  } //for gr
120  } //if
121 } // FwdHoughTree::MappingCall()
122 
123 // ---------------------------------------------------------------------------------------
124 
126  unsigned id, unsigned cdim, const double min[],
127  const double max[], const double gra[])
128 {
129  FwdHoughNodeGroup *ngroup =
130  dynamic_cast<FwdHoughNodeGroup*>(HoughTree::AddNodeGroup(id, cdim, min, max, gra));
131  ngroup->SetLocation(location);
132 
133  return ngroup;
134 } // FwdHoughTree::AddNodeGroup()
135 
136 // ---------------------------------------------------------------------------------------
137 
138 // FIXME: default group; do it better later;
139 #define _GR77_ 77
140 
142  bool cylindricalPreference,
143  const std::set<double> &xMin, const std::set<double> &xMax,
144  const std::set<double> &yMin, const std::set<double> &yMax,
145  const std::set<double> &rMin, const std::set<double> &rMax)
146 {
147  // Well, based on input data and on the individual template responses want to figure out
148  // whether this group will work in linear (XY) or cylindrical coordinates in terms of limits,
149  // meaning of coordinates given to ngroup->Pack(crs) in the FwdHoughTree::MappingCall(), etc;
150  // also guess on the optimal granularity (distinguish only isotropic spatial granularity
151  // and generic angular granularity, no complications; loop through all KF nodes in this
152  // {location,tmpl} collection;
153  std::set<double> spatialSigma, angularSigma;
154  bool cartesianOk = true, cylindricalOk = true, useCartesian = true;
155  for(unsigned kfnd=0; kfnd<location->GetNodeCount(); kfnd++) {
156  TrKalmanNode *node = location->GetNode(kfnd);
157 
158  SensitiveVolume *sv = node->GetSensitiveVolume();
159  if (!sv) continue;
160 
161  const EicKfNodeTemplate *__template = sv->GetKfNodeWrapper(tmpl)->GetKfNodeTemplate();
162 
163  // NB: in XY-case the smallest of the two will be given;
164  double aSigma = __template->GetAngularSigma(), spSigma = __template->GetSpatialSigma();
165  printf("%s -> %8.5f %8.5f\n", node->GetName(), aSigma, spSigma);
166  if (spSigma) spatialSigma.insert(spSigma);
167  if ( aSigma) angularSigma.insert( aSigma);
168 
169  // Check possible coordinate system options;
170  if (__template->CylindricalThreeDeeOnly()) cartesianOk = false;
171  if (__template->CartesianThreeDeeOnly()) cylindricalOk = false;
172  } //for kfnd
173 
174  // Choose coordinate system; since location was checked in TrKalmanFilter::SetUpLocations(),
175  // at least one option must be available; if there is no preference to use cylindrical coordinate
176  // system and cartesian is possible, use cartesian;
177  assert(cartesianOk || cylindricalOk);
178  if ((cylindricalPreference && cylindricalOk) || !cartesianOk) useCartesian = false;
179 
180  // Figure out granularity value and call respective AddNodeGroup() routines;
181  {
182  double spSigma = 0.0, aSigma = 0.0;
183 
184  if (useCartesian) {
185  // Cartesian case is simple: there should be at least one spatial estimate and angular
186  // estimate is not needed;
187  assert(spatialSigma.size());
188  spSigma = *spatialSigma.begin();
189  } else {
190  // See whether estimates are available;
191  if (spatialSigma.size()) spSigma = *spatialSigma.begin();
192  if (angularSigma.size()) aSigma = *angularSigma.begin();
193 
194  // If no angular granularity avaiable, guess on it;
195  if (!aSigma) {
196  assert(spSigma && (*rMax.rbegin()));
197  // FIXME: '5' a clear hack;
198  aSigma = spSigma/(*rMax.rbegin()/5);
199  } //if
200  } //if
201 
202  // Blow up by some factor; I guess a single coefficient must be sufficient?;
203  double spGranularity = spSigma*mTrackFinder->GetExtraGranularityFactor();
204  //printf("s: %f, g: %f\n", spSigma, spGranularity);
205  double aGranularity = aSigma*mTrackFinder->GetExtraGranularityFactor();
206 
207  const EicKfNodeTemplate *_template = location->GetTemplate(tmpl);
208 
209  double min[3], max[3], gra[3];
210  _template->FillGranularityArray(useCartesian, spGranularity, aGranularity, gra);
211  _template->FillMinMaxArrays(useCartesian, xMin, xMax, yMin, yMax, rMin, rMax, min, max);
212 
213  printf("%7.2f -> %2d --> %d\n", location->GetZ(), tmpl, location->GetMdim(tmpl));
214  printf(" -> xmin=%7.2f, xmax =%7.2f, ymin=%7.2f, ymax=%7.2f\n",
215  min[0], max[0], min[1], max[1]);
216  FwdHoughNodeGroup *ngroup =
217  AddNodeGroup(location, _GR77_, _template->GetMdim(), min, max, gra);
218 
219  // FIXME: perhaps get rid of this variable at all?;
220  ngroup->SetMarsToTemplateMtx(location->GetNodeToMaster(tmpl));
221  ngroup->SetTemplate(_template);
222  ngroup->SetCartesianFlag(useCartesian);
223 
224  {
225  //double sme[3];
226 
227  // FIXME: need to handle angular case as well;
228  //_template->FillSmearingArray(mTrackFinder->GetAbsoluteSpatialSmearing(), 0.0, sme);
229  //ngroup->SetSmearingValues(sme);
230  }
231 
232  // THINK: always give some +/- 1 cell freedom in parameter space as well?;
233  ngroup->SetPhaseSpaceSmearing(0);
234 
235  return ngroup;
236  }
237 } // FwdHoughTree::AddNodeGroup()
238 
239 // ---------------------------------------------------------------------------------------
240 
242 {
244 
246  kf->ResetFiredFlags();
247 
248  // Allocate real hits; FIXME: for now consider to take the very 1-st one
249  // if several are available after the tree search procedure;
250  for(unsigned gr=0; gr<mGroups.size(); gr++) {
251  unsigned aliveCount = match->GetAliveMemberCount(gr);
252 
253  for(unsigned mm=0; mm<match->GetLinearMemberCount(gr); mm++) {
254  GroupMember *member = match->GetSelMember(gr, mm);
255 
256  // NB: part of them could have been disabled already;
257  if (!member) continue;
258 
259  EicDetectorGroup *dgroup = (EicDetectorGroup*)member->mPtr.first;
260  EicTrackingDigiHit *hit = (EicTrackingDigiHit*)member->mPtr.second;
261  SensitiveVolume *sv = dgroup->GetSensitiveVolume(hit);
262 
263  // FIXME: this will not work for "mosaic" configuration; need to keep track on
264  // hit count per "patch";
265  KalmanNodeWrapper *wrapper = sv->GetKfNodeWrapper(hit->GetKfNodeID());
266  // FIXME: first, this is not efficient; second, may not be correct; just
267  // use 'mm' index throughout all the sensitive volumes; allocate that many,
268  // so that 'mm' index is occupied;
269  //if (!wrapper->GetKfNode(mm)) wrapper->AllocateNewKfNode(kf, sv);
270  while (!wrapper->GetKfNode(mm)) wrapper->AllocateNewKfNode(kf, sv);
271 
272  // FIXME: this will not work for "mosaic" configuration; need to keep track on
273  // hit count per "patch";
274  TrKalmanNode *trknode = static_cast <TrKalmanNode*> (wrapper->GetKfNode(mm));
275  // FIXME: remove when debugging is finished;
276  assert(trknode && !trknode->IsFired());
277  //assert(!trknode->IsFired());
278  trknode->SetHit(hit);
279 
280  EicKfNodeTemplate *kftmpl = dgroup->mDigi->mKfNodeTemplates[hit->GetKfNodeID()];
281  trknode->SetMeasurementNoise(kftmpl->GetMeasurementNoise(hit));
282  // If there are >1 hit per node group, blow up their resolutions respectively;
283  // the actual "heating" and "annealing" scheme may be different; THINK later;
284  if (aliveCount > 1)
286 
287 #if 0
288  {
289  Int_t mchitid = hit->GetRefIndex();
290  assert(mchitid >= 0);
291  FairMCPoint *myPoint = (FairMCPoint*)(dgroup->_fMCPoints->At(mchitid));
292  assert(myPoint);
293  //printf("gr#%02d, mm#%02d %p -> chi^2 %7.3f, CCDF %10.7f ... %3d\n", gr, mm, member,
294  // trknode->GetSmootherChiSquare(),
295  // ROOT::Math::chisquared_cdf_c(trknode->GetSmootherChiSquare(), trknode->GetMdim()),
296  // myPoint->GetTrackID());
297  if (myPoint->GetTrackID() != 2) {
298  match->ResetMemberPtr(gr, mm);
299  member->mMatchCandidates.erase(match);
300  continue;
301  } //if
302  }
303 #endif
304  trknode->SetFiredFlag();
305  } //for mm
306  } //for gr
307 
308  // Leave at least 1 node per "mosaic" location; but no multiple "no-hit" nodes
309  // per location (just in order to speed up KF calculations);
310  kf->SelectActiveNodes();
311  // And eventually build linked list of active nodes which can be fed to the KF engine;
312  // NB: this routinwe will call kf->CalculateMagnetOffTransportMatrices() internally;
313  kf->BuildNodeList();
314 
315  //printf(" mCurrMinOkHitCounter -> %d\n", mCurrMinOkHitCounter);
316  //assert(mCurrMinOkHitCounter == 6);
319 
320 #if _OLD_
321  TrKalmanNode *head = static_cast <TrKalmanNode *>(kf->GetHead());
322  //TrKalmanNode *tail = static_cast <TrKalmanNode *>(kf->GetTail());
323  {
324  // THINK: EicBox Generator can not help here, or?;
327  else {
328  // FIXME: pull this out either from MCTrack or from IdealGenTrack (?);
329  assert(0);
330  } //if
331 
332  // Figure out 0-th track approximation based on the track finder guess;
333  // NB: I guess tricks like building linear track approximation will not
334  // improve angular part and will be helpless for momentum determination
335  // anyway -> stick to this procedure; NB: assume FwdHoughTree knows
336  // parameter meaning better that FwdTrackFinder (after all, MappingCall()
337  // is in this class) -> decrypt [theta,phi] pair and call ResetIpNode();
338  double par[GetDdim()];
339 
340  // FIXME: may want to pack this into a separate call later;
341  ResolutionLevel *high = GetLevel(GetLdim()-1);
342  const unsigned *id = match->GetIdPtr();
343  for(unsigned iq=0; iq<GetDdim(); iq++) {
344  const HoughDimension *dimension = GetDimension(iq);
345 
346  par[iq] = dimension->GetMin() + high->GetCellSize(iq)*(id[iq] + 0.5);
347  } //for iq
348 
349  //printf("%f %f\n", par[0], par[1]); exit(0);
350  mTrackFinder->ResetVtxNode(par[0], par[1], _USE_00_);
351  }
352 #endif
353 
354  return true;
355 } // FwdHoughTree::SetupKalmanFilter()
356 
357 // ---------------------------------------------------------------------------------------
358 #if _OFF_
359 void FwdHoughTree::SeparateSiamTracks(MatchCandidate *match, unsigned minHitCount,
360  std::vector<MatchCandidate*> *newMatches)
361 {
362  //return;
363 
364  // FIXME: merge this logic with MatchCandidate::SiamGroupCandidate() later;
365 
366  // Figure out how many valid tracks can be here at all;
367  unsigned overallHitCount = 0;
368  for(int gr=0; gr<GetGdim(); gr++)
369  overallHitCount += match->GetAliveMemberCount(gr);
370 
371  unsigned maxTrackCount = overallHitCount / minHitCount;
372  printf("%d %d\n", overallHitCount, maxTrackCount); //return; //exit(0);
373 
374  assert(maxTrackCount);
375  // Check how many would actually fit the 'minHitCount' requirement;
376  unsigned hCounters[maxTrackCount], toggle = 0;
377  memset(hCounters, 0x00, sizeof(hCounters));
378 
379  for(int gr=0; gr<GetGdim(); gr++) {
380  unsigned multi = match->GetAliveMemberCount(gr);
381 
382  if (multi > maxTrackCount) multi = maxTrackCount;
383  for(unsigned iq=0; iq<multi; iq++) {
384  hCounters[toggle]++;
385  toggle = (toggle+1)%maxTrackCount;
386  } //for iq
387  } //for gr
388 
389  // Figure out really max. possible track count;
390  unsigned splitValue = 0;
391  for(unsigned tr=0; tr<maxTrackCount; tr++)
392  if (hCounters[tr] >= minHitCount)
393  splitValue++;
394 
395  printf("Double counts: %2d %2d\n", hCounters[0], hCounters[1]);
396  printf("Will try to split into %2d tracks\n", splitValue);
397 
398  // Good; now first perform KF fit of the original track; FIXME: unify with
399  // ResolveAmbiguities() call later;
401 
402  printf(" Building KF fit of original siam group track ...\n");
403 
404  // Select useful nodes and construct double linked list;
405  SetupKalmanFilter(match);
406  mTrackFinder->ResetVtxNode(match);
407 
408  {
409  //FwdMatchCandidate *fwdmatch = dynamic_cast<FwdMatchCandidate*>(match);
410 
411  TrKalmanNode *head = static_cast <TrKalmanNode *>(kf->GetHead());
412  TrKalmanNode *tail = static_cast <TrKalmanNode *>(kf->GetTail());
413 
414  // Run manually Kalman filter and smoother passes; no need to remove outliers automatically;
415  kf->FilterPass(head, tail, KalmanFilter::Forward);
416  kf->SmootherPass();
417  //fwdmatch->AssertKalmanFilterPassedFlag();
418 
419  // Loop through all plane groups and find the worst one with hit count
420  // exactly matching the split value;
421  std::multimap<double, unsigned> suggestions;//[mGroups.size()];
422  for(unsigned gr=0; gr<mGroups.size(); gr++) {
423  if (match->GetAliveMemberCount(gr) != splitValue) continue;
424 
425  double smootherDimSum = 0;
426  double smootherChiSquareSum = 0.0;
427  for(unsigned mm=0; mm<match->GetLinearMemberCount(gr); mm++) {
428  GroupMember *member = match->GetSelMember(gr, mm);
429  if (!member) continue;
430 
431  EicDetectorGroup *dgroup = (EicDetectorGroup*)member->mPtr.first;
432  EicTrackingDigiHit *hit = (EicTrackingDigiHit*)member->mPtr.second;
433  SensitiveVolume *sv = dgroup->GetSensitiveVolume(hit);
434 
435  // FIXME: this will not work for "mosaic" configuration; need to keep track on
436  // hit count per "patch";
437  KalmanNodeWrapper *wrapper = sv->GetKfNodeWrapper(hit->GetKfNodeID());
438  assert(wrapper);
439 
440  // FIXME: this will not work for "mosaic" configuration; need to keep track on
441  // hit count per "patch";
442  TrKalmanNode *trknode = static_cast <TrKalmanNode*> (wrapper->GetKfNode(mm));
443  assert(trknode);
444 
445  printf("gr#%02d mm#%02d: %10.7f (%d)\n", gr, mm, trknode->GetSmootherChiSquare(), trknode->GetMdim());
446  smootherDimSum += trknode->GetMdim();
447  smootherChiSquareSum += trknode->GetSmootherChiSquare();
448  } //for mm
449 
450  // Calculate overall chi^2 CCDF;
451  suggestions.insert(std::pair<double,
452  unsigned>(ROOT::Math::chisquared_cdf_c(smootherChiSquareSum, smootherDimSum), gr));
453  } //for gr
454 
455  if (!suggestions.size()) {
456  printf("Give up on this siam group, sorry ...\n");
457  match->SetInactive();
458  return;
459  } //if
460 
461  printf(" -> %d suggestions\n", suggestions.size());
462 
463  // Well, a few matching plane group(s) found; figure out the most attractive one
464  // to be used as the original split; NB: numerical difference can be small (depends
465  // on the tf->SetMeasurementNoiseInflationFactor() setting in reconstruction.C), but
466  // since all errors are blown proportionally the same way, at least among similar
467  // planes it gives a correct worst candidate;
468  for(std::multimap<double, unsigned>::iterator it=suggestions.begin(); it!=suggestions.end(); it++) {
469  printf("%10.7f -> gr#%02d\n", it->first, it->second);
470  } //for it
471 
472  unsigned worstGroup = suggestions.begin()->second;
473  printf(" -> so worst group is gr#%02d\n", worstGroup);
474 
475  // Create split track array and populate it;
476  FwdMatchCandidate *tracks[splitValue];
477  for(unsigned tr=0; tr<splitValue; tr++) {
478  //FwdMatchCandidate *track = tracks[tr] = dynamic_cast<FwdMatchCandidate*>(AllocateMatchCandidate());
479  if (mMatchCandidateCount == mMatchCandidates.size())
481  FwdMatchCandidate *track = tracks[tr] = dynamic_cast<FwdMatchCandidate*>(mMatchCandidates[mMatchCandidateCount]);
482 
483  // THINK: is this all really needed?;
484  //track->ResetOkGroupCounter();
485 
486  for(unsigned gr=0; gr<mGroups.size(); gr++) {
487  unsigned mmCounter = 0;
488 
489  track->ResetMemberCount(gr);
490 
491  // Loop through all member hits of the original (siam) track;
492  for(unsigned mm=0; mm<match->GetLinearMemberCount(gr); mm++) {
493  GroupMember *member = match->GetSelMember(gr, mm);
494  if (!member) continue;
495 
496  // FIXME: here I explicitely use the fact, that alive member count in this
497  // group is exactly equal to split value;
498  if (gr == worstGroup && mmCounter++ != tr) continue;
499 
500  // Copy member pointer over;
501  track->AddMember(gr, member);
502  } //for mm
503  } //for gr
504 
505  // Reset flags and let member hits know they have new candidate owner;
506  track->ShapeItUpForInspection(this, match->GetIdPtr());
508 
509  newMatches->push_back(track);
510  //mgroup->AddCandidate(track);
511  } //for tr
512 
513  // Kill the original siam track; get max alive member count;
514  unsigned maxAliveMemberCount = 0;
515  for(unsigned gr=0; gr<mGroups.size(); gr++) {
516  if (!maxAliveMemberCount || match->GetAliveMemberCount(gr) > maxAliveMemberCount)
517  maxAliveMemberCount = match->GetAliveMemberCount(gr);
518 #if 0
519  for(unsigned mm=0; mm<match->GetLinearMemberCount(gr); mm++) {
520  GroupMember *member = match->GetMember(gr, mm);
521  if (!member) continue;
522 
523  member->mMatchCandidates.erase(match);
524  } //for mm
525 #endif
526  } //for gr
527  match->SetInactive();
528 
529  unsigned unresolvedGroupCount = mGroups.size() - 1;
530  bool resolved[mGroups.size()]; memset(resolved, 0x00, sizeof(resolved));
531  resolved[worstGroup] = true;
532 
533  // Enter infinite loop attempting to distribute other ambiguous hits over
534  // split track candidates; since there can be less hits than tracks in a given
535  // plane group, need to apply tricky ordering scheme;
536  for( ; ; ) {
537  // Arrange temporary stack storage;
538  unsigned mnum[mGroups.size()];
539  double ccdf[mGroups.size()][splitValue][maxAliveMemberCount];
540 
541  // Build new KF fits for all split tracks separately;
542  for(unsigned tr=0; tr<splitValue; tr++) {
543  FwdMatchCandidate *track = tracks[tr];
544 
545  // Select useful nodes and construct double linked list;
546  SetupKalmanFilter(track);
547  mTrackFinder->ResetVtxNode(track);
548 
549  TrKalmanNode *head = static_cast <TrKalmanNode *>(kf->GetHead());
550  TrKalmanNode *tail = static_cast <TrKalmanNode *>(kf->GetTail());
551 
552  // Run manually Kalman filter and smoother passes; no need to remove outliers automatically;
553  kf->FilterPass(head, tail, KalmanFilter::Forward);
554  kf->SmootherPass();
555 
556  printf("tr#%02d -> %f\n", tr, kf->GetFilterChiSquare());
557  for(unsigned gr=0; gr<mGroups.size(); gr++) {
558  if (resolved[gr]) continue;
559 
560  // FIXME: will be assigned more than once; do it better later; can use linear
561  // count here, right (only non-zero members were inserted in split tracks);
562  mnum[gr] = track->GetLinearMemberCount(gr);
563 
564  for(unsigned mm=0; mm<track->GetLinearMemberCount(gr); mm++) {
565  GroupMember *member = track->GetSelMember(gr, mm); assert(member);
566  if (!member) continue;
567 
568  // FIXME: unify this stuff to get down to trknode pointer in a standard call;
569  EicDetectorGroup *dgroup = (EicDetectorGroup*)member->mPtr.first;
570  EicTrackingDigiHit *hit = (EicTrackingDigiHit*)member->mPtr.second;
571  SensitiveVolume *sv = dgroup->GetSensitiveVolume(hit);
572 
573  // FIXME: this will not work for "mosaic" configuration; need to keep track on
574  // hit count per "patch";
575  KalmanNodeWrapper *wrapper = sv->GetKfNodeWrapper(hit->GetKfNodeID());
576  assert(wrapper);
577 
578  // FIXME: this will not work for "mosaic" configuration; need to keep track on
579  // hit count per "patch";
580  TrKalmanNode *trknode = static_cast <TrKalmanNode*> (wrapper->GetKfNode(mm));
581  assert(trknode);
582 
583  // Store CCDF value;
584  ccdf[gr][tr][mm] = ROOT::Math::chisquared_cdf_c(trknode->GetSmootherChiSquare(),
585  trknode->GetMdim());
586  printf("gr#%02d mm#%02d: %10.7f (%d)\n", gr, mm, trknode->GetSmootherChiSquare(), trknode->GetMdim());
587  } //for mm
588  } //for gr
589  } //for tr
590 
591  // Ok, now that all hits' chi^2 CCDF values are stored, loop through
592  // all groups and figure out the "most clean" candidate to be resolved;
593  {
594  bool conflicts[mGroups.size()], cleanGroupsExist = false;
595  memset(conflicts, 0x00, sizeof(conflicts));
596 
597  int hitIds[mGroups.size()][splitValue];
598  for(unsigned gr=0; gr<mGroups.size(); gr++)
599  for(unsigned tr=0; tr<splitValue; tr++)
600  hitIds[gr][tr] = -1;
601 
602  std::multimap<double, unsigned> significances;
603 
604  for(unsigned gr=0; gr<mGroups.size(); gr++) {
605  if (resolved[gr]) continue;
606 
607  bool usedHit[mnum[gr]]; memset(usedHit, 0x00, sizeof(usedHit));
608  bool happyTrack[splitValue]; memset(happyTrack, 0x00, sizeof(happyTrack));
609 
610  // Order hits in all tracks according to their chi^2 CCDF;
611  std::multimap<double, unsigned> tracksToHits[splitValue];
612  // Order tracks at all hits according to their chi^2 CCDF value;
613  //std::multimap<double, unsigned> hitsToTracks[mnum[gr]];
614  for(unsigned tr=0; tr<splitValue; tr++) {
615  for(unsigned mm=0; mm<mnum[gr]; mm++) {
616  tracksToHits[tr].insert(std::pair<double, unsigned>(ccdf[gr][tr][mm], mm));
617 
618  //hitsToTracks[mm].insert(std::pair<double, unsigned>(ccdf[gr][tr][mm], tr));
619  } //for mm
620  } //for tr
621 
622  // Calculate "significance" of the made choice;
623  double significance = 1.0;
624 
625  // Now want to give all tracks their "best matching" hits;
626  for(unsigned tr=0; tr<splitValue; tr++) {
627  for(std::multimap<double, unsigned>::reverse_iterator it = tracksToHits[tr].rbegin();
628  it != tracksToHits[tr].rend(); it++) {
629  // This hit (and possible worse ones) can hardly belong to track anyway -> break;
630  if (!it->first) break;
631 
632  if (!usedHit[it->second]) {
633  // FIXME: for now let the very first track to grab the hit;
634  printf("#gr%02d: setting tr%02d as owner of mm#%02d\n", gr, tr, it->second);
635  hitIds[gr][tr] = it->second;
636 
637  happyTrack[tr] = true;
638  usedHit[it->second] = true;
639 
640  {
641  std::multimap<double, unsigned>::reverse_iterator is = it; is++;
642  printf("#gr%02d, tr%02d: %10.6f, %10.6f\n", gr, tr, it->first, is->first);
643  if (is != tracksToHits[tr].rend()) significance *= is->first/it->first;
644  }
645 
646  break;
647  } //if
648  } //for it
649 
650  // Check for conflicts;
651  if (!happyTrack[tr] || hitIds[gr][tr] != tracksToHits[tr].rbegin()->second) {
652  printf("Track tr#%02d is not happy about gr#%02d\n", tr, gr);
653  conflicts[gr] = true;
654  } //if
655  //else
656  //cleanGroupsExist = true;
657  } //for tr
658 
659  if (!conflicts[gr]) cleanGroupsExist = true;
660 
661  printf("gr#%02d (%d) %10.6f\n", gr, conflicts[gr], significance);
662  significances.insert(std::pair<double, unsigned>(significance, gr));
663  } //for gr
664 
665  printf("Clean group flag: %d\n", cleanGroupsExist);
666  // Choose "weak" group; the smaller significance, the better;
667  int weakGroup = -1; assert(significances.size());
668  for(std::multimap<double, unsigned>::iterator it = significances.begin();
669  it != significances.end(); it++) {
670  //printf("%10.6f -> gr#%02d\n", it->first, it->second);
671  unsigned gr = it->second;
672 
673  if (cleanGroupsExist && conflicts[gr]) continue;
674 
675  weakGroup = gr;
676  break;
677  } //for significances
678  assert(weakGroup != -1);
679  printf("Weak group: %02d\n", weakGroup);
680 
681  // Loop through all tracks and assign hits in this group accordingly;
682  for(unsigned tr=0; tr<splitValue; tr++) {
683  FwdMatchCandidate *track = tracks[tr];
684 
685  for(unsigned mm=0; mm<track->GetLinearMemberCount(weakGroup); mm++) {
686  GroupMember *member = track->GetSelMember(weakGroup, mm); assert(member);
687  if (!member) continue;
688 
689  // NB: track may have not grabbed any hits (if there were less hits
690  // than tracks in this plane group);
691  if (hitIds[weakGroup][tr] == -1 || mm != hitIds[weakGroup][tr])
692  track->ResetMemberPtr(weakGroup, mm);
693  } //for mm
694  } //for tr
695 
696  resolved[weakGroup] = true;
697  unresolvedGroupCount--;
698  }
699 
700  if (!unresolvedGroupCount) break;
701  } //for inf
702 
703  //if (!fwdmatch->HasAmbiguousHits()) return;
704  }
705 } // FwdHoughTree::SeparateSiamTracks()
706 #endif
707 // ---------------------------------------------------------------------------------------
708 
710 {
712 
713  printf(" Resolving ambiguities ...\n");
714 
715  // Select useful nodes and construct double linked list;
716  SetupKalmanFilter(match);
717  mTrackFinder->ResetVtxNode(match);
718 
719  {
720  FwdMatchCandidate *fwdmatch = dynamic_cast<FwdMatchCandidate*>(match);
721 
722  TrKalmanNode *head = static_cast <TrKalmanNode *>(kf->GetHead());
723  TrKalmanNode *tail = static_cast <TrKalmanNode *>(kf->GetTail());
724 
725  // Run manually Kalman filter and smoother passes; no need to remove outliers automatically;
726  kf->FilterPass(head, tail, KalmanFilter::Forward);
727  kf->SmootherPass();
728  fwdmatch->AssertKalmanFilterPassedFlag();
729 
730  if (!fwdmatch->HasAmbiguousHits()) return;
731  }
732 
734  // Try the easiest universal way: remove ambiguous hits with lowest chi^2 CCDF
735  // one by one;
736  std::multimap<double, std::pair<unsigned,unsigned> > ccdfs;
737 
738  for(unsigned gr=0; gr<mGroups.size(); gr++) {
739  // No need to resolve single hit situations -> skip;
740  if (match->GetAliveMemberCount(gr) <= 1) continue;
741 
742  for(unsigned mm=0; mm<match->GetLinearMemberCount(gr); mm++) {
743  TrKalmanNode *trknode = GetKfNode(match, gr, mm);
744 
745  // NB: 'member' could have been equal 0 (see FwdHoughTree::GetKfNode()) -> check on that;
746  if (trknode)
747  ccdfs.insert(std::pair<double, std::pair<unsigned,unsigned> >
748  (trknode->GetSmootherChiSquareCCDF(), std::pair<unsigned,unsigned>(gr,mm)));
749  } //for mm
750  } //for gr
751 
752  // There should be at least one entry;
753  assert(ccdfs.size());
754  std::pair<unsigned,unsigned> worstMember = ccdfs.begin()->second;
755  match->ResetMemberPtr(worstMember.first, worstMember.second);
756  } else {
757  // The other way around: choose groop with the largest gap between best and worst hits;
758  std::multimap<double, unsigned> separations;
759 
760 #ifdef __APPLE__
761  assert(0);
762 #else
763  // Same story as in HoughTree::CheckCell(): need a Mac OS workaround at some point;
764  std::multimap<double, unsigned> inspections[mGroups.size()];
765 
766  for(unsigned gr=0; gr<mGroups.size(); gr++) {
767  // No need to resolve single hit situations -> skip;
768  if (match->GetAliveMemberCount(gr) <= 1) continue;
769 
770  for(unsigned mm=0; mm<match->GetLinearMemberCount(gr); mm++) {
771  TrKalmanNode *trknode = GetKfNode(match, gr, mm);
772 
773  // NB: 'member' could have been equal 0 (see FwdHoughTree::GetKfNode()) -> check on that;
774  if (trknode)
775  inspections[gr].insert(std::pair<double,unsigned>(trknode->GetSmootherChiSquareCCDF(), mm));
776  } //for mm
777 
778  double best = inspections[gr].rbegin()->first, worst = inspections[gr].rend()->first;
779  // THINK: should be done better, otherwise have all chances to pick up a wrong hit; well,
780  // if with a blown up cov.matrix chi^2 is that bad, neither of the hits will fit I guess;
781  //assert(best >= 0.0);
782  //assert(worst >= 0.0);
783  printf("%f %f\n", best, worst); fflush(stdout);
784  double separation = (best > 0.0 && worst > 1E-10) ? best / worst : 1E10;
785  separations.insert(std::pair<double, unsigned>(separation, gr));
786  } //for gr
787 
788  unsigned highestSeparationGroup = separations.rbegin()->second;
789  match->ResetMemberPtr(highestSeparationGroup, inspections[highestSeparationGroup].begin()->second);
790 #endif
791  } //if
792 } // FwdHoughTree::ResolveAmbiguitiesNg()
793 
794 // ---------------------------------------------------------------------------------------
795 
796 TrKalmanNode *FwdHoughTree::GetKfNode(MatchCandidate *match, unsigned gr, unsigned mm)
797 {
798  GroupMember *member = match->GetSelMember(gr, mm);
799  if (!member) return 0;
800 
801  EicDetectorGroup *dgroup = (EicDetectorGroup*)member->mPtr.first;
802  EicTrackingDigiHit *hit = (EicTrackingDigiHit*)member->mPtr.second;
803  SensitiveVolume *sv = dgroup->GetSensitiveVolume(hit);
804 
805  // FIXME: this will not work for "mosaic" configuration; need to keep track on
806  // hit count per "patch";
807  KalmanNodeWrapper *wrapper = sv->GetKfNodeWrapper(hit->GetKfNodeID());
808  assert(wrapper);
809 
810  // FIXME: this will not work for "mosaic" configuration; need to keep track on
811  // hit count per "patch";
812  TrKalmanNode *trknode = static_cast <TrKalmanNode*> (wrapper->GetKfNode(mm));
813  assert(trknode);
814 
815  return trknode;
816 } // FwdHoughTree::GetKfNode()
817 
818 // ---------------------------------------------------------------------------------------
819 
820 #if _OLD_
821 // FIXME: make configurable; NB: these values are bogus anyway until multiple hits
822 // per plane are accounted in a way their errors are blown up properly;
823 #define _CCDF_MIN_ ( 0.010)
824 #define _SEPARATION_MIN_ (10.000)
825 
826 void FwdHoughTree::ResolveAmbiguities(MatchCandidate *match)
827 {
829 
830  printf(" Resolving ambiguities ...\n");
831 
832  // Select useful nodes and construct double linked list;
833  SetupKalmanFilter(match);
834  mTrackFinder->ResetVtxNode(match);
835 
836  {
837  FwdMatchCandidate *fwdmatch = dynamic_cast<FwdMatchCandidate*>(match);
838 
839  TrKalmanNode *head = static_cast <TrKalmanNode *>(kf->GetHead());
840  TrKalmanNode *tail = static_cast <TrKalmanNode *>(kf->GetTail());
841 
842  // Run manually Kalman filter and smoother passes; no need to remove outliers automatically;
843  kf->FilterPass(head, tail, KalmanFilter::Forward);
844  kf->SmootherPass();
845  fwdmatch->AssertKalmanFilterPassedFlag();
846 
847  if (!fwdmatch->HasAmbiguousHits()) return;
848  }
849 
850  //printf("%2d -> chi^2: %7.2f; CCDF: %10.7f\n", kf->GetFilterNdf(), kf->GetFilterChiSquare(),
851  // kf->GetFilterChiSquareCCDF());
852 
853  // Let STL do the job; resolve everything which is well separated
854  // (namely chi^2 CCDF value of best hit is higher threshold and separation
855  // to the "second best hit" is large enough); if no such groups found,
856  // choose the group with largest gap between best and worst hit and remove this
857  // single worst hit;
858  std::multimap<double, unsigned> inspections[mGroups.size()];
859  // Need at least one clean resolution; otherwise fall back to 'separations';
860  bool resolved = false;
861  std::multimap<double, unsigned> separations;
862 
863  // Consider simple algorithm first; just remove all duplicate hits with highest
864  // smoother chi^2 at once; so loop once again through all hits;
865  for(unsigned gr=0; gr<mGroups.size(); gr++) {
866  // No need to resolve single hit situations -> skip;
867  //#if _BACK_
868  if (match->GetAliveMemberCount(gr) <= 1) continue;
869  //#endif
870 
871  for(unsigned mm=0; mm<match->GetLinearMemberCount(gr); mm++) {
872  GroupMember *member = match->GetSelMember(gr, mm);
873  if (!member) continue;
874 
875  EicDetectorGroup *dgroup = (EicDetectorGroup*)member->mPtr.first;
876  EicTrackingDigiHit *hit = (EicTrackingDigiHit*)member->mPtr.second;
877  SensitiveVolume *sv = dgroup->GetSensitiveVolume(hit);
878 
879  // FIXME: this will not work for "mosaic" configuration; need to keep track on
880  // hit count per "patch";
881  KalmanNodeWrapper *wrapper = sv->GetKfNodeWrapper(hit->GetKfNodeID());
882  assert(wrapper);
883 
884  // FIXME: this will not work for "mosaic" configuration; need to keep track on
885  // hit count per "patch";
886  TrKalmanNode *trknode = static_cast <TrKalmanNode*> (wrapper->GetKfNode(mm));
887  assert(trknode);
888  inspections[gr].insert(std::pair<double,
889  unsigned>(ROOT::Math::chisquared_cdf_c(trknode->GetSmootherChiSquare(),
890  trknode->GetMdim()), mm));
891  {
892  Int_t mchitid = hit->GetRefIndex();
893  assert(mchitid >= 0);
894  FairMCPoint *myPoint = (FairMCPoint*)(dgroup->_fMCPoints->At(mchitid));
895  assert(myPoint);
896  printf("gr#%02d, mm#%02d %p (%d,%d) %p -> chi^2 %7.3f, CCDF %10.7f ... %3d\n",
897  gr, mm, member, member->IsBusy(), member->IsBooked(), hit,
898  trknode->GetSmootherChiSquare(),
899  ROOT::Math::chisquared_cdf_c(trknode->GetSmootherChiSquare(), trknode->GetMdim()),
900  myPoint->GetTrackID());
901  }
902  } //for mm
903  //continue;
904 
905  // This means at least one good candidate;
906  //assert(inspections[gr].rbegin()->first);
907  //printf("%f\n",
908  assert(inspections[gr].size());
909  if (inspections[gr].rbegin()->first >= _CCDF_MIN_) {
910  // NB: well, since match->GetAliveMemberCount(gr) check was made I'm guaranteed to have
911  // at least two different entries in this multimap; need separation between the best and
912  // the next-to-best here;
913  std::multimap<double, unsigned>::reverse_iterator it = inspections[gr].rbegin(); it++;
914  //assert(it->first > 0.0);// && inspections[gr].rbegin()->first > 0.0);
915  //assert(inspections[gr].rbegin()->first > 0.0);
916  //+double separation = fabs(log(it->first / inspections[gr].rbegin()->first));
917  double separation = it->first > 0.0 ? fabs(log(it->first / inspections[gr].rbegin()->first)) : 0.0;
918  separations.insert(std::pair<double, unsigned>(separation, gr));
919  printf(" best-to-next separation %10.8f; best is mm#%02d ...\n", separation,
920  inspections[gr].rbegin()->second);
921 
922  // Check whether 1) best node is "good enough", 2) next-to-best is "much worse";
923  // THINK: may want to check, that next-to-best is just "bad" instead of #2?;
924  if (inspections[gr].rbegin()->first >= _CCDF_MIN_ &&
925  separation > _SEPARATION_MIN_) {
926  resolved = true;
927 
928  for(unsigned mm=0; mm<match->GetLinearMemberCount(gr); mm++) {
929  GroupMember *member = match->GetSelMember(gr, mm);
930  if (!member) continue;
931 
932 #if _OLD_
933  if (mm == inspections[gr].rbegin()->second) {
934  // THINK: only after the final fit?;
935  //member->IncrementBusyCounter();
936  }
937  else {
938  match->ResetMemberPtr(gr, mm);
939  //member->mMatchCandidates.erase(match);
940  } //if
941 #else
942  if (mm != inspections[gr].rbegin()->second) match->ResetMemberPtr(gr, mm);
943 #endif
944  } //for mm
945  } //if
946  }
947  else
948  // FIXME: this is just a stupid hack to bypass bad tracks;
949  separations.insert(std::pair<double, unsigned>(0.0, gr));
950  } //for gr
951  //exit(0);
952 
953  // No reliable outlier detection happened in either of the groups; then a fall-back
954  // option: choose the group with highest best-to-worst separation and remove the worst one;
955  if (!resolved) {
956  assert(separations.size());
957  unsigned highestSeparationGroup = separations.rbegin()->second;
958 
959  unsigned mm = inspections[highestSeparationGroup].begin()->second;
960  printf("Highest best-to-worst separation group: gr#%02d -> removing mm#%02d ...\n",
961  highestSeparationGroup, mm);
962  GroupMember *member = match->GetSelMember(highestSeparationGroup, mm); assert(member);
963 
964  match->ResetMemberPtr(highestSeparationGroup, mm);
965  } //if
966 
967  printf("%2d -> chi^2: %7.2f; CCDF: %10.7f\n", kf->GetFilterNdf(), kf->GetFilterChiSquare(),
968  kf->GetFilterChiSquareCCDF());
969 } // FwdHoughTree::ResolveAmbiguities()
970 #endif
971 // ---------------------------------------------------------------------------------------
972 
974  // Otherwise just a dummy call, right?;
975  if (!mFastTreeSearchMode) return;
976 
977  // Not too much efficient to calculate this every time new, but overhead is negligible;
978  double minCCDF = mTrackFinder->GetStoredMinFilterChiSquareCCDF();
979 
980  double bWidth = (log(1.0) - log(minCCDF))/mTrackQualityIterationNum;
981 
982  double ccdf = exp(log(minCCDF) + (mTrackQualityIterationNum - qua - 1)*bWidth);
983  //printf("%f\n", ccdf); exit(0);
984 
986 } // FwdHoughTree::SetupTrackQualityIteration()
987 
988 // ---------------------------------------------------------------------------------------
989 
990 //FIXME: may want to use xs[] update to initialize head node if ambiguity pass was made;
991 
993 {
994  unsigned kfret = 0;
995  FwdMatchCandidate *fwdmatch = dynamic_cast<FwdMatchCandidate*>(match);
997  TrKalmanNode *head = static_cast <TrKalmanNode *>(kf->GetHead());
998 
999  printf(" Final fit started ...\n");
1000 
1001  // NB: by this time filter-smoother chain was run at least once;
1002  for(unsigned itr=0; itr<(mTrackFinder->WithMagneticField() ? 1 : 2); itr++) {
1003  SetupKalmanFilter(match);
1005 
1006  TrKalmanNode *tail = static_cast <TrKalmanNode *>(kf->GetTail());
1007 
1008  // Run Kalman filter and smoother passes with automatic outlier removal; FIXME: min. hit
1009  // count will always be set to mMinOkHitCounter (no matter what the current limit in
1010  // HoughTree loop is); this is sort of a waste of CPU time (so may want to change this
1011  // limit dynamically); but if final fit has less good hits than the current limit, track
1012  // will NOT be considered as good and its hits will not be marked as "occupied" as well;
1013  kfret =
1014  kf->FullChain(head, tail, KalmanFilter::Forward,
1015  kf->GetMinFilterChiSquareCCDF() ? _TRUST_FILTER_FCN_ : 0/*|_TRUST_SMOOTHER_FCN_*/);
1016  } //for itr
1017 
1018  {
1019  // Also figure out most probable MC track prototype; could use 'unordered_map' here;
1020  std::map<unsigned, unsigned> ptracks;
1021  // Will be used in the 2-d step (order tracks based on hit count);
1022  std::multimap<unsigned, unsigned> ordered;
1023 
1024  for(unsigned gr=0; gr<mGroups.size(); gr++) {
1025  // No need to resolve single hit situations -> skip;
1026  assert(match->GetAliveMemberCount(gr) <= 1);
1027 
1028  for(unsigned mm=0; mm<match->GetLinearMemberCount(gr); mm++) {
1029  GroupMember *member = match->GetSelMember(gr, mm);
1030  if (!member) continue;
1031 
1032  {
1033  EicDetectorGroup *dgroup = (EicDetectorGroup*)member->mPtr.first;
1034  EicTrackingDigiHit *hit = (EicTrackingDigiHit*)member->mPtr.second;
1035  SensitiveVolume *sv = dgroup->GetSensitiveVolume(hit);
1036 
1037  KalmanNodeWrapper *wrapper = sv->GetKfNodeWrapper(hit->GetKfNodeID());
1038  assert(wrapper);
1039 
1040  // FIXME: this will not work for "mosaic" configuration; need to keep track on
1041  // hit count per "patch";
1042  TrKalmanNode *trknode = static_cast <TrKalmanNode*> (wrapper->GetKfNode(mm));
1043  assert(trknode);
1044 
1045  Int_t mchitid = hit->GetRefIndex();
1046  assert(mchitid >= 0);
1047  FairMCPoint *myPoint = (FairMCPoint*)(dgroup->_fMCPoints->At(mchitid));
1048  assert(myPoint);
1049  ptracks[myPoint->GetTrackID()]++;
1050  printf(" @@@ gr#%02d, mm#%02d %p (%d,%d) %p -> chi^2 %c%7.3f (%d), CCDF %8.5f ... %3d\n",
1051  gr, mm, member, member->IsBusy(), member->IsBooked(), hit,
1052  trknode->GetSmootherChiSquare() < 100. ? ' ' : '>',
1053  trknode->GetSmootherChiSquare() < 100. ? trknode->GetSmootherChiSquare() : 100.,
1054  trknode->GetMdim(),
1055  ROOT::Math::chisquared_cdf_c(trknode->GetSmootherChiSquare(), trknode->GetMdim()),
1056  myPoint->GetTrackID());
1057  }
1058  } //for mm
1059  } //for gr
1060 
1061  // Loop through all 'ptrack' entries and order them based on hit count;
1062  // FIXME: allocate iterator once;
1063  for(std::map<unsigned, unsigned>::iterator it=ptracks.begin(); it != ptracks.end(); it++) {
1064  ordered.insert(std::pair<unsigned, unsigned>(it->second, it->first));
1065  } //for
1066 
1067  // Decrypt 'ordered' contents;
1068  fwdmatch->SetMcTrackId(ordered.rbegin()->second);
1069  fwdmatch->SetAmbiguityFlag(ordered.size() != 1);
1070  // FIXME: way want to actually calculate all minor contributions;
1071  fwdmatch->SetWrongHitCount(match->GetAliveGroupCount() - ordered.rbegin()->first);
1072  // Well, assume VTX node is the head one?;
1073  fwdmatch->SetVtxCoord(head->GetX0(0) + head->GetXs(0), head->GetX0(1) + head->GetXs(1), head->GetZ());
1074  fwdmatch->SetVtxCoordSigma(sqrt(head->GetCS(0,0)), sqrt(head->GetCS(1,1)));
1075  fwdmatch->SetVtxSlopeSigma(sqrt(head->GetCS(2,2)), sqrt(head->GetCS(3,3)));
1076  if (kf->GetFieldMode() == WithField)
1077  fwdmatch->SetVtxMomentum(head->GetX0(2) + head->GetXs(2), head->GetX0(3) + head->GetXs(3),
1078  head->GetInversedMomentum() + head->GetXs(4));
1079  else
1080  fwdmatch->SetVtxMomentum(head->GetX0(2) + head->GetXs(2), head->GetX0(3) + head->GetXs(3),
1081  head->GetInversedMomentum());
1082  }
1083 
1084  printf(" @@@ %8X -> %2d -> chi^2: %7.2f; CCDF: %10.7f\n", kfret, kf->GetFilterNdf(),
1087 
1088  // If Kalman filter-smoother chain had issues, check all nodes whether
1089  // they still carry "fired" flag; otherwise remove respective member
1090  // from the array and remove entry in member->mMatchCandidates as well;
1091  // FIXME: once debugging is over may want to unify loops, etc;
1092  if (kfret) {
1093  for(unsigned gr=0; gr<mGroups.size(); gr++) {
1094  assert(match->GetAliveMemberCount(gr) <= 1);
1095 
1096  for(unsigned mm=0; mm<match->GetLinearMemberCount(gr); mm++) {
1097  GroupMember *member = match->GetSelMember(gr, mm);
1098  if (!member) continue;
1099 
1100  {
1101  EicDetectorGroup *dgroup = (EicDetectorGroup*)member->mPtr.first;
1102  EicTrackingDigiHit *hit = (EicTrackingDigiHit*)member->mPtr.second;
1103  SensitiveVolume *sv = dgroup->GetSensitiveVolume(hit);
1104 
1105  KalmanNodeWrapper *wrapper = sv->GetKfNodeWrapper(hit->GetKfNodeID());
1106  assert(wrapper);
1107 
1108  // FIXME: this will not work for "mosaic" configuration; need to keep track on
1109  // hit count per "patch";
1110  TrKalmanNode *trknode = static_cast <TrKalmanNode*> (wrapper->GetKfNode(mm));
1111  assert(trknode);
1112  if (trknode->HasHit() && !trknode->IsFired()) match->ResetMemberPtr(gr, mm);
1113  }
1114  } //for mm
1115  } //for gr
1116  } //if
1117 
1118  // Decide whether track is good;
1119  bool trackIsOk = !kfret && match->GetAliveGroupCount() >= mCurrMinOkHitCounter;
1120 
1121  if (trackIsOk) {
1123  kf->GetFilterNdf());
1124 
1125  }
1126  else
1127  match->SetInactive();
1128 } // FwdHoughTree::FinalFit()
1129 
1130 // ---------------------------------------------------------------------------------------
1131