From 44f11973abf49f6c03d4a9a85988984608c95159 Mon Sep 17 00:00:00 2001 From: Matthew Gignac Date: Thu, 1 Oct 2026 07:24:50 -0700 Subject: [PATCH 1/4] Optionally refit target-constrained vertices about the first-pass vertex UNCONSTRAINED and BS_CONSTRAINED vertices are refit after shifting the tracks to the first-pass vertex, because the Billior fit linearizes the trajectories about the reference point. TARGET_CONSTRAINED vertices were only fit once, linearized about (0,0,0). - HpsReconParticleDriver: add refitTargetConstrained (default false, so existing behaviour is unchanged) and include TARGET_CONSTRAINED in the refit branch of fitVertex() when it is set. - BilliorVertexer.makeRk: in target-constraint mode, add the reference position back to the vertex before comparing with the absolute target position. This is a no-op when the reference is the origin (the first pass), and is needed for the refit to pin to the right point. In 2021 prompt A' MC this removes the ~2% narrowing of the opening angle and the matching low mass bias of the target-constrained vertex. --- .../particle/HpsReconParticleDriver.java | 22 ++++++++++++++++++- .../hps/recon/vertexing/BilliorVertexer.java | 9 +++++--- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/recon/src/main/java/org/hps/recon/particle/HpsReconParticleDriver.java b/recon/src/main/java/org/hps/recon/particle/HpsReconParticleDriver.java index b10c5170f7..8cfd6949ea 100644 --- a/recon/src/main/java/org/hps/recon/particle/HpsReconParticleDriver.java +++ b/recon/src/main/java/org/hps/recon/particle/HpsReconParticleDriver.java @@ -145,6 +145,13 @@ public class HpsReconParticleDriver extends ReconParticleDriver { private double[] beamPositionToUse = new double[3]; private boolean requireClustersForV0 = true; + /** + * Whether to refit TARGET_CONSTRAINED vertices after shifting the tracks + * to the first-pass vertex, as is done for UNCONSTRAINED and + * BS_CONSTRAINED. Off by default (original behaviour). + */ + private boolean refitTargetConstrained = false; + /** * Represents a type of constraint for vertex fitting. * @@ -283,6 +290,16 @@ public void setStoreCovTrkMomList(boolean b) { public void setRequireClustersForV0(boolean b) { this.requireClustersForV0 = b; } + + /** + * Set whether TARGET_CONSTRAINED vertices are refit with the tracks + * shifted to the first-pass vertex. + * + * @param b True to refit target-constrained vertices + */ + public void setRefitTargetConstrained(boolean b) { + this.refitTargetConstrained = b; + } public void setUnconstrainedMollerCandidatesColName(String s) { @@ -589,7 +606,10 @@ private BilliorVertex fitVertex(Constraint constraint, ReconstructedParticle ele // ... this is required because the vertex fit assumes trajectories // change linearly about the reference point (which we initially guess to be // (0,0,0) while for long-lived decays there is significant curvature - if (constraint == Constraint.BS_CONSTRAINED || constraint == Constraint.UNCONSTRAINED) { + // Optionally do the same for TARGET_CONSTRAINED: the first pass is + // linearized about (0,0,0), the refit about the target point itself. + if (constraint == Constraint.BS_CONSTRAINED || constraint == Constraint.UNCONSTRAINED + || (constraint == Constraint.TARGET_CONSTRAINED && refitTargetConstrained)) { List recoList = new ArrayList(); recoList.add(electron); recoList.add(positron); diff --git a/recon/src/main/java/org/hps/recon/vertexing/BilliorVertexer.java b/recon/src/main/java/org/hps/recon/vertexing/BilliorVertexer.java index ee649f990c..211bbb6714 100644 --- a/recon/src/main/java/org/hps/recon/vertexing/BilliorVertexer.java +++ b/recon/src/main/java/org/hps/recon/vertexing/BilliorVertexer.java @@ -453,9 +453,12 @@ private BasicMatrix makeRk(double Vx, double Vy, double Vz, double pxtot, double rk.setElement(1, 0, _beamPosition[1] - ((Vy + _referencePosition[1]) - pytot / pxtot * (Vx - _beamPosition[0] + _referencePosition[0]))); rk.setElement(2, 0, _beamPosition[2] - ((Vz + _referencePosition[2]) - pztot / pxtot * (Vx - _beamPosition[0] + _referencePosition[0]))); } else { - rk.setElement(0, 0, _beamPosition[0] - Vx); - rk.setElement(1, 0, _beamPosition[1] - Vy); - rk.setElement(2, 0, _beamPosition[2] - Vz); + // The vertex position is relative to the reference position, so add it back + // before comparing with the (absolute) target position. The reference is the + // origin unless the tracks were shifted for a refit (see HpsReconParticleDriver). + rk.setElement(0, 0, _beamPosition[0] - (Vx + _referencePosition[0])); + rk.setElement(1, 0, _beamPosition[1] - (Vy + _referencePosition[1])); + rk.setElement(2, 0, _beamPosition[2] - (Vz + _referencePosition[2])); } if (_debug) System.out.println("makeRk::rk = (" + rk.e(0, 0) + ", " + rk.e(1, 0) + ", " + rk.e(2, 0) + ")"); From 37d83cc3daddb7a514324a41b9e2e92568a4cb68 Mon Sep 17 00:00:00 2001 From: Matthew Gignac Date: Thu, 1 Oct 2026 07:24:50 -0700 Subject: [PATCH 2/4] Add 2021 MC 14272 steering with fixed beam position and TC refit Variant of PhysicsRun2021MCRecon_KF_WithSpacing_pass5_physics_HitSmear_HitKill_14272.lcsim: - beam position fixed at (x, y, z) = (0, 0, -1.1) mm (HPS frame) for KalmanPatRecDriver and ReconParticleDriver_Kalman, instead of being read from the conditions database, so it matches the MC beam; - refitTargetConstrained enabled. --- ..._HitSmear_HitKill_14272_fixedBeamPos.lcsim | 243 ++++++++++++++++++ 1 file changed, 243 insertions(+) create mode 100644 steering-files/src/main/resources/org/hps/steering/recon/PhysicsRun2021MCRecon_KF_WithSpacing_pass5_physics_HitSmear_HitKill_14272_fixedBeamPos.lcsim diff --git a/steering-files/src/main/resources/org/hps/steering/recon/PhysicsRun2021MCRecon_KF_WithSpacing_pass5_physics_HitSmear_HitKill_14272_fixedBeamPos.lcsim b/steering-files/src/main/resources/org/hps/steering/recon/PhysicsRun2021MCRecon_KF_WithSpacing_pass5_physics_HitSmear_HitKill_14272_fixedBeamPos.lcsim new file mode 100644 index 0000000000..9102e2702d --- /dev/null +++ b/steering-files/src/main/resources/org/hps/steering/recon/PhysicsRun2021MCRecon_KF_WithSpacing_pass5_physics_HitSmear_HitKill_14272_fixedBeamPos.lcsim @@ -0,0 +1,243 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 14 + false + + + false + + + pass5v9run14272_L1b_axial_hole.txt pass5v9run14272_L1b_stereo_hole.txt pass5v9run14272_L1t_axial_hole.txt pass5v9run14272_L1t_stereo_hole.txt pass5v9run14272_L2b_axial_hole.txt pass5v9run14272_L2b_stereo_hole.txt pass5v9run14272_L2t_axial_hole.txt pass5v9run14272_L2t_stereo_hole.txt + false + + + timeSmearing-2nsL4L7-4nsL1L3.txt + false + + + false + 0.8 + 0.8 + + + posSmearing_pass5_v9.txt + false + + + 1000 + + + HodoscopeReadoutHits + CONFIG + true + + + HodoscopeReadoutHits + 8 + CONFIG + true + + + + + + + 13.3 + + + WARNING + EcalClusters + + + EcalClusters + EcalClustersCorr + + + + SVTRawTrackerHits + + + .5 + 1 + Pileup + Migrad + true + 165 + true + true + false + true + false + false + true + false + + + 24.0 + 3.0 + false + 400 + 4.0 + 1.0 + 3.0 + 3.0 + true + true + false + + + true + true + 2 + 1 + 8.881915381218574 + 39.249197341740356 + 8.37847994612359 + 11.546843987796496 + 9 + 9 + 8 + 8 + 3 + 2 + 40.0 + 5.0 + 12.320066328390354 + 9.206482863412027 + 5.862027198856136 + 5.508828061070076 + 5 + false + 400 + 0.3473319986601534 + false + 0.0 + 0.0 + 0.055 + 0.045 + true + -1.1 + 7.197594353612735 + false + + + KalmanFullTracks + true + false + + + EcalClustersCorr + KalmanFullTracks + KalmanFullTracks + TrackClusterMatcherMinDistance + UnconstrainedV0Candidates_KF + UnconstrainedV0Vertices_KF + BeamspotConstrainedV0Candidates_KF + BeamspotConstrainedV0Vertices_KF + TargetConstrainedV0Candidates_KF + TargetConstrainedV0Vertices_KF + FinalStateParticles_KF + OtherElectrons_KF + true + false + false + + true + 0.0 + 0.0 + 0.055 + 0.045 + -1.1 + 7.0 + 7.0 + 0.0 + 40.0 + 40 + 40 + false + true + true + false + true + true + UnconstrainedMollerCandidates_KF + UnconstrainedMollerVertices_KF + BeamspotConstrainedMollerCandidates_KF + BeamspotConstrainedMollerVertices_KF + TargetConstrainedMollerCandidates_KF + TargetConstrainedMollerVertices_KF + + + ${outputFile}_hit_eff.root + + + ${outputFile}.slcio + + + + + + From 345f5793869d7111c53fb07d530c83c6d2858636 Mon Sep 17 00:00:00 2001 From: Matthew Gignac Date: Mon, 10 Aug 2026 09:41:10 -0700 Subject: [PATCH 3/4] ReconParticleDriver: make longitudinal beam size configurable via setBeamSigmaZ beamSize[0] (tracking-frame X = detector Z) is the longitudinal width used as the target-constraint vertex covariance. Previously only sigmaX/sigmaY had setters and beamSize[0] stayed hard-wired to 1 um, over-constraining the target-constrained vertex Z and skewing post-fit track momenta. Exposes it as the steering knob. --- .../org/hps/recon/particle/ReconParticleDriver.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/recon/src/main/java/org/hps/recon/particle/ReconParticleDriver.java b/recon/src/main/java/org/hps/recon/particle/ReconParticleDriver.java index a6a798c89d..b6341bab55 100644 --- a/recon/src/main/java/org/hps/recon/particle/ReconParticleDriver.java +++ b/recon/src/main/java/org/hps/recon/particle/ReconParticleDriver.java @@ -285,6 +285,16 @@ public void setBeamSigmaY(double sigmaY) { beamSize[2] = sigmaY; // The beamsize array is in the tracking frame HPS Y => TRACK Z } + /** + * Sets the beam size sigma in the z-direction. + * + * @param sigmaZ - The standard deviation of the beam width in the + * z-direction (beam/target longitudinal spread). + */ + public void setBeamSigmaZ(double sigmaZ) { + beamSize[0] = sigmaZ; // The beamsize array is in the tracking frame HPS Z => TRACK X + } + /** * Sets the beam position in the z-direction in mm. * From 596822e6a6eeba5cab08e8f12ad6dc85b4ad8d3d Mon Sep 17 00:00:00 2001 From: Matthew Gignac Date: Thu, 1 Oct 2026 15:13:42 -0700 Subject: [PATCH 4/4] Fit vertices about the track state reference point BilliorTrack(Track) takes the parameters of the first track state, which are defined about that state's reference point. For Kalman tracks this is the beam spot (e.g. z = -1.1 mm), but the first-pass vertex fit assumed the origin. The vertex position was therefore relative to the beam spot, and the target constraint pulled it to twice the target z, closing the e+e- opening angle by ~2% and biasing the target-constrained mass low. - HpsReconParticleDriver: add useTrackReferencePoint (default true). In fitVertex(), set the vertexer reference position to the reference point of the first track state before the first fit, and warn if the electron and positron reference points differ. Set false to reproduce the original behaviour. With this, target-constrained vertices no longer need refitTargetConstrained. Unconstrained and beamspot-constrained vertices are refit about the first-pass vertex as before, so their output is essentially unchanged. --- .../particle/HpsReconParticleDriver.java | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/recon/src/main/java/org/hps/recon/particle/HpsReconParticleDriver.java b/recon/src/main/java/org/hps/recon/particle/HpsReconParticleDriver.java index 8cfd6949ea..3ea4b69df4 100644 --- a/recon/src/main/java/org/hps/recon/particle/HpsReconParticleDriver.java +++ b/recon/src/main/java/org/hps/recon/particle/HpsReconParticleDriver.java @@ -152,6 +152,17 @@ public class HpsReconParticleDriver extends ReconParticleDriver { */ private boolean refitTargetConstrained = false; + /** + * Whether the first-pass vertex fit uses the reference point of the + * input track states. Kalman perigee states are defined about the beam + * spot (e.g. z = -1.1 mm), not the origin; without this the fitter + * assumes the origin, which biases target-constrained fits (the vertex + * is pulled to twice the target z and the opening angle closes by ~2%). + * On by default; set false to reproduce the original (pass5 and earlier) + * behaviour. + */ + private boolean useTrackReferencePoint = true; + /** * Represents a type of constraint for vertex fitting. * @@ -300,6 +311,17 @@ public void setRequireClustersForV0(boolean b) { public void setRefitTargetConstrained(boolean b) { this.refitTargetConstrained = b; } + + /** + * Set whether the first-pass vertex fit is done about the reference + * point of the input track states instead of the origin (default true). + * Set false only to reproduce the original behaviour. + * + * @param b True to use the track state reference point + */ + public void setUseTrackReferencePoint(boolean b) { + this.useTrackReferencePoint = b; + } public void setUnconstrainedMollerCandidatesColName(String s) { @@ -562,6 +584,22 @@ private BilliorVertex fitVertex(Constraint constraint, ReconstructedParticle ele break; } + // The Billior tracks carry the parameters of the first track state, + // which are defined about that state's reference point (tracking + // frame). Fit about the same point so that the vertex position and + // the beam/target constraints are computed in absolute coordinates. + if (useTrackReferencePoint) { + double[] eleRef = electron.getTracks().get(0).getTrackStates().get(0).getReferencePoint(); + double[] posRef = positron.getTracks().get(0).getTrackStates().get(0).getReferencePoint(); + if (Math.abs(eleRef[0] - posRef[0]) > 1e-6 || Math.abs(eleRef[1] - posRef[1]) > 1e-6 + || Math.abs(eleRef[2] - posRef[2]) > 1e-6) { + LOGGER.warning(String.format("Electron and positron track reference points differ: " + + "(%f, %f, %f) vs (%f, %f, %f); using the electron's.", + eleRef[0], eleRef[1], eleRef[2], posRef[0], posRef[1], posRef[2])); + } + vtxFitter.setReferencePosition(eleRef); + } + // Add the electron and positron tracks to a track list for // the vertex fitter. List billiorTracks = new ArrayList();