Author: [log in to unmask] Date: Wed Oct 21 12:55:14 2015 New Revision: 3867 Log: Updated trident and Moller analysis driver to include more selection methods. Modified: java/trunk/users/src/main/java/org/hps/users/kmccarty/TriggerProcessAnalysisDriver.java Modified: java/trunk/users/src/main/java/org/hps/users/kmccarty/TriggerProcessAnalysisDriver.java ============================================================================= --- java/trunk/users/src/main/java/org/hps/users/kmccarty/TriggerProcessAnalysisDriver.java (original) +++ java/trunk/users/src/main/java/org/hps/users/kmccarty/TriggerProcessAnalysisDriver.java Wed Oct 21 12:55:14 2015 @@ -9,10 +9,14 @@ import java.util.List; import java.util.Set; +import org.hps.recon.tracking.TrackType; +import org.hps.recon.tracking.TrackUtils; import org.hps.record.triggerbank.TriggerModule; import org.lcsim.event.Cluster; import org.lcsim.event.EventHeader; import org.lcsim.event.ReconstructedParticle; +import org.lcsim.event.RelationalTable; +import org.lcsim.event.Track; import org.lcsim.util.Driver; import org.lcsim.util.aida.AIDA; @@ -20,6 +24,8 @@ private int eventsProcessed = 0; private int møllersProcessed = 0; private int tridentsProcessed = 0; + private int gblMøllersProcessed = 0; + private int gblTridentsProcessed = 0; private double timeCoincidence = 2.5; private AIDA aida = AIDA.defaultInstance(); private String clusterCollectionName = "EcalClustersCorr"; @@ -38,6 +44,8 @@ private IHistogram2D trctmClusterPosition = aida.histogram2D("Tridents CTMatched/Cluster Seed Position", 46, -23, 23, 11, -5.5, 5.5); private IHistogram2D trctmEnergySum2D = aida.histogram2D("Tridents CTMatched/Cluster Energy Sum 2D", 300, 0.000, 1.500, 300, 0.000, 1.500); private IHistogram2D trctmMomentumSum2D = aida.histogram2D("Tridents CTMatched/Track Momentum Sum 2D", 300, 0.000, 1.500, 300, 0.000, 1.500); + private IHistogram2D trctmESumCoplanarity = aida.histogram2D("Tridents CTMatched/Cluster Energy Sum vs. Coplanarity", 300, 0.000, 1.500, 360, 0, 360); + private IHistogram2D trctmPSumCoplanarity = aida.histogram2D("Tridents CTMatched/Track Momentum Sum vs. Coplanarity", 300, 0.000, 1.500, 360, 0, 360); // Define the Møller cluster-track matched condition plots. private IHistogram1D møctmInvariantMass = aida.histogram1D("Møller CTMatched/Invariant Mass", 140, 0.0, 0.070); @@ -50,6 +58,29 @@ private IHistogram2D møctmClusterPosition = aida.histogram2D("Møller CTMatched/Cluster Seed Position", 46, -23, 23, 11, -5.5, 5.5); private IHistogram2D møctmEnergySum2D = aida.histogram2D("Møller CTMatched/Cluster Energy Sum 2D", 300, 0.000, 1.500, 300, 0.000, 1.500); private IHistogram2D møctmMomentumSum2D = aida.histogram2D("Møller CTMatched/Track Momentum Sum 2D", 300, 0.000, 1.500, 300, 0.000, 1.500); + private IHistogram2D møctmESumCoplanarity = aida.histogram2D("Møller CTMatched/Cluster Energy Sum vs. Coplanarity", 300, 0.000, 1.500, 360, 0, 360); + private IHistogram2D møctmPSumCoplanarity = aida.histogram2D("Møller CTMatched/Track Momentum Sum vs. Coplanarity", 300, 0.000, 1.500, 360, 0, 360); + + // Define the Møller track-only condition plots. + private IHistogram1D møgblTimeCoincidence = aida.histogram1D("Møller Track-Only/Time Coincidence", 100, -4, 4); + private IHistogram1D møgblInvariantMass = aida.histogram1D("Møller Track-Only/Invariant Mass", 140, 0.0, 0.070); + private IHistogram1D møgblInstancesInEvent = aida.histogram1D("Møller Track-Only/Instances in Event", 9, 0.5, 9.5); + private IHistogram1D møgblMomentumSum1D = aida.histogram1D("Møller Track-Only/Track Momentum Sum", 150, 0.000, 1.500); + private IHistogram1D møgblElectronMomentum = aida.histogram1D("Møller Track-Only/Electron Track Momentum", 150, 0.000, 1.500); + private IHistogram2D møgblClusterPosition = aida.histogram2D("Møller Track-Only/Extrapolated Track Position", 138, -23, 23, 33, -5.5, 5.5); + private IHistogram2D møgblMomentumSum2D = aida.histogram2D("Møller Track-Only/Track Momentum Sum 2D", 300, 0.000, 1.500, 300, 0.000, 1.500); + private IHistogram2D møgblPSumCoplanarity = aida.histogram2D("Møller Track-Only/Track Momentum Sum vs. Coplanarity", 300, 0.000, 1.500, 360, 0, 360); + + // Define the GBL trident condition plots. + private IHistogram1D trgblInvariantMass = aida.histogram1D("Tridents Track-Only/Invariant Mass", 140, 0.0, 0.070); + private IHistogram1D trgblInstancesInEvent = aida.histogram1D("Tridents Track-Only/Instances in Event", 9, 0.5, 9.5); + private IHistogram1D trgblMomentumSum1D = aida.histogram1D("Tridents Track-Only/Track Momentum Sum", 150, 0.000, 1.500); + private IHistogram1D trgblElectronMomentum = aida.histogram1D("Tridents Track-Only/Electron Track Momentum", 150, 0.000, 1.500); + private IHistogram1D trgblPositronMomentum = aida.histogram1D("Tridents Track-Only/Positron Track Momentum", 150, 0.000, 1.500); + private IHistogram1D trgblTimeCoincidence = aida.histogram1D("Tridents Track-Only/Time Coincidence", 100, -4, 4); + private IHistogram2D trgblClusterPosition = aida.histogram2D("Tridents Track-Only/Extrapolated Track Position", 46, -23, 23, 11, -5.5, 5.5); + private IHistogram2D trgblMomentumSum2D = aida.histogram2D("Tridents Track-Only/Track Momentum Sum 2D", 300, 0.000, 1.500, 300, 0.000, 1.500); + private IHistogram2D trgblPSumCoplanarity = aida.histogram2D("Tridents Track-Only/Track Momentum Sum vs. Coplanarity", 300, 0.000, 1.500, 360, 0, 360); @Override public void endOfData() { @@ -59,10 +90,19 @@ System.out.println("Processed " + eventsProcessed + " events."); System.out.println("Processed " + møllersProcessed + " Møller events"); System.out.println("\tAcceptance :: " + (100.0 * møllersProcessed / eventsProcessed) + "%"); - System.out.println("\tRate :: " + (møllersProcessed * scale) + "Hz"); + System.out.println("\tRate :: " + (møllersProcessed * scale) + " Hz"); + System.out.println("Processed " + tridentsProcessed + " trident events"); System.out.println("\tAcceptance :: " + (100.0 * tridentsProcessed / eventsProcessed) + "%"); - System.out.println("\tRate :: " + (tridentsProcessed * scale) + "Hz"); + System.out.println("\tRate :: " + (tridentsProcessed * scale) + " Hz"); + + System.out.println("Processed " + gblMøllersProcessed + " track-only Møller events"); + System.out.println("\tAcceptance :: " + (100.0 * gblMøllersProcessed / eventsProcessed) + "%"); + System.out.println("\tRate :: " + (gblMøllersProcessed * scale) + " Hz"); + + System.out.println("Processed " + gblTridentsProcessed + " Rafo trident events"); + System.out.println("\tAcceptance :: " + (100.0 * gblTridentsProcessed / eventsProcessed) + "%"); + System.out.println("\tRate :: " + (gblTridentsProcessed * scale) + " Hz"); // Scale the cluster-track matched Møller plots. møctmInvariantMass.scale(scale); @@ -123,16 +163,21 @@ List<Cluster> clusterList = event.get(Cluster.class, clusterCollectionName); // Get cluster-track matched top/bottom pairs. - List<ReconstructedParticle[]> ctMatchedPairs = getTopBottomTracksCTMatched(trackList); + List<ReconstructedParticle[]> gblMatchedPairs = getTopBottomTracksGBL(trackList); + List<ReconstructedParticle[]> ctMatchedPairs = getTopBottomTracksCTMatched(trackList); // Get the trident and Møller tracks for the matched track // and cluster pair condition sets. - List<ReconstructedParticle[]> møllers = getMøllerTracksCTMatched(ctMatchedPairs); - List<ReconstructedParticle[]> tridents = getTridentTracksCTMatched(ctMatchedPairs); + List<ReconstructedParticle[]> møllers = getMøllerTracksCTMatched(ctMatchedPairs); + List<ReconstructedParticle[]> møllersGBL = getMøllerTracksGBL(gblMatchedPairs, event); + List<ReconstructedParticle[]> tridents = getTridentTracksCTMatched(ctMatchedPairs); + List<ReconstructedParticle[]> tridentsGBL = getTridentClustersGBL(gblMatchedPairs, TriggerModule.getTopBottomPairs(clusterList, Cluster.class), event); // Track how many events had tridents and Møllers. if(!møllers.isEmpty()) { møllersProcessed++; } if(!tridents.isEmpty()) { tridentsProcessed++; } + if(!møllersGBL.isEmpty()) { gblMøllersProcessed++; } + if(!tridentsGBL.isEmpty()) { gblTridentsProcessed++; } // Produce Møller cluster-track matched plots. møctmInstancesInEvent.fill(møllers.size()); @@ -145,6 +190,7 @@ møctmElectronEnergy.fill(trackClusters[1].getEnergy()); møctmEnergySum1D.fill(TriggerModule.getValueEnergySum(trackClusters)); møctmEnergySum2D.fill(trackClusters[0].getEnergy(), trackClusters[1].getEnergy()); + møctmESumCoplanarity.fill(TriggerModule.getValueEnergySum(trackClusters), getCalculatedCoplanarity(trackClusters)); møctmTimeCoincidence.fill(TriggerModule.getClusterTime(trackClusters[0]) - TriggerModule.getClusterTime(trackClusters[1])); møctmClusterPosition.fill(TriggerModule.getClusterXIndex(trackClusters[0]), TriggerModule.getClusterYIndex(trackClusters[0])); møctmClusterPosition.fill(TriggerModule.getClusterXIndex(trackClusters[1]), TriggerModule.getClusterYIndex(trackClusters[1])); @@ -155,6 +201,8 @@ møctmElectronMomentum.fill(pair[1].getMomentum().magnitude()); møctmMomentumSum1D.fill(VecOp.add(pair[0].getMomentum(), pair[1].getMomentum()).magnitude()); møctmMomentumSum2D.fill(pair[0].getMomentum().magnitude(), pair[1].getMomentum().magnitude()); + møctmPSumCoplanarity.fill(VecOp.add(pair[0].getMomentum(), pair[1].getMomentum()).magnitude(), + getCalculatedCoplanarity(new Track[] { pair[0].getTracks().get(0), pair[1].getTracks().get(0) })); } // Produce trident cluster-track matched plots. @@ -172,8 +220,9 @@ // Populate the cluster plots. trctmElectronEnergy.fill(electronCluster.getEnergy()); trctmPositronEnergy.fill(positronCluster.getEnergy()); + trctmEnergySum2D.fill(pair[0].getEnergy(), pair[1].getEnergy()); trctmEnergySum1D.fill(TriggerModule.getValueEnergySum(trackClusters)); - trctmEnergySum2D.fill(pair[0].getEnergy(), pair[1].getEnergy()); + trctmESumCoplanarity.fill(TriggerModule.getValueEnergySum(trackClusters), getCalculatedCoplanarity(trackClusters)); trctmTimeCoincidence.fill(TriggerModule.getClusterTime(trackClusters[0]) - TriggerModule.getClusterTime(trackClusters[1])); trctmClusterPosition.fill(TriggerModule.getClusterXIndex(trackClusters[0]), TriggerModule.getClusterYIndex(trackClusters[0])); trctmClusterPosition.fill(TriggerModule.getClusterXIndex(trackClusters[1]), TriggerModule.getClusterYIndex(trackClusters[1])); @@ -184,7 +233,104 @@ trctmPositronMomentum.fill(positronTrack.getMomentum().magnitude()); trctmMomentumSum1D.fill(VecOp.add(pair[0].getMomentum(), pair[1].getMomentum()).magnitude()); trctmMomentumSum2D.fill(pair[0].getMomentum().magnitude(), pair[1].getMomentum().magnitude()); - } + trctmPSumCoplanarity.fill(VecOp.add(pair[0].getMomentum(), pair[1].getMomentum()).magnitude(), + getCalculatedCoplanarity(new Track[] { pair[0].getTracks().get(0), pair[1].getTracks().get(0) })); + } + + // Produce the Møller track-only plots. + møgblInstancesInEvent.fill(møllersGBL.size()); + RelationalTable<?, ?> hitToStrips = TrackUtils.getHitToStripsTable(event); + RelationalTable<?, ?> hitToRotated = TrackUtils.getHitToRotatedTable(event); + for(ReconstructedParticle pair[] : møllersGBL) { + // Get the tracks and track times. + Track[] tracks = { pair[0].getTracks().get(0), pair[1].getTracks().get(0) }; + double times[] = { + TrackUtils.getTrackTime(tracks[0], hitToStrips, hitToRotated), + TrackUtils.getTrackTime(tracks[1], hitToStrips, hitToRotated) + }; + + // Fill the plots. + møgblTimeCoincidence.fill(times[0] - times[1]); + møgblInvariantMass.fill(getInvariantMass(pair)); + møgblElectronMomentum.fill(pair[0].getMomentum().magnitude()); + møgblElectronMomentum.fill(pair[1].getMomentum().magnitude()); + møgblMomentumSum1D.fill(VecOp.add(pair[0].getMomentum(), pair[1].getMomentum()).magnitude()); + møgblMomentumSum2D.fill(pair[0].getMomentum().magnitude(), pair[1].getMomentum().magnitude()); + møgblClusterPosition.fill(TrackUtils.getTrackPositionAtEcal(tracks[0]).x(), TrackUtils.getTrackPositionAtEcal(tracks[0]).y()); + møgblClusterPosition.fill(TrackUtils.getTrackPositionAtEcal(tracks[1]).x(), TrackUtils.getTrackPositionAtEcal(tracks[1]).y()); + møgblPSumCoplanarity.fill(VecOp.add(pair[0].getMomentum(), pair[1].getMomentum()).magnitude(), + getCalculatedCoplanarity(new Track[] { pair[0].getTracks().get(0), pair[1].getTracks().get(0) })); + } + + // Produce track-only trident plots. + trgblInstancesInEvent.fill(tridentsGBL.size()); + for(ReconstructedParticle[] pair : tridentsGBL) { + // Get the tracks and track times. + Track[] tracks = { pair[0].getTracks().get(0), pair[1].getTracks().get(0) }; + double times[] = { + TrackUtils.getTrackTime(tracks[0], hitToStrips, hitToRotated), + TrackUtils.getTrackTime(tracks[1], hitToStrips, hitToRotated) + }; + + // Fill the plots. + trgblTimeCoincidence.fill(times[0] - times[1]); + trgblInvariantMass.fill(getInvariantMass(pair)); + trgblElectronMomentum.fill(pair[0].getMomentum().magnitude()); + trgblElectronMomentum.fill(pair[1].getMomentum().magnitude()); + trgblMomentumSum1D.fill(VecOp.add(pair[0].getMomentum(), pair[1].getMomentum()).magnitude()); + trgblMomentumSum2D.fill(pair[0].getMomentum().magnitude(), pair[1].getMomentum().magnitude()); + trgblClusterPosition.fill(TrackUtils.getTrackPositionAtEcal(tracks[0]).x(), TrackUtils.getTrackPositionAtEcal(tracks[0]).y()); + trgblClusterPosition.fill(TrackUtils.getTrackPositionAtEcal(tracks[1]).x(), TrackUtils.getTrackPositionAtEcal(tracks[1]).y()); + trgblPSumCoplanarity.fill(VecOp.add(pair[0].getMomentum(), pair[1].getMomentum()).magnitude(), + getCalculatedCoplanarity(new Track[] { pair[0].getTracks().get(0), pair[1].getTracks().get(0) })); + } + } + + /** + * Gets a list of all possible GBL top/bottom track pairs. These + * tracks are not guaranteed to have a matched cluster. + * @param trackList - A list of all possible tracks. + * @return Returns a list of track pairs. + */ + private static final List<ReconstructedParticle[]> getTopBottomTracksGBL(List<ReconstructedParticle> trackList) { + // Separate the tracks into top and bottom tracks based on + // the value of tan(Î). Use only GBL tracks to avoid track + // duplication. + List<ReconstructedParticle> topTracks = new ArrayList<ReconstructedParticle>(); + List<ReconstructedParticle> botTracks = new ArrayList<ReconstructedParticle>(); + trackLoop: + for(ReconstructedParticle track : trackList) { + // Require that the ReconstructedParticle contain an actual + // Track object. + if(track.getTracks().isEmpty()) { + continue trackLoop; + } + + // Ignore tracks that are not GBL tracks. + if(!TrackType.isGBL(track.getType())) { + continue trackLoop; + } + + // If the above tests pass, the ReconstructedParticle has + // a track and is also a GBL track. Separate it into either + // a top or a bottom track based on its tan(Î) value. + if(track.getTracks().get(0).getTrackStates().get(0).getTanLambda() > 0) { + topTracks.add(track); + } else { + botTracks.add(track); + } + } + + // Form all top/bottom pairs with the unique tracks. + List<ReconstructedParticle[]> pairList = new ArrayList<ReconstructedParticle[]>(); + for(ReconstructedParticle topTrack : topTracks) { + for(ReconstructedParticle botTrack : botTracks) { + pairList.add(new ReconstructedParticle[] { topTrack, botTrack }); + } + } + + // Return the result. + return pairList; } /** @@ -243,6 +389,81 @@ // Return the result. return pairList; + } + + private final List<ReconstructedParticle[]> getTridentClustersGBL(List<ReconstructedParticle[]> pairList, List<Cluster[]> clusterList, EventHeader event) { + // Store the set of track pairs that meet the trident condition. + List<ReconstructedParticle[]> tridentTracks = new ArrayList<ReconstructedParticle[]>(); + + // Extract track relational tables from the event object. + RelationalTable<?, ?> hitToStrips = TrackUtils.getHitToStripsTable(event); + RelationalTable<?, ?> hitToRotated = TrackUtils.getHitToRotatedTable(event); + + // Tracks will not be considered for trident analysis unless there + // is at least one top/bottom cluster pair within the time window. + boolean passesClusterCondition = false; + tridentClusterLoop: + for(Cluster[] pair : clusterList) { + // Ignore clusters that are too far apart temporally. + if(TriggerModule.getValueTimeCoincidence(pair) > timeCoincidence) { + continue tridentClusterLoop; + } + + // Require that the cluster pair be top/bottom. + boolean hasTop = TriggerModule.getClusterYIndex(pair[0]) > 0 || TriggerModule.getClusterYIndex(pair[1]) > 0; + boolean hasBot = TriggerModule.getClusterYIndex(pair[0]) < 0 || TriggerModule.getClusterYIndex(pair[1]) < 0; + if(!hasTop || !hasBot) { + continue tridentClusterLoop; + } + + // If the cluster passes, mark that it has done so and skip + // the rest. Only one pair need pass. + passesClusterCondition = true; + break tridentClusterLoop; + } + + // If no cluster pair passed the cluster condition, no tracks + // are allowed to pass either. + if(!passesClusterCondition) { + return tridentTracks; + } + + // Next, check the track pair list. A track pair must have a + // positive and a negative track and must also be within the + // time coincidence window. + tridentTrackLoop: + for(ReconstructedParticle[] pair : pairList) { + // Check that there is at least one positive and one negative + // track in the pair. + boolean hasPositive = pair[0].getCharge() > 0 || pair[1].getCharge() > 0; + boolean hasNegative = pair[0].getCharge() < 0 || pair[1].getCharge() < 0; + if(hasPositive && hasNegative) { + break tridentTrackLoop; + } + + // Check that the track pair passes the time cut. + double times[] = { + TrackUtils.getTrackTime(pair[0].getTracks().get(0), hitToStrips, hitToRotated), + TrackUtils.getTrackTime(pair[1].getTracks().get(0), hitToStrips, hitToRotated) + }; + + if(Math.abs(times[0] - times[1]) > timeCoincidence) { + continue tridentTrackLoop; + } + + // Require that the negative track have less than 900 MeV + // momentum to exclude elastic electrons. + if(pair[0].getCharge() < 0 && pair[0].getMomentum().magnitude() > 0.900 + || pair[1].getCharge() < 0 && pair[1].getMomentum().magnitude() > 0.900) { + continue tridentTrackLoop; + } + + // If the track passes both, it is considered a trident pair. + tridentTracks.add(pair); + } + + // Return the resultant pairs. + return tridentTracks; } /** @@ -305,6 +526,55 @@ // Return the list of pairs that passed the condition. return tridentTracks; + } + + private final List<ReconstructedParticle[]> getMøllerTracksGBL(List<ReconstructedParticle[]> pairList, EventHeader event) { + // Store the set of track pairs that meet the Møller condition. + List<ReconstructedParticle[]> møllerTracks = new ArrayList<ReconstructedParticle[]>(); + + // Extract track relational tables from the event object. + RelationalTable<?, ?> hitToStrips = TrackUtils.getHitToStripsTable(event); + RelationalTable<?, ?> hitToRotated = TrackUtils.getHitToRotatedTable(event); + + // Loop over the filtered pair list and apply the Møller + // condition test. + møllerLoop: + for(ReconstructedParticle[] pair : pairList) { + // Both tracks must be negatively charged. + if(pair[0].getCharge() > 0 || pair[1].getCharge() > 0) { + continue møllerLoop; + } + + // The clusters must within a limited time window. + double times[] = { + TrackUtils.getTrackTime(pair[0].getTracks().get(0), hitToStrips, hitToRotated), + TrackUtils.getTrackTime(pair[1].getTracks().get(0), hitToStrips, hitToRotated) + }; + + if(Math.abs(times[0] - times[1]) > timeCoincidence) { + continue møllerLoop; + } + + // Require that the electrons in the pair have energies + // below 900 MeV to exclude elastic electrons. + if(pair[0].getMomentum().magnitude() > 0.900 || pair[1].getMomentum().magnitude() > 0.900) { + continue møllerLoop; + } + + // Require that the energy of the pair be within a range + // that is sufficiently "Møller-like." + double momentumSum = VecOp.add(pair[0].getMomentum(), pair[1].getMomentum()).magnitude(); + if(momentumSum < 0.800 || momentumSum > 1.500) { + continue møllerLoop; + } + + // If all the above conditions are met, the pair is to be + // considered a trident pair. Add it to the list. + møllerTracks.add(pair); + } + + // Return the list of pairs that passed the condition. + return møllerTracks; } /** @@ -386,5 +656,56 @@ // Calculate the invariant mass. return Math.sqrt(p2 - Math.pow(xPro, 2) - Math.pow(yPro, 2) - Math.pow(zPro, 2)); } + + /** + * Calculates the coplanarity angle between two points, specified + * by a double array. The array must be of the format (x, y, z). + * @param position - The first position array. + * @param otherPosition - The second position array. + * @return Returns the coplanarity angle between the points in units + * of degrees. + */ + private static final double getCalculatedCoplanarity(double[] position, double[] otherPosition) { + // Define the x- and y-coordinates of the clusters as well as + // calorimeter center. + final double ORIGIN_X = 42.52; + double x[] = { position[0], otherPosition[0] }; + double y[] = { position[1], otherPosition[1] }; + + // Get the cluster angles. + double[] clusterAngle = new double[2]; + for(int i = 0; i < 2; i++) { + clusterAngle[i] = Math.atan2(y[i], x[i] - ORIGIN_X) * 180 / Math.PI; + if(clusterAngle[i] <= 0) { clusterAngle[i] += 360; } + } + + // Calculate the coplanarity cut value. + double clusterDiff = clusterAngle[0] - clusterAngle[1]; + return clusterDiff > 0 ? clusterDiff : clusterDiff + 360; + } + + /** + * Calculates the coplanarity angle of a pair of clusters. + * @param pair - The pair of clusters for which to calculate the + * coplanarity angle. + * @return Returns the coplanarity angle between the two clusters + * in degrees. + */ + private static final double getCalculatedCoplanarity(Cluster[] pair) { + return getCalculatedCoplanarity(pair[0].getPosition(), pair[1].getPosition()); + } + + /** + * Calculates the coplanarity angle of a pair of tracks. The track + * is extrapolated to the calorimeter face and its position there + * used for the arguments in the calculation. + * @param pair - The pair of tracks for which to calculate the + * coplanarity angle. + * @return Returns the coplanarity angle between the two tracks + * in degrees. + */ + private static final double getCalculatedCoplanarity(Track[] pair) { + return getCalculatedCoplanarity(TrackUtils.getTrackPositionAtEcal(pair[0]).v(), TrackUtils.getTrackPositionAtEcal(pair[1]).v()); + } } // [log in to unmask]