lcsim/src/org/lcsim/recon/tracking/seedtracker
diff -u -r1.15 -r1.16
--- ConfirmerExtender.java 19 Jan 2011 20:44:04 -0000 1.15
+++ ConfirmerExtender.java 28 Feb 2011 19:07:57 -0000 1.16
@@ -1,22 +1,30 @@
/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
+ * ConfirmerExtender.java
*/
package org.lcsim.recon.tracking.seedtracker;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
-import org.lcsim.event.MCParticle;
+import org.lcsim.fit.helicaltrack.HelicalTrackFit;
import org.lcsim.fit.helicaltrack.HelicalTrackFitter.FitStatus;
import org.lcsim.fit.helicaltrack.HelicalTrackHit;
import org.lcsim.recon.tracking.seedtracker.diagnostic.ISeedTrackerDiagnostics;
/**
+ * The ConfirmerExtender class attempts to add hits to an input seed. While the
+ * algorithms used for the confirm and extend phases are identical, there are
+ * small differences in procedures when there are no more tracker layers to check.
+ * The confirm phase simply outputs a list of SeedCandidates that have at least
+ * the minimum number of confirm hits added to the track. The extend phases
+ * completes track finding and imposes the merge criteria to eliminate inferior
+ * track candidates when a pair of candidates shares more than one hit.
*
- * @author cozzy
+ * @author cozzy, Richard Partridge
*/
public class ConfirmerExtender {
@@ -25,6 +33,8 @@
CONFIRM,
EXTEND;
}
+ private int _nfit;
+ private int _maxfit = 1000000000; // initialize maximum number of fits to 10^9
private HitManager _hmanager;
private HelixFitter _fitter;
private MergeSeedLists _merger;
@@ -32,13 +42,10 @@
private ISeedTrackerDiagnostics _diag = null;
/**
- * Constructs the Confirmer/Extender class..
- * @param workinglist The list to work on, either confirmed (extending) or seed (confirming)
- * @param task the task to do (either Task.CONFIRM or Task.EXTEND)
- * @param optimize enables the sort / cutoff optimization
- * @param helixfitter pass the fitter
- * @param hitmanager pass the hit manager
- * @param strategy pass the strategy
+ * Constructor for the ConfirmerExtender class.
+ *
+ * @param hitmanager hit manager that provides access to hits sorted by layer / sector
+ * @param helixfitter helix fitter
*/
public ConfirmerExtender(HitManager hitmanager, HelixFitter helixfitter) {
@@ -49,18 +56,53 @@
/**
* Retrieves the result of the confirmation or extension.
+ *
* @return result The list of confirmed/extended seeds..
*/
public List<SeedCandidate> getResult() {
return _result;
}
+ /**
+ * Set the maximum number of fit trials for a given seed to be confirmed/extended.
+ *
+ * @param maxfit maximum number of trials
+ */
+ public void setMaxFit(int maxfit) {
+ _maxfit = maxfit;
+ }
+
+ /**
+ * Get the number of fit trials for the last confirm/extend.
+ *
+ * @return number of helix fits
+ */
+ public int getNFit() {
+ return _nfit;
+ }
+ /**
+ * Set the diagnostic class to be used (null for no diagnostics).
+ *
+ * @param d diagnostic class
+ */
public void setDiagnostics(ISeedTrackerDiagnostics d) {
_diag = d;
_merger.setDiagnostic(_diag);
}
+ /**
+ * Try to confirm a seed using a specified strategy. The strategy specifies
+ * the layers to use in trying to confirm the seed as well as the minimum number
+ * of confirm layers that were added to the seed. Typically, there will be a
+ * single confirm layer that is required for a seed to be confirmed. Confirmed
+ * seeds must also pass the chisq cut and other constraints specified in the strategy.
+ *
+ * @param seed seed to be confirmed
+ * @param strategy strategy to use for confirming this seed
+ * @param bfield magnetic field
+ * @return true if seed was confirmed
+ */
public boolean Confirm(SeedCandidate seed, SeedStrategy strategy, double bfield) {
// Create a list to hold the confirmed / extended seeds
@@ -76,6 +118,19 @@
return _result.size() > 0;
}
+ /**
+ * Try to extend a seed using a specified strategy. The strategy specifies
+ * the layers to use in extending the seed as well as the minimum number of
+ * layers required for a seed to be a track candidate. Any track candidates
+ * found as a result of the extend operation are merged with the list of
+ * track candidates found so far to eliminate inferior fits when a pair of
+ * track candidates shares more than one hit.
+ *
+ * @param seed seed to be extended
+ * @param strategy strategy to use
+ * @param bfield magnetic field
+ * @param foundseeds list of track candidates found so far
+ */
public void Extend(SeedCandidate seed, SeedStrategy strategy, double bfield,
List<SeedCandidate> foundseeds) {
@@ -90,24 +145,102 @@
return;
}
+ /**
+ * Perform the confirm or extend step.
+ *
+ * @param inputseed seed to be confirmed/extended
+ * @param task confirm or extend (enum)
+ * @param strategy strategy to use
+ * @param bfield magnetic field
+ */
private void doTask(SeedCandidate inputseed, Task task, SeedStrategy strategy, double bfield) {
+ // Initialize the counter for the number of fits performed on this seed
+ _nfit = 0;
+
// Instantiate the fast hit checker
FastCheck checker = new FastCheck(strategy, bfield, _diag);
- // Calculate the minimum number of hits to succeed
+ // Calculate the minimum number of hits to succeed, retrieve the chisq cuts
int minhits = strategy.getMinHits();
if (task == Task.CONFIRM) minhits = strategy.getMinConfirm() + 3;
+ double badhitchisq = strategy.getBadHitChisq();
+ double maxchisq = strategy.getMaxChisq();
- // Create a LIFO queue of seeds to be searched for a confirmation/extension hit
+ // Create a LIFO queue of seeds to be searched for a confirmation/extension
+ // hit (note that a LIFO queue is used to minimize memory usage)
LinkedList<SeedCandidate> seedlist = new LinkedList<SeedCandidate>();
+ // The bestseed is a SeedCandidate the meets the requirements for becoming
+ // a track, shares at least one hit with the inputseed, and has been deemed
+ // the best such seed by the track merging criteria
+ //
+ // Initialize the best seed to null
+ SeedCandidate bestseed = null;
+
+ // If we have already found track candidates, check for duplicates
+ // that share hits with the seed, finding the best such duplicate candidate.
+ for (SeedCandidate trkcand : _result) {
+ if (_merger.isDuplicate(inputseed, trkcand))
+ bestseed = findBestCandidate(trkcand, bestseed, strategy);
+ }
+
+ // Create a map between the SeedLayers to be checked and a list of hits on the layer to check
+ Map<SeedLayer, List<HelicalTrackHit>> hitmap = new HashMap<SeedLayer, List<HelicalTrackHit>>();
+
+ // Loop over the layers to be checked
+ for (SeedLayer lyr : inputseed.getUncheckedLayers()) {
+
+ // Create a list of hits to check on this layer
+ List<HelicalTrackHit> hitlist = new ArrayList<HelicalTrackHit>();
+
+ // Loop over the sectors on this layer to collect hits to check
+ for (Sector sector : _hmanager.getSectors(lyr)) {
+
+ // If there are no hits, skip this sector
+ if (sector.Hits().isEmpty()) continue;
+
+ // See if this sector is consistent with this seed
+ if (!checker.CheckSector(inputseed, sector)) continue;
+
+ // Add the hits for this sector
+ hitlist.addAll(sector.Hits());
+ }
+
+ // Save the list of hits in the hitmap
+ if (!hitlist.isEmpty()) hitmap.put(lyr, hitlist);
+ }
+
+ // Create a list of layers that have hits to check
+ List<SeedLayer> lyrlist = new ArrayList<SeedLayer>();
+ lyrlist.addAll(hitmap.keySet());
+
+ // Sort the layers in order of increasing number of hits
+ SortLayers lyrsort = new SortLayers(hitmap);
+ Collections.sort(lyrlist, lyrsort);
+
+ // Store the layers to check in the seed
+ inputseed.setUncheckedLayers(lyrlist);
+
// Start with the input seed
seedlist.add(inputseed);
// Keep looping until we have fully processed all seed candidates
while (seedlist.size() > 0) {
+ // If we have exceeded the maximum number of fits, print warning and stop processing seed candidates
+ if (_nfit > _maxfit) {
+ System.out.println("Maximum number of fits exceeded in "+task.toString()+" step");
+ if (bestseed == null) {
+ System.out.println("No track candidates are associated with the seed hits");
+ } else {
+ System.out.println("Track candidate with "+bestseed.getHits().size()+
+ " hits and chisq of "+bestseed.getHelix().chisqtot()+
+ " associated with the seed hits");
+ }
+ break;
+ }
+
// Pull the last seed off the queue (use a LIFO queue to minimize queue length)
SeedCandidate seed = seedlist.poll();
@@ -116,6 +249,22 @@
int possiblehits = lyrsleft + seed.getHits().size();
if (possiblehits < minhits) continue;
+ // If there is a best fit candidate, see if there is still a chance of beating it
+ if (bestseed != null) {
+
+ // If the maximimum hits we can achieve is >1 fewer than the best fit, skip this candidate
+ int besthits = bestseed.getHits().size();
+ if (possiblehits < besthits - 1) continue;
+
+ // If the maximum hits we can achieve equals the best fit, skip if we have a worse chi2
+ double chisq = seed.getHelix().chisqtot();
+ double bestchisq = seed.getHelix().chisqtot();
+ if ((possiblehits == besthits) && chisq > bestchisq) continue;
+
+ // If the maximum hits we can achieve is 1 fewer than the best fit, skip if the bad hit criteria can't be met
+ if ((possiblehits == besthits - 1) && (chisq > bestchisq - badhitchisq)) continue;
+ }
+
// See if there are any layers left for confirm/extend
if (lyrsleft == 0) {
@@ -128,54 +277,31 @@
} else if (task == Task.EXTEND) {
// Merge the seed into the list of extended seeds
- _merger.Merge(_result, seed, strategy);
+ boolean merged = _merger.Merge(_result, seed, strategy);
+
+ // If the seed survived the merge, make it our new best candidate
+ if (merged) bestseed = findBestCandidate(seed, bestseed, strategy);
}
+
+ // Done with this seed
continue;
}
- // Pull the next layer off the queue
+ // Pull the next layer off the queue
SeedLayer lyr = seed.getNextLayer();
-
- // Get the list of sectors associated with this layer
- List<Sector> sectorlist = _hmanager.getSectors(lyr);
+ HelicalTrackFit helix = seed.getHelix();
// Retrieve the chisq for the last fit and initialize the best fit chisq for this layer
- double oldchisq = seed.getHelix().chisqtot();
- double oldcirclechisq = seed.getHelix().chisq()[0];
+ double oldchisq = helix.chisqtot();
+ double oldcirclechisq = helix.chisq()[0];
double chisqbest = 1.e99;
- // Create a list of hits to check for this layer / seed
- List<HelicalTrackHit> hitlist = new ArrayList<HelicalTrackHit>();
-
- // Loop over the sectors with hits
- for (Sector sector : sectorlist) {
-
- // Check that this candidate is compatible with the sector
- if (!checker.CheckSector(seed, sector)&&_hmanager.getDoSectoring()){
-// if (seed.isTrueSeed()) {
-// for (HelicalTrackHit hit : sector.Hits()) {
-// for (MCParticle mcp : hit.getMCParticles()) {
-// if (seed.getMCParticles().contains(mcp)) {
-// System.out.println("Found a true hit in an incompatible sector!!");
-// System.out.println("Sector phi lo: "+sector.philo()+" hi: "+sector.phihi());
-// System.out.println("Sector z lo: "+sector.zlo()+" hi: "+sector.zhi());
-// System.out.println("Hit: "+hit.toString());
-// }
-// }
-// }
-// }
- continue;
- }
-
- // Add the hits for this confirmation/extension sector to the hitlist
- hitlist.addAll(sector.Hits());
- }
-
- SortHits comp = new SortHits(seed.getHelix());
+ // Get the list of hits to check for this layer and sort them by x-y distance from current helix
+ List<HelicalTrackHit> hitlist = hitmap.get(lyr);
+ SortHits comp = new SortHits(helix);
Collections.sort(hitlist, comp);
// Loop over the sorted hits in this layer
- boolean foundtrueseed = false;
for (HelicalTrackHit hit : hitlist) {
// Make a test seed including the new hit
@@ -188,48 +314,59 @@
continue;
}
- if (test.isTrueSeed()) foundtrueseed = true;
-
- // See if we have a successful fit
+ // Fit the test seed
boolean success = _fitter.FitCandidate(test, strategy);
+ _nfit++;
+
+ // Check if the fit was successful
if (success) {
- // Attach the fit to the test seed
- test.setHelix(_fitter.getHelix());
+ // Success - attach the fit to the test seed
+ HelicalTrackFit newhelix = _fitter.getHelix();
+ test.setHelix(newhelix);
- // Add the seed to the LIFO queue of seed candidates
+ // Add the seed to the LIFO queue of seed candidates and update the best chisq
seedlist.addFirst(test);
- chisqbest = Math.min(chisqbest, _fitter.getHelix().chisqtot());
+ chisqbest = Math.min(chisqbest, newhelix.chisqtot());
} else {
// Stop checking hits in this layer if circle chisq increase is too big
if (_fitter.getFitStatus() != FitStatus.CircleFitFailed) {
double circlechisq = _fitter.getCircleFit().chisq();
- if (circlechisq > oldcirclechisq + strategy.getMaxChisq()) {
- if (_diag != null && seed.isTrueSeed() && !foundtrueseed) {
- for (HelicalTrackHit hitchk : hitlist) {
- for (MCParticle mcp : hitchk.getMCParticles()) {
- if (seed.getMCParticles().contains(mcp)) {
- System.out.println("Stopped checking hits when a true hit was available");
- System.out.println("p: "+mcp.getMomentum().toString());
- }
- }
- }
- }
- break;
- }
+ if (circlechisq > oldcirclechisq + maxchisq) break;
}
}
}
- // Finished checking hits in the current layer
- // If all fit tries for this layer are potentially bad hits, include the starting seed in the list
+ // Finished checking hits in the current layer. If all the fit trials for
+ // this layer are potentially bad hits, include the starting seed (less the
+ // current layer, which was popped off the layer queue) in the seed list.
if (chisqbest - oldchisq > strategy.getBadHitChisq()) seedlist.addFirst(seed);
}
- // Finished looping over the seeds in the LIFO candidate queue
+ // Finished looping over the seeds in the LIFO candidate queue - we are done!
return;
}
+
+ /**
+ * Check two track candidates and return the best one subject to the merging criteria.
+ *
+ * @param trial new candidate to try
+ * @param oldbest previous best candidate (or null if no best candidate has been found)
+ * @param strategy strategy in use
+ * @return best track candidate
+ */
+ private SeedCandidate findBestCandidate(SeedCandidate trial, SeedCandidate oldbest, SeedStrategy strategy) {
+
+ // If the old best candidate is null, return the trial candidate
+ if (oldbest == null) return trial;
+
+ // If the trial candidate is better, return it
+ if (_merger.isBetter(trial, oldbest, strategy)) return trial;
+
+ // If no improvement, return the old candidate
+ return oldbest;
+ }
}
\ No newline at end of file