diff --git a/online-monitoring/src/main/java/org/hps/monitoring/drivers/trackrecon/KFSVTOpeningAlignment.java b/online-monitoring/src/main/java/org/hps/monitoring/drivers/trackrecon/KFSVTOpeningAlignment.java index 1ae98b696e..724404c7c8 100644 --- a/online-monitoring/src/main/java/org/hps/monitoring/drivers/trackrecon/KFSVTOpeningAlignment.java +++ b/online-monitoring/src/main/java/org/hps/monitoring/drivers/trackrecon/KFSVTOpeningAlignment.java @@ -280,7 +280,7 @@ protected void detectorChanged(Detector detector) { kPar = new KalmanParams(); kPar.print(); - KI = new KalmanInterface(false, kPar, fm); + KI = new KalmanInterface(false, kPar, detector, fm); KI.createSiModules(detPlanes); // plotterParsBot = pfac.create("Bot Track Pars"); diff --git a/recon/src/main/java/org/hps/recon/particle/ReconstructedParticleRefitter.java b/recon/src/main/java/org/hps/recon/particle/ReconstructedParticleRefitter.java new file mode 100644 index 0000000000..ddcd4fe765 --- /dev/null +++ b/recon/src/main/java/org/hps/recon/particle/ReconstructedParticleRefitter.java @@ -0,0 +1,574 @@ +package org.hps.recon.particle; + +import static java.lang.Math.abs; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.apache.commons.math.util.FastMath; +import org.hps.recon.tracking.MaterialSupervisor; +import org.hps.recon.tracking.MaterialSupervisor.ScatteringDetectorVolume; +import org.hps.recon.tracking.MaterialSupervisor.SiStripPlane; +import org.hps.recon.tracking.kalman.KalmanInterface; +import org.hps.recon.tracking.kalman.KalmanParams; +import org.hps.recon.tracking.kalman.KalmanPatRecDriver; +import org.lcsim.detector.DetectorElementStore; +import org.lcsim.detector.IDetectorElement; +import org.lcsim.detector.identifier.IExpandedIdentifier; +import org.lcsim.detector.identifier.IIdentifier; +import org.lcsim.detector.identifier.IIdentifierDictionary; +import org.lcsim.detector.tracker.silicon.SiSensor; +import org.lcsim.event.Cluster; +import org.lcsim.event.EventHeader; +import org.lcsim.event.LCRelation; +import org.lcsim.event.MCParticle; +import org.lcsim.event.RawTrackerHit; +import org.lcsim.event.ReconstructedParticle; +import org.lcsim.event.RelationalTable; +import org.lcsim.event.SimTrackerHit; +import org.lcsim.event.Track; +import org.lcsim.event.TrackState; +import org.lcsim.event.TrackerHit; +import org.lcsim.event.base.BaseReconstructedParticle; +import org.lcsim.event.base.BaseRelationalTable; +import org.lcsim.geometry.Detector; +import org.lcsim.geometry.IDDecoder; +import org.lcsim.util.Driver; +import org.lcsim.util.aida.AIDA; + +/** + * This Driver is used to improve the measurement of ReconstructedParticles + * composed of a Track and an associated Ecal Cluster by including the energy of + * the cluster in a global refit. + * + * A new collection of ReconstructedParticles is added to the event. + * + * @authors Norman A. Graf and Robert P. Johnson + * + */ +public class ReconstructedParticleRefitter extends Driver { + + /** + * The histogram handler + */ + private AIDA aida = AIDA.defaultInstance(); + private String outputFileName = "ReconPartRefit.root"; + /** + * The name of the input ReconstructedParticle collection to process + */ + private String _finalStateParticleCollectionName = "FinalStateParticles_KF"; + + /** + * The name of the output ReconstructedParticle collection to add to the + * event + */ + private String _refitParticleCollectionName = "FinalStateParticles_KF_refit"; + + /** + * The tolerance on E/p for tracks and clusters to make sure we are fitting + * to a showering electron or positron and not a MIP trace or poorly matched + * combination + */ + private double _eOverpCut = 0.1; + private boolean _doEconstraint = true; + private boolean _cheat = false; + private Random ran; + + /** + * The interface to the Kalman Filter code + */ + private KalmanInterface KI; + private KalmanParams kPar = null; + private org.lcsim.geometry.FieldMap fm; + private static Logger logger; + private Level _logLevel = Level.WARNING; + private IDDecoder decoder; + + private double _eRes0 = -1.0; + private double _eRes1 = -1.0; + private double _eRes2 = -1.0; + + private static final boolean debug = false; + /** + * Feature to allow setting the log level from steering + * @param logLevel + */ + public void set_logLevel(String logLevel) { + System.out.format("ReconstructedParticleRefitter: setting the logger level to %s\n", logLevel); + _logLevel = Level.parse(logLevel); + System.out.format(" logger level = %s\n", _logLevel.getName()); + } + /** + * Option to fake the ECAL info with a Gaussian random number, for testing + * @param b true to fake the ECAL info using MC truth + */ + public void set_cheat(boolean b) { + System.out.format("ReconstructedParticleRefitter: setting control parameter for using MC truth for the energy constraint to %b\n", b); + _cheat = b; + } + /** + * Control whether the ECAL energy constraint is applied to refitted tracks + * @param b true to refit with ECAL constraint + */ + public void set_doEconstraint(boolean b) { + System.out.format("ReconstructedParticleRefitter: setting control parameter for doing the ECAL constraint to %b\n", b); + _doEconstraint = b; + } + + private ArrayList layerSkip = null; + public void set_removeLayer(int lyr) { + if (layerSkip == null) { + layerSkip = new ArrayList(); + } + layerSkip.add(lyr); + System.out.format("ReconstructedParticleRefitter: remove layer %d from the fit.\n", lyr); + } + /** + * ECal energy resolution parameterization, for the resolution as a % of E. + * @param eRes0 coefficient of the 1/E term + */ + public void set_eRes0(double eRes0) { + System.out.format("ReconstructedParticleRefitter: setting the eRes0 ECAL resolution parameter to %9.4f\n", eRes0); + _eRes0 = eRes0; + } + /** + * ECal energy resolution parameterization, for the resolution as a % of E. + * @param eRes1 coefficient of the 1/sqrt(E) term + */ + public void set_eRes1(double eRes1) { + System.out.format("ReconstructedParticleRefitter: setting the eRes1 ECAL resolution parameter to %9.4f\n", eRes1); + _eRes1 = eRes1; + } + /** + * ECal energy resolution parameterization, for the resolution as a % of E. + * @param eRes2 the constant term + */ + public void set_eRes2(double eRes2) { + System.out.format("ReconstructedParticleRefitter: setting the eRes2 ECAL resolution parameter to %9.4f\n", eRes2); + _eRes2 = eRes2; + } + + /** + * Set up the geometry and parameters for the Kalman-filter track finding and fitting. + */ + @Override + public void detectorChanged(Detector det) { + logger = Logger.getLogger(KalmanPatRecDriver.class.getName()); + if (_logLevel != null) { + logger.setLevel(_logLevel); + //LogManager.getLogManager().getLogger(Logger.GLOBAL_LOGGER_NAME).setLevel(_logLevel); + } + System.out.format("ReconstructedParticleRefitter: entering detectorChanged, logger level = %s\n", logger.getLevel().getName()); + MaterialSupervisor materialManager; + materialManager = new MaterialSupervisor(); + materialManager.buildModel(det); + ran = new Random(); + if (layerSkip != null) { + for (int lyr : layerSkip) { + System.out.format("ReconstructedParticleRefitter: layer %d will be removed in track refits\n", lyr); + } + } + decoder = det.getSubdetector("Tracker").getIDDecoder(); + + // Instantiate the interface to the Kalman-Filter code and set up the run parameters + kPar = new KalmanParams(); + // Override the default resolution parameters with numbers from the steering file + if (_eRes0 > 0. || _eRes1 > 0. || _eRes2 > 0.) kPar.setEnergyRes(_eRes0, _eRes1, _eRes2); + + fm = det.getFieldMap(); // The HPS magnetic field map + KI = new KalmanInterface(kPar, det, fm); // Instantiate the Kalman interface + ArrayList detPlanes = new ArrayList(); + List materialVols = ((MaterialSupervisor) (materialManager)).getMaterialVolumes(); + for (ScatteringDetectorVolume vol : materialVols) { + detPlanes.add((SiStripPlane)(vol)); + } + KI.createSiModules(detPlanes); // The detector geometry objects used by the Kalman code + } + + /** + * The action per event + * @param event The hps-java event header + */ + public void process(EventHeader event) { + if (debug) { + System.out.println("Entering process"); + if (event.hasCollection(LCRelation.class, "SVTTrueHitRelations")) { + System.out.println("SVTTrueHitRelations are present"); + } + if (event.hasCollection(SimTrackerHit.class, "TrackerHits")) { + System.out.println("Sim TrackerHits are present"); + } + if (event.hasCollection(MCParticle.class, "MCParticles")) { + System.out.println("MCParticles are present"); + } + if (event.hasCollection(LCRelation.class, "KalmanFullTracksToTruthTrackRelations")) { + System.out.println("KalmanFullTracksToTruthTrackRelations are present"); + } + } + if (event.hasCollection(ReconstructedParticle.class, _finalStateParticleCollectionName)) { + // setup the hit-to-sensor associations + // should not need to do this if not reading in events from disk + setupSensors(event); + // instantiate the output collection of new ReconstructedParticles + List refitReconstructedParticles = new ArrayList<>(); + //fetch the input list of ReconstructedParticles to process + List rpList = event.get(ReconstructedParticle.class, _finalStateParticleCollectionName); + for (ReconstructedParticle rp : rpList) { + // skip particles without a track, i.e. photons + if (rp.getParticleIDUsed().getPDG() != 22) { + // skip particles without an associated cluster + if (!rp.getClusters().isEmpty()) { + // cut on on E/p so we don't try to fit to the energy of MIP tracks + double eOverP = rp.getEnergy() / rp.getMomentum().magnitude(); + aida.histogram1D("e over p before refit", 100, 0., 2.).fill(eOverP); + if (debug) System.out.format("ReconstructedParticleRefitter: event %d, E/P=%10.5f, cut=%10.5f, E=%10.5f, p=%10.5f\n", event.getEventNumber(), eOverP, _eOverpCut, rp.getEnergy(), rp.getMomentum().magnitude()); + if (abs(eOverP - 1.0) < _eOverpCut) { + // Get the old track info + Track oldTrack = rp.getTracks().get(0); + TrackState oldTsAtIP = null; + for (TrackState ts : oldTrack.getTrackStates()) { + if (ts.getLocation() == TrackState.AtIP) { + oldTsAtIP = ts; + break; + } + } + if (oldTsAtIP == null) oldTsAtIP = oldTrack.getTrackStates().get(0); + double[] oldParams = oldTsAtIP.getParameters(); + Track track = rp.getTracks().get(0); + int nHits = track.getTrackerHits().size(); + if (debug) { + boolean skipped = false; + int lastLyr = -1; + for (TrackerHit hit : track.getTrackerHits()) { + List rawHits = hit.getRawHits(); + int Layer = -1; + for (RawTrackerHit rawHit : rawHits) { + long ID = rawHit.getCellID(); + decoder.setID(ID); + Layer = decoder.getValue("layer") - 1; + } + if (lastLyr >= 0) { + if (lastLyr != Layer - 1) skipped = true; + } + lastLyr = Layer; + } + System.out.format("event %d, %d track hits, nDOF=%d, skipped layer=%b\n", event.getEventNumber(), nHits, track.getNDF(), skipped); + } + int nDOF = nHits-5; + + // create a new ReconstructedParticle here... + ReconstructedParticle refitParticle = makeNewReconstructedParticle(event, rp); + if (refitParticle.getTracks().size() == 0) { + if (debug) System.out.println(" Track refit failed"); + return; + } + refitReconstructedParticles.add(refitParticle); + Track newTrack = refitParticle.getTracks().get(0); + TrackState newTsAtIP = null; + for (TrackState ts : newTrack.getTrackStates()) { + if (ts.getLocation() == TrackState.AtIP) { + newTsAtIP = ts; + break; + } + } + if (newTsAtIP == null) newTsAtIP = newTrack.getTrackStates().get(0); + double[] newParams = newTsAtIP.getParameters(); + aida.histogram1D("New helix d0", 100, -5., 5.).fill(newParams[0]); + aida.histogram1D("Old helix d0", 100, -5., 5.).fill(oldParams[0]); + aida.histogram1D("Helix parameter d0 new minus old over old", 100, -2.5, 2.5).fill((newParams[0]-oldParams[0])/oldParams[0]); + aida.histogram1D("Helix parameter phi0 new minus old over old", 100, -0.5, 0.5).fill((newParams[1]-oldParams[1])/oldParams[1]); + aida.histogram1D("Helix parameter omega new minus old over old", 100, -0.5, 0.5).fill((newParams[2]-oldParams[2])/oldParams[2]); + aida.histogram1D("Helix parameter z0 new minus old over old", 100, -0.5, 0.5).fill((newParams[3]-oldParams[3])/oldParams[3]); + aida.histogram1D("Helix parameter tan(lambda) new minus old over old", 100, -0.5, 0.5).fill((newParams[4]-oldParams[4])/oldParams[4]); + aida.histogram1D("old track chi2 per dof", 100, 0., 50.).fill(oldTrack.getChi2()/nDOF); + aida.histogram1D("old track number degrees of freedom",20,0.,20.).fill(nDOF); + aida.histogram1D("new track chi2 per dof", 100, 0., 50.).fill(newTrack.getChi2()/newTrack.getNDF()); + aida.histogram1D("new track number degrees of freedom",20,0.,20.).fill(newTrack.getNDF()); + double [] P = newTsAtIP.getMomentum(); + double pMag = FastMath.sqrt(P[0]*P[0]+P[1]*P[1]+P[2]*P[2]); + double changeInP = pMag/rp.getMomentum().magnitude(); + aida.histogram1D("new momentum over old momentum", 100, 0.5, 1.5).fill(changeInP); + if (Math.abs(changeInP-1.0) > 0.04) { + aida.histogram1D("Bad new track chi2 per dof", 100, 0., 50.).fill(newTrack.getChi2()/newTrack.getNDF()); + aida.histogram1D("Bad new track number degrees of freedom",20,0.,20.).fill(newTrack.getNDF()); + aida.histogram1D("Bad helix parameter d0 new minus old over old", 100, -2.5, 2.5).fill((newParams[0]-oldParams[0])/oldParams[0]); + aida.histogram1D("Bad helix parameter phi0 new minus old over old", 100, -0.5, 0.5).fill((newParams[1]-oldParams[1])/oldParams[1]); + aida.histogram1D("Bad helix parameter z0 new minus old over old", 100, -0.5, 0.5).fill((newParams[3]-oldParams[3])/oldParams[3]); + + } + aida.histogram1D("new particle E over p", 100, 0., 2.).fill(rp.getEnergy()/pMag); + if (debug) System.out.format("ReconstructedParticleRefitter: Event %d, eOverP=%10.4f, changeInP=%10.4f\n", + event.getEventNumber(), eOverP, changeInP); + + // Comparisons for MC events + MCParticle theMatch = getMCmatch(event, rp); + if (theMatch != null) { + double eMC = theMatch.getEnergy(); + double newPoverEMC = pMag/eMC; + if (debug) System.out.format(" MC match: p/E MC = %10.4f\n", newPoverEMC); + aida.histogram1D("new momentum over MC energy", 100, 0.5, 1.5).fill(newPoverEMC); + aida.histogram1D("old momentum over MC energy", 100, 0.5, 1.5).fill(rp.getMomentum().magnitude()/eMC); + aida.histogram1D("ECAL over MC energy", 100, 0.5, 1.5).fill(rp.getEnergy()/eMC); + TrackState newTsAtLastHit = null; + for (TrackState ts : newTrack.getTrackStates()) { + if (ts == null) { + System.out.format("ReconstructedParticle Refitter: event %d, null Trackstate pointer!\n", event.getEventNumber()); + break; + } + if (debug) System.out.format("ReconstructedParticle Refitter: trackstate at %d of %d\n", ts.getLocation(), newTrack.getTrackStates().size()); + if (ts.getLocation() == TrackState.AtLastHit) { + newTsAtLastHit = ts; + break; + } + } + if (newTsAtLastHit != null) { + double [] Plast = newTsAtLastHit.getMomentum(); + double pMagLst = FastMath.sqrt(Plast[0]*Plast[0]+Plast[1]*Plast[1]+Plast[2]*Plast[2]); + if (debug) { + double [] refPnt = newTsAtLastHit.getReferencePoint(); + double [] helix = newTsAtLastHit.getParameters(); + System.out.format("Event %d at last hit, p=%10.5f, ref=%8.3f %8.3f %8.3f\n", event.getEventNumber(), pMagLst, refPnt[0], refPnt[1], refPnt[2]); + System.out.format(" Helix at last hit = %9.5f %9.5f %9.5f %9.5f %9.5f\n", helix[0], helix[1], helix[2], helix[3], helix[4]); + } + aida.histogram1D("p at last hit over MC energy", 100, 0.5, 1.5).fill(pMagLst/eMC); + double pFrstvsPlst = (pMag - pMagLst)/pMagLst; + aida.histogram1D("fractional change of p from smoothing", 100, -0.1, 0.1).fill(pFrstvsPlst); + } + } + } else { + refitReconstructedParticles.add(rp); + } + } else { + refitReconstructedParticles.add(rp); + } + } else { + refitReconstructedParticles.add(rp); + } + } + // add the new collection to the event + event.put(_refitParticleCollectionName, refitReconstructedParticles, ReconstructedParticle.class, 0); + } + KI.clearInterface(); + } + + /** + * + * The method to create a new ReconstructedParticle by refitting the track + * along with the cluster energy + * + * @param rp the ReconstructedParticle to refit + * @return + */ + private ReconstructedParticle makeNewReconstructedParticle(EventHeader event, ReconstructedParticle rp) { + // Create a reconstructed particle to represent the track. + ReconstructedParticle particle = new BaseReconstructedParticle(); + if (debug) System.out.format("Entering makeNewReconstructedParticle for event %d\n", event.getEventNumber()); + Cluster cluster = rp.getClusters().get(0); + // refit the track + Track newTrack = refitTrack(event, rp); + if (newTrack == null) { + if (debug) System.out.format("makeNewReconstrucedParticle: failed to make new track in event %d\n", event.getEventNumber()); + return particle; + } + // Store the track in the particle. + particle.addTrack(newTrack); + if (debug) { + double [] trackP = newTrack.getTrackStates().get(0).getMomentum(); + double P=FastMath.sqrt(trackP[0]*trackP[0]+trackP[1]*trackP[1]+trackP[2]*trackP[2]); + int nHits = newTrack.getTrackerHits().size(); + int nDOF = newTrack.getNDF(); + System.out.format("makeNewReconstructedParticle: track p=%10.4f, %d hits, NDF=%d \n", P, nHits, nDOF); + } + + // Set the type of the particle. This is used to identify the tracking + // strategy used in finding the track associated with this particle. + // Modify this to flag that we have refit with the energy. + // For now, just add 1000 + ((BaseReconstructedParticle) particle).setType(1000 + newTrack.getType()); + ((BaseReconstructedParticle) particle).setParticleIdUsed(new SimpleParticleID(rp.getParticleIDUsed().getPDG(), 0, 0, 0)); + // add cluster to the particle: + particle.addCluster(cluster); + // will need to set the RP fourVector, charge, etc. + // for now, leave as zero. + // TODO discuss what the best measurement of this is + + return particle; + } + + private MCParticle getMCmatch(EventHeader event, ReconstructedParticle rp) { + MCParticle theMatch = null; + RelationalTable rawtomc = null; + if (event.hasCollection(LCRelation.class, "SVTTrueHitRelations")) { + rawtomc = new BaseRelationalTable(RelationalTable.Mode.MANY_TO_MANY, RelationalTable.Weighting.UNWEIGHTED); + List trueHitRelations = event.get(LCRelation.class, "SVTTrueHitRelations"); + for (LCRelation relation : trueHitRelations) { + if (relation != null && relation.getFrom() != null && relation.getTo() != null) { + rawtomc.add(relation.getFrom(), relation.getTo()); + } + } + } else { + return theMatch; + } + if (debug) System.out.println("getMCmatch: relation table constructed."); + ArrayList pMC = new ArrayList(1); + ArrayList cnt = new ArrayList(1); + Track track = rp.getTracks().get(0); + List hitsOnTrack = track.getTrackerHits(); + for (TrackerHit hit : hitsOnTrack) { + List rawHits = hit.getRawHits(); + for (RawTrackerHit rawHit : rawHits) { + Set simHits = rawtomc.allFrom(rawHit); + for (SimTrackerHit simHit : simHits) { + MCParticle mcp = simHit.getMCParticle(); + if (!pMC.contains(mcp)) { + pMC.add(mcp); + cnt.add(1); + } else { + int idx = pMC.indexOf(mcp); + cnt.set(idx,cnt.get(idx)+1); + } + } + } + } + if (debug) System.out.format("getMCmatch: %d MC matches found\n", pMC.size()); + int maxCnt = 0; + for (int i=0; i maxCnt) { + maxCnt = cnt.get(i); + theMatch = pMC.get(i); + } + } + return theMatch; + } + + /** + * ECAL energy resolution + * + * @param ECAL energy + * @return expected sigma of the energy measurement + */ + static double sigmaE(double E) { + double r1 = 1.62/E; + double r2 = 2.87/FastMath.sqrt(E); + double r3 = 2.5; + double res = FastMath.sqrt(r1*r1 + r2*r2 + r3*r3)/100.; + return res*E; + } + /** + * Method for refitting a track + * + * @param rp The input ReconstructedParticle with track and matching cluster + * @return the newly refit track including the cluster energy + */ + private Track refitTrack(EventHeader event, ReconstructedParticle rp) { + Track track = rp.getTracks().get(0); + Cluster cluster = rp.getClusters().get(0); + //the energy of the associated cluster + double energy = cluster.getEnergy(); + if (debug) { + int nHits = track.getTrackerHits().size(); + System.out.format("refitTrack in event %d with %d hits, chi2=%8.3f for %d dof, cluster E=%9.4f\n", event.getEventNumber(), nHits, track.getChi2(), track.getNDF(), energy); + } + // Fit a new track with this list of hits and the cluster energy. + if (_cheat) { + MCParticle theMatch = getMCmatch(event, rp); + if (theMatch != null) { + double energyMC = theMatch.getEnergy(); + energy = energyMC + sigmaE(energyMC)*ran.nextGaussian(); + if (debug) System.out.format("refitTrack: MC cheat energy = %10.5f+=%9.5f\n", energy, sigmaE(energyMC)); + aida.histogram1D("Cheat ECAL over MC energy", 100, 0.5, 1.5).fill(energy/energyMC); + } else { + System.out.format("refitTrack: no MC match was found\n"); + } + } + if (layerSkip == null) layerSkip = new ArrayList(); + return KI.refitTrackWithE(event, track, energy, sigmaE(energy), _doEconstraint, layerSkip); + } + + /** + * Method to associate SVT hits to sensors + * + * @param event + */ + private void setupSensors(EventHeader event) { + List rawTrackerHits = event.get(RawTrackerHit.class, "SVTRawTrackerHits"); + EventHeader.LCMetaData meta = event.getMetaData(rawTrackerHits); + // Get the ID dictionary and field information. + IIdentifierDictionary dict = meta.getIDDecoder().getSubdetector().getDetectorElement().getIdentifierHelper().getIdentifierDictionary(); + int fieldIdx = dict.getFieldIndex("side"); + int sideIdx = dict.getFieldIndex("strip"); + for (RawTrackerHit hit : rawTrackerHits) { + // The "side" and "strip" fields needs to be stripped from the ID for sensor lookup. + IExpandedIdentifier expId = dict.unpack(hit.getIdentifier()); + expId.setValue(fieldIdx, 0); + expId.setValue(sideIdx, 0); + IIdentifier strippedId = dict.pack(expId); + // Find the sensor DetectorElement. + List des = DetectorElementStore.getInstance().find(strippedId); + if (des == null || des.size() == 0) { + throw new RuntimeException("Failed to find any DetectorElements with stripped ID <0x" + Long.toHexString(strippedId.getValue()) + ">."); + } else if (des.size() == 1) { + hit.setDetectorElement((SiSensor) des.get(0)); + } else { + // Use first sensor found, which should work unless there are sensors with duplicate IDs. + for (IDetectorElement de : des) { + if (de instanceof SiSensor) { + hit.setDetectorElement((SiSensor) de); + break; + } + } + } + // No sensor was found. + if (hit.getDetectorElement() == null) { + throw new RuntimeException("No sensor was found for hit with stripped ID <0x" + Long.toHexString(strippedId.getValue()) + ">."); + } + } + } + + /** + * Convenience method to set the name of the input collection of + * ReconstructedParticles + * + * @param s + */ + public void set_finalStateParticleCollectionName(String s) { + System.out.format("ReconstructedParticleRefitter: setting the final state particle collection name to %s\n", s); + _finalStateParticleCollectionName = s; + } + + /** + * Convenience method to set the name of the output collection of + * ReconstructedParticles + * + * @param s + */ + public void set_refitParticleCollectionName(String s) { + System.out.format("ReconstructedParticleRefitter: setting the output collection name to %s\n", s); + _refitParticleCollectionName = s; + } + + /** + * Set the cut on E/p from the steering file + * @param d Maximum E/p allowed for electron/positron candidates + */ + public void set_eOverpCut(double d) { + System.out.format("ReconstructedParticleRefitter: setting the E/p cut value to %9.4f\n", d); + _eOverpCut = d; + } + + public void endOfData() { + System.out.format("ReconstructedParticleRefitter: end-of-data reached.\n"); + try { + System.out.format("Outputting the aida histograms now to file %s\n", outputFileName); + aida.saveAs(outputFileName); + } catch (IOException ex) { + System.out.println("ReconstructedParticleRefitter: exception when writing out histograms"); + } + KI.summary(); + } +} diff --git a/tracking/src/main/java/org/hps/recon/tracking/gbl/KalmanToGBLDriver.java b/tracking/src/main/java/org/hps/recon/tracking/gbl/KalmanToGBLDriver.java index c5cf644490..0652d246c4 100644 --- a/tracking/src/main/java/org/hps/recon/tracking/gbl/KalmanToGBLDriver.java +++ b/tracking/src/main/java/org/hps/recon/tracking/gbl/KalmanToGBLDriver.java @@ -1,8 +1,11 @@ package org.hps.recon.tracking.gbl; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; import java.util.Collections; import java.util.Comparator; @@ -11,21 +14,28 @@ //import org.lcsim.event.TrackState; import org.lcsim.event.Track; - +import org.lcsim.event.TrackState; import org.lcsim.event.EventHeader; import org.lcsim.util.Driver; -import org.lcsim.geometry.Detector; +import org.lcsim.util.aida.AIDA; +import hep.aida.IHistogram1D; +import hep.aida.IHistogramFactory; + +import org.lcsim.geometry.Detector; import org.lcsim.event.LCRelation; import org.lcsim.event.RelationalTable; import org.lcsim.event.base.BaseRelationalTable; - +import org.lcsim.fit.helicaltrack.HelicalTrackFit; import org.lcsim.constants.Constants; +import org.apache.commons.math3.util.Pair; import org.hps.recon.tracking.TrackResidualsData; +import org.hps.recon.tracking.TrackStateUtils; import org.lcsim.event.base.BaseLCRelation; //import org.lcsim.fit.helicaltrack.HelicalTrackFit; import org.hps.recon.tracking.TrackUtils; +import org.hps.recon.tracking.TrackingReconstructionPlots; /** * A Driver which refits Kalman Tracks using GBL @@ -42,19 +52,46 @@ public class KalmanToGBLDriver extends Driver { private String kinkDataCollectionName = GBLKinkData.DATA_COLLECTION; private String kinkDataRelationsName = GBLKinkData.DATA_RELATION_COLLECTION; private Boolean _debug = false; + private Boolean _analysis = false; private Boolean constrainedBSFit = false; private double bfield; private Boolean computeGBLResiduals = true; + public AIDA aida; + private IHistogram1D hGBLurt, hGBLurb, hKFurt, hKFurb, hGBLrt, hGBLrb; + private IHistogram1D hDelPhi0, hDelTanL, hDelOmega, hDelD0, hDelZ0; + private IHistogramFactory ahf; - void setDebug(boolean val) { + public void setDebug(boolean val) { _debug = val; } + public void setAnalysis(boolean val) { + _analysis = val; + } + + void definePlots() { + if (aida == null) aida = AIDA.defaultInstance(); + aida.tree().cd("/"); + ahf = aida.histogramFactory(); + hGBLurt = aida.histogram1D("GBL unbiased residuals top detector", 100, -0.25, 0.25); + hGBLurb = aida.histogram1D("GBL unbiased residuals bottom detector", 100, -0.25, 0.25); + hGBLrt = aida.histogram1D("GBL biased residuals top detector", 100, -1., 1.); + hGBLrb = aida.histogram1D("GBL biased residuals bottom detector", 100, -1., 1.); + hKFurt = aida.histogram1D("KF unbiased residuals top detector", 100, -1., 1.); + hKFurb = aida.histogram1D("KF unbiased residuals bottom detector", 100, -1., 1.); + hDelPhi0 = aida.histogram1D("KF minus GBL phi0", 100, -0.02, 0.02); + hDelD0 = aida.histogram1D("KF minus GBL D0", 100, -5., 5.); + hDelOmega = aida.histogram1D("KF minus GBL Omega", 100, -3.E-4, 3.E-4); + hDelTanL = aida.histogram1D("KF minus GBL tanLamba", 100, -0.01, 0.01); + hDelZ0 = aida.histogram1D("KF minus GBL Z0", 100, -0.1, 0.1); + } + @Override protected void detectorChanged(Detector detector) { bfield = Math.abs(TrackUtils.getBField(detector).magnitude()); + if (_analysis) definePlots(); } @Override @@ -69,19 +106,66 @@ protected void process(EventHeader event) { List kinkDataCollection = new ArrayList(); List kinkDataRelations = new ArrayList(); + List GBLtracks = null; + if (_analysis) { + if (event.hasCollection(Track.class, "GBLTracks")) { + GBLtracks = event.get(Track.class, "GBLTracks"); + if (_debug) { + System.out.format("KalmanToGBLDriver: number of input GBL tracks = %d\n", GBLtracks.size()); + } + } + } + //Get the track collection from the event - if (!event.hasCollection(Track.class, inputCollectionName)) + if (!event.hasCollection(Track.class, inputCollectionName)) { + if (_debug) System.out.format("KalmanToGBLDriver: collection %s not found\n", inputCollectionName); return; + } List tracks = event.get(Track.class,inputCollectionName); - if (_debug) - System.out.println("Found tracks: " + inputCollectionName+" " + tracks.size()); - + String residualsKFcollectionName = "KFUnbiasRes"; + List residualsKF = null; + if (event.hasCollection(TrackResidualsData.class, residualsKFcollectionName)) { + if (_debug) System.out.format("KalmanToGBLDriver: collection %s found\n", residualsKFcollectionName); + residualsKF = event.get(TrackResidualsData.class, residualsKFcollectionName); + } + String residualsRelationsKFcollectionName = "KFUnbiasResRelations"; + List residualsRelKF = null; + RelationalTable kfResidsRT = null; + if (event.hasCollection(LCRelation.class, residualsRelationsKFcollectionName)) { + if (_debug) System.out.format("KalmanToGBLDriver: collection %s found\n", residualsRelationsKFcollectionName); + residualsRelKF = event.get(LCRelation.class, residualsRelationsKFcollectionName); + kfResidsRT = new BaseRelationalTable(RelationalTable.Mode.MANY_TO_MANY, RelationalTable.Weighting.UNWEIGHTED); + for (LCRelation relation : residualsRelKF) { + if (relation != null && relation.getFrom() != null && relation.getTo() != null) { + kfResidsRT.add(relation.getFrom(), relation.getTo()); + } + } + } + + if (_debug) System.out.println("KalmanToGBLDriver, found tracks: " + inputCollectionName+" " + tracks.size()); - //Loop on Kalman Tracks + RelationalTable kfSCDsRT = null; + List kfSCDRelation = new ArrayList(); + if (event.hasCollection(LCRelation.class,"KFGBLStripClusterDataRelations")) { + kfSCDsRT = new BaseRelationalTable(RelationalTable.Mode.MANY_TO_MANY, RelationalTable.Weighting.UNWEIGHTED); + kfSCDRelation = event.get(LCRelation.class,"KFGBLStripClusterDataRelations"); + for (LCRelation relation : kfSCDRelation) { + if (relation != null && relation.getFrom() != null && relation.getTo() != null) { + kfSCDsRT.add(relation.getFrom(), relation.getTo()); + } + } + } else { + System.out.println("null KFGBLStripCluster Data Relations."); + return; + } + List newGBLtks = null; + if (_analysis) newGBLtks = new ArrayList(tracks.size()); + + //Loop on Kalman Tracks for (Track trk : tracks ) { //Remove tracks with less than 10 hits @@ -93,34 +177,12 @@ protected void process(EventHeader event) { //Remove tracks where there is high mis-tracking rate //TrackState trackState = trk.getTrackStates().get(0); //if (Math.abs(trackState.getTanLambda()) < 0.02) - // continue; - - - //Get the GBLStripClusterData - RelationalTable kfSCDsRT = null; - List kfSCDRelation = new ArrayList(); - - - if (event.hasCollection(LCRelation.class,"KFGBLStripClusterDataRelations")) { - kfSCDsRT = new BaseRelationalTable(RelationalTable.Mode.MANY_TO_MANY, RelationalTable.Weighting.UNWEIGHTED); - kfSCDRelation = event.get(LCRelation.class,"KFGBLStripClusterDataRelations"); - for (LCRelation relation : kfSCDRelation) { - if (relation != null && relation.getFrom() != null && relation.getTo() != null) { - kfSCDsRT.add(relation.getFrom(), relation.getTo()); - } - } - } else { - System.out.println("null KFGBLStripCluster Data Relations."); - return; - } - + // continue; + //Get the strip cluster data Set kfSCDs = kfSCDsRT.allFrom(trk); - - if (_debug) - System.out.println("Kalman Strip Cluster data size: " + kfSCDs.size()); - + if (_debug) System.out.println("Kalman Strip Cluster data size: " + kfSCDs.size()); //Convert the set to a list for sorting it @@ -142,6 +204,7 @@ protected void process(EventHeader event) { FittedGblTrajectory fitGbl_traj = HpsGblRefitter.fit(list_kfSCDs, bfac, false); + /* System.out.println("DEBUG::Tom::KalmanToGBLDriver - converted KF track to GBL track with " + gbl_fit_trajectory.getNumPoints() + " hits"); @@ -166,8 +229,7 @@ protected void process(EventHeader event) { }//computeGBLResiduals - - + // Get the derivatives /* @@ -191,6 +253,15 @@ protected void process(EventHeader event) { } */ + // Create a GBL track from the fitted trajectory + if (_analysis) { + int trackType = 57; // randomly chosen track type + TrackState atIP = TrackStateUtils.getTrackStateAtIP(trk); + HelicalTrackFit htkft = TrackUtils.getHTF(atIP); + Pair GBLtkrPair = MakeGblTracks.makeCorrectedTrack(fitGbl_traj, htkft, trk.getTrackerHits(), trackType, bfield, true); + newGBLtks.add(GBLtkrPair.getFirst()); + } + }// track loop if (computeGBLResiduals) { @@ -200,6 +271,63 @@ protected void process(EventHeader event) { event.put(kinkDataRelationsName, kinkDataRelations, LCRelation.class, 0); } + + // Analysis added by R. Johnson for debugging etc + if (_debug) { + if (residualsKF == null) System.out.format("KalmanToGBLDriver: residualsKF is null\n"); + if (residualsRelKF == null) System.out.format("KalmanToGBLDriver: residualsRelKF is null\n"); + } + if (_analysis && residualsKF != null && residualsRelKF != null) { + + RelationalTable gblResidsRT = new BaseRelationalTable(RelationalTable.Mode.MANY_TO_MANY, RelationalTable.Weighting.UNWEIGHTED); + for (LCRelation relation : trackResidualsRelations) { + if (relation != null && relation.getFrom() != null && relation.getTo() != null) { + gblResidsRT.add(relation.getFrom(), relation.getTo()); + } + } + for (Track trk : tracks ) { + Track trkGBL = newGBLtks.get(tracks.indexOf(trk)); + Set gblResids = gblResidsRT.allTo(trk); + TrackState atIP_GBL = TrackStateUtils.getTrackStateAtIP(trkGBL); + TrackState atIP_KF = TrackStateUtils.getTrackStateAtIP(trk); + hDelPhi0.fill(atIP_KF.getPhi() - atIP_GBL.getPhi()); + hDelOmega.fill(atIP_KF.getOmega() - atIP_GBL.getOmega()); + hDelTanL.fill(atIP_KF.getTanLambda() - atIP_GBL.getTanLambda()); + hDelD0.fill(atIP_KF.getD0() - atIP_GBL.getD0()); + hDelZ0.fill(atIP_KF.getZ0() - atIP_GBL.getZ0()); + if (_debug) { + System.out.println("GBL Track state: " + atIP_GBL.toString()); + System.out.println("KF Track state: " + atIP_KF.toString()); + System.out.format("Track %d has %d set of GBL residuals:\n", tracks.indexOf(trk), gblResids.size()); + } + for (TrackResidualsData resData : gblResids) { + int vol = resData.getIntVal(resData.getNInt()-1); + if (_debug) System.out.format(" GBL residuals for tracker volume %d\n", vol); + for (int i=0; i kfResids = kfResidsRT.allTo(trk); + if (_debug) System.out.format("Kalman track %d has %d set of residuals:\n", tracks.indexOf(trk), kfResids.size()); + for (TrackResidualsData resData : kfResids) { + int vol = resData.getIntVal(resData.getNInt()-1); + if (_debug) System.out.format(" KF residuals for tracker volume %d\n", vol); + for (int i=0; i arcLComparator = new Comparator() { diff --git a/tracking/src/main/java/org/hps/recon/tracking/kalman/FieldMap.java b/tracking/src/main/java/org/hps/recon/tracking/kalman/FieldMap.java index 88de5d113d..19a21cddda 100644 --- a/tracking/src/main/java/org/hps/recon/tracking/kalman/FieldMap.java +++ b/tracking/src/main/java/org/hps/recon/tracking/kalman/FieldMap.java @@ -133,7 +133,12 @@ public FieldMap(String FileName, String type, boolean uniform, double xOffset, d offsets.print("field map offsets"); } - double [] getField(Vec r) { // Interpolate the 3D field map + /** + * Interpolate the 3D field map + * @param r 3-vector position + * @return array of 3 field components + */ + double [] getField(Vec r) { Vec rHPS; if (uniform) { rHPS = new Vec(0., 0., 505.57); @@ -192,6 +197,17 @@ public FieldMap(String FileName, String type, boolean uniform, double xOffset, d return Bout; } + /** + * Trilinear interpolation + * @param i x index into array + * @param j y index into array + * @param k z index into array + * @param xd x value + * @param yd y value + * @param zd z value + * @param f 3D array to interpolate + * @return interpolated value of the array f at the given coordinates + */ private double triLinear(int i, int j, int k, double xd, double yd, double zd, double[][][] f) { // System.out.format(" triLinear: xd=%10.7f, yd=%10.7f, zd=%10.7f\n", xd,yd,zd); // System.out.format(" 000=%12.4e, 100=%12.4e\n", f[i][j][k],f[i+1][j][k]); @@ -210,7 +226,11 @@ private double triLinear(int i, int j, int k, double xd, double yd, double zd, d return c; } - void writeBinaryFile(String fName) { // Make a binary field map file that can be read much more quickly + /** + * Make a binary field map file that can be read much more quickly + * @param fName Name of output file + */ + void writeBinaryFile(String fName) { FileOutputStream ofile; try { ofile = new FileOutputStream(fName); diff --git a/tracking/src/main/java/org/hps/recon/tracking/kalman/Helix.java b/tracking/src/main/java/org/hps/recon/tracking/kalman/Helix.java index 7b290b84fa..e3da763a34 100644 --- a/tracking/src/main/java/org/hps/recon/tracking/kalman/Helix.java +++ b/tracking/src/main/java/org/hps/recon/tracking/kalman/Helix.java @@ -1,11 +1,14 @@ package org.hps.recon.tracking.kalman; import java.util.Random; + /** - * This is for stand-alone testing only and is not part of the Kalman fitting code. - * Create a simple helix oriented along the B field axis for testing the Kalman fit. + * This is for stand-alone testing only and is not part of the Kalman fitting + * code. Create a simple helix oriented along the B field axis for testing the + * Kalman fit. */ -class Helix { +class Helix { + Vec p; // Helix parameters drho, phi0, K, dz, tanl Vec X0; // Pivot point in the B field reference frame private double alpha; @@ -21,17 +24,19 @@ class Helix { Vec origin; // Origin of the B-field reference frame in global coordinates private FieldMap fM; private Random rndm; + private boolean doeLoss; // Construct a helix starting from a momentum vector - Helix(double Q, Vec Xinit, Vec Pinit, Vec origin, FieldMap fM, Random rndm) { + Helix(double Q, Vec Xinit, Vec Pinit, Vec origin, FieldMap fM, Random rndm, boolean doeLoss) { this.Q = Q; this.origin = origin.copy(); + this.doeLoss = doeLoss; this.fM = fM; - Vec Bf = new Vec(3,fM.getField(Xinit)); + Vec Bf = new Vec(3, fM.getField(Xinit)); B = Bf.mag(); double c = 2.99793e8; alpha = 1000.0 * 1.0E9 / (c * B); // Units are Tesla, mm, GeV - rho = 2.329; // Density of silicon in g/cm^2 + rho = 2.329; // Density of silicon in g/cm^3 tB = Bf.unitVec(B); Vec yhat = new Vec(0., 1.0, 0.); uB = yhat.cross(tB).unitVec(); @@ -53,11 +58,12 @@ class Helix { } // Construct a helix from given helix parameters (given in B field frame) - Helix(Vec HelixParams, Vec pivotGlobal, Vec origin, FieldMap fM, Random rndm) { + Helix(Vec HelixParams, Vec pivotGlobal, Vec origin, FieldMap fM, Random rndm, boolean doeLoss) { this.origin = origin.copy(); this.fM = fM; + this.doeLoss = doeLoss; p = HelixParams.copy(); - Vec Bf = new Vec(3,fM.getField(pivotGlobal)); + Vec Bf = new Vec(3, fM.getField(pivotGlobal)); B = Bf.mag(); double c = 2.99793e8; alpha = 1000.0 * 1.0E9 / (c * B); // Units are Tesla, mm, GeV @@ -85,7 +91,7 @@ class Helix { } Helix copy() { - return new Helix(p, R.inverseRotate(X0).sum(origin), origin, fM, rndm); + return new Helix(p, R.inverseRotate(X0).sum(origin), origin, fM, rndm, doeLoss); } void print(String s) { @@ -98,7 +104,7 @@ void print(String s) { System.out.format(" Pivot in B-field frame=%10.5f, %10.5f, %10.5f\n", X0.v[0], X0.v[1], X0.v[2]); Vec pivotGlobal = R.inverseRotate(X0).sum(origin); System.out.format(" Pivot in global frame=%10.5f, %10.5f, %10.5f\n", pivotGlobal.v[0], pivotGlobal.v[1], pivotGlobal.v[2]); - Vec Bf = new Vec(3,fM.getField(pivotGlobal)); + Vec Bf = new Vec(3, fM.getField(pivotGlobal)); Bf.print("B field in global frame at the pivot point"); Vec Bflocal = R.rotate(Bf); Bflocal.print("B field in its local frame; should be in +z direction"); @@ -142,7 +148,7 @@ Vec atPhiGlobal(double phi) { // return the global coordinates on the helix at a } double planeIntersect(Plane Pin) { // phi value where the helix intersects the plane P (given in global - // coordinates) + // coordinates) Plane P = Pin.toLocal(R, origin); double phi = hpi.planeIntersect(p, X0, alpha, P); // System.out.format("Helix:planeIntersect: phi = %12.10f\n", phi); @@ -172,7 +178,6 @@ Helix randomScat(Plane P, Vec r, Vec pmom, double X) { // Produce a new helix sc // Vec r = this.atPhiGlobal(phi); //double tst = r.dif(P.X()).dot(P.T()); // System.out.format("randomScat: test dot product %12.6e should be zero\n", tst); - // r.print("randomScat: r global"); // Vec pmom = getMomGlobal(phi); // pmom.print("randomScat: p global"); @@ -187,8 +192,11 @@ Helix randomScat(Plane P, Vec r, Vec pmom, double X) { // Produce a new helix sc double ct = Math.abs(P.T().dot(t)); double theta0; // Get the scattering angle - if (X == 0.) theta0 = 0.; - else theta0 = Math.sqrt((X / radLen) / ct) * (0.0136 / pmom.mag()) * (1.0 + 0.038 * Math.log((X / radLen) / ct)); + if (X == 0.) { + theta0 = 0.; + } else { + theta0 = Math.sqrt((X / radLen) / ct) * (0.0136 / pmom.mag()) * (1.0 + 0.038 * Math.log((X / radLen) / ct)); + } double thetaX = rndm.nextGaussian() * theta0; double thetaY = rndm.nextGaussian() * theta0; // System.out.format("Helix.randomScat: X=%12.5e, ct=%12.5e, theta0=%12.5e, thetaX=%12.5e, @@ -205,7 +213,7 @@ Helix randomScat(Plane P, Vec r, Vec pmom, double X) { // Produce a new helix sc // System.out.format("recalculated scattered angle=%10.7f\n", check); // Rotate the direction into the frame of the new field (evaluated at the new pivot) - Vec Bf = new Vec(3,fM.getField(r)); + Vec Bf = new Vec(3, fM.getField(r)); double Bnew = Bf.mag(); Vec tBnew = Bf.unitVec(Bnew); Vec yhat = new Vec(0., 1., 0.); @@ -216,8 +224,12 @@ Helix randomScat(Plane P, Vec r, Vec pmom, double X) { // Produce a new helix sc tnew = RB.rotate(tnew); double E = pmom.mag(); // Everything is assumed electron - double sp = 0.0; // 0.002; // Estar collision stopping power for electrons in silicon at about a - // GeV, in GeV cm2/g + double sp = 0.; + if (doeLoss) { + sp = 0.002; // Estar collision stopping power for electrons in silicon at about a GeV, in GeV cm2/g + sp = sp * 20; // ToDo temporary!!! + } + sp = sp * 20; // ToDo temporary!!! double dEdx = 0.1 * sp * rho; // in GeV/mm double eLoss = dEdx * X / ct; // System.out.format("randomScat: energy=%10.7f, energy loss=%10.7f\n", E, eLoss); @@ -229,26 +241,6 @@ Helix randomScat(Plane P, Vec r, Vec pmom, double X) { // Produce a new helix sc Vec H = new Vec(0., phi0, K, 0., tanl); // Pivot point is on the helix, at the plane intersection point, so drho and dz are zero // H.print("scattered helix parameters"); - return new Helix(H, r, P.X(), fM, rndm); // Create the new helix with new origin and pivot point - } - - Vec pivotTransform(Vec pivot) { - double xC = X0.v[0] + (p.v[0] + alpha / p.v[2]) * Math.cos(p.v[1]); // Center of the helix circle - double yC = X0.v[1] + (p.v[0] + alpha / p.v[2]) * Math.sin(p.v[1]); - // System.out.format("pivotTransform center=%10.6f, %10.6f\n", xC, yC); - - // Predicted state vector - double[] aP = new double[5]; - aP[2] = p.v[2]; - aP[4] = p.v[4]; - if (p.v[2] > 0) { - aP[1] = Math.atan2(yC - pivot.v[1], xC - pivot.v[0]); - } else { - aP[1] = Math.atan2(pivot.v[1] - yC, pivot.v[0] - xC); - } - aP[0] = (xC - pivot.v[0]) * Math.cos(aP[1]) + (yC - pivot.v[1]) * Math.sin(aP[1]) - alpha / p.v[2]; - aP[3] = X0.v[2] - pivot.v[2] + p.v[3] - (alpha / p.v[2]) * (aP[1] - p.v[1]) * p.v[4]; - - return new Vec(5, aP); + return new Helix(H, r, P.X(), fM, rndm, doeLoss); // Create the new helix with new origin and pivot point } } diff --git a/tracking/src/main/java/org/hps/recon/tracking/kalman/HelixPlaneIntersect.java b/tracking/src/main/java/org/hps/recon/tracking/kalman/HelixPlaneIntersect.java index 47a2f3b9e8..9ef189333e 100644 --- a/tracking/src/main/java/org/hps/recon/tracking/kalman/HelixPlaneIntersect.java +++ b/tracking/src/main/java/org/hps/recon/tracking/kalman/HelixPlaneIntersect.java @@ -30,17 +30,19 @@ class HelixPlaneIntersect { double arcLength() { // Return the arc length for the last call to rkIntersect return deltaS; } - // Runge Kutta integration extrapolation to a plane through a non-uniform field - // When close to the plane, then a helix is used to find the exact intersection + /** + * Runge Kutta integration extrapolation to a plane through a non-uniform field + * When close to the plane, then a helix is used to find the exact intersection + * + * @param P definition of the plane to which to extrapolate + * @param X0 3-D starting point for the extrapolation + * @param P0in 3-momentum at the start of the extrapolation + * @param Qin sign of the particle charge (+1 or -1) + * @param fM HPS field map + * @param pInt return value for the momentum at the intersection + * @return 3-D intersection point + */ Vec rkIntersect(Plane P, Vec X0, Vec P0in, double Qin, org.lcsim.geometry.FieldMap fM, Vec pInt) { - // P definition of the plane to which to extrapolate - // X0 3-D starting point for the extrapolation - // P0 3-momentum at the start of the extrapolation - // Q sign of the particle charge (+1 or -1) - // fM HPS field map - // pInt return value for the momentum at the intersection - // the function return value is the 3-D intersection point - if (debug) { System.out.format("Entering HelixPlaneIntersect.rkIntersect for plane %s\n", P.toString()); X0.print("rkIntersect start location, global coords"); @@ -144,10 +146,16 @@ Vec rkIntersect(Plane P, Vec X0, Vec P0in, double Qin, org.lcsim.geometry.FieldM return xIntGlb; } - // Given the momentum and charge at a location, return the parameters of the helix, - // assuming a reference frame in which the magnetic field is in the z direction! - // The new pivot point is the location provided, so rho0 and z0 will always be - // zero. + /** + * Given the momentum and charge at a location, return the parameters of the helix, + * assuming a reference frame in which the magnetic field is in the z direction! + * The new pivot point is the location provided, so rho0 and z0 will always be + * zero. + * @param x point on the track + * @param p 3-momentum at that point + * @param Q charge + * @return 5-vector of helix parameters + */ static Vec pToHelix(Vec x, Vec p, double Q) { double E = p.mag(); Vec t = p.unitVec(E); @@ -158,12 +166,15 @@ static Vec pToHelix(Vec x, Vec p, double Q) { return new Vec(0., phi0, K, 0., tanl); } - // Find the intersection of a helix with a plane. + /** + * Find the intersection of a helix with a plane. + * @param a 5-vector of helix parameters + * @param pivot helix pivot point + * @param alpha 10^12/c/B to convert curvature to momentum + * @param p point and direction cosines of the plane + * @return angle through which the helix turns going from the pivot point to the plane + */ double planeIntersect(Vec a, Vec pivot, double alpha, Plane p) { - // p: Plane assumed to be defined in the local helix reference frame - // a: vector of 5 helix parameters - // alpha: 10^12/c/B - // Take as a starting guess the solution for the case that the plane orientation is exactly y-hat. // System.out.format("HelixPlaneIntersection:planeIntersect, alpha=%f10.5\n", alpha); this.alpha = alpha; @@ -183,9 +194,16 @@ static Vec pToHelix(Vec x, Vec p, double Q) { return phi; } - // Safe Newton-Raphson zero finding from Numerical Recipes in C + /** + * Safe Newton-Raphson zero finding from Numerical Recipes in C + * @param xGuess starting guess for the phi angle of the helix intersection + * @param x1 minimum of search range + * @param x2 maximum of search range + * @param xacc specification of the required accuracy + * @return a zero point between x1 and x2 + */ private double rtSafe(double xGuess, double x1, double x2, double xacc) { - // Here xGuess is a starting guess for the phi angle of the helix intersection + // Here xGuess is a // x1 and x2 give a range for the value of the solution // xacc specifies the accuracy needed // The output is an accurate result for the phi of the intersection diff --git a/tracking/src/main/java/org/hps/recon/tracking/kalman/HelixState.java b/tracking/src/main/java/org/hps/recon/tracking/kalman/HelixState.java index f26370865c..66a5684a54 100644 --- a/tracking/src/main/java/org/hps/recon/tracking/kalman/HelixState.java +++ b/tracking/src/main/java/org/hps/recon/tracking/kalman/HelixState.java @@ -11,7 +11,9 @@ import org.ejml.data.DMatrixRMaj; import org.ejml.dense.row.CommonOps_DDRM; import org.ejml.dense.row.MatrixFeatures_DDRM; +import org.ejml.dense.row.factory.LinearSolverFactory_DDRM; import org.ejml.dense.fixed.CommonOps_DDF3; +import org.ejml.interfaces.linsol.LinearSolverDense; import org.hps.util.Pair; import org.lcsim.event.TrackState; @@ -31,6 +33,11 @@ class HelixState implements Cloneable { private Logger logger; private HelixPlaneIntersect hpi; private Vec xPlaneRK; + private static LinearSolverDense solver; + private static boolean initialized; + private static DMatrixRMaj Cinv; // Temporary working area + private static DMatrixRMaj X, V, H; + final static private boolean debug = false; public Object clone() { try { @@ -42,6 +49,15 @@ public Object clone() { } } + /** + * Constructor with all helix information provided by the call + * @param a 5 helix parameters + * @param X0 pivot point + * @param origin coordinate system origin + * @param C covariance matrix of helix parameters + * @param B magnetic field magnitude + * @param tB magnetic field direction + */ HelixState(Vec a, Vec X0, Vec origin, DMatrixRMaj C, double B, Vec tB) { logger = Logger.getLogger(HelixState.class.getName()); this.a = a; @@ -59,6 +75,12 @@ public Object clone() { Rot = new RotMatrix(u, v, tB); } + /** + * Constructor without helix supplied + * @param B magnetic field magnitude + * @param tB magnetic field direction + * @param origin origin of coordinate system + */ HelixState(double B, Vec tB, Vec origin) { logger = Logger.getLogger(HelixState.class.getName()); this.origin = origin; @@ -73,20 +95,134 @@ public Object clone() { Rot = new RotMatrix(u, v, tB); } + /** + * Empty constructor + */ HelixState() { logger = Logger.getLogger(HelixState.class.getName()); hpi = new HelixPlaneIntersect(); c = 2.99793e8; // Speed of light in m/s } + /** + * Deep copy + * @return new helix state + */ HelixState copy() { return new HelixState(a.copy(), X0.copy(), origin.copy(), C.copy(), B, tB.copy()); } + /** + * Invert the covariance matrix of this HelixState and store the result in a static array Cinv + */ + private void invertC() { + if (!HelixState.initialized) { + HelixState.Cinv = new DMatrixRMaj(5,5); + HelixState.X = new DMatrixRMaj(5,1); + HelixState.V = new DMatrixRMaj(5,1); + HelixState.H = new DMatrixRMaj(5,1); + solver = LinearSolverFactory_DDRM.symmPosDef(5); + HelixState.initialized = true; + } + if (!solver.setA(C.copy())) { + SquareMatrix invrs = KalTrack.mToS(C).fastInvert(); + if (invrs == null) { + logger.warning("StateVector:smooth, inversion of the covariance matrix failed"); + C.print(); + for (int i=0; i<5; ++i) { // Fill the inverse with something not too crazy and continue . . . + for (int j=0; j<5; ++j) { + if (i == j) { + HelixState.Cinv.unsafe_set(i,j,1.0/C.unsafe_get(i,j)); + } else { + HelixState.Cinv.unsafe_set(i, j, 0.); + C.unsafe_set(i, j, 0.); + } + } + } + } else { + for (int i=0; i<5; ++i) { + for (int j=0; j<5; ++j) { + HelixState.Cinv.unsafe_set(i, j, invrs.M[i][j]); + } + } + } + } else { + HelixState.solver.invert(HelixState.Cinv); + } + } + + /** + * Make a new HelixState with an energy constraint (from the ECAL). This uses the Kalman weighted-means formalism to "filter" + * the helix state vector. + * @param E The energy from the ECAL cluster + * @param sigmaE The energy uncertainty for the ECAL cluster + * @return A new HelixState with the energy constraint incorporated + */ + HelixState energyConstrained(double E, double sigmaE) { + double tanL = a.v[4]; + double K = a.v[2]; + double m = (1.0/E)*Math.signum(K); + double sigma_m = sigmaE/(E*E); + double G = 1.0/(sigma_m*sigma_m); + double dmdK = 1.0/FastMath.sqrt(1.0+tanL*tanL); + double dmdtanL = -K*tanL/FastMath.pow(1.0 + tanL*tanL, 1.5); + HelixState hsNew = this.copy(); + + invertC(); + HelixState.H.unsafe_set(2, 0, dmdK); + HelixState.H.unsafe_set(4, 0, dmdtanL); + for (int i=0; i<5; ++i) { + X.unsafe_set(i, 0, a.v[i]); + } + CommonOps_DDRM.mult(HelixState.Cinv, HelixState.X, HelixState.V); + CommonOps_DDRM.scale(G*m, HelixState.H); + CommonOps_DDRM.addEquals(HelixState.V, HelixState.H); + + for (int i=0; i<5; ++i) { + for (int j=0; j<5; ++j) { + hsNew.C.unsafe_set(i, j, HelixState.Cinv.unsafe_get(i, j)); + } + } + hsNew.C.unsafe_set(2, 2, HelixState.Cinv.unsafe_get(2, 2) + (dmdK*dmdK)*G); + hsNew.C.unsafe_set(4, 4, HelixState.Cinv.unsafe_get(4, 4) + (dmdtanL*dmdtanL)*G); + hsNew.C.unsafe_set(2, 4, HelixState.Cinv.unsafe_get(2, 4) + (dmdK*dmdtanL)*G); + hsNew.C.unsafe_set(4, 2, HelixState.Cinv.unsafe_get(4, 2) + (dmdtanL*dmdK)*G); + hsNew.invertC(); + for (int i=0; i<5; ++i) { + for (int j=0; j<5; ++j) { + hsNew.C.unsafe_set(i, j, HelixState.Cinv.unsafe_get(i, j)); + } + } + CommonOps_DDRM.mult(HelixState.Cinv, HelixState.V, HelixState.X); + for (int i=0; i<5; ++i) { + hsNew.a.v[i] = HelixState.X.unsafe_get(i, 0); + } + if (debug) { + System.out.println("Original helix params X:"); X.print(); + System.out.println("Derivative vector H:"); H.print(); + hsNew.a.print("New helix params"); + System.out.println("New covariance:"); hsNew.C.print(); + double varK = C.unsafe_get(2, 2); + double A = 1.0/FastMath.sqrt(1.0+tanL*tanL); + double Kprime = (K/varK + A*m*G)/(1.0/varK + G/(A*A)); + System.out.format(" Simple weighted mean update of kappa = %10.6f\n", Kprime); + } + return hsNew; + } + + /** + * Debug printout of the helix state + * @param s A string used to identify the printout + */ void print(String s) { System.out.format("%s", this.toString(s)); } + /** + * Debug printout to a string + * @param s A short string to identify the printout + * @return A long string that can be printed + */ String toString(String s) { String str; str = String.format("HelixState %s: helix parameters=%s, pivot=%s\n", s, a.toString(), X0.toString()); @@ -97,18 +233,34 @@ String toString(String s) { return str; } + /** + * Check whether the helix parameter covariance matrix is mathematically good + * @return true or false + */ boolean goodCov() { if (!MatrixFeatures_DDRM.isDiagonalPositive(C)) return false; if (MatrixFeatures_DDRM.hasNaN(C)) return false; return true; } - // Returns a point on the helix at the angle phi + /** + * Returns a point on the helix at the angle phi + * @param phi The angle + * @return 3-vector point on the helix + */ // Warning: the point returned is in the B-Field reference frame Vec atPhi(double phi) { return atPhi(X0, a, phi, alpha); } + /** + * Returns a point on a provided helix at the angle phi + * @param X0 pivot point of the helix + * @param a 5 helix parameters + * @param phi the angle + * @param alpha parameter to transform between curvature and momentum + * @return 3-vector point on the helix + */ static Vec atPhi(Vec X0, Vec a, double phi, double alpha) { double x = X0.v[0] + (a.v[0] + (alpha / a.v[2])) * FastMath.cos(a.v[1]) - (alpha / a.v[2]) * FastMath.cos(a.v[1] + phi); double y = X0.v[1] + (a.v[0] + (alpha / a.v[2])) * FastMath.sin(a.v[1]) - (alpha / a.v[2]) * FastMath.sin(a.v[1] + phi); @@ -116,35 +268,51 @@ static Vec atPhi(Vec X0, Vec a, double phi, double alpha) { return new Vec(x, y, z); } - // Calculate the phi angle to propagate on helix to the intersection with a - // measurement plane + /** + * Calculate the phi angle to propagate on helix to the intersection with a measurement plane + * @param pIn point and direction cosines of the plane + * @return turning angle from the pivot point to the plane + */ double planeIntersect(Plane pIn) { // pIn is assumed to be defined in the global reference frame Plane p = pIn.toLocal(Rot, origin); // Transform the plane into the B-field local reference frame return hpi.planeIntersect(a, X0, alpha, p); } - // Return errors on the helix parameters at the present pivot point + /** + * Return errors on the helix parameters at the present pivot point + * @return 5-vector of helix parameter errors + */ Vec helixErrors() { return new Vec(FastMath.sqrt(C.unsafe_get(0,0)), FastMath.sqrt(C.unsafe_get(1,1)), FastMath.sqrt(C.unsafe_get(2,2)), FastMath.sqrt(C.unsafe_get(3,3)), FastMath.sqrt(C.unsafe_get(4,4))); } - // Derivative matrix for the pivot transform (without energy loss or field rotations) - void makeF(Vec aP, DMatrixRMaj F) { - makeF(aP, F, a, alpha); - } - - static void makeF(Vec aP, DMatrixRMaj F, Vec a, double alpha) { + /** + * Create derivative matrix for the pivot transform (without energy loss or field rotations) + * @param aP Input transformed helix parameters + * @param F Returned derivative matrix + */ + void makeF(Vec aP, DMatrixRMaj F, double eFactor) { + makeF(aP, F, a, alpha, eFactor); + } + + /** + * Create derivative matrix for the pivot transform (without field rotations) + * @param aP Helix parameters + * @param F Returned derivative matrix + * @param eFactor 1.0-deltaE/E factor for energy loss + */ + static void makeF(Vec aP, DMatrixRMaj F, Vec a, double alpha, double eFactor) { F.unsafe_set(0, 0, FastMath.cos(aP.v[1] - a.v[1])); F.unsafe_set(0, 1, (a.v[0] + alpha / a.v[2]) * FastMath.sin(aP.v[1] - a.v[1])); - F.unsafe_set(0, 2, (alpha / (a.v[2] * a.v[2])) * (1.0 - FastMath.cos(aP.v[1] - a.v[1]))); + F.unsafe_set(0, 2, eFactor*(alpha / (a.v[2] * a.v[2])) * (1.0 - FastMath.cos(aP.v[1] - a.v[1]))); F.unsafe_set(1, 0, -FastMath.sin(aP.v[1] - a.v[1]) / (aP.v[0] + alpha / a.v[2])); F.unsafe_set(1, 1, (a.v[0] + alpha / a.v[2]) * FastMath.cos(aP.v[1] - a.v[1]) / (aP.v[0] + alpha / a.v[2])); - F.unsafe_set(1, 2, (alpha / (a.v[2] * a.v[2])) * FastMath.sin(aP.v[1] - a.v[1]) / (aP.v[0] + alpha / a.v[2])); - F.unsafe_set(2, 2, 1.0); + F.unsafe_set(1, 2, eFactor*(alpha / (a.v[2] * a.v[2])) * FastMath.sin(aP.v[1] - a.v[1]) / (aP.v[0] + alpha / a.v[2])); + F.unsafe_set(2, 2, eFactor); F.unsafe_set(3, 0, (alpha / a.v[2]) * a.v[4] * FastMath.sin(aP.v[1] - a.v[1]) / (aP.v[0] + alpha / a.v[2])); F.unsafe_set(3, 1, (alpha / a.v[2]) * a.v[4] * (1.0 - (a.v[0] + alpha / a.v[2]) * FastMath.cos(aP.v[1] - a.v[1]) / (aP.v[0] + alpha / a.v[2]))); - F.unsafe_set(3, 2, (alpha / (a.v[2] * a.v[2])) * a.v[4] + F.unsafe_set(3, 2, eFactor*(alpha / (a.v[2] * a.v[2])) * a.v[4] * (aP.v[1] - a.v[1] - (alpha / a.v[2]) * FastMath.sin(aP.v[1] - a.v[1]) / (aP.v[0] + alpha / a.v[2]))); F.unsafe_set(3, 3, 1.0); F.unsafe_set(3, 4, -(alpha / a.v[2]) * (aP.v[1] - a.v[1])); @@ -153,12 +321,23 @@ static void makeF(Vec aP, DMatrixRMaj F, Vec a, double alpha) { // All other values are always zero } - // Returns the particle momentum at the helix angle phi - // Warning! This is returned in the B-Field coordinate system. + /** + * Returns the particle momentum at the helix angle phi. + * Warning! This is returned in the B-Field coordinate system. + * @param phi turning angle from the pivot to the helix point of interest + * @return 3-momentum at the given angle + */ Vec getMom(double phi) { return getMom(phi, a); } - + + /** + * Returns the particle momentum at the helix angle phi. + * Warning! This is returned in the B-Field coordinate system. + * @param phi turning angle from the pivot to the helix point of interest + * @param a 5-vector of helix parameters + * @return 3-momentum at the given angle + */ static Vec getMom(double phi, Vec a) { double px = -FastMath.sin(a.v[1] + phi) / Math.abs(a.v[2]); double py = FastMath.cos(a.v[1] + phi) / Math.abs(a.v[2]); @@ -166,7 +345,11 @@ static Vec getMom(double phi, Vec a) { return new Vec(px, py, pz); } - // Momentum at the start of the given helix (point closest to the pivot) + /** + * Momentum at the start of the given helix (point closest to the pivot) + * @param a Helix parameters + * @return 3-momentum at pivot + */ static Vec aTOp(Vec a) { double px = -FastMath.sin(a.v[1]) / Math.abs(a.v[2]); double py = FastMath.cos(a.v[1]) / Math.abs(a.v[2]); @@ -174,7 +357,14 @@ static Vec aTOp(Vec a) { return new Vec(px, py, pz); } - // Transform from momentum at helix starting point back to the helix parameters + /** + * Transform from momentum at helix starting point back to the helix parameters + * @param p 3-momentum at pivot + * @param drho d-rho helix parameter (not transformed) + * @param dz dz helix parameter (not transformed) + * @param Q charge + * @return 4-vector of helix parameters + */ // drho and dz are not modified static Vec pTOa(Vec p, double drho, double dz, double Q) { double phi0 = FastMath.atan2(-p.v[0], p.v[1]); @@ -184,24 +374,39 @@ static Vec pTOa(Vec p, double drho, double dz, double Q) { return new Vec(drho, phi0, K, dz, tanl); } - // To transform a space point from global to local field coordinates, first subtract - // and then rotate by . + /** + * To transform a space point from global to local field coordinates, first subtract + * and then rotate by . + * @param xGlobal global space point + * @return local space point + */ Vec toLocal(Vec xGlobal) { Vec xLocal = Rot.rotate(xGlobal.dif(origin)); return xLocal; } - // To transform a space point from local field coordinates to global coordinates, first rotate by - // the inverse of and then add the . + /** + * To transform a space point from local field coordinates to global coordinates, first rotate by + * the inverse of and then add the . + * @param xLocal local space point + * @return global space point + */ Vec toGlobal(Vec xLocal) { Vec xGlobal = Rot.inverseRotate(xLocal).sum(origin); return xGlobal; } - // Transformation of helix parameters from one B-field frame to another, by rotation R - // Warning: the pivot point has to be transformed too! Here we assume that the new pivot point - // will be on the helix at phi=0, so drho and dz will always be returned as zero. Therefore, before - // calling this routine, make sure that the current pivot point is on the helix (drho=dz=0) + /** + * Transformation of helix parameters from one B-field frame to another, by rotation R + * Warning: the pivot point has to be transformed too! Here we assume that the new pivot point + * will be on the helix at phi=0, so drho and dz will always be returned as zero. Therefore, before + * calling this routine, make sure that the current pivot point is on the helix (drho=dz=0). + * This routine is not used if the B-Field is assumed uniform. + * @param a 5-vector of helix parameters + * @param R rotation matrix + * @param fRot returned derivative matrix + * @return 5 transformed helix parameters + */ static Vec rotateHelix(Vec a, RotMatrix R, DMatrixRMaj fRot) { // The rotation is easily applied to the momentum vector, so first we transform from helix parameters // to momentum, apply the rotation, and then transform back to helix parameters. @@ -281,29 +486,48 @@ static Vec rotateHelix(Vec a, RotMatrix R, DMatrixRMaj fRot) { return aNew; } - // Transform the helix to a pivot back at the global origin + /** + * Transform the helix to a pivot back at the global origin + * @return new helix parameters + */ Vec pivotTransform() { Vec pivot = origin.scale(-1.0); return pivotTransform(pivot); } - // Pivot transform of the state vector, from the current pivot to the pivot in - // the argument (specified in local coordinates) + /** + * Pivot transform of the state vector, from the current pivot to the pivot provided + * @param pivot the new pivot point (specified in local coordinates) + * @return the new 5-vector of helix parameters + */ Vec pivotTransform(Vec pivot) { return pivotTransform(pivot, a, X0, alpha, 0.); } - // Pivot transform including energy loss just before + /** + * Pivot transform including energy loss just before (energy loss formalism is still not tested and used) + * @param pivot + * @param deltaEoE + * @return + */ Vec pivotTransform(Vec pivot, double deltaEoE) { return pivotTransform(pivot, a, X0, alpha, deltaEoE); } + /** + * Pivot transform of the state vector, from the current pivot to the pivot provided + * @param pivot new pivot + * @param a old helix parameters + * @param X0 old pivot + * @param alpha parameter to transform between curvature and momentum + * @param deltaEoE energy loss + * @return new helix parameters + */ static Vec pivotTransform(Vec pivot, Vec a, Vec X0, double alpha, double deltaEoE) { double K = a.v[2] * (1.0 - deltaEoE); // Lose energy before propagating double xC = X0.v[0] + (a.v[0] + alpha / K) * FastMath.cos(a.v[1]); // Center of the helix circle double yC = X0.v[1] + (a.v[0] + alpha / K) * FastMath.sin(a.v[1]); - // if (verbose) System.out.format("pivotTransform center=%13.10f, %13.10f\n", - // xC, yC); + // if (verbose) System.out.format("pivotTransform center=%13.10f, %13.10f\n", xC, yC); // Predicted state vector double[] aP = new double[5]; @@ -323,17 +547,28 @@ static Vec pivotTransform(Vec pivot, Vec a, Vec X0, double alpha, double deltaEo return new Vec(5, aP); } - // Propagate a helix by Runge-Kutta integration to an arbitrary plane + /** + * Propagate a helix by Runge-Kutta integration to an arbitrary plane + * @param pln plane to where the extrapolation is taking place in global coordinates. + * The origin of pln will be the new helix pivot point in global coordinates and the origin of the B-field system. + * @param yScat input array of y values where scattering in silicon will take place. Only those between the start and finish points + * will be used, so including extras will just waste a bit of CPU time. Silicon is assumed to lie in a plane + * perpendicular to the beam axis at each of these yScat values. + * @param XL input array of y values where scattering in silicon will take place. Only those between the start and finish points + * will be used, so including extras will just waste a bit of CPU time. Silicon is assumed to lie in a plane + * perpendicular to the beam axis at each of these yScat values. + * @param fM HPS field map + * @param arcLength return the arc length to the plane + * @return helix state at the new pivot. These helix parameters are valid in the B-field coordinate system with + * origin at the pivot point and z axis in the direction of the B-field at the pivot. + */ HelixState propagateRungeKutta(Plane pln, ArrayList yScat, ArrayList XL, org.lcsim.geometry.FieldMap fM, double [] arcLength) { - // pln = plane to where the extrapolation is taking place in global coordinates. - // The origin of pln will be the new helix pivot point in global coordinates and the origin of the B-field system. - // yScat = input array of y values where scattering in silicon will take place. Only those between the start and finish points - // will be used, so including extras will just waste a bit of CPU time. Silicon is assumed to lie in a plane - // perpendicular to the beam axis at each of these yScat values. + // pln = + // + // yScat = // XL = silicon thickness in radiation lengths at each of the scattering planes - // fM = HPS field map - // return value = helix state at the new pivot. These helix parameters are valid in the B-field coordinate system with - // origin at the pivot point and z axis in the direction of the B-field at the pivot. + // fM = + // return value = final boolean debug = false; @@ -418,30 +653,54 @@ HelixState propagateRungeKutta(Plane pln, ArrayList yScat, ArrayList XL, org.lcsim.geometry.FieldMap fM, double [] arcLength) { ArrayList yScat = new ArrayList(); return propagateRungeKutta(pln, yScat, XL, fM, arcLength); } + /** + * Get the intersection point with the plane, as calculated by propagateRungeKutta + * @return 3-vector intersection point + */ Vec getRKintersection() { return xPlaneRK; } + /** + * Calculated the expected rms projected multiple scattering angle. + * @param p momentum magnitude + * @param XL radiation lengths of the scattering material + * @return rms projected angle + */ static double projMSangle(double p, double XL) { if (XL <= 0.) return 0.; - return (0.0136 / Math.abs(p)) * FastMath.sqrt(XL) * (1.0 + 0.038 * FastMath.log(XL)); - } - - boolean helixStepper(double maxStep, ArrayList yScat, ArrayList XL, DMatrixRMaj Covariance, Vec finalHelix, Vec newOrigin, org.lcsim.geometry.FieldMap fM) { - // The old and new origin points are in global coordinates. The old helix and old pivot are defined - // in a coordinate system aligned with the field and centered at the old origin. The returned - // helix will be in a coordinate system aligned with the local field at the new origin, and the - // new pivot point will be at the new origin, in global coordinates, although to use it one should transform to the field frame, which - // is defined with its origin at newOrigin and aligned there with the local field. - // All scattering layers are assumed to be of the same thickness XL, in radiation lengths - // We assume that the starting StateVector is at a layer with a hit, in which case the Kalman filter has already accounted for - // multiple scattering at that layer. + return (0.0136 / p) * FastMath.sqrt(XL) * (1.0 + 0.038 * FastMath.log(XL)); + } + + /** + * Transform a helix in discrete steps through a non-uniform B field. Each step assumes locally uniform field. + * The old and new origin points are in global coordinates. The old helix and old pivot are defined + * in a coordinate system aligned with the field and centered at the old origin. The returned + * helix will be in a coordinate system aligned with the local field at the new origin, and the + * new pivot point will be at the new origin, in global coordinates, although to use it one should transform to the field frame, which + * is defined with its origin at newOrigin and aligned there with the local field. + * All scattering layers are assumed to be of the same thickness XL, in radiation lengths + * We assume that the starting StateVector is at a layer with a hit, in which case the Kalman filter has already accounted for + * multiple scattering at that layer. + * Note: energy loss is not included. + * @param maxStep Maximum step size, in mm + * @param yScat Locations of scattering planes + * @param XL radiation lengths of scattering planes + * @param Covariance output the covariance matrix of the transformed helix + * @param finalHelix output transformed helix + * @param newOrigin new origin 3-vector point, in global coordinates + * @param fM HPS field map + * @return successful conclusion if true + */ + boolean helixStepper(double maxStep, ArrayList yScat, ArrayList XL, DMatrixRMaj Covariance, Vec finalHelix, Vec newOrigin, org.lcsim.geometry.FieldMap fM) { final boolean debug = false; @@ -561,7 +820,7 @@ boolean helixStepper(double maxStep, ArrayList yScat, ArrayList Vec newPoint = atPhi(newPivot, newHelixPivoted, dphi, localAlpha); newPoint.print("new point of intersection, should be same as the old"); } - makeF(newHelixPivoted, F, newHelix, localAlpha); + makeF(newHelixPivoted, F, newHelix, localAlpha, 1.0); newHelix = newHelixPivoted; // Rotate the helix into the field system at the new origin @@ -610,7 +869,7 @@ boolean helixStepper(double maxStep, ArrayList yScat, ArrayList Vec newOriginLocal = new Vec(0.,0.,0.); Vec oldPivot = RM.rotate(Origin.dif(newOrigin)); Vec finalHx = pivotTransform(newOriginLocal, newHelix, oldPivot, localAlpha, 0.); - makeF(finalHx, F, newHelix, localAlpha); + makeF(finalHx, F, newHelix, localAlpha, 1.0); CommonOps_DDRM.multTransB(Cov, F, cIntermediate); CommonOps_DDRM.mult(F,cIntermediate,Cov); if (debug) { @@ -626,17 +885,10 @@ boolean helixStepper(double maxStep, ArrayList yScat, ArrayList return true; } - // Transform the HelixState into a standard HPS TrackState (which loses a lot of information) - // In the returned TrackState the reference point gets set to the point on the helix closest to the - // original pivot point (e.g. the helix intersection with the plane of silicon). - // The pivot of the returned TrackState is always the origin (0,0,0) - TrackState toTrackState(double alphaCenter, Plane pln, int loc) { - // See TrackState for the different choices for loc (e.g. TrackState.atOther) - return KalmanInterface.toTrackState(this, pln, alphaCenter, loc); - } - - // Transform a helix from one pivot to another through a non-uniform B field in several steps - // Deprecated original version without scattering planes + /** + * Transform a helix from one pivot to another through a non-uniform B field in several steps + * Deprecated original version without scattering planes + */ Vec helixStepper(int nSteps, DMatrixRMaj Covariance, Vec newOrigin, org.lcsim.geometry.FieldMap fM) { double maxStep = Math.abs(newOrigin.v[1]-origin.v[1])/(double)nSteps; ArrayList yScats = new ArrayList(); @@ -645,8 +897,25 @@ Vec helixStepper(int nSteps, DMatrixRMaj Covariance, Vec newOrigin, org.lcsim.ge helixStepper(maxStep, yScats, XL, Covariance, transHelix, newOrigin, fM); return transHelix; } + + /** + * Transform the HelixState into a standard HPS TrackState (which loses a lot of information) + * In the returned TrackState the reference point gets set to the point on the helix closest to the + * original pivot point (e.g. the helix intersection with the plane of silicon). + * The pivot of the returned TrackState is always the origin (0,0,0) + * @param alphaCenter parameter to transform between curvature and momentum at the tracker center + * @param pln the position and direction cosines of the plane + * @param loc location descriptor (from LCSIM) + * @return + */ + TrackState toTrackState(double alphaCenter, Plane pln, int loc) { + // See TrackState for the different choices for loc (e.g. TrackState.atOther) + return KalmanInterface.toTrackState(this, pln, alphaCenter, loc); + } - // Comparator function for sorting pairs in helixStepper by y + /** + * Comparator function for sorting pairs in helixStepper by y + */ static Comparator> pairComparator = new Comparator>() { public int compare(Pair p1, Pair p2) { if (p1.getFirstElement() < p2.getFirstElement()) { @@ -657,8 +926,11 @@ public int compare(Pair p1, Pair p2) { } }; - // Multiple scattering matrix; assume a single thin scattering layer at the - // beginning of the helix propagation + /** + * Multiple scattering matrix; assume a single thin scattering layer at the beginning of the helix propagation + * @param sigmaMS rms projected multiple scattering angle + * @param Q returned multiple scattering matrix + */ void getQ(double sigmaMS, DMatrixRMaj Q) { double V = sigmaMS * sigmaMS; Q.unsafe_set(1, 1, V * (1.0 + a.v[4] * a.v[4])); diff --git a/tracking/src/main/java/org/hps/recon/tracking/kalman/HelixTest3.java b/tracking/src/main/java/org/hps/recon/tracking/kalman/HelixTest3.java index 5b0df88a89..24d97f72c7 100644 --- a/tracking/src/main/java/org/hps/recon/tracking/kalman/HelixTest3.java +++ b/tracking/src/main/java/org/hps/recon/tracking/kalman/HelixTest3.java @@ -12,6 +12,7 @@ import java.util.Random; import org.hps.recon.tracking.gbl.matrix.Matrix; +import org.apache.commons.math.util.FastMath; import org.ejml.data.DMatrixRMaj; import org.ejml.dense.row.CommonOps_DDRM; import org.ejml.dense.row.MatrixFeatures_DDRM; @@ -19,9 +20,11 @@ import org.hps.util.Pair; import org.lcsim.event.TrackState; +import org.lcsim.geometry.Detector; /** - * This is for stand-alone testing of the Kalman fit only and is not part of the HPS Kalman fitting code package + * This is for stand-alone testing of the Kalman fit only and is not part of the + * HPS Kalman fitting code package */ class HelixTest3 { // Program for testing the Kalman fitting code @@ -29,47 +32,60 @@ class HelixTest3 { // Program for testing the Kalman fitting code // z is the B field direction, downward in lab coordinates // y is the beam direction // x is y cross z - Random rnd; HelixTest3(String path) { // Control parameters // Units are Tesla, GeV, mm - - int nTrials = 100; // The number of test events to generate for fitting + int nTrials = 10000; // The number of test events to generate for fitting + boolean residualsEconstrained = false; // Use constrained or unconstrained tracks for residuals histograms int startLayer = 10; // Where to start the Kalman filtering int nIteration = 2; // Number of filter iterations int nAxial = 3; // Number of axial layers needed by the linear fit int nStereo = 4; // Number of stereo layers needed by the linear fit boolean cheat = false; // true to use the true helix parameters (smeared) for the starting guess boolean perfect = false; - double [] vtxRes = {0.1, 0.5, 0.05}; + double[] vtxRes = {0.1, 0.5, 0.05}; boolean verbose = nTrials < 2; double executionTime = 0.; int nBadCov = 0; - + double eCalLoc = 1394.; - + DMatrixRMaj testCov = null; Vec testHelix = null; - DMatrixRMaj tempM1 = new DMatrixRMaj(5,5); - DMatrixRMaj tempM2 = new DMatrixRMaj(5,5); - DMatrixRMaj fRot = new DMatrixRMaj(5,5); + DMatrixRMaj tempM1 = new DMatrixRMaj(5, 5); + DMatrixRMaj tempM2 = new DMatrixRMaj(5, 5); + DMatrixRMaj fRot = new DMatrixRMaj(5, 5); + for (int i = 0; i < 5; ++i) { + for (int j = 0; j < 5; ++j) { + if (i == j) { + fRot.unsafe_set(i, j, 1.); + } else { + fRot.unsafe_set(i, j, 0); + } + } + } KalmanParams kPar = new KalmanParams(); - + kPar.setEloss(true); + kPar.print(); + // Seed the random number generator long rndSeed = -3263009337738135404L; rnd = new Random(); rnd.setSeed(rndSeed); - + Histogram hGaus = new Histogram(100, -4., 0.08, "Normal Distribution 1", "x", "y"); - for (int i = 0; i < 1000000; i++) { hGaus.entry(rnd.nextGaussian()); } + for (int i = 0; i < 1000000; i++) { + hGaus.entry(rnd.nextGaussian()); + } // Read in the magnetic field map String mapType = "binary"; - String mapFile = "C:\\Users\\Robert\\Documents\\GitHub\\hps-java\\fieldmap\\125acm2_3kg_corrected_unfolded_scaled_0.7992_v3.bin"; + String mapFile = "C:\\Users\\rjohn\\Documents\\GitHub\\hps-java\\fieldmap\\418acm2_10kg_corrected_unfolded_scaled_1.0319.bin"; + //String mapFile = "C:\\Users\\rjohn\\Documents\\GitHub\\hps-java\\fieldmap\\125acm2_3kg_corrected_unfolded_scaled_0.7992_v3.bin"; FieldMap fM = null; FieldMap fMg = null; try { @@ -80,35 +96,37 @@ class HelixTest3 { // Program for testing the Kalman fitting code return; } if (mapType != "binary") { - fM.writeBinaryFile("C:\\Users\\Robert\\Documents\\GitHub\\hps-java\\fieldmap\\125acm2_3kg_corrected_unfolded_scaled_0.7992_v2.bin"); + //fM.writeBinaryFile("C:\\Users\\rjohn\\Documents\\GitHub\\hps-java\\fieldmap\\125acm2_3kg_corrected_unfolded_scaled_0.7992_v2.bin"); + fM.writeBinaryFile("C:\\Users\\rjohn\\Documents\\GitHub\\hps-java\\fieldmap\\418acm2_10kg_corrected_unfolded_scaled_1.0319.bin"); } System.out.format("B field map vs y:\n"); - for (double y=0.; y<1500.; y+=5.) { - double z1=-50.; - double z2= 50.; + for (double y = 0.; y < 1500.; y += 5.) { + double z1 = -50.; + double z2 = 50.; Vec B1 = new Vec(3, fM.getField(new Vec(0., y, z1))); Vec B2 = new Vec(3, fM.getField(new Vec(0., y, 0.))); Vec B3 = new Vec(3, fM.getField(new Vec(0., y, z2))); System.out.format("y=%6.1f z=%6.1f: %s z=0: %s z=%6.1f: %s\n", y, z1, B1.toString(), B2.toString(), z2, B3.toString()); } System.out.format("B field map vs z at ECAL:\n"); - for (double z=-200.; z<200.; z+=5.) { - double y=eCalLoc + 10.; + for (double z = -200.; z < 200.; z += 5.) { + double y = eCalLoc + 10.; Vec B = new Vec(3, fM.getField(new Vec(0., y, z))); System.out.format("x=0 y=%6.1f z=%6.1f: %s\n", y, z, B.toString()); } System.out.format("B field map vs x at center:\n"); - for (double x=-300.; x<300.; x+=5.) { - double y=505.; - double z=20.; + for (double x = -300.; x < 300.; x += 5.) { + double y = 505.; + double z = 20.; Vec B = new Vec(3, fM.getField(new Vec(x, y, z))); System.out.format("x=%6.1f y=%6.1f z=%6.1f: %s\n", x, y, z, B.toString()); } // Tracking instrument description - double thickness = 0.3; // Silicon thickness in mm - if (perfect) { thickness = 0.0000000000001; } + if (perfect) { + thickness = 0.0000000000001; + } ArrayList SiModules = new ArrayList(); Plane plnInt; SiModule newModule; @@ -187,7 +205,7 @@ class HelixTest3 { // Program for testing the Kalman fitting code plnInt = new Plane(new Vec(0., 870.0, 30.0), new Vec(0., 1.0, 0.)); newModule = new SiModule(-5, plnInt, false, 0., 900., 900., 0., fM, 0); SiModules.add(newModule); - */ + */ plnInt = new Plane(new Vec(-22.879, 905.35, 35.309), new Vec(-0.029214, -0.99957, 0.0019280), -0.049801); newModule = new SiModule(11, plnInt, true, 100., 40.34, false, thickness, fM, 0); SiModules.add(newModule); @@ -214,12 +232,12 @@ class HelixTest3 { // Program for testing the Kalman fitting code for (SiModule mod : SiModules) { if (mod.Layer == layer) { yScat.add(mod.p.X().v[1]); - XLscat.add(mod.thickness/radLen); + XLscat.add(mod.thickness / radLen); break; } } } - + double[] location = new double[nLayers]; double[] xdet = new double[SiModules.size()]; double[] ydet = new double[SiModules.size()]; @@ -227,12 +245,14 @@ class HelixTest3 { // Program for testing the Kalman fitting code int[] lyr = new int[SiModules.size()]; for (int i = 0; i < SiModules.size(); i++) { SiModule si = SiModules.get(i); - - si.p.T().v[0]=0.; - si.p.T().v[1]=1. * Math.signum(si.p.T().v[1]); // !!!!!!!!!!!!!!!!!!!!! All modules aligned - si.p.T().v[2]=0.; - - if (si.Layer >= 0) { location[si.Layer] = si.p.X().v[1]; } + + si.p.T().v[0] = 0.; + si.p.T().v[1] = 1. * Math.signum(si.p.T().v[1]); // !!!!!!!!!!!!!!!!!!!!! All modules aligned + si.p.T().v[2] = 0.; + + if (si.Layer >= 0) { + location[si.Layer] = si.p.X().v[1]; + } lyr[i] = si.Layer; xdet[i] = si.p.X().v[0]; ydet[i] = si.p.X().v[1]; @@ -243,16 +263,17 @@ class HelixTest3 { // Program for testing the Kalman fitting code double resolution = 0.006; // SSD point resolution, in mm double Q = -1.0; - double p = 1.3; + double p = 3.5; + double Etrue = p; double hitEfficiency = 1.0; Vec helixOrigin = new Vec(0., 0., 0.); // Pivot point of initial helix - Vec Bpivot = new Vec(3,fM.getField(helixOrigin)); + Vec Bpivot = new Vec(3, fM.getField(helixOrigin)); Bpivot.print("magnetic field at the initial origin"); for (int pln = 0; pln < SiModules.size(); pln++) { - Vec bf = new Vec(3,fM.getField(new Vec(xdet[pln], ydet[pln], zdet[pln]))); + Vec bf = new Vec(3, fM.getField(new Vec(xdet[pln], ydet[pln], zdet[pln]))); System.out.format("Kalman fitting B field at module %d = %10.7f, %10.7f, %10.7f\n", pln, bf.v[0], bf.v[1], bf.v[2]); - bf = new Vec(3,fMg.getField(new Vec(xdet[pln], ydet[pln], zdet[pln]))); + bf = new Vec(3, fMg.getField(new Vec(xdet[pln], ydet[pln], zdet[pln]))); System.out.format("MC generator B field at module %d = %10.7f, %10.7f, %10.7f\n", pln, bf.v[0], bf.v[1], bf.v[2]); } double Phi = 91.5 * Math.PI / 180.; @@ -273,8 +294,8 @@ class HelixTest3 { // Program for testing the Kalman fitting code Vec helixMCtrue = null; Vec momentum = new Vec(p * initialDirection.v[0], p * initialDirection.v[1], p * initialDirection.v[2]); momentum.print("initial helix momentum"); - Helix TkInitial = new Helix(Q, helixOrigin, momentum, helixOrigin, fMg, rnd); - RKhelix TkRKinitial = new RKhelix(helixOrigin, momentum, Q, fMg, rnd); + Helix TkInitial = new Helix(Q, helixOrigin, momentum, helixOrigin, fMg, rnd, kPar.eLoss); + RKhelix TkRKinitial = new RKhelix(helixOrigin, momentum, Q, fMg, rnd, kPar.eLoss); drho = TkInitial.p.v[0]; phi0 = TkInitial.p.v[1]; K = TkInitial.p.v[2]; @@ -335,7 +356,7 @@ class HelixTest3 { // Program for testing the Kalman fitting code printWriter.format("splot "); printWriter.format("$runga u 1:2:3 with lines lw 3, $helix u 1:2:3 with lines lw 3\n"); printWriter.close(); - + Vec zhat = null; Vec uhat = null; Vec vhat = null; @@ -356,6 +377,7 @@ class HelixTest3 { // Program for testing the Kalman fitting code Histogram hXscat = new Histogram(100, -1., 0.02, "X scattering angles", "degrees", "Si planes"); Histogram hZscat = new Histogram(100, -0.5, 0.01, "Z scattering angles", "degrees", "Si planes"); Histogram hChi2HelixS = new Histogram(80, 0., 0.4, "smoothed chi^2 of helix parameters", "chi^2", "tracks"); + Histogram hChi2HelixE = new Histogram(80, 0., 0.4, "Helix parameters chi^2 at the origin, energy constrained", "chi^2", "tracks"); Histogram hChi2Helix = new Histogram(80, 0., 0.4, "filtered chi^2 of helix parameters", "chi^2", "tracks"); Histogram hChi2Guess = new Histogram(80, 0., 2.0, "chi^2 of guess helix parameters", "chi^2", "tracks"); Histogram hChi2Origin = new Histogram(80, 0., .4, "Helix parameters chi^2 at the origin", "chi^2", "tracks"); @@ -376,6 +398,13 @@ class HelixTest3 { // Program for testing the Kalman fitting code Histogram hEkO = new Histogram(100, -10., 0.2, "Origin helix parameter K error", "sigmas", "track"); Histogram hEdzO = new Histogram(100, -10., 0.2, "Origin helix parameter dz error", "sigmas", "track"); Histogram hEtanlO = new Histogram(100, -10., 0.2, "Origin helix parameter tanl error", "sigmas", "track"); + Histogram hChi2E = new Histogram(80, 0., 1.0, "Helix fit chi^2 after energy constraint", "chi^2", "tracks"); + Histogram hEdrhoCon = new Histogram(100, -10., 0.2, "Origin helix parameter drho error with ECAL constraint", "sigmas", "track"); + Histogram hEphi0Con = new Histogram(100, -10., 0.2, "Origin helix parameter phi0 error with ECAL constraint", "sigmas", "track"); + Histogram hEkCon = new Histogram(100, -10., 0.2, "Origin helix parameter K error with ECAL constraint", "sigmas", "track"); + Histogram hEdzCon = new Histogram(100, -10., 0.2, "Origin helix parameter dz error with ECAL constraint", "sigmas", "track"); + Histogram hEtanlCon = new Histogram(100, -10., 0.2, "Origin helix parameter tanl error with ECAL constraint", "sigmas", "track"); + Histogram hEdrhoSigO = new Histogram(100, -10., 0.2, "Origin helix parameter drho constrained error", "sigmas", "track"); Histogram hEphi0SigO = new Histogram(100, -10., 0.2, "Origin helix parameter phi0 constrained error", "sigmas", "track"); Histogram hEkSigO = new Histogram(100, -10., 0.2, "Origin helix parameter K constrained error", "sigmas", "track"); @@ -383,16 +412,21 @@ class HelixTest3 { // Program for testing the Kalman fitting code Histogram hEtanlSigO = new Histogram(100, -10., 0.2, "Origin helix parameter tanl constrained error", "sigmas", "track"); Histogram hEadrhoO = new Histogram(100, -1., 0.02, "Origin helix parameter drho error", "mm", "track"); Histogram hEaphi0O = new Histogram(100, -0.02, 0.0004, "Origin helix parameter phi0 error", "radians", "track"); - Histogram hEakO = new Histogram(100, -0.5, 0.01, "Origin helix parameter K error", "1/GeV", "track"); + Histogram hEPhi0con = new Histogram(100, -0.02, 0.0004, "Origin helix parameter phi0 error with ECAL constraint", "radians", "track"); + Histogram hEakO = new Histogram(100, -0.2, 0.004, "Origin helix parameter K error", "1/GeV", "track"); + Histogram hEakcon = new Histogram(100, -0.2, 0.004, "Origin helix parameter K error with ECAL constraint", "1/GeV", "track"); Histogram hEadzO = new Histogram(100, -0.4, 0.008, "Origin helix parameter dz error", "mm", "track"); + Histogram hEZ0con = new Histogram(100, -0.4, 0.008, "Origin helix parameter dz error with ECAL constraint", "mm", "track"); Histogram hEatanlO = new Histogram(100, -0.005, 0.0001, "Origin helix parameter tanl error", " ", "track"); + Histogram hEatanlcon = new Histogram(100, -0.005, 0.0001, "Origin helix parameter tanl error with ECAL constraint", " ", "track"); Histogram hEcdrhoO = new Histogram(100, -1., 0.02, "Origin helix parameter drho constrained error", "mm", "track"); + Histogram hERhocon = new Histogram(100, -1., 0.02, "Origin helix parameter drho constrained error with ECAL constrain", "mm", "track"); Histogram hEcphi0O = new Histogram(100, -0.02, 0.0004, "Origin helix parameter phi0 constrained error", "radians", "track"); Histogram hEckO = new Histogram(100, -0.5, 0.01, "Origin helix parameter K constrained error", "1/GeV", "track"); Histogram hEcdzO = new Histogram(100, -0.4, 0.008, "Origin helix parameter dz constrained error", "mm", "track"); Histogram hEctanlO = new Histogram(100, -0.005, 0.0001, "Origin helix parameter tanl constrained error", " ", "track"); - Histogram hchi2inc = new Histogram(100, 0., 1., "Origin helix constrained chi^2 increment","chi2","track"); - Histogram hchi2c = new Histogram(100, 0., 1., "Origin helix constrained chi^2","chi2","track"); + Histogram hchi2inc = new Histogram(100, 0., 1., "Origin helix constrained chi^2 increment", "chi2", "track"); + Histogram hchi2c = new Histogram(100, 0., 1., "Origin helix constrained chi^2", "chi2", "track"); Histogram hResid0 = new Histogram(100, -10., 0.2, "Filtered residual for axial planes", "sigmas", "hits"); Histogram hResid1 = new Histogram(100, -10., 0.2, "Filtered residual for stereo planes", "sigmas", "hits"); Histogram hEdrhoG = new Histogram(100, -40., 0.8, "Helix guess drho error", "sigmas", "track"); @@ -412,47 +446,59 @@ class HelixTest3 { // Program for testing the Kalman fitting code Histogram[] hResidZ = new Histogram[nLayers]; Histogram[] hUnbias = new Histogram[nLayers]; Histogram[] hUnbiasSig = new Histogram[nLayers]; + String preamb = ""; + if (residualsEconstrained) { + preamb = "E constrained "; + } for (int i = 0; i < nLayers; i++) { - hResidS0[i] = new Histogram(100, -10., 0.2, String.format("Smoothed fit residual for plane %d", i), "sigmas", "hits"); - hResidS2[i] = new Histogram(100, -0.02, 0.0004, String.format("Smoothed fit residual for plane %d", i), "mm", "hits"); - hResidS4[i] = new Histogram(100, -0.1, 0.002, String.format("Smoothed true residual for plane %d", i), "mm", "hits"); - hResidX[i] = new Histogram(100, -0.8, 0.016, String.format("True residual in global X for plane %d", i), "mm", "hits"); - hResidZ[i] = new Histogram(100, -0.1, 0.002, String.format("True residual in global Z for plane %d", i), "mm", "hits"); - hUnbias[i] = new Histogram(100, -0.2, 0.004, String.format("Unbiased residual for plane %d", i), "mm", "hits"); - hUnbiasSig[i] = new Histogram(100, -10., 0.2, String.format("Unbiased residuals for layer %d", i), "sigmas", "hits"); + hResidS0[i] = new Histogram(100, -10., 0.2, String.format(preamb + "Smoothed fit residual for plane %d", i), "sigmas", "hits"); + hResidS2[i] = new Histogram(100, -0.02, 0.0004, String.format(preamb + "Smoothed fit residual for plane %d", i), "mm", "hits"); + hResidS4[i] = new Histogram(100, -0.1, 0.002, String.format(preamb + "Smoothed true residual for plane %d", i), "mm", "hits"); + hResidX[i] = new Histogram(100, -0.8, 0.016, String.format(preamb + "True residual in global X for plane %d", i), "mm", "hits"); + hResidZ[i] = new Histogram(100, -0.1, 0.002, String.format(preamb + "True residual in global Z for plane %d", i), "mm", "hits"); + hUnbias[i] = new Histogram(100, -0.2, 0.004, String.format(preamb + "Unbiased residual for plane %d", i), "mm", "hits"); + hUnbiasSig[i] = new Histogram(100, -10., 0.2, String.format(preamb + "Unbiased residuals for layer %d", i), "sigmas", "hits"); } - Histogram hpropx = new Histogram(100,-5.,0.1,"projected track-state x error","mm","track"); - Histogram hpropxs = new Histogram(100,-5.,0.1,"projected track-state x error","sigmas","track"); - Histogram hpropy = new Histogram(100,-5.,0.1,"projected track-state y error","mm","track"); - Histogram hpropys = new Histogram(100,-5.,0.1,"projected track-state y error","sigmas","track"); - Histogram hpropxu = new Histogram(100,0.,0.05,"projected track-state x uncertainty","mm","track"); - Histogram hpropyu = new Histogram(100,0.,0.05,"projected track-state y uncertainty","mm","track"); - Histogram hPropxHS = new Histogram(100,-5.,0.1,"projected HelixState x error","mm","track"); - Histogram hPropzHS = new Histogram(100,-5.,0.1,"projected HelixState z error","mm","track"); - Histogram hPropxsHS = new Histogram(100,-5.,0.1,"projected HelixState x error","sigmas","track"); - Histogram hPropzsHS = new Histogram(100,-5.,0.1,"projected HelixState z error","sigmas","track"); - Histogram hPropx1 = new Histogram(100,-5.,0.1,"projected track-state x error 1 step","mm","track"); - Histogram hPropx1s = new Histogram(100,-5.,0.1,"projected track-state x error 1 step","sigmas","track"); - Histogram hPropz1 = new Histogram(100,-5.,0.1,"projected track-state z error 1 step","mm","track"); - Histogram hPropz1s = new Histogram(100,-5.,0.1,"projected track-state z error 1 step","sigmas","track"); + Histogram hpropx = new Histogram(100, -5., 0.1, "projected track-state x error", "mm", "track"); + Histogram hpropxs = new Histogram(100, -5., 0.1, "projected track-state x error", "sigmas", "track"); + Histogram hpropy = new Histogram(100, -5., 0.1, "projected track-state y error", "mm", "track"); + Histogram hpropys = new Histogram(100, -5., 0.1, "projected track-state y error", "sigmas", "track"); + Histogram hpropxu = new Histogram(100, 0., 0.05, "projected track-state x uncertainty", "mm", "track"); + Histogram hpropyu = new Histogram(100, 0., 0.05, "projected track-state y uncertainty", "mm", "track"); + Histogram hPropxHS = new Histogram(100, -5., 0.1, "projected HelixState x error", "mm", "track"); + Histogram hPropzHS = new Histogram(100, -5., 0.1, "projected HelixState z error", "mm", "track"); + Histogram hPropxsHS = new Histogram(100, -5., 0.1, "projected HelixState x error", "sigmas", "track"); + Histogram hPropzsHS = new Histogram(100, -5., 0.1, "projected HelixState z error", "sigmas", "track"); + Histogram hPropx1 = new Histogram(100, -5., 0.1, "projected track-state x error 1 step", "mm", "track"); + Histogram hPropx1s = new Histogram(100, -5., 0.1, "projected track-state x error 1 step", "sigmas", "track"); + Histogram hPropz1 = new Histogram(100, -5., 0.1, "projected track-state z error 1 step", "mm", "track"); + Histogram hPropz1s = new Histogram(100, -5., 0.1, "projected track-state z error 1 step", "sigmas", "track"); + Instant timestamp = Instant.now(); System.out.format("Beginning time = %s\n", timestamp.toString()); LocalDateTime ldt = LocalDateTime.ofInstant(timestamp, ZoneId.systemDefault()); System.out.format("%s %d %d at %d:%d %d.%d seconds\n", ldt.getMonth(), ldt.getDayOfMonth(), ldt.getYear(), ldt.getHour(), ldt.getMinute(), ldt.getSecond(), ldt.getNano()); - double startTime = (double)(ldt.getMinute())*60. + (double)ldt.getSecond() + (double)(ldt.getNano())/1e9; + double startTime = (double) (ldt.getMinute()) * 60. + (double) ldt.getSecond() + (double) (ldt.getNano()) / 1e9; // Extrapolate the helix from the origin to the first detector layer SiModule si1 = SiModules.get(0); double phi1 = TkInitial.planeIntersect(si1.p); if (Double.isNaN(phi1)) { - if (verbose) System.out.format("Oops! No intersection found with initial plane"); + if (verbose) { + System.out.format("Oops! No intersection found with initial plane"); + } return; } + Vec lyr1Int = TkInitial.atPhi(phi1); Vec p1 = new Vec(3); + // Find intersection with 1st plane by RK integration Vec pivotBegin = TkRKinitial.planeIntersect(si1.p, p1); - Helix helixBegin = new Helix(Q, pivotBegin, p1, pivotBegin, fMg, rnd); - RKhelix helixBeginRK = new RKhelix(pivotBegin, p1, Q, fMg, rnd); + Vec errOfB = pivotBegin.dif(lyr1Int); + errOfB.print("error in extrap to lyr 1 without RK"); + Helix helixBegin = new Helix(Q, pivotBegin, p1, pivotBegin, fMg, rnd, kPar.eLoss); + Helix helixEnd = null; + RKhelix helixBeginRK = new RKhelix(pivotBegin, p1, Q, fMg, rnd, kPar.eLoss); Vec[] helixSaved = new Vec[SiModules.size()]; Vec[] pivotSaved = new Vec[SiModules.size()]; @@ -460,16 +506,21 @@ class HelixTest3 { // Program for testing the Kalman fitting code TkInitial.print("TkInitial: initial helix at the origin"); helixBegin.print("helixBegin: starting helix at layer 1"); RKhelix TkEnd = helixBeginRK; - KalmanInterface KI = new KalmanInterface(false, kPar, fM); + Detector detect = null; + KalmanInterface KI = new KalmanInterface(kPar, detect, fM); for (int iTrial = 0; iTrial < nTrials; iTrial++) { RKhelix Tk = helixBeginRK.copy(); - if (verbose) { Tk.print("copied initial helix"); } + if (verbose) { + Tk.print("copied initial helix"); + } // Populate the Si detector planes with hits from the helix scattered at each // plane for (int icm = 0; icm < SiModules.size(); icm++) { SiModule thisSi = SiModules.get(icm); - if (thisSi.Layer < 0) { continue; } + if (thisSi.Layer < 0) { + continue; + } thisSi.reset(); int pln = thisSi.Layer; int det = thisSi.detector; @@ -493,10 +544,12 @@ class HelixTest3 { // Program for testing the Kalman fitting code // Check whether the intersection is within the bounds of the detector if (rDet.v[0] > thisSi.xExtent[1] || rDet.v[0] < thisSi.xExtent[0] || rDet.v[1] > thisSi.yExtent[1] || rDet.v[1] < thisSi.yExtent[0]) { - if (verbose) { System.out.format(" Intersection point is outside of the detector %d in layer %d\n", det, pln); } + if (verbose) { + System.out.format(" Intersection point is outside of the detector %d in layer %d\n", det, pln); + } continue; } - Tk = new RKhelix(rscat, pInt, Q, fMg, rnd); + Tk = new RKhelix(rscat, pInt, Q, fMg, rnd, kPar.eLoss); if (thisSi.Layer == startLayer || rnd.nextDouble() < hitEfficiency) { // Apply some hit inefficiency double[] gran = new double[2]; if (perfect) { @@ -509,7 +562,9 @@ class HelixTest3 { // Program for testing the Kalman fitting code double smear = resolution * gran[0]; double m1 = rDet.v[1] + smear; hRes.entry(smear); - if (verbose) { System.out.format(" Measurement 1= %10.7f, Truth=%10.7f\n", m1, rDet.v[1]); } + if (verbose) { + System.out.format(" Measurement 1= %10.7f, Truth=%10.7f\n", m1, rDet.v[1]); + } Measurement thisM1 = new Measurement(m1, 0., resolution, 0., 10., rscat, rDet.v[1]); thisSi.addMeasurement(thisM1); } @@ -554,19 +609,23 @@ class HelixTest3 { // Program for testing the Kalman fitting code } } else { TkEnd = Tk; - if (verbose) { TkEnd.print("TkEnd"); } + if (verbose) { + TkEnd.print("TkEnd"); + } } } - + // Extrapolate further to the ECAL location. The helix parameters are in a coordinate system with origin // on the helix at the current location x and aligned with the local B field. Since we provide the pivot // point to be x (in global coordinates), the pivotECAL in the field system is (0,0,0). - Plane pEcal = new Plane(new Vec(0.,eCalLoc,0.), new Vec(0.,1.,0.)); + Plane pEcal = new Plane(new Vec(0., eCalLoc, 0.), new Vec(0., 1., 0.)); RKhelix TkEcal = Tk.propagateRK(pEcal); int nHits = 0; for (SiModule siM : SiModules) { - if (siM.hits.size() > 0) nHits++; + if (siM.hits.size() > 0) { + nHits++; + } } hnHit.entry(nHits); @@ -578,8 +637,12 @@ class HelixTest3 { // Program for testing the Kalman fitting code //for (int i=0; i= 0; i--) { SiModule si = SiModules.get(i); - if (si.Layer > startLayer) continue; - if (si.hits.isEmpty()) continue; + if (si.Layer > startLayer) { + continue; + } + if (si.hits.isEmpty()) { + continue; + } if (nA < nAxial) { if (!si.isStereo) { int[] ht = new int[2]; @@ -590,7 +653,9 @@ class HelixTest3 { // Program for testing the Kalman fitting code frstLyr = si.Layer; } } else { - if (nS >= nStereo) break; + if (nS >= nStereo) { + break; + } } if (nS < nStereo) { if (si.isStereo) { @@ -602,7 +667,9 @@ class HelixTest3 { // Program for testing the Kalman fitting code frstLyr = si.Layer; } } else { - if (nA >= nStereo) break; + if (nA >= nStereo) { + break; + } } } if (nS < nStereo || nA < nAxial) { @@ -610,7 +677,7 @@ class HelixTest3 { // Program for testing the Kalman fitting code continue; } - SeedTrack seed = new SeedTrack(SiModules, location[frstLyr], hitList, verbose); + SeedTrack seed = new SeedTrack(SiModules, location[frstLyr], hitList, verbose, kPar); if (!seed.success) { System.out.format("Failed to make a seed track\n"); continue; @@ -629,10 +696,9 @@ class HelixTest3 { // Program for testing the Kalman fitting code // For comparison, get the true helix in the B field frame at the first layer of the linear fit // Then transform it to the same pivot used by the linear fit. Here we have to ignore details about // coordinate system rotation, because the linear fit is only done in the global frame assuming constant field - helixMCtrue = helixSaved[frstLyr]; Vec pivotOnAxis = new Vec(0., location[frstLyr], 0.); - Vec Bpiv = new Vec(3,fMg.getField(pivotOnAxis)); + Vec Bpiv = new Vec(3, fMg.getField(pivotOnAxis)); double alpha = 1.0e12 / (2.99793e8 * Bpiv.mag()); Vec helixTrueTrans = HelixState.pivotTransform(pivotOnAxis, helixMCtrue, pivotSaved[frstLyr], alpha, 0.); Vec gErrVec = initialHelixGuess.dif(helixTrueTrans); @@ -640,7 +706,9 @@ class HelixTest3 { // Program for testing the Kalman fitting code // initialHelixGuess.v[2] - K, // initialHelixGuess.v[3] - dz, initialHelixGuess.v[4] - tanl); double[] gErr = new double[5]; - for (int i = 0; i < 5; i++) { gErr[i] = gErrVec.v[i] / GuessErrors.v[i]; } + for (int i = 0; i < 5; i++) { + gErr[i] = gErrVec.v[i] / GuessErrors.v[i]; + } if (verbose) { // helixMCtrue.print("MC true helix at this layer"); // pivotSaved[frstLyr].print("old pivot"); @@ -673,9 +741,7 @@ class HelixTest3 { // Program for testing the Kalman fitting code // double Bstart = seed.B(); // Vec tBstart = new Vec(0., 0., 1.); - // Cheating initial "guess" for the helix - double[] rn = new double[2]; if (perfect) { rn[0] = 0.; @@ -708,7 +774,7 @@ class HelixTest3 { // Program for testing the Kalman fitting code initialCovariance.unsafe_set(3, 3, dzSigma * dzSigma); initialCovariance.unsafe_set(4, 4, tanlSigma * tanlSigma); - Vec Bf0 = new Vec(3,fMg.getField(helixOrigin)); + Vec Bf0 = new Vec(3, fMg.getField(helixOrigin)); if (verbose) { initialHelixGuess.print("initial helix guess"); System.out.format("True helix: %10.6f %10.6f %10.6f %10.6f %10.6f\n", drho, phi0, K, dz, tanl); @@ -728,41 +794,57 @@ class HelixTest3 { // Program for testing the Kalman fitting code KalmanTrackFit2 kF = new KalmanTrackFit2(iTrial, SiModules, null, startLayer, nIteration, new Vec(0., location[frstLyr], 0.), initialHelixGuess, initialCovariance, kPar, fM); long endTimeF = System.nanoTime(); - double runTime = (double)(endTimeF - startTimeF)/1000000.; + double runTime = (double) (endTimeF - startTimeF) / 1000000.; executionTime += runTime; - if (!kF.success) continue; + if (!kF.success) { + continue; + } KalTrack KalmanTrack = kF.tkr; - if (KalmanTrack == null) continue; + if (KalmanTrack == null) { + continue; + } KalmanTrack.originHelix(); - if (verbose) KalmanTrack.print("KalmanTrack"); - + if (verbose) { + KalmanTrack.print("KalmanTrack"); + } + + // Apply ECAL energy constraint + if (kPar.eLoss) { + Etrue = TkEnd.p.mag(); + } + double sigmaE = 0.03 * FastMath.sqrt(Etrue); + double E = Etrue + rnd.nextGaussian() * sigmaE; + KalmanTrack.smoothIt(false, E, sigmaE); + // Check on the covariance matrix Matrix C = new Matrix(KalmanTrack.originCovariance()); - EigenvalueDecomposition eED= new EigenvalueDecomposition(C); - double [] ev = eED.getRealEigenvalues(); + EigenvalueDecomposition eED = new EigenvalueDecomposition(C); + double[] ev = eED.getRealEigenvalues(); boolean badCov = false; - for (int i=0; i<5; ++i) { + for (int i = 0; i < 5; ++i) { if (ev[i] < 0.) { System.out.format("Event %d, eigenvalue %d of covariance is negative!", iTrial, i); badCov = true; } } - if (badCov) nBadCov++; + if (badCov) { + nBadCov++; + } if (iTrial < 10) { - Vec evV = new Vec(5,ev); + Vec evV = new Vec(5, ev); evV.print("Eigenvalues of covariance"); } - + // Test the helix propagation code - List states = new ArrayList(); for (MeasurementSite site : kF.sites) { int loc = TrackState.AtOther; - if (kF.sites.indexOf(site) == 0) loc = TrackState.AtFirstHit; - else if (kF.sites.indexOf(site) == kF.sites.size()-1) { + if (kF.sites.indexOf(site) == 0) { + loc = TrackState.AtFirstHit; + } else if (kF.sites.indexOf(site) == kF.sites.size() - 1) { loc = TrackState.AtLastHit; } - TrackState ts = KI.createTrackState(site, loc, true); + TrackState ts = KI.createTrackState(site, loc, true); states.add(ts); } TrackState lastState = null; @@ -773,20 +855,22 @@ else if (kF.sites.indexOf(site) == kF.sites.size()-1) { } } if (lastState != null) { - // test propagation of a track state from one end of the track to the ECAL region - final boolean debug = false; + // test propagation of a track state from one end of the track to the ECAL region + final boolean debug = false; if (KalmanTrack.nHits >= 12) { - if (KalmanTrack.chi2/KalmanTrack.nHits < 2.) { - MeasurementSite lastSite = KalmanTrack.SiteList.get(KalmanTrack.SiteList.size()-1); + if (KalmanTrack.chi2 / KalmanTrack.nHits < 2.) { + MeasurementSite lastSite = KalmanTrack.SiteList.get(KalmanTrack.SiteList.size() - 1); Vec eCalPos = TkEcal.x; - Plane plnAtEcal = new Plane(eCalPos, new Vec(0.,1.,0.)); + Plane plnAtEcal = new Plane(eCalPos, new Vec(0., 1., 0.)); if (debug) { eCalPos.print("ECAL cluster position"); lastSite.aS.helix.print("helix at last layer"); } - double [] arcLength = new double[1]; + double[] arcLength = new double[1]; HelixState helixAtEcal = lastSite.aS.helix.propagateRungeKutta(plnAtEcal, yScat, XLscat, fM, arcLength); - if (MatrixFeatures_DDRM.hasNaN(helixAtEcal.C)) continue; + if (MatrixFeatures_DDRM.hasNaN(helixAtEcal.C)) { + continue; + } Vec intPnt = helixAtEcal.getRKintersection(); if (debug) { helixAtEcal.print("helix at ECAL cluster"); @@ -795,21 +879,21 @@ else if (kF.sites.indexOf(site) == kF.sites.size()-1) { hPropxHS.entry(intPnt.v[0] - eCalPos.v[0]); hPropzHS.entry(intPnt.v[2] - eCalPos.v[2]); DMatrixRMaj covAtEcal = helixAtEcal.C; - double [][] dadx = KalTrack.DxTOa(helixAtEcal.a); - double [][] Cx = new double[3][3]; + double[][] dadx = KalTrack.DxTOa(helixAtEcal.a); + double[][] Cx = new double[3][3]; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { Cx[i][j] = 0.; for (int k = 0; k < 5; k++) { for (int l = 0; l < 5; l++) { - Cx[i][j] += dadx[i][k] * covAtEcal.unsafe_get(k,l) * dadx[j][l]; + Cx[i][j] += dadx[i][k] * covAtEcal.unsafe_get(k, l) * dadx[j][l]; } } } } - hPropxsHS.entry((intPnt.v[0] - eCalPos.v[0])/Math.sqrt(Cx[0][0])); - hPropzsHS.entry((intPnt.v[2] - eCalPos.v[2])/Math.sqrt(Cx[2][2])); - + hPropxsHS.entry((intPnt.v[0] - eCalPos.v[0]) / Math.sqrt(Cx[0][0])); + hPropzsHS.entry((intPnt.v[2] - eCalPos.v[2]) / Math.sqrt(Cx[2][2])); + // Try in a single step---this worked perfect in uniform field, as long as last scatter is included double phiIntEcal = lastSite.aS.helix.planeIntersect(plnAtEcal); if (!Double.isNaN(phiIntEcal)) { @@ -820,22 +904,28 @@ else if (kF.sites.indexOf(site) == kF.sites.size()-1) { } Vec intcpt = lastSite.aS.helix.atPhi(phiIntEcal); Vec helixAtIntcpt = lastSite.aS.helix.pivotTransform(intcpt); - if (debug) intcpt.print("intercept local"); + if (debug) { + intcpt.print("intercept local"); + } intcpt = lastSite.aS.helix.toGlobal(intcpt); - if (debug) intcpt.print("intercept global"); - DMatrixRMaj F = new DMatrixRMaj(5,5); - lastSite.aS.helix.makeF(helixAtIntcpt, F); - if (debug) F.print("tranform matrix F"); - Vec pMom = HelixState.getMom(0.,helixAtIntcpt); + if (debug) { + intcpt.print("intercept global"); + } + DMatrixRMaj F = new DMatrixRMaj(5, 5); + lastSite.aS.helix.makeF(helixAtIntcpt, F, 1.0); + if (debug) { + F.print("tranform matrix F"); + } + Vec pMom = HelixState.getMom(0., helixAtIntcpt); double pMag = pMom.mag(); - double ct = pMom.v[1]/pMag; - double sigmaMS = HelixState.projMSangle(pMag, XLscat.get(XLscat.size()-1)/ct); - DMatrixRMaj Qmcs = new DMatrixRMaj(5,5); + double ct = pMom.v[1] / pMag; + double sigmaMS = HelixState.projMSangle(pMag, XLscat.get(XLscat.size() - 1) / ct); + DMatrixRMaj Qmcs = new DMatrixRMaj(5, 5); lastSite.aS.helix.getQ(sigmaMS, Qmcs); CommonOps_DDRM.add(lastSite.aS.helix.C, Qmcs, tempM1); CommonOps_DDRM.multTransB(tempM1, F, tempM2); - DMatrixRMaj covAtIntcpt = new DMatrixRMaj(5,5); - CommonOps_DDRM.mult(F, tempM2, covAtIntcpt); + DMatrixRMaj covAtIntcpt = new DMatrixRMaj(5, 5); + CommonOps_DDRM.mult(F, tempM2, covAtIntcpt); dadx = KalTrack.DxTOa(helixAtIntcpt); Cx = new double[3][3]; for (int i = 0; i < 3; i++) { @@ -843,21 +933,21 @@ else if (kF.sites.indexOf(site) == kF.sites.size()-1) { Cx[i][j] = 0.; for (int k = 0; k < 5; k++) { for (int l = 0; l < 5; l++) { - Cx[i][j] += dadx[i][k] * covAtIntcpt.unsafe_get(k,l) * dadx[j][l]; + Cx[i][j] += dadx[i][k] * covAtIntcpt.unsafe_get(k, l) * dadx[j][l]; } } } } if (debug) { covAtIntcpt.print("covAtIntcpt"); - new SquareMatrix(3,Cx).print("Cx"); + new SquareMatrix(3, Cx).print("Cx"); } hPropx1.entry(intcpt.v[0] - eCalPos.v[0]); - hPropz1.entry(intcpt.v[2] - eCalPos.v[2]); - hPropx1s.entry((intcpt.v[0] - eCalPos.v[0])/Math.sqrt(Cx[0][0])); - hPropz1s.entry((intcpt.v[2] - eCalPos.v[2])/Math.sqrt(Cx[2][2])); + hPropz1.entry(intcpt.v[2] - eCalPos.v[2]); + hPropx1s.entry((intcpt.v[0] - eCalPos.v[0]) / Math.sqrt(Cx[0][0])); + hPropz1s.entry((intcpt.v[2] - eCalPos.v[2]) / Math.sqrt(Cx[2][2])); } - } + } } /* for (TrackState tkState : states) { @@ -886,19 +976,19 @@ else if (kF.sites.indexOf(site) == kF.sites.size()-1) { break; } } - */ + */ } // end of test of helix propagation - + // Test the vertex constraint Vec vtx = helixOrigin.copy(); - for (int i=0; i<3; ++i) { + for (int i = 0; i < 3; ++i) { vtx.v[i] += rnd.nextGaussian() * vtxRes[i]; } SquareMatrix vtxCov = new SquareMatrix(3); - vtxCov.M[0][0] = vtxRes[0]*vtxRes[0]; - vtxCov.M[1][1] = vtxRes[1]*vtxRes[1]; - vtxCov.M[2][2] = vtxRes[2]*vtxRes[2]; + vtxCov.M[0][0] = vtxRes[0] * vtxRes[0]; + vtxCov.M[1][1] = vtxRes[1] * vtxRes[1]; + vtxCov.M[2][2] = vtxRes[2] * vtxRes[2]; HelixState constrainedHelix = KalmanTrack.originConstraint(vtx.v, vtxCov.M); hchi2inc.entry(KalmanTrack.chi2incOrigin()); hchi2c.entry(KalmanTrack.chi2incOrigin() + KalmanTrack.chi2); @@ -911,32 +1001,40 @@ else if (kF.sites.indexOf(site) == kF.sites.size()-1) { SiModule siM = site.m; if (site.m.Layer >= 0) { if (site.filtered) { - if (siM.isStereo) hResid0.entry(site.aF.r / Math.sqrt(site.aF.R)); - else hResid1.entry(site.aF.r / Math.sqrt(site.aF.R)); + if (siM.isStereo) { + hResid0.entry(site.aF.r / Math.sqrt(site.aF.R)); + } else { + hResid1.entry(site.aF.r / Math.sqrt(site.aF.R)); + } } if (site.smoothed) { - if (site.m.Layer == 4) hReducedErr.entry(Math.sqrt(site.aS.R)); - chi2s += Math.pow(site.aS.mPred - site.m.hits.get(site.hitID).vTrue, 2) / site.aS.R; - hResidS0[siM.Layer].entry(site.aS.r / Math.sqrt(site.aS.R)); - hResidS2[siM.Layer].entry(site.aS.r); - if (site.hitID >= 0) { hResidS4[siM.Layer].entry(site.m.hits.get(site.hitID).vTrue - site.aS.mPred); } + StateVector aA = site.aS; + if (site.m.Layer == 4) hReducedErr.entry(Math.sqrt(aA.R)); + chi2s += Math.pow(aA.mPred - site.m.hits.get(site.hitID).vTrue, 2) / aA.R; + hResidS0[siM.Layer].entry(aA.r / Math.sqrt(aA.R)); + hResidS2[siM.Layer].entry(aA.r); + if (site.hitID >= 0) { + hResidS4[siM.Layer].entry(site.m.hits.get(site.hitID).vTrue - aA.mPred); + } } } } } - for (int layer=0; layer < nLayers; ++layer) { + for (int layer = 0; layer < nLayers; ++layer) { Pair resid = KalmanTrack.unbiasedResidual(layer); if (resid.getSecondElement() > -999.) { double variance = resid.getSecondElement(); double sigma = Math.sqrt(variance); double unbResid = resid.getFirstElement(); hUnbias[layer].entry(unbResid); - hUnbiasSig[layer].entry(unbResid/sigma); - if (variance < resolution*resolution) { - System.out.format("Event %d layer %d, unbiased residual variance too small: %10.5f, chi2=%9.2f, hits=%d, resid=%9.6f, lyrs:", - iTrial, layer, variance, KalmanTrack.chi2, KalmanTrack.nHits, unbResid); + hUnbiasSig[layer].entry(unbResid / sigma); + if (variance < resolution * resolution) { + System.out.format("Event %d layer %d, unbiased residual variance too small: %10.5f, chi2=%9.2f, hits=%d, resid=%9.6f, lyrs:", + iTrial, layer, variance, KalmanTrack.chi2, KalmanTrack.nHits, unbResid); for (MeasurementSite site : KalmanTrack.SiteList) { - if (site.hitID < 0) continue; + if (site.hitID < 0) { + continue; + } System.out.format(" %d ", site.m.Layer); } System.out.format("\n"); @@ -946,8 +1044,12 @@ else if (kF.sites.indexOf(site) == kF.sites.size()-1) { for (MeasurementSite site : KalmanTrack.interceptVects().keySet()) { Vec loc = KalmanTrack.interceptVects().get(site); SiModule siM = site.m; - if (siM.Layer < 0) continue; - if (site.hitID < 0) { System.out.format("Missing hit ID on site with layer=%d", siM.Layer); } + if (siM.Layer < 0) { + continue; + } + if (site.hitID < 0) { + System.out.format("Missing hit ID on site with layer=%d", siM.Layer); + } Vec locMC = site.m.hits.get(site.hitID).rGlobal; hResidX[siM.Layer].entry(loc.v[0] - locMC.v[0]); hResidZ[siM.Layer].entry(loc.v[2] - locMC.v[2]); @@ -978,21 +1080,27 @@ else if (kF.sites.indexOf(site) == kF.sites.size()-1) { } Vec newPivot = kF.fittedStateBegin().helix.toLocal(helixBegin.origin.sum(helixBegin.X0)); Vec aF = kF.fittedStateBegin().helix.pivotTransform(newPivot); - // now rotate to the original field frame - - RotMatrix Rcombo = helixBegin.R.multiply(kF.fittedStateBegin().helix.Rot.invert()); - aF = HelixState.rotateHelix(aF, Rcombo, fRot); - if (verbose) { - Rcombo.print("Rcombo, into the frame of the true helix"); - aF.print("final smoothed helix parameters at the track beginning"); - newPivot.print("final smoothed helix pivot in local coordinates"); + + // now rotate to the original field frame + if (!kPar.uniformB) { + RotMatrix Rcombo = helixBegin.R.multiply(kF.fittedStateBegin().helix.Rot.invert()); + aF = HelixState.rotateHelix(aF, Rcombo, fRot); + if (verbose) { + Rcombo.print("Rcombo, into the frame of the true helix"); + aF.print("final smoothed helix parameters at the track beginning"); + newPivot.print("final smoothed helix pivot in local coordinates"); + } } Vec aFe = new Vec(5); - DMatrixRMaj aFC = kF.fittedStateBegin().covariancePivotTransform(aF); + DMatrixRMaj aFC = kF.fittedStateBegin().covariancePivotTransform(aF, 1.0); CommonOps_DDRM.multTransB(aFC, fRot, tempM1); CommonOps_DDRM.mult(fRot, tempM1, aFC); - for (int i = 0; i < 5; i++) aFe.v[i] = Math.sqrt(Math.max(0., aFC.unsafe_get(i,i))); - if (verbose) { aFe.print("error estimates on the smoothed helix parameters"); } + for (int i = 0; i < 5; i++) { + aFe.v[i] = Math.sqrt(Math.max(0., aFC.unsafe_get(i, i))); + } + if (verbose) { + aFe.print("error estimates on the smoothed helix parameters"); + } Vec trueErr = aF.dif(helixBegin.p); if (verbose) { for (int i = 0; i < 5; i++) { @@ -1007,9 +1115,12 @@ else if (kF.sites.indexOf(site) == kF.sites.size()-1) { hEtanlS.entry(trueErr.v[4] / aFe.v[4]); double helixChi2 = trueErr.dot(trueErr.leftMultiply(KalTrack.mToS(aFC).invert())); hChi2HelixS.entry(helixChi2); + Vec pivotEnd = TkEnd.x; + helixEnd = new Helix(Q, pivotEnd, TkEnd.p, pivotEnd, fMg, rnd, kPar.eLoss); if (verbose) { System.out.format("Full chi^2 of the smoothed helix parameters = %12.4e\n", helixChi2); TkEnd.print("MC true helix at the last detector plane"); + helixEnd.print("MC true helix params at the last detector plane"); kF.fittedStateEnd().print("fitted state at the last detector plane"); } newPivot = kF.fittedStateEnd().helix.toLocal(TkEnd.R(kF.fittedStateEnd().helix.origin) @@ -1019,9 +1130,11 @@ else if (kF.sites.indexOf(site) == kF.sites.size()-1) { eF.print("final smoothed helix parameters at the track end"); newPivot.print("new pivot at the track end"); } - DMatrixRMaj eFc = kF.fittedStateEnd().covariancePivotTransform(eF); + DMatrixRMaj eFc = kF.fittedStateEnd().covariancePivotTransform(eF, 1.0); Vec eFe = new Vec(5); - for (int i = 0; i < 5; i++) eFe.v[i] = Math.sqrt(Math.max(0., eFc.unsafe_get(i,i))); + for (int i = 0; i < 5; i++) { + eFe.v[i] = Math.sqrt(Math.max(0., eFc.unsafe_get(i, i))); + } Vec pivotF = new Vec(3); Vec fH = TkEnd.helixParameters(TkEnd.x, pivotF); trueErr = eF.dif(fH); @@ -1040,27 +1153,33 @@ else if (kF.sites.indexOf(site) == kF.sites.size()-1) { hEtanl.entry(trueErr.v[4] / eFe.v[4]); trueErr = eF.dif(fH); helixChi2 = trueErr.dot(trueErr.leftMultiply(KalTrack.mToS(eFc).invert())); - if (verbose) { System.out.format("Full chi^2 of the filtered helix parameters = %12.4e\n", helixChi2); } + if (verbose) { + System.out.format("Full chi^2 of the filtered helix parameters = %12.4e\n", helixChi2); + } hChi2Helix.entry(helixChi2); // Study the fitted helix extrapolated back to the origin double[] hP = KalmanTrack.originHelixParms(); - testHelix = new Vec(5,hP); + testHelix = new Vec(5, hP); testCov = new DMatrixRMaj(KalmanTrack.originCovariance()); double[] hErr = new double[5]; - for (int i = 0; i < 5; ++i) { hErr[i] = (hP[i] - TkInitial.p.v[i]); } + for (int i = 0; i < 5; ++i) { + hErr[i] = (hP[i] - TkInitial.p.v[i]); + } double[] hErrC = new double[5]; - for (int i = 0; i < 5; ++i) { hErrC[i] = (constrainedHelix.a.v[i] - TkInitial.p.v[i]); } + for (int i = 0; i < 5; ++i) { + hErrC[i] = (constrainedHelix.a.v[i] - TkInitial.p.v[i]); + } hEdrhoO.entry(hErr[0] / KalmanTrack.helixErr(0)); hEphi0O.entry(hErr[1] / KalmanTrack.helixErr(1)); hEkO.entry(hErr[2] / KalmanTrack.helixErr(2)); hEdzO.entry(hErr[3] / KalmanTrack.helixErr(3)); hEtanlO.entry(hErr[4] / KalmanTrack.helixErr(4)); - hEdrhoSigO.entry(hErrC[0] / Math.sqrt(constrainedHelix.C.unsafe_get(0,0))); - hEphi0SigO.entry(hErrC[1] / Math.sqrt(constrainedHelix.C.unsafe_get(1,1))); - hEkSigO.entry(hErrC[2] / Math.sqrt(constrainedHelix.C.unsafe_get(2,2))); - hEdzSigO.entry(hErrC[3] / Math.sqrt(constrainedHelix.C.unsafe_get(3,3))); - hEtanlSigO.entry(hErrC[4] / Math.sqrt(constrainedHelix.C.unsafe_get(4,4))); + hEdrhoSigO.entry(hErrC[0] / Math.sqrt(constrainedHelix.C.unsafe_get(0, 0))); + hEphi0SigO.entry(hErrC[1] / Math.sqrt(constrainedHelix.C.unsafe_get(1, 1))); + hEkSigO.entry(hErrC[2] / Math.sqrt(constrainedHelix.C.unsafe_get(2, 2))); + hEdzSigO.entry(hErrC[3] / Math.sqrt(constrainedHelix.C.unsafe_get(3, 3))); + hEtanlSigO.entry(hErrC[4] / Math.sqrt(constrainedHelix.C.unsafe_get(4, 4))); hEadrhoO.entry(hErr[0]); hEaphi0O.entry(hErr[1]); hEakO.entry(hErr[2]); @@ -1106,6 +1225,33 @@ else if (kF.sites.indexOf(site) == kF.sites.size()-1) { KalmanTrack.helixErr(i), hErr[i], e); } } + // Study the effect of an energy constraint + MeasurementSite lastSite = KalmanTrack.SiteList.get(KalmanTrack.SiteList.size() - 1); + if (verbose) { + lastSite.print("last site on track"); + helixEnd.print("true helix at last site on track"); + System.out.format("True energy = %10.4f, ECAL energy = %10.4f, sigma(E)=%8.3f\n", Etrue, E, sigmaE); + KalmanTrack.helixAtOrigin.a.print("helix at origin"); + TkInitial.p.print("true helix"); + KalmanTrack.printLong("after adding energy constraint"); + } + hChi2E.entry(KalmanTrack.chi2_Econstraint); + for (int i = 0; i < 5; ++i) { + hErr[i] = (KalmanTrack.helixAtOrigin.a.v[i] - TkInitial.p.v[i]); + } + hEatanlcon.entry(hErr[4]); + hEakcon.entry(hErr[2]); + hERhocon.entry(hErr[0]); + hEPhi0con.entry(hErr[1]); + hEZ0con.entry(hErr[3]); + hEdrhoCon.entry(hErr[0] / Math.sqrt(KalmanTrack.helixAtOrigin.C.unsafe_get(0,0))); + hEphi0Con.entry(hErr[1] / Math.sqrt(KalmanTrack.helixAtOrigin.C.unsafe_get(1,1))); + hEkCon.entry(hErr[2] / Math.sqrt(KalmanTrack.helixAtOrigin.C.unsafe_get(2,2))); + hEdzCon.entry(hErr[3] / Math.sqrt(KalmanTrack.helixAtOrigin.C.unsafe_get(3,3))); + hEtanlCon.entry(hErr[4] / Math.sqrt(KalmanTrack.helixAtOrigin.C.unsafe_get(4,4))); + trueErr = new Vec(5,hErr); + helixChi2 = trueErr.dot(trueErr.leftMultiply(KalTrack.mToS(KalmanTrack.helixAtOrigin.C).invert())); + hChi2HelixE.entry(helixChi2); } } } @@ -1118,7 +1264,7 @@ else if (kF.sites.indexOf(site) == kF.sites.size()-1) { ldt = LocalDateTime.ofInstant(timestamp, ZoneId.systemDefault()); System.out.format("%s %d %d at %d:%d %d.%d seconds\n", ldt.getMonth(), ldt.getDayOfMonth(), ldt.getYear(), ldt.getHour(), ldt.getMinute(), ldt.getSecond(), ldt.getNano()); - double endTime = (double)(ldt.getMinute())*60. + (double)(ldt.getSecond()) + (double)(ldt.getNano())/1e9; + double endTime = (double) (ldt.getMinute()) * 60. + (double) (ldt.getSecond()) + (double) (ldt.getNano()) / 1e9; double elapsedTime = endTime - startTime; System.out.format("Total elapsed time = %10.5f\n", elapsedTime); System.out.format("Elapsed time for Kalman Filter = %10.4f ms\n", executionTime); @@ -1139,7 +1285,9 @@ else if (kF.sites.indexOf(site) == kF.sites.size()-1) { hChi2Alt.plot(path + "chi2sAlt.gp", true, " ", " "); hChi2p.plot(path + "chi2prm.gp", true, " ", " "); hChi2f.plot(path + "chi2f.gp", true, " ", " "); + hChi2E.plot(path + "chi2E.gp", true, " ", " "); hChi2HelixS.plot(path + "chi2helixS.gp", true, " ", " "); + hChi2HelixE.plot(path + "chi2helixE.gp", true, " ", " "); hChi2Origin.plot(path + "chi2helixO.gp", true, " ", " "); hChi2OriginC.plot(path + "chi2helixOc.gp", true, " ", " "); hChi2Helix.plot(path + "chi2helixF.gp", true, " ", " "); @@ -1157,9 +1305,17 @@ else if (kF.sites.indexOf(site) == kF.sites.size()-1) { hEtanlS.plot(path + "tanlErrorS.gp", true, "gaus", " "); hEdrhoO.plot(path + "drhoErrorO.gp", true, "gaus", " "); hEphi0O.plot(path + "phi0ErrorO.gp", true, "gaus", " "); + hERhocon.plot(path + "drhoErrorE.gp", true, "gaus", " "); + hEPhi0con.plot(path + "phi0ErrorE.gp", true, "gaus", " "); + hEZ0con.plot(path + "Z0ErrorE.gp", true, "gaus", " "); hEkO.plot(path + "kErrorO.gp", true, "gaus", " "); hEdzO.plot(path + "dzErrorO.gp", true, "gaus", " "); hEtanlO.plot(path + "tanlErrorO.gp", true, "gaus", " "); + hEdrhoCon.plot(path + "drhoErrorConSig.gp", true, "gaus", " "); + hEphi0Con.plot(path + "phi0ErrorConSig.gp", true, "gaus", " "); + hEkCon.plot(path + "kErrorConSig.gp", true, "gaus", " "); + hEdzCon.plot(path + "dzErrorConSig.gp", true, "gaus", " "); + hEtanlCon.plot(path + "tanlErrorConSig.gp", true, "gaus", " "); hEdrhoSigO.plot(path + "drhoErrorSigO.gp", true, "gaus", " "); hEphi0SigO.plot(path + "phi0ErrorSigO.gp", true, "gaus", " "); hEkSigO.plot(path + "kErrorSigO.gp", true, "gaus", " "); @@ -1168,8 +1324,10 @@ else if (kF.sites.indexOf(site) == kF.sites.size()-1) { hEadrhoO.plot(path + "drhoErrorOa.gp", true, "gaus", " "); hEaphi0O.plot(path + "phi0ErrorOa.gp", true, "gaus", " "); hEakO.plot(path + "kErrorOa.gp", true, "gaus", " "); + hEakcon.plot(path + "kErrorCon.gp", true, "gaus", " "); hEadzO.plot(path + "dzErrorOa.gp", true, "gaus", " "); - hEatanlO.plot(path + "tanlErrorOa.gp", true, "gaus", " "); + hEatanlO.plot(path + "tanlErrorOa.gp", true, " ", " "); + hEatanlcon.plot(path + "tanlErrorCon.gp", true, " ", " "); hEcdrhoO.plot(path + "drhoErrorOc.gp", true, "gaus", " "); hEcphi0O.plot(path + "phi0ErrorOc.gp", true, "gaus", " "); hEckO.plot(path + "kErrorOc.gp", true, "gaus", " "); @@ -1212,8 +1370,8 @@ else if (kF.sites.indexOf(site) == kF.sites.size()-1) { hPropz1.plot(path + "propz1.gp", true, "gaus", " "); hPropx1s.plot(path + "propx1s.gp", true, "gaus", " "); hPropz1s.plot(path + "propz1s.gp", true, "gaus", " "); - - /* + + /* // Test of helix covariance extrapolation if (testCov != null && testHelix != null) { Vec X0initial = new Vec(0.,0.,0.); @@ -1415,9 +1573,9 @@ else if (kF.sites.indexOf(site) == kF.sites.size()-1) { hTanLpF.plot(path + "TestTanlpF.gp", true, "gaus", " "); System.out.println("All Done!"); } - */ + */ } - + /* double[] gausRan() { // Return two gaussian random numbers @@ -1436,5 +1594,5 @@ else if (kF.sites.indexOf(site) == kF.sites.size()-1) { return gran; } - */ + */ } diff --git a/tracking/src/main/java/org/hps/recon/tracking/kalman/KalHit.java b/tracking/src/main/java/org/hps/recon/tracking/kalman/KalHit.java index fdec0a48b5..46166ad28a 100644 --- a/tracking/src/main/java/org/hps/recon/tracking/kalman/KalHit.java +++ b/tracking/src/main/java/org/hps/recon/tracking/kalman/KalHit.java @@ -8,23 +8,42 @@ * Relationships between hits, silicon modules, and track candidates */ class KalHit { - SiModule module; - Measurement hit; - Set tkrCandidates; + SiModule module; // The silicon module in which the hit is found + Measurement hit; // The measurement object corresponding to the hit + Set tkrCandidates; // List of candidate tracks that incorporate this hit + /** + * Kalman hit constructor + * @param module silicon module object + * @param hit measurement hit object + */ KalHit(SiModule module, Measurement hit) { this.module = module; this.hit = hit; tkrCandidates = new HashSet(); } + /** + * Is the hit on a stereo layer? + * @return true or false + */ boolean isStereo() { return module.isStereo; } + + /** + * Debug printout of a given Kalman hit + * @param s Arbitrary string to identify the printout + */ void print(String s) { System.out.format("%s", this.toString(s)); } + /** + * Debug printout to a string for a given Kalman hit + * @param s Arbitrary string, to identify the printout + * @return The full string, in printable format. + */ String toString(String s) { int ntks = hit.tracks.size(); String str; @@ -45,7 +64,9 @@ String toString(String s) { return str; } - // Comparator function for sorting hits on a track candidate + /** + * Comparator function for sorting hits on a track candidate + */ static Comparator HitComparator = new Comparator() { public int compare(KalHit h1, KalHit h2) { if (h1 == h2) return 0; diff --git a/tracking/src/main/java/org/hps/recon/tracking/kalman/KalTrack.java b/tracking/src/main/java/org/hps/recon/tracking/kalman/KalTrack.java index a7c671b06f..afb4339623 100644 --- a/tracking/src/main/java/org/hps/recon/tracking/kalman/KalTrack.java +++ b/tracking/src/main/java/org/hps/recon/tracking/kalman/KalTrack.java @@ -7,7 +7,9 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +//import java.util.List; import java.util.Map; +//import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; @@ -18,19 +20,22 @@ import org.ejml.dense.row.factory.LinearSolverFactory_DDRM; import org.ejml.interfaces.linsol.LinearSolverDense; import org.hps.util.Pair; +import org.lcsim.event.MCParticle; /** * Track followed and fitted by the Kalman filter */ public class KalTrack { + public int ID; public int nHits; public double chi2; + public double chi2_Econstraint; private double reducedChi2; ArrayList SiteList; // call the corresponding functions to create and access the following two maps - private Map interceptVects; + private Map interceptVects; private Map interceptMomVects; Map millipedeMap; Map lyrMap; @@ -38,7 +43,8 @@ public class KalTrack { public boolean bad; HelixState helixAtOrigin; private boolean propagated; - private RotMatrix Rot; + boolean energyConstrained; + private RotMatrix Rot; // Rotation matrix between global and field coordinates at the beam spot private Vec originPoint; private Vec originMomentum; private ArrayList yScat; @@ -51,67 +57,102 @@ public class KalTrack { private double time; double tMin; double tMax; - static final boolean debug = false; + private static final boolean debug = false; private KalmanParams kPar; private double chi2incVtx; private static DMatrixRMaj tempV; private static DMatrixRMaj Cinv; private static Logger logger; private static boolean initialized; - private double [] arcLength; + private double[] arcLength, arcLengthE; private static LinearSolverDense solver; - static int [] nBadCov = {0, 0}; + private static final boolean uniformBatOrigin = false; + static int[] nBadCov = {0, 0}; + /** + * Track constructor + * + * @param evtNumb event number + * @param tkID integer ID for the track + * @param SiteList list of measurement sites + * @param yScat array of scattering planes to propagate through + * @param XLscat scattering radiation lengths at each plane + * @param kPar KalmanParams instance + */ KalTrack(int evtNumb, int tkID, ArrayList SiteList, ArrayList yScat, ArrayList XLscat, KalmanParams kPar) { // System.out.format("KalTrack constructor chi2=%10.6f\n", chi2); eventNumber = evtNumb; bad = false; this.yScat = yScat; - this.XLscat = XLscat; + this.XLscat = XLscat; this.kPar = kPar; ID = tkID; arcLength = null; //debug = (evtNumb == 217481); - + if (!initialized) { logger = Logger.getLogger(KalTrack.class.getName()); - tempV = new DMatrixRMaj(5,1); - Cinv = new DMatrixRMaj(5,5); + if (tempV == null) { + tempV = new DMatrixRMaj(5, 1); + } + Cinv = new DMatrixRMaj(5, 5); initialized = true; solver = LinearSolverFactory_DDRM.symmPosDef(5); } - + // Trim empty sites from the track ends Collections.sort(SiteList, MeasurementSite.SiteComparatorUp); int firstSite = -1; - for (int idx=0; idx= 0) break; + if (SiteList.get(idx).hitID >= 0) { + break; + } } int lastSite = 999; - for (int idx = SiteList.size()-1; idx >= 0; --idx) { + for (int idx = SiteList.size() - 1; idx >= 0; --idx) { lastSite = idx; - if (SiteList.get(idx).hitID >= 0) break; + if (SiteList.get(idx).hitID >= 0) { + break; + } } - + if (firstSite < 0 || lastSite == 999) { + firstSite = 0; + lastSite = SiteList.size() - 1; + logger.log(Level.WARNING, String.format("Event %d: no sites on track %d. Number of sites=%d", + eventNumber, ID, SiteList.size())); + } + // Make a new list of sites, without empty sites at beginning or end this.SiteList = new ArrayList(SiteList.size()); - for (int idx=firstSite; idx<=lastSite; ++idx) { + for (int idx = firstSite; idx <= lastSite; ++idx) { MeasurementSite site = SiteList.get(idx); - if (site.aS == null) { // This should never happen - logger.log(Level.SEVERE, String.format("Event %d: site of track %d is missing smoothed state vector for layer %d detector %d", + if (site.aS == null && site.aF == null) { // This should never happen + logger.log(Level.SEVERE, String.format("Event %d: site of track %d is missing a state vector for layer %d detector %d", eventNumber, ID, site.m.Layer, site.m.detector)); logger.log(Level.WARNING, site.toString("bad site")); bad = true; continue; } - this.SiteList.add(site); + this.SiteList.add(site); } - + if (debug) { + for (MeasurementSite site: SiteList) { + SiModule mod = site.m; + System.out.format("KalTrack: SiModule on layer %d wafer %d, %d hits\n", mod.Layer, mod.detector, mod.hits.size()); + } + } + helixAtOrigin = null; propagated = false; + energyConstrained = false; MeasurementSite site0 = this.SiteList.get(0); - Vec B = KalmanInterface.getField(new Vec(3,kPar.beamSpot), site0.m.Bfield); + Vec B = null; + if (kPar.uniformB) { + B = KalmanInterface.getField(new Vec(0., kPar.SVTcenter, 0.), site0.m.Bfield); + } else { + B = KalmanInterface.getField(new Vec(3, kPar.beamSpot), site0.m.Bfield); + } Bmag = B.mag(); tB = B.unitVec(Bmag); Vec yhat = new Vec(0., 1.0, 0.); @@ -123,31 +164,39 @@ public class KalTrack { double c = 2.99793e8; // Speed of light in m/s alpha = 1.0e12 / (c * Bmag); // Convert from pt in GeV to curvature in mm Cx = null; - Cp = null; + Cp = null; // Fill the maps time = 0.; tMin = 9.9e9; tMax = -9.9e9; this.chi2 = 0.; this.nHits = 0; - if (debug) System.out.format("KalTrack: event %d, creating track %d\n", evtNumb, ID); + if (debug) { + System.out.format("KalTrack: event %d, creating track %d\n", evtNumb, ID); + } for (MeasurementSite site : this.SiteList) { - if (site.hitID < 0) continue; + if (site.hitID < 0) { + continue; + } nHits++; time += site.m.hits.get(site.hitID).time; tMin = Math.min(tMin, site.m.hits.get(site.hitID).time); - tMax = Math.max(tMax, site.m.hits.get(site.hitID).time); + tMax = Math.max(tMax, site.m.hits.get(site.hitID).time); this.chi2 += site.chi2inc; - if (debug) System.out.format(" Layer %d, chi^2 increment=%10.5f, a=%s\n", site.m.Layer, site.chi2inc, site.aS.helix.a.toString()); + if (debug) { + StateVector aA = site.aS; + if (aA == null) aA = site.aF; + System.out.format(" Layer %d, chi^2 increment=%10.5f, a=%s\n", site.m.Layer, site.chi2inc, aA.helix.a.toString()); + } } - time = time/(double)nHits; - reducedChi2 = chi2/(double)nHits; + time = time / (double) nHits; + reducedChi2 = chi2 / (double) nHits; lyrMap = null; millipedeMap = null; interceptVects = null; interceptMomVects = null; if (nHits < 5) { // This should never happen - logger.log(Level.WARNING, "KalTrack error: not enough hits ("+nHits+") on the candidate track (ID::"+ID+") for event "+eventNumber); + logger.log(Level.WARNING, "KalTrack error: not enough hits (" + nHits + ") on the candidate track (ID::" + ID + ") for event " + eventNumber); bad = true; //for (MeasurementSite site : SiteList) logger.log(Level.FINE, site.toString("in KalTrack input list")); //logger.log(Level.FINE, String.format("KalTrack error in event %d: not enough hits on track %d: ",evtNumb,tkID)); @@ -160,61 +209,106 @@ public class KalTrack { } } + /** + * Get the time of the track + * + * @return time in ns + */ public double getTime() { return time; } - + + /** + * Make a map between measurement sites and 3-vector intercepts of the track + * at the SSD planes of those sites + */ public Map interceptVects() { if (interceptVects == null) { interceptVects = new HashMap(nHits); for (MeasurementSite site : this.SiteList) { StateVector sV = null; - if (site.smoothed) sV = site.aS; - else sV = site.aP; + if (site.smoothed) { + sV = site.aS; + } else { + sV = site.aP; + } double phiS = sV.helix.planeIntersect(site.m.p); - if (Double.isNaN(phiS)) phiS = 0.; - interceptVects.put(site, sV.helix.toGlobal(sV.helix.atPhi(phiS))); + if (Double.isNaN(phiS)) { + phiS = 0.; + } + interceptVects.put(site, sV.helix.toGlobal(sV.helix.atPhi(phiS))); } } return interceptVects; } - + + /** + * Make a map between measurement sites and 3-vector momentum at the + * intercept of the track and SSD plane + */ public Map interceptMomVects() { if (interceptMomVects == null) { interceptMomVects = new HashMap(); for (MeasurementSite site : this.SiteList) { StateVector sV = null; - if (site.smoothed) sV = site.aS; - else sV = site.aP; + if (site.smoothed) { + sV = site.aS; + } else { + sV = site.aP; + } double phiS = sV.helix.planeIntersect(site.m.p); - if (Double.isNaN(phiS)) phiS = 0.; + if (Double.isNaN(phiS)) { + phiS = 0.; + } interceptMomVects.put(site, sV.helix.Rot.inverseRotate(sV.helix.getMom(phiS))); } } return interceptMomVects; } - - // Calculate and return the intersection point of the Kaltrack with an SiModule. - // Local sensor coordinates (u,v) are returned. - // The global intersection can be returned via rGbl if an array of length 3 is passed. - public double [] moduleIntercept(SiModule mod, double [] rGbl) { + + /** + * Calculate and return the intersection point of the Kaltrack with an + * SiModule. // Local sensor coordinates (u,v) are returned. // The global + * intersection can be returned via rGbl if an array of length 3 is passed. + * + * @param mod module + * @param rGbl returned global intersection point + * @return intersection point + */ + public double[] moduleIntercept(SiModule mod, double[] rGbl) { HelixState hx = null; + StateVector aA = null; for (MeasurementSite site : SiteList) { - if (site.m == mod) hx = site.aS.helix; + if (site.aS == null) aA = site.aF; + else aA = site.aS; + if (site.m == mod) { + hx = aA.helix; + } } if (hx == null) { int mxLayer = -1; for (MeasurementSite site : SiteList) { - if (site.m.Layer > mod.Layer) continue; + if (site.m.Layer > mod.Layer) { + continue; + } if (site.m.Layer > mxLayer) { mxLayer = site.m.Layer; - hx = site.aS.helix; + if (site.aS == null) aA = site.aF; + else aA = site.aS; + hx = aA.helix; } } } - if (hx == null) hx = SiteList.get(0).aS.helix; + if (hx == null) { + MeasurementSite site = SiteList.get(0); + if (site.aS == null) aA = site.aF; + else aA = site.aS; + hx = aA.helix; + } double phiS = hx.planeIntersect(mod.p); - if (Double.isNaN(phiS)) phiS = 0.; + if (Double.isNaN(phiS)) { + phiS = 0.; + } Vec intGlb = hx.toGlobal(hx.atPhi(phiS)); if (rGbl != null) { rGbl[0] = intGlb.v[0]; @@ -222,197 +316,354 @@ public Map interceptMomVects() { rGbl[2] = intGlb.v[2]; } Vec intLcl = mod.toLocal(intGlb); - double [] rtnArray = {intLcl.v[0], intLcl.v[1]}; + double[] rtnArray = {intLcl.v[0], intLcl.v[1]}; return rtnArray; } - + + /** + * Make a map between the track measurement sites and the tracker layers + */ private void makeLyrMap() { lyrMap = new HashMap(nHits); for (MeasurementSite site : SiteList) { lyrMap.put(site.m.Layer, site); } } - + + /** + * Make a map between the measurement sites and the corresponding Millipede + * IDs + */ private void makeMillipedeMap() { millipedeMap = new HashMap(nHits); for (MeasurementSite site : SiteList) { millipedeMap.put(site.m.millipedeID, site); } } - - // Find the change in smoothed helix angle in XY between one layer and the next + + /** + * Find the change in smoothed helix angle in XY between one layer and the + * next + * + * @param layer layer from 0 to 13 + * @return difference angle in XY + */ public double scatX(int layer) { - if (lyrMap == null) makeLyrMap(); - if (!lyrMap.containsKey(layer)) return -999.; + if (lyrMap == null) { + makeLyrMap(); + } + if (!lyrMap.containsKey(layer)) { + return -999.; + } int lyrNxt = layer + 1; - while (lyrNxt <= 13 && !lyrMap.containsKey(lyrNxt)) lyrNxt++; - if (lyrNxt > 13) return -999.; + while (lyrNxt <= 13 && !lyrMap.containsKey(lyrNxt)) { + lyrNxt++; + } + if (lyrNxt > 13) { + return -999.; + } MeasurementSite s1 = lyrMap.get(layer); MeasurementSite s2 = lyrMap.get(lyrNxt); - if (s1.aS == null || s2.aS == null) return -999.; + if (s1.aS == null || s2.aS == null) { + return -999.; + } double phiS1 = s1.aS.helix.planeIntersect(s2.m.p); - if (Double.isNaN(phiS1)) return -999.; + if (Double.isNaN(phiS1)) { + return -999.; + } Vec p1 = s1.aS.helix.getMom(phiS1); double t1 = FastMath.atan2(p1.v[0], p1.v[1]); double phiS2 = s2.aS.helix.planeIntersect(s2.m.p); - if (Double.isNaN(phiS2)) return -999.; + if (Double.isNaN(phiS2)) { + return -999.; + } Vec p2 = s2.aS.helix.getMom(phiS2); double t2 = FastMath.atan2(p2.v[0], p2.v[1]); return t1 - t2; } - // Find the change in smoothed helix angle in ZY between one layer and the next + /** + * Find the change in smoothed helix angle in ZY between one layer and the + * next + * + * @param layer layer from 0 to 13 + * @return difference angle in ZY + */ public double scatZ(int layer) { - if (lyrMap == null) makeLyrMap(); - if (!lyrMap.containsKey(layer)) return -999.; + if (lyrMap == null) { + makeLyrMap(); + } + if (!lyrMap.containsKey(layer)) { + return -999.; + } int lyrNxt = layer + 1; - while (lyrNxt <= 13 && !lyrMap.containsKey(lyrNxt)) lyrNxt++; - if (lyrNxt > 13) return -999.; + while (lyrNxt <= 13 && !lyrMap.containsKey(lyrNxt)) { + lyrNxt++; + } + if (lyrNxt > 13) { + return -999.; + } MeasurementSite s1 = lyrMap.get(layer); MeasurementSite s2 = lyrMap.get(lyrNxt); - if (s1.aS == null || s2.aS == null) return -999.; + if (s1.aS == null || s2.aS == null) { + return -999.; + } double phiS1 = s1.aS.helix.planeIntersect(s2.m.p); - if (Double.isNaN(phiS1)) return -999.; + if (Double.isNaN(phiS1)) { + return -999.; + } Vec p1 = s1.aS.helix.Rot.inverseRotate(s1.aS.helix.getMom(phiS1)); double t1 = FastMath.atan2(p1.v[2], p1.v[1]); double phiS2 = s2.aS.helix.planeIntersect(s2.m.p); - if (Double.isNaN(phiS2)) return -999.; + if (Double.isNaN(phiS2)) { + return -999.; + } Vec p2 = s2.aS.helix.Rot.inverseRotate(s2.aS.helix.getMom(phiS2)); double t2 = FastMath.atan2(p2.v[2], p2.v[1]); return t1 - t2; } - public double chi2prime() { // Alternative calculation of the fit chi^2, considering only residuals divided by the hit sigma + /** + * Alternative calculation of the fit chi^2, considering only residuals + * divided by the hit sigma + * + * @return chi-squared + */ + public double chi2prime() { double c2 = 0.; for (MeasurementSite S : SiteList) { - if (S.aS == null) continue; + if (S.aS == null) { + continue; + } double phiS = S.aS.helix.planeIntersect(S.m.p); - if (Double.isNaN(phiS)) { phiS = 0.; } + if (Double.isNaN(phiS)) { + phiS = 0.; + } double vpred = S.h(S.aS, S.m, phiS); for (Measurement hit : S.m.hits) { - for (KalTrack tkr : hit.tracks) { - if (tkr.equals(this)) c2 += FastMath.pow((vpred - hit.v) / hit.sigma, 2); + for (KalTrack tkr : hit.tracks) { + if (tkr.equals(this)) { + c2 += FastMath.pow((vpred - hit.v) / hit.sigma, 2); + } } } } return c2; } - public Pair unbiasedResidualMillipede(int millipedeID) { - if (millipedeMap == null) makeMillipedeMap(); + /** + * Get track unbiased residuals by Millipede ID + * + * @param millipedeID + * @return pair of unbiased residual and its error estimate + */ + public Pair unbiasedResidualMillipede(int millipedeID) { + if (millipedeMap == null) { + makeMillipedeMap(); + } if (millipedeMap.containsKey(millipedeID)) { return unbiasedResidual(millipedeMap.get(millipedeID)); - } else { - return new Pair(-999., -999.); + } else { + return new Pair(-999., -999.); } } - - public Pair unbiasedResidual(int layer) { - if (lyrMap == null) makeLyrMap(); + + /** + * Get track unbiased residual by layer number + * + * @param layer + * @return pair of unbiased residual and its error estimate + */ + public Pair unbiasedResidual(int layer) { + if (lyrMap == null) { + makeLyrMap(); + } if (lyrMap.containsKey(layer)) { return unbiasedResidual(lyrMap.get(layer)); - } else { - return new Pair(-999., -999.); + } else { + return new Pair(-999., -999.); } - } - - // Returns the unbiased residual for the track at a given layer, together with the variance on that residual - public Pair unbiasedResidual(MeasurementSite site) { + } + + /** + * Returns the unbiased residual for the track at a given site, together + * with the variance on that residual + * + * @param site measurement site + * @return residual and error + */ + public Pair unbiasedResidual(MeasurementSite site) { double resid = -999.; - double varResid = -999.; - Vec aStar = null; + double varResid = -999.; + Vec aStar = null; if (site.hitID >= 0) { + StateVector aA = site.aS; + if (aA == null) aA = site.aF; double sigma = site.m.hits.get(site.hitID).sigma; - DMatrixRMaj Cstar = new DMatrixRMaj(5,5); - aStar = site.aS.inverseFilter(site.H, sigma*sigma, Cstar); + DMatrixRMaj Cstar = new DMatrixRMaj(5, 5); + aStar = aA.inverseFilter(site.H, sigma * sigma, Cstar); HelixPlaneIntersect hpi = new HelixPlaneIntersect(); - Plane pTrans = site.m.p.toLocal(site.aS.helix.Rot, site.aS.helix.origin); - double phiInt = hpi.planeIntersect(aStar, site.aS.helix.X0, site.aS.helix.alpha, pTrans); + Plane pTrans = site.m.p.toLocal(aA.helix.Rot, aA.helix.origin); + double phiInt = hpi.planeIntersect(aStar, aA.helix.X0, aA.helix.alpha, pTrans); if (!Double.isNaN(phiInt)) { - Vec intPnt = HelixState.atPhi(site.aS.helix.X0, aStar, phiInt, site.aS.helix.alpha); - Vec globalInt = site.aS.helix.toGlobal(intPnt); + Vec intPnt = HelixState.atPhi(aA.helix.X0, aStar, phiInt, aA.helix.alpha); + Vec globalInt = aA.helix.toGlobal(intPnt); Vec localInt = site.m.toLocal(globalInt); resid = site.m.hits.get(site.hitID).v - localInt.v[1]; - - CommonOps_DDRM.mult(Cstar, site.H, tempV); - varResid = sigma*sigma + CommonOps_DDRM.dot(site.H, tempV); + + CommonOps_DDRM.mult(Cstar, site.H, tempV); + varResid = sigma * sigma + CommonOps_DDRM.dot(site.H, tempV); } - } - return new Pair(resid, varResid); + } + return new Pair(resid, varResid); } - public Pair biasedResidual(int layer) { - if (lyrMap == null) makeLyrMap(); + /** + * Returns the biased residual for the track at a given layer, together with + * the variance on that residual + * + * @param layer + * @return biased residual and error + */ + public Pair biasedResidual(int layer) { + if (lyrMap == null) { + makeLyrMap(); + } if (lyrMap.containsKey(layer)) { return biasedResidual(lyrMap.get(layer)); - } else { - return new Pair(-999., -999.); + } else { + return new Pair(-999., -999.); } } - - public Pair biasedResidualMillipede(int millipedeID) { - if (millipedeMap == null) makeMillipedeMap(); + + /** + * Get track biased residuals by Millipede ID + * + * @param millipedeID + * @return pair of biased residual and its error estimate + */ + public Pair biasedResidualMillipede(int millipedeID) { + if (millipedeMap == null) { + makeMillipedeMap(); + } if (millipedeMap.containsKey(millipedeID)) { return biasedResidual(millipedeMap.get(millipedeID)); - } else { - return new Pair(-999., -999.); + } else { + return new Pair(-999., -999.); } } - - public Pair biasedResidual(MeasurementSite site) { + + /** + * Get track biased residuals by measurement site + * + * @param site + * @return pair of biased residual and its error estimate + */ + public Pair biasedResidual(MeasurementSite site) { double resid = -999.; - double varResid = -999.; + double varResid = -999.; if (site.aS != null) { resid = site.aS.r; varResid = site.aS.R; } - return new Pair(resid, varResid); + return new Pair(resid, varResid); } - + + /** + * Relatively short debug printout + * + * @param s Arbitrary string for the user's reference + */ public void print(String s) { System.out.format("\nKalTrack %s: Event %d, ID=%d, %d hits, chi^2=%10.5f, t=%5.1f from %5.1f to %5.1f, bad=%b\n", s, eventNumber, ID, nHits, chi2, time, tMin, tMax, bad); - if (propagated) System.out.format(" Helix parameters at origin = %s\n", helixAtOrigin.a.toString()); + if (propagated) { + System.out.format(" Helix parameters at origin = %s\n", helixAtOrigin.a.toString()); + } + System.out.format(" Magnetic field magnitude = %10.5f and direction = %s\n", Bmag, tB.toString()); MeasurementSite site0 = this.SiteList.get(0); - if (site0.aS != null) { - System.out.format(" Helix at layer %d: %s, pivot=%s\n", site0.m.Layer, site0.aS.helix.a.toString(), site0.aS.helix.X0.toString()); + StateVector aA = site0.aS; + if (aA == null) aA = site0.aF; + if (aA != null) { + double tanL = aA.helix.a.v[4]; + double K = aA.helix.a.v[2]; + double energy = FastMath.sqrt(1.0+tanL*tanL)/Math.abs(K); + System.out.format(" Helix at layer %d: %s, pivot=%s, E=%9.4f\n", site0.m.Layer, aA.helix.a.toString(), aA.helix.X0.toString(), energy); } for (int i = 0; i < SiteList.size(); i++) { MeasurementSite site = SiteList.get(i); SiModule m = site.m; + StateVector aSite = site.aS; + if (aSite == null) aSite = site.aF; + double energy = 0.; + if (aSite != null) { + double tanL = aSite.helix.a.v[4]; + double K = aSite.helix.a.v[2]; + energy = FastMath.sqrt(1.0+tanL*tanL)/Math.abs(K); + } int hitID = site.hitID; - System.out.format(" Layer %d, detector %d, stereo=%b, chi^2 inc.=%10.6f ", m.Layer, m.detector, m.isStereo, - site.chi2inc); - if (hitID>=0) { + System.out.format(" Layer %d, detector %d, stereo=%b, chi^2 inc.=%10.6f E=%8.3f ", m.Layer, m.detector, m.isStereo, + site.chi2inc, energy); + if (hitID >= 0) { + if (site.aS == null) aA = site.aF; + else aA = site.aS; System.out.format(" t=%5.1f ", site.m.hits.get(site.hitID).time); - double residual = site.m.hits.get(hitID).v - site.aS.mPred; - Pair unBiasedResid = unbiasedResidual(site); - double [] lclint = moduleIntercept(m, null); - System.out.format(" measurement=%10.5f, predicted=%10.5f, residual=%9.5f, x=%9.5f limit=%9.5f \n", site.m.hits.get(hitID).v, site.aS.mPred, - residual, lclint[0], m.xExtent[1]); + double residual = site.m.hits.get(hitID).v - aA.mPred; + Pair unBiasedResid = unbiasedResidual(site); + double[] lclint = moduleIntercept(m, null); + System.out.format(" measurement=%10.5f, predicted=%10.5f, residual=%9.5f, unbiased=%9.5f, x=%9.5f limit=%9.5f \n", site.m.hits.get(hitID).v, aA.mPred, + residual, unBiasedResid.getFirstElement(), lclint[0], m.xExtent[1]); } else { System.out.format("\n"); } } } - + + /** + * Long detailed debug printout + * + * @param s Arbitrary string for the user's reference + */ public void printLong(String s) { - System.out.format("%s", this.toString(s)); + System.out.format("%s", this.toString(s)); } - + + /** + * Long detailed debug printout to a string + * + * @param s Arbitrary string for the user's reference + */ String toString(String s) { String str = String.format("\n KalTrack %s: Event %d, ID=%d, %d hits, chi^2=%10.5f, t=%5.1f from %5.1f to %5.1f, bad=%b\n", s, eventNumber, ID, nHits, chi2, time, tMin, tMax, bad); if (propagated) { - str=str+String.format(" B-field at the origin=%10.6f, direction=%8.6f %8.6f %8.6f\n", Bmag, tB.v[0], tB.v[1], tB.v[2]); - str=str+helixAtOrigin.toString("helix state for a pivot at the origin")+"\n"; - str=str+originPoint.toString("point on the helix closest to the origin")+"\n"; - str=str+String.format(" arc length from the origin to the first measurement=%9.4f\n", arcLength[0]); + str = str + String.format(" B-field at the origin=%10.6f, direction=%8.6f %8.6f %8.6f\n", Bmag, tB.v[0], tB.v[1], tB.v[2]); + str = str + helixAtOrigin.toString("helix state for a pivot at the origin") + "\n"; + str = str + originPoint.toString("point on the helix closest to the origin") + "\n"; + str = str + String.format(" arc length from the origin to the first measurement=%9.4f\n", arcLength[0]); SquareMatrix C1 = new SquareMatrix(3, Cx); - str=str+C1.toString("covariance matrix for the point"); - str=str+originMomentum.toString("momentum of the particle at closest approach to the origin\n"); + str = str + C1.toString("covariance matrix for the point"); + str = str + originMomentum.toString("momentum of the particle at closest approach to the origin\n"); SquareMatrix C2 = new SquareMatrix(3, Cp); - str=str+C2.toString("covariance matrix for the momentum"); + str = str + C2.toString("covariance matrix for the momentum"); + double tanL = helixAtOrigin.a.v[4]; + double K = helixAtOrigin.a.v[2]; + double energy = FastMath.sqrt(1.0+tanL*tanL)/Math.abs(K); + str=str+String.format(" Energy unconstrained = %10.6f\n", energy); } + if (energyConstrained) { + str=str+String.format("Chi-squared of energy constrained fit = %10.5f\n", chi2_Econstraint); + //str=str+originPoint.toString("point on the helix closest to the origin")+"\n"; + //str=str+String.format(" arc length from the origin to the first measurement=%9.4f\n", arcLength[0]); + //SquareMatrix C1 = new SquareMatrix(3, Cx); + //str=str+C1.toString("covariance matrix for the point"); + //str=str+originMomentum.toString("momentum of the particle at closest approach to the origin\n"); + //SquareMatrix C2 = new SquareMatrix(3, Cp); + //str=str+C2.toString("covariance matrix for the momentum"); + double tanL = helixAtOrigin.a.v[4]; + double K = helixAtOrigin.a.v[2]; + double energy = FastMath.sqrt(1.0 + tanL * tanL) / Math.abs(K); + str = str + String.format(" Energy constrained = %10.6f\n", energy); + } MeasurementSite site0 = this.SiteList.get(0); if (site0.aS != null) { str=str + String.format(" Helix at layer %d: %s\n", site0.m.Layer, site0.aS.helix.a.toString()); @@ -421,35 +672,41 @@ String toString(String s) { MeasurementSite site = SiteList.get(i); SiModule m = site.m; int hitID = site.hitID; - str=str+String.format("Layer %d, detector %d, stereo=%b, chi^2 inc.=%10.6f, Xscat=%10.8f Zscat=%10.8f, arc=%10.5f, hit=%d", m.Layer, m.detector, m.isStereo, + str = str + String.format("Layer %d, detector %d, stereo=%b, chi^2 inc.=%10.6f, Xscat=%10.8f Zscat=%10.8f, arc=%10.5f, hit=%d", m.Layer, m.detector, m.isStereo, site.chi2inc, site.scatX(), site.scatZ(), site.arcLength, hitID); if (hitID < 0) { - str=str+"\n"; + str = str + "\n"; continue; } - str=str+String.format(", t=%5.1f", site.m.hits.get(site.hitID).time); + str = str + String.format(", t=%5.1f", site.m.hits.get(site.hitID).time); if (m.hits.get(hitID).tksMC != null) { - str=str+String.format(" MC tracks: "); + str = str + String.format(" MC tracks: "); for (int iMC : m.hits.get(hitID).tksMC) { - str=str+String.format(" %d ", iMC); + str = str + String.format(" %d ", iMC); } - str=str+"\n"; + str = str + "\n"; } if (interceptVects().containsKey(site)) { Vec interceptVec = interceptVects().get(site); Vec interceptMomVec = interceptMomVects().get(site); double residual = site.m.hits.get(hitID).v - site.aS.mPred; - Pair unBiasedResid = unbiasedResidual(site); - str=str+String.format(" Intercept=%s, p=%s, measurement=%10.5f, predicted=%10.5f, residual=%9.5f, unbiased=%9.5f+-%9.5f, error=%9.5f \n", interceptVec.toString(), + Pair unBiasedResid = unbiasedResidual(site); + str = str + String.format(" Intercept=%s, p=%s, measurement=%10.5f, predicted=%10.5f, residual=%9.5f, unbiased=%9.5f+-%9.5f, error=%9.5f \n", interceptVec.toString(), interceptMomVec.toString(), site.m.hits.get(hitID).v, site.aS.mPred, residual, unBiasedResid.getFirstElement(), unBiasedResid.getSecondElement(), FastMath.sqrt(site.aS.R)); } } - str=str+String.format("End of printing for KalTrack %s ID %d in event %d\n\n", s, ID, eventNumber); + str = str + String.format("End of printing for KalTrack %s ID %d in event %d\n\n", s, ID, eventNumber); return str; } - // Method to make simple yz plots of the track and the residuals. Note that in the yz plot of the track the hits are - // placed at the strip center, so they generally will not appear to be right on the track. Use gnuplot to display. + /** + * Method to make simple yz plots of the track and the residuals. Note that + * in the yz plot of the track the hits are placed at the strip center, so + * they generally will not appear to be right on the track. Use gnuplot to + * display. + * + * @param path where to put the output + */ public void plot(String path) { File file = new File(String.format("%s/Track%d_%d.gp", path, ID, eventNumber)); file.getParentFile().mkdirs(); @@ -499,13 +756,19 @@ public void plot(String path) { pW.format("set yrange[-0.025 : 0.025]\n"); pW.format("$resids << EOD\n"); for (MeasurementSite site : SiteList) { - if (site.m.Layer < 0) continue; + if (site.m.Layer < 0) { + continue; + } double phiS = site.aS.helix.planeIntersect(site.m.p); - if (Double.isNaN(phiS)) { continue; } + if (Double.isNaN(phiS)) { + continue; + } Vec rHelixG = site.aS.helix.toGlobal(site.aS.helix.atPhi(phiS)); Vec rHelixL = site.m.toLocal(rHelixG); double residual = -999.; - if (site.hitID >= 0) residual = site.m.hits.get(site.hitID).v - rHelixL.v[1]; + if (site.hitID >= 0) { + residual = site.m.hits.get(site.hitID).v - rHelixL.v[1]; + } pW.format(" %10.5f %10.6f # %10.6f\n", rHelixG.v[1], residual, site.aS.r); } pW.format("EOD\n"); @@ -513,16 +776,30 @@ public void plot(String path) { pW.close(); } - // Arc length along track from the origin to the first measurement + /** + * Arc length along track from the origin to the first measurement + * + * @return arc length in mm + */ public double originArcLength() { - if (!propagated || arcLength == null) originHelix(); - if (arcLength == null) return 0.; + if (!propagated || arcLength == null) { + originHelix(); + } + if (arcLength == null) { + return 0.; + } return arcLength[0]; } - - // Runge Kutta propagation of the helix to the origin + + /** + * Runge Kutta propagation of the helix to the origin + * + * @return helix state at the origin + */ public boolean originHelix() { - if (propagated) return true; + if (propagated) { + return true; + } // Find the measurement site closest to the origin (target) MeasurementSite innerSite = null; @@ -543,29 +820,43 @@ public boolean originHelix() { return false; } Vec beamSpot = new Vec(3, kPar.beamSpot); - + // This propagated helix will have its pivot at the origin but is in the origin B-field frame // The StateVector method propagateRungeKutta transforms the origin plane into the origin B-field frame - Plane originPlane = new Plane(beamSpot, new Vec(0., 1., 0.)); + Plane originPlane = new Plane(beamSpot, new Vec(0., 1., 0.)); arcLength = new double[1]; - helixAtOrigin = innerSite.aS.helix.propagateRungeKutta(originPlane, yScat, XLscat, innerSite.m.Bfield, arcLength); - if (debug) System.out.format("KalTrack::originHelix: arc length to the first measurement = %9.4f\n", arcLength[0]); - if (covNaN()) return false; - if (!solver.setA(helixAtOrigin.C.copy())) { - logger.fine("KalTrack:originHelix, cannot invert the covariance matrix"); - for (int i=0; i<5; ++i) { // Fill the matrix and inverse with something not too crazy and continue . . . - for (int j=0; j<5; ++j) { - if (i == j) { - Cinv.unsafe_set(i,j,1.0/Math.abs(helixAtOrigin.C.unsafe_get(i, j))); - helixAtOrigin.C.unsafe_set(i, j, Math.abs(helixAtOrigin.C.unsafe_get(i, j))); - } else { - Cinv.unsafe_set(i, j, 0.); - helixAtOrigin.C.unsafe_set(i, j, 0.); + if (uniformBatOrigin) { // Just a simple pivot transform is needed if the field is assumed uniform + Vec origin = new Vec(0., 0., 0.); + Vec newHelixParams = innerSite.aS.helix.pivotTransform(); + DMatrixRMaj newCovariance = innerSite.aS.covariancePivotTransform(newHelixParams, 1.0); + helixAtOrigin = new HelixState(newHelixParams, origin, origin, newCovariance, Bmag, tB); + //innerSite.aS.helix.print("at innerSite"); + //helixAtOrigin.print("uniformB"); + } else { + helixAtOrigin = innerSite.aS.helix.propagateRungeKutta(originPlane, yScat, XLscat, innerSite.m.Bfield, arcLength); + //helixAtOrigin.print("nonuniformB"); + if (debug) { + System.out.format("KalTrack::originHelix: arc length to the first measurement = %9.4f\n", arcLength[0]); + } + if (covNaN()) { + return false; + } + if (!solver.setA(helixAtOrigin.C.copy())) { + logger.fine("KalTrack:originHelix, cannot invert the covariance matrix"); + for (int i = 0; i < 5; ++i) { // Fill the matrix and inverse with something not too crazy and continue . . . + for (int j = 0; j < 5; ++j) { + if (i == j) { + Cinv.unsafe_set(i, j, 1.0 / Math.abs(helixAtOrigin.C.unsafe_get(i, j))); + helixAtOrigin.C.unsafe_set(i, j, Math.abs(helixAtOrigin.C.unsafe_get(i, j))); + } else { + Cinv.unsafe_set(i, j, 0.); + helixAtOrigin.C.unsafe_set(i, j, 0.); + } } } - } - } else { - solver.invert(Cinv); + } else { + solver.invert(Cinv); + } } // Find the position and momentum of the particle near the origin, including covariance @@ -597,64 +888,136 @@ public boolean originHelix() { return true; } + /** + * Get the extrapolate point in the plane of the origin + * + * @return 3-vector array of coordinates + */ public double[] originX() { - if (!propagated) originHelix(); - if (!propagated) return new double [] {0.,0.,0.}; + if (!propagated) { + originHelix(); + } + if (!propagated) { + return new double[]{0., 0., 0.}; + } return originPoint.v.clone(); } + /** + * Get the covariance of the track point in the origin plane + * + * @return 2D array, 3 by 3 + */ public double[][] originXcov() { return Cx.clone(); } + /** + * Get the track momentum at the origin + * + * @return 3-vector momentum + */ + public double[] originP() { + if (!propagated) { + originHelix(); + } + if (!propagated) { + return new double[]{0., 0., 0.}; + } + return originMomentum.v.clone(); + } + + /** + * Get the covariance of the momentum at the origin + * + * @return 3 by 3 array + */ public double[][] originPcov() { return Cp.clone(); } + /** + * Get the helix pivot point near the origin + * + * @return 3-vector array + */ public double[] originPivot() { - if (propagated) return helixAtOrigin.X0.v.clone(); - else return null; - } - - public double[] originP() { - if (!propagated) originHelix(); - if (!propagated) return new double [] {0.,0.,0.}; - return originMomentum.v.clone(); + if (propagated) { + return helixAtOrigin.X0.v.clone(); + } else { + return null; + } } + /** + * Get the covariance of helix parameters at the origin + * + * @return 5 by 5 array + */ public double[][] originCovariance() { - if (!propagated) originHelix(); - double [][] M = new double[5][5]; - for (int i=0; i<5; ++i) { - for (int j=0; j<5; ++j) { - if (propagated) M[i][j] = helixAtOrigin.C.unsafe_get(i, j); - else M[i][j] = SiteList.get(0).aS.helix.C.unsafe_get(i, j); + if (!propagated) { + originHelix(); + } + double[][] M = new double[5][5]; + for (int i = 0; i < 5; ++i) { + for (int j = 0; j < 5; ++j) { + if (propagated) { + M[i][j] = helixAtOrigin.C.unsafe_get(i, j); + } else { + M[i][j] = SiteList.get(0).aS.helix.C.unsafe_get(i, j); + } } } return M; } - public boolean covNaN() { - if (helixAtOrigin.C == null) return true; + /** + * Check whether all elements of the covariance are real numbers + * + * @return false if the covariance is rotten + */ + public boolean covNaN() { + if (helixAtOrigin.C == null) { + return true; + } return MatrixFeatures_DDRM.hasNaN(helixAtOrigin.C); } - + + /** + * Return the helix parameters of the track at the origin + * + * @return array of 5 doubles + */ public double[] originHelixParms() { - if (propagated) return helixAtOrigin.a.v.clone(); - else return null; + if (propagated) { + return helixAtOrigin.a.v.clone(); + } else { + return null; + } } - - //Update the helix parameters at the "origin" by using the target position or vertex as a constraint - public HelixState originConstraint(double [] vtx, double [][] vtxCov) { - if (!propagated) originHelix(); - if (!propagated) return null; - + + /** + * Update the helix parameters at the "origin" by using the target position + * or vertex as a constraint + * + * @param vtx vertex location + * @param vtxCov vertex error matrix + * @return constrained helix state + */ + public HelixState originConstraint(double[] vtx, double[][] vtxCov) { + if (!propagated) { + originHelix(); + } + if (!propagated) { + return null; + } + // Transform the inputs in the the helix field-oriented coordinate system - Vec v = helixAtOrigin.toLocal(new Vec(3,vtx)); - SquareMatrix Cov = helixAtOrigin.Rot.rotate(new SquareMatrix(3,vtxCov)); + Vec v = helixAtOrigin.toLocal(new Vec(3, vtx)); + SquareMatrix Cov = helixAtOrigin.Rot.rotate(new SquareMatrix(3, vtxCov)); Vec X0 = helixAtOrigin.X0; double phi = phiDOCA(helixAtOrigin.a, v, X0, alpha); -/* if (debug) { // Test the DOCA algorithm + /* if (debug) { // Test the DOCA algorithm Vec rDoca = HelixState.atPhi(X0, helixAtOrigin.a, phi, alpha); System.out.format("originConstraint: phi of DOCA=%10.5e\n", phi); rDoca.print(" DOCA point"); @@ -673,54 +1036,58 @@ public HelixState originConstraint(double [] vtx, double [][] vtxCov) { double df2 = deriv * delPhi; System.out.format("Test of fDOCA derivative: df exact = %11.7f; df from derivative = %11.7f\n", df1, df2); }*/ - double [][] H = buildH(helixAtOrigin.a, v, X0, phi, alpha); + double[][] H = buildH(helixAtOrigin.a, v, X0, phi, alpha); Vec pntDOCA = HelixState.atPhi(X0, helixAtOrigin.a, phi, alpha); if (debug) { matrixPrint("H", H, 3, 5); - + // Derivative test HelixState hx = helixAtOrigin.copy(); - double daRel[] = { -0.04, 0.03, -0.16, -0.02, -0.015 }; - for (int i=0; i<5; ++i) daRel[i] = daRel[i]/100.; - for (int i = 0; i < 5; i++) { hx.a.v[i] = hx.a.v[i] * (1.0 + daRel[i]); } + double daRel[] = {-0.04, 0.03, -0.16, -0.02, -0.015}; + for (int i = 0; i < 5; ++i) { + daRel[i] = daRel[i] / 100.; + } + for (int i = 0; i < 5; i++) { + hx.a.v[i] = hx.a.v[i] * (1.0 + daRel[i]); + } Vec da = new Vec(hx.a.v[0] * daRel[0], hx.a.v[1] * daRel[1], hx.a.v[2] * daRel[2], hx.a.v[3] * daRel[3], hx.a.v[4] * daRel[4]); double phi2 = phiDOCA(hx.a, v, X0, alpha); Vec newX = HelixState.atPhi(X0, hx.a, phi2, alpha); Vec dxTrue = newX.dif(pntDOCA); dxTrue.print("originConstraint derivative test actual difference"); - + Vec dx = new Vec(3); - for (int i=0; i<3; ++i) { - for (int j=0; j<5; ++j) { - dx.v[i] += H[i][j]*da.v[j]; + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 5; ++j) { + dx.v[i] += H[i][j] * da.v[j]; } } dx.print("difference from H derivative matrix"); - for (int i=0; i<3; ++i) { - double err = 100.*(dxTrue.v[i] - dx.v[i])/dxTrue.v[i]; + for (int i = 0; i < 3; ++i) { + double err = 100. * (dxTrue.v[i] - dx.v[i]) / dxTrue.v[i]; System.out.format(" Coordiante %d: percent difference = %10.6f\n", i, err); } System.out.println("helix covariance:"); helixAtOrigin.C.print(); } SquareMatrix Ginv = new SquareMatrix(3); - for (int i=0; i<3; ++i) { - for (int j=0; j<3; ++j) { + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { Ginv.M[i][j] = Cov.M[i][j]; - for (int k=0; k<5; ++k) { - for (int l=0; l<5; ++l) { + for (int k = 0; k < 5; ++k) { + for (int l = 0; l < 5; ++l) { Ginv.M[i][j] += H[i][k] * helixAtOrigin.C.unsafe_get(k, l) * H[j][l]; } } } } SquareMatrix G = Ginv.fastInvert(); - double [][] K = new double[5][3]; // Kalman gain matrix - for (int i=0; i<5; ++i) { - for (int j=0; j<3; ++j) { - for (int k=0; k<5; ++k) { - for (int l=0; l<3; ++l) { + double[][] K = new double[5][3]; // Kalman gain matrix + for (int i = 0; i < 5; ++i) { + for (int j = 0; j < 3; ++j) { + for (int k = 0; k < 5; ++k) { + for (int l = 0; l < 3; ++l) { K[i][j] += helixAtOrigin.C.unsafe_get(i, k) * H[l][k] * G.M[l][j]; } } @@ -730,24 +1097,24 @@ public HelixState originConstraint(double [] vtx, double [][] vtxCov) { G.print("G"); matrixPrint("K", K, 5, 3); } - double [] newHelixParms = new double[5]; - double [][] newHelixCov = new double[5][5]; - for (int i=0; i<5; ++i) { + double[] newHelixParms = new double[5]; + double[][] newHelixCov = new double[5][5]; + for (int i = 0; i < 5; ++i) { newHelixParms[i] = helixAtOrigin.a.v[i]; - for (int j=0; j<3; ++j) { + for (int j = 0; j < 3; ++j) { newHelixParms[i] += K[i][j] * (vtx[j] - pntDOCA.v[j]); } - for (int j=0; j<5; ++j) { + for (int j = 0; j < 5; ++j) { newHelixCov[i][j] = helixAtOrigin.C.unsafe_get(i, j); - for (int k=0; k<3; ++k) { - for (int l=0; l<5; ++l) { + for (int k = 0; k < 3; ++k) { + for (int l = 0; l < 5; ++l) { newHelixCov[i][j] -= K[i][k] * H[k][l] * helixAtOrigin.C.unsafe_get(l, j); } } } } // Calculate the chi-squared contribution - Vec newHelix = new Vec(5,newHelixParms); + Vec newHelix = new Vec(5, newHelixParms); phi = phiDOCA(newHelix, v, X0, alpha); SquareMatrix CovInv = Cov.invert(); pntDOCA = HelixState.atPhi(X0, newHelix, phi, alpha); @@ -759,10 +1126,10 @@ public HelixState originConstraint(double [] vtx, double [][] vtxCov) { solver.setA(helixAtOrigin.C); solver.invert(Cinv); SquareMatrix CinvS = mToS(Cinv); - for (int i=0; i<5; ++i) { - for (int j=0; j<5; ++j) { - for (int k=0; k<3; ++k) { - for (int l=0; l<3; ++l) { + for (int i = 0; i < 5; ++i) { + for (int j = 0; j < 5; ++j) { + for (int k = 0; k < 3; ++k) { + for (int l = 0; l < 3; ++l) { CinvS.M[i][j] += H[k][i] * Vinv.M[k][l] * H[l][j]; } } @@ -770,11 +1137,11 @@ public HelixState originConstraint(double [] vtx, double [][] vtxCov) { } SquareMatrix Calt = CinvS.invert(); Calt.print("Alternative filtered covariance"); - double [][] Kp = new double[5][3]; - for (int i=0; i<5; ++i) { - for (int j=0; j<3; ++j) { - for (int k=0; k<5; ++k) { - for (int l=0; l<3; ++l) { + double[][] Kp = new double[5][3]; + for (int i = 0; i < 5; ++i) { + for (int j = 0; j < 3; ++j) { + for (int k = 0; k < 5; ++k) { + for (int l = 0; l < 3; ++l) { Kp[i][j] += Calt.M[i][k] * H[l][k] * Vinv.M[l][j]; } } @@ -784,57 +1151,75 @@ public HelixState originConstraint(double [] vtx, double [][] vtxCov) { } return new HelixState(newHelix, X0, helixAtOrigin.origin, new DMatrixRMaj(newHelixCov), helixAtOrigin.B, helixAtOrigin.tB); } - + public double chi2incOrigin() { return chi2incVtx; } - - private static void matrixPrint(String s, double [][] A, int M, int N) { + + private static void matrixPrint(String s, double[][] A, int M, int N) { System.out.format("Dump of %d by %d matrix %s:\n", M, N, s); - for (int i=0; i= x2) { - Logger.getLogger(KalTrack.class.getName()).log(Level.WARNING,"rtsafe: initial guess needs to be bracketed."); + Logger.getLogger(KalTrack.class.getName()).log(Level.WARNING, "rtsafe: initial guess needs to be bracketed."); return xGuess; } fl = fDOCA(x1, a, v, X0, alpha); fh = fDOCA(x2, a, v, X0, alpha); int nTry = 0; - while (fl*fh > 0.0) { + while (fl * fh > 0.0) { if (nTry == 5) { - Logger.getLogger(KalTrack.class.getName()).log(Level.FINE,String.format("Root is not bracketed in zero finding, fl=%12.5e, fh=%12.5e, alpha=%10.6f, x1=%12.5f x2=%12.5f xGuess=%12.5f", + Logger.getLogger(KalTrack.class.getName()).log(Level.FINE, String.format("Root is not bracketed in zero finding, fl=%12.5e, fh=%12.5e, alpha=%10.6f, x1=%12.5f x2=%12.5f xGuess=%12.5f", fl, fh, alpha, x1, x2, xGuess)); return xGuess; } @@ -845,8 +1230,12 @@ private static double rtSafe(double xGuess, double x1, double x2, double xacc, V nTry++; } //if (nTry > 0) System.out.format("KalTrack.rtsafe: %d tries needed to bracket solution.\n", nTry); - if (fl == 0.) return x1; - if (fh == 0.) return x2; + if (fl == 0.) { + return x1; + } + if (fh == 0.) { + return x2; + } if (fl < 0.0) { xl = x1; xh = x2; @@ -858,19 +1247,23 @@ private static double rtSafe(double xGuess, double x1, double x2, double xacc, V dxold = Math.abs(x2 - x1); dx = dxold; f = fDOCA(rts, a, v, X0, alpha); - df = dfDOCAdPhi(rts,a, v, X0, alpha); + df = dfDOCAdPhi(rts, a, v, X0, alpha); for (int j = 1; j <= MAXIT; j++) { if ((((rts - xh) * df - f) * ((rts - xl) * df - f) > 0.0) || (Math.abs(2.0 * f) > Math.abs(dxold * df))) { dxold = dx; dx = 0.5 * (xh - xl); // Use bisection if the Newton-Raphson method is going bonkers rts = xl + dx; - if (xl == rts) return rts; + if (xl == rts) { + return rts; + } } else { dxold = dx; dx = f / df; // Newton-Raphson method temp = rts; rts -= dx; - if (temp == rts) return rts; + if (temp == rts) { + return rts; + } } if (Math.abs(dx) < xacc) { // System.out.format("KalTrack.rtSafe: solution converged in %d iterations.\n", @@ -878,25 +1271,45 @@ private static double rtSafe(double xGuess, double x1, double x2, double xacc, V return rts; } f = fDOCA(rts, a, v, X0, alpha); - df = dfDOCAdPhi(rts,a, v, X0, alpha); + df = dfDOCAdPhi(rts, a, v, X0, alpha); if (f < 0.0) { xl = rts; } else { xh = rts; } } - Logger.getLogger(KalTrack.class.getName()).log(Level.WARNING,"rtsafe: maximum number of iterations exceeded."); + Logger.getLogger(KalTrack.class.getName()).log(Level.WARNING, "rtsafe: maximum number of iterations exceeded."); return rts; } - - // Function that is zero when the helix turning angle phi is at the point of closest approach to v + + /** + * Function that is zero when the helix turning angle phi is at the point of + * closest approach to v + * + * @param phi turning angle + * @param a helix parameters + * @param v point of interest + * @param X0 helix pivot point + * @param alpha magnetic field constant + * @return deviation from zero + */ private static double fDOCA(double phi, Vec a, Vec v, Vec X0, double alpha) { Vec t = tangentVec(a, phi, alpha); Vec x = HelixState.atPhi(X0, a, phi, alpha); return (v.dif(x)).dot(t); } - - // derivative of the fDOCA function with respect to phi, for the zero-finding algorithm + + /** + * derivative of the fDOCA function with respect to phi, for the + * zero-finding algorithm + * + * @param phi turning angle + * @param a helix parameters + * @param v point of interest + * @param X0 helix pivot point + * @param alpha magnetic field constant + * @return derivative + */ private static double dfDOCAdPhi(double phi, Vec a, Vec v, Vec X0, double alpha) { Vec x = HelixState.atPhi(X0, a, phi, alpha); Vec dxdphi = dXdPhi(a, phi, alpha); @@ -906,45 +1319,65 @@ private static double dfDOCAdPhi(double phi, Vec a, Vec v, Vec X0, double alpha) double dfdphi = -t.dot(dxdphi) + dfdt.dot(dtdphi); return dfdphi; } - - // Derivatives of position along a helix with respect to the turning angle phi + + /** + * Derivatives of position along a helix with respect to the turning angle + * phi + * + * @param a helix parameters + * @param phi turning angle + * @param alpha magnetic field parameter + * @return derivative + */ private static Vec dXdPhi(Vec a, double phi, double alpha) { return new Vec((alpha / a.v[2]) * FastMath.sin(a.v[1] + phi), -(alpha / a.v[2]) * FastMath.cos(a.v[1] + phi), -(alpha / a.v[2]) * a.v[4]); } - - // A vector tangent to the helix 'a' at the alpha phi + + /** + * A vector tangent to the helix 'a' at the alpha phi + * + * @param a helix parameters + * @param phi turning angle + * @param alpha magnetic field parameter + * @return tangent vector + */ private static Vec tangentVec(Vec a, double phi, double alpha) { - return new Vec((alpha/a.v[2])*FastMath.sin(a.v[1]+phi), -(alpha/a.v[2])*FastMath.cos(a.v[1]+phi), -(alpha/a.v[2])*a.v[4]); + return new Vec((alpha / a.v[2]) * FastMath.sin(a.v[1] + phi), -(alpha / a.v[2]) * FastMath.cos(a.v[1] + phi), -(alpha / a.v[2]) * a.v[4]); } - + private static Vec dTangentVecDphi(Vec a, double phi, double alpha) { - return new Vec((alpha/a.v[2])*FastMath.cos(a.v[1]+phi), (alpha/a.v[2])*FastMath.sin(a.v[2]+phi), 0.); + return new Vec((alpha / a.v[2]) * FastMath.cos(a.v[1] + phi), (alpha / a.v[2]) * FastMath.sin(a.v[2] + phi), 0.); } - - //Derivative matrix for the helix 'a' point of closet approach to point 'v' - private static double [][] buildH(Vec a, Vec v, Vec X0, double phi, double alpha) { - // a = helix parameters - // X0 = pivot point of helix - // phi = angle along helix to the point of closet approach - // v = 3D point for which we are finding the DOCA (the "measurement" point) - // alpha = constant to convert from curvature to 1/pt - + + /** + * /Derivative matrix for the helix 'a' point of closet approach to point + * 'v' + * + * @param a helix parameters + * @param v 3D point for which we are finding the DOCA (the "measurement" + * point) + * @param X0 pivot point of helix + * @param phi angle along helix to the point of closet approach + * @param alpha constant to convert from curvature to 1/pt + * @return derivative matrix + */ + private static double[][] buildH(Vec a, Vec v, Vec X0, double phi, double alpha) { Vec x = HelixState.atPhi(X0, a, phi, alpha); Vec dxdphi = dXdPhi(a, phi, alpha); Vec t = tangentVec(a, phi, alpha); Vec dtdphi = dTangentVecDphi(a, phi, alpha); Vec dfdt = v.dif(x); double dfdphi = -t.dot(dxdphi) + dfdt.dot(dtdphi); - double [][] dtda = new double[3][5]; - dtda[0][1] = (alpha/a.v[2])*FastMath.cos(a.v[1]+phi); - dtda[0][2] = (-alpha/(a.v[2]*a.v[2]))*FastMath.sin(a.v[1]+phi); - dtda[1][1] = (alpha/a.v[2])*FastMath.sin(a.v[1]+phi); - dtda[1][2] = (alpha/(a.v[2]*a.v[2]))*FastMath.cos(a.v[1]+phi); - dtda[2][2] = (alpha/(a.v[2]*a.v[2]))*a.v[4]; - dtda[2][4] = -alpha/a.v[2]; - - double [][] dxda = new double[3][5]; + double[][] dtda = new double[3][5]; + dtda[0][1] = (alpha / a.v[2]) * FastMath.cos(a.v[1] + phi); + dtda[0][2] = (-alpha / (a.v[2] * a.v[2])) * FastMath.sin(a.v[1] + phi); + dtda[1][1] = (alpha / a.v[2]) * FastMath.sin(a.v[1] + phi); + dtda[1][2] = (alpha / (a.v[2] * a.v[2])) * FastMath.cos(a.v[1] + phi); + dtda[2][2] = (alpha / (a.v[2] * a.v[2])) * a.v[4]; + dtda[2][4] = -alpha / a.v[2]; + + double[][] dxda = new double[3][5]; dxda[0][0] = FastMath.cos(a.v[1]); dxda[1][0] = FastMath.sin(a.v[1]); dxda[0][1] = -(a.v[0] + alpha / a.v[2]) * FastMath.sin(a.v[1]) + (alpha / a.v[2]) * FastMath.sin(a.v[1] + phi); @@ -954,41 +1387,65 @@ private static Vec dTangentVecDphi(Vec a, double phi, double alpha) { dxda[2][2] = (alpha / (a.v[2] * a.v[2])) * a.v[4] * phi; dxda[2][3] = 1.0; dxda[2][4] = -(alpha / a.v[2]) * phi; - + Vec dfda = new Vec(5); Vec dphida = new Vec(5); - for (int i=0; i<5; ++i) { - for (int j=0; j<3; ++j) { - dfda.v[i] += -t.v[j]*dxda[j][i] + dfdt.v[j]*dtda[j][i]; + for (int i = 0; i < 5; ++i) { + for (int j = 0; j < 3; ++j) { + dfda.v[i] += -t.v[j] * dxda[j][i] + dfdt.v[j] * dtda[j][i]; } - dphida.v[i] = -dfda.v[i]/dfdphi; + dphida.v[i] = -dfda.v[i] / dfdphi; } - - double [][] H = new double[3][5]; - for (int i=0; i<3; ++i) { - for (int j=0; j<5; ++j) { - H[i][j] = dxdphi.v[i]*dphida.v[j] + dxda[i][j]; - } + + double[][] H = new double[3][5]; + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 5; ++j) { + H[i][j] = dxdphi.v[i] * dphida.v[j] + dxda[i][j]; + } } - + return H; } + /** + * Return the error on one of the helix parameters at the origin + * + * @param i 0 to 4 to select which parameter + * @return the error estimate + */ public double helixErr(int i) { return FastMath.sqrt(helixAtOrigin.C.unsafe_get(i, i)); } + /** + * Rotate a 3-vector from local field coordinates to global coordinates + * + * @param x input 3-vector + * @return output 3-vector + */ public double[] rotateToGlobal(double[] x) { Vec xIn = new Vec(x[0], x[1], x[2]); return Rot.inverseRotate(xIn).v.clone(); } + /** + * Rotate a 3-vector from global coordinates to local field coordinates + * + * @param x input 3-vector + * @return output 3-vector + */ public double[] rotateToLocal(double[] x) { Vec xIn = new Vec(x[0], x[1], x[2]); return Rot.rotate(xIn).v.clone(); } - // Figure out which measurement site on this track points to a given detector module + /** + * Figure out which measurement site on this track points to a given + * detector module + * + * @param module silicon module + * @return measurement site + */ public int whichSite(SiModule module) { if (lyrMap != null) { return SiteList.indexOf(lyrMap.get(module.Layer)); @@ -1001,29 +1458,49 @@ public int whichSite(SiModule module) { return -1; } + /** + * Sort the measurement sites in order of SVT layer number + * + * @param ascending true for ascending order, false for descending + */ public void sortSites(boolean ascending) { - if (ascending) Collections.sort(SiteList, MeasurementSite.SiteComparatorUp); - else Collections.sort(SiteList, MeasurementSite.SiteComparatorDn); + if (ascending) { + Collections.sort(SiteList, MeasurementSite.SiteComparatorUp); + } else { + Collections.sort(SiteList, MeasurementSite.SiteComparatorDn); + } } + /** + * Remove a selected hit from a KalTrack object. Try to add a different hit. + * + * @param site The measurement site of the hit to be removed + * @param mxChi2Inc Maximum chi^2 increment to add another hit in the same + * layer + * @param mxTdif Maximum time difference of all hits to add another hit in + * the same layer + * @return + */ public boolean removeHit(MeasurementSite site, double mxChi2Inc, double mxTdif) { boolean exchange = false; - if (debug) System.out.format("Event %d track %d remove hit %d on layer %d detector %d\n", - eventNumber, ID, site.hitID, site.m.Layer, site.m.detector); + if (debug) { + System.out.format("Event %d track %d remove hit %d on layer %d detector %d\n", + eventNumber, ID, site.hitID, site.m.Layer, site.m.detector); + } if (site.hitID < 0) { // This should never happen - logger.log(Level.WARNING, String.format("Event %d track %d, trying to remove nonexistent hit on layer %d detector %d", + logger.log(Level.WARNING, String.format("Event %d track %d, trying to remove nonexistent hit on layer %d detector %d", eventNumber, ID, site.m.Layer, site.m.detector)); return exchange; } if (site.m.hits.get(site.hitID).tracks.contains(this)) { site.m.hits.get(site.hitID).tracks.remove(this); } else { // This should never happen - logger.log(Level.WARNING, String.format("track %d is missing on hit %d track list in layer %d detector %d", + logger.log(Level.WARNING, String.format("track %d is missing on hit %d track list in layer %d detector %d", ID, site.hitID, site.m.Layer, site.m.detector)); } chi2 -= site.chi2inc; nHits--; - reducedChi2 = chi2/(double)nHits; + reducedChi2 = chi2 / (double) nHits; int oldID = site.hitID; site.removeHit(); // Check whether there might be another hit available @@ -1035,83 +1512,117 @@ public boolean removeHit(MeasurementSite site, double mxChi2Inc, double mxTdif) tMax = Math.max(tMax, newHit.time); exchange = true; nHits++; - if (debug) System.out.format("Event %d track %d added hit %d on layer %d detector %d\n", - eventNumber, ID, site.hitID, site.m.Layer, site.m.detector); + if (debug) { + System.out.format("Event %d track %d added hit %d on layer %d detector %d\n", + eventNumber, ID, site.hitID, site.m.Layer, site.m.detector); + } } else { SiteList.remove(site); } return exchange; } - - // Try to add missing hits to the track + + /** + * Try to add missing hits to the track + * + * @param data All the SVT data + * @param mxResid Maximum residual + * @param mxChi2inc Maximum chi^2 increase + * @param mxTdif Maximum time difference of all hits, including the new one + * @param verbose true to spew lots of printout + * @return number of hits added + */ public int addHits(ArrayList data, double mxResid, double mxChi2inc, double mxTdif, boolean verbose) { - int numAdded = 0; + int numAdded = 0; int numLayers = 14; - if (nHits == numLayers) return numAdded; - - if (verbose) logger.setLevel(Level.FINER); - if (debug) System.out.format("addHits, event %d: trying to add hits to track %d\n", eventNumber, ID); - + if (nHits == numLayers) { + return numAdded; + } + + if (verbose) { + logger.setLevel(Level.FINER); + } + if (debug) { + System.out.format("addHits, event %d: trying to add hits to track %d\n", eventNumber, ID); + } + sortSites(true); if (debug) { String str = String.format("KalTrac.addHits: initial list of sites: "); for (MeasurementSite site : SiteList) { - str = str + String.format("(%d, %d, %d) ",site.m.Layer, site.m.detector, site.hitID); + str = str + String.format("(%d, %d, %d) ", site.m.Layer, site.m.detector, site.hitID); } System.out.format("%s\n", str); } - + ArrayList> moduleList = new ArrayList>(numLayers); for (int lyr = 0; lyr < numLayers; lyr++) { ArrayList modules = new ArrayList(); moduleList.add(modules); } for (SiModule thisSi : data) { - if (thisSi.hits.size() > 0) moduleList.get(thisSi.Layer).add(thisSi); + if (thisSi.hits.size() > 0) { + moduleList.get(thisSi.Layer).add(thisSi); + } } - + ArrayList newSites = new ArrayList(); - for (int idx = 0; idx < SiteList.size()-1; idx++) { - MeasurementSite site = SiteList.get(idx); - if (site.hitID < 0) continue; + for (int idx = 0; idx < SiteList.size() - 1; idx++) { + MeasurementSite site = SiteList.get(idx); + if (site.hitID < 0) { + continue; + } int nxtIdx = -1; - for (int jdx = idx+1; jdx < SiteList.size(); jdx++) { + for (int jdx = idx + 1; jdx < SiteList.size(); jdx++) { if (SiteList.get(jdx).hitID >= 0) { nxtIdx = jdx; break; } } - if (nxtIdx < 0) break; + if (nxtIdx < 0) { + break; + } MeasurementSite nxtSite = SiteList.get(nxtIdx); MeasurementSite siteFrom = site; - for (int lyr=site.m.Layer+1; lyr tMax) tMax = hitTime; - else if (hitTime < tMin) tMin = hitTime; + if (hitTime > tMax) { + tMax = hitTime; + } else if (hitTime < tMin) { + tMin = hitTime; + } break; } } } } - } + } } } if (numAdded > 0) { @@ -1123,34 +1634,48 @@ public int addHits(ArrayList data, double mxResid, double mxChi2inc, d break; } } - if (debug) System.out.format("KalTrack.addHits event %d: added hit %d on layer %d detector %d\n", eventNumber, site.hitID, site.m.Layer, site.m.detector); - if (siteToDelete != null) SiteList.remove(siteToDelete); + if (debug) { + System.out.format("KalTrack.addHits event %d: added hit %d on layer %d detector %d\n", eventNumber, site.hitID, site.m.Layer, site.m.detector); + } + if (siteToDelete != null) { + SiteList.remove(siteToDelete); + } SiteList.add(site); } sortSites(true); if (debug) { String str = String.format("KalTrack.addHits: final list of sites: "); for (MeasurementSite site : SiteList) { - str = str + String.format("(%d, %d, %d) ",site.m.Layer, site.m.detector, site.hitID); + str = str + String.format("(%d, %d, %d) ", site.m.Layer, site.m.detector, site.hitID); } System.out.format("%s\n", str); } } else { - if (debug) System.out.format("KalTrack.addHits: no hits added in event %d to track %d\n", eventNumber, ID); + if (debug) { + System.out.format("KalTrack.addHits: no hits added in event %d to track %d\n", eventNumber, ID); + } } return numAdded; } - - // re-fit the track + + /** + * Re-fit the track + * + * @param keep true if there might be another recursion after dropping more hits + * @return true if the refit was successful + */ public boolean fit(boolean keep) { - // keep = true if there might be another recursion after dropping more hits double chi2s = 0.; - if (debug) System.out.format("Entering KalTrack.fit for event %d, track %d\n", eventNumber, ID); + if (debug) { + System.out.format("Entering KalTrack.fit for event %d, track %d\n", eventNumber, ID); + } StateVector sH = SiteList.get(0).aS.copy(); boolean badC = KalmanPatRecHPS.negativeCov(sH.helix.C); if (badC) { - if (debug) System.out.format("KalTrack.fit: negative starting covariance, event %d track %d\n", eventNumber, ID); + if (debug) { + System.out.format("KalTrack.fit: negative starting covariance, event %d track %d\n", eventNumber, ID); + } KalmanPatRecHPS.setInitCov(sH.helix.C, sH.helix.a, false); } else { CommonOps_DDRM.scale(100., sH.helix.C); // Blow up the initial covariance matrix to avoid double counting measurements @@ -1162,42 +1687,56 @@ public boolean fit(boolean keep) { for (int idx = 0; idx < SiteList.size(); idx++) { // Redo all the filter steps MeasurementSite currentSite = SiteList.get(idx); MeasurementSite newSite = new MeasurementSite(currentSite.m.Layer, currentSite.m, kPar); - + boolean allowSharing = false; boolean pickupHits = false; boolean checkBounds = false; - double [] tRange = {-999., 999.}; + double[] tRange = {-999., 999.}; if (newSite.makePrediction(sH, prevMod, currentSite.hitID, allowSharing, pickupHits, checkBounds, tRange, 0) < 0) { - if (debug) System.out.format("KalTrack.fit: event %d, track %d failed to make prediction at layer %d!\n", eventNumber, ID, newSite.m.Layer); + if (debug) { + System.out.format("KalTrack.fit: event %d, track %d failed to make prediction at layer %d!\n", eventNumber, ID, newSite.m.Layer); + } return false; } if (!newSite.filter()) { - if (debug) System.out.format("KalTrack.fit: event %d, track %d failed to filter!\n", eventNumber, ID); + if (debug) { + System.out.format("KalTrack.fit: event %d, track %d failed to filter!\n", eventNumber, ID); + } return false; } if (KalmanPatRecHPS.negativeCov(currentSite.aF.helix.C)) { - if (debug) System.out.format("KalTrack: event %d, ID %d, negative covariance after filtering at layer %d\n", - eventNumber,ID,currentSite.m.Layer); + if (debug) { + System.out.format("KalTrack: event %d, ID %d, negative covariance after filtering at layer %d\n", + eventNumber, ID, currentSite.m.Layer); + } badCov = true; KalmanPatRecHPS.fixCov(currentSite.aF.helix.C, currentSite.aF.helix.a); } if (debug) { - if (newSite.hitID >= 0) chi2f += Math.max(currentSite.chi2inc,0.); + if (newSite.hitID >= 0) { + chi2f += Math.max(currentSite.chi2inc, 0.); + } } newSite.hitID = currentSite.hitID; sH = newSite.aF; - if (debug) System.out.format(" Layer %d hit %d filter, chi^2 increment=%10.5f, a=%s\n", - newSite.m.Layer, newSite.hitID, newSite.chi2inc, newSite.aF.helix.a.toString()); + if (debug) { + System.out.format(" Layer %d hit %d filter, chi^2 increment=%10.5f, a=%s\n", + newSite.m.Layer, newSite.hitID, newSite.chi2inc, newSite.aF.helix.a.toString()); + } prevMod = newSite.m; - if (keep) currentSite.chi2inc = newSite.chi2inc; // Residuals to cut out hits in next recursion + if (keep) { + currentSite.chi2inc = newSite.chi2inc; // Residuals to cut out hits in next recursion + } newSiteList.add(newSite); } if (badCov) { nBadCov[0]++; bad = true; - } - if (debug) System.out.format("KalTrack.fit: Track %d, Fit chi^2 after filtering = %12.4e\n", ID, chi2f); - + } + if (debug) { + System.out.format("KalTrack.fit: Track %d, Fit chi^2 after filtering = %12.4e\n", ID, chi2f); + } + chi2s = 0.; int nNewHits = 0; badCov = false; @@ -1211,18 +1750,20 @@ public boolean fit(boolean keep) { currentSite.smooth(nextSite); } if (currentSite.hitID >= 0) { - chi2s += Math.max(currentSite.chi2inc,0.); + chi2s += Math.max(currentSite.chi2inc, 0.); nNewHits++; } if (KalmanPatRecHPS.negativeCov(currentSite.aS.helix.C)) { - if (debug) System.out.format("KalTrack: event %d, ID %d, negative covariance after smoothing at layer %d\n", - eventNumber,ID,currentSite.m.Layer); + if (debug) { + System.out.format("KalTrack: event %d, ID %d, negative covariance after smoothing at layer %d\n", + eventNumber, ID, currentSite.m.Layer); + } badCov = true; KalmanPatRecHPS.fixCov(currentSite.aS.helix.C, currentSite.aS.helix.a); } if (debug) { - System.out.format(" Layer %d hit %d, smooth, chi^2 increment=%10.5f, a=%s\n", - currentSite.m.Layer, currentSite.hitID, currentSite.chi2inc, currentSite.aS.helix.a.toString()); + System.out.format(" Layer %d hit %d, smooth, chi^2 increment=%10.5f, a=%s\n", + currentSite.m.Layer, currentSite.hitID, currentSite.chi2inc, currentSite.aS.helix.a.toString()); } nextSite = currentSite; } @@ -1230,24 +1771,164 @@ public boolean fit(boolean keep) { nBadCov[1]++; bad = true; } - if (debug) System.out.format("KalTrack.fit: Track %d, Fit chi^2 after smoothing = %12.4e\n", ID, chi2s); + if (debug) { + System.out.format("KalTrack.fit: Track %d, Fit chi^2 after smoothing = %12.4e\n", ID, chi2s); + } if (!keep) { - if (chi2s/(double)nNewHits > reducedChi2*1.2) { - if (debug) System.out.format("KalTrack.fit event %d track %d: fit chisquared=%10.5f is not an improvement. Discard new fit.\n", - eventNumber, ID, chi2s); + if (chi2s / (double) nNewHits > reducedChi2 * 1.2) { + if (debug) { + System.out.format("KalTrack.fit event %d track %d: fit chisquared=%10.5f is not an improvement. Discard new fit.\n", + eventNumber, ID, chi2s); + } return false; } } SiteList = newSiteList; this.chi2 = chi2s; this.nHits = nNewHits; - this.reducedChi2 = chi2s/(double)nNewHits; + this.reducedChi2 = chi2s / (double) nNewHits; propagated = false; - if (debug) System.out.format("Exiting KalTrack.fit for event %d, track %d\n", eventNumber, ID); + if (debug) { + System.out.format("Exiting KalTrack.fit for event %d, track %d\n", eventNumber, ID); + } return true; } - // Derivative matrix for propagating the covariance of the helix parameters to a covariance of momentum + /** + * Starting from the most downstream tracker hit, run a filter to include + * the ECAL energy information (roughly a weighted mean with the SVT + * momentum measurement). Then smooth back to the first layer and propagate + * to the origin. + * + * @param eConstraint true to include the energy constraint + * @param E ECAL energy + * @param sigmaE ECAL energy uncertainty + */ + void smoothIt(boolean eConstraint, double E, double sigmaE) { + // The prediction step from the last tracker site to the ECAL has F=1, + // since we don't alter an helix parameters in the prediction. + // The HelixState.energyConstrained method then uses the Kalman weighted + // means formalism for the filter step to update the helix parameters. + // The smoothing gain matrix for the step to the ECAL is simply unity, + // so the smoothed state vector with energy constraint at the last tracker + // site is exactly what is returned by the energyConstrained method. The + // smoothing back to the first tracker site can then proceed as usual. + //boolean debug = true; + if (debug) System.out.format("Entering KalTrack.smoothIt, eConstraint=%b, E=%f, sigmaE=%f\n", eConstraint, E, sigmaE); + int idxLast = SiteList.size() - 1; + MeasurementSite lastSite = SiteList.get(idxLast); + lastSite.energyConstrained = eConstraint; + if (!eConstraint) { + lastSite.aS = lastSite.aF; + this.chi2 = lastSite.chi2inc; + } else { + if (debug) { + double kappa = lastSite.aF.helix.a.v[2]; + double tanl = lastSite.aF.helix.a.v[4]; + double ePredict = FastMath.sqrt(1.0 + tanl * tanl) / Math.abs(kappa); + System.out.format("KalTrack.smoothIt at last layer, E=%9.4f\n", ePredict); + } + lastSite.aS = new StateVector(lastSite.aF.kLow, lastSite.aF.uniformB); // Blank new state vector + HelixState energyConstrainedHelix = lastSite.aF.helix.energyConstrained(E, sigmaE); + lastSite.aS.helix = energyConstrainedHelix; + lastSite.aS.kUp = lastSite.aF.kUp; + lastSite.aS.F = lastSite.aF.F; // Don't deep copy the F matrix + lastSite.aS.mPred = lastSite.aF.mPred; + lastSite.aS.R = lastSite.aF.R; + lastSite.aS.r = lastSite.aF.r; + lastSite.aS.K = lastSite.aF.K; + lastSite.smoothed = true; + lastSite.chi2inc = (lastSite.aS.r * lastSite.aS.r) / lastSite.aS.R; + + // Get the residual of the prediction at the ECAL + double kappa = energyConstrainedHelix.a.v[2]; + double tanl = energyConstrainedHelix.a.v[4]; + double ePredict = FastMath.sqrt(1.0 + tanl * tanl) / Math.abs(kappa); + double chi = (E - ePredict) / sigmaE; + this.chi2_Econstraint = lastSite.chi2inc + chi * chi; + if (debug) { + System.out.format("KalTrack:smoothIt E=%9.4f, Epredict=%9.4f, sigmaE=%9.4f, chi=%9.4f\n", + E,ePredict,sigmaE,chi); + System.out.format(" constrained helix = %s\n", energyConstrainedHelix.a.toString()); + } + } + lastSite.smoothed = true; + MeasurementSite nS = lastSite; + for (int idx = idxLast - 1; idx >= 0; --idx) { + MeasurementSite thisSite = SiteList.get(idx); + thisSite.smooth(nS); + thisSite.energyConstrained = eConstraint; + thisSite.smoothed = true; + if (eConstraint) { + this.chi2_Econstraint += thisSite.chi2inc; + } else { + this.chi2 += thisSite.chi2inc; + } + if (KalmanPatRecHPS.negativeCov(thisSite.aS.helix.C)) { + if (debug) { + System.out.format("KalTrack: event %d, ID %d, negative covariance after smoothing at layer %d\n", + eventNumber, ID, thisSite.m.Layer); + } + bad = true; + KalmanPatRecHPS.fixCov(thisSite.aS.helix.C, thisSite.aS.helix.a); + } + nS = thisSite; + } + Vec beamSpot = new Vec(3, kPar.beamSpot); + + // This propagated helix will have its pivot at the origin but is in the origin B-field frame + // The StateVector method propagateRungeKutta transforms the origin plane into the origin B-field frame + Plane originPlane = new Plane(beamSpot, new Vec(0., 1., 0.)); + arcLengthE = new double[1]; + helixAtOrigin = SiteList.get(0).aS.helix.propagateRungeKutta(originPlane, yScat, XLscat, SiteList.get(0).m.Bfield, arcLengthE); + energyConstrained = eConstraint; + } + + /** + * Return the best matched MC particle for this track + * @return best match MCParticle + */ + MCParticle getBestMCmatch() { + MCParticle bestMC = null; + ArrayList mcParts = new ArrayList(); + ArrayList mcCnt = new ArrayList(); + for (MeasurementSite site : SiteList) { + StateVector aS = site.aS; + SiModule mod = site.m; + if (aS != null && mod != null) { + if (site.hitID >= 0) { + Measurement hit = mod.hits.get(site.hitID); + if (hit.pMC != null) { + for (MCParticle mcp : hit.pMC) { + if (mcParts.contains(mcp)) { + int id = mcParts.indexOf(mcp); + mcCnt.set(id, mcCnt.get(id)+1); + } else { + mcParts.add(mcp); + mcCnt.add(1); + } + } + } + } + } + } + int maxCnt = 0; + for (int i=0; i maxCnt) { + maxCnt = mcCnt.get(i); + bestMC = mcParts.get(i); + } + } + return bestMC; + } + + /** + * Derivative matrix for propagating the covariance of the helix parameters + * to a covariance of momentum + * + * @param a helix parameters + * @return 2D array of derivatives + */ static double[][] DpTOa(Vec a) { double[][] M = new double[3][5]; double K = Math.abs(a.v[2]); @@ -1261,8 +1942,13 @@ static double[][] DpTOa(Vec a) { return M; } - // Derivative matrix for propagating the covariance of the helix parameter to a - // covariance of the point of closest approach to the origin (i.e. at phi=0) + /** + * Derivative matrix for propagating the covariance of the helix parameter + * to a covariance of the point of closest approach to the origin (i.e. at + * phi=0) + * + * @param a helix parameters + */ static double[][] DxTOa(Vec a) { double[][] M = new double[3][5]; M[0][0] = FastMath.cos(a.v[1]); @@ -1273,46 +1959,84 @@ static double[][] DxTOa(Vec a) { return M; } - // Comparator function for sorting tracks by quality + /** + * Comparator function for sorting tracks by quality + */ static Comparator TkrComparator = new Comparator() { public int compare(KalTrack t1, KalTrack t2) { double penalty1 = 1.0; double penalty2 = 1.0; - if (!t1.SiteList.get(0).aS.helix.goodCov()) penalty1 = 9.9e3; - if (!t2.SiteList.get(0).aS.helix.goodCov()) penalty2 = 9.9e3; + if (!t1.SiteList.get(0).aS.helix.goodCov()) { + penalty1 = 9.9e3; + } + if (!t2.SiteList.get(0).aS.helix.goodCov()) { + penalty2 = 9.9e3; + } - Double chi1 = new Double((penalty1*t1.chi2) / t1.nHits + 300.0*(1.0 - (double)t1.nHits/14.)); - Double chi2 = new Double((penalty2*t2.chi2) / t2.nHits + 300.0*(1.0 - (double)t2.nHits/14.)); + Double chi1 = Double.valueOf((penalty1 * t1.chi2) / t1.nHits + 300.0 * (1.0 - (double) t1.nHits / 14.)); + Double chi2 = Double.valueOf((penalty2 * t2.chi2) / t2.nHits + 300.0 * (1.0 - (double) t2.nHits / 14.)); return chi1.compareTo(chi2); } }; + static double sigmaECAL(double E) { + return FastMath.sqrt(2.62 + (8.24 + 6.25 * E) * E) / 100.; + } + + /** + * Transform a DMatrixRMaj object into a Kalman SquareMatrix object + * + * @param M DMatrixRMaj object + * @return the SquareMatrix equivalent + */ static SquareMatrix mToS(DMatrixRMaj M) { SquareMatrix S = new SquareMatrix(M.numRows); - if (M.numCols != M.numRows) return null; - for (int i=0; i detPlanes; @@ -170,7 +173,8 @@ public void detectorChanged(Detector det) { sensors = det.getSubdetector("Tracker").getDetectorElement().findDescendants(HpsSiSensor.class); KalmanParams kPar = new KalmanParams(); - KI = new KalmanInterface(this.uniformB, kPar, fm); + kPar.setUniformB(uniformB); + KI = new KalmanInterface(kPar, det, fm); KI.createSiModules(detPlanes); System.out.format("KalmanDriver: the B field is assumed uniform? %b\n", uniformB); @@ -193,9 +197,7 @@ public void process(EventHeader event) { System.out.println(trackCollectionName + " does not exist; skipping event"); return; } - int runNumber = event.getRunNumber(); - KI.setRunNumber(runNumber); int evtNumb = event.getEventNumber(); List tracks = event.get(Track.class, trackCollectionName); List outputSeedTracks = new ArrayList(); @@ -260,7 +262,7 @@ public void process(EventHeader event) { if (createSeed) { // Start with the linear helix fit if (verbose) System.out.println("Use a linear helix fit to get a starting guess for the Kalman filter\n"); - SeedTrack seedKalmanTrack = KI.createKalmanSeedTrack(trk, hitToStrips, hitToRotated); + SeedTrack seedKalmanTrack = KI.createKalmanSeedTrack(event, trk, hitToStrips, hitToRotated); if (verbose) { System.out.println("\nPrinting info for Kalman SeedTrack:"); seedKalmanTrack.print("testKalmanTrack"); @@ -277,7 +279,7 @@ public void process(EventHeader event) { outputSeedTracks.add(HPStrk); //full track - ktf2 = KI.createKalmanTrackFit(evtNumb, seedKalmanTrack, trk, hitToStrips, hitToRotated, 2); + ktf2 = KI.createKalmanTrackFit(event, seedKalmanTrack, trk, hitToStrips, hitToRotated, 2); if (!ktf2.success) { KI.clearInterface(); continue; @@ -343,7 +345,7 @@ public void process(EventHeader event) { cov.print("GBL covariance for starting Kalman fit"); } //full track - ktf2 = KI.createKalmanTrackFit(evtNumb, kalParams, newPivot, cov, trk, hitToStrips, hitToRotated, 2); + ktf2 = KI.createKalmanTrackFit(event, kalParams, newPivot, cov, trk, hitToStrips, hitToRotated, 2); if (!ktf2.success) { KI.clearInterface(); continue; @@ -392,7 +394,7 @@ public void process(EventHeader event) { } if (fullKalmanTrack != null) { - Track fullKalmanTrackHPS = KI.createTrack(fullKalmanTrack, true); + Track fullKalmanTrackHPS = KI.createTrack(fullKalmanTrack, true, event); outputFullTracks.add(fullKalmanTrackHPS); // Fill histograms here diff --git a/tracking/src/main/java/org/hps/recon/tracking/kalman/KalmanInterface.java b/tracking/src/main/java/org/hps/recon/tracking/kalman/KalmanInterface.java index 2cb675c58c..9d6e9ca054 100644 --- a/tracking/src/main/java/org/hps/recon/tracking/kalman/KalmanInterface.java +++ b/tracking/src/main/java/org/hps/recon/tracking/kalman/KalmanInterface.java @@ -8,6 +8,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Random; @@ -48,20 +49,25 @@ import org.lcsim.recon.tracking.digitization.sisim.TrackerHitType; /** - * This class provides an interface between hps-java and the Kalman Filter fitting and pattern recognition code. - * It can be used to refit the hits on an existing hps track, or it can be used to drive the pattern recognition. - * However, both cannot be done at the same time. The interface must be reset between doing one and the other. + * This class provides an interface between hps-java and the Kalman Filter + * fitting and pattern recognition code. It can be used to refit the hits on an + * existing hps track, or it can be used to drive the pattern recognition. + * However, both cannot be done at the same time. The interface must be reset + * between doing one and the other and should also be reset after each event, + * before going to the next event. */ public class KalmanInterface { + private Detector det; - private List sensors; private Map hitMap; private Map simHitMap; private Map moduleMap; + private Map> layerMap; private ArrayList trackHitsKalman; private ArrayList SiMlist; private List SeedTrackLayers = null; private int _siHitsLimit = -1; + private static boolean uniformB; double alphaCenter; private List detPlanes; double svtAngle; @@ -73,99 +79,167 @@ public class KalmanInterface { public static RotMatrix HpsSvtToKalman; public static RotMatrix KalmanToHpsSvt; public static BasicHep3Matrix HpsSvtToKalmanMatrix; - private static boolean uniformB; private static DMatrixRMaj tempM; private static DMatrixRMaj Ft; + private static DMatrixRMaj helixCov; private int maxHits; private int nBigEvents; private int eventNumber; - - private static final boolean debug = false; - private static final double SVTcenter = 505.57; + private static Vec centerB; + private IDDecoder decoder; + private HelixPlaneIntersect hpi; + + private static final int mxLyrs = 14; + private static final boolean debug = false; private static final double c = 2.99793e8; // Speed of light in m/s - - private int runNumber = 14168; - - public void setRunNumber(int runNumber){ - this.runNumber = runNumber; - } - + + + /** + * Set the limit on the maximum number of hits in an event to analyze + * (larger events will be skipped) + * + * @param limit desired hit limit + */ + public void setSiHitsLimit(int limit) { _siHitsLimit = limit; } - + + /** + * Get the current setting on the maximum number of hits to analyze + * + * @return hit limit + */ public int getSiHitsLimit() { return _siHitsLimit; } - - // Get the HPS tracker hit corresponding to a Kalman hit + + /** + * Get the HPS tracker hit corresponding to a Kalman hit + * + * @param km Kalman Measurement object + * @return identity of the HPS hit + */ public TrackerHit getHpsHit(Measurement km) { return hitMap.get(km); } - - // Get the HPS sensor that corresponds to a Kalman SiModule + + /** + * Get the HPS sensor that corresponds to a Kalman SiModule + * + * @param kalmanSiMod Kalman SiModule object (geometry of one sensor) + * @return HpsSiSensor object + */ public HpsSiSensor getHpsSensor(SiModule kalmanSiMod) { - if (moduleMap == null) return null; - else { + if (moduleMap == null) { + return null; + } else { SiStripPlane temp = moduleMap.get(kalmanSiMod); - if (temp == null) return null; - else return (HpsSiSensor) (temp.getSensor()); + if (temp == null) { + return null; + } else { + return (HpsSiSensor) (temp.getSensor()); + } } } - - // Return the entire map relating HPS sensors to Kalman SiModule objects + + /** + * Get the entire map relating HPS sensors to Kalman SiModule objects + * + * @return Map from HPS sensors to Kalman SiModule objects + */ public Map getModuleMap() { return moduleMap; } - // The HPS field map is in the HPS global coordinate system. This routine includes the transformations - // to return the field in the Kalman global coordinate system given a coordinate in the same system. + /** + * Get the HPS B field at a provided location. The HPS field map is in the + * HPS global coordinate system. This routine includes the transformations + * to return the field in the Kalman global coordinate system given a + * coordinate in the same system. Don't confuse this with + * org.lcsim.geometry.FieldMap.getField, which works with double arrays in + * HPS coordinates. + * + * @param kalPos Kalman 3-vector object specifying the position + * @param hpsFm HPS field map + * @return Kalman 3-vector object specifying the magnetic field at the given + * location + */ static Vec getField(Vec kalPos, org.lcsim.geometry.FieldMap hpsFm) { return new Vec(3, getFielD(kalPos, hpsFm)); } - - static double [] getFielD(Vec kalPos, org.lcsim.geometry.FieldMap hpsFm) { + + /** + * Get the HPS B field at a provided location for stand-alone running of the + * Kalman code. For hps-java use KalmanInterface.getField to return a + * 3-vector Vec object. + * + * @param kalPos Kalman 3-vector object specifying the position + * @param hpsFm HPS field map + * @return Kalman 3-vector object specifying the magnetic field at the given + * location + */ + static double[] getFielD(Vec kalPos, org.lcsim.geometry.FieldMap hpsFm) { // Field map for stand-alone running - if (FieldMap.class.isInstance(hpsFm)) return ((FieldMap) (hpsFm)).getField(kalPos); + if (FieldMap.class.isInstance(hpsFm)) { + return ((FieldMap) (hpsFm)).getField(kalPos); + } - // Standard field map for running in hps-java + // Interface to the standard field map for running in hps-java, including conversions for Kalman coordinates //System.out.format("Accessing HPS field map for position %8.3f %8.3f %8.3f\n", kalPos.v[0], kalPos.v[1], kalPos.v[2]); - double[] hpsPos = { kalPos.v[0], -1.0 * kalPos.v[2], kalPos.v[1] }; - if (uniformB) { - hpsPos[0] = 0.; - hpsPos[1] = 0.; - hpsPos[2] = SVTcenter; - } else { - if (hpsPos[1] > 70.0) hpsPos[1] = 70.0; // To avoid getting a field returned that is identically equal to zero - if (hpsPos[1] < -70.0) hpsPos[1] = -70.0; - if (hpsPos[0] < -225.) hpsPos[0] = -225.; - if (hpsPos[0] > 270.) hpsPos[0] = 270.; + double[] hpsPos = {kalPos.v[0], -1.0 * kalPos.v[2], kalPos.v[1]}; + //hpsPos[0] = 0.; + //hpsPos[1] = 0.; + //hpsPos[2] = 505.57; + + // To avoid going off the map and getting a field returned that is identically equal to zero + if (hpsPos[1] > 70.0) { + hpsPos[1] = 70.0; } - double[] hpsField = hpsFm.getField(hpsPos); - if (uniformB) { - double [] kalField = {0., 0., -1.0 * hpsField[1]}; - return kalField; + if (hpsPos[1] < -70.0) { + hpsPos[1] = -70.0; } - double [] kalField = {hpsField[0], hpsField[2], -1.0 * hpsField[1]}; + if (hpsPos[0] < -225.) { + hpsPos[0] = -225.; + } + if (hpsPos[0] > 270.) { + hpsPos[0] = 270.; + } + double[] hpsField = hpsFm.getField(hpsPos); + double[] kalField = {hpsField[0], hpsField[2], -1.0 * hpsField[1]}; return kalField; } - // Set the layers to be used for finding seed tracks (not used by Kalman pattern recognition) + /** + * Set the layers to be used for finding seed tracks (not used by Kalman + * pattern recognition) + * + * @param input List of layers + */ public void setSeedTrackLayers(List input) { SeedTrackLayers = input; } - // Constructor with no uniformB argument defaults to non-uniform field - public KalmanInterface(KalmanParams kPar, org.lcsim.geometry.FieldMap fM) { - this(false, kPar, fM); + /** + * alternate constructor for backward compatibility + */ + public KalmanInterface(boolean uniformB, KalmanParams kPar, Detector det, org.lcsim.geometry.FieldMap fM) { + this(kPar, det, fM); } - public KalmanInterface(boolean uniformB, KalmanParams kPar, org.lcsim.geometry.FieldMap fM) { - - this.det = det; - this.sensors = sensors; + /** + * Constructor for the interface between the Kalman-filter tracking code and + * other HPS-Java code + * + * @param kPar Object containing all the control parameters for the Kalman + * code + * @param fM The HPS field map + */ + public KalmanInterface(KalmanParams kPar, Detector det, org.lcsim.geometry.FieldMap fM) { + this.fM = fM; this.kPar = kPar; + this.decoder = det.getSubdetector("Tracker").getIDDecoder(); logger = Logger.getLogger(KalmanInterface.class.getName()); logger.info("Entering the KalmanInterface constructor"); maxHits = 0; @@ -173,75 +247,102 @@ public KalmanInterface(boolean uniformB, KalmanParams kPar, org.lcsim.geometry.F tempM = new DMatrixRMaj(5,5); Ft = new DMatrixRMaj(5,5); + helixCov = null; - KalmanInterface.uniformB = uniformB; hitMap = new HashMap(); simHitMap = new HashMap(); moduleMap = new HashMap(); + layerMap = new HashMap>(); trackHitsKalman = new ArrayList(); // Used only to refit existing GBL tracks SiMlist = new ArrayList(); SeedTrackLayers = new ArrayList(); - // SeedTrackLayers.add(2); - SeedTrackLayers.add(3); - SeedTrackLayers.add(4); - SeedTrackLayers.add(5); + //SeedTrackLayers.add(2); + //SeedTrackLayers.add(3); + //SeedTrackLayers.add(4); + //SeedTrackLayers.add(5); + hpi = new HelixPlaneIntersect(); - if (uniformB) { + if (kPar.uniformB) { logger.log(Level.WARNING, "KalmanInterface WARNING: the magnetic field is set to a uniform value."); } - + uniformB = kPar.uniformB; + // Transformation from HPS SVT tracking coordinates to Kalman global coordinates - double[][] HpsSvtToKalmanVals = { { 0, 1, 0 }, { 1, 0, 0 }, { 0, 0, -1 } }; + double[][] HpsSvtToKalmanVals = {{0, 1, 0}, {1, 0, 0}, {0, 0, -1}}; HpsSvtToKalman = new RotMatrix(HpsSvtToKalmanVals); HpsSvtToKalmanMatrix = new BasicHep3Matrix(); for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) + for (int j = 0; j < 3; j++) { HpsSvtToKalmanMatrix.setElement(i, j, HpsSvtToKalmanVals[i][j]); + } } KalmanToHpsSvt = HpsSvtToKalman.invert(); if (debug) { HpsSvtToKalman.print("HPS tracking to Kalman conversion"); KalmanToHpsSvt.print("Kalman to HPS tracking conversion"); - Vec zHPS = new Vec(0.,0.,1.); - Vec xHPS = new Vec(1.,0.,0.); - Vec yHPS = new Vec(0.,1.,0.); + Vec zHPS = new Vec(0., 0., 1.); + Vec xHPS = new Vec(1., 0., 0.); + Vec yHPS = new Vec(0., 1., 0.); HpsSvtToKalman.rotate(xHPS).print("HPS tracking x axis in Kalman coordinates"); HpsSvtToKalman.rotate(yHPS).print("HPS tracking y axis in Kalman coordinates"); HpsSvtToKalman.rotate(zHPS).print("HPS tracking z axis in Kalman coordinates"); } - + // Seed the random number generator long rndSeed = -3263009337738135404L; rnd = new Random(); rnd.setSeed(rndSeed); - + kPat = new KalmanPatRecHPS(kPar); - - Vec centerB = KalmanInterface.getField(new Vec(0., SVTcenter, 0.), fM); + + centerB = KalmanInterface.getField(new Vec(0., kPar.SVTcenter, 0.), fM); + logger.log(Level.INFO, String.format("The magnetic field at the detector center (%10.5f mm) is %10.5f\n", kPar.SVTcenter, centerB.mag())); double conFac = 1.0e12 / c; - alphaCenter = conFac/ centerB.mag(); + alphaCenter = conFac / centerB.mag(); } - + + /** + * End-of-job formatted printout of some counters. + */ public void summary() { - System.out.format("KalmanInterface::summary: number of events with > 200 hits=%d.\n", nBigEvents); + System.out.format("KalmanInterface::summary: number of events with > 200 hits = %d.\n", nBigEvents); System.out.format(" Maximum event size = %d strip hits.\n", maxHits); - System.out.format(" Events with > %d hits were not processed.\n", _siHitsLimit); - System.out.format(" Number of tracks with bad covariance in filterTrack= %d %d\n", KalmanPatRecHPS.nBadCov[0], KalmanPatRecHPS.nBadCov[1]); - System.out.format(" Number of tracks with bad covariance in KalTrack.fit=%d %d\n", KalTrack.nBadCov[0], KalTrack.nBadCov[1]); + if (_siHitsLimit > 0) { + System.out.format(" Events with > %d hits were not processed.\n", _siHitsLimit); + } + System.out.format(" Number of tracks with bad covariance in filterTrack= %d is %d\n", KalmanPatRecHPS.nBadCov[0], KalmanPatRecHPS.nBadCov[1]); + System.out.format(" Number of tracks with bad covariance in KalTrack.fit=%d is %d\n", KalTrack.nBadCov[0], KalTrack.nBadCov[1]); } - // Return the reference to the parameter setting code for the driver to use + /** + * Return the reference to the parameter setting object for the driver to + * use + * + * @return reference to the parameter setting object KalParams.java + */ public KalmanParams getKalmanParams() { return kPar; } - - // Transformation from HPS global coordinates to Kalman global coordinates - public static Vec vectorGlbToKalman(double[] HPSvec) { + + /** + * Transformation from HPS global coordinates to Kalman global coordinates + * + * @param HPSvec 3-vector array of HPS global coordinates + * @return 3-vector object of Kalman global coordinates + * @see Definitions of coordinate systems in the Kalman pdf documentation. + */ + public static Vec vectorGlbToKalman(double[] HPSvec) { Vec kalVec = new Vec(HPSvec[0], HPSvec[2], -HPSvec[1]); return kalVec; } - - // Transformation from Kalman global coordinates to HPS global coordinates + + /** + * Transformation from Kalman global coordinates to HPS global coordinates + * + * @param KalVec 3-vector object of Kalman global coordinates + * @return 3-vector array of HPS global coordinates + * @see Definitions of coordinate systems in the Kalman pdf documentation. + */ public static double[] vectorKalmanToGlb(Vec KalVec) { double[] HPSvec = new double[3]; HPSvec[0] = KalVec.v[0]; @@ -249,8 +350,14 @@ public static double[] vectorKalmanToGlb(Vec KalVec) { HPSvec[2] = KalVec.v[1]; return HPSvec; } - - // Transformation from Kalman global coordinates to HPS tracking coordinates + + /** + * Transformation from Kalman global coordinates to HPS tracking coordinates + * + * @param KalVec Kalman 3-vector object in Kalman global coordinates + * @return HPS tracking coordinates a 3-vector array + * @see Definitions of coordinate systems in the Kalman pdf documentation. + */ public static double[] vectorKalmanToTrk(Vec KalVec) { double[] HPSvec = new double[3]; HPSvec[0] = KalVec.v[1]; @@ -258,20 +365,38 @@ public static double[] vectorKalmanToTrk(Vec KalVec) { HPSvec[2] = -KalVec.v[2]; return HPSvec; } - - // Transformation from HPS tracking coordinates to Kalman global coordinates - public static Vec vectorTrkToKalman(double[] HPSvec) { + + /** + * Transformation from HPS tracking coordinates to Kalman global coordinates + * + * @param HPSvec HPS tracking coordinates a 3-vector array + * @return Kalman 3-vector object in Kalman global coordinates + * @see Definitions of coordinate systems in the Kalman pdf documentation. + */ + public static Vec vectorTrkToKalman(double[] HPSvec) { Vec kalVec = new Vec(HPSvec[1], HPSvec[0], -HPSvec[2]); return kalVec; } - - // Transformation from HPS sensor coordinates to Kalman sensor coordinates - public static Vec localHpsToKal(double[] HPSvec) { + + /** + * Transformation from HPS sensor coordinates to Kalman sensor coordinates + * + * @param HPSvec HPS 3-vector sensor coordinate double-precision array + * @return Kalman 3-vector object in Kalman sensor coordinates + * @see Definitions of coordinate systems in the Kalman pdf documentation. + */ + public static Vec localHpsToKal(double[] HPSvec) { Vec kalVec = new Vec(-HPSvec[1], HPSvec[0], HPSvec[2]); return kalVec; } - - // Transformation from Kalman sensor coordinates to HPS sensor coordinates + + /** + * Transformation from Kalman sensor coordinates to HPS sensor coordinates + * + * @param KalVec 3-vector object in Kalman coordinates + * @return 3-vector double-precision array in HPS coordinates + * @see Definitions of coordinate systems in the Kalman pdf documentation. + */ public static double[] localKalToHps(Vec KalVec) { double[] HPSvec = new double[3]; HPSvec[0] = KalVec.v[1]; @@ -280,12 +405,21 @@ public static double[] localKalToHps(Vec KalVec) { return HPSvec; } - // Return the entire list of Kalman SiModule + /** + * Return the entire list of Kalman SiModule (geometry description for the + * Kalman filter code) + * + * @return List of all SiModule objects + */ public ArrayList getSiModuleList() { return SiMlist; } - // Return a list of all measurements for all SiModule + /** + * Return a list of all measurements for all SiModule + * + * @return List of all Measurement objects in the detector + */ public ArrayList getMeasurements() { ArrayList measList = new ArrayList();; for (SiModule SiM : SiMlist) { @@ -296,7 +430,10 @@ public ArrayList getMeasurements() { return measList; } - // Clear the event hit and track information without deleting the SiModule geometry information + /** + * Clear the Kalman interface event hit and track information without + * deleting the SiModule geometry information + */ public void clearInterface() { logger.fine("Clearing the Kalman interface"); hitMap.clear(); @@ -307,102 +444,228 @@ public void clearInterface() { } } - // Create an HPS TrackState from a Kalman HelixState at the location of a particular SiModule - public TrackState createTrackState(MeasurementSite ms, int loc, boolean useSmoothed) { - // Note that the helix parameters that get stored in the TrackState assume a B-field exactly oriented in the - // z direction and a pivot point at the origin (0,0,0). The referencePoint of the TrackState is set to the - // intersection point with the detector plane. - StateVector sv = null; - if (useSmoothed) { - if (!ms.smoothed) return null; - sv = ms.aS; - } else { // using the filtered state is really not recommended - if (!ms.filtered) return null; - sv = ms.aF; - } - - return sv.helix.toTrackState(alphaCenter, ms.m.p, loc); + /** + * Find the intersection of a track with a detector plane. All of the input + * and output are assumed to be in HPS coordinates (not Kalman coordinates). + * Memory must be provided for xLoc and pAtPlane, the charge must be -1 or + * +1, and the axis unit vectors uGlb, vGlb, tGlb must be normalized and + * right-handed. + * + * @param x 3-vector coordinate on the track + * @param p 3-vector momentum at the point x + * @param Q particle charge (-1.0 or +1.0) + * @param pointOnPlane 3-vector point on the detector plane + * @param uGlb 3-vector direction cosines of the u-axis of the detector + * plane (perp to strips) + * @param vGlb 3-vector direction cosines of the v-axis of the detector + * plane (parallel to strips) + * @param tGlb 3-vector direction cosines of the t-axis of the detector + * plane (perp to plane) + * @param fM HPS field map + * @param xLocal return 3-vector intersection in detector coordinates (u, v, + * t) + * @param pAtPlane return 3-vector momentum at the intersection, in global + * coordinates + * @return 3-vector point of intersection on the detector plane in global + * coordinates + */ + public static double[] hpsHelixIntersect(double[] x, double[] p, double Q, double[] pointOnPlane, double[] uGlb, double[] vGlb, double[] tGlb, + org.lcsim.geometry.FieldMap fM, double[] xLocal, double[] pAtPlane) { + + // Transform the input information into Kalman coordinates + Vec uK = (vectorGlbToKalman(vGlb).scale(-1.0)); // u and v are reversed in hps compared to kalman + Vec vK = vectorGlbToKalman(uGlb); + Vec tK = vectorGlbToKalman(tGlb); + Vec pointOnPlaneTransformed = vectorGlbToKalman(pointOnPlane); + Vec xK = vectorGlbToKalman(x); + Vec pK = vectorGlbToKalman(p); + + // Define the detector plane (in Kalman coordinates) + Plane detPln = new Plane(pointOnPlaneTransformed, tK, uK, vK); + + // Integrate to find the intersection with the detector plane + Vec pInt = new Vec(3); + HelixPlaneIntersect hpi = new HelixPlaneIntersect(); + Vec intercept = hpi.rkIntersect(detPln, xK, pK, Q, fM, pInt); + + // Convert the results back to HPS coordinates + double[] interceptPos = vectorKalmanToGlb(intercept); + pAtPlane = vectorKalmanToGlb(pInt); + + // Transform the intersection to local coordinates in the Kalman sensor system + // and transform the result to HPS local coordinates. + RotMatrix R = new RotMatrix(detPln.U(), detPln.V(), detPln.T()); + Vec xLocK = R.rotate(intercept.dif(detPln.X())); + xLocal = localKalToHps(xLocK); + + return interceptPos; } - // Transform a Kalman helix to an HPS helix rotated to the global frame and with the pivot at the origin - // Provide covHPS with 15 elements to get the covariance as well - // Provide 3-vector position to get the location in HPS global coordinates of the original helix pivot - static double [] toHPShelix(HelixState helixState, Plane pln, double alphaCenter, double [] covHPS, double[] position) { - final boolean debug = false; + /** + * Transform a Kalman helix to an HPS helix rotated to the global frame and + * with the pivot at the origin The calling routine must provide empty + * covHPS[15] and position[3] arrays. No rotations are needed if the field + * is assumed to be uniform. + * + * @param helixState description of the Kalman helix + * @param pln Kalman plane object (origin point and t,u,v axis directions) + * @param alphaCenter alpha value to use to convert between momentum and + * curvature + * @param covHPS return array of LCSIM helix parameter covariance (15 values + * for the symmetric matrix) + * @param position return 3-vector array of the location in HPS global + * coordinates of the original helix pivot + * @return array of 5 LCSIM helix parameters + */ + static double[] toHPShelix(HelixState helixState, Plane pln, double alphaCenter, double[] covHPS, double[] position) { + Vec finalHelixParams = null; + Vec pivotGlobal = null; + DMatrixRMaj F = new DMatrixRMaj(5, 5); + DMatrixRMaj covRotated = new DMatrixRMaj(5, 5); double phiInt = helixState.planeIntersect(pln); if (Double.isNaN(phiInt)) { Logger logger = Logger.getLogger(KalmanInterface.class.getName()); - logger.fine(String.format("toHPShelix: no intersection with the plane at %s",pln.toString())); + logger.fine(String.format("toHPShelix: no intersection with the plane at %s", pln.toString())); phiInt = 0.; } - // Transforms helix to a pivot point on the helix itself (so rho0 and z0 become zero) Vec newPivot = helixState.atPhi(phiInt); - Vec helixParamsPivoted = helixState.pivotTransform(newPivot); - DMatrixRMaj F = new DMatrixRMaj(5,5); - helixState.makeF(helixParamsPivoted, F); - if (debug) { - System.out.format("Entering KalmanInterface.toHPShelix"); - helixState.print("provided"); - pln.print("provided"); - newPivot.print("new pivot"); - helixParamsPivoted.print("pivoted helix params"); - System.out.format("turning angle to the plane containing the helixState origin=%10.6f\n", phiInt); - Vec intGlb = helixState.toGlobal(newPivot); - intGlb.print("global intersection with plane"); - } - - // Then rotate the helix to the global system. This isn't quite kosher, since the B-field will not - // be aligned with the global system in general, but we have to do it to fit back into the HPS TrackState - // coordinate convention, for which the field is assumed to be uniform and aligned. - DMatrixRMaj fRot = new DMatrixRMaj(5,5); - Vec helixParamsRotated = HelixState.rotateHelix(helixParamsPivoted, helixState.Rot.invert(), fRot); - CommonOps_DDRM.mult(fRot, F, Ft); - - CommonOps_DDRM.multTransB(helixState.C, Ft, tempM); - DMatrixRMaj covRotated = new DMatrixRMaj(5,5); - CommonOps_DDRM.mult(Ft, tempM, covRotated); - if (debug) helixParamsRotated.print("rotated helix params"); - - // Transform the pivot to the global system. - Vec pivotGlobal = helixState.toGlobal(newPivot); - if (debug) pivotGlobal.print("pivot in global system"); - - // Pivot transform to the final pivot at the origin - Vec finalPivot = new Vec(0.,0.,0.); - Vec finalHelixParams = HelixState.pivotTransform(finalPivot, helixParamsRotated, pivotGlobal, alphaCenter, 0.); - HelixState.makeF(finalHelixParams, F, helixParamsRotated, alphaCenter); - CommonOps_DDRM.multTransB(covRotated, F, tempM); - CommonOps_DDRM.mult(F, tempM, covRotated); - if (debug) { - finalPivot.print("final pivot point"); - finalHelixParams.print("final helix parameters"); - HelixPlaneIntersect hpi = new HelixPlaneIntersect(); - phiInt = hpi.planeIntersect(finalHelixParams, finalPivot, alphaCenter, pln); - if (!Double.isNaN(phiInt)) { - Vec rInt = HelixState.atPhi(finalPivot, finalHelixParams, phiInt, alphaCenter); - rInt.print("final helix intersection with given plane"); + Vec finalPivot = new Vec(0., 0., 0.); + if (!uniformB) { + // Transform helix to a pivot point on the helix itself (so rho0 and z0 become zero) + Vec helixParamsPivoted = helixState.pivotTransform(newPivot); + helixState.makeF(helixParamsPivoted, F, 1.0); + if (debug) { + System.out.format("Entering KalmanInterface.toHPShelix"); + helixState.print("provided"); + pln.print("provided"); + newPivot.print("new pivot"); + helixParamsPivoted.print("pivoted helix params"); + System.out.format("turning angle to the plane containing the helixState origin=%10.6f\n", phiInt); + Vec intGlb = helixState.toGlobal(newPivot); + intGlb.print("global intersection with plane"); + } + + // Then rotate the helix to the global system. This isn't quite kosher, since the B-field will not + // be aligned with the global system in general, but we have to do it to fit back into the HPS TrackState + // coordinate convention, for which the field is assumed to be uniform and aligned. + DMatrixRMaj fRot = new DMatrixRMaj(5, 5); + Vec helixParamsRotated = HelixState.rotateHelix(helixParamsPivoted, helixState.Rot.invert(), fRot); + CommonOps_DDRM.mult(fRot, F, Ft); + + CommonOps_DDRM.multTransB(helixState.C, Ft, tempM); + CommonOps_DDRM.mult(Ft, tempM, covRotated); + if (debug) { + helixParamsRotated.print("rotated helix params"); + } + + // Transform the pivot to the global system. + pivotGlobal = helixState.toGlobal(newPivot); + if (debug) { + pivotGlobal.print("pivot in global system"); + } + + // Pivot transform to the final pivot at the origin + finalHelixParams = HelixState.pivotTransform(finalPivot, helixParamsRotated, pivotGlobal, alphaCenter, 0.); + HelixState.makeF(finalHelixParams, F, helixParamsRotated, alphaCenter, 1.0); + CommonOps_DDRM.multTransB(covRotated, F, tempM); + CommonOps_DDRM.mult(F, tempM, covRotated); + if (debug) { + finalPivot.print("final pivot point"); + finalHelixParams.print("final helix parameters"); + HelixPlaneIntersect hpi = new HelixPlaneIntersect(); + phiInt = hpi.planeIntersect(finalHelixParams, finalPivot, alphaCenter, pln); + if (!Double.isNaN(phiInt)) { + Vec rInt = HelixState.atPhi(finalPivot, finalHelixParams, phiInt, alphaCenter); + rInt.print("final helix intersection with given plane"); + } + System.out.format("Exiting KalmanInterface.toHPShelix\n"); } - System.out.format("Exiting KalmanInterface.toHPShelix\n"); + } else { // For a uniform field, all we have to do is a pivot transform to the origin + pivotGlobal = newPivot; // Intersection with the provided plane + finalHelixParams = helixState.pivotTransform(finalPivot); + helixState.makeF(finalHelixParams, F, 1.0); + CommonOps_DDRM.multTransB(helixState.C, F, tempM); + CommonOps_DDRM.mult(F, tempM, covRotated); } if (covHPS != null) { - double [] temp = KalmanInterface.getLCSimCov(covRotated, alphaCenter).asPackedArray(true); - for (int i=0; i<15; ++i) covHPS[i] = temp[i]; + double[] temp = KalmanInterface.getLCSimCov(covRotated, alphaCenter).asPackedArray(true); + for (int i = 0; i < 15; ++i) { + covHPS[i] = temp[i]; + } } if (position != null) { - double [] temp = KalmanInterface.vectorKalmanToGlb(pivotGlobal); - for (int i=0; i<3; ++i) position[i] = temp[i]; + double[] temp = KalmanInterface.vectorKalmanToGlb(pivotGlobal); + for (int i = 0; i < 3; ++i) { + position[i] = temp[i]; + } } return KalmanInterface.getLCSimParams(finalHelixParams.v, alphaCenter); } - + + /** + * Transform a Kalman HelixState to an HPS TrackState. + * + * @param helixState The Kalman HelixState object. + * @param pln The Kalman Plane object (origin point and t,u,v axis + * directions) + * @param alphaCenter The alpha value used to calculate momentum from + * curvature. + * @param loc Integer representation of the TrackState location (AtIP, + * AtFirstHit, AtLastHit, AtCalorimeter, AtVertex, AtOther) + * @return The HPS TrackState + */ static TrackState toTrackState(HelixState helixState, Plane pln, double alphaCenter, int loc) { - double [] covHPS = new double[15]; - double [] position = new double[3]; - double [] helixHPS = KalmanInterface.toHPShelix(helixState, pln, alphaCenter, covHPS, position); - - return new BaseTrackState( helixHPS, covHPS, position, loc); + double[] covHPS = new double[15]; + double[] position = new double[3]; + double[] helixHPS = KalmanInterface.toHPShelix(helixState, pln, alphaCenter, covHPS, position); + + TrackState newState = new BaseTrackState(helixHPS, covHPS, position, loc); + + ((BaseTrackState) newState).computeMomentum(centerB.mag()); + return newState; } - + + /** + * Create an HPS TrackState from a Kalman HelixState at the location of a particular SiModule + * + * @param ms Kalman MeasurementSite object + * @param loc Integer representation of the TrackState location (AtIP, + * AtFirstHit, AtLastHit, AtCalorimeter, AtVertex, AtOther) + * @param useSmoothed Choose between the smoothed vs filtered Kalman helix + * @return The HPS TrackState + */ + public TrackState createTrackState(MeasurementSite ms, int loc, boolean useSmoothed) { + // Note that the helix parameters that get stored in the TrackState assume a B-field exactly oriented in the + // z direction and a pivot point at the origin (0,0,0). The referencePoint of the TrackState is set to the + // intersection point with the detector plane. + //boolean debug = true; + StateVector sv = null; + if (useSmoothed) { + if (!ms.smoothed) { + return null; + } + sv = ms.aS; + } else { // using the filtered state is really not recommended + if (!ms.filtered) { + return null; + } + sv = ms.aF; + } + if (debug) System.out.format("createTrackState: created TrackState at layer %d location %d\n",ms.m.Layer,loc); + TrackState newTrackState = sv.helix.toTrackState(alphaCenter, ms.m.p, loc); + if (debug) { + double [] p= newTrackState.getMomentum(); + double pmag = Math.sqrt(p[0]*p[0]+p[1]*p[1]+p[2]*p[2]); + System.out.format(" new trackstate momentum = %10.5f\n", pmag); + } + return newTrackState; + } + + /** + * Formatted debug printout of a single GBLStripClusterData object. + * + * @param clstr The GBLStripClusterData object to be printed + */ public void printGBLStripClusterData(GBLStripClusterData clstr) { System.out.format("\nKalmanInterface.printGBLStripClusterData: cluster ID=%d, scatterOnly=%d\n", clstr.getId(), clstr.getScatterOnly()); Pair IDdecode = TrackUtils.getLayerSide(clstr.getVolume(), clstr.getId()); @@ -417,76 +680,85 @@ public void printGBLStripClusterData(GBLStripClusterData clstr) { System.out.format(" Track intercept in sensor frame = %s\n", clstr.getTrackPos().toString()); System.out.format(" RMS projected scattering angle=%10.6f\n", clstr.getScatterAngle()); } - - // Make a GBLStripClusterData object for each MeasurementSite of a Kalman track + + /** + * Make a GBLStripClusterData object for each MeasurementSite of a Kalman + * track. This can be used to refit the track using the GBL program. + * + * @param kT The Kalman track + * @return List of strip-cluster data for this Kalman track + */ public List createGBLStripClusterData(KalTrack kT) { List rtnList = new ArrayList(kT.SiteList.size()); - //Arc length from origin to first plane - - double phi_org = kT.originHelixParms()[1]; - double phi_1state = kT.SiteList.get(0).aS.helix.a.v[1]; - double alpha = kT.helixAtOrigin.alpha; - double radius = Math.abs(alpha/kT.originHelixParms()[2]); - double arcLength2D = radius*(phi_1state-phi_org); - double arcLength = arcLength2D*Math.sqrt(1.0 + kT.originHelixParms()[4] * kT.originHelixParms()[4]); + double phi_org = kT.originHelixParms()[1]; + double phi_1state = kT.SiteList.get(0).aS.helix.a.v[1]; + double alpha = kT.helixAtOrigin.alpha; + double radius = Math.abs(alpha / kT.originHelixParms()[2]); + double arcLength2D = radius * (phi_1state - phi_org); + double arcLength = arcLength2D * Math.sqrt(1.0 + kT.originHelixParms()[4] * kT.originHelixParms()[4]); double total_s3D = arcLength; double total_s2D = arcLength2D; - + double phiLast = 9999.; for (MeasurementSite site : kT.SiteList) { GBLStripClusterData clstr = new GBLStripClusterData(site.m.millipedeID); clstr.setVolume(site.m.topBottom); - + // Sites without hits are "scatter-only" - if (site.hitID < 0) clstr.setScatterOnly(1); - else clstr.setScatterOnly(0); - + if (site.hitID < 0) { + clstr.setScatterOnly(1); + } else { + clstr.setScatterOnly(0); + } + // Store the total Arc length in the GBLStripClusterData total_s3D += site.arcLength; clstr.setPath3D(total_s3D); double tanL = site.aS.helix.a.v[4]; - clstr.setPath(site.arcLength/FastMath.sqrt(1.+tanL*tanL)); - + clstr.setPath(site.arcLength / FastMath.sqrt(1. + tanL * tanL)); + Hep3Vector u = new BasicHep3Vector(vectorKalmanToTrk(site.m.p.V())); Hep3Vector v = new BasicHep3Vector(vectorKalmanToTrk(site.m.p.U().scale(-1.0))); Hep3Vector w = new BasicHep3Vector(vectorKalmanToTrk(site.m.p.T())); - + clstr.setU(u); clstr.setV(v); clstr.setW(w); - + // Direction of the track in the HPS tracking coordinate system // Find the momentum from the smoothed helix at the sensor location, make it a unit vector, // and then transform from the B-field frame to the Kalman global tracking frame. Vec momentum = site.aS.helix.getMom(0.); - Vec pDir= site.aS.helix.Rot.inverseRotate(momentum.unitVec()); + Vec pDir = site.aS.helix.Rot.inverseRotate(momentum.unitVec()); Hep3Vector trackDir = new BasicHep3Vector(vectorKalmanToTrk(pDir)); clstr.setTrackDir(trackDir); - + // Phi and lambda of the track (assuming standard spherical polar coordinates) double phi = FastMath.atan2(trackDir.y(), trackDir.x()); - double ct = trackDir.z()/trackDir.magnitude(); - double tanLambda = ct/FastMath.sqrt(1-ct*ct); // Should be very much the same as tanL above, after accounting for the field tilt + double ct = trackDir.z() / trackDir.magnitude(); + double tanLambda = ct / FastMath.sqrt(1 - ct * ct); // Should be very much the same as tanL above, after accounting for the field tilt if (debug) { - Vec tilted = site.aS.helix.Rot.inverseRotate(new Vec(0.,0.,1.)); + Vec tilted = site.aS.helix.Rot.inverseRotate(new Vec(0., 0., 1.)); double tiltAngle = FastMath.acos(tilted.v[2]); - System.out.format("KalmanInterface.createGBLStripClusterData: layer=%d det=%d tanL=%10.6f, tanLambda=%10.6f, tilt=%10.6f, sum=%10.6f\n", - site.m.Layer, site.m.detector, -tanL, tanLambda, tiltAngle, tiltAngle+tanLambda); + System.out.format("KalmanInterface.createGBLStripClusterData: layer=%d det=%d tanL=%10.6f, tanLambda=%10.6f, tilt=%10.6f, sum=%10.6f\n", + site.m.Layer, site.m.detector, -tanL, tanLambda, tiltAngle, tiltAngle + tanLambda); } clstr.setTrackPhi(phi); if (phiLast < 10.) { - if (Math.abs(phi - phiLast) > 1.2) System.out.format("Big phi change in event %d\n", eventNumber); + if (Math.abs(phi - phiLast) > 1.2) { + System.out.format("Big phi change in event %d\n", eventNumber); + } } phiLast = phi; clstr.setTrackLambda(FastMath.atan(tanLambda)); - + // Measured value in the sensor coordinates (u-value in the HPS system) double uMeas, uMeasErr; if (site.hitID >= 0) { - uMeas = site.m.hits.get(site.hitID).v; + uMeas = site.m.hits.get(site.hitID).v; uMeasErr = site.m.hits.get(site.hitID).sigma; //This is the un-biased residual error (post-fit) //uMeasErr = Math.sqrt(site.aS.R); @@ -497,44 +769,64 @@ public List createGBLStripClusterData(KalTrack kT) { } clstr.setMeas(uMeas); clstr.setMeasErr(uMeasErr); - + // Track position in local frame. First coordinate will be the predicted measurement. Vec rGlb = site.aS.helix.toGlobal(site.aS.helix.atPhi(0.)); Vec rLoc = site.m.toLocal(rGlb); Hep3Vector rLocHps = new BasicHep3Vector(localKalToHps(rLoc)); clstr.setTrackPos(rLocHps); - + // rms projected scattering angle double ctSensor = pDir.dot(site.m.p.T()); double XL = Math.abs((site.m.thickness / site.radLen) / ctSensor); clstr.setScatterAngle(HelixState.projMSangle(momentum.mag(), XL)); - + rtnList.add(clstr); } return rtnList; } - // Propagate a TrackState "stateHPS" to a plane given by "location" and "direction". - // The PropagatedTrackState object created includes the new propagated TrackState plus information on - // the intersection point of the track with the plane. - public PropagatedTrackState propagateTrackState(TrackState stateHPS, double [] location, double [] direction) { - return new PropagatedTrackState(stateHPS, location, direction, detPlanes, fM); + /** + * Propagate a TrackState "stateHPS" to a plane given by "location" and + * "direction". The PropagatedTrackState object created includes the new + * propagated TrackState plus information on the intersection point of the + * track with the plane. This propagations is done using the full field map, + * even if kPar.uniformB is true. + * + * @param stateHPS HPS trackstate at the plane in question + * @param location HPS location description (AtIP, AtFirstHit, AtLastHit, + * AtCalorimeter, AtVertex, AtOther) + * @param direction Direction cosines of the plane + * @return Kalman PropagatedTrackState object + */ + public PropagatedTrackState propagateTrackState(TrackState stateHPS, double[] location, double[] direction) { + return new PropagatedTrackState(stateHPS, location, direction, detPlanes, kPar.SVTcenter, fM); } - - // Create an HPS track from a Kalman track - public BaseTrack createTrack(KalTrack kT, boolean storeTrackStates) { + + /** + * Create an HPS track from a Kalman track + * + * @param kT Kalman track object + * @param storeTrackStates true to store in addition all of the track states + * @return HPS track + */ + public BaseTrack createTrack(KalTrack kT, boolean storeTrackStates, EventHeader event) { + + //boolean debug = true; if (kT.SiteList == null) { logger.log(Level.WARNING, "KalmanInterface.createTrack: Kalman track is incomplete."); + if (debug) System.out.format("createTrack: track %d is incomplete\n", kT.ID); return null; } if (kT.covNaN()) { logger.log(Level.FINE, "KalmanInterface.createTrack: Kalman track has NaN cov matrix."); return null; } - + if (debug) System.out.format("Entering createTrack for track %d\n", kT.ID); + kT.sortSites(true); BaseTrack newTrack = new BaseTrack(); - + // Add trackstate at IP as first trackstate, // and make this trackstate's params the overall track params DMatrixRMaj globalCov = new DMatrixRMaj(kT.originCovariance()); @@ -543,41 +835,44 @@ public BaseTrack createTrack(KalTrack kT, boolean storeTrackStates) { // center of the magnet), so we need to use the magnetic field there to convert from 1/pt to curvature. // Field at the origin => For 2016 this is ~ 0.430612 T // In the center of SVT => For 2016 this is ~ 0.52340 T - Vec Bfield = KalmanInterface.getField(new Vec(0., SVTcenter ,0.), kT.SiteList.get(0).m.Bfield); + Vec Bfield = KalmanInterface.getField(new Vec(0., kPar.SVTcenter, 0.), kT.SiteList.get(0).m.Bfield); double B = Bfield.mag(); double[] newParams = getLCSimParams(globalParams, alphaCenter); double[] newCov = getLCSimCov(globalCov, alphaCenter).asPackedArray(true); TrackState ts = new BaseTrackState(newParams, newCov, new double[]{0., 0., 0.}, TrackState.AtIP); if (ts != null) { - newTrack.getTrackStates().add(ts); + newTrack.getTrackStates().add(ts); newTrack.setTrackParameters(ts.getParameters(), B); newTrack.setCovarianceMatrix(new SymmetricMatrix(5, ts.getCovMatrix(), true)); } - + // Add the hits to the track for (MeasurementSite site : kT.SiteList) { - if (site.hitID < 0) continue; + if (site.hitID < 0) { + continue; + } newTrack.addHit(getHpsHit(site.m.hits.get(site.hitID))); } - //System.out.printf("PF::Debug::newTrack site size %d \n",newTrack.getTrackerHits().size()); - + if (debug) System.out.printf("createTrack: newTrack site size %d \n",newTrack.getTrackerHits().size()); + // Get the track states at each layer for (int i = 0; i < kT.SiteList.size(); i++) { MeasurementSite site = kT.SiteList.get(i); ts = null; int loc = TrackState.AtOther; - - //HpsSiSensor hssd = (HpsSiSensor) moduleMap.get(site.m).getSensor(); - //int lay = hssd.getMillepedeId(); - // System.out.printf("ssp id %d \n", hssd.getMillepedeId()); - + if (debug) { + HpsSiSensor hssd = (HpsSiSensor) moduleMap.get(site.m).getSensor(); + int lay = hssd.getMillepedeId(); + System.out.printf("createTrack: ssp id %d in layer %d\n", hssd.getMillepedeId(), lay); + } if (i == 0) { loc = TrackState.AtFirstHit; - } else if (i == kT.SiteList.size() - 1) + } else if (i == kT.SiteList.size() - 1) { loc = TrackState.AtLastHit; - + } + /* - //DO Not att the missing layer track states yet. + //DO Not store the missing layer track states (yet). if (storeTrackStates) { for (int k = 1; k < lay - prevID; k++) { // uses new lcsim constructor @@ -587,30 +882,69 @@ public BaseTrack createTrack(KalTrack kT, boolean storeTrackStates) { } prevID = lay; } - */ - + */ + if (debug) System.out.format("createTrack: at layer %d, loc=%d\n", site.m.Layer, loc); if (loc == TrackState.AtFirstHit || loc == TrackState.AtLastHit || storeTrackStates) { ts = createTrackState(site, loc, true); - if (ts != null) newTrack.getTrackStates().add(ts); + if (ts != null) { + newTrack.getTrackStates().add(ts); + } } } - + // Extrapolate to the ECAL and make a new trackState there. - - BaseTrackState ts_ecal = TrackUtils.getTrackExtrapAtEcalRK(newTrack, fM, runNumber); + + BaseTrackState ts_ecal = TrackUtils.getTrackExtrapAtEcalRK(newTrack, fM, event.getRunNumber()); + if (debug) { + double [] p = ts_ecal.getMomentum(); + double pmag = Math.sqrt(p[0]*p[0]+p[1]*p[1]+p[2]*p[2]); + System.out.format("createTrack: at ECAL, loc=%d, p=%10.5f\n",ts_ecal.getLocation(), pmag); + } newTrack.getTrackStates().add(ts_ecal); - - // other track properties - newTrack.setChisq(kT.chi2); - newTrack.setNDF(newTrack.getTrackerHits().size() - 5); + + // Other track properties + if (kT.energyConstrained) { + newTrack.setChisq(kT.chi2_Econstraint); + newTrack.setNDF(newTrack.getTrackerHits().size() - 4); + } else { + newTrack.setChisq(kT.chi2); + newTrack.setNDF(newTrack.getTrackerHits().size() - 5); + } + newTrack.setTrackType(BaseTrack.TrackType.Y_FIELD.ordinal()); newTrack.setFitSuccess(true); - + return newTrack; } - // Convert helix parameters from Kalman to LCSim - static double[] getLCSimParams(double[] oldParams, double alpha) { + /** + * Create an HPS track from a Kalman seed + * + * @param trk Seed + * @return HPS track + */ + public BaseTrack createTrack(SeedTrack trk) { + double[] newPivot = { 0., 0., 0. }; + double[] params = getLCSimParams(trk.pivotTransform(newPivot), alphaCenter); + SymmetricMatrix cov = getLCSimCov(trk.covariance(), alphaCenter); + BaseTrack newTrack = new BaseTrack(); + newTrack.setTrackParameters(params, trk.B()); + newTrack.setCovarianceMatrix(cov); + addHitsToTrack(newTrack); + newTrack.setTrackType(BaseTrack.TrackType.Y_FIELD.ordinal()); + newTrack.setFitSuccess(trk.success); + + return newTrack; + } + + /** + * Convert helix parameters from Kalman to LCSim + * + * @param oldParams Array of 5 Kalman helix parameters + * @param alpha Parameter to use to convert between momentum and curvature + * @return Array of 5 LCSim helix parameters + */ + static double[] getLCSimParams(double[] oldParams, double alpha) { // Note: since HPS-java has assumed a constant field for tracking, the alpha value used here should // correspond to the field at the center of the SVT or magnet. See the class variable alphaCenter. double[] params = new double[5]; @@ -623,8 +957,14 @@ static double[] getLCSimParams(double[] oldParams, double alpha) { return params; } - // Convert helix parameters from LCSim to Kalman - static double[] unGetLCSimParams(double[] oldParams, double alpha) { + /** + * Convert helix parameters from LCSim to Kalman + * + * @param oldParams Array of 5 LCSim helix parameters + * @param alpha Parameter to use to convert between momentum and curvature + * @return Array of 5 Kalman helix parameters + */ + static double[] unGetLCSimParams(double[] oldParams, double alpha) { // Note: since HPS-java has assumed a constant field for tracking, the alpha value used here should // correspond to the field at the center of the SVT or magnet. See the class variable alphaCenter. double[] params = new double[5]; @@ -636,11 +976,17 @@ static double[] unGetLCSimParams(double[] oldParams, double alpha) { return params; } - // Convert helix parameter covariance matrix from Kalman to LCSim + /** + * Convert helix parameter covariance matrix from Kalman to LCSim + * + * @param oldCov Kalman covariance matrix + * @param alpha Parameter to use to convert between momentum and curvature + * @return Covariance matrix of LCSim helix parameters + */ static SymmetricMatrix getLCSimCov(DMatrixRMaj oldCov, double alpha) { // Note: since HPS-java has assumed a constant field for tracking, the alpha value used here should // correspond to the field at the center of the SVT or magnet. See the class variable alphaCenter. - double[] d = { 1.0, -1.0, -1.0 / alpha, -1.0, -1.0 }; + double[] d = {1.0, -1.0, -1.0 / alpha, -1.0, -1.0}; SymmetricMatrix cov = new SymmetricMatrix(5); for (int i = 0; i < 5; i++) { for (int j = 0; j < 5; j++) { @@ -656,11 +1002,18 @@ static SymmetricMatrix getLCSimCov(DMatrixRMaj oldCov, double alpha) { return cov; } - // Convert helix parameter covariance matrix from LCSim to Kalman + /** + * Convert helix parameter covariance matrix from LCSim to Kalman + * + * @param oldCov LCSIM symmetric covariance matrix as a linear array of 15 + * elements + * @param alpha Parameter to use to convert between momentum and curvature + * @return 5 by 5 covariance matrix for Kalman helix parameters + */ static double[][] ungetLCSimCov(double[] oldCov, double alpha) { // Note: since HPS-java has assumed a constant field for tracking, the alpha value used here should // correspond to the field at the center of the SVT or magnet. See the class variable alphaCenter. - double[] d = { 1.0, -1.0, -1.0 * alpha, -1.0, -1.0 }; + double[] d = {1.0, -1.0, -1.0 * alpha, -1.0, -1.0}; double[][] cov = new double[5][5]; cov[0][0] = oldCov[0] * d[0] * d[0]; cov[1][0] = oldCov[1] * d[1] * d[0]; @@ -685,50 +1038,47 @@ static double[][] ungetLCSimCov(double[] oldCov, double alpha) { return cov; } - // Used only for Kalman tracks made by fitting hits on a GBL track + /** + * Add hits to an existing track. Used only for Kalman tracks made by fitting + * hits on a GBL track + * + * @param newTrack Track to which hits will be added + */ private void addHitsToTrack(BaseTrack newTrack) { List measList = getMeasurements(); for (Measurement meas : measList) { TrackerHit hit = hitMap.get(meas); - if (hit != null) { - if (!newTrack.getTrackerHits().contains(hit)) newTrack.addHit(hit); + if (hit != null) { + if (!newTrack.getTrackerHits().contains(hit)) { + newTrack.addHit(hit); + } } } newTrack.setNDF(newTrack.getTrackerHits().size()); } - // Create an HPS track from a Kalman seed - public BaseTrack createTrack(SeedTrack trk) { - double[] newPivot = { 0., 0., 0. }; - double[] params = getLCSimParams(trk.pivotTransform(newPivot), alphaCenter); - SymmetricMatrix cov = getLCSimCov(trk.covariance(), alphaCenter); - BaseTrack newTrack = new BaseTrack(); - newTrack.setTrackParameters(params, trk.B()); - newTrack.setCovarianceMatrix(cov); - addHitsToTrack(newTrack); - newTrack.setTrackType(BaseTrack.TrackType.Y_FIELD.ordinal()); - newTrack.setFitSuccess(trk.success); - - return newTrack; - } - - // Method to create one Kalman SiModule object for each silicon-strip detector + /** + * Method to create one Kalman SiModule geometry object for each + * silicon-strip detector + * + * @param inputPlanes List of all the silicon-strip detector planes + */ public void createSiModules(List inputPlanes) { if (debug) { System.out.format("Entering KalmanInterface.creasteSiModules\n"); } detPlanes = inputPlanes; // keep this reference for use by other methods - + //2016 => 12 planes, 2019 => 14 planes int nPlanes = inputPlanes.size(); //System.out.printf("PF::nPlanes::%d", nPlanes); if (nPlanes == 40) { // 2019 vs 2016 detector first layer kPar.setFirstLayer(0); - } else { + } else { kPar.setFirstLayer(2); } - + SiMlist.clear(); for (SiStripPlane inputPlane : inputPlanes) { @@ -738,10 +1088,10 @@ public void createSiModules(List inputPlanes) { double[] uGlb = new double[3]; double[] vGlb = new double[3]; double[] tGlb = new double[3]; - for (int row=0; row<3; ++row) { - uGlb[row] = inputPlane.getSensor().getGeometry().getLocalToGlobal().getRotation().getRotationMatrix().e(row,0); - vGlb[row] = inputPlane.getSensor().getGeometry().getLocalToGlobal().getRotation().getRotationMatrix().e(row,1); - tGlb[row] = inputPlane.getSensor().getGeometry().getLocalToGlobal().getRotation().getRotationMatrix().e(row,2); + for (int row = 0; row < 3; ++row) { + uGlb[row] = inputPlane.getSensor().getGeometry().getLocalToGlobal().getRotation().getRotationMatrix().e(row, 0); + vGlb[row] = inputPlane.getSensor().getGeometry().getLocalToGlobal().getRotation().getRotationMatrix().e(row, 1); + tGlb[row] = inputPlane.getSensor().getGeometry().getLocalToGlobal().getRotation().getRotationMatrix().e(row, 2); } Vec uK = (vectorGlbToKalman(vGlb).scale(-1.0)); // u and v are reversed in hps compared to kalman Vec vK = vectorGlbToKalman(uGlb); @@ -751,18 +1101,18 @@ public void createSiModules(List inputPlanes) { Vec pointOnPlaneTransformed = vectorGlbToKalman(pointOnPlane); if (debug) { - System.out.format("\nSiTrackerHit local to global rotation matrix for %s:\n",temp.getName()); - for (int row=0; row<3; ++row) { - for (int col=0; col<3; ++col) { - System.out.format(" %10.6f", inputPlane.getSensor().getGeometry().getLocalToGlobal().getRotation().getRotationMatrix().e(row,col)); + System.out.format("\nSiTrackerHit local to global rotation matrix for %s:\n", temp.getName()); + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) { + System.out.format(" %10.6f", inputPlane.getSensor().getGeometry().getLocalToGlobal().getRotation().getRotationMatrix().e(row, col)); } System.out.format("\n"); } - System.out.format("SiTrackerHit local to global translation vector for %s: %s\n",temp.getName(), - inputPlane.getSensor().getGeometry().getLocalToGlobal().getTranslation().getTranslationVector().toString()); + System.out.format("SiTrackerHit local to global translation vector for %s: %s\n", temp.getName(), + inputPlane.getSensor().getGeometry().getLocalToGlobal().getTranslation().getTranslationVector().toString()); uK.print("u in Kalman coordinates"); vK.print("v in Kalman coordinates"); - tK.print("t in Kalman coordinates"); + tK.print("t in Kalman coordinates"); pointOnPlaneTransformed.print("point on plane in Kalman coordinates"); Vec oldBadPoint = HpsSvtToKalman.rotate(new Vec(3, inputPlane.origin().v())); oldBadPoint.print("old, bad point on plane in Kalman coordinates"); @@ -770,58 +1120,86 @@ public void createSiModules(List inputPlanes) { System.out.printf(" Building with Kalman plane: point %s normal %s \n", new BasicHep3Vector(pointOnPlaneTransformed.v).toString(), new BasicHep3Vector(tK.v).toString()); } - Plane p =new Plane(pointOnPlaneTransformed, tK, uK, vK); - - int kalLayer; + Plane p = new Plane(pointOnPlaneTransformed, tK, uK, vK); + + int kalLayer; boolean split; if (nPlanes == 40) { //Indexing valid for 2019 detector -> Include new layer-0, layers go from 0 to 13!! - kalLayer = temp.getLayerNumber()-1; + kalLayer = temp.getLayerNumber() - 1; split = (kalLayer < 4); } else { //Indexing valid for 2016 detector - kalLayer = temp.getLayerNumber()+1; + kalLayer = temp.getLayerNumber() + 1; split = false; } int topBottom = 1; - if (temp.isBottomLayer()) topBottom = 0; + if (temp.isBottomLayer()) { + topBottom = 0; + } int detector = temp.getModuleNumber(); - if (kalLayer > 13) { - System.out.format("***KalmanInterface.createSiModules Warning: Kalman layer %d , tempLayer = %d out of range.***\n", kalLayer,temp.getLayerNumber()); - } + if (kalLayer >= mxLyrs) { + System.out.format("***KalmanInterface.createSiModules Warning: Kalman layer %d , tempLayer = %d out of range.***\n", kalLayer, temp.getLayerNumber()); + } int millipedeID = temp.getMillepedeId(); SiModule newMod = new SiModule(kalLayer, p, temp.isStereo(), inputPlane.getWidth(), inputPlane.getLength(), - split, inputPlane.getThickness(), fM, detector, millipedeID, topBottom); + split, inputPlane.getThickness(), fM, detector, millipedeID, topBottom); moduleMap.put(newMod, inputPlane); SiMlist.add(newMod); } Collections.sort(SiMlist, new SortByLayer()); for (SiModule sim : SiMlist) { logger.log(Level.INFO, sim.toString()); + int lyr = sim.Layer; + if (layerMap.containsKey(lyr)) { + layerMap.get(lyr).add(sim); + } else { + layerMap.put(lyr, new ArrayList()); + layerMap.get(lyr).add(sim); + } + } + for (int lyr=0; lyr mods = layerMap.get(lyr); + if (mods == null) { + System.out.format("KalmanInterface.createSiModules: error in layer %d, no module list\n", lyr); + continue; + } + for (SiModule mod : mods) { + if (mod.Layer != lyr) System.out.format("KalmanInterface.createSiModules: error in layer map at layer %d\n", lyr); + System.out.format("KalmanInterface.createSiModules: layer %d module %d has SiModule at location %s\n", lyr, mod.detector, mod.p.X().toString()); + } } } - - // Method to feed simulated hits into the pattern recognition, for testing - private boolean fillAllSimHits(EventHeader event, IDDecoder decoder) { + + /** + * Method to feed simulated hits into the pattern recognition, for testing + * + * @param event Event header + * @return true if it executed successfully + */ + boolean fillAllSimHits(EventHeader event) { boolean success = false; eventNumber = event.getEventNumber(); - if (debug || event.getEventNumber() < 50) System.out.format("KalmanInterface.fillAllSimHits: entering for event %d\n", event.getEventNumber()); - + if (debug || event.getEventNumber() < 50) { + System.out.format("KalmanInterface.fillAllSimHits: entering for event %d\n", event.getEventNumber()); + } + // Get the collection of 1D hits String stripHitInputCollectionName = "TrackerHits"; - if (!event.hasCollection(TrackerHit.class, stripHitInputCollectionName)) + if (!event.hasCollection(TrackerHit.class, stripHitInputCollectionName)) { return false; - + } + List striphits = event.get(SimTrackerHit.class, stripHitInputCollectionName); // Make a mapping from (Layer,Module) to hits - Map, ArrayList> hitSensorMap = new HashMap, ArrayList>(); + Map, ArrayList> hitSensorMap = new HashMap, ArrayList>(); for (SimTrackerHit hit1D : striphits) { //double[] tkMom = hit1D.getMomentum(); //System.out.format("MC true hit momentum=%10.5f %10.5f %10.5f\n",tkMom[0],tkMom[1],tkMom[2]); decoder.setID(hit1D.getCellID()); - int Layer = decoder.getValue("layer") + 1; + int Layer = decoder.getValue("layer") - 1; int Module = decoder.getValue("module"); - Pair sensor = new Pair(Layer,Module); + Pair sensor = new Pair(Layer, Module); ArrayList hitsInSensor = null; if (hitSensorMap.containsKey(sensor)) { @@ -831,18 +1209,26 @@ private boolean fillAllSimHits(EventHeader event, IDDecoder decoder) { } hitsInSensor.add(hit1D); hitSensorMap.put(sensor, hitsInSensor); - if (debug) System.out.format(" Adding hit for layer %d, module %d\n", Layer, Module); + if (debug) { + System.out.format(" Adding hit for layer %d, module %d\n", Layer, Module); + } } int hitsFilled = 0; for (int modIndex = 0; modIndex < SiMlist.size(); ++modIndex) { SiModule module = SiMlist.get(modIndex); - Pair sensor = new Pair(module.Layer,module.detector); - if (debug) System.out.format(" Si module in layer %d module %d, extent=%8.3f to %8.3f\n", module.Layer, module.detector, module.yExtent[0], module.yExtent[1]); + Pair sensor = new Pair(module.Layer, module.detector); + if (debug) { + System.out.format(" Si module in layer %d module %d, extent=%8.3f to %8.3f\n", module.Layer, module.detector, module.yExtent[0], module.yExtent[1]); + } - if (!hitSensorMap.containsKey(sensor)) continue; + if (!hitSensorMap.containsKey(sensor)) { + continue; + } ArrayList hitsInSensor = hitSensorMap.get(sensor); - if (hitsInSensor == null) continue; + if (hitsInSensor == null) { + continue; + } for (int i = 0; i < hitsInSensor.size(); i++) { SimTrackerHit hit = hitsInSensor.get(i); @@ -852,7 +1238,7 @@ private boolean fillAllSimHits(EventHeader event, IDDecoder decoder) { //module.p.print("Si module plane"); double du = 0.006; - double umeas = rLocal.v[1] + rnd.nextGaussian()*du; + double umeas = rLocal.v[1] + rnd.nextGaussian() * du; if (debug) { System.out.format("\nKalmanInterface:fillAllSimHits %d, the measurement uncertainty is set to %10.7f\n", i, du); @@ -864,22 +1250,34 @@ private boolean fillAllSimHits(EventHeader event, IDDecoder decoder) { globalY.print("globalY"); System.out.format(" Adding measurement %10.5f to layer %d, module %d\n", umeas, module.Layer, module.detector); } - Measurement m = new Measurement(umeas, 0., du, 0., hit.getdEdx()*1000000., rGlobal, rLocal.v[1]); + Measurement m = new Measurement(umeas, 0., du, 0., hit.getdEdx() * 1000000., rGlobal, rLocal.v[1]); //rGlobal.print("new global hit location"); module.addMeasurement(m); simHitMap.put(m, hit); hitsFilled++; } - if (debug) { module.print("SiModule-filled"); } + if (debug) { + module.print("SiModule-filled"); + } + } + if (hitsFilled > 0) { + success = true; + } + if (debug) { + System.out.format("KalmanInterface.fillAllSimHits: %d hits were filled into Si Modules\n", hitsFilled); } - if (hitsFilled > 0) success = true; - if (debug) System.out.format("KalmanInterface.fillAllSimHits: %d hits were filled into Si Modules\n", hitsFilled); return success; } - - // Method to fill all Si hits into the SiModule objects, to feed to the pattern recognition. + + /** + * Method to fill all Si hits into the SiModule objects, to feed to the + * pattern recognition. + * + * @param event Event header + * @return true if it executed successfully + */ private boolean fillAllMeasurements(EventHeader event) { boolean success = false; eventNumber = event.getEventNumber(); @@ -887,11 +1285,13 @@ private boolean fillAllMeasurements(EventHeader event) { // Get the collection of 1D hits String stripHitInputCollectionName = "StripClusterer_SiTrackerHitStrip1D"; List striphits = event.get(TrackerHit.class, stripHitInputCollectionName); - - if (striphits.size() > maxHits) maxHits = striphits.size(); - if (striphits.size() > 200) nBigEvents++; + + if (striphits.size() > maxHits) { + maxHits = striphits.size(); + } if (_siHitsLimit > 0 && striphits.size() > _siHitsLimit) { - System.out.format("KalmanInterface::Skip event %d with %s %d hits > %d\n", event.getEventNumber(), + nBigEvents++; + System.out.format("KalmanInterface::Skip event %d with %s %d hits > %d\n", event.getEventNumber(), stripHitInputCollectionName, striphits.size(), _siHitsLimit); return false; } else if (striphits.size() > 500) { @@ -902,7 +1302,7 @@ private boolean fillAllMeasurements(EventHeader event) { Map> hitSensorMap = new HashMap>(); if (debug) { if (striphits.size() == 0) { - System.out.format("KalmanInterface:fillAllMeasurements, there are no strip hits in event %d\n",event.getEventNumber()); + System.out.format("KalmanInterface:fillAllMeasurements, there are no strip hits in event %d\n", event.getEventNumber()); } } for (TrackerHit hit1D : striphits) { @@ -922,9 +1322,13 @@ private boolean fillAllMeasurements(EventHeader event) { for (int modIndex = 0; modIndex < SiMlist.size(); ++modIndex) { SiModule module = SiMlist.get(modIndex); SiStripPlane plane = moduleMap.get(module); - if (!hitSensorMap.containsKey(plane.getSensor())) continue; + if (!hitSensorMap.containsKey(plane.getSensor())) { + continue; + } ArrayList hitsInSensor = hitSensorMap.get(plane.getSensor()); - if (hitsInSensor == null) continue; + if (hitsInSensor == null) { + continue; + } for (int i = 0; i < hitsInSensor.size(); i++) { TrackerHit hit = hitsInSensor.get(i); @@ -933,13 +1337,13 @@ private boolean fillAllMeasurements(EventHeader event) { if (debug) { System.out.format("\nFilling hits in SiModule for %s\n", plane.getName()); - SiTrackerHitStrip1D global = (new SiTrackerHitStrip1D(hit)).getTransformedHit(TrackerHitType.CoordinateSystem.GLOBAL); + SiTrackerHitStrip1D global = (new SiTrackerHitStrip1D(hit)).getTransformedHit(TrackerHitType.CoordinateSystem.GLOBAL); Vec rGlobal = vectorGlbToKalman(global.getPosition()); Vec rLocal = module.toLocal(rGlobal); - Vec hpsLocal = new Vec(3,localHit.getPosition()); + Vec hpsLocal = new Vec(3, localHit.getPosition()); rLocal.print("hps global hit transformed to Kalman local frame"); hpsLocal.print("hps local hit"); - + /* System.out.format("\nTesting the hps coordinate transformation matrices and displacements for %s\n",plane.getSensor().getName()); Vec hitGlobal = new Vec(3,global.getPosition()); @@ -977,21 +1381,21 @@ private boolean fillAllMeasurements(EventHeader event) { newHitLocal.print("transformed global hit"); newHitLocal = lTogRot.inverseRotate(hitGlobal.dif(lTogTran)); newHitLocal.print("transformed global hit"); - */ + */ } - double [] lpos = localHit.getPosition(); + double[] lpos = localHit.getPosition(); double umeas = lpos[0]; double du = FastMath.sqrt(localHit.getCovarianceAsMatrix().diagonal(0)); - double time = localHit.getTime(); + double time = localHit.getTime(); double xStrip = -lpos[1]; // Center of strip, i.e. ~0 except in layers 0 and 1 if (xStrip > module.xExtent[1] || xStrip < module.xExtent[0]) { - logger.log(Level.WARNING, String.format("Event %d Layer %d, local hit at %9.4f %9.4f, %9.4f is outside detector extents %8.3f->%8.3f %8.3f->%8.3f", + logger.log(Level.WARNING, String.format("Event %d Layer %d, local hit at %9.4f %9.4f, %9.4f is outside detector extents %8.3f->%8.3f %8.3f->%8.3f", event.getEventNumber(), module.Layer, lpos[0], lpos[1], lpos[2], module.yExtent[0], module.yExtent[1], module.xExtent[0], module.xExtent[1])); } if (debug) { int nstrp = localHit.getRawHits().size(); - System.out.format("%d %d u = %9.4f +- %9.4f cov=%10.4e, %10.4e, %10.4e\n", module.Layer, nstrp, umeas, du, localHit.getCovarianceAsMatrix().e(0,0), - localHit.getCovarianceAsMatrix().e(1,0), localHit.getCovarianceAsMatrix().e(1,1)); + System.out.format("%d %d u = %9.4f +- %9.4f cov=%10.4e, %10.4e, %10.4e\n", module.Layer, nstrp, umeas, du, localHit.getCovarianceAsMatrix().e(0, 0), + localHit.getCovarianceAsMatrix().e(1, 0), localHit.getCovarianceAsMatrix().e(1, 1)); } // If HPS measured coordinate axis is opposite to Kalman measured coordinate axis @@ -1017,76 +1421,124 @@ private boolean fillAllMeasurements(EventHeader event) { globalX.print("globalX"); globalY.print("globalY"); } - Measurement m = new Measurement(umeas, xStrip, du, time, localHit.getdEdx()*1000000.); + Measurement m = new Measurement(umeas, xStrip, du, time, localHit.getdEdx() * 1000000.); module.addMeasurement(m); hitMap.put(m, hit); hitsFilled++; } - if (debug) { module.print("SiModule-filled"); } + if (debug) { + module.print("SiModule-filled"); + } } - if (hitsFilled > 0) success = true; - if (debug) System.out.format("KalmanInterface.fillAllMeasurements: %d hits were filled into Si Modules\n", hitsFilled); - + if (hitsFilled > 0) { + success = true; + } + if (debug) { + System.out.format("KalmanInterface.fillAllMeasurements: %d hits were filled into Si Modules\n", hitsFilled); + } + // Add MC truth information to each hit if it is available if (event.hasCollection(LCRelation.class, "SVTTrueHitRelations")) { RelationalTable rawtomc = new BaseRelationalTable(RelationalTable.Mode.MANY_TO_MANY, RelationalTable.Weighting.UNWEIGHTED); - + //if (debug) System.out.println("SVTTrueHitRelations found"); List trueHitRelations = event.get(LCRelation.class, "SVTTrueHitRelations"); for (LCRelation relation : trueHitRelations) { - if (relation != null && relation.getFrom() != null && relation.getTo() != null) + if (relation != null && relation.getFrom() != null && relation.getTo() != null) { rawtomc.add(relation.getFrom(), relation.getTo()); + } } for (SiModule mod : SiMlist) { for (Measurement hit : mod.hits) { - hit.tksMC = new ArrayList(); + hit.pMC = new ArrayList(1); TrackerHit hpsHit = getHpsHit(hit); List rawHits = hpsHit.getRawHits(); for (RawTrackerHit rawHit : rawHits) { Set simHits = rawtomc.allFrom(rawHit); for (SimTrackerHit simHit : simHits) { - if (hit.rGlobal == null) hit.rGlobal = vectorGlbToKalman(simHit.getPosition()); + if (hit.rGlobal == null) { + hit.rGlobal = vectorGlbToKalman(simHit.getPosition()); + hit.vTrue = mod.toLocal(hit.rGlobal).v[1]; + } MCParticle mcp = simHit.getMCParticle(); - if (!hit.tksMC.contains(mcp.hashCode())) hit.tksMC.add(mcp.hashCode()); + if (!hit.pMC.contains(mcp)) { + hit.pMC.add(mcp); + //if (debug) System.out.println("adding MC particle to hit"); + } } } } } - } + } return success; } - // Method to fill the Si hits into the SiModule objects for a given track, in order to refit the track - private double fillMeasurements(List hits1D, int addMode) { + /** + * Method to fill the Si hits into the SiModule objects for a given GBL + * track, in order to refit the track using Kalman filter + * + * @param hits1D 1-dimensional tracker hits + * @param addMode 0 to skip layers not already having hits; 1 to skip layers + * already having hits; 2 not to skip + * @return + */ + private double fillMeasurements(EventHeader event, List hits1D, int addMode) { double firstZ = 10000; + //boolean debug = true; + boolean hasMC = false; Map> hitsMap = new HashMap>(); - + if (debug) System.out.format("Entering fillMeasurements in event %d for %d hits, addmode %d\n", + event.getEventNumber(), hits1D.size(), addMode); for (TrackerHit hit1D : hits1D) { - HpsSiSensor temp = ((HpsSiSensor) ((RawTrackerHit) hit1D.getRawHits().get(0)).getDetectorElement()); - int lay = temp.getLayerNumber(); - if (addMode == 0 && !SeedTrackLayers.contains((lay + 1) / 2)) continue; - else if (addMode == 1 && SeedTrackLayers.contains((lay + 1) / 2)) continue; + HpsSiSensor sensor = ((HpsSiSensor) ((RawTrackerHit) hit1D.getRawHits().get(0)).getDetectorElement()); + int lay = sensor.getLayerNumber(); + if (addMode == 0 && !SeedTrackLayers.contains((lay + 1) / 2)) { + continue; + } else if (addMode == 1 && SeedTrackLayers.contains((lay + 1) / 2)) { + continue; + } ArrayList hitsInLayer = null; - if (hitsMap.containsKey(temp)) { - hitsInLayer = hitsMap.get(temp); + if (hitsMap.containsKey(sensor)) { + hitsInLayer = hitsMap.get(sensor); } else { hitsInLayer = new ArrayList(); } hitsInLayer.add(hit1D); - if (hit1D.getPosition()[2] < firstZ) firstZ = hit1D.getPosition()[2]; - hitsMap.put(temp, hitsInLayer); + if (hit1D.getPosition()[2] < firstZ) { + firstZ = hit1D.getPosition()[2]; + } + hitsMap.put(sensor, hitsInLayer); } + // Add MC truth information to each hit if it is available + RelationalTable rawtomc = null; + if (event.hasCollection(LCRelation.class, "SVTTrueHitRelations")) { + rawtomc = new BaseRelationalTable(RelationalTable.Mode.MANY_TO_MANY, RelationalTable.Weighting.UNWEIGHTED); + if (debug) System.out.println("KalmanInterface.fillMeasurements: SVTTrueHitRelations found"); + List trueHitRelations = event.get(LCRelation.class, "SVTTrueHitRelations"); + for (LCRelation relation : trueHitRelations) { + if (relation != null && relation.getFrom() != null && relation.getTo() != null) { + rawtomc.add(relation.getFrom(), relation.getTo()); + } + } + hasMC = true; + } + + int nHitsFilled = 0; for (SiModule mod : SiMlist) { SiStripPlane plane = moduleMap.get(mod); - if (!hitsMap.containsKey(plane.getSensor())) { continue; } - ArrayList temp = hitsMap.get(plane.getSensor()); - if (temp == null) { continue; } + if (!hitsMap.containsKey(plane.getSensor())) { + continue; + } + ArrayList theHits = hitsMap.get(plane.getSensor()); + if (theHits == null) { + continue; + } Hep3Vector planeMeasuredVec = VecOp.mult(HpsSvtToKalmanMatrix, plane.getMeasuredCoordinate()); - for (int i = 0; i < temp.size(); i++) { - TrackerHit hit = temp.get(i); + for (int i = 0; i < theHits.size(); i++) { + TrackerHit hit = theHits.get(i); SiTrackerHitStrip1D local = (new SiTrackerHitStrip1D(hit)).getTransformedHit(TrackerHitType.CoordinateSystem.SENSOR); // SiTrackerHitStrip1D global = (new @@ -1095,21 +1547,23 @@ private double fillMeasurements(List hits1D, int addMode) { double umeas = local.getPosition()[0]; double xStrip = -local.getPosition()[1]; double du = FastMath.sqrt(local.getCovarianceAsMatrix().diagonal(0)); + double time = local.getTime(); // if hps measured coord axis is opposite to kalman measured coord axis // This really should not happen, as the Kalman axis is copied directly from the hps geometry. - if (planeMeasuredVec.z() * mod.p.V().v[2] < 0) { + if (planeMeasuredVec.z() * mod.p.V().v[2] < 0) { System.out.format("*** KalmanInterface.fillMeasurements: flipping Kalman coordinate sign %d! ***\n", i); - umeas *= -1.0; + umeas *= -1.0; } if (debug) { - System.out.format("\nKalmanInterface:fillMeasurements Measurement %d, the measurement uncertainty is set to %10.7f\n", i, + System.out.format("\nKalmanInterface:fillMeasurements, Event %d, Layer %d\n",event.getEventNumber(),mod.Layer); + System.out.format("KalmanInterface:fillMeasurements Measurement %d, the measurement uncertainty is set to %10.7f\n", i, du); System.out.printf("Filling SiMod: %s \n", plane.getName()); - System.out.printf("HPSplane MeasuredCoord %s UnmeasuredCoord %s Normal %s umeas %f\n", + System.out.printf("HPSplane MeasuredCoord %s UnmeasuredCoord %s Normal %s umeas %f xStrip %f time %f\n", plane.getMeasuredCoordinate().toString(), plane.getUnmeasuredCoordinate().toString(), plane.normal().toString(), - umeas); + umeas, xStrip, time); System.out.printf(" converted to Kalman Coords Measured %s Unmeasured %s umeas %f \n", planeMeasuredVec.toString(), VecOp.mult(HpsSvtToKalmanMatrix, plane.getUnmeasuredCoordinate()).toString(), umeas); mod.p.print("Corresponding KalmanPlane"); @@ -1118,53 +1572,109 @@ private double fillMeasurements(List hits1D, int addMode) { globalX.print("globalX"); globalY.print("globalY"); } - Measurement m = new Measurement(umeas, xStrip, du, 0., hit.getdEdx()*1000000.); - - KalHit hitPair = new KalHit(mod,m); + Measurement m = new Measurement(umeas, xStrip, du, time, local.getdEdx() * 1000000.); + nHitsFilled++; + if (hasMC) { + m.pMC = new ArrayList(1); + List rawHits = hit.getRawHits(); + for (RawTrackerHit rawHit : rawHits) { + Set simHits = rawtomc.allFrom(rawHit); + for (SimTrackerHit simHit : simHits) { + if (m.rGlobal == null) { + m.rGlobal = vectorGlbToKalman(simHit.getPosition()); + m.vTrue = mod.toLocal(m.rGlobal).v[1]; + } + MCParticle mcp = simHit.getMCParticle(); + if (!m.pMC.contains(mcp)) { + m.pMC.add(mcp); + if (debug) System.out.println("KalmanInterface.fillMeasurements: adding MC particle to hit"); + } + } + } + } + + KalHit hitPair = new KalHit(mod, m); trackHitsKalman.add(hitPair); mod.addMeasurement(m); hitMap.put(m, hit); } - if (debug) { mod.print("SiModule-filled"); } + if (debug) { + mod.print("SiModule-filled"); + } + } + if (debug) System.out.format("KalmanInterface.fillMeasurements: total hits filled = %d\n", nHitsFilled); return firstZ; } - // Make a linear fit to a set of hits to be used to initialize the Kalman Filter - public SeedTrack createKalmanSeedTrack(Track track, RelationalTable hitToStrips, RelationalTable hitToRotated) { + /** + * Make a linear fit to a set of hits to be used to initialize the Kalman + * Filter + * + * @param track GBL track + * @param hitToStrips Relation table for GBL hits to strips + * @param hitToRotated Relation table for GBL hits to rotated hits + * @return New track seed + */ + public SeedTrack createKalmanSeedTrack(EventHeader event, Track track, RelationalTable hitToStrips, RelationalTable hitToRotated) { List hitsOnTrack = TrackUtils.getStripHits(track, hitToStrips, hitToRotated); - double firstHitZ = fillMeasurements(hitsOnTrack, 0); - if (debug) System.out.printf("firstHitZ %f \n", firstHitZ); - return new SeedTrack(trackHitsKalman, firstHitZ, 0.); + double firstHitZ = fillMeasurements(event, hitsOnTrack, 0); + if (debug) { + System.out.printf("firstHitZ %f \n", firstHitZ); + } + return new SeedTrack(trackHitsKalman, firstHitZ, 0., kPar); } - // Method to refit an existing track's hits, using the Kalman seed-track to initialize the Kalman Filter. - public KalmanTrackFit2 createKalmanTrackFit(int evtNumb, SeedTrack seed, Track track, RelationalTable hitToStrips, + /** + * Method to refit an existing track's hits, using the Kalman seed-track to + * initialize the Kalman Filter. + * + * @param evtNumb Event number + * @param seed Kalman track seed + * @param track GBL track + * @param hitToStrips Relation table for GBL hits to strips + * @param hitToRotated Relation table for GBL hits to rotated hits + * @param nIt Number of iterations + * @return Kalman track fit object + */ + public KalmanTrackFit2 createKalmanTrackFit(EventHeader event, SeedTrack seed, Track track, RelationalTable hitToStrips, RelationalTable hitToRotated, int nIt) { double firstHitZ = 10000.; + int evtNumb = event.getEventNumber(); List hitsOnTrack = TrackUtils.getStripHits(track, hitToStrips, hitToRotated); - if (debug) { System.out.format("createKalmanTrackFit: number of hits on track = %d\n", hitsOnTrack.size()); } + if (debug) { + System.out.format("createKalmanTrackFit: number of hits on track = %d\n", hitsOnTrack.size()); + } for (TrackerHit hit1D : hitsOnTrack) { - if (hit1D.getPosition()[2] < firstHitZ) firstHitZ = hit1D.getPosition()[2]; + if (hit1D.getPosition()[2] < firstHitZ) { + firstHitZ = hit1D.getPosition()[2]; + } } ArrayList SiMoccupied = new ArrayList(); int startIndex = 0; - fillMeasurements(hitsOnTrack, 1); + fillMeasurements(event, hitsOnTrack, 1); for (SiModule SiM : SiMlist) { - if (!SiM.hits.isEmpty()) SiMoccupied.add(SiM); + if (!SiM.hits.isEmpty()) { + SiMoccupied.add(SiM); + } } Collections.sort(SiMoccupied, new SortByLayer()); for (int i = 0; i < SiMoccupied.size(); i++) { SiModule SiM = SiMoccupied.get(i); - if (SeedTrackLayers.contains((SiM.Layer + 1) / 2) && (i > startIndex)) { startIndex = i; } - if (debug) { SiM.print(String.format("SiMoccupied%d", i)); } + if (SeedTrackLayers.contains((SiM.Layer + 1) / 2) && (i > startIndex)) { + startIndex = i; + } + if (debug) { + SiM.print(String.format("SiMoccupied%d", i)); + } } - // startIndex++; - if (debug) { System.out.printf("createKTF: using %d SiModules, startIndex %d \n", SiMoccupied.size(), startIndex); } + if (debug) { + System.out.printf("createKTF: using %d SiModules, startIndex %d \n", SiMoccupied.size(), startIndex); + } DMatrixRMaj cov = seed.covariance().copy(); CommonOps_DDRM.scale(10., cov); @@ -1172,60 +1682,438 @@ public KalmanTrackFit2 createKalmanTrackFit(int evtNumb, SeedTrack seed, Track t return new KalmanTrackFit2(evtNumb, SiMoccupied, null, startIndex, nIt, new Vec(0., seed.yOrigin, 0.), seed.helixParams(), cov, kPar, fM); } - // Method to refit an existing track, using the track's helix parameters and covariance to initialize the Kalman Filter. - public KalmanTrackFit2 createKalmanTrackFit(int evtNumb, Vec helixParams, Vec pivot, DMatrixRMaj cov, Track track, + /** + * Method to refit an existing GBL track, using the track's helix parameters + * and covariance to initialize the Kalman Filter. + * + * @param evtNumb Event number + * @param helixParams 5-vector of starting-guess Kalman helix parameters + * @param pivot Pivot point for the helix parameters + * @param cov Starting-guess Helix parameters covariance + * @param track GBL track + * @param hitToStrips Relation table for GBL hits to strips + * @param hitToRotated Relation table for GBL hits to rotated hits + * @param nIt Number of Kalman fit iterations + * @return Kalman track-fit object + */ + public KalmanTrackFit2 createKalmanTrackFit(EventHeader event, Vec helixParams, Vec pivot, DMatrixRMaj cov, Track track, RelationalTable hitToStrips, RelationalTable hitToRotated, int nIt) { List hitsOnTrack = TrackUtils.getStripHits(track, hitToStrips, hitToRotated); - if (debug) { System.out.format("createKalmanTrackFit: using GBL fit as start; number of hits on track = %d\n", hitsOnTrack.size()); } + if (debug) { + System.out.format("createKalmanTrackFit: using GBL fit as start; number of hits on track = %d\n", hitsOnTrack.size()); + } ArrayList SiMoccupied = new ArrayList(); - fillMeasurements(hitsOnTrack, 2); + fillMeasurements(event, hitsOnTrack, 2); for (SiModule SiM : SiMlist) { - if (!SiM.hits.isEmpty()) SiMoccupied.add(SiM); + if (!SiM.hits.isEmpty()) { + SiMoccupied.add(SiM); + } } Collections.sort(SiMoccupied, new SortByLayer()); for (int i = 0; i < SiMoccupied.size(); i++) { SiModule SiM = SiMoccupied.get(i); - if (debug) SiM.print(String.format("SiMoccupied%d", i)); + if (debug) { + SiM.print(String.format("SiMoccupied%d", i)); + } } int startIndex = 0; - if (debug) System.out.printf("createKTF: using %d SiModules, startIndex %d \n", SiMoccupied.size(), startIndex); + if (debug) { + System.out.printf("createKTF: using %d SiModules, startIndex %d \n", SiMoccupied.size(), startIndex); + } CommonOps_DDRM.scale(10., cov); + int evtNumb = event.getEventNumber(); return new KalmanTrackFit2(evtNumb, SiMoccupied, null, startIndex, nIt, pivot, helixParams, cov, kPar, fM); } - // public KalTrack createKalmanTrack(KalmanTrackFit2 ktf, int trackID) { - // return new KalTrack(trackID, ktf.sites.size(), ktf.sites, ktf.chi2s); - // } + /** + * Refit an existing HPS track with the associated energy measurement added in. + * @param event Event header + * @param track HPS track to refit + * @param energy ECal energy in case the constraint is needed (otherwise not used) + * @param sigmaE ECAL energy resolution + * @param eConstraint true to include the ECal energy constraint + * @param layerSkip List of layers to skip in the fit (any hit on these layers is deleted) + * @return The new HPS track + */ + public Track refitTrackWithE(EventHeader event, Track track, double energy, double sigmaE, boolean eConstraint, ArrayList layerSkip) { + // First we need initial guesses for the helix parameters and covariance. + // Preferentially take them from a TrackState. If there is no TrackState, + // then estimate from a linear fit to the set of hits. + //boolean debug = true; + List tkrStates = track.getTrackStates(); + TrackState theTrackState = null; + double [] helixParams = null; + Vec kalHelixParams = null; + Vec pivot = new Vec(0., 0., 0.); + if (debug) { + System.out.format("Entering refitTrackWithE: event=%d, energy = %10.4f, E-constraint=%b\n", event.getEventNumber(), energy, eConstraint); + for (int lyr : layerSkip) { + System.out.format(" Layer %d will be skipped.\n", lyr); + } + } + if (tkrStates != null) { + for (TrackState tkrState : tkrStates) { + if (tkrState.getLocation() == TrackState.AtFirstHit) { + theTrackState = tkrState; + if (debug) { + System.out.println("refitTrackWithE: trackstate at first hit"); + } + break; + } + } + if (theTrackState == null) { + for (TrackState tkrState : tkrStates) { + if (tkrState.getLocation() == TrackState.AtIP) { + theTrackState = tkrState; + if (debug) { + System.out.println("refitTrackWithE: trackstate at IP"); + } + break; + } + } + } + if (theTrackState != null) { + Vec refPnt = new Vec(3, theTrackState.getReferencePoint()); + helixParams = theTrackState.getParameters(); + double bField = KalmanInterface.getField(refPnt, fM).mag(); + double alpha = 1000.0 * 1.0e9 / (c * bField); + kalHelixParams = new Vec(5, KalmanInterface.unGetLCSimParams(helixParams, alpha)); + double[] covHPS = theTrackState.getCovMatrix(); + helixCov = new DMatrixRMaj(KalmanInterface.ungetLCSimCov(covHPS, alpha)); + if (debug) { + ((BaseTrackState) theTrackState).computeMomentum(centerB.mag()); + double [] ptk = theTrackState.getMomentum(); + double ptkmag = Math.sqrt(ptk[0]*ptk[0]+ptk[1]*ptk[1]+ptk[2]*ptk[2]); + System.out.format("refitTrackWithE: at IP, p=%10.5f, E to constrain to = %10.5f\n", ptkmag, energy); + System.out.format("refitTrackWithE: LCSim helix params = %9.5f %9.5f %9.5f %9.5f %9.5f\n", + helixParams[0], helixParams[1], helixParams[2], helixParams[3], helixParams[4]); + System.out.format("refitTrackWithE: Kalman helix params = %s\n", kalHelixParams.toString("helix")); + } + } + } else { + if (debug) { + System.out.println("KalmanInterface.refitTrackWithE: no track states exist "); + } + } + // Get the list of tracker hits on this track + List hitsOnTrack = track.getTrackerHits(); + if (debug) { + for (TrackerHit hit : hitsOnTrack) { + double time = hit.getTime(); + double [] pos = hit.getPosition(); + System.out.format("KalmanInterface.refitTrackWithE: hit with time=%9.3f, position=(%9.4f, %9.4f, %9.4f)\n", + time, pos[0], pos[1], pos[2]); + List rawHits = hit.getRawHits(); + for (RawTrackerHit rawHit : rawHits) { + long ID = rawHit.getCellID(); + decoder.setID(ID); + int Layer = decoder.getValue("layer") - 1; + int Module = decoder.getValue("module"); + double [] ps = rawHit.getPosition(); + int t = rawHit.getTime(); + System.out.format(" raw hit on layer %d, module %d, time=%d, position=(%9.4f, %9.4f, %9.4f)\n", + Layer, Module, t, ps[0], ps[1], ps[2]); + } + } + } + // Remove hits on certain layers, if requested + if (layerSkip.size() > 0) { + Iterator iter = hitsOnTrack.iterator(); + while (iter.hasNext()) { + TrackerHit hit = iter.next(); + List rawHits = hit.getRawHits(); + long ID = rawHits.get(0).getCellID(); + decoder.setID(ID); + int Layer = decoder.getValue("layer") - 1; + for (int lyr : layerSkip) { + if (lyr == Layer) { + iter.remove(); + if (debug) System.out.format("KalmanInterface.refitTrackWithE: removing hit on layer %d\n", Layer); + break; + } + } + } + } + // Fill the hit information into the SiModule objects + double firstHitZ = fillMeasurements(event, hitsOnTrack, 2); + + // If no TrackState info was available, do a linear fit to the track hits to get the helix parameter guesses + if (theTrackState == null) { + if (debug) System.out.format("refitTrackWithE: firstHitZ %f. Generate a seed from linear fit.\n", firstHitZ); + SeedTrack seed = new SeedTrack(trackHitsKalman, firstHitZ, 0., kPar); + kalHelixParams = seed.helixParams(); + helixCov = seed.covariance(); + pivot.v[1] = seed.yOrigin; + } + + // Find the range of tracker layers covered by the hits on the track + int lyrMin = 20; + int lyrMax = -1; + for (TrackerHit hit : hitsOnTrack) { + List rawHits = hit.getRawHits(); + long ID = rawHits.get(0).getCellID(); + decoder.setID(ID); + int Layer = decoder.getValue("layer") - 1; + if (Layer > lyrMax) lyrMax = Layer; + if (Layer < lyrMin) lyrMin = Layer; + } + if (debug) System.out.format("refitTrackWithE: first layer = %d, last layer = %d\n", lyrMin, lyrMax); + + // Select SiModules along the range of the track, to use in the fit + double tanL = kalHelixParams.v[4]; + int topBottom; + if (tanL > 0) topBottom = 0; + else topBottom = 1; + if (debug) System.out.format("refitTrackWithE: look for modules in detector %d\n", topBottom); + ArrayList SiMoccupied = new ArrayList(); + for (int lyr=lyrMin; lyr<=lyrMax; ++lyr) { + if (layerMap.containsKey(lyr)) { + int nHitsLyr = 0; + List mods = layerMap.get(lyr); + for (SiModule mod : mods) { + int tb; + if (mod.p.X().v[2] > 0) tb = 0; + else tb = 1; + if (debug) System.out.format(" module %d in layer %d detector %d\n", mod.detector, mod.Layer, tb); + if (mod.hits.size() > 0) { + nHitsLyr += mod.hits.size(); + SiMoccupied.add(mod); + if (tb != topBottom) { + System.out.format("refitTrackWithE: wrong detector at layer %d, tb=%d\n", lyr, tb); + } + if (debug) System.out.format("refitTrackWithE: module in layer %d wafer %d has %d hits\n", lyr, mod.detector, mod.hits.size()); + break; + } + } + if (nHitsLyr == 0) { + SiModule modHit = null; + for (SiModule mod : mods) { + int tb; + if (mod.p.X().v[2] > 0) tb = 0; + else tb = 1; + if (tb != topBottom) continue; + modHit = mod; + double bField = centerB.mag(); + double alpha = 1000.0 * 1.0e9 / (c * bField); + double phiInt = hpi.planeIntersect(kalHelixParams, pivot, alpha, mod.p); + if (Double.isNaN(phiInt)) { + continue; + } + Vec rGlob = HelixState.atPhi(pivot, kalHelixParams, phiInt, alpha); + Vec rLoc = mod.toLocal(rGlob); + if (debug) { + System.out.format("refitTrackWithE: layer %d wafer %d with no hits, intersection=%s\n", lyr, mod.detector, rGlob.toString()); + System.out.format(" Intersection in local coordinates=%s\n", rLoc.toString()); + System.out.format(" module X extents: %9.4f %9.4f\n", mod.xExtent[0], mod.xExtent[1]); + System.out.format(" module Y extents: %9.4f %9.4f\n", mod.yExtent[0], mod.yExtent[1]); + } + if (rLoc.v[0] > mod.xExtent[0] && rGlob.v[0] < mod.xExtent[1]) { + if (debug) System.out.format(" Adding SiModule with no hits at layer %d wafer %d\n", lyr, mod.detector); + modHit = mod; + break; + } + } + SiMoccupied.add(modHit); + } + } else { + logger.warning(String.format("SVT layer %d has no SiModules", lyr)); + } + } + + Collections.sort(SiMoccupied, new SortByLayer()); + if (debug) { + for (int i = 0; i < SiMoccupied.size(); i++) { + SiModule SiM = SiMoccupied.get(i); + int lyr = SiM.Layer; + int nHits = SiM.hits.size(); + if (nHits == 0) { + System.out.format("refitTrackWithE: layer %d det %d with no hits\n", lyr, SiM.detector); + continue; + } + double vHit = SiM.hits.get(0).v; + double eHit = SiM.hits.get(0).sigma; + double vTrue = SiM.hits.get(0).vTrue; + System.out.format("refitTrackWithE: layer %d det %d stereo=%b #hits=%d m=%9.5f+-%8.5f vTrue=%9.5f\n", lyr, SiM.detector, SiM.isStereo, nHits, vHit, eHit, vTrue); + } + } + + if (SiMoccupied.size() < 6) { + System.out.format("refitTrackWithE: only %d hits on track, and we need at least 6\n", SiMoccupied.size()); + return track; + } + + int startIndex = 0; + if (debug) System.out.format("refitTrackWithE: createKTF: using %d SiModules, startIndex %d \n", SiMoccupied.size(), startIndex); + CommonOps_DDRM.scale(1000., helixCov); + // Do the Kalman track fit only up through the filter step + KalTrack newTrack = kalmanFilterTrack(event.getEventNumber(), track.hashCode(), SiMoccupied, null, kalHelixParams, pivot, helixCov); + if (newTrack == null) return null; + if (debug) newTrack.print("filtered track"); + + if (newTrack.bad || newTrack.nHits < 6) { + System.out.format("refitTrackWithE: bad filter of track %d, only %d hits\n", newTrack.ID, newTrack.nHits); + return track; + } + + // Include the eCal information and smooth back toward the target + newTrack.smoothIt(eConstraint, energy, sigmaE); + if (debug) { + if (eConstraint) newTrack.print("energy constrained"); + else newTrack.print("newly smoothed"); + } + + // Iterate the track fit + if (!eConstraint) { + newTrack.fit(true); // This refit method does not include energy constraint + if (debug) { + newTrack.print("newly smoothed after refit"); + } + } + + // Convert the KalTrack object into and HPS Track and TrackState + Track outputTrack = createTrack(newTrack, true, event); + + trackHitsKalman.clear(); + for (SiModule SiM : SiMlist) { + SiM.hits.clear(); + } + return outputTrack; + } + + /** + * Run the Kalman filter (no smoothing) on a set of hits. + * @param eventNumber + * @param data List of Si modules with data points to be included in the fit + * @param hits Which hit to use in each SiModule. Can be null if each module has only 1 hit. + * @param helixParams 5 helix parameters for the starting "guess" helix + * @param pivot Pivot point for the starting "guess" helix + * @param C Full covariance matrix for the starting "guess" helix + * @return The new filtered KalTrack object + */ + KalTrack kalmanFilterTrack(int eventNumber, int tkID, ArrayList data, ArrayList hits, Vec helixParams, Vec pivot, DMatrixRMaj C) { + + //boolean debug = true; + if (debug) { + System.out.format("Entering KalmanInterface.KalmanFilterTrack: event %d, data size=%d\n", eventNumber, data.size()); + System.out.format(" pivot = %s\n", pivot.toString()); + } + // Create an state vector to initialize the Kalman filter + Vec Bfield = null; + if (kPar.uniformB) { + Bfield = KalmanInterface.getField(new Vec(0., kPar.SVTcenter, 0.), fM); + } else { + Bfield = KalmanInterface.getField(pivot, fM); + } + double B = Bfield.mag(); + Vec t = Bfield.unitVec(B); + StateVector sI = new StateVector(-1, helixParams, C, pivot, B, t, new Vec(0., 0., 0.), kPar.uniformB); + if (debug) sI.print("KalmanFilterTrack initial"); + ArrayList sites = new ArrayList(data.size()); + MeasurementSite prevSite = null; + double chi2f = 0.; + MeasurementSite newSite = null; + boolean success = true; + for (int idx=0; idx 0) { + if (hits == null) { + hitNumber = 0; + } else { + hitNumber = hits.get(idx); + } + } + if (debug) System.out.format("KalmanFilterTrack: layer %d, hit=%d\n", m.Layer, hitNumber); + newSite = new MeasurementSite(idx, m, kPar); + if (prevSite == null) { + int rc = newSite.makePrediction(sI, hitNumber, false, false); + if (rc < 0) { + logger.warning(String.format("kalmanFilterTrack: failed to make initial prediction at site %d, idx=%d. Abort", sites.indexOf(newSite), idx)); + success = false; + break; + } + if (debug) System.out.format(" Initial prediction rc=%d\n", rc); + } else { + int rc = newSite.makePrediction(prevSite.aF, prevSite.m, hitNumber, false, false); + if ( rc < 0) { + logger.warning(String.format("kalmanFilterTrack: failed to make prediction at site %d, idx=%d. Abort", sites.indexOf(newSite), idx)); + success = false; + break; + } + if (debug) System.out.format(" Prediction at layer %d rc=%d\n", m.Layer, rc); + } + //if (debug) newSite.print("new site"); + if (!newSite.filter()) { + logger.warning(String.format("kalmanFilterTrack failed to filter at site %d, idx=%d. Ignore remaining sites", sites.indexOf(newSite), idx)); + if (debug) System.out.format("KalmanFilterTrack: aborting at layer %d for bad filter\n", newSite.m.Layer); + success = false; + break; + } + + if (m.Layer >= 0 && hitNumber >= 0) chi2f += newSite.chi2inc; + newSite.filtered = true; + sites.add(newSite); + prevSite = newSite; + } + if (debug) { + for (MeasurementSite site : sites) { + System.out.format("kalmanFilterTrack: hit on layer %d, ID=%d\n", site.m.Layer, site.hitID); + } + System.out.format("kalmanFilterTrack: filter chi^2 = %9.3f\n", chi2f); + } + ArrayList yScat = new ArrayList(); + ArrayList XLscat = new ArrayList(); + if (success) return new KalTrack(eventNumber, tkID, sites, yScat, XLscat, kPar); + else return null; + } + + /** + * Sort the Kalman-filter geometry objects according to tracker layer + * + */ class SortByLayer implements Comparator { + @Override public int compare(SiModule o1, SiModule o2) { return o1.Layer - o2.Layer; } } - // Method to drive the Kalman-Filter based pattern recognition - public ArrayList[] KalmanPatRec(EventHeader event, IDDecoder decoder) { - if (debug) System.out.format("KalmanInterface: entering KalmanPatRec for event %d\n", event.getEventNumber()); + /** + * Method to drive the Kalman-Filter based pattern recognition + * + * @param event Event header + * @return Two lists of Kalman KalTrack objects, for top and bottom + * detectors + */ + public ArrayList[] KalmanPatRec(EventHeader event) { + if (debug) { + System.out.format("KalmanInterface: entering KalmanPatRec for event %d\n", event.getEventNumber()); + } ArrayList[] outList = new ArrayList[2]; if (!fillAllMeasurements(event)) { - if (debug) System.out.format("KalmanInterface.KalmanPatRec: recon SVT hits not found for event %d\n",event.getEventNumber()); - for (int topBottom=0; topBottom<2; ++topBottom) { + if (debug) { + System.out.format("KalmanInterface.KalmanPatRec: recon SVT hits not found for event %d\n", event.getEventNumber()); + } + for (int topBottom = 0; topBottom < 2; ++topBottom) { outList[topBottom] = new ArrayList(); } return outList; // Return empty track lists if there are no hits } int evtNum = event.getEventNumber(); - - for (int topBottom=0; topBottom<2; ++topBottom) { + + for (int topBottom = 0; topBottom < 2; ++topBottom) { ArrayList SiMoccupied = new ArrayList(); for (SiModule SiM : SiMlist) { - if (SiM.topBottom != topBottom) continue; + if (SiM.topBottom != topBottom) { + continue; + } //if (topBottom == 0) { // if (SiM.p.X().v[2] < 0.) continue; //} else { @@ -1234,7 +2122,7 @@ public ArrayList[] KalmanPatRec(EventHeader event, IDDecoder decoder) SiMoccupied.add(SiM); // Need to keep all of these even if there are no hits!!!!!! } Collections.sort(SiMoccupied, new SortByLayer()); - + if (debug) { for (int i = 0; i < SiMoccupied.size(); i++) { SiModule SiM = SiMoccupied.get(i); @@ -1243,21 +2131,30 @@ public ArrayList[] KalmanPatRec(EventHeader event, IDDecoder decoder) System.out.format("KalmanInterface.KalmanPatRec event %d: calling KalmanPatRecHPS for topBottom=%d\n", event.getEventNumber(), topBottom); } outList[topBottom] = kPat.kalmanPatRec(event, hitMap, SiMoccupied, topBottom); + + //for (KalTrack tkr : outList[topBottom]) tkr.printLong("Kalman Track"); } return outList; } - // The following method is a debugging aid for comparing SeedTracker/GBL tracks to the Kalman counterparts. + /** + * The following method is a debugging aid for comparing SeedTracker/GBL + * tracks to the Kalman counterparts. + * + * @param trackCollectionName Tracks to be compared to Kalman + * @param event Event header + * @param kPatList Lists of all Kalman tracks top and bottom + */ public void compareAllTracks(String trackCollectionName, EventHeader event, ArrayList[] kPatList) { if (!event.hasCollection(Track.class, trackCollectionName)) { - System.out.format("\nKalmanInterface.compareAllTracks: the track collection %s is missing. Abort.\n",trackCollectionName); + System.out.format("\nKalmanInterface.compareAllTracks: the track collection %s is missing. Abort.\n", trackCollectionName); return; } String stripHitInputCollectionName = "StripClusterer_SiTrackerHitStrip1D"; if (!event.hasCollection(TrackerHit.class, stripHitInputCollectionName)) { - System.out.format("\nKalmanInterface.compareAllTracks: the hit collection %s is missing. Abort.\n",stripHitInputCollectionName); + System.out.format("\nKalmanInterface.compareAllTracks: the hit collection %s is missing. Abort.\n", stripHitInputCollectionName); return; - } + } List tracksGBL = event.get(Track.class, trackCollectionName); System.out.format("\nPrinting %s tracks for event %d\n", trackCollectionName, event.getEventNumber()); RelationalTable hitToStrips = TrackUtils.getHitToStripsTable(event); @@ -1277,19 +2174,19 @@ public void compareAllTracks(String trackCollectionName, EventHeader event, Arra System.out.format("Track %d, missing TrackState.\n", tracksGBL.indexOf(tkr)); continue; } - double [] a = new double[5]; - for (int i=0; i<5; ++i) { + double[] a = new double[5]; + for (int i = 0; i < 5; ++i) { a[i] = ts1.getParameter(i); } double[] covHPS = ts1.getCovMatrix(); double Q = tkr.getCharge(); double chi2 = tkr.getChi2(); int nHits = tkr.getTrackerHits().size(); - System.out.format("Track %d, Q=%4.1f, %d 3D hits, chi^2=%7.1f, helix=%8.3f %9.6f %9.6f %8.4f %8.4f\n", tracksGBL.indexOf(tkr), + System.out.format("Track %d, Q=%4.1f, %d 3D hits, chi^2=%7.1f, helix=%8.3f %9.6f %9.6f %8.4f %8.4f\n", tracksGBL.indexOf(tkr), Q, nHits, chi2, a[0], a[1], a[2], a[3], a[4]); - Vec kalParms = new Vec(5,unGetLCSimParams(a, alphaCenter)); + Vec kalParms = new Vec(5, unGetLCSimParams(a, alphaCenter)); System.out.format(" Helix in Kalman parameterization = %s\n", kalParms.toString()); -/* for (TrackerHit hit3D : tkr.getTrackerHits()) { + /* for (TrackerHit hit3D : tkr.getTrackerHits()) { double [] hitPos3D = hit3D.getPosition(); System.out.format("compareAllTracks: tracker 3D hit %10.6f %10.6f %10.6f\n", hitPos3D[0], hitPos3D[1], hitPos3D[2]); List hits = new ArrayList(); @@ -1307,13 +2204,13 @@ public void compareAllTracks(String trackCollectionName, EventHeader event, Arra } } } - */ + */ List hitsOnTrack = TrackUtils.getStripHits(tkr, hitToStrips, hitToRotated); //System.out.format(" hitsOnTrack = %s, %d\n", hitsOnTrack.toString(), hitsOnTrack.size()); - int [] chanGBL = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; - int [] chanKAL = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; - for (TrackerHit ht : hitsOnTrack) { - double [] pnt = ht.getPosition(); + int[] chanGBL = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + int[] chanKAL = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + for (TrackerHit ht : hitsOnTrack) { + double[] pnt = ht.getPosition(); System.out.format(" Hit global position: %10.6f %10.6f %10.6f\n", pnt[0], pnt[1], pnt[2]); List rawHits = ht.getRawHits(); for (RawTrackerHit rawHit : rawHits) { @@ -1322,7 +2219,9 @@ public void compareAllTracks(String trackCollectionName, EventHeader event, Arra int Layer = sensor.getLayerNumber(); int sensorID = sensor.getModuleNumber(); System.out.format(" Raw hit in layer %d, sensor %d, channel %d\n", Layer, sensorID, chan); - if (sensorID*10000 + chan > chanGBL[Layer-1]) chanGBL[Layer-1] = sensorID*10000 + chan; + if (sensorID * 10000 + chan > chanGBL[Layer - 1]) { + chanGBL[Layer - 1] = sensorID * 10000 + chan; + } } } //double [] pnt0 = hitsOnTrack.get(0).getPosition(); @@ -1335,12 +2234,16 @@ public void compareAllTracks(String trackCollectionName, EventHeader event, Arra //} int topBottom = 0; int nGood = 0; - if (kalParms.v[4] < 0.) topBottom = 1; + if (kalParms.v[4] < 0.) { + topBottom = 1; + } KalTrack kMatch = null; for (KalTrack ktk : kPatList[topBottom]) { int nMatch = 0; for (MeasurementSite site : ktk.SiteList) { - if (site.hitID < 0) continue; + if (site.hitID < 0) { + continue; + } SiModule mod = site.m; if (mod != null) { TrackerHit ht = this.getHpsHit(mod.hits.get(site.hitID)); @@ -1350,7 +2253,7 @@ public void compareAllTracks(String trackCollectionName, EventHeader event, Arra HpsSiSensor sensor = (HpsSiSensor) rawHit.getDetectorElement(); int Layer = sensor.getLayerNumber(); int sensorID = sensor.getModuleNumber(); - if (chanGBL[Layer-1] == 10000*sensorID + chan) { + if (chanGBL[Layer - 1] == 10000 * sensorID + chan) { nMatch++; break; } @@ -1365,7 +2268,9 @@ public void compareAllTracks(String trackCollectionName, EventHeader event, Arra if (kMatch != null) { int nKalHits = 0; for (MeasurementSite site : kMatch.SiteList) { - if (site.hitID < 0) continue; + if (site.hitID < 0) { + continue; + } nKalHits++; SiModule mod = site.m; if (mod != null) { @@ -1376,12 +2281,14 @@ public void compareAllTracks(String trackCollectionName, EventHeader event, Arra HpsSiSensor sensor = (HpsSiSensor) rawHit.getDetectorElement(); int Layer = sensor.getLayerNumber(); int sensorID = sensor.getModuleNumber(); - if (10000*sensorID + chan > chanKAL[Layer-1]) chanKAL[Layer-1] = 10000*sensorID + chan; + if (10000 * sensorID + chan > chanKAL[Layer - 1]) { + chanKAL[Layer - 1] = 10000 * sensorID + chan; + } } } } System.out.format("GBL/Kalman match, ID=%d, %d hits, with %d matching layers\n", kMatch.ID, nKalHits, nGood); - for (int lyr=0; lyr<14; ++lyr) { + for (int lyr = 0; lyr < 14; ++lyr) { System.out.format(" Layer %d: GBL=%d KAL=%d\n", lyr, chanGBL[lyr], chanKAL[lyr]); } boolean refit = false; @@ -1398,16 +2305,19 @@ public void compareAllTracks(String trackCollectionName, EventHeader event, Arra if (refit) { ArrayList modList = new ArrayList(nGood); ArrayList hits = new ArrayList(nGood); - for (int lyr=0; lyr<14; ++lyr) { + for (int lyr = 0; lyr < 14; ++lyr) { if (chanGBL[lyr] >= 0) { for (SiModule mod : SiMlist) { - if (mod.Layer != lyr) continue; + if (mod.Layer != lyr) { + continue; + } int sensorID = mod.detector; - HitLoop: for (Measurement kalHt : mod.hits) { + HitLoop: + for (Measurement kalHt : mod.hits) { TrackerHit ht = this.getHpsHit(kalHt); List rawHits = ht.getRawHits(); for (RawTrackerHit rawHit : rawHits) { - if (10000*sensorID + rawHit.getIdentifierFieldValue("strip") == chanGBL[lyr]) { + if (10000 * sensorID + rawHit.getIdentifierFieldValue("strip") == chanGBL[lyr]) { modList.add(mod); hits.add(mod.hits.indexOf(kalHt)); break HitLoop; @@ -1416,8 +2326,8 @@ public void compareAllTracks(String trackCollectionName, EventHeader event, Arra } } } - } - + } + // The following is for testing by refitting the existing Kalman track //for (MeasurementSite site : kMatch.SiteList) { // if (site.hitID < 0) continue; @@ -1427,26 +2337,34 @@ public void compareAllTracks(String trackCollectionName, EventHeader event, Arra DMatrixRMaj cov = hx.C.copy(); CommonOps_DDRM.scale(10., cov); KalmanTrackFit2 kft2 = new KalmanTrackFit2(event.getEventNumber(), modList, hits, 0, 2, hx.X0, hx.a, cov, kPar, fM); - if (kft2 != null) kft2.printFit("refit with GBL hits"); + if (kft2 != null) { + kft2.printFit("refit with GBL hits"); + } } kMatch.print("matching Kalman track"); } } - } - + } + + /** + * Plots GBL tracks by creating an ASCII file to be displayed by GNUplot + * + * @param path Path to the folder where the output file should be written + * @param event Event header + */ public void plotGBLtracks(String path, EventHeader event) { - + String trackCollectionName = "GBLTracks"; if (!event.hasCollection(Track.class, trackCollectionName)) { - System.out.format("KalmanInterface.plotGBLtracks: the track collection %s is missing. Abort.\n",trackCollectionName); + System.out.format("KalmanInterface.plotGBLtracks: the track collection %s is missing. Abort.\n", trackCollectionName); return; } String stripHitInputCollectionName = "StripClusterer_SiTrackerHitStrip1D"; if (!event.hasCollection(TrackerHit.class, stripHitInputCollectionName)) { - System.out.format("KalmanInterface.plotGBLtracks: the hit collection %s is missing. Abort.\n",stripHitInputCollectionName); + System.out.format("KalmanInterface.plotGBLtracks: the hit collection %s is missing. Abort.\n", stripHitInputCollectionName); return; } - + PrintWriter printWriter3 = null; int eventNumber = event.getEventNumber(); String fn = String.format("%sGBLhelix_%d.gp", path, eventNumber); @@ -1465,7 +2383,6 @@ public void plotGBLtracks(String path, EventHeader event) { printWriter3.format("set title 'GBL Event Number %d'\n", eventNumber); printWriter3.format("set xlabel 'X'\n"); printWriter3.format("set ylabel 'Y'\n"); - List tracksGBL = event.get(Track.class, trackCollectionName); RelationalTable hitToStrips = TrackUtils.getHitToStripsTable(event); @@ -1473,55 +2390,58 @@ public void plotGBLtracks(String path, EventHeader event) { double vPos = 0.9; for (Track tkr : tracksGBL) { - double [] a = new double[5]; - for (int i=0; i<5; ++i) { + double[] a = new double[5]; + for (int i = 0; i < 5; ++i) { a[i] = tkr.getTrackStates().get(0).getParameter(i); } double Q = tkr.getCharge(); double chi2 = tkr.getChi2(); int nHits = tkr.getTrackerHits().size(); - String s = String.format("Track %d, Q=%4.1f, %d hits, chi^2=%7.1f, helix=%8.3f %8.3f %8.3f %8.3f %8.3f", tracksGBL.indexOf(tkr), + String s = String.format("Track %d, Q=%4.1f, %d hits, chi^2=%7.1f, helix=%8.3f %8.3f %8.3f %8.3f %8.3f", tracksGBL.indexOf(tkr), Q, nHits, chi2, a[0], a[1], a[2], a[3], a[4]); printWriter3.format("set label '%s' at screen 0.1, %2.2f\n", s, vPos); vPos = vPos - 0.03; } - + for (Track tkr : tracksGBL) { printWriter3.format("$tkr%d << EOD\n", tracksGBL.indexOf(tkr)); for (TrackState state : tkr.getTrackStates()) { int loc = state.getLocation(); if (loc != state.AtIP && loc != state.AtCalorimeter && loc != state.AtOther && loc != state.AtVertex) { - double [] pnt = state.getReferencePoint(); + double[] pnt = state.getReferencePoint(); printWriter3.format(" %10.6f %10.6f %10.6f\n", pnt[0], pnt[2], -pnt[1]); } } printWriter3.format("EOD\n"); - } - + } + for (Track tkr : tracksGBL) { printWriter3.format("$tkp%d << EOD\n", tracksGBL.indexOf(tkr)); List hitsOnTrack = TrackUtils.getStripHits(tkr, hitToStrips, hitToRotated); - for (TrackerHit ht : hitsOnTrack) { - double [] pnt = ht.getPosition(); + for (TrackerHit ht : hitsOnTrack) { + double[] pnt = ht.getPosition(); printWriter3.format(" %10.6f %10.6f %10.6f\n", pnt[0], pnt[2], -pnt[1]); } printWriter3.format("EOD\n"); } - + List stripHits = event.get(TrackerHit.class, stripHitInputCollectionName); printWriter3.format("$pnts << EOD\n"); - unUsedHits: for (TrackerHit ht : stripHits) { + unUsedHits: + for (TrackerHit ht : stripHits) { for (Track tkr : tracksGBL) { List hitsOnTrack = TrackUtils.getStripHits(tkr, hitToStrips, hitToRotated); for (TrackerHit ht2 : hitsOnTrack) { - if (ht2 == ht) continue unUsedHits; + if (ht2 == ht) { + continue unUsedHits; + } } } - double [] pnt = ht.getPosition(); + double[] pnt = ht.getPosition(); printWriter3.format(" %10.6f %10.6f %10.6f\n", pnt[0], pnt[2], -pnt[1]); } printWriter3.format("EOD\n"); - + printWriter3.format("splot $pnts u 1:2:3 with points pt 6 ps 2"); for (Track tkr : tracksGBL) { printWriter3.format(", $tkp%d u 1:2:3 with points pt 7 ps 2", tracksGBL.indexOf(tkr)); @@ -1530,10 +2450,17 @@ public void plotGBLtracks(String path, EventHeader event) { printWriter3.format("\n"); printWriter3.close(); } - // This method makes a Gnuplot file to display the Kalman tracks and hits in 3D. + + /** + * This method makes a Gnuplot file to display the Kalman tracks and hits in + * 3D. + * + * @param path Path to the folder where the output text file will be writtng + * @param event Event header + * @param patRecList Array of dimension 2 (up vs down) of lists of tracks to + * be plotted + */ public void plotKalmanEvent(String path, EventHeader event, ArrayList[] patRecList) { - - boolean debug = false; PrintWriter printWriter3 = null; int eventNumber = event.getEventNumber(); String fn = String.format("%shelix3_%d.gp", path, eventNumber); @@ -1553,19 +2480,21 @@ public void plotKalmanEvent(String path, EventHeader event, ArrayList[ printWriter3.format("set xlabel 'X'\n"); printWriter3.format("set ylabel 'Y'\n"); double vPos = 0.9; - for (int topBottom=0; topBottom<2; ++topBottom) { + for (int topBottom = 0; topBottom < 2; ++topBottom) { for (KalTrack tkr : patRecList[topBottom]) { - double [] a = tkr.originHelixParms(); - if (a == null) a = tkr.SiteList.get(0).aS.helix.a.v; - String s = String.format("TB %d Track %d, %d hits, chi^2=%7.1f, a=%8.3f %8.3f %8.3f %8.3f %8.3f t=%6.1f", + double[] a = tkr.originHelixParms(); + if (a == null) { + a = tkr.SiteList.get(0).aS.helix.a.v; + } + String s = String.format("TB %d Track %d, %d hits, chi^2=%7.1f, a=%8.3f %8.3f %8.3f %8.3f %8.3f t=%6.1f", topBottom, tkr.ID, tkr.nHits, tkr.chi2, a[0], a[1], a[2], a[3], a[4], tkr.getTime()); printWriter3.format("set label '%s' at screen 0.1, %2.2f\n", s, vPos); vPos = vPos - 0.03; } } - int [] nTkpL = {0, 0}; - int [] nTkpS = {0, 0}; - for (int topBottom=0; topBottom<2; ++topBottom) { // Plotting tracks as lines + int[] nTkpL = {0, 0}; + int[] nTkpS = {0, 0}; + for (int topBottom = 0; topBottom < 2; ++topBottom) { // Plotting tracks as lines for (KalTrack tkr : patRecList[topBottom]) { printWriter3.format("$tkr%d_%d << EOD\n", tkr.ID, topBottom); for (MeasurementSite site : tkr.SiteList) { @@ -1582,7 +2511,9 @@ public void plotKalmanEvent(String path, EventHeader event, ArrayList[ continue; } double phiS = aS.helix.planeIntersect(module.p); - if (Double.isNaN(phiS)) continue; + if (Double.isNaN(phiS)) { + continue; + } Vec rLocal = aS.helix.atPhi(phiS); Vec rGlobal = aS.helix.toGlobal(rLocal); printWriter3.format(" %10.6f %10.6f %10.6f\n", rGlobal.v[0], rGlobal.v[1], rGlobal.v[2]); @@ -1605,10 +2536,16 @@ public void plotKalmanEvent(String path, EventHeader event, ArrayList[ for (MeasurementSite site : tkr.SiteList) { SiModule module = site.m; int hitID = site.hitID; - if (hitID < 0) continue; + if (hitID < 0) { + continue; + } Measurement mm = module.hits.get(hitID); - if (mm.energy < kPar.minSeedE[module.Layer]) continue; - if (mm.tracks.size() > 1) continue; + if (mm.energy < kPar.minSeedE[module.Layer]) { + continue; + } + if (mm.tracks.size() > 1) { + continue; + } Vec rLoc = null; if (mm.rGlobal == null) { // If there is no MC truth, use the track intersection for x and z StateVector aS = site.aS; @@ -1628,16 +2565,18 @@ public void plotKalmanEvent(String path, EventHeader event, ArrayList[ } } else { if (debug) { - System.out.format("plotKalmanEvent %d: tk %d lyr %d phiS is NaN.\n", event.getEventNumber(),tkr.ID, module.Layer); + System.out.format("plotKalmanEvent %d: tk %d lyr %d phiS is NaN.\n", event.getEventNumber(), tkr.ID, module.Layer); aS.helix.a.print(" helix parameters "); } - rLoc = new Vec(0.,0.,0.); + rLoc = new Vec(0., 0., 0.); } } else { rLoc = module.toLocal(mm.rGlobal); // Use MC truth for the x and z coordinates in the detector frame } Vec rmG = module.toGlobal(new Vec(rLoc.v[0], mm.v, rLoc.v[2])); - if (debug) System.out.format("plotKalmanEvent %d: tk %d lyr %d rmG=%s\n", event.getEventNumber(), tkr.ID, module.Layer, rmG.toString()); + if (debug) { + System.out.format("plotKalmanEvent %d: tk %d lyr %d rmG=%s\n", event.getEventNumber(), tkr.ID, module.Layer, rmG.toString()); + } printWriter3.format(" %10.6f %10.6f %10.6f\n", rmG.v[0], rmG.v[1], rmG.v[2]); } printWriter3.format("EOD\n"); @@ -1647,10 +2586,16 @@ public void plotKalmanEvent(String path, EventHeader event, ArrayList[ for (MeasurementSite site : tkr.SiteList) { SiModule module = site.m; int hitID = site.hitID; - if (hitID < 0) continue; + if (hitID < 0) { + continue; + } Measurement mm = module.hits.get(hitID); - if (mm.energy < kPar.minSeedE[module.Layer]) continue; - if (mm.tracks.size() <= 1) continue; + if (mm.energy < kPar.minSeedE[module.Layer]) { + continue; + } + if (mm.tracks.size() <= 1) { + continue; + } Vec rLoc = null; if (mm.rGlobal == null) { // If there is no MC truth, use the track intersection for x and z StateVector aS = site.aS; @@ -1660,7 +2605,7 @@ public void plotKalmanEvent(String path, EventHeader event, ArrayList[ Vec rGlobal = aS.helix.toGlobal(rLocal); // Position in the global frame rLoc = module.toLocal(rGlobal); // Position in the detector frame } else { - rLoc = new Vec(0.,0.,0.); + rLoc = new Vec(0., 0., 0.); } } else { rLoc = module.toLocal(mm.rGlobal); // Use MC truth for the x and z coordinates in the detector frame @@ -1676,9 +2621,13 @@ public void plotKalmanEvent(String path, EventHeader event, ArrayList[ for (MeasurementSite site : tkr.SiteList) { SiModule module = site.m; int hitID = site.hitID; - if (hitID < 0) continue; + if (hitID < 0) { + continue; + } Measurement mm = module.hits.get(hitID); - if (mm.energy >= kPar.minSeedE[module.Layer]) continue; + if (mm.energy >= kPar.minSeedE[module.Layer]) { + continue; + } Vec rLoc = null; if (mm.rGlobal == null) { // If there is no MC truth, use the track intersection for x and z StateVector aS = site.aS; @@ -1688,7 +2637,7 @@ public void plotKalmanEvent(String path, EventHeader event, ArrayList[ Vec rGlobal = aS.helix.toGlobal(rLocal); // Position in the global frame rLoc = module.toLocal(rGlobal); // Position in the detector frame } else { - rLoc = new Vec(0.,0.,0.); + rLoc = new Vec(0., 0., 0.); } } else { rLoc = module.toLocal(mm.rGlobal); // Use MC truth for the x and z coordinates in the detector frame @@ -1703,11 +2652,15 @@ public void plotKalmanEvent(String path, EventHeader event, ArrayList[ printWriter3.format("$pnts << EOD\n"); for (SiModule si : SiMlist) { for (Measurement mm : si.hits) { // Plotting high-amplitude hits not on tracks - if (mm.tracks.size() > 0) continue; - if (mm.energy < kPar.minSeedE[si.Layer]) continue; + if (mm.tracks.size() > 0) { + continue; + } + if (mm.energy < kPar.minSeedE[si.Layer]) { + continue; + } Vec rLoc = null; if (mm.rGlobal == null) { - rLoc = new Vec(0.,0.,0.); // Use the center of the detector if there is no MC truth info + rLoc = new Vec(0., 0., 0.); // Use the center of the detector if there is no MC truth info } else { rLoc = si.toLocal(mm.rGlobal); // Use MC truth for the x and z coordinates in the detector frame } @@ -1719,11 +2672,15 @@ public void plotKalmanEvent(String path, EventHeader event, ArrayList[ printWriter3.format("$pntsL << EOD\n"); for (SiModule si : SiMlist) { for (Measurement mm : si.hits) { // Plotting low-amplitude hits not on tracks - if (mm.tracks.size() > 0) continue; - if (mm.energy >= kPar.minSeedE[si.Layer]) continue; + if (mm.tracks.size() > 0) { + continue; + } + if (mm.energy >= kPar.minSeedE[si.Layer]) { + continue; + } Vec rLoc = null; if (mm.rGlobal == null) { - rLoc = new Vec(0.,0.,0.); // Use the center of the detector if there is no MC truth info + rLoc = new Vec(0., 0., 0.); // Use the center of the detector if there is no MC truth info } else { rLoc = si.toLocal(mm.rGlobal); // Use MC truth for the x and z coordinates in the detector frame } @@ -1736,13 +2693,17 @@ public void plotKalmanEvent(String path, EventHeader event, ArrayList[ printWriter3.format("splot $pnts u 1:2:3 with points pt 6 ps 2 lc %d", idx); idx++; printWriter3.format(", $pntsL u 1:2:3 with points pt 4 ps 1 lc %d", idx); - for (int topBottom=0; topBottom<2; ++topBottom) { - for (KalTrack tkr : patRecList[topBottom]) { + for (int topBottom = 0; topBottom < 2; ++topBottom) { + for (KalTrack tkr : patRecList[topBottom]) { idx++; - printWriter3.format(", $tkr%d_%d u 1:2:3 with lines lw 3 lc %d", tkr.ID, topBottom, idx); - printWriter3.format(", $tkp%d_%d u 1:2:3 with points pt 7 ps 2 lc %d", tkr.ID, topBottom, idx); - if (nTkpL[topBottom] > 0) printWriter3.format(", $tkpL%d_%d u 1:2:3 with points pt 9 ps 2 lc %d", tkr.ID, topBottom, idx); - if (nTkpS[topBottom] > 0) printWriter3.format(", $tkpS%d_%d u 1:2:3 with points pt 15 ps 2 lc %d", tkr.ID, topBottom, idx); + printWriter3.format(", $tkr%d_%d u 1:2:3 with lines lw 3 lc %d", tkr.ID, topBottom, idx); + printWriter3.format(", $tkp%d_%d u 1:2:3 with points pt 7 ps 2 lc %d", tkr.ID, topBottom, idx); + if (nTkpL[topBottom] > 0) { + printWriter3.format(", $tkpL%d_%d u 1:2:3 with points pt 9 ps 2 lc %d", tkr.ID, topBottom, idx); + } + if (nTkpS[topBottom] > 0) { + printWriter3.format(", $tkpS%d_%d u 1:2:3 with points pt 15 ps 2 lc %d", tkr.ID, topBottom, idx); + } } } printWriter3.format("\n"); diff --git a/tracking/src/main/java/org/hps/recon/tracking/kalman/KalmanKinkFit.java b/tracking/src/main/java/org/hps/recon/tracking/kalman/KalmanKinkFit.java index e5c3aea834..0621db8abe 100644 --- a/tracking/src/main/java/org/hps/recon/tracking/kalman/KalmanKinkFit.java +++ b/tracking/src/main/java/org/hps/recon/tracking/kalman/KalmanKinkFit.java @@ -17,9 +17,10 @@ import java.util.Set; import org.lcsim.event.GenericObject; - -// Break a Kalman track into two halves and fit them separately. -// Find the kink angle between the two halves +/** + * Break a Kalman track into two halves and fit them separately. + * Find the kink angle between the two halves + */ public class KalmanKinkFit { private KalmanTrackFit2 innerTrack, outerTrack; private Vec innerP, outerP; diff --git a/tracking/src/main/java/org/hps/recon/tracking/kalman/KalmanKinkFitDriver.java b/tracking/src/main/java/org/hps/recon/tracking/kalman/KalmanKinkFitDriver.java index 877d82aedb..9d4578fd16 100644 --- a/tracking/src/main/java/org/hps/recon/tracking/kalman/KalmanKinkFitDriver.java +++ b/tracking/src/main/java/org/hps/recon/tracking/kalman/KalmanKinkFitDriver.java @@ -19,6 +19,10 @@ import hep.aida.IHistogram1D; +/** + * Driver to study kinks in Kalman tracks, measured by breaking the track into two halves + * + */ public class KalmanKinkFitDriver extends Driver { private ArrayList detPlanes; @@ -65,7 +69,7 @@ public void detectorChanged(Detector det) { KalmanParams kPar = new KalmanParams(); kPar.print(); - KI = new KalmanInterface(false, kPar, fm); + KI = new KalmanInterface(kPar, det, fm); KI.createSiModules(detPlanes); } diff --git a/tracking/src/main/java/org/hps/recon/tracking/kalman/KalmanParams.java b/tracking/src/main/java/org/hps/recon/tracking/kalman/KalmanParams.java index eb0bb209c1..f1c57b063b 100644 --- a/tracking/src/main/java/org/hps/recon/tracking/kalman/KalmanParams.java +++ b/tracking/src/main/java/org/hps/recon/tracking/kalman/KalmanParams.java @@ -6,50 +6,62 @@ /** * Parameters used by the Kalman-Filter pattern recognition and fitting - */ + */ public class KalmanParams { + static final int mxTrials = 2; // Max number of iterations through the entire pattern recognition; not configurable int nTrials; // Number of iterations through the entire pattern recognition int nIterations; // Number of Kalman-fit iterations in the final fit - double[] kMax; + double[] kMax; double kMin; - double[] tanlMax; - double[] dRhoMax; + double[] tanlMax; + double[] dRhoMax; double[] dzMax; - double[] chi2mx1; + double[] chi2mx1; int minHits0; - int[] minHits1; - double mxChi2Inc; + int[] minHits1; + double mxChi2Inc; double minChi2IncBad; double mxChi2Vtx; - double[] mxResid; - double mxResidShare; + double[] mxResid; + double mxResidShare; double mxChi2double; int firstLayer; - int mxShared; - int [] minStereo; + int mxShared; + int[] minStereo; int minAxial; double mxTdif; double lowPhThresh; double seedCompThr; // Compatibility threshold for seedTracks helix parameters; - ArrayList [] lyrList; - double [] beamSpot; - double [] vtxSize; - double [] minSeedE; + ArrayList[] lyrList; + double[] beamSpot; + double[] vtxSize; + double[] minSeedE; double edgeTolerance; static final int numLayers = 14; + boolean uniformB; + boolean eLoss; + double [] eRes; private int[] Swap = {1,0, 3,2, 5,4, 7,6, 9,8, 11,10, 13,12}; private String [] tb; private Logger logger; int maxListIter1; - + double SVTcenter = 505.57; // Location to evaluate the field in case it is assumed uniform + + /** + * Print all the Kalman Tracking parameters values (good idea to call it at + * the beginning of the run) + */ public void print() { System.out.format("\nKalmanParams: dump of the Kalman pattern recognition cuts and parameters\n"); System.out.println(" (In the case of two values, they refer to the two iterations.)"); System.out.format(" There are %d layers in the tracker.\n", numLayers); + if (uniformB) { + System.out.format(" The magnetic field is assumed to be uniform!\n"); + } System.out.format(" Cluster energy cuts for seeds, by layer: "); - for (int lyr=0; lyr(); - } + } // 0 1 2 3 4 5 6 // A S A S A S A S A S A S A S top // 0,1,2,3,4,5,6,7,8,9,10,11,12,13 @@ -185,7 +208,7 @@ public KalmanParams() { addStrategy("SBB0000"); addStrategy("SABS000"); maxListIter1 = 16; // The maximum index for lyrList for the first iteration - + beamSpot = new double[3]; beamSpot[0] = 0.; beamSpot[1] = 0.; @@ -212,251 +235,283 @@ public KalmanParams() { // } } + public void setEnergyRes(double a, double b, double c) { + if (a > 0.) eRes[0] = a; + if (b > 0.) eRes[1] = b; + if (c > 0.) eRes[2] = c; + logger.config(String.format("Setting CAL energy resolution to %8.2f/E + %8.2f/sqrt(E) + %8.2f", eRes[0], eRes[1], eRes[2])); + } + public double getEres(int i) { + if (i<0 || i>1) { + logger.warning(String.format("KalmanParams: invalid eRes index %d\n", i)); + return eRes[0]; + } + return eRes[i]; + } + + public void setUniformB(boolean input) { + logger.config(String.format("Setting the field to be uniform? %b", input)); + uniformB = input; + } + + public void setEloss(boolean eLoss) { + logger.config(String.format("Setting the energy loss to %b", eLoss)); + this.eLoss = eLoss; + } + public void setLowPhThreshold(double cut) { - if (cut <0. || cut > 1.) { + if (cut < 0. || cut > 1.) { logger.warning(String.format("low pulse-height threshold %10.4f is not valid and is ignored.", cut)); return; } logger.config(String.format("Setting the low-pulse-height threshold to %10.4f", cut)); lowPhThresh = cut; } - + public void setMinSeedEnergy(double minE) { logger.config("Setting the minimum seed energy to " + Double.toString(minE)); - for (int lyr=0; lyr mxTrials) { - logger.log(Level.WARNING,String.format("Number of global iterations %d is not valid and is ignored.", nTrials)); + logger.log(Level.WARNING, String.format("Number of global iterations %d is not valid and is ignored.", nTrials)); return; } logger.log(Level.CONFIG, String.format("Setting the number of global patrec iterations to %d", nTrials)); this.nTrials = nTrials; } - + public void setFirstLayer(int firstLayer) { if (firstLayer != 0 && firstLayer != 2) { - logger.log(Level.WARNING,String.format("First layer of %d is not valid and is ignored.", firstLayer)); + logger.log(Level.WARNING, String.format("First layer of %d is not valid and is ignored.", firstLayer)); return; } logger.log(Level.CONFIG, String.format("Setting the first tracking layer to %d", firstLayer)); this.firstLayer = firstLayer; } - + public void setIterations(int N) { if (N < 1) { - logger.log(Level.WARNING,String.format("%d iterations not allowed.", N)); + logger.log(Level.WARNING, String.format("%d iterations not allowed.", N)); return; } - logger.log(Level.CONFIG,String.format("Setting the number of Kalman Filter iterations to %d.", N)); + logger.log(Level.CONFIG, String.format("Setting the number of Kalman Filter iterations to %d.", N)); nIterations = N; } - + public void setMxChi2Inc(double mxC) { if (mxC <= 1.) { - logger.log(Level.WARNING,String.format("Maximum chi^2 increment must be at least unity. %8.2f not valid.", mxC)); + logger.log(Level.WARNING, String.format("Maximum chi^2 increment must be at least unity. %8.2f not valid.", mxC)); return; } - logger.log(Level.CONFIG,String.format("Maximum chi^2 increment to add a hit to a track to %8.2f.", mxC)); + logger.log(Level.CONFIG, String.format("Maximum chi^2 increment to add a hit to a track to %8.2f.", mxC)); mxChi2Inc = mxC; } - + public void setMxChi2double(double mxDb) { if (mxDb <= 0.) { - logger.log(Level.WARNING,String.format("Maximum chi^2 increment of shared hit of %8.2f not allowed.", mxDb)); + logger.log(Level.WARNING, String.format("Maximum chi^2 increment of shared hit of %8.2f not allowed.", mxDb)); return; } - logger.log(Level.CONFIG,String.format("Setting the maximum chi^2 increment of shared hit to %8.2f sigma.", mxDb)); - mxChi2double = mxDb; + logger.log(Level.CONFIG, String.format("Setting the maximum chi^2 increment of shared hit to %8.2f sigma.", mxDb)); + mxChi2double = mxDb; } - + public void setMinChi2IncBad(double mnB) { if (mnB <= 3.0) { - logger.log(Level.WARNING,String.format("Minimum chi^2 increment to remove a bad hit must be at least 3. %8.2f not valid.", mnB)); + logger.log(Level.WARNING, String.format("Minimum chi^2 increment to remove a bad hit must be at least 3. %8.2f not valid.", mnB)); return; } - logger.log(Level.CONFIG,String.format("Setting the minimum chi^2 increment to remove a bad hit to %8.2f.", mnB)); - minChi2IncBad = mnB; + logger.log(Level.CONFIG, String.format("Setting the minimum chi^2 increment to remove a bad hit to %8.2f.", mnB)); + minChi2IncBad = mnB; } - + public void setMxResidShare(double mxSh) { if (mxSh <= 0.) { - logger.log(Level.WARNING,String.format("Maximum residual of shared hit of %8.2f not allowed.", mxSh)); + logger.log(Level.WARNING, String.format("Maximum residual of shared hit of %8.2f not allowed.", mxSh)); return; } - logger.log(Level.CONFIG,String.format("Setting the maximum residual for a shared hit to %8.2f sigma.", mxSh)); - mxResidShare = mxSh; + logger.log(Level.CONFIG, String.format("Setting the maximum residual for a shared hit to %8.2f sigma.", mxSh)); + mxResidShare = mxSh; } - + public void setMaxK(double kMx) { if (kMx <= 0.) { - logger.log(Level.WARNING,String.format("Max 1/pt of %8.2f not allowed.", kMx)); + logger.log(Level.WARNING, String.format("Max 1/pt of %8.2f not allowed.", kMx)); return; } - logger.log(Level.CONFIG,String.format("Setting the maximum 1/pt to %8.2f.", kMx)); + logger.log(Level.CONFIG, String.format("Setting the maximum 1/pt to %8.2f.", kMx)); kMax[1] = kMx; - kMax[0] = Math.min(kMax[0], 0.5*kMx); + kMax[0] = Math.min(kMax[0], 0.5 * kMx); } - + void setMinK(double kMn) { if (kMn < 0.) { - logger.log(Level.WARNING,String.format("Min 1/pt of %8.2f not allowed.", kMn)); + logger.log(Level.WARNING, String.format("Min 1/pt of %8.2f not allowed.", kMn)); return; } kMin = kMn; } - + public void setMxResid(double mxR) { if (mxR <= 1.) { - logger.log(Level.WARNING,String.format("Max resid of %8.2f not allowed.", mxR)); + logger.log(Level.WARNING, String.format("Max resid of %8.2f not allowed.", mxR)); return; } - logger.log(Level.CONFIG,String.format("Setting the maximum residual to pick up hits to %8.2f sigma.", mxR)); + logger.log(Level.CONFIG, String.format("Setting the maximum residual to pick up hits to %8.2f sigma.", mxR)); mxResid[1] = mxR; - mxResid[0] = Math.min(mxResid[0], 0.5*mxR); + mxResid[0] = Math.min(mxResid[0], 0.5 * mxR); } - + public void setMaxTanL(double tlMx) { if (tlMx <= 0.) { - logger.log(Level.WARNING,String.format("Max seed tan(lambda) of %8.2f not allowed.", tlMx)); + logger.log(Level.WARNING, String.format("Max seed tan(lambda) of %8.2f not allowed.", tlMx)); return; } - logger.log(Level.CONFIG,String.format("Setting the maximum seed tan(lambda) to %8.2f.", tlMx)); + logger.log(Level.CONFIG, String.format("Setting the maximum seed tan(lambda) to %8.2f.", tlMx)); tanlMax[1] = tlMx; - tanlMax[0] = Math.min(tanlMax[0], 0.8*tlMx); + tanlMax[0] = Math.min(tanlMax[0], 0.8 * tlMx); } - + public void setMaxdRho(double dMx) { if (dMx <= 0.0) { - logger.log(Level.WARNING,String.format("Max dRho of %8.2f not allowed.", dMx)); + logger.log(Level.WARNING, String.format("Max dRho of %8.2f not allowed.", dMx)); return; } - logger.log(Level.CONFIG,String.format("Setting the maximum dRho to %8.2f mm.", dMx)); + logger.log(Level.CONFIG, String.format("Setting the maximum dRho to %8.2f mm.", dMx)); dRhoMax[1] = dMx; - dRhoMax[0] = Math.min(dRhoMax[0], 0.6*dMx); + dRhoMax[0] = Math.min(dRhoMax[0], 0.6 * dMx); } - + public void setMaxdZ(double zMx) { if (zMx <= 0.0) { - logger.log(Level.WARNING,String.format("Max dZ of %8.2f not allowed.", zMx)); + logger.log(Level.WARNING, String.format("Max dZ of %8.2f not allowed.", zMx)); return; } - logger.log(Level.CONFIG,String.format("Setting the maximum dz to %8.2f mm.", zMx)); + logger.log(Level.CONFIG, String.format("Setting the maximum dz to %8.2f mm.", zMx)); dzMax[1] = zMx; - dzMax[0] = Math.min(dzMax[0], 0.4*zMx); + dzMax[0] = Math.min(dzMax[0], 0.4 * zMx); } - + public void setMaxChi2(double xMx) { if (xMx <= 0.) { - logger.log(Level.WARNING,String.format("Max chi2 of %8.2f not allowed.", xMx)); + logger.log(Level.WARNING, String.format("Max chi2 of %8.2f not allowed.", xMx)); return; } - logger.log(Level.CONFIG,String.format("Setting the maximum chi^2/hit to %8.2f.", xMx)); + logger.log(Level.CONFIG, String.format("Setting the maximum chi^2/hit to %8.2f.", xMx)); chi2mx1[1] = xMx; - chi2mx1[0] = Math.min(chi2mx1[0], 0.5*xMx); + chi2mx1[0] = Math.min(chi2mx1[0], 0.5 * xMx); } - + public void setMaxChi2Vtx(double xMx) { if (xMx <= 0.) { - logger.log(Level.WARNING,String.format("Max chi2 of %8.2f not allowed.", xMx)); + logger.log(Level.WARNING, String.format("Max chi2 of %8.2f not allowed.", xMx)); return; } - logger.log(Level.CONFIG,String.format("Setting the maximum chi^2 for 5-hit tracks with vertex constraint to %8.2f.", xMx)); + logger.log(Level.CONFIG, String.format("Setting the maximum chi^2 for 5-hit tracks with vertex constraint to %8.2f.", xMx)); mxChi2Vtx = xMx; } - + public void setMinHits(int minH) { if (minH < 5) { - logger.log(Level.WARNING,String.format("Minimum number of hits = %d not allowed.", minH)); + logger.log(Level.WARNING, String.format("Minimum number of hits = %d not allowed.", minH)); return; } - logger.log(Level.CONFIG,String.format("Setting the minimum number of hits to %d.", minH)); + logger.log(Level.CONFIG, String.format("Setting the minimum number of hits to %d.", minH)); minHits1[1] = minH; - minHits1[0] = Math.max(minHits1[0], minH+1); + minHits1[0] = Math.max(minHits1[0], minH + 1); } - + public void setMinStereo(int minS) { if (minS < 3) { - logger.log(Level.WARNING,String.format("Minimum number of stereo hits = %d not allowed.", minS)); + logger.log(Level.WARNING, String.format("Minimum number of stereo hits = %d not allowed.", minS)); return; } - logger.log(Level.CONFIG,String.format("Setting the minimum number of stereo hits to %d.", minS)); + logger.log(Level.CONFIG, String.format("Setting the minimum number of stereo hits to %d.", minS)); minStereo[1] = minS; - minStereo[0] = Math.max(minStereo[0], minS+1); + minStereo[0] = Math.max(minStereo[0], minS + 1); } - + public void setMaxShared(int mxSh) { - logger.log(Level.CONFIG,String.format("Setting the maximum number of shared hits to %d.", mxSh)); + logger.log(Level.CONFIG, String.format("Setting the maximum number of shared hits to %d.", mxSh)); mxShared = mxSh; } - + public void setMaxTimeRange(double mxT) { - logger.log(Level.CONFIG,String.format("Setting the maximum time range for hits on a track to %8.2f ns.", mxT)); + logger.log(Level.CONFIG, String.format("Setting the maximum time range for hits on a track to %8.2f ns.", mxT)); mxTdif = mxT; } - public void setSeedCompThr(double seedComp_Thr) { + public void setSeedCompThr(double seedComp_Thr) { if (seedComp_Thr < 0.) { logger.log(Level.CONFIG, "SeedTracks duplicate removal is turned off."); return; } - logger.log(Level.CONFIG, String.format("Setting the SeedTracks duplicate removal threshold to %f percent.",seedComp_Thr*100.)); + logger.log(Level.CONFIG, String.format("Setting the SeedTracks duplicate removal threshold to %f percent.", seedComp_Thr * 100.)); seedCompThr = seedComp_Thr; } public void setBeamSpotY(double spot) { beamSpot[1] = spot; - logger.log(Level.CONFIG, String.format("Setting the Y beam spot location to %f %f %f", beamSpot[0], beamSpot[1], beamSpot[2])); + logger.log(Level.CONFIG, String.format("Setting the Y beam spot location to %f %f %f", beamSpot[0], beamSpot[1], beamSpot[2])); } - + public void setBeamSizeY(double size) { vtxSize[1] = size; - logger.log(Level.CONFIG, String.format("Setting the Y beam spot size to %f %f %f", vtxSize[0], vtxSize[1], vtxSize[2])); + logger.log(Level.CONFIG, String.format("Setting the Y beam spot size to %f %f %f", vtxSize[0], vtxSize[1], vtxSize[2])); } + public void setBeamSpotX(double spot) { beamSpot[0] = spot; - logger.log(Level.CONFIG, String.format("Setting the X beam spot location to %f %f %f", beamSpot[0], beamSpot[1], beamSpot[2])); + logger.log(Level.CONFIG, String.format("Setting the X beam spot location to %f %f %f", beamSpot[0], beamSpot[1], beamSpot[2])); } - + public void setBeamSizeX(double size) { vtxSize[0] = size; - logger.log(Level.CONFIG, String.format("Setting the X beam spot size to %f %f %f", vtxSize[0], vtxSize[1], vtxSize[2])); + logger.log(Level.CONFIG, String.format("Setting the X beam spot size to %f %f %f", vtxSize[0], vtxSize[1], vtxSize[2])); } + public void setBeamSpotZ(double spot) { beamSpot[2] = spot; - logger.log(Level.CONFIG, String.format("Setting the Z beam spot location to %f %f %f", beamSpot[0], beamSpot[1], beamSpot[2])); + logger.log(Level.CONFIG, String.format("Setting the Z beam spot location to %f %f %f", beamSpot[0], beamSpot[1], beamSpot[2])); } - + public void setBeamSizeZ(double size) { vtxSize[2] = size; - logger.log(Level.CONFIG, String.format("Setting the Z beam spot size to %f %f %f", vtxSize[0], vtxSize[1], vtxSize[2])); + logger.log(Level.CONFIG, String.format("Setting the Z beam spot size to %f %f %f", vtxSize[0], vtxSize[1], vtxSize[2])); } - + public void clrStrategies() { - logger.log(Level.CONFIG,String.format("Clearing all lists of search strategies..")); + logger.log(Level.CONFIG, String.format("Clearing all lists of search strategies..")); lyrList[0].clear(); lyrList[1].clear(); } - + public void setNumSeedIter1(int num) { int n = num; - for (int topBottom=0; topBottom<2; ++topBottom) { + for (int topBottom = 0; topBottom < 2; ++topBottom) { if (n > lyrList[topBottom].size()) { n = lyrList[topBottom].size(); } } logger.config(String.format("The number of seeds used in iteration 1 is set to %d", n)); - maxListIter1 = n-1; + maxListIter1 = n - 1; } - + private boolean addStrategy(String strategy) { - return addStrategy(strategy,"top") && addStrategy(strategy,"bottom"); + return addStrategy(strategy, "top") && addStrategy(strategy, "bottom"); } - - // Add a seed search strategy for the bottom or top tracker + + /** + * Add a seed search strategy for the bottom or top tracker + * + * @param strategy string specifying the strategy + * @param topBottom string specifying "top" or "bottom" + * @return true if successful + */ public boolean addStrategy(String strategy, String topBottom) { if (!(topBottom == "top" || topBottom == "bottom")) { logger.config("The argument topBottom must be 'top' or 'bottom'. This seed strategy is ignored."); @@ -467,42 +522,44 @@ public boolean addStrategy(String strategy, String topBottom) { return false; } int iTB; - if (topBottom=="top") { + if (topBottom == "top") { iTB = 1; } else { iTB = 0; } int nAxial = 0; int nStereo = 0; - int n=0; - int [] newList = new int[5]; + int n = 0; + int[] newList = new int[5]; String goodChars = "0AaSsBb"; - for (int lyr=0; lyr<7; ++lyr) { + for (int lyr = 0; lyr < 7; ++lyr) { if (goodChars.indexOf(strategy.charAt(lyr)) < 0) { - logger.warning(String.format("Character %c for layer %d in strategy %s is not recognized. Should be 0, A, S, B, a, s, or b", + logger.warning(String.format("Character %c for layer %d in strategy %s is not recognized. Should be 0, A, S, B, a, s, or b", strategy.charAt(lyr), lyr, strategy)); continue; } - if (strategy.charAt(lyr) == '0') continue; + if (strategy.charAt(lyr) == '0') { + continue; + } int nA = n; int nS = n; - if (strategy.charAt(lyr)=='B' || strategy.charAt(lyr)=='b') { - if (topBottom=="top") { - nS=n+1; + if (strategy.charAt(lyr) == 'B' || strategy.charAt(lyr) == 'b') { + if (topBottom == "top") { + nS = n + 1; } else { - nA=n+1; + nA = n + 1; } n += 2; - } else if (strategy.charAt(lyr)=='A' || strategy.charAt(lyr)=='a' || strategy.charAt(lyr)=='S' || strategy.charAt(lyr)=='s') { + } else if (strategy.charAt(lyr) == 'A' || strategy.charAt(lyr) == 'a' || strategy.charAt(lyr) == 'S' || strategy.charAt(lyr) == 's') { n++; } //System.out.format("addStrategy %s: lyr=%d, nA=%d, nS=%d, strategy=%s\n",topBottom,lyr,nA,nS,strategy); - if (strategy.charAt(lyr)=='A' || strategy.charAt(lyr)=='B' || strategy.charAt(lyr)=='a' || strategy.charAt(lyr)=='b') { + if (strategy.charAt(lyr) == 'A' || strategy.charAt(lyr) == 'B' || strategy.charAt(lyr) == 'a' || strategy.charAt(lyr) == 'b') { if (topBottom == "top") { if (nA > 4) { logger.warning("Strategy " + strategy + " has more than 5 layers! The extra ones are ignored"); } else { - newList[nA] = 2*lyr; // The top tracker begins with an axial layer + newList[nA] = 2 * lyr; // The top tracker begins with an axial layer //System.out.format("addStrategy %s: adding axial element %d, lyr=%d\n", topBottom, nA, 2*lyr); nAxial++; } @@ -510,18 +567,18 @@ public boolean addStrategy(String strategy, String topBottom) { if (nA > 4) { logger.warning("Strategy " + strategy + " has more than 5 layers! The extra ones are ignored"); } else { - newList[nA] = 2*lyr + 1; // The bottom tracker begins with a stereo layer + newList[nA] = 2 * lyr + 1; // The bottom tracker begins with a stereo layer //System.out.format("addStrategy %s: adding axial element %d, lyr=%d\n", topBottom, nA, 2*lyr+1); nAxial++; } } } - if (strategy.charAt(lyr)=='S' || strategy.charAt(lyr)=='B' || strategy.charAt(lyr)=='s' || strategy.charAt(lyr)=='b') { + if (strategy.charAt(lyr) == 'S' || strategy.charAt(lyr) == 'B' || strategy.charAt(lyr) == 's' || strategy.charAt(lyr) == 'b') { if (topBottom == "top") { if (nS > 4) { logger.warning("Strategy " + strategy + " has more than 5 layers! The extra ones are ignored"); } else { - newList[nS] = 2*lyr + 1; // The top tracker begins with an axial layer + newList[nS] = 2 * lyr + 1; // The top tracker begins with an axial layer //System.out.format("addStrategy %s: adding stereo element %d, lyr=%d\n", topBottom, nS, 2*lyr+1); nStereo++; } @@ -529,7 +586,7 @@ public boolean addStrategy(String strategy, String topBottom) { if (nS > 4) { logger.warning("Strategy " + strategy + " has more than 5 layers! The extra ones are ignored"); } else { - newList[nS] = 2*lyr; // The bottom tracker begins with a stereo layer + newList[nS] = 2 * lyr; // The bottom tracker begins with a stereo layer //System.out.format("addStrategy %s: adding stereo element %d, lyr=%d\n", topBottom, nS, 2*lyr); nStereo++; } @@ -537,22 +594,24 @@ public boolean addStrategy(String strategy, String topBottom) { } } if (nAxial != 2 || nStereo != 3) { - logger.log(Level.WARNING,String.format("addStrategy: Invalid search strategy " + strategy + " for topBottom=%s: %d %d %d %d %d", - topBottom, newList[0],newList[1],newList[2],newList[3],newList[4])); + logger.log(Level.WARNING, String.format("addStrategy: Invalid search strategy " + strategy + " for topBottom=%s: %d %d %d %d %d", + topBottom, newList[0], newList[1], newList[2], newList[3], newList[4])); return false; } - for (int [] oldList : lyrList[iTB]) { + for (int[] oldList : lyrList[iTB]) { int nMatch = 0; - for (int i=0; i<5; ++i) { - if (oldList[i] == newList[i]) nMatch++; + for (int i = 0; i < 5; ++i) { + if (oldList[i] == newList[i]) { + nMatch++; + } } if (nMatch == 5) { - logger.log(Level.WARNING,String.format("addStrategy: strategy %s is already in the list", strategy)); + logger.log(Level.WARNING, String.format("addStrategy: strategy %s is already in the list", strategy)); return false; } } - logger.log(Level.CONFIG,String.format("addStrategy: adding search strategy %d %d %d %d %d for %s", - newList[0],newList[1],newList[2],newList[3],newList[4],topBottom)); + logger.log(Level.CONFIG, String.format("addStrategy: adding search strategy %d %d %d %d %d for %s", + newList[0], newList[1], newList[2], newList[3], newList[4], topBottom)); lyrList[iTB].add(newList); return true; } diff --git a/tracking/src/main/java/org/hps/recon/tracking/kalman/KalmanPatRecDriver.java b/tracking/src/main/java/org/hps/recon/tracking/kalman/KalmanPatRecDriver.java index 5000cdd133..e6e6d82ce0 100644 --- a/tracking/src/main/java/org/hps/recon/tracking/kalman/KalmanPatRecDriver.java +++ b/tracking/src/main/java/org/hps/recon/tracking/kalman/KalmanPatRecDriver.java @@ -34,7 +34,8 @@ import org.lcsim.geometry.IDDecoder; /** - * Driver for pattern recognition and fitting of HPS tracks using the Kalman Filter + * Driver for pattern recognition and fitting of HPS tracks using the Kalman + * Filter */ public class KalmanPatRecDriver extends Driver { @@ -44,7 +45,9 @@ public class KalmanPatRecDriver extends Driver { private org.lcsim.geometry.FieldMap fm; private KalmanInterface KI; private boolean verbose = false; - private boolean uniformB = false; + private boolean eLoss = false; + private boolean uniformB = false; // If true, fit tracks assuming a uniform B field + private boolean addResiduals = false; // If true add the hit-on-track residuals to the LCIO event private String outputFullTrackCollectionName = "KalmanFullTracks"; public AIDA aida; private int nTracks; @@ -56,7 +59,7 @@ public class KalmanPatRecDriver extends Driver { private KalmanParams kPar; private KalmanPatRecPlots kPlot; private static Logger logger; - + // Parameters for the Kalman pattern recognition that can be set by the user in the steering file: private ArrayList strategies; // List of seed strategies for both top and bottom trackers, from steering private ArrayList strategiesTop; // List of all the top tracker seed strategies from the steering file @@ -81,7 +84,7 @@ public class KalmanPatRecDriver extends Driver { private int numEvtPlots; // Number of event displays to plot (gnuplot files) private boolean doDebugPlots; // Whether to make all the debugging histograms private int siHitsLimit; // Maximum number of SiClusters in one event allowed for KF pattern reco - // (protection against monster events) + // (protection against monster events) private double seedCompThr; // Threshold for seedTrack helix parameters compatibility private int numStrategyIter1; // Number of seed strategies to use in the first iteration of pattern recognition private double beamPositionZ; // Beam spot location along the beam axis @@ -91,46 +94,94 @@ public class KalmanPatRecDriver extends Driver { private double beamPositionY; private double beamSigmaY; private double lowPhThresh; // Threshold in residual ratio to prefer a low-ph hit over a high-ph hit - private double minSeedEnergy=-1.; // Minimum energy of a hit for it to be used in a pattern recognition seed + private double minSeedEnergy = -1.; // Minimum energy of a hit for it to be used in a pattern recognition seed private boolean useBeamPositionConditions; // True to use beam position from database private boolean useFixedVertexZPosition; // True to override the database just for the z beam position private Level logLevel = Level.WARNING; // Set log level from steering - private boolean addResiduals; // If true add the hit-on-track residuals to the LCIO event private List sensors = null; // List of tracker sensors - - + public String getOutputFullTrackCollectionName() { return outputFullTrackCollectionName; } + /** + * Used to change the track collection name from the default + * "KalmanFullTracks" + * + * @param input Desired new name for the track collection + */ public void setOutputFullTrackCollectionName(String input) { outputFullTrackCollectionName = input; } - public void setVerbose(boolean input) { - verbose = input; + /** + * Optional call to force a change to the amount of debug printing + * + * @param input true to turn on lots of debug printing + */ + public void setVerbose(boolean verbose) { + this.verbose = verbose; + if (verbose) { + System.out.println("KalmanPatRecDriver: verbose print output is turned on!"); + } + } + + /** + * Option to treat the B field as uniform. This normally is used only for + * development and debugging work. + * + * @param input Set true to assume a uniform B field. + */ + public void setUniformB(boolean uniformB) { + this.uniformB = uniformB; + if (uniformB) { + System.out.println("KalmanPatRecDriver: a uniform field will be used in track fitting: %b"); + } } - public void setUniformB(boolean input) { - uniformB = input; - logger.config("KalmanPatRecDriver: the B field will be assumed uniform.\n"); + public void setELoss(boolean eLoss) { + this.eLoss = eLoss; + if (eLoss) { + System.out.println("KalmanPatRecDriver: energy loss will be included in fits."); + } } public void setMaterialManager(MaterialSupervisor mm) { _materialManager = mm; } + /** + * Determine whether residuals will be added to the event output + * + * @param input set true to force residuals to be output with the event + */ + public void setAddResiduals(boolean addResiduals) { + this.addResiduals = addResiduals; + System.out.format("KalmanPatRecDriver: residuals will be added to the event output: %b\n", addResiduals); + } + + /** + * Do-nothing method. See instead setOutputFullTrackCollectionName. + * + * @param input ignored---the call will do nothing. + */ public void setTrackCollectionName(String input) { } - + + /** + * Set the maximum number of SiClusters in one event allowed for KF pattern + * recognition (protection against monster events) + * + * @param input Maximum number of hits to use + */ public void setSiHitsLimit(int input) { siHitsLimit = input; - } - - public void setAddResiduals(boolean input) { - addResiduals = input; } - + + /** + * Set up the geometry and parameters for the Kalman-filter track finding + * and fitting. + */ @Override public void detectorChanged(Detector det) { logger = Logger.getLogger(KalmanPatRecDriver.class.getName()); @@ -138,83 +189,134 @@ public void detectorChanged(Detector det) { logger.setLevel(logLevel); //LogManager.getLogManager().getLogger(Logger.GLOBAL_LOGGER_NAME).setLevel(logLevel); } - verbose = (logger.getLevel()==Level.FINE); + verbose = (logger.getLevel() == Level.FINE); System.out.format("KalmanPatRecDriver: entering detectorChanged, logger level = %s\n", logger.getLevel().getName()); executionTime = 0.; interfaceTime = 0.; plottingTime = 0.; maxTime = 0.; - + _materialManager = new MaterialSupervisor(); _materialManager.buildModel(det); fm = det.getFieldMap(); - /* - System.out.format("B field map vs y:\n"); + + System.out.format("B field map vs y in Kalman coordinates:\n"); double eCalLoc = 1394.; - for (double y=0.; y<1500.; y+=5.) { - double z1=-50.; - double z2= 50.; - Vec B1 = new Vec(3, KalmanInterface.getFielD(new Vec(0., y, z1), fm)); - Vec B2 = new Vec(3, KalmanInterface.getFielD(new Vec(0., y, 0.), fm)); - Vec B3 = new Vec(3, KalmanInterface.getFielD(new Vec(0., y, z2), fm)); + for (double y = 0.; y < 1500.; y += 5.) { + double z1 = -50.; + double z2 = 50.; + Vec B1 = KalmanInterface.getField(new Vec(0., y, z1), fm); + Vec B2 = KalmanInterface.getField(new Vec(0., y, 0.), fm); + Vec B3 = KalmanInterface.getField(new Vec(0., y, z2), fm); System.out.format("y=%6.1f z=%6.1f: %s z=0: %s z=%6.1f: %s\n", y, z1, B1.toString(), B2.toString(), z2, B3.toString()); } - System.out.format("B field map vs z at ECAL:\n"); - for (double z=-200.; z<200.; z+=5.) { - double y=eCalLoc; - Vec B = new Vec(3, KalmanInterface.getFielD(new Vec(0., y, z), fm)); + System.out.format("B field map vs z at ECAL, in Kalman coordinates:\n"); + for (double z = -200.; z < 200.; z += 5.) { + double y = eCalLoc; + Vec B = KalmanInterface.getField(new Vec(0., y, z), fm); System.out.format("x=0 y=%6.1f z=%6.1f: %s\n", y, z, B.toString()); } - System.out.format("B field map vs x at ECAL:\n"); - for (double x=-200.; x<200.; x+=5.) { - double y=eCalLoc; - double z=20.; - Vec B = new Vec(3, KalmanInterface.getFielD(new Vec(x, y, z), fm)); + System.out.format("B field map vs x at ECAL, in Kalman coordinates:\n"); + for (double x = -200.; x < 200.; x += 5.) { + double y = eCalLoc; + double z = 20.; + Vec B = KalmanInterface.getField(new Vec(x, y, z), fm); System.out.format("x=%6.1f y=%6.1f z=%6.1f: %s\n", x, y, z, B.toString()); } - */ - + detPlanes = new ArrayList(); List materialVols = ((MaterialSupervisor) (_materialManager)).getMaterialVolumes(); for (ScatteringDetectorVolume vol : materialVols) { detPlanes.add((SiStripPlane) (vol)); } - + sensors = det.getSubdetector("Tracker").getDetectorElement().findDescendants(HpsSiSensor.class); // Instantiate the interface to the Kalman-Filter code and set up the geometry KalmanParams kPar = new KalmanParams(); - + // Change Kalman parameters per settings supplied by the steering file // We assume that if not set by the steering file, then the parameters will have the Java default values for the primitives // Note that all of the parameters have defaults hard coded in KalmanParams.java - if (numPatRecIteration != 0) kPar.setGlbIterations(numPatRecIteration); - if (numKalmanIteration != 0) kPar.setIterations(numKalmanIteration); - if (maxPtInverse != 0.0) kPar.setMaxK(maxPtInverse); - if (maxD0 != 0.0) kPar.setMaxdRho(maxD0); - if (maxZ0 != 0.0) kPar.setMaxdZ(maxZ0); - if (maxChi2 != 0.0) kPar.setMaxChi2(maxChi2); - if (minHits != 0) kPar.setMinHits(minHits); - if (minStereo != 0) kPar.setMinStereo(minStereo); - if (maxSharedHits != 0) kPar.setMaxShared(maxSharedHits); - if (maxTimeRange != 0.0) kPar.setMaxTimeRange(maxTimeRange); - if (maxTanLambda != 0.0) kPar.setMaxTanL(maxTanLambda); - if (maxResidual != 0.0) kPar.setMxResid(maxResidual); - if (maxChi2Inc != 0.0) kPar.setMxChi2Inc(maxChi2Inc); - if (minChi2IncBad != 0.0) kPar.setMinChi2IncBad(minChi2IncBad); - if (maxResidShare != 0.0) kPar.setMxResidShare(maxResidShare); - if (maxChi2IncShare != 0.0) kPar.setMxChi2double(maxChi2IncShare); - if (seedCompThr != 0.0) kPar.setSeedCompThr(seedCompThr); - if (beamPositionZ != 0.0) kPar.setBeamSpotY(beamPositionZ); - if (beamSigmaZ != 0.0) kPar.setBeamSizeY(beamSigmaZ); - if (beamPositionX != 0.0) kPar.setBeamSpotX(beamPositionX); - if (beamSigmaX != 0.0) kPar.setBeamSizeX(beamSigmaX); - if (beamPositionY != 0.0) kPar.setBeamSpotZ(-beamPositionY); - if (beamSigmaY != 0.0) kPar.setBeamSizeZ(beamSigmaY); - if (mxChi2Vtx != 0.0) kPar.setMaxChi2Vtx(mxChi2Vtx); - if (minSeedEnergy >= 0.) kPar.setMinSeedEnergy(minSeedEnergy); - + if (numPatRecIteration != 0) { + kPar.setGlbIterations(numPatRecIteration); + } + if (numKalmanIteration != 0) { + kPar.setIterations(numKalmanIteration); + } + if (maxPtInverse != 0.0) { + kPar.setMaxK(maxPtInverse); + } + if (maxD0 != 0.0) { + kPar.setMaxdRho(maxD0); + } + if (maxZ0 != 0.0) { + kPar.setMaxdZ(maxZ0); + } + if (maxChi2 != 0.0) { + kPar.setMaxChi2(maxChi2); + } + if (minHits != 0) { + kPar.setMinHits(minHits); + } + if (minStereo != 0) { + kPar.setMinStereo(minStereo); + } + if (maxSharedHits != 0) { + kPar.setMaxShared(maxSharedHits); + } + if (maxTimeRange != 0.0) { + kPar.setMaxTimeRange(maxTimeRange); + } + if (maxTanLambda != 0.0) { + kPar.setMaxTanL(maxTanLambda); + } + if (maxResidual != 0.0) { + kPar.setMxResid(maxResidual); + } + if (maxChi2Inc != 0.0) { + kPar.setMxChi2Inc(maxChi2Inc); + } + if (minChi2IncBad != 0.0) { + kPar.setMinChi2IncBad(minChi2IncBad); + } + if (maxResidShare != 0.0) { + kPar.setMxResidShare(maxResidShare); + } + if (maxChi2IncShare != 0.0) { + kPar.setMxChi2double(maxChi2IncShare); + } + if (seedCompThr != 0.0) { + kPar.setSeedCompThr(seedCompThr); + } + if (beamPositionZ != 0.0) { + kPar.setBeamSpotY(beamPositionZ); + } + if (beamSigmaZ != 0.0) { + kPar.setBeamSizeY(beamSigmaZ); + } + if (beamPositionX != 0.0) { + kPar.setBeamSpotX(beamPositionX); + } + if (beamSigmaX != 0.0) { + kPar.setBeamSizeX(beamSigmaX); + } + if (beamPositionY != 0.0) { + kPar.setBeamSpotZ(-beamPositionY); + } + if (beamSigmaY != 0.0) { + kPar.setBeamSizeZ(beamSigmaY); + } + if (mxChi2Vtx != 0.0) { + kPar.setMaxChi2Vtx(mxChi2Vtx); + } + if (minSeedEnergy >= 0.) { + kPar.setMinSeedEnergy(minSeedEnergy); + } + kPar.setUniformB(uniformB); + kPar.setEloss(eLoss); + // Here we set the seed strategies for the pattern recognition if (strategies != null || (strategiesTop != null && strategiesBot != null)) { logger.config("The Kalman pattern recognition seed strategies are being set from the steering file"); @@ -244,30 +346,35 @@ public void detectorChanged(Detector det) { kPar.setNumSeedIter1(nA + nT); kPar.setNumSeedIter1(nA + nB); } - if (numStrategyIter1 != 0) kPar.setNumSeedIter1(numStrategyIter1); - + if (numStrategyIter1 != 0) { + kPar.setNumSeedIter1(numStrategyIter1); + } + // Setup optional usage of beam positions from database. final DatabaseConditionsManager mgr = DatabaseConditionsManager.getInstance(); if (useBeamPositionConditions && mgr.hasConditionsRecord("beam_positions")) { logger.config("Using Kalman beam position from the conditions database"); - BeamPositionCollection beamPositions = - mgr.getCachedConditions(BeamPositionCollection.class, "beam_positions").getCachedData(); - BeamPosition beamPositionCond = beamPositions.get(0); - if (!useFixedVertexZPosition) kPar.setBeamSpotY(beamPositionCond.getPositionZ()); - else logger.config("Using fixed Kalman beam Z position: " + kPar.beamSpot[1]); + BeamPositionCollection beamPositions + = mgr.getCachedConditions(BeamPositionCollection.class, "beam_positions").getCachedData(); + BeamPosition beamPositionCond = beamPositions.get(0); + if (!useFixedVertexZPosition) { + kPar.setBeamSpotY(beamPositionCond.getPositionZ()); + } else { + logger.config("Using fixed Kalman beam Z position: " + kPar.beamSpot[1]); + } kPar.setBeamSpotX(beamPositionCond.getPositionX()); // Includes a transformation to Kalman coordinates kPar.setBeamSpotZ(-beamPositionCond.getPositionY()); } else { logger.config("Using Kalman beam position from the steering file or default"); } logger.config("Using Kalman beam position [ Z, X, Y ]: " + String.format("[ %f, %f, %f ]", - kPar.beamSpot[0], -kPar.beamSpot[2], kPar.beamSpot[1]) + " in HPS coordinates."); - + kPar.beamSpot[0], -kPar.beamSpot[2], kPar.beamSpot[1]) + " in HPS coordinates."); + logger.config(String.format("KalmanPatRecDriver: the B field is assumed uniform? %b\n", uniformB)); logger.config("KalmanPatRecDriver: done with configuration changes."); kPar.print(); - - KI = new KalmanInterface(uniformB, kPar, fm); + + KI = new KalmanInterface(kPar, det, fm); KI.setSiHitsLimit(siHitsLimit); KI.createSiModules(detPlanes); decoder = det.getSubdetector("Tracker").getIDDecoder(); @@ -276,53 +383,55 @@ public void detectorChanged(Detector det) { } } + /** + * Top level method called for each event, to do the Kalman-filter tracking + * finding and fitting + * + * @param event input the header for this event + */ @Override public void process(EventHeader event) { - int runNumber = event.getRunNumber(); - - KI.setRunNumber(runNumber); - List outputFullTracks = new ArrayList(); - + //For additional track information List trackDataCollection = new ArrayList(); List trackDataRelations = new ArrayList(); - + //For GBL Refitting List allClstrs = new ArrayList(); - List gblStripClusterDataRelations = new ArrayList(); - + List gblStripClusterDataRelations = new ArrayList(); + //For hit-on-track residuals information List trackResiduals = new ArrayList(); List trackResidualsRelations = new ArrayList(); - + ArrayList[] kPatList = prepareTrackCollections(event, outputFullTracks, trackDataCollection, trackDataRelations, allClstrs, gblStripClusterDataRelations, trackResiduals, trackResidualsRelations); - + int flag = 1 << LCIOConstants.TRBIT_HITS; event.put(outputFullTrackCollectionName, outputFullTracks, Track.class, flag); event.put("KFGBLStripClusterData", allClstrs, GBLStripClusterData.class, flag); event.put("KFGBLStripClusterDataRelations", gblStripClusterDataRelations, LCRelation.class, flag); - event.put("KFTrackData",trackDataCollection, TrackData.class,0); - event.put("KFTrackDataRelations",trackDataRelations,LCRelation.class,0); - + event.put("KFTrackData", trackDataCollection, TrackData.class, 0); + event.put("KFTrackDataRelations", trackDataRelations, LCRelation.class, 0); + if (addResiduals) { - event.put("KFUnbiasRes", trackResiduals, TrackResidualsData.class,0); - event.put("KFUnbiasResRelations",trackResidualsRelations, LCRelation.class,0); + event.put("KFUnbiasRes", trackResiduals, TrackResidualsData.class, 0); + event.put("KFUnbiasResRelations", trackResidualsRelations, LCRelation.class, 0); } if (kPlot != null) { long startTime = System.nanoTime(); - + kPlot.process(event, sensors, kPatList, outputFullTracks); long endPlottingTime = System.nanoTime(); - double runTime = (double)(endPlottingTime - startTime)/1000000.; - plottingTime += runTime; + double runTime = (double) (endPlottingTime - startTime) / 1000000.; + plottingTime += runTime; } - + KI.clearInterface(); logger.log(Level.FINE, String.format("\n KalmanPatRecDriver.process: Done with event %d", event.getEventNumber())); } - + class SortByZ implements Comparator> { @Override @@ -330,28 +439,43 @@ public int compare(Pair o1, Pair o2) { return (int) (o1.getSecondElement()[2] - o2.getSecondElement()[2]); } } - + class SortByZ2 implements Comparator { - + @Override - public int compare(TrackerHit o1, TrackerHit o2) { + public int compare(TrackerHit o1, TrackerHit o2) { return (int) (o1.getPosition()[2] - o2.getPosition()[2]); } } + /** + * Execute the Kalman pattern recognition. All but the first argument are + * outputs, but the calling routine has to supply the empty data structures. + * + * @param event input the header for this event + * @param outputFullTracks output the list of HPS tracks + * @param trackDataCollection output list of data that go with the tracks + * @param trackDataRelations output the relations between tracks and the data + * @param allClstrs output all the clusters needed for refitting the Kalman tracks by GBL + * @param gblStripClusterDataRelations output relations for the clusters + * @param trackResiduals output the residuals for hits on Kalman tracks + * @param trackResidualsRelations output relations for the residuals + * @return Two lists of native Kalman tracks, one for each of top and bottom + * detectors + */ private ArrayList[] prepareTrackCollections(EventHeader event, List outputFullTracks, List trackDataCollection, List trackDataRelations, List allClstrs, List gblStripClusterDataRelations, List trackResiduals, List trackResidualsRelations) { - + int evtNumb = event.getEventNumber(); String stripHitInputCollectionName = "StripClusterer_SiTrackerHitStrip1D"; if (!event.hasCollection(TrackerHit.class, stripHitInputCollectionName)) { System.out.format("KalmanPatRecDriver.process:" + stripHitInputCollectionName + " does not exist; skipping event %d\n", evtNumb); return null; } - + long startTime = System.nanoTime(); - ArrayList[] kPatList = KI.KalmanPatRec(event, decoder); + ArrayList[] kPatList = KI.KalmanPatRec(event); long endTime = System.nanoTime(); - double runTime = (double)(endTime - startTime)/1000000.; + double runTime = (double) (endTime - startTime) / 1000000.; executionTime += runTime; if (verbose) { if (runTime > 200.) { @@ -360,234 +484,271 @@ private ArrayList[] prepareTrackCollections(EventHeader event, List maxTime) maxTime = runTime; + if (runTime > maxTime) { + maxTime = runTime; + } nEvents++; - logger.log(Level.FINE,"KalmanPatRecDriver.process: run time for pattern recognition at event "+evtNumb+" is "+runTime+" milliseconds"); - + logger.log(Level.FINE, "KalmanPatRecDriver.process: run time for pattern recognition at event " + evtNumb + " is " + runTime + " milliseconds"); + //List rawhits = event.get(RawTrackerHit.class, "SVTRawTrackerHits"); //if (rawhits == null) { // logger.log(Level.FINE, String.format("KalmanPatRecDriver.process: the raw hits collection is missing")); // return null; //} - int nKalTracks = 0; - for (int topBottom=0; topBottom<2; ++topBottom) { + for (int topBottom = 0; topBottom < 2; ++topBottom) { ArrayList kPat = kPatList[topBottom]; if (kPat.size() == 0) { logger.log(Level.FINE, String.format("KalmanPatRecDriver.process: pattern recognition failed to find tracks in tracker %d for event %d.", topBottom, evtNumb)); } for (KalTrack kTk : kPat) { - if (verbose) kTk.print(String.format(" PatRec for topBot=%d ",topBottom)); - double [][] covar = kTk.originCovariance(); - for (int ix=0; ix<5; ++ix) { - for (int iy=0; iy<5; ++iy) { + if (verbose) { + kTk.print(String.format(" PatRec for topBot=%d ", topBottom)); + } + double[][] covar = kTk.originCovariance(); + for (int ix = 0; ix < 5; ++ix) { + for (int iy = 0; iy < 5; ++iy) { if (Double.isNaN(covar[ix][iy])) { - logger.log(Level.FINE, String.format("KalmanPatRecDriver.process event %d: NaN at %d %d in covariance for track %d",evtNumb,ix,iy,kTk.ID)); + logger.log(Level.FINE, String.format("KalmanPatRecDriver.process event %d: NaN at %d %d in covariance for track %d", evtNumb, ix, iy, kTk.ID)); } } - } + } nKalTracks++; - + //Here is where the tracks to be persisted are formed - Track KalmanTrackHPS = KI.createTrack(kTk, true); - if (KalmanTrackHPS == null) continue; - + Track KalmanTrackHPS = KI.createTrack(kTk, true, event); + if (KalmanTrackHPS == null) { + continue; + } + //pT cut //double [] hParams_check = kTk.originHelixParms(); //double ptInv_check = hParams_check[2]; //double pt = Math.abs(1./ptInv_check); - outputFullTracks.add(KalmanTrackHPS); + + // Create clusters that can be used to refit the Kalman track using GBL List clstrs = KI.createGBLStripClusterData(kTk); if (verbose) { for (GBLStripClusterData clstr : clstrs) { KI.printGBLStripClusterData(clstr); } } - - //Ecal extrapolation - For the moment done here, but should be moved inside the KalmanInterface (DONE) - //BaseTrackState ts_ecal = TrackUtils.getTrackExtrapAtEcalRK(KalmanTrackHPS,fm); - //KalmanTrackHPS.getTrackStates().add(ts_ecal); - + allClstrs.addAll(clstrs); for (GBLStripClusterData clstr : clstrs) { gblStripClusterDataRelations.add(new BaseLCRelation(KalmanTrackHPS, clstr)); } - + //Set top by default int trackerVolume = 0; //if tanLamda<0 set bottom - if (KalmanTrackHPS.getTrackStates().get(0).getTanLambda() < 0) trackerVolume = 1; - + if (KalmanTrackHPS.getTrackStates().get(0).getTanLambda() < 0) { + trackerVolume = 1; + } + //TODO: compute isolations double qualityArray[] = new double[1]; - qualityArray[0]= -1; - + qualityArray[0] = -1; + //Get the track momentum and convert it into detector frame and float values Hep3Vector momentum = new BasicHep3Vector(KalmanTrackHPS.getTrackStates().get(0).getMomentum()); momentum = CoordinateTransformations.transformVectorToDetector(momentum); - + float[] momentum_f = new float[3]; momentum_f[0] = (float) momentum.x(); momentum_f[1] = (float) momentum.y(); momentum_f[2] = (float) momentum.z(); - + //Add the Track Data TrackData KFtrackData = new TrackData(trackerVolume, (float) kTk.getTime(), qualityArray, momentum_f); trackDataCollection.add(KFtrackData); trackDataRelations.add(new BaseLCRelation(KFtrackData, KalmanTrackHPS)); - //Add the TrackResiduas - - List layers = new ArrayList(); - List residuals = new ArrayList(); - List sigmas = new ArrayList(); - - for (int ilay = 0; ilay<14; ilay++) { - Pair res_and_sigma = kTk.unbiasedResidual(ilay); - if (res_and_sigma.getSecondElement() > -1.) { + //Add the TrackResiduals + List layers = new ArrayList(); + List residuals = new ArrayList(); + List sigmas = new ArrayList(); + + for (int ilay = 0; ilay < 14; ilay++) { + Pair res_and_sigma = kTk.unbiasedResidual(ilay); + if (res_and_sigma.getSecondElement() > -1.) { layers.add(ilay); residuals.add(res_and_sigma.getFirstElement()); sigmas.add(res_and_sigma.getSecondElement().floatValue()); } - }//Loop on layers - - TrackResidualsData resData = new TrackResidualsData(trackerVolume,layers,residuals,sigmas); + } // End loop on layers + + TrackResidualsData resData = new TrackResidualsData(trackerVolume, layers, residuals, sigmas); trackResiduals.add(resData); - trackResidualsRelations.add(new BaseLCRelation(resData,KalmanTrackHPS)); + trackResidualsRelations.add(new BaseLCRelation(resData, KalmanTrackHPS)); /* if (KalmanTrackHPS.getTrackerHits().size() != residuals.size()) { System.out.println("KalmanPatRecDriver::Residuals consistency check failed."); System.out.printf("Track has %d hits while I have %d residuals \n", KalmanTrackHPS.getTrackerHits().size(), residuals.size()); } - */ - + */ + } // end of loop on tracks } // end of loop on trackers - + nTracks += nKalTracks; - + long endInterfaceTime = System.nanoTime(); - runTime = (double)(endInterfaceTime - endTime)/1000000.; + runTime = (double) (endInterfaceTime - endTime) / 1000000.; interfaceTime += runTime; - + return kPatList; } @Override + /** + * Print a summary of Kalman Filter tracking at the end of the job. + */ public void endOfData() { - System.out.format("KalmanPatRecDriver.endOfData: total pattern recognition execution time=%12.4f ms for %d events and %d tracks.\n", + System.out.format("KalmanPatRecDriver.endOfData: total pattern recognition execution time=%12.4f ms for %d events and %d tracks.\n", executionTime, nEvents, nTracks); - double evtTime = executionTime/(double)nEvents; - double tkrTime = executionTime/(double)nTracks; + double evtTime = executionTime / (double) nEvents; + double tkrTime = executionTime / (double) nTracks; System.out.format(" Kalman Patrec Time per event = %9.4f ms; Time per track = %9.4f ms\n", evtTime, tkrTime); System.out.format(" Kalman Patrec maximum time for one event = %10.4f ms\n", maxTime); - evtTime = interfaceTime/(double)nEvents; + evtTime = interfaceTime / (double) nEvents; System.out.format(" Kalman Interface Time per event = %9.4f ms\n", evtTime); if (kPlot != null) { kPlot.output(); - evtTime = plottingTime/(double)nEvents; + evtTime = plottingTime / (double) nEvents; System.out.format(" Kalman Plotting Time per event = %9.4f ms\n", evtTime); } KI.summary(); } - + // Methods to set Kalman parameters from within the steering file public void setNumPatRecIteration(int numPatRecIteration) { this.numPatRecIteration = numPatRecIteration; } + public void setNumKalmanIteration(int numKalmanIteration) { this.numKalmanIteration = numKalmanIteration; } + public void setMaxPtInverse(double maxPtInverse) { this.maxPtInverse = maxPtInverse; } + public void setMaxD0(double maxD0) { this.maxD0 = maxD0; } + public void setMaxZ0(double maxZ0) { this.maxZ0 = maxZ0; } + public void setMaxChi2(double maxChi2) { this.maxChi2 = maxChi2; } + public void setMinHits(int minHits) { this.minHits = minHits; } + public void setMinStereo(int minStereo) { this.minStereo = minStereo; } + public void setMaxSharedHits(int maxSharedHits) { this.maxSharedHits = maxSharedHits; } + public void setMaxTimeRange(double maxTimeRange) { this.maxTimeRange = maxTimeRange; } + public void setMaxTanLambda(double maxTanLambda) { this.maxTanLambda = maxTanLambda; } + public void setMaxResidual(double maxResidual) { this.maxResidual = maxResidual; } + public void setMaxChi2Inc(double maxChi2Inc) { this.maxChi2Inc = maxChi2Inc; } + public void setMinChi2IncBad(double minChi2IncBad) { this.minChi2IncBad = minChi2IncBad; } + public void setMxChi2Vtx(double mxChi2Vtx) { this.mxChi2Vtx = mxChi2Vtx; } + public void setMaxResidShare(double maxResidShare) { this.maxResidShare = maxResidShare; } + public void setMaxChi2IncShare(double maxChi2IncShare) { this.maxChi2IncShare = maxChi2IncShare; } + public void setNumEvtPlots(int numEvtPlots) { this.numEvtPlots = numEvtPlots; } + public void setDoDebugPlots(boolean doDebugPlots) { this.doDebugPlots = doDebugPlots; - } + } + public void setSeedCompThr(double seedCompThr) { this.seedCompThr = seedCompThr; } + public void setBeamPositionZ(double beamPositionZ) { this.beamPositionZ = beamPositionZ; } + public void setBeamSigmaZ(double beamSigmaZ) { this.beamSigmaZ = beamSigmaZ; } + public void setBeamPositionX(double beamPositionX) { this.beamPositionX = beamPositionX; } + public void setBeamSigmaX(double beamSigmaX) { this.beamSigmaX = beamSigmaX; } + public void setBeamPositionY(double beamPositionY) { this.beamPositionY = beamPositionY; } + public void setBeamSigmaY(double beamSigmaY) { this.beamSigmaY = beamSigmaY; } + public void setUseBeamPositionConditions(boolean useBeamPositionConditions) { this.useBeamPositionConditions = useBeamPositionConditions; } + public void setUseFixedVertexZPosition(boolean useFixedVertexZPosition) { this.useFixedVertexZPosition = useFixedVertexZPosition; } + public void setNumStrategyIter1(int numStrategyIter1) { this.numStrategyIter1 = numStrategyIter1; } + public void setMinSeedEnergy(double minSeedEnergy) { this.minSeedEnergy = minSeedEnergy; System.out.format("KalmanPatRecDriver: minimum seed energy from steering = %8.4f\n", minSeedEnergy); } + public void setLowPhThresh(double lowPhThresh) { this.lowPhThresh = lowPhThresh; System.out.format("KalmanPatRecDriver: setting lowPhThresh from steering : %12.4f\n", lowPhThresh); } + public void setSeedStrategy(String seedStrategy) { if (strategies == null) { strategies = new ArrayList(); @@ -595,6 +756,7 @@ public void setSeedStrategy(String seedStrategy) { strategies.add(seedStrategy); System.out.format("KalmanPatRecDriver: top and bottom strategy %s specified by steering.\n", seedStrategy); } + public void setSeedStrategyTop(String seedStrategy) { if (strategiesTop == null) { strategiesTop = new ArrayList(); @@ -602,6 +764,7 @@ public void setSeedStrategyTop(String seedStrategy) { strategiesTop.add(seedStrategy); System.out.format("KalmanPatRecDriver: top strategy %s specified by steering.\n", seedStrategy); } + public void setSeedStrategyBottom(String seedStrategy) { if (strategiesBot == null) { strategiesBot = new ArrayList(); @@ -609,9 +772,10 @@ public void setSeedStrategyBottom(String seedStrategy) { strategiesBot.add(seedStrategy); System.out.format("KalmanPatRecDriver: bottom strategy %s specified by steering.\n", seedStrategy); } + public void setLogLevel(String logLevel) { System.out.format("KalmanPatRecDriver: setting the logger level to %s\n", logLevel); this.logLevel = Level.parse(logLevel); - System.out.format(" logger level = %s\n",this.logLevel.getName()); + System.out.format(" logger level = %s\n", this.logLevel.getName()); } } diff --git a/tracking/src/main/java/org/hps/recon/tracking/kalman/KalmanPatRecHPS.java b/tracking/src/main/java/org/hps/recon/tracking/kalman/KalmanPatRecHPS.java index 1e66fb7446..1c1cdc2261 100644 --- a/tracking/src/main/java/org/hps/recon/tracking/kalman/KalmanPatRecHPS.java +++ b/tracking/src/main/java/org/hps/recon/tracking/kalman/KalmanPatRecHPS.java @@ -63,6 +63,10 @@ class KalmanPatRecHPS { static int [] nBadCov = {0, 0}; private ArrayList candidateList; + /** + * Constructor to call each event for each detector (top/bottom). + * @param kPar Object containing all the cuts and parameters + */ KalmanPatRecHPS(KalmanParams kPar) { startTime = (long)0.; this.kPar = kPar; @@ -92,14 +96,25 @@ class KalmanPatRecHPS { eventNumber = 0; } - // Override the default vertex location and size + /** + * Override the default vertex location and size + * @param vtx 3-vector vertex position + * @param vtxCov 3 by 3 covariance matrix for the vertex position + */ void patRecSetVtx(double [] vtx, double [][] vtxCov) { this.vtx = vtx; this.vtxCov = vtxCov; } + /** + * Execute the Kalman-Filter pattern recognition for a given detector in a given event. + * @param event Event header + * @param hitMapHPS Map between tracker hits and measurement objects + * @param data Array of SiModule objects containing all the hit data + * @param topBottom 0 for the bottom tracker (z>0), 1 for the top tracker (z<0) + * @return + */ ArrayList kalmanPatRec(EventHeader event, Map hitMapHPS, ArrayList data, int topBottom) { - // topBottom = 0 for the bottom tracker (z>0); 1 for the top tracker (z<0) if (event != null) eventNumber = event.getEventNumber(); else eventNumber++; @@ -174,6 +189,14 @@ ArrayList kalmanPatRec(EventHeader event, Map } System.out.format("\n"); } + + Vec tB = null; // Magnetic field direction + double Bmag = 0.; // Magnetic field magnitude + if (kPar.uniformB) { + Vec Bfield = KalmanInterface.getField(new Vec(0., kPar.SVTcenter, 0.), data.get(0).Bfield); + tB = new Vec(0., 0., 1.); + Bmag = Bfield.v[2]; + } // Loop over seed strategies, each with 2 non-stereo layers and 3 stereo layers // For each strategy generate a seed track for every hit combination @@ -310,7 +333,7 @@ ArrayList kalmanPatRec(EventHeader event, Map if (redundantSeed) continue; // Fit the seed to extract helix parameters - SeedTrack seed = new SeedTrack(hitList, yOrigin, kPar.beamSpot[1]); + SeedTrack seed = new SeedTrack(hitList, yOrigin, kPar.beamSpot[1], kPar); if (!seed.success) { if (debug) { System.out.format("Seed %d %d %d %d %d failed fit\n",idx[0], idx[1], idx[2], idx[3], idx[4]); @@ -378,9 +401,11 @@ ArrayList kalmanPatRec(EventHeader event, Map } // Kalman filter the sorted seeds - Vec Bfield = KalmanInterface.getField(pivot, m0.Bfield); - double Bmag = Bfield.mag(); - Vec tB = Bfield.unitVec(Bmag); + if (!kPar.uniformB) { + Vec Bfield = KalmanInterface.getField(pivot, m0.Bfield); + Bmag = Bfield.mag(); + tB = Bfield.unitVec(Bmag); + } seedLoop: for (SeedTrack seed : seedList) { if (debug) { System.out.format("\n\nStart the filter step for seed"); @@ -419,7 +444,7 @@ ArrayList kalmanPatRec(EventHeader event, Map setInitCov(CovGuess, seed.helixParams(), true); // Create an state vector from the input seed to initialize the Kalman filter - StateVector sI = new StateVector(-1, seed.helixParams(), CovGuess, new Vec(0., 0., 0.), Bmag, tB, pivot); + StateVector sI = new StateVector(-1, seed.helixParams(), CovGuess, new Vec(0., 0., 0.), Bmag, tB, pivot, kPar.uniformB); TrackCandidate candidateTrack = new TrackCandidate(candID, seed.hits, kPar, hitMap, eventNumber); candID++; filterTrack(candidateTrack, list[0], KalmanParams.numLayers - 1, sI, trial, true, true); @@ -1361,6 +1386,9 @@ ArrayList kalmanPatRec(EventHeader event, Map return TkrList; } + /** + * Print a debug summary of all the track candidates + */ private void printCandidateList() { System.out.format("KalmanPatRecHPS: list of track candidates in event %d\n", eventNumber); for (TrackCandidate tkr : candidateList) { @@ -1374,7 +1402,12 @@ private void printCandidateList() { } } - // Remove the worst hit from lousy track candidates + /** + * Remove the worst hit from lousy track candidates + * @param tkr The track candidate + * @param trial Trial (0 or 1, to select what cuts are used in kPar) + * @return true for success + */ private boolean removeBadHits(TrackCandidate tkr, int trial) { if (tkr.chi2s/(double) tkr.hits.size() < kPar.chi2mx1[trial]) return false; @@ -1411,7 +1444,10 @@ private boolean removeBadHits(TrackCandidate tkr, int trial) { return false; } - // Method to smooth an already filtered track candidate + /** + * Method to smooth an already filtered track candidate + * @param filteredTkr Track candidate to be smoothed + */ private void smoothTrack(TrackCandidate filteredTkr) { MeasurementSite nextSite = null; boolean badCov = false; @@ -1436,11 +1472,20 @@ private void smoothTrack(TrackCandidate filteredTkr) { filteredTkr.smoothed = true; } - // Execute the Kalman prediction and filter steps over a range of SVT layers + /** + * Execute the Kalman prediction and filter steps over a range of SVT layers + * @param tkrCandidate Track candidate to be worked on + * @param lyrBegin Beginning layer + * @param lyrEnd Ending layer + * @param sI State vector to use for initialization + * @param trial Trial level (0 or 1) to select what cuts and parameters to use + * @param startNew Start the fit over from the beginning, if true + * @param pickUp true to allow picking up new hits + */ private void filterTrack(TrackCandidate tkrCandidate, int lyrBegin, // layer on which to start the filtering - int lyrEnd, // layer on which to end the filtering - StateVector sI, // initialization state vector - int trial, // trial level, for selecting cuts + int lyrEnd, // layer on which to end the filtering + StateVector sI, // initialization state vector + int trial, // trial level, for selecting cuts boolean startNew, // Start the fit over boolean pickUp // true to allow picking up new hits ) { @@ -1636,6 +1681,12 @@ private void filterTrack(TrackCandidate tkrCandidate, int lyrBegin, // layer on return; } + /** + * Store a track candidate as a KalTrack object + * @param tkID Integer ID for the track + * @param tkrCand The candidate + * @return + */ boolean storeTrack(int tkID, TrackCandidate tkrCand) { if (debug) System.out.format("entering storeTrack for track %d, debug=%b\n", tkID, debug); @@ -1704,6 +1755,11 @@ public int compare(Pair p1, Pair p2) { } }; + /** + * Check for negative covariance in a simple way, by looking at just the diagonal elements + * @param C Covariance matrix to check + * @return true if it is not positive definite + */ static boolean negativeCov(DMatrixRMaj C) { boolean badCov = false; for (int i=0; i<5; ++i) { @@ -1715,6 +1771,12 @@ static boolean negativeCov(DMatrixRMaj C) { return badCov; } + /** + * Make an initial-guess helix covariance matrix + * @param C The covariance + * @param helix The helix parameters + * @param newM true if it is a new matrix (i.e. all elements are zero) + */ static void setInitCov(DMatrixRMaj C, Vec helix, boolean newM) { C.unsafe_set(0, 0, 0.5); C.unsafe_set(1, 1, .00005); @@ -1730,6 +1792,11 @@ static void setInitCov(DMatrixRMaj C, Vec helix, boolean newM) { } } + /** + * Try to fix the helix covariance matrix + * @param C The bad covariance matrix + * @param helix The helix parameters + */ static void fixCov(DMatrixRMaj C, Vec helix) { for (int i=0; i<5; ++i) { if (C.unsafe_get(i, i) < 0.) { @@ -1761,7 +1828,12 @@ static void fixCov(DMatrixRMaj C, Vec helix) { } } - // Quick check on where the seed track is heading, using only the two axial layers in the seed + /** + * Quick check on where the seed track is heading, using only the two axial layers in the seed + * @param j The axial layer where we are interested + * @param iter Iteration (0,1) for choosing the kPar cuts and parameters + * @return true if the direction is no good for a track seed + */ private boolean seedNoGood(int j, int iter) { // j must point to an axial layer in the seed. // Find the previous axial layer, if there is one. . . diff --git a/tracking/src/main/java/org/hps/recon/tracking/kalman/KalmanPatRecPlots.java b/tracking/src/main/java/org/hps/recon/tracking/kalman/KalmanPatRecPlots.java index a8446d7740..08a8d8aafb 100644 --- a/tracking/src/main/java/org/hps/recon/tracking/kalman/KalmanPatRecPlots.java +++ b/tracking/src/main/java/org/hps/recon/tracking/kalman/KalmanPatRecPlots.java @@ -102,7 +102,7 @@ class KalmanPatRecPlots { aida.histogram1D("GBL >=12-hit track chi^2", 100, 0., 200.); aida.histogram1D("Kalman Track Number Hits", 20, 0., 20.); aida.histogram1D("GBL number tracks", 10, 0., 10.); - aida.histogram1D("Kalman missed hit residual", 100, -1.0, 1.0); + aida.histogram1D("Kalman missed hit residual", 100, -2.0, 2.0); aida.histogram1D("Kalman track hit residual, sigmas", 100, -5., 5.); aida.histogram1D("Kalman track hit residual >= 10 hits, sigmas", 100, -5., 5.); aida.histogram1D("Kalman track hit residual", 100, -0.1, 0.1); @@ -157,7 +157,7 @@ class KalmanPatRecPlots { aida.histogram1D("dz error estimate",50,0.,0.2); aida.histogram1D("tanl error estimate",50,0.,.005); for (int lyr=0; lyr<14; ++lyr) { - aida.histogram1D(String.format("Layers/Kalman missed hit residual in layer %d",lyr), 100, -1.0, 1.0); + aida.histogram1D(String.format("Layers/Kalman missed hit residual in layer %d",lyr), 100, -2.0, 2.0); aida.histogram1D(String.format("Layers/Kalman track hit residual in layer %d",lyr), 100, -0.1, 0.1); aida.histogram1D(String.format("Layers/Kalman track hit residual in layer %d, sigmas",lyr), 100, -5., 5.); aida.histogram1D(String.format("Layers/Kalman track unbiased hit residual in layer %d",lyr), 100, -0.1, 0.1); @@ -193,6 +193,16 @@ class KalmanPatRecPlots { aida.histogram1D("seed slope",100,0.,0.3); aida.histogram1D("seed z intercept",100,0.,10.); aida.histogram1D("seed y intercept",100,-100.,100.); + for (int topBottom=0; topBottom<2; ++topBottom) { + aida.histogram1D(String.format("Missed hit residual/ det=%d, stereo=%b",topBottom,true), 200, -10.0, 10.0); + aida.histogram1D(String.format("Missed hit residual/ det=%d, stereo=%b",topBottom,false), 200, -10.0, 10.0); + for (int lyr=8; lyr<14; ++lyr) { + boolean isStereo; + if (topBottom == 0) isStereo = lyr%2 == 0; + else isStereo = lyr%2 != 0; + aida.histogram1D(String.format("Missed hit residual/ det=%d, lyr=%d, stereo=%b",topBottom,lyr,isStereo), 100, -10.0, 10.0); + } + } } void process(EventHeader event, List sensors, ArrayList[] kPatList, @@ -488,6 +498,41 @@ void process(EventHeader event, List sensors, ArrayList[] aida.histogram1D("tanl error estimate").fill(Math.sqrt(thisCov.unsafe_get(4, 4))); } + // Analyze high momentum tracks for missing hits in the outer layers + if (pMag > 2.0) { + //System.out.format("pMag=%12.6f\n", pMag); + int firstLayer = kTk.SiteList.get(0).m.Layer; + if (firstLayer < 2) { + int lastSite = 999; + for (int idx = kTk.SiteList.size()-1; idx >= 0; --idx) { + lastSite = idx; + if (kTk.SiteList.get(idx).hitID >= 0) break; + } + int lastLayer = kTk.SiteList.get(lastSite).m.Layer; + //System.out.format("firstLayer=%d lastLayer=%d\n", firstLayer, lastLayer); + if (lastLayer >= 7 && lastLayer <= 9 && kTk.nHits >= 8) { + for (MeasurementSite site: kTk.SiteList) { + if (site.hitID >= 0) continue; + SiModule mod = site.m; + if (mod == null) continue; + if (mod.Layer < 8) continue; + //System.out.format(" Layer %d\n", mod.Layer); + double [] rGbl = null; + double hitV = kTk.moduleIntercept(mod, rGbl)[1]; + double minResid = 9.9e9; + for (Measurement m : mod.hits) { + double resid = m.v - hitV; + if (Math.abs(resid) < Math.abs(minResid)) minResid = resid; + } + if (Math.abs(minResid) < 10.) { + aida.histogram1D(String.format("Missed hit residual/ det=%d, stereo=%b",topBottom,mod.isStereo)).fill(minResid); + aida.histogram1D(String.format("Missed hit residual/ det=%d, lyr=%d, stereo=%b",topBottom,mod.Layer,mod.isStereo)).fill(minResid); + } + } + } + } + } + // Histogram residuals of hits in layers with no hits on the track and with hits ArrayList mcParts = new ArrayList(); ArrayList mcCnt= new ArrayList(); @@ -503,9 +548,9 @@ void process(EventHeader event, List sensors, ArrayList[] double minResid = 9.9e9; for (Measurement m : mod.hits) { double resid = m.v - hitV; - if (resid < minResid) minResid = resid; + if (Math.abs(resid) < Math.abs(minResid)) minResid = resid; } - if (kTk.nHits >= 10 && Math.abs(minResid) < 1.0) { + if (kTk.nHits >= 10 && Math.abs(minResid) < 2.0) { aida.histogram1D("Kalman missed hit residual").fill(minResid); aida.histogram1D(String.format("Layers/Kalman missed hit residual in layer %d",mod.Layer)).fill(minResid); } diff --git a/tracking/src/main/java/org/hps/recon/tracking/kalman/KalmanTrackFit2.java b/tracking/src/main/java/org/hps/recon/tracking/kalman/KalmanTrackFit2.java index 756033bbb2..ed39882644 100644 --- a/tracking/src/main/java/org/hps/recon/tracking/kalman/KalmanTrackFit2.java +++ b/tracking/src/main/java/org/hps/recon/tracking/kalman/KalmanTrackFit2.java @@ -47,10 +47,15 @@ class KalmanTrackFit2 { tkr = null; // Create an state vector from the input seed to initialize the Kalman filter - Vec Bfield = KalmanInterface.getField(pivot, fM); + Vec Bfield = null; + if (kPar.uniformB) { + Bfield = KalmanInterface.getField(new Vec(0., kPar.SVTcenter, 0.), fM); + } else { + Bfield = KalmanInterface.getField(pivot, fM); + } double B = Bfield.mag(); Vec t = Bfield.unitVec(B); - StateVector sI = new StateVector(-1, helixParams, C, new Vec(0., 0., 0.), B, t, pivot); + StateVector sI = new StateVector(-1, helixParams, C, new Vec(0., 0., 0.), B, t, pivot, kPar.uniformB); if (verbose) { System.out.format("KalmanTrackFit2: begin Kalman fit, start=%d, number iterations=%d\n", start, nIterations); @@ -58,8 +63,6 @@ class KalmanTrackFit2 { sI.print("initial state for KalmanTrackFit"); } - double mxResid = 9999.; - sites = new ArrayList(); initialSite = 0; finalSite = 0; @@ -135,7 +138,6 @@ class KalmanTrackFit2 { startSite = newSite; } - int nHits = 0; // Restart the fit at the first layer and iterate the fit if requested for (int iteration = 0; iteration < nIterations; iteration++) { StateVector sH = null; @@ -246,7 +248,6 @@ class KalmanTrackFit2 { } if (currentSite.hitID >= 0) { chi2s += currentSite.chi2inc; - if (iteration == nIterations - 1) nHits++; } if (verbose) { diff --git a/tracking/src/main/java/org/hps/recon/tracking/kalman/Measurement.java b/tracking/src/main/java/org/hps/recon/tracking/kalman/Measurement.java index be87e0bd3f..5d24404259 100644 --- a/tracking/src/main/java/org/hps/recon/tracking/kalman/Measurement.java +++ b/tracking/src/main/java/org/hps/recon/tracking/kalman/Measurement.java @@ -2,6 +2,8 @@ import java.util.ArrayList; +import org.lcsim.event.MCParticle; + /** * Holds a single silicon-strip measurement (single-sided), to interface with the Kalman fit */ @@ -14,8 +16,12 @@ class Measurement { // double vTrue; // MC truth measurement value Vec rGlobal; // Global MC truth ArrayList tracks; // Tracks that this hit lies on - ArrayList tksMC; // MC tracks that contributed to this hit + ArrayList tksMC; // MC track IDs that contributed to this hit (for stand-alone test program) + ArrayList pMC; // List of hps-java MCparticle objects that contributed to this hit + /** + * Constructor with no MC truth info stored + */ Measurement(double value, double xStrip, double resolution, double t, double E) { v = value; x = xStrip; @@ -26,8 +32,19 @@ class Measurement { // vTrue = 0.; rGlobal = null; tksMC = null; + pMC = null; } + /** + * Full constructor, including MC truth + * @param value value of the measured coordinate on the detector coordinate system + * @param xStrip x value of the center of the strip in the detector coordinate system + * @param resolution uncertainty in v + * @param t measured time + * @param E measured energy deposit + * @param rGlobal global MC truth hit position + * @param vTrue MC truth measurement value + */ Measurement(double value, double xStrip, double resolution, double t, double E, Vec rGlobal, double vTrue) { v = value; x = xStrip; @@ -38,24 +55,29 @@ class Measurement { // this.vTrue = vTrue; tracks = new ArrayList(); tksMC = new ArrayList(); + pMC = null; } + /** + * Add a MC truth track to the list + * @param idx index of the MC truth track + */ void addMC(int idx) { tksMC.add(idx); } + /** + * Debug printout of the measurement instance + * @param s Arbitrary string for the user's reference + */ void print(String s) { - System.out.format("Measurement %s: Measurement value=%10.5f+-%8.6f; xStrip=%7.2f, MC truth=%10.5f; t=%8.3f; E=%8.3f", s, v, sigma, x, vTrue, time, energy); - if (tracks.size() == 0) { - System.out.format(" Not on any track.\n"); - } else { - System.out.format(" Tracks: "); - for (KalTrack tk : tracks) System.out.format(" %d ", tk.ID); - System.out.format("\n"); - } - if (rGlobal != null) rGlobal.print("global location from MC truth"); + System.out.format("%s", toString(s)); } + /** + * Debug printout to a string of the measurement instance + * @param s Arbitrary string for the user's reference + */ String toString(String s) { String str = String.format("Measurement %s: Measurement value=%10.5f+-%8.6f; xStrip=%7.2f, MC truth=%10.5f; t=%8.3f; E=%8.3f", s, v, sigma, x, vTrue, time, energy); if (tracks.size() == 0) { @@ -66,6 +88,12 @@ String toString(String s) { str = str + String.format("\n"); } if (rGlobal != null) str = str + rGlobal.toString("global location from MC truth"); + if (pMC != null) { + str = str + "\n"; + for (MCParticle mcp : pMC) { + str = str + String.format(" MC particle type %d, E=%9.5f\n", mcp.getPDGID(),mcp.getEnergy()); + } + } return str; } } diff --git a/tracking/src/main/java/org/hps/recon/tracking/kalman/MeasurementSite.java b/tracking/src/main/java/org/hps/recon/tracking/kalman/MeasurementSite.java index 895def7ee9..332c995d19 100644 --- a/tracking/src/main/java/org/hps/recon/tracking/kalman/MeasurementSite.java +++ b/tracking/src/main/java/org/hps/recon/tracking/kalman/MeasurementSite.java @@ -21,6 +21,7 @@ class MeasurementSite { boolean filtered; // True if the filtered state vector has been built StateVector aS; // Smoothed state vector boolean smoothed; // True if the smoothed state vector has been built + boolean energyConstrained; // True if the smoothed state vector is energy constrained double chi2inc; // chi^2 increment for this site DMatrixRMaj H; // Derivatives of the transformation from state vector to measurement double arcLength; // Arc length from the previous measurement @@ -34,14 +35,19 @@ class MeasurementSite { private static Logger logger; private static DMatrixRMaj tempV; private static boolean initialized; - - // Note: I can remove the concept of a dummy layer and make all layers equivalent, except that the non-physical ones - // will never have a hit and thus will be handled the same as physical layers that lack hits + /** + * Debug printout of a MeasurementSite instance + * @param s Arbitrary string for the user's reference + */ void print(String s) { System.out.format("%s", this.toString(s)); } + /** + * Debug printout to a string of a MeasurementSite instance + * @param s Arbitrary string for the user's reference + */ String toString(String s) { String str; if (m.Layer < 0) { @@ -56,10 +62,17 @@ String toString(String s) { } else if (predicted) { str=str+" This site has been predicted\n"; } + if (energyConstrained) str=str+" This site has been energy constrained\n"; str=str+String.format(" Hit ID=%d, maximum allowed residual=%10.5f\n", hitID, kPar.mxResid[1]); str = str + m.toString("for this site"); - double B = KalmanInterface.getField(m.p.X(), m.Bfield).mag(); - Vec tB = KalmanInterface.getField(m.p.X(), m.Bfield).unitVec(); + Vec Bfield = null; + if (kPar.uniformB) { + Bfield = KalmanInterface.getField(new Vec(0., kPar.SVTcenter, 0.), m.Bfield); + } else { + Bfield = KalmanInterface.getField(m.p.X(), m.Bfield); + } + double B = Bfield.mag(); + Vec tB = Bfield.unitVec(); str=str+String.format(" Magnetic field strength=%10.6f; alpha=%10.6f\n", B, alpha); str = str + tB.toString("magnetic field direction") + "\n"; str=str+String.format(" chi^2 increment=%12.4e\n", chi2inc); @@ -77,6 +90,12 @@ String toString(String s) { return str; } + /** + * Constructor + * @param thisSite integer index + * @param data SiModule holding the data for this site + * @param kPar instance of Kalman run parameters + */ MeasurementSite(int thisSite, SiModule data, KalmanParams kPar) { this.thisSite = thisSite; this.kPar = kPar; @@ -84,12 +103,18 @@ String toString(String s) { hitID = -1; double c = 2.99793e8; // Speed of light in m/s conFac = 1.0e12 / c; - Vec Bfield = KalmanInterface.getField(m.p.X(), m.Bfield); + Vec Bfield = null; + if (kPar.uniformB) { + Bfield = KalmanInterface.getField(new Vec(0., kPar.SVTcenter, 0.), m.Bfield); + } else { + Bfield = KalmanInterface.getField(m.p.X(), m.Bfield); + } B = Bfield.mag(); alpha = conFac / B; // Convert from 1/pt in 1/GeV to curvature in mm predicted = false; filtered = false; smoothed = false; + energyConstrained = false; double rho = 2.329; // Density of silicon in g/cm^3 radLen = (21.82 / rho) * 10.0; // Radiation length of silicon in millimeters double sp = 0.002; // Estar collision stopping power for electrons in silicon at about a GeV, in GeV cm2/g @@ -103,7 +128,11 @@ String toString(String s) { } } - double scatX() { // scattering angle in the x,y plane for the filtered state vector + /** + * Scattering angle in the x,y plane for the filtered state vector + * @return the scattering angle in radians + */ + double scatX() { if (aP == null || aF == null) return -999.; Vec p1 = aP.helix.getMom(0.); double t1 = FastMath.atan2(p1.v[0], p1.v[1]); @@ -112,7 +141,11 @@ String toString(String s) { return t1 - t2; } - double scatZ() { // scattering angle in the z,y plane for the filtered state vector + /** + * Scattering angle in the z,y plane for the filtered state vector + * @return the scattering angle in radians + */ + double scatZ() { if (aP == null || aF == null) return -999.; Vec p1 = aP.helix.getMom(0.); double t1 = FastMath.atan2(p1.v[2], p1.v[1]); @@ -121,31 +154,50 @@ String toString(String s) { return t1 - t2; } + /** + * Create predicted state vector by propagating from previous site to the new site + */ int makePrediction(StateVector pS, int hitNumber, boolean sharingOK, boolean pickup) { SiModule mPs = null; return makePrediction(pS, mPs, hitNumber, sharingOK, pickup, false); } + /** + * Create predicted state vector by propagating from previous site to the new site + */ int makePrediction(StateVector pS, SiModule mPs, int hitNumber, boolean sharingOK, boolean pickup) { return makePrediction(pS, mPs, hitNumber, sharingOK, false, false); } + /** + * Create predicted state vector by propagating from previous site to the new site + */ int makePrediction(StateVector pS, SiModule mPs, int hitNumber, boolean sharingOK, boolean pickup, boolean checkBounds) { double [] dT = {-1000., 1000.}; return makePrediction(pS, mPs, hitNumber, sharingOK, pickup, checkBounds, dT, 0); } - + + /** + * Create predicted state vector by propagating from previous site to the new site + */ int makePrediction(StateVector pS, SiModule mPs, int hitNumber, boolean sharingOK, boolean pickup, boolean checkBounds, double [] tRange, int trial) { return makePrediction(pS, mPs, hitNumber, sharingOK, pickup, checkBounds, tRange, trial, false); } - int makePrediction(StateVector pS, SiModule mPs, int hitNumber, boolean sharingOK, boolean pickup, boolean checkBounds, double [] tRange, int trial, boolean verbose2) { // Create predicted state vector by propagating from previous site - // pS = state vector that we are predicting from - // mPS = Si module that we are predicting from, if any - // tRange = allowed time range [tmin,tmax] for picking up a hit - // minE = minimum hit energy for it to be picked up, unless no other hit exists - // sharingOK = whether to allow sharing of a hit between multiple tracks - // pickup = whether we are doing pattern recognition here and need to pick up hits to add to the track + /** + * Create predicted state vector by propagating from previous site to the new site + * @param pS Previous site state vector that we are predicting from + * @param mPs Si module that we are predicting from, if any + * @param hitNumber hit number to assume if we are not doing pattern recognition + * @param sharingOK true to allow sharing of a hit between multiple tracks + * @param pickup true if we are doing pattern recognition here and need to pick up hits to add to the track + * @param checkBounds true to check that the track is within bounds of the silicon, if doing pattern recognition + * @param tRange Allowed time range [tmin,tmax] for picking up a hit (from KalParams) + * @param trial 0 or 1 for which trial is being executed, to index into KalParams + * @param verbose2 True to get lots of extra debug printout spew + * @return Flag: -2 for extrapolation not within detector, -1 for error, 1 for a hit was used, 0 no hit used + */ + int makePrediction(StateVector pS, SiModule mPs, int hitNumber, boolean sharingOK, boolean pickup, boolean checkBounds, double [] tRange, int trial, boolean verbose2) { int returnFlag = 0; double phi = pS.helix.planeIntersect(m.p); if (debug) verbose2 = true; @@ -169,10 +221,13 @@ int makePrediction(StateVector pS, SiModule mPs, int hitNumber, boolean sharingO check); } - double deltaE = 0.; // dEdx*thickness/ct; - Vec origin = m.p.X(); - Vec Bfield = KalmanInterface.getField(pS.helix.toGlobal(X0), m.Bfield); + Vec Bfield = null; + if (kPar.uniformB) { + Bfield = KalmanInterface.getField(new Vec(0., kPar.SVTcenter, 0.), m.Bfield); + } else { + Bfield = KalmanInterface.getField(pS.helix.toGlobal(X0), m.Bfield); + } double B = Bfield.mag(); Vec tB = Bfield.unitVec(B); if (debug) { @@ -184,13 +239,16 @@ int makePrediction(StateVector pS, SiModule mPs, int hitNumber, boolean sharingO // First we need the momentum direction to calculate how much silicon we pass through Vec pMom = pS.helix.Rot.inverseRotate(pS.helix.getMom(0.)); double XL; + double deltaE = 0.; if (mPs == null) { XL = 0.; arcLength = 0.; } else { double ct = pMom.unitVec().dot(mPs.p.T()); // cos(theta) at the **previous** site + double dL = mPs.thickness/FastMath.abs(ct); + if (kPar.eLoss) deltaE = dEdx*dL; double radius = alpha/pS.helix.a.v[2]; - XL = mPs.thickness / radLen / Math.abs(ct); // Si scattering thickness at previous site + XL = dL / radLen; // Si scattering thickness at previous site arcLength = -radius*phi*FastMath.sqrt(1.0 + pS.helix.a.v[4] * pS.helix.a.v[4]); if (debug) { double dx = m.p.X().v[0]-mPs.p.X().v[0]; @@ -416,7 +474,11 @@ int makePrediction(StateVector pS, SiModule mPs, int hitNumber, boolean sharingO return returnFlag; // -2 for extrapolation not within detector, -1 for error, 1 for a hit was used, 0 no hit used } - boolean filter() { // Produce the filtered state vector for this site + /** + * Produce the filtered state vector for this site + * @return true for success, false for bad news + */ + boolean filter() { if (!predicted) { System.out.format("******MeasurementSite.filter: Warning, this site is not in the correct state!\n"); } @@ -484,7 +546,11 @@ boolean filter() { // Produce the filtered state vector for this site return true; } - // Inverse Kalman filter: remove this site from the smoothed track fit + /** + * Inverse Kalman filter: remove this site from the smoothed track fit + * This didn't really work, so it is stripped down to simply deleting the hit. + * @return true for success, false if there was no hit to remove + */ boolean removeHit() { if (hitID < 0) return false; hitID = -1; @@ -494,6 +560,14 @@ boolean removeHit() { return true; } + /** + * Try to add a hit to a given track + * @param tkr KalTrack instance to which we want to add a hit + * @param cut Maximum chi^2 increment allowed for the new hit + * @param mxTdif Maximum different in time for the track when the new hit is included + * @param oldID ID of a hit that was just removed + * @return The measurement instance added to the track, or null if none + */ Measurement addHit(KalTrack tkr, double cut, double mxTdif, int oldID) { if (aP == null) { logger.log(Level.WARNING, "******MeasurementSite.addHit: Warning, this site is not in the correct state!"); @@ -557,7 +631,12 @@ Measurement addHit(KalTrack tkr, double cut, double mxTdif, int oldID) { return null; } - // Produce the smoothed state vector for this site + + /** + * Produce the smoothed state vector for this site + * @param nS the previous site that was smoothed + * @return true for success + */ boolean smooth(MeasurementSite nS) { // nS is the next site in the filtering chain (i.e. the previous site that was smoothed) if (!filtered) { @@ -603,7 +682,13 @@ boolean smooth(MeasurementSite nS) { return true; } - double h(StateVector pS, SiModule siM) {// Predict the measurement for a helix passing through this plane + /** + * Predict the measurement for a helix passing through this plane + * @param pS The StateVector + * @param siM The silicon module + * @return The predicted measurement value + */ + double h(StateVector pS, SiModule siM) { double phi = pS.helix.planeIntersect(siM.p); if (Double.isNaN(phi)) { logger.log(Level.FINE, "MeasurementSite.h: warning, no intersection of helix with the plane exists."); @@ -612,7 +697,15 @@ boolean smooth(MeasurementSite nS) { return h(pS, siM, phi); } - double h(StateVector pS, SiModule siM, double phi) { // Shortcut call in case phi is already known + /** + * Predict the measurement for a helix passing through this plane. + * Shortcut call in case phi is already known + * @param pS The StateVector + * @param siM The silicon module + * @param phi Turning angle to the intersection with the plane, in radians + * @return The predicted measurement value + */ + double h(StateVector pS, SiModule siM, double phi) { Vec rGlobal = pS.helix.toGlobal(pS.helix.atPhi(phi)); Vec rLocal = siM.toLocal(rGlobal); // Rotate into the detector coordinate system if (debug) { @@ -624,7 +717,11 @@ boolean smooth(MeasurementSite nS) { return rLocal.v[1]; } - // Create the derivative matrix for prediction of the measurement from the helix + /** + * Create the derivative matrix for prediction of the measurement from the helix + * @param S The StateVector + * @param H The derivative matrix that is filled in by this method + */ private void buildH(StateVector S, DMatrixRMaj H) { double phi = S.helix.planeIntersect(m.p); @@ -634,9 +731,7 @@ private void buildH(StateVector S, DMatrixRMaj H) { } if (debug) { System.out.format("MeasurementSite.buildH: phi=%10.7f\n", phi); - // S.print("given to buildH"); - // R.print("in buildH"); - // p.print("in buildH"); + S.print("given to buildH"); } Vec dxdphi = new Vec((alpha / S.helix.a.v[2]) * FastMath.sin(S.helix.a.v[1] + phi), -(alpha / S.helix.a.v[2]) * FastMath.cos(S.helix.a.v[1] + phi), -(alpha / S.helix.a.v[2]) * S.helix.a.v[4]); @@ -717,7 +812,9 @@ private void buildH(StateVector S, DMatrixRMaj H) { } } - // Comparator functions for sorting measurement sites by layer number + /** + * Comparator function for sorting measurement sites by increasing layer number + */ static Comparator SiteComparatorUp = new Comparator() { public int compare(MeasurementSite s1, MeasurementSite s2) { int lyr1 = s1.m.Layer; @@ -725,6 +822,10 @@ public int compare(MeasurementSite s1, MeasurementSite s2) { return lyr1 - lyr2; } }; + + /** + * Comparator function for sorting measurement sites by decreasing layer number + */ static Comparator SiteComparatorDn = new Comparator() { public int compare(MeasurementSite s1, MeasurementSite s2) { int lyr1 = s1.m.Layer; diff --git a/tracking/src/main/java/org/hps/recon/tracking/kalman/PatRecTest.java b/tracking/src/main/java/org/hps/recon/tracking/kalman/PatRecTest.java index c93bd26c57..b408fb4ba2 100644 --- a/tracking/src/main/java/org/hps/recon/tracking/kalman/PatRecTest.java +++ b/tracking/src/main/java/org/hps/recon/tracking/kalman/PatRecTest.java @@ -26,7 +26,7 @@ class PatRecTest { PatRecTest(String path) { // Units are Tesla, GeV, mm - int nTrials = 1000; // The number of test eventNumbers to generate for pattern recognition and fitting + int nTrials = 3; // The number of test eventNumbers to generate for pattern recognition and fitting int mxPlot = 10; // Maximum number of single event plots int [] eventToPrint = {1,2,3,4,5,6,7,8,9,10}; boolean perfect = false; @@ -273,7 +273,7 @@ class PatRecTest { for (int i = 0; i < nHelices; i++) { Vec momentum = new Vec(p[i] * initialDirection[i].v[0], p[i] * initialDirection[i].v[1], p[i] * initialDirection[i].v[2]); if (verbose) momentum.print("initial helix momentum"); - TkInitial[i] = new Helix(Q[i], helixOrigin, momentum, helixOrigin, fM, rnd); + TkInitial[i] = new Helix(Q[i], helixOrigin, momentum, helixOrigin, fM, rnd, kPar.eLoss); drho[i] = TkInitial[i].p.v[0]; phi0[i] = TkInitial[i].p.v[1]; K[i] = TkInitial[i].p.v[2]; @@ -302,7 +302,7 @@ class PatRecTest { Vec p1 = new Vec(3); HelixPlaneIntersect hpi1 = new HelixPlaneIntersect(); Vec pivotBegin = hpi1.rkIntersect(si1.p, TkInitial[i].atPhiGlobal(0.), TkInitial[i].getMomGlobal(0.), Q[i], fM, p1); - helixBegin[i] = new Helix(Q[i], pivotBegin, p1, pivotBegin, fM, rnd); + helixBegin[i] = new Helix(Q[i], pivotBegin, p1, pivotBegin, fM, rnd, kPar.eLoss); if (verbose) helixBegin[i].print("helixBegin"); } PrintWriter printWriter2 = null; diff --git a/tracking/src/main/java/org/hps/recon/tracking/kalman/Plane.java b/tracking/src/main/java/org/hps/recon/tracking/kalman/Plane.java index 6c4bce4c2b..2669a79758 100644 --- a/tracking/src/main/java/org/hps/recon/tracking/kalman/Plane.java +++ b/tracking/src/main/java/org/hps/recon/tracking/kalman/Plane.java @@ -10,6 +10,11 @@ class Plane { private Vec u; // Local axis always lies in the world system x,y plane private Vec v; // Other local axis, t,u,v forms a RH system + /** + * Constructor + * @param point 3-vector point on the plane + * @param direction 3-vector direction cosines of the plane + */ Plane(Vec point, Vec direction) { x = point.copy(); t = direction.copy(); @@ -18,10 +23,21 @@ class Plane { v = t.cross(u); } + /** + * Copy method + * @return A new deep copy of the plane + */ Plane copy() { return new Plane(x, t); } + /** + * Alternative constructor + * @param newX 3-vector point on the plane + * @param newT t unit vector perpendicular to the plane + * @param newU u unit vector in the plane and in the x,y plane + * @param newV v unit vector perpendicular to t and u (t,u,v is a RH system) + */ Plane(Vec newX, Vec newT, Vec newU, Vec newV) { x = newX; t = newT; @@ -29,6 +45,12 @@ Plane copy() { v = newV; } + /** + * Alternative constuctor + * @param X 3-vector point on the plane + * @param T t unit vector perpendicular to the plane + * @param U u unit vector in the plane and in the x,y plane + */ Plane(Vec X, Vec T, Vec U) { x = X; t = T; @@ -36,7 +58,12 @@ Plane copy() { v = t.cross(u); } - // strange constructor for backward compatibility of SiModule.java + /** + * strange constructor for backward compatibility of SiModule.java + * @param X 3-vector point on the plane + * @param T t unit vector perpendicular to the plane + * @param theta angle used to rotate into the t,u,v system + */ Plane(Vec X, Vec T, double theta) { x = X; Vec zhat = new Vec(0., 0., 1.); @@ -53,10 +80,18 @@ Plane copy() { } } + /** + * Debug printout of a plane instance + * @param s Arbitrary string for the user's reference + */ void print(String s) { System.out.format("%s",this.toString(s)); } + /** + * Debug printout to a string of a plane instance + * @param s Arbitrary string for the user's reference + */ String toString(String s) { String str = String.format("Printout of plane %s\n", s); str=str+" point="+x.toString(); @@ -66,28 +101,51 @@ String toString(String s) { return str; } + /** + * Short string describing the plane + */ public String toString() { return String.format("pnt %s dir %s", x.toString(), t.toString()); } - - Vec U() { // unit vector in the plane perpendicular to t and the world z axis + /** + * Get u + * @return The unit vector in the plane perpendicular to t and the world z axis + */ + Vec U() { return u; } - Vec V() { // another orthogonal unit vector in the plane perp to t and u + /** + * Get v + * @return Another orthogonal unit vector in the plane perp to t and u + */ + Vec V() { return v; } + /** + * Get t + * @return The unit vector perpendicular to the plane + */ Vec T() { return t; } + /** + * Get x + * @return The point in the plane + */ Vec X() { return x; } - // Convert the plane to a local coordinate system + /** + * Convert the plane to a local coordinate system + * @param R Rotation matrix from global to local + * @param origin Origin of the local system + * @return The plane in the local system + */ Plane toLocal(RotMatrix R, Vec origin) { return new Plane(R.rotate(x.dif(origin)), R.rotate(t), R.rotate(u), R.rotate(v)); } diff --git a/tracking/src/main/java/org/hps/recon/tracking/kalman/PropagatedTrackState.java b/tracking/src/main/java/org/hps/recon/tracking/kalman/PropagatedTrackState.java index 52d100fd18..3b3221657f 100644 --- a/tracking/src/main/java/org/hps/recon/tracking/kalman/PropagatedTrackState.java +++ b/tracking/src/main/java/org/hps/recon/tracking/kalman/PropagatedTrackState.java @@ -15,7 +15,6 @@ */ public class PropagatedTrackState { private static final double c = 2.99793e8; - private static final double SVTcenter = 505.57; private static final boolean debug = false; private double conFac; private List detPlanes; @@ -33,7 +32,7 @@ public class PropagatedTrackState { private static DMatrixRMaj Ft; private static boolean initialized; - PropagatedTrackState (TrackState stateHPS, double [] location, double [] direction, List detPlanes, org.lcsim.geometry.FieldMap fM) { + PropagatedTrackState (TrackState stateHPS, double [] location, double [] direction, List detPlanes, double SVTcenter, org.lcsim.geometry.FieldMap fM) { logger = Logger.getLogger(PropagatedTrackState.class.getName()); // stateHPS = HPS track state to be propagated // location = 3-D point on the plane to which the track is to be propagated @@ -121,7 +120,7 @@ public class PropagatedTrackState { pMom.print(String.format("initial momentum from pivoted helix, p=%10.6f",pMom.mag())); } DMatrixRMaj F = new DMatrixRMaj(5,5); - HelixState.makeF(helixParamsPivoted, F, helixParams, alpha); + HelixState.makeF(helixParamsPivoted, F, helixParams, alpha, 1.0); // Then rotate the helix into the B-field reference frame DMatrixRMaj fRot = new DMatrixRMaj(5,5); @@ -213,10 +212,18 @@ public class PropagatedTrackState { if (debug) printTrackState(trackState,"final"); } + /** + * Get the track state + * @return TrackState corresponding to the propagated helix + */ public TrackState getTrackState() { return trackState; } + /** + * Get the predicted intersection with the destination plane, in global coordinates + * @return 3-vector array of doubles + */ public double [] getIntersection() { double phi = newHelixState.planeIntersect(destinationPlane); if (Double.isNaN(phi)) { @@ -228,10 +235,14 @@ public TrackState getTrackState() { return KalmanInterface.vectorKalmanToGlb(xPlaneGlb); } + /** + * Get the covariance matrix of the predicted intersection point + * @return 3 by 3 array of doubles + */ public double [][] getIntersectionCov() { Vec helixAtInt = getIntersectionHelix(); DMatrixRMaj F = new DMatrixRMaj(5,5); - newHelixState.makeF(helixAtInt, F); + newHelixState.makeF(helixAtInt, F, 1.0); CommonOps_DDRM.multTransB(newHelixState.C, F, tempM); DMatrixRMaj covAtInt = new DMatrixRMaj(5,5); CommonOps_DDRM.mult(F, tempM, covAtInt); @@ -253,6 +264,10 @@ public TrackState getTrackState() { return Chps.M; } + /** + * Debug printout + * @param s Arbitrary string for the user's reference + */ public void print(String s) { System.out.format("Dump of PropagatedTrackState %s at plane %s\n", s, destinationPlane.toString()); printTrackState(trackState,"internal"); @@ -264,6 +279,11 @@ public void print(String s) { System.out.format("End of PropagatedTrackState dump\n"); } + /** + * Debug printout of a track state + * @param trackState The instance to print + * @param s Arbitrary string for the user's reference + */ public static void printTrackState(TrackState trackState, String s) { double [] r = trackState.getReferencePoint(); System.out.format("Dump of TrackState %s at location %d ref. pnt %10.6f %10.6f %10.6f\n", @@ -279,18 +299,34 @@ public static void printTrackState(TrackState trackState, String s) { System.out.format(" %12.4e %12.4e %12.4e %12.4e %12.4e\n", C[10], C[11], C[12], C[13], C[14]); } + /** + * Get the 3-D point on the plane to which the track is to be propagated + * @return 3-vector of doubles + */ public double [] getLocation() { return location; } + /** + * Get the direction cosines of the plane to which the track is to be propagated + * @return 3-vector of doubles + */ public double [] getDirection() { return direction; } + /** + * Get the list of scattering planes through which the track is propagated + * @return the list of silicon-strip plane objects + */ public List getDetPlanes() { return detPlanes; } + /** + * Get the helix parameters at the intersection point (in the local B-field coordinate system, of course) + * @return 5-vector of helix parameters + */ private Vec getIntersectionHelix() { if (xPlane == null) getIntersection(); Vec helixAtInt = newHelixState.pivotTransform(xPlane); diff --git a/tracking/src/main/java/org/hps/recon/tracking/kalman/RKhelix.java b/tracking/src/main/java/org/hps/recon/tracking/kalman/RKhelix.java index b544ba4628..a4890221b8 100644 --- a/tracking/src/main/java/org/hps/recon/tracking/kalman/RKhelix.java +++ b/tracking/src/main/java/org/hps/recon/tracking/kalman/RKhelix.java @@ -5,38 +5,41 @@ import org.apache.commons.math.util.FastMath; /** - * Runge-Kutta propagation through the detector, including Gaussian MCS at silicon planes. - * This code is only to help with internal testing of the Kalman package and is not part of the fitting or pattern recognition. + * Runge-Kutta propagation through the detector, including Gaussian MCS at + * silicon planes. This code is only to help with internal testing of the Kalman + * package and is not part of the fitting or pattern recognition. */ class RKhelix { Vec x; // point on the track Vec p; // momentum of the track at point x double Q; // charge of the particle - + private org.lcsim.geometry.FieldMap fM; private Random rndm; private HelixPlaneIntersect hpi; private double rho; private double radLen; - - RKhelix(Vec x, Vec p, double Q, org.lcsim.geometry.FieldMap fM, Random rndm) { + private boolean doeLoss; + + RKhelix(Vec x, Vec p, double Q, org.lcsim.geometry.FieldMap fM, Random rndm, boolean doeLoss) { this.rndm = rndm; this.fM = fM; this.x = x.copy(); this.p = p.copy(); this.Q = Q; + this.doeLoss = doeLoss; hpi = new HelixPlaneIntersect(); rho = 2.329; // Density of silicon in g/cm^2 radLen = (21.82 / rho) * 10.0; // Radiation length of silicon in millimeters } - + RKhelix propagateRK(Plane pln) { Vec newP = new Vec(3); Vec newX = planeIntersect(pln, newP); - return new RKhelix(newX, newP, Q, fM, rndm); + return new RKhelix(newX, newP, Q, fM, rndm, doeLoss); } - + Vec planeIntersect(Plane pln, Vec pInt) { // phi value where the helix intersects the plane P (given in global coordinates) return hpi.rkIntersect(pln, x, p, Q, fM, pInt); } @@ -44,7 +47,7 @@ Vec planeIntersect(Plane pln, Vec pInt) { // phi value where the helix intersect void print(String s) { System.out.format("RKhelix %s: x=%s, p=%s, Q=%3.1f\n", s, x.toString(), p.toString(), Q); } - + // Get parameters for the helix passing through the point x. // pivotF is the pivot point in the helix field reference system. // Input "pivot", the desired pivot point in global coordinates. This will be the origin of the field reference system. @@ -58,16 +61,16 @@ Vec helixParameters(Vec pivot, Vec pivotF) { Vec helixAtX = HelixState.pTOa(pF, 0., 0., Q); // Helix with pivot at x in field frame // Transform the desired pivot point into the field frame Vec pivotTrans = Rot.rotate(pivot.dif(x)); - for (int i=0; i<3; ++i) { + for (int i = 0; i < 3; ++i) { pivotF.v[i] = pivotTrans.v[i]; } return HelixState.pivotTransform(pivotF, helixAtX, new Vec(0., 0., 0.), alpha, 0.); } - - RKhelix copy() { - return new RKhelix(x.copy(),p.copy(),Q,fM,rndm); + + RKhelix copy() { + return new RKhelix(x.copy(), p.copy(), Q, fM, rndm, doeLoss); } - + RotMatrix R(Vec position) { Vec B = KalmanInterface.getField(position, fM); double Bmag = B.mag(); @@ -77,7 +80,7 @@ RotMatrix R(Vec position) { Vec v = t.cross(u); return new RotMatrix(u, v, t); } - + RKhelix randomScat(Plane P, double X) { // Produce a new helix scattered randomly in a given plane P Vec t = p.unitVec(); @@ -87,9 +90,12 @@ RKhelix randomScat(Plane P, double X) { // Produce a new helix scattered randoml RotMatrix Rp = new RotMatrix(uhat, vhat, t); double ct = Math.abs(P.T().dot(t)); double theta0; - - if (X == 0.) theta0 = 0.; // Get the scattering angle - else theta0 = FastMath.sqrt((X / radLen) / ct) * (0.0136 / p.mag()) * (1.0 + 0.038 * FastMath.log((X / radLen) / ct)); + + if (X == 0.) { + theta0 = 0.; // Get the scattering angle + } else { + theta0 = FastMath.sqrt((X / radLen) / ct) * (0.0136 / p.mag()) * (1.0 + 0.038 * FastMath.log((X / radLen) / ct)); + } double thetaX = rndm.nextGaussian() * theta0; double thetaY = rndm.nextGaussian() * theta0; double tx = FastMath.sin(thetaX); @@ -98,13 +104,17 @@ RKhelix randomScat(Plane P, double X) { // Produce a new helix scattered randoml Vec tnew = Rp.inverseRotate(tLoc); double E = p.mag(); // Everything is assumed electron - double sp = 0.0; // 0.002; // Estar collision stopping power for electrons in silicon at about a GeV, in GeV cm2/g + double sp = 0.0; + if (doeLoss) { + sp = 0.002; // Estar collision stopping power for electrons in silicon at about a GeV, in GeV cm2/g + sp = sp * 20; // ToDo temporary!!! + } double dEdx = 0.1 * sp * rho; // in GeV/mm double eLoss = dEdx * X / ct; E = E - eLoss; Vec pNew = tnew.scale(E); - return new RKhelix(x, pNew, Q, fM, rndm); // Create the new helix with new origin and pivot point + return new RKhelix(x, pNew, Q, fM, rndm, doeLoss); // Create the new helix with new origin and pivot point } } diff --git a/tracking/src/main/java/org/hps/recon/tracking/kalman/RotMatrix.java b/tracking/src/main/java/org/hps/recon/tracking/kalman/RotMatrix.java index 4d9f2142cf..a61f9a3fe5 100644 --- a/tracking/src/main/java/org/hps/recon/tracking/kalman/RotMatrix.java +++ b/tracking/src/main/java/org/hps/recon/tracking/kalman/RotMatrix.java @@ -6,16 +6,30 @@ class RotMatrix { double[][] M = null; - RotMatrix() { // Create a blank matrix + /** + * Constructor of a blank matrix + */ + RotMatrix() { M = new double[3][3]; } - // Careful here: a new array is not made + /** + * Constructor from a provided array. + * Careful here: a new array is not made. + * @param Min 3 by 3 array of doubles (should be orthogonal, but not checked!) + */ RotMatrix(double[][] Min) { M = Min; } - RotMatrix(Vec u, Vec v, Vec t) { // Transforms from global coordinates to frame with 3-D unit vectors u, v, t + /** + * Constructor from a RH u,v,t coordinate system + * Transforms from global coordinates to frame with 3-D unit vectors u, v, t + * @param u 3-vector direction cosines of u + * @param v 3-vector direction cosines of v + * @param t 3-vector direction cosines of t + */ + RotMatrix(Vec u, Vec v, Vec t) { M = new double[3][3]; for (int i = 0; i < 3; i++) { // Simply place the vectors u, v, and t along the successive rows of the matrix M[0][i] = u.v[i]; @@ -24,7 +38,16 @@ class RotMatrix { } } - RotMatrix(Vec u1, Vec v1, Vec t1, Vec u2, Vec v2, Vec t2) { // Rotation from frame 1 to frame 2 + /** + * Construct the matrix for a rotation from frame 1 to frame 2 + * @param u1 u axis in frame 1 + * @param v1 v axis in frame 1 + * @param t1 t axis in frame 1 + * @param u2 u axis in frame 2 + * @param v2 v axis in frame 2 + * @param t2 t axis in frame 2 + */ + RotMatrix(Vec u1, Vec v1, Vec t1, Vec u2, Vec v2, Vec t2) { M = new double[3][3]; M[0][0] = u2.dot(u1); M[0][1] = u2.dot(v1); @@ -37,7 +60,13 @@ class RotMatrix { M[2][2] = t2.dot(t1); } - RotMatrix(double phi, double theta, double psi) {// Create the rotation matrix from Euler angles + /** + * Construct the rotation matrix from Euler angles + * @param phi First Euler angle (rotation angle about z axis) + * @param theta Second Euler angle (rotation angle about x' axis) + * @param psi Third Euler angle (rotation angle about z'' axis) + */ + RotMatrix(double phi, double theta, double psi) {// double c1 = Math.cos(phi); double c2 = Math.cos(theta); double c3 = Math.cos(psi); @@ -56,7 +85,11 @@ class RotMatrix { M[2][2] = c2; } - RotMatrix(double phi) { // Simple rotation only about z + /** + * Constructor for a simple rotation only about z + * @param phi angle of rotation about z, in radians + */ + RotMatrix(double phi) { double c1 = Math.cos(phi); double s1 = Math.sin(phi); M = new double[3][3]; @@ -67,9 +100,12 @@ class RotMatrix { M[2][2] = 1.0; } - RotMatrix(Vec k, double theta) { // Rodrigues' rotation formula - // k is a unit vector defining the axis of rotation - // theta is the angle of rotation counterclockwise about that axis + /** + * Constructor using Rodrigues' rotation formula + * @param k unit vector defining the axis of rotation + * @param theta angle of rotation counterclockwise about the axis + */ + RotMatrix(Vec k, double theta) { // double[][] K = new double[3][3]; K[0][0] = 0.; K[0][1] = -k.v[2]; @@ -94,6 +130,10 @@ class RotMatrix { } } + /** + * Deep copy + * @return the copy + */ RotMatrix copy() { RotMatrix Rnew = new RotMatrix(); for (int i = 0; i < 3; i++) { @@ -104,6 +144,10 @@ RotMatrix copy() { return Rnew; } + /** + * Invert the rotation matrix (i.e. the transpose) + * @return the inverted matrix + */ RotMatrix invert() { RotMatrix mInv = new RotMatrix(); for (int i = 0; i < 3; i++) { @@ -114,6 +158,11 @@ RotMatrix invert() { return mInv; } + /** + * Matrix multiplication + * @param R2 Second matrix in the product + * @return The product matrix + */ RotMatrix multiply(RotMatrix R2) { // Multiply one rotation matrix by another RotMatrix R3 = new RotMatrix(); for (int i = 0; i < 3; i++) { @@ -126,10 +175,18 @@ RotMatrix multiply(RotMatrix R2) { // Multiply one rotation matrix by another return R3; } + /** + * Debug printout + * @param s Arbitrary string for the user's reference + */ void print(String s) { System.out.format("%s",this.toString(s)); } + /** + * Debug printout to a string + * @param s Arbitrary string for the user's reference + */ String toString(String s) { String str = String.format("The 3 by 3 rotation matrix %s:\n", s); for (int i = 0; i < 3; i++) { @@ -141,6 +198,11 @@ String toString(String s) { return str; } + /** + * Rotate a vector + * @param V The vector to rotate + * @return The rotated vector + */ Vec rotate(Vec V) { // Use the matrix to rotate a 3-D vector Vec Vp = new Vec(0., 0., 0.); for (int i = 0; i < 3; i++) { @@ -151,6 +213,11 @@ Vec rotate(Vec V) { // Use the matrix to rotate a 3-D vector return Vp; } + /** + * Rotate a 3 by 3 matrix (similarity transformation) + * @param S The square matrix to rotate + * @return Rotated square matrix + */ SquareMatrix rotate(SquareMatrix S) { // Use the matrix to rotate a 3 by 3 matrix SquareMatrix Q = new SquareMatrix(3); if (S.N != 3) System.out.format("RotMatrix rotate: the input matrix must be 3 by 3.\n"); @@ -166,6 +233,11 @@ SquareMatrix rotate(SquareMatrix S) { // Use the matrix to rotate a 3 by 3 matri return Q; } + /** + * Inverse rotation of a vector + * @param V Vector to rotate + * @return Rotated vector + */ Vec inverseRotate(Vec V) { // Use the matrix to rotate a 3-D vector in the opposite sense, using the // inverse (i.e. transpose) of the rotation matrix Vec Vp = new Vec(0., 0., 0.); @@ -177,6 +249,11 @@ Vec inverseRotate(Vec V) { // Use the matrix to rotate a 3-D vector in the oppos return Vp; } + /** + * Inverse rotate a 3 by 3 matrix (similarity transformation) + * @param S The square matrix to rotate + * @return Rotated square matrix + */ SquareMatrix inverseRotate(SquareMatrix S) { // Use the matrix to rotate a 3 by 3 matrix in the opposite sense SquareMatrix Q = new SquareMatrix(3); if (S.N != 3) System.out.format("RotMatrix rotate: the input matrix must be 3 by 3.\n"); diff --git a/tracking/src/main/java/org/hps/recon/tracking/kalman/RungeKutta4.java b/tracking/src/main/java/org/hps/recon/tracking/kalman/RungeKutta4.java index 9218dab067..6a29893ca1 100644 --- a/tracking/src/main/java/org/hps/recon/tracking/kalman/RungeKutta4.java +++ b/tracking/src/main/java/org/hps/recon/tracking/kalman/RungeKutta4.java @@ -14,6 +14,12 @@ public class RungeKutta4 { private double alpha; private org.lcsim.geometry.FieldMap fM; + /** + * Constructor + * @param Q Particle charge (+ or - one) + * @param dx Step size in mm + * @param fM Field map + */ public RungeKutta4(double Q, double dx, org.lcsim.geometry.FieldMap fM) { alpha = Q * 2.99792458e-4; // Q is the charge in units of the proton charge h = dx; // Step size in mm @@ -21,10 +27,14 @@ public RungeKutta4(double Q, double dx, org.lcsim.geometry.FieldMap fM) { this.fM = fM; // Magnetic field map } + /** + * Execute the integration + * @param r0 The initial point 3-vector in mm + * @param p0 The initial 3-vector momentum in GeV/c + * @param s Distance to propagate, along the trajectory + * @return Final position 3-vector of doubles + */ double[] integrate(Vec r0, Vec p0, double s) { - // r0 is the initial point in mm - // p0 is the initial momentum in GeV/c - // s is the distance to propagate (approximate to distance dx) double[] r = { r0.v[0], r0.v[1], r0.v[2], p0.v[0], p0.v[1], p0.v[2] }; double[] k1 = new double[6]; double[] k2 = new double[6]; @@ -49,7 +59,13 @@ public RungeKutta4(double Q, double dx, org.lcsim.geometry.FieldMap fM) { return r; } - private double[] f(Vec x, double[] p) { // Return all the derivatives + /** + * Return all the derivatives needed to make an integration step + * @param x 3-vector position in mm + * @param p 3-vector momentum in GeV/c + * @return 6 derivatives + */ + private double[] f(Vec x, double[] p) { double [] B = KalmanInterface.getFielD(x, fM); // This field routine assumes the Kalman-Filter coordinate system. double[] d = new double[6]; double pmag = FastMath.sqrt(p[0] * p[0] + p[1] * p[1] + p[2] * p[2]); diff --git a/tracking/src/main/java/org/hps/recon/tracking/kalman/SeedTrack.java b/tracking/src/main/java/org/hps/recon/tracking/kalman/SeedTrack.java index eab25e7c37..b14fff033f 100644 --- a/tracking/src/main/java/org/hps/recon/tracking/kalman/SeedTrack.java +++ b/tracking/src/main/java/org/hps/recon/tracking/kalman/SeedTrack.java @@ -51,14 +51,25 @@ class SeedTrack { private static final boolean debug = false; // Set true to generate lots of debug printout //private static int nCalls; + /** + * Get the field conversion factor + */ double getAlpha() { return alpha; } + /** + * Debug print of the seed + * @param s Arbitrary string for the user's reference + */ void print(String s) { System.out.format("%s", this.toString(s)); } + /** + * Debug print of the seed to a string + * @param s Arbitrary string for the user's reference + */ String toString(String s) { String str; if (success) { @@ -84,11 +95,19 @@ String toString(String s) { return str; } - // Older interface - SeedTrack(ArrayList data, // List of Si modules with data - double yOrigin, // New origin along beam to use for the fit - ArrayList hitList, // Element 0= index of Si module; Element 1= hit number - boolean verbose // Set true for lots of debug printout + /** + * Old constructor + * @param data List of Si modules with data + * @param yOrigin New origin along beam to use for the fit + * @param hitList List of hits to fit to, element 0= index of Si module; Element 1= hit number + * @param verbose No longer used + * @param kPar KalmanParams instance + */ + SeedTrack(ArrayList data, + double yOrigin, + ArrayList hitList, + boolean verbose, + KalmanParams kPar ) { ArrayList theHits = new ArrayList(hitList.size()); for (int i = 0; i < hitList.size(); i++) { @@ -97,16 +116,28 @@ String toString(String s) { theHits.add(tmpHit); } double yTarget = 0.; - SeedTracker(theHits, yOrigin, yTarget); + SeedTracker(theHits, yOrigin, yTarget, kPar); } - // Newer interface - SeedTrack(ArrayList hitList, double yOrigin, double yTarget) { - SeedTracker(hitList, yOrigin, yTarget); + /** + * New constructor + * @param hitList List of Si modules with data + * @param yOrigin New origin along beam to use for the fit + * @param yTarget Location along the beam of the target itself + * @param kPar KalmanParams instance + */ + SeedTrack(ArrayList hitList, double yOrigin, double yTarget, KalmanParams kPar) { + SeedTracker(hitList, yOrigin, yTarget, kPar); } - - private void SeedTracker(ArrayList hitList, double yOrigin, double yTarget) { + /** + * Actual code for the constructor + * @param hitList List of Si modules with data + * @param yOrigin New origin along beam to use for the fit + * @param yTarget Location along the beam of the target itself + * @param kPar KalmanParams instance + */ + private void SeedTracker(ArrayList hitList, double yOrigin, double yTarget, KalmanParams kPar) { // yOrigin is the location along the beam about which we fit the seed helix // yTarget is the location along the beam of the target itself //if (nCalls < 3) debug = true; @@ -153,14 +184,22 @@ private void SeedTracker(ArrayList hitList, double yOrigin, double yTarg Nnonbending = 0; // First find the average field - Vec Bvec = new Vec(0., 0., 0.); - for (KalHit pnt : hitList) { + + Vec Bvec = null; + if (kPar.uniformB) { + KalHit pnt = hitList.get(0); SiModule thisSi = pnt.module; - Vec thisB = KalmanInterface.getField(thisSi.toGlobal(new Vec(0., 0., 0.)), thisSi.Bfield); - Bvec = Bvec.sum(thisB); + Bvec = KalmanInterface.getField(new Vec(0., kPar.SVTcenter, 0.), thisSi.Bfield); + } else { + Bvec = new Vec(0., 0., 0.); + for (KalHit pnt : hitList) { + SiModule thisSi = pnt.module; + Vec thisB = KalmanInterface.getField(thisSi.toGlobal(new Vec(0., 0., 0.)), thisSi.Bfield); + Bvec = Bvec.sum(thisB); + } + double sF = 1.0 / ((double) nHits); + Bvec = Bvec.scale(sF); } - double sF = 1.0 / ((double) nHits); - Bvec = Bvec.scale(sF); Bavg = Bvec.mag(); if (debug) { System.out.format("*** SeedTrack: nHits=%d, Bavg=%10.5e\n", nHits, Bavg); } double c = 2.99793e8; // Speed of light in m/s @@ -287,51 +326,80 @@ private void SeedTracker(ArrayList hitList, double yOrigin, double yTarg // will give trouble here! // Rotate the result into the frame of the B field at the specified origin - KalHit itr = hitList.get(0); - SiModule firstSi = itr.module; - Vec firstB = KalmanInterface.getField(new Vec(0., yOrigin, 0.), firstSi.Bfield); - if (Math.abs(firstB.v[1] / firstB.v[2]) > 0.002) { - Vec zhat = firstB.unitVec(); - Vec yhat = new Vec(0., 1., 0.); - Vec xhat = (yhat.cross(zhat)).unitVec(); - yhat = zhat.cross(xhat); - RotMatrix Rot = new RotMatrix(xhat, yhat, zhat); - - hParm = rotateHelix(new Vec(drho, phi0, K, dz, tanl), Rot); - if (debug) { - firstB.print("Seedtrack, B field"); - Rot.print("Seedtrack, rotation matrix"); - hParm.print("Seedtrack, rotated helix"); + if (!kPar.uniformB) { + KalHit itr = hitList.get(0); + SiModule firstSi = itr.module; + Vec firstB = KalmanInterface.getField(new Vec(0., yOrigin, 0.), firstSi.Bfield); + if (Math.abs(firstB.v[1] / firstB.v[2]) > 0.002) { + Vec zhat = firstB.unitVec(); + Vec yhat = new Vec(0., 1., 0.); + Vec xhat = (yhat.cross(zhat)).unitVec(); + yhat = zhat.cross(xhat); + RotMatrix Rot = new RotMatrix(xhat, yhat, zhat); + + hParm = rotateHelix(new Vec(drho, phi0, K, dz, tanl), Rot); + if (debug) { + firstB.print("Seedtrack, B field"); + Rot.print("Seedtrack, rotation matrix"); + hParm.print("Seedtrack, rotated helix"); + } + } else { + hParm = new Vec(drho, phi0, K, dz, tanl); + if (debug) { hParm.print("Seedtrack, rotated helix"); } } } else { hParm = new Vec(drho, phi0, K, dz, tanl); - if (debug) { hParm.print("Seedtrack, rotated helix"); } + if (debug) hParm.print("Seedtrack, rotated helix"); } - success = true; } + /** + * Square a double + * @param x number to square + * @return the square + */ private double square(double x) { return x * x; } - Vec helixParams() { // Return the fitted helix parameters + /** + * Return the fitted helix parameters + * @return 5-vector of helix parameters + */ + Vec helixParams() { // return hParm; } - DMatrixRMaj covariance() { // Return covariance matrix of the fitted helix parameters + /** + * Return covariance matrix of the fitted helix parameters + * @return Covariance matrix + */ + DMatrixRMaj covariance() { return C; } - Vec solution() { // Return the 5 polynomial coefficients + /** + * Return the solution to the linear fit + * @return 5-vector of polynomial coefficients + */ + Vec solution() { return new Vec(a.unsafe_get(0, 0),a.unsafe_get(1, 0),a.unsafe_get(2, 0),a.unsafe_get(3, 0),a.unsafe_get(4, 0)); } - double B() { // Return the average field + /** + * Return the average field + * @return B field + */ + double B() { return Bavg; } - SquareMatrix solutionCovariance() { // Return covariance of the polynomial coefficients + /** + * Return covariance of the polynomial coefficients + * @return 5 by 5 square matrix + */ + SquareMatrix solutionCovariance() { // SquareMatrix CM = new SquareMatrix(5); for (int i=0; i<5; ++i) { for (int j=0; j<5; ++j) { @@ -341,19 +409,31 @@ SquareMatrix solutionCovariance() { // Return covariance of the polynomial coeff return CM; } - Vec solutionErrors() { // Return errors on the polynomial coefficients (for testing) + /** + * Return errors on the polynomial coefficients (for testing) + * @return 5-vector of errors + */ + Vec solutionErrors() { return new Vec(FastMath.sqrt(CovA.unsafe_get(0,0)), FastMath.sqrt(CovA.unsafe_get(1,1)), FastMath.sqrt(CovA.unsafe_get(2,2)), FastMath.sqrt(CovA.unsafe_get(3,3)), FastMath.sqrt(CovA.unsafe_get(4,4))); } - Vec errors() { // Return errors on the helix parameters + /** + * Return errors on the helix parameters + * @return 5-vector of errors + */ + Vec errors() { return new Vec(FastMath.sqrt(C.unsafe_get(0,0)), FastMath.sqrt(C.unsafe_get(1,1)), FastMath.sqrt(C.unsafe_get(2,2)), FastMath.sqrt(C.unsafe_get(3,3)), FastMath.sqrt(C.unsafe_get(4,4))); } - private double[] parabolaToCircle(double sgn, Vec coef) { // Utility to convert from parabola coefficients to circle - // (i.e. helix) - // parameters drho, phi0, and K + /** + * Utility to convert from parabola coefficients to circle (i.e. helix) + * @param sgn charge sign + * @param coef parameters drho, phi0, and K + * @return + */ + private double[] parabolaToCircle(double sgn, Vec coef) { R = -sgn / (2.0 * coef.v[2]); yc = sgn * R * coef.v[1]; xc = coef.v[0] - sgn * R * (1.0 - 0.5 * coef.v[1] * coef.v[1]); @@ -373,7 +453,12 @@ private double[] parabolaToCircle(double sgn, Vec coef) { // Utility to convert return r; } - // Transformation of a helix from one B-field frame to another, by rotation R + /** + * Transformation of a helix from one B-field frame to another, by rotation R + * @param a Helix parameters + * @param R Rotation matrix + * @return New helix parameters + */ private Vec rotateHelix(Vec a, RotMatrix R) { // a = 5 helix parameters // R = 3 by 3 rotation matrix @@ -386,7 +471,12 @@ private Vec rotateHelix(Vec a, RotMatrix R) { return new Vec(a.v[0], a_prime.v[0], a_prime.v[1], a.v[3], a_prime.v[2]); } - // Transform from momentum at helix starting point back to the helix parameters + /** + * Transform from momentum at helix starting point back to the helix parameters + * @param p Momentum vector + * @param Q Charge + * @return Helix parameters + */ private static Vec pTOa(Vec p, Double Q) { double phi0 = FastMath.atan2(-p.v[0], p.v[1]); double K = Q / FastMath.sqrt(p.v[0] * p.v[0] + p.v[1] * p.v[1]); @@ -397,7 +487,9 @@ private static Vec pTOa(Vec p, Double Q) { return new Vec(phi0, K, tanl); } - // Comparator function for sorting seed tracks by curvature + /** + * Comparator function for sorting seed tracks by curvature + */ static Comparator curvatureComparator = new Comparator() { public int compare(SeedTrack t1, SeedTrack t2) { double K1 = Math.abs(t1.helixParams().v[2]); @@ -410,7 +502,9 @@ public int compare(SeedTrack t1, SeedTrack t2) { } }; - // Comparator function for sorting seeds by distance from origin in x,z plane at the target + /** + * Comparator function for sorting seeds by distance from origin in x,z plane at the target + */ static Comparator dRhoComparator = new Comparator() { public int compare(SeedTrack t1, SeedTrack t2) { Vec pInt1 = t1.planeIntersection(t1.p0); @@ -423,7 +517,12 @@ public int compare(SeedTrack t1, SeedTrack t2) { } }; - //Check if two seeds are compatible given a certain relative threshold + /** + * Check if two seeds are compatible given a certain relative threshold + * @param st Other seed + * @param rel_eps Tolerance + * @return True if they are the same + */ boolean isCompatibleTo(SeedTrack st, double rel_eps) { Vec st_hp = st.helixParams(); boolean compatible = true; @@ -436,19 +535,34 @@ boolean isCompatibleTo(SeedTrack st, double rel_eps) { return compatible; } + /** + * Intersection of the helix with a plane perpendicular to the y axis + * @param p instance of a plane + * @return 3-vector intersection point + */ Vec planeIntersection(Plane p) { double arg = (K / alpha) * ((drho + (alpha / K)) * FastMath.sin(phi0) - (p.X().v[1] - yOrigin)); double phiInt = -phi0 + FastMath.asin(arg); return atPhi(phiInt); } - private Vec atPhi(double phi) { // point on the helix at the angle phi + /** + * Point on the helix at the angle phi + * @param phi radians + * @return 3-vector + */ + private Vec atPhi(double phi) { double x = (drho + (alpha / K)) * FastMath.cos(phi0) - (alpha / K) * FastMath.cos(phi0 + phi); double y = yOrigin + (drho + (alpha / K)) * FastMath.sin(phi0) - (alpha / K) * FastMath.sin(phi0 + phi); double z = dz - (alpha / K) * phi * tanl; return new Vec(x, y, z); } + /** + * Helix pivot transform + * @param pivot New pivot point + * @return Transformed helix parameters + */ double[] pivotTransform(double[] pivot) { Vec X0 = new Vec(0., yOrigin, 0.); double xC = X0.v[0] + (drho + alpha / K) * FastMath.cos(phi0); // Center of the helix circle @@ -469,14 +583,16 @@ private Vec atPhi(double phi) { // point on the helix at the angle phi return aP; } + /** + * Linear fit of a set of points to an approximate helix (parabola plus line) + * @param N Number of measurement points (detector layers) to fit. This must be no less than 5, with at least 2 axial and 2 stereo + * @param y nominal location of each measurement plane along the beam axis + * @param v array of measurement values in the detector coordinate system, perpendicular to the strips + * @param s one sigma error estimate on each measurement + * @param delta offset of the detector coordinate system from the global system (minus the nominal y value along the beam axis) + * @param R2 2nd row of the general rotation from the global system to the local detector system + */ private void LinearHelixFit(int N, double[] y, double[] v, double[] s, double[][] delta, double[][] R2) { - // N = number of measurement points (detector layers) to fit. This must be no less than 5, with at least 2 axial and 2 stereo - // y = nominal location of each measurement plane along the beam axis - // v = measurement value in the detector coordinate system, perpendicular to the strips - // s = one sigma error estimate on each measurement - // delta = offset of the detector coordinate system from the global system (minus the nominal y value along the beam axis) - // R2 = 2nd row of the general rotation from the global system to the local detector system - A[0][0] = 0.; A[0][1] = 0.; A[0][2] = 0.; @@ -561,14 +677,27 @@ private void LinearHelixFit(int N, double[] y, double[] v, double[] s, double[][ } } + /** + * Find a point on the line given y + * @param y y value + * @return z value + */ private double evaluateLine(double y) { return a.unsafe_get(0,0) + a.unsafe_get(1,0) * y; } + /** + * Find a point on the parabola given y + * @param y y value (along beam) + * @return x value + */ private double evaluateParabola(double y) { return a.unsafe_get(2,0) + (a.unsafe_get(3,0) + a.unsafe_get(4,0) * y) * y; } + /** + * Debug print the fit results + */ private void printFit(int N, double[] x, double[] y, double[] z, double[] v, double[] s) { System.out.format("LinearHelixFit: parabola a=%10.7f b=%10.7f c=%10.7f\n", a.get(2,0), a.get(3,0), a.get(4,0)); System.out.format("LinearHelixFit: line a=%10.7f b=%10.7f\n", a.get(0,0), a.get(1,0)); diff --git a/tracking/src/main/java/org/hps/recon/tracking/kalman/SiModule.java b/tracking/src/main/java/org/hps/recon/tracking/kalman/SiModule.java index 2367a7aed9..12cfab4c40 100644 --- a/tracking/src/main/java/org/hps/recon/tracking/kalman/SiModule.java +++ b/tracking/src/main/java/org/hps/recon/tracking/kalman/SiModule.java @@ -2,19 +2,17 @@ import java.util.ArrayList; import java.util.Iterator; -import java.util.logging.Level; -import java.util.logging.Logger; import org.apache.commons.math3.util.Pair; import org.hps.recon.tracking.TrackUtils; /** - * Description of a single silicon-strip module, and a container for its hits + * Description of a single silicon-strip module for the Kalman code, and a container for its hits. */ class SiModule { int Layer; // Tracker layer number, or a negative integer for a dummy layer added just for stepping in a // non-uniform field - int detector; // Detector number within the layer + int detector; // Detector or module number within the layer int millipedeID; // ID used by millipede for alignment ArrayList hits; // Hits ordered by coordinate value, from minimum to maximum Plane p; // Orientation and offset of the detector measurement plane in global coordinates @@ -25,33 +23,41 @@ class SiModule { double[] yExtent; // Plus and minus limits on the detector active area in the y direction // (perpendicular to the strips) boolean split; // True if the strips are split into two channels at the middle - RotMatrix R; // Rotation from the detector coordinates to global coordinates (not field coordinates) - RotMatrix Rinv; // Rotation from global (not field) coordinates to detector coordinates (transpose of R) - // The local coordinate system is u, v, t where t is more-or-less the beam direction (y-global) - // and v is the measurement direction. + RotMatrix R; // Rotation from the detector coordinates to global coordinates (not field coordinates) + RotMatrix Rinv; // Rotation from global (not field) coordinates to detector coordinates (transpose of R) + // The local coordinate system is u, v, t where t is more-or-less the beam direction (y-global) + // and v is the measurement direction. double thickness; // Silicon thickness in mm (should be 0 for a dummy layer!) int topBottom; // 0 for bottom tracker, 1 for top tracker org.lcsim.geometry.FieldMap Bfield; boolean isStereo; - private boolean verbose; - private Logger logger; + static final private boolean debug = false; + /** + * Constructor for backwards-compatibility with old stand-alone development code: assume axial layers have stereo angle=0 + */ SiModule(int Layer, Plane p, double stereo, double width, double height, boolean split, double thickness, org.lcsim.geometry.FieldMap Bfield) { - // for backwards-compatibility with old stand-alone development code: assume axial - // layers have stereo angle=0 + // this(Layer, p, stereo != 0.0, width, height, split, thickness, Bfield, 0, 0, 0); topBottom = 1; if (p.X().v[2]>0) topBottom = 0; } + /** + * Constructor for backward compatibility with old test code + */ SiModule(int Layer, Plane p, boolean isStereo, double width, double height, boolean split, double thickness, org.lcsim.geometry.FieldMap Bfield) { this(Layer, p, isStereo, width, height, split, thickness, Bfield, 0, 0, 0); topBottom = 1; if (p.X().v[2]>0) topBottom = 0; } - + + /** + * Another constructor for backward compatibility. + * No millipedeID and figures out top or bottom from the location coordinate. + */ SiModule(int Layer, Plane p, boolean isStereo, double width, double height, boolean split, double thickness, org.lcsim.geometry.FieldMap Bfield, int detector) { this(Layer, p, isStereo, width, height, split, thickness, Bfield, detector, 0, 0); @@ -59,19 +65,29 @@ class SiModule { if (p.X().v[2]>0) topBottom = 0; } + /** + * Full constructor + * @param Layer Layer number from 0 to 13 for 2019/2021 data + * @param p Plane describing the sensor location and orientation + * @param isStereo True for stereo layers, false for axial + * @param width Silicon wafer width + * @param height Silicon wafer height + * @param split True if the strips are split into two channels at the middle (thin sensors) + * @param thickness Silicon thickness + * @param Bfield Field map + * @param detector 0 or 1 (only zero for planes with a single sensor) + * @param millipedeID ID used for alignment studies + * @param topBottom Which detector, top (1) or bottom (0)? + */ SiModule(int Layer, Plane p, boolean isStereo, double width, double height, boolean split, double thickness, org.lcsim.geometry.FieldMap Bfield, int detector, int millipedeID, int topBottom) { this.topBottom = topBottom; - logger = Logger.getLogger(SiModule.class.getName()); - verbose = (logger.getLevel()==Level.FINE); this.millipedeID = millipedeID; - if (verbose) { + if (debug) { System.out.format("SiModule constructor called with layer = %d, detector module = %d, y=%8.2f\n", Layer, detector, p.X().v[1]); p.print("of SiModule"); - } - Vec BOnAxis = KalmanInterface.getField(new Vec(0.,p.X().v[1],0.), Bfield); - Vec BatCenter = KalmanInterface.getField(p.X(), Bfield); - if (verbose) { + Vec BOnAxis = KalmanInterface.getField(new Vec(0.,p.X().v[1],0.), Bfield); + Vec BatCenter = KalmanInterface.getField(p.X(), Bfield); BOnAxis.print("B field on axis"); BatCenter.print("B at detector center"); } @@ -94,10 +110,18 @@ class SiModule { hits = new ArrayList(); } + /** + * Debug printout of all details of an instance. + * @param s Arbitrary string for the user's reference. + */ void print(String s) { System.out.format("%s",this.toString(s)); } - + + /** + * Debug printout to a string of all details of an instance. + * @param s Arbitrary string for the user's reference. + */ String toString(String s) { boolean top = topBottom == 1; String str = String.format( @@ -125,6 +149,10 @@ String toString(String s) { } return str; } + + /** + * Abbreviated debug printout to a string. + */ public String toString() { boolean top = topBottom == 1; String str = String.format("Si Module: Lyr=%2d Det=%2d Top=%b Mpd=%d split=%b stereo=%b pnt=%8.3f %8.3f %8.3f t=%7.3f %7.3f %7.3f\n", @@ -134,11 +162,17 @@ public String toString() { return str; } - // Delete all the existing hits + /** + * Delete all the existing hits + */ void reset() { hits = new ArrayList(); } + /** + * Add a silicon-strip measurement to the list for this module + * @param m The measurement object + */ void addMeasurement(Measurement m) { if (hits.size() == 0) hits.add(m); else { @@ -154,11 +188,21 @@ void addMeasurement(Measurement m) { } } - Vec toGlobal(Vec vLocal) { // Convert a position vector from local detector coordinates to global + /** + * Convert a position vector from local detector coordinates to global + * @param vLocal 3-vector of the point in local coordinates + * @return 3-vector of the point in global coordinates + */ + Vec toGlobal(Vec vLocal) { return p.X().sum(R.rotate(vLocal)); } - Vec toLocal(Vec vGlobal) { // Convert a position vector from global coordinates to local detector + /** + * Convert a position vector from global coordinates to local detector + * @param vGlobal 3-vector of the point in global coordinates + * @return 3-vector of the point in local coordinates + */ + Vec toLocal(Vec vGlobal) { return Rinv.rotate(vGlobal.dif(p.X())); } diff --git a/tracking/src/main/java/org/hps/recon/tracking/kalman/StateVector.java b/tracking/src/main/java/org/hps/recon/tracking/kalman/StateVector.java index 5ad7205c46..7a13d8eebf 100644 --- a/tracking/src/main/java/org/hps/recon/tracking/kalman/StateVector.java +++ b/tracking/src/main/java/org/hps/recon/tracking/kalman/StateVector.java @@ -6,6 +6,7 @@ import org.ejml.dense.row.CommonOps_DDRM; import org.ejml.dense.row.factory.LinearSolverFactory_DDRM; import org.ejml.interfaces.linsol.LinearSolverDense; +import org.apache.commons.math.util.FastMath; /** * Helix state vector (projected, filtered, or smoothed) for the Kalman filter @@ -21,7 +22,7 @@ class StateVector { final static private boolean debug = false; DMatrixRMaj F; // Propagator matrix to propagate from this site to the next site private static Logger logger; - private DMatrixRMaj K; // Kalman gain matrix + DMatrixRMaj K; // Kalman gain matrix // Working arrays for efficiency, to avoid creating temporary working space over and over private static DMatrixRMaj tempV; @@ -33,14 +34,26 @@ class StateVector { private static DMatrixRMaj U; // Unit matrix private static LinearSolverDense solver; private static boolean initialized; - - // Constructor for the initial state vector used to start the Kalman filter. - StateVector(int site, Vec helixParams, DMatrixRMaj Cov, Vec pivot, double B, Vec tB, Vec origin) { + boolean uniformB; + + /** + * Constructor for the initial state vector used to start the Kalman filter. + * @param site integer index of the site + * @param helixParams 5-vector of helix parameters + * @param Cov covariance matrix of the helix parameters + * @param pivot 3-vector pivot point of the helix + * @param B magnetic field magnitude + * @param tB magnetic field direction cosines + * @param origin 3-vector origin of the coordinate system + * @param uniformB true to treat the magnetic field as uniform + */ + StateVector(int site, Vec helixParams, DMatrixRMaj Cov, Vec pivot, double B, Vec tB, Vec origin, boolean uniformB) { // Here tB is the B field direction, while B is the magnitude if (debug) System.out.format("StateVector: constructing an initial state vector\n"); helix = new HelixState(helixParams, pivot, origin, Cov, B, tB); kLow = site; kUp = kLow; + this.uniformB = uniformB; if (!initialized) { // Initialize the static working arrays on the first call logger = Logger.getLogger(StateVector.class.getName()); tempV = new DMatrixRMaj(5,1); @@ -55,10 +68,13 @@ class StateVector { } } - // Constructor for a new blank state vector with a new B field - StateVector(int site, double B, Vec tB, Vec origin) { + /** + * Constructor for a new blank state vector with a new B field + */ + StateVector(int site, double B, Vec tB, Vec origin, boolean uniformB) { helix = new HelixState(B, tB, origin); kLow = site; + this.uniformB = uniformB; if (!initialized) { // Initialize the static working arrays on the first call logger = Logger.getLogger(StateVector.class.getName()); tempV = new DMatrixRMaj(5,1); @@ -73,9 +89,13 @@ class StateVector { } } - // Constructor for a new completely blank state vector - StateVector(int site) { + /** + * Constructor for a new completely blank state vector + * @param site + */ + StateVector(int site, boolean uniformB) { kLow = site; + this.uniformB = uniformB; if (!initialized) { // Initialize the static working arrays on the first call logger = Logger.getLogger(StateVector.class.getName()); tempV = new DMatrixRMaj(5,1); @@ -90,8 +110,12 @@ class StateVector { } } + /** + * Deep copy of the state vector + * @return copy + */ StateVector copy() { - StateVector q = new StateVector(kLow); + StateVector q = new StateVector(kLow, uniformB); q.helix = (HelixState)helix.copy(); // Deep copy q.kUp = kUp; q.F = F; // Don't deep copy the F matrix @@ -102,18 +126,25 @@ StateVector copy() { return q; } - // Debug printout of the state vector + /** + * Debug printout of the state vector + * @param s Arbitrary string for the user's reference + */ void print(String s) { System.out.format("%s", this.toString(s)); } - + + /** + * Debug printout to a string of the state vector + * @param s Arbitrary string for the user's reference + */ String toString(String s) { String str = String.format(">>>Dump of state vector %s %d %d\n", s, kUp, kLow); str = str + helix.toString(" "); if (F != null) str = str + "Propagator matrix: " + F.toString(); double sigmas; if (R > 0.) { - sigmas = r / Math.sqrt(R); + sigmas = r / FastMath.sqrt(R); } else { sigmas = 0.; } @@ -123,22 +154,26 @@ String toString(String s) { return str; } - // Create a predicted state vector by propagating a given helix to a measurement site + /** + * Create a predicted state vector by propagating a given helix to a measurement site + * @param newSite index of the new site + * @param pivot pivot point of the new site in the local coordinates of this state vector (i.e. coordinates of the old site) + * @param B magnitude of the magnetic field at the pivot point, in global coordinates + * @param t direction of the magnetic field at the pivot point, in global coordinates + * @param originPrime origin of the detector coordinates at the new site in global coordinates + * @param XL thickness of the scattering material + * @param deltaE energy loss in the scattering material + * @return predicted state vector + */ StateVector predict(int newSite, Vec pivot, double B, Vec t, Vec originPrime, double XL, double deltaE) { - // newSite = index of the new site - // pivot = pivot point of the new site in the local coordinates of this state vector (i.e. coordinates of the old site) - // B and t = magnitude and direction of the magnetic field at the pivot point, in global coordinates - // XL = thickness of the scattering material - // deltaE = energy loss in the scattering material - // originPrime = origin of the detector coordinates at the new site in global coordinates // This constructs a new blank state vector with pivot and helix parameters undefined as yet - StateVector aPrime = new StateVector(newSite, B, t, originPrime); + StateVector aPrime = new StateVector(newSite, B, t, originPrime, uniformB); aPrime.kUp = kUp; aPrime.helix.X0 = pivot; // pivot before helix rotation, in coordinate system of the previous site - double E = helix.a.v[2] * Math.sqrt(1.0 + helix.a.v[4] * helix.a.v[4]); - double deltaEoE = deltaE / E; + double momentum = FastMath.sqrt(1.0 + helix.a.v[4] * helix.a.v[4])/FastMath.abs(helix.a.v[2]); + double deltaEoE = deltaE / momentum; // Transform helix in old coordinate system to new pivot point lying on the next detector plane if (deltaE == 0.) { @@ -154,63 +189,62 @@ StateVector predict(int newSite, Vec pivot, double B, Vec t, Vec originPrime, do helix.X0.print("old pivot"); } + double eFactor = 1.0 - deltaEoE; F = new DMatrixRMaj(5,5); - this.helix.makeF(aPrime.helix.a, F); // Calculate derivatives of the pivot transform - if (deltaE != 0.) { - double factor = 1.0 - deltaEoE; - for (int i = 0; i < 5; i++) F.unsafe_set(i, 2, F.unsafe_get(i,2)*factor); - } + this.helix.makeF(aPrime.helix.a, F, eFactor); // Calculate derivatives of the pivot transform // Transform to the coordinate system of the field at the new site // First, transform the pivot point to the new system aPrime.helix.X0 = aPrime.helix.toLocal(this.helix.toGlobal(aPrime.helix.X0)); - - // Calculate the matrix for the net rotation from the old site coordinates to the new site coordinates - RotMatrix Rt = aPrime.helix.Rot.multiply(this.helix.Rot.invert()); - if (debug) { - aPrime.helix.Rot.print("aPrime rotation matrix"); - this.helix.Rot.print("this rotation matrix"); - Rt.print("rotation from old local frame to new local frame"); - aPrime.helix.a.print("StateVector:predict helix before rotation"); - } - - // Rotate the helix parameters here. - // dz and drho will remain unchanged at zero - // phi0 and tanl(lambda) change, as does kappa (1/pt). However, |p| should be unchanged by the rotation. - // This call to rotateHelix also calculates the derivative matrix fRot - aPrime.helix.a = HelixState.rotateHelix(aPrime.helix.a, Rt, tempM); - if (debug) { - aPrime.helix.a.print("StateVector:predict helix after rotation"); - System.out.println("fRot from StateVector:predict"); - tempM.print(); - } - CommonOps_DDRM.mult(tempM, F, tempA); - - // Test the derivatives - /* - if (debug) { - double daRel[] = { 0.01, 0.03, -0.02, 0.05, -0.01 }; - StateVector aPda = this.Copy(); - for (int i = 0; i < 5; i++) { - aPda.a.v[i] = a.v[i] * (1.0 + daRel[i]); + if (!uniformB) { + + // Calculate the matrix for the net rotation from the old site coordinates to the new site coordinates + RotMatrix Rt = aPrime.helix.Rot.multiply(this.helix.Rot.invert()); + if (debug) { + aPrime.helix.Rot.print("aPrime rotation matrix"); + this.helix.Rot.print("this rotation matrix"); + Rt.print("rotation from old local frame to new local frame"); + aPrime.helix.a.print("StateVector:predict helix before rotation"); + } + + // Rotate the helix parameters here. + // dz and drho will remain unchanged at zero + // phi0 and tanl(lambda) change, as does kappa (1/pt). However, |p| should be unchanged by the rotation. + // This call to rotateHelix also calculates the derivative matrix fRot = tempM + aPrime.helix.a = HelixState.rotateHelix(aPrime.helix.a, Rt, tempM); + if (debug) { + aPrime.helix.a.print("StateVector:predict helix after rotation"); + System.out.println("fRot from StateVector:predict"); + tempM.print(); } - Vec da = aPda.a.dif(a); - StateVector aPrimeNew = this.Copy(); - aPrimeNew.a = aPda.pivotTransform(pivot); - RotMatrix RtTmp = Rot.invert().multiply(aPrime.Rot); - SquareMatrix fRotTmp = new SquareMatrix(5); - aPrimeNew.a = rotateHelix(aPrimeNew.a, RtTmp, fRotTmp); - for (int i = 0; i < 5; i++) { - double deltaExact = aPrimeNew.a.v[i] - aPrime.a.v[i]; - double delta = 0.; - for (int j = 0; j < 5; j++) { - delta += F.M[i][j] * da.v[j]; + CommonOps_DDRM.mult(tempM, F, tempA); + + // Test the derivatives + /* + if (debug) { + double daRel[] = { 0.01, 0.03, -0.02, 0.05, -0.01 }; + StateVector aPda = this.Copy(); + for (int i = 0; i < 5; i++) { + aPda.a.v[i] = a.v[i] * (1.0 + daRel[i]); + } + Vec da = aPda.a.dif(a); + StateVector aPrimeNew = this.Copy(); + aPrimeNew.a = aPda.pivotTransform(pivot); + RotMatrix RtTmp = Rot.invert().multiply(aPrime.Rot); + SquareMatrix fRotTmp = new SquareMatrix(5); + aPrimeNew.a = rotateHelix(aPrimeNew.a, RtTmp, fRotTmp); + for (int i = 0; i < 5; i++) { + double deltaExact = aPrimeNew.a.v[i] - aPrime.a.v[i]; + double delta = 0.; + for (int j = 0; j < 5; j++) { + delta += F.M[i][j] * da.v[j]; + } + System.out.format("Test of F: Helix parameter %d, deltaExact=%10.8f, delta=%10.8f\n", i, deltaExact, + delta); } - System.out.format("Test of F: Helix parameter %d, deltaExact=%10.8f, delta=%10.8f\n", i, deltaExact, - delta); } + */ } - */ aPrime.kLow = newSite; aPrime.kUp = kUp; @@ -221,7 +255,6 @@ StateVector predict(int newSite, Vec pivot, double B, Vec t, Vec originPrime, do Cinv.set(this.helix.C); if (debug) System.out.format("StateVector.predict: XL=%9.6f\n", XL); } else { - double momentum = (1.0 / helix.a.v[2]) * Math.sqrt(1.0 + helix.a.v[4] * helix.a.v[4]); double sigmaMS = HelixState.projMSangle(momentum, XL); if (debug) System.out.format("StateVector.predict: momentum=%12.5e, XL=%9.6f sigmaMS=%12.5e\n", momentum, XL, sigmaMS); this.helix.getQ(sigmaMS, Q); @@ -229,18 +262,25 @@ StateVector predict(int newSite, Vec pivot, double B, Vec t, Vec originPrime, do } // Now propagate the multiple scattering matrix and covariance matrix to the new site - CommonOps_DDRM.multTransB(Cinv, tempA, tempM); aPrime.helix.C = new DMatrixRMaj(5,5); - CommonOps_DDRM.mult(tempA, tempM, aPrime.helix.C); + if (uniformB) { + CommonOps_DDRM.multTransB(Cinv, F, tempM); + CommonOps_DDRM.mult(F, tempM, aPrime.helix.C); + } else { + CommonOps_DDRM.multTransB(Cinv, tempA, tempM); + CommonOps_DDRM.mult(tempA, tempM, aPrime.helix.C); + } return aPrime; } - // Create a filtered state vector from a predicted state vector + /** + * Create a filtered state vector from a predicted state vector + * @param H prediction matrix (5-vector) + * @param V hit variance (1/sigma^2) + * @return filtered state vector + */ StateVector filter(DMatrixRMaj H, double V) { - // H = prediction matrix (5-vector) - // V = hit variance (1/sigma^2) - StateVector aPrime = this.copy(); aPrime.kUp = kLow; @@ -315,7 +355,13 @@ StateVector filter(DMatrixRMaj H, double V) { return aPrime; } - // Modify the state vector by removing the hit information + /** + * Modify the state vector by removing the hit information + * @param H + * @param V + * @param Cnew + * @return + */ Vec inverseFilter(DMatrixRMaj H, double V, DMatrixRMaj Cnew) { CommonOps_DDRM.mult(helix.C, H, tempV); double denom = -V + CommonOps_DDRM.dot(H, tempV); @@ -337,7 +383,12 @@ Vec inverseFilter(DMatrixRMaj H, double V, DMatrixRMaj Cnew) { return aNew; } - // Create a smoothed state vector from the filtered state vector + /** + * Create a smoothed state vector from the filtered state vector + * @param snS Smoothed state vector of the next layer out + * @param snP Predicted state vector of the next layer out + * @return Smoothed state vector corresponding to this layer's filtered state vector + */ StateVector smooth(StateVector snS, StateVector snP) { if (debug) System.out.format("StateVector.smooth of filtered state %d %d, using smoothed state %d %d and predicted state %d %d\n", kLow, kUp, snS.kLow, snS.kUp, snP.kLow, snP.kUp); @@ -414,36 +465,60 @@ StateVector smooth(StateVector snS, StateVector snP) { return sS; } - // Return errors on the helix parameters at the global origin + /** + * Return errors on the helix parameters at the global origin + * @param aPrime helix parameters for a pivot at the global origin, assumed already to be calculated by pivotTransform() + * @return 5-vector of errors + */ Vec helixErrors(Vec aPrime) { - // aPrime are the helix parameters for a pivot at the global origin, assumed - // already to be calculated by pivotTransform() - DMatrixRMaj tC = covariancePivotTransform(aPrime); - return new Vec(Math.sqrt(tC.unsafe_get(0,0)), Math.sqrt(tC.unsafe_get(1,1)), Math.sqrt(tC.unsafe_get(2,2)), - Math.sqrt(tC.unsafe_get(3,3)), Math.sqrt(tC.unsafe_get(4,4))); + + DMatrixRMaj tC = covariancePivotTransform(aPrime, 1.0); + return new Vec(FastMath.sqrt(tC.unsafe_get(0,0)), FastMath.sqrt(tC.unsafe_get(1,1)), FastMath.sqrt(tC.unsafe_get(2,2)), + FastMath.sqrt(tC.unsafe_get(3,3)), FastMath.sqrt(tC.unsafe_get(4,4))); } - // Transform the helix covariance to new pivot point (specified in local coordinates) - DMatrixRMaj covariancePivotTransform(Vec aP) { + /** + * Transform the helix covariance to new pivot point (specified in local coordinates) + * @param aP 5-vector of helix parameters + * @return transformed helix parameters + */ + DMatrixRMaj covariancePivotTransform(Vec aP, double eFactor) { // aP are the helix parameters for the new pivot point, assumed already to be // calculated by pivotTransform() // Note that no field rotation is assumed or accounted for here DMatrixRMaj mF = new DMatrixRMaj(5,5); - helix.makeF(aP, mF); + helix.makeF(aP, mF, eFactor); CommonOps_DDRM.multTransB(helix.C, mF, tempM); CommonOps_DDRM.mult(mF, tempM, tempA); return tempA; } - // Go to and from 1D EJML matrix for a vector Vec + + /** + * Go to and from 1D EJML matrix for a vector Vec + * @param a vector + * @param m EJML matrix out + */ static void vecToM(Vec a, DMatrixRMaj m) { for (int i=0; i hitMap) { + if (event == null) return -1; String trackCollectionName = "GBLTracks"; if (!event.hasCollection(Track.class, trackCollectionName)) { System.out.format("\nKalmanInterface.compareAllTracks: the track collection %s is missing. Abort.\n",trackCollectionName); @@ -155,14 +164,27 @@ int compareGBL(EventHeader event, Map hitMap) { return -1; } + /** + * See if the track candidate contains all of the given list of hits + * @param hitList list of hits to check + * @return true for a match + */ boolean contains(ArrayList hitList) { return hits.containsAll(hitList); } + /** + * Return the number of hits on a track candidate + * @return number of hits + */ int numHits() { return hits.size(); } + /** + * Return the number of stereo hits on the track candidate + * @return number of stereo hits + */ int numStereo() { int nStereo = 0; for (KalHit ht : hits) { @@ -171,6 +193,10 @@ int numStereo() { return nStereo; } + /** + * Return the helix parameters at the origin + * @return 5-vector of helix parameters + */ Vec originHelix() { MeasurementSite site0 = null; for (MeasurementSite site: sites) { // sites are assumed to be sorted @@ -182,6 +208,11 @@ Vec originHelix() { return site0.aS.helix.pivotTransform(); } + /** + * Remove a hit from a track candidate + * @param hit the hit to remove from the fit + * @param deleteFromList true to delete it from the list of hits + */ void removeHit(KalHit hit, boolean deleteFromList) { MeasurementSite siteR = null; SiModule mod = hit.module; @@ -218,6 +249,10 @@ void removeHit(KalHit hit, boolean deleteFromList) { if (nstr < 3 || nax < 2) good = false; } + /** + * Refit a track candidate + * @return true for success + */ boolean reFit() { final boolean verbose = false; if (verbose) System.out.format("TrackCandidate.reFit: starting filtering for event %d.\n",eventNumber); @@ -364,10 +399,20 @@ boolean reFit() { return true; } + /** + * Debug printout of a track candidate + * @param s Arbitrary string for the user's reference + * @param shrt true to give an abreviated printout + */ void print(String s, boolean shrt) { System.out.format("%s", this.toString(s, shrt)); } - + + /** + * Debug printout to a string of a track candidate + * @param s Arbitrary string for the user's reference + * @param shrt true to give an abreviated printout + */ String toString(String s, boolean shrt) { String str; if (good) { @@ -454,7 +499,9 @@ String toString(String s, boolean shrt) { return str; } - // Comparator function for sorting track candidates by quality + /** + * Comparator function for sorting track candidates by quality + */ static Comparator CandidateComparator = new Comparator() { public int compare(TrackCandidate t1, TrackCandidate t2) { double p1 = 1.; @@ -469,6 +516,9 @@ public int compare(TrackCandidate t1, TrackCandidate t2) { } }; + /** + * Check whether two candidates are identical + */ @Override public boolean equals(Object other) { if (this == other) return true;